Skip to content

Commit a0dd2f5

Browse files
panvaaduh95
authored andcommitted
crypto: verify empty KMAC outputs
Compare empty KMAC outputs with empty signatures as equal, as required by the verification algorithm. Skip CRYPTO_memcmp for zero-length buffers while retaining the constant-time comparison for other MACs. Signed-off-by: Filip Skokan <panva.ip@gmail.com> Assisted-by: Codex PR-URL: #66237 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Aviv Keller <me@aviv.sh>
1 parent b218f16 commit a0dd2f5

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

‎src/crypto/crypto_kmac.cc‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,10 @@ MaybeLocal<Value> KmacTraits::EncodeOutput(Environment* env,
274274
case SignConfiguration::Mode::Verify:
275275
return Boolean::New(
276276
env->isolate(),
277-
out->size() > 0 && out->size() == params.signature.size() &&
278-
CRYPTO_memcmp(
279-
out->data(), params.signature.data(), out->size()) == 0);
277+
out->size() == params.signature.size() &&
278+
(out->size() == 0 ||
279+
CRYPTO_memcmp(
280+
out->data(), params.signature.data(), out->size()) == 0));
280281
}
281282
UNREACHABLE();
282283
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
5+
if (!common.hasCrypto)
6+
common.skip('missing crypto');
7+
8+
const { hasFIPS, hasOpenSSL } = require('../common/crypto');
9+
10+
if (!hasOpenSSL(3))
11+
common.skip('requires OpenSSL >= 3');
12+
if (hasFIPS())
13+
common.skip('empty KMAC output is not supported in FIPS mode');
14+
15+
const assert = require('assert');
16+
const { subtle } = globalThis.crypto;
17+
18+
(async () => {
19+
const data = new Uint8Array([1, 2, 3]);
20+
21+
for (const name of ['KMAC128', 'KMAC256']) {
22+
const key = await subtle.importKey(
23+
'raw-secret', new Uint8Array(32), name, false, ['sign', 'verify']);
24+
const otherKey = await subtle.importKey(
25+
'raw-secret', new Uint8Array(32).fill(1), name, false, ['verify']);
26+
const algorithm = { name, outputLength: 0 };
27+
const signature = await subtle.sign(algorithm, key, data);
28+
29+
assert.strictEqual(signature.byteLength, 0);
30+
assert.strictEqual(await subtle.verify(algorithm, key, signature, data), true);
31+
assert.strictEqual(await subtle.verify(algorithm, key, new Uint8Array([0]), data), false);
32+
assert.strictEqual(await subtle.verify(
33+
{ name, outputLength: 256 }, key, signature, data), false);
34+
35+
// Empty MACs compare equal even when the inputs differ.
36+
assert.strictEqual(await subtle.verify(
37+
algorithm, key, signature, new Uint8Array([4, 5, 6])), true);
38+
assert.strictEqual(await subtle.verify(algorithm, otherKey, signature, data), true);
39+
assert.strictEqual(await subtle.verify(
40+
{ ...algorithm, customization: new Uint8Array([1]) }, key, signature, data), true);
41+
}
42+
})().then(common.mustCall());

0 commit comments

Comments
 (0)