[WebAuthN] Implement authenticatorMakeCredential
authorjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 18 Mar 2018 01:12:48 +0000 (01:12 +0000)
committerjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 18 Mar 2018 01:12:48 +0000 (01:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=183527
<rdar://problem/35275886>

Reviewed by Brent Fulgham.

Source/WebCore:

This patch does the following few things:
1) It implements the authenticatorMakeCredential logic from the spec: https://www.w3.org/TR/webauthn/#op-make-cred.
2) It tweaks enocding and deocding of PublicKeyCredentialCreationOptions between UIProccess and WebProcess.
3) It soft links LocalAuthentication.Framework to WebCore, which was linked to WebKit.
4) It creates SPI header for DeviceIdentity.Framework, and provides stubs to link it to WebCore.

Here is a detailed explanantion of 1):
1. A helper class called LocalAuthenticator is crafted to represent Apple platform attached authenticator, i.e.
the devices themselves. All operations are currently restricted to iOS at this moment as macOS lacks attestation
support.
2. To do testing, this helper class is then moved from WebKit to WebCore even though all operations can only happens
in the UIProcess. We currently lack the ability to pretend a https environment in TestWebKitAPI which is required by
the WebAuthN API, and thus it is moved to WebCore to perform unit tesing flavor API tests. This is not enough as it
can't test message exchange between the UI and Web processes. We will address this in a subsequent patch.
3. More on testing: The attestation process is abstracted into a protected method such that the testing enviroment can
override it with self attestation as network access is restricted in the WebKit testing enviroment. Also, swizzlers of
LocalAuthentication API are provided to override the behavoir of LAContext.
4. More on testing: The actual Apple attestation can only happen in real device and with network access, therefore
it can only be covered by manual tests at this moment.
5. Back to LocalAuthenticator, it currently has two public methods:
        5.1. makeCredential(): This method is the one does all the magic.
        + It first checks some parameters.
        + It then invokes LAContext to get user consent.
        + It then talks to Apple Attestation Privacy CA to do attestations.
        + It then stores necessary information into the Keychain.
        + Finally it generates the attestation object.
        5.2 isAvailable():
        To check if a LocalAuthenticator is available or not.
6. Even though files are of .mm format, they are written in a way that mixes NS, CF and C++ types. Here is the rule:
        6.1 Use CF type only if it is requested by APIs.
        6.2 Use NS type to manipulate all Objc objects.
        6.3 Use C++ otherwise.

Covered by API tests.

* Configurations/WebCore.xcconfig:
* Modules/credentialmanagement/CredentialsMessenger.cpp:
(WebCore::getIdFromAttestationObject): Deleted.
Decoding attestation object is tedious. UIProcess will instead return credential ID and attestation object
at the same time. Therefore, this method is removed.
* Modules/credentialmanagement/CredentialsMessenger.h:
(WebCore::CreationReturnBundle::CreationReturnBundle): Deleted.
(WebCore::AssertionReturnBundle::AssertionReturnBundle): Deleted.
* Modules/webauthn/COSEConstants.h: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialType.h.
* Modules/webauthn/PublicKeyCredentialCreationOptions.h:
(WebCore::PublicKeyCredentialCreationOptions::isolatedPartialCopyPtr const):
(WebCore::PublicKeyCredentialCreationOptions::Parameters::encode const):
(WebCore::PublicKeyCredentialCreationOptions::Parameters::decode):
(WebCore::PublicKeyCredentialCreationOptions::encode const):
(WebCore::PublicKeyCredentialCreationOptions::decode):
* Modules/webauthn/PublicKeyCredentialDescriptor.h:
(WebCore::PublicKeyCredentialDescriptor::encode const):
(WebCore::PublicKeyCredentialDescriptor::decode):
* Modules/webauthn/PublicKeyCredentialType.h:
* Modules/webauthn/cocoa/LocalAuthenticator.h: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialCreationOptions.h.
* Modules/webauthn/cocoa/LocalAuthenticator.mm: Added.
(WebCore::LocalAuthenticatorInternal::freePtrs):
(WebCore::LocalAuthenticator::makeCredential const):
(WebCore::LocalAuthenticator::isAvailable const):
(WebCore::LocalAuthenticator::issueClientCertificate const):
* SourcesCocoa.txt:
* WebCore.xcodeproj/project.pbxproj:
* platform/cocoa/LocalAuthenticationSoftLink.h: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialType.h.
* platform/cocoa/LocalAuthenticationSoftLink.mm: Added.
* testing/MockCredentialsMessenger.cpp:
(WebCore::MockCredentialsMessenger::setCreationReturnBundle):
(WebCore::MockCredentialsMessenger::makeCredential):
(WebCore::MockCredentialsMessenger::makeCredentialReply):
(WebCore::MockCredentialsMessenger::setAttestationObject): Deleted.
* testing/MockCredentialsMessenger.h:
* testing/MockCredentialsMessenger.idl:

Source/WebCore/PAL:

* PAL.xcodeproj/project.pbxproj:
* pal/spi/cocoa/DeviceIdentitySPI.h: Copied from Source/WebKit/UIProcess/CredentialManagement/WebCredentialsMessengerProxy.h.

Source/WebKit:

* UIProcess/CredentialManagement/WebCredentialsMessengerProxy.cpp:
(WebKit::WebCredentialsMessengerProxy::WebCredentialsMessengerProxy):
(WebKit::WebCredentialsMessengerProxy::makeCredential):
(WebKit::WebCredentialsMessengerProxy::getAssertion):
(WebKit::WebCredentialsMessengerProxy::isUserVerifyingPlatformAuthenticatorAvailable):
(WebKit::WebCredentialsMessengerProxy::exceptionReply):
(WebKit::WebCredentialsMessengerProxy::makeCredentialReply):
* UIProcess/CredentialManagement/WebCredentialsMessengerProxy.h:
* UIProcess/CredentialManagement/WebCredentialsMessengerProxy.messages.in:
* UIProcess/CredentialManagement/cocoa/WebCredentialsMessengerProxyCocoa.mm: Removed.
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/CredentialManagement/WebCredentialsMessenger.cpp:
(WebKit::WebCredentialsMessenger::makeCredential):
(WebKit::WebCredentialsMessenger::makeCredentialReply):
* WebProcess/CredentialManagement/WebCredentialsMessenger.h:
* WebProcess/CredentialManagement/WebCredentialsMessenger.messages.in:

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/ios/LocalAuthenticator.mm: Added.
(TestWebKitAPI::getTestKey):
(TestWebKitAPI::cleanUpKeychain):
(TestWebKitAPI::LACantEvaluatePolicySwizzler::LACantEvaluatePolicySwizzler):
(TestWebKitAPI::LACantEvaluatePolicySwizzler::cantEvaluatePolicy):
(TestWebKitAPI::LACanEvaluatePolicySwizzler::LACanEvaluatePolicySwizzler):
(TestWebKitAPI::LACanEvaluatePolicySwizzler::canEvaluatePolicy):
(TestWebKitAPI::LAEvaluatePolicyFailedSwizzler::LAEvaluatePolicyFailedSwizzler):
(TestWebKitAPI::LAEvaluatePolicyFailedSwizzler::evaluatePolicyFailed):
(TestWebKitAPI::LAEvaluatePolicyPassedSwizzler::LAEvaluatePolicyPassedSwizzler):
(TestWebKitAPI::LAEvaluatePolicyPassedSwizzler::evaluatePolicyPassed):
(TestWebKitAPI::TestLocalAuthenticator::setFailureFlag):
(TestWebKitAPI::TEST):

WebKitLibraries:

* WebKitPrivateFrameworkStubs/iOS/11/DeviceIdentity.framework/DeviceIdentity.tbd: Added.

LayoutTests:

* http/wpt/credential-management/credentialscontainer-store-basics.https.html:
* http/wpt/webauthn/idl.https.html:
* http/wpt/webauthn/public-key-credential-create-success.https.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@229699 268f45cc-cd09-0410-ab3c-d52691b4dbfc

37 files changed:
LayoutTests/ChangeLog
LayoutTests/http/wpt/credential-management/credentialscontainer-store-basics.https.html
LayoutTests/http/wpt/webauthn/idl.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-create-success.https.html
Source/WebCore/ChangeLog
Source/WebCore/Configurations/WebCore.xcconfig
Source/WebCore/Modules/credentialmanagement/CredentialsMessenger.cpp
Source/WebCore/Modules/credentialmanagement/CredentialsMessenger.h
Source/WebCore/Modules/webauthn/COSEConstants.h [new file with mode: 0644]
Source/WebCore/Modules/webauthn/PublicKeyCredentialCreationOptions.h
Source/WebCore/Modules/webauthn/PublicKeyCredentialDescriptor.h
Source/WebCore/Modules/webauthn/PublicKeyCredentialType.h
Source/WebCore/Modules/webauthn/cocoa/LocalAuthenticator.h [new file with mode: 0644]
Source/WebCore/Modules/webauthn/cocoa/LocalAuthenticator.mm [new file with mode: 0644]
Source/WebCore/PAL/ChangeLog
Source/WebCore/PAL/PAL.xcodeproj/project.pbxproj
Source/WebCore/PAL/pal/spi/cocoa/DeviceIdentitySPI.h [new file with mode: 0644]
Source/WebCore/SourcesCocoa.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/platform/cocoa/LocalAuthenticationSoftLink.h [moved from Source/WebKit/UIProcess/CredentialManagement/cocoa/WebCredentialsMessengerProxyCocoa.mm with 59% similarity]
Source/WebCore/platform/cocoa/LocalAuthenticationSoftLink.mm [new file with mode: 0644]
Source/WebCore/testing/MockCredentialsMessenger.cpp
Source/WebCore/testing/MockCredentialsMessenger.h
Source/WebCore/testing/MockCredentialsMessenger.idl
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/CredentialManagement/WebCredentialsMessengerProxy.cpp
Source/WebKit/UIProcess/CredentialManagement/WebCredentialsMessengerProxy.h
Source/WebKit/UIProcess/CredentialManagement/WebCredentialsMessengerProxy.messages.in
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Source/WebKit/WebProcess/CredentialManagement/WebCredentialsMessenger.cpp
Source/WebKit/WebProcess/CredentialManagement/WebCredentialsMessenger.h
Source/WebKit/WebProcess/CredentialManagement/WebCredentialsMessenger.messages.in
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/ios/LocalAuthenticator.mm [new file with mode: 0644]
WebKitLibraries/ChangeLog
WebKitLibraries/WebKitPrivateFrameworkStubs/iOS/11/DeviceIdentity.framework/DeviceIdentity.tbd [new file with mode: 0644]

index 059bdb5..435d87a 100644 (file)
@@ -1,3 +1,15 @@
+2018-03-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Implement authenticatorMakeCredential
+        https://bugs.webkit.org/show_bug.cgi?id=183527
+        <rdar://problem/35275886>
+
+        Reviewed by Brent Fulgham.
+
+        * http/wpt/credential-management/credentialscontainer-store-basics.https.html:
+        * http/wpt/webauthn/idl.https.html:
+        * http/wpt/webauthn/public-key-credential-create-success.https.html:
+
 2018-03-16  Brent Fulgham  <bfulgham@apple.com>
 
         Correct debug assertion in Range::borderAndTextRects
index ca47ff9..1988825 100644 (file)
@@ -42,7 +42,7 @@
             }
         };
         // A mock attestation object
-        internals.mockCredentialsMessenger.setAttestationObject(hexStringToUint8Array('000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ff'));
+        internals.mockCredentialsMessenger.setCreationReturnBundle(hexStringToUint8Array('00'), hexStringToUint8Array('01'));
         const credential = await navigator.credentials.create(options);
 
         return promise_rejects(t, "NotSupportedError",
index 90e56bb..abcc64c 100644 (file)
@@ -50,7 +50,7 @@ promise_test(async () => {
         }
     };
     // A mock attestation object
-    internals.mockCredentialsMessenger.setAttestationObject(hexStringToUint8Array('000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ff'));
+    internals.mockCredentialsMessenger.setCreationReturnBundle(hexStringToUint8Array('00'), hexStringToUint8Array('01'));
     createdCredential = await navigator.credentials.create(creationOptions);
 
 
index b09417b..ab4fcc0 100644 (file)
             }
         };
         // A mock attestation object
-        internals.mockCredentialsMessenger.setAttestationObject(hexStringToUint8Array('000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ff'));
+        internals.mockCredentialsMessenger.setCreationReturnBundle(hexStringToUint8Array('00'), hexStringToUint8Array('01'));
 
         return navigator.credentials.create(options).then(function(credential) {
-                assert_equals(credential.id, '_w');
+                assert_equals(credential.id, 'AA');
                 assert_equals(credential.type, 'public-key');
-                assert_equals(bytesToHexString(credential.rawId), 'ff');
+                assert_equals(bytesToHexString(credential.rawId), '00');
                 assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.create","challenge":"MTIzNDU2","origin":"https://localhost:9443","hashAlgorithm":"SHA-256"}');
-                assert_equals(bytesToHexString(credential.response.attestationObject), '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ff');
+                assert_equals(bytesToHexString(credential.response.attestationObject), '01');
+                console.log()
                 try {
                     assert_throws("NotSupportedError", credential.getClientExtensionResults());
                 } catch(error) { }
index 27af733..9a373bd 100644 (file)
@@ -1,3 +1,83 @@
+2018-03-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Implement authenticatorMakeCredential
+        https://bugs.webkit.org/show_bug.cgi?id=183527
+        <rdar://problem/35275886>
+
+        Reviewed by Brent Fulgham.
+
+        This patch does the following few things:
+        1) It implements the authenticatorMakeCredential logic from the spec: https://www.w3.org/TR/webauthn/#op-make-cred.
+        2) It tweaks enocding and deocding of PublicKeyCredentialCreationOptions between UIProccess and WebProcess.
+        3) It soft links LocalAuthentication.Framework to WebCore, which was linked to WebKit.
+        4) It creates SPI header for DeviceIdentity.Framework, and provides stubs to link it to WebCore.
+
+        Here is a detailed explanantion of 1):
+        1. A helper class called LocalAuthenticator is crafted to represent Apple platform attached authenticator, i.e.
+        the devices themselves. All operations are currently restricted to iOS at this moment as macOS lacks attestation
+        support.
+        2. To do testing, this helper class is then moved from WebKit to WebCore even though all operations can only happens
+        in the UIProcess. We currently lack the ability to pretend a https environment in TestWebKitAPI which is required by
+        the WebAuthN API, and thus it is moved to WebCore to perform unit tesing flavor API tests. This is not enough as it
+        can't test message exchange between the UI and Web processes. We will address this in a subsequent patch.
+        3. More on testing: The attestation process is abstracted into a protected method such that the testing enviroment can
+        override it with self attestation as network access is restricted in the WebKit testing enviroment. Also, swizzlers of
+        LocalAuthentication API are provided to override the behavoir of LAContext.
+        4. More on testing: The actual Apple attestation can only happen in real device and with network access, therefore
+        it can only be covered by manual tests at this moment.
+        5. Back to LocalAuthenticator, it currently has two public methods:
+                5.1. makeCredential(): This method is the one does all the magic.
+                + It first checks some parameters.
+                + It then invokes LAContext to get user consent.
+                + It then talks to Apple Attestation Privacy CA to do attestations.
+                + It then stores necessary information into the Keychain.
+                + Finally it generates the attestation object.
+                5.2 isAvailable():
+                To check if a LocalAuthenticator is available or not.
+        6. Even though files are of .mm format, they are written in a way that mixes NS, CF and C++ types. Here is the rule:
+                6.1 Use CF type only if it is requested by APIs.
+                6.2 Use NS type to manipulate all Objc objects.
+                6.3 Use C++ otherwise.
+
+        Covered by API tests.
+
+        * Configurations/WebCore.xcconfig:
+        * Modules/credentialmanagement/CredentialsMessenger.cpp:
+        (WebCore::getIdFromAttestationObject): Deleted.
+        Decoding attestation object is tedious. UIProcess will instead return credential ID and attestation object
+        at the same time. Therefore, this method is removed.
+        * Modules/credentialmanagement/CredentialsMessenger.h:
+        (WebCore::CreationReturnBundle::CreationReturnBundle): Deleted.
+        (WebCore::AssertionReturnBundle::AssertionReturnBundle): Deleted.
+        * Modules/webauthn/COSEConstants.h: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialType.h.
+        * Modules/webauthn/PublicKeyCredentialCreationOptions.h:
+        (WebCore::PublicKeyCredentialCreationOptions::isolatedPartialCopyPtr const):
+        (WebCore::PublicKeyCredentialCreationOptions::Parameters::encode const):
+        (WebCore::PublicKeyCredentialCreationOptions::Parameters::decode):
+        (WebCore::PublicKeyCredentialCreationOptions::encode const):
+        (WebCore::PublicKeyCredentialCreationOptions::decode):
+        * Modules/webauthn/PublicKeyCredentialDescriptor.h:
+        (WebCore::PublicKeyCredentialDescriptor::encode const):
+        (WebCore::PublicKeyCredentialDescriptor::decode):
+        * Modules/webauthn/PublicKeyCredentialType.h:
+        * Modules/webauthn/cocoa/LocalAuthenticator.h: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialCreationOptions.h.
+        * Modules/webauthn/cocoa/LocalAuthenticator.mm: Added.
+        (WebCore::LocalAuthenticatorInternal::freePtrs):
+        (WebCore::LocalAuthenticator::makeCredential const):
+        (WebCore::LocalAuthenticator::isAvailable const):
+        (WebCore::LocalAuthenticator::issueClientCertificate const):
+        * SourcesCocoa.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/cocoa/LocalAuthenticationSoftLink.h: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialType.h.
+        * platform/cocoa/LocalAuthenticationSoftLink.mm: Added.
+        * testing/MockCredentialsMessenger.cpp:
+        (WebCore::MockCredentialsMessenger::setCreationReturnBundle):
+        (WebCore::MockCredentialsMessenger::makeCredential):
+        (WebCore::MockCredentialsMessenger::makeCredentialReply):
+        (WebCore::MockCredentialsMessenger::setAttestationObject): Deleted.
+        * testing/MockCredentialsMessenger.h:
+        * testing/MockCredentialsMessenger.idl:
+
 2018-03-16  Brent Fulgham  <bfulgham@apple.com>
 
         Correct debug assertion in Range::borderAndTextRects
