C unordered_map String键在存在时并不总是找到?字符串v char
c++ unordered_map string key not always found when it exists? string v char?
我在编写的单元测试方面遇到了麻烦。基本上,我正在存储用户名称 盐 来自unordered_map中密码的密钥的哈希。名称是关键。盐 哈希分别保存在具有16和32个字节的结构中。此unordered_map是从流创建的。在我的测试中,流是弦流,但是在我的构建中,我将使用Fstream。
因此,从本质上讲,我的代码试图决定是否创建新用户。如果它在流中找不到用户名,它将插入它。如果它确实在流中找到了用户名,则不会插入它。
我的单位测试应该始终找到用户名称,因为我已经对其进行了硬编码。但是,它并不总是找到它,有时会失败。特别是我期望用户size()等于1的地方,有时等于2,这意味着尽管存在密钥,但添加了用户。(我打印了用户的键,他们是相同的...
我在做什么错?
也是C 的新手,因此,如果您可以指出我做错的任何事情或有任何建议,请建议他们。谢谢!
编辑:显示无修改的代码
my_password_manager.h
#define SALT_LEN 16
#define DIGEST_LEN 32 //we use sha 256 for digest
#define ITER 100000
struct SaltDigest
{
unsigned char salt[SALT_LEN];
unsigned char digest[DIGEST_LEN];
};
class MyPasswordManager
{
private:
unordered_map<string, AppDetails> apps;
void AddUsersFromStream(iostream &mystream);
void CreateDigestFromPasswordAndSalt(string p, unsigned char *salt, unsigned char *digest);
public:
// username, salt, digest
unordered_map<string, SaltDigest> users;
string current_user;
// constructor loads the list of user profiles from file
MyPasswordManager() {}
// Creates a profile
bool CreateProfile(string user, string password, iostream &mystream);
};
my_password_manager.cc
void MyPasswordManager::AddUsersFromStream(iostream &mystream)
{
if (mystream.good())
{
while (mystream.peek() != EOF)
{
// read first byte for # of chars for username
char *userlen = new char[sizeof(int)];
mystream.read(userlen, sizeof(int));
char *username = new char[*userlen];
mystream.read(username, *userlen);
unsigned char *salt = new unsigned char[SALT_LEN];
mystream.read((char *)salt, SALT_LEN);
unsigned char *digest = new unsigned char[DIGEST_LEN];
mystream.read((char *)digest, DIGEST_LEN);
SaltDigest sd;
memcpy(sd.salt, salt, SALT_LEN);
memcpy(sd.digest, digest, DIGEST_LEN);
pair<string, SaltDigest> user_details(username, sd);
users.insert(user_details);
delete[] username;
delete[] userlen;
delete[] salt;
delete[] digest;
}
}
}
void MyPasswordManager::CreateDigestFromPasswordAndSalt(string p, unsigned char *salt, unsigned char *digest)
{
// create a key from the password & salt
char *pass = (char *)p.c_str();
int passlen = p.size();
unsigned int keylen = 16;
unsigned char *key = new unsigned char[keylen];
if (1 != PKCS5_PBKDF2_HMAC_SHA1(pass, passlen, salt, SALT_LEN, ITER, keylen, key))
handleErrors();
// hash key
digest_message(key, keylen, &digest);
delete[] key;
}
// Creat a user profile
bool MyPasswordManager::CreateProfile(string u, string p, iostream &mystream)
{
if (users.size() == 0)
AddUsersFromStream(mystream);
if (users.find(u) != users.end())
{
return false;
}
if (!mystream.good())
{
mystream.clear();
}
mystream.seekp(0, ios::end);
// create a random salt
unsigned char *salt = new unsigned char[SALT_LEN];
if (1 != RAND_bytes(salt, SALT_LEN))
handleErrors();
unsigned char *digest = new unsigned char[DIGEST_LEN];
CreateDigestFromPasswordAndSalt(p, salt, digest);
// write # of chars for username, the username, the salt & key
int userlen = u.size();
mystream.write(reinterpret_cast<const char *>(&userlen), sizeof(int));
mystream.write(u.c_str(), userlen);
mystream.write((char *)salt, SALT_LEN);
mystream.write((char *)digest, DIGEST_LEN);
// insert into users object
SaltDigest sd;
memcpy(sd.salt, salt, SALT_LEN);
memcpy(sd.digest, digest, DIGEST_LEN);
pair<string, SaltDigest> user_details(u, sd);
users.insert(user_details);
current_user = u;
delete[] digest;
delete[] salt;
return true;
}
具有digest_message函数的gentryption.cc。(这几乎直接从openssl取)
void digest_message(const unsigned char *message, size_t message_len, unsigned char **digest)
{
EVP_MD_CTX *mdctx;
// Create a Message Digest context
if ((mdctx = EVP_MD_CTX_create()) == NULL)
handleErrors();
// Initialise the context by identifying the algorithm to be used
if (1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL))
handleErrors();
// Provide the message whose digest needs to be calculated. Messages can be divided into sections and provided over a number of calls to the library if necessary
if (1 != EVP_DigestUpdate(mdctx, message, message_len))
handleErrors();
// Caclulate the digest
if ((*digest = (unsigned char *)OPENSSL_malloc(EVP_MD_size(EVP_sha256()))) == NULL)
handleErrors();
unsigned int *digest_len = nullptr;
if (1 != EVP_DigestFinal_ex(mdctx, *digest, digest_len))
handleErrors();
// Clean up the context if no longer required
EVP_MD_CTX_destroy(mdctx);
}
最后我的测试使用了Google_test
namespace
{
class CreateProfileTest : public testing::Test
{
public:
MyPasswordManagerTest() : existinguser_("ExistingUser1"),
existingpassword_("somepassword"),
digestlen_(32),
saltlen_(16) {}
protected:
MyPasswordManager mpm;
stringstream emptystream_;
stringstream existingstream_;
string existinguser_;
string existingpassword_;
unsigned char *existingsalt_;
unsigned char *digest_;
unsigned int digestlen_;
int saltlen_;
virtual void SetUp()
{
int keylen = 16;
int iter = 100000;
digest_ = new unsigned char[digestlen_];
existingsalt_ = new unsigned char[saltlen_];
unsigned char *key = new unsigned char[keylen];
unsigned int userlen = existinguser_.size();
char *pass = (char *)existingpassword_.c_str();
int passlen = existingpassword_.size();
if (1 != RAND_bytes(existingsalt_, saltlen_))
handleErrors();
if (1 != PKCS5_PBKDF2_HMAC_SHA1(pass, passlen, existingsalt_, saltlen_, iter, keylen, key))
handleErrors();
//hash the key
digest_message(key, keylen, &digest_);
cout << userlen << endl;
existingstream_.write(reinterpret_cast<const char *>(&userlen), sizeof(int));
existingstream_.write(existinguser_.c_str(), userlen);
existingstream_.write((char *)existingsalt_, saltlen_);
existingstream_.write((char *)digest_, digestlen_);
delete[] key;
}
virtual void TearDown()
{
delete[] existingsalt_;
delete[] digest_;
}
};
TEST_F(CreateProfileTest, DoesntAddUsersWhenTheyExistInStream)
{
EXPECT_FALSE(mpm.CreateProfile("ExistingUser1", "MasterPassword", existingstream_));
EXPECT_EQ(1, mpm.users.size());
EXPECT_NE(mpm.users.find("ExistingUser1"), mpm.users.end());
}
当您将用户名编码为流中时,您会占用该单词的长度,但是通过.c_str()
existingstream_.write(reinterpret_cast<const char *>(&userlen), sizeof(int));
existingstream_.write(existinguser_.c_str(), userlen);
.c_str()
添加了终止' '
,您可以通过纯词长度切断。只要您知道单词和传递数据的时间多长时间,那就很好。
当您在另一端组装时,您需要使用将长度参数的字符串构造器传递给它的字符串构造器。默认的构造函数将寻找终止" 0",有时甚至可能会有一个,这说明了为什么有时对您有用。因此,要确保字符串正确构造,您需要告诉构造函数用户名是多长时间的,例如:
pair<string, SaltDigest> user_details(std::string(username, (int)*userlen), sd);