Skip to content

Commit 152c5ef

Browse files
panvatargos
authored andcommitted
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 c6c4183 commit 152c5ef

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.
@@ -103,6 +106,7 @@ WICG proposal:
103106

104107
Algorithms:
105108

109+
* `'AES-OCB'`[^openssl30]
106110
* `'ChaCha20-Poly1305'`
107111
* `'cSHAKE128'`
108112
* `'cSHAKE256'`
@@ -500,6 +504,7 @@ implementation and the APIs supported for each:
500504
| `'AES-CTR'` | ✔ | ✔ | ✔ | |
501505
| `'AES-GCM'` | ✔ | ✔ | ✔ | |
502506
| `'AES-KW'` | ✔ | ✔ | ✔ | |
507+
| `'AES-OCB'` | ✔ | ✔ | ✔ | |
503508
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | ✔ | ✔ | |
504509
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ |
505510
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ |
@@ -538,6 +543,7 @@ implementation and the APIs supported for each:
538543
| `'AES-CTR'` | ✔ | | | ✔ | | |
539544
| `'AES-GCM'` | ✔ | | | ✔ | | |
540545
| `'AES-KW'` | | | | ✔ | | |
546+
| `'AES-OCB'` | ✔ | | | ✔ | | |
541547
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | | |
542548
| `'cSHAKE128'`[^modern-algos] | | | | | | ✔ |
543549
| `'cSHAKE256'`[^modern-algos] | | | | | | ✔ |
@@ -706,6 +712,7 @@ Valid key usages depend on the key algorithm (identified by
706712
| `'AES-CTR'` | ✔ | | | ✔ | |
707713
| `'AES-GCM'` | ✔ | | | ✔ | |
708714
| `'AES-KW'` | | | | ✔ | |
715+
| `'AES-OCB'` | ✔ | | | ✔ | |
709716
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | | | ✔ | |
710717
| `'ECDH'` | | | ✔ | | |
711718
| `'ECDSA'` | | ✔ | | | |
@@ -824,6 +831,9 @@ The algorithms currently supported include:
824831
<!-- YAML
825832
added: v15.0.0
826833
changes:
834+
- version: REPLACEME
835+
pr-url: https://github.com/nodejs/node/pull/59539
836+
description: AES-OCB algorithm is now supported.
827837
- version: REPLACEME
828838
pr-url: https://github.com/nodejs/node/pull/59365
829839
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -844,6 +854,7 @@ The algorithms currently supported include:
844854
* `'AES-CBC'`
845855
* `'AES-CTR'`
846856
* `'AES-GCM'`
857+
* `'AES-OCB'`[^modern-algos]
847858
* `'ChaCha20-Poly1305'`[^modern-algos]
848859
* `'RSA-OAEP'`
849860
@@ -1014,6 +1025,9 @@ The algorithms currently supported include:
10141025
<!-- YAML
10151026
added: v15.0.0
10161027
changes:
1028+
- version: REPLACEME
1029+
pr-url: https://github.com/nodejs/node/pull/59539
1030+
description: AES-OCB algorithm is now supported.
10171031
- version: REPLACEME
10181032
pr-url: https://github.com/nodejs/node/pull/59365
10191033
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -1034,6 +1048,7 @@ The algorithms currently supported include:
10341048
* `'AES-CBC'`
10351049
* `'AES-CTR'`
10361050
* `'AES-GCM'`
1051+
* `'AES-OCB'`[^modern-algos]
10371052
* `'ChaCha20-Poly1305'`[^modern-algos]
10381053
* `'RSA-OAEP'`
10391054
@@ -1085,6 +1100,7 @@ specification.
10851100
| `'AES-CTR'` | | | ✔ | ✔ | ✔ | | |
10861101
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
10871102
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
1103+
| `'AES-OCB'`[^modern-algos] | | | ✔ | | ✔ | | |
10881104
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
10891105
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
10901106
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
@@ -1170,6 +1186,7 @@ The {CryptoKey} (secret key) generating algorithms supported include:
11701186
* `'AES-CTR'`
11711187
* `'AES-GCM'`
11721188
* `'AES-KW'`
1189+
* `'AES-OCB'`[^modern-algos]
11731190
* `'ChaCha20-Poly1305'`[^modern-algos]
11741191
* `'HMAC'`
11751192
@@ -1227,6 +1244,7 @@ The algorithms currently supported include:
12271244
| `'AES-CTR'` | | | ✔ | ✔ | ✔ | | |
12281245
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
12291246
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
1247+
| `'AES-OCB'`[^modern-algos] | | | ✔ | | ✔ | | |
12301248
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
12311249
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
12321250
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
@@ -1293,6 +1311,9 @@ The algorithms currently supported include:
12931311
<!-- YAML
12941312
added: v15.0.0
12951313
changes:
1314+
- version: REPLACEME
1315+
pr-url: https://github.com/nodejs/node/pull/59539
1316+
description: AES-OCB algorithm is now supported.
12961317
- version: REPLACEME
12971318
pr-url: https://github.com/nodejs/node/pull/59365
12981319
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -1329,6 +1350,7 @@ The wrapping algorithms currently supported include:
13291350
* `'AES-CTR'`
13301351
* `'AES-GCM'`
13311352
* `'AES-KW'`
1353+
* `'AES-OCB'`[^modern-algos]
13321354
* `'ChaCha20-Poly1305'`[^modern-algos]
13331355
* `'RSA-OAEP'`
13341356
@@ -1338,6 +1360,7 @@ The unwrapped key algorithms supported include:
13381360
* `'AES-CTR'`
13391361
* `'AES-GCM'`
13401362
* `'AES-KW'`
1363+
* `'AES-OCB'`[^modern-algos]
13411364
* `'ChaCha20-Poly1305'`[^modern-algos]
13421365
* `'ECDH'`
13431366
* `'ECDSA'`
@@ -1403,6 +1426,9 @@ The algorithms currently supported include:
14031426
<!-- YAML
14041427
added: v15.0.0
14051428
changes:
1429+
- version: REPLACEME
1430+
pr-url: https://github.com/nodejs/node/pull/59539
1431+
description: AES-OCB algorithm is now supported.
14061432
- version: REPLACEME
14071433
pr-url: https://github.com/nodejs/node/pull/59365
14081434
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -1435,6 +1461,7 @@ The wrapping algorithms currently supported include:
14351461
* `'AES-CTR'`
14361462
* `'AES-GCM'`
14371463
* `'AES-KW'`
1464+
* `'AES-OCB'`[^modern-algos]
14381465
* `'ChaCha20-Poly1305'`[^modern-algos]
14391466
* `'RSA-OAEP'`
14401467
@@ -1492,7 +1519,7 @@ given key.
14921519
added: v15.0.0
14931520
-->
14941521
1495-
* Type: {string} Must be `'AES-GCM'` or `'ChaCha20-Poly1305'`.
1522+
* Type: {string} Must be `'AES-GCM'`, `'AES-OCB'`, or `'ChaCha20-Poly1305'`.
14961523
14971524
#### `aeadParams.tagLength`
14981525
@@ -1514,8 +1541,7 @@ added: v15.0.0
15141541
added: v15.0.0
15151542
-->
15161543
1517-
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, or
1518-
`'AES-KW'`
1544+
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, `'AES-OCB'`, or `'AES-KW'`
15191545
15201546
#### `aesDerivedKeyParams.length`
15211547
@@ -2391,6 +2417,8 @@ The length (in bytes) of the random salt to use.
23912417
23922418
[^modern-algos]: See [Modern Algorithms in the Web Cryptography API][]
23932419
2420+
[^openssl30]: Requires OpenSSL >= 3.0
2421+
23942422
[^openssl35]: Requires OpenSSL >= 3.5
23952423
23962424
[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)