index 509cc4b..928296f 100644 (file)
@@ -78,7 +78,7 @@ LIBWEBRTC_LIBRARY_DIR_USE_OVERRIDE_FRAMEWORKS_DIR_NO = $(WEBCORE_FRAMEWORKS_DIR)
 LIBWEBRTC_LIBRARY_DIR_USE_OVERRIDE_FRAMEWORKS_DIR_YES = $(WK_OVERRIDE_FRAMEWORKS_DIR);
 
 OTHER_LDFLAGS = $(inherited) $(WK_RELOCATABLE_FRAMEWORK_LDFLAGS) $(WK_UNDEFINED_SYMBOLS_LDFLAGS) $(OTHER_LDFLAGS_PLATFORM);
-OTHER_LDFLAGS_BASE = -lsqlite3 -lobjc -lANGLE -framework CoreAudio -framework Metal -allowable_client WebCoreTestSupport -allowable_client WebKitLegacy -force_load $(BUILT_PRODUCTS_DIR)/libPAL.a;
+OTHER_LDFLAGS_BASE = -lsqlite3 -lobjc -lANGLE -framework CoreAudio -framework Metal -allowable_client WebCoreTestSupport -allowable_client WebKitLegacy -force_load $(BUILT_PRODUCTS_DIR)/libPAL.a $(WK_DEVICE_IDENTITY_LDFLAGS);
 // FIXME: Reduce the number of allowable_client <rdar://problem/31823969>
 OTHER_LDFLAGS_BASE_ios = $(OTHER_LDFLAGS_BASE) -framework CFNetwork -framework CoreGraphics -framework CoreText -framework Foundation -framework ImageIO -framework MobileCoreServices -framework OpenGLES -lMobileGestalt $(WK_IOS_BINCOMPAT_LDFLAGS) -allowable_client WebKit -allowable_client iTunesU -allowable_client Casablanca -allowable_client Remote -allowable_client TVBooks -allowable_client DumpRenderTree -allowable_client WebKitTestRunner -allowable_client TestWebKitAPI;
 OTHER_LDFLAGS_PLATFORM[sdk=iphoneos*] = $(OTHER_LDFLAGS_BASE_ios) -framework IOSurface -framework IOSurfaceAccelerator -framework GraphicsServices $(LIBWEBRTC_LDFLAGS);
@@ -136,3 +136,9 @@ WK_RELOCATABLE_FRAMEWORK_LDFLAGS_YES = -Wl,-not_for_dyld_shared_cache;
 SUPPORTS_TEXT_BASED_API = $(SUPPORTS_TEXT_BASED_API_$(TARGET_NAME))
 SUPPORTS_TEXT_BASED_API_WebCore = $(WEBCORE_ENABLE_INSTALLAPI)
 TEXT_BASED_API_FILE = WebCore.tbd
+
+WK_HAVE_DEVICE_IDENTITY = $(WK_HAVE_DEVICE_IDENTITY_$(PLATFORM_NAME));
+WK_HAVE_DEVICE_IDENTITY_iphoneos = YES;
+
+WK_DEVICE_IDENTITY_LDFLAGS = $(WK_DEVICE_IDENTITY_LDFLAGS_$(WK_HAVE_DEVICE_IDENTITY));
+WK_DEVICE_IDENTITY_LDFLAGS_YES = -framework DeviceIdentity;
index b560c61..b46bd07 100644 (file)
@@ -35,12 +35,6 @@ namespace CredentialsMessengerInternal {
 const uint64_t maxMessageId = 0xFFFFFFFFFFFFFF; // 56 bits
 const size_t callBackClassifierOffset = 56;
 
-// The layout of attestation object: https://www.w3.org/TR/webauthn/#attestation-object as of 5 December 2017.
-// Here is a summary before CredentialID in the layout. All lengths are fixed.
-// RP ID hash (32) || FLAGS (1) || COUNTER (4) || AAGUID (16) || L (2) || CREDENTIAL ID (?) || ...
-const size_t credentialIdLengthOffset = 43;
-const size_t credentialIdLengthLength = 2;
-
 }
 
 void CredentialsMessenger::exceptionReply(uint64_t messageId, const ExceptionData& exception)
@@ -110,19 +104,6 @@ QueryCompletionHandler CredentialsMessenger::takeQueryCompletionHandler(uint64_t
     return m_pendingQueryCompletionHandlers.take(messageId);
 }
 
-RefPtr<ArrayBuffer> getIdFromAttestationObject(const Vector<uint8_t>& attestationObject)
-{
-    using namespace CredentialsMessengerInternal;
-
-    if (attestationObject.size() < credentialIdLengthOffset + credentialIdLengthLength)
-        return nullptr;
-    // The byte length of L is 2.
-    size_t length = (attestationObject[credentialIdLengthOffset] << 8) + attestationObject[credentialIdLengthOffset + 1];
-    if (attestationObject.size() < credentialIdLengthOffset + credentialIdLengthLength + length)
-        return nullptr;
-    return ArrayBuffer::create(attestationObject.data() + credentialIdLengthOffset + credentialIdLengthLength, length);
-}
-
 } // namespace WebCore
 
 #endif // ENABLE(WEB_AUTHN)
index 9cb6706..3d8cc95 100644 (file)
@@ -72,15 +72,18 @@ using QueryCompletionHandler = CompletionHandler<void(bool)>;
 
 class CredentialsMessenger {
     WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(CredentialsMessenger);
 public:
-    // sender
+    CredentialsMessenger() = default;
+
+    // Senders.
     virtual void makeCredential(const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions&, CreationCompletionHandler&&) = 0;
     virtual void getAssertion(const Vector<uint8_t>& hash, const PublicKeyCredentialRequestOptions&, RequestCompletionHandler&&) = 0;
     virtual void isUserVerifyingPlatformAuthenticatorAvailable(QueryCompletionHandler&&) = 0;
 
-    // receiver
+    // Receivers.
     WEBCORE_EXPORT void exceptionReply(uint64_t messageId, const ExceptionData&);
-    virtual void makeCredentialReply(uint64_t messageId, const Vector<uint8_t>&) = 0;
+    virtual void makeCredentialReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& attestationObject) = 0;
     virtual void getAssertionReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& authenticatorData, const Vector<uint8_t>& signature, const Vector<uint8_t>& userHandle) = 0;
     virtual void isUserVerifyingPlatformAuthenticatorAvailableReply(uint64_t messageId, bool) = 0;
 
@@ -111,10 +114,6 @@ private:
     HashMap<uint64_t, QueryCompletionHandler> m_pendingQueryCompletionHandlers;
 };
 
-// FIXME: We shouldn't expose this into the messenger. Since the receivers can only be passed with const references, we couldn't
-// construct an ExceptionOr wrapper and pass it to AuthenticatorManager's callback.
-WEBCORE_EXPORT RefPtr<ArrayBuffer> getIdFromAttestationObject(const Vector<uint8_t>& attestationObject);
-
 } // namespace WebCore
 
 #endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebCore/Modules/webauthn/COSEConstants.h b/Source/WebCore/Modules/webauthn/COSEConstants.h
new file mode 100644 (file)
index 0000000..f46fc4c
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace COSE {
+
+// See RFC 8152 - CBOR Object Signing and Encryption <https://tools.ietf.org/html/rfc8152>
+// Labels
+const int64_t alg = 3;
+const int64_t crv = -1;
+const int64_t kty = 1;
+const int64_t x = -2;
+const int64_t y = -3;
+
+// Values
+const int64_t EC2 = 2;
+const int64_t ES256 = -7;
+const int64_t P_256 = 1;
+
+} // namespace COSE
index 2554226..495d58b 100644 (file)
@@ -30,6 +30,7 @@
 #include "BufferSource.h"
 #include "PublicKeyCredentialDescriptor.h"
 #include "PublicKeyCredentialType.h"
+#include <wtf/CrossThreadCopier.h>
 #include <wtf/Forward.h>
 
 namespace WebCore {
@@ -45,13 +46,17 @@ struct PublicKeyCredentialCreationOptions {
     };
 
     struct UserEntity : public Entity {
-        BufferSource id;
+        BufferSource id; // id becomes idVector once it is passed to UIProcess.
+        mutable Vector<uint8_t> idVector;
         String displayName;
     };
 
     struct Parameters {
         PublicKeyCredentialType type;
-        long alg;
+        int64_t alg;
+
+        template<class Encoder> void encode(Encoder&) const;
+        template<class Decoder> static std::optional<Parameters> decode(Decoder&);
     };
 
     RpEntity rp;
@@ -62,8 +67,83 @@ struct PublicKeyCredentialCreationOptions {
 
     std::optional<unsigned long> timeout;
     Vector<PublicKeyCredentialDescriptor> excludeCredentials;
+
+    template<class Encoder> void encode(Encoder&) const;
+    template<class Decoder> static std::optional<PublicKeyCredentialCreationOptions> decode(Decoder&);
 };
 
+template<class Encoder>
+void PublicKeyCredentialCreationOptions::Parameters::encode(Encoder& encoder) const
+{
+    encoder << type << alg;
+}
+
+template<class Decoder>
+std::optional<PublicKeyCredentialCreationOptions::Parameters> PublicKeyCredentialCreationOptions::Parameters::decode(Decoder& decoder)
+{
+    PublicKeyCredentialCreationOptions::Parameters result;
+    if (!decoder.decodeEnum(result.type))
+        return std::nullopt;
+    if (!decoder.decode(result.alg))
+        return std::nullopt;
+    return result;
+}
+
+// Not every member is encoded.
+template<class Encoder>
+void PublicKeyCredentialCreationOptions::encode(Encoder& encoder) const
+{
+    encoder << rp.id << rp.name << rp.icon;
+    Vector<uint8_t> idVector;
+    idVector.append(user.id.data(), user.id.length());
+    encoder << idVector << user.displayName << user.name << user.icon << pubKeyCredParams << excludeCredentials;
+}
+
+template<class Decoder>
+std::optional<PublicKeyCredentialCreationOptions> PublicKeyCredentialCreationOptions::decode(Decoder& decoder)
+{
+    PublicKeyCredentialCreationOptions result;
+    if (!decoder.decode(result.rp.id))
+        return std::nullopt;
+    if (!decoder.decode(result.rp.name))
+        return std::nullopt;
+    if (!decoder.decode(result.rp.icon))
+        return std::nullopt;
+    if (!decoder.decode(result.user.idVector))
+        return std::nullopt;
+    if (!decoder.decode(result.user.displayName))
+        return std::nullopt;
+    if (!decoder.decode(result.user.name))
+        return std::nullopt;
+    if (!decoder.decode(result.user.icon))
+        return std::nullopt;
+    if (!decoder.decode(result.pubKeyCredParams))
+        return std::nullopt;
+    if (!decoder.decode(result.excludeCredentials))
+        return std::nullopt;
+    return result;
+}
+
 } // namespace WebCore
 
+namespace WTF {
+// Not every member is copied.
+template<> struct CrossThreadCopierBase<false, false, WebCore::PublicKeyCredentialCreationOptions> {
+    typedef WebCore::PublicKeyCredentialCreationOptions Type;
+    static Type copy(const Type& source)
+    {
+        Type result;
+        result.rp.name = source.rp.name.isolatedCopy();
+        result.rp.icon = source.rp.icon.isolatedCopy();
+        result.rp.id = source.rp.id.isolatedCopy();
+
+        result.user.name = source.user.name.isolatedCopy();
+        result.user.icon = source.user.icon.isolatedCopy();
+        result.user.displayName = source.user.displayName.isolatedCopy();
+        result.user.idVector = WTFMove(source.user.idVector);
+        return result;
+    }
+};
+} // namespace WTF
+
 #endif // ENABLE(WEB_AUTHN)
index fdd7594..f387d87 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "BufferSource.h"
 #include "PublicKeyCredentialType.h"
+#include <wtf/EnumTraits.h>
 
 namespace WebCore {
 
@@ -40,10 +41,49 @@ struct PublicKeyCredentialDescriptor {
     };
 
     PublicKeyCredentialType type;
-    BufferSource id;
+    BufferSource id; // id becomes idVector once it is passed to UIProcess.
+    Vector<uint8_t> idVector;
     Vector<AuthenticatorTransport> transports;
+
+    template<class Encoder> void encode(Encoder&) const;
+    template<class Decoder> static std::optional<PublicKeyCredentialDescriptor> decode(Decoder&);
 };
 
+template<class Encoder>
+void PublicKeyCredentialDescriptor::encode(Encoder& encoder) const
+{
+    encoder << type;
+    Vector<uint8_t> idVector;
+    idVector.append(id.data(), id.length());
+    encoder << idVector << transports;
+}
+
+template<class Decoder>
+std::optional<PublicKeyCredentialDescriptor> PublicKeyCredentialDescriptor::decode(Decoder& decoder)
+{
+    PublicKeyCredentialDescriptor result;
+    if (!decoder.decodeEnum(result.type))
+        return std::nullopt;
+    if (!decoder.decode(result.idVector))
+        return std::nullopt;
+    if (!decoder.decode(result.transports))
+        return std::nullopt;
+    return result;
+}
+
 } // namespace WebCore
 
+namespace WTF {
+
+template<> struct EnumTraits<WebCore::PublicKeyCredentialDescriptor::AuthenticatorTransport> {
+    using values = EnumValues<
+        WebCore::PublicKeyCredentialDescriptor::AuthenticatorTransport,
+        WebCore::PublicKeyCredentialDescriptor::AuthenticatorTransport::Usb,
+        WebCore::PublicKeyCredentialDescriptor::AuthenticatorTransport::Nfc,
+        WebCore::PublicKeyCredentialDescriptor::AuthenticatorTransport::Ble
+    >;
+};
+
+}
+
 #endif // ENABLE(WEB_AUTHN)
