core: add support of TOTP generation/validation (Time-based One-Time Password)
parent
172736989a
commit
d0ea801724
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <gcrypt.h>
|
||||
|
||||
#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.
|
||||
*
|
||||
|
|
|
@ -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 ();
|
||||
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* test-secure.cpp - test secured data functions
|
||||
*
|
||||
* Copyright (C) 2018 Sébastien Helleu <flashcode@flashtux.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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");
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue