├── .github └── workflows │ ├── main.yml │ └── publish.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── config ├── babel.config.js ├── eslint.config.js ├── jest.config.js └── rollup.config.js ├── data ├── ristretto.data.js └── scalars.data.js ├── dist └── ristretto255.min.js ├── package.json ├── ristretto255.benchmarks.css ├── ristretto255.benchmarks.html ├── ristretto255.benchmarks.js ├── ristretto255.benchmarks.min.js └── src ├── ristretto255.js └── ristretto255.test.js /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Yarn CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [10.x, 12.x, 14.x, 15.x] 17 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Use Node.js ${{ matrix.node-version }} 22 | uses: actions/setup-node@v2 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | - run: yarn install 26 | - run: yarn lint 27 | - run: yarn build 28 | - run: yarn test 29 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | # Setup .npmrc file to publish to npm 13 | - uses: actions/setup-node@v2 14 | with: 15 | node-version: '12.x' 16 | registry-url: 'https://registry.npmjs.org' 17 | - run: npm install 18 | - run: npm publish 19 | env: 20 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Since this is a library, ignore yarn.lock 9 | yarn.lock 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (https://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # TypeScript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # next.js build output 64 | .next 65 | 66 | # editors 67 | .vscode/ 68 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.1.3 (December 12, 2023) 4 | 5 | * Removed extraneous crypto import from rollup config 6 | 7 | ## 0.1.2 (August 18, 2021) 8 | 9 | * Updated dependencies 10 | 11 | ## 0.1.1 (April 12, 2020) 12 | 13 | * Addressing IE11 compatibility issues 14 | 15 | ## 0.1.0 (February 20, 2020) 16 | 17 | * Initial release 18 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Ristretto255.js 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Pull Requests 6 | We actively welcome your pull requests. 7 | 8 | 1. Fork the repo and create your branch from `main`. 9 | 2. If you've added code that should be tested, add tests. 10 | 3. If you've changed APIs, update the documentation. 11 | 4. Ensure the test suite passes. 12 | 5. If you haven't already, complete the Contributor License Agreement ("CLA"). 13 | 14 | ## Contributor License Agreement ("CLA") 15 | In order to accept your pull request, we need you to submit a CLA. You only need 16 | to do this once to work on any of Facebook's open source projects. 17 | 18 | Complete your CLA here: 19 | 20 | ## Issues 21 | We use GitHub issues to track public bugs. Please ensure your description is 22 | clear and has sufficient instructions to be able to reproduce the issue. 23 | 24 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 25 | disclosure of security bugs. In those cases, please go through the process 26 | outlined on that page and do not file a public issue. 27 | 28 | ## License 29 | By contributing to Ristretto255.js, you agree that your contributions will be 30 | licensed under the LICENSE file in the root directory of this source tree. 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Facebook, Inc. and its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ristretto255.js ![Build Status](https://github.com/novifinancial/ristretto255-js/workflows/Yarn%20CI/badge.svg) 2 | ============ 3 | 4 | Ristretto255.js is a pure-JS implementation of the 5 | [Ristretto255](https://ristretto.group/) group operations, built on top of the 6 | popular [TweetNaCl.js](https://tweetnacl.js.org/#/) crypto library. 7 | 8 | 9 | * [Overview](#overview) 10 | * [Installation](#installation) 11 | * [Usage](#usage) 12 | * [System requirements](#system-requirements) 13 | * [Development and testing](#development-and-testing) 14 | * [Benchmarks](#benchmarks) 15 | 16 | Overview 17 | -------- 18 | 19 | This project gives a high-level javascript API for operations in the 20 | [ristretto255](https://ristretto.group/) prime-order group. The ristretto255 21 | group enjoys the speed and safety of Curve25519 while also being prime-order, so 22 | that cryptographic protocols built on top of it will be resistant to [cofactor-related 23 | attacks](https://ristretto.group/why_ristretto.html#pitfalls-of-a-cofactor). 24 | 25 | Installation 26 | -------- 27 | 28 | To install with the yarn package manager, simply run: 29 | 30 | `yarn add ristretto255` 31 | 32 | Usage 33 | ----- 34 | 35 | The main files of this repository include: 36 | 37 | * [ristretto255.js](./ristretto255.js) contains a well documented javascript code 38 | exporting function that provide operations over the prime-order group 39 | ristretto255 as well as operations over the scalars for that group, 40 | 41 | * [ristretto255.min.js](./ristretto255.min.js) is the minified version of `ristretto255.js` 42 | identical to it in functionality; this file is ship-ready, 43 | 44 | * [ristretto255.benchmarks.html](./ristretto255.benchmarks.html) shows an example of 45 | usage for all the exported functions. This file can be opened in the browser 46 | to run the benchmarks and check for browser compatibility. 47 | 48 | 49 | This library exports the following types of arithmetic operations: 50 | 51 | ##### Operations over a prime order group (ristretto255) of order L 52 | 53 | The inputs to all of the functions below should be valid ristretto255 points (which can 54 | be checked by calling `ristretto255.isValid()`); otherwise, the behavior is 55 | undefined, and functions may throw exceptions. 56 | 57 | All ristretto255 elements are stored in the serialized format as 32-element 58 | byte arrays of type `Uint8Array(32)`. 59 | 60 | * `getRandom()`: returns a random point on ristretto255 61 | * `isValid(P)`: returns `true` or `false` 62 | * `fromHash(h)`: returns `P` instantiated from a `Uint8Array(64)` (such as the output 63 | of SHA512) 64 | * `add(P, Q)`: returns `P + Q` 65 | * `sub(P, Q)`: returns `P - Q` 66 | * `scalarMultBase(x)`: returns `x * BASE` 67 | * `scalarMult(x, P)`: returns `x * P` 68 | 69 | ##### Operations over scalars - big integers modulo L, where 70 | `L = 2^252 + 27742317777372353535851937790883648493`. 71 | 72 | Each scalar (a big integer modulo `L`) is of type `Float64Array(32)`. Each of the 32 73 | elements is at most 8 bits (auxiliary bits are needed to accommodate overflows 74 | during arithmetic operations). 75 | 76 | Scalar operations implement simple school-book methods to optimize for a minimal 77 | javascript binary size. 78 | 79 | * `scalar.getRandom()`: returns a randomly generated scalar `mod L` 80 | * `scalar.invert(x)`: returns `1/x mod L` 81 | * `scalar.negate(x)`: returns `-x mod L` 82 | * `scalar.add(x, y)`: returns `x + y mod L` 83 | * `scalar.sub(x, y)`: returns `x - y mod L` 84 | * `scalar.mul(x, y)`: returns `x * y mod L` 85 | 86 | ##### Unsafe operations over Edwards EC points 87 | 88 | Unsafe operations give a way to use the ristretto255 group more efficiently, but 89 | these APIs should be used with great care. 90 | It is recommended in the [ristretto255 RFC](https://tools.ietf.org/html/draft-hdevalence-cfrg-ristretto-01#section-4) 91 | that an implementation that does a number of group operations 92 | first transforms ristretto255 elements to EC points (`unsafe.point.fromBytes`), then performs necessary 93 | operations in the EC group (e.g. `unsafe.point.scalarMult`) and 94 | transforms the resulting EC point back to ristretto255 group (`unsafe.point.toBytes`). 95 | However since JavaScript does not provide type safety, it is easier to mix-up different types, 96 | thus we highly recommend the use of safe operations described in the previous sections instead. 97 | The implementations must not operate on internal (EC) representations other than in between 98 | `unsafe.point.fromBytes` and `unsafe.point.toBytes` functions. 99 | 100 | The format for the EC point is four coordinates: `[gf(), gf(), gf(), gf()]`, where each coordinate 101 | `gf() = Float64Array(16)` is a 16-element array, where each element has 16 least significant bits used. 102 | 103 | * `unsafe.point.alloc()`: allocated a placeholder for an EC point 104 | * `unsafe.point.toBytes(P)`: converts an EC point `P` to a ristretto255 element (the conversion is well defined only for even EC points) 105 | * `unsafe.point.fromBytes(P, E)`: converts a ristretto255 element `E` to an EC point `P` 106 | * `unsafe.point.getRandom()`: generates a random EC point 107 | * `unsafe.point.fromHash(h)`: generates an EC point from `h`, a 64-element byte array `Uint8Array(64)` such as an output of `SHA512` 108 | * `unsafe.point.add(P, Q)`: adds two EC points `P` and `Q` 109 | * `unsafe.point.sub(P, Q)`: subtracts two EC points `P` and `Q` 110 | * `unsafe.point.scalarMultBase(Q, x)`: multiplies the base EC point by a scalar `x` and stores the result in `Q` 111 | * `unsafe.point.scalarMult(Q, P, x)`: multiplies a given EC point `P` by a scalar `x` and stores the result in `Q` 112 | 113 | Development and testing 114 | ------------------------ 115 | 116 | 1. Clone this repository with `git clone`. 117 | 118 | 2. cd `ristretto255-js/` 119 | 120 | 3. To install the necessary dependencies, run `yarn`. (Note: Building this library requires node version >=6.9.0) 121 | 122 | 4. To build `ristretto255.min.js`, run `yarn build`. 123 | 124 | 5. To lint and test, run `yarn lint` and `yarn test`. 125 | 126 | 127 | Benchmarks 128 | ---------- 129 | 130 | To run the benchmarks in a browser, open 131 | [ristretto255.benchmarks.html](./ristretto255.benchmarks.html). 132 | Here are the benchmarks from a MacBook Pro (15-inch, 2018) with 2.9 GHz Intel Core 133 | i9: 134 | 135 | | ristretto255 group | | scalar group | | 136 | | ------------------------- |:------------:| ------------------------- |:------------:| 137 | | getRandom | 0.48 ms | scalar.getRandom | 0.01 ms | 138 | | fromHash | 0.53 ms | scalar.invert | 2.60 ms | 139 | | add | 0.41 ms | scalar.negate | 0.01 ms | 140 | | sub | 0.41 ms | scalar.add | 0.02 ms | 141 | | scalarMultBase | 3.47 ms | scalar.sub | 0.03 ms | 142 | | scalarMult | 3.61 ms | scalar.mul | 0.01 ms | 143 | 144 | | UNSAFE - Edwards EC group | | 145 | | ------------------------------ |:------------:| 146 | | unsafe.point.toBytes | 0.13 ms | 147 | | unsafe.point.fromBytes | 0.13 ms | 148 | | unsafe.point.getRandom | 0.26 ms | 149 | | unsafe.point.fromHash | 0.27 ms | 150 | | unsafe.point.add | 0.01 ms | 151 | | unsafe.point.sub | 0.01 ms | 152 | | unsafe.point.scalarMultBase | 3.30 ms | 153 | | unsafe.point.scalarMult | 3.27 ms | 154 | 155 | System requirements 156 | ------------------- 157 | 158 | We inherit the limitations of [TweetNaCl.js](https://tweetnacl.js.org/#/) and 159 | support modern browsers that support the [Window.crypto 160 | API](https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto) and can 161 | generate cryptographically secure random numbers (which can be checked 162 | [here](https://caniuse.com/#feat=getrandomvalues)). 163 | 164 | This code can also be run with Node.js. 165 | 166 | Contributors 167 | ------------ 168 | 169 | The authors of this code are Valeria Nikolaenko 170 | ([@valerini](https://github.com/valerini)) and Kevin Lewi 171 | ([@kevinlewi](https://github.com/kevinlewi)). 172 | 173 | ###### Acknowledgments 174 | 175 | Special thanks go to Kostas Chalkias ([@kchalkias](https://github.com/kchalkias)) and 176 | David Wong ([@mimoo](https://github.com/mimoo)) 177 | for reviewing and giving feedback, Henry de Valence ([@hdevalence](https://github.com/hdevalence)) for 178 | answering questions about ristretto255, Dmitry Chestnykh ([@dchest](https://github.com/dchest)) for 179 | extending TweetNaCl.js to support this library, and Kyle Summers 180 | ([@KyleJSummers](https://github.com/KyleJSummers)) for recommending javascript build tooling. 181 | 182 | ### License 183 | This project is [MIT licensed](./LICENSE). 184 | -------------------------------------------------------------------------------- /config/babel.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | module.exports = { 9 | presets: [['@babel/preset-env']], 10 | env: { 11 | test: { 12 | plugins: ['@babel/plugin-transform-modules-commonjs'] 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /config/eslint.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | module.exports = { 9 | env: { 10 | browser: true, 11 | es6: true, 12 | node: true, 13 | jest: true 14 | }, 15 | extends: ['airbnb-base', 'prettier'], 16 | plugins: ['jest', 'prettier'], 17 | globals: { 18 | Atomics: 'readonly', 19 | SharedArrayBuffer: 'readonly' 20 | }, 21 | parserOptions: { 22 | ecmaVersion: 2018, 23 | sourceType: 'module' 24 | }, 25 | rules: { 26 | 'prettier/prettier': ['error', { singleQuote: true }], 27 | 'no-bitwise': 'off', 28 | 'no-plusplus': 'off', 29 | 'no-param-reassign': 'off', 30 | 'prefer-destructuring': 'off' 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /config/jest.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | module.exports = { 9 | transform: { 10 | '\\.js$': ['babel-jest', { configFile: './config/babel.config.js' }] 11 | }, 12 | roots: ['../'] 13 | }; 14 | -------------------------------------------------------------------------------- /config/rollup.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import { terser } from 'rollup-plugin-terser'; 9 | import babel from '@rollup/plugin-babel'; 10 | import resolve from '@rollup/plugin-node-resolve'; 11 | import commonjs from '@rollup/plugin-commonjs'; 12 | 13 | const babelconfig = require('./babel.config'); 14 | 15 | export default [ 16 | { 17 | input: 'src/ristretto255.js', 18 | output: { 19 | file: 'dist/ristretto255.min.js', 20 | format: 'umd', 21 | name: 'ristretto255' 22 | }, 23 | plugins: [ 24 | babel({ 25 | exclude: 'node_modules/**', 26 | babelrc: false, 27 | babelHelpers: 'bundled', 28 | ...babelconfig 29 | }), 30 | terser(), 31 | resolve({ 32 | browser: true 33 | }), 34 | commonjs() 35 | ] 36 | }, 37 | { 38 | input: 'ristretto255.benchmarks.js', 39 | output: { 40 | file: 'ristretto255.benchmarks.min.js', 41 | format: 'umd', 42 | name: 'ristretto255_benchmarks' 43 | }, 44 | plugins: [ 45 | babel({ 46 | exclude: 'node_modules/**', 47 | babelrc: false, 48 | babelHelpers: 'bundled', 49 | ...babelconfig 50 | }), 51 | terser(), 52 | resolve(), 53 | commonjs() 54 | ] 55 | } 56 | ]; 57 | -------------------------------------------------------------------------------- /data/ristretto.data.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | let test_dalek = {}; 9 | 10 | /** 11 | * test_dalek_ristretto vectors were generated with the following code in curve25519-dalek source: 12 | * 13 | * let mut rng = OsRng; 14 | * for i in 0..5 { 15 | * let a = RistrettoPoint::random(&mut rng); 16 | * let b = RistrettoPoint::random(&mut rng); 17 | * let c = a + b; 18 | * let d = a - b; 19 | * let e = Scalar::random(&mut rng).reduce(); 20 | * let f = &constants::RISTRETTO_BASEPOINT_POINT * e; 21 | * let g = a * e; 22 | * let h = b * e; 23 | * 24 | * let mut j_input = [0u8; 64]; 25 | * rng.fill_bytes(&mut j_input); 26 | * let mut hash = sha2::Sha512::default(); 27 | * hash.input(&j_input[0..64]); 28 | * let output = hash.result(); 29 | * let mut j = [0u8; 64]; 30 | * j.copy_from_slice(&output.as_slice()); 31 | * 32 | * let k = RistrettoPoint::hash_from_bytes::(&j_input); 33 | * print!("[\n {:?},\n {:?},\n {:?},\n {:?},\n {:?},\n {:?},\n {:?},\n {:?},\n {:?},\n {:?},\n],\n", 34 | * &a.compress().to_bytes(), 35 | * &b.compress().to_bytes(), 36 | * &c.compress().to_bytes(), 37 | * &d.compress().to_bytes(), 38 | * &e.to_bytes(), 39 | * &f.compress().to_bytes(), 40 | * &g.compress().to_bytes(), 41 | * &h.compress().to_bytes(), 42 | * &j[0..64], 43 | * &k.compress().to_bytes(), 44 | * ); 45 | * } 46 | * 47 | * [0]: a 48 | * [1]: b 49 | * [2]: a + b 50 | * [3]: a - b 51 | * [4]: s <- random scalar 52 | * [5]: s * a 53 | * [6]: s * b 54 | * [7]: h 55 | * [8]: fromHash(h) 56 | */ 57 | test_dalek.ristretto_ops = [ 58 | [ 59 | [222, 172, 137, 107, 13, 184, 170, 28, 208, 135, 221, 235, 20, 38, 60, 70, 41, 247, 146, 88, 41, 159, 11, 222, 217, 132, 176, 99, 68, 55, 207, 19], 60 | [202, 113, 155, 239, 245, 174, 156, 23, 159, 65, 34, 139, 43, 17, 195, 214, 138, 70, 205, 163, 80, 255, 137, 86, 158, 32, 171, 182, 6, 221, 150, 52], 61 | [40, 28, 47, 139, 252, 172, 141, 116, 109, 131, 177, 73, 197, 37, 195, 12, 225, 112, 42, 134, 66, 132, 94, 229, 22, 39, 162, 242, 146, 164, 185, 104], 62 | [204, 163, 176, 129, 185, 85, 184, 244, 108, 158, 27, 17, 56, 176, 22, 230, 45, 82, 133, 177, 10, 113, 163, 101, 207, 163, 34, 154, 71, 67, 64, 20], 63 | [83, 99, 233, 64, 57, 38, 24, 138, 128, 127, 188, 6, 244, 67, 242, 69, 179, 184, 15, 127, 203, 191, 161, 186, 53, 138, 118, 209, 86, 32, 122, 4], 64 | [48, 8, 140, 16, 85, 78, 84, 188, 171, 61, 37, 2, 68, 15, 104, 191, 5, 235, 76, 195, 56, 85, 158, 68, 252, 137, 210, 161, 110, 205, 112, 64], 65 | [166, 139, 60, 205, 141, 49, 222, 95, 22, 244, 204, 252, 72, 204, 63, 79, 250, 92, 85, 174, 10, 220, 9, 23, 16, 76, 99, 23, 13, 121, 236, 5], 66 | [184, 56, 10, 78, 186, 245, 187, 179, 69, 7, 13, 236, 105, 182, 58, 210, 51, 199, 89, 147, 248, 136, 195, 139, 142, 193, 70, 129, 194, 63, 223, 126], 67 | [214, 213, 254, 45, 226, 244, 48, 88, 17, 123, 248, 100, 186, 98, 33, 38, 63, 35, 126, 169, 127, 202, 225, 177, 229, 120, 166, 82, 214, 161, 173, 78, 74, 0, 39, 72, 216, 108, 212, 244, 6, 239, 4, 9, 54, 88, 61, 63, 151, 166, 134, 78, 100, 182, 46, 189, 160, 52, 49, 74, 1, 88, 101, 65], 68 | [32, 228, 119, 27, 218, 168, 249, 17, 144, 147, 230, 3, 218, 182, 206, 203, 30, 32, 218, 45, 52, 189, 147, 62, 12, 228, 173, 108, 40, 35, 75, 15], 69 | ], 70 | [ 71 | [22, 250, 81, 23, 148, 249, 80, 63, 159, 151, 23, 106, 183, 229, 223, 21, 124, 187, 158, 151, 130, 149, 24, 41, 70, 201, 17, 12, 152, 232, 10, 89], 72 | [170, 162, 175, 27, 36, 96, 143, 3, 244, 104, 182, 24, 112, 162, 19, 239, 146, 134, 174, 242, 48, 34, 22, 175, 114, 83, 4, 218, 145, 65, 199, 98], 73 | [146, 4, 104, 147, 46, 115, 150, 88, 224, 220, 138, 88, 95, 54, 128, 76, 29, 56, 155, 161, 205, 145, 175, 173, 6, 57, 10, 63, 171, 166, 93, 116], 74 | [158, 89, 166, 93, 134, 123, 97, 212, 184, 156, 151, 199, 71, 206, 138, 214, 17, 223, 114, 206, 211, 140, 71, 40, 210, 171, 251, 35, 170, 88, 124, 82], 75 | [82, 190, 88, 208, 67, 148, 120, 218, 177, 170, 74, 115, 196, 176, 224, 127, 234, 145, 158, 83, 72, 184, 17, 51, 48, 163, 143, 37, 251, 234, 88, 2], 76 | [232, 21, 155, 176, 8, 77, 228, 29, 225, 96, 58, 19, 28, 173, 90, 143, 231, 209, 89, 62, 221, 15, 40, 17, 121, 105, 142, 63, 221, 112, 72, 22], 77 | [178, 188, 235, 53, 131, 238, 247, 134, 40, 149, 46, 202, 114, 154, 234, 135, 88, 41, 55, 114, 136, 43, 79, 251, 215, 89, 18, 49, 213, 236, 12, 58], 78 | [30, 55, 178, 118, 103, 132, 14, 96, 95, 1, 33, 83, 251, 73, 147, 218, 202, 177, 134, 147, 8, 208, 130, 117, 0, 12, 228, 186, 58, 188, 129, 29], 79 | [130, 181, 78, 181, 238, 88, 185, 245, 237, 111, 100, 68, 26, 145, 141, 107, 205, 33, 249, 58, 255, 234, 192, 210, 169, 62, 18, 188, 226, 164, 217, 216, 118, 196, 98, 5, 40, 111, 189, 93, 248, 124, 160, 186, 223, 0, 2, 77, 81, 218, 61, 49, 45, 223, 17, 91, 15, 136, 92, 27, 99, 68, 154, 131], 80 | [150, 26, 236, 173, 47, 184, 129, 92, 236, 6, 203, 37, 112, 170, 181, 77, 168, 125, 234, 61, 153, 82, 99, 171, 187, 245, 193, 16, 95, 169, 12, 63], 81 | ], 82 | [ 83 | [204, 57, 52, 220, 127, 34, 231, 181, 218, 56, 62, 108, 14, 229, 113, 102, 10, 206, 49, 31, 35, 94, 162, 127, 29, 141, 246, 221, 157, 181, 11, 94], 84 | [28, 5, 63, 139, 220, 166, 140, 102, 90, 35, 210, 36, 199, 232, 245, 141, 201, 212, 109, 68, 148, 189, 123, 237, 52, 30, 241, 102, 228, 187, 122, 55], 85 | [42, 130, 114, 0, 231, 54, 35, 252, 176, 223, 227, 139, 148, 186, 0, 90, 38, 101, 134, 212, 32, 11, 86, 5, 80, 154, 151, 179, 18, 222, 98, 102], 86 | [84, 102, 92, 175, 155, 153, 123, 162, 233, 5, 192, 93, 5, 173, 251, 179, 249, 195, 9, 192, 190, 9, 229, 225, 184, 108, 48, 213, 136, 220, 42, 19], 87 | [131, 91, 161, 52, 4, 66, 103, 97, 145, 92, 84, 146, 145, 253, 223, 15, 201, 67, 100, 242, 163, 177, 63, 167, 88, 147, 69, 174, 159, 58, 118, 10], 88 | [248, 251, 187, 141, 25, 34, 165, 39, 118, 41, 224, 187, 213, 107, 116, 78, 191, 241, 175, 92, 233, 135, 35, 49, 94, 150, 189, 1, 161, 104, 196, 81], 89 | [76, 136, 194, 125, 37, 235, 121, 132, 71, 62, 52, 33, 63, 178, 131, 143, 52, 253, 6, 12, 2, 156, 20, 55, 116, 125, 202, 194, 47, 81, 126, 60], 90 | [168, 214, 176, 84, 228, 130, 192, 84, 170, 100, 119, 182, 220, 129, 55, 138, 141, 78, 230, 222, 234, 234, 168, 176, 109, 208, 247, 247, 204, 251, 140, 58], 91 | [97, 192, 44, 53, 150, 154, 56, 15, 24, 2, 124, 214, 41, 224, 132, 173, 23, 220, 183, 82, 187, 233, 213, 112, 109, 99, 50, 161, 44, 147, 18, 135, 14, 68, 116, 35, 199, 2, 240, 170, 211, 18, 115, 19, 61, 213, 185, 117, 5, 189, 216, 166, 233, 112, 67, 31, 137, 209, 217, 56, 208, 167, 205, 183], 92 | [154, 85, 220, 144, 107, 120, 152, 107, 59, 2, 120, 26, 246, 120, 88, 193, 235, 232, 94, 31, 109, 144, 52, 250, 227, 227, 62, 180, 9, 188, 219, 99], 93 | ], 94 | [ 95 | [208, 203, 243, 27, 79, 141, 188, 229, 90, 106, 24, 152, 39, 2, 229, 158, 249, 237, 174, 81, 86, 35, 105, 249, 146, 60, 77, 171, 214, 47, 60, 81], 96 | [20, 245, 95, 8, 223, 50, 64, 249, 189, 228, 220, 30, 70, 63, 119, 217, 134, 210, 10, 16, 87, 114, 85, 170, 36, 251, 233, 27, 92, 120, 237, 60], 97 | [16, 70, 187, 42, 177, 196, 50, 133, 169, 151, 150, 62, 202, 70, 54, 248, 43, 196, 98, 29, 0, 45, 244, 63, 122, 148, 95, 75, 88, 22, 239, 101], 98 | [220, 183, 220, 13, 249, 65, 80, 30, 166, 127, 95, 15, 66, 87, 212, 46, 47, 156, 102, 144, 250, 172, 168, 154, 248, 75, 30, 199, 44, 229, 113, 73], 99 | [103, 155, 232, 16, 227, 54, 126, 52, 40, 219, 174, 200, 102, 2, 142, 4, 181, 115, 134, 114, 73, 103, 219, 204, 52, 149, 241, 144, 140, 77, 44, 8], 100 | [100, 131, 184, 86, 78, 235, 25, 178, 202, 108, 152, 102, 23, 251, 136, 1, 51, 0, 169, 163, 93, 255, 98, 172, 203, 133, 195, 249, 124, 139, 199, 126], 101 | [20, 253, 124, 118, 28, 230, 5, 214, 252, 8, 250, 123, 66, 170, 204, 39, 250, 75, 148, 81, 221, 70, 254, 95, 240, 243, 155, 35, 148, 135, 60, 52], 102 | [172, 32, 189, 159, 143, 147, 231, 171, 171, 0, 145, 44, 53, 169, 203, 24, 67, 98, 200, 4, 210, 189, 213, 14, 36, 196, 107, 77, 76, 132, 110, 57], 103 | [226, 194, 244, 165, 133, 180, 162, 73, 70, 155, 45, 57, 192, 249, 21, 188, 6, 131, 49, 127, 174, 27, 92, 12, 165, 227, 131, 28, 142, 78, 222, 16, 55, 147, 214, 92, 50, 73, 196, 79, 24, 107, 5, 179, 184, 152, 132, 84, 182, 137, 12, 214, 156, 98, 166, 104, 70, 210, 23, 13, 42, 118, 198, 134], 104 | [222, 86, 47, 85, 61, 148, 135, 32, 105, 101, 8, 234, 60, 252, 50, 98, 233, 103, 56, 69, 205, 188, 35, 33, 1, 79, 155, 25, 29, 64, 189, 68], 105 | ], 106 | [ 107 | [38, 73, 211, 93, 101, 22, 130, 225, 20, 155, 183, 132, 147, 41, 13, 39, 53, 192, 17, 76, 94, 55, 91, 72, 189, 190, 162, 16, 95, 230, 77, 31], 108 | [74, 76, 30, 21, 193, 253, 88, 170, 176, 208, 142, 171, 55, 40, 134, 96, 109, 145, 133, 80, 177, 168, 71, 246, 116, 132, 141, 91, 143, 254, 41, 29], 109 | [170, 217, 155, 183, 37, 133, 244, 99, 3, 104, 11, 24, 44, 68, 88, 59, 239, 156, 182, 169, 200, 7, 55, 2, 158, 189, 224, 142, 199, 44, 17, 99], 110 | [92, 218, 110, 126, 8, 223, 82, 64, 49, 17, 96, 204, 36, 250, 8, 42, 231, 36, 172, 81, 104, 247, 7, 224, 149, 73, 130, 87, 178, 38, 31, 90], 111 | [164, 8, 134, 215, 75, 240, 220, 58, 20, 196, 52, 168, 99, 89, 100, 138, 19, 182, 184, 49, 172, 136, 62, 253, 188, 139, 139, 113, 241, 93, 100, 10], 112 | [198, 123, 113, 84, 200, 145, 52, 106, 38, 63, 44, 241, 92, 131, 26, 218, 55, 128, 92, 81, 136, 78, 10, 166, 238, 45, 172, 52, 42, 177, 180, 76], 113 | [42, 117, 32, 13, 129, 115, 156, 161, 130, 114, 154, 78, 125, 137, 35, 95, 238, 59, 31, 54, 36, 187, 185, 189, 116, 43, 127, 2, 118, 72, 8, 85], 114 | [166, 8, 176, 156, 125, 36, 92, 34, 31, 255, 174, 155, 216, 157, 165, 35, 248, 142, 136, 66, 94, 66, 249, 56, 207, 235, 255, 45, 69, 134, 24, 13], 115 | [246, 170, 241, 111, 97, 210, 29, 177, 202, 78, 212, 155, 91, 125, 10, 65, 143, 74, 32, 45, 31, 51, 124, 173, 64, 2, 137, 62, 25, 44, 126, 110, 12, 169, 117, 153, 153, 19, 205, 40, 180, 200, 196, 27, 25, 231, 159, 120, 75, 221, 26, 73, 222, 195, 96, 220, 194, 155, 103, 164, 105, 85, 240, 157], 116 | [136, 122, 13, 72, 43, 212, 170, 200, 169, 233, 125, 70, 20, 45, 213, 25, 114, 28, 208, 254, 131, 56, 199, 57, 87, 230, 92, 239, 29, 227, 57, 112], 117 | ], 118 | ]; 119 | 120 | /** 121 | * test_dalek_ristretto_valid_or_not vectors were generated with the following code in curve25519-dalek source: 122 | * 123 | * let mut rng = OsRng; 124 | * const CAP: i32 = 20; 125 | * let mut num_valid: i32 = 0; 126 | * let mut num_invalid: i32 = 0; 127 | * while num_valid < CAP || num_invalid < CAP { 128 | * let mut a = [0u8; 32]; 129 | * rng.fill_bytes(&mut a); 130 | * if num_valid < CAP && CompressedRistretto(a).decompress().is_some() { 131 | * print!("[{:?}, {:?}],\n ", a, true); 132 | * num_valid += 1; 133 | * } else if num_invalid < CAP { 134 | * print!("[{:?}, {:?}],\n ", a, false); 135 | * num_invalid += 1; 136 | * } 137 | * } 138 | * 139 | * [0]: a 140 | * [1]: isValid(a) 141 | */ 142 | test_dalek.ristretto_valid_or_not = [ 143 | [[119, 118, 1, 137, 26, 79, 23, 196, 180, 4, 175, 182, 134, 213, 82, 100, 59, 106, 117, 110, 89, 205, 3, 229, 173, 205, 216, 236, 216, 96, 67, 17], false], 144 | [[194, 40, 173, 62, 100, 172, 163, 77, 162, 199, 242, 190, 218, 95, 25, 34, 101, 13, 196, 108, 93, 20, 220, 167, 123, 16, 80, 254, 250, 121, 207, 177], false], 145 | [[108, 128, 90, 236, 14, 9, 105, 27, 255, 92, 18, 177, 133, 0, 154, 53, 60, 105, 118, 231, 93, 47, 222, 135, 124, 167, 219, 57, 3, 31, 34, 58], true], 146 | [[171, 217, 122, 85, 9, 253, 230, 36, 117, 140, 239, 70, 245, 240, 156, 130, 245, 191, 59, 217, 98, 186, 219, 198, 165, 126, 255, 63, 4, 159, 109, 97], false], 147 | [[87, 74, 215, 17, 240, 173, 82, 15, 68, 116, 105, 55, 190, 123, 153, 46, 176, 115, 225, 23, 168, 170, 142, 21, 107, 101, 77, 185, 158, 11, 103, 156], false], 148 | [[119, 32, 247, 252, 153, 94, 191, 231, 111, 148, 227, 71, 105, 245, 41, 43, 10, 216, 115, 83, 118, 122, 247, 21, 83, 199, 186, 15, 117, 106, 10, 50], false], 149 | [[116, 204, 211, 149, 152, 33, 159, 144, 245, 173, 19, 96, 114, 162, 73, 33, 227, 230, 14, 99, 222, 2, 161, 244, 75, 73, 185, 179, 90, 14, 4, 157], false], 150 | [[159, 200, 18, 225, 235, 237, 211, 182, 10, 103, 124, 20, 114, 252, 85, 114, 72, 75, 30, 227, 44, 61, 68, 184, 115, 124, 109, 45, 54, 80, 247, 196], false], 151 | [[24, 92, 125, 228, 252, 147, 13, 70, 162, 162, 6, 68, 109, 43, 142, 218, 199, 221, 237, 135, 245, 118, 164, 64, 157, 131, 136, 29, 168, 224, 119, 89], true], 152 | [[50, 209, 153, 48, 116, 68, 141, 28, 156, 43, 21, 74, 113, 90, 15, 1, 226, 148, 225, 50, 190, 54, 202, 202, 221, 75, 63, 127, 251, 170, 109, 221], false], 153 | [[73, 37, 15, 195, 95, 22, 45, 65, 188, 251, 81, 74, 207, 133, 193, 226, 73, 99, 238, 239, 174, 121, 236, 188, 25, 104, 186, 147, 70, 50, 242, 91], false], 154 | [[87, 43, 22, 51, 160, 239, 143, 92, 195, 179, 24, 139, 174, 58, 228, 14, 230, 84, 9, 158, 37, 84, 70, 29, 171, 22, 159, 193, 191, 24, 15, 65], false], 155 | [[93, 221, 12, 236, 140, 111, 33, 212, 93, 219, 229, 151, 114, 93, 48, 49, 85, 128, 249, 255, 194, 1, 53, 139, 112, 69, 111, 229, 125, 53, 125, 204], false], 156 | [[172, 131, 142, 184, 121, 119, 220, 126, 130, 212, 159, 210, 158, 90, 168, 202, 247, 130, 239, 222, 228, 188, 41, 58, 189, 46, 89, 81, 159, 139, 66, 242], false], 157 | [[180, 254, 9, 101, 237, 122, 68, 71, 79, 116, 184, 62, 139, 87, 122, 193, 110, 63, 231, 130, 102, 196, 173, 119, 144, 14, 127, 255, 28, 33, 119, 170], false], 158 | [[83, 248, 8, 148, 41, 92, 211, 129, 45, 70, 88, 168, 58, 115, 151, 114, 141, 50, 168, 154, 198, 91, 118, 247, 35, 157, 235, 10, 88, 15, 101, 46], false], 159 | [[141, 45, 188, 31, 200, 232, 93, 156, 112, 216, 106, 201, 59, 150, 200, 33, 235, 30, 232, 23, 227, 104, 66, 178, 246, 48, 219, 212, 4, 67, 137, 179], false], 160 | [[238, 20, 85, 144, 198, 214, 162, 177, 148, 228, 254, 2, 199, 102, 246, 79, 112, 110, 34, 53, 99, 62, 180, 115, 202, 179, 40, 227, 124, 76, 254, 247], false], 161 | [[168, 190, 99, 232, 118, 38, 94, 196, 86, 183, 11, 248, 223, 139, 116, 246, 242, 14, 252, 101, 97, 130, 92, 27, 79, 124, 19, 165, 129, 66, 155, 247], false], 162 | [[106, 201, 125, 218, 30, 152, 247, 97, 197, 80, 239, 177, 219, 81, 17, 79, 19, 50, 180, 246, 36, 149, 55, 27, 20, 193, 54, 172, 20, 205, 212, 238], false], 163 | [[194, 186, 54, 128, 207, 88, 188, 120, 117, 230, 169, 204, 130, 12, 25, 162, 223, 138, 214, 102, 22, 98, 144, 218, 166, 34, 49, 181, 35, 119, 161, 20], true], 164 | [[44, 29, 53, 106, 165, 252, 29, 220, 51, 189, 216, 81, 113, 90, 210, 147, 33, 68, 140, 223, 51, 131, 35, 245, 164, 155, 28, 233, 65, 225, 211, 98], false], 165 | [[195, 135, 98, 251, 44, 199, 28, 77, 207, 57, 141, 113, 136, 50, 231, 100, 38, 39, 46, 185, 255, 163, 160, 251, 232, 176, 169, 76, 66, 38, 78, 68], false], 166 | [[146, 118, 79, 110, 129, 27, 31, 57, 32, 149, 82, 59, 101, 60, 50, 129, 240, 224, 240, 248, 182, 34, 23, 150, 8, 69, 243, 189, 92, 22, 79, 7], true], 167 | [[100, 253, 126, 1, 167, 10, 177, 29, 167, 69, 253, 200, 54, 146, 233, 193, 131, 160, 216, 44, 138, 26, 81, 78, 180, 43, 236, 249, 28, 175, 120, 84], true], 168 | [[86, 107, 245, 78, 126, 199, 96, 52, 110, 26, 205, 238, 123, 117, 204, 213, 143, 50, 90, 210, 252, 219, 95, 58, 196, 8, 133, 181, 90, 61, 56, 2], true], 169 | [[126, 124, 197, 249, 141, 10, 59, 40, 187, 98, 43, 146, 158, 33, 136, 124, 18, 41, 68, 75, 148, 24, 56, 68, 29, 123, 157, 135, 189, 18, 197, 113], true], 170 | [[138, 234, 237, 170, 185, 18, 173, 47, 81, 101, 227, 255, 13, 133, 2, 199, 196, 127, 59, 239, 199, 216, 184, 29, 57, 182, 149, 67, 218, 107, 148, 74], true], 171 | [[18, 129, 40, 162, 13, 191, 105, 182, 99, 166, 176, 94, 219, 103, 151, 117, 46, 95, 28, 165, 52, 155, 165, 36, 134, 83, 99, 253, 242, 225, 232, 19], true], 172 | [[168, 36, 150, 62, 3, 147, 172, 11, 94, 5, 136, 13, 14, 182, 181, 76, 30, 249, 95, 124, 52, 149, 45, 247, 45, 201, 69, 211, 74, 182, 146, 26], true], 173 | [[50, 76, 117, 213, 68, 83, 127, 28, 190, 113, 8, 78, 60, 232, 145, 237, 226, 35, 126, 65, 235, 129, 247, 121, 116, 61, 57, 120, 211, 128, 48, 107], true], 174 | [[222, 221, 95, 140, 250, 125, 249, 159, 252, 66, 156, 244, 235, 162, 50, 239, 199, 103, 77, 122, 88, 52, 124, 10, 160, 26, 173, 205, 81, 104, 174, 73], true], 175 | [[64, 72, 167, 97, 17, 8, 0, 229, 189, 9, 108, 15, 205, 202, 151, 72, 204, 181, 164, 130, 148, 59, 204, 112, 24, 98, 168, 48, 50, 78, 147, 74], true], 176 | [[178, 240, 136, 63, 4, 214, 112, 173, 10, 85, 2, 8, 87, 148, 104, 26, 64, 55, 200, 7, 65, 209, 103, 55, 125, 237, 185, 232, 41, 122, 54, 30], true], 177 | [[232, 192, 208, 96, 65, 220, 135, 158, 218, 92, 171, 199, 20, 114, 92, 23, 230, 81, 137, 44, 15, 169, 247, 62, 82, 61, 90, 69, 231, 138, 88, 3], true], 178 | [[184, 24, 57, 99, 38, 24, 201, 253, 14, 69, 94, 47, 156, 198, 13, 88, 144, 164, 72, 69, 152, 49, 147, 85, 239, 131, 57, 182, 242, 91, 212, 41], true], 179 | [[128, 203, 69, 126, 45, 180, 111, 157, 204, 81, 243, 204, 141, 25, 219, 141, 217, 205, 236, 80, 58, 224, 107, 1, 242, 82, 222, 247, 98, 116, 77, 65], true], 180 | [[156, 201, 26, 1, 0, 44, 185, 57, 123, 147, 188, 159, 100, 90, 66, 142, 245, 66, 232, 77, 200, 2, 111, 27, 107, 153, 187, 249, 89, 142, 191, 125], true], 181 | [[28, 125, 168, 61, 195, 24, 251, 152, 249, 217, 148, 209, 199, 0, 89, 244, 59, 117, 175, 130, 199, 48, 122, 203, 181, 104, 118, 67, 105, 27, 2, 82], true], 182 | [[250, 87, 76, 28, 189, 108, 82, 183, 64, 48, 137, 210, 226, 18, 32, 231, 153, 85, 138, 73, 164, 181, 243, 4, 202, 106, 253, 221, 133, 178, 19, 115], true], 183 | [[255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 184 | [[254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 185 | [[253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 186 | [[252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 187 | [[251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 188 | [[250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 189 | [[249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 190 | [[248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 191 | [[247, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 192 | [[246, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 193 | [[245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 194 | [[244, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 195 | [[243, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 196 | [[242, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 197 | [[241, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 198 | [[240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 199 | [[239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 200 | [[238, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 201 | [[237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 202 | [[236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 203 | [[235, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 204 | [[234, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], true], 205 | [[233, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 206 | [[232, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 207 | [[231, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 208 | [[230, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 209 | [[229, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 210 | [[228, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], true], 211 | [[227, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 212 | [[226, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], false], 213 | ]; 214 | 215 | export default test_dalek; 216 | -------------------------------------------------------------------------------- /data/scalars.data.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | /** 9 | * test_dalek_scalars vectors were generated with the following code in curve25519-dalek source: 10 | * 11 | * let mut rng = OsRng; 12 | * for i in 0..5 { 13 | * let a = Scalar::random(&mut rng); 14 | * let b = Scalar::random(&mut rng); 15 | * let a_add_b = a + b; 16 | * let a_sub_b = a - b; 17 | * let a_mul_b = a * b; 18 | * let a_inv = a.invert(); 19 | * let b_inv = b.invert(); 20 | * let a_neg = -a; 21 | * let b_neg = -b; 22 | * 23 | * print!("[\n {:?},\n {:?},\n {:?},\n {:?},\n {:?},\n {:?},\n {:?},\n {:?},\n {:?}\n],\n", 24 | * &a.to_bytes(), 25 | * &b.to_bytes(), 26 | * &a_add_b.to_bytes(), 27 | * &a_sub_b.to_bytes(), 28 | * &a_mul_b.to_bytes(), 29 | * &a_inv.to_bytes(), 30 | * &b_inv.to_bytes(), 31 | * &a_neg.to_bytes(), 32 | * &b_neg.to_bytes(), 33 | * ); 34 | * 35 | * } 36 | * 37 | * [0]: a 38 | * [1]: b 39 | * [2]: a + b 40 | * [3]: a - b 41 | * [4]: a * b 42 | * [5]: 1/a 43 | * [6]: 1/b 44 | * [7]: -a 45 | * [8]: -b 46 | */ 47 | let test_dalek_scalars = [ 48 | [ 49 | [161, 1, 198, 57, 246, 95, 218, 191, 83, 140, 156, 107, 69, 82, 195, 25, 24, 79, 104, 125, 48, 33, 35, 107, 181, 26, 249, 235, 151, 232, 223, 14], 50 | [57, 199, 24, 93, 76, 161, 234, 137, 62, 7, 166, 8, 123, 43, 133, 126, 65, 69, 35, 1, 147, 37, 14, 13, 109, 239, 203, 150, 0, 116, 50, 14], 51 | [237, 244, 232, 57, 40, 158, 178, 241, 187, 246, 74, 209, 225, 131, 105, 131, 89, 148, 139, 126, 195, 70, 49, 120, 34, 10, 197, 130, 152, 92, 18, 13], 52 | [104, 58, 173, 220, 169, 190, 239, 53, 21, 133, 246, 98, 202, 38, 62, 155, 214, 9, 69, 124, 157, 251, 20, 94, 72, 43, 45, 85, 151, 116, 173, 0], 53 | [164, 163, 123, 248, 91, 244, 234, 97, 179, 130, 106, 120, 186, 255, 228, 83, 70, 83, 62, 188, 67, 202, 131, 71, 93, 220, 93, 169, 175, 80, 64, 13], 54 | [107, 91, 254, 155, 25, 81, 169, 190, 178, 26, 166, 187, 75, 254, 78, 189, 28, 184, 143, 21, 252, 43, 130, 46, 148, 10, 226, 247, 232, 3, 224, 5], 55 | [208, 201, 9, 45, 8, 112, 176, 2, 125, 102, 152, 180, 199, 22, 40, 228, 224, 202, 33, 204, 16, 24, 194, 149, 239, 39, 131, 23, 129, 90, 10, 8], 56 | [76, 210, 47, 35, 36, 3, 56, 152, 130, 16, 91, 55, 153, 167, 27, 251, 231, 176, 151, 130, 207, 222, 220, 148, 74, 229, 6, 20, 104, 23, 32, 1], 57 | [180, 12, 221, 255, 205, 193, 39, 206, 151, 149, 81, 154, 99, 206, 89, 150, 190, 186, 220, 254, 108, 218, 241, 242, 146, 16, 52, 105, 255, 139, 205, 1] 58 | ], 59 | [ 60 | [205, 214, 177, 166, 52, 222, 183, 201, 104, 164, 192, 225, 75, 123, 35, 101, 212, 30, 177, 18, 97, 202, 133, 12, 239, 38, 69, 91, 16, 104, 14, 10], 61 | [101, 67, 119, 212, 226, 148, 142, 121, 194, 229, 106, 224, 204, 107, 116, 201, 254, 120, 166, 178, 83, 247, 64, 56, 65, 125, 149, 115, 153, 142, 115, 3], 62 | [50, 26, 41, 123, 23, 115, 70, 67, 43, 138, 43, 194, 24, 231, 151, 46, 211, 151, 87, 197, 180, 193, 198, 68, 48, 164, 218, 206, 169, 246, 129, 13], 63 | [104, 147, 58, 210, 81, 73, 41, 80, 166, 190, 85, 1, 127, 15, 175, 155, 213, 165, 10, 96, 13, 211, 68, 212, 173, 169, 175, 231, 118, 217, 154, 6], 64 | [210, 43, 104, 74, 63, 131, 100, 216, 216, 119, 110, 205, 196, 103, 15, 140, 191, 145, 128, 122, 89, 45, 95, 16, 166, 234, 237, 13, 198, 197, 187, 11], 65 | [159, 175, 47, 216, 250, 54, 129, 6, 253, 233, 15, 192, 207, 39, 50, 77, 182, 78, 21, 190, 71, 239, 127, 113, 37, 194, 115, 237, 131, 16, 119, 2], 66 | [24, 42, 76, 73, 126, 40, 170, 87, 92, 20, 134, 15, 122, 155, 172, 76, 247, 236, 11, 180, 43, 17, 245, 230, 51, 237, 142, 89, 60, 89, 251, 7], 67 | [32, 253, 67, 182, 229, 132, 90, 142, 109, 248, 54, 193, 146, 126, 187, 175, 43, 225, 78, 237, 158, 53, 122, 243, 16, 217, 186, 164, 239, 151, 241, 5], 68 | [136, 144, 126, 136, 55, 206, 131, 222, 19, 183, 140, 194, 17, 142, 106, 75, 1, 135, 89, 77, 172, 8, 191, 199, 190, 130, 106, 140, 102, 113, 140, 12] 69 | ], 70 | [ 71 | [9, 92, 197, 204, 143, 47, 77, 243, 198, 82, 49, 216, 84, 181, 254, 127, 117, 78, 199, 29, 17, 164, 174, 150, 172, 69, 224, 3, 115, 229, 83, 14], 72 | [65, 115, 116, 81, 108, 100, 173, 78, 102, 66, 147, 109, 59, 172, 228, 100, 115, 7, 82, 103, 127, 253, 228, 232, 36, 66, 113, 58, 223, 230, 223, 15], 73 | [93, 251, 67, 193, 225, 48, 232, 233, 86, 248, 204, 162, 177, 103, 4, 208, 232, 85, 25, 133, 144, 161, 147, 127, 209, 135, 81, 62, 82, 204, 51, 14], 74 | [181, 188, 70, 216, 61, 46, 178, 252, 54, 173, 149, 13, 248, 2, 249, 47, 2, 71, 117, 182, 145, 166, 201, 173, 135, 3, 111, 201, 147, 254, 115, 14], 75 | [137, 200, 241, 12, 61, 204, 76, 247, 163, 132, 82, 14, 83, 116, 8, 251, 21, 245, 139, 246, 58, 237, 160, 165, 232, 121, 50, 180, 12, 182, 80, 5], 76 | [167, 34, 180, 223, 186, 246, 134, 164, 138, 252, 148, 119, 208, 21, 207, 251, 29, 232, 84, 172, 116, 109, 252, 69, 39, 167, 120, 9, 201, 96, 244, 9], 77 | [109, 237, 135, 164, 111, 79, 246, 126, 129, 90, 132, 202, 53, 189, 192, 50, 2, 74, 145, 50, 17, 116, 252, 144, 149, 237, 86, 118, 136, 114, 80, 3], 78 | [228, 119, 48, 144, 138, 51, 197, 100, 15, 74, 198, 202, 137, 68, 224, 148, 138, 177, 56, 226, 238, 91, 81, 105, 83, 186, 31, 252, 140, 26, 172, 1], 79 | [172, 96, 129, 11, 174, 254, 100, 9, 112, 90, 100, 53, 163, 77, 250, 175, 140, 248, 173, 152, 128, 2, 27, 23, 219, 189, 142, 197, 32, 25, 32, 0] 80 | ], 81 | [ 82 | [107, 54, 103, 115, 131, 114, 172, 151, 219, 114, 143, 245, 154, 35, 27, 249, 124, 49, 67, 57, 141, 18, 228, 204, 23, 55, 24, 254, 234, 144, 194, 6], 83 | [100, 113, 142, 165, 46, 172, 112, 70, 205, 134, 59, 182, 169, 237, 166, 196, 153, 88, 196, 199, 62, 172, 69, 102, 237, 178, 202, 51, 191, 110, 227, 7], 84 | [207, 167, 245, 24, 178, 30, 29, 222, 168, 249, 202, 171, 68, 17, 194, 189, 22, 138, 7, 1, 204, 190, 41, 51, 5, 234, 226, 49, 170, 255, 165, 14], 85 | [244, 152, 206, 42, 111, 41, 78, 169, 228, 136, 75, 226, 207, 47, 83, 73, 227, 216, 126, 113, 78, 102, 158, 102, 42, 132, 77, 202, 43, 34, 223, 14], 86 | [224, 162, 158, 153, 142, 175, 11, 22, 121, 100, 92, 156, 71, 47, 223, 142, 210, 117, 170, 95, 141, 72, 168, 112, 196, 245, 253, 81, 3, 157, 169, 13], 87 | [13, 213, 19, 143, 93, 83, 143, 184, 139, 144, 22, 148, 209, 29, 19, 239, 14, 95, 48, 105, 4, 204, 114, 152, 19, 250, 184, 244, 122, 155, 105, 3], 88 | [47, 37, 243, 154, 182, 107, 127, 228, 187, 104, 190, 187, 150, 192, 210, 77, 128, 134, 197, 159, 117, 95, 86, 3, 160, 57, 221, 239, 37, 226, 105, 11], 89 | [130, 157, 142, 233, 150, 240, 101, 192, 250, 41, 104, 173, 67, 214, 195, 27, 131, 206, 188, 198, 114, 237, 27, 51, 232, 200, 231, 1, 21, 111, 61, 9], 90 | [137, 98, 103, 183, 235, 182, 161, 17, 9, 22, 188, 236, 52, 12, 56, 80, 102, 167, 59, 56, 193, 83, 186, 153, 18, 77, 53, 204, 64, 145, 28, 8] 91 | ], 92 | [ 93 | [10, 107, 120, 66, 203, 30, 90, 132, 132, 165, 160, 64, 39, 170, 71, 248, 146, 56, 211, 155, 246, 200, 139, 232, 75, 90, 86, 197, 198, 122, 8, 15], 94 | [75, 199, 155, 159, 25, 142, 111, 67, 177, 142, 145, 119, 113, 119, 186, 207, 153, 200, 61, 111, 69, 148, 40, 188, 13, 90, 94, 28, 56, 212, 244, 7], 95 | [104, 94, 30, 133, 202, 73, 183, 111, 95, 151, 58, 21, 186, 39, 35, 179, 44, 1, 17, 11, 60, 93, 180, 164, 89, 180, 180, 225, 254, 78, 253, 6], 96 | [191, 163, 220, 162, 177, 144, 234, 64, 211, 22, 15, 201, 181, 50, 141, 40, 249, 111, 149, 44, 177, 52, 99, 44, 62, 0, 248, 168, 142, 166, 19, 7], 97 | [236, 73, 193, 170, 20, 52, 134, 160, 148, 98, 220, 8, 167, 96, 76, 185, 55, 110, 210, 101, 124, 134, 243, 238, 218, 234, 80, 94, 145, 13, 32, 8], 98 | [196, 175, 43, 35, 9, 109, 32, 94, 224, 231, 80, 47, 255, 90, 189, 143, 128, 2, 248, 8, 159, 200, 79, 10, 87, 144, 200, 30, 215, 69, 205, 12], 99 | [157, 10, 170, 92, 209, 173, 148, 174, 119, 190, 182, 148, 164, 18, 17, 159, 43, 22, 242, 3, 251, 206, 94, 221, 21, 214, 57, 40, 232, 231, 203, 6], 100 | [227, 104, 125, 26, 79, 68, 184, 211, 81, 247, 86, 98, 183, 79, 151, 28, 109, 199, 44, 100, 9, 55, 116, 23, 180, 165, 169, 58, 57, 133, 247, 0], 101 | [162, 12, 90, 189, 0, 213, 162, 20, 37, 14, 102, 43, 109, 130, 36, 69, 102, 55, 194, 144, 186, 107, 215, 67, 242, 165, 161, 227, 199, 43, 11, 8] 102 | ] 103 | ]; 104 | 105 | export default test_dalek_scalars; 106 | -------------------------------------------------------------------------------- /dist/ristretto255.min.js: -------------------------------------------------------------------------------- 1 | !function(r,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(r="undefined"!=typeof globalThis?globalThis:r||self).ristretto255=t()}(this,(function(){"use strict";function r(){throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs")}var t,n=(t=Object.freeze({__proto__:null,default:{}}))&&t.default||t,e=function(r,t){return r(t={exports:{}},t.exports),t.exports}((function(t){!function(t){var e=function(r){var t,n=new Float64Array(16);if(r)for(t=0;t>24&255,r[t+1]=n>>16&255,r[t+2]=n>>8&255,r[t+3]=255&n,r[t+4]=e>>24&255,r[t+5]=e>>16&255,r[t+6]=e>>8&255,r[t+7]=255&e}function w(r,t,n,e,o){var i,f=0;for(i=0;i>>8)-1}function v(r,t,n,e){return w(r,t,n,e,16)}function b(r,t,n,e){return w(r,t,n,e,32)}function A(r,t,n,e){!function(r,t,n,e){for(var o,i=255&e[0]|(255&e[1])<<8|(255&e[2])<<16|(255&e[3])<<24,f=255&n[0]|(255&n[1])<<8|(255&n[2])<<16|(255&n[3])<<24,a=255&n[4]|(255&n[5])<<8|(255&n[6])<<16|(255&n[7])<<24,s=255&n[8]|(255&n[9])<<8|(255&n[10])<<16|(255&n[11])<<24,h=255&n[12]|(255&n[13])<<8|(255&n[14])<<16|(255&n[15])<<24,c=255&e[4]|(255&e[5])<<8|(255&e[6])<<16|(255&e[7])<<24,u=255&t[0]|(255&t[1])<<8|(255&t[2])<<16|(255&t[3])<<24,l=255&t[4]|(255&t[5])<<8|(255&t[6])<<16|(255&t[7])<<24,y=255&t[8]|(255&t[9])<<8|(255&t[10])<<16|(255&t[11])<<24,g=255&t[12]|(255&t[13])<<8|(255&t[14])<<16|(255&t[15])<<24,p=255&e[8]|(255&e[9])<<8|(255&e[10])<<16|(255&e[11])<<24,w=255&n[16]|(255&n[17])<<8|(255&n[18])<<16|(255&n[19])<<24,v=255&n[20]|(255&n[21])<<8|(255&n[22])<<16|(255&n[23])<<24,b=255&n[24]|(255&n[25])<<8|(255&n[26])<<16|(255&n[27])<<24,A=255&n[28]|(255&n[29])<<8|(255&n[30])<<16|(255&n[31])<<24,d=255&e[12]|(255&e[13])<<8|(255&e[14])<<16|(255&e[15])<<24,M=i,_=f,U=a,E=s,m=h,x=c,S=u,B=l,k=y,L=g,K=p,T=w,Y=v,R=b,z=A,Z=d,P=0;P<20;P+=2)M^=(o=(Y^=(o=(k^=(o=(m^=(o=M+Y|0)<<7|o>>>25)+M|0)<<9|o>>>23)+m|0)<<13|o>>>19)+k|0)<<18|o>>>14,x^=(o=(_^=(o=(R^=(o=(L^=(o=x+_|0)<<7|o>>>25)+x|0)<<9|o>>>23)+L|0)<<13|o>>>19)+R|0)<<18|o>>>14,K^=(o=(S^=(o=(U^=(o=(z^=(o=K+S|0)<<7|o>>>25)+K|0)<<9|o>>>23)+z|0)<<13|o>>>19)+U|0)<<18|o>>>14,Z^=(o=(T^=(o=(B^=(o=(E^=(o=Z+T|0)<<7|o>>>25)+Z|0)<<9|o>>>23)+E|0)<<13|o>>>19)+B|0)<<18|o>>>14,M^=(o=(E^=(o=(U^=(o=(_^=(o=M+E|0)<<7|o>>>25)+M|0)<<9|o>>>23)+_|0)<<13|o>>>19)+U|0)<<18|o>>>14,x^=(o=(m^=(o=(B^=(o=(S^=(o=x+m|0)<<7|o>>>25)+x|0)<<9|o>>>23)+S|0)<<13|o>>>19)+B|0)<<18|o>>>14,K^=(o=(L^=(o=(k^=(o=(T^=(o=K+L|0)<<7|o>>>25)+K|0)<<9|o>>>23)+T|0)<<13|o>>>19)+k|0)<<18|o>>>14,Z^=(o=(z^=(o=(R^=(o=(Y^=(o=Z+z|0)<<7|o>>>25)+Z|0)<<9|o>>>23)+Y|0)<<13|o>>>19)+R|0)<<18|o>>>14;M=M+i|0,_=_+f|0,U=U+a|0,E=E+s|0,m=m+h|0,x=x+c|0,S=S+u|0,B=B+l|0,k=k+y|0,L=L+g|0,K=K+p|0,T=T+w|0,Y=Y+v|0,R=R+b|0,z=z+A|0,Z=Z+d|0,r[0]=M>>>0&255,r[1]=M>>>8&255,r[2]=M>>>16&255,r[3]=M>>>24&255,r[4]=_>>>0&255,r[5]=_>>>8&255,r[6]=_>>>16&255,r[7]=_>>>24&255,r[8]=U>>>0&255,r[9]=U>>>8&255,r[10]=U>>>16&255,r[11]=U>>>24&255,r[12]=E>>>0&255,r[13]=E>>>8&255,r[14]=E>>>16&255,r[15]=E>>>24&255,r[16]=m>>>0&255,r[17]=m>>>8&255,r[18]=m>>>16&255,r[19]=m>>>24&255,r[20]=x>>>0&255,r[21]=x>>>8&255,r[22]=x>>>16&255,r[23]=x>>>24&255,r[24]=S>>>0&255,r[25]=S>>>8&255,r[26]=S>>>16&255,r[27]=S>>>24&255,r[28]=B>>>0&255,r[29]=B>>>8&255,r[30]=B>>>16&255,r[31]=B>>>24&255,r[32]=k>>>0&255,r[33]=k>>>8&255,r[34]=k>>>16&255,r[35]=k>>>24&255,r[36]=L>>>0&255,r[37]=L>>>8&255,r[38]=L>>>16&255,r[39]=L>>>24&255,r[40]=K>>>0&255,r[41]=K>>>8&255,r[42]=K>>>16&255,r[43]=K>>>24&255,r[44]=T>>>0&255,r[45]=T>>>8&255,r[46]=T>>>16&255,r[47]=T>>>24&255,r[48]=Y>>>0&255,r[49]=Y>>>8&255,r[50]=Y>>>16&255,r[51]=Y>>>24&255,r[52]=R>>>0&255,r[53]=R>>>8&255,r[54]=R>>>16&255,r[55]=R>>>24&255,r[56]=z>>>0&255,r[57]=z>>>8&255,r[58]=z>>>16&255,r[59]=z>>>24&255,r[60]=Z>>>0&255,r[61]=Z>>>8&255,r[62]=Z>>>16&255,r[63]=Z>>>24&255}(r,t,n,e)}function d(r,t,n,e){!function(r,t,n,e){for(var o,i=255&e[0]|(255&e[1])<<8|(255&e[2])<<16|(255&e[3])<<24,f=255&n[0]|(255&n[1])<<8|(255&n[2])<<16|(255&n[3])<<24,a=255&n[4]|(255&n[5])<<8|(255&n[6])<<16|(255&n[7])<<24,s=255&n[8]|(255&n[9])<<8|(255&n[10])<<16|(255&n[11])<<24,h=255&n[12]|(255&n[13])<<8|(255&n[14])<<16|(255&n[15])<<24,c=255&e[4]|(255&e[5])<<8|(255&e[6])<<16|(255&e[7])<<24,u=255&t[0]|(255&t[1])<<8|(255&t[2])<<16|(255&t[3])<<24,l=255&t[4]|(255&t[5])<<8|(255&t[6])<<16|(255&t[7])<<24,y=255&t[8]|(255&t[9])<<8|(255&t[10])<<16|(255&t[11])<<24,g=255&t[12]|(255&t[13])<<8|(255&t[14])<<16|(255&t[15])<<24,p=255&e[8]|(255&e[9])<<8|(255&e[10])<<16|(255&e[11])<<24,w=255&n[16]|(255&n[17])<<8|(255&n[18])<<16|(255&n[19])<<24,v=255&n[20]|(255&n[21])<<8|(255&n[22])<<16|(255&n[23])<<24,b=255&n[24]|(255&n[25])<<8|(255&n[26])<<16|(255&n[27])<<24,A=255&n[28]|(255&n[29])<<8|(255&n[30])<<16|(255&n[31])<<24,d=255&e[12]|(255&e[13])<<8|(255&e[14])<<16|(255&e[15])<<24,M=0;M<20;M+=2)i^=(o=(v^=(o=(y^=(o=(h^=(o=i+v|0)<<7|o>>>25)+i|0)<<9|o>>>23)+h|0)<<13|o>>>19)+y|0)<<18|o>>>14,c^=(o=(f^=(o=(b^=(o=(g^=(o=c+f|0)<<7|o>>>25)+c|0)<<9|o>>>23)+g|0)<<13|o>>>19)+b|0)<<18|o>>>14,p^=(o=(u^=(o=(a^=(o=(A^=(o=p+u|0)<<7|o>>>25)+p|0)<<9|o>>>23)+A|0)<<13|o>>>19)+a|0)<<18|o>>>14,d^=(o=(w^=(o=(l^=(o=(s^=(o=d+w|0)<<7|o>>>25)+d|0)<<9|o>>>23)+s|0)<<13|o>>>19)+l|0)<<18|o>>>14,i^=(o=(s^=(o=(a^=(o=(f^=(o=i+s|0)<<7|o>>>25)+i|0)<<9|o>>>23)+f|0)<<13|o>>>19)+a|0)<<18|o>>>14,c^=(o=(h^=(o=(l^=(o=(u^=(o=c+h|0)<<7|o>>>25)+c|0)<<9|o>>>23)+u|0)<<13|o>>>19)+l|0)<<18|o>>>14,p^=(o=(g^=(o=(y^=(o=(w^=(o=p+g|0)<<7|o>>>25)+p|0)<<9|o>>>23)+w|0)<<13|o>>>19)+y|0)<<18|o>>>14,d^=(o=(A^=(o=(b^=(o=(v^=(o=d+A|0)<<7|o>>>25)+d|0)<<9|o>>>23)+v|0)<<13|o>>>19)+b|0)<<18|o>>>14;r[0]=i>>>0&255,r[1]=i>>>8&255,r[2]=i>>>16&255,r[3]=i>>>24&255,r[4]=c>>>0&255,r[5]=c>>>8&255,r[6]=c>>>16&255,r[7]=c>>>24&255,r[8]=p>>>0&255,r[9]=p>>>8&255,r[10]=p>>>16&255,r[11]=p>>>24&255,r[12]=d>>>0&255,r[13]=d>>>8&255,r[14]=d>>>16&255,r[15]=d>>>24&255,r[16]=u>>>0&255,r[17]=u>>>8&255,r[18]=u>>>16&255,r[19]=u>>>24&255,r[20]=l>>>0&255,r[21]=l>>>8&255,r[22]=l>>>16&255,r[23]=l>>>24&255,r[24]=y>>>0&255,r[25]=y>>>8&255,r[26]=y>>>16&255,r[27]=y>>>24&255,r[28]=g>>>0&255,r[29]=g>>>8&255,r[30]=g>>>16&255,r[31]=g>>>24&255}(r,t,n,e)}var M=new Uint8Array([101,120,112,97,110,100,32,51,50,45,98,121,116,101,32,107]);function _(r,t,n,e,o,i,f){var a,s,h=new Uint8Array(16),c=new Uint8Array(64);for(s=0;s<16;s++)h[s]=0;for(s=0;s<8;s++)h[s]=i[s];for(;o>=64;){for(A(c,h,f,M),s=0;s<64;s++)r[t+s]=n[e+s]^c[s];for(a=1,s=8;s<16;s++)a=a+(255&h[s])|0,h[s]=255&a,a>>>=8;o-=64,t+=64,e+=64}if(o>0)for(A(c,h,f,M),s=0;s=64;){for(A(s,a,o,M),f=0;f<64;f++)r[t+f]=s[f];for(i=1,f=8;f<16;f++)i=i+(255&a[f])|0,a[f]=255&i,i>>>=8;n-=64,t+=64}if(n>0)for(A(s,a,o,M),f=0;f>>13|n<<3),e=255&r[4]|(255&r[5])<<8,this.r[2]=7939&(n>>>10|e<<6),o=255&r[6]|(255&r[7])<<8,this.r[3]=8191&(e>>>7|o<<9),i=255&r[8]|(255&r[9])<<8,this.r[4]=255&(o>>>4|i<<12),this.r[5]=i>>>1&8190,f=255&r[10]|(255&r[11])<<8,this.r[6]=8191&(i>>>14|f<<2),a=255&r[12]|(255&r[13])<<8,this.r[7]=8065&(f>>>11|a<<5),s=255&r[14]|(255&r[15])<<8,this.r[8]=8191&(a>>>8|s<<8),this.r[9]=s>>>5&127,this.pad[0]=255&r[16]|(255&r[17])<<8,this.pad[1]=255&r[18]|(255&r[19])<<8,this.pad[2]=255&r[20]|(255&r[21])<<8,this.pad[3]=255&r[22]|(255&r[23])<<8,this.pad[4]=255&r[24]|(255&r[25])<<8,this.pad[5]=255&r[26]|(255&r[27])<<8,this.pad[6]=255&r[28]|(255&r[29])<<8,this.pad[7]=255&r[30]|(255&r[31])<<8};function S(r,t,n,e,o,i){var f=new x(i);return f.update(n,e,o),f.finish(r,t),0}function B(r,t,n,e,o,i){var f=new Uint8Array(16);return S(f,0,n,e,o,i),v(r,t,f,0)}function k(r,t,n,e,o){var i;if(n<32)return-1;for(m(r,0,t,0,n,e,o),S(r,16,r,32,n-32,r),i=0;i<16;i++)r[i]=0;return 0}function L(r,t,n,e,o){var i,f=new Uint8Array(32);if(n<32)return-1;if(E(f,0,32,e,o),0!==B(t,16,t,32,n-32,f))return-1;for(m(r,0,t,0,n,e,o),i=0;i<32;i++)r[i]=0;return 0}function K(r,t){var n;for(n=0;n<16;n++)r[n]=0|t[n]}function T(r){var t,n,e=1;for(t=0;t<16;t++)n=r[t]+e+65535,e=Math.floor(n/65536),r[t]=n-65536*e;r[0]+=e-1+37*(e-1)}function Y(r,t,n){for(var e,o=~(n-1),i=0;i<16;i++)e=o&(r[i]^t[i]),r[i]^=e,t[i]^=e}function R(r,t){var n,o,i,f=e(),a=e();for(n=0;n<16;n++)a[n]=t[n];for(T(a),T(a),T(a),o=0;o<2;o++){for(f[0]=a[0]-65517,n=1;n<15;n++)f[n]=a[n]-65535-(f[n-1]>>16&1),f[n-1]&=65535;f[15]=a[15]-32767-(f[14]>>16&1),i=f[15]>>16&1,f[14]&=65535,Y(a,f,1-i)}for(n=0;n<16;n++)r[2*n]=255&a[n],r[2*n+1]=a[n]>>8}function z(r,t){var n=new Uint8Array(32),e=new Uint8Array(32);return R(n,r),R(e,t),b(n,0,e,0)}function Z(r){var t=new Uint8Array(32);return R(t,r),1&t[0]}function P(r,t){var n;for(n=0;n<16;n++)r[n]=t[2*n]+(t[2*n+1]<<8);r[15]&=32767}function O(r,t,n){for(var e=0;e<16;e++)r[e]=t[e]+n[e]}function F(r,t,n){for(var e=0;e<16;e++)r[e]=t[e]-n[e]}function I(r,t,n){var e,o,i=0,f=0,a=0,s=0,h=0,c=0,u=0,l=0,y=0,g=0,p=0,w=0,v=0,b=0,A=0,d=0,M=0,_=0,U=0,E=0,m=0,x=0,S=0,B=0,k=0,L=0,K=0,T=0,Y=0,R=0,z=0,Z=n[0],P=n[1],O=n[2],F=n[3],I=n[4],N=n[5],C=n[6],q=n[7],D=n[8],j=n[9],G=n[10],V=n[11],H=n[12],X=n[13],J=n[14],Q=n[15];i+=(e=t[0])*Z,f+=e*P,a+=e*O,s+=e*F,h+=e*I,c+=e*N,u+=e*C,l+=e*q,y+=e*D,g+=e*j,p+=e*G,w+=e*V,v+=e*H,b+=e*X,A+=e*J,d+=e*Q,f+=(e=t[1])*Z,a+=e*P,s+=e*O,h+=e*F,c+=e*I,u+=e*N,l+=e*C,y+=e*q,g+=e*D,p+=e*j,w+=e*G,v+=e*V,b+=e*H,A+=e*X,d+=e*J,M+=e*Q,a+=(e=t[2])*Z,s+=e*P,h+=e*O,c+=e*F,u+=e*I,l+=e*N,y+=e*C,g+=e*q,p+=e*D,w+=e*j,v+=e*G,b+=e*V,A+=e*H,d+=e*X,M+=e*J,_+=e*Q,s+=(e=t[3])*Z,h+=e*P,c+=e*O,u+=e*F,l+=e*I,y+=e*N,g+=e*C,p+=e*q,w+=e*D,v+=e*j,b+=e*G,A+=e*V,d+=e*H,M+=e*X,_+=e*J,U+=e*Q,h+=(e=t[4])*Z,c+=e*P,u+=e*O,l+=e*F,y+=e*I,g+=e*N,p+=e*C,w+=e*q,v+=e*D,b+=e*j,A+=e*G,d+=e*V,M+=e*H,_+=e*X,U+=e*J,E+=e*Q,c+=(e=t[5])*Z,u+=e*P,l+=e*O,y+=e*F,g+=e*I,p+=e*N,w+=e*C,v+=e*q,b+=e*D,A+=e*j,d+=e*G,M+=e*V,_+=e*H,U+=e*X,E+=e*J,m+=e*Q,u+=(e=t[6])*Z,l+=e*P,y+=e*O,g+=e*F,p+=e*I,w+=e*N,v+=e*C,b+=e*q,A+=e*D,d+=e*j,M+=e*G,_+=e*V,U+=e*H,E+=e*X,m+=e*J,x+=e*Q,l+=(e=t[7])*Z,y+=e*P,g+=e*O,p+=e*F,w+=e*I,v+=e*N,b+=e*C,A+=e*q,d+=e*D,M+=e*j,_+=e*G,U+=e*V,E+=e*H,m+=e*X,x+=e*J,S+=e*Q,y+=(e=t[8])*Z,g+=e*P,p+=e*O,w+=e*F,v+=e*I,b+=e*N,A+=e*C,d+=e*q,M+=e*D,_+=e*j,U+=e*G,E+=e*V,m+=e*H,x+=e*X,S+=e*J,B+=e*Q,g+=(e=t[9])*Z,p+=e*P,w+=e*O,v+=e*F,b+=e*I,A+=e*N,d+=e*C,M+=e*q,_+=e*D,U+=e*j,E+=e*G,m+=e*V,x+=e*H,S+=e*X,B+=e*J,k+=e*Q,p+=(e=t[10])*Z,w+=e*P,v+=e*O,b+=e*F,A+=e*I,d+=e*N,M+=e*C,_+=e*q,U+=e*D,E+=e*j,m+=e*G,x+=e*V,S+=e*H,B+=e*X,k+=e*J,L+=e*Q,w+=(e=t[11])*Z,v+=e*P,b+=e*O,A+=e*F,d+=e*I,M+=e*N,_+=e*C,U+=e*q,E+=e*D,m+=e*j,x+=e*G,S+=e*V,B+=e*H,k+=e*X,L+=e*J,K+=e*Q,v+=(e=t[12])*Z,b+=e*P,A+=e*O,d+=e*F,M+=e*I,_+=e*N,U+=e*C,E+=e*q,m+=e*D,x+=e*j,S+=e*G,B+=e*V,k+=e*H,L+=e*X,K+=e*J,T+=e*Q,b+=(e=t[13])*Z,A+=e*P,d+=e*O,M+=e*F,_+=e*I,U+=e*N,E+=e*C,m+=e*q,x+=e*D,S+=e*j,B+=e*G,k+=e*V,L+=e*H,K+=e*X,T+=e*J,Y+=e*Q,A+=(e=t[14])*Z,d+=e*P,M+=e*O,_+=e*F,U+=e*I,E+=e*N,m+=e*C,x+=e*q,S+=e*D,B+=e*j,k+=e*G,L+=e*V,K+=e*H,T+=e*X,Y+=e*J,R+=e*Q,d+=(e=t[15])*Z,f+=38*(_+=e*O),a+=38*(U+=e*F),s+=38*(E+=e*I),h+=38*(m+=e*N),c+=38*(x+=e*C),u+=38*(S+=e*q),l+=38*(B+=e*D),y+=38*(k+=e*j),g+=38*(L+=e*G),p+=38*(K+=e*V),w+=38*(T+=e*H),v+=38*(Y+=e*X),b+=38*(R+=e*J),A+=38*(z+=e*Q),i=(e=(i+=38*(M+=e*P))+(o=1)+65535)-65536*(o=Math.floor(e/65536)),f=(e=f+o+65535)-65536*(o=Math.floor(e/65536)),a=(e=a+o+65535)-65536*(o=Math.floor(e/65536)),s=(e=s+o+65535)-65536*(o=Math.floor(e/65536)),h=(e=h+o+65535)-65536*(o=Math.floor(e/65536)),c=(e=c+o+65535)-65536*(o=Math.floor(e/65536)),u=(e=u+o+65535)-65536*(o=Math.floor(e/65536)),l=(e=l+o+65535)-65536*(o=Math.floor(e/65536)),y=(e=y+o+65535)-65536*(o=Math.floor(e/65536)),g=(e=g+o+65535)-65536*(o=Math.floor(e/65536)),p=(e=p+o+65535)-65536*(o=Math.floor(e/65536)),w=(e=w+o+65535)-65536*(o=Math.floor(e/65536)),v=(e=v+o+65535)-65536*(o=Math.floor(e/65536)),b=(e=b+o+65535)-65536*(o=Math.floor(e/65536)),A=(e=A+o+65535)-65536*(o=Math.floor(e/65536)),d=(e=d+o+65535)-65536*(o=Math.floor(e/65536)),i=(e=(i+=o-1+37*(o-1))+(o=1)+65535)-65536*(o=Math.floor(e/65536)),f=(e=f+o+65535)-65536*(o=Math.floor(e/65536)),a=(e=a+o+65535)-65536*(o=Math.floor(e/65536)),s=(e=s+o+65535)-65536*(o=Math.floor(e/65536)),h=(e=h+o+65535)-65536*(o=Math.floor(e/65536)),c=(e=c+o+65535)-65536*(o=Math.floor(e/65536)),u=(e=u+o+65535)-65536*(o=Math.floor(e/65536)),l=(e=l+o+65535)-65536*(o=Math.floor(e/65536)),y=(e=y+o+65535)-65536*(o=Math.floor(e/65536)),g=(e=g+o+65535)-65536*(o=Math.floor(e/65536)),p=(e=p+o+65535)-65536*(o=Math.floor(e/65536)),w=(e=w+o+65535)-65536*(o=Math.floor(e/65536)),v=(e=v+o+65535)-65536*(o=Math.floor(e/65536)),b=(e=b+o+65535)-65536*(o=Math.floor(e/65536)),A=(e=A+o+65535)-65536*(o=Math.floor(e/65536)),d=(e=d+o+65535)-65536*(o=Math.floor(e/65536)),i+=o-1+37*(o-1),r[0]=i,r[1]=f,r[2]=a,r[3]=s,r[4]=h,r[5]=c,r[6]=u,r[7]=l,r[8]=y,r[9]=g,r[10]=p,r[11]=w,r[12]=v,r[13]=b,r[14]=A,r[15]=d}function N(r,t){I(r,t,t)}function C(r,t){var n,o=e();for(n=0;n<16;n++)o[n]=t[n];for(n=253;n>=0;n--)N(o,o),2!==n&&4!==n&&I(o,o,t);for(n=0;n<16;n++)r[n]=o[n]}function q(r,t){var n,o=e();for(n=0;n<16;n++)o[n]=t[n];for(n=250;n>=0;n--)N(o,o),1!==n&&I(o,o,t);for(n=0;n<16;n++)r[n]=o[n]}function D(r,t,n){var o,i,f=new Uint8Array(32),a=new Float64Array(80),s=e(),c=e(),u=e(),l=e(),y=e(),g=e();for(i=0;i<31;i++)f[i]=t[i];for(f[31]=127&t[31]|64,f[0]&=248,P(a,n),i=0;i<16;i++)c[i]=a[i],l[i]=s[i]=u[i]=0;for(s[0]=l[0]=1,i=254;i>=0;--i)Y(s,c,o=f[i>>>3]>>>(7&i)&1),Y(u,l,o),O(y,s,u),F(s,s,u),O(u,c,l),F(c,c,l),N(l,y),N(g,s),I(s,u,s),I(u,c,y),O(y,s,u),F(s,s,u),N(c,s),F(u,l,g),I(s,u,h),O(s,s,l),I(u,u,s),I(s,l,g),I(l,c,a),N(c,y),Y(s,c,o),Y(u,l,o);for(i=0;i<16;i++)a[i+16]=s[i],a[i+32]=u[i],a[i+48]=c[i],a[i+64]=l[i];var p=a.subarray(32),w=a.subarray(16);return C(p,p),I(w,w,p),R(r,w),0}function j(r,t){return D(r,t,f)}function G(r,t){return o(t,32),j(r,t)}function V(r,t,n){var e=new Uint8Array(32);return D(e,n,t),d(r,i,e,M)}x.prototype.blocks=function(r,t,n){for(var e,o,i,f,a,s,h,c,u,l,y,g,p,w,v,b,A,d,M,_=this.fin?0:2048,U=this.h[0],E=this.h[1],m=this.h[2],x=this.h[3],S=this.h[4],B=this.h[5],k=this.h[6],L=this.h[7],K=this.h[8],T=this.h[9],Y=this.r[0],R=this.r[1],z=this.r[2],Z=this.r[3],P=this.r[4],O=this.r[5],F=this.r[6],I=this.r[7],N=this.r[8],C=this.r[9];n>=16;)l=u=0,l+=(U+=8191&(e=255&r[t+0]|(255&r[t+1])<<8))*Y,l+=(E+=8191&(e>>>13|(o=255&r[t+2]|(255&r[t+3])<<8)<<3))*(5*C),l+=(m+=8191&(o>>>10|(i=255&r[t+4]|(255&r[t+5])<<8)<<6))*(5*N),l+=(x+=8191&(i>>>7|(f=255&r[t+6]|(255&r[t+7])<<8)<<9))*(5*I),u=(l+=(S+=8191&(f>>>4|(a=255&r[t+8]|(255&r[t+9])<<8)<<12))*(5*F))>>>13,l&=8191,l+=(B+=a>>>1&8191)*(5*O),l+=(k+=8191&(a>>>14|(s=255&r[t+10]|(255&r[t+11])<<8)<<2))*(5*P),l+=(L+=8191&(s>>>11|(h=255&r[t+12]|(255&r[t+13])<<8)<<5))*(5*Z),l+=(K+=8191&(h>>>8|(c=255&r[t+14]|(255&r[t+15])<<8)<<8))*(5*z),y=u+=(l+=(T+=c>>>5|_)*(5*R))>>>13,y+=U*R,y+=E*Y,y+=m*(5*C),y+=x*(5*N),u=(y+=S*(5*I))>>>13,y&=8191,y+=B*(5*F),y+=k*(5*O),y+=L*(5*P),y+=K*(5*Z),u+=(y+=T*(5*z))>>>13,y&=8191,g=u,g+=U*z,g+=E*R,g+=m*Y,g+=x*(5*C),u=(g+=S*(5*N))>>>13,g&=8191,g+=B*(5*I),g+=k*(5*F),g+=L*(5*O),g+=K*(5*P),p=u+=(g+=T*(5*Z))>>>13,p+=U*Z,p+=E*z,p+=m*R,p+=x*Y,u=(p+=S*(5*C))>>>13,p&=8191,p+=B*(5*N),p+=k*(5*I),p+=L*(5*F),p+=K*(5*O),w=u+=(p+=T*(5*P))>>>13,w+=U*P,w+=E*Z,w+=m*z,w+=x*R,u=(w+=S*Y)>>>13,w&=8191,w+=B*(5*C),w+=k*(5*N),w+=L*(5*I),w+=K*(5*F),v=u+=(w+=T*(5*O))>>>13,v+=U*O,v+=E*P,v+=m*Z,v+=x*z,u=(v+=S*R)>>>13,v&=8191,v+=B*Y,v+=k*(5*C),v+=L*(5*N),v+=K*(5*I),b=u+=(v+=T*(5*F))>>>13,b+=U*F,b+=E*O,b+=m*P,b+=x*Z,u=(b+=S*z)>>>13,b&=8191,b+=B*R,b+=k*Y,b+=L*(5*C),b+=K*(5*N),A=u+=(b+=T*(5*I))>>>13,A+=U*I,A+=E*F,A+=m*O,A+=x*P,u=(A+=S*Z)>>>13,A&=8191,A+=B*z,A+=k*R,A+=L*Y,A+=K*(5*C),d=u+=(A+=T*(5*N))>>>13,d+=U*N,d+=E*I,d+=m*F,d+=x*O,u=(d+=S*P)>>>13,d&=8191,d+=B*Z,d+=k*z,d+=L*R,d+=K*Y,M=u+=(d+=T*(5*C))>>>13,M+=U*C,M+=E*N,M+=m*I,M+=x*F,u=(M+=S*O)>>>13,M&=8191,M+=B*P,M+=k*Z,M+=L*z,M+=K*R,U=l=8191&(u=(u=((u+=(M+=T*Y)>>>13)<<2)+u|0)+(l&=8191)|0),E=y+=u>>>=13,m=g&=8191,x=p&=8191,S=w&=8191,B=v&=8191,k=b&=8191,L=A&=8191,K=d&=8191,T=M&=8191,t+=16,n-=16;this.h[0]=U,this.h[1]=E,this.h[2]=m,this.h[3]=x,this.h[4]=S,this.h[5]=B,this.h[6]=k,this.h[7]=L,this.h[8]=K,this.h[9]=T},x.prototype.finish=function(r,t){var n,e,o,i,f=new Uint16Array(10);if(this.leftover){for(i=this.leftover,this.buffer[i++]=1;i<16;i++)this.buffer[i]=0;this.fin=1,this.blocks(this.buffer,0,16)}for(n=this.h[1]>>>13,this.h[1]&=8191,i=2;i<10;i++)this.h[i]+=n,n=this.h[i]>>>13,this.h[i]&=8191;for(this.h[0]+=5*n,n=this.h[0]>>>13,this.h[0]&=8191,this.h[1]+=n,n=this.h[1]>>>13,this.h[1]&=8191,this.h[2]+=n,f[0]=this.h[0]+5,n=f[0]>>>13,f[0]&=8191,i=1;i<10;i++)f[i]=this.h[i]+n,n=f[i]>>>13,f[i]&=8191;for(f[9]-=8192,e=(1^n)-1,i=0;i<10;i++)f[i]&=e;for(e=~e,i=0;i<10;i++)this.h[i]=this.h[i]&e|f[i];for(this.h[0]=65535&(this.h[0]|this.h[1]<<13),this.h[1]=65535&(this.h[1]>>>3|this.h[2]<<10),this.h[2]=65535&(this.h[2]>>>6|this.h[3]<<7),this.h[3]=65535&(this.h[3]>>>9|this.h[4]<<4),this.h[4]=65535&(this.h[4]>>>12|this.h[5]<<1|this.h[6]<<14),this.h[5]=65535&(this.h[6]>>>2|this.h[7]<<11),this.h[6]=65535&(this.h[7]>>>5|this.h[8]<<8),this.h[7]=65535&(this.h[8]>>>8|this.h[9]<<5),o=this.h[0]+this.pad[0],this.h[0]=65535&o,i=1;i<8;i++)o=(this.h[i]+this.pad[i]|0)+(o>>>16)|0,this.h[i]=65535&o;r[t+0]=this.h[0]>>>0&255,r[t+1]=this.h[0]>>>8&255,r[t+2]=this.h[1]>>>0&255,r[t+3]=this.h[1]>>>8&255,r[t+4]=this.h[2]>>>0&255,r[t+5]=this.h[2]>>>8&255,r[t+6]=this.h[3]>>>0&255,r[t+7]=this.h[3]>>>8&255,r[t+8]=this.h[4]>>>0&255,r[t+9]=this.h[4]>>>8&255,r[t+10]=this.h[5]>>>0&255,r[t+11]=this.h[5]>>>8&255,r[t+12]=this.h[6]>>>0&255,r[t+13]=this.h[6]>>>8&255,r[t+14]=this.h[7]>>>0&255,r[t+15]=this.h[7]>>>8&255},x.prototype.update=function(r,t,n){var e,o;if(this.leftover){for((o=16-this.leftover)>n&&(o=n),e=0;e=16&&(o=n-n%16,this.blocks(r,t,o),t+=o,n-=o),n){for(e=0;e=128;){for(_=0;_<16;_++)U=8*_+H,L[_]=n[U+0]<<24|n[U+1]<<16|n[U+2]<<8|n[U+3],K[_]=n[U+4]<<24|n[U+5]<<16|n[U+6]<<8|n[U+7];for(_=0;_<80;_++)if(o=T,i=Y,f=R,a=z,s=Z,h=P,c=O,F,l=I,y=N,g=C,p=q,w=D,v=j,b=G,V,x=65535&(m=V),S=m>>>16,B=65535&(E=F),k=E>>>16,x+=65535&(m=(D>>>14|Z<<18)^(D>>>18|Z<<14)^(Z>>>9|D<<23)),S+=m>>>16,B+=65535&(E=(Z>>>14|D<<18)^(Z>>>18|D<<14)^(D>>>9|Z<<23)),k+=E>>>16,x+=65535&(m=D&j^~D&G),S+=m>>>16,B+=65535&(E=Z&P^~Z&O),k+=E>>>16,E=J[2*_],x+=65535&(m=J[2*_+1]),S+=m>>>16,B+=65535&E,k+=E>>>16,E=L[_%16],S+=(m=K[_%16])>>>16,B+=65535&E,k+=E>>>16,B+=(S+=(x+=65535&m)>>>16)>>>16,x=65535&(m=M=65535&x|S<<16),S=m>>>16,B=65535&(E=d=65535&B|(k+=B>>>16)<<16),k=E>>>16,x+=65535&(m=(I>>>28|T<<4)^(T>>>2|I<<30)^(T>>>7|I<<25)),S+=m>>>16,B+=65535&(E=(T>>>28|I<<4)^(I>>>2|T<<30)^(I>>>7|T<<25)),k+=E>>>16,S+=(m=I&N^I&C^N&C)>>>16,B+=65535&(E=T&Y^T&R^Y&R),k+=E>>>16,u=65535&(B+=(S+=(x+=65535&m)>>>16)>>>16)|(k+=B>>>16)<<16,A=65535&x|S<<16,x=65535&(m=p),S=m>>>16,B=65535&(E=a),k=E>>>16,S+=(m=M)>>>16,B+=65535&(E=d),k+=E>>>16,Y=o,R=i,z=f,Z=a=65535&(B+=(S+=(x+=65535&m)>>>16)>>>16)|(k+=B>>>16)<<16,P=s,O=h,F=c,T=u,N=l,C=y,q=g,D=p=65535&x|S<<16,j=w,G=v,V=b,I=A,_%16==15)for(U=0;U<16;U++)E=L[U],x=65535&(m=K[U]),S=m>>>16,B=65535&E,k=E>>>16,E=L[(U+9)%16],x+=65535&(m=K[(U+9)%16]),S+=m>>>16,B+=65535&E,k+=E>>>16,d=L[(U+1)%16],x+=65535&(m=((M=K[(U+1)%16])>>>1|d<<31)^(M>>>8|d<<24)^(M>>>7|d<<25)),S+=m>>>16,B+=65535&(E=(d>>>1|M<<31)^(d>>>8|M<<24)^d>>>7),k+=E>>>16,d=L[(U+14)%16],S+=(m=((M=K[(U+14)%16])>>>19|d<<13)^(d>>>29|M<<3)^(M>>>6|d<<26))>>>16,B+=65535&(E=(d>>>19|M<<13)^(M>>>29|d<<3)^d>>>6),k+=E>>>16,k+=(B+=(S+=(x+=65535&m)>>>16)>>>16)>>>16,L[U]=65535&B|k<<16,K[U]=65535&x|S<<16;x=65535&(m=I),S=m>>>16,B=65535&(E=T),k=E>>>16,E=r[0],S+=(m=t[0])>>>16,B+=65535&E,k+=E>>>16,k+=(B+=(S+=(x+=65535&m)>>>16)>>>16)>>>16,r[0]=T=65535&B|k<<16,t[0]=I=65535&x|S<<16,x=65535&(m=N),S=m>>>16,B=65535&(E=Y),k=E>>>16,E=r[1],S+=(m=t[1])>>>16,B+=65535&E,k+=E>>>16,k+=(B+=(S+=(x+=65535&m)>>>16)>>>16)>>>16,r[1]=Y=65535&B|k<<16,t[1]=N=65535&x|S<<16,x=65535&(m=C),S=m>>>16,B=65535&(E=R),k=E>>>16,E=r[2],S+=(m=t[2])>>>16,B+=65535&E,k+=E>>>16,k+=(B+=(S+=(x+=65535&m)>>>16)>>>16)>>>16,r[2]=R=65535&B|k<<16,t[2]=C=65535&x|S<<16,x=65535&(m=q),S=m>>>16,B=65535&(E=z),k=E>>>16,E=r[3],S+=(m=t[3])>>>16,B+=65535&E,k+=E>>>16,k+=(B+=(S+=(x+=65535&m)>>>16)>>>16)>>>16,r[3]=z=65535&B|k<<16,t[3]=q=65535&x|S<<16,x=65535&(m=D),S=m>>>16,B=65535&(E=Z),k=E>>>16,E=r[4],S+=(m=t[4])>>>16,B+=65535&E,k+=E>>>16,k+=(B+=(S+=(x+=65535&m)>>>16)>>>16)>>>16,r[4]=Z=65535&B|k<<16,t[4]=D=65535&x|S<<16,x=65535&(m=j),S=m>>>16,B=65535&(E=P),k=E>>>16,E=r[5],S+=(m=t[5])>>>16,B+=65535&E,k+=E>>>16,k+=(B+=(S+=(x+=65535&m)>>>16)>>>16)>>>16,r[5]=P=65535&B|k<<16,t[5]=j=65535&x|S<<16,x=65535&(m=G),S=m>>>16,B=65535&(E=O),k=E>>>16,E=r[6],S+=(m=t[6])>>>16,B+=65535&E,k+=E>>>16,k+=(B+=(S+=(x+=65535&m)>>>16)>>>16)>>>16,r[6]=O=65535&B|k<<16,t[6]=G=65535&x|S<<16,x=65535&(m=V),S=m>>>16,B=65535&(E=F),k=E>>>16,E=r[7],S+=(m=t[7])>>>16,B+=65535&E,k+=E>>>16,k+=(B+=(S+=(x+=65535&m)>>>16)>>>16)>>>16,r[7]=F=65535&B|k<<16,t[7]=V=65535&x|S<<16,H+=128,e-=128}return e}function W(r,t,n){var e,o=new Int32Array(8),i=new Int32Array(8),f=new Uint8Array(256),a=n;for(o[0]=1779033703,o[1]=3144134277,o[2]=1013904242,o[3]=2773480762,o[4]=1359893119,o[5]=2600822924,o[6]=528734635,o[7]=1541459225,i[0]=4089235720,i[1]=2227873595,i[2]=4271175723,i[3]=1595750129,i[4]=2917565137,i[5]=725511199,i[6]=4215389547,i[7]=327033209,Q(o,i,t,n),n%=128,e=0;e=0;--o)rr(r,t,e=n[o/8|0]>>(7&o)&1),$(t,r),$(r,r),rr(r,t,e)}function er(r,t){var n=[e(),e(),e(),e()];K(n[0],l),K(n[1],y),K(n[2],s),I(n[3],l,y),nr(r,n,t)}function or(r,t,n){var i,f=new Uint8Array(64),a=[e(),e(),e(),e()];for(n||o(t,32),W(f,t,32),f[0]&=248,f[31]&=127,f[31]|=64,er(a,f),tr(r,a),i=0;i<32;i++)t[i+32]=r[i];return 0}var ir=new Float64Array([237,211,245,92,26,99,18,88,214,156,247,162,222,249,222,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16]);function fr(r,t){var n,e,o,i;for(e=63;e>=32;--e){for(n=0,o=e-32,i=e-12;o>4)*ir[o],n=t[o]>>8,t[o]&=255;for(o=0;o<32;o++)t[o]-=n*ir[o];for(e=0;e<32;e++)t[e+1]+=t[e]>>8,r[e]=255&t[e]}function ar(r){var t,n=new Float64Array(64);for(t=0;t<64;t++)n[t]=r[t];for(t=0;t<64;t++)r[t]=0;fr(r,n)}function sr(r,t,n,o){var i,f,a=new Uint8Array(64),s=new Uint8Array(64),h=new Uint8Array(64),c=new Float64Array(64),u=[e(),e(),e(),e()];W(a,o,32),a[0]&=248,a[31]&=127,a[31]|=64;var l=n+64;for(i=0;i>7&&F(r[0],a,r[0]),I(r[3],r[0],r[1]),0)}(l,o))return-1;for(i=0;i=0},t.sign.keyPair=function(){var r=new Uint8Array(lr),t=new Uint8Array(yr);return or(r,t),{publicKey:r,secretKey:t}},t.sign.keyPair.fromSecretKey=function(r){if(pr(r),r.length!==yr)throw new Error("bad secret key size");for(var t=new Uint8Array(lr),n=0;n0;n--)t|=255^r[n];return t=(0|t)-1>>8,1-(1&(t&236-(0|r[0])>>8|r[0])|(255&r[31])>>7)}(t))return-1;i.unpack25519(o,t),i.S(a,o),i.set25519(s,f),i.Z(s,s,a),i.S(c,s),i.set25519(h,f),i.A(h,h,a),i.S(u,h),i.M(l,i.D,c),A(l,l),i.Z(l,l,u),i.M(y,l,u),i.set25519(e,f);const g=_(n,e,y);return i.M(r[0],n,h),i.M(r[1],n,r[0]),i.M(r[1],r[1],l),i.M(r[0],r[0],o),i.A(r[0],r[0],r[0]),M(r[0],r[0]),i.M(r[1],s,r[1]),i.set25519(r[2],f),i.M(r[3],r[0],r[1]),-(1-g|b(r[3])|w(r[1]))}function m(r,t){const n=i.gf(),e=i.gf(),o=i.gf(),a=i.gf(),s=i.gf(),h=i.gf(),c=i.gf(),y=i.gf(),w=i.gf(),b=i.gf(),d=i.gf(),U=i.gf(),E=i.gf(),m=i.gf();i.set25519(o,f),i.S(a,t),i.M(a,u,a),i.A(w,a,o),i.M(w,w,g),i.set25519(n,f),A(n,n),i.A(s,a,i.D),i.M(b,a,i.D),i.Z(b,n,b),i.M(b,b,s);const x=1-_(h,w,b);i.M(c,h,t),M(c,c),A(c,c),v(h,c,x),v(n,a,x),i.Z(e,a,o),i.M(e,e,n),i.M(e,e,p),i.Z(e,e,b),i.A(d,h,h),i.M(d,d,b),i.M(U,e,l),i.S(y,h),i.Z(E,o,y),i.A(m,o,y),i.M(r[0],d,m),i.M(r[1],E,U),i.M(r[2],U,m),i.M(r[3],d,E)}function x(r){const t=i.gf(),n=i.gf(),e=a(),o=a();return i.unpack25519(t,r.slice(0,32)),i.unpack25519(n,r.slice(32,64)),m(e,t),m(o,n),i.add(e,o),e}function S(r,t){const n=a(),e=a();if(-1===E(n,r))throw new Error("Invalid argument");if(-1===E(e,t))throw new Error("Invalid argument");return[n,e]}function B(r,t){const n=i.gf();A(n,t[3]);const e=i.gf();A(e,t[0]);const o=[e,t[1],t[2],n];i.add(r,o)}function k(){return x(e.randomBytes(64))}function L(r){const t=new Float64Array(64);for(let n=0;n<32;n++)t[n]=-r[n];const n=new Uint8Array(32);return i.modL(n,t),n}function K(r,t){const n=new Float64Array(64);for(let e=0;e<32;e++)n[e]=r[e]+t[e];const e=new Uint8Array(32);return i.modL(e,n),e}return o.scalarMultBase=function(r){const t=a();return i.scalarbase(t,r),U(t)},o.scalarMult=function(r,t){const n=a(),e=a();if(0!==E(n,t))throw new Error("Invalid argument");return i.scalarmult(e,n,r),U(e)},o.isValid=function(r){return-1!==E(a(),r)},o.add=function(r,t){const[n,e]=S(r,t);return i.add(n,e),U(n)},o.sub=function(r,t){const[n,e]=S(r,t);return B(n,e),U(n)},o.fromHash=function(r){return U(x(r))},o.getRandom=function(){return U(k())},o.basePoint=s,o.scalar={},o.scalar.getRandom=function(){let r=new Uint8Array(32),t=0;do{r=e.randomBytes(32),r[31]&=31;let n=32,o=1;do{n--,t|=r[n]-i.L[n]>>8&o,o&=(r[n]^i.L[n])-1>>8}while(0!==n)}while(0===t);return r},o.scalar.invert=function(r){const t=new Uint8Array(32);return function(r,t){for(let n=0;n<32;n++)r[n]=t[n];for(let e=251;e>=0;e--)c(r,n=r,n),0!=(h[e>>3]>>(7&e)&1)&&c(r,r,t);var n}(t,r),t},o.scalar.negate=L,o.scalar.add=K,o.scalar.sub=function(r,t){return K(r,L(t))},o.scalar.mul=function(r,t){const n=new Uint8Array(32);return c(n,r,t),n},o.unsafe={},o.unsafe.point={},o.unsafe.point.toBytes=U,o.unsafe.point.fromBytes=E,o.unsafe.point.fromHash=x,o.unsafe.point.sub=B,o.unsafe.point.add=i.add,o.unsafe.point.scalarMultBase=i.scalarbase,o.unsafe.point.scalarMult=i.scalarmult,o.unsafe.point.getRandom=k,o.unsafe.point.alloc=a,o.unsafe.constants={},o.unsafe.constants.LSub2=h,o.unsafe.constants.sqrtm1=u,o.unsafe.constants.sqrtadm1=l,o.unsafe.constants.invsqrtamd=y,o.unsafe.constants.onemsqd=g,o.unsafe.constants.sqdmone=p,o})); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ristretto255", 3 | "version": "0.1.3", 4 | "description": "Supports the ristretto255 group operations on top of TweetNaCl.js", 5 | "main": "dist/ristretto255.min.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/facebook/ristretto255-js.git" 9 | }, 10 | "author": "Valeria Nikolaenko ", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "@babel/core": "^7.7.7", 14 | "@babel/plugin-transform-modules-commonjs": "^7.7.5", 15 | "@babel/preset-env": "^7.7.7", 16 | "@rollup/plugin-babel": "^5.2.2", 17 | "@rollup/plugin-commonjs": "^11.0.1", 18 | "@rollup/plugin-node-resolve": "^7.0.0", 19 | "babel-jest": "^26.6.3", 20 | "eslint": "^6.8.0", 21 | "eslint-config-airbnb-base": "^14.0.0", 22 | "eslint-config-prettier": "^6.10.0", 23 | "eslint-config-standard": "^14.1.0", 24 | "eslint-plugin-import": "^2.20.1", 25 | "eslint-plugin-jest": "^24.1.3", 26 | "eslint-plugin-node": "^11.0.0", 27 | "eslint-plugin-prettier": "^3.1.2", 28 | "eslint-plugin-promise": "^4.2.1", 29 | "eslint-plugin-standard": "^4.0.1", 30 | "jest": "^26.6.3", 31 | "prettier": "^1.19.1", 32 | "rollup": "^2.35.1", 33 | "rollup-plugin-terser": "^7.0.2" 34 | }, 35 | "resolutions": { 36 | "minimist": "^1.2.5" 37 | }, 38 | "scripts": { 39 | "build": "rollup --config config/rollup.config.js", 40 | "test": "jest --config config/jest.config.js", 41 | "lint": "eslint --config config/eslint.config.js src/ristretto255.js src/ristretto255.test.js ristretto255.benchmarks.js config/*.js" 42 | }, 43 | "browserslist": "> 0.25%, not dead", 44 | "dependencies": { 45 | "tweetnacl": "^1.0.3" 46 | } 47 | } -------------------------------------------------------------------------------- /ristretto255.benchmarks.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | body { 9 | margin: 20px; 10 | width: 85%; 11 | display: block; 12 | margin: auto; 13 | font-family: Arial,sans-serif; 14 | font-weight: 300; 15 | } 16 | 17 | .container { 18 | width: 950px; 19 | margin: auto; 20 | padding-bottom: 100px; 21 | } 22 | 23 | h1 { 24 | color: white; 25 | background-color: #001e5c; 26 | text-align: center; 27 | } 28 | 29 | total_time { 30 | color: #001e5c; 31 | } 32 | 33 | table.benchmarks { 34 | table-layout: fixed; 35 | border-collapse: collapse; 36 | width: 100%; 37 | } 38 | 39 | table.benchmarks tr td, table.benchmarks tr th { 40 | border: 1px solid #ddd; 41 | padding-left: 8px; 42 | padding-right: 8px; 43 | padding-top: 2px; 44 | padding-bottom: 2px; 45 | } 46 | 47 | table.benchmarks tr { 48 | height: 30px; 49 | } 50 | 51 | table.benchmarks tr:nth-child(even){background-color: #f2f2f2;} 52 | table.benchmarks tr:hover {background-color: #ddd;} 53 | 54 | table.benchmarks th { 55 | padding-top: 12px; 56 | padding-bottom: 12px; 57 | padding-left: 8px; 58 | padding-right: 8px; 59 | text-align: left; 60 | background-color: #4CAF50; 61 | color: white; 62 | } 63 | 64 | table.benchmarks tr th:nth-of-type(1), table.benchmarks tr td:nth-of-type(1) { 65 | width: 300px; 66 | } 67 | 68 | table.benchmarks tr th:nth-of-type(2), table.benchmarks tr td:nth-of-type(2) { 69 | text-align: center; 70 | width: 100px; 71 | } 72 | 73 | table.benchmarks tr td:nth-of-type(2) { 74 | background-color: #ffec95; 75 | } 76 | 77 | table.benchmarks tr:nth-child(even) td:nth-of-type(2) { 78 | background-color: #ffd200; 79 | } 80 | -------------------------------------------------------------------------------- /ristretto255.benchmarks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Benchmarking Ristretto255.js 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Benchmarks of ristretto255 functions

