Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3041,6 +3041,9 @@ const Cipher Cipher::AES_256_GCM = Cipher::FromNid(NID_aes_256_gcm);
const Cipher Cipher::AES_128_KW = Cipher::FromNid(NID_id_aes128_wrap);
const Cipher Cipher::AES_192_KW = Cipher::FromNid(NID_id_aes192_wrap);
const Cipher Cipher::AES_256_KW = Cipher::FromNid(NID_id_aes256_wrap);
const Cipher Cipher::AES_128_OCB = Cipher::FromNid(NID_aes_128_ocb);
const Cipher Cipher::AES_192_OCB = Cipher::FromNid(NID_aes_192_ocb);
const Cipher Cipher::AES_256_OCB = Cipher::FromNid(NID_aes_256_ocb);
const Cipher Cipher::CHACHA20_POLY1305 = Cipher::FromNid(NID_chacha20_poly1305);

bool Cipher::isGcmMode() const {
Expand Down Expand Up @@ -3243,6 +3246,11 @@ bool CipherCtxPointer::isGcmMode() const {
return getMode() == EVP_CIPH_GCM_MODE;
}

bool CipherCtxPointer::isOcbMode() const {
if (!ctx_) return false;
return getMode() == EVP_CIPH_OCB_MODE;
}

bool CipherCtxPointer::isCcmMode() const {
if (!ctx_) return false;
return getMode() == EVP_CIPH_CCM_MODE;
Expand Down
4 changes: 4 additions & 0 deletions deps/ncrypto/ncrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,9 @@ class Cipher final {
static const Cipher AES_128_KW;
static const Cipher AES_192_KW;
static const Cipher AES_256_KW;
static const Cipher AES_128_OCB;
static const Cipher AES_192_OCB;
static const Cipher AES_256_OCB;
static const Cipher CHACHA20_POLY1305;

struct CipherParams {
Expand Down Expand Up @@ -738,6 +741,7 @@ class CipherCtxPointer final {
int getNid() const;

bool isGcmMode() const;
bool isOcbMode() const;
bool isCcmMode() const;
bool isWrapMode() const;
bool isChaCha20Poly1305() const;
Expand Down
34 changes: 31 additions & 3 deletions doc/api/webcrypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59539
description: AES-OCB algorithm is now supported.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59569
description: ML-KEM algorithms are now supported.
Expand Down Expand Up @@ -104,6 +107,7 @@ WICG proposal:

Algorithms:

* `'AES-OCB'`[^openssl30]
* `'ChaCha20-Poly1305'`
* `'cSHAKE128'`
* `'cSHAKE256'`
Expand Down Expand Up @@ -501,6 +505,7 @@ implementation and the APIs supported for each:
| `'AES-CTR'` | ✔ | ✔ | ✔ | |
| `'AES-GCM'` | ✔ | ✔ | ✔ | |
| `'AES-KW'` | ✔ | ✔ | ✔ | |
| `'AES-OCB'` | ✔ | ✔ | ✔ | |
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | ✔ | ✔ | |
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ |
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ |
Expand Down Expand Up @@ -539,6 +544,7 @@ implementation and the APIs supported for each:
| `'AES-CTR'` | ✔ | | | ✔ | | |
| `'AES-GCM'` | ✔ | | | ✔ | | |
| `'AES-KW'` | | | | ✔ | | |
| `'AES-OCB'` | ✔ | | | ✔ | | |
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | | |
| `'cSHAKE128'`[^modern-algos] | | | | | | ✔ |
| `'cSHAKE256'`[^modern-algos] | | | | | | ✔ |
Expand Down Expand Up @@ -707,6 +713,7 @@ Valid key usages depend on the key algorithm (identified by
| `'AES-CTR'` | ✔ | | | ✔ | |
| `'AES-GCM'` | ✔ | | | ✔ | |
| `'AES-KW'` | | | | ✔ | |
| `'AES-OCB'` | ✔ | | | ✔ | |
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | |
| `'ECDH'` | | | ✔ | | |
| `'ECDSA'` | | ✔ | | | |
Expand Down Expand Up @@ -825,6 +832,9 @@ The algorithms currently supported include:
<!-- YAML
added: v15.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59539
description: AES-OCB algorithm is now supported.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59365
description: ChaCha20-Poly1305 algorithm is now supported.
Expand All @@ -845,6 +855,7 @@ The algorithms currently supported include:
* `'AES-CBC'`
* `'AES-CTR'`
* `'AES-GCM'`
* `'AES-OCB'`[^modern-algos]
* `'ChaCha20-Poly1305'`[^modern-algos]
* `'RSA-OAEP'`

Expand Down Expand Up @@ -1015,6 +1026,9 @@ The algorithms currently supported include:
<!-- YAML
added: v15.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59539
description: AES-OCB algorithm is now supported.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59365
description: ChaCha20-Poly1305 algorithm is now supported.
Expand All @@ -1035,6 +1049,7 @@ The algorithms currently supported include:
* `'AES-CBC'`
* `'AES-CTR'`
* `'AES-GCM'`
* `'AES-OCB'`[^modern-algos]
* `'ChaCha20-Poly1305'`[^modern-algos]
* `'RSA-OAEP'`

Expand Down Expand Up @@ -1086,6 +1101,7 @@ specification.
| `'AES-CTR'` | | | ✔ | ✔ | ✔ | | |
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
| `'AES-OCB'`[^modern-algos] | | | ✔ | | ✔ | | |
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
Expand Down Expand Up @@ -1171,6 +1187,7 @@ The {CryptoKey} (secret key) generating algorithms supported include:
* `'AES-CTR'`
* `'AES-GCM'`
* `'AES-KW'`
* `'AES-OCB'`[^modern-algos]
* `'ChaCha20-Poly1305'`[^modern-algos]
* `'HMAC'`

Expand Down Expand Up @@ -1228,6 +1245,7 @@ The algorithms currently supported include:
| `'AES-CTR'` | | | ✔ | ✔ | ✔ | | |
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
| `'AES-OCB'`[^modern-algos] | | | ✔ | | ✔ | | |
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
Expand Down Expand Up @@ -1294,6 +1312,9 @@ The algorithms currently supported include:
<!-- YAML
added: v15.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59539
description: AES-OCB algorithm is now supported.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59365
description: ChaCha20-Poly1305 algorithm is now supported.
Expand Down Expand Up @@ -1330,6 +1351,7 @@ The wrapping algorithms currently supported include:
* `'AES-CTR'`
* `'AES-GCM'`
* `'AES-KW'`
* `'AES-OCB'`[^modern-algos]
* `'ChaCha20-Poly1305'`[^modern-algos]
* `'RSA-OAEP'`

Expand All @@ -1339,6 +1361,7 @@ The unwrapped key algorithms supported include:
* `'AES-CTR'`
* `'AES-GCM'`
* `'AES-KW'`
* `'AES-OCB'`[^modern-algos]
* `'ChaCha20-Poly1305'`[^modern-algos]
* `'ECDH'`
* `'ECDSA'`
Expand Down Expand Up @@ -1404,6 +1427,9 @@ The algorithms currently supported include:
<!-- YAML
added: v15.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59539
description: AES-OCB algorithm is now supported.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59365
description: ChaCha20-Poly1305 algorithm is now supported.
Expand Down Expand Up @@ -1436,6 +1462,7 @@ The wrapping algorithms currently supported include:
* `'AES-CTR'`
* `'AES-GCM'`
* `'AES-KW'`
* `'AES-OCB'`[^modern-algos]
* `'ChaCha20-Poly1305'`[^modern-algos]
* `'RSA-OAEP'`

Expand Down Expand Up @@ -1493,7 +1520,7 @@ given key.
added: v15.0.0
-->

* Type: {string} Must be `'AES-GCM'` or `'ChaCha20-Poly1305'`.
* Type: {string} Must be `'AES-GCM'`, `'AES-OCB'`, or `'ChaCha20-Poly1305'`.

#### `aeadParams.tagLength`

Expand All @@ -1515,8 +1542,7 @@ added: v15.0.0
added: v15.0.0
-->

* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, or
`'AES-KW'`
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, `'AES-OCB'`, or `'AES-KW'`

#### `aesDerivedKeyParams.length`

Expand Down Expand Up @@ -2392,6 +2418,8 @@ The length (in bytes) of the random salt to use.

[^modern-algos]: See [Modern Algorithms in the Web Cryptography API][]

[^openssl30]: Requires OpenSSL >= 3.0
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I gated AES-OCB behind OpenSSL >= 3.0 because in 1.1.1 it was producing ciphertexts that did not match the vectors. It could decrypt the vectors but was itself producing invalid ciphertexts.


[^openssl35]: Requires OpenSSL >= 3.5

[JSON Web Key]: https://tools.ietf.org/html/rfc7517
Expand Down
53 changes: 53 additions & 0 deletions lib/internal/crypto/aes.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@ const {
kKeyVariantAES_CBC_128,
kKeyVariantAES_GCM_128,
kKeyVariantAES_KW_128,
kKeyVariantAES_OCB_128,
kKeyVariantAES_CTR_192,
kKeyVariantAES_CBC_192,
kKeyVariantAES_GCM_192,
kKeyVariantAES_KW_192,
kKeyVariantAES_OCB_192,
kKeyVariantAES_CTR_256,
kKeyVariantAES_CBC_256,
kKeyVariantAES_GCM_256,
kKeyVariantAES_KW_256,
kKeyVariantAES_OCB_256,
kWebCryptoCipherDecrypt,
kWebCryptoCipherEncrypt,
} = internalBinding('crypto');
Expand Down Expand Up @@ -62,6 +65,7 @@ function getAlgorithmName(name, length) {
case 'AES-CTR': return `A${length}CTR`;
case 'AES-GCM': return `A${length}GCM`;
case 'AES-KW': return `A${length}KW`;
case 'AES-OCB': return `A${length}OCB`;
}
}

Expand Down Expand Up @@ -100,6 +104,13 @@ function getVariant(name, length) {
case 256: return kKeyVariantAES_KW_256;
}
break;
case 'AES-OCB':
switch (length) {
case 128: return kKeyVariantAES_OCB_128;
case 192: return kKeyVariantAES_OCB_192;
case 256: return kKeyVariantAES_OCB_256;
}
break;
}
}

Expand Down Expand Up @@ -173,11 +184,49 @@ function asyncAesGcmCipher(mode, key, data, algorithm) {
algorithm.additionalData));
}

function asyncAesOcbCipher(mode, key, data, algorithm) {
const { tagLength = 128 } = algorithm;

const tagByteLength = tagLength / 8;
let tag;
switch (mode) {
case kWebCryptoCipherDecrypt: {
const slice = ArrayBufferIsView(data) ?
TypedArrayPrototypeSlice : ArrayBufferPrototypeSlice;
tag = slice(data, -tagByteLength);

// Similar to GCM, OCB requires the tag to be present for decryption
if (tagByteLength > tag.byteLength) {
return PromiseReject(lazyDOMException(
'The provided data is too small.',
'OperationError'));
}

data = slice(data, 0, -tagByteLength);
break;
}
case kWebCryptoCipherEncrypt:
tag = tagByteLength;
break;
}

return jobPromise(() => new AESCipherJob(
kCryptoJobAsync,
mode,
key[kKeyObject][kHandle],
data,
getVariant('AES-OCB', key.algorithm.length),
algorithm.iv,
tag,
algorithm.additionalData));
}

function aesCipher(mode, key, data, algorithm) {
switch (algorithm.name) {
case 'AES-CTR': return asyncAesCtrCipher(mode, key, data, algorithm);
case 'AES-CBC': return asyncAesCbcCipher(mode, key, data, algorithm);
case 'AES-GCM': return asyncAesGcmCipher(mode, key, data, algorithm);
case 'AES-OCB': return asyncAesOcbCipher(mode, key, data, algorithm);
case 'AES-KW': return asyncAesKwCipher(mode, key, data);
}
}
Expand Down Expand Up @@ -236,7 +285,11 @@ function aesImportKey(
keyObject = keyData;
break;
}
case 'raw-secret':
case 'raw': {
if (format === 'raw' && name === 'AES-OCB') {
return undefined;
}
validateKeyLength(keyData.byteLength * 8);
keyObject = createSecretKey(keyData);
break;
Expand Down
2 changes: 2 additions & 0 deletions lib/internal/crypto/keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ const {
case 'AES-GCM':
// Fall through
case 'AES-KW':
// Fall through
case 'AES-OCB':
result = require('internal/crypto/aes')
.aesImportKey(algorithm, 'KeyObject', this, extractable, keyUsages);
break;
Expand Down
11 changes: 11 additions & 0 deletions lib/internal/crypto/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const {
EVP_PKEY_ML_KEM_512,
EVP_PKEY_ML_KEM_768,
EVP_PKEY_ML_KEM_1024,
kKeyVariantAES_OCB_128: hasAesOcbMode,
} = internalBinding('crypto');

const { getOptionValue } = require('internal/options');
Expand Down Expand Up @@ -208,6 +209,14 @@ const kAlgorithmDefinitions = {
'wrapKey': null,
'unwrapKey': null,
},
'AES-OCB': {
'generateKey': 'AesKeyGenParams',
'exportKey': null,
'importKey': null,
'encrypt': 'AeadParams',
'decrypt': 'AeadParams',
'get key length': 'AesDerivedKeyParams',
},
'ChaCha20-Poly1305': {
'generateKey': null,
'exportKey': null,
Expand Down Expand Up @@ -350,6 +359,7 @@ const kAlgorithmDefinitions = {
// Conditionally supported algorithms
const conditionalAlgorithms = {
'AES-KW': !process.features.openssl_is_boringssl,
'AES-OCB': !!hasAesOcbMode,
'ChaCha20-Poly1305': !process.features.openssl_is_boringssl ||
ArrayPrototypeIncludes(getCiphers(), 'chacha20-poly1305'),
'cSHAKE128': !process.features.openssl_is_boringssl ||
Expand All @@ -374,6 +384,7 @@ const conditionalAlgorithms = {

// Experimental algorithms
const experimentalAlgorithms = [
'AES-OCB',
'ChaCha20-Poly1305',
'cSHAKE128',
'cSHAKE256',
Expand Down
Loading
Loading