Source/WTF/ChangeLog

 12020-02-19 Jiewen Tan <jiewen_tan@apple.com>
 2
 3 [WebAuthn] Replace DeviceIdentity.framework
 4 https://bugs.webkit.org/show_bug.cgi?id=207985
 5 <rdar://problem/59369223>
 6
 7 Reviewed by Brent Fulgham.
 8
 9 * wtf/PlatformHave.h:
 10
1112020-02-18 Keith Miller <keith_miller@apple.com>
212
313 Add an os_log PrintStream

Source/WebKit/ChangeLog

 12020-02-19 Jiewen Tan <jiewen_tan@apple.com>
 2
 3 [WebAuthn] Replace DeviceIdentity.framework
 4 https://bugs.webkit.org/show_bug.cgi?id=207985
 5 <rdar://problem/59369223>
 6
 7 Reviewed by Brent Fulgham.
 8
 9 This patch replaces the DeviceIdentity.framework with a new framework that better suits our needs.
 10 The new experimental authentication logic is handled by WebKtAdditions. Please refer to the radar
 11 for detailed information.
 12
 13 Besides the replacement, this patch also:
 14 1) changes how user consent is obtained to avoid multiple prompts for biometric input.
 15 2) removes keychain workarounds for DeviceIdentity given the credential private key is now under our possession.
 16 3) removes everything that is related to DeviceIdentity.
 17
 18 Covered by new tests within existing test files.
 19
 20 * Configurations/WebKit.xcconfig:
 21 * Platform/spi/Cocoa/DeviceIdentitySPI.h: Removed.
 22 * UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h:
 23 * UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm:
 24 (WebKit::LocalAuthenticatorInternal::toNSData):
 25 (WebKit::LocalAuthenticator::makeCredential):
 26 (WebKit::LocalAuthenticator::continueMakeCredentialAfterUserConsented):
 27 (WebKit::LocalAuthenticator::continueMakeCredentialAfterAttested):
 28 (WebKit::LocalAuthenticator::getAssertion):
 29 (WebKit::LocalAuthenticator::continueGetAssertionAfterUserConsented):
 30 * UIProcess/WebAuthentication/Cocoa/LocalConnection.h:
 31 * UIProcess/WebAuthentication/Cocoa/LocalConnection.mm:
 32 (WebKit::LocalConnection::createCredentialPrivateKey const):
 33 (WebKit::LocalConnection::getAttestation const):
 34 * UIProcess/WebAuthentication/Cocoa/LocalService.mm:
 35 (WebKit::LocalService::isAvailable):
 36 * UIProcess/WebAuthentication/Mock/MockLocalConnection.h:
 37 * UIProcess/WebAuthentication/Mock/MockLocalConnection.mm:
 38 (WebKit::MockLocalConnection::createCredentialPrivateKey const):
 39 (WebKit::MockLocalConnection::getAttestation const):
 40 * WebKit.xcodeproj/project.pbxproj:
 41
1422020-02-19 Brian Burg <bburg@apple.com>
243
344 Web Automation: Automation.setWindowFrameOfBrowsingContext should accept negative x and y-origin values

Source/WTF/wtf/PlatformHave.h

557557#define HAVE_DESIGN_SYSTEM_UI_FONTS 1
558558#endif
559559
560 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR))
561 #define HAVE_DEVICE_IDENTITY 1
562 #endif
563 
564560#if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101600) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 140000)
565561#define HAVE_COOKIE_CHANGE_LISTENER_API 1
566562#endif

Source/WebKit/Configurations/WebKit.xcconfig

@@WK_CORE_PREDICTION_LDFLAGS_YES = $(WK_MACOS_WEAK_FRAMEWORK) CorePrediction;
6060WK_CORE_SERVICES_LDFLAGS = $(WK_CORE_SERVICES_LDFLAGS_$(WK_PLATFORM_NAME));
6161WK_CORE_SERVICES_LDFLAGS_macosx = -framework CoreServices;
6262
63 WK_DEVICE_IDENTITY_LDFLAGS = $(WK_DEVICE_IDENTITY_LDFLAGS_$(WK_HAVE_DEVICE_IDENTITY));
64 WK_DEVICE_IDENTITY_LDFLAGS_YES = -framework DeviceIdentity;
65 
6663WK_GRAPHICS_SERVICES_LDFLAGS = $(WK_GRAPHICS_SERVICES_LDFLAGS_$(WK_COCOA_TOUCH));
6764WK_GRAPHICS_SERVICES_LDFLAGS_cocoatouch = -framework GraphicsServices;
6865

@@WK_AUTHKIT_LDFLAGS_IOS_SINCE_13 = -framework AuthKit;
123120WK_AUTHKIT_LDFLAGS_macosx = $(WK_AUTHKIT_LDFLAGS$(WK_MACOS_1015));
124121WK_AUTHKIT_LDFLAGS_MACOS_SINCE_1015 = -framework AuthKit;
125122
126 FRAMEWORK_AND_LIBRARY_LDFLAGS = -lobjc -framework CFNetwork -framework CoreAudio -framework CoreFoundation -framework CoreGraphics -framework CoreText -framework Foundation -framework ImageIO -framework IOKit -framework IOSurface -framework WebKitLegacy -lnetwork $(WK_ACCESSIBILITY_LDFLAGS) $(WK_APPKIT_LDFLAGS) $(WK_ASSERTION_SERVICES_LDFLAGS) $(WK_AUTHKIT_LDFLAGS) $(WK_CARBON_LDFLAGS) $(WK_CORE_PREDICTION_LDFLAGS) $(WK_CORE_SERVICES_LDFLAGS) $(WK_DEVICE_IDENTITY_LDFLAGS) $(WK_GRAPHICS_SERVICES_LDFLAGS) $(WK_LIBSANDBOX_LDFLAGS) $(WK_LIBWEBRTC_LDFLAGS) $(WK_MOBILE_CORE_SERVICES_LDFLAGS) $(WK_MOBILE_GESTALT_LDFLAGS) $(WK_OPENGL_LDFLAGS) $(WK_PDFKIT_LDFLAGS) $(WK_SAFE_BROWSING_LDFLAGS) $(WK_SECURITY_INTERFACE_LDFLAGS) $(WK_UIKIT_LDFLAGS) $(WK_URL_FORMATTING_LDFLAGS) $(WK_WEBINSPECTORUI_LDFLAGS);
 123FRAMEWORK_AND_LIBRARY_LDFLAGS = -lobjc -framework CFNetwork -framework CoreAudio -framework CoreFoundation -framework CoreGraphics -framework CoreText -framework Foundation -framework ImageIO -framework IOKit -framework IOSurface -framework WebKitLegacy -lnetwork $(WK_ACCESSIBILITY_LDFLAGS) $(WK_APPKIT_LDFLAGS) $(WK_ASSERTION_SERVICES_LDFLAGS) $(WK_AUTHKIT_LDFLAGS) $(WK_CARBON_LDFLAGS) $(WK_CORE_PREDICTION_LDFLAGS) $(WK_CORE_SERVICES_LDFLAGS) $(WK_GRAPHICS_SERVICES_LDFLAGS) $(WK_LIBSANDBOX_LDFLAGS) $(WK_LIBWEBRTC_LDFLAGS) $(WK_MOBILE_CORE_SERVICES_LDFLAGS) $(WK_MOBILE_GESTALT_LDFLAGS) $(WK_OPENGL_LDFLAGS) $(WK_PDFKIT_LDFLAGS) $(WK_SAFE_BROWSING_LDFLAGS) $(WK_SECURITY_INTERFACE_LDFLAGS) $(WK_UIKIT_LDFLAGS) $(WK_URL_FORMATTING_LDFLAGS) $(WK_WEBINSPECTORUI_LDFLAGS);
