gl-sergei / u2f-token

u2f token firmware for stm32f103 and efm32hg boards
GNU General Public License v3.0
339 stars 56 forks source link

no x.509v3 U2F attestation certificate #23

Closed SecTec closed 5 years ago

SecTec commented 5 years ago

Hi,

I made a BluePill U2F device and tried it on the following webauthn demo sites: yubico.com webauthn.io webauthn.org

When registering at webauthn.org I got the following error message:

Error: registration failed: expected U2F attestation certificate to be x.509v3
Registration error: Error: registration failed: expected U2F attestation certificate to be x.509v3

Below is the whole debug output:

WEBAUTHN DEBUG TERMINAL
-----------------------
REGISTER START:

Sending Message to Server:
>>>>>>>>>>>>>>>>
{"username":"test1234","displayName":"test1234"}
>>>>>>>>>>>>>>>>

Received Message from Server:
<<<<<<<< [ STATUS 200 ] <<<<<<<<
{"status":"ok","errorMessage":"","rp":{"name":"WebAuthn.org"},"user":{"name":"test1234","id":"JVBq7f5oIwpbrRU0bTxvlg","displayName":"test1234"},"challenge":"h8vscUKHr-QQvln7YaEoeIe691IuIFbgyLlWeEsA_FewJ-iLc0tuq81pPvIJtxFEgiG7daloLN4LJ-rV8BWK9A","pubKeyCredParams":[{"type":"public-key","alg":-7},{"type":"public-key","alg":-257}],"timeout":60000,"attestation":"direct"}
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

