1 /* 2 * OTP tests 3 * (C) 2017 Jack Lloyd 4 * 5 * Botan is released under the Simplified BSD License (see license.txt) 6 */ 7 8 #include "tests.h" 9 10 #if defined(BOTAN_HAS_HOTP) && defined(BOTAN_HAS_TOTP) 11 #include <botan/parsing.h> 12 #include <botan/otp.h> 13 #include <botan/hash.h> 14 #include <botan/calendar.h> 15 #endif 16 17 namespace Botan_Tests { 18 19 #if defined(BOTAN_HAS_HOTP) && defined(BOTAN_HAS_TOTP) 20 21 class HOTP_KAT_Tests final : public Text_Based_Test 22 { 23 public: HOTP_KAT_Tests()24 HOTP_KAT_Tests() 25 : Text_Based_Test("otp/hotp.vec", "Key,Digits,Counter,OTP") 26 {} 27 clear_between_callbacks() const28 bool clear_between_callbacks() const override { return false; } 29 run_one_test(const std::string & hash_algo,const VarMap & vars)30 Test::Result run_one_test(const std::string& hash_algo, const VarMap& vars) override 31 { 32 Test::Result result("HOTP " + hash_algo); 33 34 std::unique_ptr<Botan::HashFunction> hash_test = Botan::HashFunction::create(hash_algo); 35 if(!hash_test) 36 return {result}; 37 38 const std::vector<uint8_t> key = vars.get_req_bin("Key"); 39 const uint32_t otp = static_cast<uint32_t>(vars.get_req_sz("OTP")); 40 const uint64_t counter = vars.get_req_sz("Counter"); 41 const size_t digits = vars.get_req_sz("Digits"); 42 43 Botan::HOTP hotp(key, hash_algo, digits); 44 45 result.test_int_eq("OTP", hotp.generate_hotp(counter), otp); 46 47 std::pair<bool, uint64_t> otp_res = hotp.verify_hotp(otp, counter, 0); 48 result.test_eq("OTP verify result", otp_res.first, true); 49 result.confirm("OTP verify next counter", otp_res.second == counter + 1); 50 51 // Test invalid OTP 52 otp_res = hotp.verify_hotp(otp + 1, counter, 0); 53 result.test_eq("OTP verify result", otp_res.first, false); 54 result.confirm("OTP verify next counter", otp_res.second == counter); 55 56 // Test invalid OTP with long range 57 otp_res = hotp.verify_hotp(otp + 1, counter, 100); 58 result.test_eq("OTP verify result", otp_res.first, false); 59 result.confirm("OTP verify next counter", otp_res.second == counter); 60 61 // Test valid OTP with long range 62 otp_res = hotp.verify_hotp(otp, counter - 90, 100); 63 result.test_eq("OTP verify result", otp_res.first, true); 64 result.confirm("OTP verify next counter", otp_res.second == counter + 1); 65 66 return result; 67 } 68 }; 69 70 BOTAN_REGISTER_TEST("otp", "otp_hotp", HOTP_KAT_Tests); 71 72 class TOTP_KAT_Tests final : public Text_Based_Test 73 { 74 public: TOTP_KAT_Tests()75 TOTP_KAT_Tests() 76 : Text_Based_Test("otp/totp.vec", "Key,Digits,Timestep,Timestamp,OTP") 77 {} 78 clear_between_callbacks() const79 bool clear_between_callbacks() const override { return false; } 80 run_one_test(const std::string & hash_algo,const VarMap & vars)81 Test::Result run_one_test(const std::string& hash_algo, const VarMap& vars) override 82 { 83 Test::Result result("TOTP " + hash_algo); 84 85 std::unique_ptr<Botan::HashFunction> hash_test = Botan::HashFunction::create(hash_algo); 86 if(!hash_test) 87 return {result}; 88 89 const std::vector<uint8_t> key = vars.get_req_bin("Key"); 90 const uint32_t otp = static_cast<uint32_t>(vars.get_req_sz("OTP")); 91 const size_t digits = vars.get_req_sz("Digits"); 92 const size_t timestep = vars.get_req_sz("Timestep"); 93 const std::string timestamp = vars.get_req_str("Timestamp"); 94 95 Botan::TOTP totp(key, hash_algo, digits, timestep); 96 97 std::chrono::system_clock::time_point time = from_timestring(timestamp); 98 std::chrono::system_clock::time_point later_time = time + std::chrono::seconds(timestep); 99 std::chrono::system_clock::time_point too_late = time + std::chrono::seconds(2*timestep); 100 101 result.test_int_eq("TOTP generate", totp.generate_totp(time), otp); 102 103 result.test_eq("TOTP verify valid", totp.verify_totp(otp, time, 0), true); 104 result.test_eq("TOTP verify invalid", totp.verify_totp(otp ^ 1, time, 0), false); 105 result.test_eq("TOTP verify time slip", totp.verify_totp(otp, later_time, 0), false); 106 result.test_eq("TOTP verify time slip allowed", totp.verify_totp(otp, later_time, 1), true); 107 result.test_eq("TOTP verify time slip out of range", totp.verify_totp(otp, too_late, 1), false); 108 109 return result; 110 } 111 112 private: from_timestring(const std::string & time_str)113 std::chrono::system_clock::time_point from_timestring(const std::string& time_str) 114 { 115 if(time_str.size() != 19) 116 throw Test_Error("Invalid TOTP timestamp string " + time_str); 117 // YYYY-MM-DDTHH:MM:SS 118 // 0123456789012345678 119 const uint32_t year = Botan::to_u32bit(time_str.substr(0, 4)); 120 const uint32_t month = Botan::to_u32bit(time_str.substr(5, 2)); 121 const uint32_t day = Botan::to_u32bit(time_str.substr(8, 2)); 122 const uint32_t hour = Botan::to_u32bit(time_str.substr(11, 2)); 123 const uint32_t minute = Botan::to_u32bit(time_str.substr(14, 2)); 124 const uint32_t second = Botan::to_u32bit(time_str.substr(17, 2)); 125 return Botan::calendar_point(year, month, day, hour, minute, second).to_std_timepoint(); 126 } 127 }; 128 129 BOTAN_REGISTER_TEST("otp", "otp_totp", TOTP_KAT_Tests); 130 131 #endif 132 133 } 134 135 136