Last active
October 23, 2024 19:53
-
-
Save philholden/50120652bfe0498958fd5926694ba354 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// paste in console of any https site to run (e.g. this page) | |
// sample arguments for registration | |
// https://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html#authentication-response-message-success | |
var createCredentialDefaultArgs = { | |
publicKey: { | |
// Relying Party (a.k.a. - Service): | |
rp: { | |
name: "Acme" | |
}, | |
// User: | |
user: { | |
id: new Uint8Array(16), | |
name: "[email protected]", | |
displayName: "John P. Smith" | |
}, | |
pubKeyCredParams: [{ | |
type: "public-key", | |
alg: -7 | |
}], | |
attestation: "direct", | |
timeout: 60000, | |
challenge: new Uint8Array([ // must be a cryptographically random number sent from a server | |
0x8C, 0x0A, 0x26, 0xFF, 0x22, 0x91, 0xC1, 0xE9, 0xB9, 0x4E, 0x2E, 0x17, 0x1A, 0x98, 0x6A, 0x73, | |
0x71, 0x9D, 0x43, 0x48, 0xD5, 0xA7, 0x6A, 0x15, 0x7E, 0x38, 0x94, 0x52, 0x77, 0x97, 0x0F, 0xEF | |
]).buffer | |
} | |
}; | |
// sample arguments for login | |
var getCredentialDefaultArgs = { | |
publicKey: { | |
timeout: 60000, | |
// allowCredentials: [newCredential] // see below | |
challenge: new Uint8Array([ // must be a cryptographically random number sent from a server | |
0x79, 0x50, 0x68, 0x71, 0xDA, 0xEE, 0xEE, 0xB9, 0x94, 0xC3, 0xC2, 0x15, 0x67, 0x65, 0x26, 0x22, | |
0xE3, 0xF3, 0xAB, 0x3B, 0x78, 0x2E, 0xD5, 0x6F, 0x81, 0x26, 0xE2, 0xA6, 0x01, 0x7D, 0x74, 0x50 | |
]).buffer | |
}, | |
}; | |
// register / create a new credential | |
var cred = await navigator.credentials.create(createCredentialDefaultArgs) | |
console.log("NEW CREDENTIAL", cred); | |
// normally the credential IDs available for an account would come from a server | |
// but we can just copy them from above... | |
var idList = [{ | |
id: cred.rawId, | |
transports: ["usb", "nfc", "ble"], | |
type: "public-key" | |
}]; | |
getCredentialDefaultArgs.publicKey.allowCredentials = idList; | |
var assertation = await navigator.credentials.get(getCredentialDefaultArgs); | |
console.log("ASSERTION", assertation); | |
// verify signature on server | |
var signature = await assertation.response.signature; | |
console.log("SIGNATURE", signature) | |
var clientDataJSON = await assertation.response.clientDataJSON; | |
console.log("clientDataJSON", clientDataJSON) | |
var authenticatorData = new Uint8Array(await assertation.response.authenticatorData); | |
console.log("authenticatorData", authenticatorData) | |
var clientDataHash = new Uint8Array(await crypto.subtle.digest("SHA-256", clientDataJSON)); | |
console.log("clientDataHash", clientDataHash) | |
// concat authenticatorData and clientDataHash | |
var signedData = new Uint8Array(authenticatorData.length + clientDataHash.length); | |
signedData.set(authenticatorData); | |
signedData.set(clientDataHash, authenticatorData.length); | |
console.log("signedData", signedData) | |
// import key | |
var key = await crypto.subtle.importKey( | |
// The getPublicKey() operation thus returns the credential public key as a SubjectPublicKeyInfo. See: | |
// | |
// https://w3c.github.io/webauthn/#sctn-public-key-easy | |
// | |
// crypto.subtle can import the spki format: | |
// | |
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey | |
"spki", // "spki" Simple Public Key Infrastructure rfc2692 | |
cred.response.getPublicKey(), | |
{ | |
// these are the algorithm options | |
// await cred.response.getPublicKeyAlgorithm() // returns -7 | |
// -7 is ES256 with P-256 // search -7 in https://w3c.github.io/webauthn | |
// the W3C webcrypto docs: | |
// | |
// https://www.w3.org/TR/WebCryptoAPI/#informative-references (scroll down a bit) | |
// | |
// ES256 corrisponds with the following AlgorithmIdentifier: | |
name: "ECDSA", | |
namedCurve: "P-256", | |
hash: { name: "SHA-256" } | |
}, | |
false, //whether the key is extractable (i.e. can be used in exportKey) | |
["verify"] //"verify" for public key import, "sign" for private key imports | |
); | |
// check signature with public key and signed data | |
var verified = await crypto.subtle.verify( | |
{ name: "ECDSA", namedCurve: "P-256", hash: { name: "SHA-256" } }, | |
key, | |
signature, | |
signedData.buffer | |
); | |
// verified is false I want it to be true | |
console.log('verified', verified) |
It wasn't the OS after all, sorry. I'm not sure why, but I now managed to produce the same through Chrome's virtual authenticator. Thanks anyway 🙂
I've put everything into a couple of highly-specific low-level libraries as well as a brief demo (without all the irrelevant boilerplate and cruft that the other ones I found carry).
I hope they can be helpful to others too. Any feedback is appreciated.
https://github.com/tjconcept/webauthn-tools
https://github.com/tjconcept/webauthn-json
https://github.com/tjconcept/webauthn-demo
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@tjconcept I'm on windows 11. I'm surprised that OS would change how Chrome's virtual authenticator works, but I suppose it's possible.
In any case, if you'd like to provide a snippet of code (standalone, whatever) that I can run for you, and send you results, I'm happy to help with that.