├── .circleci └── config.yml ├── .gitignore ├── COPYRIGHT ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── SECURITY.md ├── benches ├── bls12_381 │ ├── ec.rs │ ├── fq.rs │ ├── fq12.rs │ ├── fq2.rs │ ├── fr.rs │ └── mod.rs └── pairing_benches.rs ├── release.toml ├── rust-toolchain └── src ├── bls12_381 ├── README.md ├── cofactors.rs ├── ec │ ├── chain.rs │ ├── g1.rs │ ├── g2.rs │ ├── mod.rs │ └── util.rs ├── fq.rs ├── fq12.rs ├── fq2.rs ├── fq6.rs ├── fr.rs ├── mod.rs ├── serde_impl.rs └── tests │ ├── g1_compressed_valid_test_vectors.dat │ ├── g1_hashtopoint.dat │ ├── g1_uncompressed_invalid_test_vectors.dat │ ├── g1_uncompressed_valid_test_vectors.dat │ ├── g2_compressed_valid_test_vectors.dat │ ├── g2_hashtopoint.dat │ ├── g2_uncompressed_valid_test_vectors.dat │ └── mod.rs ├── hash_to_curve.rs ├── hash_to_field.rs ├── lib.rs ├── signum.rs └── tests ├── engine.rs ├── field.rs ├── mod.rs └── repr.rs /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | executors: 4 | default: 5 | docker: 6 | - image: filecoin/rust:latest 7 | working_directory: /mnt/crate 8 | resource_class: 2xlarge 9 | 10 | restore-workspace: &restore-workspace 11 | attach_workspace: 12 | at: /mnt 13 | 14 | restore-cache: &restore-cache 15 | restore_cache: 16 | keys: 17 | - cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }} 18 | - repo-source-{{ .Branch }}-{{ .Revision }} 19 | 20 | commands: 21 | test_target: 22 | parameters: 23 | target: 24 | type: string 25 | steps: 26 | - *restore-workspace 27 | - *restore-cache 28 | - run: 29 | name: Test (<< parameters.target >>) 30 | command: TARGET=<< parameters.target >> cargo test 31 | no_output_timeout: 30m 32 | 33 | jobs: 34 | cargo_fetch: 35 | executor: default 36 | steps: 37 | - checkout 38 | - run: 39 | name: Update submodules 40 | command: git submodule update --init --recursive 41 | - run: 42 | name: Calculate dependencies 43 | command: cargo generate-lockfile 44 | - restore_cache: 45 | keys: 46 | - cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }} 47 | - run: cargo update 48 | - run: cargo fetch 49 | - run: rustup install $(cat rust-toolchain) 50 | - run: rustup default $(cat rust-toolchain) 51 | - run: rustup component add rustfmt-preview 52 | - run: rustup component add clippy-preview 53 | - run: rustc --version 54 | - run: rm -rf .git 55 | - persist_to_workspace: 56 | root: /mnt 57 | paths: 58 | - crate 59 | - save_cache: 60 | key: cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }} 61 | paths: 62 | - "~/.cargo" 63 | - "~/.rustup" 64 | 65 | test_x86_64-unknown-linux-gnu: 66 | executor: default 67 | steps: 68 | - test_target: 69 | target: "x86_64-unknown-linux-gnu" 70 | 71 | rustfmt: 72 | executor: default 73 | steps: 74 | - *restore-workspace 75 | - *restore-cache 76 | - run: 77 | name: Run cargo fmt 78 | command: cargo fmt --all -- --check 79 | 80 | clippy: 81 | executor: default 82 | steps: 83 | - *restore-workspace 84 | - *restore-cache 85 | - run: 86 | name: Run cargo clippy 87 | command: cargo clippy --all --all-features 88 | 89 | build: 90 | executor: default 91 | steps: 92 | - *restore-workspace 93 | - *restore-cache 94 | - run: 95 | name: Run cargo release build 96 | command: cargo build --release 97 | 98 | 99 | workflows: 100 | version: 2.1 101 | 102 | test: 103 | jobs: 104 | - cargo_fetch 105 | - rustfmt: 106 | requires: 107 | - cargo_fetch 108 | - clippy: 109 | requires: 110 | - cargo_fetch 111 | - test_x86_64-unknown-linux-gnu: 112 | requires: 113 | - cargo_fetch 114 | - build: 115 | requires: 116 | - cargo_fetch 117 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyrights in the "pairing" library are retained by their contributors. No 2 | copyright assignment is required to contribute to the "pairing" library. 3 | 4 | The "pairing" library is licensed under either of 5 | 6 | * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) 7 | * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) 8 | 9 | at your option. 10 | 11 | Unless you explicitly state otherwise, any contribution intentionally 12 | submitted for inclusion in the work by you, as defined in the Apache-2.0 13 | license, shall be dual licensed as above, without any additional terms or 14 | conditions. 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "paired" 3 | 4 | # Remember to change version string in README.md. 5 | version = "0.22.0" 6 | authors = [ 7 | "Sean Bowe ", 8 | "Jack Grigg ", 9 | "dignifiedquire " 10 | ] 11 | readme = "README.md" 12 | license = "MIT/Apache-2.0" 13 | 14 | description = "Fork of the 'paired: Pairing-friendly elliptic curve library'" 15 | documentation = "https://docs.rs/paired/" 16 | homepage = "https://github.com/filecoin-project/pairing" 17 | repository = "https://github.com/filecoin-project/pairing" 18 | edition ="2018" 19 | 20 | [dependencies] 21 | byteorder = "1" 22 | blake2b_simd = "0.5" 23 | serde = { version = "1.0", features = ["derive"] } 24 | fff = { version = "0.3.0", features = ["derive"] } 25 | groupy = "0.4.1" 26 | rand_core = "0.5" 27 | hkdf = "0.10.0" 28 | digest = { version = "0.9.0", features = ["std"] } 29 | 30 | [dev-dependencies] 31 | rand_xorshift = "0.2" 32 | serde_json = "1.0" 33 | sha3 = "0.9.0" 34 | sha2 = "0.9.0" 35 | hex = "0.4.2" 36 | 37 | [features] 38 | default = [] 39 | unstable-features = ["expose-arith"] 40 | expose-arith = [] 41 | 42 | 43 | [badges] 44 | maintenance = { status = "actively-developed" } 45 | 46 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # paired [![Crates.io](https://img.shields.io/crates/v/paired.svg)](https://crates.io/crates/paired) 2 | 3 | > This is a fork of the great [pairing](https://github.com/zkcrypto/pairing) library. 4 | 5 | `pairing` is a crate for using pairing-friendly elliptic curves. 6 | 7 | Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) 8 | construction is implemented. 9 | 10 | ## Roadmap 11 | 12 | `pairing` is being refactored into a generic library for working with 13 | pairing-friendly curves. After the refactor, `pairing` will provide basic traits 14 | for pairing-friendly elliptic curve constructions, while specific curves will be 15 | in separate crates. 16 | 17 | ## [Documentation](https://docs.rs/paired/) 18 | 19 | Bring the `paired` crate into your project just as you normally would. 20 | 21 | ## Security Warnings 22 | 23 | This library does not make any guarantees about constant-time operations, memory 24 | access patterns, or resistance to side-channel attacks. 25 | 26 | ## License 27 | 28 | Licensed under either of 29 | 30 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 31 | http://www.apache.org/licenses/LICENSE-2.0) 32 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 33 | 34 | at your option. 35 | 36 | ### Contribution 37 | 38 | Unless you explicitly state otherwise, any contribution intentionally 39 | submitted for inclusion in the work by you, as defined in the Apache-2.0 40 | license, shall be dual licensed as above, without any additional terms or 41 | conditions. 42 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | For reporting *critical* and *security* bugs, please consult our [Security Policy and Responsible Disclosure Program information](https://github.com/filecoin-project/community/blob/master/SECURITY.md) 6 | 7 | ## Reporting a non security bug 8 | 9 | For non-critical bugs, please simply file a GitHub issue on this repo. 10 | -------------------------------------------------------------------------------- /benches/bls12_381/ec.rs: -------------------------------------------------------------------------------- 1 | mod g1 { 2 | use rand_core::SeedableRng; 3 | use rand_xorshift::XorShiftRng; 4 | 5 | use fff::Field; 6 | use groupy::CurveProjective; 7 | use paired::bls12_381::*; 8 | 9 | #[bench] 10 | fn bench_g1_mul_assign(b: &mut ::test::Bencher) { 11 | const SAMPLES: usize = 1000; 12 | 13 | let mut rng = XorShiftRng::from_seed([ 14 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 15 | 0xbc, 0xe5, 16 | ]); 17 | 18 | let v: Vec<(G1, Fr)> = (0..SAMPLES) 19 | .map(|_| (G1::random(&mut rng), Fr::random(&mut rng))) 20 | .collect(); 21 | 22 | let mut count = 0; 23 | b.iter(|| { 24 | let mut tmp = v[count].0; 25 | tmp.mul_assign(v[count].1); 26 | count = (count + 1) % SAMPLES; 27 | tmp 28 | }); 29 | } 30 | 31 | #[bench] 32 | fn bench_g1_add_assign(b: &mut ::test::Bencher) { 33 | const SAMPLES: usize = 1000; 34 | 35 | let mut rng = XorShiftRng::from_seed([ 36 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 37 | 0xbc, 0xe5, 38 | ]); 39 | 40 | let v: Vec<(G1, G1)> = (0..SAMPLES) 41 | .map(|_| (G1::random(&mut rng), G1::random(&mut rng))) 42 | .collect(); 43 | 44 | let mut count = 0; 45 | b.iter(|| { 46 | let mut tmp = v[count].0; 47 | tmp.add_assign(&v[count].1); 48 | count = (count + 1) % SAMPLES; 49 | tmp 50 | }); 51 | } 52 | 53 | #[bench] 54 | fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) { 55 | const SAMPLES: usize = 1000; 56 | 57 | let mut rng = XorShiftRng::from_seed([ 58 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 59 | 0xbc, 0xe5, 60 | ]); 61 | 62 | let v: Vec<(G1, G1Affine)> = (0..SAMPLES) 63 | .map(|_| (G1::random(&mut rng), G1::random(&mut rng).into())) 64 | .collect(); 65 | 66 | let mut count = 0; 67 | b.iter(|| { 68 | let mut tmp = v[count].0; 69 | tmp.add_assign_mixed(&v[count].1); 70 | count = (count + 1) % SAMPLES; 71 | tmp 72 | }); 73 | } 74 | } 75 | 76 | mod g2 { 77 | use rand_core::SeedableRng; 78 | use rand_xorshift::XorShiftRng; 79 | 80 | use fff::Field; 81 | use groupy::CurveProjective; 82 | use paired::bls12_381::*; 83 | 84 | #[bench] 85 | fn bench_g2_mul_assign(b: &mut ::test::Bencher) { 86 | const SAMPLES: usize = 1000; 87 | 88 | let mut rng = XorShiftRng::from_seed([ 89 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 90 | 0xbc, 0xe5, 91 | ]); 92 | 93 | let v: Vec<(G2, Fr)> = (0..SAMPLES) 94 | .map(|_| (G2::random(&mut rng), Fr::random(&mut rng))) 95 | .collect(); 96 | 97 | let mut count = 0; 98 | b.iter(|| { 99 | let mut tmp = v[count].0; 100 | tmp.mul_assign(v[count].1); 101 | count = (count + 1) % SAMPLES; 102 | tmp 103 | }); 104 | } 105 | 106 | #[bench] 107 | fn bench_g2_add_assign(b: &mut ::test::Bencher) { 108 | const SAMPLES: usize = 1000; 109 | 110 | let mut rng = XorShiftRng::from_seed([ 111 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 112 | 0xbc, 0xe5, 113 | ]); 114 | 115 | let v: Vec<(G2, G2)> = (0..SAMPLES) 116 | .map(|_| (G2::random(&mut rng), G2::random(&mut rng))) 117 | .collect(); 118 | 119 | let mut count = 0; 120 | b.iter(|| { 121 | let mut tmp = v[count].0; 122 | tmp.add_assign(&v[count].1); 123 | count = (count + 1) % SAMPLES; 124 | tmp 125 | }); 126 | } 127 | 128 | #[bench] 129 | fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) { 130 | const SAMPLES: usize = 1000; 131 | 132 | let mut rng = XorShiftRng::from_seed([ 133 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 134 | 0xbc, 0xe5, 135 | ]); 136 | 137 | let v: Vec<(G2, G2Affine)> = (0..SAMPLES) 138 | .map(|_| (G2::random(&mut rng), G2::random(&mut rng).into())) 139 | .collect(); 140 | 141 | let mut count = 0; 142 | b.iter(|| { 143 | let mut tmp = v[count].0; 144 | tmp.add_assign_mixed(&v[count].1); 145 | count = (count + 1) % SAMPLES; 146 | tmp 147 | }); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /benches/bls12_381/fq.rs: -------------------------------------------------------------------------------- 1 | use rand_core::SeedableRng; 2 | use rand_xorshift::XorShiftRng; 3 | 4 | use fff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; 5 | use paired::bls12_381::*; 6 | 7 | #[bench] 8 | fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) { 9 | const SAMPLES: usize = 1000; 10 | 11 | let mut rng = XorShiftRng::from_seed([ 12 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 13 | 0xe5, 14 | ]); 15 | 16 | let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) 17 | .map(|_| { 18 | let mut tmp1 = Fq::random(&mut rng).into_repr(); 19 | let mut tmp2 = Fq::random(&mut rng).into_repr(); 20 | // Shave a few bits off to avoid overflow. 21 | for _ in 0..3 { 22 | tmp1.div2(); 23 | tmp2.div2(); 24 | } 25 | (tmp1, tmp2) 26 | }) 27 | .collect(); 28 | 29 | let mut count = 0; 30 | b.iter(|| { 31 | let mut tmp = v[count].0; 32 | tmp.add_nocarry(&v[count].1); 33 | count = (count + 1) % SAMPLES; 34 | tmp 35 | }); 36 | } 37 | 38 | #[bench] 39 | fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) { 40 | const SAMPLES: usize = 1000; 41 | 42 | let mut rng = XorShiftRng::from_seed([ 43 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 44 | 0xe5, 45 | ]); 46 | 47 | let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) 48 | .map(|_| { 49 | let tmp1 = Fq::random(&mut rng).into_repr(); 50 | let mut tmp2 = tmp1; 51 | // Ensure tmp2 is smaller than tmp1. 52 | for _ in 0..10 { 53 | tmp2.div2(); 54 | } 55 | (tmp1, tmp2) 56 | }) 57 | .collect(); 58 | 59 | let mut count = 0; 60 | b.iter(|| { 61 | let mut tmp = v[count].0; 62 | tmp.sub_noborrow(&v[count].1); 63 | count = (count + 1) % SAMPLES; 64 | tmp 65 | }); 66 | } 67 | 68 | #[bench] 69 | fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) { 70 | const SAMPLES: usize = 1000; 71 | 72 | let mut rng = XorShiftRng::from_seed([ 73 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 74 | 0xe5, 75 | ]); 76 | 77 | let v: Vec = (0..SAMPLES) 78 | .map(|_| Fq::random(&mut rng).into_repr()) 79 | .collect(); 80 | 81 | let mut count = 0; 82 | b.iter(|| { 83 | let tmp = v[count].num_bits(); 84 | count = (count + 1) % SAMPLES; 85 | tmp 86 | }); 87 | } 88 | 89 | #[bench] 90 | fn bench_fq_repr_mul2(b: &mut ::test::Bencher) { 91 | const SAMPLES: usize = 1000; 92 | 93 | let mut rng = XorShiftRng::from_seed([ 94 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 95 | 0xe5, 96 | ]); 97 | 98 | let v: Vec = (0..SAMPLES) 99 | .map(|_| Fq::random(&mut rng).into_repr()) 100 | .collect(); 101 | 102 | let mut count = 0; 103 | b.iter(|| { 104 | let mut tmp = v[count]; 105 | tmp.mul2(); 106 | count = (count + 1) % SAMPLES; 107 | tmp 108 | }); 109 | } 110 | 111 | #[bench] 112 | fn bench_fq_repr_div2(b: &mut ::test::Bencher) { 113 | const SAMPLES: usize = 1000; 114 | 115 | let mut rng = XorShiftRng::from_seed([ 116 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 117 | 0xe5, 118 | ]); 119 | 120 | let v: Vec = (0..SAMPLES) 121 | .map(|_| Fq::random(&mut rng).into_repr()) 122 | .collect(); 123 | 124 | let mut count = 0; 125 | b.iter(|| { 126 | let mut tmp = v[count]; 127 | tmp.div2(); 128 | count = (count + 1) % SAMPLES; 129 | tmp 130 | }); 131 | } 132 | 133 | #[bench] 134 | fn bench_fq_add_assign(b: &mut ::test::Bencher) { 135 | const SAMPLES: usize = 1000; 136 | 137 | let mut rng = XorShiftRng::from_seed([ 138 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 139 | 0xe5, 140 | ]); 141 | 142 | let v: Vec<(Fq, Fq)> = (0..SAMPLES) 143 | .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) 144 | .collect(); 145 | 146 | let mut count = 0; 147 | b.iter(|| { 148 | let mut tmp = v[count].0; 149 | tmp.add_assign(&v[count].1); 150 | count = (count + 1) % SAMPLES; 151 | tmp 152 | }); 153 | } 154 | 155 | #[bench] 156 | fn bench_fq_sub_assign(b: &mut ::test::Bencher) { 157 | const SAMPLES: usize = 1000; 158 | 159 | let mut rng = XorShiftRng::from_seed([ 160 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 161 | 0xe5, 162 | ]); 163 | 164 | let v: Vec<(Fq, Fq)> = (0..SAMPLES) 165 | .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) 166 | .collect(); 167 | 168 | let mut count = 0; 169 | b.iter(|| { 170 | let mut tmp = v[count].0; 171 | tmp.sub_assign(&v[count].1); 172 | count = (count + 1) % SAMPLES; 173 | tmp 174 | }); 175 | } 176 | 177 | #[bench] 178 | fn bench_fq_mul_assign(b: &mut ::test::Bencher) { 179 | const SAMPLES: usize = 1000; 180 | 181 | let mut rng = XorShiftRng::from_seed([ 182 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 183 | 0xe5, 184 | ]); 185 | 186 | let v: Vec<(Fq, Fq)> = (0..SAMPLES) 187 | .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) 188 | .collect(); 189 | 190 | let mut count = 0; 191 | b.iter(|| { 192 | let mut tmp = v[count].0; 193 | tmp.mul_assign(&v[count].1); 194 | count = (count + 1) % SAMPLES; 195 | tmp 196 | }); 197 | } 198 | 199 | #[bench] 200 | fn bench_fq_square(b: &mut ::test::Bencher) { 201 | const SAMPLES: usize = 1000; 202 | 203 | let mut rng = XorShiftRng::from_seed([ 204 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 205 | 0xe5, 206 | ]); 207 | 208 | let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); 209 | 210 | let mut count = 0; 211 | b.iter(|| { 212 | let mut tmp = v[count]; 213 | tmp.square(); 214 | count = (count + 1) % SAMPLES; 215 | tmp 216 | }); 217 | } 218 | 219 | #[bench] 220 | fn bench_fq_inverse(b: &mut ::test::Bencher) { 221 | const SAMPLES: usize = 1000; 222 | 223 | let mut rng = XorShiftRng::from_seed([ 224 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 225 | 0xe5, 226 | ]); 227 | 228 | let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); 229 | 230 | let mut count = 0; 231 | b.iter(|| { 232 | count = (count + 1) % SAMPLES; 233 | v[count].inverse() 234 | }); 235 | } 236 | 237 | #[bench] 238 | fn bench_fq_negate(b: &mut ::test::Bencher) { 239 | const SAMPLES: usize = 1000; 240 | 241 | let mut rng = XorShiftRng::from_seed([ 242 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 243 | 0xe5, 244 | ]); 245 | 246 | let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); 247 | 248 | let mut count = 0; 249 | b.iter(|| { 250 | let mut tmp = v[count]; 251 | tmp.negate(); 252 | count = (count + 1) % SAMPLES; 253 | tmp 254 | }); 255 | } 256 | 257 | #[bench] 258 | fn bench_fq_sqrt(b: &mut ::test::Bencher) { 259 | const SAMPLES: usize = 1000; 260 | 261 | let mut rng = XorShiftRng::from_seed([ 262 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 263 | 0xe5, 264 | ]); 265 | 266 | let v: Vec = (0..SAMPLES) 267 | .map(|_| { 268 | let mut tmp = Fq::random(&mut rng); 269 | tmp.square(); 270 | tmp 271 | }) 272 | .collect(); 273 | 274 | let mut count = 0; 275 | b.iter(|| { 276 | count = (count + 1) % SAMPLES; 277 | v[count].sqrt() 278 | }); 279 | } 280 | 281 | #[bench] 282 | fn bench_fq_into_repr(b: &mut ::test::Bencher) { 283 | const SAMPLES: usize = 1000; 284 | 285 | let mut rng = XorShiftRng::from_seed([ 286 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 287 | 0xe5, 288 | ]); 289 | 290 | let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); 291 | 292 | let mut count = 0; 293 | b.iter(|| { 294 | count = (count + 1) % SAMPLES; 295 | v[count].into_repr() 296 | }); 297 | } 298 | 299 | #[bench] 300 | fn bench_fq_from_repr(b: &mut ::test::Bencher) { 301 | const SAMPLES: usize = 1000; 302 | 303 | let mut rng = XorShiftRng::from_seed([ 304 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 305 | 0xe5, 306 | ]); 307 | 308 | let v: Vec = (0..SAMPLES) 309 | .map(|_| Fq::random(&mut rng).into_repr()) 310 | .collect(); 311 | 312 | let mut count = 0; 313 | b.iter(|| { 314 | count = (count + 1) % SAMPLES; 315 | Fq::from_repr(v[count]) 316 | }); 317 | } 318 | -------------------------------------------------------------------------------- /benches/bls12_381/fq12.rs: -------------------------------------------------------------------------------- 1 | use rand_core::SeedableRng; 2 | use rand_xorshift::XorShiftRng; 3 | 4 | use fff::Field; 5 | use paired::bls12_381::*; 6 | 7 | #[bench] 8 | fn bench_fq12_add_assign(b: &mut ::test::Bencher) { 9 | const SAMPLES: usize = 1000; 10 | 11 | let mut rng = XorShiftRng::from_seed([ 12 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 13 | 0xe5, 14 | ]); 15 | 16 | let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) 17 | .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) 18 | .collect(); 19 | 20 | let mut count = 0; 21 | b.iter(|| { 22 | let mut tmp = v[count].0; 23 | tmp.add_assign(&v[count].1); 24 | count = (count + 1) % SAMPLES; 25 | tmp 26 | }); 27 | } 28 | 29 | #[bench] 30 | fn bench_fq12_sub_assign(b: &mut ::test::Bencher) { 31 | const SAMPLES: usize = 1000; 32 | 33 | let mut rng = XorShiftRng::from_seed([ 34 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 35 | 0xe5, 36 | ]); 37 | 38 | let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) 39 | .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) 40 | .collect(); 41 | 42 | let mut count = 0; 43 | b.iter(|| { 44 | let mut tmp = v[count].0; 45 | tmp.sub_assign(&v[count].1); 46 | count = (count + 1) % SAMPLES; 47 | tmp 48 | }); 49 | } 50 | 51 | #[bench] 52 | fn bench_fq12_mul_assign(b: &mut ::test::Bencher) { 53 | const SAMPLES: usize = 1000; 54 | 55 | let mut rng = XorShiftRng::from_seed([ 56 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 57 | 0xe5, 58 | ]); 59 | 60 | let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) 61 | .map(|_| (Fq12::random(&mut rng), Fq12::random(&mut rng))) 62 | .collect(); 63 | 64 | let mut count = 0; 65 | b.iter(|| { 66 | let mut tmp = v[count].0; 67 | tmp.mul_assign(&v[count].1); 68 | count = (count + 1) % SAMPLES; 69 | tmp 70 | }); 71 | } 72 | 73 | #[bench] 74 | fn bench_fq12_squaring(b: &mut ::test::Bencher) { 75 | const SAMPLES: usize = 1000; 76 | 77 | let mut rng = XorShiftRng::from_seed([ 78 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 79 | 0xe5, 80 | ]); 81 | 82 | let v: Vec = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect(); 83 | 84 | let mut count = 0; 85 | b.iter(|| { 86 | let mut tmp = v[count]; 87 | tmp.square(); 88 | count = (count + 1) % SAMPLES; 89 | tmp 90 | }); 91 | } 92 | 93 | #[bench] 94 | fn bench_fq12_inverse(b: &mut ::test::Bencher) { 95 | const SAMPLES: usize = 1000; 96 | 97 | let mut rng = XorShiftRng::from_seed([ 98 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 99 | 0xe5, 100 | ]); 101 | 102 | let v: Vec = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect(); 103 | 104 | let mut count = 0; 105 | b.iter(|| { 106 | let tmp = v[count].inverse(); 107 | count = (count + 1) % SAMPLES; 108 | tmp 109 | }); 110 | } 111 | -------------------------------------------------------------------------------- /benches/bls12_381/fq2.rs: -------------------------------------------------------------------------------- 1 | use rand_core::SeedableRng; 2 | use rand_xorshift::XorShiftRng; 3 | 4 | use fff::{Field, SqrtField}; 5 | use paired::bls12_381::*; 6 | 7 | #[bench] 8 | fn bench_fq2_add_assign(b: &mut ::test::Bencher) { 9 | const SAMPLES: usize = 1000; 10 | 11 | let mut rng = XorShiftRng::from_seed([ 12 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 13 | 0xe5, 14 | ]); 15 | 16 | let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) 17 | .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) 18 | .collect(); 19 | 20 | let mut count = 0; 21 | b.iter(|| { 22 | let mut tmp = v[count].0; 23 | tmp.add_assign(&v[count].1); 24 | count = (count + 1) % SAMPLES; 25 | tmp 26 | }); 27 | } 28 | 29 | #[bench] 30 | fn bench_fq2_sub_assign(b: &mut ::test::Bencher) { 31 | const SAMPLES: usize = 1000; 32 | 33 | let mut rng = XorShiftRng::from_seed([ 34 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 35 | 0xe5, 36 | ]); 37 | 38 | let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) 39 | .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) 40 | .collect(); 41 | 42 | let mut count = 0; 43 | b.iter(|| { 44 | let mut tmp = v[count].0; 45 | tmp.sub_assign(&v[count].1); 46 | count = (count + 1) % SAMPLES; 47 | tmp 48 | }); 49 | } 50 | 51 | #[bench] 52 | fn bench_fq2_mul_assign(b: &mut ::test::Bencher) { 53 | const SAMPLES: usize = 1000; 54 | 55 | let mut rng = XorShiftRng::from_seed([ 56 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 57 | 0xe5, 58 | ]); 59 | 60 | let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) 61 | .map(|_| (Fq2::random(&mut rng), Fq2::random(&mut rng))) 62 | .collect(); 63 | 64 | let mut count = 0; 65 | b.iter(|| { 66 | let mut tmp = v[count].0; 67 | tmp.mul_assign(&v[count].1); 68 | count = (count + 1) % SAMPLES; 69 | tmp 70 | }); 71 | } 72 | 73 | #[bench] 74 | fn bench_fq2_squaring(b: &mut ::test::Bencher) { 75 | const SAMPLES: usize = 1000; 76 | 77 | let mut rng = XorShiftRng::from_seed([ 78 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 79 | 0xe5, 80 | ]); 81 | 82 | let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); 83 | 84 | let mut count = 0; 85 | b.iter(|| { 86 | let mut tmp = v[count]; 87 | tmp.square(); 88 | count = (count + 1) % SAMPLES; 89 | tmp 90 | }); 91 | } 92 | 93 | #[bench] 94 | fn bench_fq2_inverse(b: &mut ::test::Bencher) { 95 | const SAMPLES: usize = 1000; 96 | 97 | let mut rng = XorShiftRng::from_seed([ 98 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 99 | 0xe5, 100 | ]); 101 | 102 | let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); 103 | 104 | let mut count = 0; 105 | b.iter(|| { 106 | let tmp = v[count].inverse(); 107 | count = (count + 1) % SAMPLES; 108 | tmp 109 | }); 110 | } 111 | 112 | #[bench] 113 | fn bench_fq2_sqrt(b: &mut ::test::Bencher) { 114 | const SAMPLES: usize = 1000; 115 | 116 | let mut rng = XorShiftRng::from_seed([ 117 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 118 | 0xe5, 119 | ]); 120 | 121 | let v: Vec = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect(); 122 | 123 | let mut count = 0; 124 | b.iter(|| { 125 | let tmp = v[count].sqrt(); 126 | count = (count + 1) % SAMPLES; 127 | tmp 128 | }); 129 | } 130 | -------------------------------------------------------------------------------- /benches/bls12_381/fr.rs: -------------------------------------------------------------------------------- 1 | use rand_core::SeedableRng; 2 | use rand_xorshift::XorShiftRng; 3 | 4 | use fff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; 5 | use paired::bls12_381::*; 6 | 7 | #[bench] 8 | fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) { 9 | const SAMPLES: usize = 1000; 10 | 11 | let mut rng = XorShiftRng::from_seed([ 12 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 13 | 0xe5, 14 | ]); 15 | 16 | let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) 17 | .map(|_| { 18 | let mut tmp1 = Fr::random(&mut rng).into_repr(); 19 | let mut tmp2 = Fr::random(&mut rng).into_repr(); 20 | // Shave a few bits off to avoid overflow. 21 | for _ in 0..3 { 22 | tmp1.div2(); 23 | tmp2.div2(); 24 | } 25 | (tmp1, tmp2) 26 | }) 27 | .collect(); 28 | 29 | let mut count = 0; 30 | b.iter(|| { 31 | let mut tmp = v[count].0; 32 | tmp.add_nocarry(&v[count].1); 33 | count = (count + 1) % SAMPLES; 34 | tmp 35 | }); 36 | } 37 | 38 | #[bench] 39 | fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) { 40 | const SAMPLES: usize = 1000; 41 | 42 | let mut rng = XorShiftRng::from_seed([ 43 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 44 | 0xe5, 45 | ]); 46 | 47 | let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) 48 | .map(|_| { 49 | let tmp1 = Fr::random(&mut rng).into_repr(); 50 | let mut tmp2 = tmp1; 51 | // Ensure tmp2 is smaller than tmp1. 52 | for _ in 0..10 { 53 | tmp2.div2(); 54 | } 55 | (tmp1, tmp2) 56 | }) 57 | .collect(); 58 | 59 | let mut count = 0; 60 | b.iter(|| { 61 | let mut tmp = v[count].0; 62 | tmp.sub_noborrow(&v[count].1); 63 | count = (count + 1) % SAMPLES; 64 | tmp 65 | }); 66 | } 67 | 68 | #[bench] 69 | fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) { 70 | const SAMPLES: usize = 1000; 71 | 72 | let mut rng = XorShiftRng::from_seed([ 73 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 74 | 0xe5, 75 | ]); 76 | 77 | let v: Vec = (0..SAMPLES) 78 | .map(|_| Fr::random(&mut rng).into_repr()) 79 | .collect(); 80 | 81 | let mut count = 0; 82 | b.iter(|| { 83 | let tmp = v[count].num_bits(); 84 | count = (count + 1) % SAMPLES; 85 | tmp 86 | }); 87 | } 88 | 89 | #[bench] 90 | fn bench_fr_repr_mul2(b: &mut ::test::Bencher) { 91 | const SAMPLES: usize = 1000; 92 | 93 | let mut rng = XorShiftRng::from_seed([ 94 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 95 | 0xe5, 96 | ]); 97 | 98 | let v: Vec = (0..SAMPLES) 99 | .map(|_| Fr::random(&mut rng).into_repr()) 100 | .collect(); 101 | 102 | let mut count = 0; 103 | b.iter(|| { 104 | let mut tmp = v[count]; 105 | tmp.mul2(); 106 | count = (count + 1) % SAMPLES; 107 | tmp 108 | }); 109 | } 110 | 111 | #[bench] 112 | fn bench_fr_repr_div2(b: &mut ::test::Bencher) { 113 | const SAMPLES: usize = 1000; 114 | 115 | let mut rng = XorShiftRng::from_seed([ 116 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 117 | 0xe5, 118 | ]); 119 | 120 | let v: Vec = (0..SAMPLES) 121 | .map(|_| Fr::random(&mut rng).into_repr()) 122 | .collect(); 123 | 124 | let mut count = 0; 125 | b.iter(|| { 126 | let mut tmp = v[count]; 127 | tmp.div2(); 128 | count = (count + 1) % SAMPLES; 129 | tmp 130 | }); 131 | } 132 | 133 | #[bench] 134 | fn bench_fr_add_assign(b: &mut ::test::Bencher) { 135 | const SAMPLES: usize = 1000; 136 | 137 | let mut rng = XorShiftRng::from_seed([ 138 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 139 | 0xe5, 140 | ]); 141 | 142 | let v: Vec<(Fr, Fr)> = (0..SAMPLES) 143 | .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) 144 | .collect(); 145 | 146 | let mut count = 0; 147 | b.iter(|| { 148 | let mut tmp = v[count].0; 149 | tmp.add_assign(&v[count].1); 150 | count = (count + 1) % SAMPLES; 151 | tmp 152 | }); 153 | } 154 | 155 | #[bench] 156 | fn bench_fr_sub_assign(b: &mut ::test::Bencher) { 157 | const SAMPLES: usize = 1000; 158 | 159 | let mut rng = XorShiftRng::from_seed([ 160 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 161 | 0xe5, 162 | ]); 163 | 164 | let v: Vec<(Fr, Fr)> = (0..SAMPLES) 165 | .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) 166 | .collect(); 167 | 168 | let mut count = 0; 169 | b.iter(|| { 170 | let mut tmp = v[count].0; 171 | tmp.sub_assign(&v[count].1); 172 | count = (count + 1) % SAMPLES; 173 | tmp 174 | }); 175 | } 176 | 177 | #[bench] 178 | fn bench_fr_mul_assign(b: &mut ::test::Bencher) { 179 | const SAMPLES: usize = 1000; 180 | 181 | let mut rng = XorShiftRng::from_seed([ 182 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 183 | 0xe5, 184 | ]); 185 | 186 | let v: Vec<(Fr, Fr)> = (0..SAMPLES) 187 | .map(|_| (Fr::random(&mut rng), Fr::random(&mut rng))) 188 | .collect(); 189 | 190 | let mut count = 0; 191 | b.iter(|| { 192 | let mut tmp = v[count].0; 193 | tmp.mul_assign(&v[count].1); 194 | count = (count + 1) % SAMPLES; 195 | tmp 196 | }); 197 | } 198 | 199 | #[bench] 200 | fn bench_fr_square(b: &mut ::test::Bencher) { 201 | const SAMPLES: usize = 1000; 202 | 203 | let mut rng = XorShiftRng::from_seed([ 204 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 205 | 0xe5, 206 | ]); 207 | 208 | let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); 209 | 210 | let mut count = 0; 211 | b.iter(|| { 212 | let mut tmp = v[count]; 213 | tmp.square(); 214 | count = (count + 1) % SAMPLES; 215 | tmp 216 | }); 217 | } 218 | 219 | #[bench] 220 | fn bench_fr_inverse(b: &mut ::test::Bencher) { 221 | const SAMPLES: usize = 1000; 222 | 223 | let mut rng = XorShiftRng::from_seed([ 224 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 225 | 0xe5, 226 | ]); 227 | 228 | let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); 229 | 230 | let mut count = 0; 231 | b.iter(|| { 232 | count = (count + 1) % SAMPLES; 233 | v[count].inverse() 234 | }); 235 | } 236 | 237 | #[bench] 238 | fn bench_fr_negate(b: &mut ::test::Bencher) { 239 | const SAMPLES: usize = 1000; 240 | 241 | let mut rng = XorShiftRng::from_seed([ 242 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 243 | 0xe5, 244 | ]); 245 | 246 | let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); 247 | 248 | let mut count = 0; 249 | b.iter(|| { 250 | let mut tmp = v[count]; 251 | tmp.negate(); 252 | count = (count + 1) % SAMPLES; 253 | tmp 254 | }); 255 | } 256 | 257 | #[bench] 258 | fn bench_fr_sqrt(b: &mut ::test::Bencher) { 259 | const SAMPLES: usize = 1000; 260 | 261 | let mut rng = XorShiftRng::from_seed([ 262 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 263 | 0xe5, 264 | ]); 265 | 266 | let v: Vec = (0..SAMPLES) 267 | .map(|_| { 268 | let mut tmp = Fr::random(&mut rng); 269 | tmp.square(); 270 | tmp 271 | }) 272 | .collect(); 273 | 274 | let mut count = 0; 275 | b.iter(|| { 276 | count = (count + 1) % SAMPLES; 277 | v[count].sqrt() 278 | }); 279 | } 280 | 281 | #[bench] 282 | fn bench_fr_into_repr(b: &mut ::test::Bencher) { 283 | const SAMPLES: usize = 1000; 284 | 285 | let mut rng = XorShiftRng::from_seed([ 286 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 287 | 0xe5, 288 | ]); 289 | 290 | let v: Vec = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect(); 291 | 292 | let mut count = 0; 293 | b.iter(|| { 294 | count = (count + 1) % SAMPLES; 295 | v[count].into_repr() 296 | }); 297 | } 298 | 299 | #[bench] 300 | fn bench_fr_from_repr(b: &mut ::test::Bencher) { 301 | const SAMPLES: usize = 1000; 302 | 303 | let mut rng = XorShiftRng::from_seed([ 304 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 305 | 0xe5, 306 | ]); 307 | 308 | let v: Vec = (0..SAMPLES) 309 | .map(|_| Fr::random(&mut rng).into_repr()) 310 | .collect(); 311 | 312 | let mut count = 0; 313 | b.iter(|| { 314 | count = (count + 1) % SAMPLES; 315 | Fr::from_repr(v[count]) 316 | }); 317 | } 318 | -------------------------------------------------------------------------------- /benches/bls12_381/mod.rs: -------------------------------------------------------------------------------- 1 | mod ec; 2 | mod fq; 3 | mod fq12; 4 | mod fq2; 5 | mod fr; 6 | 7 | use rand_core::SeedableRng; 8 | use rand_xorshift::XorShiftRng; 9 | 10 | use groupy::CurveProjective; 11 | use paired::bls12_381::*; 12 | use paired::{Engine, PairingCurveAffine}; 13 | 14 | #[bench] 15 | fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) { 16 | const SAMPLES: usize = 1000; 17 | 18 | let mut rng = XorShiftRng::from_seed([ 19 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 20 | 0xe5, 21 | ]); 22 | 23 | let v: Vec = (0..SAMPLES).map(|_| G1::random(&mut rng)).collect(); 24 | 25 | let mut count = 0; 26 | b.iter(|| { 27 | let tmp = G1Affine::from(v[count]).prepare(); 28 | count = (count + 1) % SAMPLES; 29 | tmp 30 | }); 31 | } 32 | 33 | #[bench] 34 | fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) { 35 | const SAMPLES: usize = 1000; 36 | 37 | let mut rng = XorShiftRng::from_seed([ 38 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 39 | 0xe5, 40 | ]); 41 | 42 | let v: Vec = (0..SAMPLES).map(|_| G2::random(&mut rng)).collect(); 43 | 44 | let mut count = 0; 45 | b.iter(|| { 46 | let tmp = G2Affine::from(v[count]).prepare(); 47 | count = (count + 1) % SAMPLES; 48 | tmp 49 | }); 50 | } 51 | 52 | #[bench] 53 | fn bench_pairing_miller_loop(b: &mut ::test::Bencher) { 54 | const SAMPLES: usize = 1000; 55 | 56 | let mut rng = XorShiftRng::from_seed([ 57 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 58 | 0xe5, 59 | ]); 60 | 61 | let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES) 62 | .map(|_| { 63 | ( 64 | G1Affine::from(G1::random(&mut rng)).prepare(), 65 | G2Affine::from(G2::random(&mut rng)).prepare(), 66 | ) 67 | }) 68 | .collect(); 69 | 70 | let mut count = 0; 71 | b.iter(|| { 72 | let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]); 73 | count = (count + 1) % SAMPLES; 74 | tmp 75 | }); 76 | } 77 | 78 | #[bench] 79 | fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) { 80 | const SAMPLES: usize = 1000; 81 | 82 | let mut rng = XorShiftRng::from_seed([ 83 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 84 | 0xe5, 85 | ]); 86 | 87 | let v: Vec = (0..SAMPLES) 88 | .map(|_| { 89 | ( 90 | G1Affine::from(G1::random(&mut rng)).prepare(), 91 | G2Affine::from(G2::random(&mut rng)).prepare(), 92 | ) 93 | }) 94 | .map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)])) 95 | .collect(); 96 | 97 | let mut count = 0; 98 | b.iter(|| { 99 | let tmp = Bls12::final_exponentiation(&v[count]); 100 | count = (count + 1) % SAMPLES; 101 | tmp 102 | }); 103 | } 104 | 105 | #[bench] 106 | fn bench_pairing_full(b: &mut ::test::Bencher) { 107 | const SAMPLES: usize = 1000; 108 | 109 | let mut rng = XorShiftRng::from_seed([ 110 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 111 | 0xe5, 112 | ]); 113 | 114 | let v: Vec<(G1, G2)> = (0..SAMPLES) 115 | .map(|_| (G1::random(&mut rng), G2::random(&mut rng))) 116 | .collect(); 117 | 118 | let mut count = 0; 119 | b.iter(|| { 120 | let tmp = Bls12::pairing(v[count].0, v[count].1); 121 | count = (count + 1) % SAMPLES; 122 | tmp 123 | }); 124 | } 125 | -------------------------------------------------------------------------------- /benches/pairing_benches.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate fff; 4 | extern crate groupy; 5 | extern crate paired; 6 | extern crate rand_core; 7 | extern crate rand_xorshift; 8 | extern crate test; 9 | 10 | mod bls12_381; 11 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | pre-release-commit-message = "chore({{crate_name}}): release {{version}}" 2 | pro-release-commit-message = "chore({{crate_name}}): starting development cycle for {{next_version}}" 3 | no-dev-version = true -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | 1.46.0 2 | -------------------------------------------------------------------------------- /src/bls12_381/README.md: -------------------------------------------------------------------------------- 1 | # BLS12-381 2 | 3 | This is an implementation of the BLS12-381 pairing-friendly elliptic curve construction. 4 | 5 | ## BLS12 Parameterization 6 | 7 | BLS12 curves are parameterized by a value *x* such that the base field modulus *q* and subgroup *r* can be computed by: 8 | 9 | * q = (x - 1)2 ((x4 - x2 + 1) / 3) + x 10 | * r = (x4 - x2 + 1) 11 | 12 | Given primes *q* and *r* parameterized as above, we can easily construct an elliptic curve over the prime field F*q* which contains a subgroup of order *r* such that *r* | (*q*12 - 1), giving it an embedding degree of 12. Instantiating its sextic twist over an extension field Fq2 gives rise to an efficient bilinear pairing function between elements of the order *r* subgroups of either curves, into an order *r* multiplicative subgroup of Fq12. 13 | 14 | In zk-SNARK schemes, we require Fr with large 2n roots of unity for performing efficient fast-fourier transforms. As such, guaranteeing that large 2n | (r - 1), or equivalently that *x* has a large 2n factor, gives rise to BLS12 curves suitable for zk-SNARKs. 15 | 16 | Due to recent research, it is estimated by many that *q* should be approximately 384 bits to target 128-bit security. Conveniently, *r* is approximately 256 bits when *q* is approximately 384 bits, making BLS12 curves ideal for 128-bit security. It also makes them ideal for many zk-SNARK applications, as the scalar field can be used for keying material such as embedded curve constructions. 17 | 18 | Many curves match our descriptions, but we require some extra properties for efficiency purposes: 19 | 20 | * *q* should be smaller than 2383, and *r* should be smaller than 2255, so that the most significant bit is unset when using 64-bit or 32-bit limbs. This allows for cheap reductions. 21 | * Fq12 is typically constructed using towers of extension fields. As a byproduct of [research](https://eprint.iacr.org/2011/465.pdf) for BLS curves of embedding degree 24, we can identify subfamilies of BLS12 curves (for our purposes, where x mod 72 = {16, 64}) that produce efficient extension field towers and twisting isomorphisms. 22 | * We desire *x* of small Hamming weight, to increase the performance of the pairing function. 23 | 24 | ## BLS12-381 Instantiation 25 | 26 | The BLS12-381 construction is instantiated by `x = -0xd201000000010000`, which produces the largest `q` and smallest Hamming weight of `x` that meets the above requirements. This produces: 27 | 28 | * q = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` (381 bits) 29 | * r = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` (255 bits) 30 | 31 | Our extension field tower is constructed as follows: 32 | 33 | 1. Fq2 is constructed as Fq(u) / (u2 - β) where β = -1. 34 | 2. Fq6 is constructed as Fq2(v) / (v3 - ξ) where ξ = u + 1 35 | 3. Fq12 is constructed as Fq6(w) / (w2 - γ) where γ = v 36 | 37 | Now, we instantiate the elliptic curve E(Fq) : y2 = x3 + 4, and the elliptic curve E'(Fq2) : y2 = x3 + 4(u + 1). 38 | 39 | The group G1 is the *r* order subgroup of E, which has cofactor (x - 1)2 / 3. The group G2 is the *r* order subgroup of E', which has cofactor (x8 - 4x7 + 5x6 - 4x4 + 6x3 - 4x2 - 4x + 13) / 9. 40 | 41 | ### Generators 42 | 43 | The generators of G1 and G2 are computed by finding the lexicographically smallest valid `x`-coordinate, and its lexicographically smallest `y`-coordinate and scaling it by the cofactor such that the result is not the point at infinity. 44 | 45 | #### G1 46 | 47 | ``` 48 | x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 49 | y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 50 | ``` 51 | 52 | #### G2 53 | 54 | ``` 55 | x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 56 | y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 57 | ``` 58 | 59 | ### Serialization 60 | 61 | * Fq elements are encoded in big-endian form. They occupy 48 bytes in this form. 62 | * Fq2 elements are encoded in big-endian form, meaning that the Fq element c0 + c1 * u is represented by the Fq element c1 followed by the Fq element c0. This means Fq2 elements occupy 96 bytes in this form. 63 | * The group G1 uses Fq elements for coordinates. The group G2 uses Fq2 elements for coordinates. 64 | * G1 and G2 elements can be encoded in uncompressed form (the x-coordinate followed by the y-coordinate) or in compressed form (just the x-coordinate). G1 elements occupy 96 bytes in uncompressed form, and 48 bytes in compressed form. G2 elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed form. 65 | 66 | The most-significant three bits of a G1 or G2 encoding should be masked away before the coordinate(s) are interpreted. These bits are used to unambiguously represent the underlying element: 67 | 68 | * The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form. 69 | * The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero. 70 | * The third-most significant bit is set if (and only if) this point is in compressed form _and_ it is not the point at infinity _and_ its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate. 71 | 72 | ### Hashing to the Curve 73 | 74 | * Elements of Fq are encoded by taking the output of a BLAKE2b digest and interpreting it as a big-endian integer. The integer is reduced (mod q), making it uniform in the field with negligible bias. 75 | * Elements of Fq2 are encoded by appending "_c0" to the supplied BLAKE2b preimage, to compute the encoding to Fq for c0, and by appending "_c1" to the same BLAKE2b preimage, to compute the encoding to Fq for c1. 76 | * Elements of E and E' are encoded from an element *t* by taking the first valid abscissa (for the b in each respective curve): 77 | * x_1 = (-1 + sqrt(-3))/2 - (sqrt(-3) * t^2)/(1 + b + t^2) 78 | * x_2 = (-1 - sqrt(-3))/2 + (sqrt(-3) * t^2)/(1 + b + t^2) 79 | * x_3 = 1 - (1 + b + t^2)^2 / (3 * t^2) 80 | * In this encoding, we always map t=0 to the point at infinity. For the encoding to E, We also map: 81 | * `t = 0x019cfaba0c258165d092f6bca9a081871e62a126c499340dc71c0e9527f923f3b299592a7a9503066cc5362484d96dd7` to the fixed generator. (See "Generators" above.) 82 | * `t = 0x186417302d5a65347a88b0f999ab2b504614aa5e2eebdeb1a014c40bceb7d2306c12a6d436befcf94d39c9db7b263cd4` to the negative of the fixed generator. (See "Generators" above.) 83 | * In this encoding, the y-coordinate of the resulting point is chosen to be the lexicographically largest if (and only if) the input `t` is also lexicographically larger than its negative. 84 | * Hashing to G1, given a message `msg`, involves hashing to Fq by supplying the BLAKE2b preimage `msg | "G1_0"` to create `t0`, and hashing to Fq by supplying the BLAKE2b preimage `msg | "G1_1"` to create `t1`. `t0` and `t1` are used to encode into E via the above encoding, the resulting points are added together, and that result is multiplied by the cofactor. 85 | * Hashing to G2, given a message `msg`, involves hashing to Fq2 by supplying the BLAKE2b preimage `msg | "G2_0"` to create `t0`, and hashing to Fq2 by supplying the BLAKE2b preimage `msg | "G2_1"` to create `t1`. `t0` and `t1` are used to encode into E' via the above encoding, the resulting points are added together, and that result is multiplied by the cofactor. 86 | -------------------------------------------------------------------------------- /src/bls12_381/ec/mod.rs: -------------------------------------------------------------------------------- 1 | macro_rules! curve_impl { 2 | ( 3 | $name:expr, 4 | $projective:ident, 5 | $affine:ident, 6 | $prepared:ident, 7 | $basefield:ident, 8 | $scalarfield:ident, 9 | $uncompressed:ident, 10 | $compressed:ident, 11 | $pairing:ident, 12 | $iso_1:expr, 13 | $iso_2:expr 14 | ) => { 15 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 16 | pub struct $affine { 17 | pub(crate) x: $basefield, 18 | pub(crate) y: $basefield, 19 | pub(crate) infinity: bool, 20 | } 21 | 22 | impl ::std::fmt::Display for $affine { 23 | fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { 24 | if self.infinity { 25 | write!(f, "{}(Infinity)", $name) 26 | } else { 27 | write!(f, "{}(x={}, y={})", $name, self.x, self.y) 28 | } 29 | } 30 | } 31 | 32 | fn y2_from_x(x: $basefield) -> $basefield { 33 | let mut y2 = x.clone(); 34 | y2.square(); 35 | y2.mul_assign(&x); 36 | y2.add_assign(&$affine::get_coeff_b()); 37 | y2 38 | } 39 | 40 | #[derive(Copy, Clone, Debug, Eq)] 41 | pub struct $projective { 42 | pub(crate) x: $basefield, 43 | pub(crate) y: $basefield, 44 | pub(crate) z: $basefield, 45 | } 46 | 47 | impl ::std::fmt::Display for $projective { 48 | fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { 49 | write!(f, "{}", self.into_affine()) 50 | } 51 | } 52 | 53 | impl PartialEq for $projective { 54 | fn eq(&self, other: &$projective) -> bool { 55 | if self.is_zero() { 56 | return other.is_zero(); 57 | } 58 | if other.is_zero() { 59 | return false; 60 | } 61 | 62 | if self.is_normalized() { 63 | if other.is_normalized() { 64 | return self.into_affine() == other.into_affine(); 65 | } 66 | } 67 | 68 | // The points (X, Y, Z) and (X', Y', Z') 69 | // are equal when (X * Z^2) = (X' * Z'^2) 70 | // and (Y * Z^3) = (Y' * Z'^3). 71 | 72 | let mut z1 = self.z; 73 | z1.square(); 74 | let mut z2 = other.z; 75 | z2.square(); 76 | 77 | let mut tmp1 = self.x; 78 | tmp1.mul_assign(&z2); 79 | 80 | let mut tmp2 = other.x; 81 | tmp2.mul_assign(&z1); 82 | 83 | if tmp1 != tmp2 { 84 | return false; 85 | } 86 | 87 | z1.mul_assign(&self.z); 88 | z2.mul_assign(&other.z); 89 | z2.mul_assign(&self.y); 90 | z1.mul_assign(&other.y); 91 | 92 | if z1 != z2 { 93 | return false; 94 | } 95 | 96 | true 97 | } 98 | } 99 | 100 | impl $projective { 101 | /// Generic isogeny evaluation function. 102 | pub(crate) fn eval_iso(&mut self, coeffs: [&[$basefield]; 4]) { 103 | // Rust (still) can't handle generic array sizes (issue #43408) 104 | let mut tmp = [$basefield::zero(); $iso_1]; 105 | let mut mapvals = [$basefield::zero(); 4]; 106 | // scope for pt borrow 107 | { 108 | // unpack input point 109 | let x = &self.x; 110 | let y = &self.y; 111 | let z = &self.z; 112 | 113 | // precompute powers of z 114 | let zpows = { 115 | let mut zpows = [$basefield::zero(); $iso_2]; 116 | zpows[0] = *z; 117 | zpows[0].square(); // z^2 118 | zpows[1] = zpows[0]; 119 | zpows[1].square(); // z^4 120 | { 121 | let (z_squared, rest) = zpows.split_at_mut(1); 122 | for idx in 1..coeffs[2].len() - 2 { 123 | if idx % 2 == 0 { 124 | rest[idx] = rest[idx / 2 - 1]; 125 | rest[idx].square(); 126 | } else { 127 | rest[idx] = rest[idx - 1]; 128 | rest[idx].mul_assign(&z_squared[0]); 129 | } 130 | } 131 | } 132 | zpows 133 | }; 134 | 135 | for idx in 0..4 { 136 | let clen = coeffs[idx].len() - 1; 137 | // multiply coeffs by powers of Z 138 | for jdx in 0..clen { 139 | tmp[jdx] = coeffs[idx][clen - 1 - jdx]; 140 | tmp[jdx].mul_assign(&zpows[jdx]); 141 | } 142 | // compute map value by Horner's rule 143 | mapvals[idx] = coeffs[idx][clen]; 144 | for tmpval in &tmp[..clen] { 145 | mapvals[idx].mul_assign(x); 146 | mapvals[idx].add_assign(tmpval); 147 | } 148 | } 149 | 150 | // x denominator is order 1 less than x numerator, so we need an extra factor of Z^2 151 | mapvals[1].mul_assign(&zpows[0]); 152 | 153 | // multiply result of Y map by the y-coord, y / z^3 154 | mapvals[2].mul_assign(y); 155 | mapvals[3].mul_assign(z); 156 | mapvals[3].mul_assign(&zpows[0]); 157 | } // pt is no longer borrowed here 158 | 159 | // hack to simultaneously access elements of tmp 160 | let (xx, yy, zz) = { 161 | let (xx, rest) = tmp.split_at_mut(1); 162 | let (yy, rest) = rest.split_at_mut(1); 163 | (&mut xx[0], &mut yy[0], &mut rest[0]) 164 | }; 165 | 166 | // compute Jacobian coordinates of resulting point 167 | *zz = mapvals[1]; 168 | zz.mul_assign(&mapvals[3]); // Zout = xden * yden 169 | 170 | *xx = mapvals[0]; 171 | xx.mul_assign(&mapvals[3]); // xnum * yden 172 | xx.mul_assign(zz); // xnum * xden * yden^2 173 | 174 | *yy = *zz; 175 | yy.square(); // xden^2 * yden^2 176 | yy.mul_assign(&mapvals[2]); // ynum * xden^2 * yden^2 177 | yy.mul_assign(&mapvals[1]); // ynum * xden^3 * yden^2 178 | 179 | self.x = *xx; 180 | self.y = *yy; 181 | self.z = *zz; 182 | } 183 | } 184 | 185 | impl $affine { 186 | fn mul_bits>(&self, bits: BitIterator) -> $projective { 187 | let mut res = $projective::zero(); 188 | for i in bits { 189 | res.double(); 190 | if i { 191 | res.add_assign_mixed(self) 192 | } 193 | } 194 | res 195 | } 196 | 197 | /// Attempts to construct an affine point given an x-coordinate. The 198 | /// point is not guaranteed to be in the prime order subgroup. 199 | /// 200 | /// If and only if `greatest` is set will the lexicographically 201 | /// largest y-coordinate be selected. 202 | fn get_point_from_x(x: $basefield, greatest: bool) -> Option<$affine> { 203 | // Compute x^3 + b 204 | let mut x3b = x; 205 | x3b.square(); 206 | x3b.mul_assign(&x); 207 | x3b.add_assign(&$affine::get_coeff_b()); 208 | 209 | x3b.sqrt().map(|y| { 210 | let mut negy = y; 211 | negy.negate(); 212 | 213 | $affine { 214 | x, 215 | y: if (y < negy) ^ greatest { y } else { negy }, 216 | infinity: false, 217 | } 218 | }) 219 | } 220 | 221 | fn is_on_curve(&self) -> bool { 222 | if self.is_zero() { 223 | true 224 | } else { 225 | // Check that the point is on the curve 226 | let mut y2 = self.y; 227 | y2.square(); 228 | 229 | y2 == y2_from_x(self.x) 230 | } 231 | } 232 | 233 | fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { 234 | self.mul($scalarfield::char()).is_zero() 235 | } 236 | 237 | /// Implements the Shallue–van de Woestijne encoding described in 238 | /// Section 3, "Indifferentiable Hashing to Barreto–Naehrig Curves" 239 | /// from Foque-Tibouchi: . 240 | /// 241 | /// The encoding is adapted for BLS12-381. 242 | /// 243 | /// This encoding produces a point in E/E'. It does not reach every 244 | /// point. The resulting point may not be in the prime order subgroup, 245 | /// but it will be on the curve. It could be the point at infinity. 246 | /// 247 | /// ## Description 248 | /// 249 | /// Lemma 3 gives us three points: 250 | /// 251 | /// x_1 = (-1 + sqrt(-3))/2 - (sqrt(-3) * t^2)/(1 + b + t^2) 252 | /// x_2 = (-1 - sqrt(-3))/2 + (sqrt(-3) * t^2)/(1 + b + t^2) 253 | /// x_3 = 1 - (1 + b + t^2)^2/(3 * t^2) 254 | /// 255 | /// Given t != 0 and t != 1 + b + t^2 != 0, at least one of 256 | /// these three points (x1, x2, x3) is valid on the curve. 257 | /// 258 | /// In the paper, 1 + b + t^2 != 0 has no solutions, but for 259 | /// E(Fq) in our construction, it does have two solutions. 260 | /// We follow the convention of the paper by mapping these 261 | /// to some arbitrary points; in our case, the positive/negative 262 | /// fixed generator (with the parity of the y-coordinate 263 | /// corresponding to the t value). 264 | /// 265 | /// Unlike the paper, which maps t = 0 to an arbitrary point, 266 | /// we map it to the point at infinity. This arrangement allows 267 | /// us to preserve sw_encode(t) = sw_encode(-t) for all t. 268 | /// 269 | /// We choose the smallest i such that x_i is on the curve. 270 | /// We choose the corresponding y-coordinate with the same 271 | /// parity, defined as the point being lexicographically larger 272 | /// than its negative. 273 | fn sw_encode(t: $basefield) -> Self { 274 | // Handle the case t == 0 275 | if t.is_zero() { 276 | return Self::zero(); 277 | } 278 | 279 | // We choose the corresponding y-coordinate with the same parity as t. 280 | let parity = t.parity(); 281 | 282 | // w = (t^2 + b + 1)^(-1) * sqrt(-3) * t 283 | let mut w = t; 284 | w.square(); 285 | w.add_assign(&$affine::get_coeff_b()); 286 | w.add_assign(&$basefield::one()); 287 | 288 | // Handle the case t^2 + b + 1 == 0 289 | if w.is_zero() { 290 | let mut ret = Self::one(); 291 | if parity { 292 | ret.negate() 293 | } 294 | return ret; 295 | } 296 | 297 | w = w.inverse().unwrap(); 298 | w.mul_assign(&$basefield::get_swenc_sqrt_neg_three()); 299 | w.mul_assign(&t); 300 | 301 | // x1 = - wt + (sqrt(-3) - 1) / 2 302 | let mut x1 = w; 303 | x1.mul_assign(&t); 304 | x1.negate(); 305 | x1.add_assign(&$basefield::get_swenc_sqrt_neg_three_minus_one_div_two()); 306 | if let Some(p) = Self::get_point_from_x(x1, parity) { 307 | return p; 308 | } 309 | 310 | // x2 = -1 - x1 311 | let mut x2 = x1; 312 | x2.negate(); 313 | x2.sub_assign(&$basefield::one()); 314 | if let Some(p) = Self::get_point_from_x(x2, parity) { 315 | return p; 316 | } 317 | 318 | // x3 = 1/w^2 + 1 319 | let mut x3 = w; 320 | x3.square(); 321 | x3 = x3.inverse().unwrap(); 322 | x3.add_assign(&$basefield::one()); 323 | Self::get_point_from_x(x3, parity) 324 | .expect("this point must be valid if the other two are not") 325 | } 326 | } 327 | 328 | impl CurveAffine for $affine { 329 | type Engine = Bls12; 330 | type Scalar = $scalarfield; 331 | type Base = $basefield; 332 | type Projective = $projective; 333 | type Uncompressed = $uncompressed; 334 | type Compressed = $compressed; 335 | 336 | fn zero() -> Self { 337 | $affine { 338 | x: $basefield::zero(), 339 | y: $basefield::one(), 340 | infinity: true, 341 | } 342 | } 343 | 344 | fn one() -> Self { 345 | Self::get_generator() 346 | } 347 | 348 | fn is_zero(&self) -> bool { 349 | self.infinity 350 | } 351 | 352 | fn mul::Repr>>(&self, by: S) -> $projective { 353 | let bits = BitIterator::new(by.into()); 354 | self.mul_bits(bits) 355 | } 356 | 357 | fn negate(&mut self) { 358 | if !self.is_zero() { 359 | self.y.negate(); 360 | } 361 | } 362 | 363 | fn into_projective(&self) -> $projective { 364 | (*self).into() 365 | } 366 | } 367 | 368 | impl PairingCurveAffine for $affine { 369 | type Prepared = $prepared; 370 | type Pair = $pairing; 371 | type PairingResult = Fq12; 372 | 373 | fn prepare(&self) -> Self::Prepared { 374 | $prepared::from_affine(*self) 375 | } 376 | 377 | fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { 378 | self.perform_pairing(other) 379 | } 380 | } 381 | 382 | impl CurveProjective for $projective { 383 | type Engine = Bls12; 384 | type Scalar = $scalarfield; 385 | type Base = $basefield; 386 | type Affine = $affine; 387 | 388 | fn random(rng: &mut R) -> Self { 389 | loop { 390 | let x = $basefield::random(rng); 391 | let greatest = rng.next_u32() % 2 != 0; 392 | 393 | if let Some(p) = $affine::get_point_from_x(x, greatest) { 394 | let p = p.scale_by_cofactor(); 395 | 396 | if !p.is_zero() { 397 | return p; 398 | } 399 | } 400 | } 401 | } 402 | 403 | // The point at infinity is always represented by 404 | // Z = 0. 405 | fn zero() -> Self { 406 | $projective { 407 | x: $basefield::zero(), 408 | y: $basefield::one(), 409 | z: $basefield::zero(), 410 | } 411 | } 412 | 413 | fn one() -> Self { 414 | $affine::one().into() 415 | } 416 | 417 | // The point at infinity is always represented by 418 | // Z = 0. 419 | fn is_zero(&self) -> bool { 420 | self.z.is_zero() 421 | } 422 | 423 | fn is_normalized(&self) -> bool { 424 | self.is_zero() || self.z == $basefield::one() 425 | } 426 | 427 | fn batch_normalization>(v: &mut [S]) { 428 | // Montgomery’s Trick and Fast Implementation of Masked AES 429 | // Genelle, Prouff and Quisquater 430 | // Section 3.2 431 | 432 | // First pass: compute [a, ab, abc, ...] 433 | let mut prod = Vec::with_capacity(v.len()); 434 | let mut tmp = $basefield::one(); 435 | for g in v 436 | .iter_mut() 437 | .map(|g| g.borrow_mut()) 438 | // Ignore normalized elements 439 | .filter(|g| !g.is_normalized()) 440 | { 441 | tmp.mul_assign(&g.z); 442 | prod.push(tmp); 443 | } 444 | 445 | // Invert `tmp`. 446 | tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero. 447 | 448 | // Second pass: iterate backwards to compute inverses 449 | for (g, s) in v 450 | .iter_mut() 451 | .map(|g| g.borrow_mut()) 452 | // Backwards 453 | .rev() 454 | // Ignore normalized elements 455 | .filter(|g| !g.is_normalized()) 456 | // Backwards, skip last element, fill in one for last term. 457 | .zip( 458 | prod.into_iter() 459 | .rev() 460 | .skip(1) 461 | .chain(Some($basefield::one())), 462 | ) 463 | { 464 | // tmp := tmp * g.z; g.z := tmp * s = 1/z 465 | let mut newtmp = tmp; 466 | newtmp.mul_assign(&g.z); 467 | g.z = tmp; 468 | g.z.mul_assign(&s); 469 | tmp = newtmp; 470 | } 471 | 472 | // Perform affine transformations 473 | for g in v 474 | .iter_mut() 475 | .map(|g| g.borrow_mut()) 476 | .filter(|g| !g.is_normalized()) 477 | { 478 | let mut z = g.z; // 1/z 479 | z.square(); // 1/z^2 480 | g.x.mul_assign(&z); // x/z^2 481 | z.mul_assign(&g.z); // 1/z^3 482 | g.y.mul_assign(&z); // y/z^3 483 | g.z = $basefield::one(); // z = 1 484 | } 485 | } 486 | 487 | fn double(&mut self) { 488 | if self.is_zero() { 489 | return; 490 | } 491 | 492 | // Other than the point at infinity, no points on E or E' 493 | // can double to equal the point at infinity, as y=0 is 494 | // never true for points on the curve. (-4 and -4u-4 495 | // are not cubic residue in their respective fields.) 496 | 497 | // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l 498 | 499 | // A = X1^2 500 | let mut a = self.x; 501 | a.square(); 502 | 503 | // B = Y1^2 504 | let mut b = self.y; 505 | b.square(); 506 | 507 | // C = B^2 508 | let mut c = b; 509 | c.square(); 510 | 511 | // D = 2*((X1+B)2-A-C) 512 | let mut d = self.x; 513 | d.add_assign(&b); 514 | d.square(); 515 | d.sub_assign(&a); 516 | d.sub_assign(&c); 517 | d.double(); 518 | 519 | // E = 3*A 520 | let mut e = a; 521 | e.double(); 522 | e.add_assign(&a); 523 | 524 | // F = E^2 525 | let mut f = e; 526 | f.square(); 527 | 528 | // Z3 = 2*Y1*Z1 529 | self.z.mul_assign(&self.y); 530 | self.z.double(); 531 | 532 | // X3 = F-2*D 533 | self.x = f; 534 | self.x.sub_assign(&d); 535 | self.x.sub_assign(&d); 536 | 537 | // Y3 = E*(D-X3)-8*C 538 | self.y = d; 539 | self.y.sub_assign(&self.x); 540 | self.y.mul_assign(&e); 541 | c.double(); 542 | c.double(); 543 | c.double(); 544 | self.y.sub_assign(&c); 545 | } 546 | 547 | fn add_assign(&mut self, other: &Self) { 548 | if self.is_zero() { 549 | *self = *other; 550 | return; 551 | } 552 | 553 | if other.is_zero() { 554 | return; 555 | } 556 | 557 | // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl 558 | 559 | // Z1Z1 = Z1^2 560 | let mut z1z1 = self.z; 561 | z1z1.square(); 562 | 563 | // Z2Z2 = Z2^2 564 | let mut z2z2 = other.z; 565 | z2z2.square(); 566 | 567 | // U1 = X1*Z2Z2 568 | let mut u1 = self.x; 569 | u1.mul_assign(&z2z2); 570 | 571 | // U2 = X2*Z1Z1 572 | let mut u2 = other.x; 573 | u2.mul_assign(&z1z1); 574 | 575 | // S1 = Y1*Z2*Z2Z2 576 | let mut s1 = self.y; 577 | s1.mul_assign(&other.z); 578 | s1.mul_assign(&z2z2); 579 | 580 | // S2 = Y2*Z1*Z1Z1 581 | let mut s2 = other.y; 582 | s2.mul_assign(&self.z); 583 | s2.mul_assign(&z1z1); 584 | 585 | if u1 == u2 && s1 == s2 { 586 | // The two points are equal, so we double. 587 | self.double(); 588 | } else { 589 | // If we're adding -a and a together, self.z becomes zero as H becomes zero. 590 | 591 | // H = U2-U1 592 | let mut h = u2; 593 | h.sub_assign(&u1); 594 | 595 | // I = (2*H)^2 596 | let mut i = h; 597 | i.double(); 598 | i.square(); 599 | 600 | // J = H*I 601 | let mut j = h; 602 | j.mul_assign(&i); 603 | 604 | // r = 2*(S2-S1) 605 | let mut r = s2; 606 | r.sub_assign(&s1); 607 | r.double(); 608 | 609 | // V = U1*I 610 | let mut v = u1; 611 | v.mul_assign(&i); 612 | 613 | // X3 = r^2 - J - 2*V 614 | self.x = r; 615 | self.x.square(); 616 | self.x.sub_assign(&j); 617 | self.x.sub_assign(&v); 618 | self.x.sub_assign(&v); 619 | 620 | // Y3 = r*(V - X3) - 2*S1*J 621 | self.y = v; 622 | self.y.sub_assign(&self.x); 623 | self.y.mul_assign(&r); 624 | s1.mul_assign(&j); // S1 = S1 * J * 2 625 | s1.double(); 626 | self.y.sub_assign(&s1); 627 | 628 | // Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H 629 | self.z.add_assign(&other.z); 630 | self.z.square(); 631 | self.z.sub_assign(&z1z1); 632 | self.z.sub_assign(&z2z2); 633 | self.z.mul_assign(&h); 634 | } 635 | } 636 | 637 | fn add_assign_mixed(&mut self, other: &Self::Affine) { 638 | if other.is_zero() { 639 | return; 640 | } 641 | 642 | if self.is_zero() { 643 | self.x = other.x; 644 | self.y = other.y; 645 | self.z = $basefield::one(); 646 | return; 647 | } 648 | 649 | // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl 650 | 651 | // Z1Z1 = Z1^2 652 | let mut z1z1 = self.z; 653 | z1z1.square(); 654 | 655 | // U2 = X2*Z1Z1 656 | let mut u2 = other.x; 657 | u2.mul_assign(&z1z1); 658 | 659 | // S2 = Y2*Z1*Z1Z1 660 | let mut s2 = other.y; 661 | s2.mul_assign(&self.z); 662 | s2.mul_assign(&z1z1); 663 | 664 | if self.x == u2 && self.y == s2 { 665 | // The two points are equal, so we double. 666 | self.double(); 667 | } else { 668 | // If we're adding -a and a together, self.z becomes zero as H becomes zero. 669 | 670 | // H = U2-X1 671 | let mut h = u2; 672 | h.sub_assign(&self.x); 673 | 674 | // HH = H^2 675 | let mut hh = h; 676 | hh.square(); 677 | 678 | // I = 4*HH 679 | let mut i = hh; 680 | i.double(); 681 | i.double(); 682 | 683 | // J = H*I 684 | let mut j = h; 685 | j.mul_assign(&i); 686 | 687 | // r = 2*(S2-Y1) 688 | let mut r = s2; 689 | r.sub_assign(&self.y); 690 | r.double(); 691 | 692 | // V = X1*I 693 | let mut v = self.x; 694 | v.mul_assign(&i); 695 | 696 | // X3 = r^2 - J - 2*V 697 | self.x = r; 698 | self.x.square(); 699 | self.x.sub_assign(&j); 700 | self.x.sub_assign(&v); 701 | self.x.sub_assign(&v); 702 | 703 | // Y3 = r*(V-X3)-2*Y1*J 704 | j.mul_assign(&self.y); // J = 2*Y1*J 705 | j.double(); 706 | self.y = v; 707 | self.y.sub_assign(&self.x); 708 | self.y.mul_assign(&r); 709 | self.y.sub_assign(&j); 710 | 711 | // Z3 = (Z1+H)^2-Z1Z1-HH 712 | self.z.add_assign(&h); 713 | self.z.square(); 714 | self.z.sub_assign(&z1z1); 715 | self.z.sub_assign(&hh); 716 | } 717 | } 718 | 719 | fn negate(&mut self) { 720 | if !self.is_zero() { 721 | self.y.negate() 722 | } 723 | } 724 | 725 | fn mul_assign::Repr>>(&mut self, other: S) { 726 | let mut res = Self::zero(); 727 | 728 | let mut found_one = false; 729 | 730 | for i in BitIterator::new(other.into()) { 731 | if found_one { 732 | res.double(); 733 | } else { 734 | found_one = i; 735 | } 736 | 737 | if i { 738 | res.add_assign(self); 739 | } 740 | } 741 | 742 | *self = res; 743 | } 744 | 745 | fn into_affine(&self) -> $affine { 746 | (*self).into() 747 | } 748 | 749 | fn recommended_wnaf_for_scalar(scalar: ::Repr) -> usize { 750 | Self::empirical_recommended_wnaf_for_scalar(scalar) 751 | } 752 | 753 | fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { 754 | Self::empirical_recommended_wnaf_for_num_scalars(num_scalars) 755 | } 756 | 757 | /// Implements "Indifferentiable Hashing to Barreto–Naehrig Curves" from Foque-Tibouchi. 758 | /// 759 | fn hash(msg: &[u8]) -> Self { 760 | // The construction of Foque et al. requires us to construct two 761 | // "random oracles" in the field, encode their image with `sw_encode`, 762 | // and finally add them. 763 | // We construct them appending to the message the string 764 | // $name_$oracle 765 | // For instance, the first oracle in group G1 appends: "G1_0". 766 | let mut hasher_0 = blake2b_simd::State::new(); 767 | hasher_0.update(msg); 768 | #[allow(clippy::string_lit_as_bytes)] 769 | hasher_0.update($name.as_bytes()); 770 | let mut hasher_1 = hasher_0.clone(); 771 | 772 | hasher_0.update(b"_0"); 773 | let t0 = Self::Base::hash(hasher_0); 774 | let t0 = Self::Affine::sw_encode(t0); 775 | 776 | hasher_1.update(b"_1"); 777 | let t1 = Self::Base::hash(hasher_1); 778 | let t1 = Self::Affine::sw_encode(t1); 779 | 780 | let mut res = t0.into_projective(); 781 | res.add_assign_mixed(&t1); 782 | res.into_affine().scale_by_cofactor() 783 | } 784 | } 785 | 786 | // The affine point X, Y is represented in the jacobian 787 | // coordinates with Z = 1. 788 | impl From<$affine> for $projective { 789 | fn from(p: $affine) -> $projective { 790 | if p.is_zero() { 791 | $projective::zero() 792 | } else { 793 | $projective { 794 | x: p.x, 795 | y: p.y, 796 | z: $basefield::one(), 797 | } 798 | } 799 | } 800 | } 801 | 802 | // The projective point X, Y, Z is represented in the affine 803 | // coordinates as X/Z^2, Y/Z^3. 804 | impl From<$projective> for $affine { 805 | fn from(p: $projective) -> $affine { 806 | if p.is_zero() { 807 | $affine::zero() 808 | } else if p.z == $basefield::one() { 809 | // If Z is one, the point is already normalized. 810 | $affine { 811 | x: p.x, 812 | y: p.y, 813 | infinity: false, 814 | } 815 | } else { 816 | // Z is nonzero, so it must have an inverse in a field. 817 | let zinv = p.z.inverse().unwrap(); 818 | let mut zinv_powered = zinv; 819 | zinv_powered.square(); 820 | 821 | // X/Z^2 822 | let mut x = p.x; 823 | x.mul_assign(&zinv_powered); 824 | 825 | // Y/Z^3 826 | let mut y = p.y; 827 | zinv_powered.mul_assign(&zinv); 828 | y.mul_assign(&zinv_powered); 829 | 830 | $affine { 831 | x, 832 | y, 833 | infinity: false, 834 | } 835 | } 836 | } 837 | } 838 | 839 | #[cfg(test)] 840 | use rand_core::SeedableRng; 841 | #[cfg(test)] 842 | use rand_xorshift::XorShiftRng; 843 | 844 | #[test] 845 | fn test_hash() { 846 | let mut rng = XorShiftRng::from_seed([ 847 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 848 | 0xbc, 0xe5, 849 | ]); 850 | 851 | let mut seed: [u8; 32] = [0u8; 32]; 852 | for _ in 0..100 { 853 | rng.fill_bytes(&mut seed); 854 | let p = $projective::hash(&seed).into_affine(); 855 | assert!(!p.is_zero()); 856 | assert!(p.is_on_curve()); 857 | assert!(p.is_in_correct_subgroup_assuming_on_curve()); 858 | } 859 | } 860 | 861 | #[test] 862 | fn test_sw_encode() { 863 | let mut rng = XorShiftRng::from_seed([ 864 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 865 | 0xbc, 0xe5, 866 | ]); 867 | 868 | for _ in 0..100 { 869 | let mut t = $basefield::random(&mut rng); 870 | let p = $affine::sw_encode(t); 871 | assert!(p.is_on_curve()); 872 | assert!(!p.is_zero()); 873 | 874 | t.negate(); 875 | let mut minus_p = $affine::sw_encode(t).into_projective(); 876 | minus_p.add_assign_mixed(&p); 877 | assert!(minus_p.is_zero()); 878 | } 879 | } 880 | }; 881 | } 882 | 883 | macro_rules! encoded_point_delegations { 884 | ($t:ident) => { 885 | impl AsRef<[u8]> for $t { 886 | fn as_ref(&self) -> &[u8] { 887 | &self.0 888 | } 889 | } 890 | impl AsMut<[u8]> for $t { 891 | fn as_mut(&mut self) -> &mut [u8] { 892 | &mut self.0 893 | } 894 | } 895 | 896 | impl PartialEq for $t { 897 | fn eq(&self, other: &$t) -> bool { 898 | PartialEq::eq(&self.0[..], &other.0[..]) 899 | } 900 | } 901 | impl Eq for $t {} 902 | impl PartialOrd for $t { 903 | fn partial_cmp(&self, other: &$t) -> Option<::std::cmp::Ordering> { 904 | PartialOrd::partial_cmp(&self.0[..], &other.0[..]) 905 | } 906 | } 907 | impl Ord for $t { 908 | fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { 909 | Ord::cmp(&self.0[..], &other.0[..]) 910 | } 911 | } 912 | 913 | impl ::std::hash::Hash for $t { 914 | fn hash(&self, state: &mut H) { 915 | self.0[..].hash(state); 916 | } 917 | } 918 | }; 919 | } // encoded_point_delegations 920 | 921 | mod chain; 922 | mod g1; 923 | mod g2; 924 | mod util; 925 | 926 | pub use self::g1::*; 927 | pub use self::g2::*; 928 | -------------------------------------------------------------------------------- /src/bls12_381/ec/util.rs: -------------------------------------------------------------------------------- 1 | use fff::Field; 2 | 3 | #[inline(always)] 4 | pub(super) fn osswu_helper(u: &F, xi: &F, ellp_a: &F, ellp_b: &F) -> [F; 7] { 5 | let usq = { 6 | let mut tmp = *u; 7 | tmp.square(); 8 | tmp 9 | }; 10 | 11 | let (nd_common, xi_usq, xi2_u4) = { 12 | let mut tmp = usq; 13 | tmp.mul_assign(xi); // xi * u^2 14 | let tmp2 = tmp; 15 | tmp.square(); // xi^2 * u^4 16 | let tmp3 = tmp; 17 | tmp.add_assign(&tmp2); // xi^2 * u^4 + xi * u^2 18 | (tmp, tmp2, tmp3) 19 | }; 20 | 21 | let x0_num = { 22 | let mut tmp = nd_common; 23 | tmp.add_assign(&F::one()); // 1 + nd_common 24 | tmp.mul_assign(ellp_b); // B * (1 + nd_common) 25 | tmp 26 | }; 27 | 28 | let x0_den = { 29 | let mut tmp = *ellp_a; 30 | if nd_common.is_zero() { 31 | tmp.mul_assign(xi); 32 | } else { 33 | tmp.mul_assign(&nd_common); 34 | tmp.negate(); 35 | } 36 | tmp 37 | }; 38 | 39 | // compute g(X0(u)) 40 | let gx0_den = { 41 | let mut tmp = x0_den; 42 | tmp.square(); 43 | tmp.mul_assign(&x0_den); 44 | tmp // x0_den ^ 3 45 | }; 46 | 47 | let gx0_num = { 48 | let mut tmp1 = gx0_den; 49 | tmp1.mul_assign(ellp_b); // B * x0_den^3 50 | let mut tmp2 = x0_den; 51 | tmp2.square(); // x0_den^2 52 | tmp2.mul_assign(&x0_num); // x0_num * x0_den^2 53 | tmp2.mul_assign(ellp_a); // A * x0_num * x0_den^2 54 | tmp1.add_assign(&tmp2); // ^^^ + B * x0_den^3 55 | tmp2 = x0_num; 56 | tmp2.square(); // x0_num^2 57 | tmp2.mul_assign(&x0_num); // x0_num^3 58 | tmp1.add_assign(&tmp2); // x0_num^3 + A * x0_num * x0_den^2 + B * x0_den^3 59 | tmp1 60 | }; 61 | 62 | [usq, xi_usq, xi2_u4, x0_num, x0_den, gx0_num, gx0_den] 63 | } 64 | 65 | #[cfg(test)] 66 | /// Check that the point (X : Y : Z)==(X/Z^2, Y/Z^3) is on E: y^2 = x^3 + ELLP_A * x + ELLP_B. 67 | pub fn check_g_prime(x: &F, y: &F, z: &F, a: &F, b: &F) { 68 | let lhs = { 69 | // y^2 70 | let mut tmp = *y; 71 | tmp.square(); 72 | tmp 73 | }; 74 | 75 | let rhs = { 76 | // x^3 + A x z^4 + B z^6 77 | let mut zsq = *z; 78 | zsq.square(); 79 | 80 | let mut z4 = zsq; 81 | z4.square(); 82 | 83 | let mut tmp1 = *x; 84 | tmp1.square(); 85 | tmp1.mul_assign(x); // x^3 86 | 87 | let mut tmp2 = *x; 88 | tmp2.mul_assign(&z4); 89 | tmp2.mul_assign(a); 90 | tmp1.add_assign(&tmp2); // + A x z^4 91 | 92 | tmp2 = z4; 93 | tmp2.mul_assign(&zsq); 94 | tmp2.mul_assign(b); 95 | tmp1.add_assign(&tmp2); // + B z^6 96 | 97 | tmp1 98 | }; 99 | 100 | assert_eq!(lhs, rhs); 101 | } 102 | -------------------------------------------------------------------------------- /src/bls12_381/fq12.rs: -------------------------------------------------------------------------------- 1 | use super::fq::FROBENIUS_COEFF_FQ12_C1; 2 | use super::fq::{Fq, FqRepr}; 3 | use super::fq2::Fq2; 4 | use super::fq6::Fq6; 5 | use fff::{Field, PrimeField, PrimeFieldRepr}; 6 | use rand_core::RngCore; 7 | use std::fmt; 8 | 9 | /// An element of Fq12, represented by c0 + c1 * w. 10 | #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 11 | pub struct Fq12 { 12 | pub c0: Fq6, 13 | pub c1: Fq6, 14 | } 15 | 16 | impl ::std::fmt::Display for Fq12 { 17 | fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { 18 | write!(f, "Fq12({} + {} * w)", self.c0, self.c1) 19 | } 20 | } 21 | 22 | impl Fq12 { 23 | pub fn conjugate(&mut self) { 24 | self.c1.negate(); 25 | } 26 | 27 | pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) { 28 | let mut aa = self.c0; 29 | aa.mul_by_01(c0, c1); 30 | let mut bb = self.c1; 31 | bb.mul_by_1(c4); 32 | let mut o = *c1; 33 | o.add_assign(c4); 34 | self.c1.add_assign(&self.c0); 35 | self.c1.mul_by_01(c0, &o); 36 | self.c1.sub_assign(&aa); 37 | self.c1.sub_assign(&bb); 38 | self.c0 = bb; 39 | self.c0.mul_by_nonresidue(); 40 | self.c0.add_assign(&aa); 41 | } 42 | 43 | /// Compress this point. Returns `None` if the element is not in the cyclomtomic subgroup. 44 | pub fn compress(&self) -> Option { 45 | if !self.is_cyc() { 46 | return None; 47 | } 48 | 49 | // Use torus-based compression from Section 4.1 in 50 | // "On Compressible Pairings and Their Computation" by Naehrig et al. 51 | let mut b = self.c0; 52 | 53 | b.c0.add_assign(&Fq2::one()); 54 | b.mul_assign(&self.c1.inverse().unwrap()); 55 | 56 | Some(Fq12Compressed(b)) 57 | } 58 | 59 | fn is_cyc(&self) -> bool { 60 | // Check if a^(p^4 - p^2 + 1) == 1. 61 | let mut t0 = *self; 62 | t0.frobenius_map(4); 63 | t0.mul_assign(self); 64 | let mut t1 = *self; 65 | t1.frobenius_map(2); 66 | 67 | t0 == t1 68 | } 69 | } 70 | 71 | impl Field for Fq12 { 72 | fn random(rng: &mut R) -> Self { 73 | Fq12 { 74 | c0: Fq6::random(rng), 75 | c1: Fq6::random(rng), 76 | } 77 | } 78 | 79 | fn zero() -> Self { 80 | Fq12 { 81 | c0: Fq6::zero(), 82 | c1: Fq6::zero(), 83 | } 84 | } 85 | 86 | fn one() -> Self { 87 | Fq12 { 88 | c0: Fq6::one(), 89 | c1: Fq6::zero(), 90 | } 91 | } 92 | 93 | fn is_zero(&self) -> bool { 94 | self.c0.is_zero() && self.c1.is_zero() 95 | } 96 | 97 | fn double(&mut self) { 98 | self.c0.double(); 99 | self.c1.double(); 100 | } 101 | 102 | fn negate(&mut self) { 103 | self.c0.negate(); 104 | self.c1.negate(); 105 | } 106 | 107 | fn add_assign(&mut self, other: &Self) { 108 | self.c0.add_assign(&other.c0); 109 | self.c1.add_assign(&other.c1); 110 | } 111 | 112 | fn sub_assign(&mut self, other: &Self) { 113 | self.c0.sub_assign(&other.c0); 114 | self.c1.sub_assign(&other.c1); 115 | } 116 | 117 | fn frobenius_map(&mut self, power: usize) { 118 | self.c0.frobenius_map(power); 119 | self.c1.frobenius_map(power); 120 | 121 | self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); 122 | self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); 123 | self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); 124 | } 125 | 126 | fn square(&mut self) { 127 | let mut ab = self.c0; 128 | ab.mul_assign(&self.c1); 129 | let mut c0c1 = self.c0; 130 | c0c1.add_assign(&self.c1); 131 | let mut c0 = self.c1; 132 | c0.mul_by_nonresidue(); 133 | c0.add_assign(&self.c0); 134 | c0.mul_assign(&c0c1); 135 | c0.sub_assign(&ab); 136 | self.c1 = ab; 137 | self.c1.add_assign(&ab); 138 | ab.mul_by_nonresidue(); 139 | c0.sub_assign(&ab); 140 | self.c0 = c0; 141 | } 142 | 143 | fn mul_assign(&mut self, other: &Self) { 144 | let mut aa = self.c0; 145 | aa.mul_assign(&other.c0); 146 | let mut bb = self.c1; 147 | bb.mul_assign(&other.c1); 148 | let mut o = other.c0; 149 | o.add_assign(&other.c1); 150 | self.c1.add_assign(&self.c0); 151 | self.c1.mul_assign(&o); 152 | self.c1.sub_assign(&aa); 153 | self.c1.sub_assign(&bb); 154 | self.c0 = bb; 155 | self.c0.mul_by_nonresidue(); 156 | self.c0.add_assign(&aa); 157 | } 158 | 159 | fn inverse(&self) -> Option { 160 | let mut c0s = self.c0; 161 | c0s.square(); 162 | let mut c1s = self.c1; 163 | c1s.square(); 164 | c1s.mul_by_nonresidue(); 165 | c0s.sub_assign(&c1s); 166 | 167 | c0s.inverse().map(|t| { 168 | let mut tmp = Fq12 { c0: t, c1: t }; 169 | tmp.c0.mul_assign(&self.c0); 170 | tmp.c1.mul_assign(&self.c1); 171 | tmp.c1.negate(); 172 | 173 | tmp 174 | }) 175 | } 176 | } 177 | 178 | /// Compressed representation of `Fq12`. 179 | #[derive(Copy, Clone, PartialEq, Eq)] 180 | pub struct Fq12Compressed(Fq6); 181 | 182 | impl fmt::Debug for Fq12Compressed { 183 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 184 | f.debug_struct("Fq12Compressed") 185 | .field("c0", &self.0) 186 | .finish() 187 | } 188 | } 189 | 190 | impl Fq12Compressed { 191 | /// Uncompress the given Fq12 element, returns `None` if the element is an invalid compression 192 | /// format. 193 | pub fn uncompress(self) -> Option { 194 | // Formula for decompression for the odd q case from Section 2 in 195 | // "Compression in finite fields and torus-based cryptography" by 196 | // Rubin-Silverberg. 197 | let mut fp6_neg_one = Fq6::one(); 198 | fp6_neg_one.negate(); 199 | let t = Fq12 { 200 | c0: self.0, 201 | c1: fp6_neg_one, 202 | } 203 | .inverse() 204 | .unwrap(); 205 | let mut c = Fq12 { 206 | c0: self.0, 207 | c1: Fq6::one(), 208 | }; 209 | c.mul_assign(&t); 210 | 211 | if c.is_cyc() { 212 | return Some(c); 213 | } 214 | 215 | None 216 | } 217 | } 218 | 219 | impl crate::Compress for Fq12 { 220 | fn write_compressed(self, mut out: W) -> std::io::Result<()> { 221 | let c = self.compress().unwrap(); 222 | 223 | c.0.c0.c0.into_repr().write_le(&mut out)?; 224 | c.0.c0.c1.into_repr().write_le(&mut out)?; 225 | 226 | c.0.c1.c0.into_repr().write_le(&mut out)?; 227 | c.0.c1.c1.into_repr().write_le(&mut out)?; 228 | 229 | c.0.c2.c0.into_repr().write_le(&mut out)?; 230 | c.0.c2.c1.into_repr().write_le(&mut out)?; 231 | 232 | Ok(()) 233 | } 234 | 235 | fn read_compressed(mut source: R) -> std::io::Result { 236 | let read_fp = |source: &mut dyn std::io::Read| { 237 | let mut repr = FqRepr::default(); 238 | repr.read_le(source)?; 239 | let fp = Fq::from_repr(repr); 240 | fp.map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err.to_string())) 241 | }; 242 | 243 | let x0 = read_fp(&mut source)?; 244 | let x1 = read_fp(&mut source)?; 245 | 246 | let y0 = read_fp(&mut source)?; 247 | let y1 = read_fp(&mut source)?; 248 | 249 | let z0 = read_fp(&mut source)?; 250 | let z1 = read_fp(&mut source)?; 251 | 252 | let x = Fq2 { c0: x0, c1: x1 }; 253 | let y = Fq2 { c0: y0, c1: y1 }; 254 | let z = Fq2 { c0: z0, c1: z1 }; 255 | 256 | let compressed = Fq12Compressed(Fq6 { 257 | c0: x, 258 | c1: y, 259 | c2: z, 260 | }); 261 | compressed.uncompress().ok_or_else(|| { 262 | std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid compression point") 263 | }) 264 | } 265 | } 266 | 267 | #[cfg(test)] 268 | mod tests { 269 | use super::*; 270 | 271 | use crate::bls12_381::{Bls12, Fq, G1, G2}; 272 | use crate::Engine; 273 | use fff::PrimeField; 274 | use groupy::CurveProjective; 275 | use rand_core::SeedableRng; 276 | use rand_xorshift::XorShiftRng; 277 | 278 | #[test] 279 | fn test_fq12_mul_by_014() { 280 | let mut rng = XorShiftRng::from_seed([ 281 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 282 | 0xbc, 0xe5, 283 | ]); 284 | 285 | for _ in 0..1000 { 286 | let c0 = Fq2::random(&mut rng); 287 | let c1 = Fq2::random(&mut rng); 288 | let c5 = Fq2::random(&mut rng); 289 | let mut a = Fq12::random(&mut rng); 290 | let mut b = a; 291 | 292 | a.mul_by_014(&c0, &c1, &c5); 293 | b.mul_assign(&Fq12 { 294 | c0: Fq6 { 295 | c0, 296 | c1, 297 | c2: Fq2::zero(), 298 | }, 299 | c1: Fq6 { 300 | c0: Fq2::zero(), 301 | c1: c5, 302 | c2: Fq2::zero(), 303 | }, 304 | }); 305 | 306 | assert_eq!(a, b); 307 | } 308 | } 309 | 310 | #[test] 311 | fn fq12_field_tests() { 312 | crate::tests::field::random_field_tests::(); 313 | crate::tests::field::random_frobenius_tests::(Fq::char(), 13); 314 | } 315 | 316 | #[test] 317 | fn fp12_compression_test() { 318 | use crate::Compress; 319 | 320 | let mut rng = XorShiftRng::from_seed([ 321 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 322 | 0xbc, 0xe5, 323 | ]); 324 | 325 | for i in 0..100 { 326 | let a = Fq12::random(&mut rng); 327 | // usually not cyclomatic, so not compressable 328 | if let Some(b) = a.compress() { 329 | let c = b.uncompress().unwrap(); 330 | assert_eq!(a, c, "{}", i); 331 | } else { 332 | println!("skipping {}", i); 333 | } 334 | 335 | // pairing result, should be compressable 336 | let p = G1::random(&mut rng).into_affine(); 337 | let q = G2::random(&mut rng).into_affine(); 338 | let a = Bls12::pairing(p, q); 339 | assert!(a.is_cyc()); 340 | 341 | let b = a.compress().unwrap(); 342 | let c = b.uncompress().unwrap(); 343 | assert_eq!(a, c, "{}", i); 344 | 345 | let mut buffer = Vec::new(); 346 | a.write_compressed(&mut buffer).unwrap(); 347 | let out = Fq12::read_compressed(std::io::Cursor::new(buffer)).unwrap(); 348 | assert_eq!(a, out); 349 | } 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/bls12_381/fq6.rs: -------------------------------------------------------------------------------- 1 | use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2}; 2 | use super::fq2::Fq2; 3 | use fff::Field; 4 | use rand_core::RngCore; 5 | 6 | /// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2). 7 | #[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] 8 | pub struct Fq6 { 9 | pub c0: Fq2, 10 | pub c1: Fq2, 11 | pub c2: Fq2, 12 | } 13 | 14 | impl ::std::fmt::Display for Fq6 { 15 | fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { 16 | write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2) 17 | } 18 | } 19 | 20 | impl Fq6 { 21 | /// Multiply by quadratic nonresidue v. 22 | pub fn mul_by_nonresidue(&mut self) { 23 | use std::mem::swap; 24 | swap(&mut self.c0, &mut self.c1); 25 | swap(&mut self.c0, &mut self.c2); 26 | 27 | self.c0.mul_by_nonresidue(); 28 | } 29 | 30 | pub fn mul_by_1(&mut self, c1: &Fq2) { 31 | let mut b_b = self.c1; 32 | b_b.mul_assign(c1); 33 | 34 | let mut t1 = *c1; 35 | { 36 | let mut tmp = self.c1; 37 | tmp.add_assign(&self.c2); 38 | 39 | t1.mul_assign(&tmp); 40 | t1.sub_assign(&b_b); 41 | t1.mul_by_nonresidue(); 42 | } 43 | 44 | let mut t2 = *c1; 45 | { 46 | let mut tmp = self.c0; 47 | tmp.add_assign(&self.c1); 48 | 49 | t2.mul_assign(&tmp); 50 | t2.sub_assign(&b_b); 51 | } 52 | 53 | self.c0 = t1; 54 | self.c1 = t2; 55 | self.c2 = b_b; 56 | } 57 | 58 | pub fn mul_by_01(&mut self, c0: &Fq2, c1: &Fq2) { 59 | let mut a_a = self.c0; 60 | let mut b_b = self.c1; 61 | a_a.mul_assign(c0); 62 | b_b.mul_assign(c1); 63 | 64 | let mut t1 = *c1; 65 | { 66 | let mut tmp = self.c1; 67 | tmp.add_assign(&self.c2); 68 | 69 | t1.mul_assign(&tmp); 70 | t1.sub_assign(&b_b); 71 | t1.mul_by_nonresidue(); 72 | t1.add_assign(&a_a); 73 | } 74 | 75 | let mut t3 = *c0; 76 | { 77 | let mut tmp = self.c0; 78 | tmp.add_assign(&self.c2); 79 | 80 | t3.mul_assign(&tmp); 81 | t3.sub_assign(&a_a); 82 | t3.add_assign(&b_b); 83 | } 84 | 85 | let mut t2 = *c0; 86 | t2.add_assign(c1); 87 | { 88 | let mut tmp = self.c0; 89 | tmp.add_assign(&self.c1); 90 | 91 | t2.mul_assign(&tmp); 92 | t2.sub_assign(&a_a); 93 | t2.sub_assign(&b_b); 94 | } 95 | 96 | self.c0 = t1; 97 | self.c1 = t2; 98 | self.c2 = t3; 99 | } 100 | } 101 | 102 | impl Field for Fq6 { 103 | fn random(rng: &mut R) -> Self { 104 | Fq6 { 105 | c0: Fq2::random(rng), 106 | c1: Fq2::random(rng), 107 | c2: Fq2::random(rng), 108 | } 109 | } 110 | 111 | fn zero() -> Self { 112 | Fq6 { 113 | c0: Fq2::zero(), 114 | c1: Fq2::zero(), 115 | c2: Fq2::zero(), 116 | } 117 | } 118 | 119 | fn one() -> Self { 120 | Fq6 { 121 | c0: Fq2::one(), 122 | c1: Fq2::zero(), 123 | c2: Fq2::zero(), 124 | } 125 | } 126 | 127 | fn is_zero(&self) -> bool { 128 | self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero() 129 | } 130 | 131 | fn double(&mut self) { 132 | self.c0.double(); 133 | self.c1.double(); 134 | self.c2.double(); 135 | } 136 | 137 | fn negate(&mut self) { 138 | self.c0.negate(); 139 | self.c1.negate(); 140 | self.c2.negate(); 141 | } 142 | 143 | fn add_assign(&mut self, other: &Self) { 144 | self.c0.add_assign(&other.c0); 145 | self.c1.add_assign(&other.c1); 146 | self.c2.add_assign(&other.c2); 147 | } 148 | 149 | fn sub_assign(&mut self, other: &Self) { 150 | self.c0.sub_assign(&other.c0); 151 | self.c1.sub_assign(&other.c1); 152 | self.c2.sub_assign(&other.c2); 153 | } 154 | 155 | fn frobenius_map(&mut self, power: usize) { 156 | self.c0.frobenius_map(power); 157 | self.c1.frobenius_map(power); 158 | self.c2.frobenius_map(power); 159 | 160 | self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]); 161 | self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]); 162 | } 163 | 164 | fn square(&mut self) { 165 | let mut s0 = self.c0; 166 | s0.square(); 167 | let mut ab = self.c0; 168 | ab.mul_assign(&self.c1); 169 | let mut s1 = ab; 170 | s1.double(); 171 | let mut s2 = self.c0; 172 | s2.sub_assign(&self.c1); 173 | s2.add_assign(&self.c2); 174 | s2.square(); 175 | let mut bc = self.c1; 176 | bc.mul_assign(&self.c2); 177 | let mut s3 = bc; 178 | s3.double(); 179 | let mut s4 = self.c2; 180 | s4.square(); 181 | 182 | self.c0 = s3; 183 | self.c0.mul_by_nonresidue(); 184 | self.c0.add_assign(&s0); 185 | 186 | self.c1 = s4; 187 | self.c1.mul_by_nonresidue(); 188 | self.c1.add_assign(&s1); 189 | 190 | self.c2 = s1; 191 | self.c2.add_assign(&s2); 192 | self.c2.add_assign(&s3); 193 | self.c2.sub_assign(&s0); 194 | self.c2.sub_assign(&s4); 195 | } 196 | 197 | fn mul_assign(&mut self, other: &Self) { 198 | let mut a_a = self.c0; 199 | let mut b_b = self.c1; 200 | let mut c_c = self.c2; 201 | a_a.mul_assign(&other.c0); 202 | b_b.mul_assign(&other.c1); 203 | c_c.mul_assign(&other.c2); 204 | 205 | let mut t1 = other.c1; 206 | t1.add_assign(&other.c2); 207 | { 208 | let mut tmp = self.c1; 209 | tmp.add_assign(&self.c2); 210 | 211 | t1.mul_assign(&tmp); 212 | t1.sub_assign(&b_b); 213 | t1.sub_assign(&c_c); 214 | t1.mul_by_nonresidue(); 215 | t1.add_assign(&a_a); 216 | } 217 | 218 | let mut t3 = other.c0; 219 | t3.add_assign(&other.c2); 220 | { 221 | let mut tmp = self.c0; 222 | tmp.add_assign(&self.c2); 223 | 224 | t3.mul_assign(&tmp); 225 | t3.sub_assign(&a_a); 226 | t3.add_assign(&b_b); 227 | t3.sub_assign(&c_c); 228 | } 229 | 230 | let mut t2 = other.c0; 231 | t2.add_assign(&other.c1); 232 | { 233 | let mut tmp = self.c0; 234 | tmp.add_assign(&self.c1); 235 | 236 | t2.mul_assign(&tmp); 237 | t2.sub_assign(&a_a); 238 | t2.sub_assign(&b_b); 239 | c_c.mul_by_nonresidue(); 240 | t2.add_assign(&c_c); 241 | } 242 | 243 | self.c0 = t1; 244 | self.c1 = t2; 245 | self.c2 = t3; 246 | } 247 | 248 | fn inverse(&self) -> Option { 249 | let mut c0 = self.c2; 250 | c0.mul_by_nonresidue(); 251 | c0.mul_assign(&self.c1); 252 | c0.negate(); 253 | { 254 | let mut c0s = self.c0; 255 | c0s.square(); 256 | c0.add_assign(&c0s); 257 | } 258 | let mut c1 = self.c2; 259 | c1.square(); 260 | c1.mul_by_nonresidue(); 261 | { 262 | let mut c01 = self.c0; 263 | c01.mul_assign(&self.c1); 264 | c1.sub_assign(&c01); 265 | } 266 | let mut c2 = self.c1; 267 | c2.square(); 268 | { 269 | let mut c02 = self.c0; 270 | c02.mul_assign(&self.c2); 271 | c2.sub_assign(&c02); 272 | } 273 | 274 | let mut tmp1 = self.c2; 275 | tmp1.mul_assign(&c1); 276 | let mut tmp2 = self.c1; 277 | tmp2.mul_assign(&c2); 278 | tmp1.add_assign(&tmp2); 279 | tmp1.mul_by_nonresidue(); 280 | tmp2 = self.c0; 281 | tmp2.mul_assign(&c0); 282 | tmp1.add_assign(&tmp2); 283 | 284 | match tmp1.inverse() { 285 | Some(t) => { 286 | let mut tmp = Fq6 { 287 | c0: t, 288 | c1: t, 289 | c2: t, 290 | }; 291 | tmp.c0.mul_assign(&c0); 292 | tmp.c1.mul_assign(&c1); 293 | tmp.c2.mul_assign(&c2); 294 | 295 | Some(tmp) 296 | } 297 | None => None, 298 | } 299 | } 300 | } 301 | 302 | #[cfg(test)] 303 | use rand_core::SeedableRng; 304 | #[cfg(test)] 305 | use rand_xorshift::XorShiftRng; 306 | 307 | #[test] 308 | fn test_fq6_mul_nonresidue() { 309 | let mut rng = XorShiftRng::from_seed([ 310 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 311 | 0xe5, 312 | ]); 313 | 314 | let nqr = Fq6 { 315 | c0: Fq2::zero(), 316 | c1: Fq2::one(), 317 | c2: Fq2::zero(), 318 | }; 319 | 320 | for _ in 0..1000 { 321 | let mut a = Fq6::random(&mut rng); 322 | let mut b = a; 323 | a.mul_by_nonresidue(); 324 | b.mul_assign(&nqr); 325 | 326 | assert_eq!(a, b); 327 | } 328 | } 329 | 330 | #[test] 331 | fn test_fq6_mul_by_1() { 332 | let mut rng = XorShiftRng::from_seed([ 333 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 334 | 0xe5, 335 | ]); 336 | 337 | for _ in 0..1000 { 338 | let c1 = Fq2::random(&mut rng); 339 | let mut a = Fq6::random(&mut rng); 340 | let mut b = a; 341 | 342 | a.mul_by_1(&c1); 343 | b.mul_assign(&Fq6 { 344 | c0: Fq2::zero(), 345 | c1, 346 | c2: Fq2::zero(), 347 | }); 348 | 349 | assert_eq!(a, b); 350 | } 351 | } 352 | 353 | #[test] 354 | fn test_fq6_mul_by_01() { 355 | let mut rng = XorShiftRng::from_seed([ 356 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 357 | 0xe5, 358 | ]); 359 | 360 | for _ in 0..1000 { 361 | let c0 = Fq2::random(&mut rng); 362 | let c1 = Fq2::random(&mut rng); 363 | let mut a = Fq6::random(&mut rng); 364 | let mut b = a; 365 | 366 | a.mul_by_01(&c0, &c1); 367 | b.mul_assign(&Fq6 { 368 | c0, 369 | c1, 370 | c2: Fq2::zero(), 371 | }); 372 | 373 | assert_eq!(a, b); 374 | } 375 | } 376 | 377 | #[test] 378 | fn fq6_field_tests() { 379 | use fff::PrimeField; 380 | 381 | crate::tests::field::random_field_tests::(); 382 | crate::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); 383 | } 384 | -------------------------------------------------------------------------------- /src/bls12_381/mod.rs: -------------------------------------------------------------------------------- 1 | //! An implementation of the BLS12-381 pairing-friendly elliptic curve 2 | //! construction. 3 | 4 | mod ec; 5 | mod fq; 6 | mod fq12; 7 | mod fq2; 8 | mod fq6; 9 | mod fr; 10 | 11 | mod cofactors; 12 | mod serde_impl; 13 | 14 | #[cfg(test)] 15 | mod tests; 16 | 17 | pub use self::ec::{ 18 | G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2Affine, G2Compressed, G2Prepared, 19 | G2Uncompressed, G1, G2, 20 | }; 21 | pub use self::fq::{Fq, FqRepr}; 22 | pub use self::fq12::Fq12; 23 | pub use self::fq2::Fq2; 24 | pub use self::fq6::Fq6; 25 | pub use self::fr::{Fr, FrRepr}; 26 | 27 | pub use self::cofactors::ClearH; 28 | 29 | use super::{Engine, PairingCurveAffine}; 30 | 31 | use fff::{BitIterator, Field, ScalarEngine}; 32 | use groupy::{CurveAffine, CurveProjective}; 33 | 34 | // The BLS parameter x for BLS12-381 is -0xd201000000010000 35 | const BLS_X: u64 = 0xd201000000010000; 36 | const BLS_X_IS_NEGATIVE: bool = true; 37 | 38 | #[derive(Clone, Debug)] 39 | pub struct Bls12; 40 | 41 | impl ScalarEngine for Bls12 { 42 | type Fr = Fr; 43 | } 44 | 45 | impl Engine for Bls12 { 46 | type G1 = G1; 47 | type G1Affine = G1Affine; 48 | type G2 = G2; 49 | type G2Affine = G2Affine; 50 | type Fq = Fq; 51 | type Fqe = Fq2; 52 | type Fqk = Fq12; 53 | 54 | fn miller_loop<'a, I>(i: I) -> Self::Fqk 55 | where 56 | I: IntoIterator< 57 | Item = &'a ( 58 | &'a ::Prepared, 59 | &'a ::Prepared, 60 | ), 61 | >, 62 | { 63 | let mut pairs = vec![]; 64 | for &(p, q) in i { 65 | if !p.is_zero() && !q.is_zero() { 66 | pairs.push((p, q.coeffs.iter())); 67 | } 68 | } 69 | 70 | // Twisting isomorphism from E to E' 71 | fn ell(f: &mut Fq12, coeffs: &(Fq2, Fq2, Fq2), p: &G1Affine) { 72 | let mut c0 = coeffs.0; 73 | let mut c1 = coeffs.1; 74 | 75 | c0.c0.mul_assign(&p.y); 76 | c0.c1.mul_assign(&p.y); 77 | 78 | c1.c0.mul_assign(&p.x); 79 | c1.c1.mul_assign(&p.x); 80 | 81 | // Sparse multiplication in Fq12 82 | f.mul_by_014(&coeffs.2, &c1, &c0); 83 | } 84 | 85 | let mut f = Fq12::one(); 86 | 87 | let mut found_one = false; 88 | for i in BitIterator::new(&[BLS_X >> 1]) { 89 | if !found_one { 90 | found_one = i; 91 | continue; 92 | } 93 | 94 | for &mut (p, ref mut coeffs) in &mut pairs { 95 | ell(&mut f, coeffs.next().unwrap(), &p.0); 96 | } 97 | 98 | if i { 99 | for &mut (p, ref mut coeffs) in &mut pairs { 100 | ell(&mut f, coeffs.next().unwrap(), &p.0); 101 | } 102 | } 103 | 104 | f.square(); 105 | } 106 | 107 | for &mut (p, ref mut coeffs) in &mut pairs { 108 | ell(&mut f, coeffs.next().unwrap(), &p.0); 109 | } 110 | 111 | if BLS_X_IS_NEGATIVE { 112 | f.conjugate(); 113 | } 114 | 115 | f 116 | } 117 | 118 | fn final_exponentiation(r: &Fq12) -> Option { 119 | let mut f1 = *r; 120 | f1.conjugate(); 121 | 122 | match r.inverse() { 123 | Some(mut f2) => { 124 | let mut r = f1; 125 | r.mul_assign(&f2); 126 | f2 = r; 127 | r.frobenius_map(2); 128 | r.mul_assign(&f2); 129 | 130 | fn exp_by_x(f: &mut Fq12, x: u64) { 131 | *f = f.pow(&[x]); 132 | if BLS_X_IS_NEGATIVE { 133 | f.conjugate(); 134 | } 135 | } 136 | 137 | let mut x = BLS_X; 138 | let mut y0 = r; 139 | y0.square(); 140 | let mut y1 = y0; 141 | exp_by_x(&mut y1, x); 142 | x >>= 1; 143 | let mut y2 = y1; 144 | exp_by_x(&mut y2, x); 145 | x <<= 1; 146 | let mut y3 = r; 147 | y3.conjugate(); 148 | y1.mul_assign(&y3); 149 | y1.conjugate(); 150 | y1.mul_assign(&y2); 151 | y2 = y1; 152 | exp_by_x(&mut y2, x); 153 | y3 = y2; 154 | exp_by_x(&mut y3, x); 155 | y1.conjugate(); 156 | y3.mul_assign(&y1); 157 | y1.conjugate(); 158 | y1.frobenius_map(3); 159 | y2.frobenius_map(2); 160 | y1.mul_assign(&y2); 161 | y2 = y3; 162 | exp_by_x(&mut y2, x); 163 | y2.mul_assign(&y0); 164 | y2.mul_assign(&r); 165 | y1.mul_assign(&y2); 166 | y2 = y3; 167 | y2.frobenius_map(1); 168 | y1.mul_assign(&y2); 169 | 170 | Some(y1) 171 | } 172 | None => None, 173 | } 174 | } 175 | } 176 | 177 | impl G2Prepared { 178 | pub fn is_zero(&self) -> bool { 179 | self.infinity 180 | } 181 | 182 | pub fn from_affine(q: G2Affine) -> Self { 183 | if q.is_zero() { 184 | return G2Prepared { 185 | coeffs: vec![], 186 | infinity: true, 187 | }; 188 | } 189 | 190 | fn doubling_step(r: &mut G2) -> (Fq2, Fq2, Fq2) { 191 | // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf 192 | let mut tmp0 = r.x; 193 | tmp0.square(); 194 | 195 | let mut tmp1 = r.y; 196 | tmp1.square(); 197 | 198 | let mut tmp2 = tmp1; 199 | tmp2.square(); 200 | 201 | let mut tmp3 = tmp1; 202 | tmp3.add_assign(&r.x); 203 | tmp3.square(); 204 | tmp3.sub_assign(&tmp0); 205 | tmp3.sub_assign(&tmp2); 206 | tmp3.double(); 207 | 208 | let mut tmp4 = tmp0; 209 | tmp4.double(); 210 | tmp4.add_assign(&tmp0); 211 | 212 | let mut tmp6 = r.x; 213 | tmp6.add_assign(&tmp4); 214 | 215 | let mut tmp5 = tmp4; 216 | tmp5.square(); 217 | 218 | let mut zsquared = r.z; 219 | zsquared.square(); 220 | 221 | r.x = tmp5; 222 | r.x.sub_assign(&tmp3); 223 | r.x.sub_assign(&tmp3); 224 | 225 | r.z.add_assign(&r.y); 226 | r.z.square(); 227 | r.z.sub_assign(&tmp1); 228 | r.z.sub_assign(&zsquared); 229 | 230 | r.y = tmp3; 231 | r.y.sub_assign(&r.x); 232 | r.y.mul_assign(&tmp4); 233 | 234 | tmp2.double(); 235 | tmp2.double(); 236 | tmp2.double(); 237 | 238 | r.y.sub_assign(&tmp2); 239 | 240 | tmp3 = tmp4; 241 | tmp3.mul_assign(&zsquared); 242 | tmp3.double(); 243 | tmp3.negate(); 244 | 245 | tmp6.square(); 246 | tmp6.sub_assign(&tmp0); 247 | tmp6.sub_assign(&tmp5); 248 | 249 | tmp1.double(); 250 | tmp1.double(); 251 | 252 | tmp6.sub_assign(&tmp1); 253 | 254 | tmp0 = r.z; 255 | tmp0.mul_assign(&zsquared); 256 | tmp0.double(); 257 | 258 | (tmp0, tmp3, tmp6) 259 | } 260 | 261 | fn addition_step(r: &mut G2, q: &G2Affine) -> (Fq2, Fq2, Fq2) { 262 | // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf 263 | let mut zsquared = r.z; 264 | zsquared.square(); 265 | 266 | let mut ysquared = q.y; 267 | ysquared.square(); 268 | 269 | let mut t0 = zsquared; 270 | t0.mul_assign(&q.x); 271 | 272 | let mut t1 = q.y; 273 | t1.add_assign(&r.z); 274 | t1.square(); 275 | t1.sub_assign(&ysquared); 276 | t1.sub_assign(&zsquared); 277 | t1.mul_assign(&zsquared); 278 | 279 | let mut t2 = t0; 280 | t2.sub_assign(&r.x); 281 | 282 | let mut t3 = t2; 283 | t3.square(); 284 | 285 | let mut t4 = t3; 286 | t4.double(); 287 | t4.double(); 288 | 289 | let mut t5 = t4; 290 | t5.mul_assign(&t2); 291 | 292 | let mut t6 = t1; 293 | t6.sub_assign(&r.y); 294 | t6.sub_assign(&r.y); 295 | 296 | let mut t9 = t6; 297 | t9.mul_assign(&q.x); 298 | 299 | let mut t7 = t4; 300 | t7.mul_assign(&r.x); 301 | 302 | r.x = t6; 303 | r.x.square(); 304 | r.x.sub_assign(&t5); 305 | r.x.sub_assign(&t7); 306 | r.x.sub_assign(&t7); 307 | 308 | r.z.add_assign(&t2); 309 | r.z.square(); 310 | r.z.sub_assign(&zsquared); 311 | r.z.sub_assign(&t3); 312 | 313 | let mut t10 = q.y; 314 | t10.add_assign(&r.z); 315 | 316 | let mut t8 = t7; 317 | t8.sub_assign(&r.x); 318 | t8.mul_assign(&t6); 319 | 320 | t0 = r.y; 321 | t0.mul_assign(&t5); 322 | t0.double(); 323 | 324 | r.y = t8; 325 | r.y.sub_assign(&t0); 326 | 327 | t10.square(); 328 | t10.sub_assign(&ysquared); 329 | 330 | let mut ztsquared = r.z; 331 | ztsquared.square(); 332 | 333 | t10.sub_assign(&ztsquared); 334 | 335 | t9.double(); 336 | t9.sub_assign(&t10); 337 | 338 | t10 = r.z; 339 | t10.double(); 340 | 341 | t6.negate(); 342 | 343 | t1 = t6; 344 | t1.double(); 345 | 346 | (t10, t1, t9) 347 | } 348 | 349 | let mut coeffs = vec![]; 350 | let mut r: G2 = q.into(); 351 | 352 | let mut found_one = false; 353 | for i in BitIterator::new([BLS_X >> 1]) { 354 | if !found_one { 355 | found_one = i; 356 | continue; 357 | } 358 | 359 | coeffs.push(doubling_step(&mut r)); 360 | 361 | if i { 362 | coeffs.push(addition_step(&mut r, &q)); 363 | } 364 | } 365 | 366 | coeffs.push(doubling_step(&mut r)); 367 | 368 | G2Prepared { 369 | coeffs, 370 | infinity: false, 371 | } 372 | } 373 | } 374 | 375 | /// Evaluate isogeny map from curve with non-zero j-invariant. 376 | pub trait IsogenyMap { 377 | /// Eavluate isogeny map. 378 | fn isogeny_map(&mut self); 379 | } 380 | 381 | /// Trait executing Optimized Simplified SWU maps. 382 | /// 383 | /// Implemented for G1 and G2 based on https://eprint.iacr.org/2019/403. 384 | pub trait OsswuMap: CurveProjective { 385 | /// Evaluate optimized simplified SWU map on supplied base field element. 386 | fn osswu_map(u: &::Base) -> Self; 387 | } 388 | 389 | #[test] 390 | fn bls12_engine_tests() { 391 | crate::tests::engine::engine_tests::(); 392 | } 393 | -------------------------------------------------------------------------------- /src/bls12_381/serde_impl.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::marker::PhantomData; 3 | 4 | use super::{G1Affine, G2Affine, G1, G2}; 5 | use groupy::{CurveAffine, CurveProjective, EncodedPoint}; 6 | 7 | use serde::de::{Error as DeserializeError, SeqAccess, Visitor}; 8 | use serde::ser::SerializeTuple; 9 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 10 | 11 | const ERR_CODE: &str = "deserialized bytes don't encode a group element"; 12 | 13 | impl Serialize for G1 { 14 | fn serialize(&self, s: S) -> Result { 15 | self.into_affine().serialize(s) 16 | } 17 | } 18 | 19 | impl<'de> Deserialize<'de> for G1 { 20 | fn deserialize>(d: D) -> Result { 21 | Ok(G1Affine::deserialize(d)?.into_projective()) 22 | } 23 | } 24 | 25 | impl Serialize for G1Affine { 26 | fn serialize(&self, s: S) -> Result { 27 | serialize_affine(self, s) 28 | } 29 | } 30 | 31 | impl<'de> Deserialize<'de> for G1Affine { 32 | fn deserialize>(d: D) -> Result { 33 | Ok(deserialize_affine(d)?) 34 | } 35 | } 36 | 37 | impl Serialize for G2 { 38 | fn serialize(&self, s: S) -> Result { 39 | self.into_affine().serialize(s) 40 | } 41 | } 42 | 43 | impl<'de> Deserialize<'de> for G2 { 44 | fn deserialize>(d: D) -> Result { 45 | Ok(G2Affine::deserialize(d)?.into_projective()) 46 | } 47 | } 48 | 49 | impl Serialize for G2Affine { 50 | fn serialize(&self, s: S) -> Result { 51 | serialize_affine(self, s) 52 | } 53 | } 54 | 55 | impl<'de> Deserialize<'de> for G2Affine { 56 | fn deserialize>(d: D) -> Result { 57 | Ok(deserialize_affine(d)?) 58 | } 59 | } 60 | 61 | /// Serializes a group element using its compressed representation. 62 | fn serialize_affine(c: &C, s: S) -> Result { 63 | let len = C::Compressed::size(); 64 | let mut tup = s.serialize_tuple(len)?; 65 | for byte in c.into_compressed().as_ref() { 66 | tup.serialize_element(byte)?; 67 | } 68 | tup.end() 69 | } 70 | 71 | /// Deserializes the compressed representation of a group element. 72 | fn deserialize_affine<'de, D: Deserializer<'de>, C: CurveAffine>(d: D) -> Result { 73 | struct TupleVisitor { 74 | _ph: PhantomData, 75 | } 76 | 77 | impl<'de, C: CurveAffine> Visitor<'de> for TupleVisitor { 78 | type Value = C; 79 | 80 | fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { 81 | let len = C::Compressed::size(); 82 | write!(f, "a tuple of size {}", len) 83 | } 84 | 85 | #[inline] 86 | fn visit_seq>(self, mut seq: A) -> Result { 87 | let mut compressed = C::Compressed::empty(); 88 | for (i, byte) in compressed.as_mut().iter_mut().enumerate() { 89 | let len_err = || DeserializeError::invalid_length(i, &self); 90 | *byte = seq.next_element()?.ok_or_else(len_err)?; 91 | } 92 | let to_err = |_| DeserializeError::custom(ERR_CODE); 93 | compressed.into_affine().map_err(to_err) 94 | } 95 | } 96 | 97 | let len = C::Compressed::size(); 98 | d.deserialize_tuple(len, TupleVisitor { _ph: PhantomData }) 99 | } 100 | 101 | #[cfg(test)] 102 | mod tests { 103 | extern crate serde_json; 104 | 105 | use super::*; 106 | use crate::bls12_381::{Fq, Fq12, Fr}; 107 | 108 | use std::fmt::Debug; 109 | 110 | use fff::{Field, PrimeField}; 111 | use rand_core::SeedableRng; 112 | use rand_xorshift::XorShiftRng; 113 | 114 | fn test_roundtrip Deserialize<'a> + Debug + PartialEq>(t: &T) { 115 | let ser = serde_json::to_vec(t).unwrap(); 116 | assert_eq!(*t, serde_json::from_slice(&ser).unwrap()); 117 | } 118 | 119 | #[test] 120 | fn serde_g1() { 121 | let mut rng = XorShiftRng::from_seed([ 122 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 123 | 0xbc, 0xe5, 124 | ]); 125 | 126 | let g = G1::random(&mut rng); 127 | test_roundtrip(&g); 128 | test_roundtrip(&g.into_affine()); 129 | } 130 | 131 | #[test] 132 | fn serde_g2() { 133 | let mut rng = XorShiftRng::from_seed([ 134 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 135 | 0xbc, 0xe5, 136 | ]); 137 | 138 | let g = G2::random(&mut rng); 139 | test_roundtrip(&g); 140 | test_roundtrip(&g.into_affine()); 141 | } 142 | 143 | #[test] 144 | fn serde_fr() { 145 | let mut rng = XorShiftRng::from_seed([ 146 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 147 | 0xbc, 0xe5, 148 | ]); 149 | let f = Fr::random(&mut rng); 150 | test_roundtrip(&f); 151 | test_roundtrip(&f.into_repr()); 152 | } 153 | 154 | #[test] 155 | fn serde_fq() { 156 | let mut rng = XorShiftRng::from_seed([ 157 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 158 | 0xbc, 0xe5, 159 | ]); 160 | 161 | let f = Fq::random(&mut rng); 162 | test_roundtrip(&f); 163 | test_roundtrip(&f.into_repr()); 164 | } 165 | 166 | #[test] 167 | fn serde_fq12() { 168 | let mut rng = XorShiftRng::from_seed([ 169 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 170 | 0xbc, 0xe5, 171 | ]); 172 | let f = Fq12::random(&mut rng); 173 | test_roundtrip(&f); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/bls12_381/tests/g1_compressed_valid_test_vectors.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filecoin-project/paired/80b765cb0afa2b38eb9fabdfbc12e0ad056cbb40/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat -------------------------------------------------------------------------------- /src/bls12_381/tests/g1_hashtopoint.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filecoin-project/paired/80b765cb0afa2b38eb9fabdfbc12e0ad056cbb40/src/bls12_381/tests/g1_hashtopoint.dat -------------------------------------------------------------------------------- /src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filecoin-project/paired/80b765cb0afa2b38eb9fabdfbc12e0ad056cbb40/src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat -------------------------------------------------------------------------------- /src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filecoin-project/paired/80b765cb0afa2b38eb9fabdfbc12e0ad056cbb40/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat -------------------------------------------------------------------------------- /src/bls12_381/tests/g2_compressed_valid_test_vectors.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filecoin-project/paired/80b765cb0afa2b38eb9fabdfbc12e0ad056cbb40/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat -------------------------------------------------------------------------------- /src/bls12_381/tests/g2_hashtopoint.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filecoin-project/paired/80b765cb0afa2b38eb9fabdfbc12e0ad056cbb40/src/bls12_381/tests/g2_hashtopoint.dat -------------------------------------------------------------------------------- /src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/filecoin-project/paired/80b765cb0afa2b38eb9fabdfbc12e0ad056cbb40/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat -------------------------------------------------------------------------------- /src/bls12_381/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use fff::PrimeFieldRepr; 2 | use groupy::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; 3 | 4 | use super::*; 5 | use crate::*; 6 | 7 | #[test] 8 | fn test_pairing_result_against_relic() { 9 | /* 10 | Sent to me from Diego Aranha (author of RELIC library): 11 | 12 | 1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6 13 | 089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F 14 | 1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87 15 | 193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F 16 | 01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5 17 | 018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6 18 | 19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D 19 | 06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A 20 | 11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57 21 | 03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2 22 | 04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF 23 | 0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631 24 | */ 25 | 26 | assert_eq!(Bls12::pairing(G1::one(), G2::one()), Fq12 { 27 | c0: Fq6 { 28 | c0: Fq2 { 29 | c0: Fq::from_str("2819105605953691245277803056322684086884703000473961065716485506033588504203831029066448642358042597501014294104502").unwrap(), 30 | c1: Fq::from_str("1323968232986996742571315206151405965104242542339680722164220900812303524334628370163366153839984196298685227734799").unwrap() 31 | }, 32 | c1: Fq2 { 33 | c0: Fq::from_str("2987335049721312504428602988447616328830341722376962214011674875969052835043875658579425548512925634040144704192135").unwrap(), 34 | c1: Fq::from_str("3879723582452552452538684314479081967502111497413076598816163759028842927668327542875108457755966417881797966271311").unwrap() 35 | }, 36 | c2: Fq2 { 37 | c0: Fq::from_str("261508182517997003171385743374653339186059518494239543139839025878870012614975302676296704930880982238308326681253").unwrap(), 38 | c1: Fq::from_str("231488992246460459663813598342448669854473942105054381511346786719005883340876032043606739070883099647773793170614").unwrap() 39 | } 40 | }, 41 | c1: Fq6 { 42 | c0: Fq2 { 43 | c0: Fq::from_str("3993582095516422658773669068931361134188738159766715576187490305611759126554796569868053818105850661142222948198557").unwrap(), 44 | c1: Fq::from_str("1074773511698422344502264006159859710502164045911412750831641680783012525555872467108249271286757399121183508900634").unwrap() 45 | }, 46 | c1: Fq2 { 47 | c0: Fq::from_str("2727588299083545686739024317998512740561167011046940249988557419323068809019137624943703910267790601287073339193943").unwrap(), 48 | c1: Fq::from_str("493643299814437640914745677854369670041080344349607504656543355799077485536288866009245028091988146107059514546594").unwrap() 49 | }, 50 | c2: Fq2 { 51 | c0: Fq::from_str("734401332196641441839439105942623141234148957972407782257355060229193854324927417865401895596108124443575283868655").unwrap(), 52 | c1: Fq::from_str("2348330098288556420918672502923664952620152483128593484301759394583320358354186482723629999370241674973832318248497").unwrap() 53 | } 54 | } 55 | }); 56 | } 57 | 58 | #[test] 59 | fn test_g1_hash_vectors() { 60 | let mut expected = &include_bytes!("g1_hashtopoint.dat")[..]; 61 | 62 | for i in 0..1000 { 63 | let p = G1::hash(format!("{}", i).as_bytes()) 64 | .into_affine() 65 | .into_uncompressed(); 66 | 67 | assert_eq!(p.as_ref(), &expected[0..96]); 68 | expected = &expected[96..]; 69 | } 70 | assert_eq!(expected.len(), 0); 71 | } 72 | 73 | #[test] 74 | fn test_g2_hash_vectors() { 75 | let mut expected = &include_bytes!("g2_hashtopoint.dat")[..]; 76 | 77 | for i in 0..1000 { 78 | let p = G2::hash(format!("{}", i).as_bytes()) 79 | .into_affine() 80 | .into_uncompressed(); 81 | 82 | assert_eq!(p.as_ref(), &expected[0..(96 * 2)]); 83 | expected = &expected[(96 * 2)..]; 84 | } 85 | assert_eq!(expected.len(), 0); 86 | } 87 | 88 | fn test_vectors>(expected: &[u8]) { 89 | let mut e = G::zero(); 90 | 91 | let mut v = vec![]; 92 | { 93 | let mut expected = expected; 94 | for _ in 0..1000 { 95 | let e_affine = e.into_affine(); 96 | let encoded = E::from_affine(e_affine); 97 | v.extend_from_slice(encoded.as_ref()); 98 | 99 | let mut decoded = E::empty(); 100 | decoded.as_mut().copy_from_slice(&expected[0..E::size()]); 101 | expected = &expected[E::size()..]; 102 | let decoded = decoded.into_affine().unwrap(); 103 | assert_eq!(e_affine, decoded); 104 | 105 | e.add_assign(&G::one()); 106 | } 107 | } 108 | 109 | assert_eq!(&v[..], expected); 110 | } 111 | 112 | #[test] 113 | fn test_g1_uncompressed_valid_vectors() { 114 | test_vectors::(include_bytes!("g1_uncompressed_valid_test_vectors.dat")); 115 | } 116 | 117 | #[test] 118 | fn test_g1_compressed_valid_vectors() { 119 | test_vectors::(include_bytes!("g1_compressed_valid_test_vectors.dat")); 120 | } 121 | 122 | #[test] 123 | fn test_g2_uncompressed_valid_vectors() { 124 | test_vectors::(include_bytes!("g2_uncompressed_valid_test_vectors.dat")); 125 | } 126 | 127 | #[test] 128 | fn test_g2_compressed_valid_vectors() { 129 | test_vectors::(include_bytes!("g2_compressed_valid_test_vectors.dat")); 130 | } 131 | 132 | #[test] 133 | fn test_g1_uncompressed_invalid_vectors() { 134 | { 135 | let z = G1Affine::zero().into_uncompressed(); 136 | 137 | { 138 | let mut z = z; 139 | z.as_mut()[0] |= 0b1000_0000; 140 | if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { 141 | // :) 142 | } else { 143 | panic!("should have rejected the point because we expected an uncompressed point"); 144 | } 145 | } 146 | 147 | { 148 | let mut z = z; 149 | z.as_mut()[0] |= 0b0010_0000; 150 | if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { 151 | // :) 152 | } else { 153 | panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); 154 | } 155 | } 156 | 157 | for i in 0..G1Uncompressed::size() { 158 | let mut z = z; 159 | z.as_mut()[i] |= 0b0000_0001; 160 | if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { 161 | // :) 162 | } else { 163 | panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); 164 | } 165 | } 166 | } 167 | 168 | let o = G1Affine::one().into_uncompressed(); 169 | 170 | { 171 | let mut o = o; 172 | o.as_mut()[0] |= 0b1000_0000; 173 | if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { 174 | // :) 175 | } else { 176 | panic!("should have rejected the point because we expected an uncompressed point"); 177 | } 178 | } 179 | 180 | let m = Fq::char(); 181 | 182 | { 183 | let mut o = o; 184 | m.write_be(&mut o.as_mut()[0..]).unwrap(); 185 | 186 | if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { 187 | assert_eq!(coordinate, "x coordinate"); 188 | } else { 189 | panic!("should have rejected the point") 190 | } 191 | } 192 | 193 | { 194 | let mut o = o; 195 | m.write_be(&mut o.as_mut()[48..]).unwrap(); 196 | 197 | if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { 198 | assert_eq!(coordinate, "y coordinate"); 199 | } else { 200 | panic!("should have rejected the point") 201 | } 202 | } 203 | 204 | { 205 | let m = Fq::zero().into_repr(); 206 | 207 | let mut o = o; 208 | m.write_be(&mut o.as_mut()[0..]).unwrap(); 209 | 210 | if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { 211 | // :) 212 | } else { 213 | panic!("should have rejected the point because it isn't on the curve") 214 | } 215 | } 216 | 217 | { 218 | let mut o = o; 219 | let mut x = Fq::one(); 220 | 221 | loop { 222 | let mut x3b = x; 223 | x3b.square(); 224 | x3b.mul_assign(&x); 225 | x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? 226 | 227 | if let Some(y) = x3b.sqrt() { 228 | // We know this is on the curve, but it's likely not going to be in the correct subgroup. 229 | x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); 230 | y.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); 231 | 232 | if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { 233 | break; 234 | } else { 235 | panic!( 236 | "should have rejected the point because it isn't in the correct subgroup" 237 | ) 238 | } 239 | } else { 240 | x.add_assign(&Fq::one()); 241 | } 242 | } 243 | } 244 | } 245 | 246 | #[test] 247 | fn test_g2_uncompressed_invalid_vectors() { 248 | { 249 | let z = G2Affine::zero().into_uncompressed(); 250 | 251 | { 252 | let mut z = z; 253 | z.as_mut()[0] |= 0b1000_0000; 254 | if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { 255 | // :) 256 | } else { 257 | panic!("should have rejected the point because we expected an uncompressed point"); 258 | } 259 | } 260 | 261 | { 262 | let mut z = z; 263 | z.as_mut()[0] |= 0b0010_0000; 264 | if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { 265 | // :) 266 | } else { 267 | panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); 268 | } 269 | } 270 | 271 | for i in 0..G2Uncompressed::size() { 272 | let mut z = z; 273 | z.as_mut()[i] |= 0b0000_0001; 274 | if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { 275 | // :) 276 | } else { 277 | panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); 278 | } 279 | } 280 | } 281 | 282 | let o = G2Affine::one().into_uncompressed(); 283 | 284 | { 285 | let mut o = o; 286 | o.as_mut()[0] |= 0b1000_0000; 287 | if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { 288 | // :) 289 | } else { 290 | panic!("should have rejected the point because we expected an uncompressed point"); 291 | } 292 | } 293 | 294 | let m = Fq::char(); 295 | 296 | { 297 | let mut o = o; 298 | m.write_be(&mut o.as_mut()[0..]).unwrap(); 299 | 300 | if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { 301 | assert_eq!(coordinate, "x coordinate (c1)"); 302 | } else { 303 | panic!("should have rejected the point") 304 | } 305 | } 306 | 307 | { 308 | let mut o = o; 309 | m.write_be(&mut o.as_mut()[48..]).unwrap(); 310 | 311 | if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { 312 | assert_eq!(coordinate, "x coordinate (c0)"); 313 | } else { 314 | panic!("should have rejected the point") 315 | } 316 | } 317 | 318 | { 319 | let mut o = o; 320 | m.write_be(&mut o.as_mut()[96..]).unwrap(); 321 | 322 | if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { 323 | assert_eq!(coordinate, "y coordinate (c1)"); 324 | } else { 325 | panic!("should have rejected the point") 326 | } 327 | } 328 | 329 | { 330 | let mut o = o; 331 | m.write_be(&mut o.as_mut()[144..]).unwrap(); 332 | 333 | if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { 334 | assert_eq!(coordinate, "y coordinate (c0)"); 335 | } else { 336 | panic!("should have rejected the point") 337 | } 338 | } 339 | 340 | { 341 | let m = Fq::zero().into_repr(); 342 | 343 | let mut o = o; 344 | m.write_be(&mut o.as_mut()[0..]).unwrap(); 345 | m.write_be(&mut o.as_mut()[48..]).unwrap(); 346 | 347 | if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { 348 | // :) 349 | } else { 350 | panic!("should have rejected the point because it isn't on the curve") 351 | } 352 | } 353 | 354 | { 355 | let mut o = o; 356 | let mut x = Fq2::one(); 357 | 358 | loop { 359 | let mut x3b = x; 360 | x3b.square(); 361 | x3b.mul_assign(&x); 362 | x3b.add_assign(&Fq2 { 363 | c0: Fq::from_repr(FqRepr::from(4)).unwrap(), 364 | c1: Fq::from_repr(FqRepr::from(4)).unwrap(), 365 | }); // TODO: perhaps expose coeff_b through API? 366 | 367 | if let Some(y) = x3b.sqrt() { 368 | // We know this is on the curve, but it's likely not going to be in the correct subgroup. 369 | x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); 370 | x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); 371 | y.c1.into_repr().write_be(&mut o.as_mut()[96..]).unwrap(); 372 | y.c0.into_repr().write_be(&mut o.as_mut()[144..]).unwrap(); 373 | 374 | if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { 375 | break; 376 | } else { 377 | panic!( 378 | "should have rejected the point because it isn't in the correct subgroup" 379 | ) 380 | } 381 | } else { 382 | x.add_assign(&Fq2::one()); 383 | } 384 | } 385 | } 386 | } 387 | 388 | #[test] 389 | fn test_g1_compressed_invalid_vectors() { 390 | { 391 | let z = G1Affine::zero().into_compressed(); 392 | 393 | { 394 | let mut z = z; 395 | z.as_mut()[0] &= 0b0111_1111; 396 | if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { 397 | // :) 398 | } else { 399 | panic!("should have rejected the point because we expected a compressed point"); 400 | } 401 | } 402 | 403 | { 404 | let mut z = z; 405 | z.as_mut()[0] |= 0b0010_0000; 406 | if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { 407 | // :) 408 | } else { 409 | panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); 410 | } 411 | } 412 | 413 | for i in 0..G1Compressed::size() { 414 | let mut z = z; 415 | z.as_mut()[i] |= 0b0000_0001; 416 | if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { 417 | // :) 418 | } else { 419 | panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); 420 | } 421 | } 422 | } 423 | 424 | let o = G1Affine::one().into_compressed(); 425 | 426 | { 427 | let mut o = o; 428 | o.as_mut()[0] &= 0b0111_1111; 429 | if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { 430 | // :) 431 | } else { 432 | panic!("should have rejected the point because we expected a compressed point"); 433 | } 434 | } 435 | 436 | let m = Fq::char(); 437 | 438 | { 439 | let mut o = o; 440 | m.write_be(&mut o.as_mut()[0..]).unwrap(); 441 | o.as_mut()[0] |= 0b1000_0000; 442 | 443 | if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { 444 | assert_eq!(coordinate, "x coordinate"); 445 | } else { 446 | panic!("should have rejected the point") 447 | } 448 | } 449 | 450 | { 451 | let mut o = o; 452 | let mut x = Fq::one(); 453 | 454 | loop { 455 | let mut x3b = x; 456 | x3b.square(); 457 | x3b.mul_assign(&x); 458 | x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? 459 | 460 | if let Some(_) = x3b.sqrt() { 461 | x.add_assign(&Fq::one()); 462 | } else { 463 | x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); 464 | o.as_mut()[0] |= 0b1000_0000; 465 | 466 | if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { 467 | break; 468 | } else { 469 | panic!("should have rejected the point because it isn't on the curve") 470 | } 471 | } 472 | } 473 | } 474 | 475 | { 476 | let mut o = o; 477 | let mut x = Fq::one(); 478 | 479 | loop { 480 | let mut x3b = x; 481 | x3b.square(); 482 | x3b.mul_assign(&x); 483 | x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? 484 | 485 | if let Some(_) = x3b.sqrt() { 486 | // We know this is on the curve, but it's likely not going to be in the correct subgroup. 487 | x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); 488 | o.as_mut()[0] |= 0b1000_0000; 489 | 490 | if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { 491 | break; 492 | } else { 493 | panic!( 494 | "should have rejected the point because it isn't in the correct subgroup" 495 | ) 496 | } 497 | } else { 498 | x.add_assign(&Fq::one()); 499 | } 500 | } 501 | } 502 | } 503 | 504 | #[test] 505 | fn test_g2_compressed_invalid_vectors() { 506 | { 507 | let z = G2Affine::zero().into_compressed(); 508 | 509 | { 510 | let mut z = z; 511 | z.as_mut()[0] &= 0b0111_1111; 512 | if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { 513 | // :) 514 | } else { 515 | panic!("should have rejected the point because we expected a compressed point"); 516 | } 517 | } 518 | 519 | { 520 | let mut z = z; 521 | z.as_mut()[0] |= 0b0010_0000; 522 | if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { 523 | // :) 524 | } else { 525 | panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); 526 | } 527 | } 528 | 529 | for i in 0..G2Compressed::size() { 530 | let mut z = z; 531 | z.as_mut()[i] |= 0b0000_0001; 532 | if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { 533 | // :) 534 | } else { 535 | panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); 536 | } 537 | } 538 | } 539 | 540 | let o = G2Affine::one().into_compressed(); 541 | 542 | { 543 | let mut o = o; 544 | o.as_mut()[0] &= 0b0111_1111; 545 | if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { 546 | // :) 547 | } else { 548 | panic!("should have rejected the point because we expected a compressed point"); 549 | } 550 | } 551 | 552 | let m = Fq::char(); 553 | 554 | { 555 | let mut o = o; 556 | m.write_be(&mut o.as_mut()[0..]).unwrap(); 557 | o.as_mut()[0] |= 0b1000_0000; 558 | 559 | if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { 560 | assert_eq!(coordinate, "x coordinate (c1)"); 561 | } else { 562 | panic!("should have rejected the point") 563 | } 564 | } 565 | 566 | { 567 | let mut o = o; 568 | m.write_be(&mut o.as_mut()[48..]).unwrap(); 569 | o.as_mut()[0] |= 0b1000_0000; 570 | 571 | if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { 572 | assert_eq!(coordinate, "x coordinate (c0)"); 573 | } else { 574 | panic!("should have rejected the point") 575 | } 576 | } 577 | 578 | { 579 | let mut o = o; 580 | let mut x = Fq2 { 581 | c0: Fq::one(), 582 | c1: Fq::one(), 583 | }; 584 | 585 | loop { 586 | let mut x3b = x; 587 | x3b.square(); 588 | x3b.mul_assign(&x); 589 | x3b.add_assign(&Fq2 { 590 | c0: Fq::from_repr(FqRepr::from(4)).unwrap(), 591 | c1: Fq::from_repr(FqRepr::from(4)).unwrap(), 592 | }); // TODO: perhaps expose coeff_b through API? 593 | 594 | if let Some(_) = x3b.sqrt() { 595 | x.add_assign(&Fq2::one()); 596 | } else { 597 | x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); 598 | x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); 599 | o.as_mut()[0] |= 0b1000_0000; 600 | 601 | if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { 602 | break; 603 | } else { 604 | panic!("should have rejected the point because it isn't on the curve") 605 | } 606 | } 607 | } 608 | } 609 | 610 | { 611 | let mut o = o; 612 | let mut x = Fq2 { 613 | c0: Fq::one(), 614 | c1: Fq::one(), 615 | }; 616 | 617 | loop { 618 | let mut x3b = x; 619 | x3b.square(); 620 | x3b.mul_assign(&x); 621 | x3b.add_assign(&Fq2 { 622 | c0: Fq::from_repr(FqRepr::from(4)).unwrap(), 623 | c1: Fq::from_repr(FqRepr::from(4)).unwrap(), 624 | }); // TODO: perhaps expose coeff_b through API? 625 | 626 | if let Some(_) = x3b.sqrt() { 627 | // We know this is on the curve, but it's likely not going to be in the correct subgroup. 628 | x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); 629 | x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); 630 | o.as_mut()[0] |= 0b1000_0000; 631 | 632 | if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { 633 | break; 634 | } else { 635 | panic!( 636 | "should have rejected the point because it isn't in the correct subgroup" 637 | ) 638 | } 639 | } else { 640 | x.add_assign(&Fq2::one()); 641 | } 642 | } 643 | } 644 | } 645 | -------------------------------------------------------------------------------- /src/hash_to_curve.rs: -------------------------------------------------------------------------------- 1 | //! This module defines a hash_to_curve trait. 2 | 3 | use crate::bls12_381::{ClearH, IsogenyMap, OsswuMap}; 4 | use crate::hash_to_field::{hash_to_field, ExpandMsg, FromRO}; 5 | use crate::CurveProjective; 6 | 7 | type CoordT = ::Base; 8 | 9 | /// Random oracle and injective maps to curve. 10 | pub trait HashToCurve 11 | where 12 | X: ExpandMsg, 13 | { 14 | /// Random oracle. 15 | fn hash_to_curve, Dt: AsRef<[u8]>>(msg: Mt, dst: Dt) -> Self; 16 | 17 | /// Injective encoding. 18 | fn encode_to_curve, Dt: AsRef<[u8]>>(msg: Mt, dst: Dt) -> Self; 19 | } 20 | 21 | impl HashToCurve for PtT 22 | where 23 | PtT: ClearH + IsogenyMap + OsswuMap, 24 | CoordT: FromRO, 25 | X: ExpandMsg, 26 | { 27 | fn hash_to_curve, Dt: AsRef<[u8]>>(msg: Mt, dst: Dt) -> PtT { 28 | let mut p = { 29 | let u = hash_to_field::, X>(msg.as_ref(), dst.as_ref(), 2); 30 | let mut tmp = PtT::osswu_map(&u[0]); 31 | tmp.add_assign(&PtT::osswu_map(&u[1])); 32 | tmp 33 | }; 34 | p.isogeny_map(); 35 | p.clear_h(); 36 | p 37 | } 38 | 39 | fn encode_to_curve, Dt: AsRef<[u8]>>(msg: Mt, dst: Dt) -> PtT { 40 | let mut p = { 41 | let u = hash_to_field::, X>(msg.as_ref(), dst.as_ref(), 1); 42 | PtT::osswu_map(&u[0]) 43 | }; 44 | p.isogeny_map(); 45 | p.clear_h(); 46 | p 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/hash_to_field.rs: -------------------------------------------------------------------------------- 1 | //! This module implements hash_to_field and related hashing primitives 2 | //! for use with BLS signatures. 3 | 4 | use digest::generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; 5 | use digest::{BlockInput, Digest, ExtendableOutput, Update}; 6 | use std::marker::PhantomData; 7 | 8 | /// Hash to field for they type `T` using ExpandMsg variant `X`. 9 | pub fn hash_to_field(msg: &[u8], dst: &[u8], count: usize) -> Vec 10 | where 11 | T: FromRO, 12 | X: ExpandMsg, 13 | { 14 | let len_per_elm = ::Length::to_usize(); 15 | let len_in_bytes = count * len_per_elm; 16 | let pseudo_random_bytes = X::expand_message(msg, dst, len_in_bytes); 17 | 18 | let mut ret = Vec::::with_capacity(count); 19 | for idx in 0..count { 20 | let bytes_to_convert = &pseudo_random_bytes[idx * len_per_elm..(idx + 1) * len_per_elm]; 21 | let bytes_arr = GenericArray::::Length>::from_slice(bytes_to_convert); 22 | ret.push(T::from_ro(bytes_arr)); 23 | } 24 | 25 | ret 26 | } 27 | 28 | /// Generate a field element from a random string of bytes. 29 | pub trait FromRO { 30 | type Length: ArrayLength; 31 | 32 | fn from_ro(okm: &GenericArray::Length>) -> Self; 33 | } 34 | 35 | /// BaseFromRO is a FromRO impl for a field with extension degree 1. 36 | impl FromRO for T { 37 | type Length = ::BaseLength; 38 | 39 | fn from_ro(okm: &GenericArray::Length>) -> T { 40 | T::from_okm(okm) 41 | } 42 | } 43 | 44 | /// Generate an element of a base field for a random string of bytes 45 | /// (used by FromRO for extension fields). 46 | pub trait BaseFromRO { 47 | type BaseLength: ArrayLength; 48 | 49 | fn from_okm(okm: &GenericArray::BaseLength>) -> Self; 50 | } 51 | 52 | /// Trait for types implementing expand_message interface for hash_to_field 53 | pub trait ExpandMsg { 54 | fn expand_message(msg: &[u8], dst: &[u8], len_in_bytes: usize) -> Vec; 55 | } 56 | 57 | /// Placeholder type for implementing expand_message_xof based on a hash function 58 | #[derive(Debug)] 59 | pub struct ExpandMsgXof { 60 | phantom: PhantomData, 61 | } 62 | 63 | /// ExpandMsgXof implements expand_message_xof for the ExpandMsg trait 64 | impl ExpandMsg for ExpandMsgXof 65 | where 66 | HashT: Default + ExtendableOutput + Update, 67 | { 68 | fn expand_message(msg: &[u8], dst: &[u8], len_in_bytes: usize) -> Vec { 69 | assert!( 70 | dst.len() < 256, 71 | "dst of more than 255bytes is not supported" 72 | ); 73 | HashT::default() 74 | .chain(msg) 75 | .chain([(len_in_bytes >> 8) as u8, len_in_bytes as u8]) 76 | .chain(dst) 77 | .chain([dst.len() as u8]) 78 | .finalize_boxed(len_in_bytes) 79 | .to_vec() 80 | } 81 | } 82 | 83 | /// Placeholder type for implementing `expand_message_xmd` based on a hash function. 84 | #[derive(Debug)] 85 | pub struct ExpandMsgXmd { 86 | phantom: PhantomData, 87 | } 88 | 89 | /// ExpandMsgXmd implements expand_message_xmd for the ExpandMsg trait 90 | impl ExpandMsg for ExpandMsgXmd 91 | where 92 | HashT: Digest + BlockInput, 93 | { 94 | fn expand_message(msg: &[u8], dst: &[u8], len_in_bytes: usize) -> Vec { 95 | assert!( 96 | dst.len() < 256, 97 | "dst of more than 255bytes is not supported" 98 | ); 99 | let b_in_bytes = ::OutputSize::to_usize(); 100 | let ell = (len_in_bytes + b_in_bytes - 1) / b_in_bytes; 101 | if ell > 255 { 102 | panic!("ell was too big in expand_message_xmd"); 103 | } 104 | let b_0 = HashT::new() 105 | .chain(GenericArray::::BlockSize>::default()) 106 | .chain(msg) 107 | .chain([(len_in_bytes >> 8) as u8, len_in_bytes as u8, 0u8]) 108 | .chain(dst) 109 | .chain([dst.len() as u8]) 110 | .finalize(); 111 | 112 | let mut b_vals = Vec::::with_capacity(ell * b_in_bytes); 113 | // b_1 114 | b_vals.extend_from_slice( 115 | HashT::new() 116 | .chain(&b_0[..]) 117 | .chain([1u8]) 118 | .chain(dst) 119 | .chain([dst.len() as u8]) 120 | .finalize() 121 | .as_ref(), 122 | ); 123 | 124 | for idx in 1..ell { 125 | // b_0 XOR b_(idx - 1) 126 | let mut tmp = GenericArray::::OutputSize>::default(); 127 | b_0.iter() 128 | .zip(&b_vals[(idx - 1) * b_in_bytes..idx * b_in_bytes]) 129 | .enumerate() 130 | .for_each(|(jdx, (b0val, bi1val))| tmp[jdx] = b0val ^ bi1val); 131 | b_vals.extend_from_slice( 132 | HashT::new() 133 | .chain(tmp) 134 | .chain([(idx + 1) as u8]) 135 | .chain(dst) 136 | .chain([dst.len() as u8]) 137 | .finalize() 138 | .as_ref(), 139 | ); 140 | } 141 | 142 | b_vals.truncate(len_in_bytes); 143 | b_vals 144 | } 145 | } 146 | 147 | #[cfg(test)] 148 | mod tests { 149 | use super::*; 150 | use sha2::{Sha256, Sha512}; 151 | use sha3::Shake128; 152 | 153 | // Except internal variables, expand_message_xmd and expand_message_xof did not change 154 | // between draft 7 and draft 8 (https://tools.ietf.org/rfcdiff?difftype=--hwdiff&url2=draft-irtf-cfrg-hash-to-curve-08.txt). 155 | // So we can use test vector introduced in draft 8 to test the draft 7 implementation. 156 | 157 | /// From https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-08#appendix-I.1 158 | #[test] 159 | fn expand_message_xmd_works_for_draft8_testvectors_sha256() { 160 | let dst = b"QUUX-V01-CS02-with-expander"; 161 | 162 | let msg = b""; 163 | let len_in_bytes = 0x20; 164 | let uniform_bytes = 165 | hex::decode("f659819a6473c1835b25ea59e3d38914c98b374f0970b7e4c92181df928fca88") 166 | .unwrap(); 167 | assert_eq!( 168 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 169 | uniform_bytes 170 | ); 171 | 172 | let msg = b"abc"; 173 | let len_in_bytes = 0x20; 174 | let uniform_bytes = 175 | hex::decode("1c38f7c211ef233367b2420d04798fa4698080a8901021a795a1151775fe4da7") 176 | .unwrap(); 177 | assert_eq!( 178 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 179 | uniform_bytes 180 | ); 181 | 182 | let msg = b"abcdef0123456789"; 183 | let len_in_bytes = 0x20; 184 | let uniform_bytes = 185 | hex::decode("8f7e7b66791f0da0dbb5ec7c22ec637f79758c0a48170bfb7c4611bd304ece89") 186 | .unwrap(); 187 | assert_eq!( 188 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 189 | uniform_bytes 190 | ); 191 | 192 | let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; 193 | let len_in_bytes = 0x20; 194 | let uniform_bytes = 195 | hex::decode("72d5aa5ec810370d1f0013c0df2f1d65699494ee2a39f72e1716b1b964e1c642") 196 | .unwrap(); 197 | assert_eq!( 198 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 199 | uniform_bytes 200 | ); 201 | 202 | let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 203 | let len_in_bytes = 0x20; 204 | let uniform_bytes = 205 | hex::decode("3b8e704fc48336aca4c2a12195b720882f2162a4b7b13a9c350db46f429b771b") 206 | .unwrap(); 207 | assert_eq!( 208 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 209 | uniform_bytes 210 | ); 211 | 212 | let msg = b""; 213 | let len_in_bytes = 0x80; 214 | let uniform_bytes = 215 | hex::decode("8bcffd1a3cae24cf9cd7ab85628fd111bb17e3739d3b53f89580d217aa79526f1708354a76a402d3569d6a9d19ef3de4d0b991e4f54b9f20dcde9b95a66824cbdf6c1a963a1913d43fd7ac443a02fc5d9d8d77e2071b86ab114a9f34150954a7531da568a1ea8c760861c0cde2005afc2c114042ee7b5848f5303f0611cf297f") 216 | .unwrap(); 217 | assert_eq!( 218 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 219 | uniform_bytes 220 | ); 221 | 222 | let msg = b"abc"; 223 | let len_in_bytes = 0x80; 224 | let uniform_bytes = 225 | hex::decode("fe994ec51bdaa821598047b3121c149b364b178606d5e72bfbb713933acc29c186f316baecf7ea22212f2496ef3f785a27e84a40d8b299cec56032763eceeff4c61bd1fe65ed81decafff4a31d0198619c0aa0c6c51fca15520789925e813dcfd318b542f8799441271f4db9ee3b8092a7a2e8d5b75b73e28fb1ab6b4573c192") 226 | .unwrap(); 227 | assert_eq!( 228 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 229 | uniform_bytes 230 | ); 231 | 232 | let msg = b"abcdef0123456789"; 233 | let len_in_bytes = 0x80; 234 | let uniform_bytes = 235 | hex::decode("c9ec7941811b1e19ce98e21db28d22259354d4d0643e301175e2f474e030d32694e9dd5520dde93f3600d8edad94e5c364903088a7228cc9eff685d7eaac50d5a5a8229d083b51de4ccc3733917f4b9535a819b445814890b7029b5de805bf62b33a4dc7e24acdf2c924e9fe50d55a6b832c8c84c7f82474b34e48c6d43867be") 236 | .unwrap(); 237 | assert_eq!( 238 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 239 | uniform_bytes 240 | ); 241 | 242 | let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; 243 | let len_in_bytes = 0x80; 244 | let uniform_bytes = 245 | hex::decode("48e256ddba722053ba462b2b93351fc966026e6d6db493189798181c5f3feea377b5a6f1d8368d7453faef715f9aecb078cd402cbd548c0e179c4ed1e4c7e5b048e0a39d31817b5b24f50db58bb3720fe96ba53db947842120a068816ac05c159bb5266c63658b4f000cbf87b1209a225def8ef1dca917bcda79a1e42acd8069") 246 | .unwrap(); 247 | assert_eq!( 248 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 249 | uniform_bytes 250 | ); 251 | 252 | let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 253 | let len_in_bytes = 0x80; 254 | let uniform_bytes = 255 | hex::decode("396962db47f749ec3b5042ce2452b619607f27fd3939ece2746a7614fb83a1d097f554df3927b084e55de92c7871430d6b95c2a13896d8a33bc48587b1f66d21b128a1a8240d5b0c26dfe795a1a842a0807bb148b77c2ef82ed4b6c9f7fcb732e7f94466c8b51e52bf378fba044a31f5cb44583a892f5969dcd73b3fa128816e") 256 | .unwrap(); 257 | assert_eq!( 258 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 259 | uniform_bytes 260 | ); 261 | } 262 | 263 | /// From https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-08#appendix-I.2 264 | #[test] 265 | fn expand_message_xmd_works_for_draft8_testvectors_sha512() { 266 | let dst = b"QUUX-V01-CS02-with-expander"; 267 | 268 | let msg = b""; 269 | let len_in_bytes = 0x20; 270 | let uniform_bytes = 271 | hex::decode("2eaa1f7b5715f4736e6a5dbe288257abf1faa028680c1d938cd62ac699ead642") 272 | .unwrap(); 273 | assert_eq!( 274 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 275 | uniform_bytes 276 | ); 277 | 278 | let msg = b"abc"; 279 | let len_in_bytes = 0x20; 280 | let uniform_bytes = 281 | hex::decode("0eeda81f69376c80c0f8986496f22f21124cb3c562cf1dc608d2c13005553b0f") 282 | .unwrap(); 283 | assert_eq!( 284 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 285 | uniform_bytes 286 | ); 287 | 288 | let msg = b"abcdef0123456789"; 289 | let len_in_bytes = 0x20; 290 | let uniform_bytes = 291 | hex::decode("2e375fc05e05e80dbf3083796fde2911789d9e8847e1fcebf4ca4b36e239b338") 292 | .unwrap(); 293 | assert_eq!( 294 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 295 | uniform_bytes 296 | ); 297 | 298 | let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; 299 | let len_in_bytes = 0x20; 300 | let uniform_bytes = 301 | hex::decode("c37f9095fe7fe4f01c03c3540c1229e6ac8583b07510085920f62ec66acc0197") 302 | .unwrap(); 303 | assert_eq!( 304 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 305 | uniform_bytes 306 | ); 307 | 308 | let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 309 | let len_in_bytes = 0x20; 310 | let uniform_bytes = 311 | hex::decode("af57a7f56e9ed2aa88c6eab45c8c6e7638ae02da7c92cc04f6648c874ebd560e") 312 | .unwrap(); 313 | assert_eq!( 314 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 315 | uniform_bytes 316 | ); 317 | 318 | let msg = b""; 319 | let len_in_bytes = 0x80; 320 | let uniform_bytes = 321 | hex::decode("0687ce02eba5eb3faf1c3c539d1f04babd3c0f420edae244eeb2253b6c6d6865145c31458e824b4e87ca61c3442dc7c8c9872b0b7250aa33e0668ccebbd2b386de658ca11a1dcceb51368721ae6dcd2d4bc86eaebc4e0d11fa02ad053289c9b28a03da6c942b2e12c14e88dbde3b0ba619d6214f47212b628f3e1b537b66efcf") 322 | .unwrap(); 323 | assert_eq!( 324 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 325 | uniform_bytes 326 | ); 327 | 328 | let msg = b"abc"; 329 | let len_in_bytes = 0x80; 330 | let uniform_bytes = 331 | hex::decode("779ae4fd8a92f365e4df96b9fde97b40486bb005c1a2096c86f55f3d92875d89045fbdbc4a0e9f2d3e1e6bcd870b2d7131d868225b6fe72881a81cc5166b5285393f71d2e68bb0ac603479959370d06bdbe5f0d8bfd9af9494d1e4029bd68ab35a561341dd3f866b3ef0c95c1fdfaab384ce24a23427803dda1db0c7d8d5344a") 332 | .unwrap(); 333 | assert_eq!( 334 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 335 | uniform_bytes 336 | ); 337 | 338 | let msg = b"abcdef0123456789"; 339 | let len_in_bytes = 0x80; 340 | let uniform_bytes = 341 | hex::decode("f0953d28846a50e9f88b7ae35b643fc43733c9618751b569a73960c655c068db7b9f044ad5a40d49d91c62302eaa26163c12abfa982e2b5d753049e000adf7630ae117aeb1fb9b61fc724431ac68b369e12a9481b4294384c3c890d576a79264787bc8076e7cdabe50c044130e480501046920ff090c1a091c88391502f0fbac") 342 | .unwrap(); 343 | assert_eq!( 344 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 345 | uniform_bytes 346 | ); 347 | 348 | let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; 349 | let len_in_bytes = 0x80; 350 | let uniform_bytes = 351 | hex::decode("64d3e59f0bc3c5e653011c914b419ba8310390a9585311fddb26791d26663bd71971c347e1b5e88ba9274d2445ed9dcf48eea9528d807b7952924159b7c27caa4f25a2ea94df9508e70a7012dfce0e8021b37e59ea21b80aa9af7f1a1f2efa4fbe523c4266ce7d342acaacd438e452c501c131156b4945515e9008d2b155c258") 352 | .unwrap(); 353 | assert_eq!( 354 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 355 | uniform_bytes 356 | ); 357 | 358 | let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 359 | let len_in_bytes = 0x80; 360 | let uniform_bytes = 361 | hex::decode("01524feea5b22f6509f6b1e805c97df94faf4d821b01aadeebc89e9daaed0733b4544e50852fd3e019d58eaad6d267a134c8bc2c08bc46c10bfeff3ee03110bcd8a0d695d75a34092bd8b677bdd369a13325549abab54f4ac907b712bdd3567f38c4554c51902b735b81f43a7ef6f938c7690d107c052c7e7b795ac635b3200a") 362 | .unwrap(); 363 | assert_eq!( 364 | ExpandMsgXmd::::expand_message(msg, dst, len_in_bytes), 365 | uniform_bytes 366 | ); 367 | } 368 | 369 | /// From https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-08#appendix-I.3 370 | #[test] 371 | fn expand_message_xof_works_for_draft8_testvectors_shake128() { 372 | let dst = b"QUUX-V01-CS02-with-expander"; 373 | 374 | let msg = b""; 375 | let len_in_bytes = 0x20; 376 | let uniform_bytes = 377 | hex::decode("eca3fe8f7f5f1d52d7ed3691c321adc7d2a0fef1f843d221f7002530070746de") 378 | .unwrap(); 379 | assert_eq!( 380 | ExpandMsgXof::::expand_message(msg, dst, len_in_bytes), 381 | uniform_bytes 382 | ); 383 | 384 | let msg = b"abc"; 385 | let len_in_bytes = 0x20; 386 | let uniform_bytes = 387 | hex::decode("c79b8ea0af10fd8871eda98334ea9d54e9e5282be97521678f987718b187bc08") 388 | .unwrap(); 389 | assert_eq!( 390 | ExpandMsgXof::::expand_message(msg, dst, len_in_bytes), 391 | uniform_bytes 392 | ); 393 | 394 | let msg = b"abcdef0123456789"; 395 | let len_in_bytes = 0x20; 396 | let uniform_bytes = 397 | hex::decode("fb6f4af2a83f6276e9d41784f1e29da5e27566167c33e5cf2682c30096878b73") 398 | .unwrap(); 399 | assert_eq!( 400 | ExpandMsgXof::::expand_message(msg, dst, len_in_bytes), 401 | uniform_bytes 402 | ); 403 | 404 | let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; 405 | let len_in_bytes = 0x20; 406 | let uniform_bytes = 407 | hex::decode("125d05850db915e0683d17d044d87477e6e7b3f70a450dd097761e18d1d1dcdf") 408 | .unwrap(); 409 | assert_eq!( 410 | ExpandMsgXof::::expand_message(msg, dst, len_in_bytes), 411 | uniform_bytes 412 | ); 413 | 414 | let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 415 | let len_in_bytes = 0x20; 416 | let uniform_bytes = 417 | hex::decode("beafd026cb942c86f6a2b31bb8e6bf7173fb1b0caf3c21ea4b3b9d05d904fd23") 418 | .unwrap(); 419 | assert_eq!( 420 | ExpandMsgXof::::expand_message(msg, dst, len_in_bytes), 421 | uniform_bytes 422 | ); 423 | 424 | let msg = b""; 425 | let len_in_bytes = 0x80; 426 | let uniform_bytes = 427 | hex::decode("15733b3fb22fac0e0902c220aeea48e5e47d39f36c2cc03eac34367c48f2a3ebbcb3baa8a0cf17ab12fff4defc7ce22aed47188b6c163e828741473bd89cc646a082cb68b8e835b1374ea9a6315d61db0043f4abf506c26386e84668e077c85ebd9d632f4390559b979e70e9e7affbd0ac2a212c03b698efbbe940f2d164732b") 428 | .unwrap(); 429 | assert_eq!( 430 | ExpandMsgXof::::expand_message(msg, dst, len_in_bytes), 431 | uniform_bytes 432 | ); 433 | 434 | let msg = b"abc"; 435 | let len_in_bytes = 0x80; 436 | let uniform_bytes = 437 | hex::decode("4ccafb6d95b91537798d1fbb25b9fbe1a5bbe1683f43a4f6f03ef540b811235317bfc0aefb217faca055e1b8f32dfde9eb102cdc026ed27caa71530e361b3adbb92ccf68da35aed8b9dc7e4e6b5db0666c607a31df05513ddaf4c8ee23b0ee7f395a6e8be32eb13ca97da289f2643616ac30fe9104bb0d3a67a0a525837c2dc6") 438 | .unwrap(); 439 | assert_eq!( 440 | ExpandMsgXof::::expand_message(msg, dst, len_in_bytes), 441 | uniform_bytes 442 | ); 443 | 444 | let msg = b"abcdef0123456789"; 445 | let len_in_bytes = 0x80; 446 | let uniform_bytes = 447 | hex::decode("c8ee0e12736efbc9b47781db9d1e5db9c853684344a6776eb362d75b354f4b74cf60ba1373dc2e22c68efb76a022ed5391f67c77990802018c8cdc7af6d00c86b66a3b3ccad3f18d90f4437a165186f6601cf0bb281ea5d80d1de20fe22bb2e2d8acab0c043e76e3a0f34e0a1e66c9ade4fef9ef3b431130ad6f232babe9fe68") 448 | .unwrap(); 449 | assert_eq!( 450 | ExpandMsgXof::::expand_message(msg, dst, len_in_bytes), 451 | uniform_bytes 452 | ); 453 | 454 | let msg = b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; 455 | let len_in_bytes = 0x80; 456 | let uniform_bytes = 457 | hex::decode("3eebe6721b2ec746629856dc2dd3f03a830dabfefd7e2d1e72aaf2127d6ad17c988b5762f32e6edf61972378a4106dc4b63fa108ad03b793eedf4588f34c4df2a95b30995a464cb3ee31d6dca30adbfc90ffdf5414d7893082c55b269d9ec9cd6d2a715b9c4fad4eb70ed56f878b55a17b5994ef0de5b338675aad35354195cd") 458 | .unwrap(); 459 | assert_eq!( 460 | ExpandMsgXof::::expand_message(msg, dst, len_in_bytes), 461 | uniform_bytes 462 | ); 463 | 464 | let msg = b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 465 | let len_in_bytes = 0x80; 466 | let uniform_bytes = 467 | hex::decode("858cb4a6a5668a97d0f7039b5d6d574dde18dd2323cf6b203945c66df86477d1f747b46401903b3fa66d1276108ea7187b4411b7499acf4600080ce34ff6d21555c2af16f091adf8b285c8439f2e47fa0553c3a6ef5a4227a13f34406241b7d7fd8853a080bad25ec4804cdfe4fda500e1c872e71b8c61a8e160691894b96058") 468 | .unwrap(); 469 | assert_eq!( 470 | ExpandMsgXof::::expand_message(msg, dst, len_in_bytes), 471 | uniform_bytes 472 | ); 473 | } 474 | } 475 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A library for working with pairing-friendly curves. 2 | 3 | // `clippy` is a code linting tool for improving code quality by catching 4 | // common mistakes or strange code patterns. If the `cargo-clippy` feature 5 | // is provided, all compiler warnings are prohibited. 6 | #![cfg_attr(feature = "cargo-clippy", deny(warnings))] 7 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))] 8 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))] 9 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] 10 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))] 11 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] 12 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))] 13 | // Catch documentation errors caused by code changes. 14 | #![deny(intra_doc_link_resolution_failure)] 15 | // Force public structures to implement Debug 16 | #![deny(missing_debug_implementations)] 17 | 18 | #[cfg(test)] 19 | pub mod tests; 20 | 21 | pub mod bls12_381; 22 | mod hash_to_curve; 23 | mod hash_to_field; 24 | mod signum; 25 | 26 | pub use self::hash_to_curve::HashToCurve; 27 | pub use self::hash_to_field::{hash_to_field, BaseFromRO, ExpandMsgXmd, ExpandMsgXof, FromRO}; 28 | pub use self::signum::{Sgn0Result, Signum0}; 29 | 30 | use fff::{Field, PrimeField, ScalarEngine, SqrtField}; 31 | use groupy::{CurveAffine, CurveProjective}; 32 | 33 | /// This traits enables reading and writing a compressed version. 34 | pub trait Compress: Sized { 35 | fn write_compressed(self, out: W) -> std::io::Result<()>; 36 | fn read_compressed(source: R) -> std::io::Result; 37 | } 38 | 39 | /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) 40 | /// with well-defined relationships. In particular, the G1/G2 curve groups are 41 | /// of prime order `r`, and are equipped with a bilinear pairing function. 42 | pub trait Engine: ScalarEngine { 43 | /// The projective representation of an element in G1. 44 | type G1: CurveProjective 45 | + From; 46 | 47 | /// The affine representation of an element in G1. 48 | type G1Affine: PairingCurveAffine< 49 | Engine = Self, 50 | Base = Self::Fq, 51 | Scalar = Self::Fr, 52 | Projective = Self::G1, 53 | Pair = Self::G2Affine, 54 | PairingResult = Self::Fqk, 55 | > + From; 56 | 57 | /// The projective representation of an element in G2. 58 | type G2: CurveProjective 59 | + From; 60 | 61 | /// The affine representation of an element in G2. 62 | type G2Affine: PairingCurveAffine< 63 | Engine = Self, 64 | Base = Self::Fqe, 65 | Scalar = Self::Fr, 66 | Projective = Self::G2, 67 | Pair = Self::G1Affine, 68 | PairingResult = Self::Fqk, 69 | > + From; 70 | 71 | /// The base field that hosts G1. 72 | type Fq: PrimeField + SqrtField; 73 | 74 | /// The extension field that hosts G2. 75 | type Fqe: SqrtField; 76 | 77 | /// The extension field that hosts the target group of the pairing. 78 | type Fqk: Field + Compress; 79 | 80 | /// Perform a miller loop with some number of (G1, G2) pairs. 81 | fn miller_loop<'a, I>(i: I) -> Self::Fqk 82 | where 83 | I: IntoIterator< 84 | Item = &'a ( 85 | &'a ::Prepared, 86 | &'a ::Prepared, 87 | ), 88 | >; 89 | 90 | /// Perform final exponentiation of the result of a miller loop. 91 | fn final_exponentiation(_: &Self::Fqk) -> Option; 92 | 93 | /// Performs a complete pairing operation `(p, q)`. 94 | fn pairing(p: G1, q: G2) -> Self::Fqk 95 | where 96 | G1: Into, 97 | G2: Into, 98 | { 99 | Self::final_exponentiation(&Self::miller_loop( 100 | [(&(p.into().prepare()), &(q.into().prepare()))].iter(), 101 | )) 102 | .unwrap() 103 | } 104 | } 105 | 106 | /// Affine representation of an elliptic curve point that can be used 107 | /// to perform pairings. 108 | pub trait PairingCurveAffine: CurveAffine { 109 | type Prepared: Clone + Send + Sync + 'static; 110 | type Pair: PairingCurveAffine; 111 | type PairingResult: Field; 112 | 113 | /// Prepares this element for pairing purposes. 114 | fn prepare(&self) -> Self::Prepared; 115 | 116 | /// Perform a pairing 117 | fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; 118 | } 119 | -------------------------------------------------------------------------------- /src/signum.rs: -------------------------------------------------------------------------------- 1 | //! Signum trait: sgn0 for field elements 2 | 3 | use fff::Field; 4 | use std::ops::{BitAnd, BitXor}; 5 | 6 | /// Result of Sgn0. 7 | #[derive(Debug, PartialEq, Eq)] 8 | pub enum Sgn0Result { 9 | /// Either 0 or positive 10 | NonNegative, 11 | /// Neither 0 nor positive 12 | Negative, 13 | } 14 | 15 | impl From for Sgn0Result { 16 | fn from(val: bool) -> Self { 17 | if val { 18 | // Negative values = 1 19 | Sgn0Result::Negative 20 | } else { 21 | // Non negative values = 0 22 | Sgn0Result::NonNegative 23 | } 24 | } 25 | } 26 | 27 | impl BitAnd for Sgn0Result { 28 | type Output = Self; 29 | 30 | fn bitand(self, rhs: Self) -> Self::Output { 31 | if self == rhs && self == Sgn0Result::Negative { 32 | // 1 & 1 == 1 33 | Sgn0Result::Negative 34 | } else { 35 | Sgn0Result::NonNegative 36 | } 37 | } 38 | } 39 | 40 | impl BitXor for Sgn0Result { 41 | type Output = Self; 42 | fn bitxor(self, rhs: Self) -> Self { 43 | if self == rhs { 44 | Sgn0Result::NonNegative 45 | } else { 46 | Sgn0Result::Negative 47 | } 48 | } 49 | } 50 | 51 | /// Signum computations and conditional in-place negation. 52 | pub trait Signum0: Field { 53 | /// Returns either Negative or NonNegative. 54 | fn sgn0(&self) -> Sgn0Result; 55 | 56 | /// Negate if the argument is Negative. 57 | fn negate_if(&mut self, sgn: Sgn0Result) { 58 | if sgn == Sgn0Result::Negative { 59 | self.negate(); 60 | } 61 | } 62 | } 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | use super::*; 67 | #[test] 68 | #[allow(clippy::eq_op)] 69 | fn test_sgn0result_xor() { 70 | assert_eq!( 71 | Sgn0Result::Negative ^ Sgn0Result::Negative, 72 | Sgn0Result::NonNegative 73 | ); 74 | assert_eq!( 75 | Sgn0Result::Negative ^ Sgn0Result::NonNegative, 76 | Sgn0Result::Negative 77 | ); 78 | assert_eq!( 79 | Sgn0Result::NonNegative ^ Sgn0Result::Negative, 80 | Sgn0Result::Negative 81 | ); 82 | assert_eq!( 83 | Sgn0Result::NonNegative ^ Sgn0Result::NonNegative, 84 | Sgn0Result::NonNegative 85 | ); 86 | } 87 | 88 | #[test] 89 | #[allow(clippy::eq_op)] 90 | fn test_sgn0result_and() { 91 | assert_eq!( 92 | Sgn0Result::Negative & Sgn0Result::Negative, 93 | Sgn0Result::Negative 94 | ); 95 | assert_eq!( 96 | Sgn0Result::Negative & Sgn0Result::NonNegative, 97 | Sgn0Result::NonNegative 98 | ); 99 | assert_eq!( 100 | Sgn0Result::NonNegative & Sgn0Result::Negative, 101 | Sgn0Result::NonNegative 102 | ); 103 | assert_eq!( 104 | Sgn0Result::NonNegative & Sgn0Result::NonNegative, 105 | Sgn0Result::NonNegative 106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/tests/engine.rs: -------------------------------------------------------------------------------- 1 | use groupy::{CurveAffine, CurveProjective}; 2 | use rand_core::SeedableRng; 3 | use rand_xorshift::XorShiftRng; 4 | 5 | use crate::{Engine, Field, PairingCurveAffine, PrimeField}; 6 | 7 | pub fn engine_tests() { 8 | let mut rng = XorShiftRng::from_seed([ 9 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 10 | 0xe5, 11 | ]); 12 | 13 | for _ in 0..10 { 14 | let a = E::G1::random(&mut rng).into_affine(); 15 | let b = E::G2::random(&mut rng).into_affine(); 16 | 17 | assert!(a.pairing_with(&b) == b.pairing_with(&a)); 18 | assert!(a.pairing_with(&b) == E::pairing(a, b)); 19 | } 20 | 21 | for _ in 0..1000 { 22 | let z1 = E::G1Affine::zero().prepare(); 23 | let z2 = E::G2Affine::zero().prepare(); 24 | 25 | let a = E::G1::random(&mut rng).into_affine().prepare(); 26 | let b = E::G2::random(&mut rng).into_affine().prepare(); 27 | let c = E::G1::random(&mut rng).into_affine().prepare(); 28 | let d = E::G2::random(&mut rng).into_affine().prepare(); 29 | 30 | assert_eq!( 31 | E::Fqk::one(), 32 | E::final_exponentiation(&E::miller_loop(&[(&z1, &b)])).unwrap() 33 | ); 34 | 35 | assert_eq!( 36 | E::Fqk::one(), 37 | E::final_exponentiation(&E::miller_loop(&[(&a, &z2)])).unwrap() 38 | ); 39 | 40 | assert_eq!( 41 | E::final_exponentiation(&E::miller_loop(&[(&z1, &b), (&c, &d)])).unwrap(), 42 | E::final_exponentiation(&E::miller_loop(&[(&a, &z2), (&c, &d)])).unwrap() 43 | ); 44 | 45 | assert_eq!( 46 | E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&z1, &d)])).unwrap(), 47 | E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &z2)])).unwrap() 48 | ); 49 | } 50 | 51 | random_bilinearity_tests::(); 52 | random_miller_loop_tests::(); 53 | } 54 | 55 | fn random_miller_loop_tests() { 56 | let mut rng = XorShiftRng::from_seed([ 57 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 58 | 0xe5, 59 | ]); 60 | 61 | // Exercise the miller loop for a reduced pairing 62 | for _ in 0..1000 { 63 | let a = E::G1::random(&mut rng); 64 | let b = E::G2::random(&mut rng); 65 | 66 | let p2 = E::pairing(a, b); 67 | 68 | let a = a.into_affine().prepare(); 69 | let b = b.into_affine().prepare(); 70 | 71 | let p1 = E::final_exponentiation(&E::miller_loop(&[(&a, &b)])).unwrap(); 72 | 73 | assert_eq!(p1, p2); 74 | } 75 | 76 | // Exercise a double miller loop 77 | for _ in 0..1000 { 78 | let a = E::G1::random(&mut rng); 79 | let b = E::G2::random(&mut rng); 80 | let c = E::G1::random(&mut rng); 81 | let d = E::G2::random(&mut rng); 82 | 83 | let ab = E::pairing(a, b); 84 | let cd = E::pairing(c, d); 85 | 86 | let mut abcd = ab; 87 | abcd.mul_assign(&cd); 88 | 89 | let a = a.into_affine().prepare(); 90 | let b = b.into_affine().prepare(); 91 | let c = c.into_affine().prepare(); 92 | let d = d.into_affine().prepare(); 93 | 94 | let abcd_with_double_loop = 95 | E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &d)])).unwrap(); 96 | 97 | assert_eq!(abcd, abcd_with_double_loop); 98 | } 99 | } 100 | 101 | fn random_bilinearity_tests() { 102 | let mut rng = XorShiftRng::from_seed([ 103 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 104 | 0xe5, 105 | ]); 106 | 107 | for _ in 0..1000 { 108 | let a = E::G1::random(&mut rng); 109 | let b = E::G2::random(&mut rng); 110 | 111 | let c = E::Fr::random(&mut rng); 112 | let d = E::Fr::random(&mut rng); 113 | 114 | let mut ac = a; 115 | ac.mul_assign(c); 116 | 117 | let mut ad = a; 118 | ad.mul_assign(d); 119 | 120 | let mut bc = b; 121 | bc.mul_assign(c); 122 | 123 | let mut bd = b; 124 | bd.mul_assign(d); 125 | 126 | let acbd = E::pairing(ac, bd); 127 | let adbc = E::pairing(ad, bc); 128 | 129 | let mut cd = c; 130 | cd.mul_assign(&d); 131 | 132 | let abcd = E::pairing(a, b).pow(cd.into_repr()); 133 | 134 | assert_eq!(acbd, adbc); 135 | assert_eq!(acbd, abcd); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/tests/field.rs: -------------------------------------------------------------------------------- 1 | use fff::{Field, LegendreSymbol, PrimeField, SqrtField}; 2 | use rand_core::{RngCore, SeedableRng}; 3 | use rand_xorshift::XorShiftRng; 4 | 5 | pub fn random_frobenius_tests>(characteristic: C, maxpower: usize) { 6 | let mut rng = XorShiftRng::from_seed([ 7 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 8 | 0xe5, 9 | ]); 10 | 11 | for _ in 0..100 { 12 | for i in 0..(maxpower + 1) { 13 | let mut a = F::random(&mut rng); 14 | let mut b = a; 15 | 16 | for _ in 0..i { 17 | a = a.pow(&characteristic); 18 | } 19 | b.frobenius_map(i); 20 | 21 | assert_eq!(a, b); 22 | } 23 | } 24 | } 25 | 26 | pub fn random_sqrt_tests() { 27 | let mut rng = XorShiftRng::from_seed([ 28 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 29 | 0xe5, 30 | ]); 31 | 32 | for _ in 0..10000 { 33 | let a = F::random(&mut rng); 34 | let mut b = a; 35 | b.square(); 36 | assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); 37 | 38 | let b = b.sqrt().unwrap(); 39 | let mut negb = b; 40 | negb.negate(); 41 | 42 | assert!(a == b || a == negb); 43 | } 44 | 45 | let mut c = F::one(); 46 | for _ in 0..10000 { 47 | let mut b = c; 48 | b.square(); 49 | assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); 50 | 51 | b = b.sqrt().unwrap(); 52 | 53 | if b != c { 54 | b.negate(); 55 | } 56 | 57 | assert_eq!(b, c); 58 | 59 | c.add_assign(&F::one()); 60 | } 61 | } 62 | 63 | pub fn random_field_tests() { 64 | let mut rng = XorShiftRng::from_seed([ 65 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 66 | 0xe5, 67 | ]); 68 | 69 | random_multiplication_tests::(&mut rng); 70 | random_addition_tests::(&mut rng); 71 | random_subtraction_tests::(&mut rng); 72 | random_negation_tests::(&mut rng); 73 | random_doubling_tests::(&mut rng); 74 | random_squaring_tests::(&mut rng); 75 | random_inversion_tests::(&mut rng); 76 | random_expansion_tests::(&mut rng); 77 | 78 | assert!(F::zero().is_zero()); 79 | { 80 | let mut z = F::zero(); 81 | z.negate(); 82 | assert!(z.is_zero()); 83 | } 84 | 85 | assert!(F::zero().inverse().is_none()); 86 | 87 | // Multiplication by zero 88 | { 89 | let mut a = F::random(&mut rng); 90 | a.mul_assign(&F::zero()); 91 | assert!(a.is_zero()); 92 | } 93 | 94 | // Addition by zero 95 | { 96 | let mut a = F::random(&mut rng); 97 | let copy = a; 98 | a.add_assign(&F::zero()); 99 | assert_eq!(a, copy); 100 | } 101 | } 102 | 103 | pub fn from_str_tests() { 104 | { 105 | let a = "84395729384759238745923745892374598234705297301958723458712394587103249587213984572934750213947582345792304758273458972349582734958273495872304598234"; 106 | let b = "38495729084572938457298347502349857029384609283450692834058293405982304598230458230495820394850293845098234059823049582309485203948502938452093482039"; 107 | let c = "3248875134290623212325429203829831876024364170316860259933542844758450336418538569901990710701240661702808867062612075657861768196242274635305077449545396068598317421057721935408562373834079015873933065667961469731886739181625866970316226171512545167081793907058686908697431878454091011239990119126"; 108 | 109 | let mut a = F::from_str(a).unwrap(); 110 | let b = F::from_str(b).unwrap(); 111 | let c = F::from_str(c).unwrap(); 112 | 113 | a.mul_assign(&b); 114 | 115 | assert_eq!(a, c); 116 | } 117 | 118 | { 119 | let mut rng = XorShiftRng::from_seed([ 120 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 121 | 0xbc, 0xe5, 122 | ]); 123 | 124 | for _ in 0..1000 { 125 | let n = rng.next_u64(); 126 | 127 | let a = F::from_str(&format!("{}", n)).unwrap(); 128 | let b = F::from_repr(n.into()).unwrap(); 129 | 130 | assert_eq!(a, b); 131 | } 132 | } 133 | 134 | assert!(F::from_str("").is_none()); 135 | assert!(F::from_str("0").unwrap().is_zero()); 136 | assert!(F::from_str("00").is_none()); 137 | assert!(F::from_str("00000000000").is_none()); 138 | } 139 | 140 | fn random_multiplication_tests(rng: &mut R) { 141 | for _ in 0..10000 { 142 | let a = F::random(rng); 143 | let b = F::random(rng); 144 | let c = F::random(rng); 145 | 146 | let mut t0 = a; // (a * b) * c 147 | t0.mul_assign(&b); 148 | t0.mul_assign(&c); 149 | 150 | let mut t1 = a; // (a * c) * b 151 | t1.mul_assign(&c); 152 | t1.mul_assign(&b); 153 | 154 | let mut t2 = b; // (b * c) * a 155 | t2.mul_assign(&c); 156 | t2.mul_assign(&a); 157 | 158 | assert_eq!(t0, t1); 159 | assert_eq!(t1, t2); 160 | } 161 | } 162 | 163 | fn random_addition_tests(rng: &mut R) { 164 | for _ in 0..10000 { 165 | let a = F::random(rng); 166 | let b = F::random(rng); 167 | let c = F::random(rng); 168 | 169 | let mut t0 = a; // (a + b) + c 170 | t0.add_assign(&b); 171 | t0.add_assign(&c); 172 | 173 | let mut t1 = a; // (a + c) + b 174 | t1.add_assign(&c); 175 | t1.add_assign(&b); 176 | 177 | let mut t2 = b; // (b + c) + a 178 | t2.add_assign(&c); 179 | t2.add_assign(&a); 180 | 181 | assert_eq!(t0, t1); 182 | assert_eq!(t1, t2); 183 | } 184 | } 185 | 186 | fn random_subtraction_tests(rng: &mut R) { 187 | for _ in 0..10000 { 188 | let b = F::random(rng); 189 | let a = F::random(rng); 190 | 191 | let mut t0 = a; // (a - b) 192 | t0.sub_assign(&b); 193 | 194 | let mut t1 = b; // (b - a) 195 | t1.sub_assign(&a); 196 | 197 | let mut t2 = t0; // (a - b) + (b - a) = 0 198 | t2.add_assign(&t1); 199 | 200 | assert!(t2.is_zero()); 201 | } 202 | } 203 | 204 | fn random_negation_tests(rng: &mut R) { 205 | for _ in 0..10000 { 206 | let a = F::random(rng); 207 | let mut b = a; 208 | b.negate(); 209 | b.add_assign(&a); 210 | 211 | assert!(b.is_zero()); 212 | } 213 | } 214 | 215 | fn random_doubling_tests(rng: &mut R) { 216 | for _ in 0..10000 { 217 | let mut a = F::random(rng); 218 | let mut b = a; 219 | a.add_assign(&b); 220 | b.double(); 221 | 222 | assert_eq!(a, b); 223 | } 224 | } 225 | 226 | fn random_squaring_tests(rng: &mut R) { 227 | for _ in 0..10000 { 228 | let mut a = F::random(rng); 229 | let mut b = a; 230 | a.mul_assign(&b); 231 | b.square(); 232 | 233 | assert_eq!(a, b); 234 | } 235 | } 236 | 237 | fn random_inversion_tests(rng: &mut R) { 238 | assert!(F::zero().inverse().is_none()); 239 | 240 | for _ in 0..10000 { 241 | let mut a = F::random(rng); 242 | let b = a.inverse().unwrap(); // probablistically nonzero 243 | a.mul_assign(&b); 244 | 245 | assert_eq!(a, F::one()); 246 | } 247 | } 248 | 249 | fn random_expansion_tests(rng: &mut R) { 250 | for _ in 0..10000 { 251 | // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) 252 | 253 | let a = F::random(rng); 254 | let b = F::random(rng); 255 | let c = F::random(rng); 256 | let d = F::random(rng); 257 | 258 | let mut t0 = a; 259 | t0.add_assign(&b); 260 | let mut t1 = c; 261 | t1.add_assign(&d); 262 | t0.mul_assign(&t1); 263 | 264 | let mut t2 = a; 265 | t2.mul_assign(&c); 266 | let mut t3 = b; 267 | t3.mul_assign(&c); 268 | let mut t4 = a; 269 | t4.mul_assign(&d); 270 | let mut t5 = b; 271 | t5.mul_assign(&d); 272 | 273 | t2.add_assign(&t3); 274 | t2.add_assign(&t4); 275 | t2.add_assign(&t5); 276 | 277 | assert_eq!(t0, t2); 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod engine; 2 | pub mod field; 3 | pub mod repr; 4 | -------------------------------------------------------------------------------- /src/tests/repr.rs: -------------------------------------------------------------------------------- 1 | use fff::{PrimeField, PrimeFieldRepr}; 2 | use rand_core::SeedableRng; 3 | use rand_xorshift::XorShiftRng; 4 | 5 | pub fn random_repr_tests() { 6 | random_encoding_tests::

