-
Notifications
You must be signed in to change notification settings - Fork 286
/
totp.html
224 lines (215 loc) · 9.2 KB
/
totp.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!-- TOTP Debugger
--
-- Copyright 2011 Google Inc.
-- Author: Markus Gutschke
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-->
<head>
<link rel="shortcut icon" href="http://code.google.com/p/google-authenticator/logo" type="image/png">
<title>TOTP Debugger</title>
<script src="https://utc-time.appspot.com"></script>
<!-- Returns a line of the form:
-- var timeskew = new Date().getTime() - XXX.XX;
-->
<script> <!--
// Given a secret key "K" and a timestamp "t" (in 30s units since the
// beginning of the epoch), return a TOTP code.
function totp(K,t) {
function sha1(C){
function L(x,b){return x<<b|x>>>32-b;}
var l=C.length,D=C.concat([1<<31]),V=0x67452301,W=0x88888888,
Y=271733878,X=Y^W,Z=0xC3D2E1F0;W^=V;
do D.push(0);while(D.length+1&15);D.push(32*l);
while (D.length){
var E=D.splice(0,16),a=V,b=W,c=X,d=Y,e=Z,f,k,i=12;
function I(x){var t=L(a,5)+f+e+k+E[x];e=d;d=c;c=L(b,30);b=a;a=t;}
for(;++i<77;)E.push(L(E[i]^E[i-5]^E[i-11]^E[i-13],1));
k=0x5A827999;for(i=0;i<20;I(i++))f=b&c|~b&d;
k=0x6ED9EBA1;for(;i<40;I(i++))f=b^c^d;
k=0x8F1BBCDC;for(;i<60;I(i++))f=b&c|b&d|c&d;
k=0xCA62C1D6;for(;i<80;I(i++))f=b^c^d;
V+=a;W+=b;X+=c;Y+=d;Z+=e;}
return[V,W,X,Y,Z];
}
var k=[],l=[],i=0,j=0,c=0;
for (;i<K.length;){
c=c*32+'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.
indexOf(K.charAt(i++).toUpperCase());
if((j+=5)>31)k.push(Math.floor(c/(1<<(j-=32)))),c&=31;}
j&&k.push(c<<(32-j));
for(i=0;i<16;++i)l.push(0x6A6A6A6A^(k[i]=k[i]^0x5C5C5C5C));
var s=sha1(k.concat(sha1(l.concat([0,t])))),o=s[4]&0xF;
return ((s[o>>2]<<8*(o&3)|(o&3?s[(o>>2)+1]>>>8*(4-o&3):0))&-1>>>1)%1000000;
}
// Periodically check whether we need to update the UI. It would be a little
// more efficient to only call this function as a direct result of
// significant state changes. But polling is cheap, and keeps the code a
// little easier.
var lastsecret,lastlabel,lastepochseconds,lastoverrideepoch;
var lasttimestamp,lastoverride,lastsearch;
function refresh() {
// Compute current TOTP code
var k=document.getElementById('secret').value.
replace(/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]/gi, '');
var d=document.getElementById('overrideepoch').value.replace(/[^0-9]/g, '');
if (d) e=parseInt(d); else e=Math.floor(new Date().getTime()/1000);
var t=Math.floor(e/30);
var s=document.getElementById('override').value.replace(/[^0-9]/g, '');
if (s) { t=parseInt(s); e=30*t; }
var label=escape(document.getElementById('label').value);
var search=document.getElementById('search').value;
// If TOTP code has changed (either because of user edits, or
// because of elapsed time), update the user interface.
if (k != lastsecret || label != lastlabel || e != lastepochseconds ||
d != lastoverrideepoch || t != lasttimestamp || s != lastoverride ||
search != lastsearch) {
if (d != lastoverrideepoch) {
document.getElementById('override').value = '';
s = '';
} else if (s != lastoverride) {
document.getElementById('overrideepoch').value = '';
d = '';
}
lastsecret=k;
lastlabel=label;
lastepochseconds=e;
lastoverrideepoch=d;
lasttimestamp=t;
lastoverride=s;
lastsearch=search;
var code=totp(k,t);
// Compute the OTPAuth URL and the associated QR code
var h='https://www.google.com/chart?chs=200x200&chld=M|0&'+
'cht=qr&chl=otpauth://totp/'+encodeURI(label)+'%3Fsecret%3D'+k;
var a=document.getElementById('authurl')
a.innerHTML='otpauth://totp/'+label+'?secret='+k;
a.href=h;
document.getElementById('aqr').href=h;
var q=document.getElementById('qr');
q.src=h;
q.alt=label+' '+k;
q.title=label+' '+k;
// Show the current time in seconds and in 30s increments since midnight
// Jan 1st, 1970. Optionally, let the user override this timestamp.
document.getElementById('epoch').innerHTML=e;
document.getElementById('ts').innerHTML=t;
// Show the current TOTP code.
document.getElementById('totp').innerHTML=code;
// If the user manually entered a TOTP code, try to find a matching code
// within a 25h window.
var result='';
if (search && !!(search=parseInt(search))) {
for (var i=0; i < 25*120; ++i) {
if (search == totp(k, t+(i&1?-Math.floor(i/2):Math.floor(i/2)))) {
if (i<2) {
result=' ';
break;
}
if (i >= 120) {
result=result + Math.floor(i/120) + 'h ';
i%=120;
}
if (i >= 4) {
result=result + Math.floor(i/4) + 'min ';
i%=4;
}
if (i&2) {
result=result + '30s ';
}
if (i&1) {
result='Code was valid ' + result + 'ago';
} else {
result='Code will be valid in ' + result;
}
break;
}
}
if (!result) {
result='No such code within a ±12h window';
}
}
document.getElementById('searchresult').innerHTML=result + ' ';
// If possible, compare the current time as reported by Javascript
// to "official" time as reported by AppEngine. If there is any significant
// difference, show a warning message. We always expect at least a minor
// time skew due to round trip delays, which we are not bothering to
// compensate for.
if (typeof timeskew != undefined) {
var ts=document.getElementById('timeskew');
if (Math.abs(timeskew) < 2000) {
ts.style.color='';
ts.innerHTML="Your computer's time is set correctly. TOTP codes " +
"will be computed accurately.";
} else if (Math.abs(timeskew) < 30000) {
ts.style.color='';
ts.innerHTML="Your computer's time is off by " +
(Math.round(Math.abs(timeskew)/1000)) + " seconds. This is within " +
"acceptable tolerances. Computed TOTP codes might be different " +
"from the ones in the mobile application, but they will be " +
"accepted by the server.";
} else {
ts.style.color='#dd0000';
ts.innerHTML="<b>Your computer's time is off by " +
(Math.round(Math.abs(timeskew)/1000)) + " seconds. Computed TOTP " +
"codes are probably incorrect.</b>";
}
}
}
}
--></script>
</head>
<body style="font-family: sans-serif" onload="setInterval(refresh, 100)">
<h1>TOTP Debugger</h1>
<table>
<tr><td colspan="7"><a style="text-decoration: none; color: black"
id="authurl"></a></td></tr>
<tr><td colspan="3">Enter secret key in BASE32: </td>
<td><input type="text" id="secret" /></td>
<td> </td>
<td rowspan="8"><a style="text-decoration: none" id="aqr">
<img id="qr" border="0"></a></td><td width="100%"> </td></tr>
<tr><td colspan="3">Account label: </td>
<td><input type="text" id="label" /></td></tr>
<tr><td>Interval: </td><td align="right">30s</td><td> </td></tr>
<tr><td>Time: </td><td align="right">
<span id="epoch"></span></td><td> </td><td>
<input type="text" id="overrideepoch" /></td></tr>
<tr><td>Timestamp: </td><td align="right"><span id="ts"></span></td>
<td> </td><td><input type="text" id="override" /></td></tr>
<tr><td>TOTP: </td><td align="right"><span id="totp"></span></td>
<td> </td><td><input type="text" id="search" /></td></tr>
<tr><td colspan="4" id="searchresult"></td></tr>
</table>
<br />
<div id="timeskew" style="width: 80%"></div>
<br />
<br />
<div style="width: 80%; color: #dd0000; font-size: small">
<p><b>WARNING!</b> This website is a development and debugging tool only.
Do <b>not</b> use it to generate security tokens for logging into your
account. You should never store, process or otherwise access the secret
key on the same machine that you use for accessing your account. Doing so
completely defeats the security of two-step verification.</p>
<p>Instead, use one of the mobile phone clients made available by the
<a href="https://github.com/google/google-authenticator">Google
Authenticator</a> project. Or follow the <a href="http://www.google.com/support/accounts/bin/static.py?hl=en&page=guide.cs&guide=1056283&topic=1056285">instructions</a>
provided by Google.</p>
<p>If you ever entered your real secret key into this website, please
immediately <a href="https://www.google.com/accounts/SmsAuthConfig">reset
your secret key</a>.</p>
</div>
</body>
</html>