From 260383215bef153d59826a0272f314c6b097d118 Mon Sep 17 00:00:00 2001 From: Ingmar Runge Date: Thu, 3 Nov 2011 05:15:09 +0100 Subject: [PATCH] node_crypto: use EVP_Cipher*_ex methods, not 'obsolete' versions This also fixes an issue that made blowfish's ECB mode unusable. --- src/node_crypto.cc | 53 ++++++++++++++++++++-------------- test/simple/test-crypto-ecb.js | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 test/simple/test-crypto-ecb.js diff --git a/src/node_crypto.cc b/src/node_crypto.cc index d0d30964d1..d1f3513398 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -1900,15 +1900,19 @@ class Cipher : public ObjectWrap { } unsigned char key[EVP_MAX_KEY_LENGTH],iv[EVP_MAX_IV_LENGTH]; - int key_len = EVP_BytesToKey(cipher, EVP_md5(), NULL, (unsigned char*) key_buf, key_buf_len, 1, key, iv); + int key_len = EVP_BytesToKey(cipher, EVP_md5(), NULL, + (unsigned char*) key_buf, key_buf_len, 1, key, iv); EVP_CIPHER_CTX_init(&ctx); - EVP_CipherInit(&ctx,cipher,(unsigned char *)key,(unsigned char *)iv, true); - if (!EVP_CIPHER_CTX_set_key_length(&ctx,key_len)) { + EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, true); + if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) { fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len); EVP_CIPHER_CTX_cleanup(&ctx); return false; } + EVP_CipherInit_ex(&ctx, NULL, NULL, + (unsigned char *)key, + (unsigned char *)iv, true); initialised_ = true; return true; } @@ -1924,17 +1928,23 @@ class Cipher : public ObjectWrap { fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType); return false; } - if (EVP_CIPHER_iv_length(cipher)!=iv_len) { + /* OpenSSL versions up to 0.9.8l failed to return the correct + iv_length (0) for ECB ciphers */ + if (EVP_CIPHER_iv_length(cipher) != iv_len && + !(EVP_CIPHER_mode(cipher) == EVP_CIPH_ECB_MODE && iv_len == 0)) { fprintf(stderr, "node-crypto : Invalid IV length %d\n", iv_len); return false; } EVP_CIPHER_CTX_init(&ctx); - EVP_CipherInit(&ctx,cipher,(unsigned char *)key,(unsigned char *)iv, true); - if (!EVP_CIPHER_CTX_set_key_length(&ctx,key_len)) { + EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, true); + if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) { fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len); EVP_CIPHER_CTX_cleanup(&ctx); return false; } + EVP_CipherInit_ex(&ctx, NULL, NULL, + (unsigned char *)key, + (unsigned char *)iv, true); initialised_ = true; return true; } @@ -1952,7 +1962,7 @@ class Cipher : public ObjectWrap { int CipherFinal(unsigned char** out, int *out_len) { if (!initialised_) return 0; *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)]; - EVP_CipherFinal(&ctx,*out,out_len); + EVP_CipherFinal_ex(&ctx,*out,out_len); EVP_CIPHER_CTX_cleanup(&ctx); initialised_ = false; return 1; @@ -2271,16 +2281,15 @@ class Decipher : public ObjectWrap { iv); EVP_CIPHER_CTX_init(&ctx); - EVP_CipherInit(&ctx, - cipher_, - (unsigned char*)(key), - (unsigned char *)(iv), - false); - if (!EVP_CIPHER_CTX_set_key_length(&ctx,key_len)) { + EVP_CipherInit_ex(&ctx, cipher_, NULL, NULL, NULL, false); + if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) { fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len); EVP_CIPHER_CTX_cleanup(&ctx); return false; } + EVP_CipherInit_ex(&ctx, NULL, NULL, + (unsigned char *)key, + (unsigned char *)iv, false); initialised_ = true; return true; } @@ -2296,21 +2305,23 @@ class Decipher : public ObjectWrap { fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType); return false; } - if (EVP_CIPHER_iv_length(cipher_) != iv_len) { + /* OpenSSL versions up to 0.9.8l failed to return the correct + iv_length (0) for ECB ciphers */ + if (EVP_CIPHER_iv_length(cipher_) != iv_len && + !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_ECB_MODE && iv_len == 0)) { fprintf(stderr, "node-crypto : Invalid IV length %d\n", iv_len); return false; } EVP_CIPHER_CTX_init(&ctx); - EVP_CipherInit(&ctx, - cipher_, - (unsigned char*)(key), - (unsigned char *)(iv), - false); - if (!EVP_CIPHER_CTX_set_key_length(&ctx,key_len)) { + EVP_CipherInit_ex(&ctx, cipher_, NULL, NULL, NULL, false); + if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) { fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len); EVP_CIPHER_CTX_cleanup(&ctx); return false; } + EVP_CipherInit_ex(&ctx, NULL, NULL, + (unsigned char *)key, + (unsigned char *)iv, false); initialised_ = true; return true; } @@ -2331,7 +2342,7 @@ class Decipher : public ObjectWrap { if (tolerate_padding) { local_EVP_DecryptFinal_ex(&ctx,*out,out_len); } else { - EVP_CipherFinal(&ctx,*out,out_len); + EVP_CipherFinal_ex(&ctx,*out,out_len); } EVP_CIPHER_CTX_cleanup(&ctx); initialised_ = false; diff --git a/test/simple/test-crypto-ecb.js b/test/simple/test-crypto-ecb.js new file mode 100644 index 0000000000..23a5e682f1 --- /dev/null +++ b/test/simple/test-crypto-ecb.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + +var common = require('../common'); +var assert = require('assert'); + +try { + var crypto = require('crypto'); +} catch (e) { + console.log('Not compiled with OPENSSL support.'); + process.exit(); +} + +// Testing whether EVP_CipherInit_ex is functioning correctly. +// Reference: bug#1997 + +(function() +{ + var encrypt = crypto.createCipheriv('BF-ECB', 'SomeRandomBlahz0c5GZVnR', ''); + var hex = encrypt.update('Hello World!', 'ascii', 'hex'); + hex += encrypt.final('hex'); + assert.equal(hex.toUpperCase(), '6D385F424AAB0CFBF0BB86E07FFB7D71'); +}()); + +(function() +{ + var decrypt = crypto.createDecipheriv('BF-ECB', 'SomeRandomBlahz0c5GZVnR', ''); + var msg = decrypt.update('6D385F424AAB0CFBF0BB86E07FFB7D71', 'hex', 'ascii'); + msg += decrypt.final('ascii'); + assert.equal(msg, 'Hello World!'); +}());