From def681a07ee78fda84056e403e20aef21b5d0161 Mon Sep 17 00:00:00 2001 From: Stefan Budeanu Date: Mon, 9 Nov 2015 00:00:56 -0500 Subject: [PATCH] crypto: disable crypto.createCipher in FIPS mode FIPS 140-2 disallows use of MD5, which is used to derive the initialization vector and key for createCipher(). Modify all tests to expect exceptions in FIPS mode when disallowed API is used, or to avoid testing such API in FIPS Mode. PR-URL: https://github.com/nodejs/node/pull/3754 Reviewed-By: Michael Dawson Reviewed-By: Shigeki Ohtsu Reviewed-By: James M Snell --- src/node_crypto.cc | 5 ++ test/parallel/test-crypto-authenticated.js | 52 +++++++++------ test/parallel/test-crypto-binary-default.js | 11 ++-- test/parallel/test-crypto-cipher-decipher.js | 63 ++---------------- .../test-crypto-cipheriv-decipheriv.js | 65 +++++++++++++++++++ test/parallel/test-crypto-dh.js | 4 +- test/parallel/test-crypto.js | 4 +- 7 files changed, 116 insertions(+), 88 deletions(-) create mode 100644 test/parallel/test-crypto-cipheriv-decipheriv.js diff --git a/src/node_crypto.cc b/src/node_crypto.cc index f699ef8222ec8d..bd7314c9db902c 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3054,6 +3054,11 @@ void CipherBase::Init(const char* cipher_type, int key_buf_len) { HandleScope scope(env()->isolate()); +#ifdef NODE_FIPS_MODE + return env()->ThrowError( + "crypto.createCipher() is not supported in FIPS mode."); +#endif // NODE_FIPS_MODE + CHECK_EQ(cipher_, nullptr); cipher_ = EVP_get_cipherbyname(cipher_type); if (cipher_ == nullptr) { diff --git a/test/parallel/test-crypto-authenticated.js b/test/parallel/test-crypto-authenticated.js index 2cc36278332c9b..fa9a78c26e5b59 100644 --- a/test/parallel/test-crypto-authenticated.js +++ b/test/parallel/test-crypto-authenticated.js @@ -93,32 +93,44 @@ for (var i in TEST_CASES) { (function() { if (!test.password) return; - var encrypt = crypto.createCipher(test.algo, test.password); - if (test.aad) - encrypt.setAAD(new Buffer(test.aad, 'hex')); - var hex = encrypt.update(test.plain, 'ascii', 'hex'); - hex += encrypt.final('hex'); - var auth_tag = encrypt.getAuthTag(); - // only test basic encryption run if output is marked as tampered. - if (!test.tampered) { - assert.equal(hex.toUpperCase(), test.ct); - assert.equal(auth_tag.toString('hex').toUpperCase(), test.tag); + if (common.hasFipsCrypto) { + assert.throws(function() + { crypto.createCipher(test.algo, test.password); }, + /not supported in FIPS mode/); + } else { + var encrypt = crypto.createCipher(test.algo, test.password); + if (test.aad) + encrypt.setAAD(new Buffer(test.aad, 'hex')); + var hex = encrypt.update(test.plain, 'ascii', 'hex'); + hex += encrypt.final('hex'); + var auth_tag = encrypt.getAuthTag(); + // only test basic encryption run if output is marked as tampered. + if (!test.tampered) { + assert.equal(hex.toUpperCase(), test.ct); + assert.equal(auth_tag.toString('hex').toUpperCase(), test.tag); + } } })(); (function() { if (!test.password) return; - var decrypt = crypto.createDecipher(test.algo, test.password); - decrypt.setAuthTag(new Buffer(test.tag, 'hex')); - if (test.aad) - decrypt.setAAD(new Buffer(test.aad, 'hex')); - var msg = decrypt.update(test.ct, 'hex', 'ascii'); - if (!test.tampered) { - msg += decrypt.final('ascii'); - assert.equal(msg, test.plain); + if (common.hasFipsCrypto) { + assert.throws(function() + { crypto.createDecipher(test.algo, test.password); }, + /not supported in FIPS mode/); } else { - // assert that final throws if input data could not be verified! - assert.throws(function() { decrypt.final('ascii'); }, / auth/); + var decrypt = crypto.createDecipher(test.algo, test.password); + decrypt.setAuthTag(new Buffer(test.tag, 'hex')); + if (test.aad) + decrypt.setAAD(new Buffer(test.aad, 'hex')); + var msg = decrypt.update(test.ct, 'hex', 'ascii'); + if (!test.tampered) { + msg += decrypt.final('ascii'); + assert.equal(msg, test.plain); + } else { + // assert that final throws if input data could not be verified! + assert.throws(function() { decrypt.final('ascii'); }, / auth/); + } } })(); diff --git a/test/parallel/test-crypto-binary-default.js b/test/parallel/test-crypto-binary-default.js index 6299c869087a6e..806f5de18bbb1f 100644 --- a/test/parallel/test-crypto-binary-default.js +++ b/test/parallel/test-crypto-binary-default.js @@ -496,12 +496,13 @@ function testCipher4(key, iv) { assert.equal(txt, plaintext, 'encryption and decryption with key and iv'); } +if (!common.hasFipsCrypto) { + testCipher1('MySecretKey123'); + testCipher1(new Buffer('MySecretKey123')); -testCipher1('MySecretKey123'); -testCipher1(new Buffer('MySecretKey123')); - -testCipher2('0123456789abcdef'); -testCipher2(new Buffer('0123456789abcdef')); + testCipher2('0123456789abcdef'); + testCipher2(new Buffer('0123456789abcdef')); +} testCipher3('0123456789abcd0123456789', '12345678'); testCipher3('0123456789abcd0123456789', new Buffer('12345678')); diff --git a/test/parallel/test-crypto-cipher-decipher.js b/test/parallel/test-crypto-cipher-decipher.js index f33ea5482c8c3a..5f867739abe7ed 100644 --- a/test/parallel/test-crypto-cipher-decipher.js +++ b/test/parallel/test-crypto-cipher-decipher.js @@ -6,6 +6,10 @@ if (!common.hasCrypto) { console.log('1..0 # Skipped: missing crypto'); return; } +if (common.hasFipsCrypto) { + console.log('1..0 # Skipped: not supported in FIPS mode'); + return; +} var crypto = require('crypto'); function testCipher1(key) { @@ -62,71 +66,12 @@ function testCipher2(key) { assert.equal(txt, plaintext, 'encryption and decryption with Base64'); } - -function testCipher3(key, iv) { - // Test encyrption and decryption with explicit key and iv - var plaintext = - '32|RmVZZkFUVmpRRkp0TmJaUm56ZU9qcnJkaXNNWVNpTTU*|iXmckfRWZBGWWELw' + - 'eCBsThSsfUHLeRe0KCsK8ooHgxie0zOINpXxfZi/oNG7uq9JWFVCk70gfzQH8ZUJ' + - 'jAfaFg**'; - var cipher = crypto.createCipheriv('des-ede3-cbc', key, iv); - var ciph = cipher.update(plaintext, 'utf8', 'hex'); - ciph += cipher.final('hex'); - - var decipher = crypto.createDecipheriv('des-ede3-cbc', key, iv); - var txt = decipher.update(ciph, 'hex', 'utf8'); - txt += decipher.final('utf8'); - - assert.equal(txt, plaintext, 'encryption and decryption with key and iv'); - - // streaming cipher interface - // NB: In real life, it's not guaranteed that you can get all of it - // in a single read() like this. But in this case, we know it's - // quite small, so there's no harm. - var cStream = crypto.createCipheriv('des-ede3-cbc', key, iv); - cStream.end(plaintext); - ciph = cStream.read(); - - var dStream = crypto.createDecipheriv('des-ede3-cbc', key, iv); - dStream.end(ciph); - txt = dStream.read().toString('utf8'); - - assert.equal(txt, plaintext, 'streaming cipher iv'); -} - - -function testCipher4(key, iv) { - // Test encyrption and decryption with explicit key and iv - var plaintext = - '32|RmVZZkFUVmpRRkp0TmJaUm56ZU9qcnJkaXNNWVNpTTU*|iXmckfRWZBGWWELw' + - 'eCBsThSsfUHLeRe0KCsK8ooHgxie0zOINpXxfZi/oNG7uq9JWFVCk70gfzQH8ZUJ' + - 'jAfaFg**'; - var cipher = crypto.createCipheriv('des-ede3-cbc', key, iv); - var ciph = cipher.update(plaintext, 'utf8', 'buffer'); - ciph = Buffer.concat([ciph, cipher.final('buffer')]); - - var decipher = crypto.createDecipheriv('des-ede3-cbc', key, iv); - var txt = decipher.update(ciph, 'buffer', 'utf8'); - txt += decipher.final('utf8'); - - assert.equal(txt, plaintext, 'encryption and decryption with key and iv'); -} - - testCipher1('MySecretKey123'); testCipher1(new Buffer('MySecretKey123')); testCipher2('0123456789abcdef'); testCipher2(new Buffer('0123456789abcdef')); -testCipher3('0123456789abcd0123456789', '12345678'); -testCipher3('0123456789abcd0123456789', new Buffer('12345678')); -testCipher3(new Buffer('0123456789abcd0123456789'), '12345678'); -testCipher3(new Buffer('0123456789abcd0123456789'), new Buffer('12345678')); - -testCipher4(new Buffer('0123456789abcd0123456789'), new Buffer('12345678')); - - // Base64 padding regression test, see #4837. (function() { var c = crypto.createCipher('aes-256-cbc', 'secret'); diff --git a/test/parallel/test-crypto-cipheriv-decipheriv.js b/test/parallel/test-crypto-cipheriv-decipheriv.js new file mode 100644 index 00000000000000..d325c0facf7e54 --- /dev/null +++ b/test/parallel/test-crypto-cipheriv-decipheriv.js @@ -0,0 +1,65 @@ +'use strict'; +var common = require('../common'); +var assert = require('assert'); + +if (!common.hasCrypto) { + console.log('1..0 # Skipped: missing crypto'); + return; +} +var crypto = require('crypto'); + +function testCipher1(key, iv) { + // Test encyrption and decryption with explicit key and iv + var plaintext = + '32|RmVZZkFUVmpRRkp0TmJaUm56ZU9qcnJkaXNNWVNpTTU*|iXmckfRWZBGWWELw' + + 'eCBsThSsfUHLeRe0KCsK8ooHgxie0zOINpXxfZi/oNG7uq9JWFVCk70gfzQH8ZUJ' + + 'jAfaFg**'; + var cipher = crypto.createCipheriv('des-ede3-cbc', key, iv); + var ciph = cipher.update(plaintext, 'utf8', 'hex'); + ciph += cipher.final('hex'); + + var decipher = crypto.createDecipheriv('des-ede3-cbc', key, iv); + var txt = decipher.update(ciph, 'hex', 'utf8'); + txt += decipher.final('utf8'); + + assert.equal(txt, plaintext, 'encryption and decryption with key and iv'); + + // streaming cipher interface + // NB: In real life, it's not guaranteed that you can get all of it + // in a single read() like this. But in this case, we know it's + // quite small, so there's no harm. + var cStream = crypto.createCipheriv('des-ede3-cbc', key, iv); + cStream.end(plaintext); + ciph = cStream.read(); + + var dStream = crypto.createDecipheriv('des-ede3-cbc', key, iv); + dStream.end(ciph); + txt = dStream.read().toString('utf8'); + + assert.equal(txt, plaintext, 'streaming cipher iv'); +} + + +function testCipher2(key, iv) { + // Test encyrption and decryption with explicit key and iv + var plaintext = + '32|RmVZZkFUVmpRRkp0TmJaUm56ZU9qcnJkaXNNWVNpTTU*|iXmckfRWZBGWWELw' + + 'eCBsThSsfUHLeRe0KCsK8ooHgxie0zOINpXxfZi/oNG7uq9JWFVCk70gfzQH8ZUJ' + + 'jAfaFg**'; + var cipher = crypto.createCipheriv('des-ede3-cbc', key, iv); + var ciph = cipher.update(plaintext, 'utf8', 'buffer'); + ciph = Buffer.concat([ciph, cipher.final('buffer')]); + + var decipher = crypto.createDecipheriv('des-ede3-cbc', key, iv); + var txt = decipher.update(ciph, 'buffer', 'utf8'); + txt += decipher.final('utf8'); + + assert.equal(txt, plaintext, 'encryption and decryption with key and iv'); +} + +testCipher1('0123456789abcd0123456789', '12345678'); +testCipher1('0123456789abcd0123456789', new Buffer('12345678')); +testCipher1(new Buffer('0123456789abcd0123456789'), '12345678'); +testCipher1(new Buffer('0123456789abcd0123456789'), new Buffer('12345678')); + +testCipher2(new Buffer('0123456789abcd0123456789'), new Buffer('12345678')); diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index 322b5d1c70a073..f3e54c46a8e16b 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -58,7 +58,7 @@ assert.equal(secret1, secret3); // Run this one twice to make sure that the dh3 clears its error properly (function() { - var c = crypto.createDecipher('aes-128-ecb', ''); + var c = crypto.createDecipheriv('aes-128-ecb', crypto.randomBytes(16), ''); assert.throws(function() { c.final('utf8'); }, /wrong final block length/); })(); @@ -67,7 +67,7 @@ assert.throws(function() { }, /key is too small/i); (function() { - var c = crypto.createDecipher('aes-128-ecb', ''); + var c = crypto.createDecipheriv('aes-128-ecb', crypto.randomBytes(16), ''); assert.throws(function() { c.final('utf8'); }, /wrong final block length/); })(); diff --git a/test/parallel/test-crypto.js b/test/parallel/test-crypto.js index e5fe8db58c132e..df59fd9bded726 100644 --- a/test/parallel/test-crypto.js +++ b/test/parallel/test-crypto.js @@ -93,11 +93,11 @@ assertSorted(crypto.getCurves()); // throw, not assert in C++ land. assert.throws(function() { crypto.createCipher('aes192', 'test').update('0', 'hex'); -}, /Bad input string/); +}, common.hasFipsCrypto ? /not supported in FIPS mode/ : /Bad input string/); assert.throws(function() { crypto.createDecipher('aes192', 'test').update('0', 'hex'); -}, /Bad input string/); +}, common.hasFipsCrypto ? /not supported in FIPS mode/ : /Bad input string/); assert.throws(function() { crypto.createHash('sha1').update('0', 'hex');