WebAuthn navigator.credentials.create() options:
[CreateOptions] {
    rp: {
        name: "WebAuthn.org",
    },
    user: {
        name: "test1234",
        id: [ArrayBuffer] (16 bytes)
            25 50 6A ED FE 68 23 0A 5B AD 15 34 6D 3C 6F 96,
        displayName: "test1234",
    },
    challenge: [ArrayBuffer] (64 bytes)
        87 CB EC 71 42 87 AF E4 10 BE 59 FB 61 A1 28 78
        87 BA F7 52 2E 20 56 E0 C8 B9 56 78 4B 00 FC 57
        B0 27 E8 8B 73 4B 6E AB CD 69 3E F2 09 B7 11 44
        82 21 BB 75 A9 68 2C DE 0B 27 EA D5 F0 15 8A F4,
    pubKeyCredParams: [
        {
            type: "public-key",
            alg: -7,
        },
        {
            type: "public-key",
            alg: -257,
        },
    ],
    timeout: 60000,
    attestation: "direct",
}
WAITING FOR USER PRESENCE...
USER PRESENCE DONE.
WebAuthn navigator.credentials.create() result:
[CredentialAttestation] {
    rawId: [ArrayBuffer] (64 bytes)
        D5 12 7C 54 27 26 82 E9 1F 56 6E 74 F9 97 52 1B
        5A A7 DF 7C B2 D7 03 2E 68 60 B3 08 D8 26 4E 23
        77 6C 2B D8 60 91 BB 15 13 DF 0F 38 8E 21 68 47
        7F 51 3E 8F F2 BB 17 48 DC D8 1C 1C 7F BA 0F A2,
    id: [ArrayBuffer] (64 bytes)
        D5 12 7C 54 27 26 82 E9 1F 56 6E 74 F9 97 52 1B
        5A A7 DF 7C B2 D7 03 2E 68 60 B3 08 D8 26 4E 23
        77 6C 2B D8 60 91 BB 15 13 DF 0F 38 8E 21 68 47
        7F 51 3E 8F F2 BB 17 48 DC D8 1C 1C 7F BA 0F A2,
    response: {
        clientDataJSON: [ArrayBuffer] (159 bytes)
            7B 22 63 68 61 6C 6C 65 6E 67 65 22 3A 22 68 38
            76 73 63 55 4B 48 72 2D 51 51 76 6C 6E 37 59 61
            45 6F 65 49 65 36 39 31 49 75 49 46 62 67 79 4C
            6C 57 65 45 73 41 5F 46 65 77 4A 2D 69 4C 63 30
            74 75 71 38 31 70 50 76 49 4A 74 78 46 45 67 69
            47 37 64 61 6C 6F 4C 4E 34 4C 4A 2D 72 56 38 42
            57 4B 39 41 22 2C 22 6F 72 69 67 69 6E 22 3A 22
            68 74 74 70 73 3A 2F 2F 77 65 62 61 75 74 68 6E
            2E 6F 72 67 22 2C 22 74 79 70 65 22 3A 22 77 65
            62 61 75 74 68 6E 2E 63 72 65 61 74 65 22 7D,
        attestationObject: [ArrayBuffer] (611 bytes)
            A3 63 66 6D 74 68 66 69 64 6F 2D 75 32 66 67 61
            74 74 53 74 6D 74 A2 63 73 69 67 58 48 30 46 02
            21 00 C8 27 7D 02 24 F1 66 FF D9 23 54 86 A1 23
            63 38 91 82 E0 DD 38 FE F2 51 F9 FF 3C 46 51 41
            7A 91 02 21 00 F3 39 29 7F DD 25 B9 A5 2C EC AF
            0E 22 DF 32 1D 77 6A 13 E8 A5 5A F6 C0 DB 41 1C
            D0 04 F4 D4 7C 63 78 35 63 81 59 01 27 30 82 01
            23 30 81 C9 02 14 6C F9 0C 9E 22 BD DF 03 66 B5
            B9 63 29 EC 86 CF 80 D2 13 50 30 0A 06 08 2A 86
            48 CE 3D 04 03 02 30 14 31 12 30 10 06 03 55 04
            03 0C 09 55 32 46 20 54 6F 6B 65 6E 30 1E 17 0D
            31 39 30 33 30 37 32 30 31 32 32 39 5A 17 0D 32
            39 30 33 30 34 32 30 31 32 32 39 5A 30 14 31 12
            30 10 06 03 55 04 03 0C 09 55 32 46 20 54 6F 6B
            65 6E 30 59 30 13 06 07 2A 86 48 CE 3D 02 01 06
            08 2A 86 48 CE 3D 03 01 07 03 42 00 04 E4 EA 69
            5E A7 36 D6 EC FF F9 9B 2B B1 F0 39 91 CB 11 97
            29 5B E4 30 08 28 DA 17 FF CC F4 D0 5B 05 93 82
            1A 1E 2F 57 30 9A 2A C1 56 37 0D 6A 95 BE 62 39
            55 3A 7A C7 55 FB F9 B5 5D 2A B8 AA 87 30 0A 06
            08 2A 86 48 CE 3D 04 03 02 03 49 00 30 46 02 21
            00 CD EA 3B 6D 2E 03 78 A1 2A E4 B2 B2 F7 4C 50
            2E B4 86 CC A3 6B 8E 10 2B 64 8A 84 4D 4E E9 FD
            89 02 21 00 94 B7 20 3E E7 EB 5C D8 7F 74 E5 35
            20 A4 F0 45 0D 87 93 F6 3F CE 8F CD 88 10 E6 45
            C7 B0 76 EB 68 61 75 74 68 44 61 74 61 58 C4 95
            69 08 8F 1E CE E3 23 29 54 03 5D BD 10 D7 CA E3
            91 30 5A 27 51 B5 59 BB 8F D7 CB B2 29 BD D4 41
            00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
            00 00 00 00 00 40 D5 12 7C 54 27 26 82 E9 1F 56
            6E 74 F9 97 52 1B 5A A7 DF 7C B2 D7 03 2E 68 60
            B3 08 D8 26 4E 23 77 6C 2B D8 60 91 BB 15 13 DF
            0F 38 8E 21 68 47 7F 51 3E 8F F2 BB 17 48 DC D8
            1C 1C 7F BA 0F A2 A5 01 02 03 26 20 01 21 58 20
            32 F4 4C 31 A2 AC 13 A5 68 6D 00 3A 29 8C 02 19
            AD 65 B8 2F 74 CB B9 FE 2D 4D BE 3C 1C 3D 71 0E
            22 58 20 D1 C8 23 59 24 FD DA D0 8C 32 32 75 49
            F3 F4 7E 51 C0 17 CD 74 7D B5 2E CB 44 6A F7 FD
            AF 04 64,
    },
}