index d00f350..8e7f81e 100644 (file)
@@ -27,6 +27,8 @@
 
 #if ENABLE(WEB_AUTHN)
 
+#include <wtf/EnumTraits.h>
+
 namespace WebCore {
 
 enum class PublicKeyCredentialType {
@@ -35,4 +37,15 @@ enum class PublicKeyCredentialType {
 
 } // namespace WebCore
 
+namespace WTF {
+
+template<> struct EnumTraits<WebCore::PublicKeyCredentialType> {
+    using values = EnumValues<
+        WebCore::PublicKeyCredentialType,
+        WebCore::PublicKeyCredentialType::PublicKey
+    >;
+};
+
+}
+
 #endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebCore/Modules/webauthn/cocoa/LocalAuthenticator.h b/Source/WebCore/Modules/webauthn/cocoa/LocalAuthenticator.h
new file mode 100644 (file)
index 0000000..3e5668f
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_AUTHN)
+
+#include <wtf/Forward.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/WeakPtr.h>
+
+namespace WebCore {
+
+struct ExceptionData;
+struct PublicKeyCredentialCreationOptions;
+
+using CreationCallback = Function<void(const Vector<uint8_t>&, const Vector<uint8_t>&)>;
+using ExceptionCallback = Function<void(const WebCore::ExceptionData&)>;
+
+typedef void (^CompletionBlock)(SecKeyRef _Nullable referenceKey, NSArray * _Nullable certificates, NSError * _Nullable error);
+
+// FIXME(182769): LocalAuthenticator should belongs to WebKit. However, we need unit tests.
+class WEBCORE_EXPORT LocalAuthenticator {
+    WTF_MAKE_NONCOPYABLE(LocalAuthenticator);
+public:
+    LocalAuthenticator() = default;
+    virtual ~LocalAuthenticator() = default;
+
+    void makeCredential(const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions&, CreationCallback&&, ExceptionCallback&&);
+    bool isAvailable() const;
+
+protected:
+    // Apple Attestation is moved into this virtual method such that it can be overrided by self attestation for testing.
+    virtual void issueClientCertificate(const String& rpId, const String& username, const Vector<uint8_t>& hash, CompletionBlock _Nonnull) const;
+
+    WeakPtrFactory<LocalAuthenticator> m_weakFactory;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebCore/Modules/webauthn/cocoa/LocalAuthenticator.mm b/Source/WebCore/Modules/webauthn/cocoa/LocalAuthenticator.mm
new file mode 100644 (file)
index 0000000..95df5e6
--- /dev/null
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "LocalAuthenticator.h"
+
+#if ENABLE(WEB_AUTHN)
+
+#import "CBORWriter.h"
+#import "COSEConstants.h"
+#import "ExceptionData.h"
+#import "PublicKeyCredentialCreationOptions.h"
+#import <Security/SecItem.h>
+#import <pal/crypto/CryptoDigest.h>
+#import <pal/spi/cocoa/DeviceIdentitySPI.h>
+#import <wtf/BlockPtr.h>
+#import <wtf/MainThread.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/Vector.h>
+
+#import "LocalAuthenticationSoftLink.h"
+
+namespace WebCore {
+
+namespace LocalAuthenticatorInternal {
+
+// UP, UV and AT are set. See https://www.w3.org/TR/webauthn/#flags.
+const uint8_t authenticatorDataFlags = 69;
+// FIXME(rdar://problem/38320512): Define Apple AAGUID.
+const uint8_t AAGUID[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 16 bytes
+const size_t maxCredentialIdLength = 0xffff; // 2 bytes
+const size_t ES256KeySizeInBytes = 32;
+
+} // LocalAuthenticatorInternal
+
+void LocalAuthenticator::makeCredential(const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions& options, CreationCallback&& callback, ExceptionCallback&& exceptionCallback)
+{
+    using namespace LocalAuthenticatorInternal;
+
+#if !PLATFORM(IOS)
+    // FIXME(182772)
+    ASSERT_UNUSED(hash, hash == hash);
+    ASSERT_UNUSED(options, options.rp.name.isEmpty());
+    ASSERT_UNUSED(callback, callback);
+    exceptionCallback({ NotAllowedError, ASCIILiteral("No avaliable authenticators.") });
+#else
+    // The following implements https://www.w3.org/TR/webauthn/#op-make-cred as of 5 December 2017.
+    // Skip Step 4-5 as requireResidentKey and requireUserVerification are enforced.
+    // Skip Step 9 as extensions are not supported yet.
+    // Step 8 is implicitly captured by all UnknownError exception callbacks.
+    // Step 2.
+    bool canFullfillPubKeyCredParams = false;
+    for (auto& pubKeyCredParam : options.pubKeyCredParams) {
+        if (pubKeyCredParam.type == PublicKeyCredentialType::PublicKey && pubKeyCredParam.alg == COSE::ES256) {
+            canFullfillPubKeyCredParams = true;
+            break;
+        }
+    }
+    if (!canFullfillPubKeyCredParams) {
+        exceptionCallback({ NotSupportedError, ASCIILiteral("The platform attached authenticator doesn't support any provided PublicKeyCredentialParameters.") });
+        return;
+    }
+
+    // Step 3.
+    for (auto& excludeCredential : options.excludeCredentials) {
+        if (excludeCredential.type == PublicKeyCredentialType::PublicKey && excludeCredential.transports.isEmpty()) {
+            // Search Keychain for the Credential ID and RP ID, which is stored in the kSecAttrApplicationLabel and kSecAttrLabel attribute respectively.
+            NSDictionary *query = @{
+                (id)kSecClass: (id)kSecClassKey,
+                (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
+                (id)kSecAttrApplicationLabel: [NSData dataWithBytes:excludeCredential.idVector.data() length:excludeCredential.idVector.size()],
+                (id)kSecAttrLabel: options.rp.id
+            };
+            OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);
+            if (!status) {
+                exceptionCallback({ NotAllowedError, ASCIILiteral("At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.") });
+                return;
+            }
+            if (status != errSecItemNotFound) {
+                LOG_ERROR("Couldn't query Keychain: %d", status);
+                exceptionCallback({ UnknownError, ASCIILiteral("Unknown internal error.") });
+                return;
+            }
+        }
+    }
+
+    // Step 6.
+    // FIXME(rdar://problem/35900593): Update to a formal UI.
+    // Get user consent.
+    auto context = adoptNS([allocLAContextInstance() init]);
+    NSError *error = nil;
+    NSString *reason = [NSString stringWithFormat:@"Allow %@ to create a public key credential for %@", (id)options.rp.id, (id)options.user.name];
+    if (![context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
+        LOG_ERROR("Couldn't evaluate authentication with biometrics policy: %@", error);
+        // FIXME(182767)
+        exceptionCallback({ NotAllowedError, ASCIILiteral("No avaliable authenticators.") });
+        return;
+    }
+
+    // FIXME(183534): Optimize the following nested callbacks and threading.
+    [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:reason reply:BlockPtr<void(BOOL, NSError *)>::fromCallable([weakThis = m_weakFactory.createWeakPtr(*this), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), options = crossThreadCopy(options), hash] (BOOL success, NSError *error) mutable {
+        ASSERT(!isMainThread());
+        if (!success || error) {
+            LOG_ERROR("Couldn't authenticate with biometrics: %@", error);
+            exceptionCallback({ NotAllowedError, ASCIILiteral("Couldn't get user consent.") });
+            return;
+        }
+
+        // Step 7.5.
+        // Userhandle is stored in kSecAttrApplicationTag attribute.
+        // Failures after this point could block users' accounts forever. Should we follow the spec?
+        NSDictionary* deleteQuery = @{
+            (id)kSecClass: (id)kSecClassKey,
+            (id)kSecAttrLabel: options.rp.id,
+            (id)kSecAttrApplicationTag: [NSData dataWithBytes:options.user.idVector.data() length:options.user.idVector.size()],
+        };
+        OSStatus status = SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
+        if (status && status != errSecItemNotFound) {
+            LOG_ERROR("Couldn't detele older credential: %d", status);
+            exceptionCallback({ UnknownError, ASCIILiteral("Unknown internal error.") });
+            return;
+        }
+
+        // Step 7.1, 13. Apple Attestation
+        // FIXME(183534)
+        if (!weakThis)
+            return;
+        weakThis->issueClientCertificate(options.rp.id, options.user.name, hash, BlockPtr<void(SecKeyRef, NSArray *, NSError *)>::fromCallable([callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), options = crossThreadCopy(options)] (_Nullable SecKeyRef privateKey, NSArray * _Nullable certificates, NSError * _Nullable error) {
+            ASSERT(!isMainThread());
+            if (error) {
+                LOG_ERROR("Couldn't attest: %@", error);
+                exceptionCallback({ UnknownError, ASCIILiteral("Unknown internal error.") });
+                return;
+            }
+            // Attestation Certificate and Attestation Issuing CA
+            ASSERT(certificates && ([certificates count] == 2));
+
+            // Step 7.2 - 7.4.
+            // FIXME(183533): A single kSecClassKey item couldn't store all meta data. The following schema is a tentative solution
+            // to accommodate the most important meta data, i.e. RP ID, Credential ID, and userhandle.
+            // kSecAttrLabel: RP ID
+            // kSecAttrApplicationLabel: Credential ID (auto-gen by Keychain)
+            // kSecAttrApplicationTag: userhandle
+            // Noted, the current DeviceIdentity.Framework would only allow us to pass the kSecAttrLabel as the inital attribute
+            // for the Keychain item. Since that's the only clue we have to locate the unique item, we use the pattern username@rp.id
+            // as the initial value.
+            // Also noted, the vale of kSecAttrApplicationLabel is automatically generated by the Keychain, which is a SHA-1 hash of
+            // the public key. We borrow it directly for now to workaround the stated limitations.
+            // Update the Keychain item to the above schema.
+            // FIXME(183533): DeviceIdentity.Framework would insert certificates into Keychain as well. We should update those as well.
+            Vector<uint8_t> credentialId;
+            {
+                String label(options.user.name);
+                label.append("@" + options.rp.id + "-rk"); // -rk is added by DeviceIdentity.Framework.
+                NSDictionary *credentialIdQuery = @{
+                    (id)kSecClass: (id)kSecClassKey,
+                    (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
+                    (id)kSecAttrLabel: label,
+                    (id)kSecReturnAttributes: @YES
+                };
+                CFTypeRef attributesRef = NULL;
+                OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)credentialIdQuery, &attributesRef);
+                if (status) {
+                    LOG_ERROR("Couldn't get Credential ID: %d", status);
+                    exceptionCallback({ UnknownError, ASCIILiteral("Unknown internal error.") });
+                    return;
+                }
+                auto retainAttributes = adoptCF(attributesRef);
+
+                NSDictionary *nsAttributes = (NSDictionary *)attributesRef;
+                NSData *nsCredentialId = nsAttributes[(id)kSecAttrApplicationLabel];
+                credentialId.append(static_cast<const uint8_t*>(nsCredentialId.bytes), nsCredentialId.length);
+
+                NSDictionary *updateQuery = @{
+                    (id)kSecClass: (id)kSecClassKey,
+                    (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
+                    (id)kSecAttrApplicationLabel: nsCredentialId,
+                };
+                NSDictionary *updateParams = @{
+                    (id)kSecAttrLabel: options.rp.id,
+                    (id)kSecAttrApplicationTag: [NSData dataWithBytes:options.user.idVector.data() length:options.user.idVector.size()],
+                };
+                status = SecItemUpdate((__bridge CFDictionaryRef)updateQuery, (__bridge CFDictionaryRef)updateParams);
+                if (status) {
+                    LOG_ERROR("Couldn't update the Keychain item: %d", status);
+                    exceptionCallback({ UnknownError, ASCIILiteral("Unknown internal error.") });
+                    return;
+                }
+            }
+
+            // Step 12.
+            // Apple Attestation Cont'
+            // Assemble the attestation object:
+            // https://www.w3.org/TR/webauthn/#attestation-object
+            // FIXME(183534): authData could throttle.
+            Vector<uint8_t> authData;
+            {
+                // RP ID hash
+                auto crypto = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_256);
+                // FIXME(183534): Test IDN.
+                ASSERT(options.rp.id.isAllASCII());
+                auto asciiRpId = options.rp.id.ascii();
+                crypto->addBytes(asciiRpId.data(), asciiRpId.length());
+                authData = crypto->computeHash();
+
+                // FLAGS
+                authData.append(authenticatorDataFlags);
+
+                // Step. 10.
+                // COUNTER
+                // FIXME(183533): store the counter.
+                union {
+                    uint32_t integer;
+                    uint8_t bytes[4];
+                } counter = {0x00000000};
+                authData.append(counter.bytes, sizeof(counter.bytes));
+
+                // Step 11.
+                // AAGUID
+                authData.append(AAGUID, sizeof(AAGUID));
+
+                // L
+                ASSERT(credentialId.size() <= maxCredentialIdLength);
+                // Assume little endian here.
+                union {
+                    uint16_t integer;
+                    uint8_t bytes[2];
+                } credentialIdLength;
+                credentialIdLength.integer = static_cast<uint16_t>(credentialId.size());
+                authData.append(credentialIdLength.bytes, sizeof(uint16_t));
+
+                // CREDENTIAL ID
+                authData.appendVector(credentialId);
+
+                // CREDENTIAL PUBLIC KEY
+                CFDataRef publicKeyDataRef = NULL;
+                {
+                    auto publicKey = adoptCF(SecKeyCopyPublicKey(privateKey));
+                    CFErrorRef errorRef = NULL;
+                    publicKeyDataRef = SecKeyCopyExternalRepresentation(publicKey.get(), &errorRef);
+                    auto retainError = adoptCF(errorRef);
+                    if (errorRef) {
+                        LOG_ERROR("Couldn't export the public key: %@", (NSError*)errorRef);
+                        exceptionCallback({ UnknownError, ASCIILiteral("Unknown internal error.") });
+                        return;
+                    }
+                    ASSERT(((NSData *)publicKeyDataRef).length == (1 + 2*ES256KeySizeInBytes)); // 04 | X | Y
+                }
+                auto retainPublicKeyData = adoptCF(publicKeyDataRef);
+
+                // COSE Encoding
+                // FIXME(183535): Improve CBOR encoder to work with bytes directly.
+                Vector<uint8_t> x(ES256KeySizeInBytes);
+                [(NSData *)publicKeyDataRef getBytes: x.data() range:NSMakeRange(1, ES256KeySizeInBytes)];
+                Vector<uint8_t> y(ES256KeySizeInBytes);
+                [(NSData *)publicKeyDataRef getBytes: y.data() range:NSMakeRange(1 + ES256KeySizeInBytes, ES256KeySizeInBytes)];
+                cbor::CBORValue::MapValue publicKeyMap;
+                publicKeyMap[cbor::CBORValue(COSE::kty)] = cbor::CBORValue(COSE::EC2);
+                publicKeyMap[cbor::CBORValue(COSE::alg)] = cbor::CBORValue(COSE::ES256);
+                publicKeyMap[cbor::CBORValue(COSE::crv)] = cbor::CBORValue(COSE::P_256);
+                publicKeyMap[cbor::CBORValue(COSE::x)] = cbor::CBORValue(WTFMove(x));
+                publicKeyMap[cbor::CBORValue(COSE::y)] = cbor::CBORValue(WTFMove(y));
+                auto cosePublicKey = cbor::CBORWriter::write(cbor::CBORValue(WTFMove(publicKeyMap)));
+                if (!cosePublicKey) {
+                    LOG_ERROR("Couldn't encode the public key into COSE binaries.");
+                    exceptionCallback({ UnknownError, ASCIILiteral("Unknown internal error.") });
+                    return;
+                }
+                authData.appendVector(cosePublicKey.value());
+            }
+
+            cbor::CBORValue::MapValue attestationStatementMap;
+            {
+                Vector<uint8_t> signature;
+                {
+                    CFErrorRef errorRef = NULL;
+                    // FIXME(183652): Reduce prompt for biometrics
+                    CFDataRef signatureRef = SecKeyCreateSignature(privateKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (__bridge CFDataRef)[NSData dataWithBytes:authData.data() length:authData.size()], &errorRef);
+                    auto retainError = adoptCF(errorRef);
+                    if (errorRef) {
+                        LOG_ERROR("Couldn't export the public key: %@", (NSError*)errorRef);
+                        exceptionCallback({ UnknownError, ASCIILiteral("Unknown internal error.") });
+                        return;
+                    }
+                    auto retainSignature = adoptCF(signatureRef);
+                    NSData *nsSignature = (NSData *)signatureRef;
+                    signature.append(static_cast<const uint8_t*>(nsSignature.bytes), nsSignature.length);
+                }
+                attestationStatementMap[cbor::CBORValue("alg")] = cbor::CBORValue(COSE::ES256);
+                attestationStatementMap[cbor::CBORValue("sig")] = cbor::CBORValue(signature);
+                Vector<cbor::CBORValue> cborArray;
+                for (size_t i = 0; i < [certificates count]; i++) {
+                    CFDataRef dataRef = SecCertificateCopyData((__bridge SecCertificateRef)certificates[i]);
+                    auto retainData = adoptCF(dataRef);
+                    NSData *nsData = (NSData *)dataRef;
+                    Vector<uint8_t> data;
+                    data.append(static_cast<const uint8_t*>(nsData.bytes), nsData.length);
+                    cborArray.append(cbor::CBORValue(WTFMove(data)));
+                }
+                attestationStatementMap[cbor::CBORValue("x5c")] = cbor::CBORValue(WTFMove(cborArray));
+            }
+
+            cbor::CBORValue::MapValue attestationObjectMap;
+            attestationObjectMap[cbor::CBORValue("authData")] = cbor::CBORValue(authData);
+            attestationObjectMap[cbor::CBORValue("fmt")] = cbor::CBORValue("Apple");
+            attestationObjectMap[cbor::CBORValue("attStmt")] = cbor::CBORValue(WTFMove(attestationStatementMap));
+            auto attestationObject = cbor::CBORWriter::write(cbor::CBORValue(WTFMove(attestationObjectMap)));
+            if (!attestationObject) {
+                LOG_ERROR("Couldn't encode the attestation object.");
+                exceptionCallback({ UnknownError, ASCIILiteral("Unknown internal error.") });
+                return;
+            }
+
+            callback(credentialId, attestationObject.value());
+        }).get());
+    }).get()];
+#endif // !PLATFORM(IOS)
+}
+
+bool LocalAuthenticator::isAvailable() const
+{
+#if !PLATFORM(IOS)
+    // FIXME(182772)
+    return false;
+#else
+    auto context = adoptNS([allocLAContextInstance() init]);
+    NSError *error = nil;
+
+    if (![context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
+        LOG_ERROR("Couldn't evaluate authentication with biometrics policy: %@", error);
+        return true;
+    }
+    return true;
+#endif // !PLATFORM(IOS)
+}
+
+void LocalAuthenticator::issueClientCertificate(const String& rpId, const String& username, const Vector<uint8_t>& hash, CompletionBlock _Nonnull completionHandler) const
+{
+// DeviceIdentity.Framework is not avaliable in iOS simulator.
+#if !PLATFORM(IOS) || PLATFORM(IOS_SIMULATOR)
+    // FIXME(182772)
+    ASSERT_UNUSED(rpId, !rpId.isEmpty());
+    ASSERT_UNUSED(username, !username.isEmpty());
+    ASSERT_UNUSED(hash, !hash.isEmpty());
+    completionHandler(NULL, NULL, [NSError errorWithDomain:@"com.apple.WebKit.WebAuthN" code:1 userInfo:nil]);
+#else
+    // Apple Attestation
+    ASSERT(hash.size() <= 32);
+
+    SecAccessControlRef accessControlRef;
+    {
+        CFErrorRef errorRef = NULL;
+        accessControlRef = SecAccessControlCreateWithFlags(NULL, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlPrivateKeyUsage | kSecAccessControlUserPresence, &errorRef);
+        auto retainError = adoptCF(errorRef);
+        if (errorRef) {
+            LOG_ERROR("Couldn't create ACL: %@", (NSError *)errorRef);
+            completionHandler(NULL, NULL, [NSError errorWithDomain:@"com.apple.WebKit.WebAuthN" code:1 userInfo:nil]);
+            return;
+        }
+    }
+    auto retainAccessControl = adoptCF(accessControlRef);
+
+    String label(username);
+    label.append("@" + rpId);
+    NSDictionary *options = @{
+        kMAOptionsBAAKeychainLabel: label,
+        // FIXME(rdar://problem/38489134): Need a formal name.
+        kMAOptionsBAAKeychainAccessGroup: @"com.apple.safari.WebAuthN.credentials",
+        kMAOptionsBAAIgnoreExistingKeychainItems: @(YES),
+        // FIXME(rdar://problem/38489134): Determine a proper lifespan.
+        kMAOptionsBAAValidity: @(1440), // Last one day.
+        kMAOptionsBAASCRTAttestation: @(NO),
+        kMAOptionsBAANonce: [NSData dataWithBytes:hash.data() length:hash.size()],
+        kMAOptionsBAAAccessControls: (id)accessControlRef,
+        kMAOptionsBAAOIDSToInclude: @[kMAOptionsBAAOIDNonce]
+    };
+
+    // FIXME(183652): Reduce prompt for biometrics
+    DeviceIdentityIssueClientCertificateWithCompletion(NULL, options, completionHandler);
+#endif
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUTHN)
index d3fe273..e7ef373 100644 (file)
@@ -1,3 +1,14 @@
+2018-03-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Implement authenticatorMakeCredential
+        https://bugs.webkit.org/show_bug.cgi?id=183527
+        <rdar://problem/35275886>
+
+        Reviewed by Brent Fulgham.
+
+        * PAL.xcodeproj/project.pbxproj:
+        * pal/spi/cocoa/DeviceIdentitySPI.h: Copied from Source/WebKit/UIProcess/CredentialManagement/WebCredentialsMessengerProxy.h.
+
 2018-03-14  Tim Horton  <timothy_horton@apple.com>
 
         Fix the build after r229567
index ddc55ff..b4030b5 100644 (file)
                1C4876D91F8D7F4E00CCEEBD /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C4876D71F8D7F4E00CCEEBD /* Logging.h */; };
                1C4876E01F8D837500CCEEBD /* LoggingCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C4876DF1F8D837500CCEEBD /* LoggingCocoa.mm */; };
                2D02E93C2056FAA700A13797 /* AudioToolboxSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D02E93B2056FAA700A13797 /* AudioToolboxSPI.h */; };
+               57F12518205787D7001AB8A6 /* DeviceIdentitySPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 57F12517205787C8001AB8A6 /* DeviceIdentitySPI.h */; };
                7A1656441F97B2B900BA3CE4 /* NSKeyedArchiverSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A1656431F97B2B800BA3CE4 /* NSKeyedArchiverSPI.h */; };
                A10265871F56746100B4C844 /* FoundationSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = A10265861F56746100B4C844 /* FoundationSPI.h */; };
                A10265891F56747A00B4C844 /* HIToolboxSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = A10265881F56747A00B4C844 /* HIToolboxSPI.h */; };
                1C67CEA11E32EDA800F80F2E /* FeatureDefines.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = FeatureDefines.xcconfig; sourceTree = "<group>"; };
                1C67CEA21E32EE2600F80F2E /* Version.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = "<group>"; };
                2D02E93B2056FAA700A13797 /* AudioToolboxSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioToolboxSPI.h; sourceTree = "<group>"; };
+               57F12517205787C8001AB8A6 /* DeviceIdentitySPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeviceIdentitySPI.h; sourceTree = "<group>"; };
                7A1656431F97B2B800BA3CE4 /* NSKeyedArchiverSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSKeyedArchiverSPI.h; sourceTree = "<group>"; };
                93E5909C1F93BF1E0067F8CF /* UnencodableHandling.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UnencodableHandling.h; sourceTree = "<group>"; };
                A10265861F56746100B4C844 /* FoundationSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FoundationSPI.h; sourceTree = "<group>"; };
                                0C2DA1231F3BEB4900DBC317 /* CFNSURLConnectionSPI.h */,
                                0C2DA1241F3BEB4900DBC317 /* CoreTextSPI.h */,
                                0C2DA1251F3BEB4900DBC317 /* DataDetectorsCoreSPI.h */,
