From dfb051112ae8ee5f05ae745a69da1708b0b9e56e 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. --- 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 | 66 +++++++++++++++++++ test/parallel/test-crypto-dh.js | 4 +- test/parallel/test-crypto.js | 4 +- 7 files changed, 117 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 2bbfa0552b2f3f..2dd2684a211269 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3027,6 +3027,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 bebc91dc8ed0b1..d93aa24c28d917 100644 --- a/test/parallel/test-crypto-binary-default.js +++ b/test/parallel/test-crypto-binary-default.js @@ -490,12 +490,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..d0e36098894dbf --- /dev/null +++ b/test/parallel/test-crypto-cipheriv-decipheriv.js @@ -0,0 +1,66 @@ +'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 dfb912c0a4c9f1..551ee7e721f091 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 57191b24ae351a..329538795507d2 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');