MasterKale / SimpleWebAuthn

WebAuthn, Simplified. A collection of TypeScript-first libraries for simpler WebAuthn integration. Supports modern browsers, Node, Deno, and more.
https://simplewebauthn.dev
MIT License
1.62k stars 137 forks source link

Server throws error `Credential public key was missing numeric alg` #185

Closed versoworks closed 2 years ago

versoworks commented 2 years ago

When calling verifyRegistrationResponse(options) on the server, it throws an error: Credential public key was missing numeric alg.

These are the options passed in to startRegistration:

{ "challenge": "RnZ2It6b4y_IoSTeVxZXGmPKsOFAjgf28hhw4XNRSvI", "rp": { "name": "localhost test", "id": "localhost" }, "user": { "id": "61577eb5cb412affc183ed1f", "name": "some@email.addr", "displayName": "some@email.addr" }, "pubKeyCredParams": [ { "alg": -7, "type": "public-key" }, { "alg": -257, "type": "public-key" } ], "timeout": 60000, "attestation": "none", "excludeCredentials": [], "authenticatorSelection": { "authenticatorAttachment": "platform", "userVerification": "required", "requireResidentKey": false }, "extensions": null }

Here's the response from startRegistration:

{ "id": "AfaNwKs71uoY_YlspvubZqaEt_LYr2-YQkQKG-OlF6g7PabeurAU6fLrNBM96XOAT_S3WNTIhBmUh5XVfSM4oWTz4KVXMMISTfmNKNIE0zEiZ3ZUOLbqDuw", "rawId": "AfaNwKs71uoY_YlspvubZqaEt_LYr2-YQkQKG-OlF6g7PabeurAU6fLrNBM96XOAT_S3WNTIhBmUh5XVfSM4oWTz4KVXMMISTfmNKNIE0zEiZ3ZUOLbqDuw", "response": { "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjdSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFYi-f063OAAI1vMYKZIsLJfHwVQMAWQH2jcCrO9bqGP2JbKb7m2amhLfy2K9vmEJEChvjpReoOz2m3rqwFOny6zQTPelzgE_0t1jUyIQZlIeV1X0jOKFk8-ClVzDCEk35jSjSBNMxImd2VDi26g7spQECAyYgASFYINTQXIxmC-uQZRryAtgpfpVSJ0BcZbPhWQBW2bt8-cDbIlggaiEFMOfj9zEKWdCOKRwr69tB8Pcvo_2bwVNkrHC1JYA", "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiUm5aMkl0NmI0eV9Jb1NUZVZ4WlhHbVBLc09GQWpnZjI4aGh3NFhOUlN2SSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCIsImNyb3NzT3JpZ2luIjpmYWxzZX0" }, "type": "public-key", "clientExtensionResults": {}, "transports": [ "internal" ] }

Using @simplewebauthn/browser v4.1.0 & @simplewebauthn/server v4.4.0. Tested on MacBook Pro with TouchID, MacOS Monterey v12.3. Tested with Microsoft Edge v99.0.1150.39, Safari v15.4 (17613.1.17.1.6), virtual WebAuthn on Chrome.

MasterKale commented 2 years ago

@versoworks Thank you for opening this issue. It's odd to me that verifyRegistrationResponse() errors out here, I see an alg of -7 in the debugger.

Nothing immediately looks off, I'll dig into this and report back.

Edit: @versoworks can you also include how you're calling verifyRegistrationResponse()? I called it thusly and everything verified fine...

const verification = await verifyRegistrationResponse({
  credential: {
    "id": "AfaNwKs71uoY_YlspvubZqaEt_LYr2-YQkQKG-OlF6g7PabeurAU6fLrNBM96XOAT_S3WNTIhBmUh5XVfSM4oWTz4KVXMMISTfmNKNIE0zEiZ3ZUOLbqDuw",
    "rawId": "AfaNwKs71uoY_YlspvubZqaEt_LYr2-YQkQKG-OlF6g7PabeurAU6fLrNBM96XOAT_S3WNTIhBmUh5XVfSM4oWTz4KVXMMISTfmNKNIE0zEiZ3ZUOLbqDuw",
    "response": {
      "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjdSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFYi-f063OAAI1vMYKZIsLJfHwVQMAWQH2jcCrO9bqGP2JbKb7m2amhLfy2K9vmEJEChvjpReoOz2m3rqwFOny6zQTPelzgE_0t1jUyIQZlIeV1X0jOKFk8-ClVzDCEk35jSjSBNMxImd2VDi26g7spQECAyYgASFYINTQXIxmC-uQZRryAtgpfpVSJ0BcZbPhWQBW2bt8-cDbIlggaiEFMOfj9zEKWdCOKRwr69tB8Pcvo_2bwVNkrHC1JYA",
      "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiUm5aMkl0NmI0eV9Jb1NUZVZ4WlhHbVBLc09GQWpnZjI4aGh3NFhOUlN2SSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
    },
    "type": "public-key",
    "clientExtensionResults": {},
    "transports": ["internal"],
  },
  expectedChallenge: "RnZ2It6b4y_IoSTeVxZXGmPKsOFAjgf28hhw4XNRSvI",
  expectedOrigin: 'http://localhost:3000',
  expectedRPID: 'localhost',
});

