diff --git a/ChangeLog.adoc b/ChangeLog.adoc index c04e331c3..c14bd9cb0 100644 --- a/ChangeLog.adoc +++ b/ChangeLog.adoc @@ -18,6 +18,10 @@ https://weechat.org/files/releasenotes/ReleaseNotes-devel.html[release notes] [[v2.4]] == Version 2.4 (under dev) +New features:: + + * api: add support of Time-based One-Time Password (TOTP), add infos "totp_generate" and "totp_validate" + Bug fixes:: * buflist: fix warning displayed when script buffers.pl is loaded (issue #1274) diff --git a/doc/en/weechat_dev.en.adoc b/doc/en/weechat_dev.en.adoc index cd6404a30..d2c7d71ae 100644 --- a/doc/en/weechat_dev.en.adoc +++ b/doc/en/weechat_dev.en.adoc @@ -389,6 +389,7 @@ WeeChat "core" is located in following directories: |          test-hook.cpp | Tests: hooks. |          test-infolist.cpp | Tests: infolists. |          test-list.cpp | Tests: lists. +|          test-secure.cpp | Tests: secured data. |          test-string.cpp | Tests: strings. |          test-url.cpp | Tests: URLs. |          test-utf8.cpp | Tests: UTF-8. diff --git a/doc/fr/weechat_dev.fr.adoc b/doc/fr/weechat_dev.fr.adoc index 5e86c8a7b..4fb2e60ff 100644 --- a/doc/fr/weechat_dev.fr.adoc +++ b/doc/fr/weechat_dev.fr.adoc @@ -391,6 +391,7 @@ Le cœur de WeeChat est situé dans les répertoires suivants : |          test-hook.cpp | Tests : hooks. |          test-infolist.cpp | Tests : infolists. |          test-list.cpp | Tests : listes. +|          test-secure.cpp | Tests : données sécurisées. |          test-string.cpp | Tests : chaînes. |          test-url.cpp | Tests : URLs. |          test-utf8.cpp | Tests : UTF-8. diff --git a/doc/ja/weechat_dev.ja.adoc b/doc/ja/weechat_dev.ja.adoc index 13e9eeaa2..fc611542c 100644 --- a/doc/ja/weechat_dev.ja.adoc +++ b/doc/ja/weechat_dev.ja.adoc @@ -399,6 +399,8 @@ WeeChat "core" は以下のディレクトリに配置されています: |          test-hook.cpp | Tests: hooks. |          test-infolist.cpp | テスト: インフォリスト |          test-list.cpp | テスト: リスト +// TRANSLATION MISSING +|          test-secure.cpp | Tests: secured data. |          test-string.cpp | テスト: 文字列 |          test-url.cpp | テスト: URL |          test-utf8.cpp | テスト: UTF-8 diff --git a/src/core/wee-secure.c b/src/core/wee-secure.c index c4382d3f9..b9abe62c9 100644 --- a/src/core/wee-secure.c +++ b/src/core/wee-secure.c @@ -25,6 +25,9 @@ #include #include +#include +#include +#include #include #include "weechat.h" @@ -494,6 +497,195 @@ secure_decrypt_data_not_decrypted (const char *passphrase) return num_ok; } +/* + * Generates a Time-based One-Time Password (TOTP), as described + * in the RFC 6238. + * + * Returns: + * 1: OK + * 0: error + */ + +int +secure_totp_generate_internal (const char *secret, int length_secret, + uint64_t moving_factor, int digits, + char *result) +{ + gcry_md_hd_t hd_md; + uint64_t moving_factor_swapped; + unsigned char *ptr_hash; + char hash[20]; + int offset, length; + unsigned long bin_code; + + if (gcry_md_open (&hd_md, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC) != 0) + return 0; + + if (gcry_md_setkey (hd_md, secret, length_secret) != 0) + { + gcry_md_close (hd_md); + return 0; + } + + moving_factor_swapped = (moving_factor >> 56) + | ((moving_factor << 40) & 0x00FF000000000000) + | ((moving_factor << 24) & 0x0000FF0000000000) + | ((moving_factor << 8) & 0x000000FF00000000) + | ((moving_factor >> 8) & 0x00000000FF000000) + | ((moving_factor >> 24) & 0x0000000000FF0000) + | ((moving_factor >> 40) & 0x000000000000FF00) + | (moving_factor << 56); + + gcry_md_write (hd_md, + &moving_factor_swapped, sizeof (moving_factor_swapped)); + + ptr_hash = gcry_md_read (hd_md, GCRY_MD_SHA1); + if (!ptr_hash) + { + gcry_md_close (hd_md); + return 0; + } + + memcpy (hash, ptr_hash, sizeof (hash)); + + gcry_md_close (hd_md); + + offset = hash[19] & 0xf; + bin_code = (hash[offset] & 0x7f) << 24 + | (hash[offset+1] & 0xff) << 16 + | (hash[offset+2] & 0xff) << 8 + | (hash[offset+3] & 0xff); + + bin_code %= (unsigned long)(pow (10, digits)); + + length = snprintf (result, digits + 1, "%.*lu", digits, bin_code); + if (length != digits) + return 0; + + return 1; +} + +/* + * Generates a Time-based One-Time Password (TOTP), as described + * in the RFC 6238. + * + * Returns the password as string, NULL if error. + * + * Note: result must be freed after use. + */ + +char * +secure_totp_generate (const char *secret_base32, time_t totp_time, int digits) +{ + char *result, *secret; + int length_secret, rc; + uint64_t moving_factor; + + secret = NULL; + result = NULL; + + if (!secret_base32 || !secret_base32[0] + || (digits < SECURE_TOTP_MIN_DIGITS) + || (digits > SECURE_TOTP_MAX_DIGITS)) + { + goto error; + } + + secret = malloc ((strlen (secret_base32) * 4) + 16 + 1); + if (!secret) + goto error; + + length_secret = string_decode_base32 (secret_base32, secret); + if (length_secret < 0) + goto error; + + result = malloc (digits + 1); + if (!result) + goto error; + + if (totp_time == 0) + totp_time = time (NULL); + + moving_factor = totp_time / 30; + + rc = secure_totp_generate_internal (secret, length_secret, + moving_factor, digits, result); + if (!rc) + goto error; + + free (secret); + + return result; + +error: + if (secret) + free (secret); + if (result) + free (result); + return NULL; +} + +/* + * Validates a Time-based One-Time Password (TOTP). + * + * Returns: + * 1: OTP is OK + * 0: OTP is invalid + */ + +int +secure_totp_validate (const char *secret_base32, time_t totp_time, int window, + const char *otp) +{ + char *secret, str_otp[16]; + int length_secret, digits, rc, otp_ok; + uint64_t i, moving_factor; + + secret = NULL; + + if (!secret_base32 || !secret_base32[0] || (window < 0) || !otp || !otp[0]) + goto error; + + digits = strlen (otp); + if ((digits < SECURE_TOTP_MIN_DIGITS) || (digits > SECURE_TOTP_MAX_DIGITS)) + goto error; + + secret = malloc (strlen (secret_base32) + 1); + if (!secret) + goto error; + + length_secret = string_decode_base32 (secret_base32, secret); + if (length_secret < 0) + goto error; + + if (totp_time == 0) + totp_time = time (NULL); + + moving_factor = totp_time / 30; + + otp_ok = 0; + + for (i = moving_factor - window; i <= moving_factor + window; i++) + { + rc = secure_totp_generate_internal (secret, length_secret, + i, digits, str_otp); + if (rc && (strcmp (str_otp, otp) == 0)) + { + otp_ok = 1; + break; + } + } + + free (secret); + + return otp_ok; + +error: + if (secret) + free (secret); + return 0; +} + /* * Initializes secured data. * diff --git a/src/core/wee-secure.h b/src/core/wee-secure.h index 4a2e9dc9d..f20a249fd 100644 --- a/src/core/wee-secure.h +++ b/src/core/wee-secure.h @@ -24,6 +24,8 @@ #define SECURE_SALT_DEFAULT "WeeChat!" #define SECURE_DATA_PASSPHRASE_FLAG "__passphrase__" #define SECURE_SALT_SIZE 8 +#define SECURE_TOTP_MIN_DIGITS 4 +#define SECURE_TOTP_MAX_DIGITS 10 enum t_secure_config_hash_algo { @@ -59,6 +61,10 @@ extern int secure_decrypt_data (const char *buffer, int length_buffer, const char *passphrase, char **decrypted, int *length_decrypted); extern int secure_decrypt_data_not_decrypted (const char *passphrase); +extern char *secure_totp_generate (const char *secret, time_t totp_time, + int digits); +extern int secure_totp_validate (const char *secret, time_t totp_time, + int window, const char *otp); extern int secure_init (); extern void secure_end (); diff --git a/src/core/wee-string.c b/src/core/wee-string.c index 9c525ff6c..8ff1de3de 100644 --- a/src/core/wee-string.c +++ b/src/core/wee-string.c @@ -2775,6 +2775,163 @@ string_decode_base16 (const char *from, char *to) return to_length; } +/* + * Encodes a string in base32. + * + * Argument "length" is number of bytes in "from" to convert (commonly + * strlen(from)). + * + * This function is inspired by: + * https://github.com/google/google-authenticator-libpam/blob/master/src/base32.c + * + * Original copyright: + * + * Copyright 2010 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. + * + * Returns length of string in "*to" (it does not count final \0). + */ + +int +string_encode_base32 (const char *from, int length, char *to) +{ + unsigned char base32_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + int count, value, next, bits_left, pad, index; + int length_padding[8] = { 0, 0, 6, 0, 4, 3, 0, 2 }; + + if (!from || !to) + return -1; + + count = 0; + + if (length > 0) + { + value = from[0]; + next = 1; + bits_left = 8; + while ((bits_left > 0) || (next < length)) + { + if (bits_left < 5) + { + if (next < length) + { + value <<= 8; + value |= from[next++] & 0xFF; + bits_left += 8; + } + else + { + pad = 5 - bits_left; + value <<= pad; + bits_left += pad; + } + } + index = 0x1F & (value >> (bits_left - 5)); + bits_left -= 5; + to[count++] = base32_table[index]; + } + } + pad = length_padding[count % 8]; + while (pad > 0) + { + to[count++] = '='; + pad--; + } + to[count] = '\0'; + + return count; +} + +/* + * Decodes a base32 string. + * + * This function is inspired by: + * https://github.com/google/google-authenticator-libpam/blob/master/src/base32.c + * + * Original copyright: + * + * Copyright 2010 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. + * + * + * Returns length of string in "*to" (it does not count final \0). + */ + +int +string_decode_base32 (const char *from, char *to) +{ + const char *ptr_from; + int value, bits_left, count; + unsigned char c; + + if (!from || !to) + return -1; + + ptr_from = from; + value = 0; + bits_left = 0; + count = 0; + + while (ptr_from[0]) + { + c = (unsigned char)ptr_from[0]; + value <<= 5; + if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) + { + c = (c & 0x1F) - 1; + } + else if ((c >= '2') && (c <= '7')) + { + c -= '2' - 26; + } + else if (c == '=') + { + /* padding */ + break; + } + else + { + /* invalid base32 char */ + return -1; + } + value |= c; + bits_left += 5; + if (bits_left >= 8) + { + to[count++] = value >> (bits_left - 8); + bits_left -= 8; + } + ptr_from++; + } + to[count] = '\0'; + + return count; +} + /* * Converts 3 bytes of 8 bits in 4 bytes of 6 bits. */ diff --git a/src/core/wee-string.h b/src/core/wee-string.h index 605aa1598..7e33352c8 100644 --- a/src/core/wee-string.h +++ b/src/core/wee-string.h @@ -107,6 +107,8 @@ extern int string_fprintf (FILE *file, const char *data, ...); extern char *string_format_size (unsigned long long size); extern void string_encode_base16 (const char *from, int length, char *to); extern int string_decode_base16 (const char *from, char *to); +extern int string_encode_base32 (const char *from, int length, char *to); +extern int string_decode_base32 (const char *from, char *to); extern void string_encode_base64 (const char *from, int length, char *to); extern int string_decode_base64 (const char *from, char *to); extern char *string_hex_dump (const char *data, int data_size, diff --git a/src/plugins/plugin-api.c b/src/plugins/plugin-api.c index e2702eeef..adbcdad9a 100644 --- a/src/plugins/plugin-api.c +++ b/src/plugins/plugin-api.c @@ -41,6 +41,7 @@ #include "../core/wee-infolist.h" #include "../core/wee-input.h" #include "../core/wee-proxy.h" +#include "../core/wee-secure.h" #include "../core/wee-string.h" #include "../core/wee-url.h" #include "../core/wee-util.h" @@ -897,6 +898,149 @@ plugin_api_info_uptime_cb (const void *pointer, void *data, return NULL; } +/* + * Returns WeeChat info "totp_generate": generates a Time-based One-Time + * Password (TOTP). + * + * Arguments: "secret,timestamp,digits" (timestamp and digits are optional). + */ + +const char * +plugin_api_info_totp_generate_cb (const void *pointer, void *data, + const char *info_name, + const char *arguments) +{ + static char value[32]; + char **argv, *ptr_secret, *error, *totp; + int argc, digits, length; + long number; + time_t totp_time; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) info_name; + + argv = NULL; + totp = NULL; + + if (!arguments || !arguments[0]) + goto error; + + argv = string_split (arguments, ",", 0, 0, &argc); + if (!argv || (argc < 1)) + goto error; + + ptr_secret = argv[0]; + totp_time = 0; + digits = 6; + + if (argc > 1) + { + error = NULL; + number = (int)strtol (argv[1], &error, 10); + if (!error || error[0] || (number < 0)) + goto error; + totp_time = (time_t)number; + } + if (argc > 2) + { + error = NULL; + number = (int)strtol (argv[2], &error, 10); + if (!error || error[0] || (number < 0)) + goto error; + digits = number; + } + + totp = secure_totp_generate (ptr_secret, totp_time, digits); + if (!totp) + goto error; + + length = snprintf (value, sizeof (value), "%s", totp); + if (length != digits) + goto error; + + string_free_split (argv); + free (totp); + + return value; + +error: + if (argv) + string_free_split (argv); + if (totp) + free (totp); + return NULL; +} + +/* + * Returns WeeChat info "totp_validate": validates a Time-based One-Time + * Password (TOTP). + * + * Arguments: "secret,otp,timestamp,window" (timestamp and window are optional). + */ + +const char * +plugin_api_info_totp_validate_cb (const void *pointer, void *data, + const char *info_name, + const char *arguments) +{ + static char value[16]; + char **argv, *ptr_secret, *ptr_otp, *error; + int argc, window, rc; + long number; + time_t totp_time; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) info_name; + + argv = NULL; + + if (!arguments || !arguments[0]) + goto error; + + argv = string_split (arguments, ",", 0, 0, &argc); + if (!argv || (argc < 2)) + goto error; + + ptr_secret = argv[0]; + ptr_otp = argv[1]; + totp_time = 0; + window = 0; + + if (argc > 2) + { + error = NULL; + number = (int)strtol (argv[2], &error, 10); + if (!error || error[0] || (number < 0)) + goto error; + totp_time = (time_t)number; + } + if (argc > 3) + { + error = NULL; + number = (int)strtol (argv[3], &error, 10); + if (!error || error[0] || (number < 0)) + goto error; + window = number; + } + + rc = secure_totp_validate (ptr_secret, totp_time, window, ptr_otp); + + snprintf (value, sizeof (value), "%d", rc); + + string_free_split (argv); + + return value; + +error: + if (argv) + string_free_split (argv); + return NULL; +} + /* * Returns WeeChat infolist "bar". * @@ -1983,6 +2127,19 @@ plugin_api_init () N_("\"days\" (number of days) or \"seconds\" (number of " "seconds) (optional)"), &plugin_api_info_uptime_cb, NULL, NULL); + hook_info (NULL, "totp_generate", + N_("generate a Time-based One-Time Password (TOTP)"), + N_("secret (in base32), timestamp (optional, current time by " + "default), number of digits (optional, between 4 and 10, " + "6 is default and recommended value)"), + &plugin_api_info_totp_generate_cb, NULL, NULL); + hook_info (NULL, "totp_validate", + N_("validate a Time-based One-Time Password (TOTP): 1 if TOTP " + "is correct, otherwise 0"), + N_("secret (in base32), one-time password, " + "timestamp (optional), number of OTP after/before to test " + "(optional, 0 by default)"), + &plugin_api_info_totp_validate_cb, NULL, NULL); /* WeeChat core infolist hooks */ hook_infolist (NULL, "bar", diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8d2ee9511..2d0fe7a1f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -32,6 +32,7 @@ set(LIB_WEECHAT_UNIT_TESTS_SRC unit/core/test-hook.cpp unit/core/test-infolist.cpp unit/core/test-list.cpp + unit/core/test-secure.cpp unit/core/test-string.cpp unit/core/test-url.cpp unit/core/test-utf8.cpp diff --git a/tests/tests.cpp b/tests/tests.cpp index 3fc351fa6..cd6f44614 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -62,6 +62,7 @@ IMPORT_TEST_GROUP(CoreHdata); IMPORT_TEST_GROUP(CoreHook); IMPORT_TEST_GROUP(CoreInfolist); IMPORT_TEST_GROUP(CoreList); +IMPORT_TEST_GROUP(CoreSecure); IMPORT_TEST_GROUP(CoreString); IMPORT_TEST_GROUP(CoreUrl); IMPORT_TEST_GROUP(CoreUtf8); diff --git a/tests/unit/core/test-secure.cpp b/tests/unit/core/test-secure.cpp new file mode 100644 index 000000000..831544660 --- /dev/null +++ b/tests/unit/core/test-secure.cpp @@ -0,0 +1,137 @@ +/* + * test-secure.cpp - test secured data functions + * + * Copyright (C) 2018 Sébastien Helleu + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see . + */ + +#include "CppUTest/TestHarness.h" + +extern "C" +{ +#include "src/core/wee-secure.h" +} + +#define TOTP_SECRET "secretpasswordbase32" + +#define WEE_CHECK_TOTP_GENERATE(__result, __secret, __time, __digits) \ + totp = secure_totp_generate (__secret, __time, __digits); \ + if (__result == NULL) \ + { \ + POINTERS_EQUAL(NULL, totp); \ + } \ + else \ + { \ + STRCMP_EQUAL(__result, totp); \ + free (totp); \ + } +#define WEE_CHECK_TOTP_VALIDATE(__result, __secret, __time, __window, \ + __otp) \ + LONGS_EQUAL(__result, secure_totp_validate (__secret, __time, \ + __window, __otp)); + +TEST_GROUP(CoreSecure) +{ +}; + +/* + * Tests functions: + * secure_totp_generate + */ + +TEST(CoreSecure, TotpGenerate) +{ + char *totp; + + /* invalid secret */ + WEE_CHECK_TOTP_GENERATE(NULL, NULL, 0, 6); + WEE_CHECK_TOTP_GENERATE(NULL, "", 0, 6); + WEE_CHECK_TOTP_GENERATE(NULL, "not_in_base32_0189", 0, 6); + + /* invalid number of digits (must be between 4 and 10) */ + WEE_CHECK_TOTP_GENERATE(NULL, TOTP_SECRET, 0, 3); + WEE_CHECK_TOTP_GENERATE(NULL, TOTP_SECRET, 0, 11); + + /* TOTP with 6 digits */ + WEE_CHECK_TOTP_GENERATE("065486", TOTP_SECRET, 1540624066, 6); + WEE_CHECK_TOTP_GENERATE("640073", TOTP_SECRET, 1540624085, 6); + WEE_CHECK_TOTP_GENERATE("725645", TOTP_SECRET, 1540624110, 6); + + /* TOTP with 7 digits */ + WEE_CHECK_TOTP_GENERATE("0065486", TOTP_SECRET, 1540624066, 7); + WEE_CHECK_TOTP_GENERATE("6640073", TOTP_SECRET, 1540624085, 7); + WEE_CHECK_TOTP_GENERATE("4725645", TOTP_SECRET, 1540624110, 7); + + /* TOTP with 8 digits */ + WEE_CHECK_TOTP_GENERATE("40065486", TOTP_SECRET, 1540624066, 8); + WEE_CHECK_TOTP_GENERATE("16640073", TOTP_SECRET, 1540624085, 8); + WEE_CHECK_TOTP_GENERATE("94725645", TOTP_SECRET, 1540624110, 8); +} + +/* + * Tests functions: + * secure_totp_validate + */ + +TEST(CoreSecure, TotpValidate) +{ + /* invalid secret */ + WEE_CHECK_TOTP_VALIDATE(0, NULL, 0, 0, "123456"); + WEE_CHECK_TOTP_VALIDATE(0, "", 0, 0, "123456"); + WEE_CHECK_TOTP_VALIDATE(0, "not_in_base32_0189", 0, 0, "123456"); + + /* invalid window (must be ≥ 0) */ + WEE_CHECK_TOTP_VALIDATE(0, TOTP_SECRET, 0, -1, "123456"); + + /* invalid OTP */ + WEE_CHECK_TOTP_VALIDATE(0, TOTP_SECRET, 0, 0, NULL); + WEE_CHECK_TOTP_VALIDATE(0, TOTP_SECRET, 0, 0, ""); + + /* validation error (wrong OTP) */ + WEE_CHECK_TOTP_VALIDATE(0, TOTP_SECRET, 1540624110, 0, "065486"); + WEE_CHECK_TOTP_VALIDATE(0, TOTP_SECRET, 1540624110, 1, "065486"); + + /* TOTP with 6 digits */ + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624066, 0, "065486"); + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624085, 0, "640073"); + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624110, 0, "725645"); + + /* TOTP with 7 digits */ + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624066, 0, "0065486"); + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624085, 0, "6640073"); + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624110, 0, "4725645"); + + /* TOTP with 7 digits */ + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624066, 0, "40065486"); + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624085, 0, "16640073"); + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624110, 0, "94725645"); + + /* TOTP with 6 digits, using window */ + WEE_CHECK_TOTP_VALIDATE(0, TOTP_SECRET, 1540624110, 0, "065486"); + WEE_CHECK_TOTP_VALIDATE(0, TOTP_SECRET, 1540624110, 1, "065486"); + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624110, 2, "065486"); + + /* TOTP with 7 digits, using window */ + WEE_CHECK_TOTP_VALIDATE(0, TOTP_SECRET, 1540624110, 0, "0065486"); + WEE_CHECK_TOTP_VALIDATE(0, TOTP_SECRET, 1540624110, 1, "0065486"); + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624110, 2, "0065486"); + + /* TOTP with 8 digits, using window */ + WEE_CHECK_TOTP_VALIDATE(0, TOTP_SECRET, 1540624110, 0, "40065486"); + WEE_CHECK_TOTP_VALIDATE(0, TOTP_SECRET, 1540624110, 1, "40065486"); + WEE_CHECK_TOTP_VALIDATE(1, TOTP_SECRET, 1540624110, 2, "40065486"); +} diff --git a/tests/unit/core/test-string.cpp b/tests/unit/core/test-string.cpp index 7fcc0ed7a..068ea1ed4 100644 --- a/tests/unit/core/test-string.cpp +++ b/tests/unit/core/test-string.cpp @@ -1335,6 +1335,94 @@ TEST(CoreString, FormatSize) * Tests functions: * string_encode_base16 * string_decode_base16 + */ + +TEST(CoreString, Base16) +{ + char str[1024]; + + /* string_encode_base16 */ + string_encode_base16 (NULL, 0, NULL); + string_encode_base16 (NULL, 0, str); + string_encode_base16 ("", 0, NULL); + str[0] = 0xAA; + string_encode_base16 ("", -1, str); + BYTES_EQUAL(0x0, str[0]); + str[0] = 0xAA; + string_encode_base16 ("", 0, str); + BYTES_EQUAL(0x0, str[0]); + string_encode_base16 ("abc", 3, str); + STRCMP_EQUAL("616263", str); + + /* string_decode_base16 */ + LONGS_EQUAL(0, string_decode_base16 (NULL, NULL)); + LONGS_EQUAL(0, string_decode_base16 (NULL, str)); + LONGS_EQUAL(0, string_decode_base16 ("", NULL)); + LONGS_EQUAL(0, string_decode_base16 ("", str)); + LONGS_EQUAL(3, string_decode_base16 ("616263", str)); + STRCMP_EQUAL("abc", str); +} + +/* + * Tests functions: + * string_encode_base32 + * string_decode_base32 + */ + +TEST(CoreString, Base32) +{ + int i, length; + char str[1024]; + const char *str_base32[][2] = + { { "", "" }, + { "A", "IE======" }, + { "B", "II======" }, + { "C", "IM======" }, + { "D", "IQ======" }, + { "abc", "MFRGG===" }, + { "This is a test.", "KRUGS4ZANFZSAYJAORSXG5BO" }, + { "This is a test..", "KRUGS4ZANFZSAYJAORSXG5BOFY======" }, + { "This is a test...", "KRUGS4ZANFZSAYJAORSXG5BOFYXA====" }, + { "This is a test....", "KRUGS4ZANFZSAYJAORSXG5BOFYXC4===" }, + { "This is a long long long sentence here...", + "KRUGS4ZANFZSAYJANRXW4ZZANRXW4ZZANRXW4ZZAONSW45DFNZRWKIDIMVZGKLRO" + "FY======" }, + { NULL, NULL } }; + + /* string_encode_base32 */ + LONGS_EQUAL(-1, string_encode_base32 (NULL, 0, NULL)); + LONGS_EQUAL(-1, string_encode_base32 (NULL, 0, str)); + LONGS_EQUAL(-1, string_encode_base32 ("", 0, NULL)); + str[0] = 0xAA; + LONGS_EQUAL(0, string_encode_base32 ("", -1, str)); + BYTES_EQUAL(0x0, str[0]); + str[0] = 0xAA; + LONGS_EQUAL(0, string_encode_base32 ("", 0, str)); + BYTES_EQUAL(0x0, str[0]); + for (i = 0; str_base32[i][0]; i++) + { + length = strlen (str_base32[i][1]); + LONGS_EQUAL(length, string_encode_base32 (str_base32[i][0], + strlen (str_base32[i][0]), + str)); + STRCMP_EQUAL(str_base32[i][1], str); + } + + /* string_decode_base32 */ + LONGS_EQUAL(-1, string_decode_base32 (NULL, NULL)); + LONGS_EQUAL(-1, string_decode_base32 (NULL, str)); + LONGS_EQUAL(-1, string_decode_base32 ("", NULL)); + LONGS_EQUAL(0, string_decode_base32 ("", str)); + for (i = 0; str_base32[i][0]; i++) + { + length = strlen (str_base32[i][0]); + LONGS_EQUAL(length, string_decode_base32 (str_base32[i][1], str)); + STRCMP_EQUAL(str_base32[i][0], str); + } +} + +/* + * Tests functions: * string_encode_base64 * string_decode_base64 */