1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/StaticPtr.h"
8 #include "mozilla/ipc/BackgroundParent.h"
9 #include "mozilla/jni/GeckoBundleUtils.h"
11 #include "AndroidWebAuthnService.h"
12 #include "JavaBuiltins.h"
13 #include "JavaExceptions.h"
14 #include "WebAuthnPromiseHolder.h"
15 #include "WebAuthnEnumStrings.h"
16 #include "WebAuthnResult.h"
17 #include "mozilla/StaticPrefs_security.h"
18 #include "mozilla/java/WebAuthnTokenManagerWrappers.h"
19 #include "mozilla/jni/Conversions.h"
24 dom::AndroidWebAuthnError
Java2Native(mozilla::jni::Object::Param aData
,
26 MOZ_ASSERT(aData
.IsInstanceOf
<jni::Throwable
>());
27 java::sdk::Throwable::LocalRef
throwable(aData
);
28 return dom::AndroidWebAuthnError(throwable
->GetMessage()->ToString());
34 NS_IMPL_ISUPPORTS(AndroidWebAuthnService
, nsIWebAuthnService
)
37 AndroidWebAuthnService::GetIsUVPAA(bool* aAvailable
) {
38 return NS_ERROR_NOT_IMPLEMENTED
;
42 AndroidWebAuthnService::MakeCredential(uint64_t aTransactionId
,
43 uint64_t aBrowsingContextId
,
44 nsIWebAuthnRegisterArgs
* aArgs
,
45 nsIWebAuthnRegisterPromise
* aPromise
) {
48 GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
49 "java::WebAuthnTokenManager::WebAuthnMakeCredential",
50 [aArgs
= RefPtr
{aArgs
}, aPromise
= RefPtr
{aPromise
}]() {
51 AssertIsOnMainThread();
53 GECKOBUNDLE_START(credentialBundle
);
54 GECKOBUNDLE_PUT(credentialBundle
, "isWebAuthn",
55 java::sdk::Integer::ValueOf(1));
58 Unused
<< aArgs
->GetRpId(rpId
);
59 GECKOBUNDLE_PUT(credentialBundle
, "rpId", jni::StringParam(rpId
));
62 Unused
<< aArgs
->GetRpName(rpName
);
63 GECKOBUNDLE_PUT(credentialBundle
, "rpName", jni::StringParam(rpName
));
66 Unused
<< aArgs
->GetUserName(userName
);
67 GECKOBUNDLE_PUT(credentialBundle
, "userName",
68 jni::StringParam(userName
));
70 nsString userDisplayName
;
71 Unused
<< aArgs
->GetUserDisplayName(userDisplayName
);
72 GECKOBUNDLE_PUT(credentialBundle
, "userDisplayName",
73 jni::StringParam(userDisplayName
));
76 Unused
<< aArgs
->GetOrigin(origin
);
77 GECKOBUNDLE_PUT(credentialBundle
, "origin", jni::StringParam(origin
));
80 Unused
<< aArgs
->GetTimeoutMS(&timeout
);
81 GECKOBUNDLE_PUT(credentialBundle
, "timeoutMS",
82 java::sdk::Double::New(timeout
));
83 GECKOBUNDLE_FINISH(credentialBundle
);
85 nsTArray
<uint8_t> userId
;
86 Unused
<< aArgs
->GetUserId(userId
);
87 jni::ByteBuffer::LocalRef uid
= jni::ByteBuffer::New(
88 const_cast<void*>(static_cast<const void*>(userId
.Elements())),
91 nsTArray
<uint8_t> challBuf
;
92 Unused
<< aArgs
->GetChallenge(challBuf
);
93 jni::ByteBuffer::LocalRef challenge
= jni::ByteBuffer::New(
94 const_cast<void*>(static_cast<const void*>(challBuf
.Elements())),
97 nsTArray
<nsTArray
<uint8_t>> excludeList
;
98 Unused
<< aArgs
->GetExcludeList(excludeList
);
99 jni::ObjectArray::LocalRef idList
=
100 jni::ObjectArray::New(excludeList
.Length());
102 for (const nsTArray
<uint8_t>& credId
: excludeList
) {
103 jni::ByteBuffer::LocalRef id
= jni::ByteBuffer::New(
104 const_cast<void*>(static_cast<const void*>(credId
.Elements())),
107 idList
->SetElement(ix
, id
);
112 nsTArray
<uint8_t> transportBuf
;
113 Unused
<< aArgs
->GetExcludeListTransports(transportBuf
);
114 jni::ByteBuffer::LocalRef transportList
= jni::ByteBuffer::New(
116 static_cast<const void*>(transportBuf
.Elements())),
117 transportBuf
.Length());
119 GECKOBUNDLE_START(authSelBundle
);
120 // Add UI support to consent to attestation, bug 1550164
121 GECKOBUNDLE_PUT(authSelBundle
, "attestationPreference",
122 jni::StringParam(u
"none"_ns
));
124 nsString residentKey
;
125 Unused
<< aArgs
->GetResidentKey(residentKey
);
128 bool requestedCredProps
;
129 Unused
<< aArgs
->GetCredProps(&requestedCredProps
);
131 // Unfortunately, GMS's FIDO2 API has no option for Passkey. If using
132 // residentKey, credential will be synced with Passkey via Google
133 // account or credential provider service. So this is experimental.
134 Maybe
<bool> credPropsResponse
;
135 if (requestedCredProps
&&
137 security_webauthn_webauthn_enable_android_fido2_residentkey()) {
138 GECKOBUNDLE_PUT(authSelBundle
, "residentKey",
139 jni::StringParam(residentKey
));
140 bool residentKeyRequired
= residentKey
.EqualsLiteral(
141 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED
);
142 credPropsResponse
= Some(residentKeyRequired
);
145 nsString userVerification
;
146 Unused
<< aArgs
->GetUserVerification(userVerification
);
147 if (userVerification
.EqualsLiteral(
148 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED
)) {
149 GECKOBUNDLE_PUT(authSelBundle
, "requireUserVerification",
150 java::sdk::Integer::ValueOf(1));
153 nsString authenticatorAttachment
;
155 aArgs
->GetAuthenticatorAttachment(authenticatorAttachment
);
156 if (rv
!= NS_ERROR_NOT_AVAILABLE
) {
158 aPromise
->Reject(rv
);
161 if (authenticatorAttachment
.EqualsLiteral(
162 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM
)) {
163 GECKOBUNDLE_PUT(authSelBundle
, "requirePlatformAttachment",
164 java::sdk::Integer::ValueOf(1));
166 authenticatorAttachment
.EqualsLiteral(
167 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM
)) {
168 GECKOBUNDLE_PUT(authSelBundle
, "requireCrossPlatformAttachment",
169 java::sdk::Integer::ValueOf(1));
172 GECKOBUNDLE_FINISH(authSelBundle
);
174 GECKOBUNDLE_START(extensionsBundle
);
175 GECKOBUNDLE_FINISH(extensionsBundle
);
177 auto result
= java::WebAuthnTokenManager::WebAuthnMakeCredential(
178 credentialBundle
, uid
, challenge
, idList
, transportList
,
179 authSelBundle
, extensionsBundle
);
181 auto geckoResult
= java::GeckoResult::LocalRef(std::move(result
));
183 MozPromise
<RefPtr
<WebAuthnRegisterResult
>, AndroidWebAuthnError
,
184 true>::FromGeckoResult(geckoResult
)
186 GetCurrentSerialEventTarget(), __func__
,
187 [aPromise
, credPropsResponse
= std::move(credPropsResponse
)](
188 RefPtr
<WebAuthnRegisterResult
>&& aValue
) {
189 if (credPropsResponse
.isSome()) {
190 Unused
<< aValue
->SetCredPropsRk(credPropsResponse
.ref());
192 aPromise
->Resolve(aValue
);
194 [aPromise
](AndroidWebAuthnError
&& aValue
) {
195 aPromise
->Reject(aValue
.GetError());
203 AndroidWebAuthnService::GetAssertion(uint64_t aTransactionId
,
204 uint64_t aBrowsingContextId
,
205 nsIWebAuthnSignArgs
* aArgs
,
206 nsIWebAuthnSignPromise
* aPromise
) {
209 bool conditionallyMediated
;
210 Unused
<< aArgs
->GetConditionallyMediated(&conditionallyMediated
);
211 if (conditionallyMediated
) {
212 aPromise
->Reject(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
216 GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
217 "java::WebAuthnTokenManager::WebAuthnGetAssertion",
218 [self
= RefPtr
{this}, aArgs
= RefPtr
{aArgs
},
219 aPromise
= RefPtr
{aPromise
}]() {
220 AssertIsOnMainThread();
222 nsTArray
<uint8_t> challBuf
;
223 Unused
<< aArgs
->GetChallenge(challBuf
);
224 jni::ByteBuffer::LocalRef challenge
= jni::ByteBuffer::New(
225 const_cast<void*>(static_cast<const void*>(challBuf
.Elements())),
228 nsTArray
<nsTArray
<uint8_t>> allowList
;
229 Unused
<< aArgs
->GetAllowList(allowList
);
230 jni::ObjectArray::LocalRef idList
=
231 jni::ObjectArray::New(allowList
.Length());
233 for (const nsTArray
<uint8_t>& credId
: allowList
) {
234 jni::ByteBuffer::LocalRef id
= jni::ByteBuffer::New(
235 const_cast<void*>(static_cast<const void*>(credId
.Elements())),
238 idList
->SetElement(ix
, id
);
243 nsTArray
<uint8_t> transportBuf
;
244 Unused
<< aArgs
->GetAllowListTransports(transportBuf
);
245 jni::ByteBuffer::LocalRef transportList
= jni::ByteBuffer::New(
247 static_cast<const void*>(transportBuf
.Elements())),
248 transportBuf
.Length());
250 GECKOBUNDLE_START(assertionBundle
);
252 GECKOBUNDLE_PUT(assertionBundle
, "isWebAuthn",
253 java::sdk::Integer::ValueOf(1));
256 Unused
<< aArgs
->GetRpId(rpId
);
257 GECKOBUNDLE_PUT(assertionBundle
, "rpId", jni::StringParam(rpId
));
260 Unused
<< aArgs
->GetOrigin(origin
);
261 GECKOBUNDLE_PUT(assertionBundle
, "origin", jni::StringParam(origin
));
264 Unused
<< aArgs
->GetTimeoutMS(&timeout
);
265 GECKOBUNDLE_PUT(assertionBundle
, "timeoutMS",
266 java::sdk::Double::New(timeout
));
268 // User Verification Requirement is not currently used in the
271 GECKOBUNDLE_FINISH(assertionBundle
);
273 GECKOBUNDLE_START(extensionsBundle
);
276 nsresult rv
= aArgs
->GetAppId(appId
);
277 if (rv
!= NS_ERROR_NOT_AVAILABLE
) {
279 aPromise
->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR
);
282 GECKOBUNDLE_PUT(extensionsBundle
, "fidoAppId",
283 jni::StringParam(appId
));
286 GECKOBUNDLE_FINISH(extensionsBundle
);
288 auto result
= java::WebAuthnTokenManager::WebAuthnGetAssertion(
289 challenge
, idList
, transportList
, assertionBundle
,
291 auto geckoResult
= java::GeckoResult::LocalRef(std::move(result
));
292 MozPromise
<RefPtr
<WebAuthnSignResult
>, AndroidWebAuthnError
,
293 true>::FromGeckoResult(geckoResult
)
295 GetCurrentSerialEventTarget(), __func__
,
296 [aPromise
](RefPtr
<WebAuthnSignResult
>&& aValue
) {
297 aPromise
->Resolve(aValue
);
299 [aPromise
](AndroidWebAuthnError
&& aValue
) {
300 aPromise
->Reject(aValue
.GetError());
308 AndroidWebAuthnService::Reset() {
309 mRegisterCredPropsRk
.reset();
315 AndroidWebAuthnService::Cancel(uint64_t aTransactionId
) {
316 return NS_ERROR_NOT_IMPLEMENTED
;
320 AndroidWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId
,
321 const nsAString
& aOrigin
,
323 // Signal that there is no pending conditional get request, so the caller
324 // will not attempt to call GetAutoFillEntries, SelectAutoFillEntry, or
325 // ResumeConditionalGet (as these are not implemented).
331 AndroidWebAuthnService::GetAutoFillEntries(
332 uint64_t aTransactionId
, nsTArray
<RefPtr
<nsIWebAuthnAutoFillEntry
>>& aRv
) {
333 return NS_ERROR_NOT_IMPLEMENTED
;
337 AndroidWebAuthnService::SelectAutoFillEntry(
338 uint64_t aTransactionId
, const nsTArray
<uint8_t>& aCredentialId
) {
339 return NS_ERROR_NOT_IMPLEMENTED
;
343 AndroidWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId
) {
344 return NS_ERROR_NOT_IMPLEMENTED
;
348 AndroidWebAuthnService::PinCallback(uint64_t aTransactionId
,
349 const nsACString
& aPin
) {
350 return NS_ERROR_NOT_IMPLEMENTED
;
354 AndroidWebAuthnService::SetHasAttestationConsent(uint64_t aTransactionId
,
356 return NS_ERROR_NOT_IMPLEMENTED
;
360 AndroidWebAuthnService::SelectionCallback(uint64_t aTransactionId
,
362 return NS_ERROR_NOT_IMPLEMENTED
;
366 AndroidWebAuthnService::AddVirtualAuthenticator(
367 const nsACString
& protocol
, const nsACString
& transport
,
368 bool hasResidentKey
, bool hasUserVerification
, bool isUserConsenting
,
369 bool isUserVerified
, uint64_t* _retval
) {
370 return NS_ERROR_NOT_IMPLEMENTED
;
374 AndroidWebAuthnService::RemoveVirtualAuthenticator(uint64_t authenticatorId
) {
375 return NS_ERROR_NOT_IMPLEMENTED
;
379 AndroidWebAuthnService::AddCredential(uint64_t authenticatorId
,
380 const nsACString
& credentialId
,
381 bool isResidentCredential
,
382 const nsACString
& rpId
,
383 const nsACString
& privateKey
,
384 const nsACString
& userHandle
,
385 uint32_t signCount
) {
386 return NS_ERROR_NOT_IMPLEMENTED
;
390 AndroidWebAuthnService::GetCredentials(
391 uint64_t authenticatorId
,
392 nsTArray
<RefPtr
<nsICredentialParameters
>>& _retval
) {
393 return NS_ERROR_NOT_IMPLEMENTED
;
397 AndroidWebAuthnService::RemoveCredential(uint64_t authenticatorId
,
398 const nsACString
& credentialId
) {
399 return NS_ERROR_NOT_IMPLEMENTED
;
403 AndroidWebAuthnService::RemoveAllCredentials(uint64_t authenticatorId
) {
404 return NS_ERROR_NOT_IMPLEMENTED
;
408 AndroidWebAuthnService::SetUserVerified(uint64_t authenticatorId
,
409 bool isUserVerified
) {
410 return NS_ERROR_NOT_IMPLEMENTED
;
414 AndroidWebAuthnService::Listen() { return NS_ERROR_NOT_IMPLEMENTED
; }
417 AndroidWebAuthnService::RunCommand(const nsACString
& cmd
) {
418 return NS_ERROR_NOT_IMPLEMENTED
;
422 } // namespace mozilla