14 |
Rendering benchmarks...
15 |
16 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ristretto255.benchmarks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | /* global ristretto255 */ 9 | 10 | if (!Uint8Array.prototype.fill) { 11 | // eslint-disable-next-line 12 | Uint8Array.prototype.fill = Array.prototype.fill; 13 | } 14 | 15 | /* Set-up */ 16 | const t00 = performance.now(); 17 | const NUM_OF_REPS = 100; 18 | const ristrettoECPoints = []; 19 | const ristrettoSerializedPoints = []; 20 | const hashes = []; 21 | const scalars = []; 22 | const h = ristretto255.unsafe.point.alloc(); 23 | /* Generate random ristretto points */ 24 | for (let i = 0; i < NUM_OF_REPS; i++) { 25 | const ristrettoECPoint = ristretto255.unsafe.point.getRandom(); 26 | ristrettoECPoints.push(ristrettoECPoint); 27 | ristrettoSerializedPoints.push( 28 | ristretto255.unsafe.point.toBytes(ristrettoECPoint) 29 | ); 30 | const hTemp = new Uint8Array(32); 31 | for (let j = 0; j < 32; j++) { 32 | hTemp[j] = i; 33 | } 34 | hashes.push(hTemp); 35 | scalars.push(ristretto255.scalar.getRandom()); 36 | } 37 | 38 | const functions = [ 39 | { 40 | // ristretto points are represented as Uint8Array(32) 41 | name: 'High-level ristretto255 group operations', 42 | functions: [ 43 | { 44 | name: 'getRandom', 45 | description: 'Generate a random group element', 46 | execute: () => ristretto255.getRandom() 47 | }, 48 | { 49 | name: 'isValid', 50 | description: 'Check if a value represents a valid element', 51 | execute: i => ristretto255.isValid(ristrettoSerializedPoints[i]) 52 | }, 53 | { 54 | name: 'fromHash', 55 | description: 56 | 'Hash to group: generate a group element from 64-element byte array, e.g. an output of SHA-512', 57 | execute: i => ristretto255.fromHash(hashes[i]) 58 | }, 59 | { 60 | name: 'add', 61 | description: 'Add two group elements', 62 | execute: i => 63 | ristretto255.add( 64 | ristrettoSerializedPoints[i], 65 | ristrettoSerializedPoints[(i + 1) % NUM_OF_REPS] 66 | ) 67 | }, 68 | { 69 | name: 'sub', 70 | description: 'Subtract two group elements', 71 | execute: i => 72 | ristretto255.sub( 73 | ristrettoSerializedPoints[i], 74 | ristrettoSerializedPoints[(i + 1) % NUM_OF_REPS] 75 | ) 76 | }, 77 | { 78 | name: 'scalarMultBase', 79 | description: 'Multiply a generator of the group by a scalar', 80 | execute: i => ristretto255.scalarMultBase(scalars[i]) 81 | }, 82 | { 83 | name: 'scalarMult', 84 | description: 'Multiply a group element by a scalar', 85 | execute: i => 86 | ristretto255.scalarMult(scalars[i], ristrettoSerializedPoints[i]) 87 | } 88 | ] 89 | }, 90 | { 91 | name: 'Scalar operations', 92 | functions: [ 93 | { 94 | name: 'scalar.getRandom', 95 | description: 'Generate a random scalar', 96 | execute: () => ristretto255.scalar.getRandom() 97 | }, 98 | { 99 | name: 'scalar.invert', 100 | description: 'Invert a scalar', 101 | execute: i => ristretto255.scalar.invert(scalars[i]) 102 | }, 103 | { 104 | name: 'scalar.negate', 105 | description: 'Negate a scalar', 106 | execute: i => 107 | ristretto255.scalar.negate(scalars[i], scalars[i % NUM_OF_REPS]) 108 | }, 109 | { 110 | name: 'scalar.add', 111 | description: 'Add two scalars', 112 | execute: i => 113 | ristretto255.scalar.add(scalars[i], scalars[i % NUM_OF_REPS]) 114 | }, 115 | { 116 | name: 'scalar.sub', 117 | description: 'Subtract two scalars', 118 | execute: i => 119 | ristretto255.scalar.sub(scalars[i], scalars[i % NUM_OF_REPS]) 120 | }, 121 | { 122 | name: 'scalar.mul', 123 | description: 'Multiply two scalars', 124 | execute: i => 125 | ristretto255.scalar.mul(scalars[i], scalars[i % NUM_OF_REPS]) 126 | } 127 | ] 128 | }, 129 | { 130 | name: 'Low-level unsafe functions (unless if used by a cryptographer)', 131 | functions: [ 132 | { 133 | name: 'unsafe.point.toBytes', 134 | description: 135 | 'Serialize a curve25519 point to ristretto255 group element', 136 | execute: i => ristretto255.unsafe.point.toBytes(ristrettoECPoints[i]) 137 | }, 138 | { 139 | name: 'unsafe.point.fromBytes', 140 | description: 141 | 'Deserialize a curve25519 point from ristretto255 group element', 142 | execute: i => 143 | ristretto255.unsafe.point.fromBytes(h, ristrettoSerializedPoints[i]) 144 | }, 145 | { 146 | name: 'unsafe.point.getRandom', 147 | description: 148 | 'Generate a random ristretto255 group element represented as curve25519 point', 149 | execute: () => ristretto255.unsafe.point.getRandom() 150 | }, 151 | { 152 | name: 'unsafe.point.fromHash', 153 | description: 154 | 'Generate a ristretto255 group element represented as curve25519 point from a 64 elements byte array such as an output of SHA512', 155 | execute: i => ristretto255.unsafe.point.fromHash(hashes[i]) 156 | }, 157 | { 158 | name: 'unsafe.point.add', 159 | description: 'Add two curve25519 points', 160 | execute: i => 161 | ristretto255.unsafe.point.add( 162 | ristrettoECPoints[i], 163 | ristrettoECPoints[i % NUM_OF_REPS] 164 | ) 165 | }, 166 | { 167 | name: 'unsafe.point.sub', 168 | description: 'Subtract two curve25519 points', 169 | execute: i => 170 | ristretto255.unsafe.point.sub( 171 | ristrettoECPoints[i], 172 | ristrettoECPoints[i % NUM_OF_REPS] 173 | ) 174 | }, 175 | { 176 | name: 'unsafe.point.scalarMultBase', 177 | description: "Multiply a curve25519's base point by a scalar", 178 | execute: i => 179 | ristretto255.unsafe.point.scalarMultBase( 180 | ristrettoECPoints[i], 181 | scalars[i] 182 | ) 183 | }, 184 | { 185 | name: 'unsafe.point.scalarMult', 186 | description: "Multiply a curve25519's point by a scalar", 187 | execute: i => 188 | ristretto255.unsafe.point.scalarMult( 189 | ristrettoECPoints[i], 190 | ristrettoECPoints[i % NUM_OF_REPS], 191 | scalars[i] 192 | ) 193 | } 194 | ] 195 | } 196 | ]; 197 | 198 | const template = (groupName, results) => ` 199 |

${groupName}

200 |
201 | 202 | 203 | 204 | 205 | 206 | 207 | ${results 208 | .map(result => { 209 | return ` 210 | 211 | 212 | 213 | 214 | 215 | `; 216 | }) 217 | .join('')} 218 |
Function nameTime in msComments
${result.functionName}${result.timing}${result.description}
219 |
`; 220 | 221 | function average(data) { 222 | const sum = data.reduce(function add(acc, value) { 223 | return acc + value; 224 | }, 0); 225 | 226 | const avg = sum / data.length; 227 | return avg; 228 | } 229 | 230 | // The credit for computing std goes to 231 | // https://derickbailey.com/2014/09/21/calculating-standard-deviation-with-array-map-and-array-reduce-in-javascript/ 232 | function standardDeviation(values) { 233 | const avg = average(values); 234 | 235 | const squareDiffs = values.map(function f(value) { 236 | const diff = value - avg; 237 | const sqrDiff = diff * diff; 238 | return sqrDiff; 239 | }); 240 | 241 | const stdDev = Math.sqrt(average(squareDiffs)); 242 | return stdDev; 243 | } 244 | 245 | const generateBenchmarks = () => { 246 | functions.forEach(group => { 247 | const results = group.functions.map(func => { 248 | // const t0 = performance.now(); 249 | const timing = []; 250 | for (let i = 0; i < NUM_OF_REPS; i++) { 251 | const t0 = performance.now(); 252 | func.execute(i); 253 | const t1 = performance.now(); 254 | timing.push(t1 - t0); 255 | } 256 | // compute the average 257 | const avg = average(timing); 258 | const std = standardDeviation(timing); 259 | 260 | return { 261 | functionName: func.name, 262 | description: func.description, 263 | timing: `${avg.toFixed( 264 | 2 265 | )} ± ${std.toFixed(2)}` 266 | }; 267 | }); 268 | document.getElementById('container').innerHTML += template( 269 | group.name, 270 | results 271 | ); 272 | }); 273 | }; 274 | 275 | generateBenchmarks(); 276 | 277 | const t01 = performance.now(); 278 | document.getElementById('total_time').innerHTML = `Benchmark runtime: ${( 279 | (t01 - t00) / 280 | 1000 281 | ).toFixed(2)} sec with ${NUM_OF_REPS} reps on each operation.`; 282 | -------------------------------------------------------------------------------- /ristretto255.benchmarks.min.js: -------------------------------------------------------------------------------- 1 | !function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";Uint8Array.prototype.fill||(Uint8Array.prototype.fill=Array.prototype.fill);const e=performance.now(),t=100,n=[],r=[],a=[],o=[],s=ristretto255.unsafe.point.alloc();for(let e=0;eristretto255.getRandom()},{name:"isValid",description:"Check if a value represents a valid element",execute:e=>ristretto255.isValid(r[e])},{name:"fromHash",description:"Hash to group: generate a group element from 64-element byte array, e.g. an output of SHA-512",execute:e=>ristretto255.fromHash(a[e])},{name:"add",description:"Add two group elements",execute:e=>ristretto255.add(r[e],r[(e+1)%t])},{name:"sub",description:"Subtract two group elements",execute:e=>ristretto255.sub(r[e],r[(e+1)%t])},{name:"scalarMultBase",description:"Multiply a generator of the group by a scalar",execute:e=>ristretto255.scalarMultBase(o[e])},{name:"scalarMult",description:"Multiply a group element by a scalar",execute:e=>ristretto255.scalarMult(o[e],r[e])}]},{name:"Scalar operations",functions:[{name:"scalar.getRandom",description:"Generate a random scalar",execute:()=>ristretto255.scalar.getRandom()},{name:"scalar.invert",description:"Invert a scalar",execute:e=>ristretto255.scalar.invert(o[e])},{name:"scalar.negate",description:"Negate a scalar",execute:e=>ristretto255.scalar.negate(o[e],o[e%t])},{name:"scalar.add",description:"Add two scalars",execute:e=>ristretto255.scalar.add(o[e],o[e%t])},{name:"scalar.sub",description:"Subtract two scalars",execute:e=>ristretto255.scalar.sub(o[e],o[e%t])},{name:"scalar.mul",description:"Multiply two scalars",execute:e=>ristretto255.scalar.mul(o[e],o[e%t])}]},{name:"Low-level unsafe functions (unless if used by a cryptographer)",functions:[{name:"unsafe.point.toBytes",description:"Serialize a curve25519 point to ristretto255 group element",execute:e=>ristretto255.unsafe.point.toBytes(n[e])},{name:"unsafe.point.fromBytes",description:"Deserialize a curve25519 point from ristretto255 group element",execute:e=>ristretto255.unsafe.point.fromBytes(s,r[e])},{name:"unsafe.point.getRandom",description:"Generate a random ristretto255 group element represented as curve25519 point",execute:()=>ristretto255.unsafe.point.getRandom()},{name:"unsafe.point.fromHash",description:"Generate a ristretto255 group element represented as curve25519 point from a 64 elements byte array such as an output of SHA512",execute:e=>ristretto255.unsafe.point.fromHash(a[e])},{name:"unsafe.point.add",description:"Add two curve25519 points",execute:e=>ristretto255.unsafe.point.add(n[e],n[e%t])},{name:"unsafe.point.sub",description:"Subtract two curve25519 points",execute:e=>ristretto255.unsafe.point.sub(n[e],n[e%t])},{name:"unsafe.point.scalarMultBase",description:"Multiply a curve25519's base point by a scalar",execute:e=>ristretto255.unsafe.point.scalarMultBase(n[e],o[e])},{name:"unsafe.point.scalarMult",description:"Multiply a curve25519's point by a scalar",execute:e=>ristretto255.unsafe.point.scalarMult(n[e],n[e%t],o[e])}]}].forEach((e=>{const n=e.functions.map((e=>{const n=[];for(let r=0;r ± ${a.toFixed(2)}`}}));document.getElementById("container").innerHTML+=((e,t)=>`\n

${e}

\n
\n \n \n \n \n \n \n ${t.map((e=>`\n \n \n \n \n \n `)).join("")}\n
Function nameTime in msComments
${e.functionName}${e.timing}${e.description}
\n
`)(e.name,n)}));const c=performance.now();document.getElementById("total_time").innerHTML=`Benchmark runtime: ${((c-e)/1e3).toFixed(2)} sec with 100 reps on each operation.`})); 2 | -------------------------------------------------------------------------------- /src/ristretto255.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | /** 9 | * @fileoverview ristretto255.js file exports arithmetic for scalars (mod L) and 10 | * for ristretto255 group elements 11 | * 12 | * * Scalars are stored as Uint8Array(32)s, and the operations implement simple 13 | * school-book arithmetic (mod L), where L is a prime and L = 2^252 + l, 14 | * with l = 27742317777372353535851937790883648493 = 3 * 610042537739 * 15 | * 15158679415041928064055629. The order of the base point (X, Y) is L. 16 | * The exported functions give a set of available arithmetic operations: 17 | * ristretto255.scalar.{getRandom, invert, negate, add, sub, mul} 18 | * 19 | * * ristretto255 group elements are stored as Uint8Array(32)s. 20 | * See https://ristretto.group/ for a reference on the ristretto255 group design. 21 | * The exported functions give a set of available arithmetic operations: 22 | * ristretto255.{getRandom, isValid, fromHash, add, sub, scalarMultBase, scalarMult} 23 | * 24 | * * Field elements are stored as Float64Array(16) they are used for coordinates of 25 | * elliptic curve (EC) points. The EC points hold internal representation of ristretto group elements. 26 | * Operations over the field elements are done modulo 2^255 - 19. 27 | * 28 | * * EC points are stored as an array of four field elements for extended projective coordinates. 29 | * 30 | * IMPORTANT NOTES 31 | * 32 | * * Little-endian encoding is used everywhere: the 0x0A0B0C0D 32-bit integer 33 | * will be stored as a Uint8Array([0D, 0C, 0B, 0A]) byte array. 34 | * 35 | * * Double 64 bits IEEE 754 - default format for a number in javascript. 36 | * An integer in the range -(2^53 - 1) and 2^53 - 1 can be stored precisely! 37 | * Number.isSafeInteger() can be used to check for that. 38 | * 39 | * * For binary operations, the number is implicitly converted to a signed 40 | * 32-bits integer, and numbers with more than 32 bits get their most significant 41 | * bits discarded. Bitshifts preserve the sign, for example: (-9) >> 2 gives -3. 42 | * 43 | * * A note on random number generation: 44 | * For most of the cryptographic protocols it is crucial to have a 45 | * good source of randomness. This code uses tweetnacl's nacl.randomBytes function 46 | * for generating random bytes. See this note: 47 | * https://github.com/dchest/tweetnacl-js#random-bytes-generation 48 | * for more information on the limitations and browser support for obtaining 49 | * these random values. 50 | */ 51 | 52 | import nacl from 'tweetnacl'; 53 | 54 | // polyfill for Uint8Array.slice 55 | if (!Uint8Array.prototype.slice) { 56 | // eslint-disable-next-line 57 | Object.defineProperty(Uint8Array.prototype, 'slice', { 58 | value(begin, end) { 59 | return new Uint8Array(Array.prototype.slice.call(this, begin, end)); 60 | } 61 | }); 62 | } 63 | 64 | const ristretto255 = {}; 65 | 66 | const { lowlevel } = nacl; 67 | 68 | const gf1 = lowlevel.gf([1]); 69 | 70 | function fe() { 71 | return [lowlevel.gf(), lowlevel.gf(), lowlevel.gf(), lowlevel.gf()]; 72 | } 73 | 74 | const basePoint = new Uint8Array([ 75 | 0xe2, 76 | 0xf2, 77 | 0xae, 78 | 0x0a, 79 | 0x6a, 80 | 0xbc, 81 | 0x4e, 82 | 0x71, 83 | 0xa8, 84 | 0x84, 85 | 0xa9, 86 | 0x61, 87 | 0xc5, 88 | 0x00, 89 | 0x51, 90 | 0x5f, 91 | 0x58, 92 | 0xe3, 93 | 0x0b, 94 | 0x6a, 95 | 0xa5, 96 | 0x82, 97 | 0xdd, 98 | 0x8d, 99 | 0xb6, 100 | 0xa6, 101 | 0x59, 102 | 0x45, 103 | 0xe0, 104 | 0x8d, 105 | 0x2d, 106 | 0x76 107 | ]); 108 | 109 | /* L - 2, this constant is used to compute the inverse */ 110 | const LSub2 = new Float64Array([ 111 | 0xeb, 112 | 0xd3, 113 | 0xf5, 114 | 0x5c, 115 | 0x1a, 116 | 0x63, 117 | 0x12, 118 | 0x58, 119 | 0xd6, 120 | 0x9c, 121 | 0xf7, 122 | 0xa2, 123 | 0xde, 124 | 0xf9, 125 | 0xde, 126 | 0x14, 127 | 0x0, 128 | 0x0, 129 | 0x0, 130 | 0x0, 131 | 0x0, 132 | 0x0, 133 | 0x0, 134 | 0x0, 135 | 0x0, 136 | 0x0, 137 | 0x0, 138 | 0x0, 139 | 0x0, 140 | 0x0, 141 | 0x0, 142 | 0x10 143 | ]); 144 | 145 | /** 146 | * Multiplication of two scalars: school-book multiplication 147 | * 148 | * @param {Uint8Array(32)} o output scalar: o := a * b mod L 149 | * @param {Uint8Array(32)} a input scalar 150 | * @param {Uint8Array(32)} b input scalar 151 | */ 152 | function MmodL(o, a, b) { 153 | const t = new Float64Array(64); 154 | 155 | // Simple "operand scanning" schoolbook multiplication in two nested loops. 156 | // Elements of the resulting t have the max number of bits represented 157 | // by this 64-elements vector: 158 | // [16, 17, 18, 18, 19, 19, 19, 19, 20, 20, 159 | // 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 160 | // 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 161 | // 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 162 | // 21, 21, 21, 21, 21, 21, 21, 20, 20, 20, 163 | // 20, 20, 20, 20, 20, 19, 19, 19, 19, 18, 164 | // 18, 17, 16, 0] 165 | for (let i = 0; i < 32; i++) { 166 | for (let j = 0; j < 32; j++) { 167 | t[i + j] += a[i] * b[j]; 168 | } 169 | } 170 | 171 | // Reduce t mod L and write to o 172 | lowlevel.modL(o, t); 173 | } 174 | 175 | /** 176 | * Squaring of a scalar 177 | * 178 | * @param {Uint8Array(32)} o output scalar: o := a^2 mod L 179 | * @param {Uint8Array(32)} a input scalar 180 | */ 181 | function SmodL(o, a) { 182 | MmodL(o, a, a); 183 | } 184 | 185 | /** 186 | * Computing the inverse of a scalar: 1/r mod L == r^(L-2) mod L. 187 | * Here we implement the simplest method: one-bit square-and-multiply 188 | * ladder that requires 251 squarings and 72 multiplications. 189 | * A more efficient approach for inversion (also implemented in 190 | * curve25519-dalek) requires only 250 squarings and 34 multiplications 191 | * (https://briansmith.org/ecc-inversion-addition-chains-01#curve25519_scalar_inversion), 192 | * but the code will be more lengthy. 193 | * 194 | * @param {Uint8Array(32)} invX output scalar: invX := 1/x mod L 195 | * @param {Uint8Array(32)} x input scalar 196 | */ 197 | function invmodL(invX, x) { 198 | for (let i = 0; i < 32; i++) invX[i] = x[i]; 199 | for (let i = 251; i >= 0; i--) { 200 | // squaring 201 | SmodL(invX, invX); 202 | // parsing the bits of the modulus 203 | // i & 0x07 == i % 8 204 | // i >> 3 == i / 8 (integer division) 205 | if (((LSub2[i >> 3] >> (i & 0x07)) & 1) !== 0) { 206 | // multiply by x 207 | MmodL(invX, invX, x); 208 | } 209 | } 210 | } 211 | 212 | /* Here a and d are the parameters of the curve: a = -1, d = -121665 / 121666 */ 213 | /* sqrt(-1) */ 214 | const sqrtm1 = lowlevel.gf([ 215 | 0xa0b0, 216 | 0x4a0e, 217 | 0x1b27, 218 | 0xc4ee, 219 | 0xe478, 220 | 0xad2f, 221 | 0x1806, 222 | 0x2f43, 223 | 0xd7a7, 224 | 0x3dfb, 225 | 0x0099, 226 | 0x2b4d, 227 | 0xdf0b, 228 | 0x4fc1, 229 | 0x2480, 230 | 0x2b83 231 | ]); 232 | /* sqrt(a * d - 1) */ 233 | const sqrtadm1 = lowlevel.gf([ 234 | 0x2e1b, 235 | 0x497b, 236 | 0xf6a0, 237 | 0x7e97, 238 | 0x54bd, 239 | 0x1b78, 240 | 0x8e0c, 241 | 0xaf9d, 242 | 0xd1fd, 243 | 0x31f5, 244 | 0xfcc9, 245 | 0x0f3c, 246 | 0x48ac, 247 | 0x2b83, 248 | 0x31bf, 249 | 0x3769 250 | ]); 251 | /* 1 / sqrt(a - d) */ 252 | const invsqrtamd = lowlevel.gf([ 253 | 0x40ea, 254 | 0x805d, 255 | 0xfdaa, 256 | 0x99c8, 257 | 0x72be, 258 | 0x5a41, 259 | 0x1617, 260 | 0x9d2f, 261 | 0xd840, 262 | 0xfe01, 263 | 0x7b91, 264 | 0x16c2, 265 | 0xfca2, 266 | 0xcfaf, 267 | 0x8905, 268 | 0x786c 269 | ]); 270 | /* (1-d^2) */ 271 | const onemsqd = lowlevel.gf([ 272 | 0xc176, 273 | 0x945f, 274 | 0x09c1, 275 | 0xe27c, 276 | 0x350f, 277 | 0xcd5e, 278 | 0xa138, 279 | 0x2c81, 280 | 0xdfe4, 281 | 0xbe70, 282 | 0xabdd, 283 | 0x9994, 284 | 0xe0d7, 285 | 0xb2b3, 286 | 0x72a8, 287 | 0x0290 288 | ]); 289 | /* (d-1)^2 */ 290 | const sqdmone = lowlevel.gf([ 291 | 0x4d20, 292 | 0x44ed, 293 | 0x5aaa, 294 | 0x31ad, 295 | 0x1999, 296 | 0xb01e, 297 | 0x4a2c, 298 | 0xd29e, 299 | 0x4eeb, 300 | 0x529b, 301 | 0xd32f, 302 | 0x4cdc, 303 | 0x2241, 304 | 0xf66c, 305 | 0xb37a, 306 | 0x5968 307 | ]); 308 | 309 | /** 310 | * Returns 1 iff the input field element (mod 2^255 - 19) is zero 311 | * 312 | * @param {Float64Array(16)} p input field element 313 | * @return {int} 1 iff p == 0 314 | */ 315 | function iszero25519(p) { 316 | // first pack the element which does a final reduction mod 2^255-19, 317 | // otherwise the element is stored mod 2^256-38 for convenience by nacl.js 318 | const s = new Uint8Array(32); 319 | lowlevel.pack25519(s, p); 320 | // do byte-by-byte comparison 321 | let res = 1; 322 | for (let i = 0; i < 32; i++) { 323 | res &= s[i] === 0; 324 | } 325 | return res; 326 | } 327 | 328 | /** 329 | * Conditional move of field elements (mod 2^255 - 19) based on b: 330 | * replace (p,q) with (q,q) if b == 1; 331 | * replace (p,q) with (p,q) if b == 0. 332 | * 333 | * @param {Float64Array(16)} p input/output field element: p == (b&1) * q + (1 - (b&1)) * p 334 | * @param {Float64Array(16)} q input field element 335 | * @param {int} b The integer in {0, 1} 336 | */ 337 | function cmov25519(p, q, b) { 338 | // if b = 1, c = 0xFFFFFFFF (32 bits); 339 | // else if b = 0, c = 0; 340 | // otherwise the behaviour is undefined 341 | let t; 342 | const c = -b; 343 | for (let i = 0; i < 16; i++) { 344 | t = p[i] ^ q[i]; 345 | t &= c; 346 | p[i] ^= t; 347 | } 348 | } 349 | 350 | /** 351 | * Returns true if the input field element (mod 2^255 - 19) is negative. 352 | * By convention the element is negative is its least significant bit is 1. 353 | * 354 | * @param {Float64Array(16)} f input field element 355 | * @return 1 if f is in {1,3,5,...,q-2}; 0 if f is in {0,2,4,...,q-1} 356 | */ 357 | function isneg25519(f) { 358 | const s = new Uint8Array(32); 359 | lowlevel.pack25519(s, f); 360 | return s[0] & 1; 361 | } 362 | 363 | /** 364 | * Computes a negation of the input field element (mod 2^255-19) 365 | * 366 | * @param {Float64Array(16)} h output field element: h := (-f) 367 | * @param {Float64Array(16)} f input field element 368 | */ 369 | function neg25519(h, f) { 370 | lowlevel.Z(h, lowlevel.gf(), f); 371 | } 372 | 373 | /** 374 | * Conditional negation of the field element (mod 2^255-19) f written into h based on b: 375 | * replace (h,f) with (-f,f) if b == 1; 376 | * replace (h,f) with (f,f) if b == 0. 377 | * 378 | * @param {Float64Array(16)} h output field element 379 | * @param {Float64Array(16)} f input field element 380 | * @param {int} b input integer in {0, 1} 381 | */ 382 | function cneg25519(h, f, b) { 383 | const negf = lowlevel.gf(); 384 | 385 | neg25519(negf, f); 386 | lowlevel.set25519(h, f); 387 | cmov25519(h, negf, b); 388 | } 389 | 390 | /** 391 | * Computes an absolute value of f field element (mod 2^255-19) and writes it into h: replace (h,f) with (|f|,f) 392 | * 393 | * @param {Float64Array(16)} h output field element 394 | * @param {Float64Array(16)} f input field element 395 | */ 396 | function abs25519(h, f) { 397 | cneg25519(h, f, isneg25519(f)); 398 | } 399 | 400 | /** 401 | * Computes a square root of (u/v) and writes it into x 402 | * See https://ristretto.group/formulas/invsqrt.html 403 | * 404 | * @param {[Float64Array(16), Float64Array(16), Float64Array(16), Float64Array(16)]} x output EC point: x will contain the sqrt(u/v) or sqrt(i * u/v) whichever exists 405 | * @param {[Float64Array(16), Float64Array(16), Float64Array(16), Float64Array(16)]} u input EC point 406 | * @param {[Float64Array(16), Float64Array(16), Float64Array(16), Float64Array(16)]} v input ECpoint 407 | * 408 | * @return {int} 1 iff u/v was square, 0 otherwise 409 | */ 410 | function sqrtRatioM1(x, u, v) { 411 | const v3 = lowlevel.gf(); 412 | const vxx = lowlevel.gf(); 413 | const mRootCheck = lowlevel.gf(); 414 | const pRootCheck = lowlevel.gf(); 415 | const fRootCheck = lowlevel.gf(); 416 | const xSqrtM1 = lowlevel.gf(); // gf elements 417 | 418 | lowlevel.S(v3, v); 419 | lowlevel.M(v3, v3, v); /* v3 = v^3 */ 420 | 421 | lowlevel.S(x, v3); 422 | lowlevel.M(x, x, v); 423 | lowlevel.M(x, x, u); /* x = uv^7 */ 424 | 425 | lowlevel.pow2523(x, x); /* x = (uv^7)^((q-5)/8), ((q-5)/8) = 2^252 - 3 */ 426 | lowlevel.M(x, x, v3); 427 | lowlevel.M(x, x, u); /* x = uv^3(uv^7)^((q-5)/8) */ 428 | 429 | lowlevel.S(vxx, x); 430 | lowlevel.M(vxx, vxx, v); /* vx^2 */ 431 | 432 | lowlevel.Z(mRootCheck, vxx, u); /* vx^2-u */ 433 | lowlevel.A(pRootCheck, vxx, u); /* vx^2+u */ 434 | lowlevel.M(fRootCheck, u, sqrtm1); /* u*sqrt(-1) */ 435 | lowlevel.A(fRootCheck, vxx, fRootCheck); /* vx^2+u*sqrt(-1) */ 436 | const hasMRoot = iszero25519(mRootCheck); /* hasMRoot = (vxx == u) */ 437 | const hasPRoot = iszero25519(pRootCheck); /* hasPRoot = (vxx == -u) */ 438 | const hasFRoot = iszero25519(fRootCheck); /* hasFRoot = (vxx = -u*sqrt(-1)) */ 439 | lowlevel.M(xSqrtM1, x, sqrtm1); /* x*sqrt(-1) */ 440 | 441 | cmov25519(x, xSqrtM1, hasPRoot | hasFRoot); 442 | abs25519(x, x); 443 | 444 | return hasMRoot | hasPRoot; 445 | } 446 | 447 | /** 448 | * Serializes elliptic-curve point into a ristretto string 449 | * See https://ristretto.group/formulas/encoding.html 450 | * 451 | * @param {[Float64Array(16), Float64Array(16), Float64Array(16), Float64Array(16)]} h input EC point 452 | * 453 | * @return {Uint8Array(32)} byte array - the result of the serialization 454 | */ 455 | function toBytes(h) { 456 | /* h.X = h[0], h.Y = h[1], h.Z = h[2], h.T = h[3] */ 457 | const den1 = lowlevel.gf(); 458 | const den2 = lowlevel.gf(); 459 | let denInv = lowlevel.gf(); 460 | const eden = lowlevel.gf(); 461 | const invSqrt = lowlevel.gf(); 462 | const ix = lowlevel.gf(); 463 | const iy = lowlevel.gf(); 464 | const sVar = lowlevel.gf(); 465 | const tZInv = lowlevel.gf(); 466 | const u1 = lowlevel.gf(); 467 | const u2 = lowlevel.gf(); 468 | const u1U2U2 = lowlevel.gf(); 469 | let xVar = lowlevel.gf(); 470 | let yVar = lowlevel.gf(); 471 | const xZInv = lowlevel.gf(); 472 | const zInv = lowlevel.gf(); 473 | const zmy = lowlevel.gf(); 474 | let rotate = 0; 475 | const s = new Uint8Array(32); 476 | 477 | lowlevel.A(u1, h[2], h[1]); /* u1 = Z+Y */ 478 | lowlevel.Z(zmy, h[2], h[1]); /* zmy = Z-Y */ 479 | lowlevel.M(u1, u1, zmy); /* u1 = (Z+Y)*(Z-Y) */ 480 | 481 | lowlevel.M(u2, h[0], h[1]); /* u2 = X*Y */ 482 | 483 | lowlevel.S(u1U2U2, u2); /* u1U2U2 = u2^2 */ 484 | lowlevel.M(u1U2U2, u1, u1U2U2); /* u1U2U2 = u1*u2^2 */ 485 | 486 | sqrtRatioM1(invSqrt, gf1, u1U2U2); 487 | 488 | lowlevel.M(den1, invSqrt, u1); /* den1 = invSqrt*u1 */ 489 | lowlevel.M(den2, invSqrt, u2); /* den2 = invSqrt*u2 */ 490 | lowlevel.M(zInv, den1, den2); /* z_inv = den1*den2 */ 491 | lowlevel.M(zInv, zInv, h[3]); /* z_inv = den1*den2*T */ 492 | 493 | lowlevel.M(ix, h[0], sqrtm1); /* ix = X*sqrt(-1) */ 494 | lowlevel.M(iy, h[1], sqrtm1); /* iy = Y*sqrt(-1) */ 495 | lowlevel.M(eden, den1, invsqrtamd); /* eden = den1*sqrt(a-d) */ 496 | 497 | lowlevel.M(tZInv, h[3], zInv); /* tZInv = T*z_inv */ 498 | rotate = isneg25519(tZInv); 499 | 500 | xVar = lowlevel.gf(h[0]); 501 | yVar = lowlevel.gf(h[1]); 502 | denInv = lowlevel.gf(den2); 503 | 504 | cmov25519(xVar, iy, rotate); 505 | cmov25519(yVar, ix, rotate); 506 | cmov25519(denInv, eden, rotate); 507 | 508 | lowlevel.M(xZInv, xVar, zInv); 509 | cneg25519(yVar, yVar, isneg25519(xZInv)); 510 | 511 | lowlevel.Z(sVar, h[2], yVar); 512 | lowlevel.M(sVar, denInv, sVar); 513 | abs25519(sVar, sVar); 514 | 515 | lowlevel.pack25519(s, sVar); 516 | return s; 517 | } 518 | 519 | /** 520 | * Check is the input byte array is a canonical encoding of a field element 521 | * by checking that the following holds: 522 | * a) s must be 32 bytes 523 | * b) s < p, where p = 2^255-19 524 | * c) s is nonnegative <=> (s[0] & 1) == 0 525 | * NB: A canonical ristretto255 group element is not guaranted to be valid (i.e. 526 | * fromBytes may still fail). Valid points are those for which fromBytes 527 | * succeeds. 528 | * 529 | * @param {Uint8Array(32)} s input byte array 530 | * 531 | * @return {int} 1 iff s represents a valid ristretto255 group element 532 | */ 533 | function isCanonical(s) { 534 | let c = (s[31] & 0x7f) ^ 0x7f; 535 | /* here c == 0 iff s[31] == 0x7f */ 536 | for (let i = 30; i > 0; i--) { 537 | c |= s[i] ^ 0xff; 538 | } 539 | /* here c == 0 iff s = 0x7f 0xff 0xff ... 0xff 0x** */ 540 | c = ((c | (0 >>> 0)) - 1) >> 8; 541 | /* here c & 1 == 1 iff s = 0x7f 0xff 0xff ... 0xff 0x** */ 542 | const d = (0xed - 1 - (s[0] | (0 >>> 0))) >> 8; 543 | /* here d & 1 == 1 iff s[0] >= 0xed */ 544 | /* here (c & d) & 1 == 1 iff 2^255-1 >= s >= 2^255-19 */ 545 | return 1 - ((((c & d) | s[0]) & 1) | ((s[31] & 0xff) >> 7)); 546 | } 547 | 548 | /** 549 | * Deserializes the byte array into an elliptic curve point. 550 | * See https://ristretto.group/formulas/decoding.html 551 | * 552 | * @param {[Float64Array(16), Float64Array(16), Float64Array(16), Float64Array(16)]} h output EC point 553 | * @param {Uint8Array(32)} s input byte array 554 | * @return {int} -1 on failure 555 | */ 556 | function fromBytes(h, s) { 557 | const invSqrt = lowlevel.gf(); 558 | const one = lowlevel.gf(); 559 | const sVar = lowlevel.gf(); 560 | const ss = lowlevel.gf(); 561 | const u1 = lowlevel.gf(); 562 | const u2 = lowlevel.gf(); 563 | const u1u1 = lowlevel.gf(); 564 | const u2u2 = lowlevel.gf(); 565 | const v = lowlevel.gf(); 566 | const vU2U2 = lowlevel.gf(); 567 | 568 | if (isCanonical(s) === 0) { 569 | return -1; 570 | } 571 | lowlevel.unpack25519(sVar, s); 572 | lowlevel.S(ss, sVar); /* ss = s^2 */ 573 | 574 | lowlevel.set25519(u1, gf1); /* u1 = 1 */ 575 | lowlevel.Z(u1, u1, ss); /* u1 = 1-ss */ 576 | lowlevel.S(u1u1, u1); /* u1u1 = u1^2 */ 577 | 578 | lowlevel.set25519(u2, gf1); /* u2 = 1 */ 579 | lowlevel.A(u2, u2, ss); /* u2 = 1+ss */ 580 | lowlevel.S(u2u2, u2); /* u2u2 = u2^2 */ 581 | 582 | lowlevel.M(v, lowlevel.D, u1u1); /* v = d*u1^2 */ 583 | neg25519(v, v); /* v = -d*u1^2 */ 584 | lowlevel.Z(v, v, u2u2); /* v = -(d*u1^2)-u2^2 */ 585 | 586 | lowlevel.M(vU2U2, v, u2u2); /* v_u2u2 = v*u2^2 */ 587 | 588 | lowlevel.set25519(one, gf1); /* one = 1 */ 589 | const wasSquare = sqrtRatioM1(invSqrt, one, vU2U2); 590 | lowlevel.M(h[0], invSqrt, u2); 591 | lowlevel.M(h[1], invSqrt, h[0]); 592 | lowlevel.M(h[1], h[1], v); 593 | 594 | lowlevel.M(h[0], h[0], sVar); 595 | lowlevel.A(h[0], h[0], h[0]); 596 | abs25519(h[0], h[0]); 597 | lowlevel.M(h[1], u1, h[1]); 598 | lowlevel.set25519(h[2], gf1); /* h->Z = 1 */ 599 | lowlevel.M(h[3], h[0], h[1]); 600 | 601 | return -((1 - wasSquare) | isneg25519(h[3]) | iszero25519(h[1])); 602 | } 603 | 604 | /** 605 | * Helper function for fromHash: implements Elligator 2 to map a 606 | * field element to a curve point. 607 | * See https://ristretto.group/formulas/elligator.html 608 | * Note that simpler methods based on rejection sampling are difficult to 609 | * implement in constant time. 610 | * 611 | * @param {[Float64Array(16), Float64Array(16), Float64Array(16), Float64Array(16)]} p output EC point 612 | * @param {Float64Array(16)} t input field element 613 | * @return none 614 | */ 615 | function elligator(p, t) { 616 | const c = lowlevel.gf(); 617 | const n = lowlevel.gf(); 618 | const one = lowlevel.gf(); 619 | const r = lowlevel.gf(); 620 | const rpd = lowlevel.gf(); 621 | const s = lowlevel.gf(); 622 | const sPrime = lowlevel.gf(); 623 | const ss = lowlevel.gf(); 624 | const u = lowlevel.gf(); 625 | const v = lowlevel.gf(); 626 | const w0 = lowlevel.gf(); 627 | const w1 = lowlevel.gf(); 628 | const w2 = lowlevel.gf(); 629 | const w3 = lowlevel.gf(); 630 | 631 | lowlevel.set25519(one, gf1); /* one = 1 */ 632 | lowlevel.S(r, t); /* r = t^2 */ 633 | lowlevel.M(r, sqrtm1, r); /* r = sqrt(-1)*t^2 */ 634 | lowlevel.A(u, r, one); /* u = r+1 = sqrt(-1)*t^2 + 1 */ 635 | lowlevel.M( 636 | u, 637 | u, 638 | onemsqd 639 | ); /* u = (r+1)*(1-d^2) = (sqrt(-1)*t^2 + 1) * (1-d^2) */ 640 | lowlevel.set25519(c, gf1); /* c = 1 */ 641 | neg25519(c, c); /* c = -1 */ 642 | lowlevel.A(rpd, r, lowlevel.D); /* rpd = r*d */ 643 | lowlevel.M(v, r, lowlevel.D); /* v = r*d */ 644 | lowlevel.Z(v, c, v); /* v = c-r*d */ 645 | lowlevel.M(v, v, rpd); /* v = (c-r*d)*(r+d) */ 646 | 647 | const wasntSquare = 1 - sqrtRatioM1(s, u, v); 648 | lowlevel.M(sPrime, s, t); 649 | abs25519(sPrime, sPrime); 650 | neg25519(sPrime, sPrime); /* s_prime = -|s*t| */ 651 | cmov25519(s, sPrime, wasntSquare); 652 | cmov25519(c, r, wasntSquare); 653 | 654 | lowlevel.Z(n, r, one); /* n = r-1 */ 655 | lowlevel.M(n, n, c); /* n = c*(r-1) */ 656 | lowlevel.M(n, n, sqdmone); /* n = c*(r-1)*(d-1)^2 */ 657 | lowlevel.Z(n, n, v); /* n = c*(r-1)*(d-1)^2-v */ 658 | 659 | lowlevel.A(w0, s, s); /* w0 = 2s */ 660 | lowlevel.M(w0, w0, v); /* w0 = 2s*v */ 661 | lowlevel.M(w1, n, sqrtadm1); /* w1 = n*sqrt(ad-1) */ 662 | lowlevel.S(ss, s); /* ss = s^2 */ 663 | lowlevel.Z(w2, one, ss); /* w2 = 1-s^2 */ 664 | lowlevel.A(w3, one, ss); /* w3 = 1+s^2 */ 665 | 666 | lowlevel.M(p[0], w0, w3); 667 | lowlevel.M(p[1], w2, w1); 668 | lowlevel.M(p[2], w1, w3); 669 | lowlevel.M(p[3], w0, w2); 670 | } 671 | 672 | /** 673 | * Hash to ristretto255 group with Elligator. 674 | * This function can be used anywhere where a random oracle is required by 675 | * calling it with a 512-bits random bit-string h 676 | * (can be a SHA-512 hash of a 256-bits randomness source or an HKDF if 677 | * instantiated from a low-entropy message). 678 | * See https://ristretto.group/formulas/elligator.html 679 | * Note: for h of length 256 bits and p = 2^255-19, the value "h mod p" 680 | * has a slightly larger probability of being in [0, 37], 681 | * than a uniformly random element mod p. 682 | * To get a bias of at most 1/2^128, h should have at 683 | * least ceil(log2(p)) + 128 bits = ceil((255 + 128)/8)*8 = 384 bits. 684 | * For a small-length message HKDF can be used to expand and 685 | * extract the element h. 686 | * (see https://www.ietf.org/id/draft-irtf-cfrg-hash-to-curve-05.txt) 687 | * The reason two base field points are being produced, then hashed and 688 | * added is the line of work that shows that f(h(m)) is not a random oracle 689 | * (where f is map_to_curve and h is a secure hash function like SHA) 690 | * because f is not surjective necessarily, therefore it might be easy to 691 | * distinguish f(h(m)) from a truly random point. 692 | * 693 | * See [BCIMRT10] for rational behind hashing to two points and adding them up: 694 | * Brier, E., Coron, J., Icart, T., Madore, D., Randriam, H., 695 | * and M. Tibouchi, "Efficient Indifferentiable Hashing into 696 | * Ordinary Elliptic Curves", In Advances in Cryptology - 697 | * CRYPTO 2010, pages 237-254, 698 | * 699 | * 700 | * See [FFSTV13] and [TK17] for the improved analysis over the previous paper: 701 | * [FFSTV13] Farashahi, R., Fouque, P., Shparlinski, I., Tibouch, M., 702 | * and J. Voloch, "Indifferentiable deterministic hashing to 703 | * elliptic and hyperelliptic curves", In Math. Comp. vol 82, 704 | * pages 491-512, 2013, 705 | * . 706 | * [TK17] Tibouchi, M. and T. Kim, "Improved elliptic curve hashing 707 | * and point representation", In Designs, Codes, and 708 | * Cryptography, vol 82, pages 161-177, 709 | * . 710 | * 711 | * @param {Uint8Array(64)} h input 64 elements byte array such as the output of SHA512 712 | * @return {[Float64Array(16), Float64Array(16), Float64Array(16), Float64Array(16)]} EC point 713 | */ 714 | function pointFromHash(h) { 715 | const r0 = lowlevel.gf(); 716 | const r1 = lowlevel.gf(); 717 | const p0 = fe(); 718 | const p1 = fe(); 719 | 720 | lowlevel.unpack25519(r0, h.slice(0, 32)); 721 | lowlevel.unpack25519(r1, h.slice(32, 64)); 722 | elligator(p0, r0); 723 | elligator(p1, r1); 724 | 725 | lowlevel.add(p0, p1); 726 | return p0; 727 | } 728 | 729 | /** 730 | * Multiply base point by scalar 731 | * 732 | * @param {Uint8Array(32)} n input scalar 733 | * @return {Uint8Array(32)} ristretto255 group element 734 | */ 735 | function scalarMultBase(n) { 736 | const Q = fe(); 737 | lowlevel.scalarbase(Q, n); // Q = BASE * n 738 | return toBytes(Q); 739 | } 740 | 741 | /** 742 | * Multiply given point by scalar 743 | * 744 | * @param {Uint8Array(32)} n input scalar 745 | * @param {Uint8Array(32)} p input ristretto255 group element 746 | * @return {Uint8Array(32)} ristretto255 group element (p * n) 747 | */ 748 | function scalarMult(n, p) { 749 | const P = fe(); 750 | const Q = fe(); 751 | 752 | if (fromBytes(P, p) !== 0) { 753 | throw new Error('Invalid argument'); 754 | } 755 | lowlevel.scalarmult(Q, P, n); // Q = P * n 756 | return toBytes(Q); 757 | } 758 | 759 | /** 760 | * Checking if the input array of bytes represents a serialization of a 761 | * valid ristretto255 group element 762 | * 763 | * @param {Uint8Array(32)} p input byte array 764 | * @return {Boolean} true on success, false on failure 765 | */ 766 | function isValid(p) { 767 | const P = fe(); 768 | if (fromBytes(P, p) === -1) { 769 | return false; 770 | } 771 | return true; 772 | } 773 | 774 | /** 775 | * Helper private deserialization function reused in add(p, q) and sub(p, q) 776 | * 777 | * @param {Uint8Array(32)} p input ristretto255 group element 778 | * @param {Uint8Array(32)} q input ristretto255 group element 779 | * @return {[Uint8Array(32), Uint8Array(32)]} two ristretto255 group elements 780 | */ 781 | function deserializePandQ(p, q) { 782 | const P = fe(); 783 | const Q = fe(); 784 | 785 | if (fromBytes(P, p) === -1) { 786 | throw new Error('Invalid argument'); 787 | } 788 | if (fromBytes(Q, q) === -1) { 789 | throw new Error('Invalid argument'); 790 | } 791 | return [P, Q]; 792 | } 793 | 794 | /** 795 | * Adding two ristretto255 group elements 796 | * 797 | * @param {Uint8Array(32)} p input ristretto255 group element 798 | * @param {Uint8Array(32)} q input ristretto255 group element 799 | * @return {Uint8Array(32)} ristretto255 group element (p+q) 800 | */ 801 | function add(p, q) { 802 | const [P, Q] = deserializePandQ(p, q); 803 | lowlevel.add(P, Q); // P = P + Q 804 | return toBytes(P); 805 | } 806 | 807 | /** 808 | * Subtracting two EC points - this function is symmetric to lowlevel.add 809 | * 810 | * @param {[Float64Array(16), Float64Array(16), Float64Array(16), Float64Array(16)]} P input/output EC point: P := P - Q 811 | * @param {[Float64Array(16), Float64Array(16), Float64Array(16), Float64Array(16)]} Q input EC point 812 | * @return {Uint8Array(32)} ristretto255 group element (p+q) 813 | */ 814 | function lowlevelSub(P, Q) { 815 | // negate Q: -(x,y,z,t) = (-x, y, z, -t) 816 | const negQ3 = lowlevel.gf(); 817 | neg25519(negQ3, Q[3]); 818 | const negQ0 = lowlevel.gf(); 819 | neg25519(negQ0, Q[0]); 820 | const negQ = [negQ0, Q[1], Q[2], negQ3]; 821 | lowlevel.add(P, negQ); 822 | } 823 | 824 | /** 825 | * Subtracting two ristretto255 group elements 826 | * 827 | * @param {Uint8Array(32)} p input ristretto255 group element 828 | * @param {Uint8Array(32)} q input ristretto255 group element 829 | * @return {Uint8Array(32)} ristretto255 group element (p-q) 830 | */ 831 | function sub(p, q) { 832 | const [P, Q] = deserializePandQ(p, q); 833 | lowlevelSub(P, Q); // P = P - Q 834 | return toBytes(P); 835 | } 836 | 837 | /** 838 | * Generate a random EC point that will serialize to a valid ristretto255 group element 839 | * 840 | * @return {[Float64Array(16), Float64Array(16), Float64Array(16), Float64Array(16)]} EC point 841 | */ 842 | function getRandomPoint() { 843 | // create a random hash string 844 | const h = nacl.randomBytes(64); 845 | // let h = new Uint8Array(64); 846 | // lowlevel.randombytes(h, 64); 847 | return pointFromHash(h); 848 | } 849 | 850 | /** 851 | * Create and serialize a ristretto255 group element from a hash byte array 852 | * 853 | * @param {Uint8Array(64)} h input 64 elements byte array such as the output of SHA512 854 | * @return {Uint8Array(32)} ristretto255 group element 855 | */ 856 | function fromHash(h) { 857 | return toBytes(pointFromHash(h)); 858 | } 859 | 860 | /** 861 | * Generate a random ristretto255 group element 862 | * 863 | * @param none 864 | * @return {Uint8Array(32)} ristretto255 group element 865 | */ 866 | function getRandom() { 867 | return toBytes(getRandomPoint()); 868 | } 869 | 870 | /** 871 | * Generating a random scalar via rejection sampling 872 | * 873 | * @return {Uint8Array(32)} scalar 874 | */ 875 | function getRandomScalar() { 876 | let r = new Uint8Array(32); 877 | let c = 0; 878 | // rejection sampling loop with constant-time body 879 | do { 880 | r = nacl.randomBytes(32); 881 | // lowlevel.randombytes(r, 32); 882 | r[31] &= 0x1f; 883 | 884 | // constant-time check for r < L, if so break and return r 885 | let i = 32; 886 | let n = 1; 887 | do { 888 | i--; 889 | c |= ((r[i] - lowlevel.L[i]) >> 8) & n; 890 | n &= ((r[i] ^ lowlevel.L[i]) - 1) >> 8; 891 | } while (i !== 0); 892 | } while (c === 0); 893 | 894 | return r; 895 | } 896 | 897 | /** 898 | * Inverting a scalar 899 | * 900 | * @param {Uint8Array(32)} s input scalar 901 | * @return {Uint8Array(32)} output inverted scalar 1/s 902 | */ 903 | function invertScalar(s) { 904 | const res = new Uint8Array(32); 905 | invmodL(res, s); 906 | return res; 907 | } 908 | 909 | /** 910 | * Negating a scalar 911 | * 912 | * @param {Uint8Array(32)} s input scalar 913 | * @return {Uint8Array(32)} output scalar: (-s) 914 | */ 915 | function negateScalar(s) { 916 | const negS = new Float64Array(64); 917 | // neg_s := L - s 918 | for (let i = 0; i < 32; i++) { 919 | negS[i] = -s[i]; 920 | } 921 | const o = new Uint8Array(32); 922 | lowlevel.modL(o, negS); 923 | return o; 924 | } 925 | 926 | /** 927 | * Adding two scalars 928 | * 929 | * @param {Uint8Array(32)} x input scalar 930 | * @param {Uint8Array(32)} y input scalar 931 | * @return {Uint8Array(32)} output scalar: x + y 932 | */ 933 | function addScalar(x, y) { 934 | const z = new Float64Array(64); 935 | for (let i = 0; i < 32; i++) { 936 | z[i] = x[i] + y[i]; 937 | } 938 | const o = new Uint8Array(32); 939 | lowlevel.modL(o, z); 940 | return o; 941 | } 942 | 943 | /** 944 | * Subtracting two scalars 945 | * 946 | * @param {Uint8Array(32)} x input scalar 947 | * @param {Uint8Array(32)} y input scalar 948 | * @return {Uint8Array(32)} output scalar: x - y 949 | */ 950 | function subScalar(x, y) { 951 | return addScalar(x, negateScalar(y)); 952 | } 953 | 954 | /** 955 | * Multiplying two scalars 956 | * 957 | * @param {Uint8Array(32)} x input scalar 958 | * @param {Uint8Array(32)} y input scalar 959 | * @return {Uint8Array(32)} output scalar: x * y 960 | */ 961 | function mulScalar(x, y) { 962 | const res = new Uint8Array(32); 963 | MmodL(res, x, y); 964 | return res; 965 | } 966 | 967 | /** 968 | * EXPORTS 969 | * High-level ristretto255 functions operate on ristretto255 group elements stored in Uint8Array(32). 970 | * Scalar functions (invertScalar, addScalar, etc.) operate on scalars mod L stored in Uint8Array(32). 971 | */ 972 | ristretto255.scalarMultBase = scalarMultBase; 973 | ristretto255.scalarMult = scalarMult; 974 | ristretto255.isValid = isValid; 975 | ristretto255.add = add; 976 | ristretto255.sub = sub; 977 | ristretto255.fromHash = fromHash; 978 | ristretto255.getRandom = getRandom; 979 | ristretto255.basePoint = basePoint; 980 | 981 | ristretto255.scalar = {}; 982 | ristretto255.scalar.getRandom = getRandomScalar; 983 | ristretto255.scalar.invert = invertScalar; 984 | ristretto255.scalar.negate = negateScalar; 985 | ristretto255.scalar.add = addScalar; 986 | ristretto255.scalar.sub = subScalar; 987 | ristretto255.scalar.mul = mulScalar; 988 | 989 | /* Unsafe functions are exposed mainly for benchmarking and testing purposes */ 990 | /* Exercise care if using these in any production environments */ 991 | ristretto255.unsafe = {}; 992 | ristretto255.unsafe.point = {}; 993 | ristretto255.unsafe.point.toBytes = toBytes; 994 | ristretto255.unsafe.point.fromBytes = fromBytes; 995 | ristretto255.unsafe.point.fromHash = pointFromHash; 996 | ristretto255.unsafe.point.sub = lowlevelSub; 997 | ristretto255.unsafe.point.add = lowlevel.add; 998 | ristretto255.unsafe.point.scalarMultBase = lowlevel.scalarbase; 999 | ristretto255.unsafe.point.scalarMult = lowlevel.scalarmult; 1000 | ristretto255.unsafe.point.getRandom = getRandomPoint; 1001 | ristretto255.unsafe.point.alloc = fe; 1002 | 1003 | ristretto255.unsafe.constants = {}; 1004 | ristretto255.unsafe.constants.LSub2 = LSub2; 1005 | ristretto255.unsafe.constants.sqrtm1 = sqrtm1; 1006 | ristretto255.unsafe.constants.sqrtadm1 = sqrtadm1; 1007 | ristretto255.unsafe.constants.invsqrtamd = invsqrtamd; 1008 | ristretto255.unsafe.constants.onemsqd = onemsqd; 1009 | ristretto255.unsafe.constants.sqdmone = sqdmone; 1010 | 1011 | export default ristretto255; 1012 | -------------------------------------------------------------------------------- /src/ristretto255.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import nacl from 'tweetnacl'; 9 | import testDalekScalars from '../data/scalars.data'; 10 | import testDalek from '../data/ristretto.data'; 11 | 12 | const ristretto255 = require('./ristretto255').default; 13 | 14 | const { lowlevel } = nacl; 15 | 16 | /** 17 | * Helper functions for tests 18 | */ 19 | 20 | // eslint-disable-next-line no-restricted-globals 21 | let crypto = typeof self !== 'undefined' ? self.crypto || self.msCrypto : null; 22 | if ((!crypto || !crypto.getRandomValues) && typeof require !== 'undefined') { 23 | // eslint-disable-next-line global-require 24 | crypto = require('crypto'); 25 | } 26 | 27 | /** 28 | * Function scalarmodL creates an array of 32 element Float64Array(32) for representing points mod L 29 | */ 30 | function scalarmodL(init) { 31 | let i; 32 | const r = new Float64Array(32); 33 | if (init) for (i = 0; i < init.length; i++) r[i] = init[i]; 34 | return r; 35 | } 36 | 37 | /** 38 | * Constant scalars of type Float64Array(32) 39 | */ 40 | const scalarmodL1 = scalarmodL([1]); 41 | const scalarmodL0 = scalarmodL([0]); 42 | 43 | /* Helper functions */ 44 | /* Padding the string s to size with leading zeroes, returns resulting string */ 45 | function pad(s, size) { 46 | let res = `${s}`; 47 | while (res.length < size) res = `0${res}`; 48 | return res; 49 | } 50 | 51 | /* Takes in a hex string and returns a parsed byte array */ 52 | function hexToByteArray(hexString) { 53 | const result = []; 54 | for (let i = 0; i < hexString.length; i += 2) { 55 | result.push(parseInt(hexString.substr(i, 2), 16)); 56 | } 57 | return result; 58 | } 59 | 60 | /* Takes in a byte array and returns a hex string */ 61 | function byteArrayToHex(byteArray) { 62 | return Array.from(byteArray, function f(byte) { 63 | return pad((byte & 0xff).toString(16), 2); 64 | }).join(''); 65 | } 66 | 67 | /** 68 | * Testing if the input byte array is all zeroes 69 | * 70 | * @param {Uint8Array} arr byte array 71 | * @return 1 iff arr is all zeroes 72 | */ 73 | function testIsZeroArray(arr) { 74 | let i; 75 | let d = 0; 76 | for (i = 0; i < arr.length; i++) { 77 | d |= arr[i]; 78 | } 79 | return 1 & ((d - 1) >> 8); 80 | } 81 | 82 | /** 83 | * Testing if the two scalars are the same 84 | * 85 | * @param {Float64Array(32)} x 86 | * @param {Float64Array(32)} y 87 | * @return 1 iff x == y 88 | */ 89 | function testScalarEq(x, y) { 90 | let i; 91 | for (i = 0; i < 32; i++) { 92 | if (x[i] !== y[i]) { 93 | return 0; 94 | } 95 | } 96 | return 1; 97 | } 98 | 99 | /** * 100 | *** Tests 101 | ** */ 102 | 103 | // Testing scalar operations against test vectors 104 | test('Scalars: add, sub, mul, invert, negate', () => { 105 | for (let i = 0; i < testDalekScalars.length; i++) { 106 | const a = testDalekScalars[i][0]; 107 | const b = testDalekScalars[i][1]; 108 | 109 | let resExp = testDalekScalars[i][2]; 110 | let res = ristretto255.scalar.add(a, b); 111 | expect(resExp.toString()).toBe(res.toString()); 112 | 113 | resExp = testDalekScalars[i][3]; 114 | res = ristretto255.scalar.sub(a, b); 115 | expect(resExp.toString()).toBe(res.toString()); 116 | 117 | resExp = testDalekScalars[i][4]; 118 | res = ristretto255.scalar.mul(a, b); 119 | expect(resExp.toString()).toBe(res.toString()); 120 | 121 | resExp = testDalekScalars[i][5]; 122 | res = ristretto255.scalar.invert(a); 123 | expect(resExp.toString()).toBe(res.toString()); 124 | 125 | resExp = testDalekScalars[i][6]; 126 | res = ristretto255.scalar.invert(b); 127 | expect(resExp.toString()).toBe(res.toString()); 128 | 129 | resExp = testDalekScalars[i][7]; 130 | res = ristretto255.scalar.negate(a); 131 | expect(resExp.toString()).toBe(res.toString()); 132 | 133 | resExp = testDalekScalars[i][8]; 134 | res = ristretto255.scalar.negate(b); 135 | expect(resExp.toString()).toBe(res.toString()); 136 | } 137 | }); 138 | 139 | // Testing ristretto operations against test vectors 140 | test('Ristretto: add, sub, scalarMultBase, scalarMult, fromHash, isValid', () => { 141 | for (let i = 0; i < testDalek.ristretto_ops.length; i++) { 142 | const a = testDalek.ristretto_ops[i][0]; 143 | const b = testDalek.ristretto_ops[i][1]; 144 | 145 | let resExp = testDalek.ristretto_ops[i][2]; 146 | let res = ristretto255.add(a, b); 147 | expect(resExp.toString()).toBe(res.toString()); 148 | 149 | resExp = testDalek.ristretto_ops[i][3]; 150 | res = ristretto255.sub(a, b); 151 | expect(resExp.toString()).toBe(res.toString()); 152 | 153 | let s = testDalek.ristretto_ops[i][4]; 154 | 155 | resExp = testDalek.ristretto_ops[i][5]; 156 | res = ristretto255.scalarMultBase(s); 157 | expect(resExp.toString()).toBe(res.toString()); 158 | 159 | resExp = testDalek.ristretto_ops[i][6]; 160 | res = ristretto255.scalarMult(s, a); 161 | expect(resExp.toString()).toBe(res.toString()); 162 | 163 | resExp = testDalek.ristretto_ops[i][7]; 164 | res = ristretto255.scalarMult(s, b); 165 | expect(resExp.toString()).toBe(res.toString()); 166 | 167 | s = testDalek.ristretto_ops[i][8]; 168 | resExp = testDalek.ristretto_ops[i][9]; 169 | res = ristretto255.fromHash(s); 170 | expect(resExp.toString()).toBe(res.toString()); 171 | } 172 | 173 | for (let i = 1; i < testDalek.ristretto_valid_or_not.length; i++) { 174 | const a = testDalek.ristretto_valid_or_not[i][0]; 175 | const resExp = testDalek.ristretto_valid_or_not[i][1]; 176 | const res = ristretto255.isValid(a); 177 | expect(resExp.toString()).toBe(res.toString()); 178 | } 179 | }); 180 | 181 | const FUZZY_TESTS_ITERATIONS_NUMBER = 5; 182 | 183 | /* Checking for ristretto test vectors from https://ristretto.group/test_vectors/ristretto255.html */ 184 | const encodingsOfSmallMultiples = [ 185 | // This is the identity point 186 | '0000000000000000000000000000000000000000000000000000000000000000', 187 | // This is the basepoint 188 | 'e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76', 189 | // These are small multiples of the basepoint 190 | '6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919', 191 | '94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259', 192 | 'da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57', 193 | 'e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e', 194 | 'f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403', 195 | '44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d', 196 | '903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c', 197 | '02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031', 198 | '20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f', 199 | 'bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42', 200 | 'e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460', 201 | 'aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f', 202 | '46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e', 203 | 'e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e' 204 | ]; 205 | 206 | const P = ristretto255.unsafe.point.alloc(); 207 | 208 | test('Ristretto official: Checking encodings of small multiples', () => { 209 | for (let i = 0; i < 16; i++) { 210 | lowlevel.scalarbase(P, lowlevel.gf([i])); 211 | const res = byteArrayToHex(ristretto255.unsafe.point.toBytes(P)); 212 | expect(res).toBe(encodingsOfSmallMultiples[i]); 213 | } 214 | }); 215 | 216 | const badEncodings = [ 217 | // These are all bad because they're non-canonical field encodings. 218 | '00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 219 | 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', 220 | 'f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', 221 | 'edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', 222 | // These are all bad because they're negative field elements. 223 | '0100000000000000000000000000000000000000000000000000000000000000', 224 | '01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f', 225 | 'ed57ffd8c914fb201471d1c3d245ce3c746fcbe63a3679d51b6a516ebebe0e20', 226 | 'c34c4e1826e5d403b78e246e88aa051c36ccf0aafebffe137d148a2bf9104562', 227 | 'c940e5a4404157cfb1628b108db051a8d439e1a421394ec4ebccb9ec92a8ac78', 228 | '47cfc5497c53dc8e61c91d17fd626ffb1c49e2bca94eed052281b510b1117a24', 229 | 'f1c6165d33367351b0da8f6e4511010c68174a03b6581212c71c0e1d026c3c72', 230 | '87260f7a2f12495118360f02c26a470f450dadf34a413d21042b43b9d93e1309', 231 | // These are all bad because they give a nonsquare x^2. 232 | '26948d35ca62e643e26a83177332e6b6afeb9d08e4268b650f1f5bbd8d81d371', 233 | '4eac077a713c57b4f4397629a4145982c661f48044dd3f96427d40b147d9742f', 234 | 'de6a7b00deadc788eb6b6c8d20c0ae96c2f2019078fa604fee5b87d6e989ad7b', 235 | 'bcab477be20861e01e4a0e295284146a510150d9817763caf1a6f4b422d67042', 236 | '2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08', 237 | 'f4a9e534fc0d216c44b218fa0c42d99635a0127ee2e53c712f70609649fdff22', 238 | '8268436f8c4126196cf64b3c7ddbda90746a378625f9813dd9b8457077256731', 239 | '2810e5cbc2cc4d4eece54f61c6f69758e289aa7ab440b3cbeaa21995c2f4232b', 240 | // These are all bad because they give a negative xy value. 241 | '3eb858e78f5a7254d8c9731174a94f76755fd3941c0ac93735c07ba14579630e', 242 | 'a45fdc55c76448c049a1ab33f17023edfb2be3581e9c7aade8a6125215e04220', 243 | 'd483fe813c6ba647ebbfd3ec41adca1c6130c2beeee9d9bf065c8d151c5f396e', 244 | '8a2e1d30050198c65a54483123960ccc38aef6848e1ec8f5f780e8523769ba32', 245 | '32888462f8b486c68ad7dd9610be5192bbeaf3b443951ac1a8118419d9fa097b', 246 | '227142501b9d4355ccba290404bde41575b037693cef1f438c47f8fbf35d1165', 247 | '5c37cc491da847cfeb9281d407efc41e15144c876e0170b499a96a22ed31e01e', 248 | '445425117cb8c90edcbc7c1cc0e74f747f2c1efa5630a967c64f287792a48a4b', 249 | // This is s = -1, which causes y = 0. 250 | 'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f' 251 | ]; 252 | 253 | /* Testing for bad encodings */ 254 | test('Ristretto official: Checking bad encodings', () => { 255 | for (let i = 0; i < badEncodings.length; i++) { 256 | const res = ristretto255.unsafe.point.fromBytes( 257 | P, 258 | hexToByteArray(badEncodings[i]) 259 | ); 260 | expect(res).toBe(-1); 261 | } 262 | }); 263 | 264 | /* Testing for good encodings: using the small multiples of the base point */ 265 | test('Ristretto official: Checking good encodings', () => { 266 | for (let i = 0; i < encodingsOfSmallMultiples.length; i++) { 267 | const res = ristretto255.unsafe.point.fromBytes( 268 | P, 269 | hexToByteArray(encodingsOfSmallMultiples[i]) 270 | ); 271 | expect(res).not.toBe(-1); 272 | } 273 | }); 274 | 275 | const labels = [ 276 | 'Ristretto is traditionally a short shot of espresso coffee', 277 | 'made with the normal amount of ground coffee but extracted with', 278 | 'about half the amount of water in the same amount of time', 279 | 'by using a finer grind.', 280 | 'This produces a concentrated shot of coffee per volume.', 281 | 'Just pulling a normal shot short will produce a weaker shot', 282 | 'and is not a Ristretto as some believe.' 283 | ]; 284 | 285 | const intermediateHash = [ 286 | '5d1be09e3d0c82fc538112490e35701979d99e06ca3e2b5b54bffe8b4dc772c14d98b696a1bbfb5ca32c436cc61c16563790306c79eaca7705668b47dffe5bb6', 287 | 'f116b34b8f17ceb56e8732a60d913dd10cce47a6d53bee9204be8b44f6678b270102a56902e2488c46120e9276cfe54638286b9e4b3cdb470b542d46c2068d38', 288 | '8422e1bbdaab52938b81fd602effb6f89110e1e57208ad12d9ad767e2e25510c27140775f9337088b982d83d7fcf0b2fa1edffe51952cbe7365e95c86eaf325c', 289 | 'ac22415129b61427bf464e17baee8db65940c233b98afce8d17c57beeb7876c2150d15af1cb1fb824bbd14955f2b57d08d388aab431a391cfc33d5bafb5dbbaf', 290 | '165d697a1ef3d5cf3c38565beefcf88c0f282b8e7dbd28544c483432f1cec7675debea8ebb4e5fe7d6f6e5db15f15587ac4d4d4a1de7191e0c1ca6664abcc413', 291 | 'a836e6c9a9ca9f1e8d486273ad56a78c70cf18f0ce10abb1c7172ddd605d7fd2979854f47ae1ccf204a33102095b4200e5befc0465accc263175485f0e17ea5c', 292 | '2cdc11eaeb95daf01189417cdddbf95952993aa9cb9c640eb5058d09702c74622c9965a697a3b345ec24ee56335b556e677b30e6f90ac77d781064f866a3c982' 293 | ]; 294 | 295 | const encodedHashToPoints = [ 296 | '3066f82a1a747d45120d1740f14358531a8f04bbffe6a819f86dfe50f44a0a46', 297 | 'f26e5b6f7d362d2d2a94c5d0e7602cb4773c95a2e5c31a64f133189fa76ed61b', 298 | '006ccd2a9e6867e6a2c5cea83d3302cc9de128dd2a9a57dd8ee7b9d7ffe02826', 299 | 'f8f0c87cf237953c5890aec3998169005dae3eca1fbb04548c635953c817f92a', 300 | 'ae81e7dedf20a497e10c304a765c1767a42d6e06029758d2d7e8ef7cc4c41179', 301 | 'e2705652ff9f5e44d3e841bf1c251cf7dddb77d140870d1ab2ed64f1a9ce8628', 302 | '80bd07262511cdde4863f8a7434cef696750681cb9510eea557088f76d9e5065' 303 | ]; 304 | 305 | test('Ristretto official: Checking hash-to-point', () => { 306 | for (let i = 0; i < 7; i++) { 307 | const h = crypto 308 | .createHash('sha512') 309 | .update(labels[i]) 310 | .digest(); 311 | expect(byteArrayToHex(h)).toBe(intermediateHash[i]); 312 | const res = byteArrayToHex( 313 | ristretto255.unsafe.point.toBytes(ristretto255.unsafe.point.fromHash(h)) 314 | ); 315 | expect(res).toBe(encodedHashToPoints[i]); 316 | } 317 | }); 318 | 319 | // Porting libsodium test tv3 https://github.com/jedisct1/libsodium/blob/master/test/default/core_ristretto255.c#L110 320 | test('Fuzzy checking ristretto ops (libsodium tv3)', () => { 321 | for (let i = 0; i < FUZZY_TESTS_ITERATIONS_NUMBER; i++) { 322 | // s := random_scalar * BASE 323 | const r = ristretto255.scalar.getRandom(); 324 | let s = ristretto255.scalarMultBase(r); 325 | // test s is valid 326 | expect(ristretto255.isValid(s)).toBe(true); 327 | 328 | // s := random 329 | s = ristretto255.getRandom(); 330 | // test s is valid 331 | expect(ristretto255.isValid(s)).toBe(true); 332 | 333 | // s := s * L 334 | s = ristretto255.scalarMult(lowlevel.L, s); 335 | // test s == 0 336 | expect(testIsZeroArray(s)).toBe(1); 337 | 338 | // s := from hash h 339 | let h = new Uint8Array(64); 340 | // let h = crypto.randomBytes(64); 341 | h = nacl.randomBytes(64); 342 | s = ristretto255.fromHash(h); 343 | // test s is valid 344 | expect(ristretto255.isValid(s)).toBe(true); 345 | 346 | // s := s * L 347 | s = ristretto255.scalarMult(lowlevel.L, s); 348 | // test s == 0 349 | expect(testIsZeroArray(s)).toBe(1); 350 | 351 | // s2 := s * r 352 | let s2 = ristretto255.scalarMult(r, s); 353 | // test s2 is valid 354 | expect(ristretto255.isValid(s2)).toBe(true); 355 | 356 | // sVar := s2 * (1/r) 357 | const rInv = ristretto255.scalar.invert(r); 358 | let sVar = ristretto255.scalarMult(rInv, s2); 359 | // test sVar is valid 360 | expect(ristretto255.isValid(sVar)).toBe(true); 361 | 362 | // test sVar == s 363 | // both sVar and s are of type Uint8Array(32) 364 | for (let j = 0; j < 32; j++) { 365 | expect(sVar[j]).toBe(s[j]); 366 | } 367 | 368 | // s2 := s2 * L 369 | s2 = ristretto255.scalarMult(lowlevel.L, s2); 370 | // test s2 == 0 371 | expect(testIsZeroArray(s2)).toBe(1); 372 | 373 | // s2 := s + s 374 | s2 = ristretto255.add(s, sVar); 375 | // test s2 is valid 376 | expect(ristretto255.isValid(s2)).toBe(true); 377 | // s2 := s2 - s 378 | s2 = ristretto255.sub(s2, sVar); 379 | // test s2 is valid 380 | expect(ristretto255.isValid(s2)).toBe(true); 381 | // test s2 == s 382 | for (let j = 0; j < 32; j++) { 383 | expect(s2[j]).toBe(s[j]); 384 | } 385 | 386 | // s2 := s2 - s 387 | s2 = ristretto255.sub(s2, sVar); 388 | // test s2 is valid 389 | expect(ristretto255.isValid(s2)).toBe(true); 390 | // test s2 == 0 391 | expect(testIsZeroArray(s2)).toBe(1); 392 | 393 | s = ristretto255.getRandom(); 394 | sVar = new Uint8Array(32); 395 | sVar.fill(0xfe); 396 | // test sVar is invalid 397 | expect(ristretto255.isValid(sVar)).toBe(false); 398 | 399 | // add should throw an exception on invalid inputs 400 | try { 401 | ristretto255.add(sVar, s); 402 | expect(0).toBe(1); 403 | } catch (err) { 404 | // empty 405 | } 406 | try { 407 | ristretto255.add(s, sVar); 408 | expect(0).toBe(1); 409 | } catch (err) { 410 | // empty 411 | } 412 | try { 413 | ristretto255.add(sVar, sVar); 414 | expect(0).toBe(1); 415 | } catch (err) { 416 | // empty 417 | } 418 | try { 419 | s2 = ristretto255.add(s, s); 420 | } catch (err) { 421 | expect(0).toBe(1); 422 | } 423 | 424 | // sub should throw an exception on invalid inputs 425 | try { 426 | ristretto255.sub(sVar, s); 427 | expect(0).toBe(1); 428 | } catch (err) { 429 | // empty 430 | } 431 | try { 432 | ristretto255.sub(s, sVar); 433 | expect(0).toBe(1); 434 | } catch (err) { 435 | // empty 436 | } 437 | try { 438 | ristretto255.sub(sVar, sVar); 439 | expect(0).toBe(1); 440 | } catch (err) { 441 | // empty 442 | } 443 | try { 444 | s2 = ristretto255.sub(s, s); 445 | } catch (err) { 446 | expect(0).toBe(1); 447 | } 448 | } 449 | }); 450 | 451 | // Porting libsodium test tv4 https://github.com/jedisct1/libsodium/blob/master/test/default/core_ristretto255.c#L210 452 | test('Fuzzy checking ristretto ops (libsodium tv4)', () => { 453 | for (let i = 0; i < FUZZY_TESTS_ITERATIONS_NUMBER; i++) { 454 | // s1 := random 455 | let s1 = ristretto255.scalar.getRandom(); 456 | // s2 := random 457 | let s2 = ristretto255.scalar.getRandom(); 458 | // s3 := s1 + s2 459 | const s3 = ristretto255.scalar.add(s1, s2); 460 | // s4 := s1 - s2 461 | let s4 = ristretto255.scalar.sub(s1, s2); 462 | // s2 := s3 + s4 == 2 * org_s1 463 | s2 = ristretto255.scalar.add(s3, s4); 464 | // s2 := s2 - s1 == org_s1 465 | s2 = ristretto255.scalar.sub(s2, s1); 466 | // s2 := s3 * s2 == (org_s1 + org_s2) * org_s1 467 | s2 = ristretto255.scalar.mul(s3, s2); 468 | // s4 = 1/s3 == 1 / (org_s1 + org_s2) 469 | s4 = ristretto255.scalar.invert(s3); 470 | // s2 := s2 * s4 == org_s1 471 | s2 = ristretto255.scalar.mul(s2, s4); 472 | // s1 := -s1 == -org_s1 473 | s1 = ristretto255.scalar.negate(s1); 474 | // s2 := s2 + s1 == 0 475 | s2 = ristretto255.scalar.add(s2, s1); 476 | // test s2 == 0 477 | expect(testIsZeroArray(s2)).toBe(1); 478 | } 479 | }); 480 | 481 | // Test basepoint round trip: serialization/deserialization 482 | test('Ristretto base point round trip', () => { 483 | const BASE = ristretto255.unsafe.point.alloc(); 484 | lowlevel.scalarbase(BASE, scalarmodL1); 485 | const base = ristretto255.unsafe.point.toBytes(BASE); 486 | const BASE2 = ristretto255.unsafe.point.alloc(); 487 | const res = ristretto255.unsafe.point.fromBytes(BASE2, base); 488 | expect(res).not.toBe(-1); 489 | const base2 = ristretto255.unsafe.point.toBytes(BASE2); 490 | // test base == base2 491 | for (let j = 0; j < 32; j++) { 492 | expect(base[j]).toBe(base2[j]); 493 | } 494 | }); 495 | 496 | // Test random point round trip: serialization/deserialization 497 | test('Ristretto random point round trip', () => { 498 | for (let i = 0; i < FUZZY_TESTS_ITERATIONS_NUMBER; i++) { 499 | const RANDOM = ristretto255.unsafe.point.getRandom(); 500 | const random = ristretto255.unsafe.point.toBytes(RANDOM); 501 | const RANDOM2 = [ 502 | lowlevel.gf(), 503 | lowlevel.gf(), 504 | lowlevel.gf(), 505 | lowlevel.gf() 506 | ]; 507 | const res = ristretto255.unsafe.point.fromBytes(RANDOM2, random); 508 | expect(res).not.toBe(-1); 509 | const random2 = ristretto255.unsafe.point.toBytes(RANDOM2); 510 | // test random == random2 511 | for (let j = 0; j < 32; j++) { 512 | expect(random[j]).toBe(random2[j]); 513 | } 514 | } 515 | }); 516 | 517 | // Test scalar mult and add 518 | test('Ristretto random ops', () => { 519 | const BASE = [lowlevel.gf(), lowlevel.gf(), lowlevel.gf(), lowlevel.gf()]; 520 | const P1 = [lowlevel.gf(), lowlevel.gf(), lowlevel.gf(), lowlevel.gf()]; 521 | const P2 = [lowlevel.gf(), lowlevel.gf(), lowlevel.gf(), lowlevel.gf()]; 522 | lowlevel.scalarbase(BASE, scalarmodL1); 523 | // console.log("BASE = " + ristretto255.unsafe.point.toBytes(BASE)); 524 | const s1 = new Float64Array(32); 525 | s1[0] = 33; 526 | const s2 = new Float64Array(32); 527 | s2[0] = 66; 528 | // P1 := BASE * s1 529 | lowlevel.scalarmult(P1, BASE, s1); 530 | // P1 := P1 + P1 531 | lowlevel.add(P1, P1); 532 | // P2 := BASE * s2 533 | lowlevel.scalarbase(BASE, scalarmodL1); 534 | lowlevel.scalarmult(P2, BASE, s2); 535 | 536 | expect(byteArrayToHex(ristretto255.unsafe.point.toBytes(P1))).toBe( 537 | byteArrayToHex(ristretto255.unsafe.point.toBytes(P2)) 538 | ); 539 | }); 540 | 541 | // Test scalar mult and scalar inverse near the modulus 542 | test('Ristretto ops corner cases', () => { 543 | const s = new Float64Array(32); 544 | for (let i = 0; i < 32; i++) { 545 | s[i] = lowlevel.L[i]; 546 | } 547 | 548 | const BASE = [lowlevel.gf(), lowlevel.gf(), lowlevel.gf(), lowlevel.gf()]; 549 | const P1 = [lowlevel.gf(), lowlevel.gf(), lowlevel.gf(), lowlevel.gf()]; 550 | const P2 = [lowlevel.gf(), lowlevel.gf(), lowlevel.gf(), lowlevel.gf()]; 551 | lowlevel.scalarbase(BASE, scalarmodL1); 552 | const s1 = new Float64Array(32); 553 | s1[0] = 33; 554 | const s2 = new Float64Array(32); 555 | s2[0] = 66; 556 | // P1 := BASE * s1 557 | lowlevel.scalarmult(P1, BASE, s1); 558 | // P1 := P1 + P1 559 | lowlevel.add(P1, P1); 560 | // P2 := BASE * s2 561 | lowlevel.scalarbase(BASE, scalarmodL1); 562 | lowlevel.scalarmult(P2, BASE, s2); 563 | 564 | expect(byteArrayToHex(ristretto255.unsafe.point.toBytes(P1))).toBe( 565 | byteArrayToHex(ristretto255.unsafe.point.toBytes(P2)) 566 | ); 567 | }); 568 | 569 | // Test border-scalars 570 | test('Scalar operations, corner cases', () => { 571 | const x = new Uint8Array(32); 572 | for (let i = 0; i < 32; i++) x[i] = lowlevel.L[i]; 573 | 574 | x[0] -= 1; // x = L - 1 575 | 576 | const xInv = ristretto255.scalar.invert(x); 577 | // one = x * (1/x) 578 | let one = ristretto255.scalar.mul(x, xInv); 579 | expect(testScalarEq(scalarmodL1, one)).toBe(1); 580 | one = ristretto255.scalar.mul(xInv, x); 581 | expect(testScalarEq(scalarmodL1, one)).toBe(1); 582 | 583 | const zero = ristretto255.scalar.add(x, scalarmodL1); 584 | expect(testScalarEq(scalarmodL0, zero)).toBe(1); 585 | 586 | const negS = new Float64Array(32); 587 | // negS := L - s 588 | for (let i = 0; i < 32; i++) { 589 | negS[i] = -scalarmodL1[i]; 590 | } 591 | const o = new Float64Array(32); 592 | lowlevel.modL(o, negS); 593 | const x2 = ristretto255.scalar.sub(scalarmodL0, scalarmodL1); 594 | expect(testScalarEq(x, x2)).toBe(1); 595 | }); 596 | 597 | // Test constants and arithmetic on field elemenst as a bonus 598 | test('Constants', () => { 599 | // a = -1, d = -121665 / 121666 600 | const zeroGF = lowlevel.gf(); 601 | const oneGF = lowlevel.gf([1]); 602 | 603 | // LSub2 = L - 2 604 | let x = ristretto255.unsafe.constants.LSub2; 605 | let y = new Uint8Array(32); // y == 0 606 | y[0] = 2; // y == 2 607 | y = ristretto255.scalar.sub(lowlevel.L, y); // y == L - 2 608 | expect(testScalarEq(y, x)).toBe(1); 609 | 610 | y = new Float64Array(32); // y == 0 611 | x = new Float64Array(32); // x == 0 612 | // sqrtm1 == sqrt(-1) 613 | lowlevel.S(x, ristretto255.unsafe.constants.sqrtm1); 614 | const xPacked = new Float64Array(32); 615 | lowlevel.pack25519(xPacked, x); 616 | lowlevel.Z(y, zeroGF, oneGF); 617 | const yPacked = new Float64Array(32); 618 | lowlevel.pack25519(yPacked, y); 619 | expect(testScalarEq(xPacked, yPacked)).toBe(1); 620 | 621 | // D == -121665 / 121666 622 | lowlevel.Z(x, zeroGF, lowlevel.D); // x = -D 623 | lowlevel.M(x, x, lowlevel.gf([0xdb42, 1])); // x *= 121666 624 | lowlevel.pack25519(xPacked, x); 625 | lowlevel.pack25519(yPacked, lowlevel.gf([0xdb41, 1])); // y = 121665 626 | expect(testScalarEq(xPacked, yPacked)).toBe(1); 627 | 628 | // sqrtadm1 == sqrt(-D-1) 629 | lowlevel.Z(x, zeroGF, lowlevel.D); // x = -D 630 | lowlevel.Z(x, x, oneGF); // x -= 1 631 | lowlevel.S(y, ristretto255.unsafe.constants.sqrtadm1); // y = sqrtadm1^2 632 | lowlevel.pack25519(xPacked, x); 633 | lowlevel.pack25519(yPacked, y); 634 | expect(testScalarEq(xPacked, yPacked)).toBe(1); 635 | 636 | // invsqrtamd == 1 / sqrt(-D-1) 637 | lowlevel.Z(x, zeroGF, lowlevel.D); // x = -D 638 | lowlevel.Z(x, x, oneGF); // x -= 1 639 | lowlevel.S(y, ristretto255.unsafe.constants.invsqrtamd); // y = invsqrtamd^2 640 | lowlevel.M(x, y, x); 641 | lowlevel.pack25519(xPacked, x); 642 | lowlevel.pack25519(yPacked, oneGF); 643 | expect(testScalarEq(xPacked, yPacked)).toBe(1); 644 | 645 | // onemsqd == (1-d^2) 646 | lowlevel.S(x, lowlevel.D); 647 | lowlevel.Z(x, oneGF, x); 648 | lowlevel.pack25519(xPacked, x); 649 | lowlevel.pack25519(yPacked, ristretto255.unsafe.constants.onemsqd); 650 | expect(testScalarEq(xPacked, yPacked)).toBe(1); 651 | 652 | // sqdmone == (d-1)^2 653 | lowlevel.Z(x, lowlevel.D, oneGF); 654 | lowlevel.S(x, x); 655 | lowlevel.pack25519(xPacked, x); 656 | lowlevel.pack25519(yPacked, ristretto255.unsafe.constants.sqdmone); 657 | expect(testScalarEq(xPacked, yPacked)).toBe(1); 658 | 659 | // base point 660 | const one = new Uint8Array(32); 661 | one[0] = 1; 662 | const b = ristretto255.scalarMultBase(one); 663 | const b2 = [ 664 | lowlevel.gf([ 665 | 0xd51a, 666 | 0x8f25, 667 | 0x2d60, 668 | 0xc956, 669 | 0xa7b2, 670 | 0x9525, 671 | 0xc760, 672 | 0x692c, 673 | 0xdc5c, 674 | 0xfdd6, 675 | 0xe231, 676 | 0xc0a4, 677 | 0x53fe, 678 | 0xcd6e, 679 | 0x36d3, 680 | 0x2169 681 | ]), 682 | lowlevel.gf([ 683 | 0x6658, 684 | 0x6666, 685 | 0x6666, 686 | 0x6666, 687 | 0x6666, 688 | 0x6666, 689 | 0x6666, 690 | 0x6666, 691 | 0x6666, 692 | 0x6666, 693 | 0x6666, 694 | 0x6666, 695 | 0x6666, 696 | 0x6666, 697 | 0x6666, 698 | 0x6666 699 | ]), 700 | lowlevel.gf([1]), 701 | lowlevel.gf() 702 | ]; 703 | lowlevel.M(b2[3], b2[0], b2[1]); 704 | 705 | const b3 = ristretto255.unsafe.point.toBytes(b2); 706 | expect(testScalarEq(b3, b)).toBe(1); 707 | expect(testScalarEq(ristretto255.basePoint, b)).toBe(1); 708 | }); 709 | --------------------------------------------------------------------------------