File: extension_key_permissions_service.h

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,811; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (225 lines) | stat: -rw-r--r-- 10,234 bytes parent folder | download | duplicates (4)
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_EXTENSION_KEY_PERMISSIONS_SERVICE_H_
#define CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_EXTENSION_KEY_PERMISSIONS_SERVICE_H_

#include <stdint.h>

#include <memory>
#include <string>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
#include "chromeos/crosapi/mojom/keystore_error.mojom.h"
#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
#include "extensions/common/extension_id.h"

namespace extensions {
class StateStore;
}

namespace policy {
class PolicyService;
}

namespace content {
class BrowserContext;
}

namespace chromeos::platform_keys {

// PlatformKeys is a field stored in each extension's state store. It saves
// signing permissions of keys in the context of a (Profile, Extension) pair.
// Those permissions are only relevant for asymmetric keys used for signing
// requests (i.e. RSASSA-PKCS1-V1_5 and ECDSA). For that reason, keys with other
// algorithms (e.g. RSA-OAEP) will have no entry in the extension state storage.
// The current format of data that is written to the PlatformKeys field is a
// list of serialized KeyEntry objects:
//   { 'SPKI': string,
//     'signOnce': bool,  // if not present, defaults to false
//     'signUnlimited': bool  // if not present, defaults to false
//   }
//
// Do not change this constant as clients will lose their existing state.
inline constexpr char kStateStorePlatformKeys[] = "PlatformKeys";

using ExtensionKeyPermissionQueryCallback =
    base::OnceCallback<void(bool allowed)>;

using ExtensionKeyPermissionOperationCallback =
    base::OnceCallback<void(bool is_error,
                            crosapi::mojom::KeystoreError error)>;

// ** ExtensionKeyPermissionsService Responsibility **
// - Managing usage permissions for a (Profile, Extension) pair.
// - Answering usage permissions queries for a (Profile, Extension) pair.
// - Account for the special permissions of signing with asymmetric keys.
//
// The permission model depends on whether the user account is managed or not.
//
// ** If the user account is not managed **
// The user is under full control of the keys that are generated or imported
// while the device is not managed. For that, a user can grant a specific
// extension the permission to sign arbitrary data (e.g. a certificate requests)
// with a specific asymmetric key for an unlimited number of times. Use cases
// other than signing arbitrary data using asymmetric keys are currently not
// supported for unmanaged users. Therefore, the permission will default to
// `false` in those cases.
//
// ** If the user account is managed **
// The administrator is in charge of granting access to keys that are meant for
// corporate usage. The KeyPermissions policy allows the administrator to list
// exactly the extensions that are allowed to use corporate keys. Non-corporate
// keys are not affected.
//
// ** One-off Permission for the Certification Requests **
// Independent of the above, the extension that generates an asymmetric key with
// type RSASSA-PKCS1-V1_5 or ECDSA using the chrome.enterprise.platformKeys API
// is allowed to sign arbitrary data with the private key for a single time in
// order to create a certification request. The assumption is that certification
// requests usually require a signature of data including the public key. So,
// the one-off permission implies that, once a certificate authority creates the
// certificate of the generated key, the generating extension isn't able to use
// the key anymore, except if explicitly permitted by the administrator.
//
// ** IMPORTANT: Synchronization / Possible Race Conditions **
// This class reads from the extensions::StateStore only once on creation and
// caches the result. Further reads are done using the cached result. This can
// lead to race conditions once there can exist more than one instance of the
// service for the same (Profile, Extension) pair. Currently this will not
// happen as the only class owning ExtensionKeyPermissionsServices is
// ExtensionPlatformKeysService, which never owns two
// ExtensionKeyPermissionsServices at the same time.
class ExtensionKeyPermissionsService {
 public:
  // |key_permissions_service| must not be null and outlive this object.
  // Methods of this object refer implicitly to the extension with the id
  // |extension_id|. Don't use this constructor directly. Call
  // |ExtensionKeyPermissionsServiceFactory::GetForBrowserContextAndExtension|
  // instead.
  ExtensionKeyPermissionsService(const std::string& extension_id,
                                 extensions::StateStore* state_store,
                                 base::Value::List state_store_value,
                                 policy::PolicyService* profile_policies,
                                 content::BrowserContext* browser_context);

  ExtensionKeyPermissionsService(const ExtensionKeyPermissionsService&) =
      delete;
  ExtensionKeyPermissionsService& operator=(
      const ExtensionKeyPermissionsService&) = delete;
  ~ExtensionKeyPermissionsService();

