wallet: wipe seed from memory where appropriate

This commit is contained in:
moneromooo-monero 2018-07-07 00:03:15 +01:00
parent b780cf4db1
commit ea37614efe
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
19 changed files with 653 additions and 144 deletions

View file

@ -72,7 +72,8 @@ set(unit_tests_sources
ringct.cpp
output_selection.cpp
vercmp.cpp
ringdb.cpp)
ringdb.cpp
wipeable_string.cpp)
set(unit_tests_headers
unit_tests_utils.h)

View file

@ -27,6 +27,8 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "gtest/gtest.h"
#include "wipeable_string.h"
#include "mnemonics/language_base.h"
#include "mnemonics/electrum-words.h"
#include "crypto/crypto.h"
#include <stdlib.h>
@ -74,14 +76,16 @@ namespace
void test_language(const Language::Base &language)
{
const std::vector<std::string> &word_list = language.get_word_list();
std::string seed = "", return_seed = "";
epee::wipeable_string w_seed = "", w_return_seed = "";
std::string seed, return_seed;
// Generate a random seed without checksum
crypto::secret_key randkey;
for (size_t ii = 0; ii < sizeof(randkey); ++ii)
{
randkey.data[ii] = rand();
}
crypto::ElectrumWords::bytes_to_words(randkey, seed, language.get_language_name());
crypto::ElectrumWords::bytes_to_words(randkey, w_seed, language.get_language_name());
seed = std::string(w_seed.data(), w_seed.size());
// remove the checksum word
const char *space = strrchr(seed.c_str(), ' ');
ASSERT_TRUE(space != NULL);
@ -103,7 +107,8 @@ namespace
ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str());
// Convert the secret key back to seed
crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name());
crypto::ElectrumWords::bytes_to_words(key, w_return_seed, language.get_language_name());
return_seed = std::string(w_return_seed.data(), w_return_seed.size());
ASSERT_EQ(true, res);
std::cout << "Returned seed:\n";
std::cout << return_seed << std::endl;
@ -126,8 +131,9 @@ namespace
std::cout << "Detected language: " << language_name << std::endl;
ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str());
return_seed = "";
crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name());
w_return_seed = "";
crypto::ElectrumWords::bytes_to_words(key, w_return_seed, language.get_language_name());
return_seed = std::string(w_return_seed.data(), w_return_seed.size());
ASSERT_EQ(true, res);
std::cout << "Returned seed:\n";
std::cout << return_seed << std::endl;
@ -202,3 +208,17 @@ TEST(mnemonics, language_detection_with_bad_checksum)
ASSERT_EQ(true, res);
ASSERT_STREQ(language_name.c_str(), "Português");
}
TEST(mnemonics, utf8prefix)
{
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 0) == "");
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 1) == "f");
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 2) == "fo");
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 3) == "foo");
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 4) == "foo");
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 0) == "");
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 1) == "æ");
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 2) == "æo");
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 3) == "æon");
ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 4) == "æon");
}

View file

