Skip to content
This repository has been archived by the owner on Jun 26, 2024. It is now read-only.

Commit

Permalink
EdDSA: add optional signature verification to prevent outputting faul…
Browse files Browse the repository at this point in the history
…ty signatures

EdDSA is known to be vulnerable to fault attacks which can lead to secret key extraction if
two signatures over the same message can be collected.
Randomly occurring bitflips in specific parts of the computation might in principle result
in vulnerable faulty signatures being generated, hence we add the option to validate the signatures
before outputting them.
  • Loading branch information
larabr committed Nov 15, 2023
1 parent 45b6405 commit 8af01d1
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 1 deletion.
22 changes: 21 additions & 1 deletion src/abstract/edwards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
}

/** Signs message with privateKey. RFC8032 5.1.6 */
function sign(msg: Hex, privKey: Hex, options: { context?: Hex } = {}): Uint8Array {
function sign(msg: Hex, privKey: Hex, options: { context?: Hex, validate?: boolean } = {}): Uint8Array {
msg = ensureBytes('message', msg);
if (prehash) msg = prehash(msg); // for ed25519ph etc.
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
Expand All @@ -457,6 +457,26 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
const s = modN(r.add( k.mul(scalar) )); // S = (r + k * s) mod L
assertGE0(s); // 0 <= s < l
if (options.validate) {
/**
* Detect faulty signatures caused by random bitflips which could lead to private key extraction
* if two signatures over the same message are obtained.
* See https://github.com/jedisct1/libsodium/issues/170.
* If the input data is not deterministic, e.g. thanks to the random salt in v6 OpenPGP signatures, then the generated signature
* is always safe, and the validation step is not needed.
* Otherwise, we need to check intermediate values to ensure that no bitflip occured:
* - in M between the computation of `r` and `h`.
* - in the public key before computing `h`
* An alternative to these low-level checks would be to verify the generated signature, but this is less efficient since
* doing so here requires re-deriving the public key anyway.
*/
const { pointBytes: pointBytesCompare } = getExtendedPublicKey(privKey);
const rCompare = hashDomainToScalar(options.context, prefix, msg);
const kCompare = hashDomainToScalar(options.context, R, pointBytesCompare, msg);
if (!r.equal(rCompare) || !k.equal(kCompare) || !ut.equalBytes(pointBytes, pointBytesCompare)) {
throw new Error('transient signing failure');
}
}
const res = ut.concatBytes(R, s.toUint8Array('le', Fp.BYTES));
return ensureBytes('result', res, nByteLength * 2); // 64-byte signature
}
Expand Down
5 changes: 5 additions & 0 deletions test/ed25519.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ describe('ed25519', () => {
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
});
should('sign with validation and verify', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey, { validate: true });
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
});
});
describe('sync methods', () => {
should('sign and verify', () => {
Expand Down
5 changes: 5 additions & 0 deletions test/ed448.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,11 @@ describe('ed448', () => {
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
});
should('sign with validation and verify', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey, { validate: true });
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
});
});
describe('sync methods', () => {
should('sign and verify', () => {
Expand Down

0 comments on commit 8af01d1

Please sign in to comment.