+                               57F12517205787C8001AB8A6 /* DeviceIdentitySPI.h */,
                                A10265861F56746100B4C844 /* FoundationSPI.h */,
                                0C2DA1261F3BEB4900DBC317 /* IOPMLibSPI.h */,
                                0C2DA1271F3BEB4900DBC317 /* IOPSLibSPI.h */,
                                0C77858A1F45130F00F4EBB6 /* DataDetectorsSPI.h in Headers */,
                                0C5AF91A1F43A4C7002EAC02 /* DataDetectorsUISPI.h in Headers */,
                                A1175B571F6B470500C4B9F0 /* DefaultSearchProvider.h in Headers */,
+                               57F12518205787D7001AB8A6 /* DeviceIdentitySPI.h in Headers */,
                                0C2D9E731EEF5AF600DBC317 /* ExportMacros.h in Headers */,
                                F44291601FA5261E002CC93E /* FileSizeFormatter.h in Headers */,
                                A10265871F56746100B4C844 /* FoundationSPI.h in Headers */,
diff --git a/Source/WebCore/PAL/pal/spi/cocoa/DeviceIdentitySPI.h b/Source/WebCore/PAL/pal/spi/cocoa/DeviceIdentitySPI.h
new file mode 100644 (file)
index 0000000..7af384c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_AUTHN)
+
+#if USE(APPLE_INTERNAL_SDK) && PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
+
+extern "C" {
+#import <DeviceIdentity/DeviceIdentity.h>
+}
+
+#else
+
+typedef void (^MABAACompletionBlock)(_Nullable SecKeyRef referenceKey, NSArray * _Nullable certificates, NSError * _Nullable error);
+
+extern NSString * _Nonnull const kMAOptionsBAAAccessControls;
+extern NSString * _Nonnull const kMAOptionsBAAIgnoreExistingKeychainItems;
+extern NSString * _Nonnull const kMAOptionsBAAKeychainAccessGroup;
+extern NSString * _Nonnull const kMAOptionsBAAKeychainLabel;
+extern NSString * _Nonnull const kMAOptionsBAANonce;
+extern NSString * _Nonnull const kMAOptionsBAAOIDNonce;
+extern NSString * _Nonnull const kMAOptionsBAAOIDSToInclude;
+extern NSString * _Nonnull const kMAOptionsBAASCRTAttestation;
+extern NSString * _Nonnull const kMAOptionsBAAValidity;
+
+extern "C"
+void DeviceIdentityIssueClientCertificateWithCompletion(dispatch_queue_t _Nullable, NSDictionary * _Nullable options, MABAACompletionBlock _Nonnull);
+
+#endif // USE(APPLE_INTERNAL_SDK) && PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
+
+#endif // ENABLE(WEB_AUTHN)
index 5be255b..8778f4b 100644 (file)
@@ -25,6 +25,7 @@ Modules/mediasession/WebMediaSessionManager.cpp
 
 Modules/plugins/QuickTimePluginReplacement.mm
 Modules/plugins/YouTubePluginReplacement.cpp
+Modules/webauthn/cocoa/LocalAuthenticator.mm
 
 accessibility/ios/AccessibilityObjectIOS.mm
 accessibility/ios/AXObjectCacheIOS.mm
@@ -194,6 +195,7 @@ platform/cocoa/FileMonitorCocoa.mm
 platform/cocoa/FileSystemCocoa.mm
 platform/cocoa/KeyEventCocoa.mm
 platform/cocoa/LocalizedStringsCocoa.mm
+platform/cocoa/LocalAuthenticationSoftLink.mm
 platform/cocoa/MachSendRight.cpp
 platform/cocoa/MIMETypeRegistryCocoa.mm
 platform/cocoa/NetworkExtensionContentFilter.mm
index 47ee189..28929b8 100644 (file)
                571252691E524EB1008FF369 /* CryptoAlgorithmAES_CFB.h in Headers */ = {isa = PBXBuildFile; fileRef = 571252681E524EB1008FF369 /* CryptoAlgorithmAES_CFB.h */; };
                571F21891DA57C54005C9EFD /* JSSubtleCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 571F21881DA57C54005C9EFD /* JSSubtleCrypto.h */; };
                572093D31DDCEB9A00310AB0 /* CryptoAlgorithmAesCbcCfbParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 572093D21DDCEB9A00310AB0 /* CryptoAlgorithmAesCbcCfbParams.h */; };
+               57212152205365650062AA1F /* LocalAuthenticationSoftLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 5721214E20535D710062AA1F /* LocalAuthenticationSoftLink.h */; };
                5721A9871ECE53B10081295A /* CryptoDigestAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = 5721A9861ECE53B10081295A /* CryptoDigestAlgorithm.h */; };
                5721A98B1ECE57040081295A /* CryptoAlgorithmRsaPssParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 5721A9881ECE57040081295A /* CryptoAlgorithmRsaPssParams.h */; };
                572A7F211C6E5719009C6149 /* SimulatedClick.h in Headers */ = {isa = PBXBuildFile; fileRef = 572A7F201C6E5719009C6149 /* SimulatedClick.h */; };
                57303BC12006E00C00355965 /* CBORReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BBF2006E00C00355965 /* CBORReader.h */; settings = {ATTRIBUTES = (Private, ); }; };
                57303BD220087A8300355965 /* AuthenticatorResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BCF20087A8300355965 /* AuthenticatorResponse.h */; };
                57303BE120095D6100355965 /* JSAuthenticatorResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BDD20095B2600355965 /* JSAuthenticatorResponse.h */; };
-               57303BE92009748D00355965 /* PublicKeyCredentialCreationOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BE62009747A00355965 /* PublicKeyCredentialCreationOptions.h */; };
-               57303BEB20097F4000355965 /* PublicKeyCredentialType.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BEA20097F4000355965 /* PublicKeyCredentialType.h */; };
-               57303BEF200980C600355965 /* PublicKeyCredentialDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BEC200980BF00355965 /* PublicKeyCredentialDescriptor.h */; };
+               57303BE92009748D00355965 /* PublicKeyCredentialCreationOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BE62009747A00355965 /* PublicKeyCredentialCreationOptions.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               57303BEB20097F4000355965 /* PublicKeyCredentialType.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BEA20097F4000355965 /* PublicKeyCredentialType.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               57303BEF200980C600355965 /* PublicKeyCredentialDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BEC200980BF00355965 /* PublicKeyCredentialDescriptor.h */; settings = {ATTRIBUTES = (Private, ); }; };
                57303BF42009904600355965 /* JSPublicKeyCredentialDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BF22009904500355965 /* JSPublicKeyCredentialDescriptor.h */; };
                57303C002009951C00355965 /* JSPublicKeyCredentialType.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BF92009913400355965 /* JSPublicKeyCredentialType.h */; };
                57303C0A20099BAD00355965 /* PublicKeyCredentialRequestOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303C06200998F800355965 /* PublicKeyCredentialRequestOptions.h */; };
                57303C4620105D2F00355965 /* AuthenticatorManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303C4320105B3D00355965 /* AuthenticatorManager.h */; settings = {ATTRIBUTES = (Private, ); }; };
                573489391DAC6B6E00DC0667 /* CryptoAlgorithmParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 573489381DAC6B6D00DC0667 /* CryptoAlgorithmParameters.h */; };
                5739E12F1DAC7F7800E14383 /* JSCryptoAlgorithmParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 5739E12E1DAC7F7800E14383 /* JSCryptoAlgorithmParameters.h */; };
