-
-
Notifications
You must be signed in to change notification settings - Fork 160
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation for Apple anonymous attestation
- Loading branch information
Showing
7 changed files
with
155 additions
and
3 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Security.Cryptography; | ||
using System.Security.Cryptography.X509Certificates; | ||
using Asn1; | ||
using Fido2NetLib.Objects; | ||
using PeterO.Cbor; | ||
|
||
namespace Fido2NetLib | ||
{ | ||
internal class Apple : AttestationVerifier | ||
{ | ||
public static byte[] AppleAttestationExtensionBytes(X509ExtensionCollection exts) | ||
{ | ||
foreach (var ext in exts) | ||
{ | ||
if (ext.Oid.Value.Equals("1.2.840.113635.100.8.2")) // AppleAttestationRecordOid | ||
{ | ||
var appleAttestationASN = AsnElt.Decode(ext.RawData); | ||
appleAttestationASN.CheckConstructed(); | ||
appleAttestationASN.CheckTag(AsnElt.SEQUENCE); | ||
appleAttestationASN.CheckNumSub(1); | ||
|
||
var sequence = appleAttestationASN.GetSub(0); | ||
sequence.CheckConstructed(); | ||
sequence.CheckNumSub(1); | ||
|
||
var context = sequence.GetSub(0); | ||
context.CheckPrimitive(); | ||
context.CheckTag(AsnElt.OCTET_STRING); | ||
|
||
return context.GetOctetString(); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
public override (AttestationType, X509Certificate2[]) Verify() | ||
{ | ||
// 1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields. | ||
// (handled in base class) | ||
|
||
// 2. Verify x5c is a valid certificate chain starting from the credCert to the Apple WebAuthn root certificate. | ||
// https://www.apple.com/certificateauthority/Apple_WebAuthn_Root_CA.pem | ||
var appleWebAuthnRoots = new string[] { | ||
"MIICEjCCAZmgAwIBAgIQaB0BbHo84wIlpQGUKEdXcTAKBggqhkjOPQQDAzBLMR8w" + | ||
"HQYDVQQDDBZBcHBsZSBXZWJBdXRobiBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJ" + | ||
"bmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMB4XDTIwMDMxODE4MjEzMloXDTQ1MDMx" + | ||
"NTAwMDAwMFowSzEfMB0GA1UEAwwWQXBwbGUgV2ViQXV0aG4gUm9vdCBDQTETMBEG" + | ||
"A1UECgwKQXBwbGUgSW5jLjETMBEGA1UECAwKQ2FsaWZvcm5pYTB2MBAGByqGSM49" + | ||
"AgEGBSuBBAAiA2IABCJCQ2pTVhzjl4Wo6IhHtMSAzO2cv+H9DQKev3//fG59G11k" + | ||
"xu9eI0/7o6V5uShBpe1u6l6mS19S1FEh6yGljnZAJ+2GNP1mi/YK2kSXIuTHjxA/" + | ||
"pcoRf7XkOtO4o1qlcaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUJtdk" + | ||
"2cV4wlpn0afeaxLQG2PxxtcwDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA" + | ||
"MGQCMFrZ+9DsJ1PW9hfNdBywZDsWDbWFp28it1d/5w2RPkRX3Bbn/UbDTNLx7Jr3" + | ||
"jAGGiQIwHFj+dJZYUJR786osByBelJYsVZd2GbHQu209b5RCmGQ21gpSAk9QZW4B" + | ||
"1bWeT0vT"}; | ||
|
||
var trustPath = X5c.Values | ||
.Select(x => new X509Certificate2(x.GetByteString())) | ||
.ToArray(); | ||
|
||
var appleWebAuthnRootCerts = appleWebAuthnRoots | ||
.Select(x => new X509Certificate2(Convert.FromBase64String(x))) | ||
.ToArray(); | ||
|
||
if (!CryptoUtils.ValidateTrustChain(trustPath, appleWebAuthnRootCerts)) | ||
throw new Fido2VerificationException("Invalid certificate chain in Apple attestation"); | ||
|
||
// 3. Concatenate authenticatorData and clientDataHash to form nonceToHash. | ||
var nonceToHash = Data; | ||
|
||
// 4. Perform SHA-256 hash of nonceToHash to produce nonce. | ||
var nonce = CryptoUtils.GetHasher(HashAlgorithmName.SHA256).ComputeHash(nonceToHash); | ||
|
||
// 5. Verify nonce matches the value of the extension with OID ( 1.2.840.113635.100.8.2 ) in credCert. | ||
var credCert = trustPath[0]; | ||
if (!nonce.SequenceEqual(AppleAttestationExtensionBytes(credCert.Extensions))) | ||
throw new Fido2VerificationException("Mismatch between nonce and credCert attestation extension in Apple attestation"); | ||
|
||
// 6. Verify credential public key matches the Subject Public Key of credCert. | ||
var coseAlg = CredentialPublicKey[CBORObject.FromObject(COSE.KeyCommonParameter.Alg)].AsInt32(); | ||
var cpk = new CredentialPublicKey(credCert, coseAlg); | ||
|
||
if (!cpk.GetBytes().SequenceEqual(AuthData.AttestedCredentialData.CredentialPublicKey.GetBytes())) | ||
throw new Fido2VerificationException("Credential public key in Apple attestation does not match subject public key of credCert"); | ||
|
||
// 7. If successful, return implementation-specific values representing attestation type Anonymous CA and attestation trust path x5c. | ||
return (AttestationType.Basic, trustPath); | ||
} | ||
} | ||
} |
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using fido2_net_lib.Test; | ||
using PeterO.Cbor; | ||
|
||
namespace Test.Attestation | ||
{ | ||
public class Apple : Fido2Tests.Attestation | ||
{ | ||
public Apple() | ||
{ | ||
_attestationObject = CBORObject.NewMap().Add("fmt", "apple"); | ||
} | ||
} | ||
} |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"status": "ok", | ||
"errorMessage": "", | ||
"rp": { | ||
"name": "Fido2 test", | ||
"id": "6cc3c9e7967a.ngrok.io" | ||
}, | ||
"user": { | ||
"name": "KXDmnFe45QYAngSfMY9R", | ||
"id": "KXDmnFe45QYAngSfMY9R", | ||
"displayName": "Eleanor Duchene" | ||
}, | ||
"challenge": "kOwMvE2mQO6ou0B0jjD0VA", | ||
"pubKeyCredParams": [ | ||
{ | ||
"type": "public-key", | ||
"alg": -7 | ||
}, | ||
{ | ||
"type": "public-key", | ||
"alg": -257 | ||
} | ||
], | ||
"timeout": 60000, | ||
"attestation": "direct", | ||
"authenticatorSelection": null, | ||
"excludeCredentials": [] | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"rawId": "U5cxFNxLbU9-SAi1K7k9atYwXhghkAMbxpL__VPtBlw", | ||
"id": "U5cxFNxLbU9-SAi1K7k9atYwXhghkAMbxpL__VPtBlw", | ||
"response": { | ||
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoia093TXZFMm1RTzZvdTBCMGpqRDBWQSIsIm9yaWdpbiI6Imh0dHBzOi8vNmNjM2M5ZTc5NjdhLm5ncm9rLmlvIn0", | ||
"attestationObject": "o2NmbXRlYXBwbGVnYXR0U3RtdKJjYWxnJmN4NWOCWQJIMIICRDCCAcmgAwIBAgIGAXUCfWGDMAoGCCqGSM49BAMCMEgxHDAaBgNVBAMME0FwcGxlIFdlYkF1dGhuIENBIDExEzARBgNVBAoMCkFwcGxlIEluYy4xEzARBgNVBAgMCkNhbGlmb3JuaWEwHhcNMjAxMDA3MDk0NjEyWhcNMjAxMDA4MDk1NjEyWjCBkTFJMEcGA1UEAwxANjEyNzZmYzAyZDNmZThkMTZiMzNiNTU0OWQ4MTkyMzZjODE3NDZhODNmMmU5NGE2ZTRiZWUxYzcwZjgxYjViYzEaMBgGA1UECwwRQUFBIENlcnRpZmljYXRpb24xEzARBgNVBAoMCkFwcGxlIEluYy4xEzARBgNVBAgMCkNhbGlmb3JuaWEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAR5_lkIu1EpyAk4t1TATSs0DvpmFbmHaYv1naTlPqPm_vsD2qEnDVgE6KthwVqsokNcfb82nXHKFcUjsABKG3W3o1UwUzAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB_wQEAwIE8DAzBgkqhkiG92NkCAIEJjAkoSIEIJxgAhVAs-GYNN_jfsYkRcieGylPeSzka5QTwyMO84aBMAoGCCqGSM49BAMCA2kAMGYCMQDaHBjrI75xAF7SXzyF5zSQB_Lg9PjTdyye-w7stiqy84K6lmo8d3fIptYjLQx81bsCMQCvC8MSN-aewiaU0bMsdxRbdDerCJJj3xJb3KZwloevJ3daCmCcrZrAPYfLp2kDOshZAjgwggI0MIIBuqADAgECAhBWJVOVx6f7QOviKNgmCFO2MAoGCCqGSM49BAMDMEsxHzAdBgNVBAMMFkFwcGxlIFdlYkF1dGhuIFJvb3QgQ0ExEzARBgNVBAoMCkFwcGxlIEluYy4xEzARBgNVBAgMCkNhbGlmb3JuaWEwHhcNMjAwMzE4MTgzODAxWhcNMzAwMzEzMDAwMDAwWjBIMRwwGgYDVQQDDBNBcHBsZSBXZWJBdXRobiBDQSAxMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEgy6HLyYUkYECJbn1_Na7Y3i19V8_ywRbxzWZNHX9VJBE35v-GSEXZcaaHdoFCzjUUINAGkNPsk0RLVbD4c-_y5iR_sBpYIG--Wy8d8iN3a9Gpa7h3VFbWvqrk76cCyaRo2YwZDASBgNVHRMBAf8ECDAGAQH_AgEAMB8GA1UdIwQYMBaAFCbXZNnFeMJaZ9Gn3msS0Btj8cbXMB0GA1UdDgQWBBTrroLE_6GsW1HUzyRhBQC-Y713iDAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIxAN2LGjSBpfrZ27TnZXuEHhRMJ7dbh2pBhsKxR1dQM3In7-VURX72SJUMYy5cSD5wwQIwLIpgRNwgH8_lm8NNKTDBSHhR2WDtanXx60rKvjjNJbiX0MgFvvDH94sHpXHG6A4HaGF1dGhEYXRhWJhWHo8_bWPQzAMKYRIrGXu__PkMUfuqHM4RH7Jea4WDgkUAAAAAAAAAAAAAAAAAAAAAAAAAAAAUomGfdaNI-cYgWrq2klNk97zkcg-lAQIDJiABIVggef5ZCLtRKcgJOLdUwE0rNA76ZhW5h2mL9Z2k5T6j5v4iWCD7A9qhJw1YBOirYcFarKJDXH2_Np1xyhXFI7AASht1tw"}, | ||
"type": "public-key" | ||
} |