127124
128125// Prevent C++ standard library basic_stringstream, operator new, delete and their related exception types from being exported as weak symbols.
129126UNEXPORTED_SYMBOL_LDFLAGS = -Wl,-unexported_symbol -Wl,__ZTISt9bad_alloc -Wl,-unexported_symbol -Wl,__ZTISt9exception -Wl,-unexported_symbol -Wl,__ZTSSt9bad_alloc -Wl,-unexported_symbol -Wl,__ZTSSt9exception -Wl,-unexported_symbol -Wl,__ZdlPvS_ -Wl,-unexported_symbol -Wl,__ZnwmPv -Wl,-unexported_symbol -Wl,__Znwm -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEC2EOS4_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEC1EOS4_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEaSEDn -Wl,-unexported_symbol -Wl,__ZNKSt3__18functionIFvN7WebCore12PolicyActionEEEclES2_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEE4swapERS4_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEC1ERKS4_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEC2ERKS4_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEED1Ev -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEED2Ev -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEaSERKS4_ -Wl,-unexported_symbol -Wl,__ZTVNSt3__117bad_function_callE -Wl,-unexported_symbol -Wl,__ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE -Wl,-unexported_symbol -Wl,__ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE -Wl,-unexported_symbol -Wl,__ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE -Wl,-unexported_symbol -Wl,__ZTTNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE -Wl,-unexported_symbol -Wl,__ZTVNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE -Wl,-unexported_symbol -Wl,__ZTVNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE -Wl,-unexported_symbol -Wl,__ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE8_NS_13basic_ostreamIcS2_EE;

@@WK_FRAMEWORK_HEADER_POSTPROCESSING_DISABLED_IOS_BEFORE_1300 = YES;
161158WK_RELOCATABLE_FRAMEWORK_LDFLAGS = $(WK_RELOCATABLE_FRAMEWORK_LDFLAGS_$(WK_RELOCATABLE_FRAMEWORKS)_$(WK_PLATFORM_NAME));
162159WK_RELOCATABLE_FRAMEWORK_LDFLAGS_YES_macosx = -Wl,-not_for_dyld_shared_cache;
163160
164 WK_HAVE_DEVICE_IDENTITY = $(WK_HAVE_DEVICE_IDENTITY_$(WK_PLATFORM_NAME));
165 WK_HAVE_DEVICE_IDENTITY_iphoneos = YES;
166 WK_HAVE_DEVICE_IDENTITY_macosx = $(WK_HAVE_DEVICE_IDENTITY$(WK_MACOS_1014));
167 WK_HAVE_DEVICE_IDENTITY_MACOS_SINCE_1014 = YES;
168 
169161WK_HAVE_URL_FORMATTING = $(WK_HAVE_URL_FORMATTING_$(WK_PLATFORM_NAME));
170162WK_HAVE_URL_FORMATTING_iphoneos = $(WK_HAVE_URL_FORMATTING$(WK_IOS_12));
171163WK_HAVE_URL_FORMATTING_maccatalyst = $(WK_HAVE_URL_FORMATTING$(WK_IOS_12));

Source/WebKit/Platform/spi/Cocoa/DeviceIdentitySPI.h

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 #pragma once
27 
28 #if ENABLE(WEB_AUTHN)
29 
30 #if HAVE(DEVICE_IDENTITY)
31 
32 #if USE(APPLE_INTERNAL_SDK)
33 
34 extern "C" {
35 #import <DeviceIdentity/DeviceIdentity.h>
36 }
37 
38 #else
39 
40 typedef void (^MABAACompletionBlock)(_Nullable SecKeyRef referenceKey, NSArray * _Nullable certificates, NSError * _Nullable error);
41 
42 extern NSString * _Nonnull const kMAOptionsBAAAccessControls;
43 extern NSString * _Nonnull const kMAOptionsBAAIgnoreExistingKeychainItems;
44 extern NSString * _Nonnull const kMAOptionsBAAKeychainAccessGroup;
45 extern NSString * _Nonnull const kMAOptionsBAAKeychainLabel;
46 extern NSString * _Nonnull const kMAOptionsBAANonce;
47 extern NSString * _Nonnull const kMAOptionsBAAOIDNonce;
48 extern NSString * _Nonnull const kMAOptionsBAAOIDSToInclude;
49 extern NSString * _Nonnull const kMAOptionsBAASCRTAttestation;
50 extern NSString * _Nonnull const kMAOptionsBAAValidity;
51 
52 extern "C"
53 void DeviceIdentityIssueClientCertificateWithCompletion(dispatch_queue_t _Nullable, NSDictionary * _Nullable options, MABAACompletionBlock _Nonnull);
54 
55 #endif // USE(APPLE_INTERNAL_SDK)
56 
57 #endif // HAVE(DEVICE_IDENTITY)
58 
59 #endif // ENABLE(WEB_AUTHN)

Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h

@@private:
5656 explicit LocalAuthenticator(UniqueRef<LocalConnection>&&);
5757
5858 void makeCredential() final;
59  void continueMakeCredentialAfterUserConsented(LocalConnection::UserConsent);
60  void continueMakeCredentialAfterAttested(SecKeyRef, NSArray *certificates, NSError *);
 59 void continueMakeCredentialAfterUserConsented(SecAccessControlRef, LocalConnection::UserConsent, LAContext *);
 60 void continueMakeCredentialAfterAttested(SecKeyRef, Vector<uint8_t>&& credentialId, Vector<uint8_t>&& authData, NSArray *certificates, NSError *);
