Skip to content

Commit 6930654

Browse files
committed
crypto: add AES-OCB Web Cryptography algorithm
1 parent eaf1c15 commit 6930654

17 files changed

+448
-23
lines changed

deps/ncrypto/ncrypto.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2927,6 +2927,9 @@ const Cipher Cipher::AES_256_GCM = Cipher::FromNid(NID_aes_256_gcm);
29272927
const Cipher Cipher::AES_128_KW = Cipher::FromNid(NID_id_aes128_wrap);
29282928
const Cipher Cipher::AES_192_KW = Cipher::FromNid(NID_id_aes192_wrap);
29292929
const Cipher Cipher::AES_256_KW = Cipher::FromNid(NID_id_aes256_wrap);
2930+
const Cipher Cipher::AES_128_OCB = Cipher::FromNid(NID_aes_128_ocb);
2931+
const Cipher Cipher::AES_192_OCB = Cipher::FromNid(NID_aes_192_ocb);
2932+
const Cipher Cipher::AES_256_OCB = Cipher::FromNid(NID_aes_256_ocb);
29302933
const Cipher Cipher::CHACHA20_POLY1305 = Cipher::FromNid(NID_chacha20_poly1305);
29312934

29322935
bool Cipher::isGcmMode() const {
@@ -3129,6 +3132,11 @@ bool CipherCtxPointer::isGcmMode() const {
31293132
return getMode() == EVP_CIPH_GCM_MODE;
31303133
}
31313134

3135+
bool CipherCtxPointer::isOcbMode() const {
3136+
if (!ctx_) return false;
3137+
return getMode() == EVP_CIPH_OCB_MODE;
3138+
}
3139+
31323140
bool CipherCtxPointer::isCcmMode() const {
31333141
if (!ctx_) return false;
31343142
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: 39 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/59365
7+
description: AES-OCB algorithm is now supported.
58
- version: REPLACEME
69
pr-url: https://github.com/nodejs/node/pull/59365
710
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -101,6 +104,7 @@ WICG proposal:
101104

102105
Algorithms:
103106

107+
* `'AES-OCB'`[^openssl30]
104108
* `'ChaCha20-Poly1305'`
105109
* `'cSHAKE128'`
106110
* `'cSHAKE256'`
@@ -489,6 +493,7 @@ implementation and the APIs supported for each:
489493
| `'AES-CTR'` | ✔ | ✔ | ✔ | ✔ | ✔ | | | | |
490494
| `'AES-GCM'` | ✔ | ✔ | ✔ | ✔ | ✔ | | | | |
491495
| `'AES-KW'` | ✔ | ✔ | ✔ | | ✔ | | | | |
496+
| `'AES-OCB'`[^modern-algos] | ✔ | ✔ | ✔ | ✔ | ✔ | | | | |
492497
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | ✔ | ✔ | ✔ | ✔ | | | | |
493498
| `'cSHAKE128'`[^modern-algos] | | | | | | | | ✔ | |
494499
| `'cSHAKE256'`[^modern-algos] | | | | | | | | ✔ | |
@@ -641,6 +646,7 @@ Valid key usages depend on the key algorithm (identified by
641646
| `'AES-CTR'` | ✔ | ✔ | | | | | ✔ | ✔ |
642647
| `'AES-GCM'` | ✔ | ✔ | | | | | ✔ | ✔ |
643648
| `'AES-KW'` | | | | | | | ✔ | ✔ |
649+
| `'AES-OCB'`[^modern-algos] | ✔ | ✔ | | | | | ✔ | ✔ |
644650
| `'ChaCha20-Poly1305'`[^modern-algos] | ✔ | ✔ | | | | | ✔ | ✔ |
645651
| `'ECDH'` | | | | | ✔ | ✔ | | |
646652
| `'ECDSA'` | | | ✔ | ✔ | | | | |
@@ -715,6 +721,9 @@ which can be used to detect whether a given algorithm identifier
715721
<!-- YAML
716722
added: v15.0.0
717723
changes:
724+
- version: REPLACEME
725+
pr-url: https://github.com/nodejs/node/pull/59365
726+
description: AES-OCB algorithm is now supported.
718727
- version: REPLACEME
719728
pr-url: https://github.com/nodejs/node/pull/59365
720729
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -735,6 +744,7 @@ The algorithms currently supported include:
735744
* `'AES-CBC'`
736745
* `'AES-CTR'`
737746
* `'AES-GCM'`
747+
* `'AES-OCB'`[^modern-algos]
738748
* `'ChaCha20-Poly1305'`[^modern-algos]
739749
* `'RSA-OAEP'`
740750
@@ -866,6 +876,9 @@ whose value is one of the above.
866876
<!-- YAML
867877
added: v15.0.0
868878
changes:
879+
- version: REPLACEME
880+
pr-url: https://github.com/nodejs/node/pull/59365
881+
description: AES-OCB algorithm is now supported.
869882
- version: REPLACEME
870883
pr-url: https://github.com/nodejs/node/pull/59365
871884
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -886,6 +899,7 @@ The algorithms currently supported include:
886899
* `'AES-CBC'`
887900
* `'AES-CTR'`
888901
* `'AES-GCM'`
902+
* `'AES-OCB'`[^modern-algos]
889903
* `'ChaCha20-Poly1305'`[^modern-algos]
890904
* `'RSA-OAEP'`
891905
@@ -894,6 +908,9 @@ The algorithms currently supported include:
894908
<!-- YAML
895909
added: v15.0.0
896910
changes:
911+
- version: REPLACEME
912+
pr-url: https://github.com/nodejs/node/pull/59365
913+
description: AES-OCB algorithm is now supported.
897914
- version: REPLACEME
898915
pr-url: https://github.com/nodejs/node/pull/59365
899916
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -934,6 +951,7 @@ specification.
934951
| `'AES-CTR'` | | | ✔ | ✔ | ✔ | | |
935952
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
936953
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
954+
| `'AES-OCB'`[^modern-algos] | | | ✔ | | ✔ | | |
937955
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
938956
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
939957
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
@@ -966,6 +984,9 @@ Derives the public key from a given private key.
966984
<!-- YAML
967985
added: v15.0.0
968986
changes:
987+
- version: REPLACEME
988+
pr-url: https://github.com/nodejs/node/pull/59365
989+
description: AES-OCB algorithm is now supported.
969990
- version: REPLACEME
970991
pr-url: https://github.com/nodejs/node/pull/59365
971992
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -1010,6 +1031,7 @@ The {CryptoKey} (secret key) generating algorithms supported include:
10101031
* `'AES-CTR'`
10111032
* `'AES-GCM'`
10121033
* `'AES-KW'`
1034+
* `'AES-OCB'`[^modern-algos]
10131035
* `'ChaCha20-Poly1305'`[^modern-algos]
10141036
* `'HMAC'`
10151037
@@ -1018,6 +1040,9 @@ The {CryptoKey} (secret key) generating algorithms supported include:
10181040
<!-- YAML
10191041
added: v15.0.0
10201042
changes:
1043+
- version: REPLACEME
1044+
pr-url: https://github.com/nodejs/node/pull/59365
1045+
description: AES-OCB algorithm is now supported.
10211046
- version: REPLACEME
10221047
pr-url: https://github.com/nodejs/node/pull/59365
10231048
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -1064,6 +1089,7 @@ The algorithms currently supported include:
10641089
| `'AES-CTR'` | | | ✔ | ✔ | ✔ | | |
10651090
| `'AES-GCM'` | | | ✔ | ✔ | ✔ | | |
10661091
| `'AES-KW'` | | | ✔ | ✔ | ✔ | | |
1092+
| `'AES-OCB'`[^modern-algos] | | | ✔ | | ✔ | | |
10671093
| `'ChaCha20-Poly1305'`[^modern-algos] | | | ✔ | | ✔ | | |
10681094
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
10691095
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
@@ -1127,6 +1153,9 @@ The algorithms currently supported include:
11271153
<!-- YAML
11281154
added: v15.0.0
11291155
changes:
1156+
- version: REPLACEME
1157+
pr-url: https://github.com/nodejs/node/pull/59365
1158+
description: AES-OCB algorithm is now supported.
11301159
- version: REPLACEME
11311160
pr-url: https://github.com/nodejs/node/pull/59365
11321161
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -1163,6 +1192,7 @@ The wrapping algorithms currently supported include:
11631192
* `'AES-CTR'`
11641193
* `'AES-GCM'`
11651194
* `'AES-KW'`
1195+
* `'AES-OCB'`[^modern-algos]
11661196
* `'ChaCha20-Poly1305'`[^modern-algos]
11671197
* `'RSA-OAEP'`
11681198
@@ -1172,6 +1202,7 @@ The unwrapped key algorithms supported include:
11721202
* `'AES-CTR'`
11731203
* `'AES-GCM'`
11741204
* `'AES-KW'`
1205+
* `'AES-OCB'`[^modern-algos]
11751206
* `'ChaCha20-Poly1305'`[^modern-algos]
11761207
* `'ECDH'`
11771208
* `'ECDSA'`
@@ -1234,6 +1265,9 @@ The algorithms currently supported include:
12341265
<!-- YAML
12351266
added: v15.0.0
12361267
changes:
1268+
- version: REPLACEME
1269+
pr-url: https://github.com/nodejs/node/pull/59365
1270+
description: AES-OCB algorithm is now supported.
12371271
- version: REPLACEME
12381272
pr-url: https://github.com/nodejs/node/pull/59365
12391273
description: ChaCha20-Poly1305 algorithm is now supported.
@@ -1266,6 +1300,7 @@ The wrapping algorithms currently supported include:
12661300
* `'AES-CTR'`
12671301
* `'AES-GCM'`
12681302
* `'AES-KW'`
1303+
* `'AES-OCB'`[^modern-algos]
12691304
* `'ChaCha20-Poly1305'`[^modern-algos]
12701305
* `'RSA-OAEP'`
12711306
@@ -1323,7 +1358,7 @@ given key.
13231358
added: v15.0.0
13241359
-->
13251360
1326-
* Type: {string} Must be `'AES-GCM'` or `'ChaCha20-Poly1305'`.
1361+
* Type: {string} Must be `'AES-GCM'`, `'AES-OCB'`, or `'ChaCha20-Poly1305'`.
13271362
13281363
#### `aeadParams.tagLength`
13291364
@@ -1345,8 +1380,7 @@ added: v15.0.0
13451380
added: v15.0.0
13461381
-->
13471382
1348-
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, or
1349-
`'AES-KW'`
1383+
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, `'AES-OCB'`, or `'AES-KW'`
13501384
13511385
#### `aesDerivedKeyParams.length`
13521386
@@ -2178,6 +2212,8 @@ The length (in bytes) of the random salt to use.
21782212
21792213
[^modern-algos]: See [Modern Algorithms in the Web Cryptography API][]
21802214
2215+
[^openssl30]: Requires OpenSSL >= 3.0
2216+
21812217
[^openssl35]: Requires OpenSSL >= 3.5
21822218
21832219
[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');
@@ -61,6 +64,7 @@ function getAlgorithmName(name, length) {
6164
case 'AES-CTR': return `A${length}CTR`;
6265
case 'AES-GCM': return `A${length}GCM`;
6366
case 'AES-KW': return `A${length}KW`;
67+
case 'AES-OCB': return `A${length}OCB`;
6468
}
6569
}
6670

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

@@ -172,11 +183,49 @@ function asyncAesGcmCipher(mode, key, data, algorithm) {
172183
algorithm.additionalData));
173184
}
174185

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

lib/internal/crypto/keys.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ const {
196196
case 'AES-GCM':
197197
// Fall through
198198
case 'AES-KW':
199+
// Fall through
200+
case 'AES-OCB':
199201
result = require('internal/crypto/aes')
200202
.aesImportKey(algorithm, 'KeyObject', this, extractable, keyUsages);
201203
break;

lib/internal/crypto/util.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const {
3636
EVP_PKEY_ML_DSA_44,
3737
EVP_PKEY_ML_DSA_65,
3838
EVP_PKEY_ML_DSA_87,
39+
kKeyVariantAES_OCB_128: hasAesOcbMode,
3940
} = internalBinding('crypto');
4041

4142
const { getOptionValue } = require('internal/options');
@@ -205,6 +206,14 @@ const kAlgorithmDefinitions = {
205206
'wrapKey': null,
206207
'unwrapKey': null,
207208
},
209+
'AES-OCB': {
210+
'generateKey': 'AesKeyGenParams',
211+
'exportKey': null,
212+
'importKey': null,
213+
'encrypt': 'AeadParams',
214+
'decrypt': 'AeadParams',
215+
'get key length': 'AesDerivedKeyParams',
216+
},
208217
'ChaCha20-Poly1305': {
209218
'generateKey': null,
210219
'exportKey': null,
@@ -326,6 +335,7 @@ const kAlgorithmDefinitions = {
326335
// Conditionally supported algorithms
327336
const conditionalAlgorithms = {
328337
'AES-KW': !process.features.openssl_is_boringssl,
338+
'AES-OCB': !!hasAesOcbMode,
329339
'ChaCha20-Poly1305': !process.features.openssl_is_boringssl ||
330340
ArrayPrototypeIncludes(getCiphers(), 'chacha20-poly1305'),
331341
'cSHAKE128': !process.features.openssl_is_boringssl ||
@@ -347,6 +357,7 @@ const conditionalAlgorithms = {
347357

348358
// Experimental algorithms
349359
const experimentalAlgorithms = [
360+
'AES-OCB',
350361
'ChaCha20-Poly1305',
351362
'cSHAKE128',
352363
'cSHAKE256',

0 commit comments

Comments
 (0)