1'use strict'; 2const common = require('../common'); 3 4if (!common.hasCrypto) 5 common.skip('missing crypto'); 6 7if (common.hasFipsCrypto) 8 common.skip('not supported in FIPS mode'); 9 10const crypto = require('crypto'); 11const assert = require('assert'); 12 13common.expectWarning({ 14 Warning: [ 15 ['Use Cipheriv for counter mode of aes-256-gcm'], 16 ], 17 DeprecationWarning: [ 18 ['crypto.createCipher is deprecated.', 'DEP0106'], 19 ] 20}); 21 22function testCipher1(key) { 23 // Test encryption and decryption 24 const plaintext = 'Keep this a secret? No! Tell everyone about node.js!'; 25 const cipher = crypto.createCipher('aes192', key); 26 27 // Encrypt plaintext which is in utf8 format 28 // to a ciphertext which will be in hex 29 let ciph = cipher.update(plaintext, 'utf8', 'hex'); 30 // Only use binary or hex, not base64. 31 ciph += cipher.final('hex'); 32 33 const decipher = crypto.createDecipher('aes192', key); 34 let txt = decipher.update(ciph, 'hex', 'utf8'); 35 txt += decipher.final('utf8'); 36 37 assert.strictEqual(txt, plaintext); 38 39 // Streaming cipher interface 40 // NB: In real life, it's not guaranteed that you can get all of it 41 // in a single read() like this. But in this case, we know it's 42 // quite small, so there's no harm. 43 const cStream = crypto.createCipher('aes192', key); 44 cStream.end(plaintext); 45 ciph = cStream.read(); 46 47 const dStream = crypto.createDecipher('aes192', key); 48 dStream.end(ciph); 49 txt = dStream.read().toString('utf8'); 50 51 assert.strictEqual(txt, plaintext); 52} 53 54 55function testCipher2(key) { 56 // Encryption and decryption with Base64. 57 // Reported in https://github.com/joyent/node/issues/738 58 const plaintext = 59 '32|RmVZZkFUVmpRRkp0TmJaUm56ZU9qcnJkaXNNWVNpTTU*|iXmckfRWZBGWWELw' + 60 'eCBsThSsfUHLeRe0KCsK8ooHgxie0zOINpXxfZi/oNG7uq9JWFVCk70gfzQH8ZUJ' + 61 'jAfaFg**'; 62 const cipher = crypto.createCipher('aes256', key); 63 64 // Encrypt plaintext which is in utf8 format to a ciphertext which will be in 65 // Base64. 66 let ciph = cipher.update(plaintext, 'utf8', 'base64'); 67 ciph += cipher.final('base64'); 68 69 const decipher = crypto.createDecipher('aes256', key); 70 let txt = decipher.update(ciph, 'base64', 'utf8'); 71 txt += decipher.final('utf8'); 72 73 assert.strictEqual(txt, plaintext); 74} 75 76testCipher1('MySecretKey123'); 77testCipher1(Buffer.from('MySecretKey123')); 78 79testCipher2('0123456789abcdef'); 80testCipher2(Buffer.from('0123456789abcdef')); 81 82{ 83 const Cipher = crypto.Cipher; 84 const instance = crypto.Cipher('aes-256-cbc', 'secret'); 85 assert(instance instanceof Cipher, 'Cipher is expected to return a new ' + 86 'instance when called without `new`'); 87 88 assert.throws( 89 () => crypto.createCipher(null), 90 { 91 code: 'ERR_INVALID_ARG_TYPE', 92 name: 'TypeError', 93 message: 'The "cipher" argument must be of type string. ' + 94 'Received null' 95 }); 96 97 assert.throws( 98 () => crypto.createCipher('aes-256-cbc', null), 99 { 100 code: 'ERR_INVALID_ARG_TYPE', 101 name: 'TypeError', 102 message: 'The "password" argument must be of type string or an instance' + 103 ' of Buffer, TypedArray, or DataView. Received null' 104 }); 105 106 assert.throws( 107 () => crypto.createCipher('aes-256-cbc', 'secret').update(null), 108 { 109 code: 'ERR_INVALID_ARG_TYPE', 110 name: 'TypeError', 111 message: 'The "data" argument must be of type string or an instance' + 112 ' of Buffer, TypedArray, or DataView. Received null' 113 }); 114 115 assert.throws( 116 () => crypto.createCipher('aes-256-cbc', 'secret').setAAD(null), 117 { 118 code: 'ERR_INVALID_ARG_TYPE', 119 name: 'TypeError', 120 message: 'The "buffer" argument must be an instance' + 121 ' of Buffer, TypedArray, or DataView. Received null' 122 }); 123} 124 125{ 126 const Decipher = crypto.Decipher; 127 const instance = crypto.Decipher('aes-256-cbc', 'secret'); 128 assert(instance instanceof Decipher, 'Decipher is expected to return a new ' + 129 'instance when called without `new`'); 130 131 assert.throws( 132 () => crypto.createDecipher(null), 133 { 134 code: 'ERR_INVALID_ARG_TYPE', 135 name: 'TypeError', 136 message: 'The "cipher" argument must be of type string. ' + 137 'Received null' 138 }); 139 140 assert.throws( 141 () => crypto.createDecipher('aes-256-cbc', 'secret').setAuthTag(null), 142 { 143 code: 'ERR_INVALID_ARG_TYPE', 144 name: 'TypeError', 145 message: 'The "buffer" argument must be an instance of Buffer, ' + 146 'TypedArray, or DataView. Received null' 147 }); 148 149 assert.throws( 150 () => crypto.createDecipher('aes-256-cbc', null), 151 { 152 code: 'ERR_INVALID_ARG_TYPE', 153 name: 'TypeError', 154 message: 'The "password" argument must be of type string or an ' + 155 'instance of Buffer, TypedArray, or DataView. Received null' 156 }); 157} 158 159// Base64 padding regression test, see 160// https://github.com/nodejs/node-v0.x-archive/issues/4837. 161{ 162 const c = crypto.createCipher('aes-256-cbc', 'secret'); 163 const s = c.update('test', 'utf8', 'base64') + c.final('base64'); 164 assert.strictEqual(s, '375oxUQCIocvxmC5At+rvA=='); 165} 166 167// Calling Cipher.final() or Decipher.final() twice should error but 168// not assert. See https://github.com/nodejs/node-v0.x-archive/issues/4886. 169{ 170 const c = crypto.createCipher('aes-256-cbc', 'secret'); 171 try { c.final('xxx'); } catch { /* Ignore. */ } 172 try { c.final('xxx'); } catch { /* Ignore. */ } 173 try { c.final('xxx'); } catch { /* Ignore. */ } 174 const d = crypto.createDecipher('aes-256-cbc', 'secret'); 175 try { d.final('xxx'); } catch { /* Ignore. */ } 176 try { d.final('xxx'); } catch { /* Ignore. */ } 177 try { d.final('xxx'); } catch { /* Ignore. */ } 178} 179 180// Regression test for https://github.com/nodejs/node-v0.x-archive/issues/5482: 181// string to Cipher#update() should not assert. 182{ 183 const c = crypto.createCipher('aes192', '0123456789abcdef'); 184 c.update('update'); 185 c.final(); 186} 187 188// https://github.com/nodejs/node-v0.x-archive/issues/5655 regression tests, 189// 'utf-8' and 'utf8' are identical. 190{ 191 let c = crypto.createCipher('aes192', '0123456789abcdef'); 192 c.update('update', ''); // Defaults to "utf8". 193 c.final('utf-8'); // Should not throw. 194 195 c = crypto.createCipher('aes192', '0123456789abcdef'); 196 c.update('update', 'utf8'); 197 c.final('utf-8'); // Should not throw. 198 199 c = crypto.createCipher('aes192', '0123456789abcdef'); 200 c.update('update', 'utf-8'); 201 c.final('utf8'); // Should not throw. 202} 203 204// Regression tests for https://github.com/nodejs/node/issues/8236. 205{ 206 const key = '0123456789abcdef'; 207 const plaintext = 'Top secret!!!'; 208 const c = crypto.createCipher('aes192', key); 209 let ciph = c.update(plaintext, 'utf16le', 'base64'); 210 ciph += c.final('base64'); 211 212 let decipher = crypto.createDecipher('aes192', key); 213 214 let txt; 215 txt = decipher.update(ciph, 'base64', 'ucs2'); 216 txt += decipher.final('ucs2'); 217 assert.strictEqual(txt, plaintext); 218 219 decipher = crypto.createDecipher('aes192', key); 220 txt = decipher.update(ciph, 'base64', 'ucs-2'); 221 txt += decipher.final('ucs-2'); 222 assert.strictEqual(txt, plaintext); 223 224 decipher = crypto.createDecipher('aes192', key); 225 txt = decipher.update(ciph, 'base64', 'utf-16le'); 226 txt += decipher.final('utf-16le'); 227 assert.strictEqual(txt, plaintext); 228} 229 230// setAutoPadding/setAuthTag/setAAD should return `this` 231{ 232 const key = '0123456789'; 233 const tagbuf = Buffer.from('auth_tag'); 234 const aadbuf = Buffer.from('aadbuf'); 235 const decipher = crypto.createDecipher('aes-256-gcm', key); 236 assert.strictEqual(decipher.setAutoPadding(), decipher); 237 assert.strictEqual(decipher.setAuthTag(tagbuf), decipher); 238 assert.strictEqual(decipher.setAAD(aadbuf), decipher); 239} 240 241// Error throwing in setAAD/setAuthTag/getAuthTag/setAutoPadding 242{ 243 const key = '0123456789'; 244 const aadbuf = Buffer.from('aadbuf'); 245 const data = Buffer.from('test-crypto-cipher-decipher'); 246 247 const cipher = crypto.createCipher('aes-256-gcm', key); 248 cipher.setAAD(aadbuf); 249 cipher.setAutoPadding(); 250 251 assert.throws( 252 () => cipher.getAuthTag(), 253 { 254 code: 'ERR_CRYPTO_INVALID_STATE', 255 name: 'Error', 256 message: 'Invalid state for operation getAuthTag' 257 } 258 ); 259 260 const encrypted = Buffer.concat([cipher.update(data), cipher.final()]); 261 262 const decipher = crypto.createDecipher('aes-256-gcm', key); 263 decipher.setAAD(aadbuf); 264 decipher.setAuthTag(cipher.getAuthTag()); 265 decipher.setAutoPadding(); 266 decipher.update(encrypted); 267 decipher.final(); 268 269 assert.throws( 270 () => decipher.setAAD(aadbuf), 271 { 272 code: 'ERR_CRYPTO_INVALID_STATE', 273 name: 'Error', 274 message: 'Invalid state for operation setAAD' 275 }); 276 277 assert.throws( 278 () => decipher.setAuthTag(cipher.getAuthTag()), 279 { 280 code: 'ERR_CRYPTO_INVALID_STATE', 281 name: 'Error', 282 message: 'Invalid state for operation setAuthTag' 283 }); 284 285 assert.throws( 286 () => decipher.setAutoPadding(), 287 { 288 code: 'ERR_CRYPTO_INVALID_STATE', 289 name: 'Error', 290 message: 'Invalid state for operation setAutoPadding' 291 } 292 ); 293} 294