Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f82a804
crypto: use internal random buffer bounds
panva Sep 22, 2026
1cbd118
crypto: match algorithm names as ASCII
panva Sep 22, 2026
131cc41
crypto: reject short AES-KW inputs
panva Sep 22, 2026
0e286f0
crypto: isolate cipher job errors
panva Sep 22, 2026
5d64d76
crypto: convert getRandomValues views
panva Sep 22, 2026
bd27c23
crypto: validate normalized hash names
panva Sep 22, 2026
7129683
crypto: allow short AES-GCM IVs
panva Sep 22, 2026
3d8975b
crypto: allow unbound SubtleCrypto.supports
panva Sep 22, 2026
7f417a0
crypto: resolve supports overloads by type
panva Sep 22, 2026
9b6c75e
crypto: copy detached parameters as empty
panva Sep 22, 2026
a45414a
crypto: convert importKey data as a union
panva Sep 22, 2026
677404d
crypto: copy parameters without array species
panva Sep 22, 2026
4f7f16f
crypto: check RSA JWK alg with SHA-3 hashes
panva Sep 22, 2026
191d52a
crypto: reject duplicate unknown key usages
panva Sep 22, 2026
2ae6b20
crypto: handle PBKDF2 iteration limits
panva Sep 22, 2026
e937421
crypto: separate conversion from validation
panva Sep 22, 2026
36bb4f6
crypto: copy derived bits without species
panva Sep 22, 2026
716fc4e
crypto: minimize RSA public exponent metadata
panva Sep 22, 2026
02d6adf
crypto: create key usage arrays without species
panva Sep 22, 2026
6e9984c
crypto: derive Argon2 without worker threads
panva Sep 22, 2026
baaf5fc
crypto: report actual RSA modulus lengths
panva Sep 22, 2026
ac0931c
crypto: split hybrid keys without species
panva Sep 22, 2026
a95665b
crypto: use the public CryptoKey prototype
panva Sep 22, 2026
3e45b42
crypto: include EC public keys in PKCS8 exports
panva Sep 22, 2026
1f701b0
crypto: map JWK export failures to OperationError
panva Sep 22, 2026
36d932d
crypto: check EC coordinate conversion results
panva Sep 22, 2026
4a5c46f
benchmark: cover Web Crypto conversion costs
panva Sep 23, 2026
a216416
crypto: use backend cSHAKE and KMAC
panva Sep 23, 2026
4f26a35
crypto: verify empty KMAC outputs
panva Sep 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
crypto: reject short AES-KW inputs
Enforce the minimum key-wrap input lengths before the empty-input
shortcut, including the integrity-check block when unwrapping.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
  • Loading branch information
panva committed Sep 24, 2026
commit 131cc414ec4da1ab411347021254d311e094e18c
9 changes: 7 additions & 2 deletions src/crypto/crypto_aes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ WebCryptoCipherStatus AES_Cipher(Environment* env,
ByteSource* out) {
CHECK_EQ(key_data.GetKeyType(), kKeyTypeSecret);

const bool encrypt = cipher_mode == kWebCryptoCipherEncrypt;
// AES-KW requires at least two 64-bit plaintext blocks, plus the
// 64-bit integrity check value when unwrapping.
if (params.cipher.isWrapMode() && in.size() < (encrypt ? 16u : 24u)) {
return WebCryptoCipherStatus::FAILED;
}

auto ctx = CipherCtxPointer::New();
if (!ctx) {
return WebCryptoCipherStatus::FAILED;
Expand All @@ -56,8 +63,6 @@ WebCryptoCipherStatus AES_Cipher(Environment* env,
ctx.setAllowWrap();
}

const bool encrypt = cipher_mode == kWebCryptoCipherEncrypt;

if (!ctx.init(params.cipher, encrypt)) {
// Cipher init failed
return WebCryptoCipherStatus::FAILED;
Expand Down
49 changes: 49 additions & 0 deletions test/parallel/test-webcrypto-aes-kw-short-input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict';

const common = require('../common');

if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const { getFips } = require('crypto');
const { hasOpenSSL } = require('../common/crypto');
const { subtle } = globalThis.crypto;

(async () => {
const keyToWrap = await subtle.importKey(
'raw', new Uint8Array(16), 'AES-GCM', true, ['encrypt']);
let emptyKey;
if (hasOpenSSL(3) && getFips() !== 1) {
emptyKey = await subtle.importKey(
'raw-secret', new Uint8Array(0), 'KMAC128', true, ['sign']);
}

for (const length of [128, 192, 256]) {
const wrappingKey = await subtle.generateKey(
{ name: 'AES-KW', length }, false, ['wrapKey', 'unwrapKey']);

for (const byteLength of [0, 8, 16, 23]) {
// HKDF accepts an empty key, so the unwrap operation must reject
// before attempting to import the plaintext as a key.
await assert.rejects(subtle.unwrapKey(
'raw', new Uint8Array(byteLength), wrappingKey, 'AES-KW',
'HKDF', false, ['deriveBits']), { name: 'OperationError' });
}

if (emptyKey !== undefined) {
await assert.rejects(subtle.wrapKey(
'raw-secret', emptyKey, wrappingKey, 'AES-KW'),
{ name: 'OperationError' });
}

const wrapped = await subtle.wrapKey(
'raw', keyToWrap, wrappingKey, 'AES-KW');
assert.strictEqual(wrapped.byteLength, 24);
const unwrapped = await subtle.unwrapKey(
'raw', wrapped, wrappingKey, 'AES-KW', 'AES-GCM', true, ['encrypt']);
assert.deepStrictEqual(
new Uint8Array(await subtle.exportKey('raw', unwrapped)),
new Uint8Array(16));
}
})().then(common.mustCall());