Skip to content

Commit 8692e60

Browse files
authored
crypto: add AES-OCB Web Cryptography algorithm
PR-URL: #59539 Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 28e6ba8 commit 8692e60

17 files changed

+440
-23
lines changed

deps/ncrypto/ncrypto.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3041,6 +3041,9 @@ const Cipher Cipher::AES_256_GCM = Cipher::FromNid(NID_aes_256_gcm);
30413041
const Cipher Cipher::AES_128_KW = Cipher::FromNid(NID_id_aes128_wrap);
30423042
const Cipher Cipher::AES_192_KW = Cipher::FromNid(NID_id_aes192_wrap);
30433043
const Cipher Cipher::AES_256_KW = Cipher::FromNid(NID_id_aes256_wrap);
3044+
const Cipher Cipher::AES_128_OCB = Cipher::FromNid(NID_aes_128_ocb);
3045+
const Cipher Cipher::AES_192_OCB = Cipher::FromNid(NID_aes_192_ocb);
3046+
const Cipher Cipher::AES_256_OCB = Cipher::FromNid(NID_aes_256_ocb);
30443047
const Cipher Cipher::CHACHA20_POLY1305 = Cipher::FromNid(NID_chacha20_poly1305);
30453048

30463049
bool Cipher::isGcmMode() const {
@@ -3243,6 +3246,11 @@ bool CipherCtxPointer::isGcmMode() const {
32433246
return getMode() == EVP_CIPH_GCM_MODE;
32443247
}
32453248

3249+
bool CipherCtxPointer::isOcbMode() const {
3250+
if (!ctx_) return false;
3251+
return getMode() == EVP_CIPH_OCB_MODE;
3252+
}
3253+
32463254
bool CipherCtxPointer::isCcmMode() const {
32473255
if (!ctx_) return false;
32483256
return getMode() == EVP_CIPH_CCM_MODE;

deps/ncrypto/ncrypto.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ class Cipher final {
373373
static const Cipher AES_128_KW;
374374
static const Cipher AES_192_KW;
375375
static const Cipher AES_256_KW;
376+
static const Cipher AES_128_OCB;
377+
static const Cipher AES_192_OCB;
378+
static const Cipher AES_256_OCB;
376379
static const Cipher CHACHA20_POLY1305;
377380

378381
struct CipherParams {
@@ -738,6 +741,7 @@ class CipherCtxPointer final {
738741
int getNid() const;
739742

740743
bool isGcmMode() const;
744+
bool isOcbMode() const;
741745
bool isCcmMode() const;
742746
bool isWrapMode() const;
743747
bool isChaCha20Poly1305() const;

doc/api/webcrypto.md

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

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

105108
Algorithms:
106109

110+
* `'AES-OCB'`[^openssl30]
107111
* `'ChaCha20-Poly1305'`
108112
* `'cSHAKE128'`
109113
* `'cSHAKE256'`
@@ -501,6 +505,7 @@ implementation and the APIs supported for each:
501505
| `'AES-CTR'` | ✔ | ✔ | ✔ | |
502506
| `'AES-GCM'` | ✔ | ✔ | ✔ | |
503507
| `'AES-KW'` | ✔ | ✔ | ✔ | |
508+
| `'AES-OCB'` | ✔ | ✔ | ✔ | |
504509
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | ✔ | ✔ | |
505510
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ |
506511
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ |
@@ -539,6 +544,7 @@ implementation and the APIs supported for each:
539544
| `'AES-CTR'` | ✔ | | | ✔ | | |
540545
| `'AES-GCM'` | ✔ | | | ✔ | | |
541546
| `'AES-KW'` | | | | ✔ | | |
547+
| `'AES-OCB'` | ✔ | | | ✔ | | |
542548
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | | |
543549
| `'cSHAKE128'`[^modern-algos] | | | | | | ✔ |
544550
| `'cSHAKE256'`[^modern-algos] | | | | | | ✔ |
@@ -707,6 +713,7 @@ Valid key usages depend on the key algorithm (identified by
707713
| `'AES-CTR'` | ✔ | | | ✔ | |
708714
| `'AES-GCM'` | ✔ | | | ✔ | |
709715
| `'AES-KW'` | | | | ✔ | |
716+
| `'AES-OCB'` | ✔ | | | ✔ | |
710717
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | |
711718
| `'ECDH'` | | | ✔ | | |
712719
| `'ECDSA'` | | ✔ | | | |
@@ -825,6 +832,9 @@ The algorithms currently supported include:
825832
<!-- YAML
826833
added: v15.0.0
827834
changes:
835+
- version: REPLACEME
836+
pr-url: https://github.com/nodejs/node/pull/59539
837+
description: AES-OCB algorithm is now supported.
828838
- version: REPLACEME
829839
pr-url: https://github.com/nodejs/node/pull/59365
830840
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -845,6 +855,7 @@ The algorithms currently supported include:
845855
* `'AES-CBC'`
846856
* `'AES-CTR'`
847857
* `'AES-GCM'`
858+
* `'AES-OCB'`[^modern-algos]
848859
* `'ChaCha20-Poly1305'`[^modern-algos]
849860
* `'RSA-OAEP'`
850861
@@ -1015,6 +1026,9 @@ The algorithms currently supported include:
10151026
<!-- YAML
10161027
added: v15.0.0
10171028
changes:
1029+
- version: REPLACEME
1030+
pr-url: https://github.com/nodejs/node/pull/59539
1031+
description: AES-OCB algorithm is now supported.
10181032
- version: REPLACEME
10191033
pr-url: https://github.com/nodejs/node/pull/59365
10201034
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -1035,6 +1049,7 @@ The algorithms currently supported include:
10351049
* `'AES-CBC'`
10361050
* `'AES-CTR'`
10371051
* `'AES-GCM'`
1052+
* `'AES-OCB'`[^modern-algos]
10381053
* `'ChaCha20-Poly1305'`[^modern-algos]
10391054
* `'RSA-OAEP'`
10401055
@@ -1086,6 +1101,7 @@ specification.
10861101
| `'AES-CTR'` | | | ✔ | ✔ | ✔ | | |
10871102
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
10881103
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
1104+
| `'AES-OCB'`[^modern-algos] | | | ✔ | | ✔ | | |
10891105
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
10901106
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
10911107
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
@@ -1171,6 +1187,7 @@ The {CryptoKey} (secret key) generating algorithms supported include:
11711187
* `'AES-CTR'`
11721188
* `'AES-GCM'`
11731189
* `'AES-KW'`
1190+
* `'AES-OCB'`[^modern-algos]
11741191
* `'ChaCha20-Poly1305'`[^modern-algos]
11751192
* `'HMAC'`
11761193
@@ -1228,6 +1245,7 @@ The algorithms currently supported include:
12281245
| `'AES-CTR'` | | | ✔ | ✔ | ✔ | | |
12291246
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
12301247
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
1248+
| `'AES-OCB'`[^modern-algos] | | | ✔ | | ✔ | | |
12311249
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
12321250
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
12331251
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
@@ -1294,6 +1312,9 @@ The algorithms currently supported include:
12941312
<!-- YAML
12951313
added: v15.0.0
12961314
changes:
1315+
- version: REPLACEME
1316+
pr-url: https://github.com/nodejs/node/pull/59539
1317+
description: AES-OCB algorithm is now supported.
12971318
- version: REPLACEME
12981319
pr-url: https://github.com/nodejs/node/pull/59365
12991320
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -1330,6 +1351,7 @@ The wrapping algorithms currently supported include:
13301351
* `'AES-CTR'`
13311352
* `'AES-GCM'`
13321353
* `'AES-KW'`
1354+
* `'AES-OCB'`[^modern-algos]
13331355
* `'ChaCha20-Poly1305'`[^modern-algos]
13341356
* `'RSA-OAEP'`
13351357
@@ -1339,6 +1361,7 @@ The unwrapped key algorithms supported include:
13391361
* `'AES-CTR'`
13401362
* `'AES-GCM'`
13411363
* `'AES-KW'`
1364+
* `'AES-OCB'`[^modern-algos]
13421365
* `'ChaCha20-Poly1305'`[^modern-algos]
13431366
* `'ECDH'`
13441367
* `'ECDSA'`
@@ -1404,6 +1427,9 @@ The algorithms currently supported include:
14041427
<!-- YAML
14051428
added: v15.0.0
14061429
changes:
1430+
- version: REPLACEME
1431+
pr-url: https://github.com/nodejs/node/pull/59539
1432+
description: AES-OCB algorithm is now supported.
14071433
- version: REPLACEME
14081434
pr-url: https://github.com/nodejs/node/pull/59365
14091435
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -1436,6 +1462,7 @@ The wrapping algorithms currently supported include:
14361462
* `'AES-CTR'`
14371463
* `'AES-GCM'`
14381464
* `'AES-KW'`
1465+
* `'AES-OCB'`[^modern-algos]
14391466
* `'ChaCha20-Poly1305'`[^modern-algos]
14401467
* `'RSA-OAEP'`
14411468
@@ -1493,7 +1520,7 @@ given key.
14931520
added: v15.0.0
14941521
-->
14951522
1496-
* Type: {string} Must be `'AES-GCM'` or `'ChaCha20-Poly1305'`.
1523+
* Type: {string} Must be `'AES-GCM'`, `'AES-OCB'`, or `'ChaCha20-Poly1305'`.
14971524
14981525
#### `aeadParams.tagLength`
14991526
@@ -1515,8 +1542,7 @@ added: v15.0.0
15151542
added: v15.0.0
15161543
-->
15171544
1518-
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, or
1519-
`'AES-KW'`
1545+
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, `'AES-OCB'`, or `'AES-KW'`
15201546
15211547
#### `aesDerivedKeyParams.length`
15221548
@@ -2392,6 +2418,8 @@ The length (in bytes) of the random salt to use.
23922418
23932419
[^modern-algos]: See [Modern Algorithms in the Web Cryptography API][]
23942420
2421+
[^openssl30]: Requires OpenSSL >= 3.0
2422+
23952423
[^openssl35]: Requires OpenSSL >= 3.5
23962424
23972425
[JSON Web Key]: https://tools.ietf.org/html/rfc7517

lib/internal/crypto/aes.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,17 @@ const {
1818
kKeyVariantAES_CBC_128,
1919
kKeyVariantAES_GCM_128,
2020
kKeyVariantAES_KW_128,
21+
kKeyVariantAES_OCB_128,
2122
kKeyVariantAES_CTR_192,
2223
kKeyVariantAES_CBC_192,
2324
kKeyVariantAES_GCM_192,
2425
kKeyVariantAES_KW_192,
26+
kKeyVariantAES_OCB_192,
2527
kKeyVariantAES_CTR_256,
2628
kKeyVariantAES_CBC_256,
2729
kKeyVariantAES_GCM_256,
2830
kKeyVariantAES_KW_256,
31+
kKeyVariantAES_OCB_256,
2932
kWebCryptoCipherDecrypt,
3033
kWebCryptoCipherEncrypt,
3134
} = internalBinding('crypto');
@@ -62,6 +65,7 @@ function getAlgorithmName(name, length) {
6265
case 'AES-CTR': return `A${length}CTR`;
6366
case 'AES-GCM': return `A${length}GCM`;
6467
case 'AES-KW': return `A${length}KW`;
68+
case 'AES-OCB': return `A${length}OCB`;
6569
}
6670
}
6771

@@ -100,6 +104,13 @@ function getVariant(name, length) {
100104
case 256: return kKeyVariantAES_KW_256;
101105
}
102106
break;
107+
case 'AES-OCB':
108+
switch (length) {
109+
case 128: return kKeyVariantAES_OCB_128;
110+
case 192: return kKeyVariantAES_OCB_192;
111+
case 256: return kKeyVariantAES_OCB_256;
112+
}
113+
break;
103114
}
104115
}
105116

@@ -173,11 +184,49 @@ function asyncAesGcmCipher(mode, key, data, algorithm) {
173184
algorithm.additionalData));
174185
}
175186

187+
function asyncAesOcbCipher(mode, key, data, algorithm) {
188+
const { tagLength = 128 } = algorithm;
189+
190+
const tagByteLength = tagLength / 8;
191+
let tag;
192+
switch (mode) {
193+
case kWebCryptoCipherDecrypt: {
194+
const slice = ArrayBufferIsView(data) ?
195+
TypedArrayPrototypeSlice : ArrayBufferPrototypeSlice;
196+
tag = slice(data, -tagByteLength);
197+
198+
// Similar to GCM, OCB requires the tag to be present for decryption
199+
if (tagByteLength > tag.byteLength) {
200+
return PromiseReject(lazyDOMException(
201+
'The provided data is too small.',
202+
'OperationError'));
203+
}
204+
205+
data = slice(data, 0, -tagByteLength);
206+
break;
207+
}
208+
case kWebCryptoCipherEncrypt:
209+
tag = tagByteLength;
210+
break;
211+
}
212+
213+
return jobPromise(() => new AESCipherJob(
214+
kCryptoJobAsync,
215+
mode,
216+
key[kKeyObject][kHandle],
217+
data,
218+
getVariant('AES-OCB', key.algorithm.length),
219+
algorithm.iv,
220+
tag,
221+
algorithm.additionalData));
222+
}
223+
176224
function aesCipher(mode, key, data, algorithm) {
177225
switch (algorithm.name) {
178226
case 'AES-CTR': return asyncAesCtrCipher(mode, key, data, algorithm);
179227
case 'AES-CBC': return asyncAesCbcCipher(mode, key, data, algorithm);
180228
case 'AES-GCM': return asyncAesGcmCipher(mode, key, data, algorithm);
229+
case 'AES-OCB': return asyncAesOcbCipher(mode, key, data, algorithm);
181230
case 'AES-KW': return asyncAesKwCipher(mode, key, data);
182231
}
183232
}
@@ -236,7 +285,11 @@ function aesImportKey(
236285
keyObject = keyData;
237286
break;
238287
}
288+
case 'raw-secret':
239289
case 'raw': {
290+
if (format === 'raw' && name === 'AES-OCB') {
291+
return undefined;
292+
}
240293
validateKeyLength(keyData.byteLength * 8);
241294
keyObject = createSecretKey(keyData);
242295
break;

lib/internal/crypto/keys.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ const {
199199
case 'AES-GCM':
200200
// Fall through
201201
case 'AES-KW':
202+
// Fall through
203+
case 'AES-OCB':
202204
result = require('internal/crypto/aes')
203205
.aesImportKey(algorithm, 'KeyObject', this, extractable, keyUsages);
204206
break;

lib/internal/crypto/util.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const {
3939
EVP_PKEY_ML_KEM_512,
4040
EVP_PKEY_ML_KEM_768,
4141
EVP_PKEY_ML_KEM_1024,
42+
kKeyVariantAES_OCB_128: hasAesOcbMode,
4243
} = internalBinding('crypto');
4344

4445
const { getOptionValue } = require('internal/options');
@@ -208,6 +209,14 @@ const kAlgorithmDefinitions = {
208209
'wrapKey': null,
209210
'unwrapKey': null,
210211
},
212+
'AES-OCB': {
213+
'generateKey': 'AesKeyGenParams',
214+
'exportKey': null,
215+
'importKey': null,
216+
'encrypt': 'AeadParams',
217+
'decrypt': 'AeadParams',
218+
'get key length': 'AesDerivedKeyParams',
219+
},
211220
'ChaCha20-Poly1305': {
212221
'generateKey': null,
213222
'exportKey': null,
@@ -350,6 +359,7 @@ const kAlgorithmDefinitions = {
350359
// Conditionally supported algorithms
351360
const conditionalAlgorithms = {
352361
'AES-KW': !process.features.openssl_is_boringssl,
362+
'AES-OCB': !!hasAesOcbMode,
353363
'ChaCha20-Poly1305': !process.features.openssl_is_boringssl ||
354364
ArrayPrototypeIncludes(getCiphers(), 'chacha20-poly1305'),
355365
'cSHAKE128': !process.features.openssl_is_boringssl ||
@@ -374,6 +384,7 @@ const conditionalAlgorithms = {
374384

375385
// Experimental algorithms
376386
const experimentalAlgorithms = [
387+
'AES-OCB',
377388
'ChaCha20-Poly1305',
378389
'cSHAKE128',
379390
'cSHAKE256',

0 commit comments

Comments
 (0)