【问题标题】:What is wrong with this "StretchKey" implementation?这个“StretchKey”实现有什么问题?
【发布时间】:2009-08-24 15:42:01
【问题描述】:

我正在尝试创建此算法的 Delphi 版本:

void PWSfileV3::StretchKey(const unsigned char *salt, unsigned long saltLen,
                           const StringX &passkey,
                           unsigned int N, unsigned char *Ptag)
{
  /*
  * P' is the "stretched key" of the user's passphrase and the SALT, as defined
  * by the hash-function-based key stretching algorithm in
  * http://www.schneier.com/paper-low-entropy.pdf (Section 4.1), with SHA-256
  * as the hash function, and N iterations.
  */
  int passLen = 0;
  unsigned char *pstr = NULL;

  ConvertString(passkey, pstr, passLen);
  unsigned char *X = Ptag;
  SHA256 H0;
  H0.Update(pstr, passLen);
  H0.Update(salt, saltLen);
  H0.Final(X);

#ifdef UNICODE
  trashMemory(pstr, passLen);
  delete[] pstr;
#endif

  ASSERT(N >= MIN_HASH_ITERATIONS); // minimal value we're willing to use
  for (unsigned int i = 0; i < N; i++) {
    SHA256 H;
    // The 2nd param in next line was sizeof(X) in Beta-1
    // (bug #1451422). This change broke the ability to read beta-1
    // generated databases. If this is really needed, we should
    // hack the read functionality to try both variants (ugh).
    H.Update(X, SHA256::HASHLEN);
    H.Final(X);
  }
}

更新:(缺少功能)

void ConvertString(const StringX &text,
                   unsigned char *&txt,
                   int &txtlen)
{
  LPCTSTR txtstr = text.c_str(); 
  txtlen = text.length();

#ifndef UNICODE
  txt = (unsigned char *)txtstr; // don't delete[] (ugh)!!!
#else
#ifdef _WIN32
  txt = new unsigned char[3*txtlen]; // safe upper limit
  int len = WideCharToMultiByte(CP_ACP, 0, txtstr, txtlen,
    LPSTR(txt), 3*txtlen, NULL, NULL);
  ASSERT(len != 0);
#else
  mbstate_t mbs;
  memset(&mbs, '\0', sizeof(mbs));
  size_t len = wcsrtombs(NULL, &txtstr, 0, &mbs);
  txt = new unsigned char[len+1];
  len = wcsrtombs((char *)txt, &txtstr, len, &mbs);
  ASSERT(len != (size_t)-1);
#endif
  txtlen = len;
  txt[len] = '\0';
#endif /* UNICODE */
}

这是我得到的(D2009 版):

(请注意:T256BitArray 定义为 Array[0..31] of byte)

procedure StretchKey(Const Salt:T256BitArray; Const Passkey:string; Const Iterations:LongWord; Var KeyResult:T256BitArray);
var
   pStr : RawByteString;
   wHash : THash_sha256;
   loop : integer;
begin
  pStr := AnsiString(PassKey);

  wHash := THash_SHA256.Create;
  try
     wHash.Init;
     wHash.Calc(pStr[1], Length(pStr));
     wHash.Calc(Salt, Length(Salt));
     wHash.Done;
     PStr := wHash.DigestStr;
  finally
     FreeAndNil(wHash);
  end;

  for loop := 0 to Iterations-1 do
  begin
     wHash := THash_sha256.Create;
     try
        wHash.Init;
        wHash.Calc(PStr[1], wHash.DigestSize);
        wHash.Done;
        PStr := wHash.DigestStr;
     finally
        FreeAndNil(wHash);
     end;
  end;

  move(pStr[1], KeyResult, sizeof(KeyResult));
end;

原始代码 sn-p 来自 Password Safe 开源应用程序。

我正在尝试打开现有的密码保存 (v3) 数据库进行读取。

看来我做什么都没有关系,我无法让算法生成所需的哈希。

在上面的 Delphi sn-p 中,我使用的是 DEC v5.2 2009 组件集。我也试过 DCPcrypt 库。有趣的是,我从两个库中获得了相同的值,但没有任何结果与 PWSv3 文件中的哈希兼容。

我使用的 SHA256 组件都通过了 SHA256 测试向量哈希,所以我假设这是我在重新编码方法时做错了。

我错过了什么吗?

已解决:一切都是正确的。问题在于密钥字符串的转换。我发现我必须使用 WideCharToMultiByte 函数来获得正确的代码页转换。

【问题讨论】:

  • 在开始摆弄字符串类型和强制转换之前,请查看类似问题的答案:stackoverflow.com/questions/392657/md5-hashing-in-delphi-2009/…
  • @mghie:虽然我不会忽视这可能是问题的事实,但缺少的函数(我已添加)从原始代码转换字符串将字符串从 Unicode 转换为我相信是ANSI等效的。无论如何,我已经使用 fddling 运行了我所有的测试用例,以验证我通过已知的测试哈希得到了我应该得到的东西。我目前相当有信心我这样做是正确的。我在 D2007 中尝试过,但仍然没有任何乐趣。
  • @Ryan:如果你已经解决了你的问题,你应该将它作为答案提交并接受它。看看你是如何解决这个问题的,这对以后遇到类似问题的人可能会很有帮助。

标签: delphi delphi-2009 sha256


【解决方案1】:

下部的循环不应该有 wHash.Init;在里面。

【讨论】:

  • 为什么?使用前不需要初始化吗?每次循环都是一个全新的对象,就像在原始代码中一样。
  • @Rob:你是对的。在我看来,原始算法每次都使用一个新的组件实例。顺便说一句,我尝试将 init 移到循环之外,它也不起作用。
【解决方案2】:

我已经解决了这个问题。

我实现的一切都是正确的。问题在于密钥字符串的转换。我发现我绝对必须使用 WideCharToMultiByte 函数来获得正确的代码页转换。

以下是更正后的代码:

procedure StretchKey(Const Salt:T256BitArray; Const Passkey:string; Const Iterations:LongWord; Var KeyResult:T256BitArray);
var
   pStr : RawByteString;
   wHash : THash_sha256;
   loop : integer;
   wStr : AnsiString;
   wLen : integer;
begin
  wLen := 3*length(PassKey);
  SetLength(wStr, wLen);

  wLen := WideCharToMultiByte(GetACP, 0, PChar(PassKey), length(PassKey), PAnsiChar(wStr), wLen, nil, nil);
  SetLength(wStr, wLen);

  pStr := wStr;

  wHash := THash_SHA256.Create;
  try
     wHash.Init;
     wHash.Calc(pStr[1], Length(pStr));
     wHash.Calc(Salt, Length(Salt));
     wHash.Done;
     PStr := wHash.DigestStr;
  finally
     FreeAndNil(wHash);
  end;

  for loop := 0 to Iterations-1 do
  begin
     wHash := THash_sha256.Create;
     try
        wHash.Init;
        wHash.Calc(PStr[1], wHash.DigestSize);
        wHash.Done;
        PStr := wHash.DigestStr;
     finally
        FreeAndNil(wHash);
     end;
  end;

  move(pStr[1], KeyResult, sizeof(KeyResult));
end;

【讨论】:

    猜你喜欢
    • 2021-10-17
    • 2011-09-28
    • 2011-12-14
    • 2017-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-27
    • 2023-03-04
    相关资源
    最近更新 更多