  // Returns true if the private key matching `public_key_spki_der` can be used
  // by the extension with id `extension_id_`. This method also takes into
  // account the particular case of signing with an asymmetric key, by checking
  // the extension's state store entry (if present).
  void CanUseKey(const std::vector<uint8_t>& public_key_spki_der,
                 bool is_sign_operation,
                 ExtensionKeyPermissionQueryCallback callback);

  // Must be called when the extension with id |extension_id| used the private
  // key matching |public_key_spki_der| for signing. Updates the permissions
  // accordingly. E.g. if this extension generated the key and no other
  // permission was granted then the permission to sign with this key is
  // removed.
  void SetKeyUsedForSigning(const std::vector<uint8_t>& public_key_spki_der,
                            ExtensionKeyPermissionOperationCallback callback);

  // Marks the private key matching |public_key_spki_der| as corporate.
  void RegisterKeyForCorporateUsage(
      const std::vector<uint8_t>& public_key_spki_der,
      ExtensionKeyPermissionOperationCallback callback);

  // Registers that the private key matching |public_key_spki_der| is permitted
  // to sign arbitrary content once by the extension with id |extension_id|.
  void RegisterOneTimeSigningPermissionForKey(
      const std::vector<uint8_t>& public_key_spki_der);

  // Sets the user granted permission that the extension with id |extension_id|
  // can use the private key matching |public_key_spki_der| for signing.
  void SetUserGrantedSigningPermission(
      const std::vector<uint8_t>& public_key_spki_der,
      ExtensionKeyPermissionOperationCallback callback);

  // Returns the list of apps and extensions ids allowed to use corporate usage
  // keys by policy in |profile_policies|.
  static std::vector<std::string> GetCorporateKeyUsageAllowedAppIds(
      policy::PolicyService* const profile_policies);

 private:
  struct KeyEntry {
    explicit KeyEntry(const std::string& public_key_spki_der_b64)
        : spki_b64(public_key_spki_der_b64) {}

    // The base64-encoded DER of a X.509 Subject Public Key Info.
    std::string spki_b64;

    // True if the key can be used once for singing.
    // This permission is granted if an extension generated a key using the
    // enterprise.platformKeys API, so that it can build a certification
    // request. After the first signing operation this permission will be
    // revoked.
    bool sign_once = false;

    // True if the key can be used for signing an unlimited number of times.
    // This permission is granted by the user or by admin policy to allow the
    // extension to use the key for signing through the enterprise.platformKeys
    // or platformKeys API. This permission is granted until revoked by the user
    // or the policy.
    bool sign_unlimited = false;
  };

  void OnGotExtensionValue(std::optional<base::Value> value);

  // Writes the current |state_store_entries_| to the state store of
  // |extension_id_|.
  void WriteToStateStore();

  // Reads a KeyEntry list from |state| and stores them in
  // |state_store_entries_|.
  void KeyEntriesFromState(const base::Value::List& state);

  // Converts |state_store_entries_| to a base::Value for storing in the state
  // store.
  base::Value::List KeyEntriesToState();

  // Returns an existing entry for |public_key_spki_der_b64| from
  // |state_store_entries_|. If there is no existing entry, creates, adds and
  // returns a new entry.
  // |public_key_spki_der| must be the base64 encoding of the DER of a Subject
  // Public Key Info.
  KeyEntry* GetStateStoreEntry(const std::string& public_key_spki_der_b64);

  // Writes |value| to the state store of the extension.
  void SetPlatformKeysInStateStore(std::optional<base::Value> value);

  bool PolicyAllowsCorporateKeyUsage() const;

  void CanUseKeyWithFlags(ExtensionKeyPermissionQueryCallback callback,
                          bool is_sign_operation,
                          bool sign_unlimited_allowed,
                          crosapi::mojom::GetKeyTagsResultPtr key_tags);

  void SetUserGrantedSigningPermissionWithFlag(
      const std::vector<uint8_t>& public_key_spki_der,
      ExtensionKeyPermissionOperationCallback callback,
      bool can_user_grant_permission);

  const extensions::ExtensionId extension_id_;
  raw_ptr<extensions::StateStore, FlakyDanglingUntriaged>
      extensions_state_store_ = nullptr;
  std::vector<KeyEntry> state_store_entries_;
  const raw_ptr<policy::PolicyService> profile_policies_;
  const raw_ptr<crosapi::mojom::KeystoreService> keystore_service_ = nullptr;
  base::WeakPtrFactory<ExtensionKeyPermissionsService> weak_factory_{this};
};

}  // namespace chromeos::platform_keys

#endif  // CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_EXTENSION_KEY_PERMISSIONS_SERVICE_H_