From 36b7d3080401c2d6f8162e5ad5f941ed1d8ce21d Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 3 Aug 2023 22:13:33 -0400 Subject: [PATCH] Fix failing crypto tests (#5948) * Change everything to Uint8Array related to https://github.com/jestjs/jest/issues/14379 * Work on failing type tests * Revert changes to custom matcher setup * Remove last BufferArrays from tests * Fix custom matcher type errors in vscode * Remove errant `.buffer` calls on Uint8Arrays * Encryption Pair should serialize Array Buffer and Uint8Array * Fix EncArrayBuffer encryption --------- Co-authored-by: Thomas Rittson --- .../background/nativeMessaging.background.ts | 8 +- ...cal-backed-session-storage.service.spec.ts | 4 +- .../admin-console/commands/confirm.command.ts | 2 +- apps/cli/src/commands/get.command.ts | 2 +- .../node-env-secure-storage.service.ts | 4 +- .../tools/send/commands/receive.command.ts | 4 +- .../src/native-message.service.ts | 4 +- .../auth/login/login-approval.component.ts | 2 +- .../services/electron-state.service.ts | 2 +- .../native-message-handler.service.ts | 2 +- .../src/services/native-messaging.service.ts | 4 +- .../manage/user-confirm.component.ts | 5 +- .../components/bulk/bulk-confirm.component.ts | 4 +- .../organizations/members/people.component.ts | 2 +- .../settings/account.component.ts | 4 +- .../enroll-master-password-reset.component.ts | 2 +- .../app/auth/accept-organization.component.ts | 2 +- .../emergency-access-confirm.component.ts | 2 +- .../emergency-access.component.ts | 4 +- .../src/app/common/base.people.component.ts | 2 +- .../app/settings/change-password.component.ts | 4 +- .../account-fingerprint.component.ts | 2 +- .../vault-items/vault-items.stories.ts | 2 +- .../device-approvals.component.ts | 2 +- .../providers/manage/people.component.ts | 2 +- .../components/login-with-device.component.ts | 2 +- .../src/components/set-password.component.ts | 5 +- .../src/platform/pipes/fingerprint.pipe.ts | 5 +- libs/common/custom-matchers.d.ts | 15 ++ libs/common/spec/matchers/to-equal-buffer.ts | 9 +- libs/common/src/auth/services/auth.service.ts | 4 +- .../abstractions/crypto-function.service.ts | 70 ++++----- .../platform/abstractions/crypto.service.ts | 20 +-- .../platform/abstractions/encrypt.service.ts | 6 +- .../platform/abstractions/state.service.ts | 8 +- .../src/platform/interfaces/encrypted.ts | 6 +- .../models/domain/account-keys.spec.ts | 6 +- .../src/platform/models/domain/account.ts | 11 +- .../models/domain/enc-array-buffer.spec.ts | 14 +- .../models/domain/enc-array-buffer.ts | 24 ++-- .../src/platform/models/domain/enc-string.ts | 12 +- .../models/domain/encrypted-object.ts | 6 +- .../models/domain/encryption-pair.spec.ts | 7 + .../domain/symmetric-crypto-key.spec.ts | 4 +- .../models/domain/symmetric-crypto-key.ts | 10 +- .../src/platform/services/crypto.service.ts | 34 ++--- .../encrypt.service.implementation.ts | 22 ++- .../platform/services/encrypt.service.spec.ts | 14 +- .../src/platform/services/state.service.ts | 8 +- .../web-crypto-function.service.spec.ts | 69 +++++---- .../services/web-crypto-function.service.ts | 106 ++++++++------ .../services/device-crypto.service.spec.ts | 20 +-- libs/common/src/services/totp.service.ts | 2 +- .../src/tools/send/models/view/send.view.ts | 4 +- .../src/tools/send/services/send.service.ts | 2 +- libs/common/src/types/csprng.d.ts | 2 +- .../src/vault/services/cipher.service.spec.ts | 2 +- .../src/vault/services/cipher.service.ts | 2 +- libs/common/test.setup.ts | 12 +- libs/common/tsconfig.json | 2 +- .../node-crypto-function.service.spec.ts | 69 +++------ .../services/node-crypto-function.service.ts | 134 +++++++++--------- 62 files changed, 401 insertions(+), 424 deletions(-) create mode 100644 libs/common/custom-matchers.d.ts diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index 90c114a865..e482c7cc83 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -59,8 +59,8 @@ export class NativeMessagingBackground { private port: browser.runtime.Port | chrome.runtime.Port; private resolver: any = null; - private privateKey: ArrayBuffer = null; - private publicKey: ArrayBuffer = null; + private privateKey: Uint8Array = null; + private publicKey: Uint8Array = null; private secureSetupResolve: any = null; private sharedSecret: SymmetricCryptoKey; private appId: string; @@ -129,7 +129,7 @@ export class NativeMessagingBackground { const encrypted = Utils.fromB64ToArray(message.sharedSecret); const decrypted = await this.cryptoFunctionService.rsaDecrypt( - encrypted.buffer, + encrypted, this.privateKey, EncryptionAlgorithm ); @@ -321,7 +321,7 @@ export class NativeMessagingBackground { if (message.response === "unlocked") { await this.cryptoService.setKey( - new SymmetricCryptoKey(Utils.fromB64ToArray(message.keyB64).buffer) + new SymmetricCryptoKey(Utils.fromB64ToArray(message.keyB64)) ); // Verify key is correct by attempting to decrypt a secret diff --git a/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts b/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts index d2da5f6fc1..4946541472 100644 --- a/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts +++ b/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts @@ -21,9 +21,7 @@ describe("Browser Session Storage Service", () => { let localStorage: BrowserLocalStorageService; let sessionStorage: BrowserMemoryStorageService; - const key = new SymmetricCryptoKey( - Utils.fromUtf8ToArray("00000000000000000000000000000000").buffer - ); + const key = new SymmetricCryptoKey(Utils.fromUtf8ToArray("00000000000000000000000000000000")); let getSessionKeySpy: jest.SpyInstance; const mockEnc = (input: string) => Promise.resolve(new EncString("ENCRYPTED" + input)); diff --git a/apps/cli/src/admin-console/commands/confirm.command.ts b/apps/cli/src/admin-console/commands/confirm.command.ts index 912c841775..be1264c399 100644 --- a/apps/cli/src/admin-console/commands/confirm.command.ts +++ b/apps/cli/src/admin-console/commands/confirm.command.ts @@ -51,7 +51,7 @@ export class ConfirmCommand { } const publicKeyResponse = await this.apiService.getUserPublicKey(orgUser.userId); const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey); - const key = await this.cryptoService.rsaEncrypt(orgKey.key, publicKey.buffer); + const key = await this.cryptoService.rsaEncrypt(orgKey.key, publicKey); const req = new OrganizationUserConfirmRequest(); req.key = key.encryptedString; await this.organizationUserService.postOrganizationUserConfirm( diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts index 44a94fb430..265e24c9d4 100644 --- a/apps/cli/src/commands/get.command.ts +++ b/apps/cli/src/commands/get.command.ts @@ -513,7 +513,7 @@ export class GetCommand extends DownloadCommand { try { const response = await this.apiService.getUserPublicKey(id); const pubKey = Utils.fromB64ToArray(response.publicKey); - fingerprint = await this.cryptoService.getFingerprint(id, pubKey.buffer); + fingerprint = await this.cryptoService.getFingerprint(id, pubKey); } catch { // eslint-disable-next-line } diff --git a/apps/cli/src/platform/services/node-env-secure-storage.service.ts b/apps/cli/src/platform/services/node-env-secure-storage.service.ts index 80e3cea64c..491ec32cb6 100644 --- a/apps/cli/src/platform/services/node-env-secure-storage.service.ts +++ b/apps/cli/src/platform/services/node-env-secure-storage.service.ts @@ -47,7 +47,7 @@ export class NodeEnvSecureStorageService implements AbstractStorageService { throw new Error("No session key available."); } const encValue = await this.cryptoService().encryptToBytes( - Utils.fromB64ToArray(plainValue).buffer, + Utils.fromB64ToArray(plainValue), sessionKey ); if (encValue == null) { @@ -81,7 +81,7 @@ export class NodeEnvSecureStorageService implements AbstractStorageService { private getSessionKey() { try { if (process.env.BW_SESSION != null) { - const sessionBuffer = Utils.fromB64ToArray(process.env.BW_SESSION).buffer; + const sessionBuffer = Utils.fromB64ToArray(process.env.BW_SESSION); if (sessionBuffer != null) { const sessionKey = new SymmetricCryptoKey(sessionBuffer); if (sessionBuffer != null) { diff --git a/apps/cli/src/tools/send/commands/receive.command.ts b/apps/cli/src/tools/send/commands/receive.command.ts index 486afed261..52cff0b462 100644 --- a/apps/cli/src/tools/send/commands/receive.command.ts +++ b/apps/cli/src/tools/send/commands/receive.command.ts @@ -121,7 +121,7 @@ export class SendReceiveCommand extends DownloadCommand { } } - private async getUnlockedPassword(password: string, keyArray: ArrayBuffer) { + private async getUnlockedPassword(password: string, keyArray: Uint8Array) { const passwordHash = await this.cryptoFunctionService.pbkdf2( password, keyArray, @@ -134,7 +134,7 @@ export class SendReceiveCommand extends DownloadCommand { private async sendRequest( url: string, id: string, - key: ArrayBuffer + key: Uint8Array ): Promise { try { const sendResponse = await this.sendApiService.postSendAccess( diff --git a/apps/desktop/native-messaging-test-runner/src/native-message.service.ts b/apps/desktop/native-messaging-test-runner/src/native-message.service.ts index a6a535a73d..4782389b7f 100644 --- a/apps/desktop/native-messaging-test-runner/src/native-message.service.ts +++ b/apps/desktop/native-messaging-test-runner/src/native-message.service.ts @@ -225,8 +225,8 @@ export default class NativeMessageService { } private async getSharedKeyForKey(key: string): Promise { - const dataBuffer = Utils.fromB64ToArray(key).buffer; - const privKey = Utils.fromB64ToArray(config.testRsaPrivateKey).buffer; + const dataBuffer = Utils.fromB64ToArray(key); + const privKey = Utils.fromB64ToArray(config.testRsaPrivateKey); return new SymmetricCryptoKey( await this.nodeCryptoFunctionService.rsaDecrypt(dataBuffer, privKey, "sha1") diff --git a/apps/desktop/src/auth/login/login-approval.component.ts b/apps/desktop/src/auth/login/login-approval.component.ts index d28e69e358..807059107f 100644 --- a/apps/desktop/src/auth/login/login-approval.component.ts +++ b/apps/desktop/src/auth/login/login-approval.component.ts @@ -68,7 +68,7 @@ export class LoginApprovalComponent implements OnInit, OnDestroy { const publicKey = Utils.fromB64ToArray(this.authRequestResponse.publicKey); this.email = await this.stateService.getEmail(); this.fingerprintPhrase = ( - await this.cryptoService.getFingerprint(this.email, publicKey.buffer) + await this.cryptoService.getFingerprint(this.email, publicKey) ).join("-"); this.updateTimeText(); diff --git a/apps/desktop/src/platform/services/electron-state.service.ts b/apps/desktop/src/platform/services/electron-state.service.ts index 7c3ff38a1b..334cab9669 100644 --- a/apps/desktop/src/platform/services/electron-state.service.ts +++ b/apps/desktop/src/platform/services/electron-state.service.ts @@ -98,7 +98,7 @@ export class ElectronStateService options ); - return new SymmetricCryptoKey(Utils.fromB64ToArray(b64DeviceKey).buffer) as DeviceKey; + return new SymmetricCryptoKey(Utils.fromB64ToArray(b64DeviceKey)) as DeviceKey; } override async setDeviceKey(value: DeviceKey, options?: StorageOptions): Promise { diff --git a/apps/desktop/src/services/native-message-handler.service.ts b/apps/desktop/src/services/native-message-handler.service.ts index 1921ea5fa0..644dfe8f90 100644 --- a/apps/desktop/src/services/native-message-handler.service.ts +++ b/apps/desktop/src/services/native-message-handler.service.ts @@ -70,7 +70,7 @@ export class NativeMessageHandlerService { } try { - const remotePublicKey = Utils.fromB64ToArray(publicKey).buffer; + const remotePublicKey = Utils.fromB64ToArray(publicKey); const ddgEnabled = await this.stateService.getEnableDuckDuckGoBrowserIntegration(); if (!ddgEnabled) { diff --git a/apps/desktop/src/services/native-messaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts index 494a65b670..7647410fd7 100644 --- a/apps/desktop/src/services/native-messaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -56,7 +56,7 @@ export class NativeMessagingService { // Request to setup secure encryption if ("command" in rawMessage && rawMessage.command === "setupEncryption") { - const remotePublicKey = Utils.fromB64ToArray(rawMessage.publicKey).buffer; + const remotePublicKey = Utils.fromB64ToArray(rawMessage.publicKey); // Validate the UserId to ensure we are logged into the same account. const accounts = await firstValueFrom(this.stateService.accounts$); @@ -169,7 +169,7 @@ export class NativeMessagingService { ipcRenderer.send("nativeMessagingReply", { appId: appId, message: encrypted }); } - private async secureCommunication(remotePublicKey: ArrayBuffer, appId: string) { + private async secureCommunication(remotePublicKey: Uint8Array, appId: string) { const secret = await this.cryptoFunctionService.randomBytes(64); this.sharedSecrets.set(appId, new SymmetricCryptoKey(secret)); diff --git a/apps/web/src/app/admin-console/organizations/manage/user-confirm.component.ts b/apps/web/src/app/admin-console/organizations/manage/user-confirm.component.ts index 4d3cd0b6ed..22f080b991 100644 --- a/apps/web/src/app/admin-console/organizations/manage/user-confirm.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/user-confirm.component.ts @@ -28,10 +28,7 @@ export class UserConfirmComponent implements OnInit { async ngOnInit() { try { if (this.publicKey != null) { - const fingerprint = await this.cryptoService.getFingerprint( - this.userId, - this.publicKey.buffer - ); + const fingerprint = await this.cryptoService.getFingerprint(this.userId, this.publicKey); if (fingerprint != null) { this.fingerprint = fingerprint.join("-"); } diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts index 669a8954ee..31ced8aee1 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts @@ -47,7 +47,7 @@ export class BulkConfirmComponent implements OnInit { for (const entry of response.data) { const publicKey = Utils.fromB64ToArray(entry.key); - const fingerprint = await this.cryptoService.getFingerprint(entry.userId, publicKey.buffer); + const fingerprint = await this.cryptoService.getFingerprint(entry.userId, publicKey); if (fingerprint != null) { this.publicKeys.set(entry.id, publicKey); this.fingerprints.set(entry.id, fingerprint.join("-")); @@ -67,7 +67,7 @@ export class BulkConfirmComponent implements OnInit { if (publicKey == null) { continue; } - const encryptedKey = await this.cryptoService.rsaEncrypt(key.key, publicKey.buffer); + const encryptedKey = await this.cryptoService.rsaEncrypt(key.key, publicKey); userIdsWithKeys.push({ id: user.id, key: encryptedKey.encryptedString, diff --git a/apps/web/src/app/admin-console/organizations/members/people.component.ts b/apps/web/src/app/admin-console/organizations/members/people.component.ts index ec353fa7ff..8c4d565c85 100644 --- a/apps/web/src/app/admin-console/organizations/members/people.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/people.component.ts @@ -302,7 +302,7 @@ export class PeopleComponent async confirmUser(user: OrganizationUserView, publicKey: Uint8Array): Promise { const orgKey = await this.cryptoService.getOrgKey(this.organization.id); - const key = await this.cryptoService.rsaEncrypt(orgKey.key, publicKey.buffer); + const key = await this.cryptoService.rsaEncrypt(orgKey.key, publicKey); const request = new OrganizationUserConfirmRequest(); request.key = key.encryptedString; await this.organizationUserService.postOrganizationUserConfirm( diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts index 48dcb2c88c..f8628ed1f7 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts @@ -61,7 +61,7 @@ export class AccountComponent { }); protected organizationId: string; - protected publicKeyBuffer: ArrayBuffer; + protected publicKeyBuffer: Uint8Array; private destroy$ = new Subject(); @@ -106,7 +106,7 @@ export class AccountComponent { this.org = orgResponse; // Public Key Buffer for Org Fingerprint Generation - this.publicKeyBuffer = Utils.fromB64ToArray(orgKeys?.publicKey)?.buffer; + this.publicKeyBuffer = Utils.fromB64ToArray(orgKeys?.publicKey); // Patch existing values this.formGroup.patchValue({ diff --git a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts index 882bb78cea..a533967d8f 100644 --- a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts +++ b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts @@ -59,7 +59,7 @@ export class EnrollMasterPasswordReset { // RSA Encrypt user's encKey.key with organization public key const encKey = await this.cryptoService.getEncKey(); - const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey.buffer); + const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey); keyString = encryptedKey.encryptedString; toastStringRef = "enrollPasswordResetSuccess"; diff --git a/apps/web/src/app/auth/accept-organization.component.ts b/apps/web/src/app/auth/accept-organization.component.ts index 291cea09a4..bdc3511f76 100644 --- a/apps/web/src/app/auth/accept-organization.component.ts +++ b/apps/web/src/app/auth/accept-organization.component.ts @@ -142,7 +142,7 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { // RSA Encrypt user's encKey.key with organization public key const encKey = await this.cryptoService.getEncKey(); - const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey.buffer); + const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey); // Add reset password key to accept request request.resetPasswordKey = encryptedKey.encryptedString; diff --git a/apps/web/src/app/auth/settings/emergency-access/emergency-access-confirm.component.ts b/apps/web/src/app/auth/settings/emergency-access/emergency-access-confirm.component.ts index 1b5cf883d4..d55d1ceb42 100644 --- a/apps/web/src/app/auth/settings/emergency-access/emergency-access-confirm.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/emergency-access-confirm.component.ts @@ -33,7 +33,7 @@ export class EmergencyAccessConfirmComponent implements OnInit { const publicKeyResponse = await this.apiService.getUserPublicKey(this.userId); if (publicKeyResponse != null) { const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey); - const fingerprint = await this.cryptoService.getFingerprint(this.userId, publicKey.buffer); + const fingerprint = await this.cryptoService.getFingerprint(this.userId, publicKey); if (fingerprint != null) { this.fingerprint = fingerprint.join("-"); } diff --git a/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.ts b/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.ts index 73f8f1f653..fd1158ed47 100644 --- a/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/emergency-access.component.ts @@ -309,13 +309,13 @@ export class EmergencyAccessComponent implements OnInit { try { this.logService.debug( "User's fingerprint: " + - (await this.cryptoService.getFingerprint(details.granteeId, publicKey.buffer)).join("-") + (await this.cryptoService.getFingerprint(details.granteeId, publicKey)).join("-") ); } catch { // Ignore errors since it's just a debug message } - const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey.buffer); + const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey); const request = new EmergencyAccessConfirmRequest(); request.key = encryptedKey.encryptedString; await this.apiService.postEmergencyAccessConfirm(details.id, request); diff --git a/apps/web/src/app/common/base.people.component.ts b/apps/web/src/app/common/base.people.component.ts index d6044bc9ef..e14706c2e9 100644 --- a/apps/web/src/app/common/base.people.component.ts +++ b/apps/web/src/app/common/base.people.component.ts @@ -374,7 +374,7 @@ export abstract class BasePeopleComponent< } try { - const fingerprint = await this.cryptoService.getFingerprint(user.userId, publicKey.buffer); + const fingerprint = await this.cryptoService.getFingerprint(user.userId, publicKey); this.logService.info(`User's fingerprint: ${fingerprint.join("-")}`); } catch (e) { this.logService.error(e); diff --git a/apps/web/src/app/settings/change-password.component.ts b/apps/web/src/app/settings/change-password.component.ts index 8b57c6cfb6..4b6d5d5af4 100644 --- a/apps/web/src/app/settings/change-password.component.ts +++ b/apps/web/src/app/settings/change-password.component.ts @@ -274,7 +274,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { const publicKeyResponse = await this.apiService.getUserPublicKey(details.granteeId); const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey); - const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey.buffer); + const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey); const updateRequest = new EmergencyAccessUpdateRequest(); updateRequest.type = details.type; @@ -299,7 +299,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { const publicKey = Utils.fromB64ToArray(response?.publicKey); // Re-enroll - encrypt user's encKey.key with organization public key - const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey.buffer); + const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey); // Create/Execute request const request = new OrganizationUserResetPasswordEnrollmentRequest(); diff --git a/apps/web/src/app/shared/components/account-fingerprint/account-fingerprint.component.ts b/apps/web/src/app/shared/components/account-fingerprint/account-fingerprint.component.ts index f3d67880f2..012b9c5223 100644 --- a/apps/web/src/app/shared/components/account-fingerprint/account-fingerprint.component.ts +++ b/apps/web/src/app/shared/components/account-fingerprint/account-fingerprint.component.ts @@ -12,7 +12,7 @@ import { SharedModule } from "../../shared.module"; }) export class AccountFingerprintComponent implements OnInit { @Input() fingerprintMaterial: string; - @Input() publicKeyBuffer: ArrayBuffer; + @Input() publicKeyBuffer: Uint8Array; @Input() fingerprintLabel: string; protected fingerprint: string; diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts index cfe5e41719..e673634843 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts @@ -277,7 +277,7 @@ function createCipherView(i: number, deleted = false): CipherView { view.attachments = [attachment]; } else if (i % 5 === 0) { const attachment = new AttachmentView(); - attachment.key = new SymmetricCryptoKey(new ArrayBuffer(32)); + attachment.key = new SymmetricCryptoKey(new Uint8Array(32)); view.attachments = [attachment]; } diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts index 6325a72f80..b0f7b8bcad 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts @@ -90,7 +90,7 @@ export class DeviceApprovalsComponent implements OnInit, OnDestroy { const userSymKey = new SymmetricCryptoKey(decValue); // Re-encrypt User's Symmetric Key with the Device Public Key - return await this.cryptoService.rsaEncrypt(userSymKey.key, devicePubKey.buffer); + return await this.cryptoService.rsaEncrypt(userSymKey.key, devicePubKey); } async approveRequest(authRequest: PendingAuthRequestView) { diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts index 304ab346f3..48cad98f1d 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts @@ -138,7 +138,7 @@ export class PeopleComponent async confirmUser(user: ProviderUserUserDetailsResponse, publicKey: Uint8Array): Promise { const providerKey = await this.cryptoService.getProviderKey(this.providerId); - const key = await this.cryptoService.rsaEncrypt(providerKey.key, publicKey.buffer); + const key = await this.cryptoService.rsaEncrypt(providerKey.key, publicKey); const request = new ProviderUserConfirmRequest(); request.key = key.encryptedString; await this.apiService.postProviderUserConfirm(this.providerId, user.id, request); diff --git a/libs/angular/src/auth/components/login-with-device.component.ts b/libs/angular/src/auth/components/login-with-device.component.ts index 7af5afeca7..a991f0a8b1 100644 --- a/libs/angular/src/auth/components/login-with-device.component.ts +++ b/libs/angular/src/auth/components/login-with-device.component.ts @@ -46,7 +46,7 @@ export class LoginWithDeviceComponent protected successRoute = "vault"; protected forcePasswordResetRoute = "update-temp-password"; private resendTimeout = 12000; - private authRequestKeyPair: [publicKey: ArrayBuffer, privateKey: ArrayBuffer]; + private authRequestKeyPair: [publicKey: Uint8Array, privateKey: Uint8Array]; constructor( protected router: Router, diff --git a/libs/angular/src/components/set-password.component.ts b/libs/angular/src/components/set-password.component.ts index a4ae3edb80..3f0d6eab5e 100644 --- a/libs/angular/src/components/set-password.component.ts +++ b/libs/angular/src/components/set-password.component.ts @@ -133,10 +133,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { // RSA Encrypt user's encKey.key with organization public key const userEncKey = await this.cryptoService.getEncKey(); - const encryptedKey = await this.cryptoService.rsaEncrypt( - userEncKey.key, - publicKey.buffer - ); + const encryptedKey = await this.cryptoService.rsaEncrypt(userEncKey.key, publicKey); const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest(); resetRequest.masterPasswordHash = masterPasswordHash; diff --git a/libs/angular/src/platform/pipes/fingerprint.pipe.ts b/libs/angular/src/platform/pipes/fingerprint.pipe.ts index 198b3a57f7..a01ec21e6c 100644 --- a/libs/angular/src/platform/pipes/fingerprint.pipe.ts +++ b/libs/angular/src/platform/pipes/fingerprint.pipe.ts @@ -15,10 +15,7 @@ export class FingerprintPipe { publicKey = Utils.fromB64ToArray(publicKey); } - const fingerprint = await this.cryptoService.getFingerprint( - fingerprintMaterial, - publicKey.buffer - ); + const fingerprint = await this.cryptoService.getFingerprint(fingerprintMaterial, publicKey); if (fingerprint != null) { return fingerprint.join("-"); diff --git a/libs/common/custom-matchers.d.ts b/libs/common/custom-matchers.d.ts new file mode 100644 index 0000000000..214529ff02 --- /dev/null +++ b/libs/common/custom-matchers.d.ts @@ -0,0 +1,15 @@ +import type { CustomMatchers } from "./test.setup"; + +// This declares the types for our custom matchers so that they're recognised by Typescript +// This file must also be included in the TS compilation (via the tsconfig.json "include" property) to be recognised by +// vscode + +/* eslint-disable */ +declare global { + namespace jest { + interface Expect extends CustomMatchers {} + interface Matchers extends CustomMatchers {} + interface InverseAsymmetricMatchers extends CustomMatchers {} + } +} +/* eslint-enable */ diff --git a/libs/common/spec/matchers/to-equal-buffer.ts b/libs/common/spec/matchers/to-equal-buffer.ts index 796d88cd82..e723d85a57 100644 --- a/libs/common/spec/matchers/to-equal-buffer.ts +++ b/libs/common/spec/matchers/to-equal-buffer.ts @@ -5,14 +5,11 @@ * (and optionally, the expected value) and then call toEqual() on the resulting Uint8Arrays. */ export const toEqualBuffer: jest.CustomMatcher = function ( - received: ArrayBuffer, - expected: Uint8Array | ArrayBuffer + received: ArrayBuffer | Uint8Array, + expected: ArrayBuffer | Uint8Array ) { received = new Uint8Array(received); - - if (expected instanceof ArrayBuffer) { - expected = new Uint8Array(expected); - } + expected = new Uint8Array(expected); if (this.equals(received, expected)) { return { diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index 7164b0ae4f..53d1b79922 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -302,13 +302,13 @@ export class AuthService implements AuthServiceAbstraction { ( await this.cryptoService.getKey() ).encKey, - pubKey.buffer + pubKey ); let encryptedMasterPassword = null; if ((await this.stateService.getKeyHash()) != null) { encryptedMasterPassword = await this.cryptoService.rsaEncrypt( Utils.fromUtf8ToArray(await this.stateService.getKeyHash()), - pubKey.buffer + pubKey ); } const request = new PasswordlessAuthRequest( diff --git a/libs/common/src/platform/abstractions/crypto-function.service.ts b/libs/common/src/platform/abstractions/crypto-function.service.ts index 98b47b0f38..7c76702532 100644 --- a/libs/common/src/platform/abstractions/crypto-function.service.ts +++ b/libs/common/src/platform/abstractions/crypto-function.service.ts @@ -4,67 +4,67 @@ import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; export abstract class CryptoFunctionService { pbkdf2: ( - password: string | ArrayBuffer, - salt: string | ArrayBuffer, + password: string | Uint8Array, + salt: string | Uint8Array, algorithm: "sha256" | "sha512", iterations: number - ) => Promise; + ) => Promise; argon2: ( - password: string | ArrayBuffer, - salt: string | ArrayBuffer, + password: string | Uint8Array, + salt: string | Uint8Array, iterations: number, memory: number, parallelism: number - ) => Promise; + ) => Promise; hkdf: ( - ikm: ArrayBuffer, - salt: string | ArrayBuffer, - info: string | ArrayBuffer, + ikm: Uint8Array, + salt: string | Uint8Array, + info: string | Uint8Array, outputByteSize: number, algorithm: "sha256" | "sha512" - ) => Promise; + ) => Promise; hkdfExpand: ( - prk: ArrayBuffer, - info: string | ArrayBuffer, + prk: Uint8Array, + info: string | Uint8Array, outputByteSize: number, algorithm: "sha256" | "sha512" - ) => Promise; + ) => Promise; hash: ( - value: string | ArrayBuffer, + value: string | Uint8Array, algorithm: "sha1" | "sha256" | "sha512" | "md5" - ) => Promise; + ) => Promise; hmac: ( - value: ArrayBuffer, - key: ArrayBuffer, + value: Uint8Array, + key: Uint8Array, algorithm: "sha1" | "sha256" | "sha512" - ) => Promise; - compare: (a: ArrayBuffer, b: ArrayBuffer) => Promise; + ) => Promise; + compare: (a: Uint8Array, b: Uint8Array) => Promise; hmacFast: ( - value: ArrayBuffer | string, - key: ArrayBuffer | string, + value: Uint8Array | string, + key: Uint8Array | string, algorithm: "sha1" | "sha256" | "sha512" - ) => Promise; - compareFast: (a: ArrayBuffer | string, b: ArrayBuffer | string) => Promise; - aesEncrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise; + ) => Promise; + compareFast: (a: Uint8Array | string, b: Uint8Array | string) => Promise; + aesEncrypt: (data: Uint8Array, iv: Uint8Array, key: Uint8Array) => Promise; aesDecryptFastParameters: ( data: string, iv: string, mac: string, key: SymmetricCryptoKey - ) => DecryptParameters; - aesDecryptFast: (parameters: DecryptParameters) => Promise; - aesDecrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise; + ) => DecryptParameters; + aesDecryptFast: (parameters: DecryptParameters) => Promise; + aesDecrypt: (data: Uint8Array, iv: Uint8Array, key: Uint8Array) => Promise; rsaEncrypt: ( - data: ArrayBuffer, - publicKey: ArrayBuffer, + data: Uint8Array, + publicKey: Uint8Array, algorithm: "sha1" | "sha256" - ) => Promise; + ) => Promise; rsaDecrypt: ( - data: ArrayBuffer, - privateKey: ArrayBuffer, + data: Uint8Array, + privateKey: Uint8Array, algorithm: "sha1" | "sha256" - ) => Promise; - rsaExtractPublicKey: (privateKey: ArrayBuffer) => Promise; - rsaGenerateKeyPair: (length: 1024 | 2048 | 4096) => Promise<[ArrayBuffer, ArrayBuffer]>; + ) => Promise; + rsaExtractPublicKey: (privateKey: Uint8Array) => Promise; + rsaGenerateKeyPair: (length: 1024 | 2048 | 4096) => Promise<[Uint8Array, Uint8Array]>; randomBytes: (length: number) => Promise; } diff --git a/libs/common/src/platform/abstractions/crypto.service.ts b/libs/common/src/platform/abstractions/crypto.service.ts index 990fed0c31..e8b95ff2f1 100644 --- a/libs/common/src/platform/abstractions/crypto.service.ts +++ b/libs/common/src/platform/abstractions/crypto.service.ts @@ -22,9 +22,9 @@ export abstract class CryptoService { getKeyHash: () => Promise; compareAndUpdateKeyHash: (masterPassword: string, key: SymmetricCryptoKey) => Promise; getEncKey: (key?: SymmetricCryptoKey) => Promise; - getPublicKey: () => Promise; - getPrivateKey: () => Promise; - getFingerprint: (fingerprintMaterial: string, publicKey?: ArrayBuffer) => Promise; + getPublicKey: () => Promise; + getPrivateKey: () => Promise; + getFingerprint: (fingerprintMaterial: string, publicKey?: Uint8Array) => Promise; getOrgKeys: () => Promise>; getOrgKey: (orgId: string) => Promise; getProviderKey: (providerId: string) => Promise; @@ -63,7 +63,7 @@ export abstract class CryptoService { kdf: KdfType, kdfConfig: KdfConfig ) => Promise; - makeSendKey: (keyMaterial: ArrayBuffer) => Promise; + makeSendKey: (keyMaterial: Uint8Array) => Promise; hashPassword: ( password: string, key: SymmetricCryptoKey, @@ -74,13 +74,13 @@ export abstract class CryptoService { key: SymmetricCryptoKey, encKey?: SymmetricCryptoKey ) => Promise<[SymmetricCryptoKey, EncString]>; - encrypt: (plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey) => Promise; - encryptToBytes: (plainValue: ArrayBuffer, key?: SymmetricCryptoKey) => Promise; - rsaEncrypt: (data: ArrayBuffer, publicKey?: ArrayBuffer) => Promise; - rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise; - decryptToBytes: (encString: EncString, key?: SymmetricCryptoKey) => Promise; + encrypt: (plainValue: string | Uint8Array, key?: SymmetricCryptoKey) => Promise; + encryptToBytes: (plainValue: Uint8Array, key?: SymmetricCryptoKey) => Promise; + rsaEncrypt: (data: Uint8Array, publicKey?: Uint8Array) => Promise; + rsaDecrypt: (encValue: string, privateKeyValue?: Uint8Array) => Promise; + decryptToBytes: (encString: EncString, key?: SymmetricCryptoKey) => Promise; decryptToUtf8: (encString: EncString, key?: SymmetricCryptoKey) => Promise; - decryptFromBytes: (encBuffer: EncArrayBuffer, key: SymmetricCryptoKey) => Promise; + decryptFromBytes: (encBuffer: EncArrayBuffer, key: SymmetricCryptoKey) => Promise; randomNumber: (min: number, max: number) => Promise; validateKey: (key: SymmetricCryptoKey) => Promise; } diff --git a/libs/common/src/platform/abstractions/encrypt.service.ts b/libs/common/src/platform/abstractions/encrypt.service.ts index 98802b5b3b..588a8bb043 100644 --- a/libs/common/src/platform/abstractions/encrypt.service.ts +++ b/libs/common/src/platform/abstractions/encrypt.service.ts @@ -6,13 +6,13 @@ import { EncString } from "../models/domain/enc-string"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; export abstract class EncryptService { - abstract encrypt(plainValue: string | ArrayBuffer, key: SymmetricCryptoKey): Promise; + abstract encrypt(plainValue: string | Uint8Array, key: SymmetricCryptoKey): Promise; abstract encryptToBytes: ( - plainValue: ArrayBuffer, + plainValue: Uint8Array, key?: SymmetricCryptoKey ) => Promise; abstract decryptToUtf8: (encString: EncString, key: SymmetricCryptoKey) => Promise; - abstract decryptToBytes: (encThing: Encrypted, key: SymmetricCryptoKey) => Promise; + abstract decryptToBytes: (encThing: Encrypted, key: SymmetricCryptoKey) => Promise; abstract resolveLegacyKey: (key: SymmetricCryptoKey, encThing: Encrypted) => SymmetricCryptoKey; abstract decryptItems: ( items: Decryptable[], diff --git a/libs/common/src/platform/abstractions/state.service.ts b/libs/common/src/platform/abstractions/state.service.ts index e5b4e17fb8..80159a9a8b 100644 --- a/libs/common/src/platform/abstractions/state.service.ts +++ b/libs/common/src/platform/abstractions/state.service.ts @@ -113,8 +113,8 @@ export abstract class StateService { * @deprecated Do not call this, use PolicyService */ setDecryptedPolicies: (value: Policy[], options?: StorageOptions) => Promise; - getDecryptedPrivateKey: (options?: StorageOptions) => Promise; - setDecryptedPrivateKey: (value: ArrayBuffer, options?: StorageOptions) => Promise; + getDecryptedPrivateKey: (options?: StorageOptions) => Promise; + setDecryptedPrivateKey: (value: Uint8Array, options?: StorageOptions) => Promise; getDecryptedProviderKeys: (options?: StorageOptions) => Promise>; setDecryptedProviderKeys: ( value: Map, @@ -331,8 +331,8 @@ export abstract class StateService { setProtectedPin: (value: string, options?: StorageOptions) => Promise; getProviders: (options?: StorageOptions) => Promise<{ [id: string]: ProviderData }>; setProviders: (value: { [id: string]: ProviderData }, options?: StorageOptions) => Promise; - getPublicKey: (options?: StorageOptions) => Promise; - setPublicKey: (value: ArrayBuffer, options?: StorageOptions) => Promise; + getPublicKey: (options?: StorageOptions) => Promise; + setPublicKey: (value: Uint8Array, options?: StorageOptions) => Promise; getRefreshToken: (options?: StorageOptions) => Promise; setRefreshToken: (value: string, options?: StorageOptions) => Promise; getRememberedEmail: (options?: StorageOptions) => Promise; diff --git a/libs/common/src/platform/interfaces/encrypted.ts b/libs/common/src/platform/interfaces/encrypted.ts index a5730302dc..26a49a32ec 100644 --- a/libs/common/src/platform/interfaces/encrypted.ts +++ b/libs/common/src/platform/interfaces/encrypted.ts @@ -2,7 +2,7 @@ import { EncryptionType } from "../../enums"; export interface Encrypted { encryptionType?: EncryptionType; - dataBytes: ArrayBuffer; - macBytes: ArrayBuffer; - ivBytes: ArrayBuffer; + dataBytes: Uint8Array; + macBytes: Uint8Array; + ivBytes: Uint8Array; } diff --git a/libs/common/src/platform/models/domain/account-keys.spec.ts b/libs/common/src/platform/models/domain/account-keys.spec.ts index 88183229f5..36fdb6a276 100644 --- a/libs/common/src/platform/models/domain/account-keys.spec.ts +++ b/libs/common/src/platform/models/domain/account-keys.spec.ts @@ -8,7 +8,7 @@ describe("AccountKeys", () => { describe("toJSON", () => { it("should serialize itself", () => { const keys = new AccountKeys(); - const buffer = makeStaticByteArray(64).buffer; + const buffer = makeStaticByteArray(64); keys.publicKey = buffer; const bufferSpy = jest.spyOn(Utils, "fromBufferToByteString"); @@ -18,7 +18,7 @@ describe("AccountKeys", () => { it("should serialize public key as a string", () => { const keys = new AccountKeys(); - keys.publicKey = Utils.fromByteStringToArray("hello").buffer; + keys.publicKey = Utils.fromByteStringToArray("hello"); const json = JSON.stringify(keys); expect(json).toContain('"publicKey":"hello"'); }); @@ -29,7 +29,7 @@ describe("AccountKeys", () => { const keys = AccountKeys.fromJSON({ publicKey: "hello", }); - expect(keys.publicKey).toEqual(Utils.fromByteStringToArray("hello").buffer); + expect(keys.publicKey).toEqual(Utils.fromByteStringToArray("hello")); }); it("should deserialize cryptoMasterKey", () => { diff --git a/libs/common/src/platform/models/domain/account.ts b/libs/common/src/platform/models/domain/account.ts index a8cea255c1..f40df3aa53 100644 --- a/libs/common/src/platform/models/domain/account.ts +++ b/libs/common/src/platform/models/domain/account.ts @@ -119,8 +119,8 @@ export class AccountKeys { any, Record >(); - privateKey?: EncryptionPair = new EncryptionPair(); - publicKey?: ArrayBuffer; + privateKey?: EncryptionPair = new EncryptionPair(); + publicKey?: Uint8Array; apiKeyClientSecret?: string; toJSON() { @@ -142,11 +142,10 @@ export class AccountKeys { ), organizationKeys: AccountKeys.initRecordEncryptionPairsFromJSON(obj?.organizationKeys), providerKeys: AccountKeys.initRecordEncryptionPairsFromJSON(obj?.providerKeys), - privateKey: EncryptionPair.fromJSON( - obj?.privateKey, - (decObj: string) => Utils.fromByteStringToArray(decObj).buffer + privateKey: EncryptionPair.fromJSON(obj?.privateKey, (decObj: string) => + Utils.fromByteStringToArray(decObj) ), - publicKey: Utils.fromByteStringToArray(obj?.publicKey)?.buffer, + publicKey: Utils.fromByteStringToArray(obj?.publicKey), }); } diff --git a/libs/common/src/platform/models/domain/enc-array-buffer.spec.ts b/libs/common/src/platform/models/domain/enc-array-buffer.spec.ts index 9e15d05e88..4e0464fd62 100644 --- a/libs/common/src/platform/models/domain/enc-array-buffer.spec.ts +++ b/libs/common/src/platform/models/domain/enc-array-buffer.spec.ts @@ -20,7 +20,7 @@ describe("encArrayBuffer", () => { array.set(mac, 1 + iv.byteLength); array.set(data, 1 + iv.byteLength + mac.byteLength); - const actual = new EncArrayBuffer(array.buffer); + const actual = new EncArrayBuffer(array); expect(actual.encryptionType).toEqual(encType); expect(actual.ivBytes).toEqualBuffer(iv); @@ -39,11 +39,11 @@ describe("encArrayBuffer", () => { array.set(iv, 1); array.set(data, 1 + iv.byteLength); - const actual = new EncArrayBuffer(array.buffer); + const actual = new EncArrayBuffer(array); expect(actual.encryptionType).toEqual(encType); - expect(actual.ivBytes).toEqualBuffer(iv); - expect(actual.dataBytes).toEqualBuffer(data); + expect(actual.ivBytes).toEqual(iv); + expect(actual.dataBytes).toEqual(data); expect(actual.macBytes).toBeNull(); }); }); @@ -58,13 +58,11 @@ describe("encArrayBuffer", () => { // Minus 1 to leave room for the encType, minus 1 to make it invalid const invalidBytes = makeStaticByteArray(minLength - 2); - const invalidArray = new Uint8Array(1 + invalidBytes.buffer.byteLength); + const invalidArray = new Uint8Array(1 + invalidBytes.byteLength); invalidArray.set([encType]); invalidArray.set(invalidBytes, 1); - expect(() => new EncArrayBuffer(invalidArray.buffer)).toThrow( - "Error parsing encrypted ArrayBuffer" - ); + expect(() => new EncArrayBuffer(invalidArray)).toThrow("Error parsing encrypted ArrayBuffer"); }); }); diff --git a/libs/common/src/platform/models/domain/enc-array-buffer.ts b/libs/common/src/platform/models/domain/enc-array-buffer.ts index afaa4a5fb6..afe9bad57f 100644 --- a/libs/common/src/platform/models/domain/enc-array-buffer.ts +++ b/libs/common/src/platform/models/domain/enc-array-buffer.ts @@ -9,12 +9,12 @@ const MIN_DATA_LENGTH = 1; export class EncArrayBuffer implements Encrypted { readonly encryptionType: EncryptionType = null; - readonly dataBytes: ArrayBuffer = null; - readonly ivBytes: ArrayBuffer = null; - readonly macBytes: ArrayBuffer = null; + readonly dataBytes: Uint8Array = null; + readonly ivBytes: Uint8Array = null; + readonly macBytes: Uint8Array = null; - constructor(readonly buffer: ArrayBuffer) { - const encBytes = new Uint8Array(buffer); + constructor(readonly buffer: Uint8Array) { + const encBytes = buffer; const encType = encBytes[0]; switch (encType) { @@ -25,12 +25,12 @@ export class EncArrayBuffer implements Encrypted { this.throwDecryptionError(); } - this.ivBytes = encBytes.slice(ENC_TYPE_LENGTH, ENC_TYPE_LENGTH + IV_LENGTH).buffer; + this.ivBytes = encBytes.slice(ENC_TYPE_LENGTH, ENC_TYPE_LENGTH + IV_LENGTH); this.macBytes = encBytes.slice( ENC_TYPE_LENGTH + IV_LENGTH, ENC_TYPE_LENGTH + IV_LENGTH + MAC_LENGTH - ).buffer; - this.dataBytes = encBytes.slice(ENC_TYPE_LENGTH + IV_LENGTH + MAC_LENGTH).buffer; + ); + this.dataBytes = encBytes.slice(ENC_TYPE_LENGTH + IV_LENGTH + MAC_LENGTH); break; } case EncryptionType.AesCbc256_B64: { @@ -39,8 +39,8 @@ export class EncArrayBuffer implements Encrypted { this.throwDecryptionError(); } - this.ivBytes = encBytes.slice(ENC_TYPE_LENGTH, ENC_TYPE_LENGTH + IV_LENGTH).buffer; - this.dataBytes = encBytes.slice(ENC_TYPE_LENGTH + IV_LENGTH).buffer; + this.ivBytes = encBytes.slice(ENC_TYPE_LENGTH, ENC_TYPE_LENGTH + IV_LENGTH); + this.dataBytes = encBytes.slice(ENC_TYPE_LENGTH + IV_LENGTH); break; } default: @@ -63,11 +63,11 @@ export class EncArrayBuffer implements Encrypted { if (buffer == null) { throw new Error("Cannot create EncArrayBuffer from Response - Response is empty"); } - return new EncArrayBuffer(buffer); + return new EncArrayBuffer(new Uint8Array(buffer)); } static fromB64(b64: string) { - const buffer = Utils.fromB64ToArray(b64).buffer; + const buffer = Utils.fromB64ToArray(b64); return new EncArrayBuffer(buffer); } } diff --git a/libs/common/src/platform/models/domain/enc-string.ts b/libs/common/src/platform/models/domain/enc-string.ts index 8fe9fbe93f..017e021114 100644 --- a/libs/common/src/platform/models/domain/enc-string.ts +++ b/libs/common/src/platform/models/domain/enc-string.ts @@ -27,16 +27,16 @@ export class EncString implements Encrypted { } } - get ivBytes(): ArrayBuffer { - return this.iv == null ? null : Utils.fromB64ToArray(this.iv).buffer; + get ivBytes(): Uint8Array { + return this.iv == null ? null : Utils.fromB64ToArray(this.iv); } - get macBytes(): ArrayBuffer { - return this.mac == null ? null : Utils.fromB64ToArray(this.mac).buffer; + get macBytes(): Uint8Array { + return this.mac == null ? null : Utils.fromB64ToArray(this.mac); } - get dataBytes(): ArrayBuffer { - return this.data == null ? null : Utils.fromB64ToArray(this.data).buffer; + get dataBytes(): Uint8Array { + return this.data == null ? null : Utils.fromB64ToArray(this.data); } toJSON() { diff --git a/libs/common/src/platform/models/domain/encrypted-object.ts b/libs/common/src/platform/models/domain/encrypted-object.ts index 7679788efa..22d5a38811 100644 --- a/libs/common/src/platform/models/domain/encrypted-object.ts +++ b/libs/common/src/platform/models/domain/encrypted-object.ts @@ -1,8 +1,8 @@ import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; export class EncryptedObject { - iv: ArrayBuffer; - data: ArrayBuffer; - mac: ArrayBuffer; + iv: Uint8Array; + data: Uint8Array; + mac: Uint8Array; key: SymmetricCryptoKey; } diff --git a/libs/common/src/platform/models/domain/encryption-pair.spec.ts b/libs/common/src/platform/models/domain/encryption-pair.spec.ts index c91cb3d2b9..1418c125ed 100644 --- a/libs/common/src/platform/models/domain/encryption-pair.spec.ts +++ b/libs/common/src/platform/models/domain/encryption-pair.spec.ts @@ -11,6 +11,13 @@ describe("EncryptionPair", () => { expect(json.decrypted).toEqual("hello"); }); + it("should populate decryptedSerialized for TypesArrays", () => { + const pair = new EncryptionPair(); + pair.decrypted = Utils.fromByteStringToArray("hello"); + const json = pair.toJSON(); + expect(json.decrypted).toEqual(new Uint8Array([104, 101, 108, 108, 111])); + }); + it("should serialize encrypted and decrypted", () => { const pair = new EncryptionPair(); pair.encrypted = "hello"; diff --git a/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts b/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts index 85aae2e30e..13d5835c91 100644 --- a/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts +++ b/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts @@ -68,7 +68,7 @@ describe("SymmetricCryptoKey", () => { }); it("toJSON creates object for serialization", () => { - const key = new SymmetricCryptoKey(makeStaticByteArray(64).buffer); + const key = new SymmetricCryptoKey(makeStaticByteArray(64)); const actual = key.toJSON(); const expected = { keyB64: key.keyB64 }; @@ -77,7 +77,7 @@ describe("SymmetricCryptoKey", () => { }); it("fromJSON hydrates new object", () => { - const expected = new SymmetricCryptoKey(makeStaticByteArray(64).buffer); + const expected = new SymmetricCryptoKey(makeStaticByteArray(64)); const actual = SymmetricCryptoKey.fromJSON({ keyB64: expected.keyB64 }); expect(actual).toEqual(expected); 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 a553b1ae4f..8ba6c41127 100644 --- a/libs/common/src/platform/models/domain/symmetric-crypto-key.ts +++ b/libs/common/src/platform/models/domain/symmetric-crypto-key.ts @@ -4,9 +4,9 @@ import { EncryptionType } from "../../../enums"; import { Utils } from "../../../platform/misc/utils"; export class SymmetricCryptoKey { - key: ArrayBuffer; - encKey?: ArrayBuffer; - macKey?: ArrayBuffer; + key: Uint8Array; + encKey?: Uint8Array; + macKey?: Uint8Array; encType: EncryptionType; keyB64: string; @@ -15,7 +15,7 @@ export class SymmetricCryptoKey { meta: any; - constructor(key: ArrayBuffer, encType?: EncryptionType) { + constructor(key: Uint8Array, encType?: EncryptionType) { if (key == null) { throw new Error("Must provide key"); } @@ -67,7 +67,7 @@ export class SymmetricCryptoKey { return null; } - const arrayBuffer = Utils.fromB64ToArray(s).buffer; + const arrayBuffer = Utils.fromB64ToArray(s); return new SymmetricCryptoKey(arrayBuffer); } diff --git a/libs/common/src/platform/services/crypto.service.ts b/libs/common/src/platform/services/crypto.service.ts index 6dc4a8c883..c42c7cc32a 100644 --- a/libs/common/src/platform/services/crypto.service.ts +++ b/libs/common/src/platform/services/crypto.service.ts @@ -123,7 +123,7 @@ export class CryptoService implements CryptoServiceAbstraction { ): Promise { const key = await this.retrieveKeyFromStorage(keySuffix, userId); if (key != null) { - const symmetricKey = new SymmetricCryptoKey(Utils.fromB64ToArray(key).buffer); + const symmetricKey = new SymmetricCryptoKey(Utils.fromB64ToArray(key)); if (!(await this.validateKey(symmetricKey))) { this.logService.warning("Wrong key, throwing away stored key"); @@ -172,7 +172,7 @@ export class CryptoService implements CryptoServiceAbstraction { return this.getEncKeyHelper(key); } - async getPublicKey(): Promise { + async getPublicKey(): Promise { const inMemoryPublicKey = await this.stateService.getPublicKey(); if (inMemoryPublicKey != null) { return inMemoryPublicKey; @@ -188,7 +188,7 @@ export class CryptoService implements CryptoServiceAbstraction { return publicKey; } - async getPrivateKey(): Promise { + async getPrivateKey(): Promise { const decryptedPrivateKey = await this.stateService.getDecryptedPrivateKey(); if (decryptedPrivateKey != null) { return decryptedPrivateKey; @@ -204,7 +204,7 @@ export class CryptoService implements CryptoServiceAbstraction { return privateKey; } - async getFingerprint(fingerprintMaterial: string, publicKey?: ArrayBuffer): Promise { + async getFingerprint(fingerprintMaterial: string, publicKey?: Uint8Array): Promise { if (publicKey == null) { publicKey = await this.getPublicKey(); } @@ -416,7 +416,7 @@ export class CryptoService implements CryptoServiceAbstraction { kdf: KdfType, kdfConfig: KdfConfig ): Promise { - let key: ArrayBuffer = null; + let key: Uint8Array = null; if (kdf == null || kdf === KdfType.PBKDF2_SHA256) { if (kdfConfig.iterations == null) { kdfConfig.iterations = 5000; @@ -502,7 +502,7 @@ export class CryptoService implements CryptoServiceAbstraction { return await this.stretchKey(pinKey); } - async makeSendKey(keyMaterial: ArrayBuffer): Promise { + async makeSendKey(keyMaterial: Uint8Array): Promise { const sendKey = await this.cryptoFunctionService.hkdf( keyMaterial, "bitwarden-send", @@ -550,7 +550,7 @@ export class CryptoService implements CryptoServiceAbstraction { * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey) * and then call encryptService.encrypt */ - async encrypt(plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey): Promise { + async encrypt(plainValue: string | Uint8Array, key?: SymmetricCryptoKey): Promise { key = await this.getKeyForUserEncryption(key); return await this.encryptService.encrypt(plainValue, key); } @@ -559,12 +559,12 @@ export class CryptoService implements CryptoServiceAbstraction { * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey) * and then call encryptService.encryptToBytes */ - async encryptToBytes(plainValue: ArrayBuffer, key?: SymmetricCryptoKey): Promise { + async encryptToBytes(plainValue: Uint8Array, key?: SymmetricCryptoKey): Promise { key = await this.getKeyForUserEncryption(key); return this.encryptService.encryptToBytes(plainValue, key); } - async rsaEncrypt(data: ArrayBuffer, publicKey?: ArrayBuffer): Promise { + async rsaEncrypt(data: Uint8Array, publicKey?: Uint8Array): Promise { if (publicKey == null) { publicKey = await this.getPublicKey(); } @@ -576,7 +576,7 @@ export class CryptoService implements CryptoServiceAbstraction { return new EncString(EncryptionType.Rsa2048_OaepSha1_B64, Utils.fromBufferToB64(encBytes)); } - async rsaDecrypt(encValue: string, privateKeyValue?: ArrayBuffer): Promise { + async rsaDecrypt(encValue: string, privateKeyValue?: Uint8Array): Promise { const headerPieces = encValue.split("."); let encType: EncryptionType = null; let encPieces: string[]; @@ -607,7 +607,7 @@ export class CryptoService implements CryptoServiceAbstraction { throw new Error("encPieces unavailable."); } - const data = Utils.fromB64ToArray(encPieces[0]).buffer; + const data = Utils.fromB64ToArray(encPieces[0]); const privateKey = privateKeyValue ?? (await this.getPrivateKey()); if (privateKey == null) { throw new Error("No private key."); @@ -633,7 +633,7 @@ export class CryptoService implements CryptoServiceAbstraction { * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey) * and then call encryptService.decryptToBytes */ - async decryptToBytes(encString: EncString, key?: SymmetricCryptoKey): Promise { + async decryptToBytes(encString: EncString, key?: SymmetricCryptoKey): Promise { const keyForEnc = await this.getKeyForUserEncryption(key); return this.encryptService.decryptToBytes(encString, keyForEnc); } @@ -651,7 +651,7 @@ export class CryptoService implements CryptoServiceAbstraction { * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey) * and then call encryptService.decryptToBytes */ - async decryptFromBytes(encBuffer: EncArrayBuffer, key: SymmetricCryptoKey): Promise { + async decryptFromBytes(encBuffer: EncArrayBuffer, key: SymmetricCryptoKey): Promise { if (encBuffer == null) { throw new Error("No buffer provided for decryption."); } @@ -768,10 +768,10 @@ export class CryptoService implements CryptoServiceAbstraction { const macKey = await this.cryptoFunctionService.hkdfExpand(key.key, "mac", 32, "sha256"); newKey.set(new Uint8Array(encKey)); newKey.set(new Uint8Array(macKey), 32); - return new SymmetricCryptoKey(newKey.buffer); + return new SymmetricCryptoKey(newKey); } - private async hashPhrase(hash: ArrayBuffer, minimumEntropy = 64) { + private async hashPhrase(hash: Uint8Array, minimumEntropy = 64) { const entropyPerWord = Math.log(EFFLongWordList.length) / Math.log(2); let numWords = Math.ceil(minimumEntropy / entropyPerWord); @@ -793,7 +793,7 @@ export class CryptoService implements CryptoServiceAbstraction { private async buildEncKey( key: SymmetricCryptoKey, - encKey: ArrayBuffer + encKey: Uint8Array ): Promise<[SymmetricCryptoKey, EncString]> { let encKeyEnc: EncString = null; if (key.key.byteLength === 32) { @@ -830,7 +830,7 @@ export class CryptoService implements CryptoServiceAbstraction { return null; } - let decEncKey: ArrayBuffer; + let decEncKey: Uint8Array; const encKeyCipher = new EncString(encKey); if (encKeyCipher.encryptionType === EncryptionType.AesCbc256_B64) { decEncKey = await this.decryptToBytes(encKeyCipher, key); diff --git a/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts b/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts index 2ccd6208ce..d847dcf77b 100644 --- a/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts +++ b/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts @@ -18,7 +18,7 @@ export class EncryptServiceImplementation implements EncryptService { protected logMacFailures: boolean ) {} - async encrypt(plainValue: string | ArrayBuffer, key: SymmetricCryptoKey): Promise { + async encrypt(plainValue: string | Uint8Array, key: SymmetricCryptoKey): Promise { if (key == null) { throw new Error("No encryption key provided."); } @@ -27,9 +27,9 @@ export class EncryptServiceImplementation implements EncryptService { return Promise.resolve(null); } - let plainBuf: ArrayBuffer; + let plainBuf: Uint8Array; if (typeof plainValue === "string") { - plainBuf = Utils.fromUtf8ToArray(plainValue).buffer; + plainBuf = Utils.fromUtf8ToArray(plainValue); } else { plainBuf = plainValue; } @@ -41,7 +41,7 @@ export class EncryptServiceImplementation implements EncryptService { return new EncString(encObj.key.encType, data, iv, mac); } - async encryptToBytes(plainValue: ArrayBuffer, key: SymmetricCryptoKey): Promise { + async encryptToBytes(plainValue: Uint8Array, key: SymmetricCryptoKey): Promise { if (key == null) { throw new Error("No encryption key provided."); } @@ -60,7 +60,7 @@ export class EncryptServiceImplementation implements EncryptService { } encBytes.set(new Uint8Array(encValue.data), 1 + encValue.iv.byteLength + macLen); - return new EncArrayBuffer(encBytes.buffer); + return new EncArrayBuffer(encBytes); } async decryptToUtf8(encString: EncString, key: SymmetricCryptoKey): Promise { @@ -102,7 +102,7 @@ export class EncryptServiceImplementation implements EncryptService { return await this.cryptoFunctionService.aesDecryptFast(fastParams); } - async decryptToBytes(encThing: Encrypted, key: SymmetricCryptoKey): Promise { + async decryptToBytes(encThing: Encrypted, key: SymmetricCryptoKey): Promise { if (key == null) { throw new Error("No encryption key provided."); } @@ -125,11 +125,7 @@ export class EncryptServiceImplementation implements EncryptService { 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.buffer, - key.macKey, - "sha256" - ); + const computedMac = await this.cryptoFunctionService.hmac(macData, key.macKey, "sha256"); if (computedMac === null) { return null; } @@ -161,7 +157,7 @@ export class EncryptServiceImplementation implements EncryptService { return await Promise.all(items.map((item) => item.decrypt(key))); } - private async aesEncrypt(data: ArrayBuffer, key: SymmetricCryptoKey): Promise { + private async aesEncrypt(data: Uint8Array, key: SymmetricCryptoKey): Promise { const obj = new EncryptedObject(); obj.key = key; obj.iv = await this.cryptoFunctionService.randomBytes(16); @@ -171,7 +167,7 @@ export class EncryptServiceImplementation implements EncryptService { const macData = new Uint8Array(obj.iv.byteLength + obj.data.byteLength); macData.set(new Uint8Array(obj.iv), 0); macData.set(new Uint8Array(obj.data), obj.iv.byteLength); - obj.mac = await this.cryptoFunctionService.hmac(macData.buffer, obj.key.macKey, "sha256"); + obj.mac = await this.cryptoFunctionService.hmac(macData, obj.key.macKey, "sha256"); } return obj; diff --git a/libs/common/src/platform/services/encrypt.service.spec.ts b/libs/common/src/platform/services/encrypt.service.spec.ts index 4a69bb7edc..bc1be50a16 100644 --- a/libs/common/src/platform/services/encrypt.service.spec.ts +++ b/libs/common/src/platform/services/encrypt.service.spec.ts @@ -37,10 +37,8 @@ describe("EncryptService", () => { describe("encrypts data", () => { beforeEach(() => { - cryptoFunctionService.randomBytes - .calledWith(16) - .mockResolvedValueOnce(iv.buffer as CsprngArray); - cryptoFunctionService.aesEncrypt.mockResolvedValue(encryptedData.buffer); + cryptoFunctionService.randomBytes.calledWith(16).mockResolvedValueOnce(iv as CsprngArray); + cryptoFunctionService.aesEncrypt.mockResolvedValue(encryptedData); }); it("using a key which supports mac", async () => { @@ -50,7 +48,7 @@ describe("EncryptService", () => { key.macKey = makeStaticByteArray(16, 20); - cryptoFunctionService.hmac.mockResolvedValue(mac.buffer); + cryptoFunctionService.hmac.mockResolvedValue(mac); const actual = await encryptService.encryptToBytes(plainValue, key); @@ -86,7 +84,7 @@ describe("EncryptService", () => { describe("decryptToBytes", () => { const encType = EncryptionType.AesCbc256_HmacSha256_B64; const key = new SymmetricCryptoKey(makeStaticByteArray(64, 100), encType); - const computedMac = new Uint8Array(1).buffer; + const computedMac = new Uint8Array(1); const encBuffer = new EncArrayBuffer(makeStaticByteArray(60, encType)); beforeEach(() => { @@ -106,9 +104,9 @@ describe("EncryptService", () => { }); it("decrypts data with provided key", async () => { - const decryptedBytes = makeStaticByteArray(10, 200).buffer; + const decryptedBytes = makeStaticByteArray(10, 200); - cryptoFunctionService.hmac.mockResolvedValue(makeStaticByteArray(1).buffer); + cryptoFunctionService.hmac.mockResolvedValue(makeStaticByteArray(1)); cryptoFunctionService.compare.mockResolvedValue(true); cryptoFunctionService.aesDecrypt.mockResolvedValueOnce(decryptedBytes); diff --git a/libs/common/src/platform/services/state.service.ts b/libs/common/src/platform/services/state.service.ts index 57b2a47da8..98525c16b0 100644 --- a/libs/common/src/platform/services/state.service.ts +++ b/libs/common/src/platform/services/state.service.ts @@ -763,13 +763,13 @@ export class StateService< ); } - async getDecryptedPrivateKey(options?: StorageOptions): Promise { + async getDecryptedPrivateKey(options?: StorageOptions): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) )?.keys?.privateKey.decrypted; } - async setDecryptedPrivateKey(value: ArrayBuffer, options?: StorageOptions): Promise { + async setDecryptedPrivateKey(value: Uint8Array, options?: StorageOptions): Promise { const account = await this.getAccount( this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); @@ -2097,14 +2097,14 @@ export class StateService< ); } - async getPublicKey(options?: StorageOptions): Promise { + async getPublicKey(options?: StorageOptions): Promise { const keys = ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) )?.keys; return keys?.publicKey; } - async setPublicKey(value: ArrayBuffer, options?: StorageOptions): Promise { + async setPublicKey(value: Uint8Array, options?: StorageOptions): Promise { const account = await this.getAccount( this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); diff --git a/libs/common/src/platform/services/web-crypto-function.service.spec.ts b/libs/common/src/platform/services/web-crypto-function.service.spec.ts index fa3562cb50..1f88a41982 100644 --- a/libs/common/src/platform/services/web-crypto-function.service.spec.ts +++ b/libs/common/src/platform/services/web-crypto-function.service.spec.ts @@ -160,7 +160,7 @@ describe("WebCrypto Function Service", () => { const a = new Uint8Array(2); a[0] = 1; a[1] = 2; - const equal = await cryptoFunctionService.compare(a.buffer, a.buffer); + const equal = await cryptoFunctionService.compare(a, a); expect(equal).toBe(true); }); @@ -172,7 +172,7 @@ describe("WebCrypto Function Service", () => { const b = new Uint8Array(2); b[0] = 3; b[1] = 4; - const equal = await cryptoFunctionService.compare(a.buffer, b.buffer); + const equal = await cryptoFunctionService.compare(a, b); expect(equal).toBe(false); }); @@ -183,7 +183,7 @@ describe("WebCrypto Function Service", () => { a[1] = 2; const b = new Uint8Array(2); b[0] = 3; - const equal = await cryptoFunctionService.compare(a.buffer, b.buffer); + const equal = await cryptoFunctionService.compare(a, b); expect(equal).toBe(false); }); }); @@ -200,7 +200,7 @@ describe("WebCrypto Function Service", () => { const a = new Uint8Array(2); a[0] = 1; a[1] = 2; - const aByteString = Utils.fromBufferToByteString(a.buffer); + const aByteString = Utils.fromBufferToByteString(a); const equal = await cryptoFunctionService.compareFast(aByteString, aByteString); expect(equal).toBe(true); }); @@ -210,11 +210,11 @@ describe("WebCrypto Function Service", () => { const a = new Uint8Array(2); a[0] = 1; a[1] = 2; - const aByteString = Utils.fromBufferToByteString(a.buffer); + const aByteString = Utils.fromBufferToByteString(a); const b = new Uint8Array(2); b[0] = 3; b[1] = 4; - const bByteString = Utils.fromBufferToByteString(b.buffer); + const bByteString = Utils.fromBufferToByteString(b); const equal = await cryptoFunctionService.compareFast(aByteString, bByteString); expect(equal).toBe(false); }); @@ -224,10 +224,10 @@ describe("WebCrypto Function Service", () => { const a = new Uint8Array(2); a[0] = 1; a[1] = 2; - const aByteString = Utils.fromBufferToByteString(a.buffer); + const aByteString = Utils.fromBufferToByteString(a); const b = new Uint8Array(2); b[0] = 3; - const bByteString = Utils.fromBufferToByteString(b.buffer); + const bByteString = Utils.fromBufferToByteString(b); const equal = await cryptoFunctionService.compareFast(aByteString, bByteString); expect(equal).toBe(false); }); @@ -239,7 +239,7 @@ describe("WebCrypto Function Service", () => { const iv = makeStaticByteArray(16); const key = makeStaticByteArray(32); const data = Utils.fromUtf8ToArray("EncryptMe!"); - const encValue = await cryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + const encValue = await cryptoFunctionService.aesEncrypt(data, iv, key); expect(Utils.fromBufferToB64(encValue)).toBe("ByUF8vhyX4ddU9gcooznwA=="); }); @@ -249,10 +249,10 @@ describe("WebCrypto Function Service", () => { const key = makeStaticByteArray(32); const value = "EncryptMe!"; const data = Utils.fromUtf8ToArray(value); - const encValue = await cryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + const encValue = await cryptoFunctionService.aesEncrypt(data, iv, key); const encData = Utils.fromBufferToB64(encValue); - const b64Iv = Utils.fromBufferToB64(iv.buffer); - const symKey = new SymmetricCryptoKey(key.buffer); + const b64Iv = Utils.fromBufferToB64(iv); + const symKey = new SymmetricCryptoKey(key); const params = cryptoFunctionService.aesDecryptFastParameters(encData, b64Iv, null, symKey); const decValue = await cryptoFunctionService.aesDecryptFast(params); expect(decValue).toBe(value); @@ -264,8 +264,8 @@ describe("WebCrypto Function Service", () => { const key = makeStaticByteArray(32); const value = "EncryptMe!"; const data = Utils.fromUtf8ToArray(value); - const encValue = await cryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); - const decValue = await cryptoFunctionService.aesDecrypt(encValue, iv.buffer, key.buffer); + const encValue = new Uint8Array(await cryptoFunctionService.aesEncrypt(data, iv, key)); + const decValue = await cryptoFunctionService.aesDecrypt(encValue, iv, key); expect(Utils.fromBufferToUtf8(decValue)).toBe(value); }); }); @@ -273,8 +273,8 @@ describe("WebCrypto Function Service", () => { describe("aesDecryptFast", () => { it("should successfully decrypt data", async () => { const cryptoFunctionService = getWebCryptoFunctionService(); - const iv = Utils.fromBufferToB64(makeStaticByteArray(16).buffer); - const symKey = new SymmetricCryptoKey(makeStaticByteArray(32).buffer); + const iv = Utils.fromBufferToB64(makeStaticByteArray(16)); + const symKey = new SymmetricCryptoKey(makeStaticByteArray(32)); const data = "ByUF8vhyX4ddU9gcooznwA=="; const params = cryptoFunctionService.aesDecryptFastParameters(data, iv, null, symKey); const decValue = await cryptoFunctionService.aesDecryptFast(params); @@ -288,7 +288,7 @@ describe("WebCrypto Function Service", () => { const iv = makeStaticByteArray(16); const key = makeStaticByteArray(32); const data = Utils.fromB64ToArray("ByUF8vhyX4ddU9gcooznwA=="); - const decValue = await cryptoFunctionService.aesDecrypt(data.buffer, iv.buffer, key.buffer); + const decValue = await cryptoFunctionService.aesDecrypt(data, iv, key); expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); }); }); @@ -300,8 +300,8 @@ describe("WebCrypto Function Service", () => { const privKey = Utils.fromB64ToArray(RsaPrivateKey); const value = "EncryptMe!"; const data = Utils.fromUtf8ToArray(value); - const encValue = await cryptoFunctionService.rsaEncrypt(data.buffer, pubKey.buffer, "sha1"); - const decValue = await cryptoFunctionService.rsaDecrypt(encValue, privKey.buffer, "sha1"); + const encValue = new Uint8Array(await cryptoFunctionService.rsaEncrypt(data, pubKey, "sha1")); + const decValue = await cryptoFunctionService.rsaDecrypt(encValue, privKey, "sha1"); expect(Utils.fromBufferToUtf8(decValue)).toBe(value); }); }); @@ -316,7 +316,7 @@ describe("WebCrypto Function Service", () => { "zFOIEPF2S1zgperEP23M01mr4dWVdYN18B32YF67xdJHMbFhp5dkQwv9CmscoWq7OE5HIfOb+JAh7BEZb+CmKhM3yWJvoR/D" + "/5jcercUtK2o+XrzNrL4UQ7yLZcFz6Bfwb/j6ICYvqd/YJwXNE6dwlL57OfwJyCdw2rRYf0/qI00t9u8Iitw==" ); - const decValue = await cryptoFunctionService.rsaDecrypt(data.buffer, privKey.buffer, "sha1"); + const decValue = await cryptoFunctionService.rsaDecrypt(data, privKey, "sha1"); expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); }); }); @@ -325,7 +325,7 @@ describe("WebCrypto Function Service", () => { it("should successfully extract key", async () => { const cryptoFunctionService = getWebCryptoFunctionService(); const privKey = Utils.fromB64ToArray(RsaPrivateKey); - const publicKey = await cryptoFunctionService.rsaExtractPublicKey(privKey.buffer); + const publicKey = await cryptoFunctionService.rsaExtractPublicKey(privKey); expect(Utils.fromBufferToB64(publicKey)).toBe(RsaPublicKey); }); }); @@ -390,8 +390,8 @@ function testPbkdf2( it("should create valid " + algorithm + " key from array buffer input", async () => { const cryptoFunctionService = getWebCryptoFunctionService(); const key = await cryptoFunctionService.pbkdf2( - Utils.fromUtf8ToArray(regularPassword).buffer, - Utils.fromUtf8ToArray(regularEmail).buffer, + Utils.fromUtf8ToArray(regularPassword), + Utils.fromUtf8ToArray(regularEmail), algorithm, 5000 ); @@ -437,8 +437,8 @@ function testHkdf( const cryptoFunctionService = getWebCryptoFunctionService(); const key = await cryptoFunctionService.hkdf( ikm, - Utils.fromUtf8ToArray(regularSalt).buffer, - Utils.fromUtf8ToArray(regularInfo).buffer, + Utils.fromUtf8ToArray(regularSalt), + Utils.fromUtf8ToArray(regularInfo), 32, algorithm ); @@ -496,10 +496,7 @@ function testHash( it("should create valid " + algorithm + " hash from array buffer input", async () => { const cryptoFunctionService = getWebCryptoFunctionService(); - const hash = await cryptoFunctionService.hash( - Utils.fromUtf8ToArray(regularValue).buffer, - algorithm - ); + const hash = await cryptoFunctionService.hash(Utils.fromUtf8ToArray(regularValue), algorithm); expect(Utils.fromBufferToHex(hash)).toBe(regularHash); }); } @@ -508,8 +505,8 @@ function testHmac(algorithm: "sha1" | "sha256" | "sha512", mac: string) { it("should create valid " + algorithm + " hmac", async () => { const cryptoFunctionService = getWebCryptoFunctionService(); const computedMac = await cryptoFunctionService.hmac( - Utils.fromUtf8ToArray("SignMe!!").buffer, - Utils.fromUtf8ToArray("secretkey").buffer, + Utils.fromUtf8ToArray("SignMe!!"), + Utils.fromUtf8ToArray("secretkey"), algorithm ); expect(Utils.fromBufferToHex(computedMac)).toBe(mac); @@ -519,14 +516,14 @@ function testHmac(algorithm: "sha1" | "sha256" | "sha512", mac: string) { function testHmacFast(algorithm: "sha1" | "sha256" | "sha512", mac: string) { it("should create valid " + algorithm + " hmac", async () => { const cryptoFunctionService = getWebCryptoFunctionService(); - const keyByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("secretkey").buffer); - const dataByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("SignMe!!").buffer); + const keyByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("secretkey")); + const dataByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("SignMe!!")); const computedMac = await cryptoFunctionService.hmacFast( dataByteString, keyByteString, algorithm ); - expect(Utils.fromBufferToHex(Utils.fromByteStringToArray(computedMac).buffer)).toBe(mac); + expect(Utils.fromBufferToHex(Utils.fromByteStringToArray(computedMac))).toBe(mac); }); } @@ -535,7 +532,9 @@ function testRsaGenerateKeyPair(length: 1024 | 2048 | 4096) { "should successfully generate a " + length + " bit key pair", async () => { const cryptoFunctionService = getWebCryptoFunctionService(); - const keyPair = await cryptoFunctionService.rsaGenerateKeyPair(length); + const keyPair = (await cryptoFunctionService.rsaGenerateKeyPair(length)).map( + (k) => new Uint8Array(k) + ); expect(keyPair[0] == null || keyPair[1] == null).toBe(false); const publicKey = await cryptoFunctionService.rsaExtractPublicKey(keyPair[1]); expect(Utils.fromBufferToB64(keyPair[0])).toBe(Utils.fromBufferToB64(publicKey)); 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 65ee74aa63..4cf8636a09 100644 --- a/libs/common/src/platform/services/web-crypto-function.service.ts +++ b/libs/common/src/platform/services/web-crypto-function.service.ts @@ -20,11 +20,11 @@ export class WebCryptoFunctionService implements CryptoFunctionService { } async pbkdf2( - password: string | ArrayBuffer, - salt: string | ArrayBuffer, + password: string | Uint8Array, + salt: string | Uint8Array, algorithm: "sha256" | "sha512", iterations: number - ): Promise { + ): Promise { const wcLen = algorithm === "sha256" ? 256 : 512; const passwordBuf = this.toBuf(password); const saltBuf = this.toBuf(salt); @@ -43,16 +43,17 @@ export class WebCryptoFunctionService implements CryptoFunctionService { false, ["deriveBits"] ); - return await this.subtle.deriveBits(pbkdf2Params, impKey, wcLen); + const buffer = await this.subtle.deriveBits(pbkdf2Params as any, impKey, wcLen); + return new Uint8Array(buffer); } async argon2( - password: string | ArrayBuffer, - salt: string | ArrayBuffer, + password: string | Uint8Array, + salt: string | Uint8Array, iterations: number, memory: number, parallelism: number - ): Promise { + ): Promise { if (!this.wasmSupported) { throw "Webassembly support is required for the Argon2 KDF feature."; } @@ -74,12 +75,12 @@ export class WebCryptoFunctionService implements CryptoFunctionService { } async hkdf( - ikm: ArrayBuffer, - salt: string | ArrayBuffer, - info: string | ArrayBuffer, + ikm: Uint8Array, + salt: string | Uint8Array, + info: string | Uint8Array, outputByteSize: number, algorithm: "sha256" | "sha512" - ): Promise { + ): Promise { const saltBuf = this.toBuf(salt); const infoBuf = this.toBuf(info); @@ -93,16 +94,17 @@ export class WebCryptoFunctionService implements CryptoFunctionService { const impKey = await this.subtle.importKey("raw", ikm, { name: "HKDF" } as any, false, [ "deriveBits", ]); - return await this.subtle.deriveBits(hkdfParams as any, impKey, outputByteSize * 8); + const buffer = await this.subtle.deriveBits(hkdfParams as any, impKey, outputByteSize * 8); + return new Uint8Array(buffer); } // ref: https://tools.ietf.org/html/rfc5869 async hkdfExpand( - prk: ArrayBuffer, - info: string | ArrayBuffer, + prk: Uint8Array, + info: string | Uint8Array, outputByteSize: number, algorithm: "sha256" | "sha512" - ): Promise { + ): Promise { const hashLen = algorithm === "sha256" ? 32 : 64; if (outputByteSize > 255 * hashLen) { throw new Error("outputByteSize is too large."); @@ -122,49 +124,54 @@ export class WebCryptoFunctionService implements CryptoFunctionService { t.set(previousT); t.set(infoArr, previousT.length); t.set([i + 1], t.length - 1); - previousT = new Uint8Array(await this.hmac(t.buffer, prk, algorithm)); + previousT = new Uint8Array(await this.hmac(t, prk, algorithm)); okm.set(previousT, runningOkmLength); runningOkmLength += previousT.length; if (runningOkmLength >= outputByteSize) { break; } } - return okm.slice(0, outputByteSize).buffer; + return okm.slice(0, outputByteSize); } async hash( - value: string | ArrayBuffer, + value: string | Uint8Array, algorithm: "sha1" | "sha256" | "sha512" | "md5" - ): Promise { + ): Promise { if (algorithm === "md5") { const md = algorithm === "md5" ? forge.md.md5.create() : forge.md.sha1.create(); const valueBytes = this.toByteString(value); md.update(valueBytes, "raw"); - return Utils.fromByteStringToArray(md.digest().data).buffer; + return Utils.fromByteStringToArray(md.digest().data); } const valueBuf = this.toBuf(value); - return await this.subtle.digest({ name: this.toWebCryptoAlgorithm(algorithm) }, valueBuf); + const buffer = await this.subtle.digest( + { name: this.toWebCryptoAlgorithm(algorithm) }, + valueBuf + ); + return new Uint8Array(buffer); } async hmac( - value: ArrayBuffer, - key: ArrayBuffer, + value: Uint8Array, + key: Uint8Array, algorithm: "sha1" | "sha256" | "sha512" - ): Promise { + ): Promise { const signingAlgorithm = { name: "HMAC", hash: { name: this.toWebCryptoAlgorithm(algorithm) }, }; const impKey = await this.subtle.importKey("raw", key, signingAlgorithm, false, ["sign"]); - return await this.subtle.sign(signingAlgorithm, impKey, value); + const buffer = await this.subtle.sign(signingAlgorithm, impKey, value); + return new Uint8Array(buffer); } // Safely compare two values in a way that protects against timing attacks (Double HMAC Verification). // ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/ // ref: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy - async compare(a: ArrayBuffer, b: ArrayBuffer): Promise { + async compare(a: Uint8Array, b: Uint8Array): Promise { const macKey = await this.randomBytes(32); const signingAlgorithm = { name: "HMAC", @@ -219,11 +226,12 @@ export class WebCryptoFunctionService implements CryptoFunctionService { return equals; } - async aesEncrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + async aesEncrypt(data: Uint8Array, iv: Uint8Array, key: Uint8Array): Promise { const impKey = await this.subtle.importKey("raw", key, { name: "AES-CBC" } as any, false, [ "encrypt", ]); - return await this.subtle.encrypt({ name: "AES-CBC", iv: iv }, impKey, data); + const buffer = await this.subtle.encrypt({ name: "AES-CBC", iv: iv }, impKey, data); + return new Uint8Array(buffer); } aesDecryptFastParameters( @@ -275,18 +283,19 @@ export class WebCryptoFunctionService implements CryptoFunctionService { return Promise.resolve(val); } - async aesDecrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + async aesDecrypt(data: Uint8Array, iv: Uint8Array, key: Uint8Array): Promise { const impKey = await this.subtle.importKey("raw", key, { name: "AES-CBC" } as any, false, [ "decrypt", ]); - return await this.subtle.decrypt({ name: "AES-CBC", iv: iv }, impKey, data); + const buffer = await this.subtle.decrypt({ name: "AES-CBC", iv: iv }, impKey, data); + return new Uint8Array(buffer); } async rsaEncrypt( - data: ArrayBuffer, - publicKey: ArrayBuffer, + data: Uint8Array, + publicKey: Uint8Array, algorithm: "sha1" | "sha256" - ): Promise { + ): Promise { // Note: Edge browser requires that we specify name and hash for both key import and decrypt. // We cannot use the proper types here. const rsaParams = { @@ -294,14 +303,15 @@ export class WebCryptoFunctionService implements CryptoFunctionService { hash: { name: this.toWebCryptoAlgorithm(algorithm) }, }; const impKey = await this.subtle.importKey("spki", publicKey, rsaParams, false, ["encrypt"]); - return await this.subtle.encrypt(rsaParams, impKey, data); + const buffer = await this.subtle.encrypt(rsaParams, impKey, data); + return new Uint8Array(buffer); } async rsaDecrypt( - data: ArrayBuffer, - privateKey: ArrayBuffer, + data: Uint8Array, + privateKey: Uint8Array, algorithm: "sha1" | "sha256" - ): Promise { + ): Promise { // Note: Edge browser requires that we specify name and hash for both key import and decrypt. // We cannot use the proper types here. const rsaParams = { @@ -309,10 +319,11 @@ export class WebCryptoFunctionService implements CryptoFunctionService { hash: { name: this.toWebCryptoAlgorithm(algorithm) }, }; const impKey = await this.subtle.importKey("pkcs8", privateKey, rsaParams, false, ["decrypt"]); - return await this.subtle.decrypt(rsaParams, impKey, data); + const buffer = await this.subtle.decrypt(rsaParams, impKey, data); + return new Uint8Array(buffer); } - async rsaExtractPublicKey(privateKey: ArrayBuffer): Promise { + async rsaExtractPublicKey(privateKey: Uint8Array): Promise { const rsaParams = { name: "RSA-OAEP", // Have to specify some algorithm @@ -332,10 +343,11 @@ export class WebCryptoFunctionService implements CryptoFunctionService { const impPublicKey = await this.subtle.importKey("jwk", jwkPublicKeyParams, rsaParams, true, [ "encrypt", ]); - return await this.subtle.exportKey("spki", impPublicKey); + const buffer = await this.subtle.exportKey("spki", impPublicKey); + return new Uint8Array(buffer); } - async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[ArrayBuffer, ArrayBuffer]> { + async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[Uint8Array, Uint8Array]> { const rsaParams = { name: "RSA-OAEP", modulusLength: length, @@ -349,26 +361,26 @@ export class WebCryptoFunctionService implements CryptoFunctionService { ])) as CryptoKeyPair; const publicKey = await this.subtle.exportKey("spki", keyPair.publicKey); const privateKey = await this.subtle.exportKey("pkcs8", keyPair.privateKey); - return [publicKey, privateKey]; + return [new Uint8Array(publicKey), new Uint8Array(privateKey)]; } randomBytes(length: number): Promise { const arr = new Uint8Array(length); this.crypto.getRandomValues(arr); - return Promise.resolve(arr.buffer as CsprngArray); + return Promise.resolve(arr as CsprngArray); } - private toBuf(value: string | ArrayBuffer): ArrayBuffer { - let buf: ArrayBuffer; + private toBuf(value: string | Uint8Array): Uint8Array { + let buf: Uint8Array; if (typeof value === "string") { - buf = Utils.fromUtf8ToArray(value).buffer; + buf = Utils.fromUtf8ToArray(value); } else { buf = value; } return buf; } - private toByteString(value: string | ArrayBuffer): string { + private toByteString(value: string | Uint8Array): string { let bytes: string; if (typeof value === "string") { bytes = forge.util.encodeUtf8(value); diff --git a/libs/common/src/services/device-crypto.service.spec.ts b/libs/common/src/services/device-crypto.service.spec.ts index 526e684c9b..cf01f00837 100644 --- a/libs/common/src/services/device-crypto.service.spec.ts +++ b/libs/common/src/services/device-crypto.service.spec.ts @@ -57,10 +57,10 @@ describe("deviceCryptoService", () => { let makeDeviceKeySpy: jest.SpyInstance; beforeEach(() => { - mockRandomBytes = new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray; + mockRandomBytes = new Uint8Array(deviceKeyBytesLength) as CsprngArray; mockDeviceKey = new SymmetricCryptoKey(mockRandomBytes); existingDeviceKey = new SymmetricCryptoKey( - new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray + new Uint8Array(deviceKeyBytesLength) as CsprngArray ) as DeviceKey; stateSvcGetDeviceKeySpy = jest.spyOn(stateService, "getDeviceKey"); @@ -97,7 +97,7 @@ describe("deviceCryptoService", () => { describe("makeDeviceKey", () => { it("creates a new non-null 64 byte device key, securely stores it, and returns it", async () => { - const mockRandomBytes = new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray; + const mockRandomBytes = new Uint8Array(deviceKeyBytesLength) as CsprngArray; const cryptoFuncSvcRandomBytesSpy = jest .spyOn(cryptoFunctionService, "randomBytes") @@ -128,9 +128,9 @@ describe("deviceCryptoService", () => { let mockUserSymKey: SymmetricCryptoKey; const deviceRsaKeyLength = 2048; - let mockDeviceRsaKeyPair: [ArrayBuffer, ArrayBuffer]; - let mockDevicePrivateKey: ArrayBuffer; - let mockDevicePublicKey: ArrayBuffer; + let mockDeviceRsaKeyPair: [Uint8Array, Uint8Array]; + let mockDevicePrivateKey: Uint8Array; + let mockDevicePublicKey: Uint8Array; let mockDevicePublicKeyEncryptedUserSymKey: EncString; let mockUserSymKeyEncryptedDevicePublicKey: EncString; let mockDeviceKeyEncryptedDevicePrivateKey: EncString; @@ -156,15 +156,15 @@ describe("deviceCryptoService", () => { beforeEach(() => { // Setup all spies and default return values for the happy path - mockDeviceKeyRandomBytes = new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray; + mockDeviceKeyRandomBytes = new Uint8Array(deviceKeyBytesLength) as CsprngArray; mockDeviceKey = new SymmetricCryptoKey(mockDeviceKeyRandomBytes) as DeviceKey; - mockUserSymKeyRandomBytes = new Uint8Array(userSymKeyBytesLength).buffer as CsprngArray; + mockUserSymKeyRandomBytes = new Uint8Array(userSymKeyBytesLength) as CsprngArray; mockUserSymKey = new SymmetricCryptoKey(mockUserSymKeyRandomBytes); mockDeviceRsaKeyPair = [ - new ArrayBuffer(deviceRsaKeyLength), - new ArrayBuffer(deviceRsaKeyLength), + new Uint8Array(deviceRsaKeyLength), + new Uint8Array(deviceRsaKeyLength), ]; mockDevicePublicKey = mockDeviceRsaKeyPair[0]; diff --git a/libs/common/src/services/totp.service.ts b/libs/common/src/services/totp.service.ts index 54c583b5cd..7896a8c451 100644 --- a/libs/common/src/services/totp.service.ts +++ b/libs/common/src/services/totp.service.ts @@ -162,7 +162,7 @@ export class TotpService implements TotpServiceAbstraction { timeBytes: Uint8Array, alg: "sha1" | "sha256" | "sha512" ) { - const signature = await this.cryptoFunctionService.hmac(timeBytes.buffer, keyBytes.buffer, alg); + const signature = await this.cryptoFunctionService.hmac(timeBytes, keyBytes, alg); return new Uint8Array(signature); } } diff --git a/libs/common/src/tools/send/models/view/send.view.ts b/libs/common/src/tools/send/models/view/send.view.ts index 9b85e94562..4e340916d9 100644 --- a/libs/common/src/tools/send/models/view/send.view.ts +++ b/libs/common/src/tools/send/models/view/send.view.ts @@ -13,7 +13,7 @@ export class SendView implements View { accessId: string = null; name: string = null; notes: string = null; - key: ArrayBuffer; + key: Uint8Array; cryptoKey: SymmetricCryptoKey; type: SendType = null; text = new SendTextView(); @@ -82,7 +82,7 @@ export class SendView implements View { } return Object.assign(new SendView(), json, { - key: Utils.fromB64ToArray(json.key)?.buffer, + key: Utils.fromB64ToArray(json.key), cryptoKey: SymmetricCryptoKey.fromJSON(json.cryptoKey), text: SendTextView.fromJSON(json.text), file: SendFileView.fromJSON(json.file), diff --git a/libs/common/src/tools/send/services/send.service.ts b/libs/common/src/tools/send/services/send.service.ts index eddc35ae79..2b4a268403 100644 --- a/libs/common/src/tools/send/services/send.service.ts +++ b/libs/common/src/tools/send/services/send.service.ts @@ -241,7 +241,7 @@ export class SendService implements InternalSendServiceAbstraction { key: SymmetricCryptoKey ): Promise<[EncString, EncArrayBuffer]> { const encFileName = await this.cryptoService.encrypt(fileName, key); - const encFileData = await this.cryptoService.encryptToBytes(data, key); + const encFileData = await this.cryptoService.encryptToBytes(new Uint8Array(data), key); return [encFileName, encFileData]; } diff --git a/libs/common/src/types/csprng.d.ts b/libs/common/src/types/csprng.d.ts index ec0a31a9f7..6d1c8a7cdb 100644 --- a/libs/common/src/types/csprng.d.ts +++ b/libs/common/src/types/csprng.d.ts @@ -4,6 +4,6 @@ import { Opaque } from "type-fest"; // represents an array or string value generated from a // cryptographic secure pseudorandom number generator (CSPRNG) -type CsprngArray = Opaque; +type CsprngArray = Opaque; type CsprngString = Opaque; diff --git a/libs/common/src/vault/services/cipher.service.spec.ts b/libs/common/src/vault/services/cipher.service.spec.ts index b9fbfacbc8..d7f96b9ae8 100644 --- a/libs/common/src/vault/services/cipher.service.spec.ts +++ b/libs/common/src/vault/services/cipher.service.spec.ts @@ -59,7 +59,7 @@ describe("Cipher Service", () => { it("attachments upload encrypted file contents", async () => { const fileName = "filename"; const fileData = new Uint8Array(10).buffer; - cryptoService.getOrgKey(Arg.any()).resolves(new SymmetricCryptoKey(new Uint8Array(32).buffer)); + cryptoService.getOrgKey(Arg.any()).resolves(new SymmetricCryptoKey(new Uint8Array(32))); await cipherService.saveAttachmentRawWithServer(new Cipher(), fileName, fileData); diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index 24167082d9..9828d1e93f 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -637,7 +637,7 @@ export class CipherService implements CipherServiceAbstraction { const encFileName = await this.cryptoService.encrypt(filename, key); const dataEncKey = await this.cryptoService.makeEncKey(key); - const encData = await this.cryptoService.encryptToBytes(data, dataEncKey[0]); + const encData = await this.cryptoService.encryptToBytes(new Uint8Array(data), dataEncKey[0]); const response = await this.cipherFileUploadService.upload( cipher, diff --git a/libs/common/test.setup.ts b/libs/common/test.setup.ts index dfcf7c8610..c50c7ca227 100644 --- a/libs/common/test.setup.ts +++ b/libs/common/test.setup.ts @@ -12,16 +12,6 @@ expect.extend({ toEqualBuffer: toEqualBuffer, }); -interface CustomMatchers { +export interface CustomMatchers { toEqualBuffer(expected: Uint8Array | ArrayBuffer): R; } - -/* eslint-disable */ -declare global { - namespace jest { - interface Expect extends CustomMatchers {} - interface Matchers extends CustomMatchers {} - interface InverseAsymmetricMatchers extends CustomMatchers {} - } -} -/* eslint-enable */ diff --git a/libs/common/tsconfig.json b/libs/common/tsconfig.json index 6004a56fb5..11cdb4e44c 100644 --- a/libs/common/tsconfig.json +++ b/libs/common/tsconfig.json @@ -1,5 +1,5 @@ { "extends": "../shared/tsconfig.libs", - "include": ["src", "spec"], + "include": ["src", "spec", "./custom-matchers.d.ts"], "exclude": ["node_modules", "dist"] } diff --git a/libs/node/src/services/node-crypto-function.service.spec.ts b/libs/node/src/services/node-crypto-function.service.spec.ts index 6e30ea307d..5a7f369d27 100644 --- a/libs/node/src/services/node-crypto-function.service.spec.ts +++ b/libs/node/src/services/node-crypto-function.service.spec.ts @@ -170,11 +170,7 @@ describe("NodeCrypto Function Service", () => { const iv = makeStaticByteArray(16); const key = makeStaticByteArray(32); const data = Utils.fromUtf8ToArray("EncryptMe!"); - const encValue = await nodeCryptoFunctionService.aesEncrypt( - data.buffer, - iv.buffer, - key.buffer - ); + const encValue = await nodeCryptoFunctionService.aesEncrypt(data, iv, key); expect(Utils.fromBufferToB64(encValue)).toBe("ByUF8vhyX4ddU9gcooznwA=="); }); @@ -184,12 +180,8 @@ describe("NodeCrypto Function Service", () => { const key = makeStaticByteArray(32); const value = "EncryptMe!"; const data = Utils.fromUtf8ToArray(value); - const encValue = await nodeCryptoFunctionService.aesEncrypt( - data.buffer, - iv.buffer, - key.buffer - ); - const decValue = await nodeCryptoFunctionService.aesDecrypt(encValue, iv.buffer, key.buffer); + const encValue = await nodeCryptoFunctionService.aesEncrypt(data, iv, key); + const decValue = await nodeCryptoFunctionService.aesDecrypt(encValue, iv, key); expect(Utils.fromBufferToUtf8(decValue)).toBe(value); }); }); @@ -197,8 +189,8 @@ describe("NodeCrypto Function Service", () => { describe("aesDecryptFast", () => { it("should successfully decrypt data", async () => { const nodeCryptoFunctionService = new NodeCryptoFunctionService(); - const iv = Utils.fromBufferToB64(makeStaticByteArray(16).buffer); - const symKey = new SymmetricCryptoKey(makeStaticByteArray(32).buffer); + const iv = Utils.fromBufferToB64(makeStaticByteArray(16)); + const symKey = new SymmetricCryptoKey(makeStaticByteArray(32)); const data = "ByUF8vhyX4ddU9gcooznwA=="; const params = nodeCryptoFunctionService.aesDecryptFastParameters(data, iv, null, symKey); const decValue = await nodeCryptoFunctionService.aesDecryptFast(params); @@ -212,11 +204,7 @@ describe("NodeCrypto Function Service", () => { const iv = makeStaticByteArray(16); const key = makeStaticByteArray(32); const data = Utils.fromB64ToArray("ByUF8vhyX4ddU9gcooznwA=="); - const decValue = await nodeCryptoFunctionService.aesDecrypt( - data.buffer, - iv.buffer, - key.buffer - ); + const decValue = await nodeCryptoFunctionService.aesDecrypt(data, iv, key); expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); }); }); @@ -228,12 +216,8 @@ describe("NodeCrypto Function Service", () => { const privKey = Utils.fromB64ToArray(RsaPrivateKey); const value = "EncryptMe!"; const data = Utils.fromUtf8ToArray(value); - const encValue = await nodeCryptoFunctionService.rsaEncrypt( - data.buffer, - pubKey.buffer, - "sha1" - ); - const decValue = await nodeCryptoFunctionService.rsaDecrypt(encValue, privKey.buffer, "sha1"); + const encValue = await nodeCryptoFunctionService.rsaEncrypt(data, pubKey, "sha1"); + const decValue = await nodeCryptoFunctionService.rsaDecrypt(encValue, privKey, "sha1"); expect(Utils.fromBufferToUtf8(decValue)).toBe(value); }); }); @@ -248,11 +232,7 @@ describe("NodeCrypto Function Service", () => { "zFOIEPF2S1zgperEP23M01mr4dWVdYN18B32YF67xdJHMbFhp5dkQwv9CmscoWq7OE5HIfOb+JAh7BEZb+CmKhM3yWJvoR/D" + "/5jcercUtK2o+XrzNrL4UQ7yLZcFz6Bfwb/j6ICYvqd/YJwXNE6dwlL57OfwJyCdw2rRYf0/qI00t9u8Iitw==" ); - const decValue = await nodeCryptoFunctionService.rsaDecrypt( - data.buffer, - privKey.buffer, - "sha1" - ); + const decValue = await nodeCryptoFunctionService.rsaDecrypt(data, privKey, "sha1"); expect(Utils.fromBufferToUtf8(decValue)).toBe("EncryptMe!"); }); }); @@ -261,7 +241,7 @@ describe("NodeCrypto Function Service", () => { it("should successfully extract key", async () => { const nodeCryptoFunctionService = new NodeCryptoFunctionService(); const privKey = Utils.fromB64ToArray(RsaPrivateKey); - const publicKey = await nodeCryptoFunctionService.rsaExtractPublicKey(privKey.buffer); + const publicKey = await nodeCryptoFunctionService.rsaExtractPublicKey(privKey); expect(Utils.fromBufferToB64(publicKey)).toBe(RsaPublicKey); }); }); @@ -327,8 +307,8 @@ function testPbkdf2( it("should create valid " + algorithm + " key from array buffer input", async () => { const cryptoFunctionService = new NodeCryptoFunctionService(); const key = await cryptoFunctionService.pbkdf2( - Utils.fromUtf8ToArray(regularPassword).buffer, - Utils.fromUtf8ToArray(regularEmail).buffer, + Utils.fromUtf8ToArray(regularPassword), + Utils.fromUtf8ToArray(regularEmail), algorithm, 5000 ); @@ -374,8 +354,8 @@ function testHkdf( const cryptoFunctionService = new NodeCryptoFunctionService(); const key = await cryptoFunctionService.hkdf( ikm, - Utils.fromUtf8ToArray(regularSalt).buffer, - Utils.fromUtf8ToArray(regularInfo).buffer, + Utils.fromUtf8ToArray(regularSalt), + Utils.fromUtf8ToArray(regularInfo), 32, algorithm ); @@ -433,10 +413,7 @@ function testHash( it("should create valid " + algorithm + " hash from array buffer input", async () => { const cryptoFunctionService = new NodeCryptoFunctionService(); - const hash = await cryptoFunctionService.hash( - Utils.fromUtf8ToArray(regularValue).buffer, - algorithm - ); + const hash = await cryptoFunctionService.hash(Utils.fromUtf8ToArray(regularValue), algorithm); expect(Utils.fromBufferToHex(hash)).toBe(regularHash); }); } @@ -444,8 +421,8 @@ function testHash( function testHmac(algorithm: "sha1" | "sha256" | "sha512", mac: string, fast = false) { it("should create valid " + algorithm + " hmac", async () => { const cryptoFunctionService = new NodeCryptoFunctionService(); - const value = Utils.fromUtf8ToArray("SignMe!!").buffer; - const key = Utils.fromUtf8ToArray("secretkey").buffer; + const value = Utils.fromUtf8ToArray("SignMe!!"); + const key = Utils.fromUtf8ToArray("secretkey"); let computedMac: ArrayBuffer = null; if (fast) { computedMac = await cryptoFunctionService.hmacFast(value, key, algorithm); @@ -463,8 +440,8 @@ function testCompare(fast = false) { a[0] = 1; a[1] = 2; const equal = fast - ? await cryptoFunctionService.compareFast(a.buffer, a.buffer) - : await cryptoFunctionService.compare(a.buffer, a.buffer); + ? await cryptoFunctionService.compareFast(a, a) + : await cryptoFunctionService.compare(a, a); expect(equal).toBe(true); }); @@ -477,8 +454,8 @@ function testCompare(fast = false) { b[0] = 3; b[1] = 4; const equal = fast - ? await cryptoFunctionService.compareFast(a.buffer, b.buffer) - : await cryptoFunctionService.compare(a.buffer, b.buffer); + ? await cryptoFunctionService.compareFast(a, b) + : await cryptoFunctionService.compare(a, b); expect(equal).toBe(false); }); @@ -490,8 +467,8 @@ function testCompare(fast = false) { const b = new Uint8Array(2); b[0] = 3; const equal = fast - ? await cryptoFunctionService.compareFast(a.buffer, b.buffer) - : await cryptoFunctionService.compare(a.buffer, b.buffer); + ? await cryptoFunctionService.compareFast(a, b) + : await cryptoFunctionService.compare(a, b); expect(equal).toBe(false); }); } diff --git a/libs/node/src/services/node-crypto-function.service.ts b/libs/node/src/services/node-crypto-function.service.ts index a127074dc6..b824fe1b8c 100644 --- a/libs/node/src/services/node-crypto-function.service.ts +++ b/libs/node/src/services/node-crypto-function.service.ts @@ -11,34 +11,34 @@ import { CsprngArray } from "@bitwarden/common/types/csprng"; export class NodeCryptoFunctionService implements CryptoFunctionService { pbkdf2( - password: string | ArrayBuffer, - salt: string | ArrayBuffer, + password: string | Uint8Array, + salt: string | Uint8Array, algorithm: "sha256" | "sha512", iterations: number - ): Promise { + ): Promise { const len = algorithm === "sha256" ? 32 : 64; const nodePassword = this.toNodeValue(password); const nodeSalt = this.toNodeValue(salt); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { crypto.pbkdf2(nodePassword, nodeSalt, iterations, len, algorithm, (error, key) => { if (error != null) { reject(error); } else { - resolve(this.toArrayBuffer(key)); + resolve(this.toUint8Buffer(key)); } }); }); } async argon2( - password: string | ArrayBuffer, - salt: string | ArrayBuffer, + password: string | Uint8Array, + salt: string | Uint8Array, iterations: number, memory: number, parallelism: number - ): Promise { + ): Promise { const nodePassword = this.toNodeValue(password); - const nodeSalt = this.toNodeBuffer(this.toArrayBuffer(salt)); + const nodeSalt = this.toNodeBuffer(this.toUint8Buffer(salt)); const hash = await argon2.hash(nodePassword, { salt: nodeSalt, @@ -49,29 +49,29 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { parallelism: parallelism, type: argon2.argon2id, }); - return this.toArrayBuffer(hash); + return this.toUint8Buffer(hash); } // ref: https://tools.ietf.org/html/rfc5869 async hkdf( - ikm: ArrayBuffer, - salt: string | ArrayBuffer, - info: string | ArrayBuffer, + ikm: Uint8Array, + salt: string | Uint8Array, + info: string | Uint8Array, outputByteSize: number, algorithm: "sha256" | "sha512" - ): Promise { - const saltBuf = this.toArrayBuffer(salt); + ): Promise { + const saltBuf = this.toUint8Buffer(salt); const prk = await this.hmac(ikm, saltBuf, algorithm); return this.hkdfExpand(prk, info, outputByteSize, algorithm); } // ref: https://tools.ietf.org/html/rfc5869 async hkdfExpand( - prk: ArrayBuffer, - info: string | ArrayBuffer, + prk: Uint8Array, + info: string | Uint8Array, outputByteSize: number, algorithm: "sha256" | "sha512" - ): Promise { + ): Promise { const hashLen = algorithm === "sha256" ? 32 : 64; if (outputByteSize > 255 * hashLen) { throw new Error("outputByteSize is too large."); @@ -80,7 +80,7 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { if (prkArr.length < hashLen) { throw new Error("prk is too small."); } - const infoBuf = this.toArrayBuffer(info); + const infoBuf = this.toUint8Buffer(info); const infoArr = new Uint8Array(infoBuf); let runningOkmLength = 0; let previousT = new Uint8Array(0); @@ -91,39 +91,39 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { t.set(previousT); t.set(infoArr, previousT.length); t.set([i + 1], t.length - 1); - previousT = new Uint8Array(await this.hmac(t.buffer, prk, algorithm)); + previousT = await this.hmac(t, prk, algorithm); okm.set(previousT, runningOkmLength); runningOkmLength += previousT.length; if (runningOkmLength >= outputByteSize) { break; } } - return okm.slice(0, outputByteSize).buffer; + return okm.slice(0, outputByteSize); } hash( - value: string | ArrayBuffer, + value: string | Uint8Array, algorithm: "sha1" | "sha256" | "sha512" | "md5" - ): Promise { + ): Promise { const nodeValue = this.toNodeValue(value); const hash = crypto.createHash(algorithm); hash.update(nodeValue); - return Promise.resolve(this.toArrayBuffer(hash.digest())); + return Promise.resolve(this.toUint8Buffer(hash.digest())); } hmac( - value: ArrayBuffer, - key: ArrayBuffer, + value: Uint8Array, + key: Uint8Array, algorithm: "sha1" | "sha256" | "sha512" - ): Promise { + ): Promise { const nodeValue = this.toNodeBuffer(value); const nodeKey = this.toNodeBuffer(key); const hmac = crypto.createHmac(algorithm, nodeKey); hmac.update(nodeValue); - return Promise.resolve(this.toArrayBuffer(hmac.digest())); + return Promise.resolve(this.toUint8Buffer(hmac.digest())); } - async compare(a: ArrayBuffer, b: ArrayBuffer): Promise { + async compare(a: Uint8Array, b: Uint8Array): Promise { const key = await this.randomBytes(32); const mac1 = await this.hmac(a, key, "sha256"); const mac2 = await this.hmac(b, key, "sha256"); @@ -143,24 +143,24 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { } hmacFast( - value: ArrayBuffer, - key: ArrayBuffer, + value: Uint8Array, + key: Uint8Array, algorithm: "sha1" | "sha256" | "sha512" - ): Promise { + ): Promise { return this.hmac(value, key, algorithm); } - compareFast(a: ArrayBuffer, b: ArrayBuffer): Promise { + compareFast(a: Uint8Array, b: Uint8Array): Promise { return this.compare(a, b); } - aesEncrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + aesEncrypt(data: Uint8Array, iv: Uint8Array, key: Uint8Array): Promise { const nodeData = this.toNodeBuffer(data); const nodeIv = this.toNodeBuffer(iv); const nodeKey = this.toNodeBuffer(key); const cipher = crypto.createCipheriv("aes-256-cbc", nodeKey, nodeIv); const encBuf = Buffer.concat([cipher.update(nodeData), cipher.final()]); - return Promise.resolve(this.toArrayBuffer(encBuf)); + return Promise.resolve(this.toUint8Buffer(encBuf)); } aesDecryptFastParameters( @@ -168,70 +168,70 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { iv: string, mac: string, key: SymmetricCryptoKey - ): DecryptParameters { - const p = new DecryptParameters(); + ): DecryptParameters { + const p = new DecryptParameters(); p.encKey = key.encKey; - p.data = Utils.fromB64ToArray(data).buffer; - p.iv = Utils.fromB64ToArray(iv).buffer; + p.data = Utils.fromB64ToArray(data); + p.iv = Utils.fromB64ToArray(iv); const macData = new Uint8Array(p.iv.byteLength + p.data.byteLength); macData.set(new Uint8Array(p.iv), 0); macData.set(new Uint8Array(p.data), p.iv.byteLength); - p.macData = macData.buffer; + p.macData = macData; if (key.macKey != null) { p.macKey = key.macKey; } if (mac != null) { - p.mac = Utils.fromB64ToArray(mac).buffer; + p.mac = Utils.fromB64ToArray(mac); } return p; } - async aesDecryptFast(parameters: DecryptParameters): Promise { + async aesDecryptFast(parameters: DecryptParameters): Promise { const decBuf = await this.aesDecrypt(parameters.data, parameters.iv, parameters.encKey); return Utils.fromBufferToUtf8(decBuf); } - aesDecrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { + aesDecrypt(data: Uint8Array, iv: Uint8Array, key: Uint8Array): Promise { const nodeData = this.toNodeBuffer(data); const nodeIv = this.toNodeBuffer(iv); const nodeKey = this.toNodeBuffer(key); const decipher = crypto.createDecipheriv("aes-256-cbc", nodeKey, nodeIv); const decBuf = Buffer.concat([decipher.update(nodeData), decipher.final()]); - return Promise.resolve(this.toArrayBuffer(decBuf)); + return Promise.resolve(this.toUint8Buffer(decBuf)); } rsaEncrypt( - data: ArrayBuffer, - publicKey: ArrayBuffer, + data: Uint8Array, + publicKey: Uint8Array, algorithm: "sha1" | "sha256" - ): Promise { + ): Promise { if (algorithm === "sha256") { throw new Error("Node crypto does not support RSA-OAEP SHA-256"); } const pem = this.toPemPublicKey(publicKey); const decipher = crypto.publicEncrypt(pem, this.toNodeBuffer(data)); - return Promise.resolve(this.toArrayBuffer(decipher)); + return Promise.resolve(this.toUint8Buffer(decipher)); } rsaDecrypt( - data: ArrayBuffer, - privateKey: ArrayBuffer, + data: Uint8Array, + privateKey: Uint8Array, algorithm: "sha1" | "sha256" - ): Promise { + ): Promise { if (algorithm === "sha256") { throw new Error("Node crypto does not support RSA-OAEP SHA-256"); } const pem = this.toPemPrivateKey(privateKey); const decipher = crypto.privateDecrypt(pem, this.toNodeBuffer(data)); - return Promise.resolve(this.toArrayBuffer(decipher)); + return Promise.resolve(this.toUint8Buffer(decipher)); } - rsaExtractPublicKey(privateKey: ArrayBuffer): Promise { + rsaExtractPublicKey(privateKey: Uint8Array): Promise { const privateKeyByteString = Utils.fromBufferToByteString(privateKey); const privateKeyAsn1 = forge.asn1.fromDer(privateKeyByteString); const forgePrivateKey: any = forge.pki.privateKeyFromAsn1(privateKeyAsn1); @@ -239,11 +239,11 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { const publicKeyAsn1 = forge.pki.publicKeyToAsn1(forgePublicKey); const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).data; const publicKeyArray = Utils.fromByteStringToArray(publicKeyByteString); - return Promise.resolve(publicKeyArray.buffer); + return Promise.resolve(publicKeyArray); } - async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[ArrayBuffer, ArrayBuffer]> { - return new Promise<[ArrayBuffer, ArrayBuffer]>((resolve, reject) => { + async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[Uint8Array, Uint8Array]> { + return new Promise<[Uint8Array, Uint8Array]>((resolve, reject) => { forge.pki.rsa.generateKeyPair( { bits: length, @@ -265,7 +265,7 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { const privateKeyByteString = forge.asn1.toDer(privateKeyPkcs8).getBytes(); const privateKey = Utils.fromByteStringToArray(privateKeyByteString); - resolve([publicKey.buffer, privateKey.buffer]); + resolve([publicKey, privateKey]); } ); }); @@ -277,13 +277,13 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { if (error != null) { reject(error); } else { - resolve(this.toArrayBuffer(bytes) as CsprngArray); + resolve(this.toUint8Buffer(bytes) as CsprngArray); } }); }); } - private toNodeValue(value: string | ArrayBuffer): string | Buffer { + private toNodeValue(value: string | Uint8Array): string | Buffer { let nodeValue: string | Buffer; if (typeof value === "string") { nodeValue = value; @@ -293,21 +293,21 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { return nodeValue; } - private toNodeBuffer(value: ArrayBuffer): Buffer { - return Buffer.from(new Uint8Array(value) as any); + private toNodeBuffer(value: Uint8Array): Buffer { + return Buffer.from(value); } - private toArrayBuffer(value: Buffer | string | ArrayBuffer): ArrayBuffer { - let buf: ArrayBuffer; + private toUint8Buffer(value: Buffer | string | Uint8Array): Uint8Array { + let buf: Uint8Array; if (typeof value === "string") { - buf = Utils.fromUtf8ToArray(value).buffer; + buf = Utils.fromUtf8ToArray(value); } else { - buf = new Uint8Array(value).buffer; + buf = value; } return buf; } - private toPemPrivateKey(key: ArrayBuffer): string { + private toPemPrivateKey(key: Uint8Array): string { const byteString = Utils.fromBufferToByteString(key); const asn1 = forge.asn1.fromDer(byteString); const privateKey = forge.pki.privateKeyFromAsn1(asn1); @@ -316,7 +316,7 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { return forge.pki.privateKeyInfoToPem(privateKeyInfo); } - private toPemPublicKey(key: ArrayBuffer): string { + private toPemPublicKey(key: Uint8Array): string { const byteString = Utils.fromBufferToByteString(key); const asn1 = forge.asn1.fromDer(byteString); const publicKey = forge.pki.publicKeyFromAsn1(asn1);