1/* eslint-disable node-core/crypto-check */ 2'use strict'; 3// Refs: https://github.com/nodejs/node/issues/31733 4const common = require('../common'); 5const assert = require('assert'); 6const crypto = require('crypto'); 7const fs = require('fs'); 8const path = require('path'); 9const stream = require('stream'); 10const tmpdir = require('../common/tmpdir'); 11 12class Sink extends stream.Writable { 13 constructor() { 14 super(); 15 this.chunks = []; 16 } 17 18 _write(chunk, encoding, cb) { 19 this.chunks.push(chunk); 20 cb(); 21 } 22} 23 24function direct(config) { 25 const { cipher, key, iv, aad, authTagLength, plaintextLength } = config; 26 const expected = Buffer.alloc(plaintextLength); 27 28 const c = crypto.createCipheriv(cipher, key, iv, { authTagLength }); 29 c.setAAD(aad, { plaintextLength }); 30 const ciphertext = Buffer.concat([c.update(expected), c.final()]); 31 32 const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength }); 33 d.setAAD(aad, { plaintextLength }); 34 d.setAuthTag(c.getAuthTag()); 35 const actual = Buffer.concat([d.update(ciphertext), d.final()]); 36 37 assert.deepStrictEqual(expected, actual); 38} 39 40function mstream(config) { 41 const { cipher, key, iv, aad, authTagLength, plaintextLength } = config; 42 const expected = Buffer.alloc(plaintextLength); 43 44 const c = crypto.createCipheriv(cipher, key, iv, { authTagLength }); 45 c.setAAD(aad, { plaintextLength }); 46 47 const plain = new stream.PassThrough(); 48 const crypt = new Sink(); 49 const chunks = crypt.chunks; 50 plain.pipe(c).pipe(crypt); 51 plain.end(expected); 52 53 crypt.on('close', common.mustCall(() => { 54 const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength }); 55 d.setAAD(aad, { plaintextLength }); 56 d.setAuthTag(c.getAuthTag()); 57 58 const crypt = new stream.PassThrough(); 59 const plain = new Sink(); 60 crypt.pipe(d).pipe(plain); 61 for (const chunk of chunks) crypt.write(chunk); 62 crypt.end(); 63 64 plain.on('close', common.mustCall(() => { 65 const actual = Buffer.concat(plain.chunks); 66 assert.deepStrictEqual(expected, actual); 67 })); 68 })); 69} 70 71function fstream(config) { 72 const count = fstream.count++; 73 const filename = (name) => path.join(tmpdir.path, `${name}${count}`); 74 75 const { cipher, key, iv, aad, authTagLength, plaintextLength } = config; 76 const expected = Buffer.alloc(plaintextLength); 77 fs.writeFileSync(filename('a'), expected); 78 79 const c = crypto.createCipheriv(cipher, key, iv, { authTagLength }); 80 c.setAAD(aad, { plaintextLength }); 81 82 const plain = fs.createReadStream(filename('a')); 83 const crypt = fs.createWriteStream(filename('b')); 84 plain.pipe(c).pipe(crypt); 85 86 // Observation: 'close' comes before 'end' on |c|, which definitely feels 87 // wrong. Switching to `c.on('end', ...)` doesn't fix the test though. 88 crypt.on('close', common.mustCall(() => { 89 // Just to drive home the point that decryption does actually work: 90 // reading the file synchronously, then decrypting it, works. 91 { 92 const ciphertext = fs.readFileSync(filename('b')); 93 const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength }); 94 d.setAAD(aad, { plaintextLength }); 95 d.setAuthTag(c.getAuthTag()); 96 const actual = Buffer.concat([d.update(ciphertext), d.final()]); 97 assert.deepStrictEqual(expected, actual); 98 } 99 100 const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength }); 101 d.setAAD(aad, { plaintextLength }); 102 d.setAuthTag(c.getAuthTag()); 103 104 const crypt = fs.createReadStream(filename('b')); 105 const plain = fs.createWriteStream(filename('c')); 106 crypt.pipe(d).pipe(plain); 107 108 plain.on('close', common.mustCall(() => { 109 const actual = fs.readFileSync(filename('c')); 110 assert.deepStrictEqual(expected, actual); 111 })); 112 })); 113} 114fstream.count = 0; 115 116function test(config) { 117 direct(config); 118 mstream(config); 119 fstream(config); 120} 121 122tmpdir.refresh(); 123 124// OK 125test({ 126 cipher: 'aes-128-ccm', 127 aad: Buffer.alloc(1), 128 iv: Buffer.alloc(8), 129 key: Buffer.alloc(16), 130 authTagLength: 16, 131 plaintextLength: 32768, 132}); 133 134// Fails the fstream test. 135test({ 136 cipher: 'aes-128-ccm', 137 aad: Buffer.alloc(1), 138 iv: Buffer.alloc(8), 139 key: Buffer.alloc(16), 140 authTagLength: 16, 141 plaintextLength: 32769, 142}); 143