current commit
261025.12 · test,benchmark: drop OpenSSL 1.x-only coverage
// Flags: --expose-internals

'use strict';

const common = require('../common');

if (!common.hasCrypto) {
  common.skip('missing crypto');
}

const { isBoringSSL } = require('../common/crypto');

if (isBoringSSL) {
  common.skip('OpenSSL EVP_MAC support is required');
}

const assert = require('node:assert');
const { encodingsMap } = require('internal/util');
const {
  createMac,
  getCiphers,
  getMacs,
} = require('node:crypto');

const availableMacs = new Set(getMacs());
const availableCiphers = new Set(getCiphers());
const kmacVectors = require('../fixtures/crypto/kmac')();
const gmacIVStorage = Uint8Array.from([
  0xff,
  ...Buffer.alloc(12),
  0xff,
]);
const blake2bSaltStorage = Uint8Array.from([
  0xff,
  ...Buffer.from('000102030405060708090a0b0c0d0e0f', 'hex'),
  0xff,
]);
const blake2sSaltStorage = Uint8Array.from([
  0xff,
  ...Buffer.from('0001020304050607', 'hex'),
  0xff,
]);

const vectors = [
  {
    label: 'CMAC-AES-128',
    algorithm: 'cmac',
    options: { cipher: 'aes-128-cbc' },
    key: '2b7e151628aed2a6abf7158809cf4f3c',
    data: '',
    expected: 'bb1d6929e95937287fa37d129b756746',
    cipher: 'aes-128-cbc',
  },
  {
    label: 'GMAC-AES-128',
    algorithm: 'gmac',
    options: {
      cipher: 'aes-128-gcm',
      iv: new DataView(gmacIVStorage.buffer, 1, 12),
    },
    key: '00000000000000000000000000000000',
    data: '',
    expected: '58e2fccefa7e3061367f1d57a4e7455a',
    cipher: 'aes-128-gcm',
  },
  {
    label: 'Poly1305',
    algorithm: 'poly1305',
    key: '85d6be7857556d337f4452fe42d506a8' +
         '0103808afb0db2fd4abff6af4149f51b',
    data: Buffer.from('Cryptographic Forum Research Group').toString('hex'),
    expected: 'a8061dc1305136c6c22b8baf0c0127a9',
  },
  {
    label: 'SipHash-2-4',
    algorithm: 'siphash',
    options: { outputLength: 8 },
    key: '000102030405060708090a0b0c0d0e0f',
    data: '',
    expected: '310e0edd47db6f72',
  },
  {
    label: 'BLAKE2b MAC',
    algorithm: 'blake2bmac',
    options: {
      outputLength: 32,
      salt: new DataView(blake2bSaltStorage.buffer, 1, 16),
    },
    key: '000102030405060708090a0b0c0d0e0f',
    data: Buffer.from('abc').toString('hex'),
    expected: '6e583b101a126f2d1fb6d1fff9834f3a' +
              '0d0e23c17b902cca4f1a0d7abfb327fa',
  },
  {
    label: 'BLAKE2s MAC',
    algorithm: 'blake2smac',
    options: {
      outputLength: 16,
      salt: new DataView(blake2sSaltStorage.buffer, 1, 8),
    },
    key: '000102030405060708090a0b0c0d0e0f',
    data: Buffer.from('abc').toString('hex'),
    expected: '18adff242af55a56c7b7646df6c3d9ba',
  },
];

for (const index of [0, 3]) {
  const vector = kmacVectors[index];
  const algorithm = vector.algorithm.toLowerCase();
  const options = {
    outputLength: vector.outputLength / 8,
  };
  if (vector.customization !== undefined) {
    const storage = Uint8Array.from([
      0xff,
      ...vector.customization,
      0xff,
    ]);
    options.customization = new DataView(
      storage.buffer, 1, vector.customization.length);
  }
  vectors.push({
    label: vector.algorithm,
    algorithm,
    options,
    key: vector.key.toString('hex'),
    data: vector.data.toString('hex'),
    expected: vector.expected.toString('hex'),
  });
}

for (const vector of vectors) {
  if (!availableMacs.has(vector.algorithm) ||
      (vector.cipher !== undefined &&
       !availableCiphers.has(vector.cipher))) {
    common.printSkipMessage(`${vector.label} is not available`);
    continue;
  }

  const key = Buffer.from(vector.key, 'hex');
  const data = Buffer.from(vector.data, 'hex');
  const expected = Buffer.from(vector.expected, 'hex');
  assert.deepStrictEqual(
    createMac(vector.algorithm, key, vector.options).update(data).final(),
    expected,
  );
}

if (availableMacs.has('kmac128')) {
  const vector = kmacVectors[0];
  const algorithm = 'kmac128';
  const options = { outputLength: 0 };
  for (const outputEncoding of Object.keys(encodingsMap)) {
    if (outputEncoding === 'buffer') continue;
    assert.strictEqual(
      createMac(algorithm, vector.key, options)
        .update(vector.data)
        .final(outputEncoding),
      '',
    );
  }

  for (const result of [
    createMac(algorithm, vector.key, options)
      .update(vector.data)
      .final(),
    createMac(algorithm, vector.key, options)
      .update(vector.data)
      .final('buffer'),
  ]) {
    assert(Buffer.isBuffer(result));
    assert.deepStrictEqual(result, Buffer.alloc(0));
  }

  const streamed = createMac(algorithm, vector.key, options);
  streamed.on('data', common.mustNotCall());
  streamed.on('end', common.mustCall());
  streamed.end(vector.data);
}