@ -0,0 +1,204 @@
// Copyright (c) 2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <boost/optional/optional.hpp>
#include <string.h>
#include "gtest/gtest.h"
#include "misc_log_ex.h"
#include "wipeable_string.h"
TEST(wipeable_string, ctor)
{
epee::wipeable_string s0;
ASSERT_EQ(s0.size(), 0);
epee::wipeable_string s1(std::string("foo"));
ASSERT_EQ(s1.size(), 3);
ASSERT_TRUE(!memcmp(s1.data(), "foo", s1.size()));
epee::wipeable_string s2(std::string("bar"));
ASSERT_EQ(s2.size(), 3);
ASSERT_TRUE(!memcmp(s2.data(), "bar", s2.size()));
epee::wipeable_string s3(std::string("quux"));
ASSERT_EQ(s3.size(), 4);
ASSERT_TRUE(!memcmp(s3.data(), "quux", s3.size()));
}
TEST(wipeable_string, wipe)
{
epee::wipeable_string s0(std::string("foo"));
ASSERT_EQ(s0.size(), 3);
s0.wipe();
ASSERT_EQ(s0.size(), 3);
ASSERT_TRUE(!memcmp(s0.data(), "\0\0\0", 3));
}
TEST(wipeable_string, clear)
{
epee::wipeable_string s0(std::string("foo"));
ASSERT_EQ(s0.size(), 3);
s0.clear();
ASSERT_EQ(s0.size(), 0);
}
TEST(wipeable_string, push_back)
{
epee::wipeable_string s0(std::string("fo"));
ASSERT_EQ(s0.size(), 2);
s0.push_back('o');
ASSERT_EQ(s0.size(), 3);
ASSERT_TRUE(!memcmp(s0.data(), "foo", s0.size()));
}
TEST(wipeable_string, append_char)
{
epee::wipeable_string s0(std::string("fo"));
ASSERT_EQ(s0.size(), 2);
s0 += 'o';
ASSERT_EQ(s0.size(), 3);
ASSERT_TRUE(!memcmp(s0.data(), "foo", s0.size()));
}
TEST(wipeable_string, append_string)
{
epee::wipeable_string s0(std::string("foo"));
ASSERT_EQ(s0.size(), 3);
s0 += "bar";
ASSERT_EQ(s0.size(), 6);
ASSERT_TRUE(!memcmp(s0.data(), "foobar", s0.size()));
}
TEST(wipeable_string, empty)
{
epee::wipeable_string s0;
ASSERT_TRUE(s0.empty());
s0.push_back(' ');
ASSERT_FALSE(s0.empty());
ASSERT_EQ(s0.pop_back(), ' ');
ASSERT_TRUE(s0.empty());
}
TEST(wipeable_string, pop_back)
{
epee::wipeable_string s = "test";
ASSERT_EQ(s.size(), 4);
ASSERT_EQ(s.pop_back(), 't');
ASSERT_EQ(s.size(), 3);
ASSERT_TRUE(!memcmp(s.data(), "tes", s.size()));
}
TEST(wipeable_string, equal)
{
epee::wipeable_string s0 = "foo";
epee::wipeable_string s1 = "bar";
epee::wipeable_string s0_2 = "foo";
ASSERT_TRUE(s0 == s0);
ASSERT_TRUE(s0 == s0_2);
ASSERT_TRUE(s1 == s1);
ASSERT_FALSE(s1 == s0);
ASSERT_FALSE(s1 == s0_2);
}
TEST(wipeable_string, not_equal)
{
epee::wipeable_string s0 = "foo";
epee::wipeable_string s1 = "bar";
epee::wipeable_string s0_2 = "foo";
ASSERT_FALSE(s0 != s0);
ASSERT_FALSE(s0 != s0_2);
ASSERT_FALSE(s1 != s1);
ASSERT_TRUE(s1 != s0);
ASSERT_TRUE(s1 != s0_2);
}
static epee::wipeable_string trimmed(const char *s)
{
epee::wipeable_string str(s);
str.trim();
return str;
}
TEST(wipeable_string, trim)
{
ASSERT_TRUE(trimmed("") == "");
ASSERT_TRUE(trimmed(" ") == "");
ASSERT_TRUE(trimmed(" ") == "");
ASSERT_TRUE(trimmed("a") == "a");
ASSERT_TRUE(trimmed(" a") == "a");
ASSERT_TRUE(trimmed(" a") == "a");
ASSERT_TRUE(trimmed("a ") == "a");
ASSERT_TRUE(trimmed("a ") == "a");
ASSERT_TRUE(trimmed(" a ") == "a");
ASSERT_TRUE(trimmed(" a ") == "a");
ASSERT_TRUE(trimmed(" ab ") == "ab");
ASSERT_TRUE(trimmed(" a b ") == "a b");
ASSERT_TRUE(trimmed(" a b ") == "a b");
}
static bool check_split(const char *s, const std::vector<epee::wipeable_string> &v)
{
epee::wipeable_string str(s);
std::vector<epee::wipeable_string> fields;
str.split(fields);
return v == fields;
}
TEST(wipeable_string, split)
{
ASSERT_TRUE(check_split("", {}));
ASSERT_TRUE(check_split("foo", {"foo"}));
ASSERT_TRUE(check_split(" foo ", {"foo"}));
ASSERT_TRUE(check_split("foo bar", {"foo", "bar"}));
ASSERT_TRUE(check_split("foo bar", {"foo", "bar"}));
ASSERT_TRUE(check_split("foo bar baz", {"foo", "bar", "baz"}));
ASSERT_TRUE(check_split(" foo bar baz ", {"foo", "bar", "baz"}));
ASSERT_TRUE(check_split(" foo bar baz", {"foo", "bar", "baz"}));
ASSERT_TRUE(check_split("foo bar baz ", {"foo", "bar", "baz"}));
}
TEST(wipeable_string, parse_hexstr)
{
boost::optional<epee::wipeable_string> s;
ASSERT_EQ(boost::none, epee::wipeable_string("x").parse_hexstr());
ASSERT_EQ(boost::none, epee::wipeable_string("x0000000000000000").parse_hexstr());
ASSERT_EQ(boost::none, epee::wipeable_string("0000000000000000x").parse_hexstr());
ASSERT_EQ(boost::none, epee::wipeable_string("0").parse_hexstr());
ASSERT_EQ(boost::none, epee::wipeable_string("000").parse_hexstr());
ASSERT_TRUE((s = epee::wipeable_string("").parse_hexstr()));
ASSERT_EQ(*s, "");
ASSERT_TRUE((s = epee::wipeable_string("00").parse_hexstr()));
ASSERT_EQ(*s, epee::wipeable_string("", 1));
ASSERT_TRUE((s = epee::wipeable_string("41").parse_hexstr()));
ASSERT_EQ(*s, epee::wipeable_string("A"));
ASSERT_TRUE((s = epee::wipeable_string("414243").parse_hexstr()));
ASSERT_EQ(*s, epee::wipeable_string("ABC"));
}