Sending Message to Server:
>>>>>>>>>>>>>>>>
{"rawId":"1RJ8VCcmgukfVm50-ZdSG1qn33yy1wMuaGCzCNgmTiN3bCvYYJG7FRPfDziOIWhHf1E-j_K7F0jc2Bwcf7oPog","id":"1RJ8VCcmgukfVm50-ZdSG1qn33yy1wMuaGCzCNgmTiN3bCvYYJG7FRPfDziOIWhHf1E-j_K7F0jc2Bwcf7oPog","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiJoOHZzY1VLSHItUVF2bG43WWFFb2VJZTY5MUl1SUZiZ3lMbFdlRXNBX0Zld0otaUxjMHR1cTgxcFB2SUp0eEZFZ2lHN2RhbG9MTjRMSi1yVjhCV0s5QSIsIm9yaWdpbiI6Imh0dHBzOi8vd2ViYXV0aG4ub3JnIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9","attestationObject":"o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAMgnfQIk8Wb_2SNUhqEjYziRguDdOP7yUfn_PEZRQXqRAiEA8zkpf90luaUs7K8OIt8yHXdqE-ilWvbA20Ec0AT01HxjeDVjgVkBJzCCASMwgckCFGz5DJ4ivd8DZrW5Yynshs-A0hNQMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMMCVUyRiBUb2tlbjAeFw0xOTAzMDcyMDEyMjlaFw0yOTAzMDQyMDEyMjlaMBQxEjAQBgNVBAMMCVUyRiBUb2tlbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOTqaV6nNtbs__mbK7HwOZHLEZcpW-QwCCjaF__M9NBbBZOCGh4vVzCaKsFWNw1qlb5iOVU6esdV-_m1XSq4qocwCgYIKoZIzj0EAwIDSQAwRgIhAM3qO20uA3ihKuSysvdMUC60hsyja44QK2SKhE1O6f2JAiEAlLcgPufrXNh_dOU1IKTwRQ2Hk_Y_zo_NiBDmRcewdutoYXV0aERhdGFYxJVpCI8ezuMjKVQDXb0Q18rjkTBaJ1G1WbuP18uyKb3UQQAAAAAAAAAAAAAAAAAAAAAAAAAAAEDVEnxUJyaC6R9WbnT5l1IbWqfffLLXAy5oYLMI2CZOI3dsK9hgkbsVE98POI4haEd_UT6P8rsXSNzYHBx_ug-ipQECAyYgASFYIDL0TDGirBOlaG0AOimMAhmtZbgvdMu5_i1NvjwcPXEOIlgg0cgjWST92tCMMjJ1SfP0flHAF810fbUuy0Rq9_2vBGQ"}}
>>>>>>>>>>>>>>>>

Received Message from Server:
<<<<<<<< [ STATUS 400 ] <<<<<<<<
{"status":"failed","errorMessage":"registration failed: expected U2F attestation certificate to be x.509v3"}
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Error: registration failed: expected U2F attestation certificate to be x.509v3
Registration error: Error: registration failed: expected U2F attestation certificate to be x.509v3
REGISTER DONE.
gl-sergei commented 5 years ago

