1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23const common = require('../common'); 24if (!common.hasCrypto) 25 common.skip('missing crypto'); 26 27const assert = require('assert'); 28const crypto = require('crypto'); 29const fixtures = require('../common/fixtures'); 30 31crypto.DEFAULT_ENCODING = 'buffer'; 32 33// 34// Test authenticated encryption modes. 35// 36// !NEVER USE STATIC IVs IN REAL LIFE! 37// 38 39const TEST_CASES = require(fixtures.path('aead-vectors.js')); 40 41const errMessages = { 42 auth: / auth/, 43 state: / state/, 44 FIPS: /not supported in FIPS mode/, 45 length: /Invalid IV length/, 46 authTagLength: /Invalid authentication tag length/ 47}; 48 49const ciphers = crypto.getCiphers(); 50 51const expectedWarnings = common.hasFipsCrypto ? 52 [] : [ 53 ['Use Cipheriv for counter mode of aes-192-gcm'], 54 ['Use Cipheriv for counter mode of aes-192-ccm'], 55 ['Use Cipheriv for counter mode of aes-192-ccm'], 56 ['Use Cipheriv for counter mode of aes-128-ccm'], 57 ['Use Cipheriv for counter mode of aes-128-ccm'], 58 ['Use Cipheriv for counter mode of aes-128-ccm'], 59 ['Use Cipheriv for counter mode of aes-256-ccm'], 60 ['Use Cipheriv for counter mode of aes-256-ccm'], 61 ['Use Cipheriv for counter mode of aes-256-ccm'], 62 ['Use Cipheriv for counter mode of aes-256-ccm'], 63 ['Use Cipheriv for counter mode of aes-256-ccm'], 64 ['Use Cipheriv for counter mode of aes-256-ccm'], 65 ['Use Cipheriv for counter mode of aes-256-ccm'], 66 ['Use Cipheriv for counter mode of aes-256-ccm'], 67 ['Use Cipheriv for counter mode of aes-256-ccm'], 68 ['Use Cipheriv for counter mode of aes-256-ccm'], 69 ['Use Cipheriv for counter mode of aes-256-ccm'], 70 ['Use Cipheriv for counter mode of aes-256-ccm'], 71 ['Use Cipheriv for counter mode of aes-256-ccm'], 72 ]; 73 74const expectedDeprecationWarnings = [ 75 ['crypto.DEFAULT_ENCODING is deprecated.', 'DEP0091'], 76 ['crypto.createCipher is deprecated.', 'DEP0106'], 77]; 78 79common.expectWarning({ 80 Warning: expectedWarnings, 81 DeprecationWarning: expectedDeprecationWarnings 82}); 83 84for (const test of TEST_CASES) { 85 if (!ciphers.includes(test.algo)) { 86 common.printSkipMessage(`unsupported ${test.algo} test`); 87 continue; 88 } 89 90 if (common.hasFipsCrypto && test.iv.length < 24) { 91 common.printSkipMessage('IV len < 12 bytes unsupported in FIPS mode'); 92 continue; 93 } 94 95 const isCCM = /^aes-(128|192|256)-ccm$/.test(test.algo); 96 const isOCB = /^aes-(128|192|256)-ocb$/.test(test.algo); 97 const isChacha20Poly1305 = test.algo === 'chacha20-poly1305'; 98 99 let options; 100 if (isCCM || isOCB || isChacha20Poly1305) 101 options = { authTagLength: test.tag.length / 2 }; 102 103 const inputEncoding = test.plainIsHex ? 'hex' : 'ascii'; 104 105 let aadOptions; 106 if (isCCM) { 107 aadOptions = { 108 plaintextLength: Buffer.from(test.plain, inputEncoding).length 109 }; 110 } 111 112 { 113 const encrypt = crypto.createCipheriv(test.algo, 114 Buffer.from(test.key, 'hex'), 115 Buffer.from(test.iv, 'hex'), 116 options); 117 118 if (test.aad) 119 encrypt.setAAD(Buffer.from(test.aad, 'hex'), aadOptions); 120 121 let hex = encrypt.update(test.plain, inputEncoding, 'hex'); 122 hex += encrypt.final('hex'); 123 124 const auth_tag = encrypt.getAuthTag(); 125 // Only test basic encryption run if output is marked as tampered. 126 if (!test.tampered) { 127 assert.strictEqual(hex, test.ct); 128 assert.strictEqual(auth_tag.toString('hex'), test.tag); 129 } 130 } 131 132 { 133 if (isCCM && common.hasFipsCrypto) { 134 assert.throws(() => { 135 crypto.createDecipheriv(test.algo, 136 Buffer.from(test.key, 'hex'), 137 Buffer.from(test.iv, 'hex'), 138 options); 139 }, errMessages.FIPS); 140 } else { 141 const decrypt = crypto.createDecipheriv(test.algo, 142 Buffer.from(test.key, 'hex'), 143 Buffer.from(test.iv, 'hex'), 144 options); 145 decrypt.setAuthTag(Buffer.from(test.tag, 'hex')); 146 if (test.aad) 147 decrypt.setAAD(Buffer.from(test.aad, 'hex'), aadOptions); 148 149 const outputEncoding = test.plainIsHex ? 'hex' : 'ascii'; 150 151 let msg = decrypt.update(test.ct, 'hex', outputEncoding); 152 if (!test.tampered) { 153 msg += decrypt.final(outputEncoding); 154 assert.strictEqual(msg, test.plain); 155 } else { 156 // Assert that final throws if input data could not be verified! 157 assert.throws(function() { decrypt.final('hex'); }, errMessages.auth); 158 } 159 } 160 } 161 162 if (test.password) { 163 if (common.hasFipsCrypto) { 164 assert.throws(() => { crypto.createCipher(test.algo, test.password); }, 165 errMessages.FIPS); 166 } else { 167 const encrypt = crypto.createCipher(test.algo, test.password, options); 168 if (test.aad) 169 encrypt.setAAD(Buffer.from(test.aad, 'hex'), aadOptions); 170 let hex = encrypt.update(test.plain, 'ascii', 'hex'); 171 hex += encrypt.final('hex'); 172 const auth_tag = encrypt.getAuthTag(); 173 // Only test basic encryption run if output is marked as tampered. 174 if (!test.tampered) { 175 assert.strictEqual(hex, test.ct); 176 assert.strictEqual(auth_tag.toString('hex'), test.tag); 177 } 178 } 179 } 180 181 if (test.password) { 182 if (common.hasFipsCrypto) { 183 assert.throws(() => { crypto.createDecipher(test.algo, test.password); }, 184 errMessages.FIPS); 185 } else { 186 const decrypt = crypto.createDecipher(test.algo, test.password, options); 187 decrypt.setAuthTag(Buffer.from(test.tag, 'hex')); 188 if (test.aad) 189 decrypt.setAAD(Buffer.from(test.aad, 'hex'), aadOptions); 190 let msg = decrypt.update(test.ct, 'hex', 'ascii'); 191 if (!test.tampered) { 192 msg += decrypt.final('ascii'); 193 assert.strictEqual(msg, test.plain); 194 } else { 195 // Assert that final throws if input data could not be verified! 196 assert.throws(function() { decrypt.final('ascii'); }, errMessages.auth); 197 } 198 } 199 } 200 201 { 202 // Trying to get tag before inputting all data: 203 const encrypt = crypto.createCipheriv(test.algo, 204 Buffer.from(test.key, 'hex'), 205 Buffer.from(test.iv, 'hex'), 206 options); 207 encrypt.update('blah', 'ascii'); 208 assert.throws(function() { encrypt.getAuthTag(); }, errMessages.state); 209 } 210 211 { 212 // Trying to create cipher with incorrect IV length 213 assert.throws(function() { 214 crypto.createCipheriv( 215 test.algo, 216 Buffer.from(test.key, 'hex'), 217 Buffer.alloc(0) 218 ); 219 }, errMessages.length); 220 } 221} 222 223// Non-authenticating mode: 224{ 225 const encrypt = 226 crypto.createCipheriv('aes-128-cbc', 227 'ipxp9a6i1Mb4USb4', 228 '6fKjEjR3Vl30EUYC'); 229 encrypt.update('blah', 'ascii'); 230 encrypt.final(); 231 assert.throws(() => encrypt.getAuthTag(), errMessages.state); 232 assert.throws(() => encrypt.setAAD(Buffer.from('123', 'ascii')), 233 errMessages.state); 234} 235 236// GCM only supports specific authentication tag lengths, invalid lengths should 237// throw. 238{ 239 for (const length of [0, 1, 2, 6, 9, 10, 11, 17]) { 240 assert.throws(() => { 241 const decrypt = crypto.createDecipheriv('aes-128-gcm', 242 'FxLKsqdmv0E9xrQh', 243 'qkuZpJWCewa6Szih'); 244 decrypt.setAuthTag(Buffer.from('1'.repeat(length))); 245 }, { 246 name: 'Error', 247 message: `Invalid authentication tag length: ${length}` 248 }); 249 250 assert.throws(() => { 251 crypto.createCipheriv('aes-256-gcm', 252 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 253 'qkuZpJWCewa6Szih', 254 { 255 authTagLength: length 256 }); 257 }, { 258 name: 'Error', 259 message: `Invalid authentication tag length: ${length}` 260 }); 261 262 assert.throws(() => { 263 crypto.createDecipheriv('aes-256-gcm', 264 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 265 'qkuZpJWCewa6Szih', 266 { 267 authTagLength: length 268 }); 269 }, { 270 name: 'Error', 271 message: `Invalid authentication tag length: ${length}` 272 }); 273 } 274} 275 276// Test that GCM can produce shorter authentication tags than 16 bytes. 277{ 278 const fullTag = '1debb47b2c91ba2cea16fad021703070'; 279 for (const [authTagLength, e] of [[undefined, 16], [12, 12], [4, 4]]) { 280 const cipher = crypto.createCipheriv('aes-256-gcm', 281 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 282 'qkuZpJWCewa6Szih', { 283 authTagLength 284 }); 285 cipher.setAAD(Buffer.from('abcd')); 286 cipher.update('01234567', 'hex'); 287 cipher.final(); 288 const tag = cipher.getAuthTag(); 289 assert.strictEqual(tag.toString('hex'), fullTag.substr(0, 2 * e)); 290 } 291} 292 293// Test that users can manually restrict the GCM tag length to a single value. 294{ 295 const decipher = crypto.createDecipheriv('aes-256-gcm', 296 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 297 'qkuZpJWCewa6Szih', { 298 authTagLength: 8 299 }); 300 301 assert.throws(() => { 302 // This tag would normally be allowed. 303 decipher.setAuthTag(Buffer.from('1'.repeat(12))); 304 }, { 305 name: 'Error', 306 message: 'Invalid authentication tag length: 12' 307 }); 308 309 // The Decipher object should be left intact. 310 decipher.setAuthTag(Buffer.from('445352d3ff85cf94', 'hex')); 311 const text = Buffer.concat([ 312 decipher.update('3a2a3647', 'hex'), 313 decipher.final(), 314 ]); 315 assert.strictEqual(text.toString('utf8'), 'node'); 316} 317 318// Test that create(De|C)ipher(iv)? throws if the mode is CCM and an invalid 319// authentication tag length has been specified. 320{ 321 for (const authTagLength of [-1, true, false, NaN, 5.5]) { 322 assert.throws(() => { 323 crypto.createCipheriv('aes-256-ccm', 324 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 325 'qkuZpJWCewa6S', 326 { 327 authTagLength 328 }); 329 }, { 330 name: 'TypeError', 331 code: 'ERR_INVALID_OPT_VALUE', 332 message: `The value "${authTagLength}" is invalid for option ` + 333 '"authTagLength"' 334 }); 335 336 assert.throws(() => { 337 crypto.createDecipheriv('aes-256-ccm', 338 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 339 'qkuZpJWCewa6S', 340 { 341 authTagLength 342 }); 343 }, { 344 name: 'TypeError', 345 code: 'ERR_INVALID_OPT_VALUE', 346 message: `The value "${authTagLength}" is invalid for option ` + 347 '"authTagLength"' 348 }); 349 350 if (!common.hasFipsCrypto) { 351 assert.throws(() => { 352 crypto.createCipher('aes-256-ccm', 'bad password', { authTagLength }); 353 }, { 354 name: 'TypeError', 355 code: 'ERR_INVALID_OPT_VALUE', 356 message: `The value "${authTagLength}" is invalid for option ` + 357 '"authTagLength"' 358 }); 359 360 assert.throws(() => { 361 crypto.createDecipher('aes-256-ccm', 'bad password', { authTagLength }); 362 }, { 363 name: 'TypeError', 364 code: 'ERR_INVALID_OPT_VALUE', 365 message: `The value "${authTagLength}" is invalid for option ` + 366 '"authTagLength"' 367 }); 368 } 369 } 370 371 // The following values will not be caught by the JS layer and thus will not 372 // use the default error codes. 373 for (const authTagLength of [0, 1, 2, 3, 5, 7, 9, 11, 13, 15, 17, 18]) { 374 assert.throws(() => { 375 crypto.createCipheriv('aes-256-ccm', 376 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 377 'qkuZpJWCewa6S', 378 { 379 authTagLength 380 }); 381 }, errMessages.authTagLength); 382 383 if (!common.hasFipsCrypto) { 384 assert.throws(() => { 385 crypto.createDecipheriv('aes-256-ccm', 386 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 387 'qkuZpJWCewa6S', 388 { 389 authTagLength 390 }); 391 }, errMessages.authTagLength); 392 393 assert.throws(() => { 394 crypto.createCipher('aes-256-ccm', 'bad password', { authTagLength }); 395 }, errMessages.authTagLength); 396 397 assert.throws(() => { 398 crypto.createDecipher('aes-256-ccm', 'bad password', { authTagLength }); 399 }, errMessages.authTagLength); 400 } 401 } 402} 403 404// Test that create(De|C)ipher(iv)? throws if the mode is CCM or OCB and no 405// authentication tag has been specified. 406{ 407 for (const mode of ['ccm', 'ocb']) { 408 assert.throws(() => { 409 crypto.createCipheriv(`aes-256-${mode}`, 410 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 411 'qkuZpJWCewa6S'); 412 }, { 413 message: `authTagLength required for aes-256-${mode}` 414 }); 415 416 // CCM decryption and create(De|C)ipher are unsupported in FIPS mode. 417 if (!common.hasFipsCrypto) { 418 assert.throws(() => { 419 crypto.createDecipheriv(`aes-256-${mode}`, 420 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 421 'qkuZpJWCewa6S'); 422 }, { 423 message: `authTagLength required for aes-256-${mode}` 424 }); 425 426 assert.throws(() => { 427 crypto.createCipher(`aes-256-${mode}`, 'very bad password'); 428 }, { 429 message: `authTagLength required for aes-256-${mode}` 430 }); 431 432 assert.throws(() => { 433 crypto.createDecipher(`aes-256-${mode}`, 'very bad password'); 434 }, { 435 message: `authTagLength required for aes-256-${mode}` 436 }); 437 } 438 } 439} 440 441// Test that setAAD throws if an invalid plaintext length has been specified. 442{ 443 const cipher = crypto.createCipheriv('aes-256-ccm', 444 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 445 'qkuZpJWCewa6S', 446 { 447 authTagLength: 10 448 }); 449 450 for (const plaintextLength of [-1, true, false, NaN, 5.5]) { 451 assert.throws(() => { 452 cipher.setAAD(Buffer.from('0123456789', 'hex'), { plaintextLength }); 453 }, { 454 name: 'TypeError', 455 code: 'ERR_INVALID_OPT_VALUE', 456 message: `The value "${plaintextLength}" is invalid for option ` + 457 '"plaintextLength"' 458 }); 459 } 460} 461 462// Test that setAAD and update throw if the plaintext is too long. 463{ 464 for (const ivLength of [13, 12]) { 465 const maxMessageSize = (1 << (8 * (15 - ivLength))) - 1; 466 const key = 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8'; 467 const cipher = () => crypto.createCipheriv('aes-256-ccm', key, 468 '0'.repeat(ivLength), 469 { 470 authTagLength: 10 471 }); 472 473 assert.throws(() => { 474 cipher().setAAD(Buffer.alloc(0), { 475 plaintextLength: maxMessageSize + 1 476 }); 477 }, /^Error: Message exceeds maximum size$/); 478 479 const msg = Buffer.alloc(maxMessageSize + 1); 480 assert.throws(() => { 481 cipher().update(msg); 482 }, /^Error: Message exceeds maximum size$/); 483 484 const c = cipher(); 485 c.setAAD(Buffer.alloc(0), { 486 plaintextLength: maxMessageSize 487 }); 488 c.update(msg.slice(1)); 489 } 490} 491 492// Test that setAAD throws if the mode is CCM and the plaintext length has not 493// been specified. 494{ 495 assert.throws(() => { 496 const cipher = crypto.createCipheriv('aes-256-ccm', 497 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 498 'qkuZpJWCewa6S', 499 { 500 authTagLength: 10 501 }); 502 cipher.setAAD(Buffer.from('0123456789', 'hex')); 503 }, /^Error: plaintextLength required for CCM mode with AAD$/); 504 505 if (!common.hasFipsCrypto) { 506 assert.throws(() => { 507 const cipher = crypto.createDecipheriv('aes-256-ccm', 508 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', 509 'qkuZpJWCewa6S', 510 { 511 authTagLength: 10 512 }); 513 cipher.setAAD(Buffer.from('0123456789', 'hex')); 514 }, /^Error: plaintextLength required for CCM mode with AAD$/); 515 } 516} 517 518// Test that final() throws in CCM mode when no authentication tag is provided. 519{ 520 if (!common.hasFipsCrypto) { 521 const key = Buffer.from('1ed2233fa2223ef5d7df08546049406c', 'hex'); 522 const iv = Buffer.from('7305220bca40d4c90e1791e9', 'hex'); 523 const ct = Buffer.from('8beba09d4d4d861f957d51c0794f4abf8030848e', 'hex'); 524 const decrypt = crypto.createDecipheriv('aes-128-ccm', key, iv, { 525 authTagLength: 10 526 }); 527 // Normally, we would do this: 528 // decrypt.setAuthTag(Buffer.from('0d9bcd142a94caf3d1dd', 'hex')); 529 assert.throws(() => { 530 decrypt.setAAD(Buffer.from('63616c76696e', 'hex'), { 531 plaintextLength: ct.length 532 }); 533 decrypt.update(ct); 534 decrypt.final(); 535 }, errMessages.state); 536 } 537} 538 539// Test that setAuthTag does not throw in GCM mode when called after setAAD. 540{ 541 const key = Buffer.from('1ed2233fa2223ef5d7df08546049406c', 'hex'); 542 const iv = Buffer.from('579d9dfde9cd93d743da1ceaeebb86e4', 'hex'); 543 const decrypt = crypto.createDecipheriv('aes-128-gcm', key, iv); 544 decrypt.setAAD(Buffer.from('0123456789', 'hex')); 545 decrypt.setAuthTag(Buffer.from('1bb9253e250b8069cde97151d7ef32d9', 'hex')); 546 assert.strictEqual(decrypt.update('807022', 'hex', 'hex'), 'abcdef'); 547 assert.strictEqual(decrypt.final('hex'), ''); 548} 549 550// Test that an IV length of 11 does not overflow max_message_size_. 551{ 552 const key = 'x'.repeat(16); 553 const iv = Buffer.from('112233445566778899aabb', 'hex'); 554 const options = { authTagLength: 8 }; 555 const encrypt = crypto.createCipheriv('aes-128-ccm', key, iv, options); 556 encrypt.update('boom'); // Should not throw 'Message exceeds maximum size'. 557 encrypt.final(); 558} 559 560// Test that the authentication tag can be set at any point before calling 561// final() in GCM or OCB mode. 562{ 563 const plain = Buffer.from('Hello world', 'utf8'); 564 const key = Buffer.from('0123456789abcdef', 'utf8'); 565 const iv = Buffer.from('0123456789ab', 'utf8'); 566 567 for (const mode of ['gcm', 'ocb']) { 568 for (const authTagLength of mode === 'gcm' ? [undefined, 8] : [8]) { 569 const cipher = crypto.createCipheriv(`aes-128-${mode}`, key, iv, { 570 authTagLength 571 }); 572 const ciphertext = Buffer.concat([cipher.update(plain), cipher.final()]); 573 const authTag = cipher.getAuthTag(); 574 575 for (const authTagBeforeUpdate of [true, false]) { 576 const decipher = crypto.createDecipheriv(`aes-128-${mode}`, key, iv, { 577 authTagLength 578 }); 579 if (authTagBeforeUpdate) { 580 decipher.setAuthTag(authTag); 581 } 582 const resultUpdate = decipher.update(ciphertext); 583 if (!authTagBeforeUpdate) { 584 decipher.setAuthTag(authTag); 585 } 586 const resultFinal = decipher.final(); 587 const result = Buffer.concat([resultUpdate, resultFinal]); 588 assert(result.equals(plain)); 589 } 590 } 591 } 592} 593 594// Test that setAuthTag can only be called once. 595{ 596 const plain = Buffer.from('Hello world', 'utf8'); 597 const key = Buffer.from('0123456789abcdef', 'utf8'); 598 const iv = Buffer.from('0123456789ab', 'utf8'); 599 const opts = { authTagLength: 8 }; 600 601 for (const mode of ['gcm', 'ccm', 'ocb']) { 602 const cipher = crypto.createCipheriv(`aes-128-${mode}`, key, iv, opts); 603 const ciphertext = Buffer.concat([cipher.update(plain), cipher.final()]); 604 const tag = cipher.getAuthTag(); 605 606 const decipher = crypto.createDecipheriv(`aes-128-${mode}`, key, iv, opts); 607 decipher.setAuthTag(tag); 608 assert.throws(() => { 609 decipher.setAuthTag(tag); 610 }, errMessages.state); 611 // Decryption should still work. 612 const plaintext = Buffer.concat([ 613 decipher.update(ciphertext), 614 decipher.final(), 615 ]); 616 assert(plain.equals(plaintext)); 617 } 618} 619 620 621// Test chacha20-poly1305 rejects invalid IV lengths of 13, 14, 15, and 16 (a 622// length of 17 or greater was already rejected). 623// - https://www.openssl.org/news/secadv/20190306.txt 624{ 625 // Valid extracted from TEST_CASES, check that it detects IV tampering. 626 const valid = { 627 algo: 'chacha20-poly1305', 628 key: '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f', 629 iv: '070000004041424344454647', 630 plain: '4c616469657320616e642047656e746c656d656e206f662074686520636c6173' + 631 '73206f66202739393a204966204920636f756c64206f6666657220796f75206f' + 632 '6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73' + 633 '637265656e20776f756c642062652069742e', 634 plainIsHex: true, 635 aad: '50515253c0c1c2c3c4c5c6c7', 636 ct: 'd31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5' + 637 'a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e06' + 638 '0b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fa' + 639 'b324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d265' + 640 '86cec64b6116', 641 tag: '1ae10b594f09e26a7e902ecbd0600691', 642 tampered: false, 643 }; 644 645 // Invalid IV lengths should be detected: 646 // - 12 and below are valid. 647 // - 13-16 are not detected as invalid by some OpenSSL versions. 648 check(13); 649 check(14); 650 check(15); 651 check(16); 652 // - 17 and above were always detected as invalid by OpenSSL. 653 check(17); 654 655 function check(ivLength) { 656 const prefix = ivLength - valid.iv.length / 2; 657 assert.throws(() => crypto.createCipheriv( 658 valid.algo, 659 Buffer.from(valid.key, 'hex'), 660 Buffer.from(H(prefix) + valid.iv, 'hex'), 661 { authTagLength: valid.tag.length / 2 } 662 ), errMessages.length, `iv length ${ivLength} was not rejected`); 663 664 function H(length) { return '00'.repeat(length); } 665 } 666} 667