+               574F55E0204F3ACE002948C6 /* LocalAuthenticator.h in Headers */ = {isa = PBXBuildFile; fileRef = 574F55DE204F3744002948C6 /* LocalAuthenticator.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               574F55E1204F3B23002948C6 /* COSEConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 574F55DC204F3732002948C6 /* COSEConstants.h */; settings = {ATTRIBUTES = (Private, ); }; };
                5750A9751E68D00000705C4A /* CryptoKeyEC.h in Headers */ = {isa = PBXBuildFile; fileRef = 5750A9731E68D00000705C4A /* CryptoKeyEC.h */; };
                5750A97E1E6A13EF00705C4A /* CryptoAlgorithmEcKeyParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 5750A97D1E6A13EF00705C4A /* CryptoAlgorithmEcKeyParams.h */; };
                5750A9821E6A150800705C4A /* JSEcKeyParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 5750A9801E6A150800705C4A /* JSEcKeyParams.h */; };
                571F218A1DA57C7A005C9EFD /* JSSubtleCrypto.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSSubtleCrypto.cpp; sourceTree = "<group>"; };
                572093D11DDCEA4B00310AB0 /* AesCbcCfbParams.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = AesCbcCfbParams.idl; sourceTree = "<group>"; };
                572093D21DDCEB9A00310AB0 /* CryptoAlgorithmAesCbcCfbParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoAlgorithmAesCbcCfbParams.h; sourceTree = "<group>"; };
+               5721214E20535D710062AA1F /* LocalAuthenticationSoftLink.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocalAuthenticationSoftLink.h; sourceTree = "<group>"; };
+               57212150205361D20062AA1F /* LocalAuthenticationSoftLink.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LocalAuthenticationSoftLink.mm; sourceTree = "<group>"; };
                5721A9831ECE4FB90081295A /* CryptoAlgorithmRSA_PSSMac.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CryptoAlgorithmRSA_PSSMac.cpp; sourceTree = "<group>"; };
                5721A9861ECE53B10081295A /* CryptoDigestAlgorithm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CryptoDigestAlgorithm.h; sourceTree = "<group>"; };
                5721A9881ECE57040081295A /* CryptoAlgorithmRsaPssParams.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CryptoAlgorithmRsaPssParams.h; sourceTree = "<group>"; };
                5739E1301DAC7FD100E14383 /* JSCryptoAlgorithmParameters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCryptoAlgorithmParameters.cpp; sourceTree = "<group>"; };
                574AC7531DAC367D00E9744C /* CryptoAlgorithmParameters.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CryptoAlgorithmParameters.idl; sourceTree = "<group>"; };
                574D42791D594FF6002CF50E /* GlobalCrypto.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = GlobalCrypto.idl; sourceTree = "<group>"; };
+               574F55DC204F3732002948C6 /* COSEConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = COSEConstants.h; sourceTree = "<group>"; };
+               574F55DE204F3744002948C6 /* LocalAuthenticator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalAuthenticator.h; sourceTree = "<group>"; };
+               574F55DF204F3744002948C6 /* LocalAuthenticator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LocalAuthenticator.mm; sourceTree = "<group>"; };
+               574F55E2204F3CBF002948C6 /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; };
                5750A9721E68D00000705C4A /* CryptoKeyEC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CryptoKeyEC.cpp; sourceTree = "<group>"; };
                5750A9731E68D00000705C4A /* CryptoKeyEC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoKeyEC.h; sourceTree = "<group>"; };
                5750A97A1E69161600705C4A /* CryptoKeyECMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CryptoKeyECMac.cpp; sourceTree = "<group>"; };
                                1CFAE3220A6D6A3F0032593D /* libobjc.dylib */,
                                DD763BB10992C2C900740B8E /* libxml2.dylib */,
                                379919B11200DE5000EA041C /* libz.dylib */,
+                               574F55E2204F3CBF002948C6 /* LocalAuthentication.framework */,
                                A85D7A2F0879EBA9006A9172 /* QuartzCore.framework */,
                                5FE1D291178FD1F3001AA3C3 /* Security.framework */,
                        );
                        path = cbor;
                        sourceTree = "<group>";
                };
+               574F55DD204F3744002948C6 /* cocoa */ = {
+                       isa = PBXGroup;
+                       children = (
+                               574F55DE204F3744002948C6 /* LocalAuthenticator.h */,
+                               574F55DF204F3744002948C6 /* LocalAuthenticator.mm */,
+                       );
+                       path = cocoa;
+                       sourceTree = "<group>";
+               };
                57C7A6881E56946D00C67D71 /* credentialmanagement */ = {
                        isa = PBXGroup;
                        children = (
                        isa = PBXGroup;
                        children = (
                                57303BB32006C6ED00355965 /* cbor */,
+                               574F55DD204F3744002948C6 /* cocoa */,
                                57303C282009B2FC00355965 /* AuthenticatorAssertionResponse.cpp */,
                                57303C272009B2FC00355965 /* AuthenticatorAssertionResponse.h */,
                                57303C292009B2FC00355965 /* AuthenticatorAssertionResponse.idl */,
                                57303BD020087A8300355965 /* AuthenticatorResponse.cpp */,
                                57303BCF20087A8300355965 /* AuthenticatorResponse.h */,
                                57303BD120087A8300355965 /* AuthenticatorResponse.idl */,
+                               574F55DC204F3732002948C6 /* COSEConstants.h */,
                                57D8462C1FEAF68F00CA3682 /* PublicKeyCredential.cpp */,
                                57D8462B1FEAF68F00CA3682 /* PublicKeyCredential.h */,
                                57D8462D1FEAF68F00CA3682 /* PublicKeyCredential.idl */,
                                514B3F750C722055000530DF /* FileSystemCocoa.mm */,
                                A5C974CF11485FF10066F2AB /* KeyEventCocoa.h */,
                                A5C974D011485FF10066F2AB /* KeyEventCocoa.mm */,
+                               5721214E20535D710062AA1F /* LocalAuthenticationSoftLink.h */,
+                               57212150205361D20062AA1F /* LocalAuthenticationSoftLink.mm */,
                                1A4832B21A953BA6008B4DFE /* LocalizedStringsCocoa.mm */,
                                1A0409F21A43675C009E47F3 /* MachSendRight.cpp */,
                                1A0409DB1A4360B5009E47F3 /* MachSendRight.h */,
                                3F8020371E9E47C500DEC61D /* CoreAudioCaptureDeviceManager.h in Headers */,
                                07AFF4221EFB144900B545B3 /* CoreAudioCaptureSourceIOS.h in Headers */,
                                CD7D33481C7A16BF00041293 /* CoreVideoSoftLink.h in Headers */,
+                               574F55E1204F3B23002948C6 /* COSEConstants.h in Headers */,
                                862F129E18C1576F005C54AF /* CountedUserActivity.h in Headers */,
                                A80E6D040A1989CA007FB8C5 /* Counter.h in Headers */,
                                BC5EB9790E82069200B25965 /* CounterContent.h in Headers */,
                                656D37320ADBA5DE00A4554D /* LoaderNSURLExtras.h in Headers */,
                                51E6821016387302003BBF3C /* LoaderStrategy.h in Headers */,
                                8A12E35D11FA33280025836A /* LoadTiming.h in Headers */,
+                               57212152205365650062AA1F /* LocalAuthenticationSoftLink.h in Headers */,
+                               574F55E0204F3ACE002948C6 /* LocalAuthenticator.h in Headers */,
                                06E81ED70AB5D5E900C87837 /* LocalCurrentGraphicsContext.h in Headers */,
                                445775E520472F73008DCE5D /* LocalDefaultSystemAppearance.h in Headers */,
                                F5973DE015CFB2030027F804 /* LocaleMac.h in Headers */,
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#import "config.h"
-#import "WebCredentialsMessengerProxy.h"
-
-#if ENABLE(WEB_AUTHN)
+#pragma once
 
 #import <LocalAuthentication/LocalAuthentication.h>
-#import <WebCore/NotImplemented.h>
-#import <wtf/RetainPtr.h>
 #import <wtf/SoftLinking.h>
 
-SOFT_LINK_FRAMEWORK(LocalAuthentication)
-SOFT_LINK_CLASS(LocalAuthentication, LAContext);
-
-namespace WebKit {
-
-void WebCredentialsMessengerProxy::platformIsUserVerifyingPlatformAuthenticatorAvailable(uint64_t messageId)
-{
-#if defined(__i386__)
-    ASSERT_UNUSED(messageId, messageId);
-    notImplemented();
-#else
-    auto context = adoptNS([allocLAContextInstance() init]);
-    NSError *error = nil;
-
-    if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error])
-        isUserVerifyingPlatformAuthenticatorAvailableReply(messageId, true);
-    else {
-        LOG_ERROR("Couldn't evaluate authentication with biometrics policy: %@", error);
-        isUserVerifyingPlatformAuthenticatorAvailableReply(messageId, false);
-    }
-#endif // defined(__i386__)
-}
-
-} // namespace WebKit
+SOFT_LINK_FRAMEWORK_FOR_HEADER(WebCore, LocalAuthentication)
 
-#endif // ENABLE(WEB_AUTHN)
+SOFT_LINK_CLASS_FOR_HEADER(WebCore, LocalAuthentication, LAContext)
diff --git a/Source/WebCore/platform/cocoa/LocalAuthenticationSoftLink.mm b/Source/WebCore/platform/cocoa/LocalAuthenticationSoftLink.mm
new file mode 100644 (file)
index 0000000..3e3f295
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#import <LocalAuthentication/LocalAuthentication.h>
+#import <wtf/SoftLinking.h>
+
+SOFT_LINK_FRAMEWORK_FOR_SOURCE(WebCore, LocalAuthentication)
+
+SOFT_LINK_CLASS_FOR_SOURCE(WebCore, LocalAuthentication, LAContext)
index 699cd0c..32d7080 100644 (file)
@@ -28,7 +28,6 @@
 
 #if ENABLE(WEB_AUTHN)
 
-#include "Exception.h"
 #include "Internals.h"
 #include <wtf/Vector.h>
 
@@ -46,9 +45,10 @@ MockCredentialsMessenger::~MockCredentialsMessenger()
         exceptionReply(messageId, ExceptionData { NotAllowedError, ASCIILiteral("Operation timed out.") });
 }
 
-void MockCredentialsMessenger::setAttestationObject(const BufferSource& attestationObject)
+void MockCredentialsMessenger::setCreationReturnBundle(const BufferSource& credentialId, const BufferSource& attestationObject)
 {
-    ASSERT(m_attestationObject.isEmpty());
+    ASSERT(m_credentialId.isEmpty() && m_attestationObject.isEmpty());
+    m_credentialId.append(credentialId.data(), credentialId.length());
     m_attestationObject.append(attestationObject.data(), attestationObject.length());
 }
 
@@ -84,8 +84,10 @@ void MockCredentialsMessenger::makeCredential(const Vector<uint8_t>&, const Publ
         exceptionReply(messageId, ExceptionData { NotAllowedError, ASCIILiteral("User cancelled.") });
         return;
     }
-    if (!m_attestationObject.isEmpty()) {
-        makeCredentialReply(messageId, m_attestationObject);
+    if (!m_credentialId.isEmpty()) {
+        ASSERT(!m_attestationObject.isEmpty());
+        makeCredentialReply(messageId, m_credentialId, m_attestationObject);
+        m_credentialId.clear();
         m_attestationObject.clear();
         return;
     }
@@ -127,10 +129,10 @@ void MockCredentialsMessenger::isUserVerifyingPlatformAuthenticatorAvailable(Que
         isUserVerifyingPlatformAuthenticatorAvailableReply(messageId, false);
 }
 
-void MockCredentialsMessenger::makeCredentialReply(uint64_t messageId, const Vector<uint8_t>& attestationObject)
+void MockCredentialsMessenger::makeCredentialReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& attestationObject)
 {
     auto handler = takeCreationCompletionHandler(messageId);
-    handler(CreationReturnBundle(getIdFromAttestationObject(attestationObject).releaseNonNull(), ArrayBuffer::create(attestationObject.data(), attestationObject.size())));
+    handler(CreationReturnBundle(ArrayBuffer::create(credentialId.data(), credentialId.size()), ArrayBuffer::create(attestationObject.data(), attestationObject.size())));
 }
 
 void MockCredentialsMessenger::getAssertionReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& authenticatorData, const Vector<uint8_t>& signature, const Vector<uint8_t>& userHandle)
index b3a59b6..b4b08df 100644 (file)
@@ -37,13 +37,13 @@ class Internals;
 
 class MockCredentialsMessenger final : public CredentialsMessenger {
 public:
-    MockCredentialsMessenger(Internals&);
+    explicit MockCredentialsMessenger(Internals&);
     ~MockCredentialsMessenger();
 
     void setDidTimeOut() { m_didTimeOut = true; }
     void setDidUserCancel() { m_didUserCancel = true; }
     void setDidUserVerifyingPlatformAuthenticatorPresent() { m_didUserVerifyingPlatformAuthenticatorPresent = true; }
-    void setAttestationObject(const BufferSource&);
+    void setCreationReturnBundle(const BufferSource& credentialId, const BufferSource& attestationObject);
     void setAssertionReturnBundle(const BufferSource& credentialId, const BufferSource& authenticatorData, const BufferSource& signature, const BufferSource& userHandle);
 
     void ref();
@@ -53,7 +53,7 @@ private:
     void makeCredential(const Vector<uint8_t>&, const PublicKeyCredentialCreationOptions&, CreationCompletionHandler&&) final;
     void getAssertion(const Vector<uint8_t>& hash, const PublicKeyCredentialRequestOptions&, RequestCompletionHandler&&) final;
     void isUserVerifyingPlatformAuthenticatorAvailable(QueryCompletionHandler&&) final;
-    void makeCredentialReply(uint64_t messageId, const Vector<uint8_t>&) final;
+    void makeCredentialReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& attestationObject) final;
     void getAssertionReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& authenticatorData, const Vector<uint8_t>& signature, const Vector<uint8_t>& userHandle) final;
     void isUserVerifyingPlatformAuthenticatorAvailableReply(uint64_t messageId, bool) final;
 
@@ -63,7 +63,7 @@ private:
     bool m_didUserCancel { false };
     bool m_didUserVerifyingPlatformAuthenticatorPresent { false };
     Vector<uint8_t> m_attestationObject;
-    Vector<uint8_t> m_credentialId;
+    Vector<uint8_t> m_credentialId; // Overlapped between CreationReturnBundle and AssertionReturnBundle.
     Vector<uint8_t> m_authenticatorData;
     Vector<uint8_t> m_signature;
     Vector<uint8_t> m_userHandle;
index 0371623..31e126a 100644 (file)
@@ -30,6 +30,6 @@
     void setDidTimeOut();
     void setDidUserCancel();
     void setDidUserVerifyingPlatformAuthenticatorPresent();
-    void setAttestationObject(BufferSource attestationObject);
+    void setCreationReturnBundle(BufferSource credentialId, BufferSource attestationObject);
     void setAssertionReturnBundle(BufferSource credentialId, BufferSource authenticatorData, BufferSource signature, BufferSource userHandle);
 };
index f29c2fc..251115b 100644 (file)
@@ -1,3 +1,28 @@
+2018-03-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Implement authenticatorMakeCredential
+        https://bugs.webkit.org/show_bug.cgi?id=183527
+        <rdar://problem/35275886>
+
+        Reviewed by Brent Fulgham.
+
+        * UIProcess/CredentialManagement/WebCredentialsMessengerProxy.cpp:
+        (WebKit::WebCredentialsMessengerProxy::WebCredentialsMessengerProxy):
+        (WebKit::WebCredentialsMessengerProxy::makeCredential):
+        (WebKit::WebCredentialsMessengerProxy::getAssertion):
+        (WebKit::WebCredentialsMessengerProxy::isUserVerifyingPlatformAuthenticatorAvailable):
+        (WebKit::WebCredentialsMessengerProxy::exceptionReply):
+        (WebKit::WebCredentialsMessengerProxy::makeCredentialReply):
+        * UIProcess/CredentialManagement/WebCredentialsMessengerProxy.h:
+        * UIProcess/CredentialManagement/WebCredentialsMessengerProxy.messages.in:
+        * UIProcess/CredentialManagement/cocoa/WebCredentialsMessengerProxyCocoa.mm: Removed.
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/CredentialManagement/WebCredentialsMessenger.cpp:
+        (WebKit::WebCredentialsMessenger::makeCredential):
+        (WebKit::WebCredentialsMessenger::makeCredentialReply):
+        * WebProcess/CredentialManagement/WebCredentialsMessenger.h:
+        * WebProcess/CredentialManagement/WebCredentialsMessenger.messages.in:
+
 2018-03-17  Daniel Bates  <dabates@apple.com>
 
         Tests fail in messages_unittest.py
