With the very recent Windows Anniversary update, Edge now supports biometric authentication using Windows Hello (cf. https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/dev-guide/device/web-authentication/ )
I have some samples in C#, PHP and Node.js, and am trying to make it work in Go. The following works in JS (I have hardcoded in the challenge and the key): function parseBase64(s) { s = s.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ''); return new Uint8Array(Array.prototype.map.call(atob(s), function (c) { return c.charCodeAt(0) })); } function concatUint8Array(a1,a2) { var d = new Uint8Array(a1.length + a2.length); d.set(a1); d.set(a2,a1.length); return d; } var credAlgorithm = "RSASSA-PKCS1-v1_5"; var id,authenticatorData,signature,hash; webauthn.getAssertion("chalenge").then(function(assertion) { id = assertion.credential.id; authenticatorData = assertion.authenticatorData; signature = assertion.signature; return crypto.subtle.digest("SHA-256",parseBase64(assertion.clientData )); }).then(function(h) { hash = new Uint8Array(h); var publicKey = "{\"kty\":\"RSA\",\"alg\":\"RS256\",\"ext\":false,\"n\":\"mEqGJwp0GL1oVwjRikkNfzd-Rkpb7vIbGodwQkTDsZT4_UE02WDaRa-PjxzL4lPZ4rUpV5SqVxM25aEIeGkEOR_8Xoqx7lpNKNOQs3E_o8hGBzQKpGcA7de678LeAUZdJZcnnQxXYjNf8St3aOIay7QrPoK8wQHEvv8Jqg7O1-pKEKCIwSKikCFHTxLhDDRo31KFG4XLWtLllCfEO6vmQTseT-_8OZPBSHOxR9VhIbY7VBhPq-PeAWURn3G52tQX-802waGmKBZ4B87YtEEPxCNbyyvlk8jRKP1KIrI49bgJhAe5Mow3yycQEnGuPDwLzmJ1lU6I4zgkyL1jI3Ghsw\",\"e\":\"AQAB\"}" ; return crypto.subtle.importKey("jwk",JSON.parse(publicKey),credAlgorithm ,false,["verify"]); }).then(function(key) { return crypto.subtle.verify({name:credAlgorithm, hash: { name: "SHA-256" }},key,parseBase64(signature),concatUint8Array(parseBase64(authenticatorData ),hash)); }).then(function(result) { console.log("ID=" + id + "\r\n" + result); }).catch(function(err) { console.log('got err: ', err); }); In go I have the following code, meant to match the above JS code (req is a struct with string ): func webauthnSigninConversion(g string) ([]byte, error) { g = strings.Replace(g, "-", "+", -1) g = strings.Replace(g, "_", "/", -1) switch(len(g) % 4) { // Pad with trailing '='s case 0: // No pad chars in this case case 2: // Two pad chars g = g + "==" case 3: // One pad char g = g + "="; default: return nil, fmt.Errorf("invalid string in public key") } b, err := base64.StdEncoding.DecodeString(g) if err != nil { return nil, err } return b, nil } clientData, err := webauthnSigninConversion(req.ClientData) if err != nil { return err } authenticatorData, err := webauthnSigninConversion(req.AuthenticatorData) if err != nil { return err } signature, err := webauthnSigninConversion(req.Signature) if err != nil { return err } publicKey := "{\"kty\":\"RSA\",\"alg\":\"RS256\",\"ext\":false,\"n\":\"mEqGJwp0GL1oVwjRikkNfzd-Rkpb7vIbGodwQkTDsZT4_UE02WDaRa-PjxzL4lPZ4rUpV5SqVxM25aEIeGkEOR_8Xoqx7lpNKNOQs3E_o8hGBzQKpGcA7de678LeAUZdJZcnnQxXYjNf8St3aOIay7QrPoK8wQHEvv8Jqg7O1-pKEKCIwSKikCFHTxLhDDRo31KFG4XLWtLllCfEO6vmQTseT-_8OZPBSHOxR9VhIbY7VBhPq-PeAWURn3G52tQX-802waGmKBZ4B87YtEEPxCNbyyvlk8jRKP1KIrI49bgJhAe5Mow3yycQEnGuPDwLzmJ1lU6I4zgkyL1jI3Ghsw\",\"e\":\"AQAB\"}" // this is really from a db, not hardcoded // load json from public key, extract modulus and public exponent obj := strings.Replace(publicKey, "\\", "", -1) // remove escapes var k struct { N string `json:"n"` E string `json:"e"` } if err = json.Unmarshal([]byte(obj), &k); err != nil { return err } n, err := webauthnSigninConversion(k.N) if err != nil { return err } e, err := webauthnSigninConversion(k.E) if err != nil { return err } pk := &rsa.PublicKey{ N: new(big.Int).SetBytes(n), // modulus E: int(new(big.Int).SetBytes(e).Uint64()), // public exponent } hash := sha256.Sum256(clientData) // Create data buffer to verify signature over b := append(authenticatorData, hash[:]...) if err = rsa.VerifyPKCS1v15(pk, crypto.SHA256, b, signature); err != nil { return err } // if no error, signature matches This code fails with "crypto/rsa: input must be hashed message". If I change to using "hash[:]" instead of "b" in rsa.VerifyPKCS1v15, it fails with "crypto/rsa: verification error". The reason I believe I need to combine authenticatorData and has is because that is what happens in the C# and PHP sample codes (cf, https://github.com/adrianba/fido-snippets/blob/master/csharp/app.cs , https://github.com/adrianba/fido-snippets/blob/master/php/fido-authenticator.php ). Maybe Go does it a different way? I have printed the byte arrays in JS and Go, and verified that clientData, signatureData, authenticatorData and hash (and combined array) have the exact same values. I have not been able to extract the n and e fields from JS after creating the public key. Any help would be greatly appreciated. /Alexander -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.