6161
6262 void getAssertion() final;
6363 void continueGetAssertionAfterUserConsented(LocalConnection::UserConsent, LAContext *, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& userhandle);

Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm

@@static inline Vector<uint8_t> toVector(NSData *data)
8080 return result;
8181}
8282
 83static inline RetainPtr<NSData> toNSData(const Vector<uint8_t>& data)
 84{
 85 // FIXME(183534): Consider using initWithBytesNoCopy.
 86 return adoptNS([[NSData alloc] initWithBytes:data.data() length:data.size()]);
 87}
 88
8389} // LocalAuthenticatorInternal
8490
8591LocalAuthenticator::LocalAuthenticator(UniqueRef<LocalConnection>&& connection)

@@void LocalAuthenticator::makeCredential()
99105 // Skip Step 9 as extensions are not supported yet.
100106 // Step 8 is implicitly captured by all UnknownError exception receiveResponds.
101107 // Step 2.
102  bool canFullfillPubKeyCredParams = false;
103  for (auto& pubKeyCredParam : creationOptions.pubKeyCredParams) {
104  if (pubKeyCredParam.type == PublicKeyCredentialType::PublicKey && pubKeyCredParam.alg == COSE::ES256) {
105  canFullfillPubKeyCredParams = true;
106  break;
107  }
108  }
109  if (!canFullfillPubKeyCredParams) {
 108 if (notFound == creationOptions.pubKeyCredParams.findMatching([] (auto& pubKeyCredParam) {
 109 return pubKeyCredParam.type == PublicKeyCredentialType::PublicKey && pubKeyCredParam.alg == COSE::ES256;
 110 })) {
110111 receiveRespond(ExceptionData { NotSupportedError, "The platform attached authenticator doesn't support any provided PublicKeyCredentialParameters."_s });
111112 return;
112113 }

@@void LocalAuthenticator::makeCredential()
136137 }
137138 auto retainAttributesArray = adoptCF(attributesArrayRef);
138139
 140 // FIXME(rdar://problem/35900593): Need to obtain user consent and then return different error according to the result.
