README: update Field documentation, reformat with prettier
This commit is contained in:
parent
05794c0283
commit
d92c9d14ad
85
README.md
85
README.md
@ -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:**
|
||||||
|
|
||||||
@ -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:
|
||||||
|
|
||||||
|
- 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).
|
[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).
|
The audit has been funded by [Ryan Shea](https://www.shea.io).
|
||||||
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.
|
Audit scope was abstract modules `curve`, `hash-to-curve`, `modular`, `poseidon`, `utils`, `weierstrass`,
|
||||||
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.
|
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).
|
||||||
|
|
||||||
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:
|
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.
|
||||||
|
|
||||||
- `@scure` base, bip32, bip39 (used in tests), micro-bmark (benchmark), micro-should (testing) are developed by us
|
We consider infrastructure attacks like rogue NPM modules very important;
|
||||||
and follow the same practices such as: minimal library size, auditability, signed releases
|
that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings.
|
||||||
- prettier (linter), fast-check (property-based testing),
|
If your app uses 500 dependencies, any dep could get hacked and you'll be
|
||||||
typescript versions are locked and rarely updated. Every update is checked with `npm-diff`.
|
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.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user