1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-12 22:44:11 +00:00

Prefer key encapsulation to shared key derivation

This commit is contained in:
Matt Gibson
2024-11-18 10:25:09 -08:00
parent a59512daa9
commit 6a7c05ae12
5 changed files with 0 additions and 657 deletions

View File

@@ -105,33 +105,4 @@ export abstract class CryptoFunctionService {
* Do not use this for generating encryption keys. Use aesGenerateKey or rsaGenerateKeyPair instead.
*/
abstract randomBytes(length: number): Promise<CsprngArray>;
/**
* Generate a random asymmetric key pair using the given algorithm on the given curve
*
* x25519: Curve25519, does not use the curve parameter and will throw if passed in a non-nullish curve
* ecdh: Elliptic Curve Diffie-Hellman, uses the curve parameter to specify the curve to use
* P-256: NIST P-256
* P-384: NIST P-384
* P-521: NIST P-521
* @param algorithm the algorithm to use
* @param curve the curve to use for ecdh, or undefined for x25519
* @returns a promise that resolves to an object containing the private and public keys
*/
abstract diffieHellmanGenerateKeyPair(
algorithm: "x25519" | "ecdh",
curve: undefined | "P-256" | "P-384" | "P-521",
): Promise<{ keyPair: CryptoKeyPair; publicKey: Uint8Array }>;
/**
* Derive a shared key from a private key and an external public key
*
* @param privateKey the private key to use
* @param publicKey the public key to use
* @returns a promise that resolves to the shared key bits
*/
abstract deriveSharedKeyBits(
privateKey: CryptoKey,
publicKeyRaw: Uint8Array,
algorithm: "x25519" | "ecdh",
curve: undefined | "P-256" | "P-384" | "P-521",
): Promise<Uint8Array>;
}

View File

