├── .github └── workflows │ └── main.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── cases.json ├── cases.txt ├── results.md ├── run.sh ├── scripts ├── cases.json ├── ed25519-ios │ ├── Ed25519.swift │ └── README.md ├── ed25519-java │ ├── README.md │ ├── src │ │ └── main │ │ │ └── java │ │ │ ├── Ed25519TestCase.java │ │ │ ├── EncodingUtils.java │ │ │ └── TestVectorChecker.java │ └── target │ │ └── pom.xml ├── ed25519-signal-donna │ ├── CMakeLists.txt │ ├── README.md │ ├── build+run.sh │ └── main.c ├── go │ ├── README.md │ └── main.go ├── libsodium │ ├── Makefile │ ├── README.md │ └── main.c ├── npm │ ├── README.md │ ├── eddsa_test.js │ ├── package-lock.json │ └── package.json ├── openssl_3 │ ├── README.md │ ├── main.c │ ├── test_script.sh │ └── test_vector.txt ├── pyca-openssl │ ├── README.md │ └── eddsa_utils.py ├── python-ed25519 │ ├── README.md │ └── add_test_git.patch ├── ref10 │ ├── Makefile │ ├── README.md │ ├── main │ ├── main.c │ └── to_ref10.patch └── tweetnacl-js │ ├── README.md │ ├── package.json │ ├── test.js │ └── yarn.lock └── src ├── main.rs └── non_reducing_scalar52.rs /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Rust CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | types: [opened, repoened, synchronize] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | name: test 15 | steps: 16 | - name: Checkout sources 17 | uses: actions/checkout@v2 18 | 19 | - name: Install nightly toolchain 20 | uses: actions-rs/toolchain@v1 21 | with: 22 | profile: minimal 23 | toolchain: nightly 24 | override: true 25 | components: rustfmt, clippy 26 | 27 | - name: Run cargo test 28 | uses: actions-rs/cargo@v1 29 | with: 30 | command: test 31 | 32 | clippy: 33 | name: cargo clippy 34 | runs-on: ubuntu-latest 35 | steps: 36 | - name: Checkout sources 37 | uses: actions/checkout@v2 38 | 39 | - name: Install nightly toolchain 40 | uses: actions-rs/toolchain@v1 41 | with: 42 | profile: minimal 43 | toolchain: nightly 44 | override: true 45 | components: rustfmt, clippy 46 | 47 | - name: Run cargo clippy 48 | uses: actions-rs/cargo@v1 49 | with: 50 | command: clippy 51 | args: -- -D warnings 52 | 53 | 54 | format: 55 | name: cargo fmt 56 | runs-on: ubuntu-latest 57 | steps: 58 | - name: Checkout sources 59 | uses: actions/checkout@v2 60 | 61 | - name: Install nightly toolchain 62 | uses: actions-rs/toolchain@v1 63 | with: 64 | profile: minimal 65 | toolchain: nightly 66 | override: true 67 | components: rustfmt, clippy 68 | 69 | - name: Run cargo fmt 70 | uses: actions-rs/cargo@v1 71 | with: 72 | command: fmt 73 | args: --all -- --check 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to this library 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Pull Requests 6 | We actively welcome your pull requests. 7 | 8 | 1. Fork the repo and create your branch from `master`. 9 | 2. If you've added code that should be tested, add tests. 10 | 3. If you've changed APIs, update the documentation. 11 | 4. Ensure the test suite passes. 12 | 5. If you haven't already, complete the Contributor License Agreement ("CLA"). 13 | 14 | ## Contributor License Agreement ("CLA") 15 | In order to accept your pull request, we need you to submit a CLA. You only need 16 | to do this once to work on any of Facebook's open source projects. 17 | 18 | Complete your CLA here: 19 | 20 | ## Issues 21 | We use GitHub issues to track public bugs. Please ensure your description is 22 | clear and has sufficient instructions to be able to reproduce the issue. 23 | 24 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 25 | disclosure of security bugs. In those cases, please go through the process 26 | outlined on that page and do not file a public issue. 27 | 28 | ## License 29 | By contributing to ed25519-speccheck, you agree that your contributions will be 30 | licensed under the LICENSE file in the root directory of this source tree. 31 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ed25519-speccheck" 3 | version = "0.1.0" 4 | repository = "https://github.com/novifinancial/ed25519-speccheck" 5 | description = "An implementation of the tests described in https://eprint.iacr.org/2020/1244" 6 | authors = ["Konstantinos Chalkias ", "François Garillot ", "Valeria Nikolaenko "] 7 | license = "Apache-2.0" 8 | edition = "2018" 9 | readme = "README.md" 10 | 11 | [dependencies] 12 | anyhow = "1.0.32" 13 | curve25519-dalek = "2.1.0" 14 | hacl-star = { git = "https://github.com/huitseeker/rust-hacl-star", version = "0.2.0"} 15 | hex = "0.4.2" 16 | rand = "0.7.3" 17 | sha2 = "0.9.2" 18 | serde_json = "1.0" 19 | serde = { version = "1.0.115", features = ["derive"] } 20 | log = "0.4.11" 21 | env_logger = "0.7.1" 22 | string-builder = "0.2.0" 23 | 24 | [dev-dependencies] 25 | ed25519-dalek = "1.0.0-pre.4" 26 | ed25519-zebra = "2.1.1" 27 | ring = "0.16.5" 28 | untrusted = "0.7.1" 29 | diem-crypto = { git = "https://github.com/diem/diem.git" } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ed25519-speccheck 2 | 3 | This repository generates and uses test vectors for Ed25519 signature scheme to check edge cases 4 | in the implementation of the algorithm. Namely, we test bounds checks on points 5 | and scalars involved in a signature, along with cofactored vs. cofactorless verification. 6 | 7 | We hope this helps outline the measures needed to implement the FIPS 186-5 and 8 | RFC 8032 standards rigorously. For more information, read our [paper])(https://eprint.iacr.org/2020/1244, published 9 | [at SSR'20](https://ssr2020.mozilla.org/accepted-papers). 10 | 11 | You can run this utility with `RUST_LOG=debug cargo run` to get a list of the 12 | test vectors and their inteded test conditions. 13 | 14 | # Usage 15 | 16 | To print out details on the test cases, use `RUST_LOG=debug cargo run`. 17 | 18 | To generate files with test cases, `cases.json` and `cases.txt`, use `cargo run`. 19 | 20 | To run the scripts on the connected libraries, execute the `./run.sh` script at 21 | the root of the project (some additional installations of the associated libraries might be required). 22 | 23 | ## Condition table 24 | 25 | Those are the cases we considered, with the index of the test vectors when applicable: 26 | 27 | ``` 28 | ------------------------------------------------------------------------------------------------------------ 29 | | | msg | sig | S | A ord | R ord | cof-ed | cof-less | comment | 30 | |------------------------------------------------------------------------------------------------------------| 31 | | 0| ..22b6 | ..0000 | S = 0 | small | small | V | V | small A and R | 32 | | 1| ..2e79 | ..ac04 | 0 < S < L | small | mixed | V | V | small A only | 33 | | 2| ..b9ab | ..260e | 0 < S < L | mixed | small | V | V | small R only | 34 | | 3| ..2e79 | ..d009 | 0 < S < L | mixed | mixed | V | V | succeeds unless full-order is checked | 35 | | 4| ..f56c | ..1a09 | 0 < S < L | mixed | mixed | V | X | | 36 | | 5| ..f56c | ..7405 | 0 < S < L | mixed | L | V* | X | fails cofactored iff (8h) prereduced | 37 | | 6| ..ec40 | ..a514 | S > L | L | L | V | V | S out of bounds | 38 | | 7| ..ec40 | ..8c22 | S >> L | L | L | V | V | S out of bounds | 39 | | 8| ..8b41 | ..5f0f | 0 < S < L | mixed | small*| V | V | non-canonical R, reduced for hash | 40 | | 9| ..8b41 | ..4908 | 0 < S < L | mixed | small*| V | V | non-canonical R, not reduced for hash | 41 | |10| ..155b | ..ac04 | 0 < S < L | small*| mixed | V | V | non-canonical A, reduced for hash | 42 | |11| ..c06f | ..ac04 | 0 < S < L | small*| mixed | V | V | non-canonical A, not reduced for hash | 43 | ------------------------------------------------------------------------------------------------------------ 44 | ``` 45 | 46 | Here "mixed" means with a strictly positive torsion component but not small, 47 | i.e. "mixed" and "small" are mutually exclusive. Out of the eight test cases 48 | above, only some are concretely testable: 49 | 50 | Vectors 0-2 have either small A or small R, or both. 51 | 52 | Vector 3 has A and R mixed and succeeds in both cofactored and cofactorless. 53 | 54 | Vector 4 has A and R mixed, succeeds in cofactored and fails cofactorless. This vector is the main indicator for a cofactored verification equation. 55 | 56 | Besides small components, we also test: 57 | 58 | - a large S > L (prepared to pass cofactorless and cofactored) (vectors 6, 7, 59 | where vector 7 contains an S so large it can't have a canonical serialization 60 | with a null high bit). 61 | - a "pre-reduced" scalar (vector 5), namely one that fails if the verification equation is 62 | `[8] R + [8 k] A = [8 s] B` rather than the recommended `[8] (R + k A) = [8] sB`. 63 | (which passes cofactored, without pre-reduction). 64 | - a non-canonical representation of R (vectors 8 & 9). 65 | - a non-canonical representation of A (vectors 10 & 11). 66 | 67 | For a total of 12 test vectors. 68 | 69 | ## Verified libraries 70 | 71 | - [Apple CryptoKit](https://developer.apple.com/documentation/cryptokit) : in `scripts/ed25519-ios` 72 | - BoringSSL, through [Ring](https://github.com/briansmith/ring) : in unit tests 73 | - [Bouncy Castle (Java)](https://www.bouncycastle.org/java.html) version 1.66 : in `scripts/ed25519-java` 74 | - [Dalek](https://github.com/dalek-cryptography/ed25519-dalek) : in unit tests 75 | - [ed25519-donna from Signal](https://github.com/signalapp/libsignal-protocol-c.git): in `scripts/ed25519-signal-donna` 76 | - [ed25519-java](https://github.com/str4d/ed25519-java) version 0.3.0 : in `scripts/ed25519-java` 77 | - [Go-ed25519](https://golang.org/pkg/crypto/ed25519/) : in `scripts/ed25519_test.go` 78 | - [libra-crypto](https://github.com/libra/libra/tree/master/crypto/crypto) : in unit tests 79 | - LibSodium, through [pynacl](https://github.com/pyca/pynacl) : in `scripts/pynacl_test.py` 80 | - nCipher's ed25519, by Rob Starkey 81 | - [npm's ed25519](https://www.npmjs.com/package/ed25519) : in `scripts/eddsa_test` 82 | - [OpenSSL](https://github.com/openssl/openssl) : in `scripts openssl_3/test_script.sh` 83 | - [Pyca](https://cryptography.io/en/latest/) using OpenSSL 1.1.1g as default backend : in `scripts/pyca-openssl` 84 | - [python-ed25519](https://github.com/warner/python-ed25519)) : in `scripts/python-ed25519` 85 | - [ref10 from SUPERCOP through Python bindings](https://github.com/warner/python-ed25519) : in `scripts/python-ed25519.py` 86 | - [tweetnacl](https://www.npmjs.com/package/tweetnacl) version 1.0.3 : in `scripts/tweetnacl` 87 | - [Zebra](https://github.com/ZcashFoundation/ed25519-zebra) : in unit tests 88 | 89 | ## Results 90 | 91 | ``` 92 | --------------------------------------------------------------- 93 | |Library | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 94 | |---------------+---+---+---+---+---+---+---+---+---+---+---+---| 95 | |BoringSSL | V | V | V | V | X | X | X | X | X | X | X | V | 96 | |BouncyCastle | V | V | V | V | X | X | X | X | X | X | X | X | 97 | |CryptoKit | V | V | V | V | X | X | X | X | X | X | X | V | 98 | |Dalek | V | V | V | V | X | X | X | X | X | X | X | V | 99 | |Dalek strict | X | X | X | V | X | X | X | X | X | X | X | X | 100 | |ed25519-donna | V | V | V | V | X | X | V | X | X | X | X | V | 101 | |ed25519-java | V | V | V | V | X | X | V | V | X | X | V | X | 102 | |Go | V | V | V | V | X | X | X | X | X | X | X | V | 103 | |libra-crypto | X | X | X | V | X | X | X | X | X | X | X | X | 104 | |LibSodium | X | X | X | V | X | X | X | X | X | X | X | X | 105 | |npm | V | V | V | V | X | X | X | X | X | X | X | V | 106 | |OpenSSL-3.0 | V | V | V | V | X | X | X | X | X | X | X | V | 107 | |PyCA | V | V | V | V | X | X | X | X | X | X | X | V | 108 | |python-ed25519 | V | V | V | V | X | X | V | V | X | X | X | V | 109 | |ref10 | V | V | V | V | X | X | V | X | X | X | X | V | 110 | |TweetNaCl-js | V | V | V | V | X | X | V | V | X | X | X | V | 111 | |Zebra | V | V | V | V | V | V | X | X | X | V | V | V | 112 | --------------------------------------------------------------- 113 | ``` 114 | 115 | Contributors 116 | ------------ 117 | 118 | The authors of this code are Kostas Chalkias ([@kchalkias](https://github.com/kchalkias)), François Garillot ([@huitseeker](https://github.com/huitseeker)) and Valeria Nikolaenko ([@valerini](https://github.com/valerini)). To learn more about contributing to this project, [see this document](./CONTRIBUTING.md). 119 | 120 | #### Acknowledgments 121 | 122 | Special thanks go to Yolan Romailler, Rajath Shanbag and Rob Starkey for contributing test 123 | vector results. 124 | 125 | 126 | License 127 | ------- 128 | 129 | This project is [Apache 2.0 licensed](./LICENSE). 130 | -------------------------------------------------------------------------------- /cases.json: -------------------------------------------------------------------------------- 1 | [{"message":"8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6","pub_key":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa","signature":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000"},{"message":"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79","pub_key":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa","signature":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"},{"message":"aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab","pub_key":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43","signature":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e"},{"message":"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79","pub_key":"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d","signature":"9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009"},{"message":"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c","pub_key":"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d","signature":"160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09"},{"message":"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c","pub_key":"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d","signature":"21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405"},{"message":"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40","pub_key":"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623","signature":"e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514"},{"message":"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40","pub_key":"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623","signature":"8ce5b96c8f26d0ab6c47958c9e68b937104cd36e13c33566acd2fe8d38aa19427e71f98a473474f2f13f06f97c20d58cc3f54b8bd0d272f42b695dd7e89a8c22"},{"message":"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41","pub_key":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43","signature":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f"},{"message":"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41","pub_key":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43","signature":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908"},{"message":"e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b","pub_key":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","signature":"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"},{"message":"39a591f5321bbe07fd5a23dc2f39d025d74526615746727ceefd6e82ae65c06f","pub_key":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","signature":"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"}] -------------------------------------------------------------------------------- /cases.txt: -------------------------------------------------------------------------------- 1 | 12 2 | msg=8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6 3 | pbk=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa 4 | sig=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000 5 | msg=9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79 6 | pbk=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa 7 | sig=f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04 8 | msg=aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab 9 | pbk=f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43 10 | sig=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e 11 | msg=9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79 12 | pbk=cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d 13 | sig=9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009 14 | msg=e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c 15 | pbk=cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d 16 | sig=160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09 17 | msg=e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c 18 | pbk=cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d 19 | sig=21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405 20 | msg=85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40 21 | pbk=442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623 22 | sig=e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514 23 | msg=85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40 24 | pbk=442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623 25 | sig=8ce5b96c8f26d0ab6c47958c9e68b937104cd36e13c33566acd2fe8d38aa19427e71f98a473474f2f13f06f97c20d58cc3f54b8bd0d272f42b695dd7e89a8c22 26 | msg=9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41 27 | pbk=f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43 28 | sig=ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f 29 | msg=9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41 30 | pbk=f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43 31 | sig=ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908 32 | msg=e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b 33 | pbk=ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 34 | sig=a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04 35 | msg=39a591f5321bbe07fd5a23dc2f39d025d74526615746727ceefd6e82ae65c06f 36 | pbk=ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 37 | sig=a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04 -------------------------------------------------------------------------------- /results.md: -------------------------------------------------------------------------------- 1 | |BoringSSL | V | V | V | V | X | X | X | X | X | X | X | V | 2 | |BouncyCastle | V | V | V | V | X | X | X | X | X | X | X | X | 3 | |CryptoKit | V | V | V | V | X | X | X | X | X | X | X | V | 4 | |Dalek | V | V | V | V | X | X | X | X | X | X | X | V | 5 | |Dalek strict | X | X | X | V | X | X | X | X | X | X | X | X | 6 | |ed25519-donna | V | V | V | V | X | X | V | X | X | X | X | V | 7 | |ed25519-java | V | V | V | V | X | X | V | V | X | X | V | X | 8 | |Go | V | V | V | V | X | X | X | X | X | X | X | V | 9 | |Hacl* | V | V | V | V | X | X | X | X | X | X | X | X | 10 | |libra-crypto | X | X | X | V | X | X | X | X | X | X | X | X | 11 | |LibSodium | X | X | X | V | X | X | X | X | X | X | X | X | 12 | |npm | V | V | V | V | X | X | X | X | X | X | X | V | 13 | |OpenSSL-3.0 | V | V | V | V | X | X | X | X | X | X | X | V | 14 | |PyCA | V | V | V | V | X | X | X | X | X | X | X | V | 15 | |python-ed25519 | V | V | V | V | X | X | V | V | X | X | X | V | 16 | |ref10 | V | V | V | V | X | X | V | X | X | X | X | V | 17 | |TweetNaCl-js | V | V | V | V | X | X | V | V | X | X | X | V | 18 | |Zebra | V | V | V | V | V | V | X | X | X | V | V | V | 19 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) Facebook, Inc. and its affiliates. 4 | # 5 | # This source code is licensed under the APACHE 2.0 license found in 6 | # the LICENSE file in the root directory of this source tree. 7 | 8 | SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 9 | 10 | main() { 11 | 12 | cd "$SOURCE_DIR" 13 | # Dalek, Zebra, BoringSSL, libra-crypto 14 | cargo test -- --nocapture --test-threads 1 15 | 16 | # CryptoKit 17 | pushd "$SOURCE_DIR/scripts/ed25519-ios" 18 | swift Ed25519.swift 19 | popd 20 | 21 | # ed25519-java, BouncyCastle 22 | if [[ -z "$JAVA_HOME" ]] 23 | then 24 | >&2 echo "Error running scripts/ed25519-java: JAVA_HOME is not set" 25 | else 26 | 27 | pushd "$SOURCE_DIR/scripts/ed25519-java" 28 | JAVA_LIBS="$HOME"/.m2/repository 29 | CLASSPATH=\ 30 | "$JAVA_HOME"/jre/lib/*:\ 31 | "$JAVA_HOME"/jre/lib/ext/*:\ 32 | "$JAVA_LIBS"/net/i2p/crypto/eddsa/0.3.0/eddsa-0.3.0.jar:\ 33 | "$JAVA_LIBS"/org/bouncycastle/bcprov-jdk15on/1.66/bcprov-jdk15on-1.66.jar:\ 34 | "$JAVA_LIBS"/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar 35 | 36 | javac -cp $CLASSPATH src/main/java/*.java 37 | java -cp $CLASSPATH:src/main/java TestVectorChecker 38 | popd 39 | fi 40 | 41 | # ed25519-donna 42 | pushd "$SOURCE_DIR/scripts/ed25519-signal-donna/build" 43 | cmake .. 44 | make 45 | ./test-donna 46 | popd 47 | 48 | # Go 49 | go run scripts/go/main.go 50 | 51 | # npm 52 | NODE=$(which node) 53 | pushd "$SOURCE_DIR/scripts/npm" 54 | $NODE eddsa_test.js 55 | popd 56 | 57 | # tweetnacl-js 58 | $NODE scripts/tweetnacl-js/test.js 59 | 60 | # python-ed25519 61 | pushd "$SOURCE_DIR/scripts/python-ed25519" 62 | if [ ! -d "./python-ed25519" ] 63 | then 64 | git clone git@github.com:warner/python-ed25519.git 65 | cd python-ed25519 66 | git reset --hard d57b8f2c7edffff3419d58443ccc29a4fa399a71 67 | git apply -v ../add_test_git.patch 68 | python3.7 setup.py build 69 | else 70 | cd "./python-ed25519" 71 | fi 72 | python3.7 setup.py test 73 | popd 74 | 75 | # PyCA 76 | python3.7 scripts/pyca-openssl/eddsa_utils.py 77 | 78 | # LibSodium 79 | pushd "$SOURCE_DIR/scripts/libsodium" 80 | export LIBSODIUM_INSTALL_DIR=$(pwd)/libsodium-stable-build 81 | echo $LIBSODIUM_INSTALL_DIR 82 | if [ ! -d "libsodium-stable-build" ] 83 | then 84 | wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.18-stable.tar.gz 85 | tar -xf libsodium-1.0.18-stable.tar.gz 86 | rm libsodium-1.0.18-stable.tar.gz 87 | mkdir libsodium-stable-build 88 | cd "./libsodium-stable/" 89 | ./configure --prefix="$LIBSODIUM_INSTALL_DIR" 90 | make & make check 91 | make install 92 | cd .. 93 | fi 94 | rm main 95 | make 96 | ./main 97 | popd 98 | 99 | # ref10 100 | pushd "$SOURCE_DIR/scripts/ref10" 101 | export LIBSODIUM_INSTALL_DIR=$(pwd)/libsodium-stable-build 102 | if [ ! -d "libsodium-stable-build" ] 103 | then 104 | wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.18-stable.tar.gz 105 | tar -xf libsodium-1.0.18-stable.tar.gz 106 | rm libsodium-1.0.18-stable.tar.gz 107 | mkdir libsodium-stable-build 108 | patch -p0 < to_ref10.patch 109 | cd "./libsodium-stable/" 110 | ./configure --prefix="$LIBSODIUM_INSTALL_DIR" 111 | make & make check 112 | make install 113 | cd .. 114 | fi 115 | rm main 116 | make 117 | ./main 118 | popd 119 | 120 | # openssl-3.0 121 | pushd "$SOURCE_DIR/scripts/openssl_3" 122 | export OPENSSL_INSTALL_DIR=$(pwd)/openssl-build 123 | if [ ! -d "openssl" ] 124 | then 125 | git clone git@github.com:openssl/openssl.git 126 | mkdir openssl-build 127 | cd "./openssl" 128 | git reset --hard 10203a34725ec75136b03d64fd2126b321419ac1 129 | ./Configure --prefix="$OPENSSL_INSTALL_DIR" 130 | make install 131 | cd .. 132 | fi 133 | 134 | ./test_script.sh 135 | popd 136 | } 137 | 138 | main > results.md 2>/dev/null 139 | 140 | sed -ire '/^|/!d' results.md 141 | sort -f results.md -o results.md 142 | 143 | cat results.md 144 | -------------------------------------------------------------------------------- /scripts/cases.json: -------------------------------------------------------------------------------- 1 | [{"message": "2a66241a42a9ee12994d8068dcf1bb7dfc6637b45450acd43711f637fa5080fc", "pub_key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", "signature": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000"}, 2 | {"message": "8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6", "pub_key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", "signature": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000"}, 3 | {"message": "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41", "pub_key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", "signature": "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"}, 4 | {"message": "9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79", "pub_key": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", "signature": "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"}, 5 | {"message": "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41", "pub_key": "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43", "signature": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa182ef1a5b928da07fec769cc8a12db6bcf70dab3f3227fa315e9d5e3e01a3405"}, 6 | {"message": "aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab", "pub_key": "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43", "signature": "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e"}, 7 | {"message": "e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c", "pub_key": "cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d", "signature": "160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09"}, 8 | {"message": "9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79", "pub_key": "cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d", "signature": "9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009"}, 9 | {"message": "e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c", "pub_key": "cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d", "signature": "21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405"}, 10 | {"message": "85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40", "pub_key": "442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623", "signature": "e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514"}, 11 | {"message": "85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40", "pub_key": "442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623", "signature": "8ce5b96c8f26d0ab6c47958c9e68b937104cd36e13c33566acd2fe8d38aa19427e71f98a473474f2f13f06f97c20d58cc3f54b8bd0d272f42b695dd7e89a8c22"}, 12 | {"message": "fdaebc429f4a735932a160da1301080c13280eea8bc280d1b392c6b9e6ba3a5a", "pub_key": "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43", "signature": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f454d370c8d9fc323a41450f8d513eafeb5b0697390c1e505a0d4ddc71f566607"}, 13 | {"message": "84b698d39be126ff55fe45079e6c8bf64a0d7db6994560b4e96b7021eb39c1a1", "pub_key": "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43", "signature": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f084d5b99c2a9463d9c8bd5026916996984eeec87ddf1d3be329006ace1b37b09"}, 14 | {"message": "e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b", "pub_key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", "signature": "a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"}, 15 | {"message": "39a591f5321bbe07fd5a23dc2f39d025d74526615746727ceefd6e82ae65c06f", "pub_key": "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", "signature": "a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"}] 16 | -------------------------------------------------------------------------------- /scripts/ed25519-ios/Ed25519.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | import CryptoKit 7 | import Foundation 8 | 9 | extension String { 10 | 11 | /// Create `Data` from hexadecimal string representation 12 | /// 13 | /// This creates a `Data` object from hex string. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed. 14 | /// 15 | /// - returns: Data represented by this hexadecimal string. 16 | 17 | var hexadecimal: Data? { 18 | var data = Data(capacity: count / 2) 19 | 20 | let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive) 21 | regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in 22 | let byteString = (self as NSString).substring(with: match!.range) 23 | let num = UInt8(byteString, radix: 16)! 24 | data.append(num) 25 | } 26 | 27 | guard data.count > 0 else { return nil } 28 | 29 | return data 30 | } 31 | } 32 | 33 | func verifySignature(index: Int, message: String, pubKey: String, signature: String) { 34 | let message = message.hexadecimal! 35 | let pubKeyData = pubKey.hexadecimal! 36 | let signature = signature.hexadecimal! 37 | 38 | 39 | let rawPubKey = try! Curve25519.Signing.PublicKey(rawRepresentation: pubKeyData) 40 | 41 | if (rawPubKey.isValidSignature(signature, for: message)) { 42 | print(" V |", terminator:"") 43 | } else { 44 | print(" X |", terminator:"") 45 | } 46 | // print("case \(index), \(rawPubKey.isValidSignature(signature, for: message))") 47 | } 48 | 49 | struct TestVector: Codable { 50 | let message: String 51 | let pub_key: String 52 | let signature: String 53 | } 54 | 55 | let path = "../../cases.json" 56 | let JSON = try! NSString(contentsOfFile: path, encoding: String.Encoding.ascii.rawValue) as String 57 | let jsonData = JSON.data(using: .utf8) 58 | let test_vectors = try! JSONDecoder().decode(Array.self, from: jsonData!) 59 | 60 | print("|CryptoKit |", terminator:"") 61 | 62 | var i = 0 63 | for tv in test_vectors { 64 | verifySignature( 65 | index: i, 66 | message: tv.message, 67 | pubKey: tv.pub_key, 68 | signature: tv.signature 69 | ) 70 | i += 1 71 | } 72 | print("") 73 | -------------------------------------------------------------------------------- /scripts/ed25519-ios/README.md: -------------------------------------------------------------------------------- 1 | To reproduce from terminal run `swift Ed25519.swift`. 2 | 3 | Tested for `swift --version`: 4 | Apple Swift version 5.3 (swiftlang-1200.0.28.1 clang-1200.0.30.1) 5 | Target: x86_64-apple-darwin19.6.0 6 | 7 | Output 8 | 9 | case 0, false 10 | case 1, true 11 | case 2, false 12 | case 3, true 13 | case 4, false 14 | case 5, true 15 | case 6, false 16 | case 7, true 17 | case 8, false 18 | case 9, false 19 | case 10, false 20 | case 11, false 21 | case 12, false 22 | case 13, true 23 | case 14, false -------------------------------------------------------------------------------- /scripts/ed25519-java/README.md: -------------------------------------------------------------------------------- 1 | Run from under IntelJ IDEA. 2 | 3 | Or set the CLASSPATH variable to point to all the jars in the dependencies and run 4 | `java -classpath $CLASSPATH TestVectorChecker` 5 | 6 | Note: A sample Maven `pom.xml` file with the required dependencies exists under the `/target` folder. 7 | 8 | `java -version` outputs 9 | java version "1.8.0_181" 10 | Java(TM) SE Runtime Environment (build 1.8.0_181-b13) 11 | Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode) 12 | 13 | Output: 14 | 15 | --- i2p OUTPUT --- 16 | 0: false 17 | 1: true 18 | 2: false 19 | 3: true 20 | 4: false 21 | 5: true 22 | 6: false 23 | 7: true 24 | 8: false 25 | 9: true 26 | 10: true 27 | 11: false 28 | 12: false 29 | 13: true 30 | 14: false 31 | 32 | --- BC OUTPUT --- 33 | 0: false 34 | 1: true 35 | 2: false 36 | 3: true 37 | 4: false 38 | 5: true 39 | 6: false 40 | 7: true 41 | 8: false 42 | 9: false 43 | 10: false 44 | 11: false 45 | 12: false 46 | 13: true 47 | 14: false -------------------------------------------------------------------------------- /scripts/ed25519-java/src/main/java/Ed25519TestCase.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | import net.i2p.crypto.eddsa.EdDSAEngine; 8 | import net.i2p.crypto.eddsa.EdDSAPublicKey; 9 | import net.i2p.crypto.eddsa.Utils; 10 | import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; 11 | import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec; 12 | import org.bouncycastle.crypto.Signer; 13 | import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; 14 | import org.bouncycastle.crypto.signers.Ed25519Signer; 15 | 16 | import java.security.MessageDigest; 17 | import java.security.spec.InvalidKeySpecException; 18 | import java.security.spec.X509EncodedKeySpec; 19 | 20 | /** 21 | * Ed25519 test case. 22 | **/ 23 | public class Ed25519TestCase { 24 | @SerializedName(value = "pub_key") 25 | private final String publicKeyHex; 26 | @SerializedName(value = "message") 27 | private final String messageHex; 28 | @SerializedName(value = "signature") 29 | private final String signatureHex; 30 | 31 | private final static EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); 32 | 33 | public Ed25519TestCase(String publicKeyHex, String messageHex, String signatureHex) { 34 | this.publicKeyHex = publicKeyHex; 35 | this.messageHex = messageHex; 36 | this.signatureHex = signatureHex; 37 | } 38 | 39 | /** 40 | * Return EdDSAPublicKey object from the hex representation of the compressed Edwards public key point. 41 | **/ 42 | private EdDSAPublicKey decodePublicKey() throws InvalidKeySpecException { 43 | byte[] pk = Utils.hexToBytes(this.publicKeyHex); 44 | byte[] x509pk = EncodingUtils.compressedEd25519PublicKeyToX509(pk); 45 | X509EncodedKeySpec encoded = new X509EncodedKeySpec(x509pk); 46 | return new EdDSAPublicKey(encoded); 47 | } 48 | 49 | /** 50 | * Pure Ed25519 signature verification using the i2p lib, it returns false if it fails or if an exception occurs). 51 | **/ 52 | public boolean verify_i2p() { 53 | try { 54 | EdDSAPublicKey publicKey = decodePublicKey(); 55 | byte[] messageBytes = Utils.hexToBytes(this.messageHex); 56 | byte[] signatureBytes = Utils.hexToBytes(this.signatureHex); 57 | EdDSAEngine sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm())); 58 | sgr.initVerify(publicKey); 59 | return sgr.verifyOneShot(messageBytes, signatureBytes); 60 | } catch (Exception e) { 61 | return false; 62 | } 63 | } 64 | 65 | /** 66 | * Pure Ed25519 signature verification using the BC lib, it returns false if it fails or if an exception occurs). 67 | **/ 68 | public boolean verify_bc() { 69 | try { 70 | Ed25519PublicKeyParameters publicKey = new Ed25519PublicKeyParameters( 71 | Utils.hexToBytes(this.publicKeyHex), 0); 72 | byte[] messageBytes = Utils.hexToBytes(this.messageHex); 73 | byte[] signatureBytes = Utils.hexToBytes(this.signatureHex); 74 | 75 | Signer signer = new Ed25519Signer(); 76 | signer.init(false, publicKey); 77 | signer.update(messageBytes, 0, messageBytes.length); 78 | return signer.verifySignature(signatureBytes); 79 | } catch (Exception e) { 80 | return false; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /scripts/ed25519-java/src/main/java/EncodingUtils.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | /** Encoding utils for cryptographic material. **/ 7 | public class EncodingUtils { 8 | 9 | /** Get X509 format of a compressed Edwards point (public key). **/ 10 | public static byte[] compressedEd25519PublicKeyToX509(byte[] compressedPublicKey) { 11 | int totlen = 12 + compressedPublicKey.length; 12 | byte[] rv = new byte[totlen]; 13 | int idx = 0; 14 | // sequence 15 | rv[idx++] = 0x30; 16 | rv[idx++] = (byte) (totlen - 2); 17 | // Algorithm Identifier 18 | // sequence 19 | rv[idx++] = 0x30; 20 | rv[idx++] = 5; 21 | // OID 22 | // https://msdn.microsoft.com/en-us/library/windows/desktop/bb540809%28v=vs.85%29.aspx 23 | rv[idx++] = 0x06; 24 | rv[idx++] = 3; 25 | rv[idx++] = 43; 26 | rv[idx++] = 101; 27 | rv[idx++] = (byte) 112; 28 | // params - absent 29 | // the key 30 | rv[idx++] = 0x03; // bit string 31 | rv[idx++] = (byte) (1 + compressedPublicKey.length); 32 | rv[idx++] = 0; // number of trailing unused bits 33 | System.arraycopy(compressedPublicKey, 0, rv, idx, compressedPublicKey.length); 34 | return rv; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /scripts/ed25519-java/src/main/java/TestVectorChecker.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | import com.google.gson.Gson; 7 | import com.google.gson.stream.JsonReader; 8 | 9 | import java.io.FileNotFoundException; 10 | import java.io.FileReader; 11 | 12 | public class TestVectorChecker { 13 | 14 | public static void main(String[] args) throws FileNotFoundException { 15 | String jsonFilename = "../../cases.json"; 16 | JsonReader reader = new JsonReader(new FileReader(jsonFilename)); 17 | Ed25519TestCase[] testCases = new Gson().fromJson(reader, Ed25519TestCase[].class); 18 | 19 | // For i2p ed25519-java 20 | System.out.print("|ed25519-java |"); 21 | for (Ed25519TestCase testCase : testCases) { 22 | if (testCase.verify_i2p()) { 23 | System.out.print(" V |"); 24 | } else { 25 | System.out.print(" X |"); 26 | } 27 | } 28 | System.out.println(""); 29 | 30 | // For BC ed25519 31 | System.out.print("|BouncyCastle |"); 32 | for (Ed25519TestCase testCase : testCases) { 33 | if (testCase.verify_bc()) { 34 | System.out.print(" V |"); 35 | } else { 36 | System.out.print(" X |"); 37 | } 38 | } 39 | System.out.println(""); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /scripts/ed25519-java/target/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | ed25519-rfc-compat 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | net.i2p.crypto 14 | eddsa 15 | 0.3.0 16 | 17 | 18 | org.bouncycastle 19 | bcprov-jdk15on 20 | 1.66 21 | 22 | 23 | com.google.code.gson 24 | gson 25 | 2.8.6 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /scripts/ed25519-signal-donna/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | IF(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") 4 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-variable -Wno-unused-function -Wno-shadow") 5 | ENDIF(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") 6 | 7 | IF(CMAKE_COMPILER_IS_GNUCC) 8 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-sign-compare") 9 | IF(GCC_WARN_SIGN_CONVERSION) 10 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-sign-conversion") 11 | ENDIF(GCC_WARN_SIGN_CONVERSION) 12 | ENDIF(CMAKE_COMPILER_IS_GNUCC) 13 | 14 | IF(CMAKE_C_COMPILER_ID MATCHES "Clang") 15 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-shorten-64-to-32") 16 | ENDIF(CMAKE_C_COMPILER_ID MATCHES "Clang") 17 | 18 | project(test-donna) 19 | 20 | add_subdirectory(libsignal-protocol-c/src/curve25519) 21 | 22 | add_executable(test-donna main.c) 23 | 24 | target_link_libraries(test-donna curve25519) 25 | 26 | install(TARGETS test-donna DESTINATION bin) 27 | -------------------------------------------------------------------------------- /scripts/ed25519-signal-donna/README.md: -------------------------------------------------------------------------------- 1 | This is the source from https://github.com/signalapp/libsignal-protocol-c/tree/master/src/curve25519 as of commit 3a83a4f. 2 | 3 | The test vectors must be in the same directory as your shell when you execute it and in a file named test_vector.txt 4 | 5 | To run it: 6 | ``` 7 | git clone https://github.com/signalapp/libsignal-protocol-c.git 8 | mkdir build 9 | cd build 10 | cmake .. 11 | make 12 | cp ../test_vector.txt . 13 | ./test-donna 14 | ``` 15 | -------------------------------------------------------------------------------- /scripts/ed25519-signal-donna/build+run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) Facebook, Inc. and its affiliates. 4 | # 5 | # This source code is licensed under the APACHE 2.0 license found in 6 | # the LICENSE file in the root directory of this source tree. 7 | # 8 | git clone https://github.com/signalapp/libsignal-protocol-c.git 9 | mkdir build 10 | cd build 11 | cmake .. 12 | make 13 | cp ../test_vector.txt . 14 | ./test-donna 15 | cd .. 16 | -------------------------------------------------------------------------------- /scripts/ed25519-signal-donna/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int crypto_sign_open_modified( 12 | unsigned char *m, 13 | const unsigned char *sm,unsigned long long smlen, 14 | const unsigned char *pk 15 | ); 16 | 17 | #include "libsignal-protocol-c/src/curve25519/curve25519-donna.h" 18 | #include "libsignal-protocol-c/src/curve25519/ed25519/tests/internal_fast_tests.h" 19 | 20 | void hex_string_to_byte_array(char *buff, int buf_len, unsigned char *res) { 21 | char *pos = buff; 22 | 23 | for (int i = 0; i < buf_len; i++) { 24 | sscanf(pos, "%2hhx", &res[i]); 25 | pos += 2; 26 | } 27 | } 28 | 29 | void pprint(unsigned char buf[32]) { 30 | for (int i = 0; i < 32; i++) 31 | { 32 | printf("%02X", buf[i]); 33 | } 34 | printf("\n"); 35 | } 36 | 37 | int curvesigs_cofac(int silent) 38 | { 39 | int num_test_vectors = 0; 40 | 41 | unsigned char pubkey[32]; 42 | unsigned char signature[64]; 43 | unsigned char msg[32]; 44 | unsigned char verifybuf[32+64]; 45 | unsigned char verifybuf2[32+64]; 46 | 47 | 48 | FILE *fp; 49 | char buff[255]; 50 | fp = fopen("../../../cases.txt", "r"); 51 | fscanf(fp, "%i", &num_test_vectors); 52 | printf("\n|ed25519-donna |"); 53 | 54 | for (int i = 0; i < num_test_vectors; i++) { 55 | memset(pubkey, 0, 32); 56 | memset(signature, 0, 64); 57 | memset(msg, 0, 32); 58 | memset(verifybuf, 0, 32+64); 59 | memset(verifybuf2, 0, 32+64); 60 | 61 | fscanf(fp, "%s", buff); 62 | hex_string_to_byte_array(buff + 4, 32, msg); 63 | fscanf(fp, "%s", buff); 64 | hex_string_to_byte_array(buff + 4, 32, pubkey); 65 | fscanf(fp, "%s", buff); 66 | hex_string_to_byte_array(buff + 4, 64, signature); 67 | // printf("msg:") 68 | // pprint(msg); 69 | // printf("Verification:"); 70 | 71 | /* Then perform a normal Ed25519 verification, return 0 on success */ 72 | /* The below call has a strange API: */ 73 | /* verifybuf = R || S || message */ 74 | /* verifybuf2 = internal to next call gets a copy of verifybuf, S gets 75 | replaced with pubkey for hashing */ 76 | memmove(verifybuf, signature, 64); 77 | memmove(verifybuf+64, msg, 32); 78 | if (crypto_sign_open_modified(verifybuf2, verifybuf, 64 + 32, pubkey) == 0) { 79 | printf(" V |"); 80 | } else { 81 | printf(" X |"); 82 | } 83 | } 84 | printf("\n"); 85 | return 0; 86 | } 87 | int main(void) { 88 | curvesigs_cofac(0); 89 | } 90 | -------------------------------------------------------------------------------- /scripts/go/README.md: -------------------------------------------------------------------------------- 1 | Install Go: https://golang.org/doc/install 2 | 3 | > go version 4 | go version go1.11.5 darwin/amd64 5 | 6 | To run 7 | > go run main.go -------------------------------------------------------------------------------- /scripts/go/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | package main 7 | 8 | import ( 9 | "encoding/hex" 10 | "encoding/json" 11 | "fmt" 12 | "golang.org/x/crypto/ed25519" 13 | "io/ioutil" 14 | "log" 15 | ) 16 | 17 | type Case struct { 18 | Message string 19 | Pub_Key string 20 | Signature string 21 | } 22 | 23 | func main() { 24 | content, err := ioutil.ReadFile("cases.json") 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | caseString := string(content) 29 | 30 | var cases []Case 31 | 32 | json.Unmarshal([]byte(caseString), &cases) 33 | //fmt.Printf("Cases : %+v", cases) 34 | 35 | fmt.Printf("\n|Go |") 36 | for i, c := range cases { 37 | pk_bytes, _ := hex.DecodeString(c.Pub_Key) 38 | m_bytes, _ := hex.DecodeString(c.Message) 39 | sig_bytes, _ := hex.DecodeString(c.Signature) 40 | 41 | pk := ed25519.PublicKey(pk_bytes) 42 | 43 | ver := ed25519.Verify(pk, m_bytes, sig_bytes) 44 | 45 | if ver { 46 | fmt.Printf(" V |") 47 | } else { 48 | fmt.Printf(" X |") 49 | } 50 | _ = i 51 | } 52 | fmt.Printf("\n") 53 | 54 | } 55 | -------------------------------------------------------------------------------- /scripts/libsodium/Makefile: -------------------------------------------------------------------------------- 1 | LIBSODIUM_INSTALL_DIR=./libsodium-stable-build 2 | main: main.c 3 | gcc -o main main.c -I$(LIBSODIUM_INSTALL_DIR)/include -L$(LIBSODIUM_INSTALL_DIR)/lib -lsodium 4 | -------------------------------------------------------------------------------- /scripts/libsodium/README.md: -------------------------------------------------------------------------------- 1 | > wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.18-stable.tar.gz 2 | > tar -xf libsodium-1.0.18-stable.tar.gz 3 | > rm libsodium-1.0.18-stable.tar.gz 4 | > mkdir libsodium-stable-build 5 | > export LIBSODIUM_INSTALL_DIR=$(pwd)/libsodium-stable-build 6 | > cd libsodium-stable/ 7 | > ./configure --prefix=$(LIBSODIUM_INSTALL_DIR) 8 | > make & make check 9 | > make install 10 | -------------------------------------------------------------------------------- /scripts/libsodium/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | #include 8 | 9 | #define MESSAGE_LEN 32 10 | 11 | int main(void) { 12 | if (sodium_init() < 0) { 13 | /* panic! the library couldn't be initialized, it is not safe to use */ 14 | printf("PANIC \n"); 15 | return 0; 16 | } 17 | 18 | unsigned char pk[crypto_sign_PUBLICKEYBYTES]; 19 | unsigned char message[MESSAGE_LEN]; 20 | unsigned long long message_len = MESSAGE_LEN; 21 | unsigned char signed_message[crypto_sign_BYTES + MESSAGE_LEN]; 22 | unsigned long long signed_message_len = crypto_sign_BYTES + MESSAGE_LEN; 23 | 24 | FILE *fp; 25 | int number_of_test_vectors = 0; 26 | char buff[255]; 27 | int pos; 28 | 29 | fp = fopen("../../cases.txt", "r+"); 30 | fscanf(fp, "%i", &number_of_test_vectors); 31 | // printf("Number of test vectors: %i\n", number_of_test_vectors); 32 | printf("\n|LibSodium |"); 33 | for (int i = 0; i < number_of_test_vectors; i++) { 34 | // reading the message 35 | fscanf(fp, "%s", buff); 36 | pos = 0; 37 | for (size_t count = 0; count < 32; count++) { 38 | sscanf(buff + 4 + pos, "%2hhx", &message[count]); 39 | sscanf(buff + 4 + pos, "%2hhx", &signed_message[count+64]); 40 | pos += 2; 41 | } 42 | 43 | // reading the public key 44 | fscanf(fp, "%s", buff); // message 32 bytes 45 | pos = 0; 46 | for (size_t count = 0; count < 32; count++) { 47 | sscanf(buff + 4 + pos, "%2hhx", &pk[count]); 48 | pos += 2; 49 | } 50 | 51 | // reading the signature 52 | fscanf(fp, "%s", buff); 53 | pos = 0; 54 | for (size_t count = 0; count < 64; count++) { 55 | sscanf(buff + 4 + pos, "%2hhx", &signed_message[count]); 56 | pos += 2; 57 | } 58 | 59 | int result = crypto_sign_open(message, &message_len, 60 | signed_message, signed_message_len, pk); 61 | if (result == -1) { 62 | printf(" X |"); 63 | /* Incorrect signature! */ 64 | } else { 65 | printf(" V |"); 66 | } 67 | } 68 | printf("\n"); 69 | fclose(fp); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /scripts/npm/README.md: -------------------------------------------------------------------------------- 1 | To install npm and node: https://www.npmjs.com/get-npm 2 | 3 | > npm install load-json-file 4 | > npm --version 5 | 6.14.6 6 | > node --version 7 | v12.18.3p 8 | 9 | To reproduce: 10 | > node eddsa_test.js 11 | -------------------------------------------------------------------------------- /scripts/npm/eddsa_test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | const crypto = require("crypto"); 7 | 8 | const jsonfile = require('jsonfile') 9 | const file = '../../cases.json' 10 | jsonfile.readFile(file, function (err, test_vector) { 11 | if (err) console.error(err) 12 | // console.log(test_vector); 13 | let output = "\n|npm |"; 14 | for (let i = 0; i < test_vector.length; i++) { 15 | let pub_key = toPem(test_vector[i].pub_key); 16 | let message = Buffer.from(test_vector[i].message, "hex"); 17 | let signature = Buffer.from(test_vector[i].signature, "hex"); 18 | // console.log(i + ": " + crypto.verify(null, message, pub_key, signature)); 19 | if (crypto.verify(null, message, pub_key, signature)) { 20 | output += " V |"; 21 | } else { 22 | output += " X |"; 23 | } 24 | } 25 | console.log(output + "\n"); 26 | 27 | function toPem(hexKey) { 28 | let buf1 = Buffer.from("302a300506032b6570032100", "hex"); 29 | let buf2 = Buffer.from(hexKey, "hex"); 30 | let keyBuf = Buffer.from( 31 | "-----BEGIN PUBLIC KEY-----\n" + 32 | Buffer.concat([buf1, buf2]).toString("base64") + 33 | "\n-----END PUBLIC KEY-----" 34 | ); 35 | 36 | return crypto.createPublicKey(keyBuf); 37 | } 38 | }); 39 | -------------------------------------------------------------------------------- /scripts/npm/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "@babel/code-frame": { 6 | "version": "7.10.4", 7 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", 8 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", 9 | "requires": { 10 | "@babel/highlight": "^7.10.4" 11 | } 12 | }, 13 | "@babel/helper-validator-identifier": { 14 | "version": "7.10.4", 15 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", 16 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" 17 | }, 18 | "@babel/highlight": { 19 | "version": "7.10.4", 20 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 21 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 22 | "requires": { 23 | "@babel/helper-validator-identifier": "^7.10.4", 24 | "chalk": "^2.0.0", 25 | "js-tokens": "^4.0.0" 26 | } 27 | }, 28 | "ansi-styles": { 29 | "version": "3.2.1", 30 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 31 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 32 | "requires": { 33 | "color-convert": "^1.9.0" 34 | } 35 | }, 36 | "chalk": { 37 | "version": "2.4.2", 38 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 39 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 40 | "requires": { 41 | "ansi-styles": "^3.2.1", 42 | "escape-string-regexp": "^1.0.5", 43 | "supports-color": "^5.3.0" 44 | } 45 | }, 46 | "color-convert": { 47 | "version": "1.9.3", 48 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 49 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 50 | "requires": { 51 | "color-name": "1.1.3" 52 | } 53 | }, 54 | "color-name": { 55 | "version": "1.1.3", 56 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 57 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 58 | }, 59 | "crypto": { 60 | "version": "1.0.1", 61 | "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", 62 | "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" 63 | }, 64 | "error-ex": { 65 | "version": "1.3.2", 66 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 67 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 68 | "requires": { 69 | "is-arrayish": "^0.2.1" 70 | } 71 | }, 72 | "escape-string-regexp": { 73 | "version": "1.0.5", 74 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 75 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 76 | }, 77 | "graceful-fs": { 78 | "version": "4.2.4", 79 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 80 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" 81 | }, 82 | "has-flag": { 83 | "version": "3.0.0", 84 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 85 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 86 | }, 87 | "is-arrayish": { 88 | "version": "0.2.1", 89 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 90 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" 91 | }, 92 | "js-tokens": { 93 | "version": "4.0.0", 94 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 95 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 96 | }, 97 | "json-parse-even-better-errors": { 98 | "version": "2.3.1", 99 | "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", 100 | "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" 101 | }, 102 | "jsonfile": { 103 | "version": "6.0.1", 104 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", 105 | "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", 106 | "requires": { 107 | "graceful-fs": "^4.1.6", 108 | "universalify": "^1.0.0" 109 | } 110 | }, 111 | "lines-and-columns": { 112 | "version": "1.1.6", 113 | "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", 114 | "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" 115 | }, 116 | "load-json-file": { 117 | "version": "6.2.0", 118 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz", 119 | "integrity": "sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==", 120 | "requires": { 121 | "graceful-fs": "^4.1.15", 122 | "parse-json": "^5.0.0", 123 | "strip-bom": "^4.0.0", 124 | "type-fest": "^0.6.0" 125 | } 126 | }, 127 | "parse-json": { 128 | "version": "5.1.0", 129 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", 130 | "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", 131 | "requires": { 132 | "@babel/code-frame": "^7.0.0", 133 | "error-ex": "^1.3.1", 134 | "json-parse-even-better-errors": "^2.3.0", 135 | "lines-and-columns": "^1.1.6" 136 | } 137 | }, 138 | "strip-bom": { 139 | "version": "4.0.0", 140 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", 141 | "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" 142 | }, 143 | "supports-color": { 144 | "version": "5.5.0", 145 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 146 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 147 | "requires": { 148 | "has-flag": "^3.0.0" 149 | } 150 | }, 151 | "type-fest": { 152 | "version": "0.6.0", 153 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", 154 | "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" 155 | }, 156 | "universalify": { 157 | "version": "1.0.0", 158 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", 159 | "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /scripts/npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "eddsa_test": "node eddsa_test.js" 4 | }, 5 | "dependencies": { 6 | "crypto": "^1.0.1", 7 | "jsonfile": "^6.0.1", 8 | "load-json-file": "^6.2.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scripts/openssl_3/README.md: -------------------------------------------------------------------------------- 1 | Tests against the latest version of OpenSSL from https://github.com/openssl/openssl (version: OpenSSL 3.0.0-alpha6-dev). 2 | 3 | To reproduce: 4 | 5 | clone the OpenSSL repo 6 | run ./Configure, then make 7 | fix the paths in test_script.sh to point to the local OpenSSL build 8 | run ./test_script.sh 9 | 10 | > git clone -------------------------------------------------------------------------------- /scripts/openssl_3/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | void hex_string_to_byte_array(char *buff, int buf_len, unsigned char *res) { 21 | char *pos = buff; 22 | 23 | for (int i = 0; i < buf_len; i++) { 24 | sscanf(pos, "%2hhx", &res[i]); 25 | pos += 2; 26 | } 27 | } 28 | 29 | int main(int argc, char **argv) { 30 | int num_test_vectors = 0; 31 | 32 | FILE *fp; 33 | char buff[255]; 34 | unsigned char msg[32]; 35 | unsigned char pk[32]; 36 | unsigned char sig[64]; 37 | 38 | fp = fopen("../../cases.txt", "r"); 39 | fscanf(fp, "%i", &num_test_vectors); 40 | printf("|OpenSSL-3.0 |"); 41 | for (int i = 0; i < num_test_vectors; i++) { 42 | fscanf(fp, "%s", buff); 43 | hex_string_to_byte_array(buff + 4, 32, msg); 44 | fscanf(fp, "%s", buff); 45 | hex_string_to_byte_array(buff + 4, 32, pk); 46 | fscanf(fp, "%s", buff); 47 | hex_string_to_byte_array(buff + 4, 64, sig); 48 | 49 | EVP_MD_CTX *ctx = EVP_MD_CTX_new(); 50 | EVP_PKEY *ed_pkey = EVP_PKEY_new_raw_public_key(NID_ED25519, NULL, pk, 32); 51 | EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, ed_pkey); 52 | int result = EVP_DigestVerify(ctx, sig, 64, msg, 32); 53 | printf(result ? " V |" : " X |"); 54 | } 55 | printf("\n"); 56 | fclose(fp); 57 | return 1; 58 | } 59 | -------------------------------------------------------------------------------- /scripts/openssl_3/test_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Facebook, Inc. and its affiliates. 4 | # 5 | # This source code is licensed under the APACHE 2.0 license found in 6 | # the LICENSE file in the root directory of this source tree. 7 | 8 | export OPENSSL_PATH=./openssl-build 9 | 10 | gcc -o main main.c -L"$OPENSSL_PATH"/lib -lssl -lcrypto -I"$OPENSSL_PATH"/include 11 | ./main 12 | -------------------------------------------------------------------------------- /scripts/openssl_3/test_vector.txt: -------------------------------------------------------------------------------- 1 | 12 2 | msg=2a66241a42a9ee12994d8068dcf1bb7dfc6637b45450acd43711f637fa5080fc 3 | pbk=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa 4 | sig=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000 5 | msg=8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6 6 | pbk=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa 7 | sig=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000 8 | msg=9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41 9 | pbk=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa 10 | sig=f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04 11 | msg=9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79 12 | pbk=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa 13 | sig=f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04 14 | msg=9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41 15 | pbk=f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43 16 | sig=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa182ef1a5b928da07fec769cc8a12db6bcf70dab3f3227fa315e9d5e3e01a3405 17 | msg=aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab 18 | pbk=f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43 19 | sig=c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e 20 | msg=e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c 21 | pbk=cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d 22 | sig=160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09 23 | msg=9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79 24 | pbk=cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d 25 | sig=9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009 26 | msg=e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c 27 | pbk=cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d 28 | sig=21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405 29 | msg=85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40 30 | pbk=442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623 31 | sig=e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514 32 | msg=fdaebc429f4a735932a160da1301080c13280eea8bc280d1b392c6b9e6ba3a5a 33 | pbk=f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43 34 | sig=edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f084d5b99c2a9463d9c8bd5026916996984eeec87ddf1d3be329006ace1b37b09 35 | msg=84b698d39be126ff55fe45079e6c8bf64a0d7db6994560b4e96b7021eb39c1a1 36 | pbk=f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43 37 | sig=edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f084d5b99c2a9463d9c8bd5026916996984eeec87ddf1d3be329006ace1b37b09 38 | -------------------------------------------------------------------------------- /scripts/pyca-openssl/README.md: -------------------------------------------------------------------------------- 1 | > python3.7 eddsa_util.py 2 | backend version text: OpenSSL 1.1.1g 21 Apr 2020 3 | backend version num: 269488255 -------------------------------------------------------------------------------- /scripts/pyca-openssl/eddsa_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. 2 | # 3 | # This source code is licensed under the APACHE 2.0 license found in 4 | # the LICENSE file in the root directory of this source tree. 5 | import json 6 | 7 | from cryptography.hazmat.backends import default_backend 8 | from cryptography.hazmat.primitives.asymmetric import ed25519 9 | 10 | if __name__ == "__main__": 11 | with open('cases.json') as f: 12 | data = json.load(f) 13 | # print('backend version text: ' + default_backend().openssl_version_text()) 14 | # print('backend version num: {}'.format(default_backend().openssl_version_number())) 15 | output = '\n|PyCA |' 16 | for i, test_case in enumerate(data): 17 | try: 18 | pub_key = ed25519.Ed25519PublicKey.from_public_bytes( 19 | bytes.fromhex(test_case['pub_key'])) 20 | msg = bytes.fromhex(test_case['message']) 21 | sig = bytes.fromhex(test_case['signature']) 22 | pub_key.verify(sig, msg) 23 | output = output + ' V |' 24 | except: 25 | output = output + ' X |' 26 | output = output + '\n' 27 | print(output + '\n') 28 | -------------------------------------------------------------------------------- /scripts/python-ed25519/README.md: -------------------------------------------------------------------------------- 1 | This code tests the python-ed25519 (https://github.com/warner/python-ed25519) that binds to 2 | the C code of the SUPERCOP benchmark suite (http://bench.cr.yp.to/supercop.html). 3 | To run this test: 4 | > git clone git@github.com:warner/python-ed25519.git 5 | Add the test below to src/ed25519/test_ed25519.py 6 | > python setup.py build 7 | > python setup.py test 8 | -------------------------------------------------------------------------------- /scripts/python-ed25519/add_test_git.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/ed25519/test_ed25519.py b/src/ed25519/test_ed25519.py 2 | index 8dea618..3c57c10 100644 3 | --- a/src/ed25519/test_ed25519.py 4 | +++ b/src/ed25519/test_ed25519.py 5 | @@ -5,6 +5,7 @@ import time 6 | from binascii import hexlify, unhexlify 7 | import ed25519 8 | from ed25519 import _ed25519 as raw 9 | +import json 10 | 11 | if sys.version_info[0] == 3: 12 | def int2byte(i): 13 | @@ -36,239 +37,20 @@ class Basic(unittest.TestCase): 14 | print(" (%f elapsed)" % elapsed) 15 | print(msg) 16 | 17 | - def test_version(self): 18 | - # just make sure it can be retrieved 19 | - ver = ed25519.__version__ 20 | - self.failUnless(isinstance(ver, type(""))) 21 | - 22 | - def test_constants(self): 23 | - # the secret key we get from raw.keypair() are 64 bytes long, and 24 | - # are mostly the output of a sha512 call. The first 32 bytes are the 25 | - # private exponent (random, with a few bits stomped). 26 | - self.failUnlessEqual(raw.SECRETKEYBYTES, 64) 27 | - # the public key is the encoded public point 28 | - self.failUnlessEqual(raw.PUBLICKEYBYTES, 32) 29 | - self.failUnlessEqual(raw.SIGNATUREKEYBYTES, 64) 30 | - 31 | - def test_raw(self): 32 | - sk_s = b"\x00" * 32 # usually urandom(32) 33 | - vk_s, skvk_s = raw.publickey(sk_s) 34 | - self.failUnlessEqual(len(vk_s), 32) 35 | - exp_vks = unhexlify(b"3b6a27bcceb6a42d62a3a8d02a6f0d73" 36 | - b"653215771de243a63ac048a18b59da29") 37 | - self.failUnlessEqual(vk_s, exp_vks) 38 | - self.failUnlessEqual(skvk_s[:32], sk_s) 39 | - self.failUnlessEqual(skvk_s[32:], vk_s) 40 | - msg = b"hello world" 41 | - msg_and_sig = raw.sign(msg, skvk_s) 42 | - sig = msg_and_sig[:-len(msg)] 43 | - self.failUnlessEqual(len(sig), 64) 44 | - exp_sig = unhexlify(b"b0b47780f096ae60bfff8d8e7b19c36b" 45 | - b"321ae6e69cca972f2ff987ef30f20d29" 46 | - b"774b53bae404485c4391ddf1b3f37aaa" 47 | - b"8a9747f984eb0884e8aa533386e73305") 48 | - self.failUnlessEqual(sig, exp_sig) 49 | - ret = raw.open(sig+msg, vk_s) # don't raise exception 50 | - self.failUnlessEqual(ret, msg) 51 | - self.failUnlessRaises(raw.BadSignatureError, 52 | - raw.open, 53 | - sig+msg+b".. NOT!", vk_s) 54 | - self.failUnlessRaises(raw.BadSignatureError, 55 | - raw.open, 56 | - sig+flip_bit(msg), vk_s) 57 | - self.failUnlessRaises(raw.BadSignatureError, 58 | - raw.open, 59 | - sig+msg, flip_bit(vk_s)) 60 | - self.failUnlessRaises(raw.BadSignatureError, 61 | - raw.open, 62 | - sig+msg, flip_bit(vk_s, in_byte=2)) 63 | - self.failUnlessRaises(raw.BadSignatureError, 64 | - raw.open, 65 | - flip_bit(sig)+msg, vk_s) 66 | - self.failUnlessRaises(raw.BadSignatureError, 67 | - raw.open, 68 | - flip_bit(sig, in_byte=33)+msg, vk_s) 69 | - 70 | - def test_keypair(self): 71 | - sk, vk = ed25519.create_keypair() 72 | - self.failUnless(isinstance(sk, ed25519.SigningKey), sk) 73 | - self.failUnless(isinstance(vk, ed25519.VerifyingKey), vk) 74 | - sk2, vk2 = ed25519.create_keypair() 75 | - self.failIfEqual(hexlify(sk.to_bytes()), hexlify(sk2.to_bytes())) 76 | - 77 | - # you can control the entropy source 78 | - def not_so_random(length): 79 | - return b"4"*length 80 | - sk1, vk1 = ed25519.create_keypair(entropy=not_so_random) 81 | - self.failUnlessEqual(sk1.to_ascii(encoding="base64"), 82 | - b"NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ") 83 | - self.failUnlessEqual(vk1.to_ascii(encoding="base64"), 84 | - b"6yzxO/euOl9hQWih+wknLTl3HsS4UjcngV5GbK+O4WM") 85 | - sk2, vk2 = ed25519.create_keypair(entropy=not_so_random) 86 | - self.failUnlessEqual(sk1.to_ascii(encoding="base64"), 87 | - sk2.to_ascii(encoding="base64")) 88 | - self.failUnlessEqual(vk1.to_ascii(encoding="base64"), 89 | - vk2.to_ascii(encoding="base64")) 90 | - 91 | - 92 | - def test_publickey(self): 93 | - seed = unhexlify(b"4ba96b0b5303328c7405220598a587c4" 94 | - b"acb06ed9a9601d149f85400195f1ec3d") 95 | - sk = ed25519.SigningKey(seed) 96 | - self.failUnlessEqual(hexlify(sk.to_bytes()), 97 | - (b"4ba96b0b5303328c7405220598a587c4" 98 | - b"acb06ed9a9601d149f85400195f1ec3d" 99 | - b"a66d161e090652b054740748f059f92a" 100 | - b"5b731f1c27b05571f6d942e4f8b7b264")) 101 | - self.failUnlessEqual(hexlify(sk.to_seed()), 102 | - (b"4ba96b0b5303328c7405220598a587c4" 103 | - b"acb06ed9a9601d149f85400195f1ec3d")) 104 | - self.failUnlessRaises(ValueError, 105 | - ed25519.SigningKey, b"wrong length") 106 | - sk2 = ed25519.SigningKey(seed) 107 | - self.failUnlessEqual(sk, sk2) 108 | - 109 | - def test_OOP(self): 110 | - sk_s = unhexlify(b"4ba96b0b5303328c7405220598a587c4" 111 | - b"acb06ed9a9601d149f85400195f1ec3d" 112 | - b"a66d161e090652b054740748f059f92a" 113 | - b"5b731f1c27b05571f6d942e4f8b7b264") 114 | - sk = ed25519.SigningKey(sk_s) 115 | - self.failUnlessEqual(len(sk.to_bytes()), 64) 116 | - self.failUnlessEqual(sk.to_bytes(), sk_s) 117 | - 118 | - sk2_seed = unhexlify(b"4ba96b0b5303328c7405220598a587c4" 119 | - b"acb06ed9a9601d149f85400195f1ec3d") 120 | - sk2 = ed25519.SigningKey(sk2_seed) 121 | - self.failUnlessEqual(sk2.to_bytes(), sk.to_bytes()) 122 | - 123 | - vk = sk.get_verifying_key() 124 | - self.failUnlessEqual(len(vk.to_bytes()), 32) 125 | - exp_vks = unhexlify(b"a66d161e090652b054740748f059f92a" 126 | - b"5b731f1c27b05571f6d942e4f8b7b264") 127 | - self.failUnlessEqual(vk.to_bytes(), exp_vks) 128 | - self.failUnlessEqual(ed25519.VerifyingKey(vk.to_bytes()), vk) 129 | - msg = b"hello world" 130 | - sig = sk.sign(msg) 131 | - self.failUnlessEqual(len(sig), 64) 132 | - exp_sig = unhexlify(b"6eaffe94f2972b35158b6aaa9b69c1da" 133 | - b"97f0896aca29c41b1dd7b32e6c9e2ff6" 134 | - b"76fc8d8b034709cdcc37d8aeb86bebfb" 135 | - b"173ace3c319e211ea1d7e8d8884c1808") 136 | - self.failUnlessEqual(sig, exp_sig) 137 | - self.failUnlessEqual(vk.verify(sig, msg), None) # also, don't throw 138 | - self.failUnlessRaises(ed25519.BadSignatureError, 139 | - vk.verify, sig, msg+b".. NOT!") 140 | - 141 | - def test_object_identity(self): 142 | - sk1_s = unhexlify(b"ef32972ae3f1252a5aa1395347ea008c" 143 | - b"bd2fed0773a4ea45e2d2d06c8cf8fbd4" 144 | - b"c024601a9c5b854fb100ff3116cf4f22" 145 | - b"a311565f027391cb49d3bbe11c44399d") 146 | - sk2_s = unhexlify(b"3d550c158900b4c2922b6656d2f80572" 147 | - b"89de4ee65043745179685ae7d29b944d" 148 | - b"672b8a2cb23f9e75e1d46ce249cd9c04" 149 | - b"68f816f1c734a102822b60e18b41eacd") 150 | - sk1a = ed25519.SigningKey(sk1_s) 151 | - sk1b = ed25519.SigningKey(sk1_s) 152 | - vk1a = sk1a.get_verifying_key() 153 | - vk1b = sk1b.get_verifying_key() 154 | - sk2 = ed25519.SigningKey(sk2_s) 155 | - vk2 = sk2.get_verifying_key() 156 | - self.failUnlessEqual(sk1a, sk1b) 157 | - self.failIfEqual(sk1a, sk2) 158 | - self.failUnlessEqual(vk1a, vk1b) 159 | - self.failIfEqual(vk1a, vk2) 160 | - 161 | - self.failIfEqual(sk2, b"not a SigningKey") 162 | - self.failIfEqual(vk2, b"not a VerifyingKey") 163 | - 164 | - def test_prefix(self): 165 | - sk1,vk1 = ed25519.create_keypair() 166 | - PREFIX = b"private0-" 167 | - p = sk1.to_bytes(PREFIX) 168 | - # that gives us a binary string with a prefix 169 | - self.failUnless(p[:len(PREFIX)] == PREFIX, repr(p)) 170 | - sk2 = ed25519.SigningKey(p, prefix=PREFIX) 171 | - self.failUnlessEqual(sk1, sk2) 172 | - self.failUnlessEqual(repr(sk1.to_bytes()), repr(sk2.to_bytes())) 173 | - self.failUnlessRaises(ed25519.BadPrefixError, 174 | - ed25519.SigningKey, p, prefix=b"WRONG-") 175 | - # SigningKey.to_seed() can do a prefix too 176 | - p = sk1.to_seed(PREFIX) 177 | - self.failUnless(p[:len(PREFIX)] == PREFIX, repr(p)) 178 | - sk3 = ed25519.SigningKey(p, prefix=PREFIX) 179 | - self.failUnlessEqual(sk1, sk3) 180 | - self.failUnlessEqual(repr(sk1.to_bytes()), repr(sk3.to_bytes())) 181 | - self.failUnlessRaises(ed25519.BadPrefixError, 182 | - ed25519.SigningKey, p, prefix=b"WRONG-") 183 | - 184 | - # verifying keys can do this too 185 | - PREFIX = b"public0-" 186 | - p = vk1.to_bytes(PREFIX) 187 | - self.failUnless(p.startswith(PREFIX), repr(p)) 188 | - vk2 = ed25519.VerifyingKey(p, prefix=PREFIX) 189 | - self.failUnlessEqual(vk1, vk2) 190 | - self.failUnlessEqual(repr(vk1.to_bytes()), repr(vk2.to_bytes())) 191 | - self.failUnlessRaises(ed25519.BadPrefixError, 192 | - ed25519.VerifyingKey, p, prefix=b"WRONG-") 193 | - 194 | - # and signatures 195 | - PREFIX = b"sig0-" 196 | - p = sk1.sign(b"msg", PREFIX) 197 | - self.failUnless(p.startswith(PREFIX), repr(p)) 198 | - vk1.verify(p, b"msg", PREFIX) 199 | - self.failUnlessRaises(ed25519.BadPrefixError, 200 | - vk1.verify, p, b"msg", prefix=b"WRONG-") 201 | - 202 | - def test_ascii(self): 203 | - b2a = ed25519.to_ascii 204 | - a2b = ed25519.from_ascii 205 | - for prefix in ("", "prefix-"): 206 | - for length in range(0, 100): 207 | - b1 = b"a"*length 208 | - for base in ("base64", "base32", "base16", "hex"): 209 | - a = b2a(b1, prefix, base) 210 | - b2 = a2b(a, prefix, base) 211 | - self.failUnlessEqual(b1, b2) 212 | - 213 | - def test_encoding(self): 214 | - sk_s = b"\x88" * 32 # usually urandom(32) 215 | - sk1 = ed25519.SigningKey(sk_s) 216 | - vk1 = sk1.get_verifying_key() 217 | - 218 | - def check1(encoding, expected): 219 | - PREFIX = "private0-" 220 | - p = sk1.to_ascii(PREFIX, encoding) 221 | - self.failUnlessEqual(p, expected) 222 | - sk2 = ed25519.SigningKey(p, prefix=PREFIX, encoding=encoding) 223 | - self.failUnlessEqual(repr(sk1.to_bytes()), repr(sk2.to_bytes())) 224 | - self.failUnlessEqual(sk1, sk2) 225 | - check1("base64", b"private0-iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIg") 226 | - check1("base32", b"private0-rceirceirceirceirceirceirceirceirceirceirceirceircea") 227 | - check1("hex", b"private0-8888888888888888888888888888888888888888888888888888888888888888") 228 | - 229 | - def check2(encoding, expected): 230 | - PREFIX="public0-" 231 | - p = vk1.to_ascii(PREFIX, encoding) 232 | - self.failUnlessEqual(p, expected) 233 | - vk2 = ed25519.VerifyingKey(p, prefix=PREFIX, encoding=encoding) 234 | - self.failUnlessEqual(repr(vk1.to_bytes()), repr(vk2.to_bytes())) 235 | - self.failUnlessEqual(vk1, vk2) 236 | - check2("base64", b"public0-skkdlQKuKGMKK6yy4MdFEP/N0yjDNP8+E5PnWy0x59w") 237 | - check2("base32", b"public0-wjer3ficvyuggcrlvszobr2fcd743uziym2p6pqtsptvwljr47oa") 238 | - check2("hex", b"public0-b2491d9502ae28630a2bacb2e0c74510ffcdd328c334ff3e1393e75b2d31e7dc") 239 | - 240 | - def check3(encoding, expected): 241 | - msg = b"msg" 242 | - PREFIX="sig0-" 243 | - sig = sk1.sign(msg, PREFIX, encoding) 244 | - self.failUnlessEqual(sig, expected) 245 | - vk1.verify(sig, msg, PREFIX, encoding) 246 | - check3("base64", b"sig0-MNfdUir6tMlaYQ+/p8KANJ5d+bk8g2al76v5MeJCo6RiywxURda3sU580CyiW2FBG/Q7kDRswgYqxbkQw3o5CQ") 247 | - check3("base32", b"sig0-gdl52urk7k2mswtbb672pquagspf36nzhsbwnjppvp4tdyscuosgfsymkrc5nn5rjz6nalfclnqucg7uhoidi3gcayvmloiqyn5dsci") 248 | - check3("hex", b"sig0-30d7dd522afab4c95a610fbfa7c280349e5df9b93c8366a5efabf931e242a3a462cb0c5445d6b7b14e7cd02ca25b61411bf43b90346cc2062ac5b910c37a3909") 249 | - 250 | + def test_ours(self): 251 | + with open('../../../cases.json') as f: 252 | + data = json.load(f) 253 | + output = '\n|python-ed25519 |' 254 | + for i, test_case in enumerate(data): 255 | + try: 256 | + pub_key = ed25519.VerifyingKey(bytes.fromhex(test_case['pub_key'])) 257 | + msg = bytes.fromhex(test_case['message']) 258 | + sig = bytes.fromhex(test_case['signature']) 259 | + pub_key.verify(sig, msg) 260 | + output = output + ' V |' 261 | + except: 262 | + output = output + ' X |' 263 | + print(output) 264 | 265 | if __name__ == '__main__': 266 | unittest.main() 267 | -------------------------------------------------------------------------------- /scripts/ref10/Makefile: -------------------------------------------------------------------------------- 1 | LIBSODIUM_INSTALL_DIR=./libsodium-stable-build 2 | main: main.c 3 | gcc -o main main.c -I$(LIBSODIUM_INSTALL_DIR)/include -L$(LIBSODIUM_INSTALL_DIR)/lib -lsodium 4 | -------------------------------------------------------------------------------- /scripts/ref10/README.md: -------------------------------------------------------------------------------- 1 | To reproduce the results download and install libsodium (see [https://doc.libsodium.org/installation](https://doc.libsodium.org/installation)): 2 | * Download the latest stable tarball, untar to `libsodium_untared` 3 | * Change the source code of `libsodium_untared` to use the old version, ref10 from SUPERCORP: 4 | ** Open file src/libsodium/crypto_sign/ed25519/ref10/sign_ed25519_ref10.h 5 | ** Add #define ED25519_COMPAT 6 | ** This makes the libsodium code most close to original ref10 see the history of file (libsodium/src/libsodium/crypto_sign/ed25519/ref10/open.c)[https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_sign/ed25519/ref10/open.c#L26] 7 | * libsodium_untared> ./configure --prefix=$(LIBSODIUM_INSTALL_DIR) 8 | * libsodium_untared> make & make check 9 | * libsodium_untared> make install 10 | 11 | Once libsodium installs, go to the Makefile of this directory, fix the variable LIBSODIUM_INSTALL_DIR as needed to match the one passed into ./configure. 12 | 13 | Run make & ./main to get the results. 14 | 15 | The output is: 16 | 0: false 17 | 1: true 18 | 2: false 19 | 3: true 20 | 4: false 21 | 5: true 22 | 6: false 23 | 7: true 24 | 8: false 25 | 9: true 26 | 10: false 27 | 11: false 28 | 12: false 29 | 13: false 30 | 14: false 31 | 32 | (last reproduced in Aug 2020) -------------------------------------------------------------------------------- /scripts/ref10/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novifinancial/ed25519-speccheck/65519336fda78a3d016e947df6d82848aca0c9da/scripts/ref10/main -------------------------------------------------------------------------------- /scripts/ref10/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | #include 8 | 9 | #define MESSAGE_LEN 32 10 | 11 | int main(void) { 12 | if (sodium_init() < 0) { 13 | /* panic! the library couldn't be initialized, it is not safe to use */ 14 | printf("PANIC \n"); 15 | return 0; 16 | } 17 | 18 | unsigned char pk[crypto_sign_PUBLICKEYBYTES]; 19 | unsigned char message[MESSAGE_LEN]; 20 | unsigned long long message_len = MESSAGE_LEN; 21 | unsigned char signed_message[crypto_sign_BYTES + MESSAGE_LEN]; 22 | unsigned long long signed_message_len = crypto_sign_BYTES + MESSAGE_LEN; 23 | 24 | FILE *fp; 25 | int number_of_test_vectors = 0; 26 | char buff[255]; 27 | int pos; 28 | 29 | fp = fopen("../../cases.txt", "r+"); 30 | fscanf(fp, "%i", &number_of_test_vectors); 31 | // printf("Number of test vectors: %i\n", number_of_test_vectors); 32 | printf("\n|ref10 |"); 33 | for (int i = 0; i < number_of_test_vectors; i++) { 34 | // reading the message 35 | fscanf(fp, "%s", buff); 36 | pos = 0; 37 | for (size_t count = 0; count < 32; count++) { 38 | sscanf(buff + 4 + pos, "%2hhx", &message[count]); 39 | sscanf(buff + 4 + pos, "%2hhx", &signed_message[count+64]); 40 | pos += 2; 41 | } 42 | 43 | // reading the public key 44 | fscanf(fp, "%s", buff); // message 32 bytes 45 | pos = 0; 46 | for (size_t count = 0; count < 32; count++) { 47 | sscanf(buff + 4 + pos, "%2hhx", &pk[count]); 48 | pos += 2; 49 | } 50 | 51 | // reading the signature 52 | fscanf(fp, "%s", buff); 53 | pos = 0; 54 | for (size_t count = 0; count < 64; count++) { 55 | sscanf(buff + 4 + pos, "%2hhx", &signed_message[count]); 56 | pos += 2; 57 | } 58 | 59 | int result = crypto_sign_open(message, &message_len, 60 | signed_message, signed_message_len, pk); 61 | if (result == -1) { 62 | printf(" X |"); 63 | /* Incorrect signature! */ 64 | } else { 65 | printf(" V |"); 66 | } 67 | } 68 | printf("\n"); 69 | fclose(fp); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /scripts/ref10/to_ref10.patch: -------------------------------------------------------------------------------- 1 | --- libsodium-stable/src/libsodium/crypto_sign/ed25519/ref10/sign_ed25519_ref10.h 2020-08-16 02:41:54.000000000 -0700 2 | +++ libsodium-stable/src/libsodium/crypto_sign/ed25519/ref10/sign_ed25519_ref10_new.h 2020-09-09 14:45:22.000000000 -0700 3 | @@ -1,6 +1,8 @@ 4 | #ifndef sign_ed25519_ref10_H 5 | #define sign_ed25519_ref10_H 6 | 7 | +#define ED25519_COMPAT 8 | + 9 | void _crypto_sign_ed25519_ref10_hinit(crypto_hash_sha512_state *hs, 10 | int prehashed); 11 | 12 | -------------------------------------------------------------------------------- /scripts/tweetnacl-js/README.md: -------------------------------------------------------------------------------- 1 | Tweetnacl version 1.0.3 (https://www.npmjs.com/package/tweetnacl) 2 | 3 | To install npm and node: https://www.npmjs.com/get-npm 4 | 5 | > npm install load-json-file 6 | > npm --version 7 | 6.14.6 8 | > node --version 9 | v12.18.3p 10 | 11 | To reproduce the results: 12 | > node test.js 13 | -------------------------------------------------------------------------------- /scripts/tweetnacl-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "load-json-file": "^6.2.0", 4 | "tweetnacl": "^1.0.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /scripts/tweetnacl-js/test.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | // import nacl from 'tweetnacl'; 7 | // import nacl = require("tweetnacl") // cryptographic functions 8 | // import util = require("tweetnacl-util") // encoding & decoding 9 | nacl = require("tweetnacl"); 10 | 11 | const fs = require('fs'); 12 | 13 | let rawdata = fs.readFileSync('cases.json'); 14 | let tests = JSON.parse(rawdata); 15 | 16 | const fromHexString = hexString => 17 | new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); 18 | 19 | var i; 20 | let output = "\n|TweetNaCl-js |"; 21 | for (i = 0; i < tests.length; i++) { 22 | let res = nacl.sign.detached.verify(fromHexString(tests[i].message), 23 | fromHexString(tests[i].signature), 24 | fromHexString(tests[i].pub_key)); 25 | output += (res ? " V |" : " X |"); 26 | } 27 | console.log(output + "\n"); 28 | -------------------------------------------------------------------------------- /scripts/tweetnacl-js/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | tweetnacl@^1.0.3: 6 | version "1.0.3" 7 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" 8 | integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== 9 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | use anyhow::{anyhow, Result}; 7 | use core::ops::Neg; 8 | 9 | use curve25519_dalek::{ 10 | constants::ED25519_BASEPOINT_POINT, edwards::EdwardsPoint, scalar::Scalar, traits::IsIdentity, 11 | }; 12 | use rand::{rngs::StdRng, RngCore, SeedableRng}; 13 | use sha2::{Digest, Sha512}; 14 | 15 | use serde::ser::{Serialize, SerializeStruct, Serializer}; 16 | use std::fs::File; 17 | use std::io::prelude::*; 18 | 19 | #[macro_use] 20 | extern crate log; 21 | 22 | extern crate string_builder; 23 | use string_builder::Builder; 24 | 25 | // The 8-torsion subgroup E[8]. 26 | // 27 | // In the case of Curve25519, it is cyclic; the i-th element of 28 | // the array is [i]P, where P is a point of order 8 29 | // generating E[8]. 30 | // 31 | // Thus E[4] is the points indexed by `0,2,4,6`, and 32 | // E[2] is the points indexed by `0,4`. 33 | // 34 | // The following byte arrays have been ported from curve25519-dalek /backend/serial/u64/constants.rs 35 | // and they represent the serialised version of the CompressedEdwardsY points. 36 | const EIGHT_TORSION: [[u8; 32]; 8] = [ 37 | [ 38 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39 | 0, 0, 40 | ], // (0,1), order 1, neutral element 41 | [ 42 | 199, 23, 106, 112, 61, 77, 216, 79, 186, 60, 11, 118, 13, 16, 103, 15, 42, 32, 83, 250, 44, 43 | 57, 204, 198, 78, 199, 253, 119, 146, 172, 3, 122, 44 | ], // order 8 45 | [ 46 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47 | 0, 128, 48 | ], // order 4 49 | [ 50 | 38, 232, 149, 143, 194, 178, 39, 176, 69, 195, 244, 137, 242, 239, 152, 240, 213, 223, 172, 51 | 5, 211, 198, 51, 57, 177, 56, 2, 136, 109, 83, 252, 5, 52 | ], // order 8 53 | [ 54 | 236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 55 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 56 | ], // order 2 57 | [ 58 | 38, 232, 149, 143, 194, 178, 39, 176, 69, 195, 244, 137, 242, 239, 152, 240, 213, 223, 172, 59 | 5, 211, 198, 51, 57, 177, 56, 2, 136, 109, 83, 252, 133, 60 | ], // order 8 61 | [ 62 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63 | 0, 0, 64 | ], // order 4 65 | [ 66 | 199, 23, 106, 112, 61, 77, 216, 79, 186, 60, 11, 118, 13, 16, 103, 15, 42, 32, 83, 250, 44, 67 | 57, 204, 198, 78, 199, 253, 119, 146, 172, 3, 250, 68 | ], // order 8 69 | ]; 70 | 71 | // Non canonical representations of those torsion points 72 | // for which the non-canonical serialization exist 73 | // First 3 elements are neutral elements 74 | const EIGHT_TORSION_NON_CANONICAL: [[u8; 32]; 6] = [ 75 | [ 76 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77 | 0, 128, 78 | ], // neutral element, incorrect x-sign : (-0, 1) order 1 79 | [ 80 | 238, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 81 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 82 | ], // neutral element, incorrect x-sign : (-0, 2^255 - 18) order 1 83 | [ 84 | 236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 85 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 86 | ], // incorrect x-sign : (-0, -1) order 2 87 | [ 88 | 238, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 89 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 90 | ], // neutral element with large y component : (0, 2^255 - 18) order 1 91 | [ 92 | 237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 93 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 94 | ], // (-sqrt(-1), 2^255 - 19) order 4 95 | [ 96 | 237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 97 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 98 | ], // (sqrt(-1), 2^255 - 19) order 4 99 | ]; 100 | 101 | // 8 as a Scalar - to reflect instructions of "interpreting values as 102 | // integers" 103 | fn eight() -> Scalar { 104 | let mut bytes = [0u8; 32]; 105 | bytes[31] |= 8; 106 | Scalar::from_bytes_mod_order(bytes) 107 | } 108 | 109 | fn multiple_of_eight_le(scalar: Scalar) -> bool { 110 | scalar.to_bytes()[31].trailing_zeros() >= 3 111 | } 112 | 113 | pub fn check_slice_size<'a>( 114 | slice: &'a [u8], 115 | expected_len: usize, 116 | arg_name: &'static str, 117 | ) -> Result<&'a [u8]> { 118 | if slice.len() != expected_len { 119 | return Err(anyhow!( 120 | "slice length for {} must be {} characters, got {}", 121 | arg_name, 122 | expected_len, 123 | slice.len() 124 | )); 125 | } 126 | Ok(slice) 127 | } 128 | 129 | fn deserialize_point(pt: &[u8]) -> Result { 130 | let mut bytes = [0u8; 32]; 131 | bytes.copy_from_slice(check_slice_size(pt, 32, "pt")?); 132 | 133 | curve25519_dalek::edwards::CompressedEdwardsY(bytes) 134 | .decompress() 135 | .ok_or_else(|| anyhow!("Point decompression failed!")) 136 | } 137 | 138 | #[allow(dead_code)] 139 | fn deserialize_scalar(scalar: &[u8]) -> Result { 140 | let mut bytes = [0u8; 32]; 141 | bytes.copy_from_slice(check_slice_size(scalar, 32, "scalar")?); 142 | 143 | // This permissive pass-through can produce large scalars! 144 | Ok(curve25519_dalek::scalar::Scalar::from_bits(bytes)) 145 | } 146 | 147 | #[allow(dead_code)] 148 | fn deserialize_signature(sig_bytes: &[u8]) -> Result<(EdwardsPoint, Scalar)> { 149 | let checked_sig_bytes = check_slice_size(sig_bytes, 64, "sig_bytes")?; 150 | let r = deserialize_point(&checked_sig_bytes[..32])?; 151 | let s = deserialize_scalar(&checked_sig_bytes[32..])?; 152 | Ok((r, s)) 153 | } 154 | 155 | fn serialize_signature(r: &EdwardsPoint, s: &Scalar) -> Vec { 156 | [&r.compress().as_bytes()[..], &s.as_bytes()[..]].concat() 157 | } 158 | 159 | fn compute_hram(message: &[u8], pub_key: &EdwardsPoint, signature_r: &EdwardsPoint) -> Scalar { 160 | let k_bytes = Sha512::default() 161 | .chain(&signature_r.compress().as_bytes()) 162 | .chain(&pub_key.compress().as_bytes()[..]) 163 | .chain(&message); 164 | // curve25519_dalek is stuck on an old digest version, so we can't do 165 | // Scalar::from_hash 166 | let mut k_output = [0u8; 64]; 167 | k_output.copy_from_slice(k_bytes.finalize().as_slice()); 168 | Scalar::from_bytes_mod_order_wide(&k_output) 169 | } 170 | 171 | fn compute_hram_with_r_array(message: &[u8], pub_key: &EdwardsPoint, signature_r: &[u8]) -> Scalar { 172 | let k_bytes = Sha512::default() 173 | .chain(&signature_r) 174 | .chain(&pub_key.compress().as_bytes()[..]) 175 | .chain(&message); 176 | // curve25519_dalek is stuck on an old digest version, so we can't do 177 | // Scalar::from_hash 178 | let mut k_output = [0u8; 64]; 179 | k_output.copy_from_slice(k_bytes.finalize().as_slice()); 180 | Scalar::from_bytes_mod_order_wide(&k_output) 181 | } 182 | 183 | fn compute_hram_with_pk_array( 184 | message: &[u8], 185 | pub_key_arr: &[u8], 186 | signature_r: &EdwardsPoint, 187 | ) -> Scalar { 188 | let k_bytes = Sha512::default() 189 | .chain(&signature_r.compress().as_bytes()) 190 | .chain(&pub_key_arr) 191 | .chain(&message); 192 | // curve25519_dalek is stuck on an old digest version, so we can't do 193 | // Scalar::from_hash 194 | let mut k_output = [0u8; 64]; 195 | k_output.copy_from_slice(k_bytes.finalize().as_slice()); 196 | Scalar::from_bytes_mod_order_wide(&k_output) 197 | } 198 | 199 | fn verify_cofactored( 200 | message: &[u8], 201 | pub_key: &EdwardsPoint, 202 | unpacked_signature: &(EdwardsPoint, Scalar), 203 | ) -> Result<()> { 204 | let k = compute_hram(message, pub_key, &unpacked_signature.0); 205 | verify_final_cofactored(pub_key, unpacked_signature, &k) 206 | } 207 | 208 | fn verify_cofactorless( 209 | message: &[u8], 210 | pub_key: &EdwardsPoint, 211 | unpacked_signature: &(EdwardsPoint, Scalar), 212 | ) -> Result<()> { 213 | let k = compute_hram(message, pub_key, &unpacked_signature.0); 214 | verify_final_cofactorless(pub_key, unpacked_signature, &k) 215 | } 216 | 217 | fn verify_pre_reduced_cofactored( 218 | message: &[u8], 219 | pub_key: &EdwardsPoint, 220 | unpacked_signature: &(EdwardsPoint, Scalar), 221 | ) -> Result<()> { 222 | let k = compute_hram(message, pub_key, &unpacked_signature.0); 223 | verify_final_pre_reduced_cofactored(pub_key, unpacked_signature, &k) 224 | } 225 | 226 | fn verify_final_cofactored( 227 | pub_key: &EdwardsPoint, 228 | unpacked_signature: &(EdwardsPoint, Scalar), 229 | hash: &Scalar, 230 | ) -> Result<()> { 231 | let rprime = EdwardsPoint::vartime_double_scalar_mul_basepoint( 232 | &hash, 233 | &pub_key.neg(), 234 | &unpacked_signature.1, 235 | ); 236 | if (unpacked_signature.0 - rprime) 237 | .mul_by_cofactor() 238 | .is_identity() 239 | { 240 | Ok(()) 241 | } else { 242 | Err(anyhow!("Invalid cofactored signature")) 243 | } 244 | } 245 | 246 | fn verify_final_pre_reduced_cofactored( 247 | pub_key: &EdwardsPoint, 248 | unpacked_signature: &(EdwardsPoint, Scalar), 249 | hash: &Scalar, 250 | ) -> Result<()> { 251 | let eight_hash = eight() * hash; 252 | let eight_s = eight() * unpacked_signature.1; 253 | 254 | let rprime = 255 | EdwardsPoint::vartime_double_scalar_mul_basepoint(&eight_hash, &pub_key.neg(), &eight_s); 256 | if (unpacked_signature.0.mul_by_cofactor() - rprime).is_identity() { 257 | Ok(()) 258 | } else { 259 | Err(anyhow!("Invalid pre-reduced cofactored signature")) 260 | } 261 | } 262 | 263 | fn verify_final_cofactorless( 264 | pub_key: &EdwardsPoint, 265 | unpacked_signature: &(EdwardsPoint, Scalar), 266 | hash: &Scalar, 267 | ) -> Result<()> { 268 | let rprime = EdwardsPoint::vartime_double_scalar_mul_basepoint( 269 | &hash, 270 | &pub_key.neg(), 271 | &unpacked_signature.1, 272 | ); 273 | if (unpacked_signature.0 - rprime).is_identity() { 274 | Ok(()) 275 | } else { 276 | Err(anyhow!("Invalid cofactorless signature")) 277 | } 278 | } 279 | 280 | /////////// 281 | // Cases // 282 | /////////// 283 | 284 | pub struct TestVector { 285 | #[allow(dead_code)] 286 | message: [u8; 32], 287 | #[allow(dead_code)] 288 | pub_key: [u8; 32], 289 | #[allow(dead_code)] 290 | signature: Vec, 291 | } 292 | 293 | impl Serialize for TestVector { 294 | fn serialize(&self, serializer: S) -> Result 295 | where 296 | S: Serializer, 297 | { 298 | let mut state = serializer.serialize_struct("Color", 3)?; 299 | state.serialize_field("message", &hex::encode(&self.message))?; 300 | state.serialize_field("pub_key", &hex::encode(&self.pub_key))?; 301 | state.serialize_field("signature", &hex::encode(&self.signature))?; 302 | state.end() 303 | } 304 | } 305 | 306 | fn new_rng() -> impl RngCore { 307 | let mut pi_bytes = [0u8; 32]; 308 | for i in 0..4 { 309 | pi_bytes[8 * i..8 * i + 8].copy_from_slice(&std::f64::consts::PI.to_le_bytes()[..]); 310 | } 311 | StdRng::from_seed(pi_bytes) 312 | } 313 | 314 | fn pick_small_nonzero_point(idx: usize) -> EdwardsPoint { 315 | deserialize_point(&EIGHT_TORSION[(idx % 7 + 1)]).unwrap() 316 | } 317 | 318 | ////////////////////// 319 | // 0 (cofactored) // 320 | // 1 (cofactorless) // 321 | ////////////////////// 322 | 323 | pub fn zero_small_small() -> Result<(TestVector, TestVector), anyhow::Error> { 324 | let mut rng = new_rng(); 325 | // Pick a torsion point 326 | let small_idx: usize = rng.next_u64() as usize; 327 | 328 | let pub_key = pick_small_nonzero_point(small_idx + 1); 329 | let r = pub_key.neg(); 330 | let s = Scalar::zero(); 331 | 332 | let mut message = [0u8; 32]; 333 | rng.fill_bytes(&mut message); 334 | if (r + compute_hram(&message, &pub_key, &r) * pub_key).is_identity() { 335 | return Err(anyhow!("wrong rng seed")); 336 | } 337 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 338 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_err()); 339 | debug!( 340 | "S=0, small A, small R\n\ 341 | passes cofactored, fails cofactorless, repudiable\n\ 342 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 343 | hex::encode(&message), 344 | hex::encode(&pub_key.compress().as_bytes()), 345 | hex::encode(&serialize_signature(&r, &s)) 346 | ); 347 | let tv1 = TestVector { 348 | message, 349 | pub_key: pub_key.compress().to_bytes(), 350 | signature: serialize_signature(&r, &s), 351 | }; 352 | 353 | while !(r + compute_hram(&message, &pub_key, &r) * pub_key).is_identity() { 354 | rng.fill_bytes(&mut message); 355 | } 356 | 357 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 358 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_ok()); 359 | 360 | debug!( 361 | "S=0, small A, small R\n\ 362 | passes cofactored, passes cofactorless, repudiable\n\ 363 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 364 | hex::encode(&message), 365 | hex::encode(&pub_key.compress().as_bytes()), 366 | hex::encode(&serialize_signature(&r, &s)) 367 | ); 368 | let tv2 = TestVector { 369 | message, 370 | pub_key: pub_key.compress().to_bytes(), 371 | signature: serialize_signature(&r, &s), 372 | }; 373 | 374 | Ok((tv1, tv2)) 375 | } 376 | 377 | ////////////////////// 378 | // 2 (cofactored) // 379 | // 3 (cofactorless) // 380 | ////////////////////// 381 | 382 | pub fn non_zero_mixed_small() -> Result<(TestVector, TestVector)> { 383 | let mut rng = new_rng(); 384 | // Pick a random Scalar 385 | let mut scalar_bytes = [0u8; 32]; 386 | rng.fill_bytes(&mut scalar_bytes); 387 | let s = Scalar::from_bytes_mod_order(scalar_bytes); 388 | debug_assert!(s.is_canonical()); 389 | debug_assert!(s != Scalar::zero()); 390 | 391 | let r0 = s * ED25519_BASEPOINT_POINT; 392 | 393 | // Pick a torsion point 394 | let small_idx: usize = rng.next_u64() as usize; 395 | let pub_key = pick_small_nonzero_point(small_idx + 1); 396 | 397 | let r = r0 + pub_key.neg(); 398 | 399 | let mut message = [0u8; 32]; 400 | rng.fill_bytes(&mut message); 401 | if (pub_key.neg() + compute_hram(&message, &pub_key, &r) * pub_key).is_identity() { 402 | return Err(anyhow!("wrong rng seed")); 403 | } 404 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 405 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_err()); 406 | debug!( 407 | "S > 0, small A, mixed R\n\ 408 | passes cofactored, fails cofactorless, repudiable\n\ 409 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 410 | hex::encode(&message), 411 | hex::encode(&pub_key.compress().as_bytes()), 412 | hex::encode(&serialize_signature(&r, &s)) 413 | ); 414 | let tv1 = TestVector { 415 | message, 416 | pub_key: pub_key.compress().to_bytes(), 417 | signature: serialize_signature(&r, &s), 418 | }; 419 | 420 | while !(pub_key.neg() + compute_hram(&message, &pub_key, &r) * pub_key).is_identity() { 421 | rng.fill_bytes(&mut message); 422 | } 423 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 424 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_ok()); 425 | debug!( 426 | "S > 0, small A, mixed R\n\ 427 | passes cofactored, passes cofactorless, repudiable\n\ 428 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 429 | hex::encode(&message), 430 | hex::encode(&pub_key.compress().as_bytes()), 431 | hex::encode(&serialize_signature(&r, &s)) 432 | ); 433 | let tv2 = TestVector { 434 | message, 435 | pub_key: pub_key.compress().to_bytes(), 436 | signature: serialize_signature(&r, &s), 437 | }; 438 | 439 | Ok((tv1, tv2)) 440 | } 441 | 442 | ////////////////////// 443 | // 4 (cofactored) // 444 | // 5 (cofactorless) // 445 | ////////////////////// 446 | 447 | // The symmetric case from non_zero_mixed_small 448 | pub fn non_zero_small_mixed() -> Result<(TestVector, TestVector)> { 449 | let mut rng = new_rng(); 450 | // Pick a random scalar 451 | let mut scalar_bytes = [0u8; 32]; 452 | rng.fill_bytes(&mut scalar_bytes); 453 | let a = Scalar::from_bytes_mod_order(scalar_bytes); 454 | debug_assert!(a.is_canonical()); 455 | debug_assert!(a != Scalar::zero()); 456 | 457 | let pub_key_component = a * ED25519_BASEPOINT_POINT; 458 | 459 | // Pick a torsion point 460 | let small_idx: usize = rng.next_u64() as usize; 461 | let r = pick_small_nonzero_point(small_idx + 1); 462 | 463 | let pub_key = pub_key_component + r.neg(); 464 | 465 | let mut message = [0u8; 32]; 466 | rng.fill_bytes(&mut message); 467 | if (r + compute_hram(&message, &pub_key, &r) * r.neg()).is_identity() { 468 | return Err(anyhow!("wrong rng seed")); 469 | } 470 | let s = compute_hram(&message, &pub_key, &r) * a; 471 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 472 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_err()); 473 | debug!( 474 | "S > 0, mixed A, small R\n\ 475 | passes cofactored, fails cofactorless, leaks private key\n\ 476 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 477 | hex::encode(&message), 478 | hex::encode(&pub_key.compress().as_bytes()), 479 | hex::encode(&serialize_signature(&r, &s)) 480 | ); 481 | 482 | let tv1 = TestVector { 483 | message, 484 | pub_key: pub_key.compress().to_bytes(), 485 | signature: serialize_signature(&r, &s), 486 | }; 487 | 488 | while !(r + compute_hram(&message, &pub_key, &r) * r.neg()).is_identity() { 489 | rng.fill_bytes(&mut message); 490 | } 491 | let s = compute_hram(&message, &pub_key, &r) * a; 492 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 493 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_ok()); 494 | debug!( 495 | "S > 0, mixed A, small R\n\ 496 | passes cofactored, passes cofactorless, leaks private key\n\ 497 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 498 | hex::encode(&message), 499 | hex::encode(&pub_key.compress().as_bytes()), 500 | hex::encode(&serialize_signature(&r, &s)) 501 | ); 502 | let tv2 = TestVector { 503 | message, 504 | pub_key: pub_key.compress().to_bytes(), 505 | signature: serialize_signature(&r, &s), 506 | }; 507 | 508 | Ok((tv1, tv2)) 509 | } 510 | 511 | ////////////////////// 512 | // 6 (cofactored) // 513 | // 7 (cofactorless) // 514 | ////////////////////// 515 | 516 | pub fn non_zero_mixed_mixed() -> Result<(TestVector, TestVector)> { 517 | let mut rng = new_rng(); 518 | // Pick a random scalar 519 | let mut scalar_bytes = [0u8; 32]; 520 | rng.fill_bytes(&mut scalar_bytes); 521 | let a = Scalar::from_bytes_mod_order(scalar_bytes); 522 | debug_assert!(a.is_canonical()); 523 | debug_assert!(a != Scalar::zero()); 524 | // Pick a random nonce 525 | let nonce_bytes = [0u8; 32]; 526 | rng.fill_bytes(&mut scalar_bytes); 527 | 528 | // Pick a torsion point 529 | let small_idx: usize = rng.next_u64() as usize; 530 | let small_pt = pick_small_nonzero_point(small_idx + 1); 531 | 532 | // generate the r of a "normal" signature 533 | let prelim_pub_key = a * ED25519_BASEPOINT_POINT; 534 | 535 | let mut message = [0u8; 32]; 536 | rng.fill_bytes(&mut message); 537 | let mut h = Sha512::new(); 538 | h.update(&nonce_bytes); 539 | h.update(&message); 540 | 541 | let mut output = [0u8; 64]; 542 | output.copy_from_slice(h.finalize().as_slice()); 543 | let mut prelim_r = curve25519_dalek::scalar::Scalar::from_bytes_mod_order_wide(&output); 544 | 545 | let pub_key = prelim_pub_key + small_pt; 546 | let mut r = prelim_r * ED25519_BASEPOINT_POINT + small_pt.neg(); 547 | 548 | if (small_pt.neg() + compute_hram(&message, &pub_key, &r) * small_pt).is_identity() { 549 | return Err(anyhow!("wrong rng seed")); 550 | } 551 | let s = prelim_r + compute_hram(&message, &pub_key, &r) * a; 552 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 553 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_err()); 554 | debug!( 555 | "S > 0, mixed A, mixed R\n\ 556 | passes cofactored, fails cofactorless\n\ 557 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 558 | hex::encode(&message), 559 | hex::encode(&pub_key.compress().as_bytes()), 560 | hex::encode(&serialize_signature(&r, &s)) 561 | ); 562 | 563 | let tv1 = TestVector { 564 | message, 565 | pub_key: pub_key.compress().to_bytes(), 566 | signature: serialize_signature(&r, &s), 567 | }; 568 | 569 | while !(small_pt.neg() + compute_hram(&message, &pub_key, &r) * small_pt).is_identity() { 570 | rng.fill_bytes(&mut message); 571 | let mut h = Sha512::new(); 572 | h.update(&nonce_bytes); 573 | h.update(&message); 574 | 575 | let mut output = [0u8; 64]; 576 | output.copy_from_slice(h.finalize().as_slice()); 577 | prelim_r = curve25519_dalek::scalar::Scalar::from_bytes_mod_order_wide(&output); 578 | 579 | r = prelim_r * ED25519_BASEPOINT_POINT + small_pt.neg(); 580 | } 581 | let s = prelim_r + compute_hram(&message, &pub_key, &r) * a; 582 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 583 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_ok()); 584 | debug!( 585 | "S > 0, mixed A, mixed R\n\ 586 | passes cofactored, passes cofactorless\n\ 587 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 588 | hex::encode(&message), 589 | hex::encode(&pub_key.compress().as_bytes()), 590 | hex::encode(&serialize_signature(&r, &s)) 591 | ); 592 | let tv2 = TestVector { 593 | message, 594 | pub_key: pub_key.compress().to_bytes(), 595 | signature: serialize_signature(&r, &s), 596 | }; 597 | 598 | Ok((tv1, tv2)) 599 | } 600 | 601 | //////////////////////////// 602 | // 8 (pre-reduced scalar) // 603 | //////////////////////////// 604 | 605 | fn pre_reduced_scalar() -> TestVector { 606 | let mut rng = new_rng(); 607 | 608 | // Pick a random scalar 609 | let mut scalar_bytes = [0u8; 32]; 610 | rng.fill_bytes(&mut scalar_bytes); 611 | let a = Scalar::from_bytes_mod_order(scalar_bytes); 612 | debug_assert!(a.is_canonical()); 613 | debug_assert!(a != Scalar::zero()); 614 | // Pick a random nonce 615 | let nonce_bytes = [0u8; 32]; 616 | rng.fill_bytes(&mut scalar_bytes); 617 | 618 | // generate the r of a "normal" signature 619 | let prelim_pub_key = a * ED25519_BASEPOINT_POINT; 620 | 621 | // Pick a torsion point 622 | let small_idx: usize = rng.next_u64() as usize; 623 | let small_pt = pick_small_nonzero_point(small_idx + 1); 624 | let pub_key = prelim_pub_key + small_pt; 625 | 626 | let mut message = [0u8; 32]; 627 | rng.fill_bytes(&mut message); 628 | let mut h = Sha512::new(); 629 | h.update(&nonce_bytes); 630 | h.update(&message); 631 | 632 | let mut output = [0u8; 64]; 633 | output.copy_from_slice(h.finalize().as_slice()); 634 | let r_scalar = curve25519_dalek::scalar::Scalar::from_bytes_mod_order_wide(&output); 635 | let r = r_scalar * ED25519_BASEPOINT_POINT; 636 | 637 | // grind a k so that 8*k gets reduced to a number NOT multiple of eight, 638 | // and add a small order component to the public key. 639 | while multiple_of_eight_le(eight() * compute_hram(&message, &pub_key, &r)) { 640 | rng.fill_bytes(&mut message); 641 | } 642 | 643 | let s = r_scalar + compute_hram(&message, &pub_key, &r) * a; 644 | 645 | // that's because we do cofactored verification without pre-reducing scalars 646 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 647 | 648 | // pre-reducing is a mistake 649 | debug_assert!(verify_pre_reduced_cofactored(&message, &pub_key, &(r, s)).is_err()); 650 | 651 | // as expected 652 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_err()); 653 | debug!( 654 | "S > 0, mixed A, large order R\n\ 655 | passes cofactored, fails pre-reducing cofactored, fails cofactorless\n\ 656 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 657 | hex::encode(&message), 658 | hex::encode(&pub_key.compress().as_bytes()), 659 | hex::encode(&serialize_signature(&r, &s)) 660 | ); 661 | TestVector { 662 | message, 663 | pub_key: pub_key.compress().to_bytes(), 664 | signature: serialize_signature(&r, &s), 665 | } 666 | } 667 | 668 | mod non_reducing_scalar52; 669 | use non_reducing_scalar52::Scalar52; 670 | 671 | //////// 672 | // 9 // 673 | //////// 674 | 675 | fn large_s() -> Result { 676 | let mut rng = new_rng(); 677 | // Pick a random scalar 678 | let mut scalar_bytes = [0u8; 32]; 679 | rng.fill_bytes(&mut scalar_bytes); 680 | let a = Scalar::from_bytes_mod_order(scalar_bytes); 681 | debug_assert!(a.is_canonical()); 682 | debug_assert!(a != Scalar::zero()); 683 | // Pick a random nonce 684 | let nonce_bytes = [0u8; 32]; 685 | rng.fill_bytes(&mut scalar_bytes); 686 | 687 | // generate the r of a "normal" signature 688 | let pub_key = a * ED25519_BASEPOINT_POINT; 689 | 690 | let mut message = [0u8; 32]; 691 | rng.fill_bytes(&mut message); 692 | let mut h = Sha512::new(); 693 | h.update(&nonce_bytes); 694 | h.update(&message); 695 | 696 | let mut output = [0u8; 64]; 697 | output.copy_from_slice(h.finalize().as_slice()); 698 | let r_scalar = curve25519_dalek::scalar::Scalar::from_bytes_mod_order_wide(&output); 699 | 700 | let r = r_scalar * ED25519_BASEPOINT_POINT; 701 | 702 | let s = r_scalar + compute_hram(&message, &pub_key, &r) * a; 703 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 704 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_ok()); 705 | 706 | let s_nonreducing = Scalar52::from_bytes(&s.to_bytes()); 707 | let s_prime_bytes = Scalar52::add(&s_nonreducing, &non_reducing_scalar52::L).to_bytes(); 708 | // using deserialize_scalar is key here, we use `from_bits` to represent 709 | // the scalar 710 | let s_prime = deserialize_scalar(&s_prime_bytes)?; 711 | 712 | debug_assert!(s != s_prime); 713 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s_prime)).is_ok()); 714 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s_prime)).is_ok()); 715 | 716 | debug!( 717 | "S > L, large order A, large order R\n\ 718 | passes cofactored, passes cofactorless, often excluded from both, breaks strong unforgeability\n\ 719 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 720 | hex::encode(&message), 721 | hex::encode(&pub_key.compress().as_bytes()), 722 | hex::encode(&serialize_signature(&r, &s_prime)) 723 | ); 724 | let tv = TestVector { 725 | message, 726 | pub_key: pub_key.compress().to_bytes(), 727 | signature: serialize_signature(&r, &s_prime), 728 | }; 729 | 730 | Ok(tv) 731 | } 732 | 733 | //////// 734 | // 10 // 735 | //////// 736 | 737 | fn really_large_s() -> Result { 738 | let mut rng = new_rng(); 739 | // Pick a random scalar 740 | let mut scalar_bytes = [0u8; 32]; 741 | rng.fill_bytes(&mut scalar_bytes); 742 | let a = Scalar::from_bytes_mod_order(scalar_bytes); 743 | debug_assert!(a.is_canonical()); 744 | debug_assert!(a != Scalar::zero()); 745 | // Pick a random nonce 746 | let mut nonce_bytes = [0u8; 32]; 747 | rng.fill_bytes(&mut nonce_bytes); 748 | 749 | // generate the r of a "normal" signature 750 | let pub_key = a * ED25519_BASEPOINT_POINT; 751 | 752 | let mut message = [0u8; 32]; 753 | rng.fill_bytes(&mut message); 754 | let mut h = Sha512::new(); 755 | h.update(&nonce_bytes); 756 | h.update(&message); 757 | 758 | let mut output = [0u8; 64]; 759 | output.copy_from_slice(h.finalize().as_slice()); 760 | let r_scalar = curve25519_dalek::scalar::Scalar::from_bytes_mod_order_wide(&output); 761 | 762 | let r = r_scalar * ED25519_BASEPOINT_POINT; 763 | 764 | let s = r_scalar + compute_hram(&message, &pub_key, &r) * a; 765 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 766 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_ok()); 767 | 768 | let mut s_nonreducing = Scalar52::from_bytes(&s.to_bytes()); 769 | // perform the incomplete higher-bits check often used in place of s Result> { 809 | let mut vec = Vec::new(); 810 | 811 | // r not identity, with incorrect x sign and y coordinate larger than p 812 | let r_arr = EIGHT_TORSION_NON_CANONICAL[2]; 813 | let mut rng = new_rng(); 814 | // Pick a random scalar 815 | let mut scalar_bytes = [0u8; 32]; 816 | rng.fill_bytes(&mut scalar_bytes); 817 | let a = Scalar::from_bytes_mod_order(scalar_bytes); 818 | debug_assert!(a.is_canonical()); 819 | debug_assert!(a != Scalar::zero()); 820 | 821 | let pub_key_component = a * ED25519_BASEPOINT_POINT; 822 | let r = deserialize_point(&r_arr[..32]).unwrap(); 823 | 824 | let small_idx: usize = rng.next_u64() as usize; 825 | let r2 = pick_small_nonzero_point(small_idx + 1); 826 | let pub_key = pub_key_component + r2.neg(); 827 | 828 | let mut message = [0u8; 32]; 829 | rng.fill_bytes(&mut message); 830 | 831 | while !(r + compute_hram(&message, &pub_key, &r) * r2.neg()).is_identity() 832 | || !(r + compute_hram_with_r_array(&message, &pub_key, &r_arr[..32]) * r2.neg()) 833 | .is_identity() 834 | { 835 | rng.fill_bytes(&mut message); 836 | } 837 | let s = compute_hram(&message, &pub_key, &r) * a; 838 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 839 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_ok()); 840 | let mut signature = serialize_signature(&r, &s); 841 | signature[..32].clone_from_slice(&r_arr[..32]); 842 | debug!( 843 | "S > 0, mixed A, small non-canonical R\n\ 844 | passes cofactored, passes cofactorless, leaks private key\n\ 845 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 846 | hex::encode(&message), 847 | hex::encode(&pub_key.compress().as_bytes()), 848 | hex::encode(&signature) 849 | ); 850 | let tv1 = TestVector { 851 | message, 852 | pub_key: pub_key.compress().to_bytes(), 853 | signature, 854 | }; 855 | vec.push(tv1); 856 | 857 | let s = compute_hram_with_r_array(&message, &pub_key, &r_arr[..32]) * a; 858 | let mut signature = serialize_signature(&r, &s); 859 | signature[..32].clone_from_slice(&r_arr[..32]); 860 | debug!( 861 | "S > 0, mixed A, small non-canonical R\n\ 862 | passes cofactored, passes cofactorless, leaks private key\n\ 863 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 864 | hex::encode(&message), 865 | hex::encode(&pub_key.compress().as_bytes()), 866 | hex::encode(&signature) 867 | ); 868 | let tv2 = TestVector { 869 | message, 870 | pub_key: pub_key.compress().to_bytes(), 871 | signature, 872 | }; 873 | vec.push(tv2); 874 | 875 | Ok(vec) 876 | } 877 | 878 | /////////// 879 | // 13-14 // 880 | /////////// 881 | 882 | // This test vector has A = (-0, 2^255 - 20) of order 2 in non-canonical form, serialialized as ECFFFF..FFFF. 883 | // Libraries that reject non-canonical encodings of A or reject A of small order would reject both vectors. 884 | // Libraries with cofactorless verification that accept the first vector, 885 | // but reject the second reduce A prior to hashing. 886 | // Libraries with cofactorless verification that reject the first vector, 887 | // but accept the second do not reduce A prior to hashing. 888 | // Both vectors pass for cofactored verification. 889 | #[allow(dead_code)] 890 | pub fn non_zero_mixed_small_non_canonical() -> Result> { 891 | let mut vec = Vec::new(); 892 | 893 | // pk not identity, with only incorrect x sign 894 | let pub_key_arr = EIGHT_TORSION_NON_CANONICAL[2]; 895 | 896 | let mut rng = new_rng(); 897 | // Pick a random Scalar 898 | let mut scalar_bytes = [0u8; 32]; 899 | rng.fill_bytes(&mut scalar_bytes); 900 | let s = Scalar::from_bytes_mod_order(scalar_bytes); 901 | debug_assert!(s.is_canonical()); 902 | debug_assert!(s != Scalar::zero()); 903 | 904 | let r0 = s * ED25519_BASEPOINT_POINT; 905 | let pub_key = deserialize_point(&pub_key_arr[..32]).unwrap(); 906 | let r = r0 + pub_key.neg(); 907 | 908 | let mut message = [0u8; 32]; 909 | rng.fill_bytes(&mut message); 910 | 911 | // succeeds when public key is reserialized 912 | while !(pub_key.neg() + compute_hram(&message, &pub_key, &r) * pub_key).is_identity() 913 | || (pub_key.neg() + compute_hram_with_pk_array(&message, &pub_key_arr[..32], &r) * pub_key) 914 | .is_identity() 915 | { 916 | rng.fill_bytes(&mut message); 917 | } 918 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 919 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_ok()); 920 | debug!( 921 | "S > 0, non-canonical A, mixed R\n\ 922 | passes cofactored, passes cofactorless, repudiable\n\ 923 | reserializes A\n\ 924 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 925 | hex::encode(&message), 926 | hex::encode(&pub_key.compress().as_bytes()), 927 | hex::encode(&serialize_signature(&r, &s)) 928 | ); 929 | let tv1 = TestVector { 930 | message, 931 | pub_key: pub_key_arr, 932 | signature: serialize_signature(&r, &s), 933 | }; 934 | vec.push(tv1); 935 | 936 | // succeeds when public key is not-reserialized 937 | while !(pub_key.neg() + compute_hram_with_pk_array(&message, &pub_key_arr[..32], &r) * pub_key) 938 | .is_identity() 939 | || (pub_key.neg() + compute_hram(&message, &pub_key, &r) * pub_key).is_identity() 940 | { 941 | rng.fill_bytes(&mut message); 942 | } 943 | debug_assert!(verify_cofactored(&message, &pub_key, &(r, s)).is_ok()); 944 | debug_assert!(verify_cofactorless(&message, &pub_key, &(r, s)).is_err()); 945 | debug!( 946 | "S > 0, non-canonical A, mixed R\n\ 947 | passes cofactored, passes cofactorless, repudiable\n\ 948 | does not reserialize A\n\ 949 | \"message\": \"{}\", \"pub_key\": \"{}\", \"signature\": \"{}\"", 950 | hex::encode(&message), 951 | hex::encode(&pub_key.compress().as_bytes()), 952 | hex::encode(&serialize_signature(&r, &s)) 953 | ); 954 | let tv2 = TestVector { 955 | message, 956 | pub_key: pub_key_arr, 957 | signature: serialize_signature(&r, &s), 958 | }; 959 | vec.push(tv2); 960 | 961 | Ok(vec) 962 | } 963 | 964 | fn generate_test_vectors() -> Vec { 965 | let mut info = Builder::default(); 966 | info.append("| | msg | sig | S | A | R | cof-ed | cof-less | comment |\n"); 967 | info.append("|---------------------------------------------------------------------------------------|\n"); 968 | let mut vec = Vec::new(); 969 | 970 | // #0: canonical S, small R, small A 971 | let (_tv1, tv2) = zero_small_small().unwrap(); 972 | info.append(format!( 973 | "| 0| ..{:} | ..{:} | = 0 | small | small | V | V | small A and R |\n", 974 | &hex::encode(&tv2.message)[60..], 975 | &hex::encode(&tv2.signature)[124..] 976 | )); 977 | vec.push(tv2); // passes cofactored, passes cofactorless 978 | 979 | // #1: canonical S, mixed R, small A 980 | let (_tv1, tv2) = non_zero_mixed_small().unwrap(); 981 | info.append(format!( 982 | "| 1| ..{:} | ..{:} | < L | small | mixed | V | V | small A only |\n", 983 | &hex::encode(&tv2.message)[60..], 984 | &hex::encode(&tv2.signature)[124..] 985 | )); 986 | vec.push(tv2); // passes cofactored, passes cofactorless 987 | 988 | // #2: canonical S, small R, mixed A 989 | let (_tv1, tv2) = non_zero_small_mixed().unwrap(); 990 | info.append(format!( 991 | "| 2| ..{:} | ..{:} | < L | mixed | small | V | V | small R only |\n", 992 | &hex::encode(&tv2.message)[60..], 993 | &hex::encode(&tv2.signature)[124..] 994 | )); 995 | vec.push(tv2); // passes cofactored, passes cofactorless 996 | 997 | // #3-4: canonical S, mixed R, mixed A 998 | let (tv1, tv2) = non_zero_mixed_mixed().unwrap(); 999 | info.append(format!("| 3| ..{:} | ..{:} | < L | mixed | mixed | V | V | succeeds unless full-order is checked |\n", &hex::encode(&tv2.message)[60..], &hex::encode(&tv2.signature)[124..])); 1000 | vec.push(tv2); // passes cofactored, passes cofactorless 1001 | info.append(format!( 1002 | "| 4| ..{:} | ..{:} | < L | mixed | mixed | V | X | |\n", 1003 | &hex::encode(&tv1.message)[60..], 1004 | &hex::encode(&tv1.signature)[124..] 1005 | )); 1006 | vec.push(tv1); // passes cofactored, fails cofactorless 1007 | 1008 | // #5 Prereduce scalar which fails cofactorless 1009 | let tv1 = pre_reduced_scalar(); 1010 | info.append(format!("| 5| ..{:} | ..{:} | < L | mixed | L | V* | X | fails cofactored iff (8h) prereduced |\n", &hex::encode(&tv1.message)[60..], &hex::encode(&tv1.signature)[124..])); 1011 | vec.push(tv1); 1012 | 1013 | // #6 Large S 1014 | let tv1 = large_s().unwrap(); 1015 | info.append(format!( 1016 | "| 6| ..{:} | ..{:} | > L | L | L | V | V | |\n", 1017 | &hex::encode(&tv1.message)[60..], 1018 | &hex::encode(&tv1.signature)[124..] 1019 | )); 1020 | vec.push(tv1); 1021 | 1022 | // #7 Large S beyond the high bit checks (i.e. non-canonical representation) 1023 | let tv1 = really_large_s().unwrap(); 1024 | info.append(format!( 1025 | "| 7| ..{:} | ..{:} | >> L | L | L | V | V | |\n", 1026 | &hex::encode(&tv1.message)[60..], 1027 | &hex::encode(&tv1.signature)[124..] 1028 | )); 1029 | vec.push(tv1); 1030 | 1031 | // #8-9 Non canonical R 1032 | let mut tv_vec = non_zero_small_non_canonical_mixed().unwrap(); 1033 | assert!(tv_vec.len() == 2); 1034 | info.append(format!("| 8| ..{:} | ..{:} | < L | mixed | small*| V | V | non-canonical R, reduced for hash |\n", &hex::encode(&tv_vec[0].message)[60..], &hex::encode(&tv_vec[0].signature)[124..])); 1035 | info.append(format!("| 9| ..{:} | ..{:} | < L | mixed | small*| V | V | non-canonical R, not reduced for hash |\n", &hex::encode(&tv_vec[1].message)[60..], &hex::encode(&tv_vec[1].signature)[124..])); 1036 | vec.append(&mut tv_vec); 1037 | 1038 | // #10-11 Non canonical A 1039 | let mut tv_vec = non_zero_mixed_small_non_canonical().unwrap(); 1040 | assert!(tv_vec.len() == 2); 1041 | info.append(format!("|10| ..{:} | ..{:} | < L | small*| mixed | V | V | non-canonical A, reduced for hash |\n", &hex::encode(&tv_vec[0].message)[60..], &hex::encode(&tv_vec[0].signature)[124..])); 1042 | info.append(format!("|11| ..{:} | ..{:} | < L | small*| mixed | V | V | non-canonical A, not reduced for hash |\n", &hex::encode(&tv_vec[1].message)[60..], &hex::encode(&tv_vec[1].signature)[124..])); 1043 | vec.append(&mut tv_vec); 1044 | 1045 | // print!("{}", info.string().unwrap()); 1046 | 1047 | vec 1048 | } 1049 | 1050 | fn main() -> Result<()> { 1051 | env_logger::init(); 1052 | let vec = generate_test_vectors(); 1053 | 1054 | // Write test vectors to json 1055 | let cases_json = serde_json::to_string(&vec)?; 1056 | let mut file = File::create("cases.json")?; 1057 | file.write_all(cases_json.as_bytes())?; 1058 | 1059 | // Write test vectors to txt (to ease testing C implementations) 1060 | let mut file = File::create("cases.txt")?; 1061 | file.write_all(vec.len().to_string().as_bytes())?; 1062 | for tv in vec.iter() { 1063 | file.write_all(b"\nmsg=")?; 1064 | file.write_all(hex::encode(&tv.message).as_bytes())?; 1065 | file.write_all(b"\npbk=")?; 1066 | file.write_all(hex::encode(&tv.pub_key).as_bytes())?; 1067 | file.write_all(b"\nsig=")?; 1068 | file.write_all(hex::encode(&tv.signature).as_bytes())?; 1069 | } 1070 | Ok(()) 1071 | } 1072 | 1073 | #[cfg(test)] 1074 | mod tests { 1075 | use super::*; 1076 | use diem_crypto; 1077 | use ed25519_dalek::{PublicKey, Signature, Verifier}; 1078 | use ed25519_zebra::{Signature as ZSignature, VerificationKey as ZPublicKey}; 1079 | use ring::signature; 1080 | use std::convert::TryFrom; 1081 | use untrusted; 1082 | 1083 | fn unpack_test_vector_dalek(t: &TestVector) -> (PublicKey, Signature) { 1084 | let pk = PublicKey::from_bytes(&t.pub_key[..]).unwrap(); 1085 | let sig = Signature::try_from(&t.signature[..]).unwrap(); 1086 | (pk, sig) 1087 | } 1088 | 1089 | fn unpack_test_vector_hacl( 1090 | t: &TestVector, 1091 | ) -> (hacl_star::ed25519::PublicKey, hacl_star::ed25519::Signature) { 1092 | let mut sig_bytes = [0u8; 64]; 1093 | sig_bytes.copy_from_slice(&t.signature[..]); 1094 | 1095 | let pk = hacl_star::ed25519::PublicKey(t.pub_key); 1096 | let sig = hacl_star::ed25519::Signature(sig_bytes); 1097 | (pk, sig) 1098 | } 1099 | 1100 | fn unpack_test_vector_zebra(t: &TestVector) -> (ZPublicKey, ZSignature) { 1101 | let pk = ZPublicKey::try_from(&t.pub_key[..]).unwrap(); 1102 | let sig = ZSignature::try_from(&t.signature[..]).unwrap(); 1103 | (pk, sig) 1104 | } 1105 | 1106 | fn ring_verify(t: &TestVector) -> Result<()> { 1107 | let pk = untrusted::Input::from(&t.pub_key[..]); 1108 | let sig = untrusted::Input::from(&t.signature[..]); 1109 | let msg = untrusted::Input::from(&t.message[..]); 1110 | ::verify( 1111 | &signature::ED25519, 1112 | pk, 1113 | msg, 1114 | sig, 1115 | ) 1116 | .map_err(|_| anyhow!("signature verification failed")) 1117 | } 1118 | 1119 | #[test] 1120 | fn test_diem() { 1121 | let vec = generate_test_vectors(); 1122 | 1123 | print!("\n|diem-crypto |"); 1124 | for tv in vec.iter() { 1125 | let pk = match diem_crypto::ed25519::Ed25519PublicKey::try_from(&tv.pub_key[..]) { 1126 | Ok(pk) => pk, 1127 | Err(_e) => { 1128 | print!(" X |"); 1129 | continue; 1130 | } 1131 | }; 1132 | let sig = match diem_crypto::ed25519::Ed25519Signature::try_from(&tv.signature[..]) { 1133 | Ok(sig) => sig, 1134 | Err(_e) => { 1135 | print!(" X |"); 1136 | continue; 1137 | } 1138 | }; 1139 | match diem_crypto::traits::Signature::verify_arbitrary_msg(&sig, &tv.message[..], &pk) { 1140 | Ok(_v) => print!(" V |"), 1141 | Err(_e) => print!(" X |"), 1142 | } 1143 | } 1144 | println!(); 1145 | } 1146 | 1147 | #[test] 1148 | fn test_hacl() { 1149 | let vec = generate_test_vectors(); 1150 | 1151 | print!("\n|Hacl* |"); 1152 | for tv in vec.iter() { 1153 | let (pk, sig) = unpack_test_vector_hacl(&tv); 1154 | if pk.verify(&tv.message[..], &sig) { 1155 | print!(" V |"); 1156 | } else { 1157 | print!(" X |"); 1158 | } 1159 | } 1160 | println!(); 1161 | } 1162 | 1163 | #[test] 1164 | fn test_dalek() { 1165 | let vec = generate_test_vectors(); 1166 | 1167 | print!("\n|Dalek |"); 1168 | for tv in vec.iter() { 1169 | match Signature::try_from(&tv.signature[..]) { 1170 | Ok(_v) => {} 1171 | Err(_e) => { 1172 | print!(" X |"); 1173 | continue; 1174 | } 1175 | } 1176 | 1177 | let (pk, sig) = unpack_test_vector_dalek(&tv); 1178 | match pk.verify(&tv.message[..], &sig) { 1179 | Ok(_v) => print!(" V |"), 1180 | Err(_e) => print!(" X |"), 1181 | } 1182 | } 1183 | println!(); 1184 | } 1185 | 1186 | #[test] 1187 | fn test_dalek_verify_strict() { 1188 | let vec = generate_test_vectors(); 1189 | 1190 | print!("\n|Dalek strict |"); 1191 | for tv in vec.iter() { 1192 | match Signature::try_from(&tv.signature[..]) { 1193 | Ok(_v) => {} 1194 | Err(_e) => { 1195 | print!(" X |"); 1196 | continue; 1197 | } 1198 | } 1199 | 1200 | let (pk, sig) = unpack_test_vector_dalek(&tv); 1201 | match pk.verify_strict(&tv.message[..], &sig) { 1202 | Ok(_v) => print!(" V |"), 1203 | Err(_e) => print!(" X |"), 1204 | } 1205 | } 1206 | println!(); 1207 | } 1208 | 1209 | #[test] 1210 | fn test_boringssl() { 1211 | let vec = generate_test_vectors(); 1212 | 1213 | print!("\n|BoringSSL |"); 1214 | for tv in vec.iter() { 1215 | match ring_verify(&tv) { 1216 | Ok(_v) => print!(" V |"), 1217 | Err(_e) => print!(" X |"), 1218 | } 1219 | } 1220 | println!(); 1221 | } 1222 | 1223 | #[test] 1224 | fn test_zebra() { 1225 | let vec = generate_test_vectors(); 1226 | 1227 | print!("\n|Zebra |"); 1228 | for tv in vec.iter() { 1229 | match Signature::try_from(&tv.signature[..]) { 1230 | Ok(_v) => {} 1231 | Err(_e) => { 1232 | print!(" X |"); 1233 | continue; 1234 | } 1235 | } 1236 | 1237 | let (pk, sig) = unpack_test_vector_zebra(&tv); 1238 | match pk.verify(&sig, &tv.message[..]) { 1239 | Ok(_v) => print!(" V |"), 1240 | Err(_e) => print!(" X |"), 1241 | } 1242 | } 1243 | println!(); 1244 | } 1245 | 1246 | #[test] 1247 | fn test_repudiation_dalek() { 1248 | // Pick a random Scalar 1249 | let mut rng = new_rng(); 1250 | let mut scalar_bytes = [0u8; 32]; 1251 | rng.fill_bytes(&mut scalar_bytes); 1252 | let s = Scalar::from_bytes_mod_order(scalar_bytes); 1253 | debug_assert!(s.is_canonical()); 1254 | debug_assert!(s != Scalar::zero()); 1255 | 1256 | let r0 = s * ED25519_BASEPOINT_POINT; 1257 | // Pick a torsion point of order 2 1258 | let pub_key = deserialize_point(&EIGHT_TORSION[4]).unwrap(); 1259 | let r = r0 + pub_key.neg(); 1260 | 1261 | let message1 = b"Send 100 USD to Alice"; 1262 | let message2 = b"Send 100000 USD to Alice"; 1263 | 1264 | debug_assert!( 1265 | (pub_key.neg() + compute_hram(message1, &pub_key, &r) * pub_key).is_identity() 1266 | ); 1267 | debug_assert!( 1268 | (pub_key.neg() + compute_hram(message2, &pub_key, &r) * pub_key).is_identity() 1269 | ); 1270 | 1271 | debug_assert!(verify_cofactored(message1, &pub_key, &(r, s)).is_ok()); 1272 | debug_assert!(verify_cofactorless(message1, &pub_key, &(r, s)).is_ok()); 1273 | debug_assert!(verify_cofactored(message2, &pub_key, &(r, s)).is_ok()); 1274 | debug_assert!(verify_cofactorless(message2, &pub_key, &(r, s)).is_ok()); 1275 | 1276 | println!( 1277 | "Small pk breaks non-repudiation:\n\ 1278 | \"pub_key\": \"{}\",\n\ 1279 | \"signature\": \"{}\",\n\ 1280 | \"message1\": \"{}\",\n\ 1281 | \"message2\": \"{}\"", 1282 | hex::encode(&pub_key.compress().as_bytes()), 1283 | hex::encode(&serialize_signature(&r, &s)), 1284 | hex::encode(&message1), 1285 | hex::encode(&message2), 1286 | ); 1287 | 1288 | let signature = serialize_signature(&r, &s); 1289 | let pk = PublicKey::from_bytes(&pub_key.compress().as_bytes()[..]).unwrap(); 1290 | let sig = Signature::try_from(&signature[..]).unwrap(); 1291 | debug_assert!(pk.verify(message1, &sig).is_ok()); 1292 | debug_assert!(pk.verify(message2, &sig).is_ok()); 1293 | } 1294 | } 1295 | -------------------------------------------------------------------------------- /src/non_reducing_scalar52.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the APACHE 2.0 license found in 4 | // the LICENSE file in the root directory of this source tree. 5 | 6 | use core::ops::{Index, IndexMut}; 7 | 8 | /// The `Scalar52` struct represents an element in 9 | /// ℤ/ℓℤ as 5 52-bit limbs. 10 | pub struct Scalar52(pub [u64; 5]); 11 | 12 | /// `L` is the order of base point, i.e. 2^252 + 27742317777372353535851937790883648493 13 | pub const L: Scalar52 = Scalar52([ 14 | 0x0002_631a_5cf5_d3ed, 15 | 0x000d_ea2f_79cd_6581, 16 | 0x0000_0000_0014_def9, 17 | 0x0000_0000_0000_0000, 18 | 0x0000_1000_0000_0000, 19 | ]); 20 | 21 | impl Scalar52 { 22 | /// Return the zero scalar 23 | pub fn zero() -> Scalar52 { 24 | Scalar52([0, 0, 0, 0, 0]) 25 | } 26 | 27 | /// Unpack a 32 byte / 256 bit scalar into 5 52-bit limbs. 28 | pub fn from_bytes(bytes: &[u8; 32]) -> Scalar52 { 29 | let mut words = [0u64; 4]; 30 | for i in 0..4 { 31 | for j in 0..8 { 32 | words[i] |= u64::from(bytes[(i * 8) + j]) << (j * 8) as u64; 33 | } 34 | } 35 | 36 | let mask = (1u64 << 52) - 1; 37 | let top_mask = (1u64 << 48) - 1; 38 | let mut s = Scalar52::zero(); 39 | 40 | s[0] = words[0] & mask; 41 | s[1] = ((words[0] >> 52) | (words[1] << 12)) & mask; 42 | s[2] = ((words[1] >> 40) | (words[2] << 24)) & mask; 43 | s[3] = ((words[2] >> 28) | (words[3] << 36)) & mask; 44 | s[4] = (words[3] >> 16) & top_mask; 45 | 46 | s 47 | } 48 | 49 | /// Pack the limbs of this `Scalar52` into 32 bytes 50 | pub fn to_bytes(&self) -> [u8; 32] { 51 | let mut s = [0u8; 32]; 52 | 53 | s[0] = self.0[0] as u8; 54 | s[1] = (self.0[0] >> 8) as u8; 55 | s[2] = (self.0[0] >> 16) as u8; 56 | s[3] = (self.0[0] >> 24) as u8; 57 | s[4] = (self.0[0] >> 32) as u8; 58 | s[5] = (self.0[0] >> 40) as u8; 59 | s[6] = ((self.0[0] >> 48) | (self.0[1] << 4)) as u8; 60 | s[7] = (self.0[1] >> 4) as u8; 61 | s[8] = (self.0[1] >> 12) as u8; 62 | s[9] = (self.0[1] >> 20) as u8; 63 | s[10] = (self.0[1] >> 28) as u8; 64 | s[11] = (self.0[1] >> 36) as u8; 65 | s[12] = (self.0[1] >> 44) as u8; 66 | s[13] = self.0[2] as u8; 67 | s[14] = (self.0[2] >> 8) as u8; 68 | s[15] = (self.0[2] >> 16) as u8; 69 | s[16] = (self.0[2] >> 24) as u8; 70 | s[17] = (self.0[2] >> 32) as u8; 71 | s[18] = (self.0[2] >> 40) as u8; 72 | s[19] = ((self.0[2] >> 48) | (self.0[3] << 4)) as u8; 73 | s[20] = (self.0[3] >> 4) as u8; 74 | s[21] = (self.0[3] >> 12) as u8; 75 | s[22] = (self.0[3] >> 20) as u8; 76 | s[23] = (self.0[3] >> 28) as u8; 77 | s[24] = (self.0[3] >> 36) as u8; 78 | s[25] = (self.0[3] >> 44) as u8; 79 | s[26] = self.0[4] as u8; 80 | s[27] = (self.0[4] >> 8) as u8; 81 | s[28] = (self.0[4] >> 16) as u8; 82 | s[29] = (self.0[4] >> 24) as u8; 83 | s[30] = (self.0[4] >> 32) as u8; 84 | s[31] = (self.0[4] >> 40) as u8; 85 | 86 | s 87 | } 88 | 89 | /// Compute `a + b` (without mod ℓ) 90 | pub fn add(a: &Scalar52, b: &Scalar52) -> Scalar52 { 91 | let mut sum = Scalar52::zero(); 92 | let mask = (1u64 << 52) - 1; 93 | 94 | // a + b 95 | let mut carry: u64 = 0; 96 | for i in 0..5 { 97 | carry = a[i] + b[i] + (carry >> 52); 98 | sum[i] = carry & mask; 99 | } 100 | 101 | sum 102 | } 103 | } 104 | 105 | impl Index for Scalar52 { 106 | type Output = u64; 107 | fn index(&self, _index: usize) -> &u64 { 108 | &(self.0[_index]) 109 | } 110 | } 111 | 112 | impl IndexMut for Scalar52 { 113 | fn index_mut(&mut self, _index: usize) -> &mut u64 { 114 | &mut (self.0[_index]) 115 | } 116 | } 117 | --------------------------------------------------------------------------------