From 27038ecda1a296072d5140442d016f69a6991304 Mon Sep 17 00:00:00 2001 From: Shigeki Ohtsu Date: Fri, 25 Aug 2017 01:42:55 +0900 Subject: [PATCH] crypto: fix error of createCipher in wrap mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EVP_CIPHER_CTX_FLAG_WRAP_ALLOW flag needs to be set in using wrap mode ciphers. In `crypto.createCipher()`, AES key wrap mode does not use a default IV defined in RFC3394 but a generated IV with `EVP_BytesToKey()` to be consistent API behaviors with other ciphers. The built-in AES wrap mode in OpenSSL is not supported in FIPS mode as http://openssl.6102.n7.nabble.com/AES-Key-Wrap-in-FIPS-Mode-td50238.html so its tests in FIPS mode are skipped. Fixes: https://github.com/nodejs/node/issues/15009 PR-URL: https://github.com/nodejs/node/pull/15037 Reviewed-By: Fedor Indutny Reviewed-By: Ben Noordhuis Reviewed-By: Tobias Nießen Reviewed-By: James M Snell --- src/node_crypto.cc | 10 +++++++- test/parallel/test-crypto-binary-default.js | 21 ++++++++++++++++ .../test-crypto-cipheriv-decipheriv.js | 24 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 1fa522d521..e6acb565d6 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3349,6 +3349,9 @@ void CipherBase::Init(const char* cipher_type, cipher_type); } + if (mode == EVP_CIPH_WRAP_MODE) + EVP_CIPHER_CTX_set_flags(&ctx_, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) { EVP_CIPHER_CTX_cleanup(&ctx_); return env()->ThrowError("Invalid key length"); @@ -3396,13 +3399,18 @@ void CipherBase::InitIv(const char* cipher_type, } const int expected_iv_len = EVP_CIPHER_iv_length(cipher); - const bool is_gcm_mode = (EVP_CIPH_GCM_MODE == EVP_CIPHER_mode(cipher)); + const int mode = EVP_CIPHER_mode(cipher); + const bool is_gcm_mode = (EVP_CIPH_GCM_MODE == mode); if (is_gcm_mode == false && iv_len != expected_iv_len) { return env()->ThrowError("Invalid IV length"); } EVP_CIPHER_CTX_init(&ctx_); + + if (mode == EVP_CIPH_WRAP_MODE) + EVP_CIPHER_CTX_set_flags(&ctx_, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + const bool encrypt = (kind_ == kCipher); EVP_CipherInit_ex(&ctx_, cipher, nullptr, nullptr, nullptr, encrypt); diff --git a/test/parallel/test-crypto-binary-default.js b/test/parallel/test-crypto-binary-default.js index 932db932d7..d089a01ecf 100644 --- a/test/parallel/test-crypto-binary-default.js +++ b/test/parallel/test-crypto-binary-default.js @@ -530,12 +530,33 @@ function testCipher4(key, iv) { 'encryption and decryption with key and iv'); } + +function testCipher5(key, iv) { + // Test encryption and decryption with explicit key with aes128-wrap + const plaintext = + '32|RmVZZkFUVmpRRkp0TmJaUm56ZU9qcnJkaXNNWVNpTTU*|iXmckfRWZBGWWELw' + + 'eCBsThSsfUHLeRe0KCsK8ooHgxie0zOINpXxfZi/oNG7uq9JWFVCk70gfzQH8ZUJ' + + 'jAfaFg**'; + const cipher = crypto.createCipher('id-aes128-wrap', key); + let ciph = cipher.update(plaintext, 'utf8', 'buffer'); + ciph = Buffer.concat([ciph, cipher.final('buffer')]); + + const decipher = crypto.createDecipher('id-aes128-wrap', key); + let txt = decipher.update(ciph, 'buffer', 'utf8'); + txt += decipher.final('utf8'); + + assert.strictEqual(txt, plaintext, + 'encryption and decryption with key'); +} + if (!common.hasFipsCrypto) { testCipher1('MySecretKey123'); testCipher1(Buffer.from('MySecretKey123')); testCipher2('0123456789abcdef'); testCipher2(Buffer.from('0123456789abcdef')); + + testCipher5(Buffer.from('0123456789abcd0123456789')); } testCipher3('0123456789abcd0123456789', '12345678'); diff --git a/test/parallel/test-crypto-cipheriv-decipheriv.js b/test/parallel/test-crypto-cipheriv-decipheriv.js index 1ccfe8b3b8..8a5a05b82f 100644 --- a/test/parallel/test-crypto-cipheriv-decipheriv.js +++ b/test/parallel/test-crypto-cipheriv-decipheriv.js @@ -55,12 +55,36 @@ function testCipher2(key, iv) { assert.strictEqual(txt, plaintext, 'encryption/decryption with key and iv'); } + +function testCipher3(key, iv) { + // Test encryption and decryption with explicit key and iv. + // AES Key Wrap test vector comes from RFC3394 + const plaintext = Buffer.from('00112233445566778899AABBCCDDEEFF', 'hex'); + + const cipher = crypto.createCipheriv('id-aes128-wrap', key, iv); + let ciph = cipher.update(plaintext, 'utf8', 'buffer'); + ciph = Buffer.concat([ciph, cipher.final('buffer')]); + const ciph2 = Buffer.from('1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5', + 'hex'); + assert(ciph.equals(ciph2)); + const decipher = crypto.createDecipheriv('id-aes128-wrap', key, iv); + let deciph = decipher.update(ciph, 'buffer'); + deciph = Buffer.concat([deciph, decipher.final()]); + + assert(deciph.equals(plaintext), 'encryption/decryption with key and iv'); +} + testCipher1('0123456789abcd0123456789', '12345678'); testCipher1('0123456789abcd0123456789', Buffer.from('12345678')); testCipher1(Buffer.from('0123456789abcd0123456789'), '12345678'); testCipher1(Buffer.from('0123456789abcd0123456789'), Buffer.from('12345678')); testCipher2(Buffer.from('0123456789abcd0123456789'), Buffer.from('12345678')); +if (!common.hasFipsCrypto) { + testCipher3(Buffer.from('000102030405060708090A0B0C0D0E0F', 'hex'), + Buffer.from('A6A6A6A6A6A6A6A6', 'hex')); +} + // Zero-sized IV should be accepted in ECB mode. crypto.createCipheriv('aes-128-ecb', Buffer.alloc(16), Buffer.alloc(0));