index 47d5d7c..feabea8 100644 (file)
@@ -32,6 +32,8 @@
 #include "WebCredentialsMessengerProxyMessages.h"
 #include "WebPageProxy.h"
 #include "WebProcessProxy.h"
+#include <WebCore/ExceptionData.h>
+#include <WebCore/LocalAuthenticator.h>
 
 namespace WebKit {
 
@@ -39,6 +41,7 @@ WebCredentialsMessengerProxy::WebCredentialsMessengerProxy(WebPageProxy& webPage
     : m_webPageProxy(webPageProxy)
 {
     m_webPageProxy.process().addMessageReceiver(Messages::WebCredentialsMessengerProxy::messageReceiverName(), m_webPageProxy.pageID(), *this);
+    m_authenticator = std::make_unique<WebCore::LocalAuthenticator>();
 }
 
 WebCredentialsMessengerProxy::~WebCredentialsMessengerProxy()
@@ -46,17 +49,49 @@ WebCredentialsMessengerProxy::~WebCredentialsMessengerProxy()
     m_webPageProxy.process().removeMessageReceiver(Messages::WebCredentialsMessengerProxy::messageReceiverName(), m_webPageProxy.pageID());
 }
 
-void WebCredentialsMessengerProxy::makeCredential(uint64_t)
+void WebCredentialsMessengerProxy::makeCredential(uint64_t messageId, const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialCreationOptions& options)
+{
+    // FIXME(182767)
+    if (!m_authenticator) {
+        exceptionReply(messageId, { WebCore::NotAllowedError, ASCIILiteral("No avaliable authenticators.") });
+        return;
+    }
+    // FIXME(183534): Weak pointers doesn't work in another thread because of race condition.
+    auto weakThis = m_weakFactory.createWeakPtr(*this);
+    auto callback = [weakThis, messageId] (const Vector<uint8_t>& credentialId, const Vector<uint8_t>& attestationObject) {
+        if (!weakThis)
+            return;
+        weakThis->makeCredentialReply(messageId, credentialId, attestationObject);
+    };
+    auto exceptionCallback = [weakThis, messageId] (const WebCore::ExceptionData& exception) {
+        if (!weakThis)
+            return;
+        weakThis->exceptionReply(messageId, exception);
+    };
+    m_authenticator->makeCredential(hash, options, WTFMove(callback), WTFMove(exceptionCallback));
+}
+
+void WebCredentialsMessengerProxy::getAssertion(uint64_t)
 {
 }
 
 void WebCredentialsMessengerProxy::isUserVerifyingPlatformAuthenticatorAvailable(uint64_t messageId)
 {
-    platformIsUserVerifyingPlatformAuthenticatorAvailable(messageId);
+    if (!m_authenticator) {
+        isUserVerifyingPlatformAuthenticatorAvailableReply(messageId, false);
+        return;
+    }
+    isUserVerifyingPlatformAuthenticatorAvailableReply(messageId, m_authenticator->isAvailable());
 }
 
-void WebCredentialsMessengerProxy::getAssertion(uint64_t)
+void WebCredentialsMessengerProxy::exceptionReply(uint64_t messageId, const WebCore::ExceptionData& exception)
+{
+    m_webPageProxy.send(Messages::WebCredentialsMessenger::ExceptionReply(messageId, exception));
+}
+
+void WebCredentialsMessengerProxy::makeCredentialReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& attestationObject)
 {
+    m_webPageProxy.send(Messages::WebCredentialsMessenger::MakeCredentialReply(messageId, credentialId, attestationObject));
 }
 
 void WebCredentialsMessengerProxy::isUserVerifyingPlatformAuthenticatorAvailableReply(uint64_t messageId, bool result)
index a266a3e..69eb70e 100644 (file)
 #if ENABLE(WEB_AUTHN)
 
 #include "MessageReceiver.h"
+#include <wtf/Forward.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/WeakPtr.h>
+
+namespace WebCore {
+class LocalAuthenticator;
+
+struct ExceptionData;
+struct PublicKeyCredentialCreationOptions;
+}
 
 namespace WebKit {
 
 class WebPageProxy;
 
 class WebCredentialsMessengerProxy : private IPC::MessageReceiver {
+    WTF_MAKE_NONCOPYABLE(WebCredentialsMessengerProxy);
 public:
     explicit WebCredentialsMessengerProxy(WebPageProxy&);
     ~WebCredentialsMessengerProxy();
@@ -43,19 +54,18 @@ private:
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
     // Receivers.
-    void makeCredential(uint64_t messageId);
+    void makeCredential(uint64_t messageId, const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialCreationOptions&);
     void getAssertion(uint64_t messageId);
     void isUserVerifyingPlatformAuthenticatorAvailable(uint64_t messageId);
 
     // Senders.
+    void exceptionReply(uint64_t messageId, const WebCore::ExceptionData&);
+    void makeCredentialReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& attestationObject);
     void isUserVerifyingPlatformAuthenticatorAvailableReply(uint64_t messageId, bool);
 
-    // Platform specific procedures.
-    // FIXME(182768): Might change to some forms of delegates later on.
-    // FIXME(182769): Figure out a way to auto-test the followings.
-    void platformIsUserVerifyingPlatformAuthenticatorAvailable(uint64_t messageId);
-
     WebPageProxy& m_webPageProxy;
+    std::unique_ptr<WebCore::LocalAuthenticator> m_authenticator;
+    WeakPtrFactory<WebCredentialsMessengerProxy> m_weakFactory;
 };
 
 } // namespace WebKit
index 6bc0e99..55120a2 100644 (file)
@@ -26,7 +26,7 @@
 
 messages -> WebCredentialsMessengerProxy {
 
-    MakeCredential(uint64_t messageId);
+    MakeCredential(uint64_t messageId, Vector<uint8_t> hash, struct WebCore::PublicKeyCredentialCreationOptions options);
     GetAssertion(uint64_t messageId);
     IsUserVerifyingPlatformAuthenticatorAvailable(uint64_t messageId);
 }
index f36fb3d..2977958 100644 (file)
                5760829D202D2C4000116678 /* WebCredentialsMessengerMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 5760828F20298FBD00116678 /* WebCredentialsMessengerMessages.h */; };
                5760829E202D2C4300116678 /* WebCredentialsMessengerProxyMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5760829A202BEE5A00116678 /* WebCredentialsMessengerProxyMessageReceiver.cpp */; };
                5760829F202D2C4600116678 /* WebCredentialsMessengerProxyMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 5760829B202BEE5A00116678 /* WebCredentialsMessengerProxyMessages.h */; };
-               57B90AAD203637F4000E61FA /* WebCredentialsMessengerProxyCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 57B90AAC203637F4000E61FA /* WebCredentialsMessengerProxyCocoa.mm */; };
                5C0B17781E7C880E00E9123C /* NetworkSocketStreamMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C0B17741E7C879C00E9123C /* NetworkSocketStreamMessageReceiver.cpp */; };
                5C0B17791E7C882100E9123C /* WebSocketStreamMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C0B17761E7C879C00E9123C /* WebSocketStreamMessageReceiver.cpp */; };
                5C0B177C1E7C885400E9123C /* WebSocketStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C0B177A1E7C884F00E9123C /* WebSocketStream.cpp */; };
                57608299202BDAE200116678 /* WebCredentialsMessengerProxy.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = WebCredentialsMessengerProxy.messages.in; sourceTree = "<group>"; };
                5760829A202BEE5A00116678 /* WebCredentialsMessengerProxyMessageReceiver.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WebCredentialsMessengerProxyMessageReceiver.cpp; sourceTree = "<group>"; };
                5760829B202BEE5A00116678 /* WebCredentialsMessengerProxyMessages.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebCredentialsMessengerProxyMessages.h; sourceTree = "<group>"; };
-               57B90AAC203637F4000E61FA /* WebCredentialsMessengerProxyCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebCredentialsMessengerProxyCocoa.mm; sourceTree = "<group>"; };
                5C0B17741E7C879C00E9123C /* NetworkSocketStreamMessageReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkSocketStreamMessageReceiver.cpp; sourceTree = "<group>"; };
                5C0B17751E7C879C00E9123C /* NetworkSocketStreamMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkSocketStreamMessages.h; sourceTree = "<group>"; };
                5C0B17761E7C879C00E9123C /* WebSocketStreamMessageReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebSocketStreamMessageReceiver.cpp; sourceTree = "<group>"; };
                57608294202BD84900116678 /* CredentialManagement */ = {
                        isa = PBXGroup;
                        children = (
-                               57B90AAB20363758000E61FA /* cocoa */,
                                57608296202BD8BA00116678 /* WebCredentialsMessengerProxy.cpp */,
                                57608295202BD8BA00116678 /* WebCredentialsMessengerProxy.h */,
                                57608299202BDAE200116678 /* WebCredentialsMessengerProxy.messages.in */,
                        path = CredentialManagement;
                        sourceTree = "<group>";
                };
-               57B90AAB20363758000E61FA /* cocoa */ = {
-                       isa = PBXGroup;
-                       children = (
-                               57B90AAC203637F4000E61FA /* WebCredentialsMessengerProxyCocoa.mm */,
-                       );
-                       path = cocoa;
-                       sourceTree = "<group>";
-               };
                5C1426F11C23F81700D41183 /* Downloads */ = {
                        isa = PBXGroup;
                        children = (
                                5760828E2029895E00116678 /* WebCredentialsMessenger.cpp in Sources */,
                                5760829C202D2C3C00116678 /* WebCredentialsMessengerMessageReceiver.cpp in Sources */,
                                57608298202BD8BA00116678 /* WebCredentialsMessengerProxy.cpp in Sources */,
-                               57B90AAD203637F4000E61FA /* WebCredentialsMessengerProxyCocoa.mm in Sources */,
                                5760829E202D2C4300116678 /* WebCredentialsMessengerProxyMessageReceiver.cpp in Sources */,
                                1AA83F6C1A5B63FF00026EC6 /* WebDatabaseProvider.cpp in Sources */,
                                CD19A26D1A13E82A008D650E /* WebDiagnosticLoggingClient.cpp in Sources */,
index d115aff..03bba68 100644 (file)
@@ -32,6 +32,7 @@
 #include "WebCredentialsMessengerProxyMessages.h"
 #include "WebPage.h"
 #include "WebProcess.h"
+#include <WebCore/PublicKeyCredentialCreationOptions.h>
 
 namespace WebKit {
 
@@ -46,8 +47,10 @@ WebCredentialsMessenger::~WebCredentialsMessenger()
     WebProcess::singleton().removeMessageReceiver(*this);
 }
 
-void WebCredentialsMessenger::makeCredential(const Vector<uint8_t>&, const WebCore::PublicKeyCredentialCreationOptions&, WebCore::CreationCompletionHandler&&)
+void WebCredentialsMessenger::makeCredential(const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialCreationOptions& options, WebCore::CreationCompletionHandler&& handler)
 {
+    auto messageId = addCreationCompletionHandler(WTFMove(handler));
+    m_webPage.send(Messages::WebCredentialsMessengerProxy::MakeCredential(messageId, hash, options));
 }
 
 void WebCredentialsMessenger::getAssertion(const Vector<uint8_t>&, const WebCore::PublicKeyCredentialRequestOptions&, WebCore::RequestCompletionHandler&&)
@@ -60,8 +63,10 @@ void WebCredentialsMessenger::isUserVerifyingPlatformAuthenticatorAvailable(WebC
     m_webPage.send(Messages::WebCredentialsMessengerProxy::IsUserVerifyingPlatformAuthenticatorAvailable(messageId));
 }
 
-void WebCredentialsMessenger::makeCredentialReply(uint64_t messageId, const Vector<uint8_t>&)
+void WebCredentialsMessenger::makeCredentialReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& attestationObject)
 {
+    auto handler = takeCreationCompletionHandler(messageId);
+    handler(WebCore::CreationReturnBundle(ArrayBuffer::create(credentialId.data(), credentialId.size()), ArrayBuffer::create(attestationObject.data(), attestationObject.size())));
 }
 
 void WebCredentialsMessenger::getAssertionReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& authenticatorData, const Vector<uint8_t>& signature, const Vector<uint8_t>& userHandle)
index 6de0b0e..a0ca7e5 100644 (file)
@@ -41,13 +41,13 @@ public:
 
 private:
     // WebCore::CredentialsMessenger
-    // sender
+    // Senders.
     void makeCredential(const Vector<uint8_t>&, const WebCore::PublicKeyCredentialCreationOptions&, WebCore::CreationCompletionHandler&&) final;
     void getAssertion(const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialRequestOptions&, WebCore::RequestCompletionHandler&&) final;
     void isUserVerifyingPlatformAuthenticatorAvailable(WebCore::QueryCompletionHandler&&) final;
 
-    // receiver
-    void makeCredentialReply(uint64_t messageId, const Vector<uint8_t>&) final;
+    // Receivers.
+    void makeCredentialReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& attestationObject) final;
     void getAssertionReply(uint64_t messageId, const Vector<uint8_t>& credentialId, const Vector<uint8_t>& authenticatorData, const Vector<uint8_t>& signature, const Vector<uint8_t>& userHandle) final;
     void isUserVerifyingPlatformAuthenticatorAvailableReply(uint64_t messageId, bool) final;
 
index 3ab1116..9280e06 100644 (file)
@@ -27,7 +27,7 @@
 messages -> WebCredentialsMessenger {
 
     ExceptionReply(uint64_t messageId, struct WebCore::ExceptionData exception);
-    MakeCredentialReply(uint64_t messageId, Vector<uint8_t> attestationObject);
+    MakeCredentialReply(uint64_t messageId, Vector<uint8_t> credentialId, Vector<uint8_t> attestationObject);
     GetAssertionReply(uint64_t messageId, Vector<uint8_t> credentialId, Vector<uint8_t> authenticatorData, Vector<uint8_t> signature, Vector<uint8_t> userHandle);
     IsUserVerifyingPlatformAuthenticatorAvailableReply(uint64_t messageId, bool result);
 exceptionReply(uint64_t messageId, struct WebCore::ExceptionData exception);
index bbe1934..ac11b87 100644 (file)
@@ -1,3 +1,26 @@
+2018-03-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Implement authenticatorMakeCredential
+        https://bugs.webkit.org/show_bug.cgi?id=183527
+        <rdar://problem/35275886>
+
+        Reviewed by Brent Fulgham.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/ios/LocalAuthenticator.mm: Added.
+        (TestWebKitAPI::getTestKey):
+        (TestWebKitAPI::cleanUpKeychain):
+        (TestWebKitAPI::LACantEvaluatePolicySwizzler::LACantEvaluatePolicySwizzler):
+        (TestWebKitAPI::LACantEvaluatePolicySwizzler::cantEvaluatePolicy):
+        (TestWebKitAPI::LACanEvaluatePolicySwizzler::LACanEvaluatePolicySwizzler):
+        (TestWebKitAPI::LACanEvaluatePolicySwizzler::canEvaluatePolicy):
+        (TestWebKitAPI::LAEvaluatePolicyFailedSwizzler::LAEvaluatePolicyFailedSwizzler):
+        (TestWebKitAPI::LAEvaluatePolicyFailedSwizzler::evaluatePolicyFailed):
+        (TestWebKitAPI::LAEvaluatePolicyPassedSwizzler::LAEvaluatePolicyPassedSwizzler):
+        (TestWebKitAPI::LAEvaluatePolicyPassedSwizzler::evaluatePolicyPassed):
+        (TestWebKitAPI::TestLocalAuthenticator::setFailureFlag):
+        (TestWebKitAPI::TEST):
+
 2018-03-17  Zalan Bujtas  <zalan@apple.com>
 
         [LayoutReloaded] BlockFormattingContext::placePositionedDescendants takes care of both in- and out-of-flow placement
index b394885..e557e8d 100644 (file)
                57303BC9200824D300355965 /* CBORValueTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57303BAC2006C56000355965 /* CBORValueTest.cpp */; };
                57303BCA20082C0100355965 /* CBORWriterTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57303BAB2006C55400355965 /* CBORWriterTest.cpp */; };
                57303BCB2008376500355965 /* CBORReaderTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57303BC220071E2200355965 /* CBORReaderTest.cpp */; };
