[PM-7907] No more optional `privateKey` (#9029)
* Update Emergency Access To Get Their Own Key * Migrate Organization Keys To Get Their Own Key * Remove Optional Parameters * Update Abstraction Parameter Name to Match Implementation * Add @throws Doc
This commit is contained in:
parent
a4d5717283
commit
b46766affd
|
@ -153,6 +153,7 @@ describe("EmergencyAccessService", () => {
|
|||
} as EmergencyAccessTakeoverResponse);
|
||||
|
||||
const mockDecryptedGrantorUserKey = new Uint8Array(64);
|
||||
cryptoService.getPrivateKey.mockResolvedValue(new Uint8Array(64));
|
||||
cryptoService.rsaDecrypt.mockResolvedValueOnce(mockDecryptedGrantorUserKey);
|
||||
|
||||
const mockMasterKey = new SymmetricCryptoKey(new Uint8Array(64) as CsprngArray) as MasterKey;
|
||||
|
@ -197,6 +198,7 @@ describe("EmergencyAccessService", () => {
|
|||
kdf: KdfType.PBKDF2_SHA256,
|
||||
kdfIterations: 500,
|
||||
} as EmergencyAccessTakeoverResponse);
|
||||
cryptoService.getPrivateKey.mockResolvedValue(new Uint8Array(64));
|
||||
|
||||
await expect(
|
||||
emergencyAccessService.takeover(mockId, mockEmail, mockName),
|
||||
|
@ -204,6 +206,21 @@ describe("EmergencyAccessService", () => {
|
|||
|
||||
expect(emergencyAccessApiService.postEmergencyAccessPassword).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should throw an error if the users private key cannot be retrieved", async () => {
|
||||
emergencyAccessApiService.postEmergencyAccessTakeover.mockResolvedValueOnce({
|
||||
keyEncrypted: "EncryptedKey",
|
||||
kdf: KdfType.PBKDF2_SHA256,
|
||||
kdfIterations: 500,
|
||||
} as EmergencyAccessTakeoverResponse);
|
||||
cryptoService.getPrivateKey.mockResolvedValue(null);
|
||||
|
||||
await expect(emergencyAccessService.takeover(mockId, mockEmail, mockName)).rejects.toThrow(
|
||||
"user does not have a private key",
|
||||
);
|
||||
|
||||
expect(emergencyAccessApiService.postEmergencyAccessPassword).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRotatedKeys", () => {
|
||||
|
|
|
@ -209,7 +209,16 @@ export class EmergencyAccessService {
|
|||
async getViewOnlyCiphers(id: string): Promise<CipherView[]> {
|
||||
const response = await this.emergencyAccessApiService.postEmergencyAccessView(id);
|
||||
|
||||
const grantorKeyBuffer = await this.cryptoService.rsaDecrypt(response.keyEncrypted);
|
||||
const activeUserPrivateKey = await this.cryptoService.getPrivateKey();
|
||||
|
||||
if (activeUserPrivateKey == null) {
|
||||
throw new Error("Active user does not have a private key, cannot get view only ciphers.");
|
||||
}
|
||||
|
||||
const grantorKeyBuffer = await this.cryptoService.rsaDecrypt(
|
||||
response.keyEncrypted,
|
||||
activeUserPrivateKey,
|
||||
);
|
||||
const grantorUserKey = new SymmetricCryptoKey(grantorKeyBuffer) as UserKey;
|
||||
|
||||
const ciphers = await this.encryptService.decryptItems(
|
||||
|
@ -229,7 +238,16 @@ export class EmergencyAccessService {
|
|||
async takeover(id: string, masterPassword: string, email: string) {
|
||||
const takeoverResponse = await this.emergencyAccessApiService.postEmergencyAccessTakeover(id);
|
||||
|
||||
const grantorKeyBuffer = await this.cryptoService.rsaDecrypt(takeoverResponse.keyEncrypted);
|
||||
const activeUserPrivateKey = await this.cryptoService.getPrivateKey();
|
||||
|
||||
if (activeUserPrivateKey == null) {
|
||||
throw new Error("Active user does not have a private key, cannot complete a takeover.");
|
||||
}
|
||||
|
||||
const grantorKeyBuffer = await this.cryptoService.rsaDecrypt(
|
||||
takeoverResponse.keyEncrypted,
|
||||
activeUserPrivateKey,
|
||||
);
|
||||
if (grantorKeyBuffer == null) {
|
||||
throw new Error("Failed to decrypt grantor key");
|
||||
}
|
||||
|
|
|
@ -25,7 +25,13 @@ export class EncryptedOrganizationKey implements BaseEncryptedOrganizationKey {
|
|||
constructor(private key: string) {}
|
||||
|
||||
async decrypt(cryptoService: CryptoService) {
|
||||
const decValue = await cryptoService.rsaDecrypt(this.key);
|
||||
const activeUserPrivateKey = await cryptoService.getPrivateKey();
|
||||
|
||||
if (activeUserPrivateKey == null) {
|
||||
throw new Error("Active user does not have a private key, cannot decrypt organization key.");
|
||||
}
|
||||
|
||||
const decValue = await cryptoService.rsaDecrypt(this.key, activeUserPrivateKey);
|
||||
return new SymmetricCryptoKey(decValue) as OrgKey;
|
||||
}
|
||||
|
||||
|
|
|
@ -311,15 +311,17 @@ export abstract class CryptoService {
|
|||
* @param data The data to encrypt
|
||||
* @param publicKey The public key to use for encryption, if not provided, the user's public key will be used
|
||||
* @returns The encrypted data
|
||||
* @throws If the given publicKey is a null-ish value.
|
||||
*/
|
||||
abstract rsaEncrypt(data: Uint8Array, publicKey?: Uint8Array): Promise<EncString>;
|
||||
abstract rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString>;
|
||||
/**
|
||||
* Decrypts a value using RSA.
|
||||
* @param encValue The encrypted value to decrypt
|
||||
* @param privateKeyValue The private key to use for decryption
|
||||
* @param privateKey The private key to use for decryption
|
||||
* @returns The decrypted value
|
||||
* @throws If the given privateKey is a null-ish value.
|
||||
*/
|
||||
abstract rsaDecrypt(encValue: string, privateKeyValue?: Uint8Array): Promise<Uint8Array>;
|
||||
abstract rsaDecrypt(encValue: string, privateKey: Uint8Array): Promise<Uint8Array>;
|
||||
abstract randomNumber(min: number, max: number): Promise<number>;
|
||||
/**
|
||||
* Generates a new cipher key
|
||||
|
|
|
@ -621,19 +621,20 @@ export class CryptoService implements CryptoServiceAbstraction {
|
|||
await this.stateProvider.setUserState(USER_EVER_HAD_USER_KEY, null, userId);
|
||||
}
|
||||
|
||||
async rsaEncrypt(data: Uint8Array, publicKey?: Uint8Array): Promise<EncString> {
|
||||
async rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString> {
|
||||
if (publicKey == null) {
|
||||
publicKey = await this.getPublicKey();
|
||||
}
|
||||
if (publicKey == null) {
|
||||
throw new Error("Public key unavailable.");
|
||||
throw new Error("'publicKey' is a required parameter and must be non-null");
|
||||
}
|
||||
|
||||
const encBytes = await this.cryptoFunctionService.rsaEncrypt(data, publicKey, "sha1");
|
||||
return new EncString(EncryptionType.Rsa2048_OaepSha1_B64, Utils.fromBufferToB64(encBytes));
|
||||
}
|
||||
|
||||
async rsaDecrypt(encValue: string, privateKeyValue?: Uint8Array): Promise<Uint8Array> {
|
||||
async rsaDecrypt(encValue: string, privateKey: Uint8Array): Promise<Uint8Array> {
|
||||
if (privateKey == null) {
|
||||
throw new Error("'privateKey' is a required parameter and must be non-null");
|
||||
}
|
||||
|
||||
const headerPieces = encValue.split(".");
|
||||
let encType: EncryptionType = null;
|
||||
let encPieces: string[];
|
||||
|
@ -665,10 +666,6 @@ export class CryptoService implements CryptoServiceAbstraction {
|
|||
}
|
||||
|
||||
const data = Utils.fromB64ToArray(encPieces[0]);
|
||||
const privateKey = privateKeyValue ?? (await this.getPrivateKey());
|
||||
if (privateKey == null) {
|
||||
throw new Error("No private key.");
|
||||
}
|
||||
|
||||
let alg: "sha1" | "sha256" = "sha1";
|
||||
switch (encType) {
|
||||
|
|
|
@ -65,6 +65,10 @@ describe("derived decrypted org keys", () => {
|
|||
"org-id-2": new SymmetricCryptoKey(makeStaticByteArray(64, 2)) as OrgKey,
|
||||
};
|
||||
|
||||
const userPrivateKey = makeStaticByteArray(64, 3);
|
||||
|
||||
cryptoService.getPrivateKey.mockResolvedValue(userPrivateKey);
|
||||
|
||||
// TODO: How to not have to mock these decryptions. They are internal concerns of EncryptedOrganizationKey
|
||||
cryptoService.rsaDecrypt.mockResolvedValueOnce(decryptedOrgKeys["org-id-1"].key);
|
||||
cryptoService.rsaDecrypt.mockResolvedValueOnce(decryptedOrgKeys["org-id-2"].key);
|
||||
|
|
Loading…
Reference in New Issue