forked from tornado-packages/noble-curves
Update tests
This commit is contained in:
parent
7fda6de619
commit
bfbcf733e6
@ -342,7 +342,7 @@ for (const name in CURVES) {
|
|||||||
if (!p) continue;
|
if (!p) continue;
|
||||||
|
|
||||||
const G = [p.ZERO, p.BASE];
|
const G = [p.ZERO, p.BASE];
|
||||||
for (let i = 2; i < 10; i++) G.push(G[1].multiply(i));
|
for (let i = 2n; i < 10n; i++) G.push(G[1].multiply(i));
|
||||||
const title = `${name}/${pointName}`;
|
const title = `${name}/${pointName}`;
|
||||||
describe(title, () => {
|
describe(title, () => {
|
||||||
describe('basic group laws', () => {
|
describe('basic group laws', () => {
|
||||||
@ -355,7 +355,7 @@ for (const name in CURVES) {
|
|||||||
for (let i = 0; i < G.length; i++) {
|
for (let i = 0; i < G.length; i++) {
|
||||||
const p = G[i];
|
const p = G[i];
|
||||||
equal(p, p.add(G[0]), '${i}*G + 0 = ${i}*G');
|
equal(p, p.add(G[0]), '${i}*G + 0 = ${i}*G');
|
||||||
equal(G[0].multiply(i + 1), G[0], '${i + 1}*0 = 0');
|
equal(G[0].multiply(BigInt(i + 1)), G[0], '${i + 1}*0 = 0');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('(one)', () => {
|
should('(one)', () => {
|
||||||
@ -376,7 +376,7 @@ for (const name in CURVES) {
|
|||||||
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
|
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
|
||||||
});
|
});
|
||||||
should('(multiply)', () => {
|
should('(multiply)', () => {
|
||||||
equal(G[2].multiply(3), G[6], '(2*G).multiply(3) = 6*G');
|
equal(G[2].multiply(3n), G[6], '(2*G).multiply(3) = 6*G');
|
||||||
});
|
});
|
||||||
should('(same point addition)', () => {
|
should('(same point addition)', () => {
|
||||||
equal(G[3].add(G[3]), G[6], '3*G + 3*G = 6*G');
|
equal(G[3].add(G[3]), G[6], '3*G + 3*G = 6*G');
|
||||||
|
@ -440,7 +440,7 @@ describe('ed25519', () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
noble = false;
|
noble = false;
|
||||||
}
|
}
|
||||||
deepStrictEqual(noble, v.valid_zip215);
|
deepStrictEqual(noble, v.valid_zip215, JSON.stringify(v));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('ZIP-215 compliance tests/disallows sig.s >= CURVE.n', () => {
|
should('ZIP-215 compliance tests/disallows sig.s >= CURVE.n', () => {
|
||||||
|
@ -8,7 +8,7 @@ import './ed25519.test.js';
|
|||||||
import './secp256k1.test.js';
|
import './secp256k1.test.js';
|
||||||
import './stark/stark.test.js';
|
import './stark/stark.test.js';
|
||||||
import './jubjub.test.js';
|
import './jubjub.test.js';
|
||||||
import './bls12-381.test.js';
|
// import './bls12-381.test.js';
|
||||||
import './hash-to-curve.test.js';
|
import './hash-to-curve.test.js';
|
||||||
|
|
||||||
should.run();
|
should.run();
|
||||||
|
@ -76,92 +76,94 @@ describe('secp256k1', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Point.isValidPoint()', () => {
|
describe('Point', () => {
|
||||||
for (const vector of points.valid.isPoint) {
|
should('fromHex() assertValidity', () => {
|
||||||
const { P, expected } = vector;
|
for (const vector of points.valid.isPoint) {
|
||||||
if (expected) {
|
const { P, expected } = vector;
|
||||||
secp.Point.fromHex(P);
|
if (expected) {
|
||||||
} else {
|
secp.Point.fromHex(P);
|
||||||
throws(() => secp.Point.fromHex(P));
|
} else {
|
||||||
}
|
throws(() => secp.Point.fromHex(P));
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Point.fromPrivateKey()', () => {
|
|
||||||
for (const vector of points.valid.pointFromScalar) {
|
|
||||||
const { d, expected } = vector;
|
|
||||||
let p = secp.Point.fromPrivateKey(d);
|
|
||||||
deepStrictEqual(p.toHex(true), expected);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Point#toHex(compressed)', () => {
|
|
||||||
for (const vector of points.valid.pointCompress) {
|
|
||||||
const { P, compress, expected } = vector;
|
|
||||||
let p = secp.Point.fromHex(P);
|
|
||||||
deepStrictEqual(p.toHex(compress), expected);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Point#toHex() roundtrip (failed case)', () => {
|
|
||||||
const point1 =
|
|
||||||
secp.Point.fromPrivateKey(
|
|
||||||
88572218780422190464634044548753414301110513745532121983949500266768436236425n
|
|
||||||
);
|
|
||||||
// const hex = point1.toHex(true);
|
|
||||||
// deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Point#toHex() roundtrip', () => {
|
|
||||||
fc.assert(
|
|
||||||
fc.property(FC_BIGINT, (x) => {
|
|
||||||
const point1 = secp.Point.fromPrivateKey(x);
|
|
||||||
const hex = point1.toHex(true);
|
|
||||||
deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Point#add(other)', () => {
|
|
||||||
for (const vector of points.valid.pointAdd) {
|
|
||||||
const { P, Q, expected } = vector;
|
|
||||||
let p = secp.Point.fromHex(P);
|
|
||||||
let q = secp.Point.fromHex(Q);
|
|
||||||
if (expected) {
|
|
||||||
deepStrictEqual(p.add(q).toHex(true), expected);
|
|
||||||
} else {
|
|
||||||
if (!p.equals(q.negate())) {
|
|
||||||
throws(() => p.add(q).toHex(true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
should('Point#multiply(privateKey)', () => {
|
should('.fromPrivateKey()', () => {
|
||||||
for (const vector of points.valid.pointMultiply) {
|
for (const vector of points.valid.pointFromScalar) {
|
||||||
const { P, d, expected } = vector;
|
const { d, expected } = vector;
|
||||||
const p = secp.Point.fromHex(P);
|
let p = secp.Point.fromPrivateKey(d);
|
||||||
if (expected) {
|
deepStrictEqual(p.toHex(true), expected);
|
||||||
deepStrictEqual(p.multiply(hexToNumber(d)).toHex(true), expected);
|
|
||||||
} else {
|
|
||||||
throws(() => {
|
|
||||||
p.multiply(hexToNumber(d)).toHex(true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
for (const vector of points.invalid.pointMultiply) {
|
should('#toHex(compressed)', () => {
|
||||||
const { P, d } = vector;
|
for (const vector of points.valid.pointCompress) {
|
||||||
if (hexToNumber(d) < secp.CURVE.n) {
|
const { P, compress, expected } = vector;
|
||||||
throws(() => {
|
let p = secp.Point.fromHex(P);
|
||||||
const p = secp.Point.fromHex(P);
|
deepStrictEqual(p.toHex(compress), expected);
|
||||||
p.multiply(hexToNumber(d)).toHex(true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
|
||||||
throws(() => secp.Point.BASE.multiply(num));
|
should('#toHex() roundtrip (failed case)', () => {
|
||||||
}
|
const point1 =
|
||||||
|
secp.Point.fromPrivateKey(
|
||||||
|
88572218780422190464634044548753414301110513745532121983949500266768436236425n
|
||||||
|
);
|
||||||
|
// const hex = point1.toHex(true);
|
||||||
|
// deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
||||||
|
});
|
||||||
|
|
||||||
|
should('#toHex() roundtrip', () => {
|
||||||
|
fc.assert(
|
||||||
|
fc.property(FC_BIGINT, (x) => {
|
||||||
|
const point1 = secp.Point.fromPrivateKey(x);
|
||||||
|
const hex = point1.toHex(true);
|
||||||
|
deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
should('#add(other)', () => {
|
||||||
|
for (const vector of points.valid.pointAdd) {
|
||||||
|
const { P, Q, expected } = vector;
|
||||||
|
let p = secp.Point.fromHex(P);
|
||||||
|
let q = secp.Point.fromHex(Q);
|
||||||
|
if (expected) {
|
||||||
|
deepStrictEqual(p.add(q).toHex(true), expected);
|
||||||
|
} else {
|
||||||
|
if (!p.equals(q.negate())) {
|
||||||
|
throws(() => p.add(q).toHex(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
should('#multiply(privateKey)', () => {
|
||||||
|
for (const vector of points.valid.pointMultiply) {
|
||||||
|
const { P, d, expected } = vector;
|
||||||
|
const p = secp.Point.fromHex(P);
|
||||||
|
if (expected) {
|
||||||
|
deepStrictEqual(p.multiply(hexToNumber(d)).toHex(true), expected);
|
||||||
|
} else {
|
||||||
|
throws(() => {
|
||||||
|
p.multiply(hexToNumber(d)).toHex(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const vector of points.invalid.pointMultiply) {
|
||||||
|
const { P, d } = vector;
|
||||||
|
if (hexToNumber(d) < secp.CURVE.n) {
|
||||||
|
throws(() => {
|
||||||
|
const p = secp.Point.fromHex(P);
|
||||||
|
p.multiply(hexToNumber(d)).toHex(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
||||||
|
throws(() => secp.Point.BASE.multiply(num));
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// multiply() should equal multiplyUnsafe()
|
// multiply() should equal multiplyUnsafe()
|
||||||
@ -175,262 +177,269 @@ describe('secp256k1', () => {
|
|||||||
// console.log(p0.multiply(z));
|
// console.log(p0.multiply(z));
|
||||||
// console.log(secp.ProjectivePoint.normalizeZ([p0.multiplyUnsafe(z)])[0])
|
// console.log(secp.ProjectivePoint.normalizeZ([p0.multiplyUnsafe(z)])[0])
|
||||||
// });
|
// });
|
||||||
|
describe('Signature', () => {
|
||||||
|
should('.fromCompactHex() roundtrip', () => {
|
||||||
|
fc.assert(
|
||||||
|
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||||
|
const sig = new secp.Signature(r, s);
|
||||||
|
deepStrictEqual(secp.Signature.fromCompact(sig.toCompactHex()), sig);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
should('Signature.fromCompactHex() roundtrip', () => {
|
should('.fromDERHex() roundtrip', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||||
const sig = new secp.Signature(r, s);
|
const sig = new secp.Signature(r, s);
|
||||||
deepStrictEqual(secp.Signature.fromCompact(sig.toCompactHex()), sig);
|
deepStrictEqual(secp.Signature.fromDER(sig.toDERHex()), sig);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Signature.fromDERHex() roundtrip', () => {
|
describe('sign()', () => {
|
||||||
fc.assert(
|
should('create deterministic signatures with RFC 6979', () => {
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
for (const vector of ecdsa.valid) {
|
||||||
const sig = new secp.Signature(r, s);
|
let usig = secp.sign(vector.m, vector.d);
|
||||||
deepStrictEqual(secp.Signature.fromDER(sig.toDERHex()), sig);
|
let sig = usig.toCompactHex();
|
||||||
})
|
const vsig = vector.signature;
|
||||||
);
|
deepStrictEqual(sig.slice(0, 64), vsig.slice(0, 64));
|
||||||
});
|
deepStrictEqual(sig.slice(64, 128), vsig.slice(64, 128));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
should('sign()/should create deterministic signatures with RFC 6979', () => {
|
should('not create invalid deterministic signatures with RFC 6979', () => {
|
||||||
for (const vector of ecdsa.valid) {
|
|
||||||
let usig = secp.sign(vector.m, vector.d);
|
|
||||||
let sig = usig.toCompactHex();
|
|
||||||
const vsig = vector.signature;
|
|
||||||
deepStrictEqual(sig.slice(0, 64), vsig.slice(0, 64));
|
|
||||||
deepStrictEqual(sig.slice(64, 128), vsig.slice(64, 128));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
should(
|
|
||||||
'secp256k1.sign()/should not create invalid deterministic signatures with RFC 6979',
|
|
||||||
() => {
|
|
||||||
for (const vector of ecdsa.invalid.sign) {
|
for (const vector of ecdsa.invalid.sign) {
|
||||||
throws(() => secp.sign(vector.m, vector.d));
|
throws(() => secp.sign(vector.m, vector.d));
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
should('sign()/edge cases', () => {
|
should('edge cases', () => {
|
||||||
throws(() => secp.sign());
|
throws(() => secp.sign());
|
||||||
throws(() => secp.sign(''));
|
throws(() => secp.sign(''));
|
||||||
|
});
|
||||||
|
|
||||||
|
should('create correct DER encoding against libsecp256k1', () => {
|
||||||
|
const CASES = [
|
||||||
|
[
|
||||||
|
'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b',
|
||||||
|
'304402203de2559fccb00c148574997f660e4d6f40605acc71267ee38101abf15ff467af02200950abdf40628fd13f547792ba2fc544681a485f2fdafb5c3b909a4df7350e6b',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'5f97983254982546d3976d905c6165033976ee449d300d0e382099fa74deaf82',
|
||||||
|
'3045022100c046d9ff0bd2845b9aa9dff9f997ecebb31e52349f80fe5a5a869747d31dcb88022011f72be2a6d48fe716b825e4117747b397783df26914a58139c3f4c5cbb0e66c',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0d7017a96b97cd9be21cf28aada639827b2814a654a478c81945857196187808',
|
||||||
|
'3045022100d18990bba7832bb283e3ecf8700b67beb39acc73f4200ed1c331247c46edccc602202e5c8bbfe47ae159512c583b30a3fa86575cddc62527a03de7756517ae4c6c73',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
const privKey = hexToBytes(
|
||||||
|
'0101010101010101010101010101010101010101010101010101010101010101'
|
||||||
|
);
|
||||||
|
for (const [msg, exp] of CASES) {
|
||||||
|
const res = secp.sign(msg, privKey, { extraEntropy: undefined });
|
||||||
|
deepStrictEqual(res.toDERHex(), exp);
|
||||||
|
const rs = secp.Signature.fromDER(res.toDERHex()).toCompactHex();
|
||||||
|
deepStrictEqual(secp.Signature.fromCompact(rs).toDERHex(), exp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('handle {extraData} option', () => {
|
||||||
|
const ent1 = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||||
|
const ent2 = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||||
|
const ent3 = '6e723d3fd94ed5d2b6bdd4f123364b0f3ca52af829988a63f8afe91d29db1c33';
|
||||||
|
const ent4 = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
|
||||||
|
const ent5 = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
|
||||||
|
|
||||||
|
for (const e of ecdsa.extraEntropy) {
|
||||||
|
const sign = (extraEntropy) => {
|
||||||
|
const s = secp.sign(e.m, e.d, { extraEntropy }).toCompactHex();
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
deepStrictEqual(sign(), e.signature);
|
||||||
|
deepStrictEqual(sign(ent1), e.extraEntropy0);
|
||||||
|
deepStrictEqual(sign(ent2), e.extraEntropy1);
|
||||||
|
deepStrictEqual(sign(ent3), e.extraEntropyRand);
|
||||||
|
deepStrictEqual(sign(ent4), e.extraEntropyN);
|
||||||
|
deepStrictEqual(sign(ent5), e.extraEntropyMax);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
should('sign()/should create correct DER encoding against libsecp256k1', () => {
|
describe('verify()', () => {
|
||||||
const CASES = [
|
should('verify signature', () => {
|
||||||
[
|
const MSG = '01'.repeat(32);
|
||||||
'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b',
|
const PRIV_KEY = 0x2n;
|
||||||
'304402203de2559fccb00c148574997f660e4d6f40605acc71267ee38101abf15ff467af02200950abdf40628fd13f547792ba2fc544681a485f2fdafb5c3b909a4df7350e6b',
|
const signature = secp.sign(MSG, PRIV_KEY);
|
||||||
],
|
const publicKey = secp.getPublicKey(PRIV_KEY);
|
||||||
[
|
deepStrictEqual(publicKey.length, 33);
|
||||||
'5f97983254982546d3976d905c6165033976ee449d300d0e382099fa74deaf82',
|
deepStrictEqual(secp.verify(signature, MSG, publicKey), true);
|
||||||
'3045022100c046d9ff0bd2845b9aa9dff9f997ecebb31e52349f80fe5a5a869747d31dcb88022011f72be2a6d48fe716b825e4117747b397783df26914a58139c3f4c5cbb0e66c',
|
});
|
||||||
],
|
should(' not verify signature with wrong public key', () => {
|
||||||
[
|
const MSG = '01'.repeat(32);
|
||||||
'0d7017a96b97cd9be21cf28aada639827b2814a654a478c81945857196187808',
|
const PRIV_KEY = 0x2n;
|
||||||
'3045022100d18990bba7832bb283e3ecf8700b67beb39acc73f4200ed1c331247c46edccc602202e5c8bbfe47ae159512c583b30a3fa86575cddc62527a03de7756517ae4c6c73',
|
const WRONG_PRIV_KEY = 0x22n;
|
||||||
],
|
const signature = secp.sign(MSG, PRIV_KEY);
|
||||||
];
|
const publicKey = secp.Point.fromPrivateKey(WRONG_PRIV_KEY).toHex();
|
||||||
const privKey = hexToBytes('0101010101010101010101010101010101010101010101010101010101010101');
|
deepStrictEqual(publicKey.length, 66);
|
||||||
for (const [msg, exp] of CASES) {
|
deepStrictEqual(secp.verify(signature, MSG, publicKey), false);
|
||||||
const res = secp.sign(msg, privKey, { extraEntropy: undefined });
|
});
|
||||||
deepStrictEqual(res.toDERHex(), exp);
|
should('not verify signature with wrong hash', () => {
|
||||||
const rs = secp.Signature.fromDER(res.toDERHex()).toCompactHex();
|
const MSG = '01'.repeat(32);
|
||||||
deepStrictEqual(secp.Signature.fromCompact(rs).toDERHex(), exp);
|
const PRIV_KEY = 0x2n;
|
||||||
}
|
const WRONG_MSG = '11'.repeat(32);
|
||||||
});
|
const signature = secp.sign(MSG, PRIV_KEY);
|
||||||
should('sign()/sign ecdsa extraData', () => {
|
const publicKey = secp.getPublicKey(PRIV_KEY);
|
||||||
const ent1 = '0000000000000000000000000000000000000000000000000000000000000000';
|
deepStrictEqual(publicKey.length, 33);
|
||||||
const ent2 = '0000000000000000000000000000000000000000000000000000000000000001';
|
deepStrictEqual(secp.verify(signature, WRONG_MSG, publicKey), false);
|
||||||
const ent3 = '6e723d3fd94ed5d2b6bdd4f123364b0f3ca52af829988a63f8afe91d29db1c33';
|
});
|
||||||
const ent4 = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
|
should('verify random signatures', () =>
|
||||||
const ent5 = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
|
fc.assert(
|
||||||
|
fc.property(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privKey, msg) => {
|
||||||
|
const pub = secp.getPublicKey(privKey);
|
||||||
|
const sig = secp.sign(msg, privKey);
|
||||||
|
deepStrictEqual(secp.verify(sig, msg, pub), true);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
should('not verify signature with invalid r/s', () => {
|
||||||
|
const msg = new Uint8Array([
|
||||||
|
0xbb, 0x5a, 0x52, 0xf4, 0x2f, 0x9c, 0x92, 0x61, 0xed, 0x43, 0x61, 0xf5, 0x94, 0x22, 0xa1,
|
||||||
|
0xe3, 0x00, 0x36, 0xe7, 0xc3, 0x2b, 0x27, 0x0c, 0x88, 0x07, 0xa4, 0x19, 0xfe, 0xca, 0x60,
|
||||||
|
0x50, 0x23,
|
||||||
|
]);
|
||||||
|
const x = 100260381870027870612475458630405506840396644859280795015145920502443964769584n;
|
||||||
|
const y = 41096923727651821103518389640356553930186852801619204169823347832429067794568n;
|
||||||
|
const r = 1n;
|
||||||
|
const s = 115792089237316195423570985008687907852837564279074904382605163141518162728904n;
|
||||||
|
|
||||||
for (const e of ecdsa.extraEntropy) {
|
const pub = new secp.Point(x, y);
|
||||||
const sign = (extraEntropy) => {
|
const signature = new secp.Signature(2n, 2n);
|
||||||
const s = secp.sign(e.m, e.d, { extraEntropy }).toCompactHex();
|
signature.r = r;
|
||||||
return s;
|
signature.s = s;
|
||||||
};
|
|
||||||
deepStrictEqual(sign(), e.signature);
|
|
||||||
deepStrictEqual(sign(ent1), e.extraEntropy0);
|
|
||||||
deepStrictEqual(sign(ent2), e.extraEntropy1);
|
|
||||||
deepStrictEqual(sign(ent3), e.extraEntropyRand);
|
|
||||||
deepStrictEqual(sign(ent4), e.extraEntropyN);
|
|
||||||
deepStrictEqual(sign(ent5), e.extraEntropyMax);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
should('verify()/should verify signature', () => {
|
const verified = secp.verify(signature, msg, pub);
|
||||||
const MSG = '01'.repeat(32);
|
// Verifies, but it shouldn't, because signature S > curve order
|
||||||
const PRIV_KEY = 0x2n;
|
deepStrictEqual(verified, false);
|
||||||
const signature = secp.sign(MSG, PRIV_KEY);
|
});
|
||||||
const publicKey = secp.getPublicKey(PRIV_KEY);
|
should('not verify msg = curve order', () => {
|
||||||
deepStrictEqual(publicKey.length, 65);
|
const msg = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
|
||||||
deepStrictEqual(secp.verify(signature, MSG, publicKey), true);
|
const x = 55066263022277343669578718895168534326250603453777594175500187360389116729240n;
|
||||||
});
|
const y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
|
||||||
should('verify()/should not verify signature with wrong public key', () => {
|
const r = 104546003225722045112039007203142344920046999340768276760147352389092131869133n;
|
||||||
const MSG = '01'.repeat(32);
|
const s = 96900796730960181123786672629079577025401317267213807243199432755332205217369n;
|
||||||
const PRIV_KEY = 0x2n;
|
const pub = new secp.Point(x, y);
|
||||||
const WRONG_PRIV_KEY = 0x22n;
|
const sig = new secp.Signature(r, s);
|
||||||
const signature = secp.sign(MSG, PRIV_KEY);
|
deepStrictEqual(secp.verify(sig, msg, pub), false);
|
||||||
const publicKey = secp.Point.fromPrivateKey(WRONG_PRIV_KEY).toHex();
|
});
|
||||||
deepStrictEqual(publicKey.length, 130);
|
should('verify non-strict msg bb5a...', () => {
|
||||||
deepStrictEqual(secp.verify(signature, MSG, publicKey), false);
|
const msg = 'bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023';
|
||||||
});
|
const x = 3252872872578928810725465493269682203671229454553002637820453004368632726370n;
|
||||||
should('verify()/should not verify signature with wrong hash', () => {
|
const y = 17482644437196207387910659778872952193236850502325156318830589868678978890912n;
|
||||||
const MSG = '01'.repeat(32);
|
const r = 432420386565659656852420866390673177323n;
|
||||||
const PRIV_KEY = 0x2n;
|
const s = 115792089237316195423570985008687907852837564279074904382605163141518161494334n;
|
||||||
const WRONG_MSG = '11'.repeat(32);
|
const pub = new secp.Point(x, y);
|
||||||
const signature = secp.sign(MSG, PRIV_KEY);
|
const sig = new secp.Signature(r, s);
|
||||||
const publicKey = secp.getPublicKey(PRIV_KEY);
|
deepStrictEqual(secp.verify(sig, msg, pub, { strict: false }), true);
|
||||||
deepStrictEqual(publicKey.length, 65);
|
});
|
||||||
deepStrictEqual(secp.verify(signature, WRONG_MSG, publicKey), false);
|
should('not verify invalid deterministic signatures with RFC 6979', () => {
|
||||||
});
|
|
||||||
should('verify()/should verify random signatures', () =>
|
|
||||||
fc.assert(
|
|
||||||
fc.property(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privKey, msg) => {
|
|
||||||
const pub = secp.getPublicKey(privKey);
|
|
||||||
const sig = secp.sign(msg, privKey);
|
|
||||||
deepStrictEqual(secp.verify(sig, msg, pub), true);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
should('verify()/should not verify signature with invalid r/s', () => {
|
|
||||||
const msg = new Uint8Array([
|
|
||||||
0xbb, 0x5a, 0x52, 0xf4, 0x2f, 0x9c, 0x92, 0x61, 0xed, 0x43, 0x61, 0xf5, 0x94, 0x22, 0xa1,
|
|
||||||
0xe3, 0x00, 0x36, 0xe7, 0xc3, 0x2b, 0x27, 0x0c, 0x88, 0x07, 0xa4, 0x19, 0xfe, 0xca, 0x60,
|
|
||||||
0x50, 0x23,
|
|
||||||
]);
|
|
||||||
const x = 100260381870027870612475458630405506840396644859280795015145920502443964769584n;
|
|
||||||
const y = 41096923727651821103518389640356553930186852801619204169823347832429067794568n;
|
|
||||||
const r = 1n;
|
|
||||||
const s = 115792089237316195423570985008687907852837564279074904382605163141518162728904n;
|
|
||||||
|
|
||||||
const pub = new secp.Point(x, y);
|
|
||||||
const signature = new secp.Signature(2n, 2n);
|
|
||||||
signature.r = r;
|
|
||||||
signature.s = s;
|
|
||||||
|
|
||||||
const verified = secp.verify(signature, msg, pub);
|
|
||||||
// Verifies, but it shouldn't, because signature S > curve order
|
|
||||||
deepStrictEqual(verified, false);
|
|
||||||
});
|
|
||||||
should('verify()/should not verify msg = curve order', () => {
|
|
||||||
const msg = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
|
|
||||||
const x = 55066263022277343669578718895168534326250603453777594175500187360389116729240n;
|
|
||||||
const y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
|
|
||||||
const r = 104546003225722045112039007203142344920046999340768276760147352389092131869133n;
|
|
||||||
const s = 96900796730960181123786672629079577025401317267213807243199432755332205217369n;
|
|
||||||
const pub = new secp.Point(x, y);
|
|
||||||
const sig = new secp.Signature(r, s);
|
|
||||||
deepStrictEqual(secp.verify(sig, msg, pub), false);
|
|
||||||
});
|
|
||||||
should('verify()/should verify non-strict msg bb5a...', () => {
|
|
||||||
const msg = 'bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023';
|
|
||||||
const x = 3252872872578928810725465493269682203671229454553002637820453004368632726370n;
|
|
||||||
const y = 17482644437196207387910659778872952193236850502325156318830589868678978890912n;
|
|
||||||
const r = 432420386565659656852420866390673177323n;
|
|
||||||
const s = 115792089237316195423570985008687907852837564279074904382605163141518161494334n;
|
|
||||||
const pub = new secp.Point(x, y);
|
|
||||||
const sig = new secp.Signature(r, s);
|
|
||||||
deepStrictEqual(secp.verify(sig, msg, pub, { strict: false }), true);
|
|
||||||
});
|
|
||||||
should(
|
|
||||||
'secp256k1.verify()/should not verify invalid deterministic signatures with RFC 6979',
|
|
||||||
() => {
|
|
||||||
for (const vector of ecdsa.invalid.verify) {
|
for (const vector of ecdsa.invalid.verify) {
|
||||||
const res = secp.verify(vector.signature, vector.m, vector.Q);
|
const res = secp.verify(vector.signature, vector.m, vector.Q);
|
||||||
deepStrictEqual(res, false);
|
deepStrictEqual(res, false);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
});
|
||||||
|
|
||||||
// index,secret key,public key,aux_rand,message,signature,verification result,comment
|
describe('schnorr.sign()', () => {
|
||||||
const vectors = schCsv
|
// index,secret key,public key,aux_rand,message,signature,verification result,comment
|
||||||
.split('\n')
|
const vectors = schCsv
|
||||||
.map((line) => line.split(','))
|
.split('\n')
|
||||||
.slice(1, -1);
|
.map((line) => line.split(','))
|
||||||
for (let vec of vectors) {
|
.slice(1, -1);
|
||||||
const [index, sec, pub, rnd, msg, expSig, passes, comment] = vec;
|
for (let vec of vectors) {
|
||||||
should(`sign with Schnorr scheme vector ${index}`, () => {
|
const [index, sec, pub, rnd, msg, expSig, passes, comment] = vec;
|
||||||
if (sec) {
|
should(`${comment || 'vector ' + index}`, () => {
|
||||||
deepStrictEqual(hex(schnorr.getPublicKey(sec)), pub.toLowerCase());
|
if (sec) {
|
||||||
const sig = schnorr.sign(msg, sec, rnd);
|
deepStrictEqual(hex(schnorr.getPublicKey(sec)), pub.toLowerCase());
|
||||||
deepStrictEqual(hex(sig), expSig.toLowerCase());
|
const sig = schnorr.sign(msg, sec, rnd);
|
||||||
deepStrictEqual(schnorr.verify(sig, msg, pub), true);
|
deepStrictEqual(hex(sig), expSig.toLowerCase());
|
||||||
} else {
|
deepStrictEqual(schnorr.verify(sig, msg, pub), true);
|
||||||
const passed = schnorr.verify(expSig, msg, pub);
|
} else {
|
||||||
deepStrictEqual(passed, passes === 'TRUE');
|
const passed = schnorr.verify(expSig, msg, pub);
|
||||||
|
deepStrictEqual(passed, passes === 'TRUE');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('recoverPublicKey()', () => {
|
||||||
|
should('recover public key from recovery bit', () => {
|
||||||
|
const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
|
||||||
|
const privateKey = 123456789n;
|
||||||
|
const publicKey = secp.Point.fromHex(secp.getPublicKey(privateKey)).toHex(false);
|
||||||
|
const sig = secp.sign(message, privateKey);
|
||||||
|
const recoveredPubkey = sig.recoverPublicKey(message);
|
||||||
|
// const recoveredPubkey = secp.recoverPublicKey(message, signature, recovery);
|
||||||
|
deepStrictEqual(recoveredPubkey !== null, true);
|
||||||
|
deepStrictEqual(recoveredPubkey.toHex(false), publicKey);
|
||||||
|
deepStrictEqual(secp.verify(sig, message, publicKey), true);
|
||||||
|
});
|
||||||
|
should('not recover zero points', () => {
|
||||||
|
const msgHash = '6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
||||||
|
const sig =
|
||||||
|
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
||||||
|
const recovery = 0;
|
||||||
|
throws(() => secp.recoverPublicKey(msgHash, sig, recovery));
|
||||||
|
});
|
||||||
|
should('handle all-zeros msghash', () => {
|
||||||
|
const privKey = secp.utils.randomPrivateKey();
|
||||||
|
const pub = secp.getPublicKey(privKey);
|
||||||
|
const zeros = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||||
|
const sig = secp.sign(zeros, privKey);
|
||||||
|
const recoveredKey = sig.recoverPublicKey(zeros);
|
||||||
|
deepStrictEqual(recoveredKey.toRawBytes(), pub);
|
||||||
|
});
|
||||||
|
should('handle RFC 6979 vectors', () => {
|
||||||
|
for (const vector of ecdsa.valid) {
|
||||||
|
let usig = secp.sign(vector.m, vector.d);
|
||||||
|
let sig = usig.toDERHex();
|
||||||
|
const vpub = secp.getPublicKey(vector.d);
|
||||||
|
const recovered = usig.recoverPublicKey(vector.m);
|
||||||
|
deepStrictEqual(recovered.toHex(), hex(vpub));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
should('recoverPublicKey()/should recover public key from recovery bit', () => {
|
|
||||||
const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
|
|
||||||
const privateKey = 123456789n;
|
|
||||||
const publicKey = secp.Point.fromHex(secp.getPublicKey(privateKey)).toHex(false);
|
|
||||||
const sig = secp.sign(message, privateKey);
|
|
||||||
const recoveredPubkey = sig.recoverPublicKey(message);
|
|
||||||
// const recoveredPubkey = secp.recoverPublicKey(message, signature, recovery);
|
|
||||||
deepStrictEqual(recoveredPubkey !== null, true);
|
|
||||||
deepStrictEqual(recoveredPubkey.toHex(false), publicKey);
|
|
||||||
deepStrictEqual(secp.verify(sig, message, publicKey), true);
|
|
||||||
});
|
|
||||||
should('recoverPublicKey()/should not recover zero points', () => {
|
|
||||||
const msgHash = '6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
|
||||||
const sig =
|
|
||||||
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
|
||||||
const recovery = 0;
|
|
||||||
throws(() => secp.recoverPublicKey(msgHash, sig, recovery));
|
|
||||||
});
|
|
||||||
should('recoverPublicKey()/should handle all-zeros msghash', () => {
|
|
||||||
const privKey = secp.utils.randomPrivateKey();
|
|
||||||
const pub = secp.getPublicKey(privKey);
|
|
||||||
const zeros = '0000000000000000000000000000000000000000000000000000000000000000';
|
|
||||||
const sig = secp.sign(zeros, privKey, { recovered: true });
|
|
||||||
const recoveredKey = sig.recoverPublicKey(zeros);
|
|
||||||
deepStrictEqual(recoveredKey.toRawBytes(), pub);
|
|
||||||
});
|
|
||||||
should('recoverPublicKey()/should handle RFC 6979 vectors', () => {
|
|
||||||
for (const vector of ecdsa.valid) {
|
|
||||||
let usig = secp.sign(vector.m, vector.d);
|
|
||||||
let sig = usig.toDERHex();
|
|
||||||
const vpub = secp.getPublicKey(vector.d);
|
|
||||||
const recovered = usig.recoverPublicKey(vector.m);
|
|
||||||
deepStrictEqual(recovered.toHex(), hex(vpub));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Real implementation.
|
describe('getSharedSecret()', () => {
|
||||||
function derToPub(der) {
|
// TODO: Real implementation.
|
||||||
return der.slice(46);
|
function derToPub(der) {
|
||||||
}
|
return der.slice(46);
|
||||||
should('getSharedSecret()/should produce correct results', () => {
|
|
||||||
// TODO: Once der is there, run all tests.
|
|
||||||
for (const vector of ecdh.testGroups[0].tests.slice(0, 230)) {
|
|
||||||
if (vector.result === 'invalid' || vector.private.length !== 64) {
|
|
||||||
throws(() => {
|
|
||||||
secp.getSharedSecret(vector.private, derToPub(vector.public), true);
|
|
||||||
});
|
|
||||||
} else if (vector.result === 'valid') {
|
|
||||||
const res = secp.getSharedSecret(vector.private, derToPub(vector.public), true);
|
|
||||||
deepStrictEqual(hex(res.slice(1)), `${vector.shared}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
should('produce correct results', () => {
|
||||||
should('getSharedSecret()/priv/pub order matters', () => {
|
// TODO: Once der is there, run all tests.
|
||||||
for (const vector of ecdh.testGroups[0].tests.slice(0, 100)) {
|
for (const vector of ecdh.testGroups[0].tests.slice(0, 230)) {
|
||||||
if (vector.result === 'valid') {
|
if (vector.result === 'invalid' || vector.private.length !== 64) {
|
||||||
let priv = vector.private;
|
throws(() => {
|
||||||
priv = priv.length === 66 ? priv.slice(2) : priv;
|
secp.getSharedSecret(vector.private, derToPub(vector.public), true);
|
||||||
throws(() => secp.getSharedSecret(derToPub(vector.public), priv, true));
|
});
|
||||||
|
} else if (vector.result === 'valid') {
|
||||||
|
const res = secp.getSharedSecret(vector.private, derToPub(vector.public), true);
|
||||||
|
deepStrictEqual(hex(res.slice(1)), `${vector.shared}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
should('priv/pub order matters', () => {
|
||||||
should('getSharedSecret()/rejects invalid keys', () => {
|
for (const vector of ecdh.testGroups[0].tests.slice(0, 100)) {
|
||||||
throws(() => secp.getSharedSecret('01', '02'));
|
if (vector.result === 'valid') {
|
||||||
|
let priv = vector.private;
|
||||||
|
priv = priv.length === 66 ? priv.slice(2) : priv;
|
||||||
|
throws(() => secp.getSharedSecret(derToPub(vector.public), priv, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('reject invalid keys', () => {
|
||||||
|
throws(() => secp.getSharedSecret('01', '02'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
should('utils.isValidPrivateKey()', () => {
|
should('utils.isValidPrivateKey()', () => {
|
||||||
@ -447,72 +456,74 @@ describe('secp256k1', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const Fn = Fp(secp.CURVE.n);
|
describe('tweak utilities (legacy)', () => {
|
||||||
const normal = secp.utils._normalizePrivateKey;
|
const Fn = Fp(secp.CURVE.n);
|
||||||
const tweakUtils = {
|
const normal = secp.utils._normalizePrivateKey;
|
||||||
privateAdd: (privateKey, tweak) => {
|
const tweakUtils = {
|
||||||
const p = normal(privateKey);
|
privateAdd: (privateKey, tweak) => {
|
||||||
const t = normal(tweak);
|
const p = normal(privateKey);
|
||||||
return secp.utils._bigintToBytes(Fn.create(p + t));
|
const t = normal(tweak);
|
||||||
},
|
return secp.utils._bigintToBytes(Fn.create(p + t));
|
||||||
|
},
|
||||||
|
|
||||||
privateNegate: (privateKey) => {
|
privateNegate: (privateKey) => {
|
||||||
const p = normal(privateKey);
|
const p = normal(privateKey);
|
||||||
return secp.utils._bigintToBytes(Fn.negate(p));
|
return secp.utils._bigintToBytes(Fn.negate(p));
|
||||||
},
|
},
|
||||||
|
|
||||||
pointAddScalar: (p, tweak, isCompressed) => {
|
pointAddScalar: (p, tweak, isCompressed) => {
|
||||||
const P = secp.Point.fromHex(p);
|
const P = secp.Point.fromHex(p);
|
||||||
const t = normal(tweak);
|
const t = normal(tweak);
|
||||||
const Q = secp.Point.BASE.multiplyAndAddUnsafe(P, t, 1n);
|
const Q = secp.Point.BASE.multiplyAndAddUnsafe(P, t, 1n);
|
||||||
if (!Q) throw new Error('Tweaked point at infinity');
|
if (!Q) throw new Error('Tweaked point at infinity');
|
||||||
return Q.toRawBytes(isCompressed);
|
return Q.toRawBytes(isCompressed);
|
||||||
},
|
},
|
||||||
|
|
||||||
pointMultiply: (p, tweak, isCompressed) => {
|
pointMultiply: (p, tweak, isCompressed) => {
|
||||||
const P = secp.Point.fromHex(p);
|
const P = secp.Point.fromHex(p);
|
||||||
const h = typeof tweak === 'string' ? tweak : bytesToHex(tweak);
|
const h = typeof tweak === 'string' ? tweak : bytesToHex(tweak);
|
||||||
const t = BigInt(`0x${h}`);
|
const t = BigInt(`0x${h}`);
|
||||||
return P.multiply(t).toRawBytes(isCompressed);
|
return P.multiply(t).toRawBytes(isCompressed);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
should('privateAdd()', () => {
|
should('privateAdd()', () => {
|
||||||
for (const vector of privates.valid.add) {
|
for (const vector of privates.valid.add) {
|
||||||
const { a, b, expected } = vector;
|
const { a, b, expected } = vector;
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.privateAdd(a, b)), expected);
|
deepStrictEqual(bytesToHex(tweakUtils.privateAdd(a, b)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('privateNegate()', () => {
|
should('privateNegate()', () => {
|
||||||
for (const vector of privates.valid.negate) {
|
for (const vector of privates.valid.negate) {
|
||||||
const { a, expected } = vector;
|
const { a, expected } = vector;
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.privateNegate(a)), expected);
|
deepStrictEqual(bytesToHex(tweakUtils.privateNegate(a)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('pointAddScalar()', () => {
|
should('pointAddScalar()', () => {
|
||||||
for (const vector of points.valid.pointAddScalar) {
|
for (const vector of points.valid.pointAddScalar) {
|
||||||
const { description, P, d, expected } = vector;
|
const { description, P, d, expected } = vector;
|
||||||
const compressed = !!expected && expected.length === 66; // compressed === 33 bytes
|
const compressed = !!expected && expected.length === 66; // compressed === 33 bytes
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.pointAddScalar(P, d, compressed)), expected);
|
deepStrictEqual(bytesToHex(tweakUtils.pointAddScalar(P, d, compressed)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('pointAddScalar() invalid', () => {
|
should('pointAddScalar() invalid', () => {
|
||||||
for (const vector of points.invalid.pointAddScalar) {
|
for (const vector of points.invalid.pointAddScalar) {
|
||||||
const { P, d, exception } = vector;
|
const { P, d, exception } = vector;
|
||||||
throws(() => tweakUtils.pointAddScalar(P, d));
|
throws(() => tweakUtils.pointAddScalar(P, d));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('pointMultiply()', () => {
|
should('pointMultiply()', () => {
|
||||||
for (const vector of points.valid.pointMultiply) {
|
for (const vector of points.valid.pointMultiply) {
|
||||||
const { P, d, expected } = vector;
|
const { P, d, expected } = vector;
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.pointMultiply(P, d, true)), expected);
|
deepStrictEqual(bytesToHex(tweakUtils.pointMultiply(P, d, true)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('pointMultiply() invalid', () => {
|
should('pointMultiply() invalid', () => {
|
||||||
for (const vector of points.invalid.pointMultiply) {
|
for (const vector of points.invalid.pointMultiply) {
|
||||||
const { P, d, exception } = vector;
|
const { P, d, exception } = vector;
|
||||||
throws(() => tweakUtils.pointMultiply(P, d));
|
throws(() => tweakUtils.pointMultiply(P, d));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
should('wychenproof vectors', () => {
|
should('wychenproof vectors', () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user