Edwards: prohibit number scalars, only allow bigints

This commit is contained in:
Paul Miller 2023-01-23 19:28:01 +00:00
parent 055147f1be
commit 11e78aadbf
No known key found for this signature in database
GPG Key ID: 697079DA6878B89B
4 changed files with 19 additions and 14 deletions

@ -170,16 +170,21 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
* https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates * https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
*/ */
class ExtendedPoint implements ExtendedPointType { class ExtendedPoint implements ExtendedPointType {
constructor(readonly x: bigint, readonly y: bigint, readonly z: bigint, readonly t: bigint) {} constructor(
readonly x: bigint,
readonly y: bigint,
readonly z = _1n,
readonly t = modP(x * y)
) {}
static BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy)); static BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy);
static ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n); static ZERO = new ExtendedPoint(_0n, _1n); // 0, 1, 1, 0
static fromAffine(p: Point): ExtendedPoint { static fromAffine(p: Point): ExtendedPoint {
if (!(p instanceof Point)) { if (!(p instanceof Point)) {
throw new TypeError('ExtendedPoint#fromAffine: expected Point'); throw new TypeError('ExtendedPoint#fromAffine: expected Point');
} }
if (p.equals(Point.ZERO)) return ExtendedPoint.ZERO; if (p.equals(Point.ZERO)) return ExtendedPoint.ZERO;
return new ExtendedPoint(p.x, p.y, _1n, modP(p.x * p.y)); return new ExtendedPoint(p.x, p.y);
} }
// Takes a bunch of Jacobian Points but executes only one // Takes a bunch of Jacobian Points but executes only one
// invert on all of them. invert is very slow operation, // invert on all of them. invert is very slow operation,
@ -306,8 +311,9 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
// It's faster, but should only be used when you don't care about // It's faster, but should only be used when you don't care about
// an exposed private key e.g. sig verification. // an exposed private key e.g. sig verification.
multiplyUnsafe(scalar: number | bigint): ExtendedPoint { multiplyUnsafe(scalar: number | bigint): ExtendedPoint {
let n = normalizeScalar(scalar, CURVE_ORDER, false);
const P0 = ExtendedPoint.ZERO; const P0 = ExtendedPoint.ZERO;
if (scalar === _0n) return P0;
let n = normalizeScalar(scalar, CURVE_ORDER, false);
if (n === _0n) return P0; if (n === _0n) return P0;
if (this.equals(P0) || n === _1n) return this; if (this.equals(P0) || n === _1n) return this;
if (this.equals(ExtendedPoint.BASE)) return this.wNAF(n); if (this.equals(ExtendedPoint.BASE)) return this.wNAF(n);
@ -542,7 +548,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
const groupLen = CURVE.nByteLength; const groupLen = CURVE.nByteLength;
// Normalize bigint / number / string to Uint8Array // Normalize bigint / number / string to Uint8Array
const keyb = const keyb =
typeof key === 'bigint' || typeof key === 'number' typeof key === 'bigint'
? ut.numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen) ? ut.numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen)
: key; : key;
// Hash private key with curve's hash function to produce uniformingly random input // Hash private key with curve's hash function to produce uniformingly random input

@ -7,7 +7,7 @@ const _2n = BigInt(2);
// We accept hex strings besides Uint8Array for simplicity // We accept hex strings besides Uint8Array for simplicity
export type Hex = Uint8Array | string; export type Hex = Uint8Array | string;
// Very few implementations accept numbers, we do it to ease learning curve // Very few implementations accept numbers, we do it to ease learning curve
export type PrivKey = Hex | bigint | number; export type PrivKey = Hex | bigint;
export type CHash = { export type CHash = {
(message: Uint8Array | string): Uint8Array; (message: Uint8Array | string): Uint8Array;
blockLen: number; blockLen: number;

@ -87,7 +87,7 @@ describe('ed25519', () => {
deepStrictEqual(ed.verify(signature, msg, publicKey), true); deepStrictEqual(ed.verify(signature, msg, publicKey), true);
}); });
should('not verify signature with wrong public key', () => { should('not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(12); const publicKey = ed.getPublicKey(12n);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false); deepStrictEqual(ed.verify(signature, msg, publicKey), false);
}); });
@ -104,7 +104,7 @@ describe('ed25519', () => {
deepStrictEqual(ed.verify(signature, msg, publicKey), true); deepStrictEqual(ed.verify(signature, msg, publicKey), true);
}); });
should('not verify signature with wrong public key', () => { should('not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(12); const publicKey = ed.getPublicKey(12n);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false); deepStrictEqual(ed.verify(signature, msg, publicKey), false);
}); });

@ -382,7 +382,7 @@ describe('ed448', () => {
deepStrictEqual(ed.verify(signature, msg, publicKey), true); deepStrictEqual(ed.verify(signature, msg, publicKey), true);
}); });
should('not verify signature with wrong public key', () => { should('not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(12); const publicKey = ed.getPublicKey(12n);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false); deepStrictEqual(ed.verify(signature, msg, publicKey), false);
}); });
@ -392,19 +392,18 @@ describe('ed448', () => {
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false); deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
}); });
}); });
describe('sync methods', () => { describe('sync methods', () => {
should('sign and verify', () => { should('sign and verify', () => {
const publicKey = ed.getPublicKey(privKey); const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true); deepStrictEqual(ed.verify(signature, msg, publicKey), true);
}); });
should('not verify signature with wrong public key', async () => { should('not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(12); const publicKey = ed.getPublicKey(12n);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false); deepStrictEqual(ed.verify(signature, msg, publicKey), false);
}); });
should('not verify signature with wrong hash', async () => { should('not verify signature with wrong hash', () => {
const publicKey = ed.getPublicKey(privKey); const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey); const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false); deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);