+               574F55CF204D37C5002948C6 /* LocalAuthenticator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 574F55CE204D3763002948C6 /* LocalAuthenticator.mm */; };
+               574F55D2204D47F0002948C6 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 574F55D0204D471C002948C6 /* Security.framework */; };
                57599E211F07191900A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.mm in Sources */ = {isa = PBXBuildFile; fileRef = 57599E201F07191700A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.mm */; };
                57599E271F071AA000A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3 in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57599E241F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3 */; };
                57599E281F071AA000A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-shm in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57599E261F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-shm */; };
                57599E2A1F071AA000A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityRead.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57599E251F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityRead.html */; };
                57599E2B1F071AA000A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityWrite.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57599E231F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityWrite.html */; };
                5769C50B1D9B0002000847FB /* SerializedCryptoKeyWrap.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5769C50A1D9B0001000847FB /* SerializedCryptoKeyWrap.mm */; };
+               578CBD67204FB2C80083B9F2 /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 578CBD66204FB2C70083B9F2 /* LocalAuthentication.framework */; };
                57901FB11CAF142D00ED64F9 /* LoadInvalidURLRequest.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57901FB01CAF141C00ED64F9 /* LoadInvalidURLRequest.html */; };
                5797FE311EB15A6800B2F4A0 /* NavigationClientDefaultCrypto.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5797FE2F1EB15A5F00B2F4A0 /* NavigationClientDefaultCrypto.cpp */; };
                5797FE331EB15AB100B2F4A0 /* navigation-client-default-crypto.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5797FE321EB15A8900B2F4A0 /* navigation-client-default-crypto.html */; };
                57303BAC2006C56000355965 /* CBORValueTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CBORValueTest.cpp; sourceTree = "<group>"; };
                57303BC220071E2200355965 /* CBORReaderTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CBORReaderTest.cpp; sourceTree = "<group>"; };
                5735F0251F3A4EA6000EE801 /* TestWebKitAPI-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "TestWebKitAPI-iOS.entitlements"; sourceTree = "<group>"; };
+               574F55CE204D3763002948C6 /* LocalAuthenticator.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LocalAuthenticator.mm; sourceTree = "<group>"; };
+               574F55D0204D471C002948C6 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
                57599E201F07191700A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = IndexedDBStructuredCloneBackwardCompatibility.mm; sourceTree = "<group>"; };
                57599E231F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityWrite.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = IndexedDBStructuredCloneBackwardCompatibilityWrite.html; sourceTree = "<group>"; };
                57599E241F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3 */ = {isa = PBXFileReference; lastKnownFileType = file; path = IndexedDBStructuredCloneBackwardCompatibility.sqlite3; sourceTree = "<group>"; };
                57599E251F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityRead.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = IndexedDBStructuredCloneBackwardCompatibilityRead.html; sourceTree = "<group>"; };
                57599E261F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-shm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "IndexedDBStructuredCloneBackwardCompatibility.sqlite3-shm"; sourceTree = "<group>"; };
                5769C50A1D9B0001000847FB /* SerializedCryptoKeyWrap.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SerializedCryptoKeyWrap.mm; sourceTree = "<group>"; };
+               578CBD66204FB2C70083B9F2 /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; };
                57901FAC1CAF12C200ED64F9 /* LoadInvalidURLRequest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LoadInvalidURLRequest.mm; sourceTree = "<group>"; };
                57901FAE1CAF137100ED64F9 /* LoadInvalidURLRequest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LoadInvalidURLRequest.mm; sourceTree = "<group>"; };
                57901FB01CAF141C00ED64F9 /* LoadInvalidURLRequest.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = LoadInvalidURLRequest.html; sourceTree = "<group>"; };
                                634910E01E9D3FF300880309 /* CoreLocation.framework in Frameworks */,
                                CDA3159D1ED5643F009F60D3 /* IOKit.framework in Frameworks */,
                                7C83E03F1D0A61A000FEBCF3 /* libicucore.dylib in Frameworks */,
+                               578CBD67204FB2C80083B9F2 /* LocalAuthentication.framework in Frameworks */,
                                7A010BCD1D877C0D00EDE72A /* QuartzCore.framework in Frameworks */,
+                               574F55D2204D47F0002948C6 /* Security.framework in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                2E205BA31F527746005952DD /* AccessibilityTestsIOS.mm */,
                                F45B63FC1F19D410009D38B9 /* ActionSheetTests.mm */,
                                F4D4F3B71E4E36E400BB2767 /* DataInteractionTests.mm */,
+                               574F55CE204D3763002948C6 /* LocalAuthenticator.mm */,
                                7560917719259C59009EF06E /* MemoryCacheAddImageToCacheIOS.mm */,
                                F4C8797E2059D8D3009CD00B /* ScrollViewInsetTests.mm */,
                                F46849BD1EEF58E400B937FE /* UIPasteboardTests.mm */,
                                7C83E0331D0A5F2700FEBCF3 /* libicucore.dylib */,
                                4135FB862011FABF00332139 /* libWebCoreTestSupport.dylib */,
                                7C83E0291D0A5CDF00FEBCF3 /* libWTF.a */,
+                               578CBD66204FB2C70083B9F2 /* LocalAuthentication.framework */,
                                7A010BCC1D877C0D00EDE72A /* QuartzCore.framework */,
+                               574F55D0204D471C002948C6 /* Security.framework */,
                        );
                        name = Frameworks;
                        sourceTree = "<group>";
                                5C838F7F1DB04F900082858F /* LoadInvalidURLRequest.mm in Sources */,
                                7C83E0C01D0A652700FEBCF3 /* LoadInvalidURLRequest.mm in Sources */,
                                7CCE7F001A411AE600447C4C /* LoadPageOnCrash.cpp in Sources */,
