(解决方案)TL;DR: Google 假定密钥字符串是 base32 编码的;1
用I
和0
替换任何O
。这必须在散列之前解码。
原始问题
我很难让我的代码与 GA 匹配。我什至从当前时间步开始追逐 +/- ~100,000 的计数器,但一无所获。看到我的函数通过了 RFC 6238 附录中的 SHA-1 测试,我感到非常兴奋,但是当应用于“现实生活”时,它似乎失败了。
我什至在 Github 上查看了 Google Authenticator 的开源代码(这里)。我使用密钥进行测试:"qwertyuiopasdfgh"
. 根据 Github 代码:
/*
* Return key entered by user, replacing visually similar characters 1 and 0.
*/
private String getEnteredKey() {
String enteredKey = keyEntryField.getText().toString();
return enteredKey.replace('1', 'I').replace('0', 'O');
}
我相信我的密钥不会被修改。跟踪文件,似乎密钥通过调用保持不变:AuthenticatorActivity.saveSecret() -> AccountDb.add() -> AccountDb.newContentValuesWith()
.
我比较了三个来源之间的时间:
- (二郎壳):
now()
- (重击):
date "+%s"
- (谷歌/bash):
pattern="\s*date\:\s*"; curl -I https://www.google.com 2>/dev/null | grep -iE $pattern | sed -e "s/$pattern//g" | xargs -0 date "+%s" -d
他们都是一样的。尽管如此,我的手机似乎离我的电脑有点远。它将更改与我的计算机不同步的步骤。但是,我试图将正确的时间步长 +/- 数千次,但什么也没找到。根据NetworkTimeProvider
班级,这是应用程序的时间源。
此代码适用于 RFC 中的所有 SHA-1 测试:
totp(Secret, Time) ->
% {M, S, _} = os:timestamp(),
Msg = binary:encode_unsigned(Time), %(M*1000000+S) div 30,
%% Create 0-left-padded 64-bit binary from Time
Bin = <<0:((8-size(Msg))*8),Msg/binary>>,
%% Create SHA-1 hash
Hash = crypto:hmac(sha, Secret, Bin),
%% Determine dynamic offset
Offset = 16#0f band binary:at(Hash,19),
%% Ignore that many bytes and store 4 bytes into THash
<<_:Offset/binary, THash:4/binary, _/binary>> = Hash,
%% Remove sign bit and create 6-digit code
Code = (binary:decode_unsigned(THash) band 16#7fffffff) rem 1000000,
%% Convert to text-string and 0-lead-pad if necessary
lists:flatten(string:pad(integer_to_list(Code),6,leading,$0)).
为了使其真正匹配 RFC,需要针对上面的 8 位数字进行修改。我对其进行了修改以尝试追踪正确的步骤。目标是弄清楚我的时间是怎么错的。没有解决:
totp(_,_,_,0) ->
{ok, not_found};
totp(Secret,Goal,Ctr,Stop) ->
Msg = binary:encode_unsigned(Ctr),
Bin = <<0:((8-size(Msg))*8),Msg/binary>>,
Hash = crypto:hmac(sha, Secret, Bin),
Offset = 16#0f band binary:at(Hash,19),
<<_:Offset/binary, THash:4/binary, _/binary>> = Hash,
Code = (binary:decode_unsigned(THash) band 16#7fffffff) rem 1000000,
if Code =:= Goal ->
{ok, {offset, 2880 - Stop}};
true ->
totp(Secret,Goal,Ctr+1,Stop-1) %% Did another run with Ctr-1
end.
有什么明显的突出吗?