README: update Field documentation, reformat with prettier

This commit is contained in:
Paul Miller 2023-08-11 10:23:19 +00:00
parent 05794c0283
commit d92c9d14ad
No known key found for this signature in database
GPG Key ID: 697079DA6878B89B

@ -166,7 +166,12 @@ edwardsToMontgomeryPriv(ed25519.utils.randomPrivateKey());
// hash-to-curve, ristretto255 // hash-to-curve, ristretto255
import { utf8ToBytes } from '@noble/hashes/utils'; import { utf8ToBytes } from '@noble/hashes/utils';
import { sha512 } from '@noble/hashes/sha512'; import { sha512 } from '@noble/hashes/sha512';
import { hashToCurve, encodeToCurve, RistrettoPoint, hash_to_ristretto255 } from '@noble/curves/ed25519'; import {
hashToCurve,
encodeToCurve,
RistrettoPoint,
hashToRistretto255,
} from '@noble/curves/ed25519';
const msg = utf8ToBytes('Ristretto is traditionally a short shot of espresso coffee'); const msg = utf8ToBytes('Ristretto is traditionally a short shot of espresso coffee');
hashToCurve(msg); hashToCurve(msg);
@ -179,7 +184,7 @@ RistrettoPoint.ZERO.equals(dp) === false;
// pre-hashed hash-to-curve // pre-hashed hash-to-curve
RistrettoPoint.hashToCurve(sha512(msg)); RistrettoPoint.hashToCurve(sha512(msg));
// full hash-to-curve including domain separation tag // full hash-to-curve including domain separation tag
hash_to_ristretto255(msg, { DST: 'ristretto255_XMD:SHA-512_R255MAP_RO_' }); hashToRistretto255(msg, { DST: 'ristretto255_XMD:SHA-512_R255MAP_RO_' });
``` ```
#### ed448, X448, decaf448 #### ed448, X448, decaf448
@ -207,7 +212,7 @@ edwardsToMontgomeryPub(ed448.getPublicKey(ed448.utils.randomPrivateKey()));
// hash-to-curve, decaf448 // hash-to-curve, decaf448
import { utf8ToBytes } from '@noble/hashes/utils'; import { utf8ToBytes } from '@noble/hashes/utils';
import { shake256 } from '@noble/hashes/sha3'; import { shake256 } from '@noble/hashes/sha3';
import { hashToCurve, encodeToCurve, DecafPoint, hash_to_decaf448 } from '@noble/curves/ed448'; import { hashToCurve, encodeToCurve, DecafPoint, hashToDecaf448 } from '@noble/curves/ed448';
const msg = utf8ToBytes('Ristretto is traditionally a short shot of espresso coffee'); const msg = utf8ToBytes('Ristretto is traditionally a short shot of espresso coffee');
hashToCurve(msg); hashToCurve(msg);
@ -220,7 +225,7 @@ DecafPoint.ZERO.equals(dp) === false;
// pre-hashed hash-to-curve // pre-hashed hash-to-curve
DecafPoint.hashToCurve(shake256(msg, { dkLen: 112 })); DecafPoint.hashToCurve(shake256(msg, { dkLen: 112 }));
// full hash-to-curve including domain separation tag // full hash-to-curve including domain separation tag
hash_to_decaf448(msg, { DST: 'decaf448_XOF:SHAKE256_D448MAP_RO_' }); hashToDecaf448(msg, { DST: 'decaf448_XOF:SHAKE256_D448MAP_RO_' });
``` ```
Same RFC7748 / RFC8032 / IRTF draft are followed. Same RFC7748 / RFC8032 / IRTF draft are followed.
@ -295,8 +300,9 @@ type CHash = {
``` ```
**Message hash** is expected instead of message itself: **Message hash** is expected instead of message itself:
- `.sign(msgHash, privKey)` is default behavior, you need to do `msgHash = hash(msg)` before
- `.sign(msg, privKey, {prehash: true})` if you want the library to handle hashing for you - `sign(msgHash, privKey)` is default behavior, assuming you pre-hash msg with sha2, or other hash
- `sign(msg, privKey, {prehash: true})` option can be used if you want to pass the message itself
**Weierstrass points:** **Weierstrass points:**
@ -393,7 +399,7 @@ More examples:
const priv = secq256k1.utils.randomPrivateKey(); const priv = secq256k1.utils.randomPrivateKey();
secq256k1.getPublicKey(priv); // Convert private key to public. secq256k1.getPublicKey(priv); // Convert private key to public.
const sig = secq256k1.sign(msg, priv); // Sign msg with private key. const sig = secq256k1.sign(msg, priv); // Sign msg with private key.
const sig2 = secq256k1.sign(msg, priv, {prehash: true}); // hash(msg) const sig2 = secq256k1.sign(msg, priv, { prehash: true }); // hash(msg)
secq256k1.verify(sig, msg, priv); // Verify if sig is correct. secq256k1.verify(sig, msg, priv); // Verify if sig is correct.
const Point = secq256k1.ProjectivePoint; const Point = secq256k1.ProjectivePoint;
@ -760,26 +766,39 @@ mod.invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
mod.invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion mod.invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
``` ```
Field operations are not constant-time: they are using JS bigints, see [security](#security).
The fact is mostly irrelevant, but the important method to keep in mind is `pow`,
which may leak exponent bits, when used naïvely.
`mod.Field` is always **field over prime**. Non-prime fields aren't supported for now.
We don't test for prime-ness for speed and because algorithms are probabilistic anyway.
Initializing a non-prime field could make your app suspectible to
DoS (infilite loop) on Tonelli-Shanks square root calculation.
Unlike `mod.invert`, `mod.invertBatch` won't throw on `0`: make sure to throw an error yourself.
#### Creating private keys from hashes #### Creating private keys from hashes
Suppose you have `sha256(something)` (e.g. from HMAC) and you want to make a private key from it. You can't simply make a 32-byte private key from a 32-byte hash.
Even though p256 or secp256k1 may have 32-byte private keys, Doing so will make the key [biased](https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/).
and sha256 output is also 32-byte, you can't just use it and reduce it modulo `CURVE.n`.
Doing so will make the result key [biased](https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/). It may be tempting to do `sha256(something)` (or pbkdf2 / hmac-sha256),
convert the result to bigint, and modulo-reduce the result output by `CURVE.n`,
but you need more bytes for proper security.
To avoid the bias, we implement FIPS 186 B.4.1, which allows to take arbitrary To make the bias negligible, we follow [FIPS 186-5 A.2](https://csrc.nist.gov/publications/detail/fips/186/5/final)
byte array and produce valid scalars / private keys with bias being neglible. and [h2c standard](https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-hashing-to-a-finite-field).
This means, for 32-byte key, we would need 48-byte hash to get 2^-128 bias, which matches curve security level.
Use [hash-to-curve](#abstracthash-to-curve-hashing-strings-to-curve-points) if you need Use [abstract/hash-to-curve](#abstracthash-to-curve-hashing-strings-to-curve-points) if you need
hashing to **public keys**; the function in the module instead operates on **private keys**. to hash to **public key**. `hashToPrivateScalar()` operates instead on **private keys**.
```ts ```ts
import { p256 } from '@noble/curves/p256'; import { p256 } from '@noble/curves/p256';
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
import { hkdf } from '@noble/hashes/hkdf'; import { hkdf } from '@noble/hashes/hkdf';
const someKey = new Uint8Array(32).fill(2); // Needs to actually be random, not .fill(2) const someKey = new Uint8Array(32).fill(2); // Needs to actually be random, not .fill(2)
const derived = hkdf(sha256, someKey, undefined, 'application', 40); // 40 bytes const derived = hkdf(sha256, someKey, undefined, 'application', 48); // 48 bytes for 32-byte priv
const validPrivateKey = mod.hashToPrivateScalar(derived, p256.CURVE.n); const validPrivateKey = mod.hashToPrivateScalar(derived, p256.CURVE.n);
``` ```
@ -805,18 +824,36 @@ utils.equalBytes(Uint8Array.from([0xde]), Uint8Array.from([0xde]));
## Security ## Security
1. The library has been audited in Feb 2023 by an independent security firm [Trail of Bits](https://www.trailofbits.com): 1. The library has been independently audited:
[PDF](https://github.com/trailofbits/publications/blob/master/reviews/2023-01-ryanshea-noblecurveslibrary-securityreview.pdf).
The audit has been funded by [Ryan Shea](https://www.shea.io). Audit scope was abstract modules `curve`, `hash-to-curve`, `modular`, `poseidon`, `utils`, `weierstrass`, and top-level modules `_shortw_utils` and `secp256k1`. See [changes since audit](https://github.com/paulmillr/noble-curves/compare/0.7.3..main).
2. The library has been fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz). You can run the fuzzer by yourself to check it.
3. [Timing attack](https://en.wikipedia.org/wiki/Timing_attack) considerations: _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library can't have constant-timeness_. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
We consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading malware with every `npm install`. Our goal is to minimize this attack vector. As for devDependencies used by the library: - in Feb 2023 by [Trail of Bits](https://www.trailofbits.com):
[PDF](https://github.com/trailofbits/publications/blob/master/reviews/2023-01-ryanshea-noblecurveslibrary-securityreview.pdf).
The audit has been funded by [Ryan Shea](https://www.shea.io).
Audit scope was abstract modules `curve`, `hash-to-curve`, `modular`, `poseidon`, `utils`, `weierstrass`,
and top-level modules `_shortw_utils` and `secp256k1`.
See [changes since v0.7.3 audit](https://github.com/paulmillr/noble-curves/compare/0.7.3..main).
- `@scure` base, bip32, bip39 (used in tests), micro-bmark (benchmark), micro-should (testing) are developed by us 2. The library has been fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz).
and follow the same practices such as: minimal library size, auditability, signed releases You can run the fuzzer by yourself to check it.
- prettier (linter), fast-check (property-based testing), 3. [Timing attack](https://en.wikipedia.org/wiki/Timing_attack) considerations:
typescript versions are locked and rarely updated. Every update is checked with `npm-diff`. _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to
achieve in a scripting language. Which means _any other JS library can't have
constant-timeness_. Even statically typed Rust, a language without GC,
[makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security)
for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones.
Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
We consider infrastructure attacks like rogue NPM modules very important;
that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings.
If your app uses 500 dependencies, any dep could get hacked and you'll be
downloading malware with every `npm install`. Our goal is to minimize this attack vector.
As for devDependencies used by the library:
- `@scure` base, bip32, bip39 (used in tests), micro-bmark (benchmark), micro-should (testing)
are developed by us and follow the same practices such as: minimal library size, auditability,
signed releases
- prettier (linter), fast-check (property-based testing), typescript versions
are locked and rarely updated. Every update is checked with `npm-diff`.
The packages are big, which makes it hard to audit their source code thoroughly and fully. The packages are big, which makes it hard to audit their source code thoroughly and fully.
- They are only used if you clone the git repo and want to add some feature to it. End-users won't use them. - They are only used if you clone the git repo and want to add some feature to it. End-users won't use them.
@ -1019,7 +1056,7 @@ Upgrading from [@noble/bls12-381](https://github.com/paulmillr/noble-bls12-381):
[js-libp2p-noise](https://github.com/ChainSafe/js-libp2p-noise) [js-libp2p-noise](https://github.com/ChainSafe/js-libp2p-noise)
- [ed25519-keygen](https://github.com/paulmillr/ed25519-keygen) SSH, PGP, TOR key generation - [ed25519-keygen](https://github.com/paulmillr/ed25519-keygen) SSH, PGP, TOR key generation
- [secp256k1 compatibility layer](https://github.com/ethereum/js-ethereum-cryptography/blob/2.0.0/src/secp256k1-compat.ts) - [secp256k1 compatibility layer](https://github.com/ethereum/js-ethereum-cryptography/blob/2.0.0/src/secp256k1-compat.ts)
for users who want to switch from secp256k1-node or tiny-secp256k1. Allows to see which methods map to corresponding noble code. for users who want to switch from secp256k1-node or tiny-secp256k1. Allows to see which methods map to corresponding noble code.
- [BLS BBS signatures](https://github.com/Wind4Greg/BBS-Draft-Checks) following [draft-irtf-cfrg-bbs-signatures-latest](https://identity.foundation/bbs-signature/draft-irtf-cfrg-bbs-signatures.html) - [BLS BBS signatures](https://github.com/Wind4Greg/BBS-Draft-Checks) following [draft-irtf-cfrg-bbs-signatures-latest](https://identity.foundation/bbs-signature/draft-irtf-cfrg-bbs-signatures.html)
- [KZG trusted setup ceremony](https://github.com/dsrvlabs/czg-keremony) - [KZG trusted setup ceremony](https://github.com/dsrvlabs/czg-keremony)
- See [full list of projects on GitHub](https://github.com/paulmillr/noble-curves/network/dependents). - See [full list of projects on GitHub](https://github.com/paulmillr/noble-curves/network/dependents).