summaryrefslogtreecommitdiffstats
path: root/dom/webauthn/U2FHIDTokenManager.h
blob: 1234bbb304189d7cdecad3ce54dee51a5d16fc93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_dom_U2FHIDTokenManager_h
#define mozilla_dom_U2FHIDTokenManager_h

#include "mozilla/dom/U2FTokenTransport.h"
#include "authenticator/src/u2fhid-capi.h"

/*
 * U2FHIDTokenManager is a Rust implementation of a secure token manager
 * for the U2F and WebAuthn APIs, talking to HIDs.
 */

namespace mozilla {
namespace dom {

class U2FAppIds {
 public:
  explicit U2FAppIds(const nsTArray<nsTArray<uint8_t>>& aApplications) {
    mAppIds = rust_u2f_app_ids_new();

    for (auto& app_id : aApplications) {
      rust_u2f_app_ids_add(mAppIds, app_id.Elements(), app_id.Length());
    }
  }

  rust_u2f_app_ids* Get() { return mAppIds; }

  ~U2FAppIds() { rust_u2f_app_ids_free(mAppIds); }

 private:
  rust_u2f_app_ids* mAppIds;
};

class U2FKeyHandles {
 public:
  explicit U2FKeyHandles(
      const nsTArray<WebAuthnScopedCredential>& aCredentials) {
    mKeyHandles = rust_u2f_khs_new();

    for (auto& cred : aCredentials) {
      rust_u2f_khs_add(mKeyHandles, cred.id().Elements(), cred.id().Length(),
                       cred.transports());
    }
  }

  rust_u2f_key_handles* Get() { return mKeyHandles; }

  ~U2FKeyHandles() { rust_u2f_khs_free(mKeyHandles); }

 private:
  rust_u2f_key_handles* mKeyHandles;
};

class U2FResult {
 public:
  explicit U2FResult(uint64_t aTransactionId, rust_u2f_result* aResult)
      : mTransactionId(aTransactionId), mResult(aResult) {
    MOZ_ASSERT(mResult);
  }

  ~U2FResult() { rust_u2f_res_free(mResult); }

  uint64_t GetTransactionId() { return mTransactionId; }

  bool IsError() { return NS_FAILED(GetError()); }

  nsresult GetError() {
    switch (rust_u2f_result_error(mResult)) {
      case U2F_ERROR_UKNOWN:
      case U2F_ERROR_CONSTRAINT:
        return NS_ERROR_DOM_UNKNOWN_ERR;
      case U2F_ERROR_NOT_SUPPORTED:
        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
      case U2F_ERROR_INVALID_STATE:
        return NS_ERROR_DOM_INVALID_STATE_ERR;
      case U2F_ERROR_NOT_ALLOWED:
        return NS_ERROR_DOM_NOT_ALLOWED_ERR;
      default:
        return NS_OK;
    }
  }

  bool CopyRegistration(nsTArray<uint8_t>& aBuffer) {
    return CopyBuffer(U2F_RESBUF_ID_REGISTRATION, aBuffer);
  }

  bool CopyKeyHandle(nsTArray<uint8_t>& aBuffer) {
    return CopyBuffer(U2F_RESBUF_ID_KEYHANDLE, aBuffer);
  }

  bool CopySignature(nsTArray<uint8_t>& aBuffer) {
    return CopyBuffer(U2F_RESBUF_ID_SIGNATURE, aBuffer);
  }

  bool CopyAppId(nsTArray<uint8_t>& aBuffer) {
    return CopyBuffer(U2F_RESBUF_ID_APPID, aBuffer);
  }

 private:
  bool CopyBuffer(uint8_t aResBufID, nsTArray<uint8_t>& aBuffer) {
    size_t len;
    if (!rust_u2f_resbuf_length(mResult, aResBufID, &len)) {
      return false;
    }

    if (!aBuffer.SetLength(len, fallible)) {
      return false;
    }

    return rust_u2f_resbuf_copy(mResult, aResBufID, aBuffer.Elements());
  }

  uint64_t mTransactionId;
  rust_u2f_result* mResult;
};

class U2FHIDTokenManager final : public U2FTokenTransport {
 public:
  explicit U2FHIDTokenManager();

  RefPtr<U2FRegisterPromise> Register(const WebAuthnMakeCredentialInfo& aInfo,
                                      bool aForceNoneAttestation) override;

  RefPtr<U2FSignPromise> Sign(const WebAuthnGetAssertionInfo& aInfo) override;

  void Cancel() override;
  void Drop() override;

  void HandleRegisterResult(UniquePtr<U2FResult>&& aResult);
  void HandleSignResult(UniquePtr<U2FResult>&& aResult);

 private:
  ~U2FHIDTokenManager() = default;

  void ClearPromises() {
    mRegisterPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
    mSignPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
  }

  class Transaction {
   public:
    Transaction(uint64_t aId, const nsTArray<uint8_t>& aRpIdHash,
                const Maybe<nsTArray<uint8_t>>& aAppIdHash,
                const nsCString& aClientDataJSON,
                bool aForceNoneAttestation = false)
        : mId(aId),
          mRpIdHash(aRpIdHash.Clone()),
          mClientDataJSON(aClientDataJSON),
          mForceNoneAttestation(aForceNoneAttestation) {
      if (aAppIdHash) {
        mAppIdHash = Some(aAppIdHash->Clone());
      } else {
        mAppIdHash = Nothing();
      }
    }

    // The transaction ID.
    uint64_t mId;

    // The RP ID hash.
    nsTArray<uint8_t> mRpIdHash;

    // The App ID hash, if the AppID extension was set
    Maybe<nsTArray<uint8_t>> mAppIdHash;

    // The clientData JSON.
    nsCString mClientDataJSON;

    // Whether we'll force "none" attestation.
    bool mForceNoneAttestation;
  };

  rust_u2f_manager* mU2FManager;
  Maybe<Transaction> mTransaction;
  MozPromiseHolder<U2FRegisterPromise> mRegisterPromise;
  MozPromiseHolder<U2FSignPromise> mSignPromise;
};

}  // namespace dom
}  // namespace mozilla

#endif  // mozilla_dom_U2FHIDTokenManager_h