• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CRYPTO_PROCESS_BOUND_STRING_H_
6 #define CRYPTO_PROCESS_BOUND_STRING_H_
7 
8 #include <string>
9 #include <vector>
10 
11 #include "base/check.h"
12 #include "base/containers/span.h"
13 #include "base/feature_list.h"
14 #include "base/gtest_prod_util.h"
15 #include "crypto/crypto_export.h"
16 #include "crypto/features.h"
17 
18 namespace crypto {
19 
20 namespace internal {
21 
22 // Maybe round the size of the data to a size needed for the encrypt or decrypt
23 // operation. Returns the new size, or `size` if no rounding up is needed.
24 CRYPTO_EXPORT size_t MaybeRoundUp(size_t size);
25 
26 // Maybe encrypt a buffer, in place. Returns true if the buffer was successfully
27 // encrypted or false if unsupported by the platform or failed to encrypt.
28 CRYPTO_EXPORT bool MaybeEncryptBuffer(base::span<uint8_t> buffer);
29 
30 // Maybe decrypt a buffer, in place. Returns true if the buffer was successfully
31 // decrypted or false if unsupported by the platform or failed to decrypt.
32 CRYPTO_EXPORT bool MaybeDecryptBuffer(base::span<uint8_t> buffer);
33 
34 // Securely zero a buffer using a platform specific method.
35 CRYPTO_EXPORT void SecureZeroBuffer(base::span<uint8_t> buffer);
36 
37 }  // namespace internal
38 
39 // SecureAllocator is used by the SecureString variants below to clear the
40 // memory when the string moves out of scope.
41 template <typename T>
42 struct CRYPTO_EXPORT SecureAllocator {
43   using value_type = T;
44 
45   SecureAllocator() noexcept = default;
46 
allocateSecureAllocator47   T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
48 
deallocateSecureAllocator49   void deallocate(T* p, std::size_t n) noexcept {
50     if (p) {
51       // SAFETY: deallocate() has a fixed prototype from the std library, and
52       // passes an unsafe buffer, so convert it to a base::span here.
53       internal::SecureZeroBuffer(UNSAFE_BUFFERS(
54           base::span<uint8_t>(reinterpret_cast<uint8_t*>(p), n * sizeof(T))));
55       std::allocator<T>().deallocate(p, n);
56     }
57   }
58 };
59 
60 // On supported platforms, a process bound string cannot have its content read
61 // by other processes on the system. On unsupported platforms it provides no
62 // difference over a native string except it does more copies.
63 template <typename StringType>
64 class CRYPTO_EXPORT ProcessBound {
65  public:
66   using CharType = typename StringType::value_type;
67 
68   ProcessBound(const ProcessBound& other) = default;
69   ProcessBound(ProcessBound&& other) = default;
70   ProcessBound& operator=(const ProcessBound& other) = default;
71   ProcessBound& operator=(ProcessBound&& other) = default;
72 
73   // Create a process bound string. Takes a copy of the string passed in.
ProcessBound(const StringType & value)74   explicit ProcessBound(const StringType& value)
75       : original_size_(value.size()) {
76     std::vector<CharType> data(value.begin(), value.end());
77     if (base::FeatureList::IsEnabled(
78             crypto::features::kProcessBoundStringEncryption)) {
79       data.resize(internal::MaybeRoundUp(data.size()));
80       encrypted_ =
81           internal::MaybeEncryptBuffer(base::as_writable_byte_span(data));
82     }
83     maybe_encrypted_data_ = std::move(data);
84   }
85 
86   ~ProcessBound() = default;
87 
88   // Return the decrypted string.
value()89   StringType value() const { return StringType(secure_value()); }
90 
91   // Return the decrypted string as a string that attempts to wipe itself after
92   // use. Prefer over calling `value()` if caller can support it.
93   std::basic_string<CharType,
94                     std::char_traits<CharType>,
95                     SecureAllocator<CharType>>
secure_value()96   secure_value() const {
97     if (!encrypted_) {
98       return std::basic_string<CharType, std::char_traits<CharType>,
99                                SecureAllocator<CharType>>(
100           maybe_encrypted_data_.data(), original_size_);
101     }
102 
103     // Copy to decrypt in-place.
104     std::basic_string<CharType, std::char_traits<CharType>,
105                       SecureAllocator<CharType>>
106         decrypted(maybe_encrypted_data_.begin(), maybe_encrypted_data_.end());
107     // Attempt to avoid Small String Optimization (SSO) by reserving a larger
108     // allocation than the SSO default, forcing a dynamic allocation to occur,
109     // before any decrypted data is written to the string. This value was
110     // determined empirically.
111     constexpr size_t kSSOMaxSize = 64u;
112     if (decrypted.size() < kSSOMaxSize) {
113       decrypted.reserve(kSSOMaxSize);
114     }
115     CHECK(internal::MaybeDecryptBuffer(base::as_writable_byte_span(decrypted)));
116     decrypted.resize(original_size_);
117     return decrypted;
118   }
119 
size()120   size_t size() const { return original_size_; }
empty()121   bool empty() const { return size() == 0; }
122 
123  private:
124   FRIEND_TEST_ALL_PREFIXES(ProcessBoundFeatureTest, Encryption);
125   std::vector<CharType> maybe_encrypted_data_;
126   size_t original_size_;
127   bool encrypted_ = false;
128 };
129 
130 using ProcessBoundString = ProcessBound<std::string>;
131 using ProcessBoundWString = ProcessBound<std::wstring>;
132 using ProcessBoundU16String = ProcessBound<std::u16string>;
133 
134 // SecureString variants here attempt to clean memory for the string data when
135 // the string goes out of scope. However, while in memory it can be read, and if
136 // copied somewhere else, the memory can also be read. This is a defense in
137 // depth hardening and not meant to provide strong security guarantees.
138 using SecureString =
139     std::basic_string<std::string::value_type,
140                       std::char_traits<std::string::value_type>,
141                       SecureAllocator<std::string::value_type>>;
142 using SecureWString =
143     std::basic_string<std::wstring::value_type,
144                       std::char_traits<std::wstring::value_type>,
145                       SecureAllocator<std::wstring::value_type>>;
146 using SecureU16String =
147     std::basic_string<std::u16string::value_type,
148                       std::char_traits<std::u16string::value_type>,
149                       SecureAllocator<std::u16string::value_type>>;
150 
151 }  // namespace crypto
152 
153 #endif  // CRYPTO_PROCESS_BOUND_STRING_H_
154