1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-12 14:34:02 +00:00

Include aes gcm encryption

key connector will always provide the asym keys and the clients will encapsulate a key and encrypt communications with it.
This commit is contained in:
Matt Gibson
2024-11-18 14:09:25 -08:00
parent 6a7c05ae12
commit 8ccf0b77ac
4 changed files with 122 additions and 1 deletions

View File

@@ -270,6 +270,47 @@ describe("WebCrypto Function Service", () => {
});
});
describe("aes encrypt GCM mode", () => {
it("should successfully encrypt data", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();
const iv = makeStaticByteArray(12);
const key = makeStaticByteArray(32);
const data = Utils.fromUtf8ToArray("EncryptMe!");
const encValue = await cryptoFunctionService.aesGcmEncrypt(data, iv, key);
expect(encValue).toEqual(
new Uint8Array(
Buffer.concat([Utils.fromB64ToArray("Amy1abyVtlboYFBtLnDAzAwAgb3Qg2m4fMo="), iv]),
),
);
});
it("should successfully encrypt with aad", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();
const iv = makeStaticByteArray(12);
const key = makeStaticByteArray(32);
const data = Utils.fromUtf8ToArray("EncryptMe!");
const aad = Utils.fromUtf8ToArray("aad");
const encValue = await cryptoFunctionService.aesGcmEncrypt(data, iv, key, aad);
expect(encValue).toEqual(
new Uint8Array(
Buffer.concat([Utils.fromB64ToArray("Amy1abyVtlboYJTbBTRtNtA4JtxBgjhhSCE="), iv]),
),
);
});
it("should successfully encrypt and then decrypt data", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();
const iv = makeStaticByteArray(12);
const key = makeStaticByteArray(32);
const value = "EncryptMe!";
const data = Utils.fromUtf8ToArray(value);
const encAndIv = new Uint8Array(await cryptoFunctionService.aesGcmEncrypt(data, iv, key));
const envValue = encAndIv.slice(0, encAndIv.length - 12);
const decValue = await cryptoFunctionService.aesDecrypt(envValue, iv, key, "gcm");
expect(Utils.fromBufferToUtf8(decValue)).toBe(value);
});
});
describe("aesDecryptFast CBC mode", () => {
it("should successfully decrypt data", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();

View File

@@ -234,6 +234,26 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
return new Uint8Array(buffer);
}
async aesGcmEncrypt(
data: Uint8Array,
iv: Uint8Array,
key: Uint8Array,
additionalData?: Uint8Array,
): Promise<Uint8Array> {
const impKey = await this.subtle.importKey("raw", key, { name: "AES-GCM" } as any, false, [
"encrypt",
]);
const buffer = await this.subtle.encrypt(
{ name: "AES-GCM", iv: iv, tagLength: 128, additionalData },
impKey,
data,
);
const result = new Uint8Array(buffer.byteLength + iv.byteLength);
result.set(new Uint8Array(buffer), 0);
result.set(iv, buffer.byteLength);
return result;
}
aesDecryptFastParameters(
data: string,
iv: string,

View File

@@ -187,6 +187,47 @@ describe("NodeCrypto Function Service", () => {
});
});
describe("aes encrypt GCM mode", () => {
it("should successfully encrypt data", async () => {
const cryptoFunctionService = new NodeCryptoFunctionService();
const iv = makeStaticByteArray(12);
const key = makeStaticByteArray(32);
const data = Utils.fromUtf8ToArray("EncryptMe!");
const encValue = await cryptoFunctionService.aesGcmEncrypt(data, iv, key);
expect(encValue).toEqual(
new Uint8Array(
Buffer.concat([Utils.fromB64ToArray("Amy1abyVtlboYFBtLnDAzAwAgb3Qg2m4fMo="), iv]),
),
);
});
it("should successfully encrypt with aad", async () => {
const cryptoFunctionService = new NodeCryptoFunctionService();
const iv = makeStaticByteArray(12);
const key = makeStaticByteArray(32);
const data = Utils.fromUtf8ToArray("EncryptMe!");
const aad = Utils.fromUtf8ToArray("aad");
const encValue = await cryptoFunctionService.aesGcmEncrypt(data, iv, key, aad);
expect(encValue).toEqual(
new Uint8Array(
Buffer.concat([Utils.fromB64ToArray("Amy1abyVtlboYJTbBTRtNtA4JtxBgjhhSCE="), iv]),
),
);
});
it("should successfully encrypt and then decrypt data", async () => {
const cryptoFunctionService = new NodeCryptoFunctionService();
const iv = makeStaticByteArray(12);
const key = makeStaticByteArray(32);
const value = "EncryptMe!";
const data = Utils.fromUtf8ToArray(value);
const encAndIv = new Uint8Array(await cryptoFunctionService.aesGcmEncrypt(data, iv, key));
const envValue = encAndIv.slice(0, encAndIv.length - 12);
const decValue = await cryptoFunctionService.aesDecrypt(envValue, iv, key, "gcm");
expect(Utils.fromBufferToUtf8(decValue)).toBe(value);
});
});
describe("aesDecryptFast CBC mode", () => {
it("should successfully decrypt data", async () => {
const nodeCryptoFunctionService = new NodeCryptoFunctionService();

View File

@@ -163,6 +163,25 @@ export class NodeCryptoFunctionService implements CryptoFunctionService {
return Promise.resolve(this.toUint8Buffer(encBuf));
}
async aesGcmEncrypt(
data: Uint8Array,
iv: Uint8Array,
key: Uint8Array,
additionalData?: Uint8Array,
): Promise<Uint8Array> {
const nodeData = this.toNodeBuffer(data);
const nodeIv = this.toNodeBuffer(iv);
const nodeKey = this.toNodeBuffer(key);
const cipher = crypto.createCipheriv("aes-256-gcm", nodeKey, nodeIv, { authTagLength: 16 });
if (additionalData != null) {
const nodeAdditionalData = this.toNodeBuffer(additionalData);
cipher.setAAD(nodeAdditionalData);
}
const encBuf = Buffer.concat([cipher.update(nodeData), cipher.final()]);
const tag = cipher.getAuthTag();
return Promise.resolve(this.toUint8Buffer(Buffer.concat([encBuf, tag, nodeIv])));
}
aesDecryptFastParameters(
data: string,
iv: string,
@@ -318,7 +337,7 @@ export class NodeCryptoFunctionService implements CryptoFunctionService {
if (typeof value === "string") {
buf = Utils.fromUtf8ToArray(value);
} else {
buf = value;
buf = new Uint8Array(value);
}
return buf;
}