-
Notifications
You must be signed in to change notification settings - Fork 122
Description
We have to store a public key in the interim time between the result of attestationResult and any subsequent calls to assertionResult. And I'm looking to for the most flexible yet compact storage format, i.e. a format that doesn't lock me into this library if it stops getting maintained and one that plays nice with subtle.importKey() or node:crypto.createPublicKey() in case that ever becomes useful in the future. And we have the added constraint that it has be rendered into a string in PEM format for the assertionResult() call.
It's this later constraint that I find problematic because I could give you the public key in a format that doesn't require a much preprocessing, namely that we encode it to PEM so that you can decode it from PEM.
Given that we have three options for acquiring the public key, namely result.authnrData.get( ___ ) with "credentialPublicKeyPem", "credentialPublicKeyCose", or "credentialPublicKeyJwk" as arguments to the Map call, my current solution is to get the PEM format, strip it down to a base64 string that can be parsed into a Buffer (Uint8Array) and then store the decoded bytes. Jwk would be an option that Subtle supports, but it's not as compact.
Then for assertionResult, I have to do this ugly thing:
const pubKeyStr = publicKeyBuffer.toString("base64");
const pubArr = pubKeyStr.match(/.{1,64}/g);
if (!pubArr) { throw .... }
const pemStr = "-----BEGIN PUBLIC KEY-----\n" + pubArr.join("\n") + "\n-----END PUBLIC KEY-----\n";
await fidoLib.assertionResult({
id: new TextEncoder().encode(creds.id).buffer,
rawId: new Uint8Array(creds.rawId),
response: {
clientDataJSON: response.clientDataJSON.toString("base64"),
authenticatorData: new Uint8Array(response.authenticatorData).buffer,
signature: response.signature.toString("base64"),
userHandle: response.userHandle.toString("base64url")
}
}, {
challenge: lastChallenge.toString("base64"),
origin: ...,
factor: "either",
publicKey: pemStr,
prevCounter: ...,
userHandle: response.userHandle.toString("base64url") // already assured of a match with rawId check (not shown)
});As such, I would humbly request that assertResult be a little more open in the formats that it accepts. And some added documentation about what these formats can and should be would have saved me a ton of trial & error combined with digging through and instrumenting the source code. I don't think it would be too difficult to test the inputs for string or buffer types and act accordingly under some documented assumptions.
Side note, raw ArrayBuffers are the worst. With like any other type, I could give you a view of some segment of memory and avoid copying bytes around.