mirror of
https://github.com/bitwarden/browser
synced 2025-12-19 17:53:39 +00:00
[PM-21033/PM-22863] User Encryption v2 (#14942)
* Add new encrypt service functions * Undo changes * Cleanup * Fix build * Fix comments * Switch encrypt service to use SDK functions * Move remaining functions to PureCrypto * Tests * Increase test coverage * Split up userkey rotation v2 and add tests * Fix eslint * Fix type errors * Fix tests * Implement signing keys * Fix sdk init * Remove key rotation v2 flag * Fix parsing when user does not have signing keys * Clear up trusted key naming * Split up getNewAccountKeys * Add trim and lowercase * Replace user.email with masterKeySalt * Add wasTrustDenied to verifyTrust in key rotation service * Move testable userkey rotation service code to testable class * Fix build * Add comments * Undo changes * Fix incorrect behavior on aborting key rotation and fix import * Fix tests * Make members of userkey rotation service protected * Fix type error * Cleanup and add injectable annotation * Fix tests * Update apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Remove v1 rotation request * Add upgrade to user encryption v2 * Fix types * Update sdk method calls * Update request models for new server api for rotation * Fix build * Update userkey rotation for new server API * Update crypto client call for new sdk changes * Fix rotation with signing keys * Cargo lock * Fix userkey rotation service * Fix types * Undo changes to feature flag service * Fix linting * [PM-22863] Account security state (#15309) * Add account security state * Update key rotation * Rename * Fix build * Cleanup * Further cleanup * Tests * Increase test coverage * Add test * Increase test coverage * Fix builds and update sdk * Fix build * Fix tests * Reset changes to encrypt service * Cleanup * Add comment * Cleanup * Cleanup * Rename model * Cleanup * Fix build * Clean up * Fix types * Cleanup * Cleanup * Cleanup * Add test * Simplify request model * Rename and add comments * Fix tests * Update responses to use less strict typing * Fix response parsing for v1 users * Update libs/common/src/key-management/keys/response/private-keys.response.ts Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com> * Update libs/common/src/key-management/keys/response/private-keys.response.ts Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com> * Fix build * Fix build * Fix build * Undo change * Fix attachments not encrypting for v2 users --------- Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { SecurityStateResponse } from "../../security-state/response/security-state.response";
|
||||
|
||||
import { PublicKeyEncryptionKeyPairResponse } from "./public-key-encryption-key-pair.response";
|
||||
import { SignatureKeyPairResponse } from "./signature-key-pair.response";
|
||||
|
||||
/**
|
||||
* The privately accessible view of an entity (account / org)'s keys.
|
||||
* This includes the full key-pairs for public-key encryption and signing, as well as the security state if available.
|
||||
*/
|
||||
export class PrivateKeysResponseModel {
|
||||
readonly publicKeyEncryptionKeyPair: PublicKeyEncryptionKeyPairResponse;
|
||||
readonly signatureKeyPair: SignatureKeyPairResponse | null = null;
|
||||
readonly securityState: SecurityStateResponse | null = null;
|
||||
|
||||
constructor(response: unknown) {
|
||||
if (typeof response !== "object" || response == null) {
|
||||
throw new TypeError("Response must be an object");
|
||||
}
|
||||
|
||||
if (
|
||||
!("publicKeyEncryptionKeyPair" in response) ||
|
||||
typeof response.publicKeyEncryptionKeyPair !== "object"
|
||||
) {
|
||||
throw new TypeError("Response must contain a valid publicKeyEncryptionKeyPair");
|
||||
}
|
||||
this.publicKeyEncryptionKeyPair = new PublicKeyEncryptionKeyPairResponse(
|
||||
response.publicKeyEncryptionKeyPair,
|
||||
);
|
||||
|
||||
if (
|
||||
"signatureKeyPair" in response &&
|
||||
typeof response.signatureKeyPair === "object" &&
|
||||
response.signatureKeyPair != null
|
||||
) {
|
||||
this.signatureKeyPair = new SignatureKeyPairResponse(response.signatureKeyPair);
|
||||
}
|
||||
|
||||
if (
|
||||
"securityState" in response &&
|
||||
typeof response.securityState === "object" &&
|
||||
response.securityState != null
|
||||
) {
|
||||
this.securityState = new SecurityStateResponse(response.securityState);
|
||||
}
|
||||
|
||||
if (
|
||||
(this.signatureKeyPair !== null && this.securityState === null) ||
|
||||
(this.signatureKeyPair === null && this.securityState !== null)
|
||||
) {
|
||||
throw new TypeError(
|
||||
"Both signatureKeyPair and securityState must be present or absent together",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
|
||||
import { SignedPublicKey, UnsignedPublicKey, WrappedPrivateKey } from "../../types";
|
||||
|
||||
export class PublicKeyEncryptionKeyPairResponse {
|
||||
readonly wrappedPrivateKey: WrappedPrivateKey;
|
||||
readonly publicKey: UnsignedPublicKey;
|
||||
|
||||
readonly signedPublicKey: SignedPublicKey | null = null;
|
||||
|
||||
constructor(response: unknown) {
|
||||
if (typeof response !== "object" || response == null) {
|
||||
throw new TypeError("Response must be an object");
|
||||
}
|
||||
|
||||
if (!("publicKey" in response) || typeof response.publicKey !== "string") {
|
||||
throw new TypeError("Response must contain a valid publicKey");
|
||||
}
|
||||
this.publicKey = Utils.fromB64ToArray(response.publicKey) as UnsignedPublicKey;
|
||||
|
||||
if (!("wrappedPrivateKey" in response) || typeof response.wrappedPrivateKey !== "string") {
|
||||
throw new TypeError("Response must contain a valid wrappedPrivateKey");
|
||||
}
|
||||
this.wrappedPrivateKey = response.wrappedPrivateKey as WrappedPrivateKey;
|
||||
|
||||
if ("signedPublicKey" in response && typeof response.signedPublicKey === "string") {
|
||||
this.signedPublicKey = response.signedPublicKey as SignedPublicKey;
|
||||
} else {
|
||||
this.signedPublicKey = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { SignedPublicKey } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { UnsignedPublicKey, VerifyingKey } from "../../types";
|
||||
|
||||
/**
|
||||
* The publicly accessible view of an entity (account / org)'s keys. That includes the encryption public key, and the verifying key if available.
|
||||
*/
|
||||
export class PublicKeysResponseModel {
|
||||
readonly publicKey: UnsignedPublicKey;
|
||||
readonly verifyingKey: VerifyingKey | null;
|
||||
readonly signedPublicKey?: SignedPublicKey | null;
|
||||
|
||||
constructor(response: unknown) {
|
||||
if (typeof response !== "object" || response == null) {
|
||||
throw new TypeError("Response must be an object");
|
||||
}
|
||||
|
||||
if (!("publicKey" in response) || !(response.publicKey instanceof Uint8Array)) {
|
||||
throw new TypeError("Response must contain a valid publicKey");
|
||||
}
|
||||
this.publicKey = response.publicKey as UnsignedPublicKey;
|
||||
|
||||
if ("verifyingKey" in response && typeof response.verifyingKey === "string") {
|
||||
this.verifyingKey = response.verifyingKey as VerifyingKey;
|
||||
} else {
|
||||
this.verifyingKey = null;
|
||||
}
|
||||
|
||||
if ("signedPublicKey" in response && typeof response.signedPublicKey === "string") {
|
||||
this.signedPublicKey = response.signedPublicKey as SignedPublicKey;
|
||||
} else {
|
||||
this.signedPublicKey = null;
|
||||
}
|
||||
|
||||
if (
|
||||
(this.signedPublicKey !== null && this.verifyingKey === null) ||
|
||||
(this.signedPublicKey === null && this.verifyingKey !== null)
|
||||
) {
|
||||
throw new TypeError(
|
||||
"Both signedPublicKey and verifyingKey must be present or absent together",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { VerifyingKey, WrappedSigningKey } from "../../types";
|
||||
|
||||
export class SignatureKeyPairResponse {
|
||||
readonly wrappedSigningKey: WrappedSigningKey;
|
||||
readonly verifyingKey: VerifyingKey;
|
||||
|
||||
constructor(response: unknown) {
|
||||
if (typeof response !== "object" || response == null) {
|
||||
throw new TypeError("Response must be an object");
|
||||
}
|
||||
|
||||
if (!("wrappedSigningKey" in response) || typeof response.wrappedSigningKey !== "string") {
|
||||
throw new TypeError("Response must contain a valid wrappedSigningKey");
|
||||
}
|
||||
this.wrappedSigningKey = response.wrappedSigningKey as WrappedSigningKey;
|
||||
|
||||
if (!("verifyingKey" in response) || typeof response.verifyingKey !== "string") {
|
||||
throw new TypeError("Response must contain a valid verifyingKey");
|
||||
}
|
||||
this.verifyingKey = response.verifyingKey as VerifyingKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { PublicKeysResponseModel } from "../../response/public-keys.response";
|
||||
|
||||
export abstract class KeyApiService {
|
||||
abstract getUserPublicKeys(id: string): Promise<PublicKeysResponseModel>;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { ApiService } from "../../../abstractions/api.service";
|
||||
import { PublicKeysResponseModel } from "../response/public-keys.response";
|
||||
|
||||
import { KeyApiService } from "./abstractions/key-api-service.abstraction";
|
||||
|
||||
export class DefaultKeyApiService implements KeyApiService {
|
||||
constructor(private apiService: ApiService) {}
|
||||
|
||||
async getUserPublicKeys(id: UserId): Promise<PublicKeysResponseModel> {
|
||||
const response = await this.apiService.send("GET", "/users/" + id + "/keys", null, true, true);
|
||||
return new PublicKeysResponseModel(response);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user