• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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