#ifndef CRYPTO_PROCESS_BOUND_STRING_H_
#define CRYPTO_PROCESS_BOUND_STRING_H_
#include <string>
#include <vector>
#include "base/check.h"
#include "base/containers/span.h"
#include "base/gtest_prod_util.h"
#include "crypto/crypto_export.h"
#include "crypto/features.h"
namespace crypto {
namespace internal {
CRYPTO_EXPORT size_t MaybeRoundUp(size_t size);
CRYPTO_EXPORT bool MaybeEncryptBuffer(base::span<uint8_t> buffer);
CRYPTO_EXPORT bool MaybeDecryptBuffer(base::span<uint8_t> buffer);
CRYPTO_EXPORT void SecureZeroBuffer(base::span<uint8_t> buffer);
}
template <typename T>
struct CRYPTO_EXPORT SecureAllocator {
using value_type = T;
SecureAllocator() noexcept = default;
T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
void deallocate(T* p, std::size_t n) noexcept {
if (p) {
internal::SecureZeroBuffer(UNSAFE_BUFFERS(
base::span<uint8_t>(reinterpret_cast<uint8_t*>(p), n * sizeof(T))));
std::allocator<T>().deallocate(p, n);
}
}
};
template <typename StringType>
class CRYPTO_EXPORT ProcessBound {
public:
using CharType = typename StringType::value_type;
ProcessBound(const ProcessBound& other) = default;
ProcessBound(ProcessBound&& other) = default;
ProcessBound& operator=(const ProcessBound& other) = default;
ProcessBound& operator=(ProcessBound&& other) = default;
explicit ProcessBound(const StringType& value)
: original_size_(value.size()) {
std::vector<CharType> data(value.begin(), value.end());
data.resize(internal::MaybeRoundUp(data.size()));
encrypted_ =
internal::MaybeEncryptBuffer(base::as_writable_byte_span(data));
maybe_encrypted_data_ = std::move(data);
}
~ProcessBound() = default;
StringType value() const { return StringType(secure_value()); }
std::basic_string<CharType,
std::char_traits<CharType>,
SecureAllocator<CharType>>
secure_value() const {
if (!encrypted_) {
return std::basic_string<CharType, std::char_traits<CharType>,
SecureAllocator<CharType>>(
maybe_encrypted_data_.data(), original_size_);
}
std::basic_string<CharType, std::char_traits<CharType>,
SecureAllocator<CharType>>
decrypted(maybe_encrypted_data_.begin(), maybe_encrypted_data_.end());
constexpr size_t kSSOMaxSize = 64u;
if (decrypted.size() < kSSOMaxSize) {
decrypted.reserve(kSSOMaxSize);
}
CHECK(internal::MaybeDecryptBuffer(base::as_writable_byte_span(decrypted)));
decrypted.resize(original_size_);
return decrypted;
}
size_t size() const { return original_size_; }
bool empty() const { return size() == 0; }
private:
FRIEND_TEST_ALL_PREFIXES(ProcessBoundEncryptionTest, Encryption);
std::vector<CharType> maybe_encrypted_data_;
size_t original_size_;
bool encrypted_ = false;
};
using ProcessBoundString = ProcessBound<std::string>;
using ProcessBoundWString = ProcessBound<std::wstring>;
using ProcessBoundU16String = ProcessBound<std::u16string>;
using SecureString =
std::basic_string<std::string::value_type,
std::char_traits<std::string::value_type>,
SecureAllocator<std::string::value_type>>;
using SecureWString =
std::basic_string<std::wstring::value_type,
std::char_traits<std::wstring::value_type>,
SecureAllocator<std::wstring::value_type>>;
using SecureU16String =
std::basic_string<std::u16string::value_type,
std::char_traits<std::u16string::value_type>,
SecureAllocator<std::u16string::value_type>>;
}
#endif