1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23const common = require('../common'); 24 25if (!common.hasCrypto) 26 common.skip('missing crypto'); 27 28common.expectWarning({ 29 DeprecationWarning: [ 30 ['crypto.createCipher is deprecated.', 'DEP0106'] 31 ] 32}); 33 34const assert = require('assert'); 35const crypto = require('crypto'); 36const tls = require('tls'); 37const fixtures = require('../common/fixtures'); 38 39// Test Certificates 40const certPfx = fixtures.readKey('rsa_cert.pfx'); 41 42// 'this' safety 43// https://github.com/joyent/node/issues/6690 44assert.throws(() => { 45 const credentials = tls.createSecureContext(); 46 const context = credentials.context; 47 const notcontext = { setOptions: context.setOptions }; 48 49 // Methods of native objects should not segfault when reassigned to a new 50 // object and called illegally. This core dumped in 0.10 and was fixed in 51 // 0.11. 52 notcontext.setOptions(); 53}, (err) => { 54 // Throws TypeError, so there is no opensslErrorStack property. 55 return err instanceof TypeError && 56 err.name === 'TypeError' && 57 /^TypeError: Illegal invocation$/.test(err) && 58 !('opensslErrorStack' in err); 59}); 60 61// PFX tests 62tls.createSecureContext({ pfx: certPfx, passphrase: 'sample' }); 63 64assert.throws(() => { 65 tls.createSecureContext({ pfx: certPfx }); 66}, (err) => { 67 // Throws general Error, so there is no opensslErrorStack property. 68 return err instanceof Error && 69 err.name === 'Error' && 70 /^Error: mac verify failure$/.test(err) && 71 !('opensslErrorStack' in err); 72}); 73 74assert.throws(() => { 75 tls.createSecureContext({ pfx: certPfx, passphrase: 'test' }); 76}, (err) => { 77 // Throws general Error, so there is no opensslErrorStack property. 78 return err instanceof Error && 79 err.name === 'Error' && 80 /^Error: mac verify failure$/.test(err) && 81 !('opensslErrorStack' in err); 82}); 83 84assert.throws(() => { 85 tls.createSecureContext({ pfx: 'sample', passphrase: 'test' }); 86}, (err) => { 87 // Throws general Error, so there is no opensslErrorStack property. 88 return err instanceof Error && 89 err.name === 'Error' && 90 /^Error: not enough data$/.test(err) && 91 !('opensslErrorStack' in err); 92}); 93 94 95// update() should only take buffers / strings 96assert.throws( 97 () => crypto.createHash('sha1').update({ foo: 'bar' }), 98 { 99 code: 'ERR_INVALID_ARG_TYPE', 100 name: 'TypeError' 101 }); 102 103 104function validateList(list) { 105 // The list must not be empty 106 assert(list.length > 0); 107 108 // The list should be sorted. 109 // Array#sort() modifies the list in place so make a copy. 110 const sorted = [...list].sort(); 111 assert.deepStrictEqual(list, sorted); 112 113 // Each element should be unique. 114 assert.strictEqual([...new Set(list)].length, list.length); 115 116 // Each element should be a string. 117 assert(list.every((value) => typeof value === 'string')); 118} 119 120// Assume that we have at least AES-128-CBC. 121const cryptoCiphers = crypto.getCiphers(); 122assert(crypto.getCiphers().includes('aes-128-cbc')); 123validateList(cryptoCiphers); 124 125// Assume that we have at least AES256-SHA. 126const tlsCiphers = tls.getCiphers(); 127assert(tls.getCiphers().includes('aes256-sha')); 128assert(tls.getCiphers().includes('tls_aes_128_ccm_8_sha256')); 129// There should be no capital letters in any element. 130const noCapitals = /^[^A-Z]+$/; 131assert(tlsCiphers.every((value) => noCapitals.test(value))); 132validateList(tlsCiphers); 133 134// Assert that we have sha1 and sha256 but not SHA1 and SHA256. 135assert.notStrictEqual(crypto.getHashes().length, 0); 136assert(crypto.getHashes().includes('sha1')); 137assert(crypto.getHashes().includes('sha256')); 138assert(!crypto.getHashes().includes('SHA1')); 139assert(!crypto.getHashes().includes('SHA256')); 140assert(crypto.getHashes().includes('RSA-SHA1')); 141assert(!crypto.getHashes().includes('rsa-sha1')); 142validateList(crypto.getHashes()); 143 144// Assume that we have at least secp384r1. 145assert.notStrictEqual(crypto.getCurves().length, 0); 146assert(crypto.getCurves().includes('secp384r1')); 147assert(!crypto.getCurves().includes('SECP384R1')); 148validateList(crypto.getCurves()); 149 150// Modifying return value from get* functions should not mutate subsequent 151// return values. 152function testImmutability(fn) { 153 const list = fn(); 154 const copy = [...list]; 155 list.push('some-arbitrary-value'); 156 assert.deepStrictEqual(fn(), copy); 157} 158 159testImmutability(crypto.getCiphers); 160testImmutability(tls.getCiphers); 161testImmutability(crypto.getHashes); 162testImmutability(crypto.getCurves); 163 164// Regression tests for https://github.com/nodejs/node-v0.x-archive/pull/5725: 165// hex input that's not a power of two should throw, not assert in C++ land. 166['createCipher', 'createDecipher'].forEach((funcName) => { 167 assert.throws( 168 () => crypto[funcName]('aes192', 'test').update('0', 'hex'), 169 (error) => { 170 assert.ok(!('opensslErrorStack' in error)); 171 if (common.hasFipsCrypto) { 172 return error instanceof Error && 173 error.name === 'Error' && 174 /^Error: not supported in FIPS mode$/.test(error); 175 } 176 assert.throws(() => { throw error; }, /^TypeError: Bad input string$/); 177 return true; 178 } 179 ); 180}); 181 182assert.throws( 183 () => crypto.createHmac('sha256', 'a secret').update('0', 'hex'), 184 (error) => { 185 assert.ok(!('opensslErrorStack' in error)); 186 assert.throws(() => { throw error; }, /^TypeError: Bad input string$/); 187 return true; 188 } 189); 190 191assert.throws(() => { 192 const priv = [ 193 '-----BEGIN RSA PRIVATE KEY-----', 194 'MIGrAgEAAiEA+3z+1QNF2/unumadiwEr+C5vfhezsb3hp4jAnCNRpPcCAwEAAQIgQNriSQK4', 195 'EFwczDhMZp2dvbcz7OUUyt36z3S4usFPHSECEQD/41K7SujrstBfoCPzwC1xAhEA+5kt4BJy', 196 'eKN7LggbF3Dk5wIQN6SL+fQ5H/+7NgARsVBp0QIRANxYRukavs4QvuyNhMx+vrkCEQCbf6j/', 197 'Ig6/HueCK/0Jkmp+', 198 '-----END RSA PRIVATE KEY-----', 199 '' 200 ].join('\n'); 201 crypto.createSign('SHA256').update('test').sign(priv); 202}, (err) => { 203 assert.ok(!('opensslErrorStack' in err)); 204 assert.throws(() => { throw err; }, { 205 name: 'Error', 206 message: /routines:RSA_sign:digest too big for rsa key$/, 207 library: 'rsa routines', 208 function: 'RSA_sign', 209 reason: 'digest too big for rsa key', 210 code: 'ERR_OSSL_RSA_DIGEST_TOO_BIG_FOR_RSA_KEY' 211 }); 212 return true; 213}); 214 215assert.throws(() => { 216 // The correct header inside `rsa_private_pkcs8_bad.pem` should have been 217 // -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- 218 // instead of 219 // -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY----- 220 const sha1_privateKey = fixtures.readKey('rsa_private_pkcs8_bad.pem', 221 'ascii'); 222 // This would inject errors onto OpenSSL's error stack 223 crypto.createSign('sha1').sign(sha1_privateKey); 224}, (err) => { 225 // Do the standard checks, but then do some custom checks afterwards. 226 assert.throws(() => { throw err; }, { 227 message: 'error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag', 228 library: 'asn1 encoding routines', 229 function: 'asn1_check_tlen', 230 reason: 'wrong tag', 231 code: 'ERR_OSSL_ASN1_WRONG_TAG', 232 }); 233 // Throws crypto error, so there is an opensslErrorStack property. 234 // The openSSL stack should have content. 235 assert(Array.isArray(err.opensslErrorStack)); 236 assert(err.opensslErrorStack.length > 0); 237 return true; 238}); 239 240// Make sure memory isn't released before being returned 241console.log(crypto.randomBytes(16)); 242 243assert.throws(() => { 244 tls.createSecureContext({ crl: 'not a CRL' }); 245}, (err) => { 246 // Throws general error, so there is no opensslErrorStack property. 247 return err instanceof Error && 248 /^Error: Failed to parse CRL$/.test(err) && 249 !('opensslErrorStack' in err); 250}); 251 252/** 253 * Check if the stream function uses utf8 as a default encoding. 254 */ 255 256function testEncoding(options, assertionHash) { 257 const hash = crypto.createHash('sha256', options); 258 let hashValue = ''; 259 260 hash.on('data', (data) => { 261 hashValue += data.toString('hex'); 262 }); 263 264 hash.on('end', common.mustCall(() => { 265 assert.strictEqual(hashValue, assertionHash); 266 })); 267 268 hash.write('öäü'); 269 hash.end(); 270} 271 272// Hash of "öäü" in utf8 format 273const assertionHashUtf8 = 274 '4f53d15bee524f082380e6d7247cc541e7cb0d10c64efdcc935ceeb1e7ea345c'; 275 276// Hash of "öäü" in latin1 format 277const assertionHashLatin1 = 278 'cd37bccd5786e2e76d9b18c871e919e6eb11cc12d868f5ae41c40ccff8e44830'; 279 280testEncoding(undefined, assertionHashUtf8); 281testEncoding({}, assertionHashUtf8); 282 283testEncoding({ 284 defaultEncoding: 'utf8' 285}, assertionHashUtf8); 286 287testEncoding({ 288 defaultEncoding: 'latin1' 289}, assertionHashLatin1); 290