console.log(verification.verified); // true
versoworks commented 2 years ago

@MasterKale just a follow up to this; I've been testing on a different server and can now successfully register a device, however, when authenticating, verifyAuthenticationResponse throws an error;

/node_modules/@simplewebauthn/server/dist/helpers/convertPublicKeyToPEM.js:17
    const kty = struct.get(convertCOSEtoPKCS_1.COSEKEYS.kty);
                       ^

TypeError: Cannot read properties of undefined (reading 'get')
    at Object.convertPublicKeyToPEM [as default] (/node_modules/@simplewebauthn/server/dist/helpers/convertPublicKeyToPEM.js:17:24)
    at Object.verifyAuthenticationResponse (//node_modules/@simplewebauthn/server/dist/authentication/verifyAuthenticationResponse.js:133:54)
    at /index.js:67:31

I've checked the options with your online debugger and see no issues. I've also tested with various Node.js versions (from v10.x to v16.x) with all yielding the same error.

Edit: @MasterKale these are the options passed in to verifyAuthenticationResponse:

{
 "authenticator":{"base64CredentialID":"AVdn1NZ8WwLqz4eMo1-jaYaKr-iNyRa7TtJldlIk5f7UJDwVTwrB6EcqMOCQoMtUb7lIXU4PLsApJHI1YBKlCF1jde0QSZX2eeaOziFNcMacGqqR7DWjpZg",
                  "base64PublicKey":"pQECAyYgASFYIJdyqrM2EgNbNsbJP50ttHFEPAzVN7SxBrxSNH5lSS_dIlgg4rUYm69oCjEcts8WRvG-wkcarVi0RS37P80i61ryRto",
                  "counter":1647508282,
                  "credentialID":{"data":[1,87,103,212,214,124,91,2,234,207,135,140,163,95,163,105,134,138,175,232,141,201,22,187,78,210,101,118,82,36,229,254,212,36,60,21,79,10,193,232,71,42,48,224,144,160,203,84,111,185,72,93,78,15,46,192,41,36,114,53,96,18,165,8,93,99,117,237,16,73,149,246,121,230,142,206,33,77,112,198,156,26,170,145,236,53,163,165,152],"type":"Buffer"},
                  "credentialPublicKey":{"data":[165,1,2,3,38,32,1,33,88,32,151,114,170,179,54,18,3,91,54,198,201,63,157,45,180,113,68,60,12,213,55,180,177,6,188,82,52,126,101,73,47,221,34,88,32,226,181,24,155,175,104,10,49,28,182,207,22,70,241,190,194,71,26,173,88,180,69,45,251,63,205,34,235,90,242,70,218],"type":"Buffer"},
                  "devId":"6232fb3b865daceeb00cf5a0",
                  "transports":["internal"]},
 "credential":{
    "clientExtensionResults":{},
    "id":"AVdn1NZ8WwLqz4eMo1-jaYaKr-iNyRa7TtJldlIk5f7UJDwVTwrB6EcqMOCQoMtUb7lIXU4PLsApJHI1YBKlCF1jde0QSZX2eeaOziFNcMacGqqR7DWjpZg",
    "rawId":"AVdn1NZ8WwLqz4eMo1-jaYaKr-iNyRa7TtJldlIk5f7UJDwVTwrB6EcqMOCQoMtUb7lIXU4PLsApJHI1YBKlCF1jde0QSZX2eeaOziFNcMacGqqR7DWjpZg",
    "response":{
        "authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFYjOo2A",
        "clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiX1c5OTJsWGRiYkZEcVE2VEVSOUVxTnpCazhTT2N3ODYtU2ZKQXY2bm42YyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
        "signature":"MEYCIQCjsqbTUD8tS2XK6HsRHbCy3vByRm04bX0uG8qbSvpr5AIhAK_oPh-RGhhaerPWroW6wCn0Ddf8a9saR41dXSKBrGzW",
        "userHandle":"61577eb5cb412affc183ed1f"
    },
    "type":"public-key"
 },
 "expectedChallenge":"_W992lXdbbFDqQ6TER9EqNzBk8SOcw86-SfJAv6nn6c",
 "expectedOrigin":"http://localhost:3000",
 "expectedRPID":"localhost",
 "fidoUserVerification":"required"
}
versoworks commented 2 years ago

@versoworks Thank you for opening this issue. It's odd to me that verifyRegistrationResponse() errors out here, I see an alg of -7 in the debugger.

Nothing immediately looks off, I'll dig into this and report back.

Edit: @versoworks can you also include how you're calling verifyRegistrationResponse()? I called it thusly and everything verified fine...

const verification = await verifyRegistrationResponse({
  credential: {
    "id": "AfaNwKs71uoY_YlspvubZqaEt_LYr2-YQkQKG-OlF6g7PabeurAU6fLrNBM96XOAT_S3WNTIhBmUh5XVfSM4oWTz4KVXMMISTfmNKNIE0zEiZ3ZUOLbqDuw",
    "rawId": "AfaNwKs71uoY_YlspvubZqaEt_LYr2-YQkQKG-OlF6g7PabeurAU6fLrNBM96XOAT_S3WNTIhBmUh5XVfSM4oWTz4KVXMMISTfmNKNIE0zEiZ3ZUOLbqDuw",
    "response": {
      "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjdSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFYi-f063OAAI1vMYKZIsLJfHwVQMAWQH2jcCrO9bqGP2JbKb7m2amhLfy2K9vmEJEChvjpReoOz2m3rqwFOny6zQTPelzgE_0t1jUyIQZlIeV1X0jOKFk8-ClVzDCEk35jSjSBNMxImd2VDi26g7spQECAyYgASFYINTQXIxmC-uQZRryAtgpfpVSJ0BcZbPhWQBW2bt8-cDbIlggaiEFMOfj9zEKWdCOKRwr69tB8Pcvo_2bwVNkrHC1JYA",
      "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiUm5aMkl0NmI0eV9Jb1NUZVZ4WlhHbVBLc09GQWpnZjI4aGh3NFhOUlN2SSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
    },
    "type": "public-key",
    "clientExtensionResults": {},
    "transports": ["internal"],
  },
  expectedChallenge: "RnZ2It6b4y_IoSTeVxZXGmPKsOFAjgf28hhw4XNRSvI",
  expectedOrigin: 'http://localhost:3000',
  expectedRPID: 'localhost',
});

console.log(verification.verified); // true

I call verifyRegistrationResponse() in the same manner; using await.

Edit: I think the issue may be caused by limited memory on the server, hence why I'm testing on another server.

MasterKale commented 2 years ago
{
  "base64CredentialID": "AVdn1NZ8WwLqz4eMo1-jaYaKr-iNyRa7TtJldlIk5f7UJDwVTwrB6EcqMOCQoMtUb7lIXU4PLsApJHI1YBKlCF1jde0QSZX2eeaOziFNcMacGqqR7DWjpZg",
  "base64PublicKey": "pQECAyYgASFYIJdyqrM2EgNbNsbJP50ttHFEPAzVN7SxBrxSNH5lSS_dIlgg4rUYm69oCjEcts8WRvG-wkcarVi0RS37P80i61ryRto",
  "counter": 1647508282,
  "credentialID": {
    "data": [
      1, 87, 103, 212, 214, 124, 91, 2, 234, 207, 135, 140, 163, 95, 163, 105,
      134, 138, 175, 232, 141, 201, 22, 187, 78, 210, 101, 118, 82, 36, 229,
      254, 212, 36, 60, 21, 79, 10, 193, 232, 71, 42, 48, 224, 144, 160, 203,
      84, 111, 185, 72, 93, 78, 15, 46, 192, 41, 36, 114, 53, 96, 18, 165, 8,
      93, 99, 117, 237, 16, 73, 149, 246, 121, 230, 142, 206, 33, 77, 112, 198,
      156, 26, 170, 145, 236, 53, 163, 165, 152
    ],
    "type": "Buffer"
  },
  "credentialPublicKey": {
    "data": [
      165, 1, 2, 3, 38, 32, 1, 33, 88, 32, 151, 114, 170, 179, 54, 18, 3, 91,
      54, 198, 201, 63, 157, 45, 180, 113, 68, 60, 12, 213, 55, 180, 177, 6,
      188, 82, 52, 126, 101, 73, 47, 221, 34, 88, 32, 226, 181, 24, 155, 175,
      104, 10, 49, 28, 182, 207, 22, 70, 241, 190, 194, 71, 26, 173, 88, 180,
      69, 45, 251, 63, 205, 34, 235, 90, 242, 70, 218
    ],
    "type": "Buffer"
  },
  "devId": "6232fb3b865daceeb00cf5a0",
  "transports": ["internal"]
}

@versoworks The issue is almost certainly that your authenticator argument to verifyAuthenticationResponse() is passing in Objects for credentialID and credentialPublicKey. Did you run JSON.stringify() on them before storing these values after registration? In any case you need to massage those values into Buffers - if you pass those data int arrays into Buffer.from() and pass those into the method instead I'm almost certain it will solve your problem.

versoworks commented 2 years ago

@MasterKale correct; I figured it out and resolved it thusly;

authenticator.credentialPublicKey = Buffer.from(authenticator.credentialPublicKey.data);

Cheers for your help, and apologies for the hassle.