erlang - 将 TOTP 实现与 Google Authenticator 匹配
问题描述
(解决方案)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.
有什么明显的突出吗?
解决方案
我很想制作自己的 Android 应用程序来为我的项目实施 TOTP。我确实继续查看 Java 代码。借助下载 git 存储库并grep -R
查找函数调用,我发现了我的问题。要获得与 Google Authenticator 相同的密码,密钥被假定为 base32 编码,并且必须在将其传递给哈希算法之前对其进行解码。
getEnteredKey()
通过替换0
and字符来暗示这一点,1
因为这些在 base32 字母表中不存在。
推荐阅读
- python - os.walk 在 Mac 和 Linux 上的不同文件夹排序?
- vba - CallByName 与 MsgBox 一起使用,但在分配值时不可用(“需要对象”错误)
- python - Numpy数组:如何逐行检查前X个值是否有效?
- sql-server - SQL Server 2016 遇到内存不足或会话超过服务器中允许的最大值
- javascript - 用php编写js文件时删除换行符
- javascript - 动态开放街图
- spring - 来自 liquibase 配置的 JPA 实体类生成器
- javascript - (jQuery) 在移动设备上调用函数,但 jQuery 动画不起作用
- node.js - Node JS Promise 拒绝异常
- javascript - 将 React 组件的 HTML 发送到服务器