The requirement for attestation certificate to be v3 was not present in original U2F specification. It was defined in FIDO U2F Authenticator Transports Extension for U2F 1.2 and now it is a part of FIDO2 (which is out of scope of this project). The gen.sh script simply generates shortest possible certificate and it works on every site with U2F support. It also conforms to U2F 1.0. However, you can generate your own x.509 v3 certificate using following script:

#!/bin/bash

set -e

cat > opnssl.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_ca # The extentions to add to the self signed cert
req_extensions = v3_req
x509_extensions = usr_cert
[usr_cert]
basicConstraints=CA:FALSE
nsCertType = client, server, email
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth, codeSigning, emailProtection
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
[v3_ca]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints = CA:true
[v3_req]
extendedKeyUsage = serverAuth, clientAuth, codeSigning, emailProtection
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[req_distinguished_name]
EOF

# generate key and self-signed certificate
openssl ecparam -genkey -name prime256v1 -out attestation_key.pem
openssl req -new -sha256 -key attestation_key.pem -out csr.csr -subj "/C=US/ST=New York City/O=S. Security/OU=Authenticator Attestation/CN=FIDO2 Token"
openssl req -config opnssl.cnf -x509 -sha256 -days 3650 -key attestation_key.pem -in csr.csr -out attestation.pem

# convert to der
openssl x509 -outform der -in attestation.pem -out attestation.der
openssl ec -in attestation_key.pem -outform der -out attestation_key.der

# generate C code
python dump-der.py > certificates.c || ( rm certificates.c && exit 1 )
gl-sergei commented 5 years ago

Make sure to provide your own subject.

SecTec commented 5 years ago

Hi,

when I try to deploy the attestation certificate with ./certtool init I get the following error:

Trying to initialize device HIDDevice:
    0005:0011:00 | 16d0:e90 | unknown | U2F-token (STM32) | 1.00
    release_number: 256
    usage_page: 0
    usage: 0
    interface_number: 0
HID Error: 1

Could the new generated cert be too long for the script to deploy?

And I looked at the U2F specifications. U2F 1.0 and U2F 1.2 are using v3 certificates in their examples. The FIDO2 specification does not define any certificate version.

gl-sergei commented 5 years ago

Could the new generated cert be too long for the script to deploy?

Yes. It could be the case. After some digging I came up with this script to produce short enough v3 certificate with fido u2f transport extension:

#!/bin/bash

set -e

cat > opnssl.cnf <<EOF

[req]
x509_extensions = usr_cert

[usr_cert]
1.3.6.1.4.1.45724.2.1.1=ASN1:FORMAT:BITLIST,BITSTRING:2

EOF

# generate key and self-signed certificate
openssl ecparam -genkey -name prime256v1 -out attestation_key.pem
openssl req -new -sha256 -key attestation_key.pem -out csr.csr -subj "/C=US/CN=U2F Token"
openssl req -config opnssl.cnf -x509 -sha256 -days 3650 -key attestation_key.pem -in csr.csr -out attestation.pem

# convert to der
openssl x509 -outform der -in attestation.pem -out attestation.der
openssl ec -in attestation_key.pem -outform der -out attestation_key.der

# generate C code
python dump-der.py > certificates.c || ( rm certificates.c && exit 1 )

It works on webauthn.org for me.

SecTec commented 5 years ago

It worked on all three demo sites. Thanks.

Now two questions arise:

  1. Will the script make it into the repo? If yes: Will it replace gen.sh or will it be calles gen_v3.sh.
  2. The attestation certificate has the purpose to be identical on all devices from the same manufacturer. And of course for privacy reasons too. Therefore I suggest to include one set of pre generated certificates into your repository. The possibility, that everyone can then attest their devices too, exists. But it would not be a big problem IMHO, because this keypair would become some public DIY device attestation key.
gl-sergei commented 5 years ago

I see no reason to keep an old script. Yes, having unique attestation certificate for each device allows user tracking. I put pre-generated certificate into the repository, but still since the number of users of this firmware is small, they are easy to track.