11cb0ef41Sopenharmony_ci// Copyright Joyent, Inc. and other Node contributors. 21cb0ef41Sopenharmony_ci// 31cb0ef41Sopenharmony_ci// Permission is hereby granted, free of charge, to any person obtaining a 41cb0ef41Sopenharmony_ci// copy of this software and associated documentation files (the 51cb0ef41Sopenharmony_ci// "Software"), to deal in the Software without restriction, including 61cb0ef41Sopenharmony_ci// without limitation the rights to use, copy, modify, merge, publish, 71cb0ef41Sopenharmony_ci// distribute, sublicense, and/or sell copies of the Software, and to permit 81cb0ef41Sopenharmony_ci// persons to whom the Software is furnished to do so, subject to the 91cb0ef41Sopenharmony_ci// following conditions: 101cb0ef41Sopenharmony_ci// 111cb0ef41Sopenharmony_ci// The above copyright notice and this permission notice shall be included 121cb0ef41Sopenharmony_ci// in all copies or substantial portions of the Software. 131cb0ef41Sopenharmony_ci// 141cb0ef41Sopenharmony_ci// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 151cb0ef41Sopenharmony_ci// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 161cb0ef41Sopenharmony_ci// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 171cb0ef41Sopenharmony_ci// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 181cb0ef41Sopenharmony_ci// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 191cb0ef41Sopenharmony_ci// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 201cb0ef41Sopenharmony_ci// USE OR OTHER DEALINGS IN THE SOFTWARE. 211cb0ef41Sopenharmony_ci// Flags: --no-warnings 221cb0ef41Sopenharmony_ci'use strict'; 231cb0ef41Sopenharmony_ciconst common = require('../common'); 241cb0ef41Sopenharmony_ciif (!common.hasCrypto) 251cb0ef41Sopenharmony_ci common.skip('missing crypto'); 261cb0ef41Sopenharmony_ci 271cb0ef41Sopenharmony_ciconst assert = require('assert'); 281cb0ef41Sopenharmony_ciconst crypto = require('crypto'); 291cb0ef41Sopenharmony_ciconst { inspect } = require('util'); 301cb0ef41Sopenharmony_ciconst fixtures = require('../common/fixtures'); 311cb0ef41Sopenharmony_ci 321cb0ef41Sopenharmony_cicrypto.DEFAULT_ENCODING = 'buffer'; 331cb0ef41Sopenharmony_ci 341cb0ef41Sopenharmony_ci// 351cb0ef41Sopenharmony_ci// Test authenticated encryption modes. 361cb0ef41Sopenharmony_ci// 371cb0ef41Sopenharmony_ci// !NEVER USE STATIC IVs IN REAL LIFE! 381cb0ef41Sopenharmony_ci// 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ciconst TEST_CASES = require(fixtures.path('aead-vectors.js')); 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ciconst errMessages = { 431cb0ef41Sopenharmony_ci auth: / auth/, 441cb0ef41Sopenharmony_ci state: / state/, 451cb0ef41Sopenharmony_ci FIPS: /not supported in FIPS mode/, 461cb0ef41Sopenharmony_ci length: /Invalid initialization vector/, 471cb0ef41Sopenharmony_ci authTagLength: /Invalid authentication tag length/ 481cb0ef41Sopenharmony_ci}; 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ciconst ciphers = crypto.getCiphers(); 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ciconst expectedWarnings = common.hasFipsCrypto ? 531cb0ef41Sopenharmony_ci [] : [ 541cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-192-gcm'], 551cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-192-ccm'], 561cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-192-ccm'], 571cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-128-ccm'], 581cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-128-ccm'], 591cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-128-ccm'], 601cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 611cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 621cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 631cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 641cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 651cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 661cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 671cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 681cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 691cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 701cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 711cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 721cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-256-ccm'], 731cb0ef41Sopenharmony_ci ['Use Cipheriv for counter mode of aes-128-ccm'], 741cb0ef41Sopenharmony_ci ]; 751cb0ef41Sopenharmony_ci 761cb0ef41Sopenharmony_ciconst expectedDeprecationWarnings = [ 771cb0ef41Sopenharmony_ci ['crypto.DEFAULT_ENCODING is deprecated.', 'DEP0091'], 781cb0ef41Sopenharmony_ci ['crypto.createCipher is deprecated.', 'DEP0106'], 791cb0ef41Sopenharmony_ci]; 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_cicommon.expectWarning({ 821cb0ef41Sopenharmony_ci Warning: expectedWarnings, 831cb0ef41Sopenharmony_ci DeprecationWarning: expectedDeprecationWarnings 841cb0ef41Sopenharmony_ci}); 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_cifor (const test of TEST_CASES) { 871cb0ef41Sopenharmony_ci if (!ciphers.includes(test.algo)) { 881cb0ef41Sopenharmony_ci common.printSkipMessage(`unsupported ${test.algo} test`); 891cb0ef41Sopenharmony_ci continue; 901cb0ef41Sopenharmony_ci } 911cb0ef41Sopenharmony_ci 921cb0ef41Sopenharmony_ci if (common.hasFipsCrypto && test.iv.length < 24) { 931cb0ef41Sopenharmony_ci common.printSkipMessage('IV len < 12 bytes unsupported in FIPS mode'); 941cb0ef41Sopenharmony_ci continue; 951cb0ef41Sopenharmony_ci } 961cb0ef41Sopenharmony_ci 971cb0ef41Sopenharmony_ci const isCCM = /^aes-(128|192|256)-ccm$/.test(test.algo); 981cb0ef41Sopenharmony_ci const isOCB = /^aes-(128|192|256)-ocb$/.test(test.algo); 991cb0ef41Sopenharmony_ci 1001cb0ef41Sopenharmony_ci let options; 1011cb0ef41Sopenharmony_ci if (isCCM || isOCB) 1021cb0ef41Sopenharmony_ci options = { authTagLength: test.tag.length / 2 }; 1031cb0ef41Sopenharmony_ci 1041cb0ef41Sopenharmony_ci const inputEncoding = test.plainIsHex ? 'hex' : 'ascii'; 1051cb0ef41Sopenharmony_ci 1061cb0ef41Sopenharmony_ci let aadOptions; 1071cb0ef41Sopenharmony_ci if (isCCM) { 1081cb0ef41Sopenharmony_ci aadOptions = { 1091cb0ef41Sopenharmony_ci plaintextLength: Buffer.from(test.plain, inputEncoding).length 1101cb0ef41Sopenharmony_ci }; 1111cb0ef41Sopenharmony_ci } 1121cb0ef41Sopenharmony_ci 1131cb0ef41Sopenharmony_ci { 1141cb0ef41Sopenharmony_ci const encrypt = crypto.createCipheriv(test.algo, 1151cb0ef41Sopenharmony_ci Buffer.from(test.key, 'hex'), 1161cb0ef41Sopenharmony_ci Buffer.from(test.iv, 'hex'), 1171cb0ef41Sopenharmony_ci options); 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_ci if (test.aad) 1201cb0ef41Sopenharmony_ci encrypt.setAAD(Buffer.from(test.aad, 'hex'), aadOptions); 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_ci let hex = encrypt.update(test.plain, inputEncoding, 'hex'); 1231cb0ef41Sopenharmony_ci hex += encrypt.final('hex'); 1241cb0ef41Sopenharmony_ci 1251cb0ef41Sopenharmony_ci const auth_tag = encrypt.getAuthTag(); 1261cb0ef41Sopenharmony_ci // Only test basic encryption run if output is marked as tampered. 1271cb0ef41Sopenharmony_ci if (!test.tampered) { 1281cb0ef41Sopenharmony_ci assert.strictEqual(hex, test.ct); 1291cb0ef41Sopenharmony_ci assert.strictEqual(auth_tag.toString('hex'), test.tag); 1301cb0ef41Sopenharmony_ci } 1311cb0ef41Sopenharmony_ci } 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_ci { 1341cb0ef41Sopenharmony_ci if (isCCM && common.hasFipsCrypto) { 1351cb0ef41Sopenharmony_ci assert.throws(() => { 1361cb0ef41Sopenharmony_ci crypto.createDecipheriv(test.algo, 1371cb0ef41Sopenharmony_ci Buffer.from(test.key, 'hex'), 1381cb0ef41Sopenharmony_ci Buffer.from(test.iv, 'hex'), 1391cb0ef41Sopenharmony_ci options); 1401cb0ef41Sopenharmony_ci }, errMessages.FIPS); 1411cb0ef41Sopenharmony_ci } else { 1421cb0ef41Sopenharmony_ci const decrypt = crypto.createDecipheriv(test.algo, 1431cb0ef41Sopenharmony_ci Buffer.from(test.key, 'hex'), 1441cb0ef41Sopenharmony_ci Buffer.from(test.iv, 'hex'), 1451cb0ef41Sopenharmony_ci options); 1461cb0ef41Sopenharmony_ci decrypt.setAuthTag(Buffer.from(test.tag, 'hex')); 1471cb0ef41Sopenharmony_ci if (test.aad) 1481cb0ef41Sopenharmony_ci decrypt.setAAD(Buffer.from(test.aad, 'hex'), aadOptions); 1491cb0ef41Sopenharmony_ci 1501cb0ef41Sopenharmony_ci const outputEncoding = test.plainIsHex ? 'hex' : 'ascii'; 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_ci let msg = decrypt.update(test.ct, 'hex', outputEncoding); 1531cb0ef41Sopenharmony_ci if (!test.tampered) { 1541cb0ef41Sopenharmony_ci msg += decrypt.final(outputEncoding); 1551cb0ef41Sopenharmony_ci assert.strictEqual(msg, test.plain); 1561cb0ef41Sopenharmony_ci } else { 1571cb0ef41Sopenharmony_ci // Assert that final throws if input data could not be verified! 1581cb0ef41Sopenharmony_ci assert.throws(function() { decrypt.final('hex'); }, errMessages.auth); 1591cb0ef41Sopenharmony_ci } 1601cb0ef41Sopenharmony_ci } 1611cb0ef41Sopenharmony_ci } 1621cb0ef41Sopenharmony_ci 1631cb0ef41Sopenharmony_ci if (test.password) { 1641cb0ef41Sopenharmony_ci if (common.hasFipsCrypto) { 1651cb0ef41Sopenharmony_ci assert.throws(() => { crypto.createCipher(test.algo, test.password); }, 1661cb0ef41Sopenharmony_ci errMessages.FIPS); 1671cb0ef41Sopenharmony_ci } else { 1681cb0ef41Sopenharmony_ci const encrypt = crypto.createCipher(test.algo, test.password, options); 1691cb0ef41Sopenharmony_ci if (test.aad) 1701cb0ef41Sopenharmony_ci encrypt.setAAD(Buffer.from(test.aad, 'hex'), aadOptions); 1711cb0ef41Sopenharmony_ci let hex = encrypt.update(test.plain, 'ascii', 'hex'); 1721cb0ef41Sopenharmony_ci hex += encrypt.final('hex'); 1731cb0ef41Sopenharmony_ci const auth_tag = encrypt.getAuthTag(); 1741cb0ef41Sopenharmony_ci // Only test basic encryption run if output is marked as tampered. 1751cb0ef41Sopenharmony_ci if (!test.tampered) { 1761cb0ef41Sopenharmony_ci assert.strictEqual(hex, test.ct); 1771cb0ef41Sopenharmony_ci assert.strictEqual(auth_tag.toString('hex'), test.tag); 1781cb0ef41Sopenharmony_ci } 1791cb0ef41Sopenharmony_ci } 1801cb0ef41Sopenharmony_ci } 1811cb0ef41Sopenharmony_ci 1821cb0ef41Sopenharmony_ci if (test.password) { 1831cb0ef41Sopenharmony_ci if (common.hasFipsCrypto) { 1841cb0ef41Sopenharmony_ci assert.throws(() => { crypto.createDecipher(test.algo, test.password); }, 1851cb0ef41Sopenharmony_ci errMessages.FIPS); 1861cb0ef41Sopenharmony_ci } else { 1871cb0ef41Sopenharmony_ci const decrypt = crypto.createDecipher(test.algo, test.password, options); 1881cb0ef41Sopenharmony_ci decrypt.setAuthTag(Buffer.from(test.tag, 'hex')); 1891cb0ef41Sopenharmony_ci if (test.aad) 1901cb0ef41Sopenharmony_ci decrypt.setAAD(Buffer.from(test.aad, 'hex'), aadOptions); 1911cb0ef41Sopenharmony_ci let msg = decrypt.update(test.ct, 'hex', 'ascii'); 1921cb0ef41Sopenharmony_ci if (!test.tampered) { 1931cb0ef41Sopenharmony_ci msg += decrypt.final('ascii'); 1941cb0ef41Sopenharmony_ci assert.strictEqual(msg, test.plain); 1951cb0ef41Sopenharmony_ci } else { 1961cb0ef41Sopenharmony_ci // Assert that final throws if input data could not be verified! 1971cb0ef41Sopenharmony_ci assert.throws(function() { decrypt.final('ascii'); }, errMessages.auth); 1981cb0ef41Sopenharmony_ci } 1991cb0ef41Sopenharmony_ci } 2001cb0ef41Sopenharmony_ci } 2011cb0ef41Sopenharmony_ci 2021cb0ef41Sopenharmony_ci { 2031cb0ef41Sopenharmony_ci // Trying to get tag before inputting all data: 2041cb0ef41Sopenharmony_ci const encrypt = crypto.createCipheriv(test.algo, 2051cb0ef41Sopenharmony_ci Buffer.from(test.key, 'hex'), 2061cb0ef41Sopenharmony_ci Buffer.from(test.iv, 'hex'), 2071cb0ef41Sopenharmony_ci options); 2081cb0ef41Sopenharmony_ci encrypt.update('blah', 'ascii'); 2091cb0ef41Sopenharmony_ci assert.throws(function() { encrypt.getAuthTag(); }, errMessages.state); 2101cb0ef41Sopenharmony_ci } 2111cb0ef41Sopenharmony_ci 2121cb0ef41Sopenharmony_ci { 2131cb0ef41Sopenharmony_ci // Trying to create cipher with incorrect IV length 2141cb0ef41Sopenharmony_ci assert.throws(function() { 2151cb0ef41Sopenharmony_ci crypto.createCipheriv( 2161cb0ef41Sopenharmony_ci test.algo, 2171cb0ef41Sopenharmony_ci Buffer.from(test.key, 'hex'), 2181cb0ef41Sopenharmony_ci Buffer.alloc(0) 2191cb0ef41Sopenharmony_ci ); 2201cb0ef41Sopenharmony_ci }, errMessages.length); 2211cb0ef41Sopenharmony_ci } 2221cb0ef41Sopenharmony_ci} 2231cb0ef41Sopenharmony_ci 2241cb0ef41Sopenharmony_ci// Non-authenticating mode: 2251cb0ef41Sopenharmony_ci{ 2261cb0ef41Sopenharmony_ci const encrypt = 2271cb0ef41Sopenharmony_ci crypto.createCipheriv('aes-128-cbc', 2281cb0ef41Sopenharmony_ci 'ipxp9a6i1Mb4USb4', 2291cb0ef41Sopenharmony_ci '6fKjEjR3Vl30EUYC'); 2301cb0ef41Sopenharmony_ci encrypt.update('blah', 'ascii'); 2311cb0ef41Sopenharmony_ci encrypt.final(); 2321cb0ef41Sopenharmony_ci assert.throws(() => encrypt.getAuthTag(), errMessages.state); 2331cb0ef41Sopenharmony_ci assert.throws(() => encrypt.setAAD(Buffer.from('123', 'ascii')), 2341cb0ef41Sopenharmony_ci errMessages.state); 2351cb0ef41Sopenharmony_ci} 2361cb0ef41Sopenharmony_ci 2371cb0ef41Sopenharmony_ci// GCM only supports specific authentication tag lengths, invalid lengths should 2381cb0ef41Sopenharmony_ci// throw. 2391cb0ef41Sopenharmony_ci{ 2401cb0ef41Sopenharmony_ci for (const length of [0, 1, 2, 6, 9, 10, 11, 17]) { 2411cb0ef41Sopenharmony_ci assert.throws(() => { 2421cb0ef41Sopenharmony_ci const decrypt = crypto.createDecipheriv('aes-128-gcm', 2431cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQh', 2441cb0ef41Sopenharmony_ci 'qkuZpJWCewa6Szih'); 2451cb0ef41Sopenharmony_ci decrypt.setAuthTag(Buffer.from('1'.repeat(length))); 2461cb0ef41Sopenharmony_ci }, { 2471cb0ef41Sopenharmony_ci name: 'TypeError', 2481cb0ef41Sopenharmony_ci message: /Invalid authentication tag length/ 2491cb0ef41Sopenharmony_ci }); 2501cb0ef41Sopenharmony_ci 2511cb0ef41Sopenharmony_ci assert.throws(() => { 2521cb0ef41Sopenharmony_ci crypto.createCipheriv('aes-256-gcm', 2531cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 2541cb0ef41Sopenharmony_ci 'qkuZpJWCewa6Szih', 2551cb0ef41Sopenharmony_ci { 2561cb0ef41Sopenharmony_ci authTagLength: length 2571cb0ef41Sopenharmony_ci }); 2581cb0ef41Sopenharmony_ci }, { 2591cb0ef41Sopenharmony_ci name: 'TypeError', 2601cb0ef41Sopenharmony_ci message: /Invalid authentication tag length/ 2611cb0ef41Sopenharmony_ci }); 2621cb0ef41Sopenharmony_ci 2631cb0ef41Sopenharmony_ci assert.throws(() => { 2641cb0ef41Sopenharmony_ci crypto.createDecipheriv('aes-256-gcm', 2651cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 2661cb0ef41Sopenharmony_ci 'qkuZpJWCewa6Szih', 2671cb0ef41Sopenharmony_ci { 2681cb0ef41Sopenharmony_ci authTagLength: length 2691cb0ef41Sopenharmony_ci }); 2701cb0ef41Sopenharmony_ci }, { 2711cb0ef41Sopenharmony_ci name: 'TypeError', 2721cb0ef41Sopenharmony_ci message: /Invalid authentication tag length/ 2731cb0ef41Sopenharmony_ci }); 2741cb0ef41Sopenharmony_ci } 2751cb0ef41Sopenharmony_ci} 2761cb0ef41Sopenharmony_ci 2771cb0ef41Sopenharmony_ci// Test that GCM can produce shorter authentication tags than 16 bytes. 2781cb0ef41Sopenharmony_ci{ 2791cb0ef41Sopenharmony_ci const fullTag = '1debb47b2c91ba2cea16fad021703070'; 2801cb0ef41Sopenharmony_ci for (const [authTagLength, e] of [[undefined, 16], [12, 12], [4, 4]]) { 2811cb0ef41Sopenharmony_ci const cipher = crypto.createCipheriv('aes-256-gcm', 2821cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 2831cb0ef41Sopenharmony_ci 'qkuZpJWCewa6Szih', { 2841cb0ef41Sopenharmony_ci authTagLength 2851cb0ef41Sopenharmony_ci }); 2861cb0ef41Sopenharmony_ci cipher.setAAD(Buffer.from('abcd')); 2871cb0ef41Sopenharmony_ci cipher.update('01234567', 'hex'); 2881cb0ef41Sopenharmony_ci cipher.final(); 2891cb0ef41Sopenharmony_ci const tag = cipher.getAuthTag(); 2901cb0ef41Sopenharmony_ci assert.strictEqual(tag.toString('hex'), fullTag.substr(0, 2 * e)); 2911cb0ef41Sopenharmony_ci } 2921cb0ef41Sopenharmony_ci} 2931cb0ef41Sopenharmony_ci 2941cb0ef41Sopenharmony_ci// Test that users can manually restrict the GCM tag length to a single value. 2951cb0ef41Sopenharmony_ci{ 2961cb0ef41Sopenharmony_ci const decipher = crypto.createDecipheriv('aes-256-gcm', 2971cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 2981cb0ef41Sopenharmony_ci 'qkuZpJWCewa6Szih', { 2991cb0ef41Sopenharmony_ci authTagLength: 8 3001cb0ef41Sopenharmony_ci }); 3011cb0ef41Sopenharmony_ci 3021cb0ef41Sopenharmony_ci assert.throws(() => { 3031cb0ef41Sopenharmony_ci // This tag would normally be allowed. 3041cb0ef41Sopenharmony_ci decipher.setAuthTag(Buffer.from('1'.repeat(12))); 3051cb0ef41Sopenharmony_ci }, { 3061cb0ef41Sopenharmony_ci name: 'TypeError', 3071cb0ef41Sopenharmony_ci message: /Invalid authentication tag length/ 3081cb0ef41Sopenharmony_ci }); 3091cb0ef41Sopenharmony_ci 3101cb0ef41Sopenharmony_ci // The Decipher object should be left intact. 3111cb0ef41Sopenharmony_ci decipher.setAuthTag(Buffer.from('445352d3ff85cf94', 'hex')); 3121cb0ef41Sopenharmony_ci const text = Buffer.concat([ 3131cb0ef41Sopenharmony_ci decipher.update('3a2a3647', 'hex'), 3141cb0ef41Sopenharmony_ci decipher.final(), 3151cb0ef41Sopenharmony_ci ]); 3161cb0ef41Sopenharmony_ci assert.strictEqual(text.toString('utf8'), 'node'); 3171cb0ef41Sopenharmony_ci} 3181cb0ef41Sopenharmony_ci 3191cb0ef41Sopenharmony_ci// Test that create(De|C)ipher(iv)? throws if the mode is CCM and an invalid 3201cb0ef41Sopenharmony_ci// authentication tag length has been specified. 3211cb0ef41Sopenharmony_ci{ 3221cb0ef41Sopenharmony_ci for (const authTagLength of [-1, true, false, NaN, 5.5]) { 3231cb0ef41Sopenharmony_ci assert.throws(() => { 3241cb0ef41Sopenharmony_ci crypto.createCipheriv('aes-256-ccm', 3251cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 3261cb0ef41Sopenharmony_ci 'qkuZpJWCewa6S', 3271cb0ef41Sopenharmony_ci { 3281cb0ef41Sopenharmony_ci authTagLength 3291cb0ef41Sopenharmony_ci }); 3301cb0ef41Sopenharmony_ci }, { 3311cb0ef41Sopenharmony_ci name: 'TypeError', 3321cb0ef41Sopenharmony_ci code: 'ERR_INVALID_ARG_VALUE', 3331cb0ef41Sopenharmony_ci message: "The property 'options.authTagLength' is invalid. " + 3341cb0ef41Sopenharmony_ci `Received ${inspect(authTagLength)}` 3351cb0ef41Sopenharmony_ci }); 3361cb0ef41Sopenharmony_ci 3371cb0ef41Sopenharmony_ci assert.throws(() => { 3381cb0ef41Sopenharmony_ci crypto.createDecipheriv('aes-256-ccm', 3391cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 3401cb0ef41Sopenharmony_ci 'qkuZpJWCewa6S', 3411cb0ef41Sopenharmony_ci { 3421cb0ef41Sopenharmony_ci authTagLength 3431cb0ef41Sopenharmony_ci }); 3441cb0ef41Sopenharmony_ci }, { 3451cb0ef41Sopenharmony_ci name: 'TypeError', 3461cb0ef41Sopenharmony_ci code: 'ERR_INVALID_ARG_VALUE', 3471cb0ef41Sopenharmony_ci message: "The property 'options.authTagLength' is invalid. " + 3481cb0ef41Sopenharmony_ci `Received ${inspect(authTagLength)}` 3491cb0ef41Sopenharmony_ci }); 3501cb0ef41Sopenharmony_ci 3511cb0ef41Sopenharmony_ci if (!common.hasFipsCrypto) { 3521cb0ef41Sopenharmony_ci assert.throws(() => { 3531cb0ef41Sopenharmony_ci crypto.createCipher('aes-256-ccm', 'bad password', { authTagLength }); 3541cb0ef41Sopenharmony_ci }, { 3551cb0ef41Sopenharmony_ci name: 'TypeError', 3561cb0ef41Sopenharmony_ci code: 'ERR_INVALID_ARG_VALUE', 3571cb0ef41Sopenharmony_ci message: "The property 'options.authTagLength' is invalid. " + 3581cb0ef41Sopenharmony_ci `Received ${inspect(authTagLength)}` 3591cb0ef41Sopenharmony_ci }); 3601cb0ef41Sopenharmony_ci 3611cb0ef41Sopenharmony_ci assert.throws(() => { 3621cb0ef41Sopenharmony_ci crypto.createDecipher('aes-256-ccm', 'bad password', { authTagLength }); 3631cb0ef41Sopenharmony_ci }, { 3641cb0ef41Sopenharmony_ci name: 'TypeError', 3651cb0ef41Sopenharmony_ci code: 'ERR_INVALID_ARG_VALUE', 3661cb0ef41Sopenharmony_ci message: "The property 'options.authTagLength' is invalid. " + 3671cb0ef41Sopenharmony_ci `Received ${inspect(authTagLength)}` 3681cb0ef41Sopenharmony_ci }); 3691cb0ef41Sopenharmony_ci } 3701cb0ef41Sopenharmony_ci } 3711cb0ef41Sopenharmony_ci 3721cb0ef41Sopenharmony_ci // The following values will not be caught by the JS layer and thus will not 3731cb0ef41Sopenharmony_ci // use the default error codes. 3741cb0ef41Sopenharmony_ci for (const authTagLength of [0, 1, 2, 3, 5, 7, 9, 11, 13, 15, 17, 18]) { 3751cb0ef41Sopenharmony_ci assert.throws(() => { 3761cb0ef41Sopenharmony_ci crypto.createCipheriv('aes-256-ccm', 3771cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 3781cb0ef41Sopenharmony_ci 'qkuZpJWCewa6S', 3791cb0ef41Sopenharmony_ci { 3801cb0ef41Sopenharmony_ci authTagLength 3811cb0ef41Sopenharmony_ci }); 3821cb0ef41Sopenharmony_ci }, errMessages.authTagLength); 3831cb0ef41Sopenharmony_ci 3841cb0ef41Sopenharmony_ci if (!common.hasFipsCrypto) { 3851cb0ef41Sopenharmony_ci assert.throws(() => { 3861cb0ef41Sopenharmony_ci crypto.createDecipheriv('aes-256-ccm', 3871cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 3881cb0ef41Sopenharmony_ci 'qkuZpJWCewa6S', 3891cb0ef41Sopenharmony_ci { 3901cb0ef41Sopenharmony_ci authTagLength 3911cb0ef41Sopenharmony_ci }); 3921cb0ef41Sopenharmony_ci }, errMessages.authTagLength); 3931cb0ef41Sopenharmony_ci 3941cb0ef41Sopenharmony_ci assert.throws(() => { 3951cb0ef41Sopenharmony_ci crypto.createCipher('aes-256-ccm', 'bad password', { authTagLength }); 3961cb0ef41Sopenharmony_ci }, errMessages.authTagLength); 3971cb0ef41Sopenharmony_ci 3981cb0ef41Sopenharmony_ci assert.throws(() => { 3991cb0ef41Sopenharmony_ci crypto.createDecipher('aes-256-ccm', 'bad password', { authTagLength }); 4001cb0ef41Sopenharmony_ci }, errMessages.authTagLength); 4011cb0ef41Sopenharmony_ci } 4021cb0ef41Sopenharmony_ci } 4031cb0ef41Sopenharmony_ci} 4041cb0ef41Sopenharmony_ci 4051cb0ef41Sopenharmony_ci// Test that create(De|C)ipher(iv)? throws if the mode is CCM or OCB and no 4061cb0ef41Sopenharmony_ci// authentication tag has been specified. 4071cb0ef41Sopenharmony_ci{ 4081cb0ef41Sopenharmony_ci for (const mode of ['ccm', 'ocb']) { 4091cb0ef41Sopenharmony_ci assert.throws(() => { 4101cb0ef41Sopenharmony_ci crypto.createCipheriv(`aes-256-${mode}`, 4111cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 4121cb0ef41Sopenharmony_ci 'qkuZpJWCewa6S'); 4131cb0ef41Sopenharmony_ci }, { 4141cb0ef41Sopenharmony_ci message: `authTagLength required for aes-256-${mode}` 4151cb0ef41Sopenharmony_ci }); 4161cb0ef41Sopenharmony_ci 4171cb0ef41Sopenharmony_ci // CCM decryption and create(De|C)ipher are unsupported in FIPS mode. 4181cb0ef41Sopenharmony_ci if (!common.hasFipsCrypto) { 4191cb0ef41Sopenharmony_ci assert.throws(() => { 4201cb0ef41Sopenharmony_ci crypto.createDecipheriv(`aes-256-${mode}`, 4211cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 4221cb0ef41Sopenharmony_ci 'qkuZpJWCewa6S'); 4231cb0ef41Sopenharmony_ci }, { 4241cb0ef41Sopenharmony_ci message: `authTagLength required for aes-256-${mode}` 4251cb0ef41Sopenharmony_ci }); 4261cb0ef41Sopenharmony_ci 4271cb0ef41Sopenharmony_ci assert.throws(() => { 4281cb0ef41Sopenharmony_ci crypto.createCipher(`aes-256-${mode}`, 'very bad password'); 4291cb0ef41Sopenharmony_ci }, { 4301cb0ef41Sopenharmony_ci message: `authTagLength required for aes-256-${mode}` 4311cb0ef41Sopenharmony_ci }); 4321cb0ef41Sopenharmony_ci 4331cb0ef41Sopenharmony_ci assert.throws(() => { 4341cb0ef41Sopenharmony_ci crypto.createDecipher(`aes-256-${mode}`, 'very bad password'); 4351cb0ef41Sopenharmony_ci }, { 4361cb0ef41Sopenharmony_ci message: `authTagLength required for aes-256-${mode}` 4371cb0ef41Sopenharmony_ci }); 4381cb0ef41Sopenharmony_ci } 4391cb0ef41Sopenharmony_ci } 4401cb0ef41Sopenharmony_ci} 4411cb0ef41Sopenharmony_ci 4421cb0ef41Sopenharmony_ci// Test that setAAD throws if an invalid plaintext length has been specified. 4431cb0ef41Sopenharmony_ci{ 4441cb0ef41Sopenharmony_ci const cipher = crypto.createCipheriv('aes-256-ccm', 4451cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 4461cb0ef41Sopenharmony_ci 'qkuZpJWCewa6S', 4471cb0ef41Sopenharmony_ci { 4481cb0ef41Sopenharmony_ci authTagLength: 10 4491cb0ef41Sopenharmony_ci }); 4501cb0ef41Sopenharmony_ci 4511cb0ef41Sopenharmony_ci for (const plaintextLength of [-1, true, false, NaN, 5.5]) { 4521cb0ef41Sopenharmony_ci assert.throws(() => { 4531cb0ef41Sopenharmony_ci cipher.setAAD(Buffer.from('0123456789', 'hex'), { plaintextLength }); 4541cb0ef41Sopenharmony_ci }, { 4551cb0ef41Sopenharmony_ci name: 'TypeError', 4561cb0ef41Sopenharmony_ci code: 'ERR_INVALID_ARG_VALUE', 4571cb0ef41Sopenharmony_ci message: "The property 'options.plaintextLength' is invalid. " + 4581cb0ef41Sopenharmony_ci `Received ${inspect(plaintextLength)}` 4591cb0ef41Sopenharmony_ci }); 4601cb0ef41Sopenharmony_ci } 4611cb0ef41Sopenharmony_ci} 4621cb0ef41Sopenharmony_ci 4631cb0ef41Sopenharmony_ci// Test that setAAD and update throw if the plaintext is too long. 4641cb0ef41Sopenharmony_ci{ 4651cb0ef41Sopenharmony_ci for (const ivLength of [13, 12]) { 4661cb0ef41Sopenharmony_ci const maxMessageSize = (1 << (8 * (15 - ivLength))) - 1; 4671cb0ef41Sopenharmony_ci const key = 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8'; 4681cb0ef41Sopenharmony_ci const cipher = () => crypto.createCipheriv('aes-256-ccm', key, 4691cb0ef41Sopenharmony_ci '0'.repeat(ivLength), 4701cb0ef41Sopenharmony_ci { 4711cb0ef41Sopenharmony_ci authTagLength: 10 4721cb0ef41Sopenharmony_ci }); 4731cb0ef41Sopenharmony_ci 4741cb0ef41Sopenharmony_ci assert.throws(() => { 4751cb0ef41Sopenharmony_ci cipher().setAAD(Buffer.alloc(0), { 4761cb0ef41Sopenharmony_ci plaintextLength: maxMessageSize + 1 4771cb0ef41Sopenharmony_ci }); 4781cb0ef41Sopenharmony_ci }, /Invalid message length$/); 4791cb0ef41Sopenharmony_ci 4801cb0ef41Sopenharmony_ci const msg = Buffer.alloc(maxMessageSize + 1); 4811cb0ef41Sopenharmony_ci assert.throws(() => { 4821cb0ef41Sopenharmony_ci cipher().update(msg); 4831cb0ef41Sopenharmony_ci }, /Invalid message length/); 4841cb0ef41Sopenharmony_ci 4851cb0ef41Sopenharmony_ci const c = cipher(); 4861cb0ef41Sopenharmony_ci c.setAAD(Buffer.alloc(0), { 4871cb0ef41Sopenharmony_ci plaintextLength: maxMessageSize 4881cb0ef41Sopenharmony_ci }); 4891cb0ef41Sopenharmony_ci c.update(msg.slice(1)); 4901cb0ef41Sopenharmony_ci } 4911cb0ef41Sopenharmony_ci} 4921cb0ef41Sopenharmony_ci 4931cb0ef41Sopenharmony_ci// Test that setAAD throws if the mode is CCM and the plaintext length has not 4941cb0ef41Sopenharmony_ci// been specified. 4951cb0ef41Sopenharmony_ci{ 4961cb0ef41Sopenharmony_ci assert.throws(() => { 4971cb0ef41Sopenharmony_ci const cipher = crypto.createCipheriv('aes-256-ccm', 4981cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 4991cb0ef41Sopenharmony_ci 'qkuZpJWCewa6S', 5001cb0ef41Sopenharmony_ci { 5011cb0ef41Sopenharmony_ci authTagLength: 10 5021cb0ef41Sopenharmony_ci }); 5031cb0ef41Sopenharmony_ci cipher.setAAD(Buffer.from('0123456789', 'hex')); 5041cb0ef41Sopenharmony_ci }, /options\.plaintextLength required for CCM mode with AAD/); 5051cb0ef41Sopenharmony_ci 5061cb0ef41Sopenharmony_ci if (!common.hasFipsCrypto) { 5071cb0ef41Sopenharmony_ci assert.throws(() => { 5081cb0ef41Sopenharmony_ci const cipher = crypto.createDecipheriv('aes-256-ccm', 5091cb0ef41Sopenharmony_ci 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 5101cb0ef41Sopenharmony_ci 'qkuZpJWCewa6S', 5111cb0ef41Sopenharmony_ci { 5121cb0ef41Sopenharmony_ci authTagLength: 10 5131cb0ef41Sopenharmony_ci }); 5141cb0ef41Sopenharmony_ci cipher.setAAD(Buffer.from('0123456789', 'hex')); 5151cb0ef41Sopenharmony_ci }, /options\.plaintextLength required for CCM mode with AAD/); 5161cb0ef41Sopenharmony_ci } 5171cb0ef41Sopenharmony_ci} 5181cb0ef41Sopenharmony_ci 5191cb0ef41Sopenharmony_ci// Test that final() throws in CCM mode when no authentication tag is provided. 5201cb0ef41Sopenharmony_ci{ 5211cb0ef41Sopenharmony_ci if (!common.hasFipsCrypto) { 5221cb0ef41Sopenharmony_ci const key = Buffer.from('1ed2233fa2223ef5d7df08546049406c', 'hex'); 5231cb0ef41Sopenharmony_ci const iv = Buffer.from('7305220bca40d4c90e1791e9', 'hex'); 5241cb0ef41Sopenharmony_ci const ct = Buffer.from('8beba09d4d4d861f957d51c0794f4abf8030848e', 'hex'); 5251cb0ef41Sopenharmony_ci const decrypt = crypto.createDecipheriv('aes-128-ccm', key, iv, { 5261cb0ef41Sopenharmony_ci authTagLength: 10 5271cb0ef41Sopenharmony_ci }); 5281cb0ef41Sopenharmony_ci // Normally, we would do this: 5291cb0ef41Sopenharmony_ci // decrypt.setAuthTag(Buffer.from('0d9bcd142a94caf3d1dd', 'hex')); 5301cb0ef41Sopenharmony_ci assert.throws(() => { 5311cb0ef41Sopenharmony_ci decrypt.setAAD(Buffer.from('63616c76696e', 'hex'), { 5321cb0ef41Sopenharmony_ci plaintextLength: ct.length 5331cb0ef41Sopenharmony_ci }); 5341cb0ef41Sopenharmony_ci decrypt.update(ct); 5351cb0ef41Sopenharmony_ci decrypt.final(); 5361cb0ef41Sopenharmony_ci }, errMessages.state); 5371cb0ef41Sopenharmony_ci } 5381cb0ef41Sopenharmony_ci} 5391cb0ef41Sopenharmony_ci 5401cb0ef41Sopenharmony_ci// Test that setAuthTag does not throw in GCM mode when called after setAAD. 5411cb0ef41Sopenharmony_ci{ 5421cb0ef41Sopenharmony_ci const key = Buffer.from('1ed2233fa2223ef5d7df08546049406c', 'hex'); 5431cb0ef41Sopenharmony_ci const iv = Buffer.from('579d9dfde9cd93d743da1ceaeebb86e4', 'hex'); 5441cb0ef41Sopenharmony_ci const decrypt = crypto.createDecipheriv('aes-128-gcm', key, iv); 5451cb0ef41Sopenharmony_ci decrypt.setAAD(Buffer.from('0123456789', 'hex')); 5461cb0ef41Sopenharmony_ci decrypt.setAuthTag(Buffer.from('1bb9253e250b8069cde97151d7ef32d9', 'hex')); 5471cb0ef41Sopenharmony_ci assert.strictEqual(decrypt.update('807022', 'hex', 'hex'), 'abcdef'); 5481cb0ef41Sopenharmony_ci assert.strictEqual(decrypt.final('hex'), ''); 5491cb0ef41Sopenharmony_ci} 5501cb0ef41Sopenharmony_ci 5511cb0ef41Sopenharmony_ci// Test that an IV length of 11 does not overflow max_message_size_. 5521cb0ef41Sopenharmony_ci{ 5531cb0ef41Sopenharmony_ci const key = 'x'.repeat(16); 5541cb0ef41Sopenharmony_ci const iv = Buffer.from('112233445566778899aabb', 'hex'); 5551cb0ef41Sopenharmony_ci const options = { authTagLength: 8 }; 5561cb0ef41Sopenharmony_ci const encrypt = crypto.createCipheriv('aes-128-ccm', key, iv, options); 5571cb0ef41Sopenharmony_ci encrypt.update('boom'); // Should not throw 'Message exceeds maximum size'. 5581cb0ef41Sopenharmony_ci encrypt.final(); 5591cb0ef41Sopenharmony_ci} 5601cb0ef41Sopenharmony_ci 5611cb0ef41Sopenharmony_ci// Test that the authentication tag can be set at any point before calling 5621cb0ef41Sopenharmony_ci// final() in GCM or OCB mode. 5631cb0ef41Sopenharmony_ci{ 5641cb0ef41Sopenharmony_ci const plain = Buffer.from('Hello world', 'utf8'); 5651cb0ef41Sopenharmony_ci const key = Buffer.from('0123456789abcdef', 'utf8'); 5661cb0ef41Sopenharmony_ci const iv = Buffer.from('0123456789ab', 'utf8'); 5671cb0ef41Sopenharmony_ci 5681cb0ef41Sopenharmony_ci for (const mode of ['gcm', 'ocb']) { 5691cb0ef41Sopenharmony_ci for (const authTagLength of mode === 'gcm' ? [undefined, 8] : [8]) { 5701cb0ef41Sopenharmony_ci const cipher = crypto.createCipheriv(`aes-128-${mode}`, key, iv, { 5711cb0ef41Sopenharmony_ci authTagLength 5721cb0ef41Sopenharmony_ci }); 5731cb0ef41Sopenharmony_ci const ciphertext = Buffer.concat([cipher.update(plain), cipher.final()]); 5741cb0ef41Sopenharmony_ci const authTag = cipher.getAuthTag(); 5751cb0ef41Sopenharmony_ci 5761cb0ef41Sopenharmony_ci for (const authTagBeforeUpdate of [true, false]) { 5771cb0ef41Sopenharmony_ci const decipher = crypto.createDecipheriv(`aes-128-${mode}`, key, iv, { 5781cb0ef41Sopenharmony_ci authTagLength 5791cb0ef41Sopenharmony_ci }); 5801cb0ef41Sopenharmony_ci if (authTagBeforeUpdate) { 5811cb0ef41Sopenharmony_ci decipher.setAuthTag(authTag); 5821cb0ef41Sopenharmony_ci } 5831cb0ef41Sopenharmony_ci const resultUpdate = decipher.update(ciphertext); 5841cb0ef41Sopenharmony_ci if (!authTagBeforeUpdate) { 5851cb0ef41Sopenharmony_ci decipher.setAuthTag(authTag); 5861cb0ef41Sopenharmony_ci } 5871cb0ef41Sopenharmony_ci const resultFinal = decipher.final(); 5881cb0ef41Sopenharmony_ci const result = Buffer.concat([resultUpdate, resultFinal]); 5891cb0ef41Sopenharmony_ci assert(result.equals(plain)); 5901cb0ef41Sopenharmony_ci } 5911cb0ef41Sopenharmony_ci } 5921cb0ef41Sopenharmony_ci } 5931cb0ef41Sopenharmony_ci} 5941cb0ef41Sopenharmony_ci 5951cb0ef41Sopenharmony_ci// Test that setAuthTag can only be called once. 5961cb0ef41Sopenharmony_ci{ 5971cb0ef41Sopenharmony_ci const plain = Buffer.from('Hello world', 'utf8'); 5981cb0ef41Sopenharmony_ci const key = Buffer.from('0123456789abcdef', 'utf8'); 5991cb0ef41Sopenharmony_ci const iv = Buffer.from('0123456789ab', 'utf8'); 6001cb0ef41Sopenharmony_ci const opts = { authTagLength: 8 }; 6011cb0ef41Sopenharmony_ci 6021cb0ef41Sopenharmony_ci for (const mode of ['gcm', 'ccm', 'ocb']) { 6031cb0ef41Sopenharmony_ci const cipher = crypto.createCipheriv(`aes-128-${mode}`, key, iv, opts); 6041cb0ef41Sopenharmony_ci const ciphertext = Buffer.concat([cipher.update(plain), cipher.final()]); 6051cb0ef41Sopenharmony_ci const tag = cipher.getAuthTag(); 6061cb0ef41Sopenharmony_ci 6071cb0ef41Sopenharmony_ci const decipher = crypto.createDecipheriv(`aes-128-${mode}`, key, iv, opts); 6081cb0ef41Sopenharmony_ci decipher.setAuthTag(tag); 6091cb0ef41Sopenharmony_ci assert.throws(() => { 6101cb0ef41Sopenharmony_ci decipher.setAuthTag(tag); 6111cb0ef41Sopenharmony_ci }, errMessages.state); 6121cb0ef41Sopenharmony_ci // Decryption should still work. 6131cb0ef41Sopenharmony_ci const plaintext = Buffer.concat([ 6141cb0ef41Sopenharmony_ci decipher.update(ciphertext), 6151cb0ef41Sopenharmony_ci decipher.final(), 6161cb0ef41Sopenharmony_ci ]); 6171cb0ef41Sopenharmony_ci assert(plain.equals(plaintext)); 6181cb0ef41Sopenharmony_ci } 6191cb0ef41Sopenharmony_ci} 6201cb0ef41Sopenharmony_ci 6211cb0ef41Sopenharmony_ci 6221cb0ef41Sopenharmony_ci// Test chacha20-poly1305 rejects invalid IV lengths of 13, 14, 15, and 16 (a 6231cb0ef41Sopenharmony_ci// length of 17 or greater was already rejected). 6241cb0ef41Sopenharmony_ci// - https://www.openssl.org/news/secadv/20190306.txt 6251cb0ef41Sopenharmony_ci{ 6261cb0ef41Sopenharmony_ci // Valid extracted from TEST_CASES, check that it detects IV tampering. 6271cb0ef41Sopenharmony_ci const valid = { 6281cb0ef41Sopenharmony_ci algo: 'chacha20-poly1305', 6291cb0ef41Sopenharmony_ci key: '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f', 6301cb0ef41Sopenharmony_ci iv: '070000004041424344454647', 6311cb0ef41Sopenharmony_ci plain: '4c616469657320616e642047656e746c656d656e206f662074686520636c6173' + 6321cb0ef41Sopenharmony_ci '73206f66202739393a204966204920636f756c64206f6666657220796f75206f' + 6331cb0ef41Sopenharmony_ci '6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73' + 6341cb0ef41Sopenharmony_ci '637265656e20776f756c642062652069742e', 6351cb0ef41Sopenharmony_ci plainIsHex: true, 6361cb0ef41Sopenharmony_ci aad: '50515253c0c1c2c3c4c5c6c7', 6371cb0ef41Sopenharmony_ci ct: 'd31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5' + 6381cb0ef41Sopenharmony_ci 'a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e06' + 6391cb0ef41Sopenharmony_ci '0b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fa' + 6401cb0ef41Sopenharmony_ci 'b324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d265' + 6411cb0ef41Sopenharmony_ci '86cec64b6116', 6421cb0ef41Sopenharmony_ci tag: '1ae10b594f09e26a7e902ecbd0600691', 6431cb0ef41Sopenharmony_ci tampered: false, 6441cb0ef41Sopenharmony_ci }; 6451cb0ef41Sopenharmony_ci 6461cb0ef41Sopenharmony_ci // Invalid IV lengths should be detected: 6471cb0ef41Sopenharmony_ci // - 12 and below are valid. 6481cb0ef41Sopenharmony_ci // - 13-16 are not detected as invalid by some OpenSSL versions. 6491cb0ef41Sopenharmony_ci check(13); 6501cb0ef41Sopenharmony_ci check(14); 6511cb0ef41Sopenharmony_ci check(15); 6521cb0ef41Sopenharmony_ci check(16); 6531cb0ef41Sopenharmony_ci // - 17 and above were always detected as invalid by OpenSSL. 6541cb0ef41Sopenharmony_ci check(17); 6551cb0ef41Sopenharmony_ci 6561cb0ef41Sopenharmony_ci function check(ivLength) { 6571cb0ef41Sopenharmony_ci const prefix = ivLength - valid.iv.length / 2; 6581cb0ef41Sopenharmony_ci assert.throws(() => crypto.createCipheriv( 6591cb0ef41Sopenharmony_ci valid.algo, 6601cb0ef41Sopenharmony_ci Buffer.from(valid.key, 'hex'), 6611cb0ef41Sopenharmony_ci Buffer.from(H(prefix) + valid.iv, 'hex') 6621cb0ef41Sopenharmony_ci ), errMessages.length, `iv length ${ivLength} was not rejected`); 6631cb0ef41Sopenharmony_ci 6641cb0ef41Sopenharmony_ci function H(length) { return '00'.repeat(length); } 6651cb0ef41Sopenharmony_ci } 6661cb0ef41Sopenharmony_ci} 6671cb0ef41Sopenharmony_ci 6681cb0ef41Sopenharmony_ci{ 6691cb0ef41Sopenharmony_ci // CCM cipher without data should not crash, see https://github.com/nodejs/node/issues/38035. 6701cb0ef41Sopenharmony_ci const algo = 'aes-128-ccm'; 6711cb0ef41Sopenharmony_ci const key = Buffer.alloc(16); 6721cb0ef41Sopenharmony_ci const iv = Buffer.alloc(12); 6731cb0ef41Sopenharmony_ci const opts = { authTagLength: 10 }; 6741cb0ef41Sopenharmony_ci 6751cb0ef41Sopenharmony_ci for (const cipher of [ 6761cb0ef41Sopenharmony_ci crypto.createCipher(algo, 'foo', opts), 6771cb0ef41Sopenharmony_ci crypto.createCipheriv(algo, key, iv, opts), 6781cb0ef41Sopenharmony_ci ]) { 6791cb0ef41Sopenharmony_ci assert.throws(() => { 6801cb0ef41Sopenharmony_ci cipher.final(); 6811cb0ef41Sopenharmony_ci }, common.hasOpenSSL3 ? { 6821cb0ef41Sopenharmony_ci code: 'ERR_OSSL_TAG_NOT_SET' 6831cb0ef41Sopenharmony_ci } : { 6841cb0ef41Sopenharmony_ci message: /Unsupported state/ 6851cb0ef41Sopenharmony_ci }); 6861cb0ef41Sopenharmony_ci } 6871cb0ef41Sopenharmony_ci} 6881cb0ef41Sopenharmony_ci 6891cb0ef41Sopenharmony_ci{ 6901cb0ef41Sopenharmony_ci const key = Buffer.alloc(32); 6911cb0ef41Sopenharmony_ci const iv = Buffer.alloc(12); 6921cb0ef41Sopenharmony_ci 6931cb0ef41Sopenharmony_ci for (const authTagLength of [0, 17]) { 6941cb0ef41Sopenharmony_ci assert.throws(() => { 6951cb0ef41Sopenharmony_ci crypto.createCipheriv('chacha20-poly1305', key, iv, { authTagLength }); 6961cb0ef41Sopenharmony_ci }, { 6971cb0ef41Sopenharmony_ci code: 'ERR_CRYPTO_INVALID_AUTH_TAG', 6981cb0ef41Sopenharmony_ci message: errMessages.authTagLength 6991cb0ef41Sopenharmony_ci }); 7001cb0ef41Sopenharmony_ci } 7011cb0ef41Sopenharmony_ci} 7021cb0ef41Sopenharmony_ci 7031cb0ef41Sopenharmony_ci// ChaCha20-Poly1305 should respect the authTagLength option and should not 7041cb0ef41Sopenharmony_ci// require the authentication tag before calls to update() during decryption. 7051cb0ef41Sopenharmony_ci{ 7061cb0ef41Sopenharmony_ci const key = Buffer.alloc(32); 7071cb0ef41Sopenharmony_ci const iv = Buffer.alloc(12); 7081cb0ef41Sopenharmony_ci 7091cb0ef41Sopenharmony_ci for (let authTagLength = 1; authTagLength <= 16; authTagLength++) { 7101cb0ef41Sopenharmony_ci const cipher = 7111cb0ef41Sopenharmony_ci crypto.createCipheriv('chacha20-poly1305', key, iv, { authTagLength }); 7121cb0ef41Sopenharmony_ci const ciphertext = Buffer.concat([cipher.update('foo'), cipher.final()]); 7131cb0ef41Sopenharmony_ci const authTag = cipher.getAuthTag(); 7141cb0ef41Sopenharmony_ci assert.strictEqual(authTag.length, authTagLength); 7151cb0ef41Sopenharmony_ci 7161cb0ef41Sopenharmony_ci // The decipher operation should reject all authentication tags other than 7171cb0ef41Sopenharmony_ci // that of the expected length. 7181cb0ef41Sopenharmony_ci for (let other = 1; other <= 16; other++) { 7191cb0ef41Sopenharmony_ci const decipher = crypto.createDecipheriv('chacha20-poly1305', key, iv, { 7201cb0ef41Sopenharmony_ci authTagLength: other 7211cb0ef41Sopenharmony_ci }); 7221cb0ef41Sopenharmony_ci // ChaCha20 is a stream cipher so we do not need to call final() to obtain 7231cb0ef41Sopenharmony_ci // the full plaintext. 7241cb0ef41Sopenharmony_ci const plaintext = decipher.update(ciphertext); 7251cb0ef41Sopenharmony_ci assert.strictEqual(plaintext.toString(), 'foo'); 7261cb0ef41Sopenharmony_ci if (other === authTagLength) { 7271cb0ef41Sopenharmony_ci // The authentication tag length is as expected and the tag itself is 7281cb0ef41Sopenharmony_ci // correct, so this should work. 7291cb0ef41Sopenharmony_ci decipher.setAuthTag(authTag); 7301cb0ef41Sopenharmony_ci decipher.final(); 7311cb0ef41Sopenharmony_ci } else { 7321cb0ef41Sopenharmony_ci // The authentication tag that we are going to pass to setAuthTag is 7331cb0ef41Sopenharmony_ci // either too short or too long. If other < authTagLength, the 7341cb0ef41Sopenharmony_ci // authentication tag is still correct, but it should still be rejected 7351cb0ef41Sopenharmony_ci // because its security assurance is lower than expected. 7361cb0ef41Sopenharmony_ci assert.throws(() => { 7371cb0ef41Sopenharmony_ci decipher.setAuthTag(authTag); 7381cb0ef41Sopenharmony_ci }, { 7391cb0ef41Sopenharmony_ci code: 'ERR_CRYPTO_INVALID_AUTH_TAG', 7401cb0ef41Sopenharmony_ci message: `Invalid authentication tag length: ${authTagLength}` 7411cb0ef41Sopenharmony_ci }); 7421cb0ef41Sopenharmony_ci } 7431cb0ef41Sopenharmony_ci } 7441cb0ef41Sopenharmony_ci } 7451cb0ef41Sopenharmony_ci} 7461cb0ef41Sopenharmony_ci 7471cb0ef41Sopenharmony_ci// ChaCha20-Poly1305 should default to an authTagLength of 16. When encrypting, 7481cb0ef41Sopenharmony_ci// this matches the behavior of GCM ciphers. When decrypting, however, it is 7491cb0ef41Sopenharmony_ci// stricter than GCM in that it only allows authentication tags that are exactly 7501cb0ef41Sopenharmony_ci// 16 bytes long, whereas, when no authTagLength was specified, GCM would accept 7511cb0ef41Sopenharmony_ci// shorter tags as long as their length was valid according to NIST SP 800-38D. 7521cb0ef41Sopenharmony_ci// For ChaCha20-Poly1305, we intentionally deviate from that because there are 7531cb0ef41Sopenharmony_ci// no recommended or approved authentication tag lengths below 16 bytes. 7541cb0ef41Sopenharmony_ci{ 7551cb0ef41Sopenharmony_ci const rfcTestCases = TEST_CASES.filter(({ algo, tampered }) => { 7561cb0ef41Sopenharmony_ci return algo === 'chacha20-poly1305' && tampered === false; 7571cb0ef41Sopenharmony_ci }); 7581cb0ef41Sopenharmony_ci assert.strictEqual(rfcTestCases.length, 1); 7591cb0ef41Sopenharmony_ci 7601cb0ef41Sopenharmony_ci const [testCase] = rfcTestCases; 7611cb0ef41Sopenharmony_ci const key = Buffer.from(testCase.key, 'hex'); 7621cb0ef41Sopenharmony_ci const iv = Buffer.from(testCase.iv, 'hex'); 7631cb0ef41Sopenharmony_ci const aad = Buffer.from(testCase.aad, 'hex'); 7641cb0ef41Sopenharmony_ci 7651cb0ef41Sopenharmony_ci for (const opt of [ 7661cb0ef41Sopenharmony_ci undefined, 7671cb0ef41Sopenharmony_ci { authTagLength: undefined }, 7681cb0ef41Sopenharmony_ci { authTagLength: 16 }, 7691cb0ef41Sopenharmony_ci ]) { 7701cb0ef41Sopenharmony_ci const cipher = crypto.createCipheriv('chacha20-poly1305', key, iv, opt); 7711cb0ef41Sopenharmony_ci const ciphertext = Buffer.concat([ 7721cb0ef41Sopenharmony_ci cipher.setAAD(aad).update(testCase.plain, 'hex'), 7731cb0ef41Sopenharmony_ci cipher.final(), 7741cb0ef41Sopenharmony_ci ]); 7751cb0ef41Sopenharmony_ci const authTag = cipher.getAuthTag(); 7761cb0ef41Sopenharmony_ci 7771cb0ef41Sopenharmony_ci assert.strictEqual(ciphertext.toString('hex'), testCase.ct); 7781cb0ef41Sopenharmony_ci assert.strictEqual(authTag.toString('hex'), testCase.tag); 7791cb0ef41Sopenharmony_ci 7801cb0ef41Sopenharmony_ci const decipher = crypto.createDecipheriv('chacha20-poly1305', key, iv, opt); 7811cb0ef41Sopenharmony_ci const plaintext = Buffer.concat([ 7821cb0ef41Sopenharmony_ci decipher.setAAD(aad).update(ciphertext), 7831cb0ef41Sopenharmony_ci decipher.setAuthTag(authTag).final(), 7841cb0ef41Sopenharmony_ci ]); 7851cb0ef41Sopenharmony_ci 7861cb0ef41Sopenharmony_ci assert.strictEqual(plaintext.toString('hex'), testCase.plain); 7871cb0ef41Sopenharmony_ci } 7881cb0ef41Sopenharmony_ci} 7891cb0ef41Sopenharmony_ci 7901cb0ef41Sopenharmony_ci// https://github.com/nodejs/node/issues/45874 7911cb0ef41Sopenharmony_ci{ 7921cb0ef41Sopenharmony_ci const rfcTestCases = TEST_CASES.filter(({ algo, tampered }) => { 7931cb0ef41Sopenharmony_ci return algo === 'chacha20-poly1305' && tampered === false; 7941cb0ef41Sopenharmony_ci }); 7951cb0ef41Sopenharmony_ci assert.strictEqual(rfcTestCases.length, 1); 7961cb0ef41Sopenharmony_ci 7971cb0ef41Sopenharmony_ci const [testCase] = rfcTestCases; 7981cb0ef41Sopenharmony_ci const key = Buffer.from(testCase.key, 'hex'); 7991cb0ef41Sopenharmony_ci const iv = Buffer.from(testCase.iv, 'hex'); 8001cb0ef41Sopenharmony_ci const aad = Buffer.from(testCase.aad, 'hex'); 8011cb0ef41Sopenharmony_ci const opt = { authTagLength: 16 }; 8021cb0ef41Sopenharmony_ci 8031cb0ef41Sopenharmony_ci const cipher = crypto.createCipheriv('chacha20-poly1305', key, iv, opt); 8041cb0ef41Sopenharmony_ci const ciphertext = Buffer.concat([ 8051cb0ef41Sopenharmony_ci cipher.setAAD(aad).update(testCase.plain, 'hex'), 8061cb0ef41Sopenharmony_ci cipher.final(), 8071cb0ef41Sopenharmony_ci ]); 8081cb0ef41Sopenharmony_ci const authTag = cipher.getAuthTag(); 8091cb0ef41Sopenharmony_ci 8101cb0ef41Sopenharmony_ci assert.strictEqual(ciphertext.toString('hex'), testCase.ct); 8111cb0ef41Sopenharmony_ci assert.strictEqual(authTag.toString('hex'), testCase.tag); 8121cb0ef41Sopenharmony_ci 8131cb0ef41Sopenharmony_ci const decipher = crypto.createDecipheriv('chacha20-poly1305', key, iv, opt); 8141cb0ef41Sopenharmony_ci decipher.setAAD(aad).update(ciphertext); 8151cb0ef41Sopenharmony_ci 8161cb0ef41Sopenharmony_ci assert.throws(() => { 8171cb0ef41Sopenharmony_ci decipher.final(); 8181cb0ef41Sopenharmony_ci }, /Unsupported state or unable to authenticate data/); 8191cb0ef41Sopenharmony_ci} 820