crypto: add support for OCB mode for AEAD

PR-URL: https://github.com/nodejs/node/pull/21447
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Tobias Nießen 2018-06-14 15:18:14 +02:00
parent b75bde3bc5
commit b3f459e6cf
No known key found for this signature in database
GPG Key ID: 718207F8FD156B70
3 changed files with 290 additions and 53 deletions

View File

@ -249,11 +249,11 @@ added: v1.0.0
- `plaintextLength` {number} - `plaintextLength` {number}
* Returns: {Cipher} for method chaining. * Returns: {Cipher} for method chaining.
When using an authenticated encryption mode (only `GCM` and `CCM` are currently When using an authenticated encryption mode (`GCM`, `CCM` and `OCB` are
supported), the `cipher.setAAD()` method sets the value used for the currently supported), the `cipher.setAAD()` method sets the value used for the
_additional authenticated data_ (AAD) input parameter. _additional authenticated data_ (AAD) input parameter.
The `options` argument is optional for `GCM`. When using `CCM`, the The `options` argument is optional for `GCM` and `OCB`. When using `CCM`, the
`plaintextLength` option must be specified and its value must match the length `plaintextLength` option must be specified and its value must match the length
of the plaintext in bytes. See [CCM mode][]. of the plaintext in bytes. See [CCM mode][].
@ -263,8 +263,8 @@ The `cipher.setAAD()` method must be called before [`cipher.update()`][].
<!-- YAML <!-- YAML
added: v1.0.0 added: v1.0.0
--> -->
* Returns: {Buffer} When using an authenticated encryption mode (only `GCM` and * Returns: {Buffer} When using an authenticated encryption mode (`GCM`, `CCM`
`CCM` are currently supported), the `cipher.getAuthTag()` method returns a and `OCB` are currently supported), the `cipher.getAuthTag()` method returns a
[`Buffer`][] containing the _authentication tag_ that has been computed from [`Buffer`][] containing the _authentication tag_ that has been computed from
the given data. the given data.
@ -412,8 +412,8 @@ changes:
- `plaintextLength` {number} - `plaintextLength` {number}
* Returns: {Decipher} for method chaining. * Returns: {Decipher} for method chaining.
When using an authenticated encryption mode (only `GCM` and `CCM` are currently When using an authenticated encryption mode (`GCM`, `CCM` and `OCB` are
supported), the `decipher.setAAD()` method sets the value used for the currently supported), the `decipher.setAAD()` method sets the value used for the
_additional authenticated data_ (AAD) input parameter. _additional authenticated data_ (AAD) input parameter.
The `options` argument is optional for `GCM`. When using `CCM`, the The `options` argument is optional for `GCM`. When using `CCM`, the
@ -436,8 +436,8 @@ changes:
* `buffer` {Buffer | TypedArray | DataView} * `buffer` {Buffer | TypedArray | DataView}
* Returns: {Decipher} for method chaining. * Returns: {Decipher} for method chaining.
When using an authenticated encryption mode (only `GCM` and `CCM` are currently When using an authenticated encryption mode (`GCM`, `CCM` and `OCB` are
supported), the `decipher.setAuthTag()` method is used to pass in the currently supported), the `decipher.setAuthTag()` method is used to pass in the
received _authentication tag_. If no tag is provided, or if the cipher text received _authentication tag_. If no tag is provided, or if the cipher text
has been tampered with, [`decipher.final()`][] will throw, indicating that the has been tampered with, [`decipher.final()`][] will throw, indicating that the
cipher text should be discarded due to failed authentication. If the tag length cipher text should be discarded due to failed authentication. If the tag length
@ -1321,6 +1321,9 @@ This property is deprecated. Please use `crypto.setFips()` and
added: v0.1.94 added: v0.1.94
deprecated: v10.0.0 deprecated: v10.0.0
changes: changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/21447
description: Ciphers in OCB mode are now supported.
- version: v10.2.0 - version: v10.2.0
pr-url: https://github.com/nodejs/node/pull/20235 pr-url: https://github.com/nodejs/node/pull/20235
description: The `authTagLength` option can now be used to produce shorter description: The `authTagLength` option can now be used to produce shorter
@ -1338,7 +1341,7 @@ Creates and returns a `Cipher` object that uses the given `algorithm` and
`password`. `password`.
The `options` argument controls stream behavior and is optional except when a The `options` argument controls stream behavior and is optional except when a
cipher in CCM mode is used (e.g. `'aes-128-ccm'`). In that case, the cipher in CCM or OCB mode is used (e.g. `'aes-128-ccm'`). In that case, the
`authTagLength` option is required and specifies the length of the `authTagLength` option is required and specifies the length of the
authentication tag in bytes, see [CCM mode][]. In GCM mode, the `authTagLength` authentication tag in bytes, see [CCM mode][]. In GCM mode, the `authTagLength`
option is not required but can be used to set the length of the authentication option is not required but can be used to set the length of the authentication
@ -1373,6 +1376,9 @@ Adversaries][] for details.
<!-- YAML <!-- YAML
added: v0.1.94 added: v0.1.94
changes: changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/21447
description: Ciphers in OCB mode are now supported.
- version: v10.2.0 - version: v10.2.0
pr-url: https://github.com/nodejs/node/pull/20235 pr-url: https://github.com/nodejs/node/pull/20235
description: The `authTagLength` option can now be used to produce shorter description: The `authTagLength` option can now be used to produce shorter
@ -1392,7 +1398,7 @@ Creates and returns a `Cipher` object, with the given `algorithm`, `key` and
initialization vector (`iv`). initialization vector (`iv`).
The `options` argument controls stream behavior and is optional except when a The `options` argument controls stream behavior and is optional except when a
cipher in CCM mode is used (e.g. `'aes-128-ccm'`). In that case, the cipher in CCM or OCB mode is used (e.g. `'aes-128-ccm'`). In that case, the
`authTagLength` option is required and specifies the length of the `authTagLength` option is required and specifies the length of the
authentication tag in bytes, see [CCM mode][]. In GCM mode, the `authTagLength` authentication tag in bytes, see [CCM mode][]. In GCM mode, the `authTagLength`
option is not required but can be used to set the length of the authentication option is not required but can be used to set the length of the authentication
@ -1419,6 +1425,10 @@ of time what a given IV will be.
<!-- YAML <!-- YAML
added: v0.1.94 added: v0.1.94
deprecated: v10.0.0 deprecated: v10.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/21447
description: Ciphers in OCB mode are now supported.
--> -->
> Stability: 0 - Deprecated: Use [`crypto.createDecipheriv()`][] instead. > Stability: 0 - Deprecated: Use [`crypto.createDecipheriv()`][] instead.
@ -1432,7 +1442,7 @@ Creates and returns a `Decipher` object that uses the given `algorithm` and
`password` (key). `password` (key).
The `options` argument controls stream behavior and is optional except when a The `options` argument controls stream behavior and is optional except when a
cipher in CCM mode is used (e.g. `'aes-128-ccm'`). In that case, the cipher in CCM or OCB mode is used (e.g. `'aes-128-ccm'`). In that case, the
`authTagLength` option is required and specifies the length of the `authTagLength` option is required and specifies the length of the
authentication tag in bytes, see [CCM mode][]. authentication tag in bytes, see [CCM mode][].
@ -1452,6 +1462,9 @@ to create the `Decipher` object.
<!-- YAML <!-- YAML
added: v0.1.94 added: v0.1.94
changes: changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/21447
description: Ciphers in OCB mode are now supported.
- version: v10.2.0 - version: v10.2.0
pr-url: https://github.com/nodejs/node/pull/20039 pr-url: https://github.com/nodejs/node/pull/20039
description: The `authTagLength` option can now be used to restrict accepted description: The `authTagLength` option can now be used to restrict accepted
@ -1471,7 +1484,7 @@ Creates and returns a `Decipher` object that uses the given `algorithm`, `key`
and initialization vector (`iv`). and initialization vector (`iv`).
The `options` argument controls stream behavior and is optional except when a The `options` argument controls stream behavior and is optional except when a
cipher in CCM mode is used (e.g. `'aes-128-ccm'`). In that case, the cipher in CCM or OCB mode is used (e.g. `'aes-128-ccm'`). In that case, the
`authTagLength` option is required and specifies the length of the `authTagLength` option is required and specifies the length of the
authentication tag in bytes, see [CCM mode][]. In GCM mode, the `authTagLength` authentication tag in bytes, see [CCM mode][]. In GCM mode, the `authTagLength`
option is not required but can be used to restrict accepted authentication tags option is not required but can be used to restrict accepted authentication tags
@ -2321,7 +2334,7 @@ See the reference for other recommendations and details.
### CCM mode ### CCM mode
CCM is one of the two supported [AEAD algorithms][]. Applications which use this CCM is one of the supported [AEAD algorithms][]. Applications which use this
mode must adhere to certain restrictions when using the cipher API: mode must adhere to certain restrictions when using the cipher API:
- The authentication tag length must be specified during cipher creation by - The authentication tag length must be specified during cipher creation by

