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 164const encodingError = { 165 code: 'ERR_INVALID_ARG_VALUE', 166 name: 'TypeError', 167 message: "The argument 'encoding' is invalid for data of length 1." + 168 " Received 'hex'", 169}; 170 171// Regression tests for https://github.com/nodejs/node-v0.x-archive/pull/5725: 172// hex input that's not a power of two should throw, not assert in C++ land. 173['createCipher', 'createDecipher'].forEach((funcName) => { 174 assert.throws( 175 () => crypto[funcName]('aes192', 'test').update('0', 'hex'), 176 (error) => { 177 assert.ok(!('opensslErrorStack' in error)); 178 if (common.hasFipsCrypto) { 179 return error instanceof Error && 180 error.name === 'Error' && 181 /^Error: not supported in FIPS mode$/.test(error); 182 } 183 assert.throws(() => { throw error; }, encodingError); 184 return true; 185 } 186 ); 187}); 188 189assert.throws( 190 () => crypto.createHash('sha1').update('0', 'hex'), 191 (error) => { 192 assert.ok(!('opensslErrorStack' in error)); 193 assert.throws(() => { throw error; }, encodingError); 194 return true; 195 } 196); 197 198assert.throws( 199 () => crypto.createHmac('sha256', 'a secret').update('0', 'hex'), 200 (error) => { 201 assert.ok(!('opensslErrorStack' in error)); 202 assert.throws(() => { throw error; }, encodingError); 203 return true; 204 } 205); 206 207assert.throws(() => { 208 const priv = [ 209 '-----BEGIN RSA PRIVATE KEY-----', 210 'MIGrAgEAAiEA+3z+1QNF2/unumadiwEr+C5vfhezsb3hp4jAnCNRpPcCAwEAAQIgQNriSQK4', 211 'EFwczDhMZp2dvbcz7OUUyt36z3S4usFPHSECEQD/41K7SujrstBfoCPzwC1xAhEA+5kt4BJy', 212 'eKN7LggbF3Dk5wIQN6SL+fQ5H/+7NgARsVBp0QIRANxYRukavs4QvuyNhMx+vrkCEQCbf6j/', 213 'Ig6/HueCK/0Jkmp+', 214 '-----END RSA PRIVATE KEY-----', 215 '', 216 ].join('\n'); 217 crypto.createSign('SHA256').update('test').sign(priv); 218}, (err) => { 219 assert.ok(!('opensslErrorStack' in err)); 220 assert.throws(() => { throw err; }, { 221 name: 'Error', 222 message: /routines:RSA_sign:digest too big for rsa key$/, 223 library: 'rsa routines', 224 function: 'RSA_sign', 225 reason: 'digest too big for rsa key', 226 code: 'ERR_OSSL_RSA_DIGEST_TOO_BIG_FOR_RSA_KEY' 227 }); 228 return true; 229}); 230 231assert.throws(() => { 232 // The correct header inside `rsa_private_pkcs8_bad.pem` should have been 233 // -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- 234 // instead of 235 // -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY----- 236 const sha1_privateKey = fixtures.readKey('rsa_private_pkcs8_bad.pem', 237 'ascii'); 238 // This would inject errors onto OpenSSL's error stack 239 crypto.createSign('sha1').sign(sha1_privateKey); 240}, (err) => { 241 // Do the standard checks, but then do some custom checks afterwards. 242 assert.throws(() => { throw err; }, { 243 message: 'error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag', 244 library: 'asn1 encoding routines', 245 function: 'asn1_check_tlen', 246 reason: 'wrong tag', 247 code: 'ERR_OSSL_ASN1_WRONG_TAG', 248 }); 249 // Throws crypto error, so there is an opensslErrorStack property. 250 // The openSSL stack should have content. 251 assert(Array.isArray(err.opensslErrorStack)); 252 assert(err.opensslErrorStack.length > 0); 253 return true; 254}); 255 256// Make sure memory isn't released before being returned 257console.log(crypto.randomBytes(16)); 258 259assert.throws(() => { 260 tls.createSecureContext({ crl: 'not a CRL' }); 261}, (err) => { 262 // Throws general error, so there is no opensslErrorStack property. 263 return err instanceof Error && 264 /^Error: Failed to parse CRL$/.test(err) && 265 !('opensslErrorStack' in err); 266}); 267 268/** 269 * Check if the stream function uses utf8 as a default encoding. 270 */ 271 272function testEncoding(options, assertionHash) { 273 const hash = crypto.createHash('sha256', options); 274 let hashValue = ''; 275 276 hash.on('data', (data) => { 277 hashValue += data.toString('hex'); 278 }); 279 280 hash.on('end', common.mustCall(() => { 281 assert.strictEqual(hashValue, assertionHash); 282 })); 283 284 hash.write('öäü'); 285 hash.end(); 286} 287 288// Hash of "öäü" in utf8 format 289const assertionHashUtf8 = 290 '4f53d15bee524f082380e6d7247cc541e7cb0d10c64efdcc935ceeb1e7ea345c'; 291 292// Hash of "öäü" in latin1 format 293const assertionHashLatin1 = 294 'cd37bccd5786e2e76d9b18c871e919e6eb11cc12d868f5ae41c40ccff8e44830'; 295 296testEncoding(undefined, assertionHashUtf8); 297testEncoding({}, assertionHashUtf8); 298 299testEncoding({ 300 defaultEncoding: 'utf8' 301}, assertionHashUtf8); 302 303testEncoding({ 304 defaultEncoding: 'latin1' 305}, assertionHashLatin1); 306