From c78f3eb033a733a9e57634e3417bf27d3e42beb8 Mon Sep 17 00:00:00 2001
From: Daniel Lyubomirov <daniel.lyubomirov@vereign.com>
Date: Fri, 26 Jun 2020 10:14:38 +0300
Subject: [PATCH] [17] crypto storage common base implementation

---
 cpp/src/CMakeLists.txt                        |  2 +-
 cpp/src/vereign/core/string.cc                |  4 +-
 cpp/src/vereign/core/string.hh                |  4 +-
 .../kvstore/detail/base_crypto_storage.cc     | 95 +++++++++++++++++++
 .../kvstore/detail/base_crypto_storage.hh     | 37 ++++++++
 .../kvstore/detail/linux_crypto_storage.cc    | 76 +--------------
 .../kvstore/detail/linux_crypto_storage.hh    | 16 +---
 .../kvstore/detail/win_crypto_storage.cc      | 81 ++--------------
 .../kvstore/detail/win_crypto_storage.hh      | 15 +--
 cpp/src/vereign/ncrypt/rsa.hh                 |  6 +-
 10 files changed, 157 insertions(+), 179 deletions(-)
 create mode 100644 cpp/src/vereign/kvstore/detail/base_crypto_storage.cc
 create mode 100644 cpp/src/vereign/kvstore/detail/base_crypto_storage.hh

diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt
index ff7e8f6..2fe59c9 100644
--- a/cpp/src/CMakeLists.txt
+++ b/cpp/src/CMakeLists.txt
@@ -75,8 +75,8 @@ set(VEREIGNLIB_SRC
   vereign/crypto/bio.cc
   vereign/crypto/digest.cc
 
-
   vereign/kvstore/lock.cc
+  vereign/kvstore/detail/base_crypto_storage.cc
   vereign/kvstore/sqlite_storage.cc
   vereign/kvstore/crypto_storage.cc
 
diff --git a/cpp/src/vereign/core/string.cc b/cpp/src/vereign/core/string.cc
index 933e2e2..8fb23c8 100644
--- a/cpp/src/vereign/core/string.cc
+++ b/cpp/src/vereign/core/string.cc
@@ -11,7 +11,7 @@ namespace vereign::string {
 
 #ifdef _WIN32
 
-auto widen(const std::string& utf8_str) -> std::wstring {
+auto widen(std::string_view utf8_str) -> std::wstring {
   if (utf8_str.empty()) {
     return L"";
   }
@@ -39,7 +39,7 @@ auto widen(const std::string& utf8_str) -> std::wstring {
   return result;
 }
 
-auto narrow(const std::wstring& utf16_str) -> std::string {
+auto narrow(std::wstring_view utf16_str) -> std::string {
   if (utf16_str.empty()) {
     return "";
   }
diff --git a/cpp/src/vereign/core/string.hh b/cpp/src/vereign/core/string.hh
index 1db2212..48da816 100644
--- a/cpp/src/vereign/core/string.hh
+++ b/cpp/src/vereign/core/string.hh
@@ -8,8 +8,8 @@ namespace vereign::string {
 
 #ifdef _WIN32
 
-auto widen(const std::string& utf8_str) -> std::wstring;
-auto narrow(const std::wstring& utf16_str) -> std::string;
+auto widen(std::string_view utf8_str) -> std::wstring;
+auto narrow(std::wstring_view utf16_str) -> std::string;
 
 #endif
 
diff --git a/cpp/src/vereign/kvstore/detail/base_crypto_storage.cc b/cpp/src/vereign/kvstore/detail/base_crypto_storage.cc
new file mode 100644
index 0000000..d2278a6
--- /dev/null
+++ b/cpp/src/vereign/kvstore/detail/base_crypto_storage.cc
@@ -0,0 +1,95 @@
+#include <vereign/kvstore/detail/base_crypto_storage.hh>
+
+#include <vereign/kvstore/detail/value_encoder.hh>
+#include <vereign/kvstore/errors.hh>
+#include <vereign/kvstore/lock.hh>
+
+#include <vereign/crypto/digest.hh>
+#include <vereign/crypto/errors.hh>
+#include <vereign/crypto/rand.hh>
+#include <vereign/crypto/rsa.hh>
+#include <vereign/crypto/aes.hh>
+#include <vereign/crypto/bio.hh>
+
+#include <boost/core/ignore_unused.hpp>
+#include <chrono>
+
+namespace {
+
+  // FIXME: should these be injected and provided by the integrator
+  constexpr int tagSizeBytes = 64;
+  constexpr int lockRetryCount = 10;
+  constexpr auto lockRetrySleep = std::chrono::milliseconds{1000};
+}
+
+namespace vereign::kvstore::detail {
+
+BaseCryptoStorageImpl::BaseCryptoStorageImpl(kvstore::Storage& storage)
+  : storage_{storage}
+{
+}
+
+void BaseCryptoStorageImpl::encryptBytes(const std::string& key, bytes::View value) {
+  if (key_.Size() == 0) {
+    throw Error("crypto storage is not initialized");
+  }
+
+  bytes::Buffer iv;
+  bytes::Buffer tag;
+  bytes::Buffer encrypted;
+
+  crypto::aes::GCM256Encrypt(value, key_.View(), iv, encrypted, tag);
+
+  bytes::Buffer encoded_value;
+  EncodeEncryptedValue(encoded_value, iv.View(), tag.View(), encrypted.View());
+
+  storage_.PutBytes(key, encoded_value.View());
+}
+
+void BaseCryptoStorageImpl::PutBytes(const std::string& key, bytes::View value) {
+  kvstore::Lock l{storage_, lockRetryCount, lockRetrySleep};
+
+  validateTag();
+
+  encryptBytes(key, value);
+}
+
+void BaseCryptoStorageImpl::GetBytes(const std::string& key, bytes::Buffer& value) const {
+  kvstore::Lock l{storage_, lockRetryCount, lockRetrySleep};
+
+  if (key_.Size() == 0) {
+    throw Error("crypto storage is not initialized");
+  }
+
+  bytes::Buffer encoded;
+  storage_.GetBytes(key, encoded);
+
+  bytes::View iv;
+  bytes::View tag;
+  bytes::View encrypted;
+  DecodeEncryptedValue(encoded.View(), iv, tag, encrypted);
+
+  crypto::aes::GCM256Decrypt(encrypted, key_.View(), iv, tag, value);
+}
+
+void BaseCryptoStorageImpl::initKey(bytes::Buffer&& key) {
+  key_ = std::move(key);
+}
+
+void BaseCryptoStorageImpl::validateTag() const {
+  bytes::Buffer tag;
+  try {
+    GetBytes("__tag", tag);
+  } catch (const crypto::Error& err) {
+    throw IdentityChanged{};
+  }
+}
+
+void BaseCryptoStorageImpl::updateTag() {
+  bytes::Buffer tag{tagSizeBytes};
+  crypto::Rand(tag);
+
+  encryptBytes("__tag", tag.View());
+}
+
+} // namespace vereign::kvstore::detail
diff --git a/cpp/src/vereign/kvstore/detail/base_crypto_storage.hh b/cpp/src/vereign/kvstore/detail/base_crypto_storage.hh
new file mode 100644
index 0000000..2351c82
--- /dev/null
+++ b/cpp/src/vereign/kvstore/detail/base_crypto_storage.hh
@@ -0,0 +1,37 @@
+#ifndef __VEREIGN_KVSTORE_DETAIL_BASE_CRYPTO_STORAGE_HH
+#define __VEREIGN_KVSTORE_DETAIL_BASE_CRYPTO_STORAGE_HH
+
+#include <vereign/kvstore/storage.hh>
+
+namespace vereign::kvstore::detail {
+
+class BaseCryptoStorageImpl {
+public:
+  BaseCryptoStorageImpl(kvstore::Storage& storage);
+
+  // disable copying
+  BaseCryptoStorageImpl(const BaseCryptoStorageImpl&) = delete;
+  auto operator=(const BaseCryptoStorageImpl&) -> BaseCryptoStorageImpl& = delete;
+
+  void PutBytes(const std::string& key, bytes::View value);
+  void GetBytes(const std::string& key, bytes::Buffer& value) const;
+
+protected:
+  void initKey(bytes::Buffer&& key);
+  void validateTag() const;
+  void updateTag();
+
+private:
+  void encryptBytes(const std::string& key, bytes::View value);
+
+protected:
+  kvstore::Storage& storage_;
+
+private:
+  bytes::Buffer key_;
+};
+
+} // namespace vereign::kvstore::detail
+
+
+#endif // __VEREIGN_KVSTORE_DETAIL_BASE_CRYPTO_STORAGE_HH
diff --git a/cpp/src/vereign/kvstore/detail/linux_crypto_storage.cc b/cpp/src/vereign/kvstore/detail/linux_crypto_storage.cc
index 50801aa..0d420f0 100644
--- a/cpp/src/vereign/kvstore/detail/linux_crypto_storage.cc
+++ b/cpp/src/vereign/kvstore/detail/linux_crypto_storage.cc
@@ -18,7 +18,6 @@ namespace {
   // FIXME: should these be injected and provided by the integrator
   constexpr int iterations = 1 << 18;
   constexpr int saltSizeBytes = 64;
-  constexpr int tagSizeBytes = 64;
   constexpr int aesKeySizeBytes = 32;
 
   constexpr int lockRetryCount = 10;
@@ -28,7 +27,7 @@ namespace {
 namespace vereign::kvstore::detail {
 
 CryptoStorageImpl::CryptoStorageImpl(kvstore::Storage& storage, bool disable_key_protection)
-  : storage_{storage}
+  : BaseCryptoStorageImpl{storage}
 {
   boost::ignore_unused(disable_key_protection);
 }
@@ -60,17 +59,9 @@ void CryptoStorageImpl::Open(const std::string& pin) {
 
   key.IncSize(aesKeySizeBytes);
 
-  key_ = std::move(key);
-
+  initKey(std::move(key));
   // FIXME: write tests for tampering and regular identity change
-  try {
-    bytes::Buffer tag;
-    GetBytes("__tag", tag);
-    tag_ = std::move(tag);
-
-  } catch (const crypto::Error& err) {
-    throw IdentityChanged{};
-  }
+  validateTag();
 }
 
 void CryptoStorageImpl::Reset(const std::string& pin) {
@@ -93,75 +84,18 @@ void CryptoStorageImpl::Reset(const std::string& pin) {
   }
 
   key.IncSize(aesKeySizeBytes);
-  key_ = std::move(key);
-
-  bytes::Buffer tag{tagSizeBytes};
-  crypto::Rand(tag);
+  initKey(std::move(key));
 
   {
     kvstore::Lock l{storage_, lockRetryCount, lockRetrySleep};
 
     storage_.DeleteAll();
 
-    encryptBytes("__tag", tag.View());
-    tag_ = std::move(tag);
+    updateTag();
 
     storage_.PutInt64("__master_key_iterations", iterations);
     storage_.PutBytes("__master_key_salt", salt.View());
   }
 }
 
-void CryptoStorageImpl::encryptBytes(const std::string& key, bytes::View value) {
-  if (key_.Size() == 0) {
-    throw Error("crypto storage is not initialized");
-  }
-
-  bytes::Buffer iv;
-  bytes::Buffer tag;
-  bytes::Buffer encrypted;
-
-  crypto::aes::GCM256Encrypt(value, key_.View(), iv, encrypted, tag);
-
-  bytes::Buffer encoded_value;
-  EncodeEncryptedValue(encoded_value, iv.View(), tag.View(), encrypted.View());
-
-  storage_.PutBytes(key, encoded_value.View());
-}
-
-void CryptoStorageImpl::PutBytes(const std::string& key, bytes::View value) {
-  kvstore::Lock l{storage_, lockRetryCount, lockRetrySleep};
-
-  bytes::Buffer tag;
-  try {
-    GetBytes("__tag", tag);
-  } catch (const crypto::Error& err) {
-    throw IdentityChanged{};
-  }
-
-  if (tag.View() != tag_.View()) {
-    throw IdentityChanged{};
-  }
-
-  encryptBytes(key, value);
-}
-
-void CryptoStorageImpl::GetBytes(const std::string& key, bytes::Buffer& value) {
-  kvstore::Lock l{storage_, lockRetryCount, lockRetrySleep};
-
-  if (key_.Size() == 0) {
-    throw Error("crypto storage is not initialized");
-  }
-
-  bytes::Buffer encoded;
-  storage_.GetBytes(key, encoded);
-
-  bytes::View iv;
-  bytes::View tag;
-  bytes::View encrypted;
-  DecodeEncryptedValue(encoded.View(), iv, tag, encrypted);
-
-  crypto::aes::GCM256Decrypt(encrypted, key_.View(), iv, tag, value);
-}
-
-
 } // namespace vereign::kvstore::detail
diff --git a/cpp/src/vereign/kvstore/detail/linux_crypto_storage.hh b/cpp/src/vereign/kvstore/detail/linux_crypto_storage.hh
index 1f8b082..e1675e4 100644
--- a/cpp/src/vereign/kvstore/detail/linux_crypto_storage.hh
+++ b/cpp/src/vereign/kvstore/detail/linux_crypto_storage.hh
@@ -2,10 +2,11 @@
 #define __VEREIGN_KVSTORE_DETAIL_LINUX_CRYPTO_STORAGE_HH
 
 #include <vereign/kvstore/storage.hh>
+#include <vereign/kvstore/detail/base_crypto_storage.hh>
 
 namespace vereign::kvstore::detail {
 
-class CryptoStorageImpl {
+class CryptoStorageImpl : public BaseCryptoStorageImpl {
 public:
   CryptoStorageImpl(kvstore::Storage& storage, bool disable_key_protection);
 
@@ -15,19 +16,6 @@ public:
 
   void Reset(const std::string& pin);
   void Open(const std::string& pin);
-
-  void PutBytes(const std::string& key, bytes::View value);
-  void GetBytes(const std::string& key, bytes::Buffer& value);
-
-private:
-  void encryptBytes(const std::string& key, bytes::View value);
-
-private:
-  kvstore::Storage& storage_;
-
-  bytes::Buffer key_;
-
-  bytes::Buffer tag_;
 };
 
 } // namespace vereign::kvstore::detail
diff --git a/cpp/src/vereign/kvstore/detail/win_crypto_storage.cc b/cpp/src/vereign/kvstore/detail/win_crypto_storage.cc
index e54416e..271acf4 100644
--- a/cpp/src/vereign/kvstore/detail/win_crypto_storage.cc
+++ b/cpp/src/vereign/kvstore/detail/win_crypto_storage.cc
@@ -19,7 +19,6 @@
 namespace {
   // FIXME: should these be injected and provided by the integrator
   constexpr int keySizeBits = 2048;
-  constexpr int tagSizeBytes = 64;
   constexpr int aesKeySizeBytes = 32;
 
   constexpr int lockRetryCount = 10;
@@ -37,7 +36,7 @@ namespace {
 namespace vereign::kvstore::detail {
 
 CryptoStorageImpl::CryptoStorageImpl(kvstore::Storage& storage, bool disable_key_protection)
-  : storage_{storage},
+  : BaseCryptoStorageImpl{storage},
     disable_key_protection_{disable_key_protection}
 {}
 
@@ -56,17 +55,10 @@ void CryptoStorageImpl::Open(const std::string& pin) {
 
   bytes::Buffer key;
   ncrypt::rsa::PrivateKeyDecrypt(rsa_key.Get(), encrypted_key.View(), key);
-  key_ = std::move(key);
 
+  initKey(std::move(key));
   // FIXME: write tests for tampering and regular identity change
-  try {
-    bytes::Buffer tag;
-    GetBytes("__tag", tag);
-    tag_ = std::move(tag);
-
-  } catch (const crypto::Error&) {
-    throw IdentityChanged{};
-  }
+  validateTag();
 }
 
 void CryptoStorageImpl::Reset(const std::string& pin) {
@@ -82,9 +74,9 @@ void CryptoStorageImpl::Reset(const std::string& pin) {
   std::optional<ncrypt::rsa::KeyUIPolicy> ui_policy;
   if (!disable_key_protection_) {
     ui_policy = ncrypt::rsa::KeyUIPolicy{
-      std::string(vereignKeyCreationTitle),
-      std::string(vereignKeyDescription),
-      std::string(vereignKeyFriendlyName)
+      vereignKeyCreationTitle,
+      vereignKeyDescription,
+      vereignKeyFriendlyName
     };
   }
 
@@ -103,66 +95,9 @@ void CryptoStorageImpl::Reset(const std::string& pin) {
   bytes::Buffer encrypted_key;
   ncrypt::rsa::PublicKeyEncrypt(rsa_key.Get(), key.View(), encrypted_key);
   storage_.PutBytes("__master_key", encrypted_key.View());
-  key_ = std::move(key);
-
-  bytes::Buffer tag{tagSizeBytes};
-  crypto::Rand(tag);
-
-  encryptBytes("__tag", tag.View());
-  tag_ = std::move(tag);
-}
-
-void CryptoStorageImpl::encryptBytes(const std::string& key, bytes::View value) {
-  if (key_.Size() == 0) {
-    throw Error("crypto storage is not initialized");
-  }
-
-  bytes::Buffer iv;
-  bytes::Buffer tag;
-  bytes::Buffer encrypted;
-
-  crypto::aes::GCM256Encrypt(value, key_.View(), iv, encrypted, tag);
-
-  bytes::Buffer encoded_value;
-  EncodeEncryptedValue(encoded_value, iv.View(), tag.View(), encrypted.View());
-
-  storage_.PutBytes(key, encoded_value.View());
-}
-
-void CryptoStorageImpl::PutBytes(const std::string& key, bytes::View value) {
-  kvstore::Lock l{storage_, lockRetryCount, lockRetrySleep};
 
-  bytes::Buffer tag;
-  try {
-    GetBytes("__tag", tag);
-  } catch (const crypto::Error&) {
-    throw IdentityChanged{};
-  }
-
-  if (tag.View() != tag_.View()) {
-    throw IdentityChanged{};
-  }
-
-  encryptBytes(key, value);
+  initKey(std::move(key));
+  updateTag();
 }
 
-void CryptoStorageImpl::GetBytes(const std::string& key, bytes::Buffer& value) {
-  kvstore::Lock l{storage_, lockRetryCount, lockRetrySleep};
-
-  if (key_.Size() == 0) {
-    throw Error("crypto storage is not initialized");
-  }
-
-  bytes::Buffer encoded;
-  storage_.GetBytes(key, encoded);
-
-  bytes::View iv;
-  bytes::View tag;
-  bytes::View encrypted;
-  DecodeEncryptedValue(encoded.View(), iv, tag, encrypted);
-
-  crypto::aes::GCM256Decrypt(encrypted, key_.View(), iv, tag, value);
-}
-
-
 }
diff --git a/cpp/src/vereign/kvstore/detail/win_crypto_storage.hh b/cpp/src/vereign/kvstore/detail/win_crypto_storage.hh
index 4ae120f..cc88a31 100644
--- a/cpp/src/vereign/kvstore/detail/win_crypto_storage.hh
+++ b/cpp/src/vereign/kvstore/detail/win_crypto_storage.hh
@@ -2,10 +2,11 @@
 #define __VEREIGN_KVSTORE_DETAIL_WIN_CRYPTO_STORAGE_HH
 
 #include <vereign/kvstore/storage.hh>
+#include <vereign/kvstore/detail/base_crypto_storage.hh>
 
 namespace vereign::kvstore::detail {
 
-class CryptoStorageImpl {
+class CryptoStorageImpl : public BaseCryptoStorageImpl {
 public:
   CryptoStorageImpl(kvstore::Storage& storage, bool disable_key_protection);
 
@@ -16,20 +17,8 @@ public:
   void Reset(const std::string& pin);
   void Open(const std::string& pin);
 
-  void PutBytes(const std::string& key, bytes::View value);
-  void GetBytes(const std::string& key, bytes::Buffer& value);
-
-private:
-  void encryptBytes(const std::string& key, bytes::View value);
-
 private:
-  kvstore::Storage& storage_;
-
   bool disable_key_protection_ = false;
-
-  bytes::Buffer key_;
-
-  bytes::Buffer tag_;
 };
 
 } // namespace vereign::kvstore::detail
diff --git a/cpp/src/vereign/ncrypt/rsa.hh b/cpp/src/vereign/ncrypt/rsa.hh
index 9ef0e56..6cb4048 100644
--- a/cpp/src/vereign/ncrypt/rsa.hh
+++ b/cpp/src/vereign/ncrypt/rsa.hh
@@ -12,9 +12,9 @@
 namespace vereign::ncrypt::rsa {
 
 struct KeyUIPolicy {
-  std::string CreationTitle;
-  std::string Description;
-  std::string FriendlyName;
+  std::string_view CreationTitle;
+  std::string_view Description;
+  std::string_view FriendlyName;
 };
 
 auto OpenStorageProvider() -> UniquePtr;
-- 
GitLab