(); 7 | random_shl_tests::

(); 8 | random_shr_tests::

(); 9 | } 10 | 11 | fn random_encoding_tests() { 12 | let mut rng = XorShiftRng::from_seed([ 13 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 14 | 0xe5, 15 | ]); 16 | 17 | for _ in 0..1000 { 18 | let r = P::random(&mut rng).into_repr(); 19 | 20 | // Big endian 21 | { 22 | let mut rdecoded =

::Repr::default(); 23 | 24 | let mut v: Vec = vec![]; 25 | r.write_be(&mut v).unwrap(); 26 | rdecoded.read_be(&v[0..]).unwrap(); 27 | 28 | assert_eq!(r, rdecoded); 29 | } 30 | 31 | // Little endian 32 | { 33 | let mut rdecoded =

::Repr::default(); 34 | 35 | let mut v: Vec = vec![]; 36 | r.write_le(&mut v).unwrap(); 37 | rdecoded.read_le(&v[0..]).unwrap(); 38 | 39 | assert_eq!(r, rdecoded); 40 | } 41 | 42 | { 43 | let mut rdecoded_le =

::Repr::default(); 44 | let mut rdecoded_be_flip =

::Repr::default(); 45 | 46 | let mut v: Vec = vec![]; 47 | r.write_le(&mut v).unwrap(); 48 | 49 | // This reads in little-endian, so we are done. 50 | rdecoded_le.read_le(&v[..]).unwrap(); 51 | 52 | // This reads in big-endian, so we perform a swap of the 53 | // bytes beforehand. 54 | let v: Vec = v.into_iter().rev().collect(); 55 | rdecoded_be_flip.read_be(&v[..]).unwrap(); 56 | 57 | assert_eq!(rdecoded_le, rdecoded_be_flip); 58 | } 59 | } 60 | } 61 | 62 | fn random_shl_tests() { 63 | let mut rng = XorShiftRng::from_seed([ 64 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 65 | 0xe5, 66 | ]); 67 | 68 | for _ in 0..100 { 69 | let r = P::random(&mut rng).into_repr(); 70 | 71 | for shift in 0..(r.num_bits() + 1) { 72 | let mut r1 = r; 73 | let mut r2 = r; 74 | 75 | for _ in 0..shift { 76 | r1.mul2(); 77 | } 78 | 79 | r2.shl(shift); 80 | 81 | assert_eq!(r1, r2); 82 | } 83 | } 84 | } 85 | 86 | fn random_shr_tests() { 87 | let mut rng = XorShiftRng::from_seed([ 88 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 89 | 0xe5, 90 | ]); 91 | 92 | for _ in 0..100 { 93 | let r = P::random(&mut rng).into_repr(); 94 | 95 | for shift in 0..(r.num_bits() + 1) { 96 | let mut r1 = r; 97 | let mut r2 = r; 98 | 99 | for _ in 0..shift { 100 | r1.div2(); 101 | } 102 | 103 | r2.shr(shift); 104 | 105 | assert_eq!(r1, r2); 106 | } 107 | } 108 | } 109 | --------------------------------------------------------------------------------