diff --git a/lib/mk/vfs_gtotp.mk b/lib/mk/vfs_gtotp.mk deleted file mode 100644 index c5ac6bd..0000000 --- a/lib/mk/vfs_gtotp.mk +++ /dev/null @@ -1,7 +0,0 @@ -SRC_CC = vfs.cc -vpath %.cc $(REP_DIR)/src/lib/vfs/gtotp -SHARED_LIB = yes - -LIBS += cryptopp stdcxx - -CC_CXX_WARN_STRICT = diff --git a/src/app/gtotp_report/README b/src/app/gtotp_report/README new file mode 100644 index 0000000..7541faf --- /dev/null +++ b/src/app/gtotp_report/README @@ -0,0 +1,9 @@ +This component generates reports containing +Time-based One-time Passwords generated +using the common Google Authenticator algorithm. + +It is configured by a single XML attribute, the shared secret +encoded in base32. +! ... +! +! ... diff --git a/src/app/gtotp_report/component.cc b/src/app/gtotp_report/component.cc new file mode 100644 index 0000000..c9ce7ad --- /dev/null +++ b/src/app/gtotp_report/component.cc @@ -0,0 +1,193 @@ +/* + * \brief Google Time-based One-time Password Algorithm filesystem + * \author Emery Hemingway + * \date 2016-09-07 + * + * https://en.wikipedia.org/wiki/Google_Authenticator + */ + +/* + * Copyright (C) 2016-2018 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* Crypto++ includes */ +#include +#include + + +struct Main +{ + Genode::Env &env; + + Timer::Connection timer { env }; + Genode::Reporter reporter { env, "otp" }; + + CryptoPP::HMAC hmac; + + Timer::Periodic_timeout
update_timeout { + timer, *this, &Main::update, Genode::Microseconds(30*1000*1000) }; + + static Genode::uint64_t rtc_nonce(Genode::Env &env) + { + using namespace Genode; + typedef Genode::uint64_t uint64_t; + + Rtc::Timestamp const ts = Rtc::Connection(env).current_time();; + + enum { + PERIOD = 30 /* seconds */, + PERIODS_PER_MINUTE = 60 / PERIOD, + PERIODS_PER_HOUR = PERIODS_PER_MINUTE*60, + PERIODS_PER_DAY = PERIODS_PER_HOUR*24 + }; + + unsigned const m = (ts.month + 9) % 12; + unsigned const y = ts.year - m/10; + unsigned const days = + 365*(y) + y/4 - y/100 + y/400 + + (m*306 + 5)/10 + (ts.day - 1) - 719468; + + return + (uint64_t)days*PERIODS_PER_DAY + + (uint64_t)ts.hour*PERIODS_PER_HOUR + + (uint64_t)ts.minute*PERIODS_PER_MINUTE + + (uint64_t)ts.second/PERIOD; + } + + uint64_t const initial_nonce = rtc_nonce(env); + + void update(Genode::Duration dur) + { + uint64_t const nonce = + initial_nonce + dur.trunc_to_plain_ms().value / (30 * 1000); + + uint8_t nonce_digest[hmac.DigestSize()]; + uint8_t nonce_buf[8]; + + nonce_buf[0] = nonce >> (7 * 8) & 0xff; + nonce_buf[1] = nonce >> (6 * 8) & 0xff; + nonce_buf[2] = nonce >> (5 * 8) & 0xff; + nonce_buf[3] = nonce >> (4 * 8) & 0xff; + nonce_buf[4] = nonce >> (3 * 8) & 0xff; + nonce_buf[5] = nonce >> (2 * 8) & 0xff; + nonce_buf[6] = nonce >> (1 * 8) & 0xff; + nonce_buf[7] = nonce >> (0 * 8) & 0xff; + + hmac.Update(nonce_buf, sizeof(nonce_buf)); + hmac.Final(nonce_digest); + hmac.Restart(); + + unsigned const offset = + nonce_digest[sizeof(nonce_digest)-1] & 0x0F; + + uint32_t code; + code = (nonce_digest[offset+0]&0x7F) << (3 * 8); + code += nonce_digest[offset+1] << (2 * 8); + code += nonce_digest[offset+2] << (1 * 8); + code += nonce_digest[offset+3] << (0 * 8); + + code = code % 1000000; + + char buf[7] { 0 }; + buf[0] = 0x30 | (code/100000); + buf[1] = 0x30 | ((code/10000) % 10); + buf[2] = 0x30 | ((code/1000) % 10); + buf[3] = 0x30 | ((code/100) % 10); + buf[4] = 0x30 | ((code/10) % 10); + buf[5] = 0x30 | (code%10); + + { + using namespace Genode; + + Reporter::Xml_generator gen(reporter, [&] () { + gen.attribute("value", (char const *)buf); + }); + } + } + + Main(Genode::Env &env, uint8_t *secret, size_t secret_len) + : env(env), hmac(secret, secret_len) + { + reporter.enabled(true); + } +}; + + +void Libc::Component::construct(Libc::Env &env) +{ + using namespace Genode; + + enum { + BASE32_FACTOR = 256 / 32, + MAX_SECRET_BIN_LEN = 20, + MAX_SECRET_STR_LEN = MAX_SECRET_BIN_LEN * BASE32_FACTOR + }; + + size_t i = 0; + uint8_t binsec[MAX_SECRET_BIN_LEN]; + + { + Genode::uint8_t base32_table[256]; + + char const *upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + char const *lower = "abcdefghijklmnopqrstuvwxyz"; + + for (int i = 0; i < 256; ++i) + base32_table[i] = 0xFF; + + for (int i = 0; i < 32; ++i) + base32_table[(uint8_t)upper[i]] = i; + + for (int i = 0; i < 26; ++i) + base32_table[(uint8_t)lower[i]] = i; + + typedef String Secret_string; + + Secret_string secret_string; + + env.config([&] (Xml_node const &node) { + try { + node.attribute("secret").value(&secret_string); } + catch (Xml_node::Nonexistent_attribute) { + error("'secret' attribute missing from gtotp VFS node"); + throw; + } + }); + + if ((secret_string.length()-1) % 8) { + error("gtotp secret has a strange length ", secret_string.length()-1); + throw Exception(); + } + + char const *b32str = secret_string.string(); + + uint8_t buf[8]; + size_t j = 0; + while (j < secret_string.length()-1) { + for (size_t k = 0; k < 8; ++k) + buf[k] = base32_table[(uint8_t)b32str[j+k]]; + + binsec[i+0] = buf[0]<<3 | buf[1]>>2; + binsec[i+1] = buf[1]<<6 | buf[2]<<1 | buf[3] >>4; + binsec[i+2] = buf[3]<<4 | buf[4]>>1; + binsec[i+3] = buf[4]<<7 | buf[5]<<2 | buf[6]>>3; + binsec[i+4] = buf[6]<<5 | buf[7]; + + i += 5; + j += 8; + } + } + + static Main inst(env, binsec, i); +}; diff --git a/src/app/gtotp_report/target.mk b/src/app/gtotp_report/target.mk new file mode 100644 index 0000000..29acfe5 --- /dev/null +++ b/src/app/gtotp_report/target.mk @@ -0,0 +1,6 @@ +TARGET = gtotp_report +LIBS += base cryptopp stdcxx +SRC_CC += component.cc + +# Effc++ not compatible with CryptoPP headers +CC_CXX_WARN_STRICT = diff --git a/src/lib/vfs/gtotp/README b/src/lib/vfs/gtotp/README deleted file mode 100644 index eb655b9..0000000 --- a/src/lib/vfs/gtotp/README +++ /dev/null @@ -1,15 +0,0 @@ -This is a VFS plugin that generates Time-based One-time Passwords -using the common Google Authenticator algorithms. - -It is configured with two XML attributes, name and secret, -that is the file name and a base32 encoded shared secret. -! ... -! -! ... -! -! -! -! -! ... -! -! ... diff --git a/src/lib/vfs/gtotp/target.mk b/src/lib/vfs/gtotp/target.mk deleted file mode 100644 index 763cb22..0000000 --- a/src/lib/vfs/gtotp/target.mk +++ /dev/null @@ -1,4 +0,0 @@ -TARGET = gtotp_dummy -LIBS = vfs_gtotp - -CC_CXX_WARN_STRICT = diff --git a/src/lib/vfs/gtotp/vfs.cc b/src/lib/vfs/gtotp/vfs.cc deleted file mode 100644 index 749cf55..0000000 --- a/src/lib/vfs/gtotp/vfs.cc +++ /dev/null @@ -1,214 +0,0 @@ -/* - * \brief Google Time-based One-time Password Algorithm filesystem - * \author Emery Hemingway - * \date 2016-09-07 - * - * https://en.wikipedia.org/wiki/Google_Authenticator - */ - -/* - * Copyright (C) 2016 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - - -/* Genode includes */ -#include -#include -#include -#include -#include - -/* Crypto++ includes */ -#include -#include - - -namespace Vfs { class Gtotp_file_system; }; - -class Vfs::Gtotp_file_system : public Single_file_system -{ - private: - - CryptoPP::HMAC _hmac; - - Rtc::Connection &_rtc; - - public: - - Gtotp_file_system(Genode::Xml_node &node, - Rtc::Connection &rtc, - Genode::uint8_t *secret, - Genode::size_t secret_len) - : - Vfs::Single_file_system(NODE_TYPE_CHAR_DEVICE, "gtotp", node), - _hmac(secret, secret_len), _rtc(rtc) { } - - Stat_result stat(char const *path, Stat &out) override - { - Stat_result result = Single_file_system::stat(path, out); - out.size = 7; /* six decimal digits and newline */ - - return result; - } - - Read_result read(Vfs_handle *vfs_handle, - char *buf, file_size buf_size, - file_size &out) override - { - if ((vfs_handle->seek() != 0) || (buf_size < 7)) { - out = 0; - return READ_OK; - } - - using namespace Genode; - - enum { - PERIOD = 30 /* seconds */, - PERIODS_PER_MINUTE = 60 / PERIOD, - PERIODS_PER_HOUR = PERIODS_PER_MINUTE*60, - PERIODS_PER_DAY = PERIODS_PER_HOUR*24 - }; - - Rtc::Timestamp const ts = _rtc.current_time(); - - unsigned const m = (ts.month + 9) % 12; - unsigned const y = ts.year - m/10; - unsigned const days = - 365*(y) + y/4 - y/100 + y/400 + - (m*306 + 5)/10 + (ts.day - 1) - 719468; - - Genode::uint64_t const nonce = - days*PERIODS_PER_DAY + - ts.hour*PERIODS_PER_HOUR + - ts.minute*PERIODS_PER_MINUTE + - ts.second/PERIOD; - - uint8_t nonce_digest[_hmac.DigestSize()]; - uint8_t nonce_buf[8]; - - nonce_buf[0] = nonce >> (7 * 8) & 0xff; - nonce_buf[1] = nonce >> (6 * 8) & 0xff; - nonce_buf[2] = nonce >> (5 * 8) & 0xff; - nonce_buf[3] = nonce >> (4 * 8) & 0xff; - nonce_buf[4] = nonce >> (3 * 8) & 0xff; - nonce_buf[5] = nonce >> (2 * 8) & 0xff; - nonce_buf[6] = nonce >> (1 * 8) & 0xff; - nonce_buf[7] = nonce >> (0 * 8) & 0xff; - - _hmac.Update(nonce_buf, sizeof(nonce_buf)); - _hmac.Final(nonce_digest); - _hmac.Restart(); - - unsigned const offset = - nonce_digest[sizeof(nonce_digest)-1] & 0x0F; - - uint32_t code; - code = (nonce_digest[offset+0]&0x7F) << (3 * 8); - code += nonce_digest[offset+1] << (2 * 8); - code += nonce_digest[offset+2] << (1 * 8); - code += nonce_digest[offset+3] << (0 * 8); - - code = code % 1000000; - - buf[0] = 0x30 | (code/100000); - buf[1] = 0x30 | ((code/10000) % 10); - buf[2] = 0x30 | ((code/1000) % 10); - buf[3] = 0x30 | ((code/100) % 10); - buf[4] = 0x30 | ((code/10) % 10); - buf[5] = 0x30 | (code%10); - buf[6] = '\n'; - out = 7; - - return Read_result::READ_OK; - } - - Write_result write(Vfs_handle *vfs_handle, - char const *buf, file_size buf_size, - file_size &out_count) override { - return WRITE_ERR_INVALID; } -}; - - -struct Gtotp_file_system_factory : Vfs::File_system_factory -{ - Genode::uint8_t base32_table[256]; - - Gtotp_file_system_factory() - { - char const *upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; - char const *lower = "abcdefghijklmnopqrstuvwxyz"; - - for (int i = 0; i < 256; ++i) - base32_table[i] = 0xFF; - - for (int i = 0; i < 32; ++i) - base32_table[(uint8_t)upper[i]] = i; - - for (int i = 0; i < 26; ++i) - base32_table[(uint8_t)lower[i]] = i; - } - - Vfs::File_system *create(Genode::Env &env, - Genode::Allocator &alloc, - Genode::Xml_node node) override - { - static Rtc::Connection rtc(env); - - using namespace Genode; - typedef Genode::size_t size_t; - - enum { - BASE32_FACTOR = 256 / 32, - MAX_SECRET_BIN_LEN = 20, - MAX_SECRET_STR_LEN = MAX_SECRET_BIN_LEN * BASE32_FACTOR - }; - - typedef String Secret_string; - - Secret_string secret_string; - uint8_t binsec[MAX_SECRET_BIN_LEN]; - - try { node.attribute("secret").value(&secret_string); } - catch (Xml_node::Nonexistent_attribute) { - error("'secret' attribute missing from gtotp VFS node"); - throw; - } - - if ((secret_string.length()-1) % 8) { - error("gtotp secret has a strange length ", secret_string.length()-1); - throw Exception(); - } - - char const *b32str = secret_string.string(); - - uint8_t buf[8]; - size_t i = 0; - size_t j = 0; - while (j < secret_string.length()-1) { - for (size_t k = 0; k < 8; ++k) - buf[k] = base32_table[(uint8_t)b32str[j+k]]; - - binsec[i+0] = buf[0]<<3 | buf[1]>>2; - binsec[i+1] = buf[1]<<6 | buf[2]<<1 | buf[3] >>4; - binsec[i+2] = buf[3]<<4 | buf[4]>>1; - binsec[i+3] = buf[4]<<7 | buf[5]<<2 | buf[6]>>3; - binsec[i+4] = buf[6]<<5 | buf[7]; - - i += 5; - j += 8; - } - - return new (Genode::env()->heap()) - Vfs::Gtotp_file_system(node, rtc, binsec, i); - } -}; - - -extern "C" Vfs::File_system_factory *vfs_file_system_factory(void) -{ - static Gtotp_file_system_factory factory; - return &factory; -}