[WebAuthN] Implement authenticatorMakeCredential
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / ios / LocalAuthenticator.mm
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27
28 #if PLATFORM(IOS)
29
30 #import "InstanceMethodSwizzler.h"
31 #import "PlatformUtilities.h"
32 #import <LocalAuthentication/LocalAuthentication.h>
33 #import <Security/SecItem.h>
34 #import <WebCore/CBORReader.h>
35 #import <WebCore/COSEConstants.h>
36 #import <WebCore/ExceptionData.h>
37 #import <WebCore/LocalAuthenticator.h>
38 #import <WebCore/PublicKeyCredentialCreationOptions.h>
39 #import <wtf/BlockPtr.h>
40 #import <wtf/text/Base64.h>
41 #import <wtf/text/WTFString.h>
42
43 namespace TestWebKitAPI {
44
45 const String testAttestationCertificateBase64 = String() +
46     "MIIB6jCCAZCgAwIBAgIGAWHAxcjvMAoGCCqGSM49BAMCMFMxJzAlBgNVBAMMHkJh" +
47     "c2ljIEF0dGVzdGF0aW9uIFVzZXIgU3ViIENBMTETMBEGA1UECgwKQXBwbGUgSW5j" +
48     "LjETMBEGA1UECAwKQ2FsaWZvcm5pYTAeFw0xODAyMjMwMzM3MjJaFw0xODAyMjQw" +
49     "MzQ3MjJaMGoxIjAgBgNVBAMMGTAwMDA4MDEwLTAwMEE0OUEyMzBBMDIxM0ExGjAY" +
50     "BgNVBAsMEUJBQSBDZXJ0aWZpY2F0aW9uMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMw" +
51     "EQYDVQQIDApDYWxpZm9ybmlhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvCje" +
52     "Pzr6Sg76XMoHuGabPaG6zjpLFL8Zd8/74Hh5PcL2Zq+o+f7ENXX+7nEXXYt0S8Ux" +
53     "5TIRw4hgbfxXQbWLEqM5MDcwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBPAw" +
54     "FwYJKoZIhvdjZAgCBAowCKEGBAR0ZXN0MAoGCCqGSM49BAMCA0gAMEUCIAlK8A8I" +
55     "k43TbvKuYGHZs1DTgpTwmKTBvIUw5bwgZuYnAiEAtuJjDLKbGNJAJFMi5deEBqno" +
56     "pBTCqbfbDJccfyQpjnY=";
57 const String testAttestationIssuingCACertificateBase64 = String() +
58     "MIICIzCCAaigAwIBAgIIeNjhG9tnDGgwCgYIKoZIzj0EAwIwUzEnMCUGA1UEAwwe" +
59     "QmFzaWMgQXR0ZXN0YXRpb24gVXNlciBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJ" +
60     "bmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMB4XDTE3MDQyMDAwNDIwMFoXDTMyMDMy" +
61     "MjAwMDAwMFowUzEnMCUGA1UEAwweQmFzaWMgQXR0ZXN0YXRpb24gVXNlciBTdWIg" +
62     "Q0ExMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMFkw" +
63     "EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoSZ/1t9eBAEVp5a8PrXacmbGb8zNC1X3" +
64     "StLI9YO6Y0CL7blHmSGmjGWTwD4Q+i0J2BY3+bPHTGRyA9jGB3MSbaNmMGQwEgYD" +
65     "VR0TAQH/BAgwBgEB/wIBADAfBgNVHSMEGDAWgBSD5aMhnrB0w/lhkP2XTiMQdqSj" +
66     "8jAdBgNVHQ4EFgQU5mWf1DYLTXUdQ9xmOH/uqeNSD80wDgYDVR0PAQH/BAQDAgEG" +
67     "MAoGCCqGSM49BAMCA2kAMGYCMQC3M360LLtJS60Z9q3vVjJxMgMcFQ1roGTUcKqv" +
68     "W+4hJ4CeJjySXTgq6IEHn/yWab4CMQCm5NnK6SOSK+AqWum9lL87W3E6AA1f2TvJ" +
69     "/hgok/34jr93nhS87tOQNdxDS8zyiqw=";
70 const String testCredentialIdBase64 = "SMSXHngF7hEOsElA73C3RY+8bR4=";
71 const String testES256PrivateKeyBase64 = String() +
72     "BDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749VBJPgqUIwfhWHJ91nb7U" +
73     "PH76c0+WFOzZKslPyyFse4goGIW2R7k9VHLPEZl5nfnBgEVFh5zev+/xpHQIvuq6" +
74     "RQ==";
75 const String testRpId = "localhost";
76 const String testUsername = "username";
77 const uint8_t testUserhandle[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x9};
78
79 RetainPtr<SecKeyRef> getTestKey()
80 {
81     NSDictionary* options = @{
82         (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
83         (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
84         (id)kSecAttrKeySizeInBits: @256,
85     };
86     CFErrorRef errorRef = NULL;
87     auto key = adoptCF(SecKeyCreateWithData(
88         (__bridge CFDataRef)adoptNS([[NSData alloc] initWithBase64EncodedString:testES256PrivateKeyBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get(),
89         (__bridge CFDictionaryRef)options,
90         &errorRef
91     ));
92     EXPECT_FALSE(errorRef);
93
94     return key;
95 }
96
97 void cleanUpKeychain()
98 {
99     // Cleanup the keychain.
100     NSDictionary* deleteQuery = @{
101         (id)kSecClass: (id)kSecClassKey,
102         (id)kSecAttrLabel: testRpId,
103     };
104     SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
105 }
106
107 class LACantEvaluatePolicySwizzler {
108 public:
109     LACantEvaluatePolicySwizzler()
110         : m_swizzler([LAContext class], @selector(canEvaluatePolicy:error:), reinterpret_cast<IMP>(cantEvaluatePolicy))
111     {
112     }
113 private:
114     static BOOL cantEvaluatePolicy()
115     {
116         return NO;
117     }
118     InstanceMethodSwizzler m_swizzler;
119 };
120
121 class LACanEvaluatePolicySwizzler {
122 public:
123     LACanEvaluatePolicySwizzler()
124         : m_swizzler([LAContext class], @selector(canEvaluatePolicy:error:), reinterpret_cast<IMP>(canEvaluatePolicy))
125     {
126     }
127 private:
128     static BOOL canEvaluatePolicy()
129     {
130         return YES;
131     }
132     InstanceMethodSwizzler m_swizzler;
133 };
134
135 class LAEvaluatePolicyFailedSwizzler {
136 public:
137     LAEvaluatePolicyFailedSwizzler()
138         : m_swizzler([LAContext class], @selector(evaluatePolicy:localizedReason:reply:), reinterpret_cast<IMP>(evaluatePolicyFailed))
139     {
140     }
141 private:
142     static void evaluatePolicyFailed(id self, SEL _cmd, LAPolicy policy, NSString * reason, void (^reply)(BOOL success, NSError *error))
143     {
144         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
145             Util::sleep(1); // mimic user interaction delay
146             reply(NO, nil);
147         });
148     }
149     InstanceMethodSwizzler m_swizzler;
150 };
151
152 class LAEvaluatePolicyPassedSwizzler {
153 public:
154     LAEvaluatePolicyPassedSwizzler()
155         : m_swizzler([LAContext class], @selector(evaluatePolicy:localizedReason:reply:), reinterpret_cast<IMP>(evaluatePolicyPassed))
156     {
157     }
158 private:
159     static void evaluatePolicyPassed(id self, SEL _cmd, LAPolicy policy, NSString * reason, void (^reply)(BOOL success, NSError *error))
160     {
161         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
162             Util::sleep(1); // mimic user interaction delay
163             reply(YES, nil);
164         });
165     }
166     InstanceMethodSwizzler m_swizzler;
167 };
168
169 class TestLocalAuthenticator : public WebCore::LocalAuthenticator {
170 public:
171     void setFailureFlag() { m_failureFlag = true; }
172
173 protected:
174     void issueClientCertificate(const String& rpId, const String& username, const Vector<uint8_t>& hash, WebCore::CompletionBlock _Nonnull completion) const final
175     {
176         if (m_failureFlag) {
177             completion(NULL, NULL, [NSError errorWithDomain:NSOSStatusErrorDomain code:-1 userInfo:nil]);
178             return;
179         }
180
181         ASSERT_EQ(32ul, hash.size());
182         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), BlockPtr<void()>::fromCallable([rpId = rpId.isolatedCopy(), username = username.isolatedCopy(), completion = makeBlockPtr(completion)] {
183             Util::sleep(1); // mimic network delay
184
185             // Get Key and add it to Keychain
186             auto key = getTestKey();
187             String label(username);
188             label.append("@" + rpId + "-rk"); // mimic what DeviceIdentity would do.
189             NSDictionary* addQuery = @{
190                 (id)kSecValueRef: (id)key.get(),
191                 (id)kSecClass: (id)kSecClassKey,
192                 (id)kSecAttrLabel: (id)label,
193             };
194             OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
195             ASSERT_FALSE(status);
196
197             // Construct dummy certificates
198             auto attestationCertificate = adoptCF(SecCertificateCreateWithData(NULL, (__bridge CFDataRef)adoptNS([[NSData alloc] initWithBase64EncodedString:testAttestationCertificateBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get()));
199             auto attestationIssuingCACertificate = adoptCF(SecCertificateCreateWithData(NULL, (__bridge CFDataRef)adoptNS([[NSData alloc] initWithBase64EncodedString:testAttestationIssuingCACertificateBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get()));
200
201             // Do self-attestation instead.
202             completion(key.get(), [NSArray arrayWithObjects: (__bridge id)attestationCertificate.get(), (__bridge id)attestationIssuingCACertificate.get(), nil], NULL);
203         }).get());
204     }
205
206 private:
207     bool m_failureFlag { false };
208 };
209
210 // FIXME(182769): Convert the followings to proper API tests.
211 TEST(LocalAuthenticator, MakeCredentialNotSupportedPubKeyCredParams)
212 {
213     WebCore::PublicKeyCredentialCreationOptions creationOptions;
214     creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, -35 }); // ES384
215     creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, -257 }); // RS256
216
217     bool done = false;
218     std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
219     auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
220         EXPECT_FALSE(true);
221         done = true;
222     };
223     auto exceptionCallback = [&done] (const WebCore::ExceptionData& exception) mutable {
224         EXPECT_EQ(WebCore::NotSupportedError, exception.code);
225         EXPECT_STREQ("The platform attached authenticator doesn't support any provided PublicKeyCredentialParameters.", exception.message.ascii().data());
226         done = true;
227     };
228     authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
229
230     TestWebKitAPI::Util::run(&done);
231 }
232
233 TEST(LocalAuthenticator, MakeCredentialExcludeCredentialsMatch)
234 {
235     // Insert the test key into Keychain
236     auto key = getTestKey();
237     NSDictionary* addQuery = @{
238         (id)kSecValueRef: (id)key.get(),
239         (id)kSecClass: (id)kSecClassKey,
240         (id)kSecAttrLabel: testRpId,
241         (id)kSecAttrApplicationTag: [NSData dataWithBytes:testUserhandle length:sizeof(testUserhandle)],
242     };
243     OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
244     EXPECT_FALSE(status);
245
246     // Invoke the LocalAuthenticator.
247     WebCore::PublicKeyCredentialDescriptor descriptor;
248     descriptor.type = WebCore::PublicKeyCredentialType::PublicKey;
249     WTF::base64Decode(testCredentialIdBase64, descriptor.idVector);
250     WebCore::PublicKeyCredentialCreationOptions creationOptions;
251     creationOptions.rp.id = testRpId;
252     creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
253     creationOptions.excludeCredentials.append(WTFMove(descriptor));
254
255     bool done = false;
256     std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
257     auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
258         EXPECT_FALSE(true);
259         cleanUpKeychain();
260         done = true;
261     };
262     auto exceptionCallback = [&done] (const WebCore::ExceptionData& exception) mutable {
263         EXPECT_EQ(WebCore::NotAllowedError, exception.code);
264         EXPECT_STREQ("At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.", exception.message.ascii().data());
265         cleanUpKeychain();
266         done = true;
267     };
268     authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
269
270     TestWebKitAPI::Util::run(&done);
271 }
272
273 TEST(LocalAuthenticator, MakeCredentialBiometricsNotEnrolled)
274 {
275     LACantEvaluatePolicySwizzler swizzler;
276
277     WebCore::PublicKeyCredentialCreationOptions creationOptions;
278     creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
279
280     bool done = false;
281     std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
282     auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
283         EXPECT_FALSE(true);
284         done = true;
285     };
286     auto exceptionCallback = [&done] (const WebCore::ExceptionData& exception) mutable {
287         EXPECT_EQ(WebCore::NotAllowedError, exception.code);
288         EXPECT_STREQ("No avaliable authenticators.", exception.message.ascii().data());
289         done = true;
290     };
291     authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
292
293     TestWebKitAPI::Util::run(&done);
294 }
295
296 TEST(LocalAuthenticator, MakeCredentialBiometricsNotAuthenticated)
297 {
298     LACanEvaluatePolicySwizzler canEvaluatePolicySwizzler;
299     LAEvaluatePolicyFailedSwizzler evaluatePolicyFailedSwizzler;
300
301     WebCore::PublicKeyCredentialCreationOptions creationOptions;
302     creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
303
304     bool done = false;
305     std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
306     auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
307         EXPECT_FALSE(true);
308         done = true;
309     };
310     auto exceptionCallback = [&done] (const WebCore::ExceptionData& exception) mutable {
311         EXPECT_EQ(WebCore::NotAllowedError, exception.code);
312         EXPECT_STREQ("Couldn't get user consent.", exception.message.ascii().data());
313         done = true;
314     };
315     authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
316
317     TestWebKitAPI::Util::run(&done);
318 }
319
320 TEST(LocalAuthenticator, MakeCredentialNotAttestated)
321 {
322     LACanEvaluatePolicySwizzler canEvaluatePolicySwizzler;
323     LAEvaluatePolicyPassedSwizzler evaluatePolicyPassedSwizzler;
324
325     WebCore::PublicKeyCredentialCreationOptions creationOptions;
326     creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
327
328     bool done = false;
329     std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
330     authenticator->setFailureFlag();
331     auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
332         EXPECT_FALSE(true);
333         done = true;
334     };
335     auto exceptionCallback = [&done] (const WebCore::ExceptionData& exception) mutable {
336         EXPECT_EQ(WebCore::UnknownError, exception.code);
337         EXPECT_STREQ("Unknown internal error.", exception.message.ascii().data());
338         done = true;
339     };
340     authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
341
342     TestWebKitAPI::Util::run(&done);
343 }
344
345 TEST(LocalAuthenticator, MakeCredentialDeleteOlderCredenital)
346 {
347     LACanEvaluatePolicySwizzler canEvaluatePolicySwizzler;
348     LAEvaluatePolicyPassedSwizzler evaluatePolicyPassedSwizzler;
349
350     // Insert the older credential
351     auto key = getTestKey();
352     NSDictionary* addQuery = @{
353         (id)kSecValueRef: (id)key.get(),
354         (id)kSecClass: (id)kSecClassKey,
355         (id)kSecAttrLabel: testRpId,
356         (id)kSecAttrApplicationTag: [NSData dataWithBytes:testUserhandle length:sizeof(testUserhandle)],
357     };
358     OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
359     EXPECT_FALSE(status);
360
361     WebCore::PublicKeyCredentialCreationOptions creationOptions;
362     creationOptions.rp.id = testRpId;
363     creationOptions.user.name = testUsername;
364     creationOptions.user.idVector.append(testUserhandle, sizeof(testUserhandle));
365     creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
366
367     bool done = false;
368     std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
369     authenticator->setFailureFlag();
370     auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
371         EXPECT_FALSE(true);
372         done = true;
373     };
374     auto exceptionCallback = [&done] (const WebCore::ExceptionData&) mutable {
375         NSDictionary *query = @{
376             (id)kSecClass: (id)kSecClassKey,
377             (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
378             (id)kSecAttrLabel: testRpId,
379             (id)kSecAttrApplicationTag: [NSData dataWithBytes:testUserhandle length:sizeof(testUserhandle)],
380         };
381         OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);
382         EXPECT_EQ(errSecItemNotFound, status);
383         done = true;
384     };
385     authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
386
387     TestWebKitAPI::Util::run(&done);
388 }
389
390 TEST(LocalAuthenticator, MakeCredentialPassedWithSelfAttestation)
391 {
392     LACanEvaluatePolicySwizzler canEvaluatePolicySwizzler;
393     LAEvaluatePolicyPassedSwizzler evaluatePolicyPassedSwizzler;
394
395     WebCore::PublicKeyCredentialCreationOptions creationOptions;
396     creationOptions.rp.id = testRpId;
397     creationOptions.user.name = testUsername;
398     creationOptions.user.idVector.append(testUserhandle, sizeof(testUserhandle));
399     creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
400
401     bool done = false;
402     std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
403     auto callback = [&done] (const Vector<uint8_t>& credentialId, const Vector<uint8_t>& attestationObjet) {
404         // Check Keychain
405         NSDictionary *query = @{
406             (id)kSecClass: (id)kSecClassKey,
407             (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
408             (id)kSecAttrLabel: testRpId,
409             (id)kSecAttrApplicationLabel: adoptNS([[NSData alloc] initWithBase64EncodedString:testCredentialIdBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get(),
410             (id)kSecAttrApplicationTag: [NSData dataWithBytes:testUserhandle length:sizeof(testUserhandle)],
411         };
412         OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);
413         EXPECT_FALSE(status);
414
415         // Check Credential ID
416         EXPECT_TRUE(WTF::base64Encode(credentialId.data(), credentialId.size()) == testCredentialIdBase64);
417
418         // Check Attestation Object
419         auto attestationObjectMap = cbor::CBORReader::read(attestationObjet);
420         ASSERT_TRUE(!!attestationObjectMap);
421
422         // Check Authenticator Data.
423         auto& authData = attestationObjectMap->getMap().find(cbor::CBORValue("authData"))->second.getByteString();
424         size_t pos = 0;
425         uint8_t expectedRpIdHash[] = {
426             0x49, 0x96, 0x0d, 0xe5, 0x88, 0x0e, 0x8c, 0x68,
427             0x74, 0x34, 0x17, 0x0f, 0x64, 0x76, 0x60, 0x5b,
428             0x8f, 0xe4, 0xae, 0xb9, 0xa2, 0x86, 0x32, 0xc7,
429             0x99, 0x5c, 0xf3, 0xba, 0x83, 0x1d, 0x97, 0x63
430         };
431         EXPECT_FALSE(memcmp(authData.data() + pos, expectedRpIdHash, sizeof(expectedRpIdHash)));
432         pos += sizeof(expectedRpIdHash);
433
434         // FLAGS
435         EXPECT_EQ(69, authData[pos]);
436         pos++;
437
438         uint32_t counter = -1;
439         memcpy(&counter, authData.data() + pos, sizeof(uint32_t));
440         EXPECT_EQ(0u, counter);
441         pos += sizeof(uint32_t);
442
443         uint8_t expectedAAGUID[] = {
444             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
445             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
446         };
447         EXPECT_FALSE(memcmp(authData.data() + pos, expectedAAGUID, sizeof(expectedAAGUID)));
448         pos += sizeof(expectedAAGUID);
449
450         uint16_t l = -1;
451         memcpy(&l, authData.data() + pos, sizeof(uint16_t));
452         EXPECT_EQ(20u, l);
453         pos += sizeof(uint16_t);
454
455         EXPECT_FALSE(memcmp(authData.data() + pos, credentialId.data(), l));
456         pos += l;
457
458         // Credential Public Key
459         // FIXME(183536): The CBOR reader doesn't support negative integer as map key. Thus we couldn't utilzie it.
460         EXPECT_STREQ("pQECAyYgASFYIDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749IlggVBJPgqUIwfhWHJ91nb7UPH76c0+WFOzZKslPyyFse4g=", WTF::base64Encode(authData.data() + pos, authData.size() - pos).ascii().data());
461
462         // Check Self Attestation
463         EXPECT_STREQ("Apple", attestationObjectMap->getMap().find(cbor::CBORValue("fmt"))->second.getString().ascii().data());
464
465         auto& attStmt = attestationObjectMap->getMap().find(cbor::CBORValue("attStmt"))->second.getMap();
466         EXPECT_EQ(COSE::ES256, attStmt.find(cbor::CBORValue("alg"))->second.getNegative());
467
468         auto& sig = attStmt.find(cbor::CBORValue("sig"))->second.getByteString();
469         auto privateKey = getTestKey();
470         EXPECT_TRUE(SecKeyVerifySignature(SecKeyCopyPublicKey(privateKey.get()), kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (__bridge CFDataRef)[NSData dataWithBytes:authData.data() length:authData.size()], (__bridge CFDataRef)[NSData dataWithBytes:sig.data() length:sig.size()], NULL));
471
472         // Check certificates
473         auto& x5c = attStmt.find(cbor::CBORValue("x5c"))->second.getArray();
474         auto& attestationCertificateData = x5c[0].getByteString();
475         auto attestationCertificate = adoptCF(SecCertificateCreateWithData(NULL, (__bridge CFDataRef)[NSData dataWithBytes:attestationCertificateData.data() length:attestationCertificateData.size()]));
476         CFStringRef commonName = NULL;
477         status = SecCertificateCopyCommonName(attestationCertificate.get(), &commonName);
478         auto retainCommonName = adoptCF(commonName);
479         ASSERT(!status);
480         EXPECT_STREQ("00008010-000A49A230A0213A", [(NSString *)commonName cStringUsingEncoding: NSASCIIStringEncoding]);
481
482         auto& attestationIssuingCACertificateData = x5c[1].getByteString();
483         auto attestationIssuingCACertificate = adoptCF(SecCertificateCreateWithData(NULL, (__bridge CFDataRef)[NSData dataWithBytes:attestationIssuingCACertificateData.data() length:attestationIssuingCACertificateData.size()]));
484         commonName = NULL;
485         status = SecCertificateCopyCommonName(attestationIssuingCACertificate.get(), &commonName);
486         retainCommonName = adoptCF(commonName);
487         ASSERT(!status);
488         EXPECT_STREQ("Basic Attestation User Sub CA1", [(NSString *)commonName cStringUsingEncoding: NSASCIIStringEncoding]);
489
490         cleanUpKeychain();
491         done = true;
492     };
493     auto exceptionCallback = [&done] (const WebCore::ExceptionData&) mutable {
494         EXPECT_FALSE(true);
495         cleanUpKeychain();
496         done = true;
497     };
498     Vector<uint8_t> hash(32);
499     authenticator->makeCredential(hash, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
500
501     TestWebKitAPI::Util::run(&done);
502 }
503
504 } // namespace TestWebKitAPI
505
506 #endif // PLATFORM(IOS)