aes_cbc.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #include "aes_cbc.h"
  2. #include <Windows.h>
  3. #include <algorithm>
  4. #include <array>
  5. #include <bcrypt.h>
  6. namespace sunrise::middleware::crypto::aes {
  7. namespace {
  8. /** @return True for a BCrypt status that reports success. */
  9. [[nodiscard]] bool succeeded(NTSTATUS status) noexcept {
  10. return status >= 0;
  11. }
  12. /** One opened key, closed when it leaves scope. */
  13. class Key {
  14. public:
  15. /** @param key Cypher key bytes. */
  16. explicit Key(std::span<const std::byte> key) noexcept {
  17. if (!succeeded(
  18. BCryptOpenAlgorithmProvider(&m_algorithm, BCRYPT_AES_ALGORITHM, nullptr, 0))) {
  19. m_algorithm = nullptr;
  20. return;
  21. }
  22. const auto* mode = reinterpret_cast<PUCHAR>(const_cast<wchar_t*>(BCRYPT_CHAIN_MODE_CBC));
  23. if (!succeeded(BCryptSetProperty(m_algorithm,
  24. BCRYPT_CHAINING_MODE,
  25. const_cast<PUCHAR>(mode),
  26. sizeof(BCRYPT_CHAIN_MODE_CBC),
  27. 0))
  28. || !succeeded(BCryptGenerateSymmetricKey(
  29. m_algorithm,
  30. &m_key,
  31. nullptr,
  32. 0,
  33. reinterpret_cast<PUCHAR>(const_cast<std::byte*>(key.data())),
  34. static_cast<ULONG>(key.size()),
  35. 0))) {
  36. m_key = nullptr;
  37. }
  38. }
  39. Key(const Key&) = delete;
  40. Key(Key&&) = delete;
  41. Key& operator=(const Key&) = delete;
  42. Key& operator=(Key&&) = delete;
  43. /** Closes the key and the provider in the order BCrypt requires. */
  44. ~Key() noexcept {
  45. if (m_key != nullptr) {
  46. BCryptDestroyKey(m_key);
  47. }
  48. if (m_algorithm != nullptr) {
  49. BCryptCloseAlgorithmProvider(m_algorithm, 0);
  50. }
  51. }
  52. /** @return The opened key, or null when any stage failed. */
  53. [[nodiscard]] BCRYPT_KEY_HANDLE handle() const noexcept {
  54. return m_key;
  55. }
  56. private:
  57. BCRYPT_ALG_HANDLE m_algorithm{nullptr};
  58. BCRYPT_KEY_HANDLE m_key{nullptr};
  59. };
  60. /** @return True when the buffers are a whole number of blocks and the vector is one block. */
  61. [[nodiscard]] bool sized(std::span<const std::byte> iv,
  62. std::span<const std::byte> input,
  63. std::span<std::byte> output) noexcept {
  64. return iv.size() == kBlockSize && !input.empty() && input.size() % kBlockSize == 0
  65. && output.size() >= input.size();
  66. }
  67. } // namespace
  68. /** Encrypts whole blocks in cipher block chaining mode. */
  69. bool encrypt(std::span<const std::byte> key,
  70. std::span<const std::byte> iv,
  71. std::span<const std::byte> input,
  72. std::span<std::byte> output) noexcept {
  73. if (!sized(iv, input, output)) {
  74. return false;
  75. }
  76. const Key opened{key};
  77. if (opened.handle() == nullptr) {
  78. return false;
  79. }
  80. // BCrypt overwrites the vector it is given, so the caller's is never passed in.
  81. std::array<std::byte, kBlockSize> vector{};
  82. std::copy(iv.begin(), iv.end(), vector.begin());
  83. ULONG produced = 0;
  84. const bool complete =
  85. succeeded(BCryptEncrypt(opened.handle(),
  86. reinterpret_cast<PUCHAR>(const_cast<std::byte*>(input.data())),
  87. static_cast<ULONG>(input.size()),
  88. nullptr,
  89. reinterpret_cast<PUCHAR>(vector.data()),
  90. static_cast<ULONG>(vector.size()),
  91. reinterpret_cast<PUCHAR>(output.data()),
  92. static_cast<ULONG>(input.size()),
  93. &produced,
  94. 0))
  95. && produced == input.size();
  96. SecureZeroMemory(vector.data(), vector.size());
  97. return complete;
  98. }
  99. /** Decrypts whole blocks in cipher block chaining mode. */
  100. bool decrypt(std::span<const std::byte> key,
  101. std::span<const std::byte> iv,
  102. std::span<const std::byte> input,
  103. std::span<std::byte> output) noexcept {
  104. if (!sized(iv, input, output)) {
  105. return false;
  106. }
  107. const Key opened{key};
  108. if (opened.handle() == nullptr) {
  109. return false;
  110. }
  111. std::array<std::byte, kBlockSize> vector{};
  112. std::copy(iv.begin(), iv.end(), vector.begin());
  113. ULONG produced = 0;
  114. const bool complete =
  115. succeeded(BCryptDecrypt(opened.handle(),
  116. reinterpret_cast<PUCHAR>(const_cast<std::byte*>(input.data())),
  117. static_cast<ULONG>(input.size()),
  118. nullptr,
  119. reinterpret_cast<PUCHAR>(vector.data()),
  120. static_cast<ULONG>(vector.size()),
  121. reinterpret_cast<PUCHAR>(output.data()),
  122. static_cast<ULONG>(input.size()),
  123. &produced,
  124. 0))
  125. && produced == input.size();
  126. SecureZeroMemory(vector.data(), vector.size());
  127. return complete;
  128. }
  129. } // namespace sunrise::middleware::crypto::aes