Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: add support for chacha20-poly1305 for AEAD #24081

Merged
merged 1 commit into from
Nov 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/api/crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,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.
Expand Down Expand Up @@ -1468,6 +1471,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.
Expand Down
55 changes: 30 additions & 25 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a comment here to state that chacha20-poly1305 is an AEAD cipher, but that its mode of 0 doesn't indicate that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add a comment to indicate it

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);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 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);
Expand Down Expand Up @@ -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 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;
Expand All @@ -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());
}


Expand Down Expand Up @@ -2901,7 +2906,7 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
} else {
// 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);
is_valid = cipher->auth_tag_len_ == tag_len;
}
Expand Down Expand Up @@ -3108,7 +3113,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();
}

Expand Down
37 changes: 37 additions & 0 deletions test/fixtures/aead-vectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
chux0519 marked this conversation as resolved.
Show resolved Hide resolved

{ 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
}
];
3 changes: 2 additions & 1 deletion test/parallel/test-crypto-authenticated.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,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';
Expand Down