View File

@ -2683,6 +2683,11 @@ void CipherBase::Init(const FunctionCallbackInfo<Value>& args) {
cipher->Init(*cipher_type, key_buf, key_buf_len, auth_tag_len); cipher->Init(*cipher_type, key_buf, key_buf_len, auth_tag_len);
} }
static bool IsSupportedAuthenticatedMode(int mode) {
return mode == EVP_CIPH_CCM_MODE ||
mode == EVP_CIPH_GCM_MODE ||
mode == EVP_CIPH_OCB_MODE;
}
void CipherBase::InitIv(const char* cipher_type, void CipherBase::InitIv(const char* cipher_type,
const char* key, const char* key,
@ -2700,8 +2705,7 @@ void CipherBase::InitIv(const char* cipher_type,
const int expected_iv_len = EVP_CIPHER_iv_length(cipher); const int expected_iv_len = EVP_CIPHER_iv_length(cipher);
const int mode = EVP_CIPHER_mode(cipher); const int mode = EVP_CIPHER_mode(cipher);
const bool is_gcm_mode = (EVP_CIPH_GCM_MODE == mode); const bool is_authenticated_mode = IsSupportedAuthenticatedMode(mode);
const bool is_ccm_mode = (EVP_CIPH_CCM_MODE == mode);
const bool has_iv = iv_len >= 0; const bool has_iv = iv_len >= 0;
// Throw if no IV was passed and the cipher requires an IV // Throw if no IV was passed and the cipher requires an IV
@ -2712,7 +2716,7 @@ void CipherBase::InitIv(const char* cipher_type,
} }
// Throw if an IV was passed which does not match the cipher's fixed IV length // Throw if an IV was passed which does not match the cipher's fixed IV length
if (!is_gcm_mode && !is_ccm_mode && has_iv && iv_len != expected_iv_len) { if (!is_authenticated_mode && has_iv && iv_len != expected_iv_len) {
return env()->ThrowError("Invalid IV length"); return env()->ThrowError("Invalid IV length");
} }
@ -2728,7 +2732,7 @@ void CipherBase::InitIv(const char* cipher_type,
"Failed to initialize cipher"); "Failed to initialize cipher");
} }
if (IsAuthenticatedMode()) { if (is_authenticated_mode) {
CHECK(has_iv); CHECK(has_iv);
if (!InitAuthenticated(cipher_type, iv_len, auth_tag_len)) if (!InitAuthenticated(cipher_type, iv_len, auth_tag_len))
return; return;
@ -2803,7 +2807,7 @@ bool CipherBase::InitAuthenticated(const char* cipher_type, int iv_len,
} }
const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); const int mode = EVP_CIPHER_CTX_mode(ctx_.get());
if (mode == EVP_CIPH_CCM_MODE) { if (mode == EVP_CIPH_CCM_MODE || mode == EVP_CIPH_OCB_MODE) {
if (auth_tag_len == kNoAuthTagLength) { if (auth_tag_len == kNoAuthTagLength) {
char msg[128]; char msg[128];
snprintf(msg, sizeof(msg), "authTagLength required for %s", cipher_type); snprintf(msg, sizeof(msg), "authTagLength required for %s", cipher_type);
@ -2813,25 +2817,29 @@ bool CipherBase::InitAuthenticated(const char* cipher_type, int iv_len,
#ifdef NODE_FIPS_MODE #ifdef NODE_FIPS_MODE
// TODO(tniessen) Support CCM decryption in FIPS mode // TODO(tniessen) Support CCM decryption in FIPS mode
if (kind_ == kDecipher && FIPS_mode()) { if (mode == EVP_CIPH_CCM_MODE && kind_ == kDecipher && FIPS_mode()) {
env()->ThrowError("CCM decryption not supported in FIPS mode"); env()->ThrowError("CCM decryption not supported in FIPS mode");
return false; return false;
} }
#endif #endif
if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_CCM_SET_TAG, auth_tag_len, // Tell OpenSSL about the desired length.
if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_SET_TAG, auth_tag_len,
nullptr)) { nullptr)) {
env()->ThrowError("Invalid authentication tag length"); env()->ThrowError("Invalid authentication tag length");
return false; return false;
} }
// Remember the given authentication tag length for later.
auth_tag_len_ = auth_tag_len; auth_tag_len_ = auth_tag_len;
if (mode == EVP_CIPH_CCM_MODE) {
// Restrict the message length to min(INT_MAX, 2^(8*(15-iv_len))-1) bytes. // Restrict the message length to min(INT_MAX, 2^(8*(15-iv_len))-1) bytes.
CHECK(iv_len >= 7 && iv_len <= 13); CHECK(iv_len >= 7 && iv_len <= 13);
max_message_size_ = INT_MAX; max_message_size_ = INT_MAX;
if (iv_len == 12) max_message_size_ = 16777215; if (iv_len == 12) max_message_size_ = 16777215;
if (iv_len == 13) max_message_size_ = 65535; if (iv_len == 13) max_message_size_ = 65535;
}
} else { } else {
CHECK_EQ(mode, EVP_CIPH_GCM_MODE); CHECK_EQ(mode, EVP_CIPH_GCM_MODE);
@ -2870,7 +2878,7 @@ bool CipherBase::IsAuthenticatedMode() const {
// Check if this cipher operates in an AEAD mode that we support. // Check if this cipher operates in an AEAD mode that we support.
CHECK(ctx_); CHECK(ctx_);
const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); const int mode = EVP_CIPHER_CTX_mode(ctx_.get());
return mode == EVP_CIPH_GCM_MODE || mode == EVP_CIPH_CCM_MODE; return IsSupportedAuthenticatedMode(mode);
} }
@ -2903,16 +2911,18 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
return args.GetReturnValue().Set(false); return args.GetReturnValue().Set(false);
} }
// Restrict GCM tag lengths according to NIST 800-38d, page 9.
unsigned int tag_len = Buffer::Length(args[0]); unsigned int tag_len = Buffer::Length(args[0]);
const int mode = EVP_CIPHER_CTX_mode(cipher->ctx_.get()); const int mode = EVP_CIPHER_CTX_mode(cipher->ctx_.get());
bool is_valid; bool is_valid;
if (mode == EVP_CIPH_GCM_MODE) { if (mode == EVP_CIPH_GCM_MODE) {
// Restrict GCM tag lengths according to NIST 800-38d, page 9.
is_valid = (cipher->auth_tag_len_ == kNoAuthTagLength || is_valid = (cipher->auth_tag_len_ == kNoAuthTagLength ||
cipher->auth_tag_len_ == tag_len) && cipher->auth_tag_len_ == tag_len) &&
IsValidGCMTagLength(tag_len); IsValidGCMTagLength(tag_len);
} else { } else {
CHECK_EQ(mode, EVP_CIPH_CCM_MODE); // At this point, the tag length is already known and must match the
// length of the given authentication tag.
CHECK(mode == EVP_CIPH_CCM_MODE || mode == EVP_CIPH_OCB_MODE);
CHECK_NE(cipher->auth_tag_len_, kNoAuthTagLength); CHECK_NE(cipher->auth_tag_len_, kNoAuthTagLength);
is_valid = cipher->auth_tag_len_ == tag_len; is_valid = cipher->auth_tag_len_ == tag_len;
} }
@ -3008,7 +3018,7 @@ CipherBase::UpdateResult CipherBase::Update(const char* data,
if (kind_ == kDecipher && IsAuthenticatedMode() && auth_tag_len_ > 0 && if (kind_ == kDecipher && IsAuthenticatedMode() && auth_tag_len_ > 0 &&
auth_tag_len_ != kNoAuthTagLength && !auth_tag_set_) { auth_tag_len_ != kNoAuthTagLength && !auth_tag_set_) {
CHECK(EVP_CIPHER_CTX_ctrl(ctx_.get(), CHECK(EVP_CIPHER_CTX_ctrl(ctx_.get(),
EVP_CTRL_GCM_SET_TAG, EVP_CTRL_AEAD_SET_TAG,
auth_tag_len_, auth_tag_len_,
reinterpret_cast<unsigned char*>(auth_tag_))); reinterpret_cast<unsigned char*>(auth_tag_)));
auth_tag_set_ = true; auth_tag_set_ = true;
@ -3121,10 +3131,12 @@ bool CipherBase::Final(unsigned char** out, int* out_len) {
if (ok && kind_ == kCipher && IsAuthenticatedMode()) { if (ok && kind_ == kCipher && IsAuthenticatedMode()) {
// In GCM mode, the authentication tag length can be specified in advance, // In GCM mode, the authentication tag length can be specified in advance,
// but defaults to 16 bytes when encrypting. In CCM mode, it must always // but defaults to 16 bytes when encrypting. In CCM and OCB mode, it must
// be given by the user. // always be given by the user.
if (mode == EVP_CIPH_GCM_MODE && auth_tag_len_ == kNoAuthTagLength) if (auth_tag_len_ == kNoAuthTagLength) {
CHECK(mode == EVP_CIPH_GCM_MODE);
auth_tag_len_ = sizeof(auth_tag_); auth_tag_len_ = sizeof(auth_tag_);
}
CHECK_EQ(1, EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_GET_TAG, CHECK_EQ(1, EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_GET_TAG,
auth_tag_len_, auth_tag_len_,
reinterpret_cast<unsigned char*>(auth_tag_))); reinterpret_cast<unsigned char*>(auth_tag_)));

View File

@ -499,6 +499,207 @@ const TEST_CASES = [
tag: '65a6002b2cdfe9f00027f839332ca6fc', tag: '65a6002b2cdfe9f00027f839332ca6fc',
tampered: false tampered: false
}, },
// OCB test cases from RFC7253
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa99887766554433221100',
plain: '',
ct: '',
tag: '785407bfffc8ad9edcc5520ac9111ee6'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa99887766554433221101',
plain: '0001020304050607',
plainIsHex: true,
aad: '0001020304050607',
ct: '6820b3657b6f615a',
tag: '5725bda0d3b4eb3a257c9af1f8f03009'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa99887766554433221102',
plain: '',
aad: '0001020304050607',
ct: '',
tag: '81017f8203f081277152fade694a0a00'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa99887766554433221103',
plain: '0001020304050607',
plainIsHex: true,
ct: '45dd69f8f5aae724',
tag: '14054cd1f35d82760b2cd00d2f99bfa9'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa99887766554433221104',
plain: '000102030405060708090a0b0c0d0e0f',
plainIsHex: true,
aad: '000102030405060708090a0b0c0d0e0f',
ct: '571d535b60b277188be5147170a9a22c',
tag: '3ad7a4ff3835b8c5701c1ccec8fc3358'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa99887766554433221105',
plain: '',
aad: '000102030405060708090a0b0c0d0e0f',
ct: '',
tag: '8cf761b6902ef764462ad86498ca6b97'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa99887766554433221106',
plain: '000102030405060708090a0b0c0d0e0f',
plainIsHex: true,
ct: '5ce88ec2e0692706a915c00aeb8b2396',
tag: 'f40e1c743f52436bdf06d8fa1eca343d'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa99887766554433221107',
plain: '000102030405060708090a0b0c0d0e0f1011121314151617',
plainIsHex: true,
aad: '000102030405060708090a0b0c0d0e0f1011121314151617',
ct: '1ca2207308c87c010756104d8840ce1952f09673a448a122',
tag: 'c92c62241051f57356d7f3c90bb0e07f'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa99887766554433221108',
plain: '',
aad: '000102030405060708090a0b0c0d0e0f1011121314151617',
ct: '',
tag: '6dc225a071fc1b9f7c69f93b0f1e10de'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa99887766554433221109',
plain: '000102030405060708090a0b0c0d0e0f1011121314151617',
plainIsHex: true,
ct: '221bd0de7fa6fe993eccd769460a0af2d6cded0c395b1c3c',
tag: 'e725f32494b9f914d85c0b1eb38357ff'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa9988776655443322110a',
plain: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
plainIsHex: true,
aad: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
ct: 'bd6f6c496201c69296c11efd138a467abd3c707924b964deaffc40319af5a485',
tag: '40fbba186c5553c68ad9f592a79a4240'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa9988776655443322110b',
plain: '',
aad: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
ct: '',
tag: 'fe80690bee8a485d11f32965bc9d2a32'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa9988776655443322110c',
plain: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
plainIsHex: true,
ct: '2942bfc773bda23cabc6acfd9bfd5835bd300f0973792ef46040c53f1432bcdf',
tag: 'b5e1dde3bc18a5f840b52e653444d5df'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa9988776655443322110d',
plain: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f' +
'2021222324252627',
plainIsHex: true,
aad: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20' +
'21222324252627',
ct: 'd5ca91748410c1751ff8a2f618255b68a0a12e093ff454606e59f9c1d0ddc54b65e8' +
'628e568bad7a',
tag: 'ed07ba06a4a69483a7035490c5769e60'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa9988776655443322110e',
plain: '',
plainIsHex: true,
aad: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20' +
'21222324252627',
ct: '',
tag: 'c5cd9d1850c141e358649994ee701b68'
},
{
algo: 'aes-128-ocb',
key: '000102030405060708090a0b0c0d0e0f',
iv: 'bbaa9988776655443322110f',
plain: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f' +
'2021222324252627',
plainIsHex: true,
ct: '4412923493c57d5de0d700f753cce0d1d2d95060122e9f15a5ddbfc5787e50b5cc55' +
'ee507bcb084e',
tag: '479ad363ac366b95a98ca5f3000b1479'
},
{
algo: 'aes-128-ocb',
key: '0f0e0d0c0b0a09080706050403020100',
iv: 'bbaa9988776655443322110d',
plain: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f' +
'2021222324252627',
plainIsHex: true,
aad: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20' +
'21222324252627',
ct: '1792a4e31e0755fb03e31b22116e6c2ddf9efd6e33d536f1a0124b0a55bae884ed93' +
'481529c76b6a',
tag: 'd0c515f4d1cdd4fdac4f02aa'
},
{
algo: 'aes-128-ocb',
key: '0f0e0d0c0b0a09080706050403020100',
iv: 'bbaa9988776655443322110d',
plain: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f' +
'2021222324252627',
plainIsHex: true,
aad: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20' +
'21222324252627',
ct: '1792a4e31e0755fb03e31b22116e6c2ddf9efd6e33d536f1a0124b0a55bae884ed93' +
'481529c76b6a',
tag: 'd0c515f4d1cdd4fdac4f02ab',
tampered: true
}
]; ];
const errMessages = { const errMessages = {
@ -554,9 +755,10 @@ for (const test of TEST_CASES) {
} }
const isCCM = /^aes-(128|192|256)-ccm$/.test(test.algo); const isCCM = /^aes-(128|192|256)-ccm$/.test(test.algo);
const isOCB = /^aes-(128|192|256)-ocb$/.test(test.algo);
let options; let options;
if (isCCM) if (isCCM || isOCB)
options = { authTagLength: test.tag.length / 2 }; options = { authTagLength: test.tag.length / 2 };
const inputEncoding = test.plainIsHex ? 'hex' : 'ascii'; const inputEncoding = test.plainIsHex ? 'hex' : 'ascii';
@ -881,30 +1083,40 @@ for (const test of TEST_CASES) {
} }
} }
// Test that create(De|C)ipher(iv)? throws if the mode is CCM and no // Test that create(De|C)ipher(iv)? throws if the mode is CCM or OCB and no
// authentication tag has been specified. // authentication tag has been specified.
{ {
for (const mode of ['ccm', 'ocb']) {
assert.throws(() => { assert.throws(() => {
crypto.createCipheriv('aes-256-ccm', crypto.createCipheriv(`aes-256-${mode}`,
'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
'qkuZpJWCewa6S'); 'qkuZpJWCewa6S');
}, /^Error: authTagLength required for aes-256-ccm$/); }, {
message: `authTagLength required for aes-256-${mode}`
});
// CCM decryption and create(De|C)ipher are unsupported in FIPS mode. // CCM decryption and create(De|C)ipher are unsupported in FIPS mode.
if (!common.hasFipsCrypto) { if (!common.hasFipsCrypto) {
assert.throws(() => { assert.throws(() => {
crypto.createDecipheriv('aes-256-ccm', crypto.createDecipheriv(`aes-256-${mode}`,
'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
'qkuZpJWCewa6S'); 'qkuZpJWCewa6S');
}, /^Error: authTagLength required for aes-256-ccm$/); }, {
message: `authTagLength required for aes-256-${mode}`
});
assert.throws(() => { assert.throws(() => {
crypto.createCipher('aes-256-ccm', 'very bad password'); crypto.createCipher(`aes-256-${mode}`, 'very bad password');
}, /^Error: authTagLength required for aes-256-ccm$/); }, {
message: `authTagLength required for aes-256-${mode}`
});
assert.throws(() => { assert.throws(() => {
crypto.createDecipher('aes-256-ccm', 'very bad password'); crypto.createDecipher(`aes-256-${mode}`, 'very bad password');
}, /^Error: authTagLength required for aes-256-ccm$/); }, {
message: `authTagLength required for aes-256-${mode}`
});
}
} }
} }