From df149b65f6c7403316edaa6471af33ce39c165cd Mon Sep 17 00:00:00 2001 From: Alexandre Dulaunoy Date: Sun, 13 Dec 2009 19:19:24 +0100 Subject: [PATCH] A quick-and-dirty implementation of HOTP (RFC 4226) in Javascript. --- example.html | 33 +++++++++++++++++ hotp/hotp.js | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 example.html create mode 100644 hotp/hotp.js diff --git a/example.html b/example.html new file mode 100644 index 0000000..0e4e9b7 --- /dev/null +++ b/example.html @@ -0,0 +1,33 @@ + + + + + +HOTP Sample Test Page in Javascript + + + +

HOTP Sample Test Page in Javascript

+ + +
+
+demo of hotp-js - source code available at http://www.gitorious.org/hotp-js + + + diff --git a/hotp/hotp.js b/hotp/hotp.js new file mode 100644 index 0000000..af78bc2 --- /dev/null +++ b/hotp/hotp.js @@ -0,0 +1,101 @@ +/* + A simple Javascript HOTP implementation (HMAC-Based One-Time Password Algorithm) as described in RFC 4226. + + The library is relying on crypto-js (http://code.google.com/p/crypto-js/) for the javascript HMAC-SHA1 implementation. + + The library can be used to create software token (don't forget to protect the key of the token...). + + If you want to use the library, you'll need to load the crypto-js (sha1 and hmac) and hotp.js. + + Calling the library is easy, you just have to set the hex key of the token, the counter plus the output format. + + otp = hotp("3132333435363738393031323334353637383930","4","dec6"); + + Current output formats are : hex40 (format used by ootp, a free software library) and dec6 (the 6 decimal digit as described in the RFC 4226). + + A demo page with the test vector of the RFC 4226 : http://www.foo.be/hotp/example.html* + + http://www.gitorious.org/hotp-js/ + + Copyright (C) 2009 Alexandre Dulaunoy + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +*/ + + +function hotp(key, counter, format) { + + function hotp_hexkeytobytestream(s) { + // s is the key to be converted in bytes + var b = new Array(); + var last = s.length; + for (var i = 0; i < last; i = i + 2) { + var x = s[i] + s[i + 1]; + x.toUpperCase(); + x = "0x" + x; + x = parseInt(x); + b[i] = String.fromCharCode(x); + } + var ret = new String(); + ret = b.join(''); + return ret; + + } + function hotp_movingfactortohex(count) { + // count is the moving factor in OTP to be converted in bytes + v = decimaltohex(count, 16); + var decb = new Array(); + lhex = Crypto.util.hexToBytes(v); + for (var i = 0; i < lhex.length; i++) { + decb[i] = String.fromCharCode(lhex[i]); + } + var retval = new String(); + retval = decb.join(''); + return retval; + } + + function decimaltohex(d, padding) { + // d is the decimal value + // padding is the padding to apply (O pad) + var hex = Number(d).toString(16); + padding = typeof(padding) === "undefined" || padding === null ? padding = 2 : padding; + while (hex.length < padding) { + hex = "0" + hex; + } + return hex; + } + + function truncatedvalue(h, p) { + // h is the hash value + // p is precision + offset = h[19] & 0xf; + v = (h[offset] & 0x7f) << 24 | (h[offset + 1] & 0xff) << 16 | (h[offset + 2] & 0xff) << 8 | (h[offset + 3] & 0xff); + v = "" + v; + v = v.substr(v.length - p, p); + return v; + } + + var hmacBytes = Crypto.HMAC(Crypto.SHA1, Crypto.charenc.Binary.stringToBytes((hotp_movingfactortohex(counter))), Crypto.charenc.Binary.stringToBytes(hotp_hexkeytobytestream(key))); + + if (format == "hex40") { + return hmacBytes.substring(0, 10); + } else if (format == "dec6") { + return truncatedvalue(Crypto.util.hexToBytes(hmacBytes), 6); + } + else { + return "unknown format"; + } + +}