mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +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:
@@ -302,13 +302,13 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
(
|
||||
await this.cryptoService.getKey()
|
||||
).encKey,
|
||||
pubKey.buffer
|
||||
pubKey
|
||||
);
|
||||
let encryptedMasterPassword = null;
|
||||
if ((await this.stateService.getKeyHash()) != null) {
|
||||
encryptedMasterPassword = await this.cryptoService.rsaEncrypt(
|
||||
Utils.fromUtf8ToArray(await this.stateService.getKeyHash()),
|
||||
pubKey.buffer
|
||||
pubKey
|
||||
);
|
||||
}
|
||||
const request = new PasswordlessAuthRequest(
|
||||
|
||||
@@ -4,67 +4,67 @@ import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
||||
|
||||
export abstract class CryptoFunctionService {
|
||||
pbkdf2: (
|
||||
password: string | ArrayBuffer,
|
||||
salt: string | ArrayBuffer,
|
||||
password: string | Uint8Array,
|
||||
salt: string | Uint8Array,
|
||||
algorithm: "sha256" | "sha512",
|
||||
iterations: number
|
||||
) => Promise<ArrayBuffer>;
|
||||
) => Promise<Uint8Array>;
|
||||
argon2: (
|
||||
password: string | ArrayBuffer,
|
||||
salt: string | ArrayBuffer,
|
||||
password: string | Uint8Array,
|
||||
salt: string | Uint8Array,
|
||||
iterations: number,
|
||||
memory: number,
|
||||
parallelism: number
|
||||
) => Promise<ArrayBuffer>;
|
||||
) => Promise<Uint8Array>;
|
||||
hkdf: (
|
||||
ikm: ArrayBuffer,
|
||||
salt: string | ArrayBuffer,
|
||||
info: string | ArrayBuffer,
|
||||
ikm: Uint8Array,
|
||||
salt: string | Uint8Array,
|
||||
info: string | Uint8Array,
|
||||
outputByteSize: number,
|
||||
algorithm: "sha256" | "sha512"
|
||||
) => Promise<ArrayBuffer>;
|
||||
) => Promise<Uint8Array>;
|
||||
hkdfExpand: (
|
||||
prk: ArrayBuffer,
|
||||
info: string | ArrayBuffer,
|
||||
prk: Uint8Array,
|
||||
info: string | Uint8Array,
|
||||
outputByteSize: number,
|
||||
algorithm: "sha256" | "sha512"
|
||||
) => Promise<ArrayBuffer>;
|
||||
) => Promise<Uint8Array>;
|
||||
hash: (
|
||||
value: string | ArrayBuffer,
|
||||
value: string | Uint8Array,
|
||||
algorithm: "sha1" | "sha256" | "sha512" | "md5"
|
||||
) => Promise<ArrayBuffer>;
|
||||
) => Promise<Uint8Array>;
|
||||
hmac: (
|
||||
value: ArrayBuffer,
|
||||
key: ArrayBuffer,
|
||||
value: Uint8Array,
|
||||
key: Uint8Array,
|
||||
algorithm: "sha1" | "sha256" | "sha512"
|
||||
) => Promise<ArrayBuffer>;
|
||||
compare: (a: ArrayBuffer, b: ArrayBuffer) => Promise<boolean>;
|
||||
) => Promise<Uint8Array>;
|
||||
compare: (a: Uint8Array, b: Uint8Array) => Promise<boolean>;
|
||||
hmacFast: (
|
||||
value: ArrayBuffer | string,
|
||||
key: ArrayBuffer | string,
|
||||
value: Uint8Array | string,
|
||||
key: Uint8Array | string,
|
||||
algorithm: "sha1" | "sha256" | "sha512"
|
||||
) => Promise<ArrayBuffer | string>;
|
||||
compareFast: (a: ArrayBuffer | string, b: ArrayBuffer | string) => Promise<boolean>;
|
||||
aesEncrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise<ArrayBuffer>;
|
||||
) => Promise<Uint8Array | string>;
|
||||
compareFast: (a: Uint8Array | string, b: Uint8Array | string) => Promise<boolean>;
|
||||
aesEncrypt: (data: Uint8Array, iv: Uint8Array, key: Uint8Array) => Promise<Uint8Array>;
|
||||
aesDecryptFastParameters: (
|
||||
data: string,
|
||||
iv: string,
|
||||
mac: string,
|
||||
key: SymmetricCryptoKey
|
||||
) => DecryptParameters<ArrayBuffer | string>;
|
||||
aesDecryptFast: (parameters: DecryptParameters<ArrayBuffer | string>) => Promise<string>;
|
||||
aesDecrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise<ArrayBuffer>;
|
||||
) => DecryptParameters<Uint8Array | string>;
|
||||
aesDecryptFast: (parameters: DecryptParameters<Uint8Array | string>) => Promise<string>;
|
||||
aesDecrypt: (data: Uint8Array, iv: Uint8Array, key: Uint8Array) => Promise<Uint8Array>;
|
||||
rsaEncrypt: (
|
||||
data: ArrayBuffer,
|
||||
publicKey: ArrayBuffer,
|
||||
data: Uint8Array,
|
||||
publicKey: Uint8Array,
|
||||
algorithm: "sha1" | "sha256"
|
||||
) => Promise<ArrayBuffer>;
|
||||
) => Promise<Uint8Array>;
|
||||
rsaDecrypt: (
|
||||
data: ArrayBuffer,
|
||||
privateKey: ArrayBuffer,
|
||||
data: Uint8Array,
|
||||
privateKey: Uint8Array,
|
||||
algorithm: "sha1" | "sha256"
|
||||
) => Promise<ArrayBuffer>;
|
||||
rsaExtractPublicKey: (privateKey: ArrayBuffer) => Promise<ArrayBuffer>;
|
||||
rsaGenerateKeyPair: (length: 1024 | 2048 | 4096) => Promise<[ArrayBuffer, ArrayBuffer]>;
|
||||
) => Promise<Uint8Array>;
|
||||
rsaExtractPublicKey: (privateKey: Uint8Array) => Promise<Uint8Array>;
|
||||
rsaGenerateKeyPair: (length: 1024 | 2048 | 4096) => Promise<[Uint8Array, Uint8Array]>;
|
||||
randomBytes: (length: number) => Promise<CsprngArray>;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ export abstract class CryptoService {
|
||||
getKeyHash: () => Promise<string>;
|
||||
compareAndUpdateKeyHash: (masterPassword: string, key: SymmetricCryptoKey) => Promise<boolean>;
|
||||
getEncKey: (key?: SymmetricCryptoKey) => Promise<SymmetricCryptoKey>;
|
||||
getPublicKey: () => Promise<ArrayBuffer>;
|
||||
getPrivateKey: () => Promise<ArrayBuffer>;
|
||||
getFingerprint: (fingerprintMaterial: string, publicKey?: ArrayBuffer) => Promise<string[]>;
|
||||
getPublicKey: () => Promise<Uint8Array>;
|
||||
getPrivateKey: () => Promise<Uint8Array>;
|
||||
getFingerprint: (fingerprintMaterial: string, publicKey?: Uint8Array) => Promise<string[]>;
|
||||
getOrgKeys: () => Promise<Map<string, SymmetricCryptoKey>>;
|
||||
getOrgKey: (orgId: string) => Promise<SymmetricCryptoKey>;
|
||||
getProviderKey: (providerId: string) => Promise<SymmetricCryptoKey>;
|
||||
@@ -63,7 +63,7 @@ export abstract class CryptoService {
|
||||
kdf: KdfType,
|
||||
kdfConfig: KdfConfig
|
||||
) => Promise<SymmetricCryptoKey>;
|
||||
makeSendKey: (keyMaterial: ArrayBuffer) => Promise<SymmetricCryptoKey>;
|
||||
makeSendKey: (keyMaterial: Uint8Array) => Promise<SymmetricCryptoKey>;
|
||||
hashPassword: (
|
||||
password: string,
|
||||
key: SymmetricCryptoKey,
|
||||
@@ -74,13 +74,13 @@ export abstract class CryptoService {
|
||||
key: SymmetricCryptoKey,
|
||||
encKey?: SymmetricCryptoKey
|
||||
) => Promise<[SymmetricCryptoKey, EncString]>;
|
||||
encrypt: (plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey) => Promise<EncString>;
|
||||
encryptToBytes: (plainValue: ArrayBuffer, key?: SymmetricCryptoKey) => Promise<EncArrayBuffer>;
|
||||
rsaEncrypt: (data: ArrayBuffer, publicKey?: ArrayBuffer) => Promise<EncString>;
|
||||
rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise<ArrayBuffer>;
|
||||
decryptToBytes: (encString: EncString, key?: SymmetricCryptoKey) => Promise<ArrayBuffer>;
|
||||
encrypt: (plainValue: string | Uint8Array, key?: SymmetricCryptoKey) => Promise<EncString>;
|
||||
encryptToBytes: (plainValue: Uint8Array, key?: SymmetricCryptoKey) => Promise<EncArrayBuffer>;
|
||||
rsaEncrypt: (data: Uint8Array, publicKey?: Uint8Array) => Promise<EncString>;
|
||||
rsaDecrypt: (encValue: string, privateKeyValue?: Uint8Array) => Promise<Uint8Array>;
|
||||
decryptToBytes: (encString: EncString, key?: SymmetricCryptoKey) => Promise<Uint8Array>;
|
||||
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>;
|
||||
validateKey: (key: SymmetricCryptoKey) => Promise<boolean>;
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ import { EncString } from "../models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
||||
|
||||
export abstract class EncryptService {
|
||||
abstract encrypt(plainValue: string | ArrayBuffer, key: SymmetricCryptoKey): Promise<EncString>;
|
||||
abstract encrypt(plainValue: string | Uint8Array, key: SymmetricCryptoKey): Promise<EncString>;
|
||||
abstract encryptToBytes: (
|
||||
plainValue: ArrayBuffer,
|
||||
plainValue: Uint8Array,
|
||||
key?: SymmetricCryptoKey
|
||||
) => Promise<EncArrayBuffer>;
|
||||
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 decryptItems: <T extends InitializerMetadata>(
|
||||
items: Decryptable<T>[],
|
||||
|
||||
@@ -113,8 +113,8 @@ export abstract class StateService<T extends Account = Account> {
|
||||
* @deprecated Do not call this, use PolicyService
|
||||
*/
|
||||
setDecryptedPolicies: (value: Policy[], options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedPrivateKey: (options?: StorageOptions) => Promise<ArrayBuffer>;
|
||||
setDecryptedPrivateKey: (value: ArrayBuffer, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedPrivateKey: (options?: StorageOptions) => Promise<Uint8Array>;
|
||||
setDecryptedPrivateKey: (value: Uint8Array, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedProviderKeys: (options?: StorageOptions) => Promise<Map<string, SymmetricCryptoKey>>;
|
||||
setDecryptedProviderKeys: (
|
||||
value: Map<string, SymmetricCryptoKey>,
|
||||
@@ -331,8 +331,8 @@ export abstract class StateService<T extends Account = Account> {
|
||||
setProtectedPin: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getProviders: (options?: StorageOptions) => Promise<{ [id: string]: ProviderData }>;
|
||||
setProviders: (value: { [id: string]: ProviderData }, options?: StorageOptions) => Promise<void>;
|
||||
getPublicKey: (options?: StorageOptions) => Promise<ArrayBuffer>;
|
||||
setPublicKey: (value: ArrayBuffer, options?: StorageOptions) => Promise<void>;
|
||||
getPublicKey: (options?: StorageOptions) => Promise<Uint8Array>;
|
||||
setPublicKey: (value: Uint8Array, options?: StorageOptions) => Promise<void>;
|
||||
getRefreshToken: (options?: StorageOptions) => Promise<string>;
|
||||
setRefreshToken: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getRememberedEmail: (options?: StorageOptions) => Promise<string>;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { EncryptionType } from "../../enums";
|
||||
|
||||
export interface Encrypted {
|
||||
encryptionType?: EncryptionType;
|
||||
dataBytes: ArrayBuffer;
|
||||
macBytes: ArrayBuffer;
|
||||
ivBytes: ArrayBuffer;
|
||||
dataBytes: Uint8Array;
|
||||
macBytes: Uint8Array;
|
||||
ivBytes: Uint8Array;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ describe("AccountKeys", () => {
|
||||
describe("toJSON", () => {
|
||||
it("should serialize itself", () => {
|
||||
const keys = new AccountKeys();
|
||||
const buffer = makeStaticByteArray(64).buffer;
|
||||
const buffer = makeStaticByteArray(64);
|
||||
keys.publicKey = buffer;
|
||||
|
||||
const bufferSpy = jest.spyOn(Utils, "fromBufferToByteString");
|
||||
@@ -18,7 +18,7 @@ describe("AccountKeys", () => {
|
||||
|
||||
it("should serialize public key as a string", () => {
|
||||
const keys = new AccountKeys();
|
||||
keys.publicKey = Utils.fromByteStringToArray("hello").buffer;
|
||||
keys.publicKey = Utils.fromByteStringToArray("hello");
|
||||
const json = JSON.stringify(keys);
|
||||
expect(json).toContain('"publicKey":"hello"');
|
||||
});
|
||||
@@ -29,7 +29,7 @@ describe("AccountKeys", () => {
|
||||
const keys = AccountKeys.fromJSON({
|
||||
publicKey: "hello",
|
||||
});
|
||||
expect(keys.publicKey).toEqual(Utils.fromByteStringToArray("hello").buffer);
|
||||
expect(keys.publicKey).toEqual(Utils.fromByteStringToArray("hello"));
|
||||
});
|
||||
|
||||
it("should deserialize cryptoMasterKey", () => {
|
||||
|
||||
@@ -119,8 +119,8 @@ export class AccountKeys {
|
||||
any,
|
||||
Record<string, SymmetricCryptoKey>
|
||||
>();
|
||||
privateKey?: EncryptionPair<string, ArrayBuffer> = new EncryptionPair<string, ArrayBuffer>();
|
||||
publicKey?: ArrayBuffer;
|
||||
privateKey?: EncryptionPair<string, Uint8Array> = new EncryptionPair<string, Uint8Array>();
|
||||
publicKey?: Uint8Array;
|
||||
apiKeyClientSecret?: string;
|
||||
|
||||
toJSON() {
|
||||
@@ -142,11 +142,10 @@ export class AccountKeys {
|
||||
),
|
||||
organizationKeys: AccountKeys.initRecordEncryptionPairsFromJSON(obj?.organizationKeys),
|
||||
providerKeys: AccountKeys.initRecordEncryptionPairsFromJSON(obj?.providerKeys),
|
||||
privateKey: EncryptionPair.fromJSON<string, ArrayBuffer>(
|
||||
obj?.privateKey,
|
||||
(decObj: string) => Utils.fromByteStringToArray(decObj).buffer
|
||||
privateKey: EncryptionPair.fromJSON<string, Uint8Array>(obj?.privateKey, (decObj: string) =>
|
||||
Utils.fromByteStringToArray(decObj)
|
||||
),
|
||||
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(data, 1 + iv.byteLength + mac.byteLength);
|
||||
|
||||
const actual = new EncArrayBuffer(array.buffer);
|
||||
const actual = new EncArrayBuffer(array);
|
||||
|
||||
expect(actual.encryptionType).toEqual(encType);
|
||||
expect(actual.ivBytes).toEqualBuffer(iv);
|
||||
@@ -39,11 +39,11 @@ describe("encArrayBuffer", () => {
|
||||
array.set(iv, 1);
|
||||
array.set(data, 1 + iv.byteLength);
|
||||
|
||||
const actual = new EncArrayBuffer(array.buffer);
|
||||
const actual = new EncArrayBuffer(array);
|
||||
|
||||
expect(actual.encryptionType).toEqual(encType);
|
||||
expect(actual.ivBytes).toEqualBuffer(iv);
|
||||
expect(actual.dataBytes).toEqualBuffer(data);
|
||||
expect(actual.ivBytes).toEqual(iv);
|
||||
expect(actual.dataBytes).toEqual(data);
|
||||
expect(actual.macBytes).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -58,13 +58,11 @@ describe("encArrayBuffer", () => {
|
||||
// Minus 1 to leave room for the encType, minus 1 to make it invalid
|
||||
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(invalidBytes, 1);
|
||||
|
||||
expect(() => new EncArrayBuffer(invalidArray.buffer)).toThrow(
|
||||
"Error parsing encrypted ArrayBuffer"
|
||||
);
|
||||
expect(() => new EncArrayBuffer(invalidArray)).toThrow("Error parsing encrypted ArrayBuffer");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ const MIN_DATA_LENGTH = 1;
|
||||
|
||||
export class EncArrayBuffer implements Encrypted {
|
||||
readonly encryptionType: EncryptionType = null;
|
||||
readonly dataBytes: ArrayBuffer = null;
|
||||
readonly ivBytes: ArrayBuffer = null;
|
||||
readonly macBytes: ArrayBuffer = null;
|
||||
readonly dataBytes: Uint8Array = null;
|
||||
readonly ivBytes: Uint8Array = null;
|
||||
readonly macBytes: Uint8Array = null;
|
||||
|
||||
constructor(readonly buffer: ArrayBuffer) {
|
||||
const encBytes = new Uint8Array(buffer);
|
||||
constructor(readonly buffer: Uint8Array) {
|
||||
const encBytes = buffer;
|
||||
const encType = encBytes[0];
|
||||
|
||||
switch (encType) {
|
||||
@@ -25,12 +25,12 @@ export class EncArrayBuffer implements Encrypted {
|
||||
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(
|
||||
ENC_TYPE_LENGTH + IV_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;
|
||||
}
|
||||
case EncryptionType.AesCbc256_B64: {
|
||||
@@ -39,8 +39,8 @@ export class EncArrayBuffer implements Encrypted {
|
||||
this.throwDecryptionError();
|
||||
}
|
||||
|
||||
this.ivBytes = encBytes.slice(ENC_TYPE_LENGTH, ENC_TYPE_LENGTH + IV_LENGTH).buffer;
|
||||
this.dataBytes = encBytes.slice(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);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -63,11 +63,11 @@ export class EncArrayBuffer implements Encrypted {
|
||||
if (buffer == null) {
|
||||
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) {
|
||||
const buffer = Utils.fromB64ToArray(b64).buffer;
|
||||
const buffer = Utils.fromB64ToArray(b64);
|
||||
return new EncArrayBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,16 +27,16 @@ export class EncString implements Encrypted {
|
||||
}
|
||||
}
|
||||
|
||||
get ivBytes(): ArrayBuffer {
|
||||
return this.iv == null ? null : Utils.fromB64ToArray(this.iv).buffer;
|
||||
get ivBytes(): Uint8Array {
|
||||
return this.iv == null ? null : Utils.fromB64ToArray(this.iv);
|
||||
}
|
||||
|
||||
get macBytes(): ArrayBuffer {
|
||||
return this.mac == null ? null : Utils.fromB64ToArray(this.mac).buffer;
|
||||
get macBytes(): Uint8Array {
|
||||
return this.mac == null ? null : Utils.fromB64ToArray(this.mac);
|
||||
}
|
||||
|
||||
get dataBytes(): ArrayBuffer {
|
||||
return this.data == null ? null : Utils.fromB64ToArray(this.data).buffer;
|
||||
get dataBytes(): Uint8Array {
|
||||
return this.data == null ? null : Utils.fromB64ToArray(this.data);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
|
||||
export class EncryptedObject {
|
||||
iv: ArrayBuffer;
|
||||
data: ArrayBuffer;
|
||||
mac: ArrayBuffer;
|
||||
iv: Uint8Array;
|
||||
data: Uint8Array;
|
||||
mac: Uint8Array;
|
||||
key: SymmetricCryptoKey;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,13 @@ describe("EncryptionPair", () => {
|
||||
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", () => {
|
||||
const pair = new EncryptionPair<string, string>();
|
||||
pair.encrypted = "hello";
|
||||
|
||||
@@ -68,7 +68,7 @@ describe("SymmetricCryptoKey", () => {
|
||||
});
|
||||
|
||||
it("toJSON creates object for serialization", () => {
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(64).buffer);
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(64));
|
||||
const actual = key.toJSON();
|
||||
|
||||
const expected = { keyB64: key.keyB64 };
|
||||
@@ -77,7 +77,7 @@ describe("SymmetricCryptoKey", () => {
|
||||
});
|
||||
|
||||
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 });
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
|
||||
@@ -4,9 +4,9 @@ import { EncryptionType } from "../../../enums";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
|
||||
export class SymmetricCryptoKey {
|
||||
key: ArrayBuffer;
|
||||
encKey?: ArrayBuffer;
|
||||
macKey?: ArrayBuffer;
|
||||
key: Uint8Array;
|
||||
encKey?: Uint8Array;
|
||||
macKey?: Uint8Array;
|
||||
encType: EncryptionType;
|
||||
|
||||
keyB64: string;
|
||||
@@ -15,7 +15,7 @@ export class SymmetricCryptoKey {
|
||||
|
||||
meta: any;
|
||||
|
||||
constructor(key: ArrayBuffer, encType?: EncryptionType) {
|
||||
constructor(key: Uint8Array, encType?: EncryptionType) {
|
||||
if (key == null) {
|
||||
throw new Error("Must provide key");
|
||||
}
|
||||
@@ -67,7 +67,7 @@ export class SymmetricCryptoKey {
|
||||
return null;
|
||||
}
|
||||
|
||||
const arrayBuffer = Utils.fromB64ToArray(s).buffer;
|
||||
const arrayBuffer = Utils.fromB64ToArray(s);
|
||||
return new SymmetricCryptoKey(arrayBuffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
): Promise<SymmetricCryptoKey> {
|
||||
const key = await this.retrieveKeyFromStorage(keySuffix, userId);
|
||||
if (key != null) {
|
||||
const symmetricKey = new SymmetricCryptoKey(Utils.fromB64ToArray(key).buffer);
|
||||
const symmetricKey = new SymmetricCryptoKey(Utils.fromB64ToArray(key));
|
||||
|
||||
if (!(await this.validateKey(symmetricKey))) {
|
||||
this.logService.warning("Wrong key, throwing away stored key");
|
||||
@@ -172,7 +172,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return this.getEncKeyHelper(key);
|
||||
}
|
||||
|
||||
async getPublicKey(): Promise<ArrayBuffer> {
|
||||
async getPublicKey(): Promise<Uint8Array> {
|
||||
const inMemoryPublicKey = await this.stateService.getPublicKey();
|
||||
if (inMemoryPublicKey != null) {
|
||||
return inMemoryPublicKey;
|
||||
@@ -188,7 +188,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
async getPrivateKey(): Promise<ArrayBuffer> {
|
||||
async getPrivateKey(): Promise<Uint8Array> {
|
||||
const decryptedPrivateKey = await this.stateService.getDecryptedPrivateKey();
|
||||
if (decryptedPrivateKey != null) {
|
||||
return decryptedPrivateKey;
|
||||
@@ -204,7 +204,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
async getFingerprint(fingerprintMaterial: string, publicKey?: ArrayBuffer): Promise<string[]> {
|
||||
async getFingerprint(fingerprintMaterial: string, publicKey?: Uint8Array): Promise<string[]> {
|
||||
if (publicKey == null) {
|
||||
publicKey = await this.getPublicKey();
|
||||
}
|
||||
@@ -416,7 +416,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
kdf: KdfType,
|
||||
kdfConfig: KdfConfig
|
||||
): Promise<SymmetricCryptoKey> {
|
||||
let key: ArrayBuffer = null;
|
||||
let key: Uint8Array = null;
|
||||
if (kdf == null || kdf === KdfType.PBKDF2_SHA256) {
|
||||
if (kdfConfig.iterations == null) {
|
||||
kdfConfig.iterations = 5000;
|
||||
@@ -502,7 +502,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return await this.stretchKey(pinKey);
|
||||
}
|
||||
|
||||
async makeSendKey(keyMaterial: ArrayBuffer): Promise<SymmetricCryptoKey> {
|
||||
async makeSendKey(keyMaterial: Uint8Array): Promise<SymmetricCryptoKey> {
|
||||
const sendKey = await this.cryptoFunctionService.hkdf(
|
||||
keyMaterial,
|
||||
"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)
|
||||
* 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);
|
||||
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)
|
||||
* 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);
|
||||
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) {
|
||||
publicKey = await this.getPublicKey();
|
||||
}
|
||||
@@ -576,7 +576,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
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(".");
|
||||
let encType: EncryptionType = null;
|
||||
let encPieces: string[];
|
||||
@@ -607,7 +607,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
throw new Error("encPieces unavailable.");
|
||||
}
|
||||
|
||||
const data = Utils.fromB64ToArray(encPieces[0]).buffer;
|
||||
const data = Utils.fromB64ToArray(encPieces[0]);
|
||||
const privateKey = privateKeyValue ?? (await this.getPrivateKey());
|
||||
if (privateKey == null) {
|
||||
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)
|
||||
* 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);
|
||||
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)
|
||||
* and then call encryptService.decryptToBytes
|
||||
*/
|
||||
async decryptFromBytes(encBuffer: EncArrayBuffer, key: SymmetricCryptoKey): Promise<ArrayBuffer> {
|
||||
async decryptFromBytes(encBuffer: EncArrayBuffer, key: SymmetricCryptoKey): Promise<Uint8Array> {
|
||||
if (encBuffer == null) {
|
||||
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");
|
||||
newKey.set(new Uint8Array(encKey));
|
||||
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);
|
||||
let numWords = Math.ceil(minimumEntropy / entropyPerWord);
|
||||
|
||||
@@ -793,7 +793,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
|
||||
private async buildEncKey(
|
||||
key: SymmetricCryptoKey,
|
||||
encKey: ArrayBuffer
|
||||
encKey: Uint8Array
|
||||
): Promise<[SymmetricCryptoKey, EncString]> {
|
||||
let encKeyEnc: EncString = null;
|
||||
if (key.key.byteLength === 32) {
|
||||
@@ -830,7 +830,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return null;
|
||||
}
|
||||
|
||||
let decEncKey: ArrayBuffer;
|
||||
let decEncKey: Uint8Array;
|
||||
const encKeyCipher = new EncString(encKey);
|
||||
if (encKeyCipher.encryptionType === EncryptionType.AesCbc256_B64) {
|
||||
decEncKey = await this.decryptToBytes(encKeyCipher, key);
|
||||
|
||||
@@ -18,7 +18,7 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
protected logMacFailures: boolean
|
||||
) {}
|
||||
|
||||
async encrypt(plainValue: string | ArrayBuffer, key: SymmetricCryptoKey): Promise<EncString> {
|
||||
async encrypt(plainValue: string | Uint8Array, key: SymmetricCryptoKey): Promise<EncString> {
|
||||
if (key == null) {
|
||||
throw new Error("No encryption key provided.");
|
||||
}
|
||||
@@ -27,9 +27,9 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
let plainBuf: ArrayBuffer;
|
||||
let plainBuf: Uint8Array;
|
||||
if (typeof plainValue === "string") {
|
||||
plainBuf = Utils.fromUtf8ToArray(plainValue).buffer;
|
||||
plainBuf = Utils.fromUtf8ToArray(plainValue);
|
||||
} else {
|
||||
plainBuf = plainValue;
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
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) {
|
||||
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);
|
||||
return new EncArrayBuffer(encBytes.buffer);
|
||||
return new EncArrayBuffer(encBytes);
|
||||
}
|
||||
|
||||
async decryptToUtf8(encString: EncString, key: SymmetricCryptoKey): Promise<string> {
|
||||
@@ -102,7 +102,7 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
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) {
|
||||
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);
|
||||
macData.set(new Uint8Array(encThing.ivBytes), 0);
|
||||
macData.set(new Uint8Array(encThing.dataBytes), encThing.ivBytes.byteLength);
|
||||
const computedMac = await this.cryptoFunctionService.hmac(
|
||||
macData.buffer,
|
||||
key.macKey,
|
||||
"sha256"
|
||||
);
|
||||
const computedMac = await this.cryptoFunctionService.hmac(macData, key.macKey, "sha256");
|
||||
if (computedMac === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -161,7 +157,7 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
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();
|
||||
obj.key = key;
|
||||
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);
|
||||
macData.set(new Uint8Array(obj.iv), 0);
|
||||
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;
|
||||
|
||||
@@ -37,10 +37,8 @@ describe("EncryptService", () => {
|
||||
|
||||
describe("encrypts data", () => {
|
||||
beforeEach(() => {
|
||||
cryptoFunctionService.randomBytes
|
||||
.calledWith(16)
|
||||
.mockResolvedValueOnce(iv.buffer as CsprngArray);
|
||||
cryptoFunctionService.aesEncrypt.mockResolvedValue(encryptedData.buffer);
|
||||
cryptoFunctionService.randomBytes.calledWith(16).mockResolvedValueOnce(iv as CsprngArray);
|
||||
cryptoFunctionService.aesEncrypt.mockResolvedValue(encryptedData);
|
||||
});
|
||||
|
||||
it("using a key which supports mac", async () => {
|
||||
@@ -50,7 +48,7 @@ describe("EncryptService", () => {
|
||||
|
||||
key.macKey = makeStaticByteArray(16, 20);
|
||||
|
||||
cryptoFunctionService.hmac.mockResolvedValue(mac.buffer);
|
||||
cryptoFunctionService.hmac.mockResolvedValue(mac);
|
||||
|
||||
const actual = await encryptService.encryptToBytes(plainValue, key);
|
||||
|
||||
@@ -86,7 +84,7 @@ describe("EncryptService", () => {
|
||||
describe("decryptToBytes", () => {
|
||||
const encType = EncryptionType.AesCbc256_HmacSha256_B64;
|
||||
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));
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -106,9 +104,9 @@ describe("EncryptService", () => {
|
||||
});
|
||||
|
||||
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.aesDecrypt.mockResolvedValueOnce(decryptedBytes);
|
||||
|
||||
|
||||
@@ -763,13 +763,13 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
async getDecryptedPrivateKey(options?: StorageOptions): Promise<ArrayBuffer> {
|
||||
async getDecryptedPrivateKey(options?: StorageOptions): Promise<Uint8Array> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
)?.keys?.privateKey.decrypted;
|
||||
}
|
||||
|
||||
async setDecryptedPrivateKey(value: ArrayBuffer, options?: StorageOptions): Promise<void> {
|
||||
async setDecryptedPrivateKey(value: Uint8Array, options?: StorageOptions): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
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 = (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
)?.keys;
|
||||
return keys?.publicKey;
|
||||
}
|
||||
|
||||
async setPublicKey(value: ArrayBuffer, options?: StorageOptions): Promise<void> {
|
||||
async setPublicKey(value: Uint8Array, options?: StorageOptions): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||
);
|
||||
|
||||
@@ -160,7 +160,7 @@ describe("WebCrypto Function Service", () => {
|
||||
const a = new Uint8Array(2);
|
||||
a[0] = 1;
|
||||
a[1] = 2;
|
||||
const equal = await cryptoFunctionService.compare(a.buffer, a.buffer);
|
||||
const equal = await cryptoFunctionService.compare(a, a);
|
||||
expect(equal).toBe(true);
|
||||
});
|
||||
|
||||
@@ -172,7 +172,7 @@ describe("WebCrypto Function Service", () => {
|
||||
const b = new Uint8Array(2);
|
||||
b[0] = 3;
|
||||
b[1] = 4;
|
||||
const equal = await cryptoFunctionService.compare(a.buffer, b.buffer);
|
||||
const equal = await cryptoFunctionService.compare(a, b);
|
||||
expect(equal).toBe(false);
|
||||
});
|
||||
|
||||
@@ -183,7 +183,7 @@ describe("WebCrypto Function Service", () => {
|
||||
a[1] = 2;
|
||||
const b = new Uint8Array(2);
|
||||
b[0] = 3;
|
||||
const equal = await cryptoFunctionService.compare(a.buffer, b.buffer);
|
||||
const equal = await cryptoFunctionService.compare(a, b);
|
||||
expect(equal).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -200,7 +200,7 @@ describe("WebCrypto Function Service", () => {
|
||||
const a = new Uint8Array(2);
|
||||
a[0] = 1;
|
||||
a[1] = 2;
|
||||
const aByteString = Utils.fromBufferToByteString(a.buffer);
|
||||
const aByteString = Utils.fromBufferToByteString(a);
|
||||
const equal = await cryptoFunctionService.compareFast(aByteString, aByteString);
|
||||
expect(equal).toBe(true);
|
||||
});
|
||||
@@ -210,11 +210,11 @@ describe("WebCrypto Function Service", () => {
|
||||
const a = new Uint8Array(2);
|
||||
a[0] = 1;
|
||||
a[1] = 2;
|
||||
const aByteString = Utils.fromBufferToByteString(a.buffer);
|
||||
const aByteString = Utils.fromBufferToByteString(a);
|
||||
const b = new Uint8Array(2);
|
||||
b[0] = 3;
|
||||
b[1] = 4;
|
||||
const bByteString = Utils.fromBufferToByteString(b.buffer);
|
||||
const bByteString = Utils.fromBufferToByteString(b);
|
||||
const equal = await cryptoFunctionService.compareFast(aByteString, bByteString);
|
||||
expect(equal).toBe(false);
|
||||
});
|
||||
@@ -224,10 +224,10 @@ describe("WebCrypto Function Service", () => {
|
||||
const a = new Uint8Array(2);
|
||||
a[0] = 1;
|
||||
a[1] = 2;
|
||||
const aByteString = Utils.fromBufferToByteString(a.buffer);
|
||||
const aByteString = Utils.fromBufferToByteString(a);
|
||||
const b = new Uint8Array(2);
|
||||
b[0] = 3;
|
||||
const bByteString = Utils.fromBufferToByteString(b.buffer);
|
||||
const bByteString = Utils.fromBufferToByteString(b);
|
||||
const equal = await cryptoFunctionService.compareFast(aByteString, bByteString);
|
||||
expect(equal).toBe(false);
|
||||
});
|
||||
@@ -239,7 +239,7 @@ describe("WebCrypto Function Service", () => {
|
||||
const iv = makeStaticByteArray(16);
|
||||
const key = makeStaticByteArray(32);
|
||||
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==");
|
||||
});
|
||||
|
||||
@@ -249,10 +249,10 @@ describe("WebCrypto Function Service", () => {
|
||||
const key = makeStaticByteArray(32);
|
||||
const value = "EncryptMe!";
|
||||
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 b64Iv = Utils.fromBufferToB64(iv.buffer);
|
||||
const symKey = new SymmetricCryptoKey(key.buffer);
|
||||
const b64Iv = Utils.fromBufferToB64(iv);
|
||||
const symKey = new SymmetricCryptoKey(key);
|
||||
const params = cryptoFunctionService.aesDecryptFastParameters(encData, b64Iv, null, symKey);
|
||||
const decValue = await cryptoFunctionService.aesDecryptFast(params);
|
||||
expect(decValue).toBe(value);
|
||||
@@ -264,8 +264,8 @@ describe("WebCrypto Function Service", () => {
|
||||
const key = makeStaticByteArray(32);
|
||||
const value = "EncryptMe!";
|
||||
const data = Utils.fromUtf8ToArray(value);
|
||||
const encValue = await cryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer);
|
||||
const decValue = await cryptoFunctionService.aesDecrypt(encValue, iv.buffer, key.buffer);
|
||||
const encValue = new Uint8Array(await cryptoFunctionService.aesEncrypt(data, iv, key));
|
||||
const decValue = await cryptoFunctionService.aesDecrypt(encValue, iv, key);
|
||||
expect(Utils.fromBufferToUtf8(decValue)).toBe(value);
|
||||
});
|
||||
});
|
||||
@@ -273,8 +273,8 @@ describe("WebCrypto Function Service", () => {
|
||||
describe("aesDecryptFast", () => {
|
||||
it("should successfully decrypt data", async () => {
|
||||
const cryptoFunctionService = getWebCryptoFunctionService();
|
||||
const iv = Utils.fromBufferToB64(makeStaticByteArray(16).buffer);
|
||||
const symKey = new SymmetricCryptoKey(makeStaticByteArray(32).buffer);
|
||||
const iv = Utils.fromBufferToB64(makeStaticByteArray(16));
|
||||
const symKey = new SymmetricCryptoKey(makeStaticByteArray(32));
|
||||
const data = "ByUF8vhyX4ddU9gcooznwA==";
|
||||
const params = cryptoFunctionService.aesDecryptFastParameters(data, iv, null, symKey);
|
||||
const decValue = await cryptoFunctionService.aesDecryptFast(params);
|
||||
@@ -288,7 +288,7 @@ describe("WebCrypto Function Service", () => {
|
||||
const iv = makeStaticByteArray(16);
|
||||
const key = makeStaticByteArray(32);
|
||||
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!");
|
||||
});
|
||||
});
|
||||
@@ -300,8 +300,8 @@ describe("WebCrypto Function Service", () => {
|
||||
const privKey = Utils.fromB64ToArray(RsaPrivateKey);
|
||||
const value = "EncryptMe!";
|
||||
const data = Utils.fromUtf8ToArray(value);
|
||||
const encValue = await cryptoFunctionService.rsaEncrypt(data.buffer, pubKey.buffer, "sha1");
|
||||
const decValue = await cryptoFunctionService.rsaDecrypt(encValue, privKey.buffer, "sha1");
|
||||
const encValue = new Uint8Array(await cryptoFunctionService.rsaEncrypt(data, pubKey, "sha1"));
|
||||
const decValue = await cryptoFunctionService.rsaDecrypt(encValue, privKey, "sha1");
|
||||
expect(Utils.fromBufferToUtf8(decValue)).toBe(value);
|
||||
});
|
||||
});
|
||||
@@ -316,7 +316,7 @@ describe("WebCrypto Function Service", () => {
|
||||
"zFOIEPF2S1zgperEP23M01mr4dWVdYN18B32YF67xdJHMbFhp5dkQwv9CmscoWq7OE5HIfOb+JAh7BEZb+CmKhM3yWJvoR/D" +
|
||||
"/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!");
|
||||
});
|
||||
});
|
||||
@@ -325,7 +325,7 @@ describe("WebCrypto Function Service", () => {
|
||||
it("should successfully extract key", async () => {
|
||||
const cryptoFunctionService = getWebCryptoFunctionService();
|
||||
const privKey = Utils.fromB64ToArray(RsaPrivateKey);
|
||||
const publicKey = await cryptoFunctionService.rsaExtractPublicKey(privKey.buffer);
|
||||
const publicKey = await cryptoFunctionService.rsaExtractPublicKey(privKey);
|
||||
expect(Utils.fromBufferToB64(publicKey)).toBe(RsaPublicKey);
|
||||
});
|
||||
});
|
||||
@@ -390,8 +390,8 @@ function testPbkdf2(
|
||||
it("should create valid " + algorithm + " key from array buffer input", async () => {
|
||||
const cryptoFunctionService = getWebCryptoFunctionService();
|
||||
const key = await cryptoFunctionService.pbkdf2(
|
||||
Utils.fromUtf8ToArray(regularPassword).buffer,
|
||||
Utils.fromUtf8ToArray(regularEmail).buffer,
|
||||
Utils.fromUtf8ToArray(regularPassword),
|
||||
Utils.fromUtf8ToArray(regularEmail),
|
||||
algorithm,
|
||||
5000
|
||||
);
|
||||
@@ -437,8 +437,8 @@ function testHkdf(
|
||||
const cryptoFunctionService = getWebCryptoFunctionService();
|
||||
const key = await cryptoFunctionService.hkdf(
|
||||
ikm,
|
||||
Utils.fromUtf8ToArray(regularSalt).buffer,
|
||||
Utils.fromUtf8ToArray(regularInfo).buffer,
|
||||
Utils.fromUtf8ToArray(regularSalt),
|
||||
Utils.fromUtf8ToArray(regularInfo),
|
||||
32,
|
||||
algorithm
|
||||
);
|
||||
@@ -496,10 +496,7 @@ function testHash(
|
||||
|
||||
it("should create valid " + algorithm + " hash from array buffer input", async () => {
|
||||
const cryptoFunctionService = getWebCryptoFunctionService();
|
||||
const hash = await cryptoFunctionService.hash(
|
||||
Utils.fromUtf8ToArray(regularValue).buffer,
|
||||
algorithm
|
||||
);
|
||||
const hash = await cryptoFunctionService.hash(Utils.fromUtf8ToArray(regularValue), algorithm);
|
||||
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 () => {
|
||||
const cryptoFunctionService = getWebCryptoFunctionService();
|
||||
const computedMac = await cryptoFunctionService.hmac(
|
||||
Utils.fromUtf8ToArray("SignMe!!").buffer,
|
||||
Utils.fromUtf8ToArray("secretkey").buffer,
|
||||
Utils.fromUtf8ToArray("SignMe!!"),
|
||||
Utils.fromUtf8ToArray("secretkey"),
|
||||
algorithm
|
||||
);
|
||||
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) {
|
||||
it("should create valid " + algorithm + " hmac", async () => {
|
||||
const cryptoFunctionService = getWebCryptoFunctionService();
|
||||
const keyByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("secretkey").buffer);
|
||||
const dataByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("SignMe!!").buffer);
|
||||
const keyByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("secretkey"));
|
||||
const dataByteString = Utils.fromBufferToByteString(Utils.fromUtf8ToArray("SignMe!!"));
|
||||
const computedMac = await cryptoFunctionService.hmacFast(
|
||||
dataByteString,
|
||||
keyByteString,
|
||||
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",
|
||||
async () => {
|
||||
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);
|
||||
const publicKey = await cryptoFunctionService.rsaExtractPublicKey(keyPair[1]);
|
||||
expect(Utils.fromBufferToB64(keyPair[0])).toBe(Utils.fromBufferToB64(publicKey));
|
||||
|
||||
@@ -20,11 +20,11 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
}
|
||||
|
||||
async pbkdf2(
|
||||
password: string | ArrayBuffer,
|
||||
salt: string | ArrayBuffer,
|
||||
password: string | Uint8Array,
|
||||
salt: string | Uint8Array,
|
||||
algorithm: "sha256" | "sha512",
|
||||
iterations: number
|
||||
): Promise<ArrayBuffer> {
|
||||
): Promise<Uint8Array> {
|
||||
const wcLen = algorithm === "sha256" ? 256 : 512;
|
||||
const passwordBuf = this.toBuf(password);
|
||||
const saltBuf = this.toBuf(salt);
|
||||
@@ -43,16 +43,17 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
false,
|
||||
["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(
|
||||
password: string | ArrayBuffer,
|
||||
salt: string | ArrayBuffer,
|
||||
password: string | Uint8Array,
|
||||
salt: string | Uint8Array,
|
||||
iterations: number,
|
||||
memory: number,
|
||||
parallelism: number
|
||||
): Promise<ArrayBuffer> {
|
||||
): Promise<Uint8Array> {
|
||||
if (!this.wasmSupported) {
|
||||
throw "Webassembly support is required for the Argon2 KDF feature.";
|
||||
}
|
||||
@@ -74,12 +75,12 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
}
|
||||
|
||||
async hkdf(
|
||||
ikm: ArrayBuffer,
|
||||
salt: string | ArrayBuffer,
|
||||
info: string | ArrayBuffer,
|
||||
ikm: Uint8Array,
|
||||
salt: string | Uint8Array,
|
||||
info: string | Uint8Array,
|
||||
outputByteSize: number,
|
||||
algorithm: "sha256" | "sha512"
|
||||
): Promise<ArrayBuffer> {
|
||||
): Promise<Uint8Array> {
|
||||
const saltBuf = this.toBuf(salt);
|
||||
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, [
|
||||
"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
|
||||
async hkdfExpand(
|
||||
prk: ArrayBuffer,
|
||||
info: string | ArrayBuffer,
|
||||
prk: Uint8Array,
|
||||
info: string | Uint8Array,
|
||||
outputByteSize: number,
|
||||
algorithm: "sha256" | "sha512"
|
||||
): Promise<ArrayBuffer> {
|
||||
): Promise<Uint8Array> {
|
||||
const hashLen = algorithm === "sha256" ? 32 : 64;
|
||||
if (outputByteSize > 255 * hashLen) {
|
||||
throw new Error("outputByteSize is too large.");
|
||||
@@ -122,49 +124,54 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
t.set(previousT);
|
||||
t.set(infoArr, previousT.length);
|
||||
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);
|
||||
runningOkmLength += previousT.length;
|
||||
if (runningOkmLength >= outputByteSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return okm.slice(0, outputByteSize).buffer;
|
||||
return okm.slice(0, outputByteSize);
|
||||
}
|
||||
|
||||
async hash(
|
||||
value: string | ArrayBuffer,
|
||||
value: string | Uint8Array,
|
||||
algorithm: "sha1" | "sha256" | "sha512" | "md5"
|
||||
): Promise<ArrayBuffer> {
|
||||
): Promise<Uint8Array> {
|
||||
if (algorithm === "md5") {
|
||||
const md = algorithm === "md5" ? forge.md.md5.create() : forge.md.sha1.create();
|
||||
const valueBytes = this.toByteString(value);
|
||||
md.update(valueBytes, "raw");
|
||||
return Utils.fromByteStringToArray(md.digest().data).buffer;
|
||||
return Utils.fromByteStringToArray(md.digest().data);
|
||||
}
|
||||
|
||||
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(
|
||||
value: ArrayBuffer,
|
||||
key: ArrayBuffer,
|
||||
value: Uint8Array,
|
||||
key: Uint8Array,
|
||||
algorithm: "sha1" | "sha256" | "sha512"
|
||||
): Promise<ArrayBuffer> {
|
||||
): Promise<Uint8Array> {
|
||||
const signingAlgorithm = {
|
||||
name: "HMAC",
|
||||
hash: { name: this.toWebCryptoAlgorithm(algorithm) },
|
||||
};
|
||||
|
||||
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).
|
||||
// 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
|
||||
async compare(a: ArrayBuffer, b: ArrayBuffer): Promise<boolean> {
|
||||
async compare(a: Uint8Array, b: Uint8Array): Promise<boolean> {
|
||||
const macKey = await this.randomBytes(32);
|
||||
const signingAlgorithm = {
|
||||
name: "HMAC",
|
||||
@@ -219,11 +226,12 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
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, [
|
||||
"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(
|
||||
@@ -275,18 +283,19 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
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, [
|
||||
"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(
|
||||
data: ArrayBuffer,
|
||||
publicKey: ArrayBuffer,
|
||||
data: Uint8Array,
|
||||
publicKey: Uint8Array,
|
||||
algorithm: "sha1" | "sha256"
|
||||
): Promise<ArrayBuffer> {
|
||||
): Promise<Uint8Array> {
|
||||
// Note: Edge browser requires that we specify name and hash for both key import and decrypt.
|
||||
// We cannot use the proper types here.
|
||||
const rsaParams = {
|
||||
@@ -294,14 +303,15 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
hash: { name: this.toWebCryptoAlgorithm(algorithm) },
|
||||
};
|
||||
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(
|
||||
data: ArrayBuffer,
|
||||
privateKey: ArrayBuffer,
|
||||
data: Uint8Array,
|
||||
privateKey: Uint8Array,
|
||||
algorithm: "sha1" | "sha256"
|
||||
): Promise<ArrayBuffer> {
|
||||
): Promise<Uint8Array> {
|
||||
// Note: Edge browser requires that we specify name and hash for both key import and decrypt.
|
||||
// We cannot use the proper types here.
|
||||
const rsaParams = {
|
||||
@@ -309,10 +319,11 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
hash: { name: this.toWebCryptoAlgorithm(algorithm) },
|
||||
};
|
||||
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 = {
|
||||
name: "RSA-OAEP",
|
||||
// Have to specify some algorithm
|
||||
@@ -332,10 +343,11 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
const impPublicKey = await this.subtle.importKey("jwk", jwkPublicKeyParams, rsaParams, true, [
|
||||
"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 = {
|
||||
name: "RSA-OAEP",
|
||||
modulusLength: length,
|
||||
@@ -349,26 +361,26 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
])) as CryptoKeyPair;
|
||||
const publicKey = await this.subtle.exportKey("spki", keyPair.publicKey);
|
||||
const privateKey = await this.subtle.exportKey("pkcs8", keyPair.privateKey);
|
||||
return [publicKey, privateKey];
|
||||
return [new Uint8Array(publicKey), new Uint8Array(privateKey)];
|
||||
}
|
||||
|
||||
randomBytes(length: number): Promise<CsprngArray> {
|
||||
const arr = new Uint8Array(length);
|
||||
this.crypto.getRandomValues(arr);
|
||||
return Promise.resolve(arr.buffer as CsprngArray);
|
||||
return Promise.resolve(arr as CsprngArray);
|
||||
}
|
||||
|
||||
private toBuf(value: string | ArrayBuffer): ArrayBuffer {
|
||||
let buf: ArrayBuffer;
|
||||
private toBuf(value: string | Uint8Array): Uint8Array {
|
||||
let buf: Uint8Array;
|
||||
if (typeof value === "string") {
|
||||
buf = Utils.fromUtf8ToArray(value).buffer;
|
||||
buf = Utils.fromUtf8ToArray(value);
|
||||
} else {
|
||||
buf = value;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
private toByteString(value: string | ArrayBuffer): string {
|
||||
private toByteString(value: string | Uint8Array): string {
|
||||
let bytes: string;
|
||||
if (typeof value === "string") {
|
||||
bytes = forge.util.encodeUtf8(value);
|
||||
|
||||
@@ -57,10 +57,10 @@ describe("deviceCryptoService", () => {
|
||||
let makeDeviceKeySpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRandomBytes = new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray;
|
||||
mockRandomBytes = new Uint8Array(deviceKeyBytesLength) as CsprngArray;
|
||||
mockDeviceKey = new SymmetricCryptoKey(mockRandomBytes);
|
||||
existingDeviceKey = new SymmetricCryptoKey(
|
||||
new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray
|
||||
new Uint8Array(deviceKeyBytesLength) as CsprngArray
|
||||
) as DeviceKey;
|
||||
|
||||
stateSvcGetDeviceKeySpy = jest.spyOn(stateService, "getDeviceKey");
|
||||
@@ -97,7 +97,7 @@ describe("deviceCryptoService", () => {
|
||||
|
||||
describe("makeDeviceKey", () => {
|
||||
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
|
||||
.spyOn(cryptoFunctionService, "randomBytes")
|
||||
@@ -128,9 +128,9 @@ describe("deviceCryptoService", () => {
|
||||
let mockUserSymKey: SymmetricCryptoKey;
|
||||
|
||||
const deviceRsaKeyLength = 2048;
|
||||
let mockDeviceRsaKeyPair: [ArrayBuffer, ArrayBuffer];
|
||||
let mockDevicePrivateKey: ArrayBuffer;
|
||||
let mockDevicePublicKey: ArrayBuffer;
|
||||
let mockDeviceRsaKeyPair: [Uint8Array, Uint8Array];
|
||||
let mockDevicePrivateKey: Uint8Array;
|
||||
let mockDevicePublicKey: Uint8Array;
|
||||
let mockDevicePublicKeyEncryptedUserSymKey: EncString;
|
||||
let mockUserSymKeyEncryptedDevicePublicKey: EncString;
|
||||
let mockDeviceKeyEncryptedDevicePrivateKey: EncString;
|
||||
@@ -156,15 +156,15 @@ describe("deviceCryptoService", () => {
|
||||
beforeEach(() => {
|
||||
// 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;
|
||||
|
||||
mockUserSymKeyRandomBytes = new Uint8Array(userSymKeyBytesLength).buffer as CsprngArray;
|
||||
mockUserSymKeyRandomBytes = new Uint8Array(userSymKeyBytesLength) as CsprngArray;
|
||||
mockUserSymKey = new SymmetricCryptoKey(mockUserSymKeyRandomBytes);
|
||||
|
||||
mockDeviceRsaKeyPair = [
|
||||
new ArrayBuffer(deviceRsaKeyLength),
|
||||
new ArrayBuffer(deviceRsaKeyLength),
|
||||
new Uint8Array(deviceRsaKeyLength),
|
||||
new Uint8Array(deviceRsaKeyLength),
|
||||
];
|
||||
|
||||
mockDevicePublicKey = mockDeviceRsaKeyPair[0];
|
||||
|
||||
@@ -162,7 +162,7 @@ export class TotpService implements TotpServiceAbstraction {
|
||||
timeBytes: Uint8Array,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export class SendView implements View {
|
||||
accessId: string = null;
|
||||
name: string = null;
|
||||
notes: string = null;
|
||||
key: ArrayBuffer;
|
||||
key: Uint8Array;
|
||||
cryptoKey: SymmetricCryptoKey;
|
||||
type: SendType = null;
|
||||
text = new SendTextView();
|
||||
@@ -82,7 +82,7 @@ export class SendView implements View {
|
||||
}
|
||||
|
||||
return Object.assign(new SendView(), json, {
|
||||
key: Utils.fromB64ToArray(json.key)?.buffer,
|
||||
key: Utils.fromB64ToArray(json.key),
|
||||
cryptoKey: SymmetricCryptoKey.fromJSON(json.cryptoKey),
|
||||
text: SendTextView.fromJSON(json.text),
|
||||
file: SendFileView.fromJSON(json.file),
|
||||
|
||||
@@ -241,7 +241,7 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
key: SymmetricCryptoKey
|
||||
): Promise<[EncString, EncArrayBuffer]> {
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
// cryptographic secure pseudorandom number generator (CSPRNG)
|
||||
|
||||
type CsprngArray = Opaque<ArrayBuffer, "CSPRNG">;
|
||||
type CsprngArray = Opaque<Uint8Array, "CSPRNG">;
|
||||
|
||||
type CsprngString = Opaque<string, "CSPRNG">;
|
||||
|
||||
@@ -59,7 +59,7 @@ describe("Cipher Service", () => {
|
||||
it("attachments upload encrypted file contents", async () => {
|
||||
const fileName = "filename";
|
||||
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);
|
||||
|
||||
|
||||
@@ -637,7 +637,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
const encFileName = await this.cryptoService.encrypt(filename, 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(
|
||||
cipher,
|
||||
|
||||
Reference in New Issue
Block a user