diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt
index ff7e8f66c2f273e25eabb9d537db05b46717f408..2fe59c9e8783c4b14ed939a2c958738dd8de516c 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 933e2e2fb8412ff2a0e0bf244c90bf7873924594..8fb23c828a698cd28071c35f9fa6eab28b1ef7ba 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 1db221266e4aca68d3d86764dbbbf59008adccd3..48da81694cec98dd8871d1ec61677a5a1fb62e76 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 0000000000000000000000000000000000000000..d2278a6dad01e3016f8fce82f36bc4e24e15e230
--- /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 0000000000000000000000000000000000000000..2351c822b6f0782027b12f3f2ff53f38ea9cb50a
--- /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 50801aa31232afb84197200829b54d6b2cb1743d..0d420f0ad7a2829a7c4f2d46c4170e7bbdc8ad56 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 1f8b08289811e938c7b9012049a6c0d92d77a38b..e1675e44cf4c63132e560832847016c762eeae46 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 e54416e0103262ca2ff0ada2ef60764a1fcb4f5e..271acf4bdb6f12db5af2fb80a86e9cb6ddd258ae 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 4ae120f9496d3dfb8168cbd1d70f317bddda7656..cc88a31589d2a717aa4b579e81db7b62159cf50a 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 9ef0e5674c931a9cf0a30d93fa7fd85f838fcf22..6cb4048d401b997f6f4f8ba79b38d5c9eab8581c 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;