11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci// Refs: https://github.com/nodejs/node/issues/31733
31cb0ef41Sopenharmony_ciconst common = require('../common');
41cb0ef41Sopenharmony_ciif (!common.hasCrypto)
51cb0ef41Sopenharmony_ci  common.skip('missing crypto');
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ciconst assert = require('assert');
81cb0ef41Sopenharmony_ciconst crypto = require('crypto');
91cb0ef41Sopenharmony_ciconst fs = require('fs');
101cb0ef41Sopenharmony_ciconst path = require('path');
111cb0ef41Sopenharmony_ciconst stream = require('stream');
121cb0ef41Sopenharmony_ciconst tmpdir = require('../common/tmpdir');
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ciclass Sink extends stream.Writable {
151cb0ef41Sopenharmony_ci  constructor() {
161cb0ef41Sopenharmony_ci    super();
171cb0ef41Sopenharmony_ci    this.chunks = [];
181cb0ef41Sopenharmony_ci  }
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ci  _write(chunk, encoding, cb) {
211cb0ef41Sopenharmony_ci    this.chunks.push(chunk);
221cb0ef41Sopenharmony_ci    cb();
231cb0ef41Sopenharmony_ci  }
241cb0ef41Sopenharmony_ci}
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_cifunction direct(config) {
271cb0ef41Sopenharmony_ci  const { cipher, key, iv, aad, authTagLength, plaintextLength } = config;
281cb0ef41Sopenharmony_ci  const expected = Buffer.alloc(plaintextLength);
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_ci  const c = crypto.createCipheriv(cipher, key, iv, { authTagLength });
311cb0ef41Sopenharmony_ci  c.setAAD(aad, { plaintextLength });
321cb0ef41Sopenharmony_ci  const ciphertext = Buffer.concat([c.update(expected), c.final()]);
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci  const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength });
351cb0ef41Sopenharmony_ci  d.setAAD(aad, { plaintextLength });
361cb0ef41Sopenharmony_ci  d.setAuthTag(c.getAuthTag());
371cb0ef41Sopenharmony_ci  const actual = Buffer.concat([d.update(ciphertext), d.final()]);
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci  assert.deepStrictEqual(expected, actual);
401cb0ef41Sopenharmony_ci}
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_cifunction mstream(config) {
431cb0ef41Sopenharmony_ci  const { cipher, key, iv, aad, authTagLength, plaintextLength } = config;
441cb0ef41Sopenharmony_ci  const expected = Buffer.alloc(plaintextLength);
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ci  const c = crypto.createCipheriv(cipher, key, iv, { authTagLength });
471cb0ef41Sopenharmony_ci  c.setAAD(aad, { plaintextLength });
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci  const plain = new stream.PassThrough();
501cb0ef41Sopenharmony_ci  const crypt = new Sink();
511cb0ef41Sopenharmony_ci  const chunks = crypt.chunks;
521cb0ef41Sopenharmony_ci  plain.pipe(c).pipe(crypt);
531cb0ef41Sopenharmony_ci  plain.end(expected);
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci  crypt.on('close', common.mustCall(() => {
561cb0ef41Sopenharmony_ci    const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength });
571cb0ef41Sopenharmony_ci    d.setAAD(aad, { plaintextLength });
581cb0ef41Sopenharmony_ci    d.setAuthTag(c.getAuthTag());
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_ci    const crypt = new stream.PassThrough();
611cb0ef41Sopenharmony_ci    const plain = new Sink();
621cb0ef41Sopenharmony_ci    crypt.pipe(d).pipe(plain);
631cb0ef41Sopenharmony_ci    for (const chunk of chunks) crypt.write(chunk);
641cb0ef41Sopenharmony_ci    crypt.end();
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci    plain.on('close', common.mustCall(() => {
671cb0ef41Sopenharmony_ci      const actual = Buffer.concat(plain.chunks);
681cb0ef41Sopenharmony_ci      assert.deepStrictEqual(expected, actual);
691cb0ef41Sopenharmony_ci    }));
701cb0ef41Sopenharmony_ci  }));
711cb0ef41Sopenharmony_ci}
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_cifunction fstream(config) {
741cb0ef41Sopenharmony_ci  const count = fstream.count++;
751cb0ef41Sopenharmony_ci  const filename = (name) => path.join(tmpdir.path, `${name}${count}`);
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci  const { cipher, key, iv, aad, authTagLength, plaintextLength } = config;
781cb0ef41Sopenharmony_ci  const expected = Buffer.alloc(plaintextLength);
791cb0ef41Sopenharmony_ci  fs.writeFileSync(filename('a'), expected);
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci  const c = crypto.createCipheriv(cipher, key, iv, { authTagLength });
821cb0ef41Sopenharmony_ci  c.setAAD(aad, { plaintextLength });
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci  const plain = fs.createReadStream(filename('a'));
851cb0ef41Sopenharmony_ci  const crypt = fs.createWriteStream(filename('b'));
861cb0ef41Sopenharmony_ci  plain.pipe(c).pipe(crypt);
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci  // Observation: 'close' comes before 'end' on |c|, which definitely feels
891cb0ef41Sopenharmony_ci  // wrong. Switching to `c.on('end', ...)` doesn't fix the test though.
901cb0ef41Sopenharmony_ci  crypt.on('close', common.mustCall(() => {
911cb0ef41Sopenharmony_ci    // Just to drive home the point that decryption does actually work:
921cb0ef41Sopenharmony_ci    // reading the file synchronously, then decrypting it, works.
931cb0ef41Sopenharmony_ci    {
941cb0ef41Sopenharmony_ci      const ciphertext = fs.readFileSync(filename('b'));
951cb0ef41Sopenharmony_ci      const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength });
961cb0ef41Sopenharmony_ci      d.setAAD(aad, { plaintextLength });
971cb0ef41Sopenharmony_ci      d.setAuthTag(c.getAuthTag());
981cb0ef41Sopenharmony_ci      const actual = Buffer.concat([d.update(ciphertext), d.final()]);
991cb0ef41Sopenharmony_ci      assert.deepStrictEqual(expected, actual);
1001cb0ef41Sopenharmony_ci    }
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci    const d = crypto.createDecipheriv(cipher, key, iv, { authTagLength });
1031cb0ef41Sopenharmony_ci    d.setAAD(aad, { plaintextLength });
1041cb0ef41Sopenharmony_ci    d.setAuthTag(c.getAuthTag());
1051cb0ef41Sopenharmony_ci
1061cb0ef41Sopenharmony_ci    const crypt = fs.createReadStream(filename('b'));
1071cb0ef41Sopenharmony_ci    const plain = fs.createWriteStream(filename('c'));
1081cb0ef41Sopenharmony_ci    crypt.pipe(d).pipe(plain);
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_ci    plain.on('close', common.mustCall(() => {
1111cb0ef41Sopenharmony_ci      const actual = fs.readFileSync(filename('c'));
1121cb0ef41Sopenharmony_ci      assert.deepStrictEqual(expected, actual);
1131cb0ef41Sopenharmony_ci    }));
1141cb0ef41Sopenharmony_ci  }));
1151cb0ef41Sopenharmony_ci}
1161cb0ef41Sopenharmony_cifstream.count = 0;
1171cb0ef41Sopenharmony_ci
1181cb0ef41Sopenharmony_cifunction test(config) {
1191cb0ef41Sopenharmony_ci  direct(config);
1201cb0ef41Sopenharmony_ci  mstream(config);
1211cb0ef41Sopenharmony_ci  fstream(config);
1221cb0ef41Sopenharmony_ci}
1231cb0ef41Sopenharmony_ci
1241cb0ef41Sopenharmony_citmpdir.refresh();
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_citest({
1271cb0ef41Sopenharmony_ci  cipher: 'aes-128-ccm',
1281cb0ef41Sopenharmony_ci  aad: Buffer.alloc(1),
1291cb0ef41Sopenharmony_ci  iv: Buffer.alloc(8),
1301cb0ef41Sopenharmony_ci  key: Buffer.alloc(16),
1311cb0ef41Sopenharmony_ci  authTagLength: 16,
1321cb0ef41Sopenharmony_ci  plaintextLength: 32768,
1331cb0ef41Sopenharmony_ci});
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_citest({
1361cb0ef41Sopenharmony_ci  cipher: 'aes-128-ccm',
1371cb0ef41Sopenharmony_ci  aad: Buffer.alloc(1),
1381cb0ef41Sopenharmony_ci  iv: Buffer.alloc(8),
1391cb0ef41Sopenharmony_ci  key: Buffer.alloc(16),
1401cb0ef41Sopenharmony_ci  authTagLength: 16,
1411cb0ef41Sopenharmony_ci  plaintextLength: 32769,
1421cb0ef41Sopenharmony_ci});
143