tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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