Changeset 257085 in webkit
- Timestamp:
- Feb 20, 2020 2:05:59 PM (4 years ago)
- Location:
- trunk
- Files:
-
- 1 deleted
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r257082 r257085 1 2020-02-20 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 1 13 2020-02-20 Jason Lawrence <lawrence.j@apple.com> 2 14 -
trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https-expected.txt
r236842 r257085 4 4 PASS PublicKeyCredential's [[create]] with matched exclude credentials in a mock local authenticator. 2nd 5 5 PASS PublicKeyCredential's [[create]] without user consent in a mock local authenticator. 6 PASS PublicKeyCredential's [[create]] without private keys in a mock local authenticator. 6 7 PASS PublicKeyCredential's [[create]] without attestation in a mock local authenticator. 7 8 PASS PublicKeyCredential's [[create]] deleting old credential in a mock local authenticator. -
trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https.html
r254894 r257085 119 119 if (window.internals) 120 120 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 } }); 121 141 return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Couldn't attest: The operation couldn't complete."); 122 142 }, "PublicKeyCredential's [[create]] without attestation in a mock local authenticator."); … … 141 161 testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64); 142 162 } 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(() => { 144 164 if (window.testRunner) 145 165 assert_false(testRunner.keyExistsInKeychain(testRpId, userhandleBase64)); -
trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-local.https.html
r250940 r257085 42 42 assert_equals(attestationObject.fmt, "none"); 43 43 else 44 assert_equals(attestationObject.fmt, " Apple");44 assert_equals(attestationObject.fmt, "apple"); 45 45 // Check authData 46 46 const authData = decodeAuthData(attestationObject.authData); … … 59 59 assert_array_equals(attestationObject.attStmt.x5c[0], Base64URL.parse(testAttestationCertificateBase64)); 60 60 assert_array_equals(attestationObject.attStmt.x5c[1], Base64URL.parse(testAttestationIssuingCACertificateBase64)); 61 62 // Check signature63 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 });78 61 } 79 62 } -
trunk/Source/WTF/ChangeLog
r257083 r257085 1 2020-02-20 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 1 11 2020-02-20 Eric Liang <ericliang@apple.com> 2 12 -
trunk/Source/WTF/wtf/PlatformHave.h
r257083 r257085 558 558 #endif 559 559 560 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR))561 #define HAVE_DEVICE_IDENTITY 1562 #endif563 564 560 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101600) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 140000) 565 561 #define HAVE_COOKIE_CHANGE_LISTENER_API 1 -
trunk/Source/WebKit/ChangeLog
r257083 r257085 1 2020-02-20 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 1 42 2020-02-20 Eric Liang <ericliang@apple.com> 2 43 -
trunk/Source/WebKit/Configurations/WebKit.xcconfig
r255189 r257085 61 61 WK_CORE_SERVICES_LDFLAGS_macosx = -framework CoreServices; 62 62 63 WK_DEVICE_IDENTITY_LDFLAGS = $(WK_DEVICE_IDENTITY_LDFLAGS_$(WK_HAVE_DEVICE_IDENTITY));64 WK_DEVICE_IDENTITY_LDFLAGS_YES = -framework DeviceIdentity;65 66 63 WK_GRAPHICS_SERVICES_LDFLAGS = $(WK_GRAPHICS_SERVICES_LDFLAGS_$(WK_COCOA_TOUCH)); 67 64 WK_GRAPHICS_SERVICES_LDFLAGS_cocoatouch = -framework GraphicsServices; … … 124 121 WK_AUTHKIT_LDFLAGS_MACOS_SINCE_1015 = -framework AuthKit; 125 122 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);123 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_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); 127 124 128 125 // Prevent C++ standard library basic_stringstream, operator new, delete and their related exception types from being exported as weak symbols. … … 162 159 WK_RELOCATABLE_FRAMEWORK_LDFLAGS_YES_macosx = -Wl,-not_for_dyld_shared_cache; 163 160 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 169 161 WK_HAVE_URL_FORMATTING = $(WK_HAVE_URL_FORMATTING_$(WK_PLATFORM_NAME)); 170 162 WK_HAVE_URL_FORMATTING_iphoneos = $(WK_HAVE_URL_FORMATTING$(WK_IOS_12)); -
trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h
r236481 r257085 57 57 58 58 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 *); 61 61 62 62 void getAssertion() final; -
trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm
r254894 r257085 81 81 } 82 82 83 static 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 83 89 } // LocalAuthenticatorInternal 84 90 … … 100 106 // Step 8 is implicitly captured by all UnknownError exception receiveResponds. 101 107 // 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 })) { 110 111 receiveRespond(ExceptionData { NotSupportedError, "The platform attached authenticator doesn't support any provided PublicKeyCredentialParameters."_s }); 111 112 return; … … 137 138 auto retainAttributesArray = adoptCF(attributesArrayRef); 138 139 140 // FIXME(rdar://problem/35900593): Need to obtain user consent and then return different error according to the result. 139 141 for (NSDictionary *nsAttributes in (NSArray *)attributesArrayRef) { 140 142 NSData *nsCredentialId = nsAttributes[(id)kSecAttrApplicationLabel]; … … 149 151 // FIXME(rdar://problem/35900593): Update to a formal UI. 150 152 // 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) { 152 167 ASSERT(RunLoop::isMain()); 153 168 if (!weakThis) 154 169 return; 155 170 156 weakThis->continueMakeCredentialAfterUserConsented( consent);171 weakThis->continueMakeCredentialAfterUserConsented(accessControl.get(), consent, context); 157 172 }; 158 173 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, 160 176 WTFMove(callback)); 161 177 } 162 178 163 void LocalAuthenticator::continueMakeCredentialAfterUserConsented(LocalConnection::UserConsent consent) 164 { 179 void LocalAuthenticator::continueMakeCredentialAfterUserConsented(SecAccessControlRef accessControlRef, LocalConnection::UserConsent consent, LAContext *context) 180 { 181 using namespace LocalAuthenticatorInternal; 182 165 183 ASSERT(m_state == State::RequestReceived); 166 184 m_state = State::UserConsented; … … 172 190 } 173 191 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 174 202 // Step 7.5. 175 // Userhandle is stored in kSecAttrApplicationTag attribute.176 203 // Failures after this point could block users' accounts forever. Should we follow the spec? 177 204 NSDictionary* deleteQuery = @{ 178 205 (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(), 181 208 #if HAVE(DATA_PROTECTION_KEYCHAIN) 182 209 (id)kSecUseDataProtectionKeychain: @YES … … 192 219 } 193 220 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)) }); 215 return; 216 } 217 // Attestation Certificate and Attestation Issuing CA 218 ASSERT(certificates && ([certificates count] == 2)); 219 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. 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 }); 226 return; 227 } 228 233 229 Vector<uint8_t> credentialId; 234 230 { 235 // -rk-ucrt is added by DeviceIdentity.Framework.236 String label = makeString(creationOptions.user.name, "@", creationOptions.rp.id, "-rk-ucrt");237 231 NSDictionary *credentialIdQuery = @{ 238 232 (id)kSecClass: (id)kSecClassKey, 239 233 (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate, 240 (id)kSecAttrLabel: label, 234 (id)kSecAttrLabel: secAttrLabel, 235 (id)kSecAttrApplicationTag: secAttrApplicationTag.get(), 241 236 (id)kSecReturnAttributes: @YES, 242 237 #if HAVE(DATA_PROTECTION_KEYCHAIN) … … 257 252 NSDictionary *nsAttributes = (NSDictionary *)attributesRef; 258 253 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: @YES266 #else267 (id)kSecAttrNoLegacy: @YES268 #endif269 };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 }280 254 } 281 255 … … 290 264 RetainPtr<CFDataRef> publicKeyDataRef; 291 265 { 292 auto publicKey = adoptCF(SecKeyCopyPublicKey(privateKey ));266 auto publicKey = adoptCF(SecKeyCopyPublicKey(privateKey.get())); 293 267 CFErrorRef errorRef = nullptr; 294 268 publicKeyDataRef = adoptCF(SecKeyCopyExternalRepresentation(publicKey.get(), &errorRef)); … … 315 289 auto authData = buildAuthData(creationOptions.rp.id, makeCredentialFlags, counter, attestedCredentialData); 316 290 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 303 void 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 317 319 // Step 13. Apple Attestation Cont' 318 320 // Assemble the attestation object: … … 320 322 cbor::CBORValue::MapValue attestationStatementMap; 321 323 { 322 Vector<uint8_t> signature;323 {324 CFErrorRef errorRef = nullptr;325 // FIXME(183652): Reduce prompt for biometrics326 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 }335 324 attestationStatementMap[cbor::CBORValue("alg")] = cbor::CBORValue(COSE::ES256); 336 attestationStatementMap[cbor::CBORValue("sig")] = cbor::CBORValue(signature);337 325 Vector<cbor::CBORValue> cborArray; 338 326 for (size_t i = 0; i < [certificates count]; i++) … … 340 328 attestationStatementMap[cbor::CBORValue("x5c")] = cbor::CBORValue(WTFMove(cborArray)); 341 329 } 342 auto attestationObject = buildAttestationObject(WTFMove(authData), " Apple", WTFMove(attestationStatementMap), creationOptions.attestation);330 auto attestationObject = buildAttestationObject(WTFMove(authData), "apple", WTFMove(attestationStatementMap), creationOptions.attestation); 343 331 344 332 receiveRespond(AuthenticatorAttestationResponse::create(credentialId, attestationObject)); … … 357 345 // Step 12 is implicitly captured by all UnknownError exception callbacks. 358 346 // 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. 359 348 auto allowCredentialIds = produceHashSet(requestOptions.allowCredentials); 360 349 if (!requestOptions.allowCredentials.isEmpty() && allowCredentialIds.isEmpty()) { … … 449 438 (id)kSecClass: (id)kSecClassKey, 450 439 (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate, 451 (id)kSecAttrApplicationLabel: [NSData dataWithBytes:credentialId.data() length:credentialId.size()],440 (id)kSecAttrApplicationLabel: toNSData(credentialId).get(), 452 441 (id)kSecUseAuthenticationContext: context, 453 442 (id)kSecReturnRef: @YES, -
trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.h
r250249 r257085 52 52 }; 53 53 54 using AttestationCallback = CompletionHandler<void( SecKeyRef,NSArray *, NSError *)>;54 using AttestationCallback = CompletionHandler<void(NSArray *, NSError *)>; 55 55 using UserConsentCallback = CompletionHandler<void(UserConsent)>; 56 56 using UserConsentContextCallback = CompletionHandler<void(UserConsent, LAContext *)>; … … 60 60 61 61 // Overrided by MockLocalConnection. 62 virtual void getUserConsent(const String& reason, UserConsentCallback&&) const;63 62 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; 65 65 virtual NSDictionary *selectCredential(const NSArray *) const; 66 66 }; -
trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.mm
r250249 r257085 29 29 #if ENABLE(WEB_AUTHN) 30 30 31 #import "DeviceIdentitySPI.h"32 #import <WebCore/ExceptionData.h>33 31 #import <wtf/BlockPtr.h> 34 32 #import <wtf/RunLoop.h> 33 34 #if USE(APPLE_INTERNAL_SDK) 35 #import <WebKitAdditions/LocalConnectionAdditions.h> 36 #endif 35 37 36 38 #import "LocalAuthenticationSoftLink.h" 37 39 38 40 namespace WebKit { 39 40 void LocalConnection::getUserConsent(const String& reason, UserConsentCallback&& completionHandler) const41 {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 41 58 42 void LocalConnection::getUserConsent(const String& reason, SecAccessControlRef accessControl, UserConsentContextCallback&& completionHandler) const … … 74 58 } 75 59 76 void LocalConnection::getAttestation(const String& rpId, const String& username, const Vector<uint8_t>& hash, AttestationCallback&& completionHandler) const60 RetainPtr<SecKeyRef> LocalConnection::createCredentialPrivateKey(LAContext *context, SecAccessControlRef accessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const 77 61 { 78 #if HAVE(DEVICE_IDENTITY) 79 // Apple Attestation 80 ASSERT(hash.size() <= 32); 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; 80 } 81 return credentialPrivateKey; 82 } 81 83 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 } 92 } 93 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) 84 void LocalConnection::getAttestation(SecKeyRef privateKey, NSData *authData, NSData *hash, AttestationCallback&& completionHandler) const 85 { 86 #if defined(LOCALCONNECTION_ADDITIONS) 87 LOCALCONNECTION_ADDITIONS 88 #endif 111 89 } 112 90 -
trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalService.mm
r251041 r257085 32 32 #import "LocalConnection.h" 33 33 34 #if USE(APPLE_INTERNAL_SDK) 35 #import <WebKitAdditions/LocalServiceAdditions.h> 36 #endif 37 34 38 #import "LocalAuthenticationSoftLink.h" 35 39 … … 41 45 } 42 46 43 // FIXME(rdar://problem/51048542)44 47 bool LocalService::isAvailable() 45 48 { … … 50 53 return false; 51 54 } 55 56 #if defined(LOCALSERVICE_ADDITIONS) 57 LOCALSERVICE_ADDITIONS 58 #endif 59 52 60 return true; 53 61 } -
trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.h
r250940 r257085 38 38 39 39 private: 40 void getUserConsent(const String& reason, UserConsentCallback&&) const final;41 40 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; 43 43 NSDictionary *selectCredential(const NSArray *) const final; 44 44 -
trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.mm
r255466 r257085 44 44 } 45 45 46 void MockLocalConnection::getUserConsent(const String&, UserConsentCallback&& callback) const47 {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 59 46 void MockLocalConnection::getUserConsent(const String&, SecAccessControlRef, UserConsentContextCallback&& callback) const 60 47 { … … 70 57 } 71 58 72 void MockLocalConnection::getAttestation(const String& rpId, const String& username, const Vector<uint8_t>& hash, AttestationCallback&& callback) const 59 RetainPtr<SecKeyRef> MockLocalConnection::createCredentialPrivateKey(LAContext *, SecAccessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const 60 { 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, 84 #if HAVE(DATA_PROTECTION_KEYCHAIN) 85 (id)kSecUseDataProtectionKeychain: @YES 86 #else 87 (id)kSecAttrNoLegacy: @YES 88 #endif 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 99 void MockLocalConnection::getAttestation(SecKeyRef, NSData *, NSData *, AttestationCallback&& callback) const 73 100 { 74 101 // Mock async operations. 75 RunLoop::main().dispatch([configuration = m_configuration, rpId, username, hash,callback = WTFMove(callback)]() mutable {102 RunLoop::main().dispatch([configuration = m_configuration, callback = WTFMove(callback)]() mutable { 76 103 ASSERT(configuration.local); 77 104 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, 106 #if HAVE(DATA_PROTECTION_KEYCHAIN) 107 (id)kSecUseDataProtectionKeychain: @YES 108 #else 109 (id)kSecAttrNoLegacy: @YES 110 #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] }]); 105 callback(NULL, [NSError errorWithDomain:@"WebAuthentication" code:-1 userInfo:@{ NSLocalizedDescriptionKey: @"The operation couldn't complete." }]); 115 106 return; 116 107 } … … 118 109 auto attestationCertificate = adoptCF(SecCertificateCreateWithData(NULL, (__bridge CFDataRef)adoptNS([[NSData alloc] initWithBase64EncodedString:configuration.local->userCertificateBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get())); 119 110 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); 122 112 }); 123 113 } -
trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj
r256934 r257085 1124 1124 57DCED702142EE680016B847 /* WebAuthenticatorCoordinatorProxyMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57DCED6C2142EAF90016B847 /* WebAuthenticatorCoordinatorProxyMessageReceiver.cpp */; }; 1125 1125 57DCED712142EE6C0016B847 /* WebAuthenticatorCoordinatorProxyMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCED6D2142EAFA0016B847 /* WebAuthenticatorCoordinatorProxyMessages.h */; }; 1126 57DCEDAB214C60090016B847 /* DeviceIdentitySPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCEDAA214B9B430016B847 /* DeviceIdentitySPI.h */; };1127 1126 57DCEDAC214C60270016B847 /* LocalAuthenticator.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCEDA12149C1E20016B847 /* LocalAuthenticator.h */; }; 1128 1127 57DCEDAD214C602C0016B847 /* LocalConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCEDA7214A568B0016B847 /* LocalConnection.h */; }; … … 3869 3868 57DCEDA7214A568B0016B847 /* LocalConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocalConnection.h; sourceTree = "<group>"; }; 3870 3869 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>"; };3872 3870 57DCEDC1214F114C0016B847 /* MockLocalService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockLocalService.h; sourceTree = "<group>"; }; 3873 3871 57DCEDC2214F114C0016B847 /* MockLocalService.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MockLocalService.mm; sourceTree = "<group>"; }; … … 6815 6813 1A5705101BE410E500874AF1 /* BlockSPI.h */, 6816 6814 37C21CAD1E994C0C0029D5F9 /* CorePredictionSPI.h */, 6817 57DCEDAA214B9B430016B847 /* DeviceIdentitySPI.h */,6818 6815 2DAADA8E2298C21000E36B0C /* DeviceManagementSPI.h */, 6819 6816 57B826402304EB3E00B72EB0 /* NearFieldSPI.h */, … … 10246 10243 99036AE923A970870000B06A /* DebuggableInfoData.h in Headers */, 10247 10244 BC032DA610F437D10058C15A /* Decoder.h in Headers */, 10248 57DCEDAB214C60090016B847 /* DeviceIdentitySPI.h in Headers */,10249 10245 07297F9F1C17BBEA015F0735 /* DeviceIdHashSaltStorage.h in Headers */, 10250 10246 2D0C56FD229F1DEA00BD33E7 /* DeviceManagementSoftLink.h in Headers */,
Note: See TracChangeset
for help on using the changeset viewer.