Skip to content

Commit

Permalink
add dpop encoding utils
Browse files Browse the repository at this point in the history
alphabetize import and clarify base64 test case

add keypair generation and thumbprint confirmation
  • Loading branch information
jshawl committed Feb 16, 2024
1 parent 28aa77f commit 569d30f
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 CryptoKey = {|
publicKey: mixed,
privateKey: mixed,
|};

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

// 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("keypair generation", () => {
it("memoizes the keypair", 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 569d30f

Please sign in to comment.