aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/string_utils/secret_string
diff options
context:
space:
mode:
authorqrort <qrort@yandex-team.com>2022-11-30 23:47:12 +0300
committerqrort <qrort@yandex-team.com>2022-11-30 23:47:12 +0300
commit22f8ae0e3f5d68b92aecccdf96c1d841a0334311 (patch)
treebffa27765faf54126ad44bcafa89fadecb7a73d7 /library/cpp/string_utils/secret_string
parent332b99e2173f0425444abb759eebcb2fafaa9209 (diff)
downloadydb-22f8ae0e3f5d68b92aecccdf96c1d841a0334311.tar.gz
validate canons without yatest_common
Diffstat (limited to 'library/cpp/string_utils/secret_string')
-rw-r--r--library/cpp/string_utils/secret_string/secret_string.cpp68
-rw-r--r--library/cpp/string_utils/secret_string/secret_string.h74
-rw-r--r--library/cpp/string_utils/secret_string/ut/secret_string_ut.cpp147
3 files changed, 289 insertions, 0 deletions
diff --git a/library/cpp/string_utils/secret_string/secret_string.cpp b/library/cpp/string_utils/secret_string/secret_string.cpp
new file mode 100644
index 00000000000..3b68d3cd274
--- /dev/null
+++ b/library/cpp/string_utils/secret_string/secret_string.cpp
@@ -0,0 +1,68 @@
+#include "secret_string.h"
+
+#include <util/system/madvise.h>
+
+namespace NSecretString {
+ TSecretString::TSecretString(TStringBuf value) {
+ Init(value);
+ }
+
+ TSecretString::~TSecretString() {
+ try {
+ Clear();
+ } catch (...) {
+ }
+ }
+
+ TSecretString& TSecretString::operator=(const TSecretString& o) {
+ if (&o == this) {
+ return *this;
+ }
+
+ Init(o.Value_);
+
+ return *this;
+ }
+
+ /**
+ * It is not honest "move". Actually it is copy-assignment with cleaning of other instance.
+ * This way allowes to avoid side effects of string optimizations:
+ * Copy-On-Write or Short-String-Optimization
+ */
+ TSecretString& TSecretString::operator=(TSecretString&& o) {
+ if (&o == this) {
+ return *this;
+ }
+
+ Init(o.Value_);
+ o.Clear();
+
+ return *this;
+ }
+
+ TSecretString& TSecretString::operator=(const TStringBuf o) {
+ Init(o);
+
+ return *this;
+ }
+
+ void TSecretString::Init(TStringBuf value) {
+ Clear();
+ if (value.empty()) {
+ return;
+ }
+
+ Value_ = value;
+ MadviseExcludeFromCoreDump(Value_);
+ }
+
+ void TSecretString::Clear() {
+ if (Value_.empty()) {
+ return;
+ }
+
+ SecureZero((void*)Value_.data(), Value_.size());
+ MadviseIncludeIntoCoreDump(Value_);
+ Value_.clear();
+ }
+}
diff --git a/library/cpp/string_utils/secret_string/secret_string.h b/library/cpp/string_utils/secret_string/secret_string.h
new file mode 100644
index 00000000000..fdb9f6a85ce
--- /dev/null
+++ b/library/cpp/string_utils/secret_string/secret_string.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include <library/cpp/string_utils/ztstrbuf/ztstrbuf.h>
+
+#include <util/generic/string.h>
+
+namespace NSecretString {
+ /**
+ * TSecretString allowes to store some long lived secrets in "secure" storage in memory.
+ * Common usage:
+ * 1) read secret value from disk/env/etc
+ * 2) put it into TSecretString
+ * 3) destory secret copy from 1)
+ *
+ * Useful scenerios for TSecretString:
+ * - in memory only tasks: using key to create crypto signature;
+ * - rare network cases: db password on connection or OAuth token in background tasks.
+ * These cases disclosure the secret
+ * because of sending it over network with some I/O frameworks.
+ * Usually such frameworks copy input params to provide network protocol: gRPC, for example.
+ *
+ * Supported features:
+ * 1. Exclude secret from core dump.
+ * madvise(MADV_DONTDUMP) in ctor excludes full memory page from core dump.
+ * madvise(MADV_DODUMP) in dtor reverts previous action.
+ * 2. Zero memory before free.
+ *
+ * Code dump looks like this:
+(gdb) print s
+$1 = (const TSecretString &) @0x7fff23c4c560: {
+ Value_ = {<TStringBase<TBasicString<char, std::__y1::char_traits<char> >, char, std::__y1::char_traits<char> >> = {
+ static npos = <optimized out>}, Data_ = 0x107c001d8 <error: Cannot access memory at address 0x107c001d8>}}
+ */
+
+ class TSecretString {
+ public:
+ TSecretString() = default;
+ TSecretString(TStringBuf value);
+ ~TSecretString();
+
+ TSecretString(const TSecretString& o)
+ : TSecretString(o.Value())
+ {
+ }
+
+ TSecretString(TSecretString&& o)
+ : TSecretString(o.Value())
+ {
+ o.Clear();
+ }
+
+ TSecretString& operator=(const TSecretString& o);
+ TSecretString& operator=(TSecretString&& o);
+
+ TSecretString& operator=(const TStringBuf o);
+
+ operator TZtStringBuf() const {
+ return Value();
+ }
+
+ // Provides zero terminated string
+ TZtStringBuf Value() const {
+ return TZtStringBuf(Value_);
+ }
+
+ private:
+ // TStringBuf breaks Copy-On-Write to provide correct copy-ctor and copy-assignment
+ void Init(TStringBuf value);
+ void Clear();
+
+ private:
+ TString Value_;
+ };
+}
diff --git a/library/cpp/string_utils/secret_string/ut/secret_string_ut.cpp b/library/cpp/string_utils/secret_string/ut/secret_string_ut.cpp
new file mode 100644
index 00000000000..681b75368f0
--- /dev/null
+++ b/library/cpp/string_utils/secret_string/ut/secret_string_ut.cpp
@@ -0,0 +1,147 @@
+#include <library/cpp/string_utils/secret_string/secret_string.h>
+
+#include <library/cpp/testing/unittest/registar.h>
+
+using namespace NSecretString;
+
+Y_UNIT_TEST_SUITE(SecretTest) {
+ Y_UNIT_TEST(Common) {
+ TSecretString s;
+ UNIT_ASSERT_VALUES_EQUAL("", s.Value());
+ UNIT_ASSERT_VALUES_EQUAL("", (TStringBuf)s);
+
+ TSecretString s2("qwerty");
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", (TStringBuf)s2);
+ }
+
+ Y_UNIT_TEST(CopyCtor1) {
+ TSecretString s1("qwerty");
+
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1.Value());
+
+ {
+ TSecretString s2(s1);
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1.Value());
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+ }
+
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1.Value());
+ }
+
+ Y_UNIT_TEST(CopyCtor2) {
+ auto s1 = MakeHolder<TSecretString>("qwerty");
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1->Value());
+
+ TSecretString s2(*s1);
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1->Value());
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+
+ s1.Reset();
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+ }
+
+ Y_UNIT_TEST(MoveCtor1) {
+ TSecretString s1("qwerty");
+
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1.Value());
+
+ {
+ TSecretString s2(std::move(s1));
+ UNIT_ASSERT_VALUES_EQUAL("", s1.Value());
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+ }
+
+ UNIT_ASSERT_VALUES_EQUAL("", s1.Value());
+ }
+
+ Y_UNIT_TEST(MoveCtor2) {
+ auto s1 = MakeHolder<TSecretString>("qwerty");
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1->Value());
+
+ TSecretString s2(std::move(*s1));
+ UNIT_ASSERT_VALUES_EQUAL("", s1->Value());
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+
+ s1.Reset();
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+ }
+
+ Y_UNIT_TEST(CopyAssignment1) {
+ TSecretString s1("qwerty");
+
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1.Value());
+
+ {
+ TSecretString s2;
+ UNIT_ASSERT_VALUES_EQUAL("", s2.Value());
+
+ s2 = s1;
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1.Value());
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+ }
+
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1.Value());
+ }
+
+ Y_UNIT_TEST(CopyAssignment2) {
+ auto s1 = MakeHolder<TSecretString>("qwerty");
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1->Value());
+
+ TSecretString s2;
+ UNIT_ASSERT_VALUES_EQUAL("", s2.Value());
+
+ s2 = *s1;
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1->Value());
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+
+ s1.Reset();
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+
+ TSecretString s3;
+ s2 = s3;
+ UNIT_ASSERT_VALUES_EQUAL("", s2.Value());
+ }
+
+ Y_UNIT_TEST(MoveAssignment1) {
+ TSecretString s1("qwerty");
+
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1.Value());
+
+ {
+ TSecretString s2;
+ UNIT_ASSERT_VALUES_EQUAL("", s2.Value());
+
+ s2 = std::move(s1);
+ UNIT_ASSERT_VALUES_EQUAL("", s1.Value());
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+ }
+
+ UNIT_ASSERT_VALUES_EQUAL("", s1.Value());
+ }
+
+ Y_UNIT_TEST(MoveAssignment2) {
+ auto s1 = MakeHolder<TSecretString>("qwerty");
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s1->Value());
+
+ TSecretString s2;
+ UNIT_ASSERT_VALUES_EQUAL("", s2.Value());
+
+ s2 = std::move(*s1);
+ UNIT_ASSERT_VALUES_EQUAL("", s1->Value());
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+
+ s1.Reset();
+ UNIT_ASSERT_VALUES_EQUAL("qwerty", s2.Value());
+
+ TSecretString s3;
+ s2 = std::move(s3);
+ UNIT_ASSERT_VALUES_EQUAL("", s2.Value());
+ }
+
+ Y_UNIT_TEST(ZeroTerminated) {
+ TSecretString s("qwerty");
+
+ UNIT_ASSERT_VALUES_EQUAL(s.Value().size(), strlen(s.Value().data()));
+ }
+}