139141 for (NSDictionary *nsAttributes in (NSArray *)attributesArrayRef) {
140142 NSData *nsCredentialId = nsAttributes[(id)kSecAttrApplicationLabel];
141143 if (excludeCredentialIds.contains(String(reinterpret_cast<const char*>(nsCredentialId.bytes), nsCredentialId.length))) {

@@void LocalAuthenticator::makeCredential()
148150 // Step 6.
149151 // FIXME(rdar://problem/35900593): Update to a formal UI.
150152 // Get user consent.
151  auto callback = [weakThis = makeWeakPtr(*this)](LocalConnection::UserConsent consent) {
 153 RetainPtr<SecAccessControlRef> accessControl;
 154 {
 155 CFErrorRef errorRef = nullptr;
 156 accessControl = adoptCF(SecAccessControlCreateWithFlags(NULL, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlPrivateKeyUsage | kSecAccessControlUserPresence, &errorRef));
 157 auto retainError = adoptCF(errorRef);
 158 if (errorRef) {
 159 LOG_ERROR("Couldn't create access control: %@", (NSError *)errorRef);
 160 receiveRespond(ExceptionData { UnknownError, makeString("Couldn't create access control: ", String(((NSError*)errorRef).localizedDescription)) });
 161 return;
 162 }
 163 }
 164
 165 SecAccessControlRef accessControlRef = accessControl.get();
 166 auto callback = [accessControl = WTFMove(accessControl), weakThis = makeWeakPtr(*this)] (LocalConnection::UserConsent consent, LAContext *context) {
152167 ASSERT(RunLoop::isMain());
153168 if (!weakThis)
154169 return;
155170
156  weakThis->continueMakeCredentialAfterUserConsented(consent);
 171 weakThis->continueMakeCredentialAfterUserConsented(accessControl.get(), consent, context);
157172 };
158173 m_connection->getUserConsent(
159  "allow " + creationOptions.rp.id + " to create a public key credential for " + creationOptions.user.name,
 174 makeString("allow "_s, creationOptions.rp.id, " to create a public key credential for "_s, creationOptions.user.name),
 175 accessControlRef,
160176 WTFMove(callback));
161177}
162178
163 void LocalAuthenticator::continueMakeCredentialAfterUserConsented(LocalConnection::UserConsent consent)
 179void LocalAuthenticator::continueMakeCredentialAfterUserConsented(SecAccessControlRef accessControlRef, LocalConnection::UserConsent consent, LAContext *context)
164180{
 181 using namespace LocalAuthenticatorInternal;
 182
165183 ASSERT(m_state == State::RequestReceived);
166184 m_state = State::UserConsented;
167185 auto& creationOptions = WTF::get<PublicKeyCredentialCreationOptions>(requestData().options);

@@void LocalAuthenticator::continueMakeCredentialAfterUserConsented(LocalConnectio
171189 return;
172190 }
173191
 192 // FIXME(183533): A single kSecClassKey item couldn't store all meta data. The following schema is a tentative solution
 193 // to accommodate the most important meta data, i.e. RP ID, Credential ID, and userhandle.
 194 // kSecAttrLabel: RP ID
 195 // kSecAttrApplicationLabel: Credential ID (auto-gen by Keychain)
 196 // kSecAttrApplicationTag: userhandle
 197 // Noted, the vale of kSecAttrApplicationLabel is automatically generated by the Keychain, which is a SHA-1 hash of
 198 // the public key. We borrow it directly for now to workaround the stated limitations.
 199 const auto& secAttrLabel = creationOptions.rp.id;
 200 auto secAttrApplicationTag = toNSData(creationOptions.user.idVector);
 201
174202 // Step 7.5.
175  // Userhandle is stored in kSecAttrApplicationTag attribute.
176203 // Failures after this point could block users' accounts forever. Should we follow the spec?
177204 NSDictionary* deleteQuery = @{
178205 (id)kSecClass: (id)kSecClassKey,
179  (id)kSecAttrLabel: creationOptions.rp.id,
180  (id)kSecAttrApplicationTag: [NSData dataWithBytes:creationOptions.user.idVector.data() length:creationOptions.user.idVector.size()],
 206 (id)kSecAttrLabel: secAttrLabel,
 207 (id)kSecAttrApplicationTag: secAttrApplicationTag.get(),
181208#if HAVE(DATA_PROTECTION_KEYCHAIN)
182209 (id)kSecUseDataProtectionKeychain: @YES
183210#else

@@void LocalAuthenticator::continueMakeCredentialAfterUserConsented(LocalConnectio
191218 return;
192219 }
193220
194  // Step 7.1, 13. Apple Attestation
195  auto callback = [weakThis = makeWeakPtr(*this)](SecKeyRef _Nullable privateKey, NSArray * _Nullable certificates, NSError * _Nullable error) {
196  ASSERT(RunLoop::isMain());
197  if (!weakThis)
198  return;
199  weakThis->continueMakeCredentialAfterAttested(privateKey, certificates, error);
200  };
201  m_connection->getAttestation(creationOptions.rp.id, creationOptions.user.name, requestData().hash, WTFMove(callback));
202 }
203 
204 void LocalAuthenticator::continueMakeCredentialAfterAttested(SecKeyRef privateKey, NSArray *certificates, NSError *error)
205 {
206  using namespace LocalAuthenticatorInternal;
207 
208  ASSERT(m_state == State::UserConsented);
209  m_state = State::Attested;
210  auto& creationOptions = WTF::get<PublicKeyCredentialCreationOptions>(requestData().options);
211 
212  if (error) {
213  LOG_ERROR("Couldn't attest: %@", error);
214  receiveRespond(ExceptionData { UnknownError, makeString("Couldn't attest: ", String(error.localizedDescription)) });
 221 // Step 7.1-7.4.
 222 // The above-to-create private key will be inserted into keychain while using SEP.
 223 auto privateKey = m_connection->createCredentialPrivateKey(context, accessControlRef, secAttrLabel, secAttrApplicationTag.get());
 224 if (!privateKey) {
 225 receiveRespond(ExceptionData { UnknownError, "Couldn't create private key."_s });
215226 return;
216227 }
217  // Attestation Certificate and Attestation Issuing CA
218  ASSERT(certificates && ([certificates count] == 2));
219228
220  // Step 7.2-7.4.
221  // FIXME(183533): A single kSecClassKey item couldn't store all meta data. The following schema is a tentative solution
222  // to accommodate the most important meta data, i.e. RP ID, Credential ID, and userhandle.
223  // kSecAttrLabel: RP ID
224  // kSecAttrApplicationLabel: Credential ID (auto-gen by Keychain)
225  // kSecAttrApplicationTag: userhandle
226  // Noted, the current DeviceIdentity.Framework would only allow us to pass the kSecAttrLabel as the inital attribute
227  // for the Keychain item. Since that's the only clue we have to locate the unique item, we use the pattern username@rp.id
228  // as the initial value.
229  // Also noted, the vale of kSecAttrApplicationLabel is automatically generated by the Keychain, which is a SHA-1 hash of
230  // the public key. We borrow it directly for now to workaround the stated limitations.
231  // Update the Keychain item to the above schema.
232  // FIXME(183533): DeviceIdentity.Framework would insert certificates into Keychain as well. We should update those as well.
233229 Vector<uint8_t> credentialId;
234230 {
235  // -rk-ucrt is added by DeviceIdentity.Framework.
236  String label = makeString(creationOptions.user.name, "@", creationOptions.rp.id, "-rk-ucrt");
237231 NSDictionary *credentialIdQuery = @{
238232 (id)kSecClass: (id)kSecClassKey,
239233 (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
240  (id)kSecAttrLabel: label,
 234 (id)kSecAttrLabel: secAttrLabel,
 235 (id)kSecAttrApplicationTag: secAttrApplicationTag.get(),
241236 (id)kSecReturnAttributes: @YES,
242237#if HAVE(DATA_PROTECTION_KEYCHAIN)
243238 (id)kSecUseDataProtectionKeychain: @YES

@@void LocalAuthenticator::continueMakeCredentialAfterAttested(SecKeyRef privateKe
256251
257252 NSDictionary *nsAttributes = (NSDictionary *)attributesRef;
258253 credentialId = toVector(nsAttributes[(id)kSecAttrApplicationLabel]);
259 
260  NSDictionary *updateQuery = @{
261  (id)kSecClass: (id)kSecClassKey,
262  (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
263  (id)kSecAttrApplicationLabel: nsAttributes[(id)kSecAttrApplicationLabel],
264 #if HAVE(DATA_PROTECTION_KEYCHAIN)
265  (id)kSecUseDataProtectionKeychain: @YES
266 #else
267  (id)kSecAttrNoLegacy: @YES
268 #endif
269  };
270  NSDictionary *updateParams = @{
271  (id)kSecAttrLabel: creationOptions.rp.id,
272  (id)kSecAttrApplicationTag: [NSData dataWithBytes:creationOptions.user.idVector.data() length:creationOptions.user.idVector.size()],
273  };
274  status = SecItemUpdate((__bridge CFDictionaryRef)updateQuery, (__bridge CFDictionaryRef)updateParams);
275  if (status) {
276  LOG_ERROR("Couldn't update the Keychain item: %d", status);
277  receiveRespond(ExceptionData { UnknownError, makeString("Couldn't update the Keychain item: ", status) });
278  return;
279  }
280254 }
281255
282256 // Step 10.

@@void LocalAuthenticator::continueMakeCredentialAfterAttested(SecKeyRef privateKe
289263 {
290264 RetainPtr<CFDataRef> publicKeyDataRef;
291265 {
292  auto publicKey = adoptCF(SecKeyCopyPublicKey(privateKey));
 266 auto publicKey = adoptCF(SecKeyCopyPublicKey(privateKey.get()));
293267 CFErrorRef errorRef = nullptr;
294268 publicKeyDataRef = adoptCF(SecKeyCopyExternalRepresentation(publicKey.get(), &errorRef));
295269 auto retainError = adoptCF(errorRef);

@@void LocalAuthenticator::continueMakeCredentialAfterAttested(SecKeyRef privateKe
314288 // Step 12.
315289 auto authData = buildAuthData(creationOptions.rp.id, makeCredentialFlags, counter, attestedCredentialData);
316290
 291 // Step 13. Apple Attestation
 292 auto *privateKeyRef = privateKey.get();
 293 auto nsAuthData = toNSData(authData);
 294 auto callback = [privateKey = WTFMove(privateKey), credentialId = WTFMove(credentialId), authData = WTFMove(authData), weakThis = makeWeakPtr(*this)] (NSArray * _Nullable certificates, NSError * _Nullable error) mutable {
 295 ASSERT(RunLoop::isMain());
 296 if (!weakThis)
 297 return;
 298 weakThis->continueMakeCredentialAfterAttested(privateKey.get(), WTFMove(credentialId), WTFMove(authData), certificates, error);
 299 };
 300 m_connection->getAttestation(privateKeyRef, nsAuthData.get(), toNSData(requestData().hash).get(), WTFMove(callback));
 301}
 302
 303void LocalAuthenticator::continueMakeCredentialAfterAttested(SecKeyRef privateKey, Vector<uint8_t>&& credentialId, Vector<uint8_t>&& authData, NSArray *certificates, NSError *error)
 304{
 305 using namespace LocalAuthenticatorInternal;
 306
 307 ASSERT(m_state == State::UserConsented);
 308 m_state = State::Attested;
 309 auto& creationOptions = WTF::get<PublicKeyCredentialCreationOptions>(requestData().options);
 310
 311 if (error) {
 312 LOG_ERROR("Couldn't attest: %@", error);
 313 receiveRespond(ExceptionData { UnknownError, makeString("Couldn't attest: ", String(error.localizedDescription)) });
 314 return;
 315 }
 316 // Attestation Certificate and Attestation Issuing CA
 317 ASSERT(certificates && ([certificates count] == 2));
 318
317319 // Step 13. Apple Attestation Cont'
318320 // Assemble the attestation object:
319321 // https://www.w3.org/TR/webauthn/#attestation-object
320322 cbor::CBORValue::MapValue attestationStatementMap;
321323 {
322  Vector<uint8_t> signature;
323  {
324  CFErrorRef errorRef = nullptr;
325  // FIXME(183652): Reduce prompt for biometrics
326  auto signatureRef = adoptCF(SecKeyCreateSignature(privateKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (__bridge CFDataRef)[NSData dataWithBytes:authData.data() length:authData.size()], &errorRef));
327  auto retainError = adoptCF(errorRef);
328  if (errorRef) {
329  LOG_ERROR("Couldn't generate the signature: %@", (NSError*)errorRef);
330  receiveRespond(ExceptionData { UnknownError, makeString("Couldn't generate the signature: ", String(((NSError*)errorRef).localizedDescription)) });
331  return;
332  }
333  signature = toVector((NSData *)signatureRef.get());
334  }
335324 attestationStatementMap[cbor::CBORValue("alg")] = cbor::CBORValue(COSE::ES256);
336  attestationStatementMap[cbor::CBORValue("sig")] = cbor::CBORValue(signature);
337325 Vector<cbor::CBORValue> cborArray;
338326 for (size_t i = 0; i < [certificates count]; i++)
339327 cborArray.append(cbor::CBORValue(toVector((NSData *)adoptCF(SecCertificateCopyData((__bridge SecCertificateRef)certificates[i])).get())));
340328 attestationStatementMap[cbor::CBORValue("x5c")] = cbor::CBORValue(WTFMove(cborArray));
341329 }
342  auto attestationObject = buildAttestationObject(WTFMove(authData), "Apple", WTFMove(attestationStatementMap), creationOptions.attestation);
 330 auto attestationObject = buildAttestationObject(WTFMove(authData), "apple", WTFMove(attestationStatementMap), creationOptions.attestation);
343331
344332 receiveRespond(AuthenticatorAttestationResponse::create(credentialId, attestationObject));
345333}

@@void LocalAuthenticator::getAssertion()
356344 // Skip Step 8 as extensions are not supported yet.
357345 // Step 12 is implicitly captured by all UnknownError exception callbacks.
358346 // Step 3-5. Unlike the spec, if an allow list is provided and there is no intersection between existing ones and the allow list, we always return NotAllowedError.
 347 // FIXME(rdar://problem/35900593): Need to inform users.
359348 auto allowCredentialIds = produceHashSet(requestOptions.allowCredentials);
360349 if (!requestOptions.allowCredentials.isEmpty() && allowCredentialIds.isEmpty()) {
361350 receiveRespond(ExceptionData { NotAllowedError, "No matched credentials are found in the platform attached authenticator."_s });

@@void LocalAuthenticator::continueGetAssertionAfterUserConsented(LocalConnection:
448437 NSDictionary *query = @{
449438 (id)kSecClass: (id)kSecClassKey,
450439 (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
451  (id)kSecAttrApplicationLabel: [NSData dataWithBytes:credentialId.data() length:credentialId.size()],
 440 (id)kSecAttrApplicationLabel: toNSData(credentialId).get(),
452441 (id)kSecUseAuthenticationContext: context,
453442 (id)kSecReturnRef: @YES,
454443#if HAVE(DATA_PROTECTION_KEYCHAIN)

Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.h

@@public:
5151 Yes
5252 };
5353
54  using AttestationCallback = CompletionHandler<void(SecKeyRef, NSArray *, NSError *)>;
 54 using AttestationCallback = CompletionHandler<void(NSArray *, NSError *)>;
5555 using UserConsentCallback = CompletionHandler<void(UserConsent)>;
5656 using UserConsentContextCallback = CompletionHandler<void(UserConsent, LAContext *)>;
5757

@@public:
5959 virtual ~LocalConnection() = default;
6060
6161 // Overrided by MockLocalConnection.
62  virtual void getUserConsent(const String& reason, UserConsentCallback&&) const;
6362 virtual void getUserConsent(const String& reason, SecAccessControlRef, UserConsentContextCallback&&) const;
64  virtual void getAttestation(const String& rpId, const String& username, const Vector<uint8_t>& hash, AttestationCallback&&) const;
 63 virtual RetainPtr<SecKeyRef> createCredentialPrivateKey(LAContext *, SecAccessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const;
 64 virtual void getAttestation(SecKeyRef, NSData *authData, NSData *hash, AttestationCallback&&) const;
6565 virtual NSDictionary *selectCredential(const NSArray *) const;
6666};
6767

Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.mm

2828
2929#if ENABLE(WEB_AUTHN)
3030
31 #import "DeviceIdentitySPI.h"
32 #import <WebCore/ExceptionData.h>
3331#import <wtf/BlockPtr.h>
3432#import <wtf/RunLoop.h>
3533
 34#if USE(APPLE_INTERNAL_SDK)
 35#import <WebKitAdditions/LocalConnectionAdditions.h>
 36#endif
 37
3638#import "LocalAuthenticationSoftLink.h"
3739
3840namespace WebKit {
3941
40 void LocalConnection::getUserConsent(const String& reason, UserConsentCallback&& completionHandler) const
41 {
42  auto context = adoptNS([allocLAContextInstance() init]);
43  auto reply = makeBlockPtr([completionHandler = WTFMove(completionHandler)] (BOOL success, NSError *error) mutable {
44  ASSERT(!RunLoop::isMain());
45 
46  UserConsent consent = UserConsent::Yes;
47  if (!success || error) {
48  LOG_ERROR("Couldn't authenticate with biometrics: %@", error);
49  consent = UserConsent::No;
50  }
51  RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), consent]() mutable {
52  completionHandler(consent);
53  });
54  });
55  [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:reason reply:reply.get()];
56 }
57 
5842void LocalConnection::getUserConsent(const String& reason, SecAccessControlRef accessControl, UserConsentContextCallback&& completionHandler) const
5943{
6044 auto context = adoptNS([allocLAContextInstance() init]);

@@void LocalConnection::getUserConsent(const String& reason, SecAccessControlRef a
7357 [context evaluateAccessControl:accessControl operation:LAAccessControlOperationUseKeySign localizedReason:reason reply:reply.get()];
7458}
7559
76 void LocalConnection::getAttestation(const String& rpId, const String& username, const Vector<uint8_t>& hash, AttestationCallback&& completionHandler) const
 60RetainPtr<SecKeyRef> LocalConnection::createCredentialPrivateKey(LAContext *context, SecAccessControlRef accessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const
7761{
78 #if HAVE(DEVICE_IDENTITY)
79  // Apple Attestation
80  ASSERT(hash.size() <= 32);
81 
82  RetainPtr<SecAccessControlRef> accessControlRef;
83  {
84  CFErrorRef errorRef = nullptr;
85  accessControlRef = adoptCF(SecAccessControlCreateWithFlags(NULL, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlPrivateKeyUsage | kSecAccessControlUserPresence, &errorRef));
86  auto retainError = adoptCF(errorRef);
87  if (errorRef) {
88  LOG_ERROR("Couldn't create ACL: %@", (NSError *)errorRef);
89  completionHandler(NULL, NULL, [NSError errorWithDomain:@"com.apple.WebKit.WebAuthN" code:1 userInfo:nil]);
90  return;
91  }
 62 NSDictionary *attributes = @{
 63 (id)kSecAttrTokenID: (id)kSecAttrTokenIDSecureEnclave,
 64 (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
 65 (id)kSecAttrKeySizeInBits: @256,
 66 (id)kSecPrivateKeyAttrs: @{
 67 (id)kSecUseAuthenticationContext: context,
 68 (id)kSecAttrAccessControl: (id)accessControlRef,
 69 (id)kSecAttrIsPermanent: @YES,
 70 (id)kSecAttrAccessGroup: @"com.apple.webkit.webauthn",
 71 (id)kSecAttrLabel: secAttrLabel,
 72 (id)kSecAttrApplicationTag: secAttrApplicationTag,
 73 }};
 74 CFErrorRef errorRef = nullptr;
 75 auto credentialPrivateKey = adoptCF(SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes, &errorRef));
 76 auto retainError = adoptCF(errorRef);
 77 if (errorRef) {
 78 LOG_ERROR("Couldn't create private key: %@", (NSError *)errorRef);
 79 return nullptr;
9280 }
 81 return credentialPrivateKey;
 82}
9383
94  String label = makeString(username, "@", rpId);
95  NSDictionary *options = @{
96  kMAOptionsBAAKeychainLabel: label,
97  // FIXME(rdar://problem/38489134): Need a formal name.
98  kMAOptionsBAAKeychainAccessGroup: @"com.apple.safari.WebAuthN.credentials",
99  kMAOptionsBAAIgnoreExistingKeychainItems: @YES,
100  // FIXME(rdar://problem/38489134): Determine a proper lifespan.
101  kMAOptionsBAAValidity: @(1440), // Last one day.
102  kMAOptionsBAASCRTAttestation: @NO,
103  kMAOptionsBAANonce: [NSData dataWithBytes:hash.data() length:hash.size()],
104  kMAOptionsBAAAccessControls: (id)accessControlRef.get(),
105  kMAOptionsBAAOIDSToInclude: @[kMAOptionsBAAOIDNonce]
106  };
107 
108  // FIXME(183652): Reduce prompt for biometrics
109  DeviceIdentityIssueClientCertificateWithCompletion(dispatch_get_main_queue(), options, makeBlockPtr(WTFMove(completionHandler)).get());
110 #endif // HAVE(DEVICE_IDENTITY)
 84void LocalConnection::getAttestation(SecKeyRef privateKey, NSData *authData, NSData *hash, AttestationCallback&& completionHandler) const
 85{
 86#if defined(LOCALCONNECTION_ADDITIONS)
 87LOCALCONNECTION_ADDITIONS
 88#endif
11189}
11290
11391NSDictionary *LocalConnection::selectCredential(const NSArray *credentials) const

Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalService.mm

3131#import "LocalAuthenticator.h"
3232#import "LocalConnection.h"
3333
 34#if USE(APPLE_INTERNAL_SDK)
 35#import <WebKitAdditions/LocalServiceAdditions.h>
 36#endif
 37
3438#import "LocalAuthenticationSoftLink.h"
3539
3640namespace WebKit {

@@LocalService::LocalService(Observer& observer)
4044{
4145}
4246
43 // FIXME(rdar://problem/51048542)
4447bool LocalService::isAvailable()
4548{
4649 auto context = adoptNS([allocLAContextInstance() init]);

@@bool LocalService::isAvailable()
4952 LOG_ERROR("Couldn't find local authenticators: %@", error);
5053 return false;
5154 }
 55
 56#if defined(LOCALSERVICE_ADDITIONS)
 57LOCALSERVICE_ADDITIONS
 58#endif
 59
5260 return true;
5361}
5462

Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.h

@@public:
3737 explicit MockLocalConnection(const WebCore::MockWebAuthenticationConfiguration&);
3838
3939private:
40  void getUserConsent(const String& reason, UserConsentCallback&&) const final;
4140 void getUserConsent(const String& reason, SecAccessControlRef, UserConsentContextCallback&&) const final;
42  void getAttestation(const String& rpId, const String& username, const Vector<uint8_t>& hash, AttestationCallback&&) const final;
 41 RetainPtr<SecKeyRef> createCredentialPrivateKey(LAContext *, SecAccessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const final;
 42 void getAttestation(SecKeyRef, NSData *authData, NSData *hash, AttestationCallback&&) const final;
4343 NSDictionary *selectCredential(const NSArray *) const final;
4444
4545 WebCore::MockWebAuthenticationConfiguration m_configuration;

Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.mm

@@MockLocalConnection::MockLocalConnection(const WebCore::MockWebAuthenticationCon
4343{
4444}
4545
46 void MockLocalConnection::getUserConsent(const String&, UserConsentCallback&& callback) const
47 {
48  // Mock async operations.
49  RunLoop::main().dispatch([configuration = m_configuration, callback = WTFMove(callback)]() mutable {
50  ASSERT(configuration.local);
51  if (!configuration.local->acceptAuthentication) {
52  callback(UserConsent::No);
53  return;
54  }
55  callback(UserConsent::Yes);
56  });
57 }
58 
5946void MockLocalConnection::getUserConsent(const String&, SecAccessControlRef, UserConsentContextCallback&& callback) const
6047{
6148 // Mock async operations.

@@void MockLocalConnection::getUserConsent(const String&, SecAccessControlRef, Use
6956 });
7057}
7158
72 void MockLocalConnection::getAttestation(const String& rpId, const String& username, const Vector<uint8_t>& hash, AttestationCallback&& callback) const
 59RetainPtr<SecKeyRef> MockLocalConnection::createCredentialPrivateKey(LAContext *, SecAccessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const
7360{
74  // Mock async operations.
75  RunLoop::main().dispatch([configuration = m_configuration, rpId, username, hash, callback = WTFMove(callback)]() mutable {
76  ASSERT(configuration.local);
77  if (!configuration.local->acceptAttestation) {
78  callback(NULL, NULL, [NSError errorWithDomain:@"WebAuthentication" code:-1 userInfo:@{ NSLocalizedDescriptionKey: @"The operation couldn't complete." }]);
79  return;
80  }
81 
82  // Get Key and add it to Keychain.
83  NSDictionary* options = @{
84  (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
85  (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
86  (id)kSecAttrKeySizeInBits: @256,
87  };
88  CFErrorRef errorRef = nullptr;
89  auto key = adoptCF(SecKeyCreateWithData(
90  (__bridge CFDataRef)adoptNS([[NSData alloc] initWithBase64EncodedString:configuration.local->privateKeyBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get(),
91  (__bridge CFDictionaryRef)options,
92  &errorRef
93  ));
94  if (errorRef) {
95  callback(NULL, NULL, (NSError *)errorRef);
96  return;
97  }
98 
99  // Mock what DeviceIdentity would do.
100  String label = makeString(username, "@", rpId, "-rk-ucrt");
101  NSDictionary* addQuery = @{
102  (id)kSecValueRef: (id)key.get(),
103  (id)kSecClass: (id)kSecClassKey,
104  (id)kSecAttrLabel: (id)label,
105  (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
 61 ASSERT(m_configuration.local);
 62
 63 // Get Key and add it to Keychain.
 64 NSDictionary* options = @{
 65 (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
 66 (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
 67 (id)kSecAttrKeySizeInBits: @256,
 68 };
 69 CFErrorRef errorRef = nullptr;
 70 auto key = adoptCF(SecKeyCreateWithData(
 71 (__bridge CFDataRef)adoptNS([[NSData alloc] initWithBase64EncodedString:m_configuration.local->privateKeyBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get(),
 72 (__bridge CFDictionaryRef)options,
 73 &errorRef
 74 ));
 75 if (errorRef)
 76 return nullptr;
 77
 78 NSDictionary* addQuery = @{
 79 (id)kSecValueRef: (id)key.get(),
 80 (id)kSecClass: (id)kSecClassKey,
 81 (id)kSecAttrLabel: secAttrLabel,
 82 (id)kSecAttrApplicationTag: secAttrApplicationTag,
 83 (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
10684#if HAVE(DATA_PROTECTION_KEYCHAIN)
107  (id)kSecUseDataProtectionKeychain: @YES
 85 (id)kSecUseDataProtectionKeychain: @YES
10886#else
109  (id)kSecAttrNoLegacy: @YES
 87 (id)kSecAttrNoLegacy: @YES
11088#endif
111  };
112  OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
113  if (status) {
114  callback(NULL, NULL, [NSError errorWithDomain:@"WebAuthentication" code:status userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Couldn't add the key to the keychain. %d", status] }]);
 89 };
 90 OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
 91 if (status) {
 92 LOG_ERROR("Couldn't add the key to the keychain. %d", status);
 93 return nullptr;
 94 }
 95
 96 return key;
 97}
 98
 99void MockLocalConnection::getAttestation(SecKeyRef, NSData *, NSData *, AttestationCallback&& callback) const
 100{
 101 // Mock async operations.
 102 RunLoop::main().dispatch([configuration = m_configuration, callback = WTFMove(callback)]() mutable {
 103 ASSERT(configuration.local);
 104 if (!configuration.local->acceptAttestation) {
 105 callback(NULL, [NSError errorWithDomain:@"WebAuthentication" code:-1 userInfo:@{ NSLocalizedDescriptionKey: @"The operation couldn't complete." }]);
115106 return;
116107 }
117108
118109 auto attestationCertificate = adoptCF(SecCertificateCreateWithData(NULL, (__bridge CFDataRef)adoptNS([[NSData alloc] initWithBase64EncodedString:configuration.local->userCertificateBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get()));
119110 auto attestationIssuingCACertificate = adoptCF(SecCertificateCreateWithData(NULL, (__bridge CFDataRef)adoptNS([[NSData alloc] initWithBase64EncodedString:configuration.local->intermediateCACertificateBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get()));
120 
121  callback(key.get(), [NSArray arrayWithObjects: (__bridge id)attestationCertificate.get(), (__bridge id)attestationIssuingCACertificate.get(), nil], NULL);
 111 callback([NSArray arrayWithObjects: (__bridge id)attestationCertificate.get(), (__bridge id)attestationIssuingCACertificate.get(), nil], NULL);
122112 });
123113}
124114

Source/WebKit/WebKit.xcodeproj/project.pbxproj

11231123 57DCED6F2142EE630016B847 /* WebAuthenticatorCoordinatorMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCED6A2142EAE20016B847 /* WebAuthenticatorCoordinatorMessages.h */; };
11241124 57DCED702142EE680016B847 /* WebAuthenticatorCoordinatorProxyMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57DCED6C2142EAF90016B847 /* WebAuthenticatorCoordinatorProxyMessageReceiver.cpp */; };
11251125 57DCED712142EE6C0016B847 /* WebAuthenticatorCoordinatorProxyMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCED6D2142EAFA0016B847 /* WebAuthenticatorCoordinatorProxyMessages.h */; };
1126  57DCEDAB214C60090016B847 /* DeviceIdentitySPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCEDAA214B9B430016B847 /* DeviceIdentitySPI.h */; };
11271126 57DCEDAC214C60270016B847 /* LocalAuthenticator.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCEDA12149C1E20016B847 /* LocalAuthenticator.h */; };
11281127 57DCEDAD214C602C0016B847 /* LocalConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCEDA7214A568B0016B847 /* LocalConnection.h */; };
11291128 57DCEDAE214C60330016B847 /* LocalService.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCED9A2148B0830016B847 /* LocalService.h */; };

38683867 57DCEDA62149F9DA0016B847 /* WebAuthenticationRequestData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebAuthenticationRequestData.h; sourceTree = "<group>"; };
38693868 57DCEDA7214A568B0016B847 /* LocalConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocalConnection.h; sourceTree = "<group>"; };
38703869 57DCEDA8214A568B0016B847 /* LocalConnection.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LocalConnection.mm; sourceTree = "<group>"; };
3871  57DCEDAA214B9B430016B847 /* DeviceIdentitySPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeviceIdentitySPI.h; sourceTree = "<group>"; };
38723870 57DCEDC1214F114C0016B847 /* MockLocalService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockLocalService.h; sourceTree = "<group>"; };
38733871 57DCEDC2214F114C0016B847 /* MockLocalService.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MockLocalService.mm; sourceTree = "<group>"; };
38743872 57DCEDC5214F18300016B847 /* MockLocalConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockLocalConnection.h; sourceTree = "<group>"; };

68146812 children = (
68156813 1A5705101BE410E500874AF1 /* BlockSPI.h */,
68166814 37C21CAD1E994C0C0029D5F9 /* CorePredictionSPI.h */,
6817  57DCEDAA214B9B430016B847 /* DeviceIdentitySPI.h */,
68186815 2DAADA8E2298C21000E36B0C /* DeviceManagementSPI.h */,
68196816 57B826402304EB3E00B72EB0 /* NearFieldSPI.h */,
68206817 3754D5441B3A29FD003A4C7F /* NSInvocationSPI.h */,

1024510242 1AC75380183BE50F0072CB15 /* DataReference.h in Headers */,
1024610243 99036AE923A970870000B06A /* DebuggableInfoData.h in Headers */,
1024710244 BC032DA610F437D10058C15A /* Decoder.h in Headers */,
10248  57DCEDAB214C60090016B847 /* DeviceIdentitySPI.h in Headers */,
1024910245 07297F9F1C17BBEA015F0735 /* DeviceIdHashSaltStorage.h in Headers */,
1025010246 2D0C56FD229F1DEA00BD33E7 /* DeviceManagementSoftLink.h in Headers */,
1025110247 2DAADA8F2298C21000E36B0C /* DeviceManagementSPI.h in Headers */,

LayoutTests/ChangeLog

 12020-02-19 Jiewen Tan <jiewen_tan@apple.com>
 2
 3 [WebAuthn] Replace DeviceIdentity.framework
 4 https://bugs.webkit.org/show_bug.cgi?id=207985
 5 <rdar://problem/59369223>
 6
 7 Reviewed by Brent Fulgham.
 8
 9 * http/wpt/webauthn/public-key-credential-create-failure-local.https-expected.txt:
 10 * http/wpt/webauthn/public-key-credential-create-failure-local.https.html:
 11 * http/wpt/webauthn/public-key-credential-create-success-local.https.html:
 12
1132020-02-19 Youenn Fablet <youenn@apple.com>
214
315 Add support for AudioSession handling in GPUProcess for capture

LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https-expected.txt

@@PASS PublicKeyCredential's [[create]] with unsupported public key credential par
33PASS PublicKeyCredential's [[create]] with matched exclude credentials in a mock local authenticator.
44PASS PublicKeyCredential's [[create]] with matched exclude credentials in a mock local authenticator. 2nd
55PASS PublicKeyCredential's [[create]] without user consent in a mock local authenticator.
 6PASS PublicKeyCredential's [[create]] without private keys in a mock local authenticator.
67PASS PublicKeyCredential's [[create]] without attestation in a mock local authenticator.
78PASS PublicKeyCredential's [[create]] deleting old credential in a mock local authenticator.
89PASS PublicKeyCredential's [[create]] with timeout in a mock local authenticator.

LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https.html

118118 };
119119 if (window.internals)
120120 internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
 121 return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Couldn't create private key.");
 122 }, "PublicKeyCredential's [[create]] without private keys in a mock local authenticator.");
 123
 124 promise_test(t => {
 125 const options = {
 126 publicKey: {
 127 rp: {
 128 name: "example.com"
 129 },
 130 user: {
 131 name: "John Appleseed",
 132 id: Base64URL.parse(testUserhandleBase64),
 133 displayName: "John",
 134 },
 135 challenge: asciiToUint8Array("123456"),
 136 pubKeyCredParams: [{ type: "public-key", alg: -7 }]
 137 }
 138 };
 139 if (window.internals)
 140 internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false, privateKeyBase64: privateKeyBase64 } });
121141 return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Couldn't attest: The operation couldn't complete.");
122142 }, "PublicKeyCredential's [[create]] without attestation in a mock local authenticator.");
123143

140160 internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
141161 testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
142162 }
143  return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Couldn't attest: The operation couldn't complete.").then(() => {
 163 return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Couldn't create private key.").then(() => {
144164 if (window.testRunner)
145165 assert_false(testRunner.keyExistsInKeychain(testRpId, userhandleBase64));
146166 });

LayoutTests/http/wpt/webauthn/public-key-credential-create-success-local.https.html

4141 if (isNoneAttestation)
4242 assert_equals(attestationObject.fmt, "none");
4343 else
44  assert_equals(attestationObject.fmt, "Apple");
 44 assert_equals(attestationObject.fmt, "apple");
4545 // Check authData
4646 const authData = decodeAuthData(attestationObject.authData);
4747 assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");

5858 assert_equals(attestationObject.attStmt.x5c.length, 2);
5959 assert_array_equals(attestationObject.attStmt.x5c[0], Base64URL.parse(testAttestationCertificateBase64));
6060 assert_array_equals(attestationObject.attStmt.x5c[1], Base64URL.parse(testAttestationIssuingCACertificateBase64));
61 
62  // Check signature
63  let publicKeyData = new Uint8Array(65);
64  publicKeyData[0] = 0x04;
65  publicKeyData.set(authData.publicKey['-2'], 1);
66  publicKeyData.set(authData.publicKey['-3'], 33);
67  return crypto.subtle.importKey("raw", publicKeyData, {
68  name: "ECDSA",
69  namedCurve: "P-256"
70  }, false, ['verify']).then(publicKey => {
71  return crypto.subtle.verify({
72  name: "ECDSA",
73  hash: "SHA-256"
74  }, publicKey, extractRawSignature(attestationObject.attStmt.sig), attestationObject.authData).then(verified => {
75  assert_true(verified);
76  });
77  });
7861 }
7962 }
8063