Bug 1874684 - Part 6: Limit day length calculations to safe integers. r=mgaudet
[gecko.git] / dom / webauthn / AndroidWebAuthnService.cpp
bloba4fa230455f0860da64dedd02626e507fe9ec4a1
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"
21 namespace mozilla {
22 namespace jni {
23 template <>
24 dom::AndroidWebAuthnError Java2Native(mozilla::jni::Object::Param aData,
25 JNIEnv* aEnv) {
26 MOZ_ASSERT(aData.IsInstanceOf<jni::Throwable>());
27 java::sdk::Throwable::LocalRef throwable(aData);
28 return dom::AndroidWebAuthnError(throwable->GetMessage()->ToString());
30 } // namespace jni
32 namespace dom {
34 NS_IMPL_ISUPPORTS(AndroidWebAuthnService, nsIWebAuthnService)
36 NS_IMETHODIMP
37 AndroidWebAuthnService::GetIsUVPAA(bool* aAvailable) {
38 return NS_ERROR_NOT_IMPLEMENTED;
41 NS_IMETHODIMP
42 AndroidWebAuthnService::MakeCredential(uint64_t aTransactionId,
43 uint64_t aBrowsingContextId,
44 nsIWebAuthnRegisterArgs* aArgs,
45 nsIWebAuthnRegisterPromise* aPromise) {
46 Reset();
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));
57 nsString rpId;
58 Unused << aArgs->GetRpId(rpId);
59 GECKOBUNDLE_PUT(credentialBundle, "rpId", jni::StringParam(rpId));
61 nsString rpName;
62 Unused << aArgs->GetRpName(rpName);
63 GECKOBUNDLE_PUT(credentialBundle, "rpName", jni::StringParam(rpName));
65 nsString userName;
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));
75 nsString origin;
76 Unused << aArgs->GetOrigin(origin);
77 GECKOBUNDLE_PUT(credentialBundle, "origin", jni::StringParam(origin));
79 uint32_t timeout;
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())),
89 userId.Length());
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())),
95 challBuf.Length());
97 nsTArray<nsTArray<uint8_t>> excludeList;
98 Unused << aArgs->GetExcludeList(excludeList);
99 jni::ObjectArray::LocalRef idList =
100 jni::ObjectArray::New(excludeList.Length());
101 int ix = 0;
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())),
105 credId.Length());
107 idList->SetElement(ix, id);
109 ix += 1;
112 nsTArray<uint8_t> transportBuf;
113 Unused << aArgs->GetExcludeListTransports(transportBuf);
114 jni::ByteBuffer::LocalRef transportList = jni::ByteBuffer::New(
115 const_cast<void*>(
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);
127 // Get extensions
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 &&
136 StaticPrefs::
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;
154 nsresult rv =
155 aArgs->GetAuthenticatorAttachment(authenticatorAttachment);
156 if (rv != NS_ERROR_NOT_AVAILABLE) {
157 if (NS_FAILED(rv)) {
158 aPromise->Reject(rv);
159 return;
161 if (authenticatorAttachment.EqualsLiteral(
162 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM)) {
163 GECKOBUNDLE_PUT(authSelBundle, "requirePlatformAttachment",
164 java::sdk::Integer::ValueOf(1));
165 } else if (
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)
185 ->Then(
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());
197 }));
199 return NS_OK;
202 NS_IMETHODIMP
203 AndroidWebAuthnService::GetAssertion(uint64_t aTransactionId,
204 uint64_t aBrowsingContextId,
205 nsIWebAuthnSignArgs* aArgs,
206 nsIWebAuthnSignPromise* aPromise) {
207 Reset();
209 bool conditionallyMediated;
210 Unused << aArgs->GetConditionallyMediated(&conditionallyMediated);
211 if (conditionallyMediated) {
212 aPromise->Reject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
213 return NS_OK;
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())),
226 challBuf.Length());
228 nsTArray<nsTArray<uint8_t>> allowList;
229 Unused << aArgs->GetAllowList(allowList);
230 jni::ObjectArray::LocalRef idList =
231 jni::ObjectArray::New(allowList.Length());
232 int ix = 0;
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())),
236 credId.Length());
238 idList->SetElement(ix, id);
240 ix += 1;
243 nsTArray<uint8_t> transportBuf;
244 Unused << aArgs->GetAllowListTransports(transportBuf);
245 jni::ByteBuffer::LocalRef transportList = jni::ByteBuffer::New(
246 const_cast<void*>(
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));
255 nsString rpId;
256 Unused << aArgs->GetRpId(rpId);
257 GECKOBUNDLE_PUT(assertionBundle, "rpId", jni::StringParam(rpId));
259 nsString origin;
260 Unused << aArgs->GetOrigin(origin);
261 GECKOBUNDLE_PUT(assertionBundle, "origin", jni::StringParam(origin));
263 uint32_t timeout;
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
269 // Android FIDO API.
271 GECKOBUNDLE_FINISH(assertionBundle);
273 GECKOBUNDLE_START(extensionsBundle);
275 nsString appId;
276 nsresult rv = aArgs->GetAppId(appId);
277 if (rv != NS_ERROR_NOT_AVAILABLE) {
278 if (NS_FAILED(rv)) {
279 aPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
280 return;
282 GECKOBUNDLE_PUT(extensionsBundle, "fidoAppId",
283 jni::StringParam(appId));
286 GECKOBUNDLE_FINISH(extensionsBundle);
288 auto result = java::WebAuthnTokenManager::WebAuthnGetAssertion(
289 challenge, idList, transportList, assertionBundle,
290 extensionsBundle);
291 auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
292 MozPromise<RefPtr<WebAuthnSignResult>, AndroidWebAuthnError,
293 true>::FromGeckoResult(geckoResult)
294 ->Then(
295 GetCurrentSerialEventTarget(), __func__,
296 [aPromise](RefPtr<WebAuthnSignResult>&& aValue) {
297 aPromise->Resolve(aValue);
299 [aPromise](AndroidWebAuthnError&& aValue) {
300 aPromise->Reject(aValue.GetError());
302 }));
304 return NS_OK;
307 NS_IMETHODIMP
308 AndroidWebAuthnService::Reset() {
309 mRegisterCredPropsRk.reset();
311 return NS_OK;
314 NS_IMETHODIMP
315 AndroidWebAuthnService::Cancel(uint64_t aTransactionId) {
316 return NS_ERROR_NOT_IMPLEMENTED;
319 NS_IMETHODIMP
320 AndroidWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId,
321 const nsAString& aOrigin,
322 uint64_t* aRv) {
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).
326 *aRv = 0;
327 return NS_OK;
330 NS_IMETHODIMP
331 AndroidWebAuthnService::GetAutoFillEntries(
332 uint64_t aTransactionId, nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>& aRv) {
333 return NS_ERROR_NOT_IMPLEMENTED;
336 NS_IMETHODIMP
337 AndroidWebAuthnService::SelectAutoFillEntry(
338 uint64_t aTransactionId, const nsTArray<uint8_t>& aCredentialId) {
339 return NS_ERROR_NOT_IMPLEMENTED;
342 NS_IMETHODIMP
343 AndroidWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) {
344 return NS_ERROR_NOT_IMPLEMENTED;
347 NS_IMETHODIMP
348 AndroidWebAuthnService::PinCallback(uint64_t aTransactionId,
349 const nsACString& aPin) {
350 return NS_ERROR_NOT_IMPLEMENTED;
353 NS_IMETHODIMP
354 AndroidWebAuthnService::SetHasAttestationConsent(uint64_t aTransactionId,
355 bool aHasConsent) {
356 return NS_ERROR_NOT_IMPLEMENTED;
359 NS_IMETHODIMP
360 AndroidWebAuthnService::SelectionCallback(uint64_t aTransactionId,
361 uint64_t aIndex) {
362 return NS_ERROR_NOT_IMPLEMENTED;
365 NS_IMETHODIMP
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;
373 NS_IMETHODIMP
374 AndroidWebAuthnService::RemoveVirtualAuthenticator(uint64_t authenticatorId) {
375 return NS_ERROR_NOT_IMPLEMENTED;
378 NS_IMETHODIMP
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;
389 NS_IMETHODIMP
390 AndroidWebAuthnService::GetCredentials(
391 uint64_t authenticatorId,
392 nsTArray<RefPtr<nsICredentialParameters>>& _retval) {
393 return NS_ERROR_NOT_IMPLEMENTED;
396 NS_IMETHODIMP
397 AndroidWebAuthnService::RemoveCredential(uint64_t authenticatorId,
398 const nsACString& credentialId) {
399 return NS_ERROR_NOT_IMPLEMENTED;
402 NS_IMETHODIMP
403 AndroidWebAuthnService::RemoveAllCredentials(uint64_t authenticatorId) {
404 return NS_ERROR_NOT_IMPLEMENTED;
407 NS_IMETHODIMP
408 AndroidWebAuthnService::SetUserVerified(uint64_t authenticatorId,
409 bool isUserVerified) {
410 return NS_ERROR_NOT_IMPLEMENTED;
413 NS_IMETHODIMP
414 AndroidWebAuthnService::Listen() { return NS_ERROR_NOT_IMPLEMENTED; }
416 NS_IMETHODIMP
417 AndroidWebAuthnService::RunCommand(const nsACString& cmd) {
418 return NS_ERROR_NOT_IMPLEMENTED;
421 } // namespace dom
422 } // namespace mozilla