From 649e6c5b1d3e359e2bf43bf68f05331d9e1eb31c Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 25 Feb 2025 13:50:27 +0100 Subject: [PATCH] Tmp --- .../key-rotation/user-key-rotation.service.ts | 13 +- .../device-trust.service.implementation.ts | 3 +- .../master-password.service.ts | 9 +- .../encrypt.service.implementation.ts | 150 +++++++++--------- .../src/platform/models/domain/account.ts | 4 - .../models/domain/symmetric-crypto-key.ts | 13 +- .../services/web-crypto-function.service.ts | 6 +- 7 files changed, 102 insertions(+), 96 deletions(-) diff --git a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts index ca842aa033d..ce772f62c15 100644 --- a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts @@ -13,7 +13,7 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { EncryptionType, HashPurpose } from "@bitwarden/common/platform/enums"; +import { HashPurpose } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; @@ -105,22 +105,25 @@ export class UserKeyRotationService { await this.keyService.makeUserKey(newMasterKey); const userKey = CryptoClient.generate_user_key(); - this.logService.info("[Userkey rotation] Encrypting user key in new format"); + this.logService.info("[Userkey rotation] Encrypting user key in new format" + userKey); const userkeyEncodedBytes = Utils.fromB64ToArray(userKey); const stretchedMasterKey = await this.keyGenerationService.stretchKey(newMasterKey); const userkeyEncrypted = await this.encryptService.encrypt( userkeyEncodedBytes, stretchedMasterKey, ); - const userkeyBytes = Utils.fromB64ToArray(CryptoClient.decode_userkey(userKey).Aes256CbcHmac); - newUnencryptedUserKey = new SymmetricCryptoKey(userkeyBytes, EncryptionType.AesCbc256_HmacSha256_B64) as UserKey; - newMasterKeyEncryptedUserKey = userkeyEncrypted; + this.logService.info("[Userkey rotation] User key encrypted in new format" + userkeyEncrypted.encryptedString); + newUnencryptedUserKey = new SymmetricCryptoKey(userkeyEncodedBytes) as UserKey; + newMasterKeyEncryptedUserKey + = userkeyEncrypted; if (!newUnencryptedUserKey || !newMasterKeyEncryptedUserKey) { this.logService.info("[Userkey rotation] User key could not be created. Aborting!"); throw new Error("User key could not be created"); } + this.logService.info("[Userkey rotation] User key created successfully"); + const newMasterKeyAuthenticationHash = await this.keyService.hashMasterKey( newMasterPassword, newMasterKey, diff --git a/libs/common/src/auth/services/device-trust.service.implementation.ts b/libs/common/src/auth/services/device-trust.service.implementation.ts index a7fa2984e45..a75c65aad4d 100644 --- a/libs/common/src/auth/services/device-trust.service.implementation.ts +++ b/libs/common/src/auth/services/device-trust.service.implementation.ts @@ -295,7 +295,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { return; } - await this.stateProvider.setUserState(DEVICE_KEY, deviceKey?.toJSON(), userId); + //await this.stateProvider.setUserState(DEVICE_KEY, deviceKey?.toJSON(), userId); } catch (e) { this.logService.error("Failed to set device key", e); } @@ -304,7 +304,6 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { private async makeDeviceKey(): Promise { // Create 512-bit device key const deviceKey = (await this.keyGenerationService.createKey(512)) as DeviceKey; - return deviceKey; } diff --git a/libs/common/src/auth/services/master-password/master-password.service.ts b/libs/common/src/auth/services/master-password/master-password.service.ts index 5fe8cc6dcc9..cab5de6d9e9 100644 --- a/libs/common/src/auth/services/master-password/master-password.service.ts +++ b/libs/common/src/auth/services/master-password/master-password.service.ts @@ -2,9 +2,6 @@ // @ts-strict-ignore import { firstValueFrom, map, Observable } from "rxjs"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { CryptoClient } from "@bitwarden/sdk-internal"; - import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service"; import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service"; import { LogService } from "../../../platform/abstractions/log.service"; @@ -198,11 +195,7 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr return new SymmetricCryptoKey(decUserKey) as UserKey; } else { this.logService.info("[MasterPasswordService] Userkey in new format; using sdk"); - const decodedKey = CryptoClient.decode_userkey(decUserKey); - if (decodedKey.Aes256CbcHmac != null) { - const key = Utils.fromB64ToArray(decodedKey.Aes256CbcHmac); - return new SymmetricCryptoKey(key) as UserKey; - } + return new SymmetricCryptoKey(decUserKey) as UserKey; } } else { throw new Error("Unsupported encryption type."); diff --git a/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts b/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts index 7458054bbb8..33c2f993e9b 100644 --- a/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts +++ b/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts @@ -40,11 +40,15 @@ export class EncryptServiceImplementation implements EncryptService { plainBuf = plainValue; } - const encObj = await this.aesEncrypt(plainBuf, key); - const iv = Utils.fromBufferToB64(encObj.iv); - const data = Utils.fromBufferToB64(encObj.data); - const mac = encObj.mac != null ? Utils.fromBufferToB64(encObj.mac) : null; - return new EncString(encObj.key.encryptionType(), data, iv, mac); + if (key.encryptionType() === EncryptionType.AesCbc256_HmacSha256_B64) { + const encObj = await this.aesEncrypt(plainBuf, key); + const iv = Utils.fromBufferToB64(encObj.iv); + const data = Utils.fromBufferToB64(encObj.data); + const mac = Utils.fromBufferToB64(encObj.mac); + return new EncString(key.encryptionType(), data, iv, mac); + } else { + throw new Error("Unsupported encryption type."); + } } async encryptToBytes(plainValue: Uint8Array, key: SymmetricCryptoKey): Promise { @@ -52,21 +56,17 @@ export class EncryptServiceImplementation implements EncryptService { throw new Error("No encryption key provided."); } - const encValue = await this.aesEncrypt(plainValue, key); - let macLen = 0; - if (encValue.mac != null) { - macLen = encValue.mac.byteLength; - } - - const encBytes = new Uint8Array(1 + encValue.iv.byteLength + macLen + encValue.data.byteLength); - encBytes.set([encValue.key.encryptionType()]); - encBytes.set(new Uint8Array(encValue.iv), 1); - if (encValue.mac != null) { + if (key.encryptionType() !== EncryptionType.AesCbc256_HmacSha256_B64) { + const encValue = await this.aesEncrypt(plainValue, key); + const encBytes = new Uint8Array(1 + encValue.iv.byteLength + encValue.mac.byteLength + encValue.data.byteLength); + encBytes.set([key.encryptionType()]); + encBytes.set(new Uint8Array(encValue.iv), 1); encBytes.set(new Uint8Array(encValue.mac), 1 + encValue.iv.byteLength); + encBytes.set(new Uint8Array(encValue.data), 1 + encValue.iv.byteLength + encValue.mac.byteLength); + return new EncArrayBuffer(encBytes); + } else { + throw new Error("Unsupported encryption type."); } - - encBytes.set(new Uint8Array(encValue.data), 1 + encValue.iv.byteLength + macLen); - return new EncArrayBuffer(encBytes); } async decryptToUtf8( @@ -79,35 +79,35 @@ export class EncryptServiceImplementation implements EncryptService { } // DO NOT REMOVE OR MOVE. This prevents downgrade to mac-less CBC, which would compromise integrity and confidentiality. - if (key.macKey != null && encString?.mac == null) { - this.logService.error( - "[Encrypt service] Key has mac key but payload is missing mac bytes. Key type " + - encryptionTypeName(key.encryptionType()) + - "Payload type " + - encryptionTypeName(encString.encryptionType), - "Decrypt context: " + decryptContext, - ); - return null; - } + if (key.encryptionType() === EncryptionType.AesCbc256_HmacSha256_B64) { + if (encString?.mac == null) { + this.logService.error( + "[Encrypt service] Key has mac key but payload is missing mac bytes. Key type " + + encryptionTypeName(key.encryptionType()) + + "Payload type " + + encryptionTypeName(encString.encryptionType), + "Decrypt context: " + decryptContext, + ); + return null; + } - if (key.encryptionType() !== encString.encryptionType) { - this.logService.error( - "[Encrypt service] Key encryption type does not match payload encryption type. Key type " + - encryptionTypeName(key.encryptionType()) + - "Payload type " + - encryptionTypeName(encString.encryptionType), - "Decrypt context: " + decryptContext, - ); - return null; - } + if (encString.encryptionType !== EncryptionType.AesCbc256_HmacSha256_B64) { + this.logService.error( + "[Encrypt service] Key encryption type does not match payload encryption type. Key type " + + encryptionTypeName(key.encryptionType()) + + "Payload type " + + encryptionTypeName(encString.encryptionType), + "Decrypt context: " + decryptContext, + ); + return null; + } - const fastParams = this.cryptoFunctionService.aesDecryptFastParameters( - encString.data, - encString.iv, - encString.mac, - key, - ); - if (fastParams.macKey != null && fastParams.mac != null) { + const fastParams = this.cryptoFunctionService.aesDecryptFastParameters( + encString.data, + encString.iv, + encString.mac, + key, + ); const computedMac = await this.cryptoFunctionService.hmacFast( fastParams.macData, fastParams.macKey, @@ -125,9 +125,14 @@ export class EncryptServiceImplementation implements EncryptService { ); return null; } + + return await this.cryptoFunctionService.aesDecryptFast({ mode: "cbc", parameters: fastParams }); + } else if (key.encryptionType() === EncryptionType.AesCbc256_B64) { + return Utils.fromBufferToByteString(await this.cryptoFunctionService.aesDecrypt(encString.dataBytes, encString.ivBytes, key.getInnerKey().encryptionKey, "cbc")); + } else { + throw new Error("Unsupported encryption type."); } - return await this.cryptoFunctionService.aesDecryptFast({ mode: "cbc", parameters: fastParams }); } async decryptToBytes( @@ -143,36 +148,37 @@ export class EncryptServiceImplementation implements EncryptService { throw new Error("Nothing provided for decryption."); } - // DO NOT REMOVE OR MOVE. This prevents downgrade to mac-less CBC, which would compromise integrity and confidentiality. - if (key.macKey != null && encThing.macBytes == null) { - this.logService.error( - "[Encrypt service] Key has mac key but payload is missing mac bytes. Key type " + - encryptionTypeName(key.encryptionType()) + - " Payload type " + - encryptionTypeName(encThing.encryptionType) + - " Decrypt context: " + - decryptContext, - ); - return null; - } + const innerKey = key.getInnerKey(); + if (innerKey.type == EncryptionType.AesCbc256_HmacSha256_B64) { + // DO NOT REMOVE OR MOVE. This prevents downgrade to mac-less CBC, which would compromise integrity and confidentiality. + if (encThing.macBytes == null) { + this.logService.error( + "[Encrypt service] Key has mac key but payload is missing mac bytes. Key type " + + encryptionTypeName(key.encryptionType()) + + " Payload type " + + encryptionTypeName(encThing.encryptionType) + + " Decrypt context: " + + decryptContext, + ); + return null; + } - if (key.encryptionType() !== encThing.encryptionType) { - this.logService.error( - "[Encrypt service] Key encryption type does not match payload encryption type. Key type " + - encryptionTypeName(key.encryptionType()) + - " Payload type " + - encryptionTypeName(encThing.encryptionType) + - " Decrypt context: " + - decryptContext, - ); - return null; - } + if (encThing.encryptionType !== EncryptionType.AesCbc256_HmacSha256_B64) { + this.logService.error( + "[Encrypt service] Key encryption type does not match payload encryption type. Key type " + + encryptionTypeName(key.encryptionType()) + + " Payload type " + + encryptionTypeName(encThing.encryptionType) + + " Decrypt context: " + + decryptContext, + ); + return null; + } - if (key.macKey != null && encThing.macBytes != null) { const macData = new Uint8Array(encThing.ivBytes.byteLength + encThing.dataBytes.byteLength); macData.set(new Uint8Array(encThing.ivBytes), 0); macData.set(new Uint8Array(encThing.dataBytes), encThing.ivBytes.byteLength); - const computedMac = await this.cryptoFunctionService.hmac(macData, key.macKey, "sha256"); + const computedMac = await this.cryptoFunctionService.hmac(macData, innerKey.authenticationKey, "sha256"); if (computedMac === null) { this.logMacFailed( "[Encrypt service#decryptToBytes] Failed to compute MAC." + @@ -204,7 +210,7 @@ export class EncryptServiceImplementation implements EncryptService { const result = await this.cryptoFunctionService.aesDecrypt( encThing.dataBytes, encThing.ivBytes, - key.encryptionType() + key.getInnerKey().encryptionKey, "cbc", ); diff --git a/libs/common/src/platform/models/domain/account.ts b/libs/common/src/platform/models/domain/account.ts index 9873e5c8574..6e80cf8c9c4 100644 --- a/libs/common/src/platform/models/domain/account.ts +++ b/libs/common/src/platform/models/domain/account.ts @@ -70,10 +70,6 @@ export class AccountKeys { return null; } return Object.assign(new AccountKeys(), obj, { - cryptoSymmetricKey: EncryptionPair.fromJSON( - obj?.cryptoSymmetricKey, - SymmetricCryptoKey.fromJSON, - ), publicKey: Utils.fromByteStringToArray(obj?.publicKey), }); } diff --git a/libs/common/src/platform/models/domain/symmetric-crypto-key.ts b/libs/common/src/platform/models/domain/symmetric-crypto-key.ts index 64d4e09dfe4..abbab313107 100644 --- a/libs/common/src/platform/models/domain/symmetric-crypto-key.ts +++ b/libs/common/src/platform/models/domain/symmetric-crypto-key.ts @@ -24,7 +24,7 @@ export class SymmetricCryptoKey { meta: any; - constructor(key: Uint8Array, new_format = false) { + constructor(key: Uint8Array) { if (key == null) { throw new Error("Must provide key"); } @@ -41,6 +41,7 @@ export class SymmetricCryptoKey { authenticationKey: key.slice(32), } } else if (key.byteLength > 64) { + this.newFormat = true; const decoded_key = CryptoClient.decode_userkey(key).Aes256CbcHmac; this.key = { type: EncryptionType.AesCbc256_HmacSha256_B64, @@ -56,7 +57,6 @@ export class SymmetricCryptoKey { return this.key; } - static fromString(s: string): SymmetricCryptoKey { if (s == null) { return null; @@ -68,8 +68,15 @@ export class SymmetricCryptoKey { // For test only toJSON() { + const innerKey = this.getInnerKey(); // The whole object is constructed from the initial key, so just store the B64 key - return { keyB64: Utils.fromBufferToB64(this) }; + if (innerKey.type === EncryptionType.AesCbc256_B64) { + return { keyB64: Utils.fromBufferToB64(this.key.encryptionKey) }; + } else if (innerKey.type === EncryptionType.AesCbc256_HmacSha256_B64) { + return { keyB64: Utils.fromBufferToB64(new Uint8Array([...innerKey.encryptionKey, ...innerKey.authenticationKey])) }; + } else { + throw new Error("Unsupported encryption type."); + } } // For test only diff --git a/libs/common/src/platform/services/web-crypto-function.service.ts b/libs/common/src/platform/services/web-crypto-function.service.ts index fc3c5d98b1b..f38bc8b22fc 100644 --- a/libs/common/src/platform/services/web-crypto-function.service.ts +++ b/libs/common/src/platform/services/web-crypto-function.service.ts @@ -4,6 +4,7 @@ import * as forge from "node-forge"; import { Utils } from "../../platform/misc/utils"; import { CsprngArray } from "../../types/csprng"; import { CryptoFunctionService } from "../abstractions/crypto-function.service"; +import { EncryptionType } from "../enums"; import { CbcDecryptParameters, EcbDecryptParameters } from "../models/domain/decrypt-parameters"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; @@ -256,8 +257,9 @@ export class WebCryptoFunctionService implements CryptoFunctionService { p.data = forge.util.decode64(data); p.iv = forge.util.decode64(iv); p.macData = p.iv + p.data; - if (p.macKey == null && key.macKeyB64 != null) { - p.macKey = forge.util.decode64(key.macKeyB64); + const innerKey = key.getInnerKey(); + if (p.macKey == null && innerKey.type === EncryptionType.AesCbc256_HmacSha256_B64) { + p.macKey = forge.util.decode64(Utils.fromBufferToB64(innerKey.authenticationKey)); } if (mac != null) { p.mac = forge.util.decode64(mac);