├── .github └── workflows │ └── dart.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example └── main.dart ├── lib ├── ed25519_edwards.dart └── src │ ├── const.dart │ ├── edwards25519.dart │ ├── numbers.dart │ └── util.dart ├── pubspec.yaml └── test ├── benchmark ├── ed25519_benchmark.dart └── rate_benchmark.dart └── ed25519_test.dart /.github/workflows/dart.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | name: Dart 7 | 8 | on: 9 | push: 10 | branches: [ master ] 11 | pull_request: 12 | branches: [ master ] 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | # Note: This workflow uses the latest stable version of the Dart SDK. 22 | # You can specify other versions if desired, see documentation here: 23 | # https://github.com/dart-lang/setup-dart/blob/main/README.md 24 | # - uses: dart-lang/setup-dart@v1 25 | - uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603 26 | 27 | - name: Install dependencies 28 | run: dart pub get 29 | 30 | # Uncomment this step to verify the use of 'dart format' on each commit. 31 | # - name: Verify formatting 32 | # run: dart format --output=none --set-exit-if-changed . 33 | 34 | # Consider passing '--fatal-infos' for slightly stricter analysis. 35 | - name: Analyze project source 36 | run: dart analyze 37 | 38 | # Your project will need to have tests in test/ and a dependency on 39 | # package:test for this step to succeed. Note that Flutter projects will 40 | # want to change this to 'flutter test'. 41 | - name: Run tests 42 | run: dart test 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .dart_tool/ 3 | .packages 4 | 5 | # Omit commiting pubspec.lock for library packages: 6 | # https://dart.dev/guides/libraries/private-files#pubspeclock 7 | pubspec.lock 8 | 9 | # Conventional directory for build outputs 10 | build/ 11 | 12 | # Directory created by dartdoc 13 | doc/api/ 14 | 15 | .idea 16 | .DS_Store 17 | *.iml 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.3.1 2 | Moved Number classes to dedicated library: adaptive_number [#5](https://github.com/Tougee/ed25519/pull/5), now we got a new dependencies 3 | 4 | ### 0.3.0 5 | A delightful solution to adapting web platform, thanks for [lemoony](https://github.com/lemoony)'s contribution! 6 | [Support dartjs by using fixnum/int64 on web instead](https://github.com/Tougee/ed25519/pull/4) 7 | 8 | 9 | ### 0.2.1 10 | - Add benchmark 11 | 12 | ### 0.2.0 13 | - Fix incompatibility's with flutter web 14 | 15 | ### 0.1.0 16 | - Nothing changed, just release 17 | 18 | ### 0.1.0-nullsafety.1 19 | - Upgrade dependencies 20 | 21 | ### 0.1.0-nullsafety.0 22 | - Null safety migration 23 | - Use 'crypto' instead of 'cryptography' 24 | 25 | ### 0.0.5 26 | Refine doc comments 27 | 28 | ### 0.0.4 29 | More tests from RFC 8032 30 | 31 | ## 0.0.3 32 | Fix List cast to Uint8List 33 | 34 | ## 0.0.2 35 | Add basic example 36 | 37 | ## 0.0.1 38 | Initial commit -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ed25519 2 | 3 | Dart port of ed25519 from [Golang ed25519](https://github.com/golang/crypto/tree/master/ed25519) 4 | 5 | 6 | [Pub package](https://pub.dev/packages/ed25519_edwards) 7 | 8 | ## Usage 9 | ```dart 10 | 11 | import 'package:ed25519_edwards/ed25519_edwards.dart' as ed; 12 | 13 | void signAndVerify() { 14 | var keyPair = ed.generateKey(); 15 | var privateKey = keyPair.privateKey; 16 | var publicKey = keyPair.publicKey; 17 | var message = utf8.encode('test message'); 18 | var sig = ed.sign(privateKey, message as Uint8List); 19 | var result = ed.verify(publicKey, message, sig); 20 | assert(result == true); 21 | 22 | var wrongMessage = utf8.encode('wrong message'); 23 | var wrongResult = ed.verify(publicKey, wrongMessage as Uint8List, sig); 24 | assert(wrongResult == false); 25 | } 26 | ``` 27 | 28 | 29 | ## APIs 30 | | Ed25519 | 31 | | :---: | 32 | | public | 33 | | seed | 34 | | newKeyFromSeed | 35 | | generateKey | 36 | | sign | 37 | | verify | 38 | 39 | | Edwards25519 | 40 | | :---: | 41 | | FeZero | 42 | | FeOne | 43 | | FeAdd | 44 | | FeSub | 45 | | FeCopy | 46 | | FeCMove | 47 | | FeFromBytes | 48 | | FeToBytes | 49 | | FeIsNegative | 50 | | FeIsNonZero | 51 | | FeNeg | 52 | | FeCombine | 53 | | FeMul | 54 | | FeSqaure | 55 | | FeSquare2 | 56 | | FeInvert | 57 | | GeDoubleScalarMultVartime | 58 | | PreComputedGroupElementCMove | 59 | | GeScalarMultBase | 60 | | ScMulAdd | 61 | | ScReduce | 62 | | ScMinimal | 63 | 64 | ## Benchmark 65 | 66 | Simulate from [pinenacl-dart Benchmark](https://github.com/ilap/pinenacl-dart/blob/master/benchmark/README.md) 67 | 68 | MacBook Pro (16-inch, 2019), macOS Big Sur, with 2.4GHz i9 32GB 69 | 70 | #### JiT (Dart VM) Benchmark 71 | 72 | > dart test test/benchmark/ed25519_benchmark.dart 73 | 74 | | type | rate | iterations | time | data throughput | 75 | |----------|:----------:|---------------|:-------:|:---------------:| 76 | | Ed25519 - sign | 50.54 MB/s | 254 iterations | 5025 ms | 254.00 MB | 77 | | Ed25519 - verify | 97.38 MB/s | 487 iterations | 5001 ms | 487.00 MB | 78 | 79 | #### AoT (native binary) 80 | 81 | > dart2native test/benchmark/ed25519_benchmark.dart -o ed25519_benchmark 82 | > ./ed25519_benchmark 83 | 84 | | type | rate | iterations | time | data throughput | 85 | |----------|:----------:|---------------|:-------:|:---------------:| 86 | | Ed25519 - sign | 21.64 MB/s | 109 iterations | 5036 ms | 109.00 MB | 87 | | Ed25519 - verify | 37.20 MB/s | 187 iterations | 5027 ms | 187.00 MB | 88 | 89 | #### JS (Dart2JS) benchmark (too slow, suggest using other libs for dart2js and flutter web) 90 | 91 | > dart test test/benchmark/ed25519_benchmark.dart -p chrome 92 | 93 | | type | rate | iterations | time | data throughput | 94 | |----------|:----------:|---------------|:-------:|:---------------:| 95 | | Ed25519 - sign | 797.03 KB/s | 4 iterations | 5139 ms | 4.00 MB | 96 | | Ed25519 - verify | 1.53 MB/s | 8 iterations | 5231 ms | 8.00 MB | 97 | 98 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Defines a default set of lint rules enforced for 2 | # projects at Google. For details and rationale, 3 | # see https://github.com/dart-lang/pedantic#enabled-lints. 4 | include: package:pedantic/analysis_options.yaml 5 | 6 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. 7 | # Uncomment to specify additional rules. 8 | # linter: 9 | # rules: 10 | # - camel_case_types 11 | 12 | analyzer: 13 | # exclude: 14 | # - path/to/excluded/files/** 15 | -------------------------------------------------------------------------------- /example/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:ed25519_edwards/ed25519_edwards.dart' as ed; 5 | 6 | void main() { 7 | signAndVerify(); 8 | } 9 | 10 | void signAndVerify() { 11 | var keyPair = ed.generateKey(); 12 | var privateKey = keyPair.privateKey; 13 | var publicKey = keyPair.publicKey; 14 | var message = utf8.encode('test message'); 15 | var sig = ed.sign(privateKey, message as Uint8List); 16 | var result = ed.verify(publicKey, message, sig); 17 | assert(result == true); 18 | 19 | var wrongMessage = utf8.encode('wrong message'); 20 | var wrongResult = ed.verify(publicKey, wrongMessage as Uint8List, sig); 21 | assert(wrongResult == false); 22 | } 23 | -------------------------------------------------------------------------------- /lib/ed25519_edwards.dart: -------------------------------------------------------------------------------- 1 | /// Package ed25519 implements the Ed25519 signature algorithm. See 2 | /// https://ed25519.cr.yp.to/. 3 | /// 4 | /// These functions are also compatible with the “Ed25519” function defined in 5 | /// RFC 8032. However, unlike RFC 8032's formulation, this package's private key 6 | /// representation includes a public key suffix to make multiple signing 7 | /// operations with the same key more efficient. This package refers to the RFC 8 | /// 8032 private key as the “seed”. 9 | 10 | library edwards25519; 11 | 12 | import 'dart:typed_data'; 13 | import 'package:convert/convert.dart'; 14 | import 'package:collection/collection.dart'; 15 | import 'package:crypto/crypto.dart'; 16 | import 'package:ed25519_edwards/src/edwards25519.dart'; 17 | import 'package:ed25519_edwards/src/util.dart'; 18 | 19 | /// PublicKeySize is the size, in bytes, of public keys as used in this package. 20 | const PublicKeySize = 32; 21 | 22 | /// PrivateKeySize is the size, in bytes, of private keys as used in this package. 23 | const PrivateKeySize = 64; 24 | 25 | /// SignatureSize is the size, in bytes, of signatures generated and verified by this package. 26 | const SignatureSize = 64; 27 | 28 | /// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032. 29 | const SeedSize = 32; 30 | 31 | /// PublicKey is the type of Ed25519 public keys. 32 | class PublicKey { 33 | List bytes; 34 | 35 | PublicKey(this.bytes); 36 | } 37 | 38 | /// PrivateKey is the type of Ed25519 private keys. 39 | class PrivateKey { 40 | List bytes; 41 | 42 | PrivateKey(this.bytes); 43 | } 44 | 45 | /// KeyPair is the type of Ed25519 public/private key pair. 46 | class KeyPair { 47 | final PrivateKey privateKey; 48 | 49 | final PublicKey publicKey; 50 | 51 | KeyPair(this.privateKey, this.publicKey); 52 | 53 | @override 54 | int get hashCode => publicKey.hashCode; 55 | 56 | @override 57 | bool operator ==(other) => 58 | other is KeyPair && 59 | publicKey == other.publicKey && 60 | privateKey == other.privateKey; 61 | } 62 | 63 | /// public returns the PublicKey corresponding to PrivateKey. 64 | PublicKey public(PrivateKey privateKey) { 65 | var publicKey = privateKey.bytes.sublist(32, 32 + PublicKeySize); 66 | return PublicKey(publicKey); 67 | } 68 | 69 | /// Seed returns the private key seed corresponding to priv. It is provided for 70 | /// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds 71 | /// in this package. 72 | Uint8List seed(PrivateKey privateKey) { 73 | var seed = privateKey.bytes.sublist(0, SeedSize); 74 | return seed as Uint8List; 75 | } 76 | 77 | /// GenerateKey generates a public/private key pair using entropy from secure random. 78 | KeyPair generateKey() { 79 | var seed = Uint8List(32); 80 | fillBytesWithSecureRandomNumbers(seed); 81 | var privateKey = newKeyFromSeed(seed); 82 | var publicKey = privateKey.bytes.sublist(32, PrivateKeySize); 83 | return KeyPair(privateKey, PublicKey(publicKey)); 84 | } 85 | 86 | /// NewKeyFromSeed calculates a private key from a seed. It will throw 87 | /// ArgumentError if seed.length is not SeedSize. 88 | /// This function is provided for interoperability with RFC 8032. 89 | /// RFC 8032's private keys correspond to seeds in this package. 90 | PrivateKey newKeyFromSeed(Uint8List seed) { 91 | if (seed.length != SeedSize) { 92 | throw ArgumentError('ed25519: bad seed length ${seed.length}'); 93 | } 94 | var h = sha512.convert(seed); 95 | var digest = h.bytes.sublist(0, 32); 96 | digest[0] &= 248; 97 | digest[31] &= 127; 98 | digest[31] |= 64; 99 | 100 | var A = ExtendedGroupElement(); 101 | var hBytes = digest.sublist(0); 102 | GeScalarMultBase(A, hBytes as Uint8List); 103 | var publicKeyBytes = Uint8List(32); 104 | A.ToBytes(publicKeyBytes); 105 | 106 | var privateKey = Uint8List(PrivateKeySize); 107 | arrayCopy(seed, 0, privateKey, 0, 32); 108 | arrayCopy(publicKeyBytes, 0, privateKey, 32, 32); 109 | return PrivateKey(privateKey); 110 | } 111 | 112 | /// Sign signs the message with privateKey and returns a signature. It will 113 | /// throw ArumentError if privateKey.bytes.length is not PrivateKeySize. 114 | Uint8List sign(PrivateKey privateKey, Uint8List message) { 115 | if (privateKey.bytes.length != PrivateKeySize) { 116 | throw ArgumentError( 117 | 'ed25519: bad privateKey length ${privateKey.bytes.length}'); 118 | } 119 | var h = sha512.convert(privateKey.bytes.sublist(0, 32)); 120 | var digest1 = h.bytes; 121 | var expandedSecretKey = digest1.sublist(0, 32); 122 | expandedSecretKey[0] &= 248; 123 | expandedSecretKey[31] &= 63; 124 | expandedSecretKey[31] |= 64; 125 | 126 | var output = AccumulatorSink(); 127 | var input = sha512.startChunkedConversion(output); 128 | input.add(digest1.sublist(32)); 129 | input.add(message); 130 | input.close(); 131 | var messageDigest = output.events.single.bytes; 132 | 133 | var messageDigestReduced = Uint8List(32); 134 | ScReduce(messageDigestReduced, messageDigest as Uint8List); 135 | var R = ExtendedGroupElement(); 136 | GeScalarMultBase(R, messageDigestReduced); 137 | 138 | var encodedR = Uint8List(32); 139 | R.ToBytes(encodedR); 140 | 141 | output = AccumulatorSink(); 142 | input = sha512.startChunkedConversion(output); 143 | input.add(encodedR); 144 | input.add(privateKey.bytes.sublist(32)); 145 | input.add(message); 146 | input.close(); 147 | var hramDigest = output.events.single.bytes; 148 | var hramDigestReduced = Uint8List(32); 149 | ScReduce(hramDigestReduced, hramDigest as Uint8List); 150 | 151 | var s = Uint8List(32); 152 | ScMulAdd(s, hramDigestReduced, expandedSecretKey as Uint8List, 153 | messageDigestReduced); 154 | 155 | var signature = Uint8List(SignatureSize); 156 | arrayCopy(encodedR, 0, signature, 0, 32); 157 | arrayCopy(s, 0, signature, 32, 32); 158 | 159 | return signature; 160 | } 161 | 162 | /// Verify reports whether sig is a valid signature of message by publicKey. It 163 | /// will throw ArgumentError if publicKey.bytes.length is not PublicKeySize. 164 | bool verify(PublicKey publicKey, Uint8List message, Uint8List sig) { 165 | if (publicKey.bytes.length != PublicKeySize) { 166 | throw ArgumentError( 167 | 'ed25519: bad publicKey length ${publicKey.bytes.length}'); 168 | } 169 | if (sig.length != SignatureSize || sig[63] & 224 != 0) { 170 | return false; 171 | } 172 | 173 | var A = ExtendedGroupElement(); 174 | var publicKeyBytes = Uint8List.fromList(publicKey.bytes); 175 | if (!A.FromBytes(publicKeyBytes)) { 176 | return false; 177 | } 178 | FeNeg(A.X, A.X); 179 | FeNeg(A.T, A.T); 180 | 181 | var output = AccumulatorSink(); 182 | var input = sha512.startChunkedConversion(output); 183 | input.add(sig.sublist(0, 32)); 184 | input.add(publicKeyBytes); 185 | input.add(message); 186 | input.close(); 187 | var digest = output.events.single.bytes; 188 | 189 | var hReduced = Uint8List(32); 190 | ScReduce(hReduced, digest as Uint8List); 191 | 192 | var R = ProjectiveGroupElement(); 193 | var s = sig.sublist(32); 194 | 195 | if (!ScMinimal(s)) { 196 | return false; 197 | } 198 | 199 | GeDoubleScalarMultVartime(R, hReduced, A, s); 200 | 201 | var checkR = Uint8List(32); 202 | R.ToBytes(checkR); 203 | return ListEquality().equals(sig.sublist(0, 32), checkR); 204 | } 205 | -------------------------------------------------------------------------------- /lib/src/edwards25519.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:adaptive_number/adaptive_number.dart'; 4 | import 'package:ed25519_edwards/src/numbers.dart'; 5 | 6 | import 'const.dart'; 7 | 8 | class FieldElement { 9 | late List innerList; 10 | 11 | FieldElement() { 12 | innerList = List.generate(10, (index) => Number.zero); 13 | } 14 | 15 | FieldElement.fromList(List list) { 16 | innerList = list.map((e) => Number(e)).toList(); 17 | } 18 | 19 | Number operator [](int index) { 20 | return innerList[index]; 21 | } 22 | 23 | void operator []=(int index, Number value) { 24 | innerList[index] = value; 25 | } 26 | 27 | int get length => innerList.length; 28 | } 29 | 30 | void fieldElementCopy( 31 | FieldElement src, int srcPos, FieldElement dest, int destPos, int length) { 32 | dest.innerList.setRange(destPos, length + destPos, src.innerList, srcPos); 33 | } 34 | 35 | void fieldElementFullCopy(FieldElement src, FieldElement dest) { 36 | fieldElementCopy(src, 0, dest, 0, dest.length); 37 | } 38 | 39 | var zero = FieldElement(); 40 | 41 | void FeZero(FieldElement fe) { 42 | fieldElementFullCopy(zero, fe); 43 | } 44 | 45 | void FeOne(FieldElement fe) { 46 | FeZero(fe); 47 | fe[0] = Number.one; 48 | } 49 | 50 | void FeAdd(FieldElement dst, FieldElement a, FieldElement b) { 51 | dst[0] = a[0] + b[0]; 52 | dst[1] = a[1] + b[1]; 53 | dst[2] = a[2] + b[2]; 54 | dst[3] = a[3] + b[3]; 55 | dst[4] = a[4] + b[4]; 56 | dst[5] = a[5] + b[5]; 57 | dst[6] = a[6] + b[6]; 58 | dst[7] = a[7] + b[7]; 59 | dst[8] = a[8] + b[8]; 60 | dst[9] = a[9] + b[9]; 61 | } 62 | 63 | void FeSub(FieldElement dst, FieldElement a, FieldElement b) { 64 | dst[0] = a[0] - b[0]; 65 | dst[1] = a[1] - b[1]; 66 | dst[2] = a[2] - b[2]; 67 | dst[3] = a[3] - b[3]; 68 | dst[4] = a[4] - b[4]; 69 | dst[5] = a[5] - b[5]; 70 | dst[6] = a[6] - b[6]; 71 | dst[7] = a[7] - b[7]; 72 | dst[8] = a[8] - b[8]; 73 | dst[9] = a[9] - b[9]; 74 | } 75 | 76 | void FeCopy(FieldElement dst, FieldElement src) { 77 | fieldElementFullCopy(src, dst); 78 | } 79 | 80 | /// Replace (f,g) with (g,g) if b == 1; 81 | /// replace (f,g) with (f,g) if b == 0. 82 | /// 83 | /// Preconditions: b in {0,1}. 84 | void FeCMove(FieldElement f, FieldElement g, Number b) { 85 | b = -b; 86 | f[0] ^= b & (f[0] ^ g[0]); 87 | f[1] ^= b & (f[1] ^ g[1]); 88 | f[2] ^= b & (f[2] ^ g[2]); 89 | f[3] ^= b & (f[3] ^ g[3]); 90 | f[4] ^= b & (f[4] ^ g[4]); 91 | f[5] ^= b & (f[5] ^ g[5]); 92 | f[6] ^= b & (f[6] ^ g[6]); 93 | f[7] ^= b & (f[7] ^ g[7]); 94 | f[8] ^= b & (f[8] ^ g[8]); 95 | f[9] ^= b & (f[9] ^ g[9]); 96 | } 97 | 98 | Number load3(Uint8List input) { 99 | int r; 100 | r = input[0]; 101 | r |= input[1] << 8; 102 | r |= input[2] << 16; 103 | return Number(r); 104 | } 105 | 106 | Number load4(Uint8List input) { 107 | int r; 108 | r = input[0]; 109 | r |= input[1] << 8; 110 | r |= input[2] << 16; 111 | r |= input[3] << 24; 112 | return Number(r); 113 | } 114 | 115 | void FeFromBytes(FieldElement dst, Uint8List src) { 116 | var h0 = load4(src.sublist(0, src.length)); 117 | var h1 = load3(src.sublist(4, src.length)) << 6; 118 | var h2 = load3(src.sublist(7, src.length)) << 5; 119 | var h3 = load3(src.sublist(10, src.length)) << 3; 120 | var h4 = load3(src.sublist(13, src.length)) << 2; 121 | var h5 = load4(src.sublist(16, src.length)); 122 | var h6 = load3(src.sublist(20, src.length)) << 7; 123 | var h7 = load3(src.sublist(23, src.length)) << 5; 124 | var h8 = load3(src.sublist(26, src.length)) << 4; 125 | var h9 = (load3(src.sublist(29, src.length)) & Number(8388607)) << 2; 126 | 127 | FeCombine(dst, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9); 128 | } 129 | 130 | /// FeToBytes marshals h to s. 131 | /// Preconditions: 132 | /// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. 133 | /// 134 | /// Write p=2^255-19; q=floor(h/p). 135 | /// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). 136 | /// 137 | /// Proof: 138 | /// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. 139 | /// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4. 140 | /// 141 | /// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). 142 | /// Then 0.filled(10, Number.zero); 155 | 156 | var q = (Numbers.v19 * h[9] + (Number.one << 24)) >> 25; 157 | q = (h[0] + q) >> 26; 158 | q = (h[1] + q) >> 25; 159 | q = (h[2] + q) >> 26; 160 | q = (h[3] + q) >> 25; 161 | q = (h[4] + q) >> 26; 162 | q = (h[5] + q) >> 25; 163 | q = (h[6] + q) >> 26; 164 | q = (h[7] + q) >> 25; 165 | q = (h[8] + q) >> 26; 166 | q = (h[9] + q) >> 25; 167 | 168 | // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. 169 | h[0] += Numbers.v19 * q; 170 | // Goal: Output h-2^255 q, which is between 0 and 2^255-20. 171 | 172 | carry[0] = h[0] >> 26; 173 | h[1] += carry[0]; 174 | h[0] -= carry[0] << 26; 175 | carry[1] = h[1] >> 25; 176 | h[2] += carry[1]; 177 | h[1] -= carry[1] << 25; 178 | carry[2] = h[2] >> 26; 179 | h[3] += carry[2]; 180 | h[2] -= carry[2] << 26; 181 | carry[3] = h[3] >> 25; 182 | h[4] += carry[3]; 183 | h[3] -= carry[3] << 25; 184 | carry[4] = h[4] >> 26; 185 | h[5] += carry[4]; 186 | h[4] -= carry[4] << 26; 187 | carry[5] = h[5] >> 25; 188 | h[6] += carry[5]; 189 | h[5] -= carry[5] << 25; 190 | carry[6] = h[6] >> 26; 191 | h[7] += carry[6]; 192 | h[6] -= carry[6] << 26; 193 | carry[7] = h[7] >> 25; 194 | h[8] += carry[7]; 195 | h[7] -= carry[7] << 25; 196 | carry[8] = h[8] >> 26; 197 | h[9] += carry[8]; 198 | h[8] -= carry[8] << 26; 199 | carry[9] = h[9] >> 25; 200 | h[9] -= carry[9] << 25; 201 | // h10 = carry9 202 | 203 | // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. 204 | // Have h[0]+...+2^230 h[9] between 0 and 2^255-1; 205 | // evidently 2^255 h10-2^255 q = 0. 206 | // Goal: Output h[0]+...+2^230 h[9]. 207 | 208 | s[0] = (h[0] >> 0).intValue; 209 | s[1] = (h[0] >> 8).intValue; 210 | s[2] = (h[0] >> 16).intValue; 211 | s[3] = ((h[0] >> 24) | (h[1] << 2)).intValue; 212 | s[4] = (h[1] >> 6).intValue; 213 | s[5] = (h[1] >> 14).intValue; 214 | s[6] = ((h[1] >> 22) | (h[2] << 3)).intValue; 215 | s[7] = (h[2] >> 5).intValue; 216 | s[8] = (h[2] >> 13).intValue; 217 | s[9] = ((h[2] >> 21) | (h[3] << 5)).intValue; 218 | s[10] = (h[3] >> 3).intValue; 219 | s[11] = (h[3] >> 11).intValue; 220 | s[12] = ((h[3] >> 19) | (h[4] << 6)).intValue; 221 | s[13] = (h[4] >> 2).intValue; 222 | s[14] = (h[4] >> 10).intValue; 223 | s[15] = (h[4] >> 18).intValue; 224 | s[16] = (h[5] >> 0).intValue; 225 | s[17] = (h[5] >> 8).intValue; 226 | s[18] = (h[5] >> 16).intValue; 227 | s[19] = ((h[5] >> 24) | (h[6] << 1)).intValue; 228 | s[20] = (h[6] >> 7).intValue; 229 | s[21] = (h[6] >> 15).intValue; 230 | s[22] = ((h[6] >> 23) | (h[7] << 3)).intValue; 231 | s[23] = (h[7] >> 5).intValue; 232 | s[24] = (h[7] >> 13).intValue; 233 | s[25] = ((h[7] >> 21) | (h[8] << 4)).intValue; 234 | s[26] = (h[8] >> 4).intValue; 235 | s[27] = (h[8] >> 12).intValue; 236 | s[28] = ((h[8] >> 20) | (h[9] << 6)).intValue; 237 | s[29] = (h[9] >> 2).intValue; 238 | s[30] = (h[9] >> 10).intValue; 239 | s[31] = (h[9] >> 18).intValue; 240 | } 241 | 242 | int FeIsNegative(FieldElement f) { 243 | var s = Uint8List(32); 244 | FeToBytes(s, f); 245 | return s[0] & 1; 246 | } 247 | 248 | int FeIsNonZero(FieldElement f) { 249 | var s = Uint8List(32); 250 | FeToBytes(s, f); 251 | var x = 0; 252 | for (var i = 0; i < s.length; i++) { 253 | x |= s[i]; 254 | } 255 | x |= x >> 4; 256 | x |= x >> 2; 257 | x |= x >> 1; 258 | return x & 1; 259 | } 260 | 261 | /// FeNeg sets h = -f 262 | /// 263 | /// Preconditions: 264 | /// |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. 265 | /// 266 | /// Postconditions: 267 | /// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. 268 | void FeNeg(FieldElement h, FieldElement f) { 269 | h[0] = -f[0]; 270 | h[1] = -f[1]; 271 | h[2] = -f[2]; 272 | h[3] = -f[3]; 273 | h[4] = -f[4]; 274 | h[5] = -f[5]; 275 | h[6] = -f[6]; 276 | h[7] = -f[7]; 277 | h[8] = -f[8]; 278 | h[9] = -f[9]; 279 | } 280 | 281 | void FeCombine(FieldElement h, Number h0, Number h1, Number h2, Number h3, 282 | Number h4, Number h5, Number h6, Number h7, Number h8, Number h9) { 283 | var c0 = Number.zero; 284 | var c1 = Number.zero; 285 | var c2 = Number.zero; 286 | var c3 = Number.zero; 287 | var c4 = Number.zero; 288 | var c5 = Number.zero; 289 | var c6 = Number.zero; 290 | var c7 = Number.zero; 291 | var c8 = Number.zero; 292 | var c9 = Number.zero; 293 | 294 | /* 295 | |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38)) 296 | i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8 297 | |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19)) 298 | i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9 299 | */ 300 | 301 | c0 = (h0 + (Number.one << 25)) >> 26; 302 | h1 += c0; 303 | h0 -= c0 << 26; 304 | c4 = (h4 + (Number.one << 25)) >> 26; 305 | h5 += c4; 306 | h4 -= c4 << 26; 307 | /* |h0| <= 2^25 */ 308 | /* |h4| <= 2^25 */ 309 | /* |h1| <= 1.51*2^58 */ 310 | /* |h5| <= 1.51*2^58 */ 311 | 312 | c1 = (h1 + (Number.one << 24)) >> 25; 313 | h2 += c1; 314 | h1 -= c1 << 25; 315 | c5 = (h5 + (Number.one << 24)) >> 25; 316 | h6 += c5; 317 | h5 -= c5 << 25; 318 | /* |h1| <= 2^24; from now on fits into int32 */ 319 | /* |h5| <= 2^24; from now on fits into int32 */ 320 | /* |h2| <= 1.21*2^59 */ 321 | /* |h6| <= 1.21*2^59 */ 322 | 323 | c2 = (h2 + (Number.one << 25)) >> 26; 324 | h3 += c2; 325 | h2 -= c2 << 26; 326 | c6 = (h6 + (Number.one << 25)) >> 26; 327 | h7 += c6; 328 | h6 -= c6 << 26; 329 | /* |h2| <= 2^25; from now on fits into int32 unchanged */ 330 | /* |h6| <= 2^25; from now on fits into int32 unchanged */ 331 | /* |h3| <= 1.51*2^58 */ 332 | /* |h7| <= 1.51*2^58 */ 333 | 334 | c3 = (h3 + (Number.one << 24)) >> 25; 335 | h4 += c3; 336 | h3 -= c3 << 25; 337 | c7 = (h7 + (Number.one << 24)) >> 25; 338 | h8 += c7; 339 | h7 -= c7 << 25; 340 | /* |h3| <= 2^24; from now on fits into int32 unchanged */ 341 | /* |h7| <= 2^24; from now on fits into int32 unchanged */ 342 | /* |h4| <= 1.52*2^33 */ 343 | /* |h8| <= 1.52*2^33 */ 344 | 345 | c4 = (h4 + (Number.one << 25)) >> 26; 346 | h5 += c4; 347 | h4 -= c4 << 26; 348 | c8 = (h8 + (Number.one << 25)) >> 26; 349 | h9 += c8; 350 | h8 -= c8 << 26; 351 | /* |h4| <= 2^25; from now on fits into int32 unchanged */ 352 | /* |h8| <= 2^25; from now on fits into int32 unchanged */ 353 | /* |h5| <= 1.01*2^24 */ 354 | /* |h9| <= 1.51*2^58 */ 355 | 356 | c9 = (h9 + (Number.one << 24)) >> 25; 357 | h0 += c9 * Numbers.v19; 358 | h9 -= c9 << 25; 359 | /* |h9| <= 2^24; from now on fits into int32 unchanged */ 360 | /* |h0| <= 1.8*2^37 */ 361 | 362 | c0 = (h0 + (Number.one << 25)) >> 26; 363 | h1 += c0; 364 | h0 -= c0 << 26; 365 | /* |h0| <= 2^25; from now on fits into int32 unchanged */ 366 | /* |h1| <= 1.01*2^24 */ 367 | 368 | h[0] = h0; 369 | h[1] = h1; 370 | h[2] = h2; 371 | h[3] = h3; 372 | h[4] = h4; 373 | h[5] = h5; 374 | h[6] = h6; 375 | h[7] = h7; 376 | h[8] = h8; 377 | h[9] = h9; 378 | } 379 | 380 | /// FeMul calculates h = f * g 381 | /// Can overlap h with f or g. 382 | /// 383 | /// Preconditions: 384 | /// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. 385 | /// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. 386 | /// 387 | /// Postconditions: 388 | /// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. 389 | /// 390 | /// Notes on implementation strategy: 391 | /// 392 | /// Using schoolbook multiplication. 393 | /// Karatsuba would save a little in some cost models. 394 | /// 395 | /// Most multiplications by 2 and 19 are 32-bit precomputations; 396 | /// cheaper than 64-bit postcomputations. 397 | /// 398 | /// There is one remaining multiplication by 19 in the carry chain; 399 | /// one *19 precomputation can be merged into this, 400 | /// but the resulting data flow is considerably less clean. 401 | /// 402 | /// There are 12 carries below. 403 | /// 10 of them are 2-way parallelizable and vectorizable. 404 | /// Can get away with 11 carries, but then data flow is much deeper. 405 | /// 406 | /// With tighter constraints on inputs, can squeeze carries into int32. 407 | void FeMul(FieldElement h, FieldElement f, FieldElement g) { 408 | var f0 = f[0]; 409 | var f1 = f[1]; 410 | var f2 = f[2]; 411 | var f3 = f[3]; 412 | var f4 = f[4]; 413 | var f5 = f[5]; 414 | var f6 = f[6]; 415 | var f7 = f[7]; 416 | var f8 = f[8]; 417 | var f9 = f[9]; 418 | 419 | var f1_2 = Number.two * f[1]; 420 | var f3_2 = Number.two * f[3]; 421 | var f5_2 = Number.two * f[5]; 422 | var f7_2 = Number.two * f[7]; 423 | var f9_2 = Number.two * f[9]; 424 | 425 | var g0 = g[0]; 426 | var g1 = g[1]; 427 | var g2 = g[2]; 428 | var g3 = g[3]; 429 | var g4 = g[4]; 430 | var g5 = g[5]; 431 | var g6 = g[6]; 432 | var g7 = g[7]; 433 | var g8 = g[8]; 434 | var g9 = g[9]; 435 | 436 | var g1_19 = Numbers.v19 * g[1]; /* 1.4*2^29 */ 437 | var g2_19 = Numbers.v19 * g[2]; /* 1.4*2^30; still ok */ 438 | var g3_19 = Numbers.v19 * g[3]; 439 | var g4_19 = Numbers.v19 * g[4]; 440 | var g5_19 = Numbers.v19 * g[5]; 441 | var g6_19 = Numbers.v19 * g[6]; 442 | var g7_19 = Numbers.v19 * g[7]; 443 | var g8_19 = Numbers.v19 * g[8]; 444 | var g9_19 = Numbers.v19 * g[9]; 445 | 446 | var h0 = f0 * g0 + 447 | f1_2 * g9_19 + 448 | f2 * g8_19 + 449 | f3_2 * g7_19 + 450 | f4 * g6_19 + 451 | f5_2 * g5_19 + 452 | f6 * g4_19 + 453 | f7_2 * g3_19 + 454 | f8 * g2_19 + 455 | f9_2 * g1_19; 456 | var h1 = f0 * g1 + 457 | f1 * g0 + 458 | f2 * g9_19 + 459 | f3 * g8_19 + 460 | f4 * g7_19 + 461 | f5 * g6_19 + 462 | f6 * g5_19 + 463 | f7 * g4_19 + 464 | f8 * g3_19 + 465 | f9 * g2_19; 466 | var h2 = f0 * g2 + 467 | f1_2 * g1 + 468 | f2 * g0 + 469 | f3_2 * g9_19 + 470 | f4 * g8_19 + 471 | f5_2 * g7_19 + 472 | f6 * g6_19 + 473 | f7_2 * g5_19 + 474 | f8 * g4_19 + 475 | f9_2 * g3_19; 476 | var h3 = f0 * g3 + 477 | f1 * g2 + 478 | f2 * g1 + 479 | f3 * g0 + 480 | f4 * g9_19 + 481 | f5 * g8_19 + 482 | f6 * g7_19 + 483 | f7 * g6_19 + 484 | f8 * g5_19 + 485 | f9 * g4_19; 486 | var h4 = f0 * g4 + 487 | f1_2 * g3 + 488 | f2 * g2 + 489 | f3_2 * g1 + 490 | f4 * g0 + 491 | f5_2 * g9_19 + 492 | f6 * g8_19 + 493 | f7_2 * g7_19 + 494 | f8 * g6_19 + 495 | f9_2 * g5_19; 496 | var h5 = f0 * g5 + 497 | f1 * g4 + 498 | f2 * g3 + 499 | f3 * g2 + 500 | f4 * g1 + 501 | f5 * g0 + 502 | f6 * g9_19 + 503 | f7 * g8_19 + 504 | f8 * g7_19 + 505 | f9 * g6_19; 506 | var h6 = f0 * g6 + 507 | f1_2 * g5 + 508 | f2 * g4 + 509 | f3_2 * g3 + 510 | f4 * g2 + 511 | f5_2 * g1 + 512 | f6 * g0 + 513 | f7_2 * g9_19 + 514 | f8 * g8_19 + 515 | f9_2 * g7_19; 516 | var h7 = f0 * g7 + 517 | f1 * g6 + 518 | f2 * g5 + 519 | f3 * g4 + 520 | f4 * g3 + 521 | f5 * g2 + 522 | f6 * g1 + 523 | f7 * g0 + 524 | f8 * g9_19 + 525 | f9 * g8_19; 526 | var h8 = f0 * g8 + 527 | f1_2 * g7 + 528 | f2 * g6 + 529 | f3_2 * g5 + 530 | f4 * g4 + 531 | f5_2 * g3 + 532 | f6 * g2 + 533 | f7_2 * g1 + 534 | f8 * g0 + 535 | f9_2 * g9_19; 536 | var h9 = f0 * g9 + 537 | f1 * g8 + 538 | f2 * g7 + 539 | f3 * g6 + 540 | f4 * g5 + 541 | f5 * g4 + 542 | f6 * g3 + 543 | f7 * g2 + 544 | f8 * g1 + 545 | f9 * g0; 546 | 547 | FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9); 548 | } 549 | 550 | List feSquare(FieldElement f) { 551 | var f0 = f[0]; 552 | var f1 = f[1]; 553 | var f2 = f[2]; 554 | var f3 = f[3]; 555 | var f4 = f[4]; 556 | var f5 = f[5]; 557 | var f6 = f[6]; 558 | var f7 = f[7]; 559 | var f8 = f[8]; 560 | var f9 = f[9]; 561 | var f0_2 = Number.two * f[0]; 562 | var f1_2 = Number.two * f[1]; 563 | var f2_2 = Number.two * f[2]; 564 | var f3_2 = Number.two * f[3]; 565 | var f4_2 = Number.two * f[4]; 566 | var f5_2 = Number.two * f[5]; 567 | var f6_2 = Number.two * f[6]; 568 | var f7_2 = Number.two * f[7]; 569 | var f5_38 = Numbers.v38 * f5; // 1.31*2^30 570 | var f6_19 = Numbers.v19 * f6; // 1.31*2^30 571 | var f7_38 = Numbers.v38 * f7; // 1.31*2^30 572 | var f8_19 = Numbers.v19 * f8; // 1.31*2^30 573 | var f9_38 = Numbers.v38 * f9; // 1.31*2^30 574 | 575 | var h0 = f0 * f0 + 576 | f1_2 * f9_38 + 577 | f2_2 * f8_19 + 578 | f3_2 * f7_38 + 579 | f4_2 * f6_19 + 580 | f5 * f5_38; 581 | var h1 = f0_2 * f1 + f2 * f9_38 + f3_2 * f8_19 + f4 * f7_38 + f5_2 * f6_19; 582 | var h2 = f0_2 * f2 + 583 | f1_2 * f1 + 584 | f3_2 * f9_38 + 585 | f4_2 * f8_19 + 586 | f5_2 * f7_38 + 587 | f6 * f6_19; 588 | var h3 = f0_2 * f3 + f1_2 * f2 + f4 * f9_38 + f5_2 * f8_19 + f6 * f7_38; 589 | var h4 = f0_2 * f4 + 590 | f1_2 * f3_2 + 591 | f2 * f2 + 592 | f5_2 * f9_38 + 593 | f6_2 * f8_19 + 594 | f7 * f7_38; 595 | var h5 = f0_2 * f5 + f1_2 * f4 + f2_2 * f3 + f6 * f9_38 + f7_2 * f8_19; 596 | var h6 = f0_2 * f6 + 597 | f1_2 * f5_2 + 598 | f2_2 * f4 + 599 | f3_2 * f3 + 600 | f7_2 * f9_38 + 601 | f8 * f8_19; 602 | var h7 = f0_2 * f7 + f1_2 * f6 + f2_2 * f5 + f3_2 * f4 + f8 * f9_38; 603 | var h8 = 604 | f0_2 * f8 + f1_2 * f7_2 + f2_2 * f6 + f3_2 * f5_2 + f4 * f4 + f9 * f9_38; 605 | var h9 = f0_2 * f9 + f1_2 * f8 + f2_2 * f7 + f3_2 * f6 + f4_2 * f5; 606 | 607 | return [h0, h1, h2, h3, h4, h5, h6, h7, h8, h9]; 608 | } 609 | 610 | /// FeSquare calculates h = f*f. Can overlap h with f. 611 | /// 612 | /// Preconditions: 613 | /// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. 614 | /// 615 | /// Postconditions: 616 | /// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. 617 | void FeSquare(FieldElement h, FieldElement f) { 618 | var fs = feSquare(f); 619 | var h0 = fs[0]; 620 | var h1 = fs[1]; 621 | var h2 = fs[2]; 622 | var h3 = fs[3]; 623 | var h4 = fs[4]; 624 | var h5 = fs[5]; 625 | var h6 = fs[6]; 626 | var h7 = fs[7]; 627 | var h8 = fs[8]; 628 | var h9 = fs[9]; 629 | FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9); 630 | } 631 | 632 | /// FeSquare2 sets h = 2 * f * f 633 | /// 634 | /// Can overlap h with f. 635 | /// 636 | /// Preconditions: 637 | /// |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. 638 | /// 639 | /// Postconditions: 640 | /// |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. 641 | /// See fe_mul.c for discussion of implementation strategy. 642 | void FeSquare2(FieldElement h, FieldElement f) { 643 | var fs = feSquare(f); 644 | var h0 = fs[0]; 645 | var h1 = fs[1]; 646 | var h2 = fs[2]; 647 | var h3 = fs[3]; 648 | var h4 = fs[4]; 649 | var h5 = fs[5]; 650 | var h6 = fs[6]; 651 | var h7 = fs[7]; 652 | var h8 = fs[8]; 653 | var h9 = fs[9]; 654 | 655 | h0 += h0; 656 | h1 += h1; 657 | h2 += h2; 658 | h3 += h3; 659 | h4 += h4; 660 | h5 += h5; 661 | h6 += h6; 662 | h7 += h7; 663 | h8 += h8; 664 | h9 += h9; 665 | 666 | FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9); 667 | } 668 | 669 | void FeInvert(FieldElement out, FieldElement z) { 670 | var t0 = FieldElement(); 671 | var t1 = FieldElement(); 672 | var t2 = FieldElement(); 673 | var t3 = FieldElement(); 674 | var i = 0; 675 | 676 | FeSquare(t0, z); // 2^1 677 | FeSquare(t1, t0); // 2^2 678 | for (i = 1; i < 2; i++) { 679 | // 2^3 680 | FeSquare(t1, t1); 681 | } 682 | FeMul(t1, z, t1); // 2^3 + 2^0 683 | FeMul(t0, t0, t1); // 2^3 + 2^1 + 2^0 684 | FeSquare(t2, t0); // 2^4 + 2^2 + 2^1 685 | FeMul(t1, t1, t2); // 2^4 + 2^3 + 2^2 + 2^1 + 2^0 686 | FeSquare(t2, t1); // 5,4,3,2,1 687 | for (i = 1; i < 5; i++) { 688 | // 9,8,7,6,5 689 | FeSquare(t2, t2); 690 | } 691 | FeMul(t1, t2, t1); // 9,8,7,6,5,4,3,2,1,0 692 | FeSquare(t2, t1); // 10..1 693 | for (i = 1; i < 10; i++) { 694 | // 19..10 695 | FeSquare(t2, t2); 696 | } 697 | FeMul(t2, t2, t1); // 19..0 698 | FeSquare(t3, t2); // 20..1 699 | for (i = 1; i < 20; i++) { 700 | // 39..20 701 | FeSquare(t3, t3); 702 | } 703 | FeMul(t2, t3, t2); // 39..0 704 | FeSquare(t2, t2); // 40..1 705 | for (i = 1; i < 10; i++) { 706 | // 49..10 707 | FeSquare(t2, t2); 708 | } 709 | FeMul(t1, t2, t1); // 49..0 710 | FeSquare(t2, t1); // 50..1 711 | for (i = 1; i < 50; i++) { 712 | // 99..50 713 | FeSquare(t2, t2); 714 | } 715 | FeMul(t2, t2, t1); // 99..0 716 | FeSquare(t3, t2); // 100..1 717 | for (i = 1; i < 100; i++) { 718 | // 199..100 719 | FeSquare(t3, t3); 720 | } 721 | FeMul(t2, t3, t2); // 199..0 722 | FeSquare(t2, t2); // 200..1 723 | for (i = 1; i < 50; i++) { 724 | // 249..50 725 | FeSquare(t2, t2); 726 | } 727 | FeMul(t1, t2, t1); // 249..0 728 | FeSquare(t1, t1); // 250..1 729 | for (i = 1; i < 5; i++) { 730 | // 254..5 731 | FeSquare(t1, t1); 732 | } 733 | FeMul(out, t1, t0); // 254..5,3,1,0 734 | } 735 | 736 | void fePow22523(FieldElement out, FieldElement z) { 737 | var t0 = FieldElement(); 738 | var t1 = FieldElement(); 739 | var t2 = FieldElement(); 740 | var i = 0; 741 | 742 | FeSquare(t0, z); 743 | for (i = 1; i < 1; i++) { 744 | FeSquare(t0, t0); 745 | } 746 | FeSquare(t1, t0); 747 | for (i = 1; i < 2; i++) { 748 | FeSquare(t1, t1); 749 | } 750 | FeMul(t1, z, t1); 751 | FeMul(t0, t0, t1); 752 | FeSquare(t0, t0); 753 | for (i = 1; i < 1; i++) { 754 | FeSquare(t0, t0); 755 | } 756 | FeMul(t0, t1, t0); 757 | FeSquare(t1, t0); 758 | for (i = 1; i < 5; i++) { 759 | FeSquare(t1, t1); 760 | } 761 | FeMul(t0, t1, t0); 762 | FeSquare(t1, t0); 763 | for (i = 1; i < 10; i++) { 764 | FeSquare(t1, t1); 765 | } 766 | FeMul(t1, t1, t0); 767 | FeSquare(t2, t1); 768 | for (i = 1; i < 20; i++) { 769 | FeSquare(t2, t2); 770 | } 771 | FeMul(t1, t2, t1); 772 | FeSquare(t1, t1); 773 | for (i = 1; i < 10; i++) { 774 | FeSquare(t1, t1); 775 | } 776 | FeMul(t0, t1, t0); 777 | FeSquare(t1, t0); 778 | for (i = 1; i < 50; i++) { 779 | FeSquare(t1, t1); 780 | } 781 | FeMul(t1, t1, t0); 782 | FeSquare(t2, t1); 783 | for (i = 1; i < 100; i++) { 784 | FeSquare(t2, t2); 785 | } 786 | FeMul(t1, t2, t1); 787 | FeSquare(t1, t1); 788 | for (i = 1; i < 50; i++) { 789 | FeSquare(t1, t1); 790 | } 791 | FeMul(t0, t1, t0); 792 | FeSquare(t0, t0); 793 | for (i = 1; i < 2; i++) { 794 | FeSquare(t0, t0); 795 | } 796 | FeMul(out, t0, z); 797 | } 798 | 799 | /// Group elements are members of the elliptic curve -x^2 + y^2 = 1 + d * x^2 * 800 | /// y^2 where d = -121665/121666. 801 | /// 802 | /// Several representations are used: 803 | /// ProjectiveGroupElement: (X:Y:Z) satisfying x=X/Z, y=Y/Z 804 | /// ExtendedGroupElement: (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT 805 | /// CompletedGroupElement: ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T 806 | /// PreComputedGroupElement: (y+x,y-x,2dxy) 807 | class ProjectiveGroupElement { 808 | FieldElement X = FieldElement(); 809 | FieldElement Y = FieldElement(); 810 | FieldElement Z = FieldElement(); 811 | 812 | void Zero() { 813 | FeZero(X); 814 | FeOne(Y); 815 | FeOne(Z); 816 | } 817 | 818 | void Double(CompletedGroupElement r) { 819 | var t0 = FieldElement(); 820 | 821 | FeSquare(r.X, X); 822 | FeSquare(r.Z, Y); 823 | FeSquare2(r.T, Z); 824 | FeAdd(r.Y, X, Y); 825 | FeSquare(t0, r.Y); 826 | FeAdd(r.Y, r.Z, r.X); 827 | FeSub(r.Z, r.Z, r.X); 828 | FeSub(r.X, t0, r.Y); 829 | FeSub(r.T, r.T, r.Z); 830 | } 831 | 832 | void ToBytes(Uint8List s) { 833 | var recip = FieldElement(); 834 | var x = FieldElement(); 835 | var y = FieldElement(); 836 | 837 | FeInvert(recip, Z); 838 | FeMul(x, X, recip); 839 | FeMul(y, Y, recip); 840 | FeToBytes(s, y); 841 | s[31] ^= FeIsNegative(x) << 7; 842 | } 843 | } 844 | 845 | class ExtendedGroupElement { 846 | FieldElement X = FieldElement(); 847 | FieldElement Y = FieldElement(); 848 | FieldElement Z = FieldElement(); 849 | FieldElement T = FieldElement(); 850 | 851 | void Zero() { 852 | FeZero(X); 853 | FeOne(Y); 854 | FeOne(Z); 855 | FeZero(T); 856 | } 857 | 858 | void Double(CompletedGroupElement r) { 859 | var q = ProjectiveGroupElement(); 860 | ToProjective(q); 861 | q.Double(r); 862 | } 863 | 864 | void ToCached(CachedGroupElement r) { 865 | FeAdd(r.yPlusX, Y, X); 866 | FeSub(r.yMinusX, Y, X); 867 | FeCopy(r.Z, Z); 868 | FeMul(r.T2d, T, d2); 869 | } 870 | 871 | void ToProjective(ProjectiveGroupElement r) { 872 | FeCopy(r.X, X); 873 | FeCopy(r.Y, Y); 874 | FeCopy(r.Z, Z); 875 | } 876 | 877 | void ToBytes(Uint8List s) { 878 | var recip = FieldElement(); 879 | var x = FieldElement(); 880 | var y = FieldElement(); 881 | 882 | FeInvert(recip, Z); 883 | FeMul(x, X, recip); 884 | FeMul(y, Y, recip); 885 | FeToBytes(s, y); 886 | s[31] ^= FeIsNegative(x) << 7; 887 | } 888 | 889 | bool FromBytes(Uint8List s) { 890 | var u = FieldElement(); 891 | var v = FieldElement(); 892 | var v3 = FieldElement(); 893 | var vxx = FieldElement(); 894 | var check = FieldElement(); 895 | 896 | FeFromBytes(Y, s); 897 | FeOne(Z); 898 | FeSquare(u, Y); 899 | FeMul(v, u, d); 900 | FeSub(u, u, Z); // y = y^2-1 901 | FeAdd(v, v, Z); // v = dy^2+1 902 | 903 | FeSquare(v3, v); 904 | FeMul(v3, v3, v); // v3 = v^3 905 | FeSquare(X, v3); 906 | FeMul(X, X, v); 907 | FeMul(X, X, u); // x = uv^7 908 | 909 | fePow22523(X, X); // x = (uv^7)^((q-5)/8) 910 | FeMul(X, X, v3); 911 | FeMul(X, X, u); // x = uv^3(uv^7)^((q-5)/8) 912 | 913 | var tmpX = Uint8List(32); 914 | var tmp2 = Uint8List(32); 915 | 916 | FeSquare(vxx, X); 917 | FeMul(vxx, vxx, v); 918 | FeSub(check, vxx, u); // vx^2-u 919 | if (FeIsNonZero(check) == 1) { 920 | FeAdd(check, vxx, u); // vx^2+u 921 | if (FeIsNonZero(check) == 1) { 922 | return false; 923 | } 924 | FeMul(X, X, SqrtM1); 925 | 926 | FeToBytes(tmpX, X); 927 | for (var i = 0; i < tmp2.length; i++) { 928 | tmp2[31 - i] = tmp2[i]; 929 | } 930 | } 931 | 932 | if (FeIsNegative(X) != (s[31] >> 7)) { 933 | FeNeg(X, X); 934 | } 935 | 936 | FeMul(T, X, Y); 937 | return true; 938 | } 939 | } 940 | 941 | class CompletedGroupElement { 942 | FieldElement X = FieldElement(); 943 | FieldElement Y = FieldElement(); 944 | FieldElement Z = FieldElement(); 945 | FieldElement T = FieldElement(); 946 | 947 | void ToProjective(ProjectiveGroupElement r) { 948 | FeMul(r.X, X, T); 949 | FeMul(r.Y, Y, Z); 950 | FeMul(r.Z, Z, T); 951 | } 952 | 953 | void ToExtended(ExtendedGroupElement r) { 954 | FeMul(r.X, X, T); 955 | FeMul(r.Y, Y, Z); 956 | FeMul(r.Z, Z, T); 957 | FeMul(r.T, X, Y); 958 | } 959 | } 960 | 961 | class PreComputedGroupElement { 962 | FieldElement yPlusX = FieldElement(); 963 | FieldElement yMinusX = FieldElement(); 964 | FieldElement xy2d = FieldElement(); 965 | 966 | PreComputedGroupElement(); 967 | 968 | PreComputedGroupElement.fromList( 969 | FieldElement ypx, FieldElement ymx, FieldElement xy2d) { 970 | yPlusX = ypx; 971 | yMinusX = ymx; 972 | this.xy2d = xy2d; 973 | } 974 | 975 | void Zero() { 976 | FeOne(yPlusX); 977 | FeOne(yMinusX); 978 | FeZero(xy2d); 979 | } 980 | } 981 | 982 | class CachedGroupElement { 983 | FieldElement yPlusX = FieldElement(); 984 | FieldElement yMinusX = FieldElement(); 985 | FieldElement Z = FieldElement(); 986 | FieldElement T2d = FieldElement(); 987 | } 988 | 989 | void geAdd( 990 | CompletedGroupElement r, ExtendedGroupElement p, CachedGroupElement q) { 991 | var t0 = FieldElement(); 992 | 993 | FeAdd(r.X, p.Y, p.X); 994 | FeSub(r.Y, p.Y, p.X); 995 | FeMul(r.Z, r.X, q.yPlusX); 996 | FeMul(r.Y, r.Y, q.yMinusX); 997 | FeMul(r.T, q.T2d, p.T); 998 | FeMul(r.X, p.Z, q.Z); 999 | FeAdd(t0, r.X, r.X); 1000 | FeSub(r.X, r.Z, r.Y); 1001 | FeAdd(r.Y, r.Z, r.Y); 1002 | FeAdd(r.Z, t0, r.T); 1003 | FeSub(r.T, t0, r.T); 1004 | } 1005 | 1006 | void geSub( 1007 | CompletedGroupElement r, ExtendedGroupElement p, CachedGroupElement q) { 1008 | var t0 = FieldElement(); 1009 | 1010 | FeAdd(r.X, p.Y, p.X); 1011 | FeSub(r.Y, p.Y, p.X); 1012 | FeMul(r.Z, r.X, q.yMinusX); 1013 | FeMul(r.Y, r.Y, q.yPlusX); 1014 | FeMul(r.T, q.T2d, p.T); 1015 | FeMul(r.X, p.Z, q.Z); 1016 | FeAdd(t0, r.X, r.X); 1017 | FeSub(r.X, r.Z, r.Y); 1018 | FeAdd(r.Y, r.Z, r.Y); 1019 | FeSub(r.Z, t0, r.T); 1020 | FeAdd(r.T, t0, r.T); 1021 | } 1022 | 1023 | void geMixedAdd(CompletedGroupElement r, ExtendedGroupElement p, 1024 | PreComputedGroupElement q) { 1025 | var t0 = FieldElement(); 1026 | 1027 | FeAdd(r.X, p.Y, p.X); 1028 | FeSub(r.Y, p.Y, p.X); 1029 | FeMul(r.Z, r.X, q.yPlusX); 1030 | FeMul(r.Y, r.Y, q.yMinusX); 1031 | FeMul(r.T, q.xy2d, p.T); 1032 | FeAdd(t0, p.Z, p.Z); 1033 | FeSub(r.X, r.Z, r.Y); 1034 | FeAdd(r.Y, r.Z, r.Y); 1035 | FeAdd(r.Z, t0, r.T); 1036 | FeSub(r.T, t0, r.T); 1037 | } 1038 | 1039 | void geMixedSub(CompletedGroupElement r, ExtendedGroupElement p, 1040 | PreComputedGroupElement q) { 1041 | var t0 = FieldElement(); 1042 | 1043 | FeAdd(r.X, p.Y, p.X); 1044 | FeSub(r.Y, p.Y, p.X); 1045 | FeMul(r.Z, r.X, q.yMinusX); 1046 | FeMul(r.Y, r.Y, q.yPlusX); 1047 | FeMul(r.T, q.xy2d, p.T); 1048 | FeAdd(t0, p.Z, p.Z); 1049 | FeSub(r.X, r.Z, r.Y); 1050 | FeAdd(r.Y, r.Z, r.Y); 1051 | FeSub(r.Z, t0, r.T); 1052 | FeAdd(r.T, t0, r.T); 1053 | } 1054 | 1055 | void slide(Int8List r, Uint8List a) { 1056 | for (var i = 0; i < r.length; i++) { 1057 | r[i] = 1 & (a[i >> 3] >> (i & 7)); 1058 | } 1059 | 1060 | for (var i = 0; i < r.length; i++) { 1061 | if (r[i] != 0) { 1062 | for (var b = 1; b <= 6 && i + b < 256; b++) { 1063 | if (r[i + b] != 0) { 1064 | if (r[i] + (r[i + b] << b) <= 15) { 1065 | r[i] += r[i + b] << b; 1066 | r[i + b] = 0; 1067 | } else if (r[i] - (r[i + b] << b) >= -15) { 1068 | r[i] -= r[i + b] << b; 1069 | for (var k = i + b; k < 256; k++) { 1070 | if (r[k] == 0) { 1071 | r[k] = 1; 1072 | break; 1073 | } 1074 | r[k] = 0; 1075 | } 1076 | } else { 1077 | break; 1078 | } 1079 | } 1080 | } 1081 | } 1082 | } 1083 | } 1084 | 1085 | /// GeDoubleScalarMultVartime sets r = a*A + b*B 1086 | /// where a = a[0]+256*a[1]+...+256^31 a[31]. 1087 | /// and b = b[0]+256*b[1]+...+256^31 b[31]. 1088 | /// B is the Ed25519 base point (x,4/5) with x positive. 1089 | void GeDoubleScalarMultVartime(ProjectiveGroupElement r, Uint8List a, 1090 | ExtendedGroupElement A, Uint8List b) { 1091 | var aSlide = Int8List(256); 1092 | var bSlide = Int8List(256); 1093 | var Ai = List.generate( 1094 | 8, (index) => CachedGroupElement()); // A,3A,5A,7A,9A,11A,13A,15A 1095 | var t = CompletedGroupElement(); 1096 | var u = ExtendedGroupElement(); 1097 | var A2 = ExtendedGroupElement(); 1098 | int i; 1099 | 1100 | slide(aSlide, a); 1101 | slide(bSlide, b); 1102 | 1103 | A.ToCached(Ai[0]); 1104 | A.Double(t); 1105 | t.ToExtended(A2); 1106 | 1107 | for (i = 0; i < 7; i++) { 1108 | geAdd(t, A2, Ai[i]); 1109 | t.ToExtended(u); 1110 | u.ToCached(Ai[i + 1]); 1111 | } 1112 | 1113 | r.Zero(); 1114 | 1115 | for (i = 255; i >= 0; i--) { 1116 | if (aSlide[i] != 0 || bSlide[i] != 0) { 1117 | break; 1118 | } 1119 | } 1120 | 1121 | for (; i >= 0; i--) { 1122 | r.Double(t); 1123 | 1124 | if (aSlide[i] > 0) { 1125 | t.ToExtended(u); 1126 | geAdd(t, u, Ai[aSlide[i] ~/ 2]); 1127 | } else if (aSlide[i] < 0) { 1128 | t.ToExtended(u); 1129 | geSub(t, u, Ai[(-aSlide[i]) ~/ 2]); 1130 | } 1131 | 1132 | if (bSlide[i] > 0) { 1133 | t.ToExtended(u); 1134 | geMixedAdd(t, u, bi[bSlide[i] ~/ 2]); 1135 | } else if (bSlide[i] < 0) { 1136 | t.ToExtended(u); 1137 | geMixedSub(t, u, bi[(-bSlide[i]) ~/ 2]); 1138 | } 1139 | 1140 | t.ToProjective(r); 1141 | } 1142 | } 1143 | 1144 | /// equal returns 1 if b == c and 0 otherwise, assuming that b and c are 1145 | /// non-negative. 1146 | Number equal(Number b, Number c) { 1147 | if (b == c) { 1148 | return Number.one; 1149 | } else { 1150 | return Number.zero; 1151 | } 1152 | // var x = b ^ c; 1153 | // x--; 1154 | // return x >> 31; 1155 | } 1156 | 1157 | /// negative returns 1 if b < 0 and 0 otherwise. 1158 | Number negative(Number b) { 1159 | if (b < Number.zero) { 1160 | return Number.one; 1161 | } else { 1162 | return Number.zero; 1163 | } 1164 | // return (b >> 31) & 1; 1165 | } 1166 | 1167 | void PreComputedGroupElementCMove( 1168 | PreComputedGroupElement t, PreComputedGroupElement u, Number b) { 1169 | FeCMove(t.yPlusX, u.yPlusX, b); 1170 | FeCMove(t.yMinusX, u.yMinusX, b); 1171 | FeCMove(t.xy2d, u.xy2d, b); 1172 | } 1173 | 1174 | void selectPoint(PreComputedGroupElement t, int pos, Number b) { 1175 | var minusT = PreComputedGroupElement(); 1176 | var bNegative = negative(b); 1177 | var bAbs = b - (((-bNegative) & b) << 1); 1178 | 1179 | t.Zero(); 1180 | for (var i = 0; i < 8; i++) { 1181 | PreComputedGroupElementCMove(t, base[pos][i], equal(bAbs, Number(i + 1))); 1182 | } 1183 | FeCopy(minusT.yPlusX, t.yMinusX); 1184 | FeCopy(minusT.yMinusX, t.yPlusX); 1185 | FeNeg(minusT.xy2d, t.xy2d); 1186 | PreComputedGroupElementCMove(t, minusT, bNegative); 1187 | } 1188 | 1189 | /// GeScalarMultBase computes h = a*B, where 1190 | /// a = a[0]+256*a[1]+...+256^31 a[31] 1191 | /// B is the Ed25519 base point (x,4/5) with x positive. 1192 | /// 1193 | /// Preconditions: 1194 | /// a[31] <= 127 1195 | void GeScalarMultBase(ExtendedGroupElement h, Uint8List a) { 1196 | var e = List.filled(64, Number.zero); 1197 | 1198 | for (var i = 0; i < a.length; i++) { 1199 | var v = a[i]; 1200 | e[2 * i] = Number(v) & Numbers.v15; 1201 | e[2 * i + 1] = Number((v >> 4)) & Numbers.v15; 1202 | } 1203 | 1204 | // each e[i] is between 0 and 15 and e[63] is between 0 and 7. 1205 | 1206 | var carry = Number.zero; 1207 | for (var i = 0; i < 63; i++) { 1208 | e[i] += carry; 1209 | carry = (e[i] + Numbers.v8) >> 4; 1210 | e[i] -= carry << 4; 1211 | } 1212 | e[63] += carry; 1213 | // each e[i] is between -8 and 8. 1214 | 1215 | h.Zero(); 1216 | var t = PreComputedGroupElement(); 1217 | var r = CompletedGroupElement(); 1218 | for (var i = 1; i < 64; i += 2) { 1219 | selectPoint(t, i ~/ 2, e[i]); 1220 | geMixedAdd(r, h, t); 1221 | r.ToExtended(h); 1222 | } 1223 | 1224 | var s = ProjectiveGroupElement(); 1225 | 1226 | h.Double(r); 1227 | r.ToProjective(s); 1228 | s.Double(r); 1229 | r.ToProjective(s); 1230 | s.Double(r); 1231 | r.ToProjective(s); 1232 | s.Double(r); 1233 | r.ToExtended(h); 1234 | 1235 | for (var i = 0; i < 64; i += 2) { 1236 | selectPoint(t, i ~/ 2, e[i]); 1237 | geMixedAdd(r, h, t); 1238 | r.ToExtended(h); 1239 | } 1240 | } 1241 | 1242 | /// The scalars are GF(2^252 + 27742317777372353535851937790883648493). 1243 | 1244 | /// Input: 1245 | /// a[0]+256*a[1]+...+256^31*a[31] = a 1246 | /// b[0]+256*b[1]+...+256^31*b[31] = b 1247 | /// c[0]+256*c[1]+...+256^31*c[31] = c 1248 | /// 1249 | /// Output: 1250 | /// s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l 1251 | /// where l = 2^252 + 27742317777372353535851937790883648493. 1252 | void ScMulAdd(Uint8List s, Uint8List a, Uint8List b, Uint8List c) { 1253 | var a0 = Numbers.v2097151 & load3(a.sublist(0, a.length)); 1254 | var a1 = Numbers.v2097151 & (load4(a.sublist(2, a.length)) >> 5); 1255 | var a2 = Numbers.v2097151 & (load3(a.sublist(5, a.length)) >> 2); 1256 | var a3 = Numbers.v2097151 & (load4(a.sublist(7, a.length)) >> 7); 1257 | var a4 = Numbers.v2097151 & (load4(a.sublist(10, a.length)) >> 4); 1258 | var a5 = Numbers.v2097151 & (load3(a.sublist(13, a.length)) >> 1); 1259 | var a6 = Numbers.v2097151 & (load4(a.sublist(15, a.length)) >> 6); 1260 | var a7 = Numbers.v2097151 & (load3(a.sublist(18, a.length)) >> 3); 1261 | var a8 = Numbers.v2097151 & load3(a.sublist(21, a.length)); 1262 | var a9 = Numbers.v2097151 & (load4(a.sublist(23, a.length)) >> 5); 1263 | var a10 = Numbers.v2097151 & (load3(a.sublist(26, a.length)) >> 2); 1264 | var a11 = (load4(a.sublist(28, a.length)) >> 7); 1265 | 1266 | var b0 = Numbers.v2097151 & load3(b.sublist(0, b.length)); 1267 | var b1 = Numbers.v2097151 & (load4(b.sublist(2, b.length)) >> 5); 1268 | var b2 = Numbers.v2097151 & (load3(b.sublist(5, b.length)) >> 2); 1269 | var b3 = Numbers.v2097151 & (load4(b.sublist(7, b.length)) >> 7); 1270 | var b4 = Numbers.v2097151 & (load4(b.sublist(10, b.length)) >> 4); 1271 | var b5 = Numbers.v2097151 & (load3(b.sublist(13, b.length)) >> 1); 1272 | var b6 = Numbers.v2097151 & (load4(b.sublist(15, b.length)) >> 6); 1273 | var b7 = Numbers.v2097151 & (load3(b.sublist(18, b.length)) >> 3); 1274 | var b8 = Numbers.v2097151 & load3(b.sublist(21, b.length)); 1275 | var b9 = Numbers.v2097151 & (load4(b.sublist(23, b.length)) >> 5); 1276 | var b10 = Numbers.v2097151 & (load3(b.sublist(26, b.length)) >> 2); 1277 | var b11 = (load4(b.sublist(28, b.length)) >> 7); 1278 | 1279 | var c0 = Numbers.v2097151 & load3(c.sublist(0, c.length)); 1280 | var c1 = Numbers.v2097151 & (load4(c.sublist(2, c.length)) >> 5); 1281 | var c2 = Numbers.v2097151 & (load3(c.sublist(5, c.length)) >> 2); 1282 | var c3 = Numbers.v2097151 & (load4(c.sublist(7, c.length)) >> 7); 1283 | var c4 = Numbers.v2097151 & (load4(c.sublist(10, c.length)) >> 4); 1284 | var c5 = Numbers.v2097151 & (load3(c.sublist(13, c.length)) >> 1); 1285 | var c6 = Numbers.v2097151 & (load4(c.sublist(15, c.length)) >> 6); 1286 | var c7 = Numbers.v2097151 & (load3(c.sublist(18, c.length)) >> 3); 1287 | var c8 = Numbers.v2097151 & load3(c.sublist(21, c.length)); 1288 | var c9 = Numbers.v2097151 & (load4(c.sublist(23, c.length)) >> 5); 1289 | var c10 = Numbers.v2097151 & (load3(c.sublist(26, c.length)) >> 2); 1290 | var c11 = (load4(c.sublist(28, c.length)) >> 7); 1291 | 1292 | var carry = List.filled(23, Number.zero); 1293 | 1294 | var s0 = c0 + a0 * b0; 1295 | var s1 = c1 + a0 * b1 + a1 * b0; 1296 | var s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; 1297 | var s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; 1298 | var s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; 1299 | var s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; 1300 | var s6 = 1301 | c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0; 1302 | var s7 = c7 + 1303 | a0 * b7 + 1304 | a1 * b6 + 1305 | a2 * b5 + 1306 | a3 * b4 + 1307 | a4 * b3 + 1308 | a5 * b2 + 1309 | a6 * b1 + 1310 | a7 * b0; 1311 | var s8 = c8 + 1312 | a0 * b8 + 1313 | a1 * b7 + 1314 | a2 * b6 + 1315 | a3 * b5 + 1316 | a4 * b4 + 1317 | a5 * b3 + 1318 | a6 * b2 + 1319 | a7 * b1 + 1320 | a8 * b0; 1321 | var s9 = c9 + 1322 | a0 * b9 + 1323 | a1 * b8 + 1324 | a2 * b7 + 1325 | a3 * b6 + 1326 | a4 * b5 + 1327 | a5 * b4 + 1328 | a6 * b3 + 1329 | a7 * b2 + 1330 | a8 * b1 + 1331 | a9 * b0; 1332 | var s10 = c10 + 1333 | a0 * b10 + 1334 | a1 * b9 + 1335 | a2 * b8 + 1336 | a3 * b7 + 1337 | a4 * b6 + 1338 | a5 * b5 + 1339 | a6 * b4 + 1340 | a7 * b3 + 1341 | a8 * b2 + 1342 | a9 * b1 + 1343 | a10 * b0; 1344 | var s11 = c11 + 1345 | a0 * b11 + 1346 | a1 * b10 + 1347 | a2 * b9 + 1348 | a3 * b8 + 1349 | a4 * b7 + 1350 | a5 * b6 + 1351 | a6 * b5 + 1352 | a7 * b4 + 1353 | a8 * b3 + 1354 | a9 * b2 + 1355 | a10 * b1 + 1356 | a11 * b0; 1357 | var s12 = a1 * b11 + 1358 | a2 * b10 + 1359 | a3 * b9 + 1360 | a4 * b8 + 1361 | a5 * b7 + 1362 | a6 * b6 + 1363 | a7 * b5 + 1364 | a8 * b4 + 1365 | a9 * b3 + 1366 | a10 * b2 + 1367 | a11 * b1; 1368 | var s13 = a2 * b11 + 1369 | a3 * b10 + 1370 | a4 * b9 + 1371 | a5 * b8 + 1372 | a6 * b7 + 1373 | a7 * b6 + 1374 | a8 * b5 + 1375 | a9 * b4 + 1376 | a10 * b3 + 1377 | a11 * b2; 1378 | var s14 = a3 * b11 + 1379 | a4 * b10 + 1380 | a5 * b9 + 1381 | a6 * b8 + 1382 | a7 * b7 + 1383 | a8 * b6 + 1384 | a9 * b5 + 1385 | a10 * b4 + 1386 | a11 * b3; 1387 | var s15 = a4 * b11 + 1388 | a5 * b10 + 1389 | a6 * b9 + 1390 | a7 * b8 + 1391 | a8 * b7 + 1392 | a9 * b6 + 1393 | a10 * b5 + 1394 | a11 * b4; 1395 | var s16 = 1396 | a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; 1397 | var s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; 1398 | var s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; 1399 | var s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; 1400 | var s20 = a9 * b11 + a10 * b10 + a11 * b9; 1401 | var s21 = a10 * b11 + a11 * b10; 1402 | var s22 = a11 * b11; 1403 | var s23 = Number.zero; 1404 | 1405 | carry[0] = (s0 + (Number.one << 20)) >> 21; 1406 | s1 += carry[0]; 1407 | s0 -= carry[0] << 21; 1408 | carry[2] = (s2 + (Number.one << 20)) >> 21; 1409 | s3 += carry[2]; 1410 | s2 -= carry[2] << 21; 1411 | carry[4] = (s4 + (Number.one << 20)) >> 21; 1412 | s5 += carry[4]; 1413 | s4 -= carry[4] << 21; 1414 | carry[6] = (s6 + (Number.one << 20)) >> 21; 1415 | s7 += carry[6]; 1416 | s6 -= carry[6] << 21; 1417 | carry[8] = (s8 + (Number.one << 20)) >> 21; 1418 | s9 += carry[8]; 1419 | s8 -= carry[8] << 21; 1420 | carry[10] = (s10 + (Number.one << 20)) >> 21; 1421 | s11 += carry[10]; 1422 | s10 -= carry[10] << 21; 1423 | carry[12] = (s12 + (Number.one << 20)) >> 21; 1424 | s13 += carry[12]; 1425 | s12 -= carry[12] << 21; 1426 | carry[14] = (s14 + (Number.one << 20)) >> 21; 1427 | s15 += carry[14]; 1428 | s14 -= carry[14] << 21; 1429 | carry[16] = (s16 + (Number.one << 20)) >> 21; 1430 | s17 += carry[16]; 1431 | s16 -= carry[16] << 21; 1432 | carry[18] = (s18 + (Number.one << 20)) >> 21; 1433 | s19 += carry[18]; 1434 | s18 -= carry[18] << 21; 1435 | carry[20] = (s20 + (Number.one << 20)) >> 21; 1436 | s21 += carry[20]; 1437 | s20 -= carry[20] << 21; 1438 | carry[22] = (s22 + (Number.one << 20)) >> 21; 1439 | s23 += carry[22]; 1440 | s22 -= carry[22] << 21; 1441 | 1442 | carry[1] = (s1 + (Number.one << 20)) >> 21; 1443 | s2 += carry[1]; 1444 | s1 -= carry[1] << 21; 1445 | carry[3] = (s3 + (Number.one << 20)) >> 21; 1446 | s4 += carry[3]; 1447 | s3 -= carry[3] << 21; 1448 | carry[5] = (s5 + (Number.one << 20)) >> 21; 1449 | s6 += carry[5]; 1450 | s5 -= carry[5] << 21; 1451 | carry[7] = (s7 + (Number.one << 20)) >> 21; 1452 | s8 += carry[7]; 1453 | s7 -= carry[7] << 21; 1454 | carry[9] = (s9 + (Number.one << 20)) >> 21; 1455 | s10 += carry[9]; 1456 | s9 -= carry[9] << 21; 1457 | carry[11] = (s11 + (Number.one << 20)) >> 21; 1458 | s12 += carry[11]; 1459 | s11 -= carry[11] << 21; 1460 | carry[13] = (s13 + (Number.one << 20)) >> 21; 1461 | s14 += carry[13]; 1462 | s13 -= carry[13] << 21; 1463 | carry[15] = (s15 + (Number.one << 20)) >> 21; 1464 | s16 += carry[15]; 1465 | s15 -= carry[15] << 21; 1466 | carry[17] = (s17 + (Number.one << 20)) >> 21; 1467 | s18 += carry[17]; 1468 | s17 -= carry[17] << 21; 1469 | carry[19] = (s19 + (Number.one << 20)) >> 21; 1470 | s20 += carry[19]; 1471 | s19 -= carry[19] << 21; 1472 | carry[21] = (s21 + (Number.one << 20)) >> 21; 1473 | s22 += carry[21]; 1474 | s21 -= carry[21] << 21; 1475 | 1476 | s11 += s23 * Numbers.v666643; 1477 | s12 += s23 * Numbers.v470296; 1478 | s13 += s23 * Numbers.v654183; 1479 | s14 -= s23 * Numbers.v997805; 1480 | s15 += s23 * Numbers.v136657; 1481 | s16 -= s23 * Numbers.v683901; 1482 | s23 = Number.zero; 1483 | 1484 | s10 += s22 * Numbers.v666643; 1485 | s11 += s22 * Numbers.v470296; 1486 | s12 += s22 * Numbers.v654183; 1487 | s13 -= s22 * Numbers.v997805; 1488 | s14 += s22 * Numbers.v136657; 1489 | s15 -= s22 * Numbers.v683901; 1490 | s22 = Number.zero; 1491 | 1492 | s9 += s21 * Numbers.v666643; 1493 | s10 += s21 * Numbers.v470296; 1494 | s11 += s21 * Numbers.v654183; 1495 | s12 -= s21 * Numbers.v997805; 1496 | s13 += s21 * Numbers.v136657; 1497 | s14 -= s21 * Numbers.v683901; 1498 | s21 = Number.zero; 1499 | 1500 | s8 += s20 * Numbers.v666643; 1501 | s9 += s20 * Numbers.v470296; 1502 | s10 += s20 * Numbers.v654183; 1503 | s11 -= s20 * Numbers.v997805; 1504 | s12 += s20 * Numbers.v136657; 1505 | s13 -= s20 * Numbers.v683901; 1506 | s20 = Number.zero; 1507 | 1508 | s7 += s19 * Numbers.v666643; 1509 | s8 += s19 * Numbers.v470296; 1510 | s9 += s19 * Numbers.v654183; 1511 | s10 -= s19 * Numbers.v997805; 1512 | s11 += s19 * Numbers.v136657; 1513 | s12 -= s19 * Numbers.v683901; 1514 | s19 = Number.zero; 1515 | 1516 | s6 += s18 * Numbers.v666643; 1517 | s7 += s18 * Numbers.v470296; 1518 | s8 += s18 * Numbers.v654183; 1519 | s9 -= s18 * Numbers.v997805; 1520 | s10 += s18 * Numbers.v136657; 1521 | s11 -= s18 * Numbers.v683901; 1522 | s18 = Number.zero; 1523 | 1524 | carry[6] = (s6 + (Number.one << 20)) >> 21; 1525 | s7 += carry[6]; 1526 | s6 -= carry[6] << 21; 1527 | carry[8] = (s8 + (Number.one << 20)) >> 21; 1528 | s9 += carry[8]; 1529 | s8 -= carry[8] << 21; 1530 | carry[10] = (s10 + (Number.one << 20)) >> 21; 1531 | s11 += carry[10]; 1532 | s10 -= carry[10] << 21; 1533 | carry[12] = (s12 + (Number.one << 20)) >> 21; 1534 | s13 += carry[12]; 1535 | s12 -= carry[12] << 21; 1536 | carry[14] = (s14 + (Number.one << 20)) >> 21; 1537 | s15 += carry[14]; 1538 | s14 -= carry[14] << 21; 1539 | carry[16] = (s16 + (Number.one << 20)) >> 21; 1540 | s17 += carry[16]; 1541 | s16 -= carry[16] << 21; 1542 | 1543 | carry[7] = (s7 + (Number.one << 20)) >> 21; 1544 | s8 += carry[7]; 1545 | s7 -= carry[7] << 21; 1546 | carry[9] = (s9 + (Number.one << 20)) >> 21; 1547 | s10 += carry[9]; 1548 | s9 -= carry[9] << 21; 1549 | carry[11] = (s11 + (Number.one << 20)) >> 21; 1550 | s12 += carry[11]; 1551 | s11 -= carry[11] << 21; 1552 | carry[13] = (s13 + (Number.one << 20)) >> 21; 1553 | s14 += carry[13]; 1554 | s13 -= carry[13] << 21; 1555 | carry[15] = (s15 + (Number.one << 20)) >> 21; 1556 | s16 += carry[15]; 1557 | s15 -= carry[15] << 21; 1558 | 1559 | s5 += s17 * Numbers.v666643; 1560 | s6 += s17 * Numbers.v470296; 1561 | s7 += s17 * Numbers.v654183; 1562 | s8 -= s17 * Numbers.v997805; 1563 | s9 += s17 * Numbers.v136657; 1564 | s10 -= s17 * Numbers.v683901; 1565 | s17 = Number.zero; 1566 | 1567 | s4 += s16 * Numbers.v666643; 1568 | s5 += s16 * Numbers.v470296; 1569 | s6 += s16 * Numbers.v654183; 1570 | s7 -= s16 * Numbers.v997805; 1571 | s8 += s16 * Numbers.v136657; 1572 | s9 -= s16 * Numbers.v683901; 1573 | s16 = Number.zero; 1574 | 1575 | s3 += s15 * Numbers.v666643; 1576 | s4 += s15 * Numbers.v470296; 1577 | s5 += s15 * Numbers.v654183; 1578 | s6 -= s15 * Numbers.v997805; 1579 | s7 += s15 * Numbers.v136657; 1580 | s8 -= s15 * Numbers.v683901; 1581 | s15 = Number.zero; 1582 | 1583 | s2 += s14 * Numbers.v666643; 1584 | s3 += s14 * Numbers.v470296; 1585 | s4 += s14 * Numbers.v654183; 1586 | s5 -= s14 * Numbers.v997805; 1587 | s6 += s14 * Numbers.v136657; 1588 | s7 -= s14 * Numbers.v683901; 1589 | s14 = Number.zero; 1590 | 1591 | s1 += s13 * Numbers.v666643; 1592 | s2 += s13 * Numbers.v470296; 1593 | s3 += s13 * Numbers.v654183; 1594 | s4 -= s13 * Numbers.v997805; 1595 | s5 += s13 * Numbers.v136657; 1596 | s6 -= s13 * Numbers.v683901; 1597 | s13 = Number.zero; 1598 | 1599 | s0 += s12 * Numbers.v666643; 1600 | s1 += s12 * Numbers.v470296; 1601 | s2 += s12 * Numbers.v654183; 1602 | s3 -= s12 * Numbers.v997805; 1603 | s4 += s12 * Numbers.v136657; 1604 | s5 -= s12 * Numbers.v683901; 1605 | s12 = Number.zero; 1606 | 1607 | carry[0] = (s0 + (Number.one << 20)) >> 21; 1608 | s1 += carry[0]; 1609 | s0 -= carry[0] << 21; 1610 | carry[2] = (s2 + (Number.one << 20)) >> 21; 1611 | s3 += carry[2]; 1612 | s2 -= carry[2] << 21; 1613 | carry[4] = (s4 + (Number.one << 20)) >> 21; 1614 | s5 += carry[4]; 1615 | s4 -= carry[4] << 21; 1616 | carry[6] = (s6 + (Number.one << 20)) >> 21; 1617 | s7 += carry[6]; 1618 | s6 -= carry[6] << 21; 1619 | carry[8] = (s8 + (Number.one << 20)) >> 21; 1620 | s9 += carry[8]; 1621 | s8 -= carry[8] << 21; 1622 | carry[10] = (s10 + (Number.one << 20)) >> 21; 1623 | s11 += carry[10]; 1624 | s10 -= carry[10] << 21; 1625 | 1626 | carry[1] = (s1 + (Number.one << 20)) >> 21; 1627 | s2 += carry[1]; 1628 | s1 -= carry[1] << 21; 1629 | carry[3] = (s3 + (Number.one << 20)) >> 21; 1630 | s4 += carry[3]; 1631 | s3 -= carry[3] << 21; 1632 | carry[5] = (s5 + (Number.one << 20)) >> 21; 1633 | s6 += carry[5]; 1634 | s5 -= carry[5] << 21; 1635 | carry[7] = (s7 + (Number.one << 20)) >> 21; 1636 | s8 += carry[7]; 1637 | s7 -= carry[7] << 21; 1638 | carry[9] = (s9 + (Number.one << 20)) >> 21; 1639 | s10 += carry[9]; 1640 | s9 -= carry[9] << 21; 1641 | carry[11] = (s11 + (Number.one << 20)) >> 21; 1642 | s12 += carry[11]; 1643 | s11 -= carry[11] << 21; 1644 | 1645 | s0 += s12 * Numbers.v666643; 1646 | s1 += s12 * Numbers.v470296; 1647 | s2 += s12 * Numbers.v654183; 1648 | s3 -= s12 * Numbers.v997805; 1649 | s4 += s12 * Numbers.v136657; 1650 | s5 -= s12 * Numbers.v683901; 1651 | s12 = Number.zero; 1652 | 1653 | carry[0] = s0 >> 21; 1654 | s1 += carry[0]; 1655 | s0 -= carry[0] << 21; 1656 | carry[1] = s1 >> 21; 1657 | s2 += carry[1]; 1658 | s1 -= carry[1] << 21; 1659 | carry[2] = s2 >> 21; 1660 | s3 += carry[2]; 1661 | s2 -= carry[2] << 21; 1662 | carry[3] = s3 >> 21; 1663 | s4 += carry[3]; 1664 | s3 -= carry[3] << 21; 1665 | carry[4] = s4 >> 21; 1666 | s5 += carry[4]; 1667 | s4 -= carry[4] << 21; 1668 | carry[5] = s5 >> 21; 1669 | s6 += carry[5]; 1670 | s5 -= carry[5] << 21; 1671 | carry[6] = s6 >> 21; 1672 | s7 += carry[6]; 1673 | s6 -= carry[6] << 21; 1674 | carry[7] = s7 >> 21; 1675 | s8 += carry[7]; 1676 | s7 -= carry[7] << 21; 1677 | carry[8] = s8 >> 21; 1678 | s9 += carry[8]; 1679 | s8 -= carry[8] << 21; 1680 | carry[9] = s9 >> 21; 1681 | s10 += carry[9]; 1682 | s9 -= carry[9] << 21; 1683 | carry[10] = s10 >> 21; 1684 | s11 += carry[10]; 1685 | s10 -= carry[10] << 21; 1686 | carry[11] = s11 >> 21; 1687 | s12 += carry[11]; 1688 | s11 -= carry[11] << 21; 1689 | 1690 | s0 += s12 * Numbers.v666643; 1691 | s1 += s12 * Numbers.v470296; 1692 | s2 += s12 * Numbers.v654183; 1693 | s3 -= s12 * Numbers.v997805; 1694 | s4 += s12 * Numbers.v136657; 1695 | s5 -= s12 * Numbers.v683901; 1696 | s12 = Number.zero; 1697 | 1698 | carry[0] = s0 >> 21; 1699 | s1 += carry[0]; 1700 | s0 -= carry[0] << 21; 1701 | carry[1] = s1 >> 21; 1702 | s2 += carry[1]; 1703 | s1 -= carry[1] << 21; 1704 | carry[2] = s2 >> 21; 1705 | s3 += carry[2]; 1706 | s2 -= carry[2] << 21; 1707 | carry[3] = s3 >> 21; 1708 | s4 += carry[3]; 1709 | s3 -= carry[3] << 21; 1710 | carry[4] = s4 >> 21; 1711 | s5 += carry[4]; 1712 | s4 -= carry[4] << 21; 1713 | carry[5] = s5 >> 21; 1714 | s6 += carry[5]; 1715 | s5 -= carry[5] << 21; 1716 | carry[6] = s6 >> 21; 1717 | s7 += carry[6]; 1718 | s6 -= carry[6] << 21; 1719 | carry[7] = s7 >> 21; 1720 | s8 += carry[7]; 1721 | s7 -= carry[7] << 21; 1722 | carry[8] = s8 >> 21; 1723 | s9 += carry[8]; 1724 | s8 -= carry[8] << 21; 1725 | carry[9] = s9 >> 21; 1726 | s10 += carry[9]; 1727 | s9 -= carry[9] << 21; 1728 | carry[10] = s10 >> 21; 1729 | s11 += carry[10]; 1730 | s10 -= carry[10] << 21; 1731 | 1732 | s[0] = (s0 >> 0).intValue; 1733 | s[1] = (s0 >> 8).intValue; 1734 | s[2] = ((s0 >> 16) | (s1 << 5)).intValue; 1735 | s[3] = (s1 >> 3).intValue; 1736 | s[4] = (s1 >> 11).intValue; 1737 | s[5] = ((s1 >> 19) | (s2 << 2)).intValue; 1738 | s[6] = (s2 >> 6).intValue; 1739 | s[7] = ((s2 >> 14) | (s3 << 7)).intValue; 1740 | s[8] = (s3 >> 1).intValue; 1741 | s[9] = (s3 >> 9).intValue; 1742 | s[10] = ((s3 >> 17) | (s4 << 4)).intValue; 1743 | s[11] = (s4 >> 4).intValue; 1744 | s[12] = (s4 >> 12).intValue; 1745 | s[13] = ((s4 >> 20) | (s5 << 1)).intValue; 1746 | s[14] = (s5 >> 7).intValue; 1747 | s[15] = ((s5 >> 15) | (s6 << 6)).intValue; 1748 | s[16] = (s6 >> 2).intValue; 1749 | s[17] = (s6 >> 10).intValue; 1750 | s[18] = ((s6 >> 18) | (s7 << 3)).intValue; 1751 | s[19] = (s7 >> 5).intValue; 1752 | s[20] = (s7 >> 13).intValue; 1753 | s[21] = (s8 >> 0).intValue; 1754 | s[22] = (s8 >> 8).intValue; 1755 | s[23] = ((s8 >> 16) | (s9 << 5)).intValue; 1756 | s[24] = (s9 >> 3).intValue; 1757 | s[25] = (s9 >> 11).intValue; 1758 | s[26] = ((s9 >> 19) | (s10 << 2)).intValue; 1759 | s[27] = (s10 >> 6).intValue; 1760 | s[28] = ((s10 >> 14) | (s11 << 7)).intValue; 1761 | s[29] = (s11 >> 1).intValue; 1762 | s[30] = (s11 >> 9).intValue; 1763 | s[31] = (s11 >> 17).intValue; 1764 | } 1765 | 1766 | /// Input: 1767 | /// s[0]+256*s[1]+...+256^63*s[63] = s 1768 | /// 1769 | /// Output: 1770 | /// s[0]+256*s[1]+...+256^31*s[31] = s mod l 1771 | /// where l = 2^252 + 27742317777372353535851937790883648493. 1772 | void ScReduce(Uint8List out, Uint8List s) { 1773 | var s0 = Numbers.v2097151 & load3(s.sublist(0, s.length)); 1774 | var s1 = Numbers.v2097151 & (load4(s.sublist(2, s.length)) >> 5); 1775 | var s2 = Numbers.v2097151 & (load3(s.sublist(5, s.length)) >> 2); 1776 | var s3 = Numbers.v2097151 & (load4(s.sublist(7, s.length)) >> 7); 1777 | var s4 = Numbers.v2097151 & (load4(s.sublist(10, s.length)) >> 4); 1778 | var s5 = Numbers.v2097151 & (load3(s.sublist(13, s.length)) >> 1); 1779 | var s6 = Numbers.v2097151 & (load4(s.sublist(15, s.length)) >> 6); 1780 | var s7 = Numbers.v2097151 & (load3(s.sublist(18, s.length)) >> 3); 1781 | var s8 = Numbers.v2097151 & load3(s.sublist(21, s.length)); 1782 | var s9 = Numbers.v2097151 & (load4(s.sublist(23, s.length)) >> 5); 1783 | var s10 = Numbers.v2097151 & (load3(s.sublist(26, s.length)) >> 2); 1784 | var s11 = Numbers.v2097151 & (load4(s.sublist(28, s.length)) >> 7); 1785 | var s12 = Numbers.v2097151 & (load4(s.sublist(31, s.length)) >> 4); 1786 | var s13 = Numbers.v2097151 & (load3(s.sublist(34, s.length)) >> 1); 1787 | var s14 = Numbers.v2097151 & (load4(s.sublist(36, s.length)) >> 6); 1788 | var s15 = Numbers.v2097151 & (load3(s.sublist(39, s.length)) >> 3); 1789 | var s16 = Numbers.v2097151 & load3(s.sublist(42, s.length)); 1790 | var s17 = Numbers.v2097151 & (load4(s.sublist(44, s.length)) >> 5); 1791 | var s18 = Numbers.v2097151 & (load3(s.sublist(47, s.length)) >> 2); 1792 | var s19 = Numbers.v2097151 & (load4(s.sublist(49, s.length)) >> 7); 1793 | var s20 = Numbers.v2097151 & (load4(s.sublist(52, s.length)) >> 4); 1794 | var s21 = Numbers.v2097151 & (load3(s.sublist(55, s.length)) >> 1); 1795 | var s22 = Numbers.v2097151 & (load4(s.sublist(57, s.length)) >> 6); 1796 | var s23 = (load4(s.sublist(60, s.length)) >> 3); 1797 | 1798 | s11 += s23 * Numbers.v666643; 1799 | s12 += s23 * Numbers.v470296; 1800 | s13 += s23 * Numbers.v654183; 1801 | s14 -= s23 * Numbers.v997805; 1802 | s15 += s23 * Numbers.v136657; 1803 | s16 -= s23 * Numbers.v683901; 1804 | s23 = Number.zero; 1805 | 1806 | s10 += s22 * Numbers.v666643; 1807 | s11 += s22 * Numbers.v470296; 1808 | s12 += s22 * Numbers.v654183; 1809 | s13 -= s22 * Numbers.v997805; 1810 | s14 += s22 * Numbers.v136657; 1811 | s15 -= s22 * Numbers.v683901; 1812 | s22 = Number.zero; 1813 | 1814 | s9 += s21 * Numbers.v666643; 1815 | s10 += s21 * Numbers.v470296; 1816 | s11 += s21 * Numbers.v654183; 1817 | s12 -= s21 * Numbers.v997805; 1818 | s13 += s21 * Numbers.v136657; 1819 | s14 -= s21 * Numbers.v683901; 1820 | s21 = Number.zero; 1821 | 1822 | s8 += s20 * Numbers.v666643; 1823 | s9 += s20 * Numbers.v470296; 1824 | s10 += s20 * Numbers.v654183; 1825 | s11 -= s20 * Numbers.v997805; 1826 | s12 += s20 * Numbers.v136657; 1827 | s13 -= s20 * Numbers.v683901; 1828 | s20 = Number.zero; 1829 | 1830 | s7 += s19 * Numbers.v666643; 1831 | s8 += s19 * Numbers.v470296; 1832 | s9 += s19 * Numbers.v654183; 1833 | s10 -= s19 * Numbers.v997805; 1834 | s11 += s19 * Numbers.v136657; 1835 | s12 -= s19 * Numbers.v683901; 1836 | s19 = Number.zero; 1837 | 1838 | s6 += s18 * Numbers.v666643; 1839 | s7 += s18 * Numbers.v470296; 1840 | s8 += s18 * Numbers.v654183; 1841 | s9 -= s18 * Numbers.v997805; 1842 | s10 += s18 * Numbers.v136657; 1843 | s11 -= s18 * Numbers.v683901; 1844 | s18 = Number.zero; 1845 | 1846 | var carry = List.filled(64, Number.zero); 1847 | 1848 | carry[6] = (s6 + (Number.one << 20)) >> 21; 1849 | s7 += carry[6]; 1850 | s6 -= carry[6] << 21; 1851 | carry[8] = (s8 + (Number.one << 20)) >> 21; 1852 | s9 += carry[8]; 1853 | s8 -= carry[8] << 21; 1854 | carry[10] = (s10 + (Number.one << 20)) >> 21; 1855 | s11 += carry[10]; 1856 | s10 -= carry[10] << 21; 1857 | carry[12] = (s12 + (Number.one << 20)) >> 21; 1858 | s13 += carry[12]; 1859 | s12 -= carry[12] << 21; 1860 | carry[14] = (s14 + (Number.one << 20)) >> 21; 1861 | s15 += carry[14]; 1862 | s14 -= carry[14] << 21; 1863 | carry[16] = (s16 + (Number.one << 20)) >> 21; 1864 | s17 += carry[16]; 1865 | s16 -= carry[16] << 21; 1866 | 1867 | carry[7] = (s7 + (Number.one << 20)) >> 21; 1868 | s8 += carry[7]; 1869 | s7 -= carry[7] << 21; 1870 | carry[9] = (s9 + (Number.one << 20)) >> 21; 1871 | s10 += carry[9]; 1872 | s9 -= carry[9] << 21; 1873 | carry[11] = (s11 + (Number.one << 20)) >> 21; 1874 | s12 += carry[11]; 1875 | s11 -= carry[11] << 21; 1876 | carry[13] = (s13 + (Number.one << 20)) >> 21; 1877 | s14 += carry[13]; 1878 | s13 -= carry[13] << 21; 1879 | carry[15] = (s15 + (Number.one << 20)) >> 21; 1880 | s16 += carry[15]; 1881 | s15 -= carry[15] << 21; 1882 | 1883 | s5 += s17 * Numbers.v666643; 1884 | s6 += s17 * Numbers.v470296; 1885 | s7 += s17 * Numbers.v654183; 1886 | s8 -= s17 * Numbers.v997805; 1887 | s9 += s17 * Numbers.v136657; 1888 | s10 -= s17 * Numbers.v683901; 1889 | s17 = Number.zero; 1890 | 1891 | s4 += s16 * Numbers.v666643; 1892 | s5 += s16 * Numbers.v470296; 1893 | s6 += s16 * Numbers.v654183; 1894 | s7 -= s16 * Numbers.v997805; 1895 | s8 += s16 * Numbers.v136657; 1896 | s9 -= s16 * Numbers.v683901; 1897 | s16 = Number.zero; 1898 | 1899 | s3 += s15 * Numbers.v666643; 1900 | s4 += s15 * Numbers.v470296; 1901 | s5 += s15 * Numbers.v654183; 1902 | s6 -= s15 * Numbers.v997805; 1903 | s7 += s15 * Numbers.v136657; 1904 | s8 -= s15 * Numbers.v683901; 1905 | s15 = Number.zero; 1906 | 1907 | s2 += s14 * Numbers.v666643; 1908 | s3 += s14 * Numbers.v470296; 1909 | s4 += s14 * Numbers.v654183; 1910 | s5 -= s14 * Numbers.v997805; 1911 | s6 += s14 * Numbers.v136657; 1912 | s7 -= s14 * Numbers.v683901; 1913 | s14 = Number.zero; 1914 | 1915 | s1 += s13 * Numbers.v666643; 1916 | s2 += s13 * Numbers.v470296; 1917 | s3 += s13 * Numbers.v654183; 1918 | s4 -= s13 * Numbers.v997805; 1919 | s5 += s13 * Numbers.v136657; 1920 | s6 -= s13 * Numbers.v683901; 1921 | s13 = Number.zero; 1922 | 1923 | s0 += s12 * Numbers.v666643; 1924 | s1 += s12 * Numbers.v470296; 1925 | s2 += s12 * Numbers.v654183; 1926 | s3 -= s12 * Numbers.v997805; 1927 | s4 += s12 * Numbers.v136657; 1928 | s5 -= s12 * Numbers.v683901; 1929 | s12 = Number.zero; 1930 | 1931 | carry[0] = (s0 + (Number.one << 20)) >> 21; 1932 | s1 += carry[0]; 1933 | s0 -= carry[0] << 21; 1934 | carry[2] = (s2 + (Number.one << 20)) >> 21; 1935 | s3 += carry[2]; 1936 | s2 -= carry[2] << 21; 1937 | carry[4] = (s4 + (Number.one << 20)) >> 21; 1938 | s5 += carry[4]; 1939 | s4 -= carry[4] << 21; 1940 | carry[6] = (s6 + (Number.one << 20)) >> 21; 1941 | s7 += carry[6]; 1942 | s6 -= carry[6] << 21; 1943 | carry[8] = (s8 + (Number.one << 20)) >> 21; 1944 | s9 += carry[8]; 1945 | s8 -= carry[8] << 21; 1946 | carry[10] = (s10 + (Number.one << 20)) >> 21; 1947 | s11 += carry[10]; 1948 | s10 -= carry[10] << 21; 1949 | 1950 | carry[1] = (s1 + (Number.one << 20)) >> 21; 1951 | s2 += carry[1]; 1952 | s1 -= carry[1] << 21; 1953 | carry[3] = (s3 + (Number.one << 20)) >> 21; 1954 | s4 += carry[3]; 1955 | s3 -= carry[3] << 21; 1956 | carry[5] = (s5 + (Number.one << 20)) >> 21; 1957 | s6 += carry[5]; 1958 | s5 -= carry[5] << 21; 1959 | carry[7] = (s7 + (Number.one << 20)) >> 21; 1960 | s8 += carry[7]; 1961 | s7 -= carry[7] << 21; 1962 | carry[9] = (s9 + (Number.one << 20)) >> 21; 1963 | s10 += carry[9]; 1964 | s9 -= carry[9] << 21; 1965 | carry[11] = (s11 + (Number.one << 20)) >> 21; 1966 | s12 += carry[11]; 1967 | s11 -= carry[11] << 21; 1968 | 1969 | s0 += s12 * Numbers.v666643; 1970 | s1 += s12 * Numbers.v470296; 1971 | s2 += s12 * Numbers.v654183; 1972 | s3 -= s12 * Numbers.v997805; 1973 | s4 += s12 * Numbers.v136657; 1974 | s5 -= s12 * Numbers.v683901; 1975 | s12 = Number.zero; 1976 | 1977 | carry[0] = s0 >> 21; 1978 | s1 += carry[0]; 1979 | s0 -= carry[0] << 21; 1980 | carry[1] = s1 >> 21; 1981 | s2 += carry[1]; 1982 | s1 -= carry[1] << 21; 1983 | carry[2] = s2 >> 21; 1984 | s3 += carry[2]; 1985 | s2 -= carry[2] << 21; 1986 | carry[3] = s3 >> 21; 1987 | s4 += carry[3]; 1988 | s3 -= carry[3] << 21; 1989 | carry[4] = s4 >> 21; 1990 | s5 += carry[4]; 1991 | s4 -= carry[4] << 21; 1992 | carry[5] = s5 >> 21; 1993 | s6 += carry[5]; 1994 | s5 -= carry[5] << 21; 1995 | carry[6] = s6 >> 21; 1996 | s7 += carry[6]; 1997 | s6 -= carry[6] << 21; 1998 | carry[7] = s7 >> 21; 1999 | s8 += carry[7]; 2000 | s7 -= carry[7] << 21; 2001 | carry[8] = s8 >> 21; 2002 | s9 += carry[8]; 2003 | s8 -= carry[8] << 21; 2004 | carry[9] = s9 >> 21; 2005 | s10 += carry[9]; 2006 | s9 -= carry[9] << 21; 2007 | carry[10] = s10 >> 21; 2008 | s11 += carry[10]; 2009 | s10 -= carry[10] << 21; 2010 | carry[11] = s11 >> 21; 2011 | s12 += carry[11]; 2012 | s11 -= carry[11] << 21; 2013 | 2014 | s0 += s12 * Numbers.v666643; 2015 | s1 += s12 * Numbers.v470296; 2016 | s2 += s12 * Numbers.v654183; 2017 | s3 -= s12 * Numbers.v997805; 2018 | s4 += s12 * Numbers.v136657; 2019 | s5 -= s12 * Numbers.v683901; 2020 | s12 = Number.zero; 2021 | 2022 | carry[0] = s0 >> 21; 2023 | s1 += carry[0]; 2024 | s0 -= carry[0] << 21; 2025 | carry[1] = s1 >> 21; 2026 | s2 += carry[1]; 2027 | s1 -= carry[1] << 21; 2028 | carry[2] = s2 >> 21; 2029 | s3 += carry[2]; 2030 | s2 -= carry[2] << 21; 2031 | carry[3] = s3 >> 21; 2032 | s4 += carry[3]; 2033 | s3 -= carry[3] << 21; 2034 | carry[4] = s4 >> 21; 2035 | s5 += carry[4]; 2036 | s4 -= carry[4] << 21; 2037 | carry[5] = s5 >> 21; 2038 | s6 += carry[5]; 2039 | s5 -= carry[5] << 21; 2040 | carry[6] = s6 >> 21; 2041 | s7 += carry[6]; 2042 | s6 -= carry[6] << 21; 2043 | carry[7] = s7 >> 21; 2044 | s8 += carry[7]; 2045 | s7 -= carry[7] << 21; 2046 | carry[8] = s8 >> 21; 2047 | s9 += carry[8]; 2048 | s8 -= carry[8] << 21; 2049 | carry[9] = s9 >> 21; 2050 | s10 += carry[9]; 2051 | s9 -= carry[9] << 21; 2052 | carry[10] = s10 >> 21; 2053 | s11 += carry[10]; 2054 | s10 -= carry[10] << 21; 2055 | 2056 | out[0] = (s0 >> 0).intValue; 2057 | out[1] = (s0 >> 8).intValue; 2058 | out[2] = ((s0 >> 16) | (s1 << 5)).intValue; 2059 | out[3] = (s1 >> 3).intValue; 2060 | out[4] = (s1 >> 11).intValue; 2061 | out[5] = ((s1 >> 19) | (s2 << 2)).intValue; 2062 | out[6] = (s2 >> 6).intValue; 2063 | out[7] = ((s2 >> 14) | (s3 << 7)).intValue; 2064 | out[8] = (s3 >> 1).intValue; 2065 | out[9] = (s3 >> 9).intValue; 2066 | out[10] = ((s3 >> 17) | (s4 << 4)).intValue; 2067 | out[11] = (s4 >> 4).intValue; 2068 | out[12] = (s4 >> 12).intValue; 2069 | out[13] = ((s4 >> 20) | (s5 << 1)).intValue; 2070 | out[14] = (s5 >> 7).intValue; 2071 | out[15] = ((s5 >> 15) | (s6 << 6)).intValue; 2072 | out[16] = (s6 >> 2).intValue; 2073 | out[17] = (s6 >> 10).intValue; 2074 | out[18] = ((s6 >> 18) | (s7 << 3)).intValue; 2075 | out[19] = (s7 >> 5).intValue; 2076 | out[20] = (s7 >> 13).intValue; 2077 | out[21] = (s8 >> 0).intValue; 2078 | out[22] = (s8 >> 8).intValue; 2079 | out[23] = ((s8 >> 16) | (s9 << 5)).intValue; 2080 | out[24] = (s9 >> 3).intValue; 2081 | out[25] = (s9 >> 11).intValue; 2082 | out[26] = ((s9 >> 19) | (s10 << 2)).intValue; 2083 | out[27] = (s10 >> 6).intValue; 2084 | out[28] = ((s10 >> 14) | (s11 << 7)).intValue; 2085 | out[29] = (s11 >> 1).intValue; 2086 | out[30] = (s11 >> 9).intValue; 2087 | out[31] = (s11 >> 17).intValue; 2088 | } 2089 | 2090 | /// order is the order of Curve25519 in little-endian form. 2091 | var order = List.from( 2092 | [ 2093 | BigInt.parse('0x5812631a5cf5d3ed'), 2094 | BigInt.parse('0x14def9dea2f79cd6'), 2095 | BigInt.zero, 2096 | BigInt.parse('0x1000000000000000') 2097 | ], 2098 | ); 2099 | 2100 | /// ScMinimal returns true if the given scalar is less than the order of the 2101 | /// curve. 2102 | bool ScMinimal(Uint8List scalar) { 2103 | for (var i = 3;; i--) { 2104 | // var v = binary.LittleEndian.Uint64(scalar[i*8:]); 2105 | var v = Uint64(scalar.sublist(i * 8, scalar.length)); 2106 | if (v > order[i].toInt()) { 2107 | return false; 2108 | } else if (v < order[i].toInt()) { 2109 | break; 2110 | } else if (i == 0) { 2111 | return false; 2112 | } 2113 | } 2114 | 2115 | return true; 2116 | } 2117 | 2118 | int Uint64(Uint8List b) { 2119 | return b[0] | 2120 | b[1] << 8 | 2121 | b[2] << 16 | 2122 | b[3] << 24 | 2123 | b[4] << 32 | 2124 | b[5] << 40 | 2125 | b[6] << 48 | 2126 | b[7] << 56; 2127 | } 2128 | -------------------------------------------------------------------------------- /lib/src/numbers.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_number/adaptive_number.dart'; 2 | 3 | abstract class Numbers { 4 | static Number v8 = Number(8); 5 | static Number v15 = Number(15); 6 | static Number v19 = Number(19); 7 | static Number v24 = Number(24); 8 | static Number v25 = Number(25); 9 | static Number v26 = Number(26); 10 | static Number v38 = Number(38); 11 | static Number v136657 = Number(136657); 12 | static Number v2097151 = Number(2097151); 13 | static Number v470296 = Number(470296); 14 | static Number v683901 = Number(683901); 15 | static Number v654183 = Number(654183); 16 | static Number v666643 = Number(666643); 17 | static Number v997805 = Number(997805); 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'dart:typed_data'; 4 | 5 | void arrayCopy(List src, int srcPos, List dest, int destPos, int length) { 6 | dest.setRange(destPos, length + destPos, src, srcPos); 7 | } 8 | 9 | final _defaultSecureRandom = Random.secure(); 10 | 11 | void fillBytesWithSecureRandomNumbers(Uint8List bytes, {Random? random}) { 12 | random ??= _defaultSecureRandom; 13 | for (var i = 0; i < bytes.length; i++) { 14 | bytes[i] = random.nextInt(256); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: ed25519_edwards 2 | description: Dart port of ed25519 from Go Cryptography ed25519 3 | version: 0.3.1 4 | homepage: https://github.com/Tougee/ed25519 5 | 6 | environment: 7 | sdk: '>=2.12.0 <3.0.0' 8 | 9 | dependencies: 10 | collection: ^1.15.0 11 | crypto: ^3.0.0 12 | convert: ^3.0.0 13 | adaptive_number: ^1.0.0 14 | dev_dependencies: 15 | pedantic: ^1.10.0 16 | test: ^1.16.4 17 | hex: ^0.2.0 18 | benchmark_harness: ^2.0.0 19 | -------------------------------------------------------------------------------- /test/benchmark/ed25519_benchmark.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:ed25519_edwards/ed25519_edwards.dart'; 4 | 5 | import 'rate_benchmark.dart'; 6 | 7 | class Ed25519Benchmark extends RateBenchmark { 8 | Ed25519Benchmark(bool forSigning, [int dataLength = 1024 * 1024]) 9 | : _forSigning = forSigning, 10 | _data = Uint8List(dataLength), 11 | super('Ed25519 - ${forSigning ? 'sign' : 'verify'}'); 12 | 13 | final Uint8List _data; 14 | final bool _forSigning; 15 | late final KeyPair _keyPair; 16 | Uint8List? _signature; 17 | 18 | @override 19 | void setup() { 20 | _keyPair = generateKey(); 21 | _signature = sign(_keyPair.privateKey, _data); 22 | } 23 | 24 | @override 25 | void run() { 26 | if (_forSigning) { 27 | sign(_keyPair.privateKey, _data); 28 | } else if (_signature != null) { 29 | verify(_keyPair.publicKey, _data, _signature!); 30 | } 31 | addSample(_data.length); 32 | } 33 | } 34 | 35 | void main() { 36 | Ed25519Benchmark(true).report(); 37 | Ed25519Benchmark(false).report(); 38 | } 39 | -------------------------------------------------------------------------------- /test/benchmark/rate_benchmark.dart: -------------------------------------------------------------------------------- 1 | import 'package:benchmark_harness/benchmark_harness.dart'; 2 | 3 | abstract class RateBenchmark extends BenchmarkBase { 4 | RateBenchmark(String name, {this.runLength = 5000}) 5 | : super(name, emitter: RateEmitter()) { 6 | (emitter as RateEmitter).benchmark = this; 7 | } 8 | 9 | final int runLength; 10 | int _totalData = 0; 11 | int _iterations = 0; 12 | 13 | @override 14 | ScoreEmitter get emitter => super.emitter; 15 | 16 | void addSample(int processedData) { 17 | _totalData += processedData; 18 | } 19 | 20 | @override 21 | void exercise() { 22 | _totalData = 0; 23 | _iterations = 0; 24 | 25 | var watch = Stopwatch()..start(); 26 | while (watch.elapsedMilliseconds < runLength) { 27 | run(); 28 | _iterations++; 29 | } 30 | } 31 | } 32 | 33 | class RateEmitter implements ScoreEmitter { 34 | late RateBenchmark benchmark; 35 | 36 | int get totalData => benchmark._totalData; 37 | int get iterations => benchmark._iterations; 38 | 39 | @override 40 | void emit(String testName, double value) { 41 | final ms = value / 1000; 42 | final s = ms / 1000; 43 | final date = DateTime.now().toString().split('.')[0]; 44 | 45 | print('| $date | ' 46 | '$testName | ' 47 | '${_formatDataLength(totalData / s)}/s | ' 48 | '$iterations iterations | ' 49 | '${ms.toInt()} ms | ' 50 | '${_formatDataLength(totalData)} |'); 51 | } 52 | 53 | String _formatDataLength(num dataLen) { 54 | if (dataLen < 1024) { 55 | return '${dataLen.toStringAsFixed(2)} B'; 56 | } else if (dataLen < (1024 * 1024)) { 57 | return '${(dataLen / 1024).toStringAsFixed(2)} KB'; 58 | } else if (dataLen < (1024 * 1024 * 1024)) { 59 | return '${(dataLen / (1024 * 1024)).toStringAsFixed(2)} MB'; 60 | } else { 61 | return '${(dataLen / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB'; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /test/ed25519_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:collection/collection.dart'; 5 | import 'package:ed25519_edwards/ed25519_edwards.dart' as ed; 6 | import 'package:hex/hex.dart'; 7 | import 'package:test/test.dart'; 8 | 9 | void main() { 10 | void testVector( 11 | String plain, String seedStr, String publicStr, String sigStr) { 12 | var seed = HEX.decode(seedStr); 13 | var public = HEX.decode(publicStr); 14 | var privateKey = ed.PrivateKey([...seed, ...public]); 15 | var publicKey = ed.public(privateKey); 16 | var message = HEX.decode(plain); 17 | var sig = ed.sign(privateKey, message as Uint8List); 18 | 19 | var targetSig = HEX.decode(sigStr); 20 | assert(ListEquality().equals(targetSig, sig)); 21 | 22 | var result = ed.verify(publicKey, message, sig); 23 | assert(result == true); 24 | } 25 | 26 | test('test rfc8032 cases', () { 27 | testVector( 28 | '', 29 | '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60', 30 | 'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a', 31 | 'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b'); 32 | testVector( 33 | '72', 34 | '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb', 35 | '3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c', 36 | '92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00'); 37 | testVector( 38 | 'af82', 39 | 'c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7', 40 | 'fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025', 41 | '6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a'); 42 | testVector( 43 | '08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d879de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4feba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbefefd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed185ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f27088d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b0707e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128bab27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51addd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429ec96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb751fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34dff7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e488acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a32ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5fb93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b50d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380db2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0', 44 | 'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5', 45 | '278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e', 46 | '0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03'); 47 | testVector( 48 | 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f', 49 | '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42', 50 | 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf', 51 | 'dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704'); 52 | }); 53 | 54 | test('testSignVerify', () { 55 | var keyPair = ed.generateKey(); 56 | var privateKey = keyPair.privateKey; 57 | var publicKey = keyPair.publicKey; 58 | var message = utf8.encode('test message'); 59 | var sig = ed.sign(privateKey, message as Uint8List); 60 | var result = ed.verify(publicKey, message, sig); 61 | assert(result == true); 62 | 63 | var wrongMessage = utf8.encode('wrong message'); 64 | var wrongResult = ed.verify(publicKey, wrongMessage as Uint8List, sig); 65 | assert(wrongResult == false); 66 | }); 67 | } 68 | --------------------------------------------------------------------------------