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