mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-19731] Refactor encrypt service to expose key wrapping (#14080)
* Refactor encrypt service to expose key wrapping * Fix build * Undo ts strict removal * Fix wrong method being used to encrypt key material * Rename parameters and remove todo * Add summary to encrypt * Update libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Update libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Update libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Update libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Update libs/common/src/key-management/crypto/abstractions/encrypt.service.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Update libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Add tests for unhappy paths * Add test coverage * Add links --------- Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": ["Csprng", "decryptable", "Popout", "Reprompt", "takeuntil"],
|
"cSpell.words": ["Csprng", "Decapsulation", "decryptable", "Popout", "Reprompt", "takeuntil"],
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"**/locales/[^e]*/messages.json": true,
|
"**/locales/[^e]*/messages.json": true,
|
||||||
"**/locales/*[^n]/messages.json": true,
|
"**/locales/*[^n]/messages.json": true,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ describe("RotateableKeySetService", () => {
|
|||||||
keyService.makeKeyPair.mockResolvedValue(["publicKey", encryptedPrivateKey as any]);
|
keyService.makeKeyPair.mockResolvedValue(["publicKey", encryptedPrivateKey as any]);
|
||||||
keyService.getUserKey.mockResolvedValue({ key: userKey.key } as any);
|
keyService.getUserKey.mockResolvedValue({ key: userKey.key } as any);
|
||||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue(encryptedUserKey as any);
|
encryptService.encapsulateKeyUnsigned.mockResolvedValue(encryptedUserKey as any);
|
||||||
encryptService.encrypt.mockResolvedValue(encryptedPublicKey as any);
|
encryptService.wrapEncapsulationKey.mockResolvedValue(encryptedPublicKey as any);
|
||||||
|
|
||||||
const result = await service.createKeySet(externalKey as any);
|
const result = await service.createKeySet(externalKey as any);
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ export class RotateableKeySetService {
|
|||||||
userKey,
|
userKey,
|
||||||
rawPublicKey,
|
rawPublicKey,
|
||||||
);
|
);
|
||||||
const encryptedPublicKey = await this.encryptService.encrypt(rawPublicKey, userKey);
|
const encryptedPublicKey = await this.encryptService.wrapEncapsulationKey(
|
||||||
|
rawPublicKey,
|
||||||
|
userKey,
|
||||||
|
);
|
||||||
return new RotateableKeySet(encryptedUserKey, encryptedPublicKey, encryptedPrivateKey);
|
return new RotateableKeySet(encryptedUserKey, encryptedPublicKey, encryptedPrivateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +65,10 @@ export class RotateableKeySetService {
|
|||||||
if (publicKey == null) {
|
if (publicKey == null) {
|
||||||
throw new Error("failed to rotate key set: could not decrypt public key");
|
throw new Error("failed to rotate key set: could not decrypt public key");
|
||||||
}
|
}
|
||||||
const newEncryptedPublicKey = await this.encryptService.encrypt(publicKey, newUserKey);
|
const newEncryptedPublicKey = await this.encryptService.wrapEncapsulationKey(
|
||||||
|
publicKey,
|
||||||
|
newUserKey,
|
||||||
|
);
|
||||||
const newEncryptedUserKey = await this.encryptService.encapsulateKeyUnsigned(
|
const newEncryptedUserKey = await this.encryptService.encapsulateKeyUnsigned(
|
||||||
newUserKey,
|
newUserKey,
|
||||||
publicKey,
|
publicKey,
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ describe("AcceptOrganizationInviteService", () => {
|
|||||||
"orgPublicKey",
|
"orgPublicKey",
|
||||||
{ encryptedString: "string" } as EncString,
|
{ encryptedString: "string" } as EncString,
|
||||||
]);
|
]);
|
||||||
|
encryptService.wrapDecapsulationKey.mockResolvedValue({
|
||||||
|
encryptedString: "string",
|
||||||
|
} as EncString);
|
||||||
encryptService.encrypt.mockResolvedValue({ encryptedString: "string" } as EncString);
|
encryptService.encrypt.mockResolvedValue({ encryptedString: "string" } as EncString);
|
||||||
const invite = createOrgInvite({ initOrganization: true });
|
const invite = createOrgInvite({ initOrganization: true });
|
||||||
|
|
||||||
|
|||||||
@@ -812,7 +812,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
const providerKey = await this.keyService.getProviderKey(this.providerId);
|
const providerKey = await this.keyService.getProviderKey(this.providerId);
|
||||||
providerRequest.organizationCreateRequest.key = (
|
providerRequest.organizationCreateRequest.key = (
|
||||||
await this.encryptService.encrypt(orgKey.key, providerKey)
|
await this.encryptService.wrapSymmetricKey(orgKey, providerKey)
|
||||||
).encryptedString;
|
).encryptedString;
|
||||||
const orgId = (
|
const orgId = (
|
||||||
await this.apiService.postProviderCreateOrganization(this.providerId, providerRequest)
|
await this.apiService.postProviderCreateOrganization(this.providerId, providerRequest)
|
||||||
|
|||||||
@@ -183,7 +183,10 @@ describe("KeyRotationService", () => {
|
|||||||
mockKeyService.hashMasterKey.mockResolvedValue("mockMasterPasswordHash");
|
mockKeyService.hashMasterKey.mockResolvedValue("mockMasterPasswordHash");
|
||||||
mockConfigService.getFeatureFlag.mockResolvedValue(true);
|
mockConfigService.getFeatureFlag.mockResolvedValue(true);
|
||||||
|
|
||||||
mockEncryptService.encrypt.mockResolvedValue({
|
mockEncryptService.wrapSymmetricKey.mockResolvedValue({
|
||||||
|
encryptedString: "mockEncryptedData",
|
||||||
|
} as any);
|
||||||
|
mockEncryptService.wrapDecapsulationKey.mockResolvedValue({
|
||||||
encryptedString: "mockEncryptedData",
|
encryptedString: "mockEncryptedData",
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
|
|||||||
@@ -145,7 +145,9 @@ export class UserKeyRotationService {
|
|||||||
const { privateKey, publicKey } = keyPair;
|
const { privateKey, publicKey } = keyPair;
|
||||||
|
|
||||||
const accountKeysRequest = new AccountKeysRequest(
|
const accountKeysRequest = new AccountKeysRequest(
|
||||||
(await this.encryptService.encrypt(privateKey, newUnencryptedUserKey)).encryptedString!,
|
(
|
||||||
|
await this.encryptService.wrapDecapsulationKey(privateKey, newUnencryptedUserKey)
|
||||||
|
).encryptedString!,
|
||||||
Utils.fromBufferToB64(publicKey),
|
Utils.fromBufferToB64(publicKey),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -427,6 +429,6 @@ export class UserKeyRotationService {
|
|||||||
if (privateKey == null) {
|
if (privateKey == null) {
|
||||||
throw new Error("No private key found for user key rotation");
|
throw new Error("No private key found for user key rotation");
|
||||||
}
|
}
|
||||||
return (await this.encryptService.encrypt(privateKey, newUserKey)).encryptedString;
|
return (await this.encryptService.wrapDecapsulationKey(privateKey, newUserKey)).encryptedString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class WebProviderService {
|
|||||||
const orgKey = await this.keyService.getOrgKey(organizationId);
|
const orgKey = await this.keyService.getOrgKey(organizationId);
|
||||||
const providerKey = await this.keyService.getProviderKey(providerId);
|
const providerKey = await this.keyService.getProviderKey(providerId);
|
||||||
|
|
||||||
const encryptedOrgKey = await this.encryptService.encrypt(orgKey.key, providerKey);
|
const encryptedOrgKey = await this.encryptService.wrapSymmetricKey(orgKey, providerKey);
|
||||||
|
|
||||||
const request = new ProviderAddOrganizationRequest();
|
const request = new ProviderAddOrganizationRequest();
|
||||||
request.organizationId = organizationId;
|
request.organizationId = organizationId;
|
||||||
@@ -55,7 +55,7 @@ export class WebProviderService {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
const providerKey = await this.keyService.getProviderKey(providerId);
|
const providerKey = await this.keyService.getProviderKey(providerId);
|
||||||
const encryptedOrgKey = await this.encryptService.encrypt(orgKey.key, providerKey);
|
const encryptedOrgKey = await this.encryptService.wrapSymmetricKey(orgKey, providerKey);
|
||||||
await this.providerApiService.addOrganizationToProvider(providerId, {
|
await this.providerApiService.addOrganizationToProvider(providerId, {
|
||||||
key: encryptedOrgKey.encryptedString,
|
key: encryptedOrgKey.encryptedString,
|
||||||
organizationId,
|
organizationId,
|
||||||
@@ -81,8 +81,8 @@ export class WebProviderService {
|
|||||||
|
|
||||||
const providerKey = await this.keyService.getProviderKey(providerId);
|
const providerKey = await this.keyService.getProviderKey(providerId);
|
||||||
|
|
||||||
const encryptedProviderKey = await this.encryptService.encrypt(
|
const encryptedProviderKey = await this.encryptService.wrapSymmetricKey(
|
||||||
organizationKey.key,
|
organizationKey,
|
||||||
providerKey,
|
providerKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements
|
|||||||
const existingUserPublicKeyB64 = Utils.fromBufferToB64(existingUserPublicKey);
|
const existingUserPublicKeyB64 = Utils.fromBufferToB64(existingUserPublicKey);
|
||||||
newKeyPair = [
|
newKeyPair = [
|
||||||
existingUserPublicKeyB64,
|
existingUserPublicKeyB64,
|
||||||
await this.encryptService.encrypt(existingUserPrivateKey, userKey[0]),
|
await this.encryptService.wrapDecapsulationKey(existingUserPrivateKey, userKey[0]),
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
newKeyPair = await this.keyService.makeKeyPair(userKey[0]);
|
newKeyPair = await this.keyService.makeKeyPair(userKey[0]);
|
||||||
|
|||||||
@@ -174,7 +174,8 @@ export class PinService implements PinServiceAbstraction {
|
|||||||
);
|
);
|
||||||
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
const pinKey = await this.makePinKey(pin, email, kdfConfig);
|
const pinKey = await this.makePinKey(pin, email, kdfConfig);
|
||||||
return await this.encryptService.encrypt(userKey.key, pinKey);
|
|
||||||
|
return await this.encryptService.wrapSymmetricKey(userKey, pinKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
async storePinKeyEncryptedUserKey(
|
async storePinKeyEncryptedUserKey(
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ describe("PinService", () => {
|
|||||||
await sut.createPinKeyEncryptedUserKey(mockPin, mockUserKey, mockUserId);
|
await sut.createPinKeyEncryptedUserKey(mockPin, mockUserKey, mockUserId);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(encryptService.encrypt).toHaveBeenCalledWith(mockUserKey.key, mockPinKey);
|
expect(encryptService.wrapSymmetricKey).toHaveBeenCalledWith(mockUserKey, mockPinKey);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,50 @@ import { EncString } from "../../../platform/models/domain/enc-string";
|
|||||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||||
|
|
||||||
export abstract class EncryptService {
|
export abstract class EncryptService {
|
||||||
|
/**
|
||||||
|
* Encrypts a string or Uint8Array to an EncString
|
||||||
|
* @param plainValue - The value to encrypt
|
||||||
|
* @param key - The key to encrypt the value with
|
||||||
|
*/
|
||||||
abstract encrypt(plainValue: string | Uint8Array, key: SymmetricCryptoKey): Promise<EncString>;
|
abstract encrypt(plainValue: string | Uint8Array, key: SymmetricCryptoKey): Promise<EncString>;
|
||||||
|
/**
|
||||||
|
* Encrypts a value to a Uint8Array
|
||||||
|
* @param plainValue - The value to encrypt
|
||||||
|
* @param key - The key to encrypt the value with
|
||||||
|
*/
|
||||||
abstract encryptToBytes(plainValue: Uint8Array, key: SymmetricCryptoKey): Promise<EncArrayBuffer>;
|
abstract encryptToBytes(plainValue: Uint8Array, key: SymmetricCryptoKey): Promise<EncArrayBuffer>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a decapsulation key (Private key) with a symmetric key
|
||||||
|
* @see {@link https://en.wikipedia.org/wiki/Key_wrap}
|
||||||
|
* @param decapsulationKeyPcks8 - The private key in PKCS8 format
|
||||||
|
* @param wrappingKey - The symmetric key to wrap the private key with
|
||||||
|
*/
|
||||||
|
abstract wrapDecapsulationKey(
|
||||||
|
decapsulationKeyPcks8: Uint8Array,
|
||||||
|
wrappingKey: SymmetricCryptoKey,
|
||||||
|
): Promise<EncString>;
|
||||||
|
/**
|
||||||
|
* Wraps an encapsulation key (Public key) with a symmetric key
|
||||||
|
* @see {@link https://en.wikipedia.org/wiki/Key_wrap}
|
||||||
|
* @param encapsulationKeySpki - The public key in SPKI format
|
||||||
|
* @param wrappingKey - The symmetric key to wrap the public key with
|
||||||
|
*/
|
||||||
|
abstract wrapEncapsulationKey(
|
||||||
|
encapsulationKeySpki: Uint8Array,
|
||||||
|
wrappingKey: SymmetricCryptoKey,
|
||||||
|
): Promise<EncString>;
|
||||||
|
/**
|
||||||
|
* Wraps a symmetric key with another symmetric key
|
||||||
|
* @see {@link https://en.wikipedia.org/wiki/Key_wrap}
|
||||||
|
* @param keyToBeWrapped - The symmetric key to wrap
|
||||||
|
* @param wrappingKey - The symmetric key to wrap the encapsulated key with
|
||||||
|
*/
|
||||||
|
abstract wrapSymmetricKey(
|
||||||
|
keyToBeWrapped: SymmetricCryptoKey,
|
||||||
|
wrappingKey: SymmetricCryptoKey,
|
||||||
|
): Promise<EncString>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypts an EncString to a string
|
* Decrypts an EncString to a string
|
||||||
* @param encString - The EncString to decrypt
|
* @param encString - The EncString to decrypt
|
||||||
@@ -39,6 +81,7 @@ export abstract class EncryptService {
|
|||||||
/**
|
/**
|
||||||
* Encapsulates a symmetric key with an asymmetric public key
|
* Encapsulates a symmetric key with an asymmetric public key
|
||||||
* Note: This does not establish sender authenticity
|
* Note: This does not establish sender authenticity
|
||||||
|
* @see {@link https://en.wikipedia.org/wiki/Key_encapsulation_mechanism}
|
||||||
* @param sharedKey - The symmetric key that is to be shared
|
* @param sharedKey - The symmetric key that is to be shared
|
||||||
* @param encapsulationKey - The encapsulation key (public key) of the receiver that the key is shared with
|
* @param encapsulationKey - The encapsulation key (public key) of the receiver that the key is shared with
|
||||||
*/
|
*/
|
||||||
@@ -49,6 +92,7 @@ export abstract class EncryptService {
|
|||||||
/**
|
/**
|
||||||
* Decapsulates a shared symmetric key with an asymmetric private key
|
* Decapsulates a shared symmetric key with an asymmetric private key
|
||||||
* Note: This does not establish sender authenticity
|
* Note: This does not establish sender authenticity
|
||||||
|
* @see {@link https://en.wikipedia.org/wiki/Key_encapsulation_mechanism}
|
||||||
* @param encryptedSharedKey - The encrypted shared symmetric key
|
* @param encryptedSharedKey - The encrypted shared symmetric key
|
||||||
* @param decapsulationKey - The key to decapsulate with (private key)
|
* @param decapsulationKey - The key to decapsulate with (private key)
|
||||||
*/
|
*/
|
||||||
@@ -57,13 +101,13 @@ export abstract class EncryptService {
|
|||||||
decapsulationKey: Uint8Array,
|
decapsulationKey: Uint8Array,
|
||||||
): Promise<SymmetricCryptoKey>;
|
): Promise<SymmetricCryptoKey>;
|
||||||
/**
|
/**
|
||||||
* @deprecated Use encapsulateKeyUnsigned instead
|
* @deprecated Use @see {@link encapsulateKeyUnsigned} instead
|
||||||
* @param data - The data to encrypt
|
* @param data - The data to encrypt
|
||||||
* @param publicKey - The public key to encrypt with
|
* @param publicKey - The public key to encrypt with
|
||||||
*/
|
*/
|
||||||
abstract rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString>;
|
abstract rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString>;
|
||||||
/**
|
/**
|
||||||
* @deprecated Use decapsulateKeyUnsigned instead
|
* @deprecated Use @see {@link decapsulateKeyUnsigned} instead
|
||||||
* @param data - The ciphertext to decrypt
|
* @param data - The ciphertext to decrypt
|
||||||
* @param privateKey - The privateKey to decrypt with
|
* @param privateKey - The privateKey to decrypt with
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -56,22 +56,79 @@ export class EncryptServiceImplementation implements EncryptService {
|
|||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
let plainBuf: Uint8Array;
|
|
||||||
if (typeof plainValue === "string") {
|
if (typeof plainValue === "string") {
|
||||||
plainBuf = Utils.fromUtf8ToArray(plainValue);
|
return this.encryptUint8Array(Utils.fromUtf8ToArray(plainValue), key);
|
||||||
} else {
|
} else {
|
||||||
plainBuf = plainValue;
|
return this.encryptUint8Array(plainValue, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async wrapDecapsulationKey(
|
||||||
|
decapsulationKeyPkcs8: Uint8Array,
|
||||||
|
wrappingKey: SymmetricCryptoKey,
|
||||||
|
): Promise<EncString> {
|
||||||
|
if (decapsulationKeyPkcs8 == null) {
|
||||||
|
throw new Error("No decapsulation key provided for wrapping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrappingKey == null) {
|
||||||
|
throw new Error("No wrappingKey provided for wrapping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.encryptUint8Array(decapsulationKeyPkcs8, wrappingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
async wrapEncapsulationKey(
|
||||||
|
encapsulationKeySpki: Uint8Array,
|
||||||
|
wrappingKey: SymmetricCryptoKey,
|
||||||
|
): Promise<EncString> {
|
||||||
|
if (encapsulationKeySpki == null) {
|
||||||
|
throw new Error("No encapsulation key provided for wrapping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrappingKey == null) {
|
||||||
|
throw new Error("No wrappingKey provided for wrapping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.encryptUint8Array(encapsulationKeySpki, wrappingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
async wrapSymmetricKey(
|
||||||
|
keyToBeWrapped: SymmetricCryptoKey,
|
||||||
|
wrappingKey: SymmetricCryptoKey,
|
||||||
|
): Promise<EncString> {
|
||||||
|
if (keyToBeWrapped == null) {
|
||||||
|
throw new Error("No keyToBeWrapped provided for wrapping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrappingKey == null) {
|
||||||
|
throw new Error("No wrappingKey provided for wrapping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.encryptUint8Array(keyToBeWrapped.key, wrappingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async encryptUint8Array(
|
||||||
|
plainValue: Uint8Array,
|
||||||
|
key: SymmetricCryptoKey,
|
||||||
|
): Promise<EncString> {
|
||||||
|
if (key == null) {
|
||||||
|
throw new Error("No encryption key provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plainValue == null) {
|
||||||
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const innerKey = key.inner();
|
const innerKey = key.inner();
|
||||||
if (innerKey.type === EncryptionType.AesCbc256_HmacSha256_B64) {
|
if (innerKey.type === EncryptionType.AesCbc256_HmacSha256_B64) {
|
||||||
const encObj = await this.aesEncrypt(plainBuf, innerKey);
|
const encObj = await this.aesEncrypt(plainValue, innerKey);
|
||||||
const iv = Utils.fromBufferToB64(encObj.iv);
|
const iv = Utils.fromBufferToB64(encObj.iv);
|
||||||
const data = Utils.fromBufferToB64(encObj.data);
|
const data = Utils.fromBufferToB64(encObj.data);
|
||||||
const mac = Utils.fromBufferToB64(encObj.mac);
|
const mac = Utils.fromBufferToB64(encObj.mac);
|
||||||
return new EncString(innerKey.type, data, iv, mac);
|
return new EncString(innerKey.type, data, iv, mac);
|
||||||
} else if (innerKey.type === EncryptionType.AesCbc256_B64) {
|
} else if (innerKey.type === EncryptionType.AesCbc256_B64) {
|
||||||
const encObj = await this.aesEncryptLegacy(plainBuf, innerKey);
|
const encObj = await this.aesEncryptLegacy(plainValue, innerKey);
|
||||||
const iv = Utils.fromBufferToB64(encObj.iv);
|
const iv = Utils.fromBufferToB64(encObj.iv);
|
||||||
const data = Utils.fromBufferToB64(encObj.data);
|
const data = Utils.fromBufferToB64(encObj.data);
|
||||||
return new EncString(innerKey.type, data, iv);
|
return new EncString(innerKey.type, data, iv);
|
||||||
|
|||||||
@@ -31,6 +31,88 @@ describe("EncryptService", () => {
|
|||||||
encryptService = new EncryptServiceImplementation(cryptoFunctionService, logService, true);
|
encryptService = new EncryptServiceImplementation(cryptoFunctionService, logService, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("wrapSymmetricKey", () => {
|
||||||
|
it("roundtrip encrypts and decrypts a symmetric key", async () => {
|
||||||
|
cryptoFunctionService.aesEncrypt.mockResolvedValue(makeStaticByteArray(64, 0));
|
||||||
|
cryptoFunctionService.randomBytes.mockResolvedValue(makeStaticByteArray(16) as CsprngArray);
|
||||||
|
cryptoFunctionService.hmac.mockResolvedValue(makeStaticByteArray(32));
|
||||||
|
|
||||||
|
const key = new SymmetricCryptoKey(makeStaticByteArray(64));
|
||||||
|
const wrappingKey = new SymmetricCryptoKey(makeStaticByteArray(64));
|
||||||
|
const encString = await encryptService.wrapSymmetricKey(key, wrappingKey);
|
||||||
|
expect(encString.encryptionType).toEqual(EncryptionType.AesCbc256_HmacSha256_B64);
|
||||||
|
expect(encString.data).toEqual(Utils.fromBufferToB64(makeStaticByteArray(64, 0)));
|
||||||
|
});
|
||||||
|
it("fails if key toBeWrapped is null", async () => {
|
||||||
|
const wrappingKey = new SymmetricCryptoKey(makeStaticByteArray(64));
|
||||||
|
await expect(encryptService.wrapSymmetricKey(null, wrappingKey)).rejects.toThrow(
|
||||||
|
"No keyToBeWrapped provided for wrapping.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("fails if wrapping key is null", async () => {
|
||||||
|
const key = new SymmetricCryptoKey(makeStaticByteArray(64));
|
||||||
|
await expect(encryptService.wrapSymmetricKey(key, null)).rejects.toThrow(
|
||||||
|
"No wrappingKey provided for wrapping.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("wrapDecapsulationKey", () => {
|
||||||
|
it("roundtrip encrypts and decrypts a decapsulation key", async () => {
|
||||||
|
cryptoFunctionService.aesEncrypt.mockResolvedValue(makeStaticByteArray(64, 0));
|
||||||
|
cryptoFunctionService.randomBytes.mockResolvedValue(makeStaticByteArray(16) as CsprngArray);
|
||||||
|
cryptoFunctionService.hmac.mockResolvedValue(makeStaticByteArray(32));
|
||||||
|
|
||||||
|
const wrappingKey = new SymmetricCryptoKey(makeStaticByteArray(64));
|
||||||
|
const encString = await encryptService.wrapDecapsulationKey(
|
||||||
|
makeStaticByteArray(64),
|
||||||
|
wrappingKey,
|
||||||
|
);
|
||||||
|
expect(encString.encryptionType).toEqual(EncryptionType.AesCbc256_HmacSha256_B64);
|
||||||
|
expect(encString.data).toEqual(Utils.fromBufferToB64(makeStaticByteArray(64, 0)));
|
||||||
|
});
|
||||||
|
it("fails if decapsulation key is null", async () => {
|
||||||
|
const wrappingKey = new SymmetricCryptoKey(makeStaticByteArray(64));
|
||||||
|
await expect(encryptService.wrapDecapsulationKey(null, wrappingKey)).rejects.toThrow(
|
||||||
|
"No decapsulation key provided for wrapping.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("fails if wrapping key is null", async () => {
|
||||||
|
const decapsulationKey = makeStaticByteArray(64);
|
||||||
|
await expect(encryptService.wrapDecapsulationKey(decapsulationKey, null)).rejects.toThrow(
|
||||||
|
"No wrappingKey provided for wrapping.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("wrapEncapsulationKey", () => {
|
||||||
|
it("roundtrip encrypts and decrypts an encapsulationKey key", async () => {
|
||||||
|
cryptoFunctionService.aesEncrypt.mockResolvedValue(makeStaticByteArray(64, 0));
|
||||||
|
cryptoFunctionService.randomBytes.mockResolvedValue(makeStaticByteArray(16) as CsprngArray);
|
||||||
|
cryptoFunctionService.hmac.mockResolvedValue(makeStaticByteArray(32));
|
||||||
|
|
||||||
|
const wrappingKey = new SymmetricCryptoKey(makeStaticByteArray(64));
|
||||||
|
const encString = await encryptService.wrapEncapsulationKey(
|
||||||
|
makeStaticByteArray(64),
|
||||||
|
wrappingKey,
|
||||||
|
);
|
||||||
|
expect(encString.encryptionType).toEqual(EncryptionType.AesCbc256_HmacSha256_B64);
|
||||||
|
expect(encString.data).toEqual(Utils.fromBufferToB64(makeStaticByteArray(64, 0)));
|
||||||
|
});
|
||||||
|
it("fails if encapsulation key is null", async () => {
|
||||||
|
const wrappingKey = new SymmetricCryptoKey(makeStaticByteArray(64));
|
||||||
|
await expect(encryptService.wrapEncapsulationKey(null, wrappingKey)).rejects.toThrow(
|
||||||
|
"No encapsulation key provided for wrapping.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("fails if wrapping key is null", async () => {
|
||||||
|
const encapsulationKey = makeStaticByteArray(64);
|
||||||
|
await expect(encryptService.wrapEncapsulationKey(encapsulationKey, null)).rejects.toThrow(
|
||||||
|
"No wrappingKey provided for wrapping.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("onServerConfigChange", () => {
|
describe("onServerConfigChange", () => {
|
||||||
const newConfig = mock<ServerConfig>();
|
const newConfig = mock<ServerConfig>();
|
||||||
|
|
||||||
@@ -461,6 +543,12 @@ describe("EncryptService", () => {
|
|||||||
expect(actual).toEqual(encString);
|
expect(actual).toEqual(encString);
|
||||||
expect(actual.dataBytes).toEqualBuffer(encryptedData);
|
expect(actual.dataBytes).toEqualBuffer(encryptedData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("throws if no data was provided", () => {
|
||||||
|
return expect(encryptService.rsaEncrypt(null, new Uint8Array(32))).rejects.toThrow(
|
||||||
|
"No data provided for encryption",
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("decapsulateKeyUnsigned", () => {
|
describe("decapsulateKeyUnsigned", () => {
|
||||||
|
|||||||
@@ -164,10 +164,10 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
|
|||||||
this.encryptService.encapsulateKeyUnsigned(userKey, devicePublicKey),
|
this.encryptService.encapsulateKeyUnsigned(userKey, devicePublicKey),
|
||||||
|
|
||||||
// Encrypt devicePublicKey with user key
|
// Encrypt devicePublicKey with user key
|
||||||
this.encryptService.encrypt(devicePublicKey, userKey),
|
this.encryptService.wrapEncapsulationKey(devicePublicKey, userKey),
|
||||||
|
|
||||||
// Encrypt devicePrivateKey with deviceKey
|
// Encrypt devicePrivateKey with deviceKey
|
||||||
this.encryptService.encrypt(devicePrivateKey, deviceKey),
|
this.encryptService.wrapDecapsulationKey(devicePrivateKey, deviceKey),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Send encrypted keys to server
|
// Send encrypted keys to server
|
||||||
@@ -290,7 +290,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Re-encrypt the device public key with the new user key
|
// Re-encrypt the device public key with the new user key
|
||||||
const encryptedDevicePublicKey = await this.encryptService.encrypt(
|
const encryptedDevicePublicKey = await this.encryptService.wrapEncapsulationKey(
|
||||||
decryptedDevicePublicKey,
|
decryptedDevicePublicKey,
|
||||||
newUserKey,
|
newUserKey,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -346,8 +346,6 @@ describe("deviceTrustService", () => {
|
|||||||
|
|
||||||
const deviceRsaKeyLength = 2048;
|
const deviceRsaKeyLength = 2048;
|
||||||
let mockDeviceRsaKeyPair: [Uint8Array, Uint8Array];
|
let mockDeviceRsaKeyPair: [Uint8Array, Uint8Array];
|
||||||
let mockDevicePrivateKey: Uint8Array;
|
|
||||||
let mockDevicePublicKey: Uint8Array;
|
|
||||||
let mockDevicePublicKeyEncryptedUserKey: EncString;
|
let mockDevicePublicKeyEncryptedUserKey: EncString;
|
||||||
let mockUserKeyEncryptedDevicePublicKey: EncString;
|
let mockUserKeyEncryptedDevicePublicKey: EncString;
|
||||||
let mockDeviceKeyEncryptedDevicePrivateKey: EncString;
|
let mockDeviceKeyEncryptedDevicePrivateKey: EncString;
|
||||||
@@ -366,7 +364,8 @@ describe("deviceTrustService", () => {
|
|||||||
let rsaGenerateKeyPairSpy: jest.SpyInstance;
|
let rsaGenerateKeyPairSpy: jest.SpyInstance;
|
||||||
let cryptoSvcGetUserKeySpy: jest.SpyInstance;
|
let cryptoSvcGetUserKeySpy: jest.SpyInstance;
|
||||||
let cryptoSvcRsaEncryptSpy: jest.SpyInstance;
|
let cryptoSvcRsaEncryptSpy: jest.SpyInstance;
|
||||||
let encryptServiceEncryptSpy: jest.SpyInstance;
|
let encryptServiceWrapDecapsulationKeySpy: jest.SpyInstance;
|
||||||
|
let encryptServiceWrapEncapsulationKeySpy: jest.SpyInstance;
|
||||||
let appIdServiceGetAppIdSpy: jest.SpyInstance;
|
let appIdServiceGetAppIdSpy: jest.SpyInstance;
|
||||||
let devicesApiServiceUpdateTrustedDeviceKeysSpy: jest.SpyInstance;
|
let devicesApiServiceUpdateTrustedDeviceKeysSpy: jest.SpyInstance;
|
||||||
|
|
||||||
@@ -384,9 +383,6 @@ describe("deviceTrustService", () => {
|
|||||||
new Uint8Array(deviceRsaKeyLength),
|
new Uint8Array(deviceRsaKeyLength),
|
||||||
];
|
];
|
||||||
|
|
||||||
mockDevicePublicKey = mockDeviceRsaKeyPair[0];
|
|
||||||
mockDevicePrivateKey = mockDeviceRsaKeyPair[1];
|
|
||||||
|
|
||||||
mockDevicePublicKeyEncryptedUserKey = new EncString(
|
mockDevicePublicKeyEncryptedUserKey = new EncString(
|
||||||
EncryptionType.Rsa2048_OaepSha1_B64,
|
EncryptionType.Rsa2048_OaepSha1_B64,
|
||||||
"mockDevicePublicKeyEncryptedUserKey",
|
"mockDevicePublicKeyEncryptedUserKey",
|
||||||
@@ -419,13 +415,17 @@ describe("deviceTrustService", () => {
|
|||||||
.spyOn(encryptService, "encapsulateKeyUnsigned")
|
.spyOn(encryptService, "encapsulateKeyUnsigned")
|
||||||
.mockResolvedValue(mockDevicePublicKeyEncryptedUserKey);
|
.mockResolvedValue(mockDevicePublicKeyEncryptedUserKey);
|
||||||
|
|
||||||
encryptServiceEncryptSpy = jest
|
encryptServiceWrapEncapsulationKeySpy = jest
|
||||||
.spyOn(encryptService, "encrypt")
|
.spyOn(encryptService, "wrapEncapsulationKey")
|
||||||
.mockImplementation((plainValue, key) => {
|
.mockImplementation((plainValue, key) => {
|
||||||
if (plainValue === mockDevicePublicKey && key === mockUserKey) {
|
if (plainValue instanceof Uint8Array && key instanceof SymmetricCryptoKey) {
|
||||||
return Promise.resolve(mockUserKeyEncryptedDevicePublicKey);
|
return Promise.resolve(mockUserKeyEncryptedDevicePublicKey);
|
||||||
}
|
}
|
||||||
if (plainValue === mockDevicePrivateKey && key === mockDeviceKey) {
|
});
|
||||||
|
encryptServiceWrapDecapsulationKeySpy = jest
|
||||||
|
.spyOn(encryptService, "wrapDecapsulationKey")
|
||||||
|
.mockImplementation((plainValue, key) => {
|
||||||
|
if (plainValue instanceof Uint8Array && key instanceof SymmetricCryptoKey) {
|
||||||
return Promise.resolve(mockDeviceKeyEncryptedDevicePrivateKey);
|
return Promise.resolve(mockDeviceKeyEncryptedDevicePrivateKey);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -452,7 +452,8 @@ describe("deviceTrustService", () => {
|
|||||||
const userKey = cryptoSvcRsaEncryptSpy.mock.calls[0][0];
|
const userKey = cryptoSvcRsaEncryptSpy.mock.calls[0][0];
|
||||||
expect(userKey.key.byteLength).toBe(64);
|
expect(userKey.key.byteLength).toBe(64);
|
||||||
|
|
||||||
expect(encryptServiceEncryptSpy).toHaveBeenCalledTimes(2);
|
expect(encryptServiceWrapDecapsulationKeySpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(encryptServiceWrapEncapsulationKeySpy).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
expect(appIdServiceGetAppIdSpy).toHaveBeenCalledTimes(1);
|
expect(appIdServiceGetAppIdSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(devicesApiServiceUpdateTrustedDeviceKeysSpy).toHaveBeenCalledTimes(1);
|
expect(devicesApiServiceUpdateTrustedDeviceKeysSpy).toHaveBeenCalledTimes(1);
|
||||||
@@ -508,9 +509,14 @@ describe("deviceTrustService", () => {
|
|||||||
errorText: "rsaEncrypt error",
|
errorText: "rsaEncrypt error",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: "encryptService.encrypt",
|
method: "encryptService.wrapEncapsulationKey",
|
||||||
spy: () => encryptServiceEncryptSpy,
|
spy: () => encryptServiceWrapEncapsulationKeySpy,
|
||||||
errorText: "encryptService.encrypt error",
|
errorText: "encryptService.wrapEncapsulationKey error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "encryptService.wrapDecapsulationKey",
|
||||||
|
spy: () => encryptServiceWrapDecapsulationKeySpy,
|
||||||
|
errorText: "encryptService.wrapDecapsulationKey error",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -872,7 +878,7 @@ describe("deviceTrustService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Mock the reencryption of the device public key with the new user key
|
// Mock the reencryption of the device public key with the new user key
|
||||||
encryptService.encrypt.mockImplementationOnce((plainValue, key) => {
|
encryptService.wrapEncapsulationKey.mockImplementationOnce((plainValue, key) => {
|
||||||
expect(plainValue).toBeInstanceOf(Uint8Array);
|
expect(plainValue).toBeInstanceOf(Uint8Array);
|
||||||
expect(new Uint8Array(plainValue as Uint8Array)[0]).toBe(FakeDecryptedPublicKeyMarker);
|
expect(new Uint8Array(plainValue as Uint8Array)[0]).toBe(FakeDecryptedPublicKeyMarker);
|
||||||
|
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ describe("SendService", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
encryptService.decryptToBytes.mockResolvedValue(new Uint8Array(32));
|
encryptService.decryptToBytes.mockResolvedValue(new Uint8Array(32));
|
||||||
encryptedKey = new EncString("Re-encrypted Send Key");
|
encryptedKey = new EncString("Re-encrypted Send Key");
|
||||||
encryptService.encrypt.mockResolvedValue(encryptedKey);
|
encryptService.wrapSymmetricKey.mockResolvedValue(encryptedKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns re-encrypted user sends", async () => {
|
it("returns re-encrypted user sends", async () => {
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export class SendService implements InternalSendServiceAbstraction {
|
|||||||
if (key == null) {
|
if (key == null) {
|
||||||
key = await this.keyService.getUserKey();
|
key = await this.keyService.getUserKey();
|
||||||
}
|
}
|
||||||
|
// Key is not a SymmetricCryptoKey, but key material used to derive the cryptoKey
|
||||||
send.key = await this.encryptService.encrypt(model.key, key);
|
send.key = await this.encryptService.encrypt(model.key, key);
|
||||||
send.name = await this.encryptService.encrypt(model.name, model.cryptoKey);
|
send.name = await this.encryptService.encrypt(model.name, model.cryptoKey);
|
||||||
send.notes = await this.encryptService.encrypt(model.notes, model.cryptoKey);
|
send.notes = await this.encryptService.encrypt(model.notes, model.cryptoKey);
|
||||||
@@ -287,8 +288,10 @@ export class SendService implements InternalSendServiceAbstraction {
|
|||||||
) {
|
) {
|
||||||
const requests = await Promise.all(
|
const requests = await Promise.all(
|
||||||
sends.map(async (send) => {
|
sends.map(async (send) => {
|
||||||
const sendKey = await this.encryptService.decryptToBytes(send.key, originalUserKey);
|
const sendKey = new SymmetricCryptoKey(
|
||||||
send.key = await this.encryptService.encrypt(sendKey, rotateUserKey);
|
await this.encryptService.decryptToBytes(send.key, originalUserKey),
|
||||||
|
);
|
||||||
|
send.key = await this.encryptService.wrapSymmetricKey(sendKey, rotateUserKey);
|
||||||
return new SendWithIdRequest(send);
|
return new SendWithIdRequest(send);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -280,6 +280,7 @@ describe("Cipher Service", () => {
|
|||||||
Promise.resolve(new SymmetricCryptoKey(makeStaticByteArray(64)) as CipherKey),
|
Promise.resolve(new SymmetricCryptoKey(makeStaticByteArray(64)) as CipherKey),
|
||||||
);
|
);
|
||||||
encryptService.encrypt.mockImplementation(encryptText);
|
encryptService.encrypt.mockImplementation(encryptText);
|
||||||
|
encryptService.wrapSymmetricKey.mockResolvedValue(new EncString("Re-encrypted Cipher Key"));
|
||||||
|
|
||||||
jest.spyOn(cipherService as any, "getAutofillOnPageLoadDefault").mockResolvedValue(true);
|
jest.spyOn(cipherService as any, "getAutofillOnPageLoadDefault").mockResolvedValue(true);
|
||||||
});
|
});
|
||||||
@@ -436,7 +437,7 @@ describe("Cipher Service", () => {
|
|||||||
|
|
||||||
encryptService.decryptToBytes.mockResolvedValue(new Uint8Array(32));
|
encryptService.decryptToBytes.mockResolvedValue(new Uint8Array(32));
|
||||||
encryptedKey = new EncString("Re-encrypted Cipher Key");
|
encryptedKey = new EncString("Re-encrypted Cipher Key");
|
||||||
encryptService.encrypt.mockResolvedValue(encryptedKey);
|
encryptService.wrapSymmetricKey.mockResolvedValue(encryptedKey);
|
||||||
|
|
||||||
keyService.makeCipherKey.mockResolvedValue(
|
keyService.makeCipherKey.mockResolvedValue(
|
||||||
new SymmetricCryptoKey(new Uint8Array(32)) as CipherKey,
|
new SymmetricCryptoKey(new Uint8Array(32)) as CipherKey,
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
key,
|
key,
|
||||||
).then(async () => {
|
).then(async () => {
|
||||||
if (model.key != null) {
|
if (model.key != null) {
|
||||||
attachment.key = await this.encryptService.encrypt(model.key.key, key);
|
attachment.key = await this.encryptService.wrapSymmetricKey(model.key, key);
|
||||||
}
|
}
|
||||||
encAttachments.push(attachment);
|
encAttachments.push(attachment);
|
||||||
});
|
});
|
||||||
@@ -1820,8 +1820,8 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Then, we have to encrypt the cipher key with the proper key.
|
// Then, we have to encrypt the cipher key with the proper key.
|
||||||
cipher.key = await this.encryptService.encrypt(
|
cipher.key = await this.encryptService.wrapSymmetricKey(
|
||||||
decryptedCipherKey.key,
|
decryptedCipherKey,
|
||||||
keyForCipherKeyEncryption,
|
keyForCipherKeyEncryption,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newUserKey = await this.keyGenerationService.createKey(512);
|
const newUserKey = await this.keyGenerationService.createKey(512);
|
||||||
return this.buildProtectedSymmetricKey(masterKey, newUserKey.key);
|
return this.buildProtectedSymmetricKey(masterKey, newUserKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -323,7 +323,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
|||||||
userKey?: UserKey,
|
userKey?: UserKey,
|
||||||
): Promise<[UserKey, EncString]> {
|
): Promise<[UserKey, EncString]> {
|
||||||
userKey ||= await this.getUserKey();
|
userKey ||= await this.getUserKey();
|
||||||
return await this.buildProtectedSymmetricKey(masterKey, userKey.key);
|
return await this.buildProtectedSymmetricKey(masterKey, userKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to MasterPasswordService
|
// TODO: move to MasterPasswordService
|
||||||
@@ -433,7 +433,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newSymKey = await this.keyGenerationService.createKey(512);
|
const newSymKey = await this.keyGenerationService.createKey(512);
|
||||||
return this.buildProtectedSymmetricKey(key, newSymKey.key);
|
return this.buildProtectedSymmetricKey(key, newSymKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async clearOrgKeys(userId: UserId): Promise<void> {
|
private async clearOrgKeys(userId: UserId): Promise<void> {
|
||||||
@@ -547,7 +547,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
|||||||
|
|
||||||
const keyPair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048);
|
const keyPair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048);
|
||||||
const publicB64 = Utils.fromBufferToB64(keyPair[0]);
|
const publicB64 = Utils.fromBufferToB64(keyPair[0]);
|
||||||
const privateEnc = await this.encryptService.encrypt(keyPair[1], key);
|
const privateEnc = await this.encryptService.wrapDecapsulationKey(keyPair[1], key);
|
||||||
return [publicB64, privateEnc];
|
return [publicB64, privateEnc];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,18 +820,21 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
|||||||
|
|
||||||
private async buildProtectedSymmetricKey<T extends SymmetricCryptoKey>(
|
private async buildProtectedSymmetricKey<T extends SymmetricCryptoKey>(
|
||||||
encryptionKey: SymmetricCryptoKey,
|
encryptionKey: SymmetricCryptoKey,
|
||||||
newSymKey: Uint8Array,
|
newSymKey: SymmetricCryptoKey,
|
||||||
): Promise<[T, EncString]> {
|
): Promise<[T, EncString]> {
|
||||||
let protectedSymKey: EncString;
|
let protectedSymKey: EncString;
|
||||||
if (encryptionKey.key.byteLength === 32) {
|
if (encryptionKey.key.byteLength === 32) {
|
||||||
const stretchedEncryptionKey = await this.keyGenerationService.stretchKey(encryptionKey);
|
const stretchedEncryptionKey = await this.keyGenerationService.stretchKey(encryptionKey);
|
||||||
protectedSymKey = await this.encryptService.encrypt(newSymKey, stretchedEncryptionKey);
|
protectedSymKey = await this.encryptService.wrapSymmetricKey(
|
||||||
|
newSymKey,
|
||||||
|
stretchedEncryptionKey,
|
||||||
|
);
|
||||||
} else if (encryptionKey.key.byteLength === 64) {
|
} else if (encryptionKey.key.byteLength === 64) {
|
||||||
protectedSymKey = await this.encryptService.encrypt(newSymKey, encryptionKey);
|
protectedSymKey = await this.encryptService.wrapSymmetricKey(newSymKey, encryptionKey);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid key size.");
|
throw new Error("Invalid key size.");
|
||||||
}
|
}
|
||||||
return [new SymmetricCryptoKey(newSymKey) as T, protectedSymKey];
|
return [newSymKey as T, protectedSymKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
userKey$(userId: UserId): Observable<UserKey | null> {
|
userKey$(userId: UserId): Observable<UserKey | null> {
|
||||||
|
|||||||
Reference in New Issue
Block a user