mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
crypto: add support for chacha20-poly1305 for AEAD
openSSL supports AEAD_CHACHA20_POLY1305(rfc7539) since 1.1. PR-URL: https://github.com/nodejs/node/pull/24081 Fixes: https://github.com/nodejs/node/issues/24080 Refs: https://tools.ietf.org/html/rfc7539 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
parent
12c0fd4c9a
commit
5c59622243
@ -1382,6 +1382,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/24081
|
||||||
|
description: The cipher `chacha20-poly1305` is now supported.
|
||||||
- version: v10.10.0
|
- version: v10.10.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/21447
|
pr-url: https://github.com/nodejs/node/pull/21447
|
||||||
description: Ciphers in OCB mode are now supported.
|
description: Ciphers in OCB mode are now supported.
|
||||||
@ -1468,6 +1471,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/24081
|
||||||
|
description: The cipher `chacha20-poly1305` is now supported.
|
||||||
- version: v10.10.0
|
- version: v10.10.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/21447
|
pr-url: https://github.com/nodejs/node/pull/21447
|
||||||
description: Ciphers in OCB mode are now supported.
|
description: Ciphers in OCB mode are now supported.
|
||||||
|
@ -2557,12 +2557,21 @@ int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsSupportedAuthenticatedMode(int mode) {
|
static bool IsSupportedAuthenticatedMode(const EVP_CIPHER* cipher) {
|
||||||
return mode == EVP_CIPH_CCM_MODE ||
|
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 ||
|
mode == EVP_CIPH_GCM_MODE ||
|
||||||
IS_OCB_MODE(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) {
|
void CipherBase::Initialize(Environment* env, Local<Object> target) {
|
||||||
Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
|
Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
|
||||||
|
|
||||||
@ -2610,7 +2619,7 @@ void CipherBase::CommonInit(const char* cipher_type,
|
|||||||
"Failed to initialize cipher");
|
"Failed to initialize cipher");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsSupportedAuthenticatedMode(mode)) {
|
if (IsSupportedAuthenticatedMode(cipher)) {
|
||||||
CHECK_GE(iv_len, 0);
|
CHECK_GE(iv_len, 0);
|
||||||
if (!InitAuthenticated(cipher_type, iv_len, auth_tag_len))
|
if (!InitAuthenticated(cipher_type, iv_len, auth_tag_len))
|
||||||
return;
|
return;
|
||||||
@ -2712,8 +2721,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 bool is_authenticated_mode = IsSupportedAuthenticatedMode(cipher);
|
||||||
const bool is_authenticated_mode = IsSupportedAuthenticatedMode(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
|
||||||
@ -2785,7 +2793,20 @@ 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 || 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 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) {
|
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);
|
||||||
@ -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 == 12) max_message_size_ = 16777215;
|
||||||
if (iv_len == 13) max_message_size_ = 65535;
|
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 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;
|
return true;
|
||||||
@ -2855,8 +2861,7 @@ bool CipherBase::CheckCCMMessageLength(int message_len) {
|
|||||||
bool CipherBase::IsAuthenticatedMode() const {
|
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());
|
return IsSupportedAuthenticatedMode(ctx_.get());
|
||||||
return IsSupportedAuthenticatedMode(mode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2901,7 +2906,7 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
|
|||||||
} else {
|
} else {
|
||||||
// At this point, the tag length is already known and must match the
|
// At this point, the tag length is already known and must match the
|
||||||
// length of the given authentication tag.
|
// 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);
|
CHECK_NE(cipher->auth_tag_len_, kNoAuthTagLength);
|
||||||
is_valid = cipher->auth_tag_len_ == tag_len;
|
is_valid = cipher->auth_tag_len_ == tag_len;
|
||||||
}
|
}
|
||||||
@ -3108,7 +3113,7 @@ bool CipherBase::Final(unsigned char** out, int* out_len) {
|
|||||||
*out = Malloc<unsigned char>(
|
*out = Malloc<unsigned char>(
|
||||||
static_cast<size_t>(EVP_CIPHER_CTX_block_size(ctx_.get())));
|
static_cast<size_t>(EVP_CIPHER_CTX_block_size(ctx_.get())));
|
||||||
|
|
||||||
if (kind_ == kDecipher && IsSupportedAuthenticatedMode(mode)) {
|
if (kind_ == kDecipher && IsSupportedAuthenticatedMode(ctx_.get())) {
|
||||||
MaybePassAuthTagToOpenSSL();
|
MaybePassAuthTagToOpenSSL();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
37
test/fixtures/aead-vectors.js
vendored
37
test/fixtures/aead-vectors.js
vendored
@ -662,5 +662,42 @@ module.exports = [
|
|||||||
'481529c76b6a',
|
'481529c76b6a',
|
||||||
tag: 'd0c515f4d1cdd4fdac4f02ab',
|
tag: 'd0c515f4d1cdd4fdac4f02ab',
|
||||||
tampered: true
|
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
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -94,9 +94,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);
|
const isOCB = /^aes-(128|192|256)-ocb$/.test(test.algo);
|
||||||
|
const isChacha20Poly1305 = test.algo === 'chacha20-poly1305';
|
||||||
|
|
||||||
let options;
|
let options;
|
||||||
if (isCCM || isOCB)
|
if (isCCM || isOCB || isChacha20Poly1305)
|
||||||
options = { authTagLength: test.tag.length / 2 };
|
options = { authTagLength: test.tag.length / 2 };
|
||||||
|
|
||||||
const inputEncoding = test.plainIsHex ? 'hex' : 'ascii';
|
const inputEncoding = test.plainIsHex ? 'hex' : 'ascii';
|
||||||
|
Loading…
Reference in New Issue
Block a user