+                               574F55CF204D37C5002948C6 /* LocalAuthenticator.mm in Sources */,
                                51E6A8941D2F1C0A00C004B6 /* LocalStorageClear.mm in Sources */,
                                46C519DA1D355AB200DAA51A /* LocalStorageNullEntries.mm in Sources */,
                                7A6A2C701DCCFA8C00C0D085 /* LocalStorageQuirkTest.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/ios/LocalAuthenticator.mm b/Tools/TestWebKitAPI/Tests/ios/LocalAuthenticator.mm
new file mode 100644 (file)
index 0000000..65fb1ca
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#if PLATFORM(IOS)
+
+#import "InstanceMethodSwizzler.h"
+#import "PlatformUtilities.h"
+#import <LocalAuthentication/LocalAuthentication.h>
+#import <Security/SecItem.h>
+#import <WebCore/CBORReader.h>
+#import <WebCore/COSEConstants.h>
+#import <WebCore/ExceptionData.h>
+#import <WebCore/LocalAuthenticator.h>
+#import <WebCore/PublicKeyCredentialCreationOptions.h>
+#import <wtf/BlockPtr.h>
+#import <wtf/text/Base64.h>
+#import <wtf/text/WTFString.h>
+
+namespace TestWebKitAPI {
+
+const String testAttestationCertificateBase64 = String() +
+    "MIIB6jCCAZCgAwIBAgIGAWHAxcjvMAoGCCqGSM49BAMCMFMxJzAlBgNVBAMMHkJh" +
+    "c2ljIEF0dGVzdGF0aW9uIFVzZXIgU3ViIENBMTETMBEGA1UECgwKQXBwbGUgSW5j" +
+    "LjETMBEGA1UECAwKQ2FsaWZvcm5pYTAeFw0xODAyMjMwMzM3MjJaFw0xODAyMjQw" +
+    "MzQ3MjJaMGoxIjAgBgNVBAMMGTAwMDA4MDEwLTAwMEE0OUEyMzBBMDIxM0ExGjAY" +
+    "BgNVBAsMEUJBQSBDZXJ0aWZpY2F0aW9uMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMw" +
+    "EQYDVQQIDApDYWxpZm9ybmlhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvCje" +
+    "Pzr6Sg76XMoHuGabPaG6zjpLFL8Zd8/74Hh5PcL2Zq+o+f7ENXX+7nEXXYt0S8Ux" +
+    "5TIRw4hgbfxXQbWLEqM5MDcwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBPAw" +
+    "FwYJKoZIhvdjZAgCBAowCKEGBAR0ZXN0MAoGCCqGSM49BAMCA0gAMEUCIAlK8A8I" +
+    "k43TbvKuYGHZs1DTgpTwmKTBvIUw5bwgZuYnAiEAtuJjDLKbGNJAJFMi5deEBqno" +
+    "pBTCqbfbDJccfyQpjnY=";
+const String testAttestationIssuingCACertificateBase64 = String() +
+    "MIICIzCCAaigAwIBAgIIeNjhG9tnDGgwCgYIKoZIzj0EAwIwUzEnMCUGA1UEAwwe" +
+    "QmFzaWMgQXR0ZXN0YXRpb24gVXNlciBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJ" +
+    "bmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMB4XDTE3MDQyMDAwNDIwMFoXDTMyMDMy" +
+    "MjAwMDAwMFowUzEnMCUGA1UEAwweQmFzaWMgQXR0ZXN0YXRpb24gVXNlciBTdWIg" +
+    "Q0ExMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMFkw" +
+    "EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoSZ/1t9eBAEVp5a8PrXacmbGb8zNC1X3" +
+    "StLI9YO6Y0CL7blHmSGmjGWTwD4Q+i0J2BY3+bPHTGRyA9jGB3MSbaNmMGQwEgYD" +
+    "VR0TAQH/BAgwBgEB/wIBADAfBgNVHSMEGDAWgBSD5aMhnrB0w/lhkP2XTiMQdqSj" +
+    "8jAdBgNVHQ4EFgQU5mWf1DYLTXUdQ9xmOH/uqeNSD80wDgYDVR0PAQH/BAQDAgEG" +
+    "MAoGCCqGSM49BAMCA2kAMGYCMQC3M360LLtJS60Z9q3vVjJxMgMcFQ1roGTUcKqv" +
+    "W+4hJ4CeJjySXTgq6IEHn/yWab4CMQCm5NnK6SOSK+AqWum9lL87W3E6AA1f2TvJ" +
+    "/hgok/34jr93nhS87tOQNdxDS8zyiqw=";
+const String testCredentialIdBase64 = "SMSXHngF7hEOsElA73C3RY+8bR4=";
+const String testES256PrivateKeyBase64 = String() +
+    "BDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749VBJPgqUIwfhWHJ91nb7U" +
+    "PH76c0+WFOzZKslPyyFse4goGIW2R7k9VHLPEZl5nfnBgEVFh5zev+/xpHQIvuq6" +
+    "RQ==";
+const String testRpId = "localhost";
+const String testUsername = "username";
+const uint8_t testUserhandle[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x9};
+
+RetainPtr<SecKeyRef> getTestKey()
+{
+    NSDictionary* options = @{
+        (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
+        (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
+        (id)kSecAttrKeySizeInBits: @256,
+    };
+    CFErrorRef errorRef = NULL;
+    auto key = adoptCF(SecKeyCreateWithData(
+        (__bridge CFDataRef)adoptNS([[NSData alloc] initWithBase64EncodedString:testES256PrivateKeyBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get(),
+        (__bridge CFDictionaryRef)options,
+        &errorRef
+    ));
+    EXPECT_FALSE(errorRef);
+
+    return key;
+}
+
+void cleanUpKeychain()
+{
+    // Cleanup the keychain.
+    NSDictionary* deleteQuery = @{
+        (id)kSecClass: (id)kSecClassKey,
+        (id)kSecAttrLabel: testRpId,
+    };
+    SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
+}
+
+class LACantEvaluatePolicySwizzler {
+public:
+    LACantEvaluatePolicySwizzler()
+        : m_swizzler([LAContext class], @selector(canEvaluatePolicy:error:), reinterpret_cast<IMP>(cantEvaluatePolicy))
+    {
+    }
+private:
+    static BOOL cantEvaluatePolicy()
+    {
+        return NO;
+    }
+    InstanceMethodSwizzler m_swizzler;
+};
+
+class LACanEvaluatePolicySwizzler {
+public:
+    LACanEvaluatePolicySwizzler()
+        : m_swizzler([LAContext class], @selector(canEvaluatePolicy:error:), reinterpret_cast<IMP>(canEvaluatePolicy))
+    {
+    }
+private:
+    static BOOL canEvaluatePolicy()
+    {
+        return YES;
+    }
+    InstanceMethodSwizzler m_swizzler;
+};
+
+class LAEvaluatePolicyFailedSwizzler {
+public:
+    LAEvaluatePolicyFailedSwizzler()
+        : m_swizzler([LAContext class], @selector(evaluatePolicy:localizedReason:reply:), reinterpret_cast<IMP>(evaluatePolicyFailed))
+    {
+    }
+private:
+    static void evaluatePolicyFailed(id self, SEL _cmd, LAPolicy policy, NSString * reason, void (^reply)(BOOL success, NSError *error))
+    {
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            Util::sleep(1); // mimic user interaction delay
+            reply(NO, nil);
+        });
+    }
+    InstanceMethodSwizzler m_swizzler;
+};
+
+class LAEvaluatePolicyPassedSwizzler {
+public:
+    LAEvaluatePolicyPassedSwizzler()
+        : m_swizzler([LAContext class], @selector(evaluatePolicy:localizedReason:reply:), reinterpret_cast<IMP>(evaluatePolicyPassed))
+    {
+    }
+private:
+    static void evaluatePolicyPassed(id self, SEL _cmd, LAPolicy policy, NSString * reason, void (^reply)(BOOL success, NSError *error))
+    {
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            Util::sleep(1); // mimic user interaction delay
+            reply(YES, nil);
+        });
+    }
+    InstanceMethodSwizzler m_swizzler;
+};
+
+class TestLocalAuthenticator : public WebCore::LocalAuthenticator {
+public:
+    void setFailureFlag() { m_failureFlag = true; }
+
+protected:
+    void issueClientCertificate(const String& rpId, const String& username, const Vector<uint8_t>& hash, WebCore::CompletionBlock _Nonnull completion) const final
+    {
+        if (m_failureFlag) {
+            completion(NULL, NULL, [NSError errorWithDomain:NSOSStatusErrorDomain code:-1 userInfo:nil]);
+            return;
+        }
+
+        ASSERT_EQ(32ul, hash.size());
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), BlockPtr<void()>::fromCallable([rpId = rpId.isolatedCopy(), username = username.isolatedCopy(), completion = makeBlockPtr(completion)] {
+            Util::sleep(1); // mimic network delay
+
+            // Get Key and add it to Keychain
+            auto key = getTestKey();
+            String label(username);
+            label.append("@" + rpId + "-rk"); // mimic what DeviceIdentity would do.
+            NSDictionary* addQuery = @{
+                (id)kSecValueRef: (id)key.get(),
+                (id)kSecClass: (id)kSecClassKey,
+                (id)kSecAttrLabel: (id)label,
+            };
+            OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
+            ASSERT_FALSE(status);
+
+            // Construct dummy certificates
+            auto attestationCertificate = adoptCF(SecCertificateCreateWithData(NULL, (__bridge CFDataRef)adoptNS([[NSData alloc] initWithBase64EncodedString:testAttestationCertificateBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get()));
+            auto attestationIssuingCACertificate = adoptCF(SecCertificateCreateWithData(NULL, (__bridge CFDataRef)adoptNS([[NSData alloc] initWithBase64EncodedString:testAttestationIssuingCACertificateBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get()));
+
+            // Do self-attestation instead.
+            completion(key.get(), [NSArray arrayWithObjects: (__bridge id)attestationCertificate.get(), (__bridge id)attestationIssuingCACertificate.get(), nil], NULL);
+        }).get());
+    }
+
+private:
+    bool m_failureFlag { false };
+};
+
+// FIXME(182769): Convert the followings to proper API tests.
+TEST(LocalAuthenticator, MakeCredentialNotSupportedPubKeyCredParams)
+{
+    WebCore::PublicKeyCredentialCreationOptions creationOptions;
+    creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, -35 }); // ES384
+    creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, -257 }); // RS256
+
+    bool done = false;
+    std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
+    auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
+        EXPECT_FALSE(true);
+        done = true;
+    };
+    auto exceptionCallback = [&done] (const WebCore::ExceptionData& exception) mutable {
+        EXPECT_EQ(WebCore::NotSupportedError, exception.code);
+        EXPECT_STREQ("The platform attached authenticator doesn't support any provided PublicKeyCredentialParameters.", exception.message.ascii().data());
+        done = true;
+    };
+    authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
+
+    TestWebKitAPI::Util::run(&done);
+}
+
+TEST(LocalAuthenticator, MakeCredentialExcludeCredentialsMatch)
+{
+    // Insert the test key into Keychain
+    auto key = getTestKey();
+    NSDictionary* addQuery = @{
+        (id)kSecValueRef: (id)key.get(),
+        (id)kSecClass: (id)kSecClassKey,
+        (id)kSecAttrLabel: testRpId,
+        (id)kSecAttrApplicationTag: [NSData dataWithBytes:testUserhandle length:sizeof(testUserhandle)],
+    };
+    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
+    EXPECT_FALSE(status);
+
+    // Invoke the LocalAuthenticator.
+    WebCore::PublicKeyCredentialDescriptor descriptor;
+    descriptor.type = WebCore::PublicKeyCredentialType::PublicKey;
+    WTF::base64Decode(testCredentialIdBase64, descriptor.idVector);
+    WebCore::PublicKeyCredentialCreationOptions creationOptions;
+    creationOptions.rp.id = testRpId;
+    creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
+    creationOptions.excludeCredentials.append(WTFMove(descriptor));
+
+    bool done = false;
+    std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
+    auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
+        EXPECT_FALSE(true);
+        cleanUpKeychain();
+        done = true;
+    };
+    auto exceptionCallback = [&done] (const WebCore::ExceptionData& exception) mutable {
+        EXPECT_EQ(WebCore::NotAllowedError, exception.code);
+        EXPECT_STREQ("At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.", exception.message.ascii().data());
+        cleanUpKeychain();
+        done = true;
+    };
+    authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
+
+    TestWebKitAPI::Util::run(&done);
+}
+
+TEST(LocalAuthenticator, MakeCredentialBiometricsNotEnrolled)
+{
+    LACantEvaluatePolicySwizzler swizzler;
+
+    WebCore::PublicKeyCredentialCreationOptions creationOptions;
+    creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
+
+    bool done = false;
+    std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
+    auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
+        EXPECT_FALSE(true);
+        done = true;
+    };
+    auto exceptionCallback = [&done] (const WebCore::ExceptionData& exception) mutable {
+        EXPECT_EQ(WebCore::NotAllowedError, exception.code);
+        EXPECT_STREQ("No avaliable authenticators.", exception.message.ascii().data());
+        done = true;
+    };
+    authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
+
+    TestWebKitAPI::Util::run(&done);
+}
+
+TEST(LocalAuthenticator, MakeCredentialBiometricsNotAuthenticated)
+{
+    LACanEvaluatePolicySwizzler canEvaluatePolicySwizzler;
+    LAEvaluatePolicyFailedSwizzler evaluatePolicyFailedSwizzler;
+
+    WebCore::PublicKeyCredentialCreationOptions creationOptions;
+    creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
+
+    bool done = false;
+    std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
+    auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
+        EXPECT_FALSE(true);
+        done = true;
+    };
+    auto exceptionCallback = [&done] (const WebCore::ExceptionData& exception) mutable {
+        EXPECT_EQ(WebCore::NotAllowedError, exception.code);
+        EXPECT_STREQ("Couldn't get user consent.", exception.message.ascii().data());
+        done = true;
+    };
+    authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
+
+    TestWebKitAPI::Util::run(&done);
+}
+
+TEST(LocalAuthenticator, MakeCredentialNotAttestated)
+{
+    LACanEvaluatePolicySwizzler canEvaluatePolicySwizzler;
+    LAEvaluatePolicyPassedSwizzler evaluatePolicyPassedSwizzler;
+
+    WebCore::PublicKeyCredentialCreationOptions creationOptions;
+    creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
+
+    bool done = false;
+    std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
+    authenticator->setFailureFlag();
+    auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
+        EXPECT_FALSE(true);
+        done = true;
+    };
+    auto exceptionCallback = [&done] (const WebCore::ExceptionData& exception) mutable {
+        EXPECT_EQ(WebCore::UnknownError, exception.code);
+        EXPECT_STREQ("Unknown internal error.", exception.message.ascii().data());
+        done = true;
+    };
+    authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
+
+    TestWebKitAPI::Util::run(&done);
+}
+
+TEST(LocalAuthenticator, MakeCredentialDeleteOlderCredenital)
+{
+    LACanEvaluatePolicySwizzler canEvaluatePolicySwizzler;
+    LAEvaluatePolicyPassedSwizzler evaluatePolicyPassedSwizzler;
+
+    // Insert the older credential
+    auto key = getTestKey();
+    NSDictionary* addQuery = @{
+        (id)kSecValueRef: (id)key.get(),
+        (id)kSecClass: (id)kSecClassKey,
+        (id)kSecAttrLabel: testRpId,
+        (id)kSecAttrApplicationTag: [NSData dataWithBytes:testUserhandle length:sizeof(testUserhandle)],
+    };
+    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addQuery, NULL);
+    EXPECT_FALSE(status);
+
+    WebCore::PublicKeyCredentialCreationOptions creationOptions;
+    creationOptions.rp.id = testRpId;
+    creationOptions.user.name = testUsername;
+    creationOptions.user.idVector.append(testUserhandle, sizeof(testUserhandle));
+    creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
+
+    bool done = false;
+    std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
+    authenticator->setFailureFlag();
+    auto callback = [&done] (const Vector<uint8_t>&, const Vector<uint8_t>&) {
+        EXPECT_FALSE(true);
+        done = true;
+    };
+    auto exceptionCallback = [&done] (const WebCore::ExceptionData&) mutable {
+        NSDictionary *query = @{
+            (id)kSecClass: (id)kSecClassKey,
+            (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
+            (id)kSecAttrLabel: testRpId,
+            (id)kSecAttrApplicationTag: [NSData dataWithBytes:testUserhandle length:sizeof(testUserhandle)],
+        };
+        OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);
+        EXPECT_EQ(errSecItemNotFound, status);
+        done = true;
+    };
+    authenticator->makeCredential({ }, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
+
+    TestWebKitAPI::Util::run(&done);
+}
+
+TEST(LocalAuthenticator, MakeCredentialPassedWithSelfAttestation)
+{
+    LACanEvaluatePolicySwizzler canEvaluatePolicySwizzler;
+    LAEvaluatePolicyPassedSwizzler evaluatePolicyPassedSwizzler;
+
+    WebCore::PublicKeyCredentialCreationOptions creationOptions;
+    creationOptions.rp.id = testRpId;
+    creationOptions.user.name = testUsername;
+    creationOptions.user.idVector.append(testUserhandle, sizeof(testUserhandle));
+    creationOptions.pubKeyCredParams.append({ WebCore::PublicKeyCredentialType::PublicKey, COSE::ES256 });
+
+    bool done = false;
+    std::unique_ptr<TestLocalAuthenticator> authenticator = std::make_unique<TestLocalAuthenticator>();
+    auto callback = [&done] (const Vector<uint8_t>& credentialId, const Vector<uint8_t>& attestationObjet) {
+        // Check Keychain
+        NSDictionary *query = @{
+            (id)kSecClass: (id)kSecClassKey,
+            (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
+            (id)kSecAttrLabel: testRpId,
+            (id)kSecAttrApplicationLabel: adoptNS([[NSData alloc] initWithBase64EncodedString:testCredentialIdBase64 options:NSDataBase64DecodingIgnoreUnknownCharacters]).get(),
+            (id)kSecAttrApplicationTag: [NSData dataWithBytes:testUserhandle length:sizeof(testUserhandle)],
+        };
+        OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);
+        EXPECT_FALSE(status);
+
+        // Check Credential ID
+        EXPECT_TRUE(WTF::base64Encode(credentialId.data(), credentialId.size()) == testCredentialIdBase64);
+
+        // Check Attestation Object
+        auto attestationObjectMap = cbor::CBORReader::read(attestationObjet);
+        ASSERT_TRUE(!!attestationObjectMap);
+
+        // Check Authenticator Data.
+        auto& authData = attestationObjectMap->getMap().find(cbor::CBORValue("authData"))->second.getByteString();
+        size_t pos = 0;
+        uint8_t expectedRpIdHash[] = {
+            0x49, 0x96, 0x0d, 0xe5, 0x88, 0x0e, 0x8c, 0x68,
+            0x74, 0x34, 0x17, 0x0f, 0x64, 0x76, 0x60, 0x5b,
+            0x8f, 0xe4, 0xae, 0xb9, 0xa2, 0x86, 0x32, 0xc7,
+            0x99, 0x5c, 0xf3, 0xba, 0x83, 0x1d, 0x97, 0x63
+        };
+        EXPECT_FALSE(memcmp(authData.data() + pos, expectedRpIdHash, sizeof(expectedRpIdHash)));
+        pos += sizeof(expectedRpIdHash);
+
+        // FLAGS
+        EXPECT_EQ(69, authData[pos]);
+        pos++;
+
+        uint32_t counter = -1;
+        memcpy(&counter, authData.data() + pos, sizeof(uint32_t));
+        EXPECT_EQ(0u, counter);
+        pos += sizeof(uint32_t);
+
+        uint8_t expectedAAGUID[] = {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+        };
+        EXPECT_FALSE(memcmp(authData.data() + pos, expectedAAGUID, sizeof(expectedAAGUID)));
+        pos += sizeof(expectedAAGUID);
+
+        uint16_t l = -1;
+        memcpy(&l, authData.data() + pos, sizeof(uint16_t));
+        EXPECT_EQ(20u, l);
+        pos += sizeof(uint16_t);
+
+        EXPECT_FALSE(memcmp(authData.data() + pos, credentialId.data(), l));
+        pos += l;
+
+        // Credential Public Key
+        // FIXME(183536): The CBOR reader doesn't support negative integer as map key. Thus we couldn't utilzie it.
+        EXPECT_STREQ("pQECAyYgASFYIDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749IlggVBJPgqUIwfhWHJ91nb7UPH76c0+WFOzZKslPyyFse4g=", WTF::base64Encode(authData.data() + pos, authData.size() - pos).ascii().data());
+
+        // Check Self Attestation
+        EXPECT_STREQ("Apple", attestationObjectMap->getMap().find(cbor::CBORValue("fmt"))->second.getString().ascii().data());
+
+        auto& attStmt = attestationObjectMap->getMap().find(cbor::CBORValue("attStmt"))->second.getMap();
+        EXPECT_EQ(COSE::ES256, attStmt.find(cbor::CBORValue("alg"))->second.getNegative());
+
+        auto& sig = attStmt.find(cbor::CBORValue("sig"))->second.getByteString();
+        auto privateKey = getTestKey();
+        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));
+
+        // Check certificates
+        auto& x5c = attStmt.find(cbor::CBORValue("x5c"))->second.getArray();
+        auto& attestationCertificateData = x5c[0].getByteString();
+        auto attestationCertificate = adoptCF(SecCertificateCreateWithData(NULL, (__bridge CFDataRef)[NSData dataWithBytes:attestationCertificateData.data() length:attestationCertificateData.size()]));
+        CFStringRef commonName = NULL;
+        status = SecCertificateCopyCommonName(attestationCertificate.get(), &commonName);
+        auto retainCommonName = adoptCF(commonName);
+        ASSERT(!status);
+        EXPECT_STREQ("00008010-000A49A230A0213A", [(NSString *)commonName cStringUsingEncoding: NSASCIIStringEncoding]);
+
+        auto& attestationIssuingCACertificateData = x5c[1].getByteString();
+        auto attestationIssuingCACertificate = adoptCF(SecCertificateCreateWithData(NULL, (__bridge CFDataRef)[NSData dataWithBytes:attestationIssuingCACertificateData.data() length:attestationIssuingCACertificateData.size()]));
+        commonName = NULL;
+        status = SecCertificateCopyCommonName(attestationIssuingCACertificate.get(), &commonName);
+        retainCommonName = adoptCF(commonName);
+        ASSERT(!status);
+        EXPECT_STREQ("Basic Attestation User Sub CA1", [(NSString *)commonName cStringUsingEncoding: NSASCIIStringEncoding]);
+
+        cleanUpKeychain();
+        done = true;
+    };
+    auto exceptionCallback = [&done] (const WebCore::ExceptionData&) mutable {
+        EXPECT_FALSE(true);
+        cleanUpKeychain();
+        done = true;
+    };
+    Vector<uint8_t> hash(32);
+    authenticator->makeCredential(hash, creationOptions, WTFMove(callback), WTFMove(exceptionCallback));
+
+    TestWebKitAPI::Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif // PLATFORM(IOS)
index db75753..493a625 100644 (file)
@@ -1,3 +1,13 @@
+2018-03-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Implement authenticatorMakeCredential
+        https://bugs.webkit.org/show_bug.cgi?id=183527
+        <rdar://problem/35275886>
+
+        Reviewed by Brent Fulgham.
+
+        * WebKitPrivateFrameworkStubs/iOS/11/DeviceIdentity.framework/DeviceIdentity.tbd: Added.
+
 2018-02-20  Tim Horton  <timothy_horton@apple.com>
 
         Introduce HAVE(IOSURFACE_ACCELERATOR)
diff --git a/WebKitLibraries/WebKitPrivateFrameworkStubs/iOS/11/DeviceIdentity.framework/DeviceIdentity.tbd b/WebKitLibraries/WebKitPrivateFrameworkStubs/iOS/11/DeviceIdentity.framework/DeviceIdentity.tbd
new file mode 100644 (file)
index 0000000..3cf4464
--- /dev/null
@@ -0,0 +1,30 @@
+--- !tapi-tbd-v2
+archs:
+  - armv7
+  - armv7s
+  - arm64
+  - i386
+  - x86_64
+exports:
+  -
+    archs:
+      - armv7
+      - armv7s
+      - arm64
+      - i386
+      - x86_64
+    symbols:
+      - _DeviceIdentityIssueClientCertificateWithCompletion
+      - _kMAOptionsBAAAccessControls
+      - _kMAOptionsBAAIgnoreExistingKeychainItems
+      - _kMAOptionsBAAKeychainAccessGroup
+      - _kMAOptionsBAAKeychainLabel
+      - _kMAOptionsBAANonce
+      - _kMAOptionsBAAOIDNonce
+      - _kMAOptionsBAAOIDSToInclude
+      - _kMAOptionsBAASCRTAttestation
+      - _kMAOptionsBAAValidity
+install-name: /System/Library/PrivateFrameworks/DeviceIdentity.framework/DeviceIdentity
+objc-constraint: none
+platform: ios
+...