import o from "ospec" ; import type { Hex } from "@tutao/tutanota-utils" ; import { concat , hexToUint8Array , stringToUtf8Uint8Array , uint8ArrayToHex , utf8Uint8ArrayToString } from "@tutao/tutanota-utils" ; import { aes128Decrypt , aes128Encrypt , Aes128Key , aes128RandomKey , aes256Decrypt , aes256Encrypt , Aes256Key , aes256RandomKey , IV_BYTE_LENGTH , } from "../lib/encryption/Aes.js" ; import { base64ToKey , bitArrayToUint8Array , keyToBase64 , uint8ArrayToBitArray } from "../lib/misc/Utils.js" ; import { CryptoError } from "../lib/misc/CryptoError.js" ; import { random } from "../lib/random/Randomizer.js" ; o . spec ( "aes" , function ( ) { o ( "encryption roundtrip 128 without mac" , ( ) => arrayRoundtrip ( aes128Encrypt , aes128Decrypt , aes128RandomKey ( ) , false ) ) ; o ( "encryption roundtrip 128 with mac" , ( ) => arrayRoundtrip ( aes128Encrypt , aes128Decrypt , aes128RandomKey ( ) , true ) ) ; o ( "encryption roundtrip 256 without mac" , ( ) => arrayRoundtrip ( aes256Encrypt , aes256Decrypt , aes256RandomKey ( ) , false ) ) ; o ( "encrypted roundtrip 256 with mac" , ( ) => arrayRoundtrip ( aes256Encrypt , aes256Decrypt , aes256RandomKey ( ) , true ) ) ; async function arrayRoundtrip ( encrypt , decrypt , key , useMac ) { function runArrayRoundtrip ( key , plainText ) { let encrypted = encrypt ( key , plainText , random . generateRandomData ( IV_BYTE_LENGTH ) , true , useMac ) ; return Promise . resolve ( encrypted ) . then ( encrypted => { return ( decrypt as any ) ( key , encrypted , true , useMac ) ; } ) . then ( decrypted => { o ( Array . from ( decrypted ) ) . deepEquals ( Array . from ( plainText ) ) ; } ) ; } await runArrayRoundtrip ( key , random . generateRandomData ( 0 ) ) ; await runArrayRoundtrip ( key , random . generateRandomData ( 1 ) ) ; await runArrayRoundtrip ( key , random . generateRandomData ( 15 ) ) ; await runArrayRoundtrip ( key , random . generateRandomData ( 16 ) ) ; await runArrayRoundtrip ( key , random . generateRandomData ( 17 ) ) ; await runArrayRoundtrip ( key , random . generateRandomData ( 12345 ) ) ; } o ( "generateRandomKeyAndBase64Conversion 128" , ( ) => randomKeyBase64Conversion ( aes128RandomKey , 24 ) ) ; o ( "generateRandomKeyAndBase64Conversion 256" , ( ) => randomKeyBase64Conversion ( aes256RandomKey , 44 ) ) ; function randomKeyBase64Conversion ( randomKey , length ) { let key1Base64 = keyToBase64 ( randomKey ( ) ) ; let key2Base64 = keyToBase64 ( randomKey ( ) ) ; let key3Base64 = keyToBase64 ( randomKey ( ) ) ; o ( key1Base64 ) . notEquals ( key2Base64 ) ; o ( key1Base64 ) . notEquals ( key3Base64 ) ; o ( key1Base64 . length ) . equals ( length ) ; o ( key2Base64 . length ) . equals ( length ) ; o ( key3Base64 . length ) . equals ( length ) ; o ( keyToBase64 ( base64ToKey ( key1Base64 ) ) ) . equals ( key1Base64 ) ; o ( keyToBase64 ( base64ToKey ( key2Base64 ) ) ) . equals ( key2Base64 ) ; o ( keyToBase64 ( base64ToKey ( key3Base64 ) ) ) . equals ( key3Base64 ) ; } function _hexToKey ( hex ) { return uint8ArrayToBitArray ( hexToUint8Array ( hex ) ) ; } function _keyToHex ( key ) { return uint8ArrayToHex ( bitArrayToUint8Array ( key ) ) ; } o ( "encryptWithInvalidKey 128 without mac" , done => encryptWithInvalidKey ( done , aes128Encrypt , false ) ) ; o ( "encryptWithInvalidKey 128 with mac" , done => encryptWithInvalidKey ( done , aes128Encrypt , true ) ) ; o ( "encryptWithInvalidKey 256 without mac" , done => encryptWithInvalidKey ( done , aes256Encrypt , false ) ) ; function encryptWithInvalidKey ( done , encrypt , useMac ) { let key = _hexToKey ( "7878787878" ) ; try { encrypt ( key , stringToUtf8Uint8Array ( "hello" ) , random . generateRandomData ( IV_BYTE_LENGTH ) , true , useMac ) ; } catch ( e ) { const error = e as Error ; o ( error instanceof CryptoError ) . equals ( true ) ; o ( error . message . startsWith ( "Illegal key length" ) ) . equals ( true ) ; done ( ) ; } } o ( "decryptWithInvalidKey 128" , done => decryptWithInvalidKey ( done , aes128Decrypt ) ) ; o ( "decryptWithInvalidKey 256 without hmac" , done => decryptWithInvalidKey ( done , aes256Decrypt ) ) ; function decryptWithInvalidKey ( done , decrypt ) { let key = _hexToKey ( "7878787878" ) ; try { ; ( decrypt as any ) ( key , stringToUtf8Uint8Array ( "hello" ) , true , false ) ; } catch ( e ) { const error = e as Error ; o ( error instanceof CryptoError ) . equals ( true ) ; o ( error . message . startsWith ( "Illegal key length" ) ) . equals ( true ) ; done ( ) ; } } o ( "decryptInvalidData 128" , done => decryptInvalidData ( done , aes128RandomKey ( ) , aes128Decrypt , "Invalid IV length in AES128Decrypt: 10 bytes, must be 16 bytes (128 bits)" ) ) ; o ( "decryptInvalidData 256 without hmac" , done => decryptInvalidData ( done , aes256RandomKey ( ) , aes256Decrypt , "Invalid IV length in AES256Decrypt: 10 bytes, must be 16 bytes (128 bits)" ) ) ; function decryptInvalidData ( done , key , decrypt , errorMessage ) { try { ; ( decrypt as any ) ( key , new Uint8Array ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 ] ) , true , false ) ; } catch ( e ) { const error = e as Error ; o ( error instanceof CryptoError ) . equals ( true ) ; o ( error . message ) . equals ( errorMessage ) ; done ( ) ; } } o ( "decryptManipulatedData 128 without mac" , function ( ) { let key = [ 151050668 , 1341212767 , 316219065 , 2150939763 ] ; let iv = new Uint8Array ( [ 233 , 159 , 225 , 105 , 170 , 223 , 70 , 218 , 139 , 107 , 71 , 91 , 179 , 231 , 239 , 102 ] ) ; let encrypted = aes128Encrypt ( key , stringToUtf8Uint8Array ( "hello" ) , iv , true , false ) ; encrypted [ 0 ] = encrypted [ 0 ] + 1 ; let decrypted = aes128Decrypt ( key , encrypted , true ) ; o ( utf8Uint8ArrayToString ( decrypted ) ) . equals ( "kello" ) ; } ) ; o ( "decryptManipulatedData 128 with mac" , function ( ) { let key = [ 151050668 , 1341212767 , 316219065 , 2150939763 ] ; let iv = new Uint8Array ( [ 233 , 159 , 225 , 105 , 170 , 223 , 70 , 218 , 139 , 107 , 71 , 91 , 179 , 231 , 239 , 102 ] ) ; let encrypted = aes128Encrypt ( key , stringToUtf8Uint8Array ( "hello" ) , iv , true , true ) ; encrypted [ 1 ] = encrypted [ 1 ] + 1 ; try { aes128Decrypt ( key , encrypted , true ) ; } catch ( e ) { const error = e as Error ; o ( error instanceof CryptoError ) . equals ( true ) ; o ( error . message ) . equals ( "invalid mac" ) ; } } ) ; o ( "decryptManipulatedMac 128 with mac" , function ( ) { let key = [ 151050668 , 1341212767 , 316219065 , 2150939763 ] ; let iv = new Uint8Array ( [ 233 , 159 , 225 , 105 , 170 , 223 , 70 , 218 , 139 , 107 , 71 , 91 , 179 , 231 , 239 , 102 ] ) ; let encrypted = aes128Encrypt ( key , stringToUtf8Uint8Array ( "hello" ) , iv , true , true ) ; encrypted [ encrypted . length - 1 ] = encrypted [ encrypted . length - 1 ] + 1 ; try { aes128Decrypt ( key , encrypted , true ) ; } catch ( e ) { const error = e as Error ; o ( error instanceof CryptoError ) . equals ( true ) ; o ( error . message ) . equals ( "invalid mac" ) ; } } ) ; o ( "decryptMissingMac 128" , function ( ) { let key = [ 151050668 , 1341212767 , 316219065 , 2150939763 ] ; let iv = new Uint8Array ( [ 233 , 159 , 225 , 105 , 170 , 223 , 70 , 218 , 139 , 107 , 71 , 91 , 179 , 231 , 239 , 102 ] ) ; let encrypted = aes128Encrypt ( key , stringToUtf8Uint8Array ( "hello" ) , iv , true , false ) ; encrypted = concat ( new Uint8Array ( [ 1 ] ) , encrypted ) ; try { aes128Decrypt ( key , encrypted , true ) ; } catch ( e ) { const error = e as Error ; o ( error instanceof CryptoError ) . equals ( true ) ; o ( error . message ) . equals ( "invalid mac" ) ; } } ) ; o ( "decryptWithWrongKey 128 without mac" , done => decryptWithWrongKey ( done , aes128RandomKey ( ) , aes128RandomKey ( ) , aes128Encrypt , aes128Decrypt , false , "aes decryption failed> pkcs#5 padding corrupt" ) ) ; o ( "decryptWithWrongKey 128 with mac" , done => decryptWithWrongKey ( done , aes128RandomKey ( ) , aes128RandomKey ( ) , aes128Encrypt , aes128Decrypt , true , "invalid mac" ) ) ; o ( "decryptWithWrongKey 256 without mac" , done => decryptWithWrongKey ( done , aes256RandomKey ( ) , aes256RandomKey ( ) , aes256Encrypt , aes256Decrypt , false , "aes decryption failed> pkcs#5 padding corrupt" ) ) ; function decryptWithWrongKey ( done , key , key2 , encrypt , decrypt , useMac , errorMessage ) { try { let encrypted = encrypt ( key , stringToUtf8Uint8Array ( "hello" ) , random . generateRandomData ( IV_BYTE_LENGTH ) , true , useMac ) ; ( decrypt as any ) ( key2 , encrypted , true , useMac ) ; } catch ( e ) { const error = e as Error ; o ( error instanceof CryptoError ) . equals ( true ) ; o ( error . message ) . equals ( errorMessage ) ; done ( ) ; } } o ( "ciphertextLengths 128 without mac" , ( ) => ciphertextLengths ( aes128RandomKey ( ) , aes128Encrypt , 32 , 48 , false ) ) ; o ( "ciphertextLengths 128 with mac" , ( ) => ciphertextLengths ( aes128RandomKey ( ) , aes128Encrypt , 65 , 81 , true ) ) ; o ( "ciphertextLengths 256 without mac" , ( ) => ciphertextLengths ( aes256RandomKey ( ) , aes256Encrypt , 32 , 48 , false ) ) ; function ciphertextLengths ( key , encrypt , length15BytePlainText , length16BytePlainText , useMac ) { o ( encrypt ( key , stringToUtf8Uint8Array ( "1234567890abcde" ) , random . generateRandomData ( IV_BYTE_LENGTH ) , true , useMac ) . length ) . equals ( length15BytePlainText ) ; o ( encrypt ( key , stringToUtf8Uint8Array ( "1234567890abcdef" ) , random . generateRandomData ( IV_BYTE_LENGTH ) , true , useMac ) . length ) . equals ( length16BytePlainText ) ; } } ) ;