@@ -413,292 +413,6 @@ describe("WebCrypto Function Service", () => {
expect(key.slice(0, 32)).not.toEqual(key.slice(32, 64));
});
});
describe("diffieHellmanGenerateKeyPair", () => {
describe("x25519", () => {
it("generates a key pair", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();
const { keyPair } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"x25519",
undefined,
);
expect(keyPair.privateKey).toBeDefined();
expect(keyPair.publicKey).toBeDefined();
});
it("pre-extracts the public key in raw format", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();
const { keyPair, publicKey } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"x25519",
undefined,
);
const publicFromKeyPair = await crypto.subtle.exportKey("raw", keyPair.publicKey);
expect(publicKey).toEqualBuffer(publicFromKeyPair);
});
});
describe("ecdh", () => {
describe.each(["P-256", "P-384", "P-521"] as const)("curve %s", (curve) => {
it("generates a key pair", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();
const { keyPair } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"ecdh",
curve,
);
expect(keyPair.privateKey).toBeDefined();
expect(keyPair.publicKey).toBeDefined();
});
it("pre-extracts the public key in raw format", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();
const { keyPair, publicKey } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"ecdh",
curve,
);
const publicFromKeyPair = await crypto.subtle.exportKey("raw", keyPair.publicKey);
expect(publicKey).toEqualBuffer(publicFromKeyPair);
// Should be odd
expect(publicKey.byteLength % 2).toBe(1);
// First byte should be 0x04
expect(publicKey[0]).toBe(0x04);
});
});
});
});
describe("diffieHellmanGenerateKeyPair", () => {
describe("x25519", () => {
it("generates a key pair", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();
const { keyPair } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"x25519",
undefined,
);
expect(keyPair.privateKey).toBeDefined();
expect(keyPair.publicKey).toBeDefined();
});
it("pre-extracts the public key in raw format", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();
const { keyPair, publicKey } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"x25519",
undefined,
);
const publicFromKeyPair = await crypto.subtle.exportKey("raw", keyPair.publicKey);
expect(publicKey).toEqual(new Uint8Array(publicFromKeyPair));
});
});
describe("ecdh", () => {
describe.each(["P-256", "P-384", "P-521"] as const)("curve %s", (curve) => {
it("generates a key pair", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();
const { keyPair } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"ecdh",
curve,
);
expect(keyPair.privateKey).toBeDefined();
expect(keyPair.publicKey).toBeDefined();
});
it("pre-extracts the public key in raw format", async () => {
const cryptoFunctionService = getWebCryptoFunctionService();
const { keyPair, publicKey } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"ecdh",
curve,
);
const publicFromKeyPair = await crypto.subtle.exportKey("raw", keyPair.publicKey);
expect(publicKey).toEqual(new Uint8Array(publicFromKeyPair));
// Should be odd
expect(publicKey.byteLength % 2).toBe(1);
// First byte should be 0x04
expect(publicKey[0]).toBe(0x04);
});
});
});
});
describe("deriveSharedKeyBits", () => {
let cryptoFunctionService: WebCryptoFunctionService;
beforeEach(() => {
cryptoFunctionService = getWebCryptoFunctionService();
});
describe("x25519", () => {
const testVectors = Object.freeze({
a: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
crv: "X25519",
d: "dwdtCnMYpX08FsFyUbJmRd9ML4frwJkqsXf7pR25LCo=",
x: "hSDwCYkwp1R0i33ctD73Wg2/Og0mOBr066SpjqqbTmo=",
kty: "OKP",
},
b: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
crv: "X25519",
d: "XasIfmJKikt54X+Lg4AO5m87sSkmGLb9HC+LJ/+I4Os=",
x: "3p7bfXt9wbTTW2HC7OQ1Nz+DQ8hbeGdNrfx+FG+IK08=",
kty: "OKP",
},
expected: "Sl2dW6TOLeFyjjv0gDUPJeB+IclH0Z4zdvCbPB4WF0I", // hex
});
it("computes the correct shared key for a test vector", async () => {
function testVectorToUint8Array(hex: string): Uint8Array {
return new Uint8Array(Buffer.from(hex, "hex"));
}
const aPrivateKey = await crypto.subtle.importKey("jwk", testVectors.a, "x25519", false, [
"deriveBits",
]);
const aPublicKey = new Uint8Array(Buffer.from(testVectors.a.x, "base64"));
const bPrivateKey = await crypto.subtle.importKey("jwk", testVectors.b, "x25519", false, [
"deriveBits",
]);
const bPublicKey = new Uint8Array(Buffer.from(testVectors.b.x, "base64"));
const expectedSharedKey = testVectorToUint8Array(
"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742",
);
const actualAB = await cryptoFunctionService.deriveSharedKeyBits(
aPrivateKey,
bPublicKey,
"x25519",
undefined,
);
const actualBA = await cryptoFunctionService.deriveSharedKeyBits(
bPrivateKey,
aPublicKey,
"x25519",
undefined,
);
expect(actualAB).toEqual(expectedSharedKey);
expect(actualBA).toEqual(expectedSharedKey);
});
});
describe("ecdh", () => {
// from rfc 7027 https://datatracker.ietf.org/doc/html/rfc7027.html#page-6
const testVectors = Object.freeze({
"P-256": {
a: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "slavLyCsXalaOmUgYA-cG4RFNxapMfWK5cnGFZXEsmE",
y: "fR3rqDZX_m9ba5OPD4EM4AecqltCTNyXsGmV6e5UYkk",
crv: "P-256",
d: "HauUPXMuCzvArJpcNJisBzfHq05MVPmHv1Ixd_8Trxk",
},
b: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "UDVHYHeNMuViKX2ixvJfUdRkP2vYaLH5LEtaZj0Sp5U",
y: "ScRWbvlcz4IZc6h4SkUu6ejmho1hOrg3kUgEkpJf4OE",
crv: "P-256",
d: "xquL6bqtpdDaEoZ0h2HDwpXlPdX__w0xrr8VZLAS2wQ",
},
expectedSharedKey: {
x: "89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A18BF2B",
y: "49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E59632504DE",
},
},
"P-384": {
a: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "0vsFRhdWTrrQyXF6MJcB3Lg-JjalCra973eA46nFNBwD4oLbTzpL514ZVNIb5nPG",
y: "_KZHmMem5pmjhKjAmQ2twvwkssOh5ww28ZXBabgOsCF3MYi-ICkvkqdVvdPNCPaM",
crv: "P-384",
d: "V7yD-hx8gep_0pw8nFTYD0pRVkxGGN9Uz1zL1Ynq99oSJBG-mceLkwhXACvTKN2o",
},
b: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "4pgonl58vdZpXrBurolmIw85fAwIY0gMfNRYt1TZWY-kXYz5vKxhi1ycjqotiprR",
y: "CCFnRehKJtFX1-0zJWHcXaIpm09B88rq2nhmCRcc5E9NOdgh8dRyJw63o7c7F7XQ",
crv: "P-384",
d: "40D_ztyfZdBzV3CT8r5JC-SvZfGx4IfUnQNOzbgLcD98zvGcCH04Zs41JCEQ0t-y",
},
},
"P-521": {
a: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "APD7DZbPsfQ-9tDlc1gFnZGQg5seDx59BmKeFDRERULUqr3EYoxpH-8eutJoXaKCTdzdiXWYpG7nEO99Q6CQuR6Q",
y: "ACbcq7utMbQ5XRGwg-O23brBa29Gcaht6TRQvT9k3worJDQlKN-awn8b68HGMb8WEfJe2gFoPAB9D9Cr8sRT5W35",
crv: "P-521",
d: "AVeaaA3VUhbZfz_oJOCZjhg6gqxXjf96YDJ7rawtAnJDHxtLif7LHVQcOHRKiB6dN9al5KdCGhvo8eekMgRcDXPI",
},
b: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "AD_GaHOGgjivKoC4C7L5P-fEUzvEpXKFSi32tmscW4NJqiCWQsekuR0EMNgXTw_7GTlrnsn5CcLHfianF3Way-bk",
y: "AcJO4Pl7_qys5QS0-XP_Wluy5KgBXZIIG5vmmDLK7Vhsl_30IP6_WPA5JkqEOHZs25uTMdEERvcJlw7EAItGrM5t",
crv: "P-521",
d: "ADYRyQSF0aLOcYGukdjtFEe5d_pI8wLoU5kNHXfUPoVDHqK02zC-bDRIjJB8f0VWBZYVs88WXFVabUzYlbFwcSi1",
},
},
});
describe.each(["P-256", "P-384", "P-521"] as const)("curve %s", (curve) => {
it("computes the correct shared key for a test vector", async () => {
const aPrivateKey = await crypto.subtle.importKey(
"jwk",
testVectors[curve].a,
{ name: "ecdh", namedCurve: curve },
true,
["deriveBits"],
);
const aPublicKey = Buffer.concat([
new Uint8Array([0x04]),
Buffer.from(testVectors[curve].a.x, "base64"),
Buffer.from(testVectors[curve].a.y, "base64"),
]);
const bPrivateKey = await crypto.subtle.importKey(
"jwk",
testVectors[curve].b,
{ name: "ecdh", namedCurve: curve },
true,
["deriveBits"],
);
const bPublicKey = Buffer.concat([
new Uint8Array([0x04]),
Buffer.from(testVectors[curve].b.x, "base64"),
Buffer.from(testVectors[curve].b.y, "base64"),
]);
const actualAB = cryptoFunctionService.deriveSharedKeyBits(
aPrivateKey,
bPublicKey,
"ecdh",
curve,
);
const actualBA = cryptoFunctionService.deriveSharedKeyBits(
bPrivateKey,
aPublicKey,
"ecdh",
curve,
);
expect(actualAB).toEqual(actualBA);
});
});
});
});
});
function testPbkdf2(

View File

@@ -400,61 +400,6 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
return new Uint8Array(rawKey) as CsprngArray;
}
async diffieHellmanGenerateKeyPair(
algorithm: "x25519" | "ecdh",
curve: undefined | "P-256" | "P-384" | "P-521",
): Promise<{
keyPair: CryptoKeyPair;
publicKey: Uint8Array;
}> {
if (algorithm === "x25519" && curve != null) {
throw new Error("x25519 does not use the curve parameter.");
}
const keys = await this.subtle.generateKey(
{
name: algorithm,
namedCurve: curve,
},
true,
["deriveKey", "deriveBits"],
);
return {
keyPair: keys,
publicKey: new Uint8Array(await this.subtle.exportKey("raw", keys.publicKey)),
};
}
async deriveSharedKeyBits(
privateKey: CryptoKey,
publicKeyRaw: Uint8Array,
algorithm: "x25519" | "ecdh",
curve: undefined | "P-256" | "P-384" | "P-521",
): Promise<Uint8Array> {
if (algorithm === "x25519" && curve != null) {
throw new Error("x25519 does not use the curve parameter.");
}
const publicKey = await crypto.subtle.importKey(
"raw",
publicKeyRaw,
{ name: algorithm, namedCurve: curve },
true,
[],
);
const dhSecret = await crypto.subtle.deriveBits(
{
name: algorithm,
public: publicKey,
},
privateKey,
256,
);
return new Uint8Array(dhSecret);
}
async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[Uint8Array, Uint8Array]> {
const rsaParams = {
name: "RSA-OAEP",

View File

@@ -325,238 +325,6 @@ describe("NodeCrypto Function Service", () => {
expect(spy).toHaveBeenCalledWith(32);
});
});
describe("diffieHellmanGenerateKeyPair", () => {
describe("x25519", () => {
it("generates a key pair", async () => {
const cryptoFunctionService = new NodeCryptoFunctionService();
const { keyPair } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"x25519",
undefined,
);
expect(keyPair.privateKey).toBeDefined();
expect(keyPair.publicKey).toBeDefined();
});
it("pre-extracts the public key in raw format", async () => {
const cryptoFunctionService = new NodeCryptoFunctionService();
const { keyPair, publicKey } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"x25519",
undefined,
);
const publicFromKeyPair = await crypto.subtle.exportKey("raw", keyPair.publicKey);
expect(publicKey).toEqual(new Uint8Array(publicFromKeyPair));
});
});
describe("ecdh", () => {
describe.each(["P-256", "P-384", "P-521"] as const)("curve %s", (curve) => {
it("generates a key pair", async () => {
const cryptoFunctionService = new NodeCryptoFunctionService();
const { keyPair } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"ecdh",
curve,
);
expect(keyPair.privateKey).toBeDefined();
expect(keyPair.publicKey).toBeDefined();
});
it("pre-extracts the public key in raw format", async () => {
const cryptoFunctionService = new NodeCryptoFunctionService();
const { keyPair, publicKey } = await cryptoFunctionService.diffieHellmanGenerateKeyPair(
"ecdh",
curve,
);
const publicFromKeyPair = await crypto.subtle.exportKey("raw", keyPair.publicKey);
expect(publicKey).toEqual(new Uint8Array(publicFromKeyPair));
// Should be odd
expect(publicKey.byteLength % 2).toBe(1);
// First byte should be 0x04
expect(publicKey[0]).toBe(0x04);
});
});
});
});
describe("deriveSharedKeyBits", () => {
let cryptoFunctionService: NodeCryptoFunctionService;
beforeEach(() => {
cryptoFunctionService = new NodeCryptoFunctionService();
});
describe("x25519", () => {
const testVectors = Object.freeze({
a: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
crv: "X25519",
d: "dwdtCnMYpX08FsFyUbJmRd9ML4frwJkqsXf7pR25LCo=",
x: "hSDwCYkwp1R0i33ctD73Wg2/Og0mOBr066SpjqqbTmo=",
kty: "OKP",
},
b: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
crv: "X25519",
d: "XasIfmJKikt54X+Lg4AO5m87sSkmGLb9HC+LJ/+I4Os=",
x: "3p7bfXt9wbTTW2HC7OQ1Nz+DQ8hbeGdNrfx+FG+IK08=",
kty: "OKP",
},
expected: "Sl2dW6TOLeFyjjv0gDUPJeB+IclH0Z4zdvCbPB4WF0I", // hex
});
it("computes the correct shared key for a test vector", async () => {
function testVectorToUint8Array(hex: string): Uint8Array {
return new Uint8Array(Buffer.from(hex, "hex"));
}
const aPrivateKey = await crypto.subtle.importKey("jwk", testVectors.a, "x25519", false, [
"deriveBits",
]);
const aPublicKey = new Uint8Array(Buffer.from(testVectors.a.x, "base64"));
const bPrivateKey = await crypto.subtle.importKey("jwk", testVectors.b, "x25519", false, [
"deriveBits",
]);
const bPublicKey = new Uint8Array(Buffer.from(testVectors.b.x, "base64"));
const expectedSharedKey = testVectorToUint8Array(
"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742",
);
const actualAB = await cryptoFunctionService.deriveSharedKeyBits(
aPrivateKey,
bPublicKey,
"x25519",
undefined,
);
const actualBA = await cryptoFunctionService.deriveSharedKeyBits(
bPrivateKey,
aPublicKey,
"x25519",
undefined,
);
expect(actualAB).toEqual(expectedSharedKey);
expect(actualBA).toEqual(expectedSharedKey);
});
});
describe("ecdh", () => {
// from rfc 7027 https://datatracker.ietf.org/doc/html/rfc7027.html#page-6
const testVectors = Object.freeze({
"P-256": {
a: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "slavLyCsXalaOmUgYA-cG4RFNxapMfWK5cnGFZXEsmE",
y: "fR3rqDZX_m9ba5OPD4EM4AecqltCTNyXsGmV6e5UYkk",
crv: "P-256",
d: "HauUPXMuCzvArJpcNJisBzfHq05MVPmHv1Ixd_8Trxk",
},
b: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "UDVHYHeNMuViKX2ixvJfUdRkP2vYaLH5LEtaZj0Sp5U",
y: "ScRWbvlcz4IZc6h4SkUu6ejmho1hOrg3kUgEkpJf4OE",
crv: "P-256",
d: "xquL6bqtpdDaEoZ0h2HDwpXlPdX__w0xrr8VZLAS2wQ",
},
expectedSharedKey: {
x: "89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A18BF2B",
y: "49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E59632504DE",
},
},
"P-384": {
a: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "0vsFRhdWTrrQyXF6MJcB3Lg-JjalCra973eA46nFNBwD4oLbTzpL514ZVNIb5nPG",
y: "_KZHmMem5pmjhKjAmQ2twvwkssOh5ww28ZXBabgOsCF3MYi-ICkvkqdVvdPNCPaM",
crv: "P-384",
d: "V7yD-hx8gep_0pw8nFTYD0pRVkxGGN9Uz1zL1Ynq99oSJBG-mceLkwhXACvTKN2o",
},
b: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "4pgonl58vdZpXrBurolmIw85fAwIY0gMfNRYt1TZWY-kXYz5vKxhi1ycjqotiprR",
y: "CCFnRehKJtFX1-0zJWHcXaIpm09B88rq2nhmCRcc5E9NOdgh8dRyJw63o7c7F7XQ",
crv: "P-384",
d: "40D_ztyfZdBzV3CT8r5JC-SvZfGx4IfUnQNOzbgLcD98zvGcCH04Zs41JCEQ0t-y",
},
},
"P-521": {
a: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "APD7DZbPsfQ-9tDlc1gFnZGQg5seDx59BmKeFDRERULUqr3EYoxpH-8eutJoXaKCTdzdiXWYpG7nEO99Q6CQuR6Q",
y: "ACbcq7utMbQ5XRGwg-O23brBa29Gcaht6TRQvT9k3worJDQlKN-awn8b68HGMb8WEfJe2gFoPAB9D9Cr8sRT5W35",
crv: "P-521",
d: "AVeaaA3VUhbZfz_oJOCZjhg6gqxXjf96YDJ7rawtAnJDHxtLif7LHVQcOHRKiB6dN9al5KdCGhvo8eekMgRcDXPI",
},
b: {
key_ops: ["deriveKey", "deriveBits"],
ext: true,
kty: "EC",
x: "AD_GaHOGgjivKoC4C7L5P-fEUzvEpXKFSi32tmscW4NJqiCWQsekuR0EMNgXTw_7GTlrnsn5CcLHfianF3Way-bk",
y: "AcJO4Pl7_qys5QS0-XP_Wluy5KgBXZIIG5vmmDLK7Vhsl_30IP6_WPA5JkqEOHZs25uTMdEERvcJlw7EAItGrM5t",
crv: "P-521",
d: "ADYRyQSF0aLOcYGukdjtFEe5d_pI8wLoU5kNHXfUPoVDHqK02zC-bDRIjJB8f0VWBZYVs88WXFVabUzYlbFwcSi1",
},
},
});
describe.each(["P-256", "P-384", "P-521"] as const)("curve %s", (curve) => {
it("computes the correct shared key for a test vector", async () => {
const aPrivateKey = await crypto.subtle.importKey(
"jwk",
testVectors[curve].a,
{ name: "ecdh", namedCurve: curve },
true,
["deriveBits"],
);
const aPublicKey = Buffer.concat([
new Uint8Array([0x04]),
Buffer.from(testVectors[curve].a.x, "base64"),
Buffer.from(testVectors[curve].a.y, "base64"),
]);
const bPrivateKey = await crypto.subtle.importKey(
"jwk",
testVectors[curve].b,
{ name: "ecdh", namedCurve: curve },
true,
["deriveBits"],
);
const bPublicKey = Buffer.concat([
new Uint8Array([0x04]),
Buffer.from(testVectors[curve].b.x, "base64"),
Buffer.from(testVectors[curve].b.y, "base64"),
]);
const actualAB = cryptoFunctionService.deriveSharedKeyBits(
aPrivateKey,
bPublicKey,
"ecdh",
curve,
);
const actualBA = cryptoFunctionService.deriveSharedKeyBits(
bPrivateKey,
aPublicKey,
"ecdh",
curve,
);
expect(actualAB).toEqual(actualBA);
});
});
});
});
});
function testPbkdf2(

View File

@@ -283,61 +283,6 @@ export class NodeCryptoFunctionService implements CryptoFunctionService {
});
}
async diffieHellmanGenerateKeyPair(
algorithm: "x25519" | "ecdh",
curve: undefined | "P-256" | "P-384" | "P-521",
): Promise<{
keyPair: CryptoKeyPair;
publicKey: Uint8Array;
}> {
if (algorithm === "x25519" && curve != null) {
throw new Error("x25519 does not use the curve parameter.");
}
const keys = await crypto.subtle.generateKey(
{
name: algorithm,
namedCurve: curve,
},
true,
["deriveKey", "deriveBits"],
);
return {
keyPair: keys,
publicKey: new Uint8Array(await crypto.subtle.exportKey("raw", keys.publicKey)),
};
}
async deriveSharedKeyBits(
privateKey: CryptoKey,
publicKeyRaw: Uint8Array,
algorithm: "x25519" | "ecdh",
curve: undefined | "P-256" | "P-384" | "P-521",
): Promise<Uint8Array> {
if (algorithm === "x25519" && curve != null) {
throw new Error("x25519 does not use the curve parameter.");
}
const publicKey = await crypto.subtle.importKey(
"raw",
publicKeyRaw,
{ name: algorithm, namedCurve: curve },
true,
[],
);
const dhSecret = await crypto.subtle.deriveBits(
{
name: algorithm,
public: publicKey,
},
privateKey,
256,
);
return new Uint8Array(dhSecret);
}
aesGenerateKey(bitLength: 128 | 192 | 256 | 512): Promise<CsprngArray> {
return this.randomBytes(bitLength / 8);
}