C unordered_map String键在存在时并不总是找到?字符串v char

c++ unordered_map string key not always found when it exists? string v char?

本文关键字:char 字符串 map unordered String 存在      更新时间:2023-10-16

我在编写的单元测试方面遇到了麻烦。基本上,我正在存储用户名称 盐 来自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);