Diff
Modified: trunk/Source/WebCore/ChangeLog (288815 => 288816)
--- trunk/Source/WebCore/ChangeLog 2022-01-31 16:05:31 UTC (rev 288815)
+++ trunk/Source/WebCore/ChangeLog 2022-01-31 16:55:49 UTC (rev 288816)
@@ -1,3 +1,17 @@
+2022-01-31 J Pascoe <[email protected]>
+
+ [WebAuthn] Provide SPI to export/import local credentials
+ https://bugs.webkit.org/show_bug.cgi?id=234112
+ rdar://84822000
+
+ Reviewed by Brent Fulgham.
+
+ This change adds SPI to _WKWebAuthenticationPanel to provide the ability
+ to import / export local credentials. Constants are used during serialization
+ as keys.
+
+ * Modules/webauthn/WebAuthenticationConstants.h: constants for credential serialization
+
2022-01-31 Antti Koivisto <[email protected]>
[CSS Container Queries] Check for query containers when matching rules
Modified: trunk/Source/WebCore/Modules/webauthn/WebAuthenticationConstants.h (288815 => 288816)
--- trunk/Source/WebCore/Modules/webauthn/WebAuthenticationConstants.h 2022-01-31 16:05:31 UTC (rev 288815)
+++ trunk/Source/WebCore/Modules/webauthn/WebAuthenticationConstants.h 2022-01-31 16:55:49 UTC (rev 288816)
@@ -77,8 +77,18 @@
Get
};
+// rdar://88104045 - Remove once staged change completed
const char LocalAuthenticatiorAccessGroup[] = "com.apple.webkit.webauthn";
+constexpr const char LocalAuthenticatorAccessGroup[] = "com.apple.webkit.webauthn";
+
+// Credential serialization
+constexpr const char privateKeyKey[] = "priv";
+constexpr const char keyTypeKey[] = "key_type";
+constexpr const char keySizeKey[] = "key_size";
+constexpr const char relyingPartyKey[] = "rp";
+constexpr const char applicationTagKey[] = "tag";
+
} // namespace WebCore
namespace WebAuthn {
Modified: trunk/Source/WebKit/ChangeLog (288815 => 288816)
--- trunk/Source/WebKit/ChangeLog 2022-01-31 16:05:31 UTC (rev 288815)
+++ trunk/Source/WebKit/ChangeLog 2022-01-31 16:55:49 UTC (rev 288816)
@@ -1,3 +1,25 @@
+2022-01-31 J Pascoe <[email protected]>
+
+ [WebAuthn] Provide SPI to export/import local credentials
+ https://bugs.webkit.org/show_bug.cgi?id=234112
+ rdar://84822000
+
+ Reviewed by Brent Fulgham.
+
+ Covered by new API tests.
+
+ This patch adds new SPI to _WKWebAuthenticationPanel.h to import and export local
+ webauthn credentials. CBOR is used for serialization. WKErrors are used to differentiate
+ between malformed vs duplicate keys during import.
+
+ * UIProcess/API/Cocoa/WKError.h:
+ * UIProcess/API/Cocoa/WKError.mm:
+ (localizedDescriptionForErrorCode):
+ * UIProcess/API/Cocoa/_WKWebAuthenticationPanel.h:
+ * UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm:
+ (+[_WKWebAuthenticationPanel importLocalAuthenticatorCredential:error:]):
+ (+[_WKWebAuthenticationPanel exportLocalAuthenticatorCredentialWithID:error:]):
+
2022-01-31 Alexander Mikhaylenko <[email protected]>
REGRESSION(r288644): [GTK4] Criticals when using pinch zoom
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.h (288815 => 288816)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.h 2022-01-31 16:05:31 UTC (rev 288815)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.h 2022-01-31 16:55:49 UTC (rev 288816)
@@ -63,6 +63,9 @@
WKErrorJavaScriptInvalidFrameTarget WK_API_AVAILABLE(macos(11.0), ios(14.0)),
WKErrorNavigationAppBoundDomain WK_API_AVAILABLE(macos(11.0), ios(14.0)),
WKErrorJavaScriptAppBoundDomain WK_API_AVAILABLE(macos(11.0), ios(14.0)),
+ WKErrorDuplicateCredential WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA)),
+ WKErrorMalformedCredential WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA)),
+ WKErrorCredentialNotFound WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA)),
} WK_API_AVAILABLE(macos(10.10), ios(8.0));
NS_ASSUME_NONNULL_END
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.mm (288815 => 288816)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.mm 2022-01-31 16:05:31 UTC (rev 288815)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.mm 2022-01-31 16:55:49 UTC (rev 288816)
@@ -82,6 +82,15 @@
case WKErrorJavaScriptAppBoundDomain:
return WEB_UI_STRING("_javascript_ execution targeted a frame that is not in an app-bound domain", "WKErrorJavaScriptAppBoundDomain description");
+
+ case WKErrorDuplicateCredential:
+ return WEB_UI_STRING("This credential is already present", "WKErrorDuplicateCredential description");
+
+ case WKErrorMalformedCredential:
+ return WEB_UI_STRING("This credential is malformed", "WKErrorMalformedCredential description");
+
+ case WKErrorCredentialNotFound:
+ return WEB_UI_STRING("Credential could not be found", "WKErrorCredentialNotFound description");
}
}
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.h (288815 => 288816)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.h 2022-01-31 16:05:31 UTC (rev 288815)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.h 2022-01-31 16:55:49 UTC (rev 288816)
@@ -117,6 +117,9 @@
+ (void)clearAllLocalAuthenticatorCredentials WK_API_AVAILABLE(macos(12.0), ios(15.0));
+ (void)setUsernameForLocalCredentialWithID:(NSData *)credentialID username: (NSString *)username WK_API_AVAILABLE(macos(12.0), ios(15.0));
++ (NSData *)exportLocalAuthenticatorCredentialWithID:(NSData *)credentialID error:(NSError **)error WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
++ (NSData *)importLocalAuthenticatorCredential:(NSData *)credentialBlob error:(NSError **)error WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+ (BOOL)isUserVerifyingPlatformAuthenticatorAvailable WK_API_AVAILABLE(macos(12.0), ios(15.0));
+ (NSData *)getClientDataJSONForAuthenticationType:(_WKWebAuthenticationType)type challenge:(NSData *)challenge origin:(NSString *)origin WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm (288815 => 288816)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm 2022-01-31 16:05:31 UTC (rev 288815)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm 2022-01-31 16:55:49 UTC (rev 288816)
@@ -289,7 +289,7 @@
+ (NSArray<NSDictionary *> *)getAllLocalAuthenticatorCredentials
{
#if ENABLE(WEB_AUTHN)
- return getAllLocalAuthenticatorCredentialsImpl(String(WebCore::LocalAuthenticatiorAccessGroup)).autorelease();
+ return getAllLocalAuthenticatorCredentialsImpl(String(WebCore::LocalAuthenticatorAccessGroup)).autorelease();
#else
return nullptr;
#endif
@@ -388,6 +388,192 @@
#endif
}
+#if ENABLE(WEB_AUTHN)
+static void createNSErrorFromWKErrorIfNecessary(NSError **error, WKErrorCode errorCode)
+{
+ if (error)
+ *error = [NSError errorWithDomain:WKErrorDomain code: errorCode userInfo:nil];
+}
+#endif // ENABLE(WEB_AUTHN)
+
++ (NSData *)importLocalAuthenticatorCredential:(NSData *)credentialBlob error:(NSError **)error
+{
+#if ENABLE(WEB_AUTHN)
+ auto credential = cbor::CBORReader::read(vectorFromNSData(credentialBlob));
+ if (!credential || !credential->isMap()) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorMalformedCredential);
+ return nullptr;
+ }
+
+ auto& credentialMap = credential->getMap();
+ auto it = credentialMap.find(cbor::CBORValue(WebCore::privateKeyKey));
+ if (it == credentialMap.end() || !it->second.isByteString()) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorMalformedCredential);
+ return nullptr;
+ }
+ auto privateKey = adoptNS([[NSData alloc] initWithBytes:it->second.getByteString().data() length:it->second.getByteString().size()]);
+
+ it = credentialMap.find(cbor::CBORValue(WebCore::keyTypeKey));
+ if (it == credentialMap.end() || !it->second.isInteger()) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorMalformedCredential);
+ return nullptr;
+ }
+ auto keyType = it->second.getInteger();
+
+ it = credentialMap.find(cbor::CBORValue(WebCore::keySizeKey));
+ if (it == credentialMap.end() || !it->second.isInteger()) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorMalformedCredential);
+ return nullptr;
+ }
+ auto keySize = it->second.getInteger();
+
+ it = credentialMap.find(cbor::CBORValue(WebCore::relyingPartyKey));
+ if (it == credentialMap.end() || !it->second.isString()) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorMalformedCredential);
+ return nullptr;
+ }
+ auto rp = it->second.getString();
+
+ it = credentialMap.find(cbor::CBORValue(WebCore::applicationTagKey));
+ if (it == credentialMap.end() || !it->second.isMap()) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorMalformedCredential);
+ return nullptr;
+ }
+ auto keyTag = cbor::CBORWriter::write(cbor::CBORValue(it->second.getMap()));
+
+ NSDictionary *options = @{
+ // Key type values are string values of numbers, stored as kCFNumberSInt64Type in attributes, but must be passed as string here
+ (id)kSecAttrKeyType: (id)[NSString stringWithFormat:@"%i", (int)keyType],
+ (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
+ (id)kSecAttrKeySizeInBits: @(keySize),
+ };
+ CFErrorRef errorRef = nullptr;
+ auto key = adoptCF(SecKeyCreateWithData(
+ bridge_cast(privateKey.get()),
+ bridge_cast(options),
+ &errorRef
+ ));
+ if (errorRef) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorMalformedCredential);
+ return nullptr;
+ }
+
+ auto publicKey = adoptCF(SecKeyCopyPublicKey(key.get()));
+ auto publicKeyDataRep = adoptCF(SecKeyCopyExternalRepresentation(publicKey.get(), &errorRef));
+ if (errorRef) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorMalformedCredential);
+ return nullptr;
+ }
+
+ NSData *nsPublicKeyData = (NSData *)publicKeyDataRep.get();
+ auto digest = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_1);
+ digest->addBytes(nsPublicKeyData.bytes, nsPublicKeyData.length);
+ auto credentialId = digest->computeHash();
+ auto nsCredentialId = adoptNS([[NSData alloc] initWithBytes:credentialId.data() length:credentialId.size()]);
+
+ auto query = adoptNS([[NSMutableDictionary alloc] initWithObjectsAndKeys:
+ (id)kSecClassKey, (id)kSecClass,
+ (id)kSecAttrKeyClassPrivate, (id)kSecAttrKeyClass,
+ (id)rp, (id)kSecAttrLabel,
+ nsCredentialId.get(), (id)kSecAttrApplicationLabel,
+ @YES, (id)kSecUseDataProtectionKeychain,
+ nil
+ ]);
+ updateQueryIfNecessary(query.get());
+
+ OSStatus status = SecItemCopyMatching(bridge_cast(query.get()), nullptr);
+ if (!status) {
+ // Credential with same id already exists, duplicate key.
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorDuplicateCredential);
+ return nullptr;
+ }
+
+ auto secAttrApplicationTag = adoptNS([[NSData alloc] initWithBytes:keyTag->data() length:keyTag->size()]);
+ NSDictionary *addQuery = @{
+ (id)kSecValueRef: (id)key.get(),
+ (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
+ (id)kSecAttrLabel: rp,
+ (id)kSecAttrApplicationTag: secAttrApplicationTag.get(),
+ (id)kSecUseDataProtectionKeychain: @YES,
+ (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock
+ };
+ status = SecItemAdd(bridge_cast(addQuery), NULL);
+ if (status) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorUnknown);
+ return nullptr;
+ }
+
+ return nsCredentialId.autorelease();
+#else
+ return nullptr;
+#endif // ENABLE(WEB_AUTHN)
+}
+
++ (NSData *)exportLocalAuthenticatorCredentialWithID:(NSData *)credentialID error:(NSError **)error
+{
+#if ENABLE(WEB_AUTHN)
+ auto query = adoptNS([[NSMutableDictionary alloc] initWithObjectsAndKeys:
+ (id)kSecClassKey, (id)kSecClass,
+ credentialID, (id)kSecAttrApplicationLabel,
+ (id)kSecAttrKeyClassPrivate, (id)kSecAttrKeyClass,
+ @YES, (id)kSecReturnRef,
+ @YES, (id)kSecUseDataProtectionKeychain,
+ nil
+ ]);
+ updateQueryIfNecessary(query.get());
+ CFTypeRef privateKeyRef = nullptr;
+ OSStatus status = SecItemCopyMatching(bridge_cast(query.get()), &privateKeyRef);
+ if (status && status != errSecItemNotFound) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorCredentialNotFound);
+ return nullptr;
+ }
+ auto privateKey = adoptCF(privateKeyRef);
+ CFErrorRef errorRef = nullptr;
+ auto privateKeyRep = adoptCF(SecKeyCopyExternalRepresentation((__bridge SecKeyRef)((id)privateKeyRef), &errorRef));
+ auto retainError = adoptCF(errorRef);
+ if (errorRef) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorCredentialNotFound);
+ return nullptr;
+ }
+
+ [query removeObjectForKey:(id)kSecReturnRef];
+ [query setObject: @YES forKey:(id)kSecReturnAttributes];
+ CFTypeRef attributesArrayRef = nullptr;
+ status = SecItemCopyMatching(bridge_cast(query.get()), &attributesArrayRef);
+ if (status && status != errSecItemNotFound) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorCredentialNotFound);
+ return nullptr;
+ }
+ NSDictionary *attributes = (__bridge NSDictionary *)attributesArrayRef;
+
+ int64_t keyType, keySize;
+ if (!CFNumberGetValue((__bridge CFNumberRef)attributes[bridge_id_cast(kSecAttrKeyType)], kCFNumberSInt64Type, &keyType)) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorMalformedCredential);
+ return nullptr;
+ }
+ if (!CFNumberGetValue((__bridge CFNumberRef)attributes[bridge_id_cast(kSecAttrKeySizeInBits)], kCFNumberSInt64Type, &keySize)) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorMalformedCredential);
+ return nullptr;
+ }
+
+ cbor::CBORValue::MapValue credentialMap;
+ credentialMap[cbor::CBORValue(WebCore::privateKeyKey)] = cbor::CBORValue(WebCore::toBufferSource(bridge_id_cast(privateKeyRep.get())));
+ credentialMap[cbor::CBORValue(WebCore::keyTypeKey)] = cbor::CBORValue(keyType);
+ credentialMap[cbor::CBORValue(WebCore::keySizeKey)] = cbor::CBORValue(keySize);
+ credentialMap[cbor::CBORValue(WebCore::relyingPartyKey)] = cbor::CBORValue(String(attributes[bridge_id_cast(kSecAttrLabel)]));
+ auto decodedResponse = cbor::CBORReader::read(vectorFromNSData(attributes[bridge_id_cast(kSecAttrApplicationTag)]));
+ if (!decodedResponse || !decodedResponse->isMap()) {
+ createNSErrorFromWKErrorIfNecessary(error, WKErrorMalformedCredential);
+ return nullptr;
+ }
+ credentialMap[cbor::CBORValue(WebCore::applicationTagKey)] = cbor::CBORValue(WTFMove(*decodedResponse));
+ auto serializedCredential = cbor::CBORWriter::write(cbor::CBORValue(WTFMove(credentialMap)));
+ return adoptNS([[NSData alloc] initWithBytes:serializedCredential.value().data() length:serializedCredential.value().size()]).autorelease();
+#else
+ return nullptr;
+#endif // ENABLE(WEB_AUTHN)
+}
+
- (void)cancel
{
#if ENABLE(WEB_AUTHN)
Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm (288815 => 288816)
--- trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm 2022-01-31 16:05:31 UTC (rev 288815)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm 2022-01-31 16:55:49 UTC (rev 288816)
@@ -177,7 +177,7 @@
auto query = adoptNS([[NSMutableDictionary alloc] init]);
[query setDictionary:@{
(id)kSecClass: (id)kSecClassKey,
- (id)kSecAttrAccessGroup: (id)String(LocalAuthenticatiorAccessGroup),
+ (id)kSecAttrAccessGroup: (id)String(LocalAuthenticatorAccessGroup),
(id)kSecUseDataProtectionKeychain: @YES
}];
updateQueryIfNecessary(query.get());
Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.mm (288815 => 288816)
--- trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.mm 2022-01-31 16:05:31 UTC (rev 288815)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.mm 2022-01-31 16:55:49 UTC (rev 288816)
@@ -170,7 +170,7 @@
RetainPtr privateKeyAttributes = @{
(id)kSecAttrAccessControl: (id)accessControlRef,
(id)kSecAttrIsPermanent: @YES,
- (id)kSecAttrAccessGroup: (id)String(LocalAuthenticatiorAccessGroup),
+ (id)kSecAttrAccessGroup: (id)String(LocalAuthenticatorAccessGroup),
(id)kSecAttrLabel: secAttrLabel,
(id)kSecAttrApplicationTag: secAttrApplicationTag,
};
Modified: trunk/Tools/ChangeLog (288815 => 288816)
--- trunk/Tools/ChangeLog 2022-01-31 16:05:31 UTC (rev 288815)
+++ trunk/Tools/ChangeLog 2022-01-31 16:55:49 UTC (rev 288816)
@@ -1,3 +1,16 @@
+2022-01-31 J Pascoe <[email protected]>
+
+ [WebAuthn] Provide SPI to export/import local credentials
+ https://bugs.webkit.org/show_bug.cgi?id=234112
+ rdar://84822000
+
+ Reviewed by Brent Fulgham.
+
+ Add tests for SPI to import / export local webauthn credentials.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:
+ (TestWebKitAPI::TEST): New tests for import/export
+
2022-01-31 Don Olmstead <[email protected]>
Support additional WPEToolingBackend types
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm (288815 => 288816)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm 2022-01-31 16:05:31 UTC (rev 288815)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm 2022-01-31 16:55:49 UTC (rev 288816)
@@ -2228,6 +2228,61 @@
cleanUpKeychain("example.com");
}
+TEST(WebAuthenticationPanel, ExportImportCredential)
+{
+ reset();
+
+ addKeyToKeychain(testES256PrivateKeyBase64, "example.com", testUserEntityBundleBase64);
+
+ auto *credentials = [_WKWebAuthenticationPanel getAllLocalAuthenticatorCredentialsWithAccessGroup:@"com.apple.TestWebKitAPI"];
+ EXPECT_NOT_NULL(credentials);
+ EXPECT_EQ([credentials count], 1lu);
+
+ EXPECT_NOT_NULL([credentials firstObject]);
+ NSError *error = nil;
+ auto exportedKey = [_WKWebAuthenticationPanel exportLocalAuthenticatorCredentialWithID:[credentials firstObject][_WKLocalAuthenticatorCredentialIDKey] error:&error];
+
+ cleanUpKeychain("example.com");
+
+ auto credentialId = [_WKWebAuthenticationPanel importLocalAuthenticatorCredential:exportedKey error:&error];
+ EXPECT_WK_STREQ([[credentials firstObject][_WKLocalAuthenticatorCredentialIDKey] base64EncodedStringWithOptions:0], [credentialId base64EncodedStringWithOptions:0]);
+
+ cleanUpKeychain("example.com");
+}
+
+TEST(WebAuthenticationPanel, ExportImportDuplicateCredential)
+{
+ reset();
+ cleanUpKeychain("");
+
+ addKeyToKeychain(testES256PrivateKeyBase64, "example.com", testUserEntityBundleBase64);
+
+ auto *credentials = [_WKWebAuthenticationPanel getAllLocalAuthenticatorCredentialsWithAccessGroup:@"com.apple.TestWebKitAPI"];
+ EXPECT_NOT_NULL(credentials);
+ EXPECT_EQ([credentials count], 1lu);
+
+ EXPECT_NOT_NULL([credentials firstObject]);
+ NSError *error = nil;
+ auto exportedKey = [_WKWebAuthenticationPanel exportLocalAuthenticatorCredentialWithID:[credentials firstObject][_WKLocalAuthenticatorCredentialIDKey] error:&error];
+
+ auto credentialId = [_WKWebAuthenticationPanel importLocalAuthenticatorCredential:exportedKey error:&error];
+ EXPECT_EQ(credentialId, nil);
+ EXPECT_EQ(error.code, WKErrorDuplicateCredential);
+
+ cleanUpKeychain("example.com");
+}
+
+TEST(WebAuthenticationPanel, ImportMalformedCredential)
+{
+ reset();
+
+ NSError *error = nil;
+ auto credentialId = [_WKWebAuthenticationPanel importLocalAuthenticatorCredential:adoptNS([[NSData alloc] initWithBase64EncodedString:testUserEntityBundleBase64 options:0]).get() error:&error];
+
+ EXPECT_EQ(error.code, WKErrorMalformedCredential);
+ EXPECT_EQ(credentialId, nil);
+}
+
TEST(WebAuthenticationPanel, DeleteOneCredential)
{
reset();