AndroidWebAuthnService.cpp (16331B)
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/. */ 6 7 #include "AndroidWebAuthnService.h" 8 9 #include "JavaBuiltins.h" 10 #include "JavaExceptions.h" 11 #include "WebAuthnEnumStrings.h" 12 #include "WebAuthnPromiseHolder.h" 13 #include "WebAuthnResult.h" 14 #include "mozilla/StaticPrefs_security.h" 15 #include "mozilla/StaticPtr.h" 16 #include "mozilla/ipc/BackgroundParent.h" 17 #include "mozilla/java/WebAuthnTokenManagerWrappers.h" 18 #include "mozilla/jni/Conversions.h" 19 #include "mozilla/jni/GeckoBundleUtils.h" 20 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()); 29 } 30 } // namespace jni 31 32 namespace dom { 33 34 NS_IMPL_ISUPPORTS(AndroidWebAuthnService, nsIWebAuthnService) 35 36 NS_IMETHODIMP 37 AndroidWebAuthnService::GetIsUVPAA(bool* aAvailable) { 38 return NS_ERROR_NOT_IMPLEMENTED; 39 } 40 41 NS_IMETHODIMP 42 AndroidWebAuthnService::MakeCredential(uint64_t aTransactionId, 43 uint64_t aBrowsingContextId, 44 nsIWebAuthnRegisterArgs* aArgs, 45 nsIWebAuthnRegisterPromise* aPromise) { 46 Reset(); 47 48 GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction( 49 "java::WebAuthnTokenManager::WebAuthnMakeCredential", 50 [aArgs = RefPtr{aArgs}, aPromise = RefPtr{aPromise}]() { 51 AssertIsOnMainThread(); 52 53 GECKOBUNDLE_START(credentialBundle); 54 GECKOBUNDLE_PUT(credentialBundle, "isWebAuthn", 55 java::sdk::Integer::ValueOf(1)); 56 57 { 58 GECKOBUNDLE_START(rpBundle); 59 60 nsString rpId; 61 (void)aArgs->GetRpId(rpId); 62 GECKOBUNDLE_PUT(rpBundle, "id", jni::StringParam(rpId)); 63 64 nsString rpName; 65 (void)aArgs->GetRpName(rpName); 66 GECKOBUNDLE_PUT(rpBundle, "name", jni::StringParam(rpName)); 67 68 GECKOBUNDLE_FINISH(rpBundle); 69 GECKOBUNDLE_PUT(credentialBundle, "rp", rpBundle); 70 } 71 72 { 73 GECKOBUNDLE_START(userBundle); 74 75 nsString userName; 76 (void)aArgs->GetUserName(userName); 77 GECKOBUNDLE_PUT(userBundle, "name", jni::StringParam(userName)); 78 79 nsString userDisplayName; 80 (void)aArgs->GetUserDisplayName(userDisplayName); 81 GECKOBUNDLE_PUT(userBundle, "displayName", 82 jni::StringParam(userDisplayName)); 83 84 GECKOBUNDLE_FINISH(userBundle); 85 GECKOBUNDLE_PUT(credentialBundle, "user", userBundle); 86 } 87 88 nsString origin; 89 (void)aArgs->GetOrigin(origin); 90 GECKOBUNDLE_PUT(credentialBundle, "origin", jni::StringParam(origin)); 91 92 uint32_t timeout; 93 (void)aArgs->GetTimeoutMS(&timeout); 94 GECKOBUNDLE_PUT(credentialBundle, "timeout", 95 java::sdk::Double::New(timeout)); 96 97 // Add UI support to consent to attestation, bug 1550164 98 GECKOBUNDLE_PUT(credentialBundle, "attestation", 99 jni::StringParam(u"none"_ns)); 100 101 GECKOBUNDLE_FINISH(credentialBundle); 102 103 nsTArray<uint8_t> userId; 104 (void)aArgs->GetUserId(userId); 105 jni::ByteBuffer::LocalRef uid = jni::ByteBuffer::New( 106 const_cast<void*>(static_cast<const void*>(userId.Elements())), 107 userId.Length()); 108 109 nsTArray<uint8_t> challBuf; 110 (void)aArgs->GetChallenge(challBuf); 111 jni::ByteBuffer::LocalRef challenge = jni::ByteBuffer::New( 112 const_cast<void*>(static_cast<const void*>(challBuf.Elements())), 113 challBuf.Length()); 114 115 nsTArray<nsTArray<uint8_t>> excludeList; 116 (void)aArgs->GetExcludeList(excludeList); 117 jni::ObjectArray::LocalRef idList = 118 jni::ObjectArray::New(excludeList.Length()); 119 int ix = 0; 120 for (const nsTArray<uint8_t>& credId : excludeList) { 121 jni::ByteBuffer::LocalRef id = jni::ByteBuffer::New( 122 const_cast<void*>(static_cast<const void*>(credId.Elements())), 123 credId.Length()); 124 125 idList->SetElement(ix, id); 126 127 ix += 1; 128 } 129 130 nsTArray<uint8_t> transportBuf; 131 (void)aArgs->GetExcludeListTransports(transportBuf); 132 jni::ByteBuffer::LocalRef transportList = jni::ByteBuffer::New( 133 const_cast<void*>( 134 static_cast<const void*>(transportBuf.Elements())), 135 transportBuf.Length()); 136 137 nsTArray<uint8_t> clientDataHash; 138 (void)aArgs->GetClientDataHash(clientDataHash); 139 jni::ByteBuffer::LocalRef hash = jni::ByteBuffer::New( 140 const_cast<void*>( 141 static_cast<const void*>(clientDataHash.Elements())), 142 clientDataHash.Length()); 143 144 nsTArray<int32_t> coseAlgs; 145 (void)aArgs->GetCoseAlgs(coseAlgs); 146 jni::IntArray::LocalRef algs = 147 jni::IntArray::New(coseAlgs.Elements(), coseAlgs.Length()); 148 149 GECKOBUNDLE_START(authSelBundle); 150 151 nsString residentKey; 152 (void)aArgs->GetResidentKey(residentKey); 153 154 // Get extensions 155 bool requestedCredProps; 156 (void)aArgs->GetCredProps(&requestedCredProps); 157 158 // Unfortunately, GMS's FIDO2 API has no option for Passkey. If using 159 // residentKey, credential will be synced with Passkey via Google 160 // account or credential provider service. So this is experimental. 161 if (requestedCredProps && 162 StaticPrefs:: 163 security_webauthn_webauthn_enable_android_fido2_residentkey()) { 164 GECKOBUNDLE_PUT(authSelBundle, "residentKey", 165 jni::StringParam(residentKey)); 166 } 167 168 nsString userVerification; 169 (void)aArgs->GetUserVerification(userVerification); 170 if (userVerification.EqualsLiteral( 171 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED) || 172 userVerification.EqualsLiteral( 173 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED) || 174 userVerification.EqualsLiteral( 175 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED)) { 176 GECKOBUNDLE_PUT(authSelBundle, "userVerification", 177 jni::StringParam(userVerification)); 178 } 179 180 nsString authenticatorAttachment; 181 nsresult rv = 182 aArgs->GetAuthenticatorAttachment(authenticatorAttachment); 183 if (rv != NS_ERROR_NOT_AVAILABLE) { 184 if (NS_FAILED(rv)) { 185 aPromise->Reject(rv); 186 return; 187 } 188 if (authenticatorAttachment.EqualsLiteral( 189 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM) || 190 authenticatorAttachment.EqualsLiteral( 191 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM)) { 192 GECKOBUNDLE_PUT(authSelBundle, "authenticatorAttachment", 193 jni::StringParam(authenticatorAttachment)); 194 } 195 } 196 GECKOBUNDLE_FINISH(authSelBundle); 197 198 GECKOBUNDLE_START(extensionsBundle); 199 GECKOBUNDLE_PUT(extensionsBundle, "credProps", 200 requestedCredProps ? java::sdk::Boolean::TRUE() 201 : java::sdk::Boolean::FALSE()); 202 GECKOBUNDLE_FINISH(extensionsBundle); 203 204 auto result = java::WebAuthnTokenManager::WebAuthnMakeCredential( 205 credentialBundle, uid, challenge, idList, transportList, 206 authSelBundle, extensionsBundle, algs, hash); 207 208 auto geckoResult = java::GeckoResult::LocalRef(std::move(result)); 209 210 MozPromise<RefPtr<WebAuthnRegisterResult>, AndroidWebAuthnError, 211 true>::FromGeckoResult(geckoResult) 212 ->Then( 213 GetCurrentSerialEventTarget(), __func__, 214 [aPromise](RefPtr<WebAuthnRegisterResult>&& aValue) { 215 aPromise->Resolve(aValue); 216 }, 217 [aPromise](AndroidWebAuthnError&& aValue) { 218 aPromise->Reject(aValue.GetError()); 219 }); 220 })); 221 222 return NS_OK; 223 } 224 225 NS_IMETHODIMP 226 AndroidWebAuthnService::GetAssertion(uint64_t aTransactionId, 227 uint64_t aBrowsingContextId, 228 nsIWebAuthnSignArgs* aArgs, 229 nsIWebAuthnSignPromise* aPromise) { 230 Reset(); 231 232 bool conditionallyMediated; 233 (void)aArgs->GetConditionallyMediated(&conditionallyMediated); 234 if (conditionallyMediated) { 235 aPromise->Reject(NS_ERROR_DOM_NOT_SUPPORTED_ERR); 236 return NS_OK; 237 } 238 239 GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction( 240 "java::WebAuthnTokenManager::WebAuthnGetAssertion", 241 [self = RefPtr{this}, aArgs = RefPtr{aArgs}, 242 aPromise = RefPtr{aPromise}]() { 243 AssertIsOnMainThread(); 244 245 nsTArray<uint8_t> challBuf; 246 (void)aArgs->GetChallenge(challBuf); 247 jni::ByteBuffer::LocalRef challenge = jni::ByteBuffer::New( 248 const_cast<void*>(static_cast<const void*>(challBuf.Elements())), 249 challBuf.Length()); 250 251 nsTArray<nsTArray<uint8_t>> allowList; 252 (void)aArgs->GetAllowList(allowList); 253 jni::ObjectArray::LocalRef idList = 254 jni::ObjectArray::New(allowList.Length()); 255 int ix = 0; 256 for (const nsTArray<uint8_t>& credId : allowList) { 257 jni::ByteBuffer::LocalRef id = jni::ByteBuffer::New( 258 const_cast<void*>(static_cast<const void*>(credId.Elements())), 259 credId.Length()); 260 261 idList->SetElement(ix, id); 262 263 ix += 1; 264 } 265 266 nsTArray<uint8_t> clientDataHash; 267 (void)aArgs->GetClientDataHash(clientDataHash); 268 jni::ByteBuffer::LocalRef hash = jni::ByteBuffer::New( 269 const_cast<void*>( 270 static_cast<const void*>(clientDataHash.Elements())), 271 clientDataHash.Length()); 272 273 nsTArray<uint8_t> transportBuf; 274 (void)aArgs->GetAllowListTransports(transportBuf); 275 jni::ByteBuffer::LocalRef transportList = jni::ByteBuffer::New( 276 const_cast<void*>( 277 static_cast<const void*>(transportBuf.Elements())), 278 transportBuf.Length()); 279 280 GECKOBUNDLE_START(assertionBundle); 281 282 GECKOBUNDLE_PUT(assertionBundle, "isWebAuthn", 283 java::sdk::Integer::ValueOf(1)); 284 285 nsString rpId; 286 (void)aArgs->GetRpId(rpId); 287 GECKOBUNDLE_PUT(assertionBundle, "rpId", jni::StringParam(rpId)); 288 289 nsString origin; 290 (void)aArgs->GetOrigin(origin); 291 GECKOBUNDLE_PUT(assertionBundle, "origin", jni::StringParam(origin)); 292 293 uint32_t timeout; 294 (void)aArgs->GetTimeoutMS(&timeout); 295 GECKOBUNDLE_PUT(assertionBundle, "timeout", 296 java::sdk::Double::New(timeout)); 297 298 nsString userVerification; 299 (void)aArgs->GetUserVerification(userVerification); 300 GECKOBUNDLE_PUT(assertionBundle, "userVerification", 301 jni::StringParam(userVerification)); 302 303 GECKOBUNDLE_FINISH(assertionBundle); 304 305 GECKOBUNDLE_START(extensionsBundle); 306 307 nsString appId; 308 nsresult rv = aArgs->GetAppId(appId); 309 if (rv != NS_ERROR_NOT_AVAILABLE) { 310 if (NS_FAILED(rv)) { 311 aPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR); 312 return; 313 } 314 GECKOBUNDLE_PUT(extensionsBundle, "fidoAppId", 315 jni::StringParam(appId)); 316 } 317 318 GECKOBUNDLE_FINISH(extensionsBundle); 319 320 auto result = java::WebAuthnTokenManager::WebAuthnGetAssertion( 321 challenge, idList, transportList, assertionBundle, extensionsBundle, 322 hash); 323 auto geckoResult = java::GeckoResult::LocalRef(std::move(result)); 324 MozPromise<RefPtr<WebAuthnSignResult>, AndroidWebAuthnError, 325 true>::FromGeckoResult(geckoResult) 326 ->Then( 327 GetCurrentSerialEventTarget(), __func__, 328 [aPromise](RefPtr<WebAuthnSignResult>&& aValue) { 329 aPromise->Resolve(aValue); 330 }, 331 [aPromise](AndroidWebAuthnError&& aValue) { 332 aPromise->Reject(aValue.GetError()); 333 }); 334 })); 335 336 return NS_OK; 337 } 338 339 NS_IMETHODIMP 340 AndroidWebAuthnService::Reset() { 341 mRegisterCredPropsRk.reset(); 342 343 return NS_OK; 344 } 345 346 NS_IMETHODIMP 347 AndroidWebAuthnService::Cancel(uint64_t aTransactionId) { 348 return NS_ERROR_NOT_IMPLEMENTED; 349 } 350 351 NS_IMETHODIMP 352 AndroidWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId, 353 const nsAString& aOrigin, 354 uint64_t* aRv) { 355 // Signal that there is no pending conditional get request, so the caller 356 // will not attempt to call GetAutoFillEntries, SelectAutoFillEntry, or 357 // ResumeConditionalGet (as these are not implemented). 358 *aRv = 0; 359 return NS_OK; 360 } 361 362 NS_IMETHODIMP 363 AndroidWebAuthnService::GetAutoFillEntries( 364 uint64_t aTransactionId, nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>& aRv) { 365 return NS_ERROR_NOT_IMPLEMENTED; 366 } 367 368 NS_IMETHODIMP 369 AndroidWebAuthnService::SelectAutoFillEntry( 370 uint64_t aTransactionId, const nsTArray<uint8_t>& aCredentialId) { 371 return NS_ERROR_NOT_IMPLEMENTED; 372 } 373 374 NS_IMETHODIMP 375 AndroidWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) { 376 return NS_ERROR_NOT_IMPLEMENTED; 377 } 378 379 NS_IMETHODIMP 380 AndroidWebAuthnService::PinCallback(uint64_t aTransactionId, 381 const nsACString& aPin) { 382 return NS_ERROR_NOT_IMPLEMENTED; 383 } 384 385 NS_IMETHODIMP 386 AndroidWebAuthnService::SetHasAttestationConsent(uint64_t aTransactionId, 387 bool aHasConsent) { 388 return NS_ERROR_NOT_IMPLEMENTED; 389 } 390 391 NS_IMETHODIMP 392 AndroidWebAuthnService::SelectionCallback(uint64_t aTransactionId, 393 uint64_t aIndex) { 394 return NS_ERROR_NOT_IMPLEMENTED; 395 } 396 397 NS_IMETHODIMP 398 AndroidWebAuthnService::AddVirtualAuthenticator( 399 const nsACString& aProtocol, const nsACString& aTransport, 400 bool aHasResidentKey, bool aHasUserVerification, bool aIsUserConsenting, 401 bool aIsUserVerified, nsACString& aRetval) { 402 return NS_ERROR_NOT_IMPLEMENTED; 403 } 404 405 NS_IMETHODIMP 406 AndroidWebAuthnService::RemoveVirtualAuthenticator( 407 const nsACString& aAuthenticatorId) { 408 return NS_ERROR_NOT_IMPLEMENTED; 409 } 410 411 NS_IMETHODIMP 412 AndroidWebAuthnService::AddCredential(const nsACString& aAuthenticatorId, 413 const nsACString& aCredentialId, 414 bool aIsResidentCredential, 415 const nsACString& aRpId, 416 const nsACString& aPrivateKey, 417 const nsACString& aUserHandle, 418 uint32_t aSignCount) { 419 return NS_ERROR_NOT_IMPLEMENTED; 420 } 421 422 NS_IMETHODIMP 423 AndroidWebAuthnService::GetCredentials( 424 const nsACString& aAuthenticatorId, 425 nsTArray<RefPtr<nsICredentialParameters>>& _aRetval) { 426 return NS_ERROR_NOT_IMPLEMENTED; 427 } 428 429 NS_IMETHODIMP 430 AndroidWebAuthnService::RemoveCredential(const nsACString& aAuthenticatorId, 431 const nsACString& aCredentialId) { 432 return NS_ERROR_NOT_IMPLEMENTED; 433 } 434 435 NS_IMETHODIMP 436 AndroidWebAuthnService::RemoveAllCredentials( 437 const nsACString& aAuthenticatorId) { 438 return NS_ERROR_NOT_IMPLEMENTED; 439 } 440 441 NS_IMETHODIMP 442 AndroidWebAuthnService::SetUserVerified(const nsACString& aAuthenticatorId, 443 bool aIsUserVerified) { 444 return NS_ERROR_NOT_IMPLEMENTED; 445 } 446 447 NS_IMETHODIMP 448 AndroidWebAuthnService::Listen() { return NS_ERROR_NOT_IMPLEMENTED; } 449 450 NS_IMETHODIMP 451 AndroidWebAuthnService::RunCommand(const nsACString& aCmd) { 452 return NS_ERROR_NOT_IMPLEMENTED; 453 } 454 455 } // namespace dom 456 } // namespace mozilla