nervur 0.21.4 → 0.21.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/crypto/ed25519.js +22 -14
- package/dist/crypto/subtle.d.ts +0 -7
- package/dist/crypto/subtle.js +0 -18
- package/dist/crypto/x25519.js +12 -9
- package/package.json +2 -1
- package/src/crypto/ed25519.ts +22 -14
- package/src/crypto/subtle.ts +0 -20
- package/src/crypto/x25519.ts +11 -9
package/dist/crypto/ed25519.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// Ed25519 signs, as RFC 8032 defines it,
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// Ed25519 signs, as RFC 8032 defines it, by `@noble/curves` in every
|
|
3
|
+
// engine, since an engine's Web Crypto may refuse a secret the curve takes,
|
|
4
|
+
// and verifies with the cofactorless check the spec names, which the
|
|
5
|
+
// library's own verify does not make. The decoding rules are held here:
|
|
5
6
|
//
|
|
6
7
|
// a signature of any length but sixty-four
|
|
7
8
|
// an `s` at or above the group order
|
|
8
9
|
// an `R` that does not decode, or whose bytes are not its point's encoding
|
|
9
10
|
// a public key that does not decode, or whose y is at or above the prime
|
|
10
11
|
// a public key of small order, in any spelling
|
|
11
|
-
import {
|
|
12
|
+
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
13
|
+
import { concat } from './bytes.js';
|
|
14
|
+
import { own, subtle } from './subtle.js';
|
|
12
15
|
const P = 2n ** 255n - 19n;
|
|
13
16
|
const L = 2n ** 252n + 27742317777372353535851937790883648493n;
|
|
14
17
|
const mod = (a) => ((a % P) + P) % P;
|
|
@@ -63,8 +66,14 @@ const smallOrder = (p) => {
|
|
|
63
66
|
q = add(q, q);
|
|
64
67
|
return q.x === 0n && q.y === q.z;
|
|
65
68
|
};
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
const secretOf = (secret) => {
|
|
70
|
+
if (secret.length !== 32)
|
|
71
|
+
throw new TypeError('a secret is thirty-two bytes');
|
|
72
|
+
return secret;
|
|
73
|
+
};
|
|
74
|
+
export const ed25519Public = async (secret) => ed25519.getPublicKey(secretOf(secret));
|
|
75
|
+
export const sign = async (secret, message) => ed25519.sign(message, secretOf(secret));
|
|
76
|
+
// The cofactorless check, `[s]B = R + [k]A`, on points decoded as above.
|
|
68
77
|
export const verify = async (pk, message, signature) => {
|
|
69
78
|
if (signature.length !== 64)
|
|
70
79
|
return false;
|
|
@@ -73,13 +82,12 @@ export const verify = async (pk, message, signature) => {
|
|
|
73
82
|
return false;
|
|
74
83
|
if (decode(signature.subarray(0, 32)) === null)
|
|
75
84
|
return false;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
const imported = await subtle().importKey('raw', own(pk), { name: 'Ed25519' }, false, ['verify']);
|
|
80
|
-
return await subtle().verify('Ed25519', imported, own(signature), own(message));
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
85
|
+
const s = little(signature.subarray(32));
|
|
86
|
+
if (s >= L)
|
|
83
87
|
return false;
|
|
84
|
-
|
|
88
|
+
const R = ed25519.Point.fromBytes(signature.subarray(0, 32));
|
|
89
|
+
const A = ed25519.Point.fromBytes(pk);
|
|
90
|
+
const hashed = new Uint8Array(await subtle().digest('SHA-512', own(concat(signature.subarray(0, 32), pk, message))));
|
|
91
|
+
const k = little(hashed) % L;
|
|
92
|
+
return ed25519.Point.BASE.multiplyUnsafe(s).equals(R.add(A.multiplyUnsafe(k)));
|
|
85
93
|
};
|
package/dist/crypto/subtle.d.ts
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
1
|
export declare const subtle: () => SubtleCrypto;
|
|
2
2
|
export type Bytes = Uint8Array<ArrayBuffer>;
|
|
3
3
|
export declare const own: (bytes: Uint8Array) => Bytes;
|
|
4
|
-
declare const PKCS8: {
|
|
5
|
-
readonly Ed25519: '302e020100300506032b657004220420';
|
|
6
|
-
readonly X25519: '302e020100300506032b656e04220420';
|
|
7
|
-
};
|
|
8
|
-
export declare const importSecret: (curve: keyof typeof PKCS8, secret: Uint8Array, uses: KeyUsage[]) => Promise<CryptoKey>;
|
|
9
|
-
export declare const publicOf: (key: CryptoKey) => Promise<Uint8Array>;
|
|
10
|
-
export {};
|
package/dist/crypto/subtle.js
CHANGED
|
@@ -9,21 +9,3 @@ export const subtle = () => {
|
|
|
9
9
|
};
|
|
10
10
|
// Web Crypto takes a buffer it owns, so every input is copied into one.
|
|
11
11
|
export const own = (bytes) => Uint8Array.from(bytes);
|
|
12
|
-
// The PKCS#8 wrapping of a thirty-two byte secret, which is the whole of it
|
|
13
|
-
// for both curves.
|
|
14
|
-
const PKCS8 = { Ed25519: '302e020100300506032b657004220420', X25519: '302e020100300506032b656e04220420' };
|
|
15
|
-
export const importSecret = (curve, secret, uses) => {
|
|
16
|
-
if (secret.length !== 32)
|
|
17
|
-
throw new TypeError('a secret is thirty-two bytes');
|
|
18
|
-
const prefix = Uint8Array.from(PKCS8[curve].match(/../g), (b) => parseInt(b, 16));
|
|
19
|
-
const wrapped = new Uint8Array(48);
|
|
20
|
-
wrapped.set(prefix);
|
|
21
|
-
wrapped.set(secret, 16);
|
|
22
|
-
return subtle().importKey('pkcs8', wrapped, { name: curve }, true, uses);
|
|
23
|
-
};
|
|
24
|
-
// Web Crypto gives a private key's public half only through a JWK.
|
|
25
|
-
export const publicOf = async (key) => {
|
|
26
|
-
const { x } = await subtle().exportKey('jwk', key);
|
|
27
|
-
const binary = atob(x.replaceAll('-', '+').replaceAll('_', '/'));
|
|
28
|
-
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
29
|
-
};
|
package/dist/crypto/x25519.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// X25519 agrees
|
|
3
|
-
//
|
|
2
|
+
// X25519 agrees, by `@noble/curves` in every engine, since an engine's Web
|
|
3
|
+
// Crypto may refuse a secret the curve takes. A secret is thirty-two bytes,
|
|
4
|
+
// clamped by the function and never before.
|
|
5
|
+
import { x25519 } from '@noble/curves/ed25519.js';
|
|
4
6
|
import { zero } from './bytes.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
export const x25519Public = async (secret) => {
|
|
8
|
+
if (secret.length !== 32)
|
|
9
|
+
throw new TypeError('a secret is thirty-two bytes');
|
|
10
|
+
return x25519.getPublicKey(secret);
|
|
11
|
+
};
|
|
7
12
|
// The agreement, or null for a public key that takes no seal: one whose
|
|
8
|
-
// agreement is thirty-two zero bytes
|
|
13
|
+
// agreement is thirty-two zero bytes.
|
|
9
14
|
export const agree = async (secret, pk) => {
|
|
10
|
-
if (pk.length !== 32)
|
|
15
|
+
if (pk.length !== 32 || secret.length !== 32)
|
|
11
16
|
return null;
|
|
12
17
|
try {
|
|
13
|
-
const
|
|
14
|
-
const theirs = await subtle().importKey('raw', own(pk), { name: 'X25519' }, false, []);
|
|
15
|
-
const shared = new Uint8Array(await subtle().deriveBits({ name: 'X25519', public: theirs }, mine, 256));
|
|
18
|
+
const shared = x25519.getSharedSecret(secret, pk);
|
|
16
19
|
return zero(shared) ? null : shared;
|
|
17
20
|
}
|
|
18
21
|
catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nervur",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.5",
|
|
4
4
|
"description": "Nervur's kit of Quo: an object asks another object and gets an answer, without knowing where it is.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"quo",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"node": ">=22.18"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
+
"@noble/curves": "2.4.0",
|
|
67
68
|
"@noble/post-quantum": "0.7.1"
|
|
68
69
|
},
|
|
69
70
|
"publishConfig": {
|
package/src/crypto/ed25519.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// Ed25519 signs, as RFC 8032 defines it,
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// Ed25519 signs, as RFC 8032 defines it, by `@noble/curves` in every
|
|
3
|
+
// engine, since an engine's Web Crypto may refuse a secret the curve takes,
|
|
4
|
+
// and verifies with the cofactorless check the spec names, which the
|
|
5
|
+
// library's own verify does not make. The decoding rules are held here:
|
|
5
6
|
//
|
|
6
7
|
// a signature of any length but sixty-four
|
|
7
8
|
// an `s` at or above the group order
|
|
8
9
|
// an `R` that does not decode, or whose bytes are not its point's encoding
|
|
9
10
|
// a public key that does not decode, or whose y is at or above the prime
|
|
10
11
|
// a public key of small order, in any spelling
|
|
11
|
-
import {
|
|
12
|
+
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
13
|
+
import { concat } from './bytes.ts';
|
|
14
|
+
import { own, subtle } from './subtle.ts';
|
|
12
15
|
|
|
13
16
|
const P = 2n ** 255n - 19n;
|
|
14
17
|
const L = 2n ** 252n + 27742317777372353535851937790883648493n;
|
|
@@ -64,21 +67,26 @@ const smallOrder = (p: Point): boolean => {
|
|
|
64
67
|
return q.x === 0n && q.y === q.z;
|
|
65
68
|
};
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
const secretOf = (secret: Uint8Array): Uint8Array => {
|
|
71
|
+
if (secret.length !== 32) throw new TypeError('a secret is thirty-two bytes');
|
|
72
|
+
return secret;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const ed25519Public = async (secret: Uint8Array): Promise<Uint8Array> => ed25519.getPublicKey(secretOf(secret));
|
|
68
76
|
|
|
69
|
-
export const sign = async (secret: Uint8Array, message: Uint8Array): Promise<Uint8Array> =>
|
|
70
|
-
new Uint8Array(await subtle().sign('Ed25519', await importSecret('Ed25519', secret, ['sign']), own(message)));
|
|
77
|
+
export const sign = async (secret: Uint8Array, message: Uint8Array): Promise<Uint8Array> => ed25519.sign(message, secretOf(secret));
|
|
71
78
|
|
|
79
|
+
// The cofactorless check, `[s]B = R + [k]A`, on points decoded as above.
|
|
72
80
|
export const verify = async (pk: Uint8Array, message: Uint8Array, signature: Uint8Array): Promise<boolean> => {
|
|
73
81
|
if (signature.length !== 64) return false;
|
|
74
82
|
const key = decode(pk);
|
|
75
83
|
if (key === null || smallOrder(key)) return false;
|
|
76
84
|
if (decode(signature.subarray(0, 32)) === null) return false;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
85
|
+
const s = little(signature.subarray(32));
|
|
86
|
+
if (s >= L) return false;
|
|
87
|
+
const R = ed25519.Point.fromBytes(signature.subarray(0, 32));
|
|
88
|
+
const A = ed25519.Point.fromBytes(pk);
|
|
89
|
+
const hashed = new Uint8Array(await subtle().digest('SHA-512', own(concat(signature.subarray(0, 32), pk, message))));
|
|
90
|
+
const k = little(hashed) % L;
|
|
91
|
+
return ed25519.Point.BASE.multiplyUnsafe(s).equals(R.add(A.multiplyUnsafe(k)));
|
|
84
92
|
};
|
package/src/crypto/subtle.ts
CHANGED
|
@@ -11,23 +11,3 @@ export type Bytes = Uint8Array<ArrayBuffer>;
|
|
|
11
11
|
|
|
12
12
|
// Web Crypto takes a buffer it owns, so every input is copied into one.
|
|
13
13
|
export const own = (bytes: Uint8Array): Bytes => Uint8Array.from(bytes);
|
|
14
|
-
|
|
15
|
-
// The PKCS#8 wrapping of a thirty-two byte secret, which is the whole of it
|
|
16
|
-
// for both curves.
|
|
17
|
-
const PKCS8 = { Ed25519: '302e020100300506032b657004220420', X25519: '302e020100300506032b656e04220420' } as const;
|
|
18
|
-
|
|
19
|
-
export const importSecret = (curve: keyof typeof PKCS8, secret: Uint8Array, uses: KeyUsage[]): Promise<CryptoKey> => {
|
|
20
|
-
if (secret.length !== 32) throw new TypeError('a secret is thirty-two bytes');
|
|
21
|
-
const prefix = Uint8Array.from(PKCS8[curve].match(/../g)!, (b) => parseInt(b, 16));
|
|
22
|
-
const wrapped = new Uint8Array(48);
|
|
23
|
-
wrapped.set(prefix);
|
|
24
|
-
wrapped.set(secret, 16);
|
|
25
|
-
return subtle().importKey('pkcs8', wrapped, { name: curve }, true, uses);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// Web Crypto gives a private key's public half only through a JWK.
|
|
29
|
-
export const publicOf = async (key: CryptoKey): Promise<Uint8Array> => {
|
|
30
|
-
const { x } = await subtle().exportKey('jwk', key);
|
|
31
|
-
const binary = atob(x!.replaceAll('-', '+').replaceAll('_', '/'));
|
|
32
|
-
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
33
|
-
};
|
package/src/crypto/x25519.ts
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// X25519 agrees
|
|
3
|
-
//
|
|
2
|
+
// X25519 agrees, by `@noble/curves` in every engine, since an engine's Web
|
|
3
|
+
// Crypto may refuse a secret the curve takes. A secret is thirty-two bytes,
|
|
4
|
+
// clamped by the function and never before.
|
|
5
|
+
import { x25519 } from '@noble/curves/ed25519.js';
|
|
4
6
|
import { zero } from './bytes.ts';
|
|
5
|
-
import { importSecret, own, publicOf, subtle } from './subtle.ts';
|
|
6
7
|
|
|
7
|
-
export const x25519Public = async (secret: Uint8Array): Promise<Uint8Array> =>
|
|
8
|
+
export const x25519Public = async (secret: Uint8Array): Promise<Uint8Array> => {
|
|
9
|
+
if (secret.length !== 32) throw new TypeError('a secret is thirty-two bytes');
|
|
10
|
+
return x25519.getPublicKey(secret);
|
|
11
|
+
};
|
|
8
12
|
|
|
9
13
|
// The agreement, or null for a public key that takes no seal: one whose
|
|
10
|
-
// agreement is thirty-two zero bytes
|
|
14
|
+
// agreement is thirty-two zero bytes.
|
|
11
15
|
export const agree = async (secret: Uint8Array, pk: Uint8Array): Promise<Uint8Array | null> => {
|
|
12
|
-
if (pk.length !== 32) return null;
|
|
16
|
+
if (pk.length !== 32 || secret.length !== 32) return null;
|
|
13
17
|
try {
|
|
14
|
-
const
|
|
15
|
-
const theirs = await subtle().importKey('raw', own(pk), { name: 'X25519' }, false, []);
|
|
16
|
-
const shared = new Uint8Array(await subtle().deriveBits({ name: 'X25519', public: theirs }, mine, 256));
|
|
18
|
+
const shared = x25519.getSharedSecret(secret, pk);
|
|
17
19
|
return zero(shared) ? null : shared;
|
|
18
20
|
} catch {
|
|
19
21
|
return null;
|