diff --git a/Makefile.am b/Makefile.am index 4393bb8a..73107231 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,8 @@ UTIL_FILES = \ hash/fletcher.hpp \ hash/hmac.cpp \ hash/hmac.hpp \ + hash/hotp.cpp \ + hash/hotp.hpp \ hash/md2.cpp \ hash/md2.hpp \ hash/md4.cpp \ @@ -241,6 +243,7 @@ TEST_BIN = \ test/fixed \ test/float \ test/hmac \ + test/hotp \ test/hton \ test/ip \ test/json_types \ diff --git a/hash/hotp.cpp b/hash/hotp.cpp new file mode 100644 index 00000000..c1831f66 --- /dev/null +++ b/hash/hotp.cpp @@ -0,0 +1,75 @@ +/* + * This file is part of libgim. + * + * libgim is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * libgim 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with libgim. If not, see . + * + * Copyright 2015 Danny Robson + */ + +#include "hotp.hpp" + +#include "../endian.hpp" + +#include + +using util::hash::HOTP; + + +//----------------------------------------------------------------------------- +HOTP::HOTP (const char *_key, uint64_t _counter): + HOTP (_key, strlen (_key), _counter) +{ ; } + + +//----------------------------------------------------------------------------- +HOTP::HOTP (const void *_key, size_t _len, uint64_t _counter): + m_counter (_counter), + m_hash ((const uint8_t*)_key, _len) +{ ; } + + +//----------------------------------------------------------------------------- +unsigned +HOTP::value (void) +{ + auto c = htob (m_counter); + + m_hash.update (&c, sizeof (c)); + m_hash.finish (); + + auto h = m_hash.digest (); + auto t = truncate (h); + + m_hash.reset (); + ++m_counter; + + return t % 1000000; +} + + +//----------------------------------------------------------------------------- +uint32_t +HOTP::truncate (SHA1::digest_t d) +{ + // offset into the digest by the last 4 bits + size_t o= d[d.size () - 1] & 0x0F; + + // mask the highest bit per the specification + uint32_t v = (d[o + 0] & 0x7f) << 24 | + (d[o + 1] & 0xff) << 16 | + (d[o + 2] & 0xff) << 8 | + (d[o + 3] & 0xff) << 0; + + return v; +} diff --git a/hash/hotp.hpp b/hash/hotp.hpp new file mode 100644 index 00000000..a0fcd50f --- /dev/null +++ b/hash/hotp.hpp @@ -0,0 +1,46 @@ +/* + * This file is part of libgim. + * + * libgim is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * libgim 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with libgim. If not, see . + * + * Copyright 2015 Danny Robson + */ + +#ifndef __UTIL_HASH_HOTP_HPP +#define __UTIL_HASH_HOTP_HPP + +#include "hmac.hpp" +#include "sha1.hpp" + +#include + +namespace util { namespace hash { + /// HMAC one-time password (RFC 4226) + class HOTP { + public: + HOTP (const char *key, uint64_t counter); + HOTP (const void *key, size_t len, uint64_t counter); + + unsigned value (void); + uint64_t counter (void) const; + + private: + uint32_t truncate (SHA1::digest_t); + + uint64_t m_counter; + HMAC m_hash; + }; +} } + +#endif diff --git a/test/hotp.cpp b/test/hotp.cpp new file mode 100644 index 00000000..fc52c438 --- /dev/null +++ b/test/hotp.cpp @@ -0,0 +1,28 @@ +#include "hash/hotp.hpp" + +#include "types.hpp" +#include "debug.hpp" + +using util::hash::HOTP; + +int +main (int, char**) +{ + HOTP h ("12345678901234567890", 0); + + const unsigned EXPECTED[] = { + 755224, + 287082, + 359152, + 969429, + 338314, + 254676, + 287922, + 162583, + 399871, + 520489, + }; + + for (size_t i = 0; i < elems (EXPECTED); ++i) + CHECK_EQ (EXPECTED[i], h.value ()); +}