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.63k stars 137 forks source link

Registration doesn't work on Android, verifyAttestation AndroidSafetyNet fail #174

Closed bricous closed 2 years ago

bricous commented 2 years ago

First, thank you very much for your project.

I cant register my Google pixel on android 12. I get this error :

Attestation alg "-257" did not match metadata auth algs [-7] (SafetyNet).

All is ok with keys or Window Hello.

Registration parameters are :

  rp: { name: 'masked', id: 'masked' },
  user: {
    id: 'masked',
    name: masked',
   },
  pubKeyCredParams: [
    { alg: -7, type: 'public-key' },
    { alg: -257, type: 'public-key' }
   ],
  timeout: 60000,
  attestation: 'direct',
  authenticatorSelection: {
   userVerification: 'required',
   requireResidentKey: false
 }
extensions: { credProps: true, uvm: true }

Than you for your help Best regards Bruno.

MasterKale commented 2 years ago

Can you confirm that you're leveraging MetadataService in your RP? And can you please include the response returned from startRegistration() so I can see the authenticator's aaguid? If you drop the response into https://debugger.simplewebauthn.dev/ it'll be easy to see and you can easily link it here.

Based on the error I'd guess Android chose to sign the response with RS256 (-257), but FIDO metadata for Android says that it should have only used ES256 (-7). If that's the case then the issue is with Android and/or the FIDO metadata service 🤔

bricous commented 2 years ago

Thank you for your quick answer. Yes, i use MetadataService.

Here is a part of attestation : (I don't paste all because i am not sure what is private)

"attestationObject": {
      "fmt": "android-safetynet",
      "attStmt": {
        "response": {
          "header": {
            "alg": "RS256",
---------
"authData": {
        "rpIdHash": "-------------------------------------------",
        "flags": {
          "userPresent": true,
          "userVerified": true,
          "attestedData": true,
          "extensionData": false
        },
        "counter": 0,
        "aaguid": "b93fd961-f2e6-462f-b122-82002247de78",
        "credentialID": "------------------------------------------------------",
        "credentialPublicKey": "------------------------------------------------------",
        "parsedCredentialPublicKey": {
          "keyType": "EC2 (2)",
          "algorithm": "ES256 (-7)",
          "curve": 1,
          "x": "---------------------------------------------------------------",
          "y": "---------------------------------------------------------------"
        }
      }

It seems you are right..

aseigler commented 2 years ago

That's incredibly odd. The only place you ever see RS256 normally is Windows Hello.

MasterKale commented 2 years ago

Yeah, this is a weird one. My gut tells me without metadata the registration response would have validated just fine. But since you're enforcing the greater authenticator scrutiny via FIDO MDS you'll have situations like this that require you to decide how to proceed...I can't say what the "best" thing is to do here because it really comes down to your application's security model.

aseigler commented 2 years ago

I think the thing here is the attestation can be signed with RS256, but the authenticator signature is supposed to be Es256...the MDS entry is about the authenticator signature, not the attestation statement. Just my from the hip observation.

MasterKale commented 2 years ago

I suspect this might be a legitimate issue with server, then. I'm probably naively comparing against the metadata the attestation statement algorithm, not the authenticator signature. I'll dig deeper into the code tonight and see if something doesn't jump out at me that I can fix.

MasterKale commented 2 years ago

@bricous I think you've reported a legitimate issue with @simplewebauthn/server. Looking at how I implemented metadata checks, specifically verifyAttestationWithMetadata() I'm always comparing the attestation statement's alg (or equivalent, for example HEADER.alg for "android-safetynet") against the authenticationAlgorithms present in a matching metadata statement.

As @aseigler pointed out authenticationAlgorithms should be compared against the hashing algorithm I use when I call verifySignature(). In most cases this means SHA256, as that's what verifySignature() defaults to. But I'll have to look more into this tomorrow because it's not obvious what I'm part of the registration response I'm supposed to compare instead to pass a value like -7 to match an algorithm ID included in a metadata statement.

bricous commented 2 years ago

Sorry to be so boring... I unplugged Metadataservice as suggested, but now verifyRegistrationResponse -> Object.verifyAttestationAndroidSafetyNet throws this : Error: Intermediate certificate is not yet valid or expired (SafetyNet)

bricous commented 2 years ago

I succeded ! to register with my Pixel but attestationType has to be 'none'. If 'direct' or indirect it fails.

MasterKale commented 2 years ago

@bricous Can you provide a full registration response when you get the "Error: Intermediate certificate is not yet valid or expired (SafetyNet)" error? There's nothing in there that will compromise your identity in any way, maintaining user privacy is one of the core tenets of WebAuthn, and I can't do anything with a credential anyway since it'll be bound to the origin you register it on!

I want that response, though, because I'm wondering why an intermediate certificate may be invalid. I'm having a hard time believing Android is producing expired intermediate certs...it may actually be the case that the GlobalSign R2 root cert finally expired and it's time to remove it from the library.

MasterKale commented 2 years ago

Can you try something for me? When you're initializing your server can you set up SettingsService with just the GlobalSign_Root_CA cert (the cert isn't exported in a convenient way so I just copy-pasted it into the code sample)?

import { SettingsService } from '@simplewebauthn/server';

/**
 * GlobalSign Root CA
 *
 * Downloaded from https://pki.goog/roots.pem
 *
 * Valid until 2028-01-28 @ 04:00 PST
 *
 * SHA256 Fingerprint
 * EB:D4:10:40:E4:BB:3E:C7:42:C9:E3:81:D3:1E:F2:A4:1A:48:B6:68:5C:96:E7:CE:F3:C1:DF:6C:D4:33:1C:99
 */
const GlobalSign_Root_CA = `-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----
`;

SettingsService.setRootCertificates({
  identifier: 'android-safetynet',
  certificates: [GlobalSign_Root_CA],
});

And then try registering again with attestation: 'direct' set so it generated an attestation statement. This'll confirm a hunch that it might actually be the expired GlobalSign R2 cert that's causing registration verification to fail.

bricous commented 2 years ago

After adding GlobalSign_Root_CA cert, i can register with attestation: 'direct'. Bravo!

MasterKale commented 2 years ago

Alright, then I can fix this with a couple of changes to server:

MasterKale commented 2 years ago

Oh wait, I can't also forget the metadata-related changes I need to make too:

MasterKale commented 2 years ago

@bricous Just to follow up, I've merged fixes for the first two bullet points above, and I've identified and am working on a fix for the third bullet point. The solution to the third bullet point in particular will fix the original issue that prompted you to create this issue. None of it is available yet - they'll all go out as @simplewebauthn/server@4.4.0 when I get the third fix merged.

MasterKale commented 2 years ago

@bricous Good news! I just published @simplewebauthn/server@4.4.0 with all of the fixes above! Your Android device should now be able to complete attestation: 'direct' registration with MetadataService initialized!