tutanota/packages/tutanota-crypto/lib/encryption/Aes.ts

237 lines
9.3 KiB
TypeScript
Raw Normal View History

2021-12-27 17:48:21 +01:00
import sjcl from "../internal/sjcl.js"
2022-12-27 15:37:40 +01:00
import { random } from "../random/Randomizer.js"
import { BitArray, bitArrayToUint8Array, uint8ArrayToBitArray } from "../misc/Utils.js"
import { assertNotNull, Base64, base64ToUint8Array, concat, uint8ArrayToBase64 } from "@tutao/tutanota-utils"
2022-12-27 15:37:40 +01:00
import { sha256Hash } from "../hashes/Sha256.js"
import { CryptoError } from "../misc/CryptoError.js"
import { sha512Hash } from "../hashes/Sha512.js"
2025-01-08 16:34:30 +01:00
import { hmacSha256, MacTag, verifyHmacSha256 } from "./Hmac.js"
2017-08-15 13:54:22 +02:00
2017-10-09 17:00:46 +02:00
export const ENABLE_MAC = true
2017-08-15 13:54:22 +02:00
export const IV_BYTE_LENGTH = 16
export const KEY_LENGTH_BYTES_AES_256 = 32
export const KEY_LENGTH_BITS_AES_256 = KEY_LENGTH_BYTES_AES_256 * 8
export const KEY_LENGTH_BYTES_AES_128 = 16
2017-08-15 13:54:22 +02:00
const KEY_LENGTH_BITS_AES_128 = KEY_LENGTH_BYTES_AES_128 * 8
export const MAC_ENABLED_PREFIX = 1
2017-12-12 14:54:35 +01:00
const MAC_LENGTH_BYTES = 32
2017-08-15 13:54:22 +02:00
export type Aes256Key = BitArray
export type Aes128Key = BitArray
Support group key rotation (#6588) * Allow groups to have multiple key versions tutadb#1628 * Adapt to model changes * Fix CommonMailUtilsTest * Remove symEncBucketKey from SecureExternalRecipientKeyData * Remove deprecated types Also fix tests that relied on them as dummy types * Add userKeyVersion to RecoverCode * Remove clientKey Seems to be unused. * Remove CreateFolderService Unused. * Remove symEncSessionKey from DraftCreateData Unused. * Remove symEncShareBucketKey from MailBox Unused. * Add userKeyVersion to TutanotaProperties * Remove PasswordRetrievalService type The service itself had been long gone. * Remove userKeyVersion from CustomerAccountCreateData CreateMailGroupData * Fix customer account creation Set the key version that we actually need there: the *system* admin pub key version. The sender key version is not needed, because the system admin only has RSA keys. Also, this is a new customer, so that would be version zero anyway. * Fix resolving bucket key with group reference Get the right versions along the way. * Use current group key when encrypting instance session keys * Remove left-over key getting Also document a couple of current key usages * Pass group key providers to EntityClient instead of group key * Fix types and do not provide sender key version for rsa Fix resolveServiceSessionKey * Rename constant to avoid confusion There is another constant with the same name. * Use TutanotaModelV69 * Introduce client side mechanism to handle key rotation requests see tutadb 1771 * Do not export 128-bit key generator It is only needed for tests within the package. * Remove group key version when creating user area groups Plus some minor clarity improvements. * Fix version handling when updating drafts and sending to secure external * Remove versions when creating external users They are zero. * Fix changing the admin flag * Remove (almost) all local admin related code * Improve readability * Default to user key version zero when loading entropy * Decrypt current groupKey with correct userGroupKey version * Fix system application offline migrations * Fix tutanota application offline migrations * Improve offline migration functions * Use AesKey type * Minor improvements from review * Use AesKey type instead of Aes128Key where possible * Model update after rebase * Fix getting user group key Should never try to get from the cache like a normal group key. * Fix getting former group key Start ID was off-by-one. * Minor changes from review. We just checked all usages of all public methods of KeyLoaderFacade to make sure we're using the correct versions where we need them. * More minor changes from review. * Pass ownerKeyProvider instead of ownerKey when updating with the EntityClient * Pass ownerKeyProvider only when necessary * Document ownerKeyProvider parameter * Fix offline database migration * Fix unlocking the indexer data --------- Co-authored-by: vaf <vaf@tutao.de> Co-authored-by: bedhub <bedhub@users.noreply.github.com> Co-authored-by: bed <bed@tutao.de>
2024-04-17 10:34:33 +02:00
export type AesKey = Aes128Key | Aes256Key
/**
* @return the key length in bytes
*/
Support group key rotation (#6588) * Allow groups to have multiple key versions tutadb#1628 * Adapt to model changes * Fix CommonMailUtilsTest * Remove symEncBucketKey from SecureExternalRecipientKeyData * Remove deprecated types Also fix tests that relied on them as dummy types * Add userKeyVersion to RecoverCode * Remove clientKey Seems to be unused. * Remove CreateFolderService Unused. * Remove symEncSessionKey from DraftCreateData Unused. * Remove symEncShareBucketKey from MailBox Unused. * Add userKeyVersion to TutanotaProperties * Remove PasswordRetrievalService type The service itself had been long gone. * Remove userKeyVersion from CustomerAccountCreateData CreateMailGroupData * Fix customer account creation Set the key version that we actually need there: the *system* admin pub key version. The sender key version is not needed, because the system admin only has RSA keys. Also, this is a new customer, so that would be version zero anyway. * Fix resolving bucket key with group reference Get the right versions along the way. * Use current group key when encrypting instance session keys * Remove left-over key getting Also document a couple of current key usages * Pass group key providers to EntityClient instead of group key * Fix types and do not provide sender key version for rsa Fix resolveServiceSessionKey * Rename constant to avoid confusion There is another constant with the same name. * Use TutanotaModelV69 * Introduce client side mechanism to handle key rotation requests see tutadb 1771 * Do not export 128-bit key generator It is only needed for tests within the package. * Remove group key version when creating user area groups Plus some minor clarity improvements. * Fix version handling when updating drafts and sending to secure external * Remove versions when creating external users They are zero. * Fix changing the admin flag * Remove (almost) all local admin related code * Improve readability * Default to user key version zero when loading entropy * Decrypt current groupKey with correct userGroupKey version * Fix system application offline migrations * Fix tutanota application offline migrations * Improve offline migration functions * Use AesKey type * Minor improvements from review * Use AesKey type instead of Aes128Key where possible * Model update after rebase * Fix getting user group key Should never try to get from the cache like a normal group key. * Fix getting former group key Start ID was off-by-one. * Minor changes from review. We just checked all usages of all public methods of KeyLoaderFacade to make sure we're using the correct versions where we need them. * More minor changes from review. * Pass ownerKeyProvider instead of ownerKey when updating with the EntityClient * Pass ownerKeyProvider only when necessary * Document ownerKeyProvider parameter * Fix offline database migration * Fix unlocking the indexer data --------- Co-authored-by: vaf <vaf@tutao.de> Co-authored-by: bedhub <bedhub@users.noreply.github.com> Co-authored-by: bed <bed@tutao.de>
2024-04-17 10:34:33 +02:00
export function getKeyLengthBytes(key: AesKey): number {
// stored as an array of 32-bit (4 byte) integers
return key.length * 4
}
2021-12-27 17:48:21 +01:00
2017-08-15 13:54:22 +02:00
export function aes256RandomKey(): Aes256Key {
return uint8ArrayToBitArray(random.generateRandomData(KEY_LENGTH_BYTES_AES_256))
}
2025-12-05 09:35:58 +01:00
export function generateIV(): Uint8Array<ArrayBuffer> {
return random.generateRandomData(IV_BYTE_LENGTH)
}
2017-08-15 13:54:22 +02:00
/**
* Encrypts bytes with AES128 or AES256 in CBC mode.
2017-08-15 13:54:22 +02:00
* @param key The key to use for the encryption.
* @param bytes The plain text.
* @param iv The initialization vector.
2017-08-15 13:54:22 +02:00
* @param usePadding If true, padding is used, otherwise no padding is used and the encrypted data must have the key size.
* @param useMac If true, use HMAC (note that this is required for AES-256)
* @return The encrypted bytes
2017-08-15 13:54:22 +02:00
*/
Support group key rotation (#6588) * Allow groups to have multiple key versions tutadb#1628 * Adapt to model changes * Fix CommonMailUtilsTest * Remove symEncBucketKey from SecureExternalRecipientKeyData * Remove deprecated types Also fix tests that relied on them as dummy types * Add userKeyVersion to RecoverCode * Remove clientKey Seems to be unused. * Remove CreateFolderService Unused. * Remove symEncSessionKey from DraftCreateData Unused. * Remove symEncShareBucketKey from MailBox Unused. * Add userKeyVersion to TutanotaProperties * Remove PasswordRetrievalService type The service itself had been long gone. * Remove userKeyVersion from CustomerAccountCreateData CreateMailGroupData * Fix customer account creation Set the key version that we actually need there: the *system* admin pub key version. The sender key version is not needed, because the system admin only has RSA keys. Also, this is a new customer, so that would be version zero anyway. * Fix resolving bucket key with group reference Get the right versions along the way. * Use current group key when encrypting instance session keys * Remove left-over key getting Also document a couple of current key usages * Pass group key providers to EntityClient instead of group key * Fix types and do not provide sender key version for rsa Fix resolveServiceSessionKey * Rename constant to avoid confusion There is another constant with the same name. * Use TutanotaModelV69 * Introduce client side mechanism to handle key rotation requests see tutadb 1771 * Do not export 128-bit key generator It is only needed for tests within the package. * Remove group key version when creating user area groups Plus some minor clarity improvements. * Fix version handling when updating drafts and sending to secure external * Remove versions when creating external users They are zero. * Fix changing the admin flag * Remove (almost) all local admin related code * Improve readability * Default to user key version zero when loading entropy * Decrypt current groupKey with correct userGroupKey version * Fix system application offline migrations * Fix tutanota application offline migrations * Improve offline migration functions * Use AesKey type * Minor improvements from review * Use AesKey type instead of Aes128Key where possible * Model update after rebase * Fix getting user group key Should never try to get from the cache like a normal group key. * Fix getting former group key Start ID was off-by-one. * Minor changes from review. We just checked all usages of all public methods of KeyLoaderFacade to make sure we're using the correct versions where we need them. * More minor changes from review. * Pass ownerKeyProvider instead of ownerKey when updating with the EntityClient * Pass ownerKeyProvider only when necessary * Document ownerKeyProvider parameter * Fix offline database migration * Fix unlocking the indexer data --------- Co-authored-by: vaf <vaf@tutao.de> Co-authored-by: bedhub <bedhub@users.noreply.github.com> Co-authored-by: bed <bed@tutao.de>
2024-04-17 10:34:33 +02:00
export function aesEncrypt(key: AesKey, bytes: Uint8Array, iv: Uint8Array = generateIV(), usePadding: boolean = true, useMac: boolean = true) {
verifyKeySize(key, [KEY_LENGTH_BITS_AES_128, KEY_LENGTH_BITS_AES_256])
2021-12-27 17:48:21 +01:00
2017-08-15 13:54:22 +02:00
if (iv.length !== IV_BYTE_LENGTH) {
2022-12-27 15:37:40 +01:00
throw new CryptoError(`Illegal IV length: ${iv.length} (expected: ${IV_BYTE_LENGTH}): ${uint8ArrayToBase64(iv)} `)
2017-08-15 13:54:22 +02:00
}
2017-12-12 14:54:35 +01:00
if (!useMac && getKeyLengthBytes(key) === KEY_LENGTH_BYTES_AES_256) {
throw new CryptoError(`Can't use AES-256 without MAC`)
}
let subKeys = getAesSubKeys(key, useMac)
2022-12-27 15:37:40 +01:00
let encryptedBits = sjcl.mode.cbc.encrypt(new sjcl.cipher.aes(subKeys.cKey), uint8ArrayToBitArray(bytes), uint8ArrayToBitArray(iv), [], usePadding)
2017-12-12 14:54:35 +01:00
let data = concat(iv, bitArrayToUint8Array(encryptedBits))
if (useMac) {
2025-01-08 16:34:30 +01:00
const macBytes = hmacSha256(assertNotNull(subKeys.mKey), data)
data = concat(new Uint8Array([MAC_ENABLED_PREFIX]), data, macBytes)
}
return data
}
/**
* Encrypts bytes with AES 256 in CBC mode without mac. This is legacy code and should be removed once the index has been migrated.
* @param key The key to use for the encryption.
* @param bytes The plain text.
* @param iv The initialization vector (only to be passed for testing).
* @param usePadding If true, padding is used, otherwise no padding is used and the encrypted data must have the key size.
* @return The encrypted text as words (sjcl internal structure)..
*/
2025-12-05 09:35:58 +01:00
export function aes256EncryptSearchIndexEntry(
key: Aes256Key,
bytes: Uint8Array<ArrayBuffer>,
iv: Uint8Array<ArrayBuffer> = generateIV(),
usePadding: boolean = true,
): Uint8Array {
verifyKeySize(key, [KEY_LENGTH_BITS_AES_256])
if (iv.length !== IV_BYTE_LENGTH) {
throw new CryptoError(`Illegal IV length: ${iv.length} (expected: ${IV_BYTE_LENGTH}): ${uint8ArrayToBase64(iv)} `)
2017-12-12 14:54:35 +01:00
}
2021-12-27 17:48:21 +01:00
let subKeys = getAesSubKeys(key, false)
let encryptedBits = sjcl.mode.cbc.encrypt(new sjcl.cipher.aes(subKeys.cKey), uint8ArrayToBitArray(bytes), uint8ArrayToBitArray(iv), [], usePadding)
let data = concat(iv, bitArrayToUint8Array(encryptedBits))
2017-12-12 14:54:35 +01:00
return data
2017-08-15 13:54:22 +02:00
}
/**
* Decrypts the given words with AES-128/256 in CBC mode (with HMAC-SHA-256 as mac). The mac is enforced for AES-256 but optional for AES-128.
2017-08-15 13:54:22 +02:00
* @param key The key to use for the decryption.
* @param encryptedBytes The ciphertext encoded as bytes.
2017-08-15 13:54:22 +02:00
* @param usePadding If true, padding is used, otherwise no padding is used and the encrypted data must have the key size.
* @return The decrypted bytes.
*/
Support group key rotation (#6588) * Allow groups to have multiple key versions tutadb#1628 * Adapt to model changes * Fix CommonMailUtilsTest * Remove symEncBucketKey from SecureExternalRecipientKeyData * Remove deprecated types Also fix tests that relied on them as dummy types * Add userKeyVersion to RecoverCode * Remove clientKey Seems to be unused. * Remove CreateFolderService Unused. * Remove symEncSessionKey from DraftCreateData Unused. * Remove symEncShareBucketKey from MailBox Unused. * Add userKeyVersion to TutanotaProperties * Remove PasswordRetrievalService type The service itself had been long gone. * Remove userKeyVersion from CustomerAccountCreateData CreateMailGroupData * Fix customer account creation Set the key version that we actually need there: the *system* admin pub key version. The sender key version is not needed, because the system admin only has RSA keys. Also, this is a new customer, so that would be version zero anyway. * Fix resolving bucket key with group reference Get the right versions along the way. * Use current group key when encrypting instance session keys * Remove left-over key getting Also document a couple of current key usages * Pass group key providers to EntityClient instead of group key * Fix types and do not provide sender key version for rsa Fix resolveServiceSessionKey * Rename constant to avoid confusion There is another constant with the same name. * Use TutanotaModelV69 * Introduce client side mechanism to handle key rotation requests see tutadb 1771 * Do not export 128-bit key generator It is only needed for tests within the package. * Remove group key version when creating user area groups Plus some minor clarity improvements. * Fix version handling when updating drafts and sending to secure external * Remove versions when creating external users They are zero. * Fix changing the admin flag * Remove (almost) all local admin related code * Improve readability * Default to user key version zero when loading entropy * Decrypt current groupKey with correct userGroupKey version * Fix system application offline migrations * Fix tutanota application offline migrations * Improve offline migration functions * Use AesKey type * Minor improvements from review * Use AesKey type instead of Aes128Key where possible * Model update after rebase * Fix getting user group key Should never try to get from the cache like a normal group key. * Fix getting former group key Start ID was off-by-one. * Minor changes from review. We just checked all usages of all public methods of KeyLoaderFacade to make sure we're using the correct versions where we need them. * More minor changes from review. * Pass ownerKeyProvider instead of ownerKey when updating with the EntityClient * Pass ownerKeyProvider only when necessary * Document ownerKeyProvider parameter * Fix offline database migration * Fix unlocking the indexer data --------- Co-authored-by: vaf <vaf@tutao.de> Co-authored-by: bedhub <bedhub@users.noreply.github.com> Co-authored-by: bed <bed@tutao.de>
2024-04-17 10:34:33 +02:00
export function aesDecrypt(key: AesKey, encryptedBytes: Uint8Array, usePadding: boolean = true): Uint8Array {
const keyLength = getKeyLengthBytes(key)
if (keyLength === KEY_LENGTH_BYTES_AES_128) {
return aesDecryptImpl(key, encryptedBytes, usePadding, false)
} else {
return aesDecryptImpl(key, encryptedBytes, usePadding, true)
}
}
/**
* Decrypts the given words with AES-128/ AES-256 in CBC mode with HMAC-SHA-256 as mac. Enforces the mac.
* @param key The key to use for the decryption.
* @param encryptedBytes The ciphertext encoded as bytes.
* @param usePadding If true, padding is used, otherwise no padding is used and the encrypted data must have the key size.
* @return The decrypted bytes.
*/
Support group key rotation (#6588) * Allow groups to have multiple key versions tutadb#1628 * Adapt to model changes * Fix CommonMailUtilsTest * Remove symEncBucketKey from SecureExternalRecipientKeyData * Remove deprecated types Also fix tests that relied on them as dummy types * Add userKeyVersion to RecoverCode * Remove clientKey Seems to be unused. * Remove CreateFolderService Unused. * Remove symEncSessionKey from DraftCreateData Unused. * Remove symEncShareBucketKey from MailBox Unused. * Add userKeyVersion to TutanotaProperties * Remove PasswordRetrievalService type The service itself had been long gone. * Remove userKeyVersion from CustomerAccountCreateData CreateMailGroupData * Fix customer account creation Set the key version that we actually need there: the *system* admin pub key version. The sender key version is not needed, because the system admin only has RSA keys. Also, this is a new customer, so that would be version zero anyway. * Fix resolving bucket key with group reference Get the right versions along the way. * Use current group key when encrypting instance session keys * Remove left-over key getting Also document a couple of current key usages * Pass group key providers to EntityClient instead of group key * Fix types and do not provide sender key version for rsa Fix resolveServiceSessionKey * Rename constant to avoid confusion There is another constant with the same name. * Use TutanotaModelV69 * Introduce client side mechanism to handle key rotation requests see tutadb 1771 * Do not export 128-bit key generator It is only needed for tests within the package. * Remove group key version when creating user area groups Plus some minor clarity improvements. * Fix version handling when updating drafts and sending to secure external * Remove versions when creating external users They are zero. * Fix changing the admin flag * Remove (almost) all local admin related code * Improve readability * Default to user key version zero when loading entropy * Decrypt current groupKey with correct userGroupKey version * Fix system application offline migrations * Fix tutanota application offline migrations * Improve offline migration functions * Use AesKey type * Minor improvements from review * Use AesKey type instead of Aes128Key where possible * Model update after rebase * Fix getting user group key Should never try to get from the cache like a normal group key. * Fix getting former group key Start ID was off-by-one. * Minor changes from review. We just checked all usages of all public methods of KeyLoaderFacade to make sure we're using the correct versions where we need them. * More minor changes from review. * Pass ownerKeyProvider instead of ownerKey when updating with the EntityClient * Pass ownerKeyProvider only when necessary * Document ownerKeyProvider parameter * Fix offline database migration * Fix unlocking the indexer data --------- Co-authored-by: vaf <vaf@tutao.de> Co-authored-by: bedhub <bedhub@users.noreply.github.com> Co-authored-by: bed <bed@tutao.de>
2024-04-17 10:34:33 +02:00
export function authenticatedAesDecrypt(key: AesKey, encryptedBytes: Uint8Array, usePadding: boolean = true): Uint8Array {
return aesDecryptImpl(key, encryptedBytes, usePadding, true)
}
/**
* Decrypts the given words with AES-128/256 in CBC mode. Does not enforce a mac.
* We always must enforce macs. This only exists for backward compatibility in some exceptional cases like search index entry encryption.
*
* @param key The key to use for the decryption.
* @param encryptedBytes The ciphertext encoded as bytes.
* @param usePadding If true, padding is used, otherwise no padding is used and the encrypted data must have the key size.
* @return The decrypted bytes.
*/
export function unauthenticatedAesDecrypt(key: Aes256Key, encryptedBytes: Uint8Array, usePadding: boolean = true): Uint8Array {
return aesDecryptImpl(key, encryptedBytes, usePadding, false)
}
/**
* Decrypts the given words with AES-128/256 in CBC mode.
* @param key The key to use for the decryption.
* @param encryptedBytes The ciphertext encoded as bytes.
* @param usePadding If true, padding is used, otherwise no padding is used and the encrypted data must have the key size.
* @param enforceMac if true decryption will fail if there is no valid mac. we only support false for backward compatibility.
* it must not be used with new cryto anymore.
* @return The decrypted bytes.
*/
Support group key rotation (#6588) * Allow groups to have multiple key versions tutadb#1628 * Adapt to model changes * Fix CommonMailUtilsTest * Remove symEncBucketKey from SecureExternalRecipientKeyData * Remove deprecated types Also fix tests that relied on them as dummy types * Add userKeyVersion to RecoverCode * Remove clientKey Seems to be unused. * Remove CreateFolderService Unused. * Remove symEncSessionKey from DraftCreateData Unused. * Remove symEncShareBucketKey from MailBox Unused. * Add userKeyVersion to TutanotaProperties * Remove PasswordRetrievalService type The service itself had been long gone. * Remove userKeyVersion from CustomerAccountCreateData CreateMailGroupData * Fix customer account creation Set the key version that we actually need there: the *system* admin pub key version. The sender key version is not needed, because the system admin only has RSA keys. Also, this is a new customer, so that would be version zero anyway. * Fix resolving bucket key with group reference Get the right versions along the way. * Use current group key when encrypting instance session keys * Remove left-over key getting Also document a couple of current key usages * Pass group key providers to EntityClient instead of group key * Fix types and do not provide sender key version for rsa Fix resolveServiceSessionKey * Rename constant to avoid confusion There is another constant with the same name. * Use TutanotaModelV69 * Introduce client side mechanism to handle key rotation requests see tutadb 1771 * Do not export 128-bit key generator It is only needed for tests within the package. * Remove group key version when creating user area groups Plus some minor clarity improvements. * Fix version handling when updating drafts and sending to secure external * Remove versions when creating external users They are zero. * Fix changing the admin flag * Remove (almost) all local admin related code * Improve readability * Default to user key version zero when loading entropy * Decrypt current groupKey with correct userGroupKey version * Fix system application offline migrations * Fix tutanota application offline migrations * Improve offline migration functions * Use AesKey type * Minor improvements from review * Use AesKey type instead of Aes128Key where possible * Model update after rebase * Fix getting user group key Should never try to get from the cache like a normal group key. * Fix getting former group key Start ID was off-by-one. * Minor changes from review. We just checked all usages of all public methods of KeyLoaderFacade to make sure we're using the correct versions where we need them. * More minor changes from review. * Pass ownerKeyProvider instead of ownerKey when updating with the EntityClient * Pass ownerKeyProvider only when necessary * Document ownerKeyProvider parameter * Fix offline database migration * Fix unlocking the indexer data --------- Co-authored-by: vaf <vaf@tutao.de> Co-authored-by: bedhub <bedhub@users.noreply.github.com> Co-authored-by: bed <bed@tutao.de>
2024-04-17 10:34:33 +02:00
function aesDecryptImpl(key: AesKey, encryptedBytes: Uint8Array, usePadding: boolean, enforceMac: boolean): Uint8Array {
verifyKeySize(key, [KEY_LENGTH_BITS_AES_128, KEY_LENGTH_BITS_AES_256])
const hasMac = encryptedBytes.length % 2 === 1
if (enforceMac && !hasMac) {
throw new CryptoError("mac expected but not present")
}
const subKeys = getAesSubKeys(key, hasMac)
2017-12-12 14:54:35 +01:00
let cipherTextWithoutMac
2021-12-27 17:48:21 +01:00
if (hasMac) {
2017-12-12 14:54:35 +01:00
cipherTextWithoutMac = encryptedBytes.subarray(1, encryptedBytes.length - MAC_LENGTH_BYTES)
const providedMacBytes = encryptedBytes.subarray(encryptedBytes.length - MAC_LENGTH_BYTES)
2025-01-08 16:34:30 +01:00
verifyHmacSha256(assertNotNull(subKeys.mKey), cipherTextWithoutMac, providedMacBytes as MacTag)
2017-12-12 14:54:35 +01:00
} else {
cipherTextWithoutMac = encryptedBytes
}
// take the iv from the front of the encrypted data
2019-04-10 16:42:38 +02:00
const iv = cipherTextWithoutMac.slice(0, IV_BYTE_LENGTH)
2021-12-27 17:48:21 +01:00
2019-04-10 16:42:38 +02:00
if (iv.length !== IV_BYTE_LENGTH) {
throw new CryptoError(`Invalid IV length in aesDecrypt: ${iv.length} bytes, must be 16 bytes (128 bits)`)
2019-04-10 16:42:38 +02:00
}
2021-12-27 17:48:21 +01:00
const ciphertext = cipherTextWithoutMac.slice(IV_BYTE_LENGTH)
2021-12-27 17:48:21 +01:00
2017-12-12 14:54:35 +01:00
try {
const decrypted = sjcl.mode.cbc.decrypt(new sjcl.cipher.aes(subKeys.cKey), uint8ArrayToBitArray(ciphertext), uint8ArrayToBitArray(iv), [], usePadding)
2019-08-29 09:17:05 +02:00
return new Uint8Array(bitArrayToUint8Array(decrypted))
2017-08-15 13:54:22 +02:00
} catch (e) {
2021-12-27 17:48:21 +01:00
throw new CryptoError("aes decryption failed", e as Error)
2017-08-15 13:54:22 +02:00
}
}
// visibleForTesting
Support group key rotation (#6588) * Allow groups to have multiple key versions tutadb#1628 * Adapt to model changes * Fix CommonMailUtilsTest * Remove symEncBucketKey from SecureExternalRecipientKeyData * Remove deprecated types Also fix tests that relied on them as dummy types * Add userKeyVersion to RecoverCode * Remove clientKey Seems to be unused. * Remove CreateFolderService Unused. * Remove symEncSessionKey from DraftCreateData Unused. * Remove symEncShareBucketKey from MailBox Unused. * Add userKeyVersion to TutanotaProperties * Remove PasswordRetrievalService type The service itself had been long gone. * Remove userKeyVersion from CustomerAccountCreateData CreateMailGroupData * Fix customer account creation Set the key version that we actually need there: the *system* admin pub key version. The sender key version is not needed, because the system admin only has RSA keys. Also, this is a new customer, so that would be version zero anyway. * Fix resolving bucket key with group reference Get the right versions along the way. * Use current group key when encrypting instance session keys * Remove left-over key getting Also document a couple of current key usages * Pass group key providers to EntityClient instead of group key * Fix types and do not provide sender key version for rsa Fix resolveServiceSessionKey * Rename constant to avoid confusion There is another constant with the same name. * Use TutanotaModelV69 * Introduce client side mechanism to handle key rotation requests see tutadb 1771 * Do not export 128-bit key generator It is only needed for tests within the package. * Remove group key version when creating user area groups Plus some minor clarity improvements. * Fix version handling when updating drafts and sending to secure external * Remove versions when creating external users They are zero. * Fix changing the admin flag * Remove (almost) all local admin related code * Improve readability * Default to user key version zero when loading entropy * Decrypt current groupKey with correct userGroupKey version * Fix system application offline migrations * Fix tutanota application offline migrations * Improve offline migration functions * Use AesKey type * Minor improvements from review * Use AesKey type instead of Aes128Key where possible * Model update after rebase * Fix getting user group key Should never try to get from the cache like a normal group key. * Fix getting former group key Start ID was off-by-one. * Minor changes from review. We just checked all usages of all public methods of KeyLoaderFacade to make sure we're using the correct versions where we need them. * More minor changes from review. * Pass ownerKeyProvider instead of ownerKey when updating with the EntityClient * Pass ownerKeyProvider only when necessary * Document ownerKeyProvider parameter * Fix offline database migration * Fix unlocking the indexer data --------- Co-authored-by: vaf <vaf@tutao.de> Co-authored-by: bedhub <bedhub@users.noreply.github.com> Co-authored-by: bed <bed@tutao.de>
2024-04-17 10:34:33 +02:00
export function verifyKeySize(key: AesKey, bitLength: number[]) {
if (!bitLength.includes(sjcl.bitArray.bitLength(key))) {
2017-08-15 13:54:22 +02:00
throw new CryptoError(`Illegal key length: ${sjcl.bitArray.bitLength(key)} (expected: ${bitLength})`)
}
}
/************************ Legacy AES128 ************************/
Support group key rotation (#6588) * Allow groups to have multiple key versions tutadb#1628 * Adapt to model changes * Fix CommonMailUtilsTest * Remove symEncBucketKey from SecureExternalRecipientKeyData * Remove deprecated types Also fix tests that relied on them as dummy types * Add userKeyVersion to RecoverCode * Remove clientKey Seems to be unused. * Remove CreateFolderService Unused. * Remove symEncSessionKey from DraftCreateData Unused. * Remove symEncShareBucketKey from MailBox Unused. * Add userKeyVersion to TutanotaProperties * Remove PasswordRetrievalService type The service itself had been long gone. * Remove userKeyVersion from CustomerAccountCreateData CreateMailGroupData * Fix customer account creation Set the key version that we actually need there: the *system* admin pub key version. The sender key version is not needed, because the system admin only has RSA keys. Also, this is a new customer, so that would be version zero anyway. * Fix resolving bucket key with group reference Get the right versions along the way. * Use current group key when encrypting instance session keys * Remove left-over key getting Also document a couple of current key usages * Pass group key providers to EntityClient instead of group key * Fix types and do not provide sender key version for rsa Fix resolveServiceSessionKey * Rename constant to avoid confusion There is another constant with the same name. * Use TutanotaModelV69 * Introduce client side mechanism to handle key rotation requests see tutadb 1771 * Do not export 128-bit key generator It is only needed for tests within the package. * Remove group key version when creating user area groups Plus some minor clarity improvements. * Fix version handling when updating drafts and sending to secure external * Remove versions when creating external users They are zero. * Fix changing the admin flag * Remove (almost) all local admin related code * Improve readability * Default to user key version zero when loading entropy * Decrypt current groupKey with correct userGroupKey version * Fix system application offline migrations * Fix tutanota application offline migrations * Improve offline migration functions * Use AesKey type * Minor improvements from review * Use AesKey type instead of Aes128Key where possible * Model update after rebase * Fix getting user group key Should never try to get from the cache like a normal group key. * Fix getting former group key Start ID was off-by-one. * Minor changes from review. We just checked all usages of all public methods of KeyLoaderFacade to make sure we're using the correct versions where we need them. * More minor changes from review. * Pass ownerKeyProvider instead of ownerKey when updating with the EntityClient * Pass ownerKeyProvider only when necessary * Document ownerKeyProvider parameter * Fix offline database migration * Fix unlocking the indexer data --------- Co-authored-by: vaf <vaf@tutao.de> Co-authored-by: bedhub <bedhub@users.noreply.github.com> Co-authored-by: bed <bed@tutao.de>
2024-04-17 10:34:33 +02:00
/**
* @private visible for tests
* @deprecated
* */
export function _aes128RandomKey(): Aes128Key {
2017-08-15 13:54:22 +02:00
return uint8ArrayToBitArray(random.generateRandomData(KEY_LENGTH_BYTES_AES_128))
}
export function getAesSubKeys(
Support group key rotation (#6588) * Allow groups to have multiple key versions tutadb#1628 * Adapt to model changes * Fix CommonMailUtilsTest * Remove symEncBucketKey from SecureExternalRecipientKeyData * Remove deprecated types Also fix tests that relied on them as dummy types * Add userKeyVersion to RecoverCode * Remove clientKey Seems to be unused. * Remove CreateFolderService Unused. * Remove symEncSessionKey from DraftCreateData Unused. * Remove symEncShareBucketKey from MailBox Unused. * Add userKeyVersion to TutanotaProperties * Remove PasswordRetrievalService type The service itself had been long gone. * Remove userKeyVersion from CustomerAccountCreateData CreateMailGroupData * Fix customer account creation Set the key version that we actually need there: the *system* admin pub key version. The sender key version is not needed, because the system admin only has RSA keys. Also, this is a new customer, so that would be version zero anyway. * Fix resolving bucket key with group reference Get the right versions along the way. * Use current group key when encrypting instance session keys * Remove left-over key getting Also document a couple of current key usages * Pass group key providers to EntityClient instead of group key * Fix types and do not provide sender key version for rsa Fix resolveServiceSessionKey * Rename constant to avoid confusion There is another constant with the same name. * Use TutanotaModelV69 * Introduce client side mechanism to handle key rotation requests see tutadb 1771 * Do not export 128-bit key generator It is only needed for tests within the package. * Remove group key version when creating user area groups Plus some minor clarity improvements. * Fix version handling when updating drafts and sending to secure external * Remove versions when creating external users They are zero. * Fix changing the admin flag * Remove (almost) all local admin related code * Improve readability * Default to user key version zero when loading entropy * Decrypt current groupKey with correct userGroupKey version * Fix system application offline migrations * Fix tutanota application offline migrations * Improve offline migration functions * Use AesKey type * Minor improvements from review * Use AesKey type instead of Aes128Key where possible * Model update after rebase * Fix getting user group key Should never try to get from the cache like a normal group key. * Fix getting former group key Start ID was off-by-one. * Minor changes from review. We just checked all usages of all public methods of KeyLoaderFacade to make sure we're using the correct versions where we need them. * More minor changes from review. * Pass ownerKeyProvider instead of ownerKey when updating with the EntityClient * Pass ownerKeyProvider only when necessary * Document ownerKeyProvider parameter * Fix offline database migration * Fix unlocking the indexer data --------- Co-authored-by: vaf <vaf@tutao.de> Co-authored-by: bedhub <bedhub@users.noreply.github.com> Co-authored-by: bed <bed@tutao.de>
2024-04-17 10:34:33 +02:00
key: AesKey,
2022-12-27 15:37:40 +01:00
mac: boolean,
2021-12-27 17:48:21 +01:00
): {
Support group key rotation (#6588) * Allow groups to have multiple key versions tutadb#1628 * Adapt to model changes * Fix CommonMailUtilsTest * Remove symEncBucketKey from SecureExternalRecipientKeyData * Remove deprecated types Also fix tests that relied on them as dummy types * Add userKeyVersion to RecoverCode * Remove clientKey Seems to be unused. * Remove CreateFolderService Unused. * Remove symEncSessionKey from DraftCreateData Unused. * Remove symEncShareBucketKey from MailBox Unused. * Add userKeyVersion to TutanotaProperties * Remove PasswordRetrievalService type The service itself had been long gone. * Remove userKeyVersion from CustomerAccountCreateData CreateMailGroupData * Fix customer account creation Set the key version that we actually need there: the *system* admin pub key version. The sender key version is not needed, because the system admin only has RSA keys. Also, this is a new customer, so that would be version zero anyway. * Fix resolving bucket key with group reference Get the right versions along the way. * Use current group key when encrypting instance session keys * Remove left-over key getting Also document a couple of current key usages * Pass group key providers to EntityClient instead of group key * Fix types and do not provide sender key version for rsa Fix resolveServiceSessionKey * Rename constant to avoid confusion There is another constant with the same name. * Use TutanotaModelV69 * Introduce client side mechanism to handle key rotation requests see tutadb 1771 * Do not export 128-bit key generator It is only needed for tests within the package. * Remove group key version when creating user area groups Plus some minor clarity improvements. * Fix version handling when updating drafts and sending to secure external * Remove versions when creating external users They are zero. * Fix changing the admin flag * Remove (almost) all local admin related code * Improve readability * Default to user key version zero when loading entropy * Decrypt current groupKey with correct userGroupKey version * Fix system application offline migrations * Fix tutanota application offline migrations * Improve offline migration functions * Use AesKey type * Minor improvements from review * Use AesKey type instead of Aes128Key where possible * Model update after rebase * Fix getting user group key Should never try to get from the cache like a normal group key. * Fix getting former group key Start ID was off-by-one. * Minor changes from review. We just checked all usages of all public methods of KeyLoaderFacade to make sure we're using the correct versions where we need them. * More minor changes from review. * Pass ownerKeyProvider instead of ownerKey when updating with the EntityClient * Pass ownerKeyProvider only when necessary * Document ownerKeyProvider parameter * Fix offline database migration * Fix unlocking the indexer data --------- Co-authored-by: vaf <vaf@tutao.de> Co-authored-by: bedhub <bedhub@users.noreply.github.com> Co-authored-by: bed <bed@tutao.de>
2024-04-17 10:34:33 +02:00
mKey: AesKey | null | undefined
cKey: AesKey
2021-12-27 17:48:21 +01:00
} {
2017-09-25 16:22:38 +02:00
if (mac) {
2025-12-05 09:35:58 +01:00
let hashedKey: Uint8Array<ArrayBuffer>
switch (getKeyLengthBytes(key)) {
case KEY_LENGTH_BYTES_AES_128:
hashedKey = sha256Hash(bitArrayToUint8Array(key))
break
case KEY_LENGTH_BYTES_AES_256:
hashedKey = sha512Hash(bitArrayToUint8Array(key))
break
default:
throw new Error(`unexpected key length ${getKeyLengthBytes(key)}`)
2017-09-25 16:22:38 +02:00
}
return {
cKey: uint8ArrayToBitArray(hashedKey.subarray(0, hashedKey.length / 2)),
mKey: uint8ArrayToBitArray(hashedKey.subarray(hashedKey.length / 2, hashedKey.length)),
}
2017-12-12 14:54:35 +01:00
} else {
return {
cKey: key,
2021-12-27 17:48:21 +01:00
mKey: null,
2017-12-12 14:54:35 +01:00
}
}
2022-12-27 15:37:40 +01:00
}
export function extractIvFromCipherText(encrypted: Base64): Uint8Array {
const encryptedBytes = base64ToUint8Array(encrypted)
const hasMac = encryptedBytes.length % 2 === 1
const cipherTextWithoutMac = hasMac ? encryptedBytes.subarray(1, encryptedBytes.length - MAC_LENGTH_BYTES) : encryptedBytes
if (cipherTextWithoutMac.length < IV_BYTE_LENGTH) {
throw new CryptoError(`insufficient bytes in cipherTextWithoutMac to extract iv: ${cipherTextWithoutMac.length}`)
}
return cipherTextWithoutMac.slice(0, IV_BYTE_LENGTH)
}