diff options
-rw-r--r-- | doc/api/crypto.md | 6 | ||||
-rw-r--r-- | src/node_crypto.cc | 55 | ||||
-rw-r--r-- | test/fixtures/aead-vectors.js | 37 | ||||
-rw-r--r-- | test/parallel/test-crypto-authenticated.js | 3 |
4 files changed, 75 insertions, 26 deletions
diff --git a/doc/api/crypto.md b/doc/api/crypto.md index c4ce5681b0a..087840bd938 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1444,6 +1444,9 @@ Adversaries][] for details. <!-- YAML added: v0.1.94 changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/24081 + description: The cipher `chacha20-poly1305` is now supported. - version: v10.10.0 pr-url: https://github.com/nodejs/node/pull/21447 description: Ciphers in OCB mode are now supported. @@ -1549,6 +1552,9 @@ to create the `Decipher` object. <!-- YAML added: v0.1.94 changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/24081 + description: The cipher `chacha20-poly1305` is now supported. - version: v10.10.0 pr-url: https://github.com/nodejs/node/pull/21447 description: Ciphers in OCB mode are now supported. diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 5f135c2e0ab..3d15cac394c 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -2557,12 +2557,21 @@ int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) { return 1; } -static bool IsSupportedAuthenticatedMode(int mode) { - return mode == EVP_CIPH_CCM_MODE || +static bool IsSupportedAuthenticatedMode(const EVP_CIPHER* cipher) { + const int mode = EVP_CIPHER_mode(cipher); + // Check `chacha20-poly1305` separately, it is also an AEAD cipher, + // but its mode is 0 which doesn't indicate + return EVP_CIPHER_nid(cipher) == NID_chacha20_poly1305 || + mode == EVP_CIPH_CCM_MODE || mode == EVP_CIPH_GCM_MODE || IS_OCB_MODE(mode); } +static bool IsSupportedAuthenticatedMode(const EVP_CIPHER_CTX* ctx) { + const EVP_CIPHER* cipher = EVP_CIPHER_CTX_cipher(ctx); + return IsSupportedAuthenticatedMode(cipher); +} + void CipherBase::Initialize(Environment* env, Local<Object> target) { Local<FunctionTemplate> t = env->NewFunctionTemplate(New); @@ -2610,7 +2619,7 @@ void CipherBase::CommonInit(const char* cipher_type, "Failed to initialize cipher"); } - if (IsSupportedAuthenticatedMode(mode)) { + if (IsSupportedAuthenticatedMode(cipher)) { CHECK_GE(iv_len, 0); if (!InitAuthenticated(cipher_type, iv_len, auth_tag_len)) return; @@ -2712,8 +2721,7 @@ void CipherBase::InitIv(const char* cipher_type, } const int expected_iv_len = EVP_CIPHER_iv_length(cipher); - const int mode = EVP_CIPHER_mode(cipher); - const bool is_authenticated_mode = IsSupportedAuthenticatedMode(mode); + const bool is_authenticated_mode = IsSupportedAuthenticatedMode(cipher); const bool has_iv = iv_len >= 0; // Throw if no IV was passed and the cipher requires an IV @@ -2785,7 +2793,20 @@ bool CipherBase::InitAuthenticated(const char* cipher_type, int iv_len, } const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); - if (mode == EVP_CIPH_CCM_MODE || IS_OCB_MODE(mode)) { + if (mode == EVP_CIPH_GCM_MODE) { + if (auth_tag_len != kNoAuthTagLength) { + if (!IsValidGCMTagLength(auth_tag_len)) { + char msg[50]; + snprintf(msg, sizeof(msg), + "Invalid GCM authentication tag length: %u", auth_tag_len); + env()->ThrowError(msg); + return false; + } + + // Remember the given authentication tag length for later. + auth_tag_len_ = auth_tag_len; + } + } else { if (auth_tag_len == kNoAuthTagLength) { char msg[128]; snprintf(msg, sizeof(msg), "authTagLength required for %s", cipher_type); @@ -2818,21 +2839,6 @@ bool CipherBase::InitAuthenticated(const char* cipher_type, int iv_len, if (iv_len == 12) max_message_size_ = 16777215; if (iv_len == 13) max_message_size_ = 65535; } - } else { - CHECK_EQ(mode, EVP_CIPH_GCM_MODE); - - if (auth_tag_len != kNoAuthTagLength) { - if (!IsValidGCMTagLength(auth_tag_len)) { - char msg[50]; - snprintf(msg, sizeof(msg), - "Invalid GCM authentication tag length: %u", auth_tag_len); - env()->ThrowError(msg); - return false; - } - - // Remember the given authentication tag length for later. - auth_tag_len_ = auth_tag_len; - } } return true; @@ -2855,8 +2861,7 @@ bool CipherBase::CheckCCMMessageLength(int message_len) { bool CipherBase::IsAuthenticatedMode() const { // Check if this cipher operates in an AEAD mode that we support. CHECK(ctx_); - const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); - return IsSupportedAuthenticatedMode(mode); + return IsSupportedAuthenticatedMode(ctx_.get()); } @@ -2913,7 +2918,7 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) { } else if (mode == EVP_CIPH_OCB_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 || IS_OCB_MODE(mode)); + CHECK(IsSupportedAuthenticatedMode(cipher->ctx_.get())); CHECK_NE(cipher->auth_tag_len_, kNoAuthTagLength); if (cipher->auth_tag_len_ != tag_len) { char msg[50]; @@ -3120,7 +3125,7 @@ bool CipherBase::Final(unsigned char** out, int* out_len) { *out = Malloc<unsigned char>( static_cast<size_t>(EVP_CIPHER_CTX_block_size(ctx_.get()))); - if (kind_ == kDecipher && IsSupportedAuthenticatedMode(mode)) { + if (kind_ == kDecipher && IsSupportedAuthenticatedMode(ctx_.get())) { MaybePassAuthTagToOpenSSL(); } diff --git a/test/fixtures/aead-vectors.js b/test/fixtures/aead-vectors.js index 14933bb0cc6..6b1d169b2a7 100644 --- a/test/fixtures/aead-vectors.js +++ b/test/fixtures/aead-vectors.js @@ -662,5 +662,42 @@ module.exports = [ '481529c76b6a', tag: 'd0c515f4d1cdd4fdac4f02ab', tampered: true + }, + + // Test case from rfc7539 section 2.8.2 + { algo: 'chacha20-poly1305', + key: '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f', + iv: '070000004041424344454647', + plain: '4c616469657320616e642047656e746c656d656e206f662074686520636c6173' + + '73206f66202739393a204966204920636f756c64206f6666657220796f75206f' + + '6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73' + + '637265656e20776f756c642062652069742e', + plainIsHex: true, + aad: '50515253c0c1c2c3c4c5c6c7', + ct: 'd31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5' + + 'a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e06' + + '0b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fa' + + 'b324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d265' + + '86cec64b6116', + tag: '1ae10b594f09e26a7e902ecbd0600691', + tampered: false + }, + + { algo: 'chacha20-poly1305', + key: '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f', + iv: '070000004041424344454647', + plain: '4c616469657320616e642047656e746c656d656e206f662074686520636c6173' + + '73206f66202739393a204966204920636f756c64206f6666657220796f75206f' + + '6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73' + + '637265656e20776f756c642062652069742e', + plainIsHex: true, + aad: '50515253c0c1c2c3c4c5c6c7', + ct: 'd31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5' + + 'a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e06' + + '0b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fa' + + 'b324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d265' + + '86cec64b6116', + tag: '1ae10b594f09e26a7e902ecbd0600692', + tampered: true } ]; diff --git a/test/parallel/test-crypto-authenticated.js b/test/parallel/test-crypto-authenticated.js index dc19a7b2ab8..14235de7f68 100644 --- a/test/parallel/test-crypto-authenticated.js +++ b/test/parallel/test-crypto-authenticated.js @@ -97,9 +97,10 @@ for (const test of TEST_CASES) { const isCCM = /^aes-(128|192|256)-ccm$/.test(test.algo); const isOCB = /^aes-(128|192|256)-ocb$/.test(test.algo); + const isChacha20Poly1305 = test.algo === 'chacha20-poly1305'; let options; - if (isCCM || isOCB) + if (isCCM || isOCB || isChacha20Poly1305) options = { authTagLength: test.tag.length / 2 }; const inputEncoding = test.plainIsHex ? 'hex' : 'ascii'; |