1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-30 16:23:53 +00:00

Replace uses of encstring with unsigned shared key

This commit is contained in:
Bernd Schoolmann
2025-12-12 15:55:43 +01:00
parent 3735f1c106
commit 9afce480de
42 changed files with 183 additions and 157 deletions

View File

@@ -144,7 +144,7 @@ export class MemberActionsService {
switchMap((orgKey) => this.encryptService.encapsulateKeyUnsigned(orgKey, publicKey)),
map((encKey) => {
const req = new OrganizationUserConfirmRequest();
req.key = encKey.encryptedString;
req.key = encKey;
return req;
}),
),

View File

@@ -1,6 +1,7 @@
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
import { CipherResponse } from "@bitwarden/common/vault/models/response/cipher.response";
import { KdfType } from "@bitwarden/key-management";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { EmergencyAccessStatusType } from "../enums/emergency-access-status-type";
import { EmergencyAccessType } from "../enums/emergency-access-type";
@@ -56,7 +57,7 @@ export class EmergencyAccessGrantorDetailsResponse extends BaseResponse {
}
export class EmergencyAccessTakeoverResponse extends BaseResponse {
keyEncrypted: string;
keyEncrypted: UnsignedSharedKey;
kdf: KdfType;
kdfIterations: number;
kdfMemory?: number;
@@ -74,7 +75,7 @@ export class EmergencyAccessTakeoverResponse extends BaseResponse {
}
export class EmergencyAccessViewResponse extends BaseResponse {
keyEncrypted: string;
keyEncrypted: UnsignedSharedKey;
ciphers: CipherResponse[] = [];
constructor(response: any) {

View File

@@ -5,10 +5,6 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import {
EncryptedString,
EncString,
} from "@bitwarden/common/key-management/crypto/models/enc-string";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { UserId } from "@bitwarden/common/types/guid";
@@ -24,6 +20,7 @@ import {
KdfType,
UserKeyRotationKeyRecoveryProvider,
} from "@bitwarden/key-management";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { EmergencyAccessStatusType } from "../enums/emergency-access-status-type";
import { EmergencyAccessType } from "../enums/emergency-access-type";
@@ -258,7 +255,7 @@ export class EmergencyAccessService
}
const grantorUserKey = (await this.encryptService.decapsulateKeyUnsigned(
new EncString(response.keyEncrypted),
response.keyEncrypted,
activeUserPrivateKey,
)) as UserKey;
@@ -288,7 +285,7 @@ export class EmergencyAccessService
}
const grantorKey = await this.encryptService.decapsulateKeyUnsigned(
new EncString(takeoverResponse.keyEncrypted),
takeoverResponse.keyEncrypted,
activeUserPrivateKey,
);
if (grantorKey == null) {
@@ -424,16 +421,18 @@ export class EmergencyAccessService
return requests;
}
private async encryptKey(userKey: UserKey, publicKey: Uint8Array): Promise<EncryptedString> {
const publicKeyEncryptedUserKey = await this.encryptService.encapsulateKeyUnsigned(
userKey,
publicKey,
);
private async encryptKey(userKey: UserKey, publicKey: Uint8Array): Promise<UnsignedSharedKey> {
// Encapsulate the user-key for the grantee's public key. There is no sender authentication provided here!
const granteePublicKeyEncapsulatedUnsignedGrantorUserKey =
await this.encryptService.encapsulateKeyUnsigned(userKey, publicKey);
if (publicKeyEncryptedUserKey == null || !publicKeyEncryptedUserKey.encryptedString) {
if (
granteePublicKeyEncapsulatedUnsignedGrantorUserKey == null ||
!granteePublicKeyEncapsulatedUnsignedGrantorUserKey
) {
throw new Error("publicKeyEncryptedUserKey not found");
}
return publicKeyEncryptedUserKey.encryptedString;
return granteePublicKeyEncapsulatedUnsignedGrantorUserKey;
}
}

View File

@@ -6,11 +6,11 @@ import {
OrganizationUserResetPasswordDetailsResponse,
} from "@bitwarden/admin-console/common";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { newGuid } from "@bitwarden/guid";
import { KeyService } from "@bitwarden/key-management";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { UserId } from "@bitwarden/user-core";
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
@@ -196,7 +196,7 @@ describe("OrganizationAuthRequestService", () => {
organizationUserResetPasswordDetailsResponse,
);
const encryptedUserKey = new EncString("encryptedUserKey");
const encryptedUserKey = "unsignedSharedKey" as UnsignedSharedKey;
encryptService.decapsulateKeyUnsigned.mockResolvedValue(
new SymmetricCryptoKey(new Uint8Array(32)),
);
@@ -213,13 +213,7 @@ describe("OrganizationAuthRequestService", () => {
expect(organizationAuthRequestApiService.bulkUpdatePendingRequests).toHaveBeenCalledWith(
organizationId,
[
new OrganizationAuthRequestUpdateRequest(
"requestId1",
true,
encryptedUserKey.encryptedString,
),
],
[new OrganizationAuthRequestUpdateRequest("requestId1", true, encryptedUserKey)],
);
});
});
@@ -241,7 +235,7 @@ describe("OrganizationAuthRequestService", () => {
organizationUserResetPasswordDetailsResponse,
);
const encryptedUserKey = new EncString("encryptedUserKey");
const encryptedUserKey = "unsignedSharedKey" as UnsignedSharedKey;
encryptService.decapsulateKeyUnsigned.mockResolvedValue(
new SymmetricCryptoKey(new Uint8Array(32)),
);

View File

@@ -13,6 +13,7 @@ import { OrgKey, ProviderKey } from "@bitwarden/common/types/key";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { newGuid } from "@bitwarden/guid";
import { KeyService } from "@bitwarden/key-management";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { UserId } from "@bitwarden/user-core";
import { WebProviderService } from "./web-provider.service";
@@ -122,7 +123,10 @@ describe("WebProviderService", () => {
const defaultCollectionTranslation = "Default Collection";
beforeEach(() => {
keyService.makeOrgKey.mockResolvedValue([new EncString("mockEncryptedKey"), mockOrgKey]);
keyService.makeOrgKey.mockResolvedValue([
"mockEncryptedKey" as UnsignedSharedKey,
mockOrgKey,
]);
keyService.makeKeyPair.mockResolvedValue([publicKey, encryptedPrivateKey]);
i18nService.t.mockReturnValue(defaultCollectionTranslation);
encryptService.encryptString.mockResolvedValue(encryptedCollectionName);

View File

@@ -125,7 +125,7 @@ export class SetupComponent implements OnInit, OnDestroy {
}
const activeUserId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
const providerKey = await this.keyService.makeOrgKey<ProviderKey>(activeUserId);
const key = providerKey[0].encryptedString;
const key = providerKey[0];
const request = new ProviderSetupRequest();
request.name = this.formGroup.value.name!;

View File

@@ -83,7 +83,7 @@ export class SetupBusinessUnitComponent extends BaseAcceptComponent {
);
const userId = await firstValueFrom(activeUserId$);
const [{ encryptedString: encryptedProviderKey }, providerKey] =
const [encryptedProviderKey, providerKey] =
await this.keyService.makeOrgKey<ProviderKey>(userId);
const organizationKey = await firstValueFrom(organizationKey$);

View File

@@ -1,6 +1,7 @@
import { EncryptedString } from "@bitwarden/common/key-management/crypto/models/enc-string";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
export class OrganizationUserConfirmRequest {
key: EncryptedString | undefined;
key: UnsignedSharedKey | undefined;
defaultUserCollectionName: EncryptedString | undefined;
}

View File

@@ -18,6 +18,7 @@ import { CsprngArray } from "@bitwarden/common/types/csprng";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { OrgKey } from "@bitwarden/common/types/key";
import { KeyService } from "@bitwarden/key-management";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { DefaultOrganizationUserService } from "./default-organization-user.service";
@@ -36,7 +37,7 @@ describe("DefaultOrganizationUserService", () => {
const mockPublicKey = new Uint8Array(64) as CsprngArray;
const mockRandomBytes = new Uint8Array(64) as CsprngArray;
const mockOrgKey = new SymmetricCryptoKey(mockRandomBytes) as OrgKey;
const mockEncryptedKey = { encryptedString: "encrypted-key" } as EncString;
const mockEncryptedKey = "encrypted-key" as UnsignedSharedKey;
const mockEncryptedCollectionName = { encryptedString: "encrypted-collection-name" } as EncString;
const mockDefaultCollectionName = "My Items";
@@ -116,7 +117,7 @@ describe("DefaultOrganizationUserService", () => {
mockOrganization.id,
mockUserId,
{
key: mockEncryptedKey.encryptedString,
key: mockEncryptedKey,
defaultUserCollectionName: mockEncryptedCollectionName.encryptedString,
} as OrganizationUserConfirmRequest,
);

View File

@@ -46,7 +46,7 @@ export class DefaultOrganizationUserService implements OrganizationUserService {
return combineLatest([encryptedKey$, encryptedCollectionName$]).pipe(
map(([key, collectionName]) => ({
key: key.encryptedString,
key: key,
defaultUserCollectionName: collectionName.encryptedString,
})),
);

View File

@@ -236,7 +236,7 @@ export class DefaultSetInitialPasswordService implements SetInitialPasswordServi
orgPublicKey,
);
if (orgPublicKeyEncryptedUserKey == null || !orgPublicKeyEncryptedUserKey.encryptedString) {
if (orgPublicKeyEncryptedUserKey == null || !orgPublicKeyEncryptedUserKey) {
throw new Error(
"orgPublicKeyEncryptedUserKey not found. Could not handle reset password auto enroll.",
);
@@ -244,7 +244,7 @@ export class DefaultSetInitialPasswordService implements SetInitialPasswordServi
const enrollmentRequest = new OrganizationUserResetPasswordEnrollmentRequest();
enrollmentRequest.masterPasswordHash = masterKeyHash;
enrollmentRequest.resetPasswordKey = orgPublicKeyEncryptedUserKey.encryptedString;
enrollmentRequest.resetPasswordKey = orgPublicKeyEncryptedUserKey;
await this.organizationUserApiService.putOrganizationUserResetPasswordEnrollment(
orgId,

View File

@@ -34,6 +34,7 @@ import { CsprngArray } from "@bitwarden/common/types/csprng";
import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey, UserPrivateKey, UserPublicKey } from "@bitwarden/common/types/key";
import { DEFAULT_KDF_CONFIG, KdfConfigService, KeyService } from "@bitwarden/key-management";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { DefaultSetInitialPasswordService } from "./default-set-initial-password.service.implementation";
import {
@@ -111,7 +112,7 @@ describe("DefaultSetInitialPasswordService", () => {
let keysRequest: KeysRequest;
let organizationKeys: OrganizationKeysResponse;
let orgPublicKeyEncryptedUserKey: EncString;
let orgPublicKeyEncryptedUserKey: UnsignedSharedKey;
let userDecryptionOptions: UserDecryptionOptions;
let userDecryptionOptionsSubject: BehaviorSubject<UserDecryptionOptions>;
@@ -145,7 +146,7 @@ describe("DefaultSetInitialPasswordService", () => {
privateKey: "orgPrivateKey",
publicKey: "orgPublicKey",
} as OrganizationKeysResponse;
orgPublicKeyEncryptedUserKey = new EncString("orgPublicKeyEncryptedUserKey");
orgPublicKeyEncryptedUserKey = "orgPublicKeyEncryptedUserKey" as UnsignedSharedKey;
userDecryptionOptions = new UserDecryptionOptions({ hasMasterPassword: true });
userDecryptionOptionsSubject = new BehaviorSubject(userDecryptionOptions);
@@ -164,7 +165,7 @@ describe("DefaultSetInitialPasswordService", () => {
enrollmentRequest = new OrganizationUserResetPasswordEnrollmentRequest();
enrollmentRequest.masterPasswordHash = credentials.newServerMasterKeyHash;
enrollmentRequest.resetPasswordKey = orgPublicKeyEncryptedUserKey.encryptedString;
enrollmentRequest.resetPasswordKey = orgPublicKeyEncryptedUserKey;
});
interface MockConfig {
@@ -449,7 +450,7 @@ describe("DefaultSetInitialPasswordService", () => {
if (property === "orgPublicKeyEncryptedUserKey") {
orgPublicKeyEncryptedUserKey = null;
} else {
orgPublicKeyEncryptedUserKey.encryptedString = "" as EncryptedString;
orgPublicKeyEncryptedUserKey = "" as UnsignedSharedKey;
}
setupMocks({ ...defaultMockConfig, resetPasswordAutoEnroll: true });

View File

@@ -5,6 +5,7 @@ import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth
import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response";
import { UserId } from "@bitwarden/common/types/guid";
import { UserKey, MasterKey } from "@bitwarden/common/types/key";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
export abstract class AuthRequestServiceAbstraction {
/** Emits an auth request id when an auth request has been approved. */
@@ -93,7 +94,7 @@ export abstract class AuthRequestServiceAbstraction {
* @returns The decrypted `UserKey`.
*/
abstract decryptPubKeyEncryptedUserKey(
pubKeyEncryptedUserKey: string,
pubKeyEncryptedUserKey: UnsignedSharedKey,
privateKey: ArrayBuffer,
): Promise<UserKey>;
/**

View File

@@ -6,7 +6,6 @@ import { Jsonify } from "type-fest";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { WebAuthnLoginTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/webauthn-login-token.request";
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
import { UserId } from "@bitwarden/common/types/guid";
import { UserKey } from "@bitwarden/common/types/key";
@@ -89,7 +88,7 @@ export class WebAuthnLoginStrategy extends LoginStrategy {
// decrypt user key with private key
const userKey = await this.encryptService.decapsulateKeyUnsigned(
new EncString(webAuthnPrfOption.encryptedUserKey.encryptedString),
webAuthnPrfOption.encryptedUserKey,
privateKey,
);

View File

@@ -16,6 +16,7 @@ import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { newGuid } from "@bitwarden/guid";
import { KeyService } from "@bitwarden/key-management";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { DefaultAuthRequestApiService } from "./auth-request-api.service";
import { AuthRequestService } from "./auth-request.service";
@@ -89,9 +90,9 @@ describe("AuthRequestService", () => {
describe("approveOrDenyAuthRequest", () => {
beforeEach(() => {
encryptService.encapsulateKeyUnsigned.mockResolvedValue({
encryptedString: "ENCRYPTED_STRING",
} as EncString);
encryptService.encapsulateKeyUnsigned.mockResolvedValue(
"ENCRYPTED_STRING" as UnsignedSharedKey,
);
appIdService.getAppId.mockResolvedValue("APP_ID");
});
it("should throw if auth request is missing id or key", async () => {
@@ -221,7 +222,7 @@ describe("AuthRequestService", () => {
// Act
const result = await sut.decryptPubKeyEncryptedUserKey(
mockPubKeyEncryptedUserKey,
mockPubKeyEncryptedUserKey as UnsignedSharedKey,
mockPrivateKey,
);

View File

@@ -25,6 +25,7 @@ import {
import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { KeyService } from "@bitwarden/key-management";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { AuthRequestApiServiceAbstraction } from "../../abstractions/auth-request-api.service";
import { AuthRequestServiceAbstraction } from "../../abstractions/auth-request.service.abstraction";
@@ -143,7 +144,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(keyToEncrypt, pubKey);
const response = new PasswordlessAuthRequest(
encryptedKey.encryptedString,
encryptedKey,
undefined,
await this.appIdService.getAppId(),
approve,
@@ -186,11 +187,11 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
// Decryption helpers
async decryptPubKeyEncryptedUserKey(
pubKeyEncryptedUserKey: string,
pubKeyEncryptedUserKey: UnsignedSharedKey,
privateKey: Uint8Array,
): Promise<UserKey> {
const decryptedUserKey = await this.encryptService.decapsulateKeyUnsigned(
new EncString(pubKeyEncryptedUserKey),
pubKeyEncryptedUserKey,
privateKey,
);

View File

@@ -1,10 +1,12 @@
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
export type EncryptedOrganizationKeyData =
| OrganizationEncryptedOrganizationKeyData
| ProviderEncryptedOrganizationKeyData;
type OrganizationEncryptedOrganizationKeyData = {
type: "organization";
key: string;
key: UnsignedSharedKey;
};
type ProviderEncryptedOrganizationKeyData = {

View File

@@ -1,3 +1,5 @@
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service";
import { EncString } from "../../../key-management/crypto/models/enc-string";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
@@ -5,15 +7,15 @@ import { OrgKey, UserPrivateKey } from "../../../types/key";
import { EncryptedOrganizationKeyData } from "../data/encrypted-organization-key.data";
export abstract class BaseEncryptedOrganizationKey {
abstract get encryptedOrganizationKey(): EncString;
abstract get encryptedOrganizationKey(): UnsignedSharedKey;
static fromData(data: EncryptedOrganizationKeyData) {
switch (data.type) {
case "organization":
return new EncryptedOrganizationKey(data.key);
return new EncryptedOrganizationKey(data.key as UnsignedSharedKey);
case "provider":
return new ProviderEncryptedOrganizationKey(data.key, data.providerId);
return new ProviderEncryptedOrganizationKey(data.key as UnsignedSharedKey, data.providerId);
default:
return null;
@@ -28,7 +30,7 @@ export abstract class BaseEncryptedOrganizationKey {
}
export class EncryptedOrganizationKey implements BaseEncryptedOrganizationKey {
constructor(private key: string) {}
constructor(private key: UnsignedSharedKey) {}
async decrypt(encryptService: EncryptService, privateKey: UserPrivateKey) {
return (await encryptService.decapsulateKeyUnsigned(
@@ -38,7 +40,7 @@ export class EncryptedOrganizationKey implements BaseEncryptedOrganizationKey {
}
get encryptedOrganizationKey() {
return new EncString(this.key);
return this.key;
}
toData(): EncryptedOrganizationKeyData {
@@ -51,7 +53,7 @@ export class EncryptedOrganizationKey implements BaseEncryptedOrganizationKey {
export class ProviderEncryptedOrganizationKey implements BaseEncryptedOrganizationKey {
constructor(
private key: string,
private key: UnsignedSharedKey,
private providerId: string,
) {}
@@ -67,7 +69,7 @@ export class ProviderEncryptedOrganizationKey implements BaseEncryptedOrganizati
}
get encryptedOrganizationKey() {
return new EncString(this.key);
return this.key;
}
toData(): EncryptedOrganizationKeyData {

View File

@@ -1,4 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
// @ts-strict-ignore
interface TokenizedPaymentMethod {
type: "bankAccount" | "card" | "payPal";
@@ -20,7 +23,7 @@ export class ProviderSetupRequest {
businessName: string;
billingEmail: string;
token: string;
key: string;
key: UnsignedSharedKey;
paymentMethod: TokenizedPaymentMethod;
billingAddress: BillingAddress;
}

View File

@@ -1,3 +1,5 @@
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { MemberDecryptionType } from "../../../auth/enums/sso";
import { ProductTierType } from "../../../billing/enums";
import { BaseResponse } from "../../../models/response/base.response";
@@ -29,7 +31,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
seats: number;
maxCollections: number;
maxStorageGb?: number;
key: string;
key: UnsignedSharedKey;
hasPublicAndPrivateKeys: boolean;
status: OrganizationUserStatusType;
type: OrganizationUserType;

View File

@@ -1,3 +1,5 @@
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { BaseResponse } from "../../../models/response/base.response";
import {
ProviderStatusType,
@@ -10,7 +12,7 @@ import { PermissionsApi } from "../api/permissions.api";
export class ProfileProviderResponse extends BaseResponse {
id: string;
name: string;
key: string;
key: UnsignedSharedKey;
status: ProviderUserStatusType;
type: ProviderUserType;
enabled: boolean;

View File

@@ -1,3 +1,5 @@
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { DeviceType } from "../../../enums";
import { BaseResponse } from "../../../models/response/base.response";
@@ -11,7 +13,7 @@ export class AuthRequestResponse extends BaseResponse {
requestDeviceIdentifier: string;
requestIpAddress: string;
requestCountryName: string;
key: string; // could be either an encrypted MasterKey or an encrypted UserKey
key: UnsignedSharedKey; // could be either an encrypted MasterKey or an encrypted UserKey
masterPasswordHash: string; // if hash is present, the `key` above is an encrypted MasterKey (else `key` is an encrypted UserKey)
creationDate: string;
requestApproved?: boolean;

View File

@@ -2,6 +2,8 @@
// @ts-strict-ignore
import { Jsonify } from "type-fest";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { DeviceType } from "../../../enums";
import { EncString } from "../../../key-management/crypto/models/enc-string";
import { RotateableKeySet } from "../../../key-management/keys/models/rotateable-key-set";
@@ -16,7 +18,7 @@ export class ProtectedDeviceResponse extends BaseResponse {
this.type = this.getResponseProperty("type");
this.creationDate = new Date(this.getResponseProperty("creationDate"));
if (response.encryptedUserKey) {
this.encryptedUserKey = new EncString(this.getResponseProperty("encryptedUserKey"));
this.encryptedUserKey = this.getResponseProperty("encryptedUserKey");
}
if (response.encryptedPublicKey) {
this.encryptedPublicKey = new EncString(this.getResponseProperty("encryptedPublicKey"));
@@ -32,7 +34,7 @@ export class ProtectedDeviceResponse extends BaseResponse {
* Intended to be the users symmetric key that is encrypted in some form, the current way to encrypt this is with
* the devices public key.
*/
encryptedUserKey: EncString;
encryptedUserKey: UnsignedSharedKey;
/**
* Intended to be the public key that was generated for a device upon trust and encrypted. Currenly encrypted using
* a users symmetric key so that when trusted and unlocked a user can decrypt the public key for all their devices.

View File

@@ -1,5 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { EncString } from "../../../../key-management/crypto/models/enc-string";
import { BaseResponse } from "../../../../models/response/base.response";
@@ -18,7 +20,7 @@ export class TrustedDeviceUserDecryptionOptionResponse extends BaseResponse {
hasManageResetPasswordPermission: boolean;
isTdeOffboarding: boolean;
encryptedPrivateKey: EncString;
encryptedUserKey: EncString;
encryptedUserKey: UnsignedSharedKey;
constructor(response: any) {
super(response);
@@ -35,7 +37,7 @@ export class TrustedDeviceUserDecryptionOptionResponse extends BaseResponse {
this.encryptedPrivateKey = new EncString(this.getResponseProperty("EncryptedPrivateKey"));
}
if (response.EncryptedUserKey) {
this.encryptedUserKey = new EncString(this.getResponseProperty("EncryptedUserKey"));
this.encryptedUserKey = this.getResponseProperty("EncryptedUserKey");
}
}
}

View File

@@ -1,5 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { EncString } from "../../../../key-management/crypto/models/enc-string";
import { BaseResponse } from "../../../../models/response/base.response";
@@ -10,7 +12,7 @@ export interface IWebAuthnPrfDecryptionOptionServerResponse {
export class WebAuthnPrfDecryptionOptionResponse extends BaseResponse {
encryptedPrivateKey: EncString;
encryptedUserKey: EncString;
encryptedUserKey: UnsignedSharedKey;
constructor(response: IWebAuthnPrfDecryptionOptionServerResponse) {
super(response);
@@ -18,7 +20,7 @@ export class WebAuthnPrfDecryptionOptionResponse extends BaseResponse {
this.encryptedPrivateKey = new EncString(this.getResponseProperty("EncryptedPrivateKey"));
}
if (response.EncryptedUserKey) {
this.encryptedUserKey = new EncString(this.getResponseProperty("EncryptedUserKey"));
this.encryptedUserKey = this.getResponseProperty("EncryptedUserKey") as UnsignedSharedKey;
}
}
}

View File

@@ -63,7 +63,7 @@ export class PasswordResetEnrollmentServiceImplementation
const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(userKey, orgPublicKey);
const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest();
resetRequest.resetPasswordKey = encryptedKey.encryptedString;
resetRequest.resetPasswordKey = encryptedKey;
await this.organizationUserApiService.putOrganizationUserResetPasswordEnrollment(
organizationId,

View File

@@ -16,6 +16,7 @@ import { newGuid } from "@bitwarden/guid";
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
// eslint-disable-next-line no-restricted-imports
import { KeyService } from "@bitwarden/key-management";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { UserId } from "@bitwarden/user-core";
import { OrganizationKeysRequest } from "../../admin-console/models/request/organization-keys.request";
@@ -80,7 +81,7 @@ describe("OrganizationBillingService", () => {
} as OrganizationResponse;
organizationApiService.create.mockResolvedValue(organizationResponse);
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypted-key"), {} as OrgKey]);
keyService.makeOrgKey.mockResolvedValue(["encrypted-key" as UnsignedSharedKey, {} as OrgKey]);
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
@@ -104,7 +105,7 @@ describe("OrganizationBillingService", () => {
} as SubscriptionInformation;
organizationApiService.create.mockRejectedValue(new Error("Failed to create organization"));
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypted-key"), {} as OrgKey]);
keyService.makeOrgKey.mockResolvedValue(["encrypted-key" as UnsignedSharedKey, {} as OrgKey]);
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
@@ -164,7 +165,7 @@ describe("OrganizationBillingService", () => {
} as OrganizationResponse;
organizationApiService.createWithoutPayment.mockResolvedValue(organizationResponse);
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypted-key"), {} as OrgKey]);
keyService.makeOrgKey.mockResolvedValue(["encrypted-key" as UnsignedSharedKey, {} as OrgKey]);
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
@@ -186,7 +187,7 @@ describe("OrganizationBillingService", () => {
} as SubscriptionInformation;
organizationApiService.createWithoutPayment.mockRejectedValue(new Error("Creation failed"));
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypted-key"), {} as OrgKey]);
keyService.makeOrgKey.mockResolvedValue(["encrypted-key" as UnsignedSharedKey, {} as OrgKey]);
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
@@ -224,7 +225,7 @@ describe("OrganizationBillingService", () => {
} as OrganizationResponse;
organizationApiService.create.mockResolvedValue(organizationResponse);
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypted-key"), {} as OrgKey]);
keyService.makeOrgKey.mockResolvedValue(["encrypted-key" as UnsignedSharedKey, {} as OrgKey]);
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
@@ -258,7 +259,7 @@ describe("OrganizationBillingService", () => {
} as SubscriptionInformation;
organizationApiService.create.mockRejectedValue(new Error("Failed to create organization"));
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypted-key"), {} as OrgKey]);
keyService.makeOrgKey.mockResolvedValue(["encrypted-key" as UnsignedSharedKey, {} as OrgKey]);
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
// Act & Assert
@@ -271,7 +272,7 @@ describe("OrganizationBillingService", () => {
describe("organization key creation methods", () => {
const organizationKeys = {
orgKey: new SymmetricCryptoKey(new Uint8Array(64)) as OrgKey,
publicKeyEncapsulatedOrgKey: new EncString("encryptedOrgKey"),
publicKeyEncapsulatedOrgKey: "encryptedOrgKey" as UnsignedSharedKey,
publicKey: "public-key",
encryptedPrivateKey: new EncString("encryptedPrivateKey"),
};
@@ -298,7 +299,7 @@ describe("OrganizationBillingService", () => {
billingEmail: "test@example.com",
initiationPath: "Registration form",
planType: 0,
key: organizationKeys.publicKeyEncapsulatedOrgKey.encryptedString,
key: organizationKeys.publicKeyEncapsulatedOrgKey,
keys: new OrganizationKeysRequest(
organizationKeys.publicKey,
organizationKeys.encryptedPrivateKey.encryptedString!,

View File

@@ -1,8 +1,10 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
// eslint-disable-next-line no-restricted-imports
import { KeyService } from "@bitwarden/key-management";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { UserId } from "@bitwarden/user-core";
import { ApiService } from "../../abstractions/api.service";
@@ -27,7 +29,7 @@ import { PlanType } from "../enums";
import { OrganizationNoPaymentMethodCreateRequest } from "../models/request/organization-no-payment-method-create-request";
interface OrganizationKeys {
encryptedKey: EncString;
encryptedKey: UnsignedSharedKey;
publicKey: string;
encryptedPrivateKey: EncString;
encryptedCollectionName: EncString;
@@ -158,7 +160,7 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs
request: OrganizationCreateRequest | OrganizationNoPaymentMethodCreateRequest,
keys: OrganizationKeys,
): void {
request.key = keys.encryptedKey.encryptedString;
request.key = keys.encryptedKey;
request.keys = new OrganizationKeysRequest(
keys.publicKey,
keys.encryptedPrivateKey.encryptedString,

View File

@@ -1,4 +1,5 @@
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { EncArrayBuffer } from "../../../platform/models/domain/enc-array-buffer";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
@@ -143,7 +144,7 @@ export abstract class EncryptService {
abstract encapsulateKeyUnsigned(
sharedKey: SymmetricCryptoKey,
encapsulationKey: Uint8Array,
): Promise<EncString>;
): Promise<UnsignedSharedKey>;
/**
* Decapsulates a shared symmetric key with an asymmetric private key
* Note: This does not establish sender authenticity
@@ -154,7 +155,7 @@ export abstract class EncryptService {
* @throws Error if decapsulation fails
*/
abstract decapsulateKeyUnsigned(
encryptedSharedKey: EncString,
encryptedSharedKey: UnsignedSharedKey,
decapsulationKey: Uint8Array,
): Promise<SymmetricCryptoKey>;

View File

@@ -101,15 +101,6 @@ export class EncString {
this.iv = encPieces[0];
this.data = encPieces[1];
break;
case EncryptionType.Rsa2048_OaepSha256_B64:
case EncryptionType.Rsa2048_OaepSha1_B64:
this.data = encPieces[0];
break;
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
this.data = encPieces[0];
this.mac = encPieces[1];
break;
default:
return;
}

View File

@@ -10,7 +10,7 @@ import { EncryptionType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { PureCrypto } from "@bitwarden/sdk-internal";
import { PureCrypto, UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { EncryptService } from "../abstractions/encrypt.service";
@@ -215,7 +215,7 @@ export class EncryptServiceImplementation implements EncryptService {
async encapsulateKeyUnsigned(
sharedKey: SymmetricCryptoKey,
encapsulationKey: Uint8Array,
): Promise<EncString> {
): Promise<UnsignedSharedKey> {
if (sharedKey == null) {
throw new Error("No sharedKey provided for encapsulation");
}
@@ -223,13 +223,14 @@ export class EncryptServiceImplementation implements EncryptService {
throw new Error("No encapsulationKey provided for encapsulation");
}
await SdkLoadService.Ready;
return new EncString(
PureCrypto.encapsulate_key_unsigned(sharedKey.toEncoded(), encapsulationKey),
);
return PureCrypto.encapsulate_key_unsigned(
sharedKey.toEncoded(),
encapsulationKey,
) as UnsignedSharedKey;
}
async decapsulateKeyUnsigned(
encryptedSharedKey: EncString,
encryptedSharedKey: UnsignedSharedKey,
decapsulationKey: Uint8Array,
): Promise<SymmetricCryptoKey> {
if (encryptedSharedKey == null) {
@@ -240,10 +241,7 @@ export class EncryptServiceImplementation implements EncryptService {
}
await SdkLoadService.Ready;
const keyBytes = PureCrypto.decapsulate_key_unsigned(
encryptedSharedKey.encryptedString,
decapsulationKey,
);
const keyBytes = PureCrypto.decapsulate_key_unsigned(encryptedSharedKey, decapsulationKey);
return new SymmetricCryptoKey(keyBytes);
}

View File

@@ -8,7 +8,7 @@ import { EncryptionType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { PureCrypto } from "@bitwarden/sdk-internal";
import { PureCrypto, UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { makeStaticByteArray } from "../../../../spec";
@@ -357,7 +357,7 @@ describe("EncryptService", () => {
it("encrypts data with provided key", async () => {
const actual = await encryptService.encapsulateKeyUnsigned(testKey, publicKey);
expect(actual).toEqual(new EncString("encapsulated_key_unsigned"));
expect(actual).toEqual("encapsulated_key_unsigned");
});
});
@@ -369,13 +369,16 @@ describe("EncryptService", () => {
});
it("throws if no private key is provided", () => {
return expect(encryptService.decapsulateKeyUnsigned(encString, null)).rejects.toThrow(
"No decapsulationKey provided for decapsulation",
);
const unsignedSharedKey = encString.encryptedString as unknown as UnsignedSharedKey;
return expect(
encryptService.decapsulateKeyUnsigned(unsignedSharedKey, null),
).rejects.toThrow("No decapsulationKey provided for decapsulation");
});
it("decrypts data with provided key", async () => {
const actual = await encryptService.decapsulateKeyUnsigned(makeEncString(data), privateKey);
const unsignedSharedKey = makeEncString(data)
.encryptedString as unknown as UnsignedSharedKey;
const actual = await encryptService.decapsulateKeyUnsigned(unsignedSharedKey, privateKey);
expect(actual.toEncoded()).toEqualBuffer(new Uint8Array(64));
});
});

View File

@@ -1,6 +1,7 @@
import { Observable } from "rxjs";
import { OtherDeviceKeysUpdateRequest } from "@bitwarden/common/auth/models/request/update-devices-trust.request";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { DeviceResponse } from "../../../auth/abstractions/devices/responses/device.response";
import { UserId } from "../../../types/guid";
@@ -42,7 +43,7 @@ export abstract class DeviceTrustServiceAbstraction {
abstract decryptUserKeyWithDeviceKey(
userId: UserId,
encryptedDevicePrivateKey: EncString,
encryptedUserKey: EncString,
encryptedUserKey: UnsignedSharedKey,
deviceKey: DeviceKey,
): Promise<UserKey | null>;
abstract rotateDevicesTrust(

View File

@@ -8,6 +8,7 @@ import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common"
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
// eslint-disable-next-line no-restricted-imports
import { KeyService } from "@bitwarden/key-management";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { AccountService } from "../../../auth/abstractions/account.service";
import { DeviceResponse } from "../../../auth/abstractions/devices/responses/device.response";
@@ -188,7 +189,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
const deviceIdentifier = await this.appIdService.getAppId();
const deviceResponse = await this.devicesApiService.updateTrustedDeviceKeys(
deviceIdentifier,
devicePublicKeyEncryptedUserKey.encryptedString,
devicePublicKeyEncryptedUserKey,
userKeyEncryptedDevicePublicKey.encryptedString,
deviceKeyEncryptedDevicePrivateKey.encryptedString,
);
@@ -250,7 +251,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
const request = new OtherDeviceKeysUpdateRequest();
request.encryptedPublicKey = newRotateableKeySet.encryptedPublicKey.encryptedString;
request.encryptedUserKey = newRotateableKeySet.encapsulatedDownstreamKey.encryptedString;
request.encryptedUserKey = newRotateableKeySet.encapsulatedDownstreamKey;
request.deviceId = device.id;
return request;
})
@@ -313,7 +314,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
);
const currentDeviceUpdateRequest = new DeviceKeysUpdateRequest();
currentDeviceUpdateRequest.encryptedUserKey = encryptedNewUserKey.encryptedString;
currentDeviceUpdateRequest.encryptedUserKey = encryptedNewUserKey;
currentDeviceUpdateRequest.encryptedPublicKey = encryptedDevicePublicKey.encryptedString;
// TODO: For device management, allow this method to take an array of device ids that can be looped over and individually rotated
@@ -387,7 +388,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
async decryptUserKeyWithDeviceKey(
userId: UserId,
encryptedDevicePrivateKey: EncString,
encryptedUserKey: EncString,
encryptedUserKey: UnsignedSharedKey,
deviceKey: DeviceKey,
): Promise<UserKey | null> {
if (!userId) {
@@ -418,7 +419,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
// Attempt to decrypt encryptedUserDataKey with devicePrivateKey
const userKey = await this.encryptService.decapsulateKeyUnsigned(
new EncString(encryptedUserKey.encryptedString),
encryptedUserKey,
devicePrivateKey,
);

View File

@@ -40,6 +40,7 @@ import { KeyGenerationService } from "../../crypto";
import { CryptoFunctionService } from "../../crypto/abstractions/crypto-function.service";
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
import { EncString } from "../../crypto/models/enc-string";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import {
SHOULD_TRUST_DEVICE,
@@ -350,7 +351,7 @@ describe("deviceTrustService", () => {
const deviceRsaKeyLength = 2048;
let mockDeviceRsaKeyPair: [Uint8Array, Uint8Array];
let mockDevicePublicKeyEncryptedUserKey: EncString;
let mockDevicePublicKeyEncryptedUserKey: UnsignedSharedKey;
let mockUserKeyEncryptedDevicePublicKey: EncString;
let mockDeviceKeyEncryptedDevicePrivateKey: EncString;
@@ -386,10 +387,8 @@ describe("deviceTrustService", () => {
new Uint8Array(deviceRsaKeyLength),
];
mockDevicePublicKeyEncryptedUserKey = new EncString(
EncryptionType.Rsa2048_OaepSha1_B64,
"mockDevicePublicKeyEncryptedUserKey",
);
mockDevicePublicKeyEncryptedUserKey =
"4.mockDevicePublicKeyEncryptedUserKey" as UnsignedSharedKey;
mockUserKeyEncryptedDevicePublicKey = new EncString(
EncryptionType.AesCbc256_HmacSha256_B64,
@@ -460,7 +459,7 @@ describe("deviceTrustService", () => {
expect(devicesApiServiceUpdateTrustedDeviceKeysSpy).toHaveBeenCalledTimes(1);
expect(devicesApiServiceUpdateTrustedDeviceKeysSpy).toHaveBeenCalledWith(
mockDeviceId,
mockDevicePublicKeyEncryptedUserKey.encryptedString,
mockDevicePublicKeyEncryptedUserKey,
mockUserKeyEncryptedDevicePublicKey.encryptedString,
mockDeviceKeyEncryptedDevicePrivateKey.encryptedString,
);
@@ -542,7 +541,7 @@ describe("deviceTrustService", () => {
describe("decryptUserKeyWithDeviceKey", () => {
let mockDeviceKey: DeviceKey;
let mockEncryptedDevicePrivateKey: EncString;
let mockEncryptedUserKey: EncString;
let mockEncryptedUserKey: UnsignedSharedKey;
let mockUserKey: UserKey;
beforeEach(() => {
@@ -557,10 +556,7 @@ describe("deviceTrustService", () => {
"mockEncryptedDevicePrivateKey",
);
mockEncryptedUserKey = new EncString(
EncryptionType.AesCbc256_HmacSha256_B64,
"mockEncryptedUserKey",
);
mockEncryptedUserKey = "2.mockEncryptedUserKey" as UnsignedSharedKey;
jest.clearAllMocks();
});
@@ -698,7 +694,7 @@ describe("deviceTrustService", () => {
encryptService.decryptBytes.mockResolvedValue(null);
encryptService.encryptString.mockResolvedValue(new EncString("test_encrypted_data"));
encryptService.encapsulateKeyUnsigned.mockResolvedValue(
new EncString("test_encrypted_data"),
"test_encrypted_data" as UnsignedSharedKey,
);
const protectedDeviceResponse = new ProtectedDeviceResponse({
@@ -708,7 +704,7 @@ describe("deviceTrustService", () => {
name: "Firefox",
type: DeviceType.FirefoxBrowser,
encryptedPublicKey: "",
encryptedUserKey: "",
encryptedUserKey: "" as UnsignedSharedKey,
});
devicesApiService.getDeviceKeys.mockResolvedValue(protectedDeviceResponse);
@@ -746,7 +742,7 @@ describe("deviceTrustService", () => {
encryptService.unwrapEncapsulationKey.mockResolvedValue(new Uint8Array(64));
encryptService.wrapEncapsulationKey.mockResolvedValue(new EncString("test_encrypted_data"));
encryptService.encapsulateKeyUnsigned.mockResolvedValue(
new EncString("test_encrypted_data"),
"test_encrypted_data" as UnsignedSharedKey,
);
const protectedDeviceResponse = new ProtectedDeviceResponse({
@@ -756,7 +752,7 @@ describe("deviceTrustService", () => {
name: "Firefox",
type: DeviceType.FirefoxBrowser,
encryptedPublicKey: "",
encryptedUserKey: "",
encryptedUserKey: "" as UnsignedSharedKey,
});
devicesApiService.getDeviceKeys.mockResolvedValue(protectedDeviceResponse);
const fakeOldUserKeyData = new Uint8Array(64);
@@ -823,7 +819,7 @@ describe("deviceTrustService", () => {
it("rotates current device keys and calls api service when the current device is trusted", async () => {
const currentEncryptedPublicKey = new EncString("2.cHVibGlj|cHVibGlj|cHVibGlj");
const currentEncryptedUserKey = new EncString("4.dXNlcg==");
const currentEncryptedUserKey = "4.dXNlcg==" as UnsignedSharedKey;
const fakeOldUserKeyData = new Uint8Array(new Uint8Array(64));
// Fill the first byte with something identifiable
@@ -849,7 +845,7 @@ describe("deviceTrustService", () => {
name: "Firefox",
type: DeviceType.FirefoxBrowser,
encryptedPublicKey: currentEncryptedPublicKey.encryptedString,
encryptedUserKey: currentEncryptedUserKey.encryptedString,
encryptedUserKey: currentEncryptedUserKey,
}),
);
});
@@ -871,7 +867,7 @@ describe("deviceTrustService", () => {
expect(new Uint8Array(data.toEncoded())[0]).toBe(FakeNewUserKeyMarker); // New key should have the first byte be '1';
expect(new Uint8Array(publicKey)[0]).toBe(FakeDecryptedPublicKeyMarker);
return Promise.resolve(new EncString("4.ZW5jcnlwdGVkdXNlcg=="));
return Promise.resolve("4.ZW5jcnlwdGVkdXNlcg==" as UnsignedSharedKey);
});
// Mock the reencryption of the device public key with the new user key

View File

@@ -1,3 +1,5 @@
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { PrfKey } from "../../../types/key";
import { EncString } from "../../crypto/models/enc-string";
@@ -21,7 +23,7 @@ export class RotateableKeySet<UpstreamKey extends SymmetricCryptoKey = Symmetric
constructor(
/** `DownstreamKey` protected by publicKey */
readonly encapsulatedDownstreamKey: EncString,
readonly encapsulatedDownstreamKey: UnsignedSharedKey,
/** DownstreamKey encrypted PublicKey */
readonly encryptedPublicKey: EncString,

View File

@@ -8,6 +8,7 @@ import { Utils } from "../../../platform/misc/utils";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
import { EncString } from "../../crypto/models/enc-string";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { RotateableKeySet } from "../models/rotateable-key-set";
import { DefaultRotateableKeySetService } from "./default-rotateable-key-set.service";
@@ -47,7 +48,7 @@ describe("DefaultRotateableKeySetService", () => {
it("should create a new key set", async () => {
const externalKey = createSymmetricKey();
const userKey = createSymmetricKey();
const encryptedUserKey = new EncString("encryptedUserKey");
const encryptedUserKey = "encryptedUserKey" as UnsignedSharedKey;
const encryptedPublicKey = new EncString("encryptedPublicKey");
const encryptedPrivateKey = new EncString("encryptedPrivateKey");
keyService.makeKeyPair.mockResolvedValue(["publicKey", encryptedPrivateKey]);
@@ -73,7 +74,7 @@ describe("DefaultRotateableKeySetService", () => {
describe("rotateKeySet", () => {
const keySet = new RotateableKeySet(
new EncString("encUserKey"),
"encUserKey" as UnsignedSharedKey,
new EncString("encPublicKey"),
new EncString("encPrivateKey"),
);
@@ -149,7 +150,7 @@ describe("DefaultRotateableKeySetService", () => {
const newDownstreamKey = new SymmetricCryptoKey(new Uint8Array(64));
const publicKey = Utils.fromB64ToArray("decryptedPublicKey");
const newEncryptedPublicKey = new EncString("newEncPublicKey");
const newEncryptedRotateableKey = new EncString("newEncUserKey");
const newEncryptedRotateableKey = "newEncUserKey" as UnsignedSharedKey;
encryptService.unwrapEncapsulationKey.mockResolvedValue(publicKey);
encryptService.wrapEncapsulationKey.mockResolvedValue(newEncryptedPublicKey);

View File

@@ -67,13 +67,13 @@ export class DefaultRotateableKeySetService implements RotateableKeySetService {
publicKey,
newDownstreamKey,
);
const newEncryptedRotateableKey = await this.encryptService.encapsulateKeyUnsigned(
const encapsulatedDownstreamKey = await this.encryptService.encapsulateKeyUnsigned(
newDownstreamKey,
publicKey,
);
const newRotateableKeySet = new RotateableKeySet<UpstreamKey>(
newEncryptedRotateableKey,
encapsulatedDownstreamKey,
newEncryptedPublicKey,
keySet.encryptedPrivateKey,
);

View File

@@ -1,8 +1,9 @@
import { EncryptedString } from "../../../key-management/crypto/models/enc-string";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { ProviderId } from "../../../types/guid";
import { CRYPTO_DISK, UserKeyDefinition } from "../../state";
export const USER_ENCRYPTED_PROVIDER_KEYS = UserKeyDefinition.record<EncryptedString, ProviderId>(
export const USER_ENCRYPTED_PROVIDER_KEYS = UserKeyDefinition.record<UnsignedSharedKey, ProviderId>(
CRYPTO_DISK,
"providerKeys",
{

View File

@@ -20,6 +20,7 @@ import {
UserPrivateKey,
UserPublicKey,
} from "@bitwarden/common/types/key";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { KdfConfig } from "../models/kdf-config";
@@ -262,7 +263,9 @@ export abstract class KeyService {
* @throws Error when no public key is found for the target user.
* @returns The new encrypted OrgKey | ProviderKey and the decrypted key itself
*/
abstract makeOrgKey<T extends OrgKey | ProviderKey>(userId: UserId): Promise<[EncString, T]>;
abstract makeOrgKey<T extends OrgKey | ProviderKey>(
userId: UserId,
): Promise<[UnsignedSharedKey, T]>;
/**
* Sets the user's encrypted private key in storage and
* clears the decrypted private key from memory

View File

@@ -47,6 +47,7 @@ import {
OrgKey,
ProviderKey,
} from "@bitwarden/common/types/key";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { KdfConfigService } from "./abstractions/kdf-config.service";
import { UserPrivateKeyDecryptionFailedError } from "./abstractions/key.service";
@@ -516,13 +517,12 @@ describe("keyService", () => {
return output;
}
function fakeOrgKeyDecryption(encryptedString: EncString, userPrivateKey: Uint8Array) {
function fakeOrgKeyDecryption(encryptedString: UnsignedSharedKey, userPrivateKey: Uint8Array) {
const output = new Uint8Array(64);
output.set(encryptedString.dataBytes);
output.set(
userPrivateKey.subarray(0, 64 - encryptedString.dataBytes.length),
encryptedString.dataBytes.length,
);
const dataBytes = encryptedString.split(".")[1];
const dataBytesArray = Uint8Array.from(atob(dataBytes), (c) => c.charCodeAt(0));
output.set(dataBytesArray);
output.set(userPrivateKey.subarray(0, 64 - dataBytes.length), dataBytes.length);
return output;
}
@@ -532,7 +532,7 @@ describe("keyService", () => {
userKey: UserKey;
encryptedPrivateKey: EncString;
orgKeys: Record<string, EncryptedOrganizationKeyData>;
providerKeys: Record<string, EncryptedString>;
providerKeys: Record<string, UnsignedSharedKey>;
};
function updateKeys(keys: Partial<UpdateKeysParams> = {}) {
@@ -595,7 +595,7 @@ describe("keyService", () => {
userKey: makeSymmetricCryptoKey<UserKey>(64),
encryptedPrivateKey: makeEncString("privateKey"),
orgKeys: {
[org1Id]: { type: "organization", key: makeEncString("org1Key").encryptedString! },
[org1Id]: { type: "organization", key: "org1Key" as UnsignedSharedKey },
},
});
@@ -615,7 +615,7 @@ describe("keyService", () => {
userKey: makeSymmetricCryptoKey<UserKey>(64),
encryptedPrivateKey: makeEncString("privateKey"),
orgKeys: {
[org1Id]: { type: "organization", key: makeEncString("org1Key").encryptedString! },
[org1Id]: { type: "organization", key: "org1Key" as UnsignedSharedKey },
},
providerKeys: {},
});
@@ -637,7 +637,7 @@ describe("keyService", () => {
userKey: makeSymmetricCryptoKey<UserKey>(64),
encryptedPrivateKey: makeEncString("privateKey"),
orgKeys: {
[org1Id]: { type: "organization", key: makeEncString("org1Key").encryptedString! },
[org1Id]: { type: "organization", key: "org1Key" as UnsignedSharedKey },
[org2Id]: {
type: "provider",
key: makeEncString("provider1Key").encryptedString!,
@@ -645,7 +645,7 @@ describe("keyService", () => {
},
},
providerKeys: {
provider1: makeEncString("provider1Key").encryptedString!,
provider1: "provider1Key" as UnsignedSharedKey,
},
});
@@ -700,7 +700,7 @@ describe("keyService", () => {
// User has their org keys set
updateKeys({
orgKeys: {
[org1Id]: { type: "organization", key: makeEncString("org1Key").encryptedString! },
[org1Id]: { type: "organization", key: "org1Key" as UnsignedSharedKey },
},
});
@@ -1003,7 +1003,7 @@ describe("keyService", () => {
describe("makeOrgKey", () => {
const mockUserPublicKey = new Uint8Array(64) as UserPublicKey;
const shareKey = new SymmetricCryptoKey(new Uint8Array(64));
const mockEncapsulatedKey = new EncString("mockEncapsulatedKey");
const mockEncapsulatedKey = "mockEncapsulatedKey" as UnsignedSharedKey;
beforeEach(() => {
keyService.userPublicKey$ = jest

View File

@@ -60,6 +60,7 @@ import {
UserPrivateKey,
UserPublicKey,
} from "@bitwarden/common/types/key";
import { UnsignedSharedKey } from "@bitwarden/sdk-internal";
import { KdfConfigService } from "./abstractions/kdf-config.service";
import {
@@ -434,10 +435,10 @@ export class DefaultKeyService implements KeyServiceAbstraction {
async setProviderKeys(providers: ProfileProviderResponse[], userId: UserId): Promise<void> {
await this.stateProvider.getUser(userId, USER_ENCRYPTED_PROVIDER_KEYS).update(() => {
const encProviderKeys: { [providerId: ProviderId]: EncryptedString } = {};
const encProviderKeys: { [providerId: ProviderId]: UnsignedSharedKey } = {};
providers.forEach((provider) => {
encProviderKeys[provider.id as ProviderId] = provider.key as EncryptedString;
encProviderKeys[provider.id as ProviderId] = provider.key;
});
return encProviderKeys;
@@ -464,7 +465,9 @@ export class DefaultKeyService implements KeyServiceAbstraction {
await this.stateProvider.setUserState(USER_ENCRYPTED_PROVIDER_KEYS, null, userId);
}
async makeOrgKey<T extends OrgKey | ProviderKey>(userId: UserId): Promise<[EncString, T]> {
async makeOrgKey<T extends OrgKey | ProviderKey>(
userId: UserId,
): Promise<[UnsignedSharedKey, T]> {
if (userId == null) {
throw new Error("UserId is required");
}
@@ -856,7 +859,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
// Convert each value in the record to it's own decryption observable
convertValues(async (_, value) => {
const decapsulatedKey = await this.encryptService.decapsulateKeyUnsigned(
new EncString(value),
value,
userPrivateKey,
);
return decapsulatedKey as ProviderKey;
@@ -917,7 +920,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
switchMap(async ([encryptedOrgKeys, providerKeys]) => {
const userPubKey = await this.derivePublicKey(userPrivateKey);
const result: Record<OrganizationId, EncString> = {};
const result: Record<OrganizationId, UnsignedSharedKey> = {};
encryptedOrgKeys = encryptedOrgKeys ?? {};
for (const orgId of Object.keys(encryptedOrgKeys) as OrganizationId[]) {
if (result[orgId] != null) {
@@ -928,7 +931,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
continue;
}
let orgKey: EncString;
let orgKey: UnsignedSharedKey;
// Because the SDK only supports user encrypted org keys, we need to re-encrypt
// any provider encrypted org keys with the user's public key. This should be removed