Skip to content

Commit

Permalink
add DPoP keygen and thumbprint (#186)
Browse files Browse the repository at this point in the history
  • Loading branch information
jshawl authored Feb 22, 2024
1 parent 28aa77f commit c97c16f
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 0 deletions.
39 changes: 39 additions & 0 deletions src/dpop.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
/* @flow */
/* eslint-disable promise/no-native, no-restricted-globals */

type KeyPair = {|
privateKey: mixed,
publicKey: mixed,
|};

type GenerateKeyPair = () => Promise<KeyPair>;

// https://datatracker.ietf.org/doc/html/rfc7518#section-3.1
const KEY_OPTIONS = {
create: {
name: "ECDSA",
namedCurve: "P-256",
},
extractable: false,
usages: ["sign", "verify"],
};

let keyPair;
export const generateKeyPair: GenerateKeyPair = async () => {
if (!keyPair) {
const { create, extractable, usages } = KEY_OPTIONS;
const { publicKey, privateKey } = await window.crypto.subtle.generateKey(
create,
extractable,
usages
);

keyPair = keyPair || { publicKey, privateKey };
}

return keyPair;
};

export const stringToBytes = (string: string): Uint8Array => {
return new Uint8Array([...string].map((c) => c.charCodeAt(0)));
};
Expand Down Expand Up @@ -28,4 +61,10 @@ export const sha256 = async (string: string): Promise<string> => {
return base64encodeUrlSafe(binaryString);
};

export const jsonWebKeyThumbprint = async (jwk: Object): Promise<string> => {
// https://datatracker.ietf.org/doc/html/rfc7638#section-3.2
const { crv, e, kty, n, x, y } = jwk;
return await sha256(JSON.stringify({ crv, e, kty, n, x, y }));
};

/* eslint-enable promise/no-native, no-restricted-globals */
28 changes: 28 additions & 0 deletions src/dpop.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
base64decodeUrlSafe,
base64encodeUrlSafe,
bytesToString,
generateKeyPair,
jsonWebKeyThumbprint,
sha256,
stringToBytes,
} from "./dpop";
Expand Down Expand Up @@ -44,4 +46,30 @@ describe("DPoP", () => {
expect.assertions(1);
});
});
describe("key pair generation", () => {
it("memoizes the key pair", async () => {
const { publicKey: publicKey1 } = await generateKeyPair();
const jwk1 = await window.crypto.subtle.exportKey("jwk", publicKey1);
const { publicKey: publicKey2 } = await generateKeyPair();
const jwk2 = await window.crypto.subtle.exportKey("jwk", publicKey2);
expect(jwk1.x).toBeTruthy();
expect(jwk1).toStrictEqual(jwk2);
});
});
describe("JSON Web Key Thumbprint", () => {
it("generates a correct thumbprint", async () => {
// testing a known JSON Web Key and its thumbprint from:
// https://datatracker.ietf.org/doc/html/rfc7638#section-3.1
const key = {
kty: "RSA",
n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
e: "AQAB",
alg: "RS256",
kid: "2011-04-29",
};
expect(await jsonWebKeyThumbprint(key)).toBe(
"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
);
});
});
});

0 comments on commit c97c16f

Please sign in to comment.