Codebase list libcotp / 8e8ee65
Merge changes from 1.2.8-1 Francisco Vilmar Cardoso Ruviaro 1 year, 2 months ago
13 changed file(s) with 460 addition(s) and 1051 deletion(s). Raw diff Collapse all Expand all
1111 mkdir build && cd "$_"
1212 cmake -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_TESTING=ON ..
1313 make && make install
14 ./tests/test_base32encode
15 ./tests/test_base32decode
1614 ./tests/test_cotp
1715
1816 ubuntu:
2624 mkdir build && cd "$_"
2725 cmake -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_TESTING=ON ..
2826 make && make install
29 ./tests/test_base32encode
30 ./tests/test_base32decode
3127 ./tests/test_cotp
3228
3329 workflows:
3531 build:
3632 jobs:
3733 - debian
38 - ubuntu
34 - ubuntu
35
0 cmake_minimum_required(VERSION 3.16)
1 project(cotp VERSION "2.0.0" LANGUAGES "C")
0 cmake_minimum_required(VERSION 3.5)
1 project(cotp)
22
33 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
44
55 include(GNUInstallDirs)
66
77 find_package(PkgConfig REQUIRED)
8 find_package(Gcrypt 1.8.0 REQUIRED)
8 find_package(Gcrypt 1.6.0 REQUIRED)
9 pkg_check_modules(BASEENCODE REQUIRED baseencode>=1.0.12)
910
1011 include_directories(${GCRYPT_INCLUDE_DIR} ${BASEENCODE_INCLUDE_DIRS})
1112
1415 enable_testing()
1516 add_subdirectory(tests)
1617
18 # set up versioning.
19 set(BUILD_MAJOR "1")
20 set(BUILD_MINOR "2")
21 set(BUILD_VERSION "8")
22 set(BUILD_VERSION ${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_VERSION})
23
1724 set(CMAKE_C_STANDARD 11)
1825
19 set(COTP_HEADERS
20 src/cotp.h
21 )
22 set(SOURCE_FILES
23 src/otp.c
24 src/utils/base32.c
25 )
26 set(COTP_HEADERS src/cotp.h)
27 set(SOURCE_FILES src/otp.c)
2628
27 # Set compiler flags for all targets
28 add_compile_options(-Wall -Wextra -O3 -Wformat=2 -Wmissing-format-attribute -fstack-protector-strong -Wundef -Wmissing-format-attribute
29 -fdiagnostics-color=always -Wstrict-prototypes -Wunreachable-code -Wchar-subscripts -Wwrite-strings -Wpointer-arith -Wbad-function-cast
30 -Wcast-align -Werror=format-security -Werror=implicit-function-declaration -Wno-sign-compare -Wno-format-nonliteral -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3)
31
32 add_link_options(-Wl,--no-add-needed -Wl,--as-needed -Wl,-z,relro,-z,now)
29 set(CMAKE_C_FLAGS "-Wall -Wextra -O3 -Wno-format-truncation -fstack-protector-strong -fPIC")
30 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3")
3331
3432 add_library(cotp SHARED ${SOURCE_FILES})
3533
36 target_link_libraries(cotp ${GCRYPT_LIBRARIES})
34 target_link_libraries(cotp ${GCRYPT_LIBRARIES} ${BASEENCODE_LIBRARIES})
3735
38 set_target_properties(cotp PROPERTIES VERSION ${CMAKE_PROJECT_VERSION} SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR})
36 set_target_properties(cotp PROPERTIES VERSION ${BUILD_VERSION} SOVERSION ${BUILD_MAJOR}${BUILD_MINOR})
3937
4038 set(COTP_LIB_DIR "${CMAKE_INSTALL_LIBDIR}")
4139 set(COTP_INC_DIR "${CMAKE_INSTALL_INCLUDEDIR}")
66 C library that generates TOTP and HOTP according to [RFC-6238](https://tools.ietf.org/html/rfc6238)
77
88 ## Requirements
9 - [libbaseencode](https://github.com/paolostivanin/libbaseencode)
910 - GCC/Clang and CMake to build the library
1011 - libgcrypt
1112
2122
2223 ## How To Use It
2324 ```
24 char *totp = get_totp (const char *base32_encoded_secret,
25 int digits,
26 int period,
27 int algo,
28 cotp_error_t *err);
25 char *totp = get_totp (const char *base32_encoded_secret, int digits, int period, int algo, cotp_error_t *err);
26 free (totp);
2927
30 char *steam_totp = get_steam_totp (const char *secret,
31 int period,
32 cotp_error_t *err);
28 char *steam_totp = get_steam_totp (const char *secret, int period, cotp_error_t *err)
3329
34 char *hotp = get_hotp (const char *base32_encoded_secret,
35 long counter,
36 int digits,
37 int algo,
38 cotp_error_t *err);
30 char *hotp = get_hotp (const char *base32_encoded_secret, long counter, int digits, int algo, cotp_error_t *err);
31 free (hotp);
3932
40 char *totp_at = get_totp_at (const char *base32_encoded_secret,
41 long target_date,
42 int digits,
43 int algo,
44 cotp_error_t *err);
33 char *get_totp_at (const char *base32_encoded_secret, long target_date, int digits, int algo, cotp_error_t *err)
4534
46 int64_t otp_i = otp_to_int (const char *otp,
47 cotp_error_t *err_code);
35 int is_valid = totp_verify (const har *base32_encoded_secret, const char *totp, int digits, int period, int algo, cotp_error_t *err);
36
37 int is_valid = hotp_verify (const char *base32_encoded_secret, long counter, digits, char *hotp, int algo, cotp_error_t *err);
4838 ```
4939
5040 where:
5343 - `digits` is between `3` and `10` inclusive
5444 - `period` is between `1` and `120` inclusive
5545 - `counter` is a value decided with the server
56 - `target_date` is the target date specified as the **unix epoch format in seconds**
46 - `target_date` is the target date specified as the unix epoch format in seconds
5747 - `algo` is either `SHA1`, `SHA256` or `SHA512`
5848
59 ## Return values
60 `get_totp`, `get_hotp` and `get_totp_at` return `NULL` if an error occurs and `err` is set to one of the following values:
61
62 Errors:
49 ## Errors
50 `get_totp`, `get_hotp` and `get_totp_at` return `NULL` if an error occurs and `err` is set accordingly. The following errors are currently supported:
6351 - `GCRYPT_VERSION_MISMATCH`, set if the installed Gcrypt library is too old
6452 - `INVALID_B32_INPUT`, set if the given input is not valid base32 text
6553 - `INVALID_ALGO`, set if the given algo is not supported by the library
6654 - `INVALID_PERIOD`, set if `period` is `<= 0` or `> 120` seconds
67 - `INVALID_DIGITS`, set if `digits` is `< 4` or `> 10`
68 - `MEMORY_ALLOCATION_ERROR`, set if an error happened during memory allocation
69 - `INVALID_USER_INPUT`, set if the given input is not valid
70 - `INVALID_COUNTER`, set if `counter` is `< 0`
55 - `INVALID_DIGITS`, set if `digits` is `< 3` or `> 10`
7156
72 All good:
73 - `NO_ERROR`, set if no error occurred
74 - `VALID`, set if the given OTP is valid
57 `totp_verify` and `hotp_verify` can return, in addition to one of the previous code, also the error `INVALID_OTP` if the given OTP doesn't match the computed one.
7558
76 The function `otp_to_int`:
77 * returns `-1` if an error occurs and sets `err` to `INVALID_USER_INPUT`.
78 * warns the user if the leading zero is missing. For example, since the otp string `"012345"` **can't** be returned as the integer `012345` (because it would be interpreted as octal number), the function returns `12345` and sets `err` to `MISSING_LEADING_ZERO`)
59 In case of success, the value returned by `get_totp`, `get_hotp` and `get_totp_at` **must be freed** once no longer needed.
7960
80 In case of success, the value returned by `get_totp`, `get_hotp`, `get_totp_at` and `get_steam_totp` **must be freed** once no longer needed.
81
82 # Base32 encoding and decoding
83 Since release 2.0.0, libbaseencode has been merged with libcotp. This means that you can now use base32 functions by just including `cotp.h`:
84
85 ```
86 char *base32_encode (const uchar *user_data,
87 size_t data_len,
88 cotp_error_t *err_code);
89
90 uchar *base32_decode (const char *user_data,
91 size_t data_len,
92 cotp_error_t *err_code);
93 ```
94
95 where:
96 - `user_data` is the data to be encoded/decoded
97 - `data_len` is the length of the data to be encoded/decoded
98 - `err_code` is where the error is stored
99
100 `base32_encode` returns `NULL` if an error occurs and `err_code` is set to one of the following values:
101 - `INVALID_USER_INPUT`, set if the given input is not valid
102 - `MEMORY_ALLOCATION_ERROR`, set if an error happened during memory allocation
103 - `INVALID_USER_INPUT`, set if the given input is not valid
104
105 `base32_decode` returns `NULL` if an error occurs and `err_code` is set to one of the following values:
106 - `INVALID_USER_INPUT`, set if the given input is not valid
107 - `MEMORY_ALLOCATION_ERROR`, set if an error happened during memory allocation
108 - `INVALID_B32_INPUT`, set if the given input is not valid base32 text
109 - `INVALID_USER_INPUT`, set if the given input is not valid
110
111 Both functions return and empty string if the input is an empty string. In such a case, `err` is set to `EMPTY_STRING`.
44 The following list describes whether a version is eligible or not for security updates.
55
66 | Version | Supported | EOL |
7 |---------| ------------------ |-------------|
8 | 2.0.0 | :heavy_check_mark: | - |
9 | 1.2.x | :heavy_check_mark: | 30-Jun-2023 |
7 | ------- | ------------------ |-------------|
8 | 1.2.x | :heavy_check_mark: | - |
109 | 1.1.x | :x: | 31-Dec-2021 |
1110 | 1.0.x | :x: | 31-Dec-2021 |
1211
44
55 Name: libcotp
66 Description: C library that generates TOTP and HOTP
7 Version: @CMAKE_PROJECT_VERSION@
7 Version: @BUILD_VERSION@
88 URL: URL: https://github.com/paolostivanin/libcotp
99 Libs: -L${libdir} -lcotp
1010 Cflags: -I${includedir}
66 * SONAME change: libcotp12 -> libcotp2.
77
88 -- Francisco Vilmar Cardoso Ruviaro <vilmar@debian.org> Tue, 10 Jan 2023 12:36:19 +0000
9
10 libcotp (1.2.8-1) unstable; urgency=medium
11
12 * New upstream version 1.2.8. (Closes: #1028305)
13
14 -- Francisco Vilmar Cardoso Ruviaro <vilmar@debian.org> Mon, 16 Jan 2023 21:01:00 +0000
915
1016 libcotp (1.2.7-1) unstable; urgency=medium
1117
00 #pragma once
11 #include <gcrypt.h>
2 #include <stdint.h>
32
43 #define SHA1 GCRY_MD_SHA1
54 #define SHA256 GCRY_MD_SHA256
65 #define SHA512 GCRY_MD_SHA512
76
8 #define MIN_DIGTS 4
9 #define MAX_DIGITS 10
10
11 typedef enum cotp_error {
12 NO_ERROR = 0,
13 VALID,
14 GCRYPT_VERSION_MISMATCH,
15 INVALID_B32_INPUT,
16 INVALID_ALGO,
17 INVALID_DIGITS,
18 INVALID_PERIOD,
19 MEMORY_ALLOCATION_ERROR,
20 INVALID_USER_INPUT,
21 EMPTY_STRING,
22 MISSING_LEADING_ZERO,
23 INVALID_COUNTER
7 typedef enum _cotp_errno {
8 VALID = 0,
9 GCRYPT_VERSION_MISMATCH = 1,
10 INVALID_B32_INPUT = 2,
11 INVALID_ALGO = 3,
12 INVALID_OTP = 4,
13 INVALID_DIGITS = 5,
14 INVALID_PERIOD = 6
2415 } cotp_error_t;
25
26 typedef unsigned char uchar;
2716
2817 #ifdef __cplusplus
2918 extern "C" {
3019 #endif
20 char *get_hotp (const char *base32_encoded_secret,
21 long counter,
22 int digits,
23 int sha_algo,
24 cotp_error_t *err_code);
3125
32 char *base32_encode (const uchar *user_data,
33 size_t data_len,
34 cotp_error_t *err_code);
26 char *get_totp (const char *base32_encoded_secret,
27 int digits,
28 int period,
29 int sha_algo,
30 cotp_error_t *err_code);
3531
36 uchar *base32_decode (const char *user_data_untrimmed,
37 size_t data_len,
38 cotp_error_t *err_code);
32 char *get_steam_totp (const char *base32_encoded_secret,
33 int period,
34 cotp_error_t *err_code);
3935
40 char *get_hotp (const char *base32_encoded_secret,
41 long counter,
42 int digits,
43 int sha_algo,
44 cotp_error_t *err_code);
4536
46 char *get_totp (const char *base32_encoded_secret,
47 int digits,
48 int period,
49 int sha_algo,
50 cotp_error_t *err_code);
37 char *get_totp_at (const char *base32_encoded_secret,
38 long time,
39 int digits,
40 int period,
41 int sha_algo,
42 cotp_error_t *err_code);
5143
52 char *get_steam_totp (const char *base32_encoded_secret,
53 int period,
54 cotp_error_t *err_code);
44 char *get_steam_totp_at (const char *base32_encoded_secret,
45 long timestamp,
46 int period,
47 cotp_error_t *err_code);
5548
56 char *get_totp_at (const char *base32_encoded_secret,
57 long time,
58 int digits,
59 int period,
60 int sha_algo,
61 cotp_error_t *err_code);
49 int totp_verify (const char *base32_encoded_secret,
50 const char *user_totp,
51 int digits,
52 int period,
53 int sha_algo);
6254
63 char *get_steam_totp_at (const char *base32_encoded_secret,
64 long timestamp,
65 int period,
66 cotp_error_t *err_code);
55 int hotp_verify (const char *base32_encoded_secret,
56 long counter,
57 int digits,
58 const char *user_hotp,
59 int sha_algo);
6760
68 int64_t otp_to_int (const char *otp,
69 cotp_error_t *err_code);
70
61
7162 #ifdef __cplusplus
7263 }
7364 #endif
11 #include <time.h>
22 #include <string.h>
33 #include <gcrypt.h>
4 #include <ctype.h>
4 #include <baseencode.h>
55 #include "cotp.h"
66
7 static int check_gcrypt (void);
8
9 static char *normalize_secret (const char *K);
10
11 static char *get_steam_code (const uchar *hmac);
12
13 static int truncate (const uchar *hmac,
14 int digits_length,
15 int algo);
16
17 static uchar *compute_hmac (const char *K,
18 long C,
19 int algo);
20
21 static char *finalize (int digits_length,
22 int tk);
23
24 static int check_period (int period);
25
26 static int check_otp_len (int digits_length);
27
28 static int check_algo (int algo);
29
30
31 char *
32 get_hotp (const char *secret,
33 long counter,
34 int digits,
35 int algo,
36 cotp_error_t *err_code)
37 {
38 if (check_gcrypt () == -1) {
7 #define SHA1_DIGEST_SIZE 20
8 #define SHA256_DIGEST_SIZE 32
9 #define SHA512_DIGEST_SIZE 64
10
11 static long long int DIGITS_POWER[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000};
12
13
14 static int
15 check_gcrypt()
16 {
17 if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
18 if (!gcry_check_version("1.6.0")) {
19 fprintf(stderr, "libgcrypt v1.6.0 and above is required\n");
20 return -1;
21 }
22 gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
23 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
24 }
25 return 0;
26 }
27
28
29 static char *
30 normalize_secret (const char *K)
31 {
32 char *nK = calloc (1, strlen (K) + 1);
33 if (nK == NULL) {
34 fprintf (stderr, "Error during memory allocation\n");
35 return nK;
36 }
37
38 int i = 0, j = 0;
39 while (K[i] != '\0') {
40 if (K[i] != ' ') {
41 if (K[i] >= 'a' && K[i] <= 'z') {
42 nK[j++] = (char) (K[i] - 32);
43 } else {
44 nK[j++] = K[i];
45 }
46 }
47 i++;
48 }
49 return nK;
50 }
51
52
53 static char *
54 get_steam_code(unsigned const char *hmac)
55 {
56 int offset = (hmac[SHA1_DIGEST_SIZE-1] & 0x0f);
57
58 // Starting from the offset, take the successive 4 bytes while stripping the topmost bit to prevent it being handled as a signed integer
59 int bin_code = ((hmac[offset] & 0x7f) << 24) | ((hmac[offset + 1] & 0xff) << 16) | ((hmac[offset + 2] & 0xff) << 8) | ((hmac[offset + 3] & 0xff));
60
61 const char steam_alphabet[] = "23456789BCDFGHJKMNPQRTVWXY";
62
63 char code[6];
64 size_t steam_alphabet_len = strlen(steam_alphabet);
65 for (int i = 0; i < 5; i++) {
66 int mod = (int)(bin_code % steam_alphabet_len);
67 bin_code = (int)(bin_code / steam_alphabet_len);
68 code[i] = steam_alphabet[mod];
69 }
70 code[5] = '\0';
71
72 return strdup(code);
73 }
74
75
76 static int
77 truncate(unsigned const char *hmac, int digits_length, int algo)
78 {
79 // take the lower four bits of the last byte
80 int offset = hmac[gcry_md_get_algo_dlen (algo) - 1] & 0x0f;
81
82 // Starting from the offset, take the successive 4 bytes while stripping the topmost bit to prevent it being handled as a signed integer
83 int bin_code = ((hmac[offset] & 0x7f) << 24) | ((hmac[offset + 1] & 0xff) << 16) | ((hmac[offset + 2] & 0xff) << 8) | ((hmac[offset + 3] & 0xff));
84
85 int token = (int)(bin_code % DIGITS_POWER[digits_length]);
86
87 return token;
88 }
89
90
91 static unsigned char *
92 compute_hmac(const char *K, long C, int algo)
93 {
94 baseencode_error_t err;
95 size_t secret_len = (size_t)((strlen(K) + 1.6 - 1) / 1.6);
96
97 char *normalized_K = normalize_secret (K);
98 if (normalized_K == NULL) {
99 return NULL;
100 }
101 unsigned char *secret = base32_decode(normalized_K, strlen(normalized_K), &err);
102 free (normalized_K);
103 if (secret == NULL) {
104 return NULL;
105 }
106
107 unsigned char C_reverse_byte_order[8];
108 int j, i;
109 for (j = 0, i = 7; j < 8 && i >= 0; j++, i--)
110 C_reverse_byte_order[i] = ((unsigned char *) &C)[j];
111
112 gcry_md_hd_t hd;
113 gpg_error_t gpg_err = gcry_md_open (&hd, algo, GCRY_MD_FLAG_HMAC);
114 if (gpg_err) {
115 printf("%s\n", "Error while opening the cipher handle.");
116 return NULL;
117 }
118 gpg_err = gcry_md_setkey (hd, secret, secret_len);
119 if (gpg_err) {
120 printf("%s\n", "Error while setting the cipher key.");
121 gcry_md_close (hd);
122 return NULL;
123 }
124 gcry_md_write (hd, C_reverse_byte_order, sizeof(C_reverse_byte_order));
125 gcry_md_final (hd);
126
127 unsigned char * hmac_tmp = gcry_md_read (hd, algo);
128 if (hmac_tmp == NULL) {
129 fprintf(stderr, "Error getting digest\n");
130 gcry_md_close (hd);
131 return NULL;
132 }
133
134 size_t dlen = gcry_md_get_algo_dlen(algo);
135 unsigned char *hmac = malloc (dlen);
136 if (hmac == NULL) {
137 perror("Error allocating memory");
138 gcry_md_close (hd);
139 return NULL;
140 }
141 memcpy (hmac, hmac_tmp, dlen);
142
143 free (secret);
144
145 gcry_md_close (hd);
146
147 return hmac;
148 }
149
150
151 static char *
152 finalize(int digits_length, int tk)
153 {
154 char *token = malloc((size_t)digits_length + 1);
155 if (token == NULL) {
156 fprintf (stderr, "Error during memory allocation\n");
157 return token;
158 } else {
159 int extra_char = digits_length < 10 ? 0 : 1;
160 char *fmt = calloc(1, 5 + extra_char);
161 if (fmt == NULL) {
162 fprintf (stderr, "Error during memory allocation\n");
163 free (token);
164 return fmt;
165 }
166 memcpy (fmt, "%.", 3);
167 snprintf (fmt + 2, 2 + extra_char, "%d", digits_length);
168 memcpy (fmt + 3 + extra_char, "d", 2);
169 snprintf (token, digits_length + 1, fmt, tk);
170 free (fmt);
171 }
172 return token;
173 }
174
175
176 static int
177 check_period(int period)
178 {
179 if (period <= 0 || period > 120) {
180 return INVALID_PERIOD;
181 }
182 return VALID;
183 }
184
185
186 static int
187 check_otp_len(int digits_length)
188 {
189 if (digits_length < 3 || digits_length > 10) {
190 return INVALID_DIGITS;
191 }
192 return VALID;
193 }
194
195
196 static int
197 check_algo(int algo)
198 {
199 if (algo != SHA1 && algo != SHA256 && algo != SHA512) {
200 return INVALID_ALGO;
201 } else {
202 return VALID;
203 }
204 }
205
206
207 char *
208 get_hotp(const char *secret, long timestamp, int digits, int algo, cotp_error_t *err_code)
209 {
210 if (check_gcrypt() == -1) {
39211 *err_code = GCRYPT_VERSION_MISMATCH;
40212 return NULL;
41213 }
42214
43 if (check_algo (algo) == INVALID_ALGO) {
215 if (check_algo(algo) == INVALID_ALGO) {
44216 *err_code = INVALID_ALGO;
45217 return NULL;
46218 }
47219
48 if (check_otp_len (digits) == INVALID_DIGITS) {
220 if (check_otp_len(digits) == INVALID_DIGITS) {
49221 *err_code = INVALID_DIGITS;
50222 return NULL;
51223 }
52224
53 if (counter < 0) {
54 *err_code = INVALID_COUNTER;
55 return NULL;
56 }
57
58 unsigned char *hmac = compute_hmac (secret, counter, algo);
225 unsigned char *hmac = compute_hmac(secret, timestamp, algo);
59226 if (hmac == NULL) {
60227 *err_code = INVALID_B32_INPUT;
61228 return NULL;
62229 }
63230
64 int tk = truncate (hmac, digits, algo);
65 free (hmac);
66
67 *err_code = NO_ERROR;
68
69 return finalize (digits, tk);
70 }
71
72
73 char *
74 get_totp_at (const char *secret,
75 long current_timestamp,
76 int digits,
77 int period,
78 int algo,
79 cotp_error_t *err_code)
80 {
81 if (check_gcrypt () == -1) {
82 *err_code = GCRYPT_VERSION_MISMATCH;
83 return NULL;
84 }
85
86 if (check_otp_len (digits) == INVALID_DIGITS) {
87 *err_code = INVALID_DIGITS;
88 return NULL;
89 }
90
91 if (check_period (period) == INVALID_PERIOD) {
92 *err_code = INVALID_PERIOD;
93 return NULL;
94 }
95
96 long timestamp = current_timestamp / period;
97
98 cotp_error_t err;
99 char *totp = get_hotp (secret, timestamp, digits, algo, &err);
100 if (err != NO_ERROR && err != VALID) {
101 *err_code = err;
102 return NULL;
103 }
104
105 return totp;
106 }
107
108
109 char *
110 get_totp (const char *secret,
111 int digits,
112 int period,
113 int algo,
114 cotp_error_t *err_code)
115 {
116 return get_totp_at (secret, (long)time(NULL), digits, period, algo, err_code);
117 }
118
119
120 char *
121 get_steam_totp (const char *secret,
122 int period,
123 cotp_error_t *err_code)
231 int tk = truncate(hmac, digits, algo);
232 char *token = finalize(digits, tk);
233
234 free(hmac);
235 return token;
236 }
237
238
239 char *
240 get_totp(const char *secret, int digits, int period, int algo, cotp_error_t *err_code)
241 {
242 return get_totp_at(secret, (long)time(NULL), digits, period, algo, err_code);
243 }
244
245
246 char *
247 get_steam_totp (const char *secret, int period, cotp_error_t *err_code)
124248 {
125249 // AFAIK, the secret is stored base64 encoded on the device. As I don't have time to waste on reverse engineering
126250 // this non-standard solution, the user is responsible for decoding the secret in whatever format this is and then
130254
131255
132256 char *
133 get_steam_totp_at (const char *secret,
134 long current_timestamp,
135 int period,
136 cotp_error_t *err_code)
137 {
138 if (check_gcrypt () == -1) {
257 get_totp_at(const char *secret, long current_timestamp, int digits, int period, int algo, cotp_error_t *err_code)
258 {
259 if (check_gcrypt() == -1) {
139260 *err_code = GCRYPT_VERSION_MISMATCH;
140261 return NULL;
141262 }
142263
143 if (check_period (period) == INVALID_PERIOD) {
264 if (check_otp_len(digits) == INVALID_DIGITS) {
265 *err_code = INVALID_DIGITS;
266 return NULL;
267 }
268
269 if (check_period(period) == INVALID_PERIOD) {
144270 *err_code = INVALID_PERIOD;
145271 return NULL;
146272 }
147273
148274 long timestamp = current_timestamp / period;
149275
150 unsigned char *hmac = compute_hmac (secret, timestamp, SHA1);
276 cotp_error_t err;
277 char *token = get_hotp(secret, timestamp, digits, algo, &err);
278 if (token == NULL) {
279 *err_code = err;
280 return NULL;
281 }
282 return token;
283 }
284
285
286 char *
287 get_steam_totp_at (const char *secret, long current_timestamp, int period, cotp_error_t *err_code)
288 {
289 if (check_gcrypt() == -1) {
290 *err_code = GCRYPT_VERSION_MISMATCH;
291 return NULL;
292 }
293
294 if (check_period(period) == INVALID_PERIOD) {
295 *err_code = INVALID_PERIOD;
296 return NULL;
297 }
298
299 long timestamp = current_timestamp / period;
300
301 unsigned char *hmac = compute_hmac(secret, timestamp, SHA1);
151302 if (hmac == NULL) {
152303 *err_code = INVALID_B32_INPUT;
153304 return NULL;
154305 }
155306
156 char *totp = get_steam_code (hmac);
307 char * totp = get_steam_code(hmac);
157308
158309 free(hmac);
159
160310 return totp;
161311 }
162312
163313
164 int64_t
165 otp_to_int (const char *otp,
166 cotp_error_t *err_code)
167 {
168 size_t len = strlen (otp);
169 if (len < MIN_DIGTS || len > MAX_DIGITS) {
170 *err_code = INVALID_USER_INPUT;
171 return -1;
172 }
173
174 if (otp[0] == '0') {
175 *err_code = MISSING_LEADING_ZERO;
176 }
177
178 return strtoll (otp, NULL, 10);
179 }
180
181
182 static int
183 check_gcrypt (void)
184 {
185 if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) {
186 if (!gcry_check_version ("1.8.0")) {
187 fprintf (stderr, "libgcrypt v1.8.0 and above is required\n");
188 return -1;
189 }
190 gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
191 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
192 }
193 return 0;
194 }
195
196
197 static char *
198 normalize_secret (const char *K)
199 {
200 char *nK = calloc (strlen (K) + 1, 1);
201 if (nK == NULL) {
202 fprintf (stderr, "Error during memory allocation\n");
203 return nK;
204 }
205 for (int i = 0, j = 0; K[i] != '\0'; i++) {
206 if (K[i] != ' ') {
207 nK[j++] = islower(K[i]) ? (char) toupper(K[i]) : K[i];
208 }
209 }
210 return nK;
211 }
212
213
214 static char *
215 get_steam_code (const unsigned char *hmac)
216 {
217 int offset = (hmac[gcry_md_get_algo_dlen (GCRY_MD_SHA1)-1] & 0x0f);
218
219 // Starting from the offset, take the successive 4 bytes while stripping the topmost bit to prevent it being handled as a signed integer
220 int bin_code = ((hmac[offset] & 0x7f) << 24) | ((hmac[offset + 1] & 0xff) << 16) | ((hmac[offset + 2] & 0xff) << 8) | ((hmac[offset + 3] & 0xff));
221
222 const char steam_alphabet[] = "23456789BCDFGHJKMNPQRTVWXY";
223
224 char code[6];
225 size_t steam_alphabet_len = strlen (steam_alphabet);
226 for (int i = 0; i < 5; i++) {
227 int mod = (int)(bin_code % steam_alphabet_len);
228 bin_code = (int)(bin_code / steam_alphabet_len);
229 code[i] = steam_alphabet[mod];
230 }
231 code[5] = '\0';
232
233 return strdup(code);
234 }
235
236
237 static int
238 truncate (const unsigned char *hmac,
239 int digits_length,
240 int algo)
241 {
242 // take the lower four bits of the last byte
243 int offset = hmac[gcry_md_get_algo_dlen (algo) - 1] & 0x0f;
244
245 // Starting from the offset, take the successive 4 bytes while stripping the topmost bit to prevent it being handled as a signed integer
246 int bin_code = ((hmac[offset] & 0x7f) << 24) | ((hmac[offset + 1] & 0xff) << 16) | ((hmac[offset + 2] & 0xff) << 8) | ((hmac[offset + 3] & 0xff));
247
248 long long int DIGITS_POWER[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000};
249 int token = (int)(bin_code % DIGITS_POWER[digits_length]);
250
251 return token;
252 }
253
254
255 static unsigned char *
256 compute_hmac (const char *K,
257 long C,
258 int algo)
259 {
260 size_t secret_len = (size_t)((strlen(K) + 1.6 - 1) / 1.6);
261
262 char *normalized_K = normalize_secret (K);
263 if (normalized_K == NULL) {
264 return NULL;
265 }
266
314 int
315 totp_verify(const char *secret, const char *user_totp, int digits, int period, int algo)
316 {
267317 cotp_error_t err;
268 unsigned char *secret = base32_decode (normalized_K, strlen(normalized_K), &err);
269 free (normalized_K);
270 if (secret == NULL) {
271 return NULL;
272 }
273
274 unsigned char C_reverse_byte_order[8];
275 int j, i;
276 for (j = 0, i = 7; j < 8 && i >= 0; j++, i--)
277 C_reverse_byte_order[i] = ((unsigned char *) &C)[j];
278
279 gcry_md_hd_t hd;
280 gpg_error_t gpg_err = gcry_md_open (&hd, algo, GCRY_MD_FLAG_HMAC);
281 if (gpg_err) {
282 fprintf (stderr, "Error while opening the cipher handle.\n");
283 free (secret);
284 return NULL;
285 }
286 gpg_err = gcry_md_setkey (hd, secret, secret_len);
287 if (gpg_err) {
288 fprintf (stderr, "Error while setting the cipher key.\n");
289 free (secret);
290 gcry_md_close (hd);
291 return NULL;
292 }
293 gcry_md_write (hd, C_reverse_byte_order, sizeof (C_reverse_byte_order));
294 gcry_md_final (hd);
295
296 unsigned char *hmac_tmp = gcry_md_read (hd, algo);
297 if (hmac_tmp == NULL) {
298 fprintf (stderr, "Error getting digest\n");
299 free (secret);
300 gcry_md_close (hd);
301 return NULL;
302 }
303
304 size_t dlen = gcry_md_get_algo_dlen(algo);
305 unsigned char *hmac = malloc (dlen);
306 if (hmac == NULL) {
307 fprintf (stderr, "Error allocating memory");
308 free (secret);
309 gcry_md_close (hd);
310 return NULL;
311 }
312 memcpy (hmac, hmac_tmp, dlen);
313
314 free (secret);
315
316 gcry_md_close (hd);
317
318 return hmac;
319 }
320
321
322 static char *
323 finalize (int digits_length,
324 int tk)
325 {
326 char *token = calloc (digits_length + 1, 1);
327 if (!token) return token;
328 char fmt[6];
329 sprintf (fmt, "%%0%dd", digits_length);
330 snprintf (token, digits_length + 1, fmt, tk);
331 return token;
332 }
333
334
335 static int
336 check_period (int period)
337 {
338 return (period <= 0 || period > 120) ? INVALID_PERIOD : VALID;
339 }
340
341
342 static int
343 check_otp_len (int digits_length)
344 {
345 return (digits_length < MIN_DIGTS || digits_length > MAX_DIGITS) ? INVALID_DIGITS : VALID;
346 }
347
348
349 static int
350 check_algo (int algo)
351 {
352 return (algo != SHA1 && algo != SHA256 && algo != SHA512) ? INVALID_ALGO : VALID;
353 }
318 char *current_totp = get_totp(secret, digits, period, algo, &err);
319 if (current_totp == NULL) {
320 return err;
321 }
322
323 int token_status;
324 if (strcmp(current_totp, user_totp) != 0) {
325 token_status = INVALID_OTP;
326 } else {
327 token_status = VALID;
328 }
329 free(current_totp);
330
331 return token_status;
332 }
333
334
335 int
336 hotp_verify(const char *K, long C, int N, const char *user_hotp, int algo)
337 {
338 cotp_error_t err;
339 char *current_hotp = get_hotp(K, C, N, algo, &err);
340 if (current_hotp == NULL) {
341 return err;
342 }
343
344 int token_status;
345 if (strcmp(current_hotp, user_hotp) != 0) {
346 token_status = INVALID_OTP;
347 } else {
348 token_status = VALID;
349 }
350 free(current_hotp);
351
352 return token_status;
353 }
+0
-247
src/utils/base32.c less more
0 #include <stdint.h>
1 #include <stdlib.h>
2 #include <string.h>
3 #include "../cotp.h"
4
5 #define BITS_PER_BYTE 8
6 #define BITS_PER_B32_BLOCK 5
7
8 // 64 MB should be more than enough
9 #define MAX_ENCODE_INPUT_LEN (64*1024*1024)
10 // if 64 MB of data is encoded than it should be also possible to decode it. That's why a bigger input is allowed for decoding
11 #define MAX_DECODE_BASE32_INPUT_LEN ((MAX_ENCODE_INPUT_LEN * 8 + 4) / 5)
12
13 static int is_valid_b32_input (const char *user_data);
14
15 static int get_char_index (uint8_t c);
16
17 static cotp_error_t check_input (const uint8_t *user_data,
18 size_t data_len,
19 int max_len);
20
21 static int strip_char (char *str,
22 char strip);
23
24 static const uint8_t b32_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
25
26
27 // The encoding process represents 40-bit groups of input bits as output strings of 8 encoded characters. The input data must be null terminated.
28 char *
29 base32_encode (const uint8_t *user_data,
30 size_t data_len,
31 cotp_error_t *err_code)
32 {
33 cotp_error_t error = check_input (user_data, data_len, MAX_ENCODE_INPUT_LEN);
34 if (error == EMPTY_STRING) {
35 return strdup ("");
36 }
37 if (error != NO_ERROR) {
38 *err_code = error;
39 return NULL;
40 }
41
42 size_t user_data_chars = 0, total_bits = 0;
43 int num_of_equals = 0;
44 for (int i = 0; i < data_len; i++) {
45 // As it's not known whether data_len is with or without the +1 for the null byte, a manual check is required.
46 // Check for null byte only at the end of the user given length, otherwise issue#23 may occur
47 if (user_data[i] == '\0' && i == data_len-1) {
48 break;
49 } else {
50 total_bits += 8;
51 user_data_chars += 1;
52 }
53 }
54 switch (total_bits % 40) {
55 case 8: num_of_equals = 6; break;
56 case 16: num_of_equals = 4; break;
57 case 24: num_of_equals = 3; break;
58 case 32: num_of_equals = 1; break;
59 }
60
61 size_t output_length = (user_data_chars * 8 + 4) / 5;
62 char *encoded_data = calloc (output_length + num_of_equals + 1, 1);
63 if (encoded_data == NULL) {
64 *err_code = MEMORY_ALLOCATION_ERROR;
65 return NULL;
66 }
67
68 for (int i = 0, j = 0; i < user_data_chars;) {
69 uint64_t first_octet = user_data[i++];
70 uint64_t second_octet = i < user_data_chars ? user_data[i++] : 0;
71 uint64_t third_octet = i < user_data_chars ? user_data[i++] : 0;
72 uint64_t fourth_octet = i < user_data_chars ? user_data[i++] : 0;
73 uint64_t fifth_octet = i < user_data_chars ? user_data[i++] : 0;
74 uint64_t quintuple =
75 ((first_octet >> 3) << 35) +
76 ((((first_octet & 0x7) << 2) | (second_octet >> 6)) << 30) +
77 (((second_octet & 0x3F) >> 1) << 25) +
78 ((((second_octet & 0x01) << 4) | (third_octet >> 4)) << 20) +
79 ((((third_octet & 0xF) << 1) | (fourth_octet >> 7)) << 15) +
80 (((fourth_octet & 0x7F) >> 2) << 10) +
81 ((((fourth_octet & 0x3) << 3) | (fifth_octet >> 5)) << 5) +
82 (fifth_octet & 0x1F);
83
84 encoded_data[j++] = (char)b32_alphabet[(quintuple >> 35) & 0x1F];
85 encoded_data[j++] = (char)b32_alphabet[(quintuple >> 30) & 0x1F];
86 encoded_data[j++] = (char)b32_alphabet[(quintuple >> 25) & 0x1F];
87 encoded_data[j++] = (char)b32_alphabet[(quintuple >> 20) & 0x1F];
88 encoded_data[j++] = (char)b32_alphabet[(quintuple >> 15) & 0x1F];
89 encoded_data[j++] = (char)b32_alphabet[(quintuple >> 10) & 0x1F];
90 encoded_data[j++] = (char)b32_alphabet[(quintuple >> 5) & 0x1F];
91 encoded_data[j++] = (char)b32_alphabet[(quintuple >> 0) & 0x1F];
92 }
93
94 for (int i = 0; i < num_of_equals; i++) {
95 encoded_data[output_length + i] = '=';
96 }
97 encoded_data[output_length + num_of_equals] = '\0';
98
99 *err_code = NO_ERROR;
100
101 return encoded_data;
102 }
103
104
105 uint8_t *
106 base32_decode (const char *user_data_untrimmed,
107 size_t data_len,
108 cotp_error_t *err_code)
109 {
110 cotp_error_t error = check_input ((uint8_t *)user_data_untrimmed, data_len, MAX_DECODE_BASE32_INPUT_LEN);
111 if (error == EMPTY_STRING) {
112 return (uint8_t *)strdup ("");
113 }
114 if (error != NO_ERROR) {
115 *err_code = error;
116 return NULL;
117 }
118
119 char *user_data = strdup (user_data_untrimmed);
120 if (user_data == NULL) {
121 *err_code = MEMORY_ALLOCATION_ERROR;
122 return NULL;
123 }
124 data_len -= strip_char(user_data, ' ');
125
126 if (!is_valid_b32_input(user_data)) {
127 free (user_data);
128 *err_code = INVALID_B32_INPUT;
129 return NULL;
130 }
131
132 size_t user_data_chars = 0;
133 for (int i = 0; i < data_len; i++) {
134 // As it's not known whether data_len is with or without the +1 for the null byte, a manual check is required.
135 if (user_data[i] != '=' && user_data[i] != '\0') {
136 user_data_chars += 1;
137 }
138 }
139
140 size_t output_length = (size_t)((user_data_chars + 1.6 + 1) / 1.6); // round up
141 uint8_t *decoded_data = calloc(output_length + 1, 1);
142 if (decoded_data == NULL) {
143 free (user_data);
144 *err_code = MEMORY_ALLOCATION_ERROR;
145 return NULL;
146 }
147
148 uint8_t mask, current_byte = 0;
149 int bits_left = 8;
150 for (int i = 0, j = 0; i < user_data_chars; i++) {
151 int char_index = get_char_index ((uint8_t)user_data[i]);
152 if (bits_left > BITS_PER_B32_BLOCK) {
153 mask = (uint8_t)char_index << (bits_left - BITS_PER_B32_BLOCK);
154 current_byte = (uint8_t) (current_byte | mask);
155 bits_left -= BITS_PER_B32_BLOCK;
156 } else {
157 mask = (uint8_t)char_index >> (BITS_PER_B32_BLOCK - bits_left);
158 current_byte = (uint8_t) (current_byte | mask);
159 decoded_data[j++] = current_byte;
160 current_byte = (uint8_t) (char_index << (BITS_PER_BYTE - BITS_PER_B32_BLOCK + bits_left));
161 bits_left += BITS_PER_BYTE - BITS_PER_B32_BLOCK;
162 }
163 }
164 decoded_data[output_length] = '\0';
165
166 free (user_data);
167
168 *err_code = NO_ERROR;
169
170 return decoded_data;
171 }
172
173
174 static int
175 is_valid_b32_input (const char *user_data)
176 {
177 // Create a lookup table for ASCII characters
178 uint8_t table[128];
179 memset (table, 0, sizeof (table));
180 for (size_t i = 0; i < sizeof (b32_alphabet); i++) {
181 table[b32_alphabet[i]] = 1;
182 }
183 table['='] = 1;
184
185 const char *p;
186 for (p = user_data; *p; p++) {
187 if (!table[*(uint8_t *)p]) {
188 return 0;
189 }
190 }
191 return 1;
192 }
193
194
195 static int
196 get_char_index (uint8_t c)
197 {
198 for (int i = 0; i < sizeof (b32_alphabet); i++) {
199 if (b32_alphabet[i] == c) {
200 return i;
201 }
202 }
203 return -1;
204 }
205
206
207 static int
208 strip_char (char *str,
209 char strip)
210 {
211 int found = 0;
212
213 // Create a lookup table for ASCII characters
214 uint8_t table[128];
215 memset (table, 0, sizeof (table));
216 table[(int)strip] = 1;
217
218 // Iterate through the string using pointers
219 char *p, *q;
220 for (q = p = str; *p; p++) {
221 if (!table[*(uint8_t *)p]) {
222 *q++ = *p;
223 } else {
224 found++;
225 }
226 }
227 *q = '\0';
228 return found;
229 }
230
231
232 static cotp_error_t
233 check_input (const uint8_t *user_data,
234 size_t data_len,
235 int max_len)
236 {
237 if (!user_data || (data_len == 0 && user_data[0] != '\0') || (data_len > (size_t)max_len)) {
238 return INVALID_USER_INPUT;
239 }
240
241 if (user_data[0] == '\0') {
242 return EMPTY_STRING;
243 }
244
245 return NO_ERROR;
246 }
00
11 IF(BUILD_TESTING)
22 add_executable (test_cotp test_otp.c)
3 add_executable (test_base32encode test_base32encode.c)
4 add_executable (test_base32decode test_base32decode.c)
53
6 target_link_libraries (test_cotp -lcotp -lcriterion -lgcrypt)
7 target_link_libraries (test_base32encode -lcotp -lcriterion -lgcrypt)
8 target_link_libraries (test_base32decode -lcotp -lcriterion -lgcrypt)
4 target_link_libraries (test_cotp -lcotp -lcriterion -lbaseencode -lgcrypt)
95 target_link_directories (test_cotp PRIVATE ${PROJECT_BINARY_DIR})
10 target_link_directories (test_base32encode PRIVATE ${PROJECT_BINARY_DIR})
11 target_link_directories (test_base32decode PRIVATE ${PROJECT_BINARY_DIR})
126 add_dependencies (test_cotp cotp)
13 add_dependencies (test_base32encode cotp)
14 add_dependencies (test_base32decode cotp)
157
168 add_test (NAME TestCOTP COMMAND test_cotp)
17 add_test (NAME TestBase32Encode COMMAND test_base32encode)
18 add_test (NAME TestBase32Decode COMMAND test_base32decode)
199 ENDIF(BUILD_TESTING)
+0
-104
tests/test_base32decode.c less more
0 #include <stdio.h>
1 #include <criterion/criterion.h>
2 #include "../src/cotp.h"
3
4
5 Test(b32_decode_test, b32_all_chars) {
6 cotp_error_t err;
7 const char *k = "IFCEMRZUGEZSDQVDEQSSMJRIFAXT6XWDU7B2SKS3LURSSLJOFR6DYPRL";
8 const char *k_dec = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+";
9
10 char *dk = base32_decode (k, strlen(k)+1, &err);
11
12 cr_expect(strcmp(dk, k_dec) == 0, "Expected %s to be equal to %s", dk, k_dec);
13
14 free(dk);
15 }
16
17
18 Test(b32_decode_test, b32_all_chars_noplusone) {
19 cotp_error_t err;
20 const char *k = "IFCEMRZUGEZSDQVDEQSSMJRIFAXT6XWDU7B2SKS3LURSSLJOFR6DYPRL";
21 const char *k_dec = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+";
22
23 char *dk = base32_decode (k, strlen(k), &err);
24
25 cr_expect(strcmp(dk, k_dec) == 0, "Expected %s to be equal to %s", dk, k_dec);
26
27 free(dk);
28 }
29
30
31 Test(b32_decode_test, b32_rfc4648) {
32 cotp_error_t err;
33 const char *k[] = {"", "MY======", "MZXQ====", "MZXW6===", "MZXW6YQ=", "MZXW6YTB", "MZXW6YTBOI======"};
34 const char *k_dec[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"};
35
36 for (int i = 0; i < 7; i++) {
37 char *dk = base32_decode (k[i], strlen(k[i])+1, &err);
38 cr_expect(strcmp(dk, k_dec[i]) == 0, "Expected %s to be equal to %s", dk, k_dec[i]);
39 free(dk);
40 }
41 }
42
43
44 Test(b32_decode_test, b32_rfc4648_noplusone) {
45 cotp_error_t err;
46 const char *k[] = {"", "MY======", "MZXQ====", "MZXW6===", "MZXW6YQ=", "MZXW6YTB", "MZXW6YTBOI======"};
47 const char *k_dec[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"};
48
49 for (int i = 0; i < 7; i++) {
50 char *dk = base32_decode (k[i], strlen(k[i]), &err);
51 cr_expect(strcmp(dk, k_dec[i]) == 0, "Expected %s to be equal to %s", dk, k_dec[i]);
52 free(dk);
53 }
54 }
55
56
57 Test(b32_decode_test, b32_invalid_input) {
58 cotp_error_t err;
59 const char *k = "£&/(&/";
60 size_t len = strlen(k);
61
62 uint8_t *dk = base32_decode (k, len, &err);
63
64 cr_expect_null (dk, "%s");
65 cr_expect_eq (err, INVALID_B32_INPUT);
66 }
67
68
69 Test(b32_decode_test, b32_decode_input_exceeded) {
70 cotp_error_t err;
71 const char *k = "ASDF";
72 size_t len = 128*1024*1024;
73
74 uint8_t *dk = base32_decode (k, len, &err);
75
76 cr_expect_null (dk, "%s");
77 cr_expect_eq (err, INVALID_USER_INPUT);
78 }
79
80
81 Test(b32_decode_test, b32_decode_input_whitespaces) {
82 cotp_error_t err;
83 const char *k = "MZ XW 6Y TB";
84 const char *expected = "fooba";
85
86 uint8_t *dk = base32_decode (k, strlen(k), &err);
87
88 cr_expect_str_eq (dk, expected, "%s");
89 }
90
91 Test(b32_decode_test, b32_decode_encode_null) {
92 const char* token = "LLFTSZYMUGKHEDQBAAACAZAMUFKKVFLS";
93 cotp_error_t err;
94
95 uint8_t* binary = base32_decode (token, strlen(token)+1, &err);
96 cr_expect_eq (err, NO_ERROR);
97
98 char* result = base32_encode (binary, 20, &err);
99 cr_expect_eq (err, NO_ERROR);
100
101 cr_expect_str_eq (result, token, "%s");
102 }
103
+0
-92
tests/test_base32encode.c less more
0 #include <stdio.h>
1 #include <criterion/criterion.h>
2 #include "../src/cotp.h"
3
4
5 Test(b32_encode_test, null_input) {
6 cotp_error_t err;
7 const char *k = NULL;
8
9 char *ek = base32_encode (k, 5, &err);
10
11 cr_expect_null (ek, "%s");
12 }
13
14
15 Test(b32_encode_test, data_nodata_size_nosize) {
16 cotp_error_t err;
17 const char *k1 = "";
18 const char *k2 = "asdiasjdijis";
19
20 // test no-data with given size, data with no-size and no-data no-size
21 char *ek1 = base32_encode (k1, 30, &err);
22 char *ek2 = base32_encode (k2, 0, &err);
23
24 cr_expect (strcmp (k1, ek1) == 0, "Expected %s to be equal to %s", ek1, k1);
25 cr_expect_null (ek2, "%s");
26
27 free (ek1);
28 }
29
30
31 Test(b32_encode_test, b32_all_chars) {
32 cotp_error_t err;
33 const char *k = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+";
34 const char *k_enc = "IFCEMRZUGEZSDQVDEQSSMJRIFAXT6XWDU7B2SKS3LURSSLJOFR6DYPRL";
35
36 char *ek = base32_encode (k, strlen(k)+1, &err);
37
38 cr_expect (strcmp (ek, k_enc) == 0, "Expected %s to be equal to %s", ek, k_enc);
39
40 free (ek);
41 }
42
43
44 Test(b32_encode_test, b32_all_chars_noplusone) {
45 cotp_error_t err;
46 const char *k = "ADFG413!£$%&&((/?^çé*[]#)-.,|<>+";
47 const char *k_enc = "IFCEMRZUGEZSDQVDEQSSMJRIFAXT6XWDU7B2SKS3LURSSLJOFR6DYPRL";
48
49 char *ek = base32_encode (k, strlen(k), &err);
50
51 cr_expect (strcmp (ek, k_enc) == 0, "Expected %s to be equal to %s", ek, k_enc);
52
53 free (ek);
54 }
55
56
57 Test(b32_encode_test, b32_rfc4648) {
58 cotp_error_t err;
59 const char *k[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"};
60 const char *k_enc[] = {"", "MY======", "MZXQ====", "MZXW6===", "MZXW6YQ=", "MZXW6YTB", "MZXW6YTBOI======"};
61
62 for (int i = 0; i < 7; i++) {
63 char *ek = base32_encode (k[i], strlen(k[i])+1, &err);
64 cr_expect (strcmp (ek, k_enc[i]) == 0, "Expected %s to be equal to %s", ek, k_enc[i]);
65 free (ek);
66 }
67 }
68
69
70 Test(b32_encode_test, b32_rfc4648_noplusone) {
71 cotp_error_t err;
72 const char *k[] = {"", "f", "fo", "foo", "foob", "fooba", "foobar"};
73 const char *k_enc[] = {"", "MY======", "MZXQ====", "MZXW6===", "MZXW6YQ=", "MZXW6YTB", "MZXW6YTBOI======"};
74
75 for (int i = 0; i < 7; i++) {
76 char *ek = base32_encode (k[i], strlen(k[i]), &err);
77 cr_expect (strcmp (ek, k_enc[i]) == 0, "Expected %s to be equal to %s", ek, k_enc[i]);
78 free (ek);
79 }
80 }
81
82
83 Test(b32_encode_test, b32_encode_input_exceeded) {
84 cotp_error_t err;
85 const char *k = "test";
86 size_t len = 65*1024*1024;
87
88 char *ek = base32_encode (k, len, &err);
89 cr_expect_null (ek, "%s");
90 cr_expect_eq (err, INVALID_USER_INPUT);
91 }
00 #include <criterion/criterion.h>
11 #include <string.h>
22 #include "../src/cotp.h"
3 #include <baseencode.h>
4
35
46 Test(totp_rfc6238, test_8_digits_sha1) {
57 const char *K = "12345678901234567890";
68 const int64_t counter[] = {59, 1111111109, 1111111111, 1234567890, 2000000000, 20000000000};
79 const char *expected_totp[] = {"94287082", "07081804", "14050471", "89005924", "69279037", "65353130"};
810
9 cotp_error_t cotp_err;
10 char *K_base32 = base32_encode ((const uchar *)K, strlen(K)+1, &cotp_err);
11
12 cotp_error_t err;
13 char *totp;
11 baseencode_error_t base_err;
12 char *K_base32 = base32_encode(K, strlen(K)+1, &base_err);
13
14 cotp_error_t err;
1415 for (int i = 0; i < 6; i++) {
15 totp = get_totp_at (K_base32, counter[i], 8, 30, SHA1, &err);
16 cr_expect_str_eq (totp, expected_totp[i], "Expected %s to be equal to %s\n", totp, expected_totp[i]);
17 free (totp);
18 }
19 free (K_base32);
20 }
21
22
23 Test(totp_rfc6238, test_8_digits_sha1_toint) {
24 const char *K = "12345678901234567890";
25 const int64_t counter[] = {59, 1111111109, 1111111111, 1234567890, 2000000000, 20000000000};
26 const int64_t expected_totp[] = {94287082, 7081804, 14050471, 89005924, 69279037, 65353130};
27
28 cotp_error_t cotp_err;
29 char *K_base32 = base32_encode ((const uchar *)K, strlen(K)+1, &cotp_err);
30
31 cotp_error_t err;
32 for (int i = 0; i < 6; i++) {
33 int64_t totp = otp_to_int (get_totp_at (K_base32, counter[i], 8, 30, SHA1, &err), &err);
34 cr_expect_eq (totp, expected_totp[i], "Expected %08ld to be equal to %08ld\n", totp, expected_totp[i]);
35 }
36 free (K_base32);
16 char *totp = get_totp_at(K_base32, counter[i], 8, 30, SHA1, &err);
17 cr_expect_str_eq(totp, expected_totp[i], "Expected %s to be equal to %s\n", totp, expected_totp[i]);
18 free(totp);
19 }
20 free(K_base32);
3721 }
3822
3923
4226 const long counter = 1234567890;
4327 const char *expected_totp = "0689005924";
4428
45 cotp_error_t cotp_err;
46 char *K_base32 = base32_encode ((const uchar *)K, strlen(K)+1, &cotp_err);
47
48 cotp_error_t err;
49 char *totp = get_totp_at (K_base32, counter, 10, 30, SHA1, &err);
50 cr_expect_str_eq (totp, expected_totp, "Expected %s to be equal to %s\n", totp, expected_totp);
51 free (totp);
52 free (K_base32);
53 }
54
55
56 Test(totp_rfc6238, test_10_digits_sha1_toint) {
57 const char *K = "12345678901234567890";
58 const long counter = 1234567890;
59 int64_t expected_totp = 689005924;
60
61 cotp_error_t cotp_err;
62 char *K_base32 = base32_encode ((const uchar *)K, strlen(K)+1, &cotp_err);
63
64 cotp_error_t err;
65 int64_t totp = otp_to_int (get_totp_at (K_base32, counter, 10, 30, SHA1, &err), &err);
66 cr_expect_eq (totp, expected_totp, "Expected %010ld to be equal to %010ld\n", totp, expected_totp);
67
68 free (K_base32);
69 }
29 baseencode_error_t base_err;
30 char *K_base32 = base32_encode(K, strlen(K)+1, &base_err);
31
32 cotp_error_t err;
33 char *totp = get_totp_at(K_base32, counter, 10, 30, SHA1, &err);
34 cr_expect_str_eq(totp, expected_totp, "Expected %s to be equal to %s\n", totp, expected_totp);
35 free(totp);
36 free(K_base32);
37 }
38
7039
7140
7241 Test(totp_rfc6238, test_8_digits_sha256) {
7443 const int64_t counter[] = {59, 1111111109, 1111111111, 1234567890, 2000000000, 20000000000};
7544 const char *expected_totp[] = {"46119246", "68084774", "67062674", "91819424", "90698825", "77737706"};
7645
77 cotp_error_t cotp_err;
78 char *K_base32 = base32_encode ((const uchar *)K, strlen(K)+1, &cotp_err);
79
80 cotp_error_t err;
81 char *totp;
46 baseencode_error_t base_err;
47 char *K_base32 = base32_encode(K, strlen(K)+1, &base_err);
48
49 cotp_error_t err;
8250 for (int i = 0; i < 6; i++) {
83 totp = get_totp_at (K_base32, counter[i], 8, 30, SHA256, &err);
84 cr_expect_str_eq (totp, expected_totp[i], "Expected %s to be equal to %s\n", totp, expected_totp[i]);
85 free (totp);
86 }
87 free (K_base32);
51 char *totp = get_totp_at(K_base32, counter[i], 8, 30, SHA256, &err);
52 cr_expect_str_eq(totp, expected_totp[i], "Expected %s to be equal to %s\n", totp, expected_totp[i]);
53 free(totp);
54 }
55 free(K_base32);
8856 }
8957
9058
9361 const int64_t counter[] = {59, 1111111109, 1111111111, 1234567890, 2000000000, 20000000000};
9462 const char *expected_totp[] = {"90693936", "25091201", "99943326", "93441116", "38618901", "47863826"};
9563
96 cotp_error_t cotp_err;
97 char *K_base32 = base32_encode ((const uchar *)K, strlen (K) + 1, &cotp_err);
98
99 cotp_error_t err;
100 char *totp;
64 baseencode_error_t base_err;
65 char *K_base32 = base32_encode(K, strlen(K)+1, &base_err);
66
67 cotp_error_t err;
10168 for (int i = 0; i < 6; i++) {
102 totp = get_totp_at (K_base32, counter[i], 8, 30, SHA512, &err);
103 cr_expect_str_eq (totp, expected_totp[i], "Expected %s to be equal to %s\n", totp, expected_totp[i]);
104 free (totp);
105 }
106 free (K_base32);
69 char *totp = get_totp_at(K_base32, counter[i], 8, 30, SHA512, &err);
70 cr_expect_str_eq(totp, expected_totp[i], "Expected %s to be equal to %s\n", totp, expected_totp[i]);
71 free(totp);
72 }
73 free(K_base32);
10774 }
10875
10976
11279 const int counter[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
11380 const char *expected_hotp[] = {"755224", "287082", "359152", "969429", "338314", "254676", "287922", "162583", "399871", "520489"};
11481
115 cotp_error_t cotp_err;
116 char *K_base32 = base32_encode((const uchar *)K, strlen(K)+1, &cotp_err);
117
118 cotp_error_t err;
119 char *hotp;
82 baseencode_error_t base_err;
83 char *K_base32 = base32_encode(K, strlen(K)+1, &base_err);
84
85 cotp_error_t err;
12086 for (int i = 0; i < 10; i++) {
121 hotp = get_hotp (K_base32, counter[i], 6, SHA1, &err);
122 cr_expect_str_eq (hotp, expected_hotp[i], "Expected %s to be equal to %s\n", hotp, expected_hotp[i]);
123 free (hotp);
124 }
125 free (K_base32);
87 char *hotp = get_hotp(K_base32, counter[i], 6, SHA1, &err);
88 cr_expect_str_eq(hotp, expected_hotp[i], "Expected %s to be equal to %s\n", hotp, expected_hotp[i]);
89 free(hotp);
90 }
91 free(K_base32);
12692 }
12793
12894
13298 cotp_error_t err;
13399 char *totp = get_totp (K, 2, 30, SHA1, &err);
134100
101 cr_expect_null (totp, "Expected totp to be null");
135102 cr_expect_eq (err, INVALID_DIGITS, "Expected %d to be equal to %d\n", err, INVALID_DIGITS);
136 cr_assert_null (totp);
137103
138104 free (totp);
139105 }
145111 cotp_error_t err;
146112 char *totp = get_totp (K, 16, 30, SHA1, &err);
147113
114 cr_expect_null (totp, "Expected totp to be null");
148115 cr_expect_eq (err, INVALID_DIGITS, "Expected %d to be equal to %d\n", err, INVALID_DIGITS);
149 cr_assert_null (totp);
150116
151117 free (totp);
152118 }
158124 cotp_error_t err;
159125 char *totp = get_totp (K, 6, 0, SHA1, &err);
160126
127 cr_expect_null (totp, "Expected totp to be null");
161128 cr_expect_eq (err, INVALID_PERIOD, "Expected %d to be equal to %d\n", err, INVALID_PERIOD);
162 cr_assert_null (totp);
163
164 free (totp);
165 }
166
167
168 Test(hotp_rfc, test_totp_wrong_negative) {
129
130 free (totp);
131 }
132
133
134 Test(hotp_rfc, test_wrong_negative) {
169135 const char *K = "this is a secret";
170136
171137 cotp_error_t err;
172138 char *totp = get_totp (K, 6, -20, SHA1, &err);
173139
140 cr_expect_null (totp, "Expected totp to be null");
174141 cr_expect_eq (err, INVALID_PERIOD, "Expected %d to be equal to %d\n", err, INVALID_PERIOD);
175 cr_assert_null (totp);
176
177 free (totp);
178 }
179
180
181 Test(hotp_rfc, test_hotp_wrong_negative) {
182 const char *K = "this is a secret";
183
184 cotp_error_t err;
185 char *hotp = get_hotp (K, -6, 8, SHA1, &err);
186
187 cr_expect_eq (err, INVALID_COUNTER, "Expected %d to be equal to %d\n", err, INVALID_COUNTER);
188 cr_assert_null (hotp);
142
143 free (totp);
189144 }
190145
191146
207162 cotp_error_t err;
208163 char *totp = get_totp (K, 6, 30, SHA1, &err);
209164
165 cr_expect_null (totp, "Expected totp to be null");
210166 cr_expect_eq (err, INVALID_B32_INPUT, "Expected %d to be equal to %d\n", err, INVALID_B32_INPUT);
211 cr_assert_null (totp);
212167 }
213168
214169
218173 cotp_error_t err;
219174 char *totp = get_totp (K, 6, 30, GCRY_MD_MD5, &err);
220175
176 cr_expect_null (totp, "Expected totp to be null");
221177 cr_expect_eq (err, INVALID_ALGO, "Expected %d to be equal to %d\n", err, INVALID_ALGO);
222 cr_assert_null (totp);
223178 }
224179
225180
243198 char *totp = get_steam_totp (b64_encoded_secret, 30, &err);
244199 cr_expect_null (totp, "Expected totp to be null");
245200 cr_expect_eq (err, INVALID_B32_INPUT, "Expected %d to be equal to %d\n", err, INVALID_B32_INPUT);
246 cr_assert_null (totp);
247 }
248
201 }
249202
250203 Test(totp_rfc6238, test_60seconds) {
251 const char *K = "12345678901234567890";
204 const char *secret = "12345678901234567890";
252205 const char *expected_totp = "360094";
253206
254 cotp_error_t cotp_err;
255 char *secret_base32 = base32_encode ((const uchar *)K, strlen (K)+1, &cotp_err);
256
257 cotp_error_t err;
258 char *totp = get_totp_at (secret_base32, 1111111109, 6, 60, SHA1, &err);
259 cr_expect_str_eq (totp, expected_totp, "Expected %s to be equal to %s\n", totp, expected_totp);
260
261 free (totp);
262 free (secret_base32);
263 }
264
265
266 Test(totp_int, test_err_is_missing_zero) {
267 const char *K = "12345678901234567890";
268 const long counter = 1234567890;
269 int64_t expected_totp = 689005924;
270
271 cotp_error_t cotp_err;
272 char *K_base32 = base32_encode ((const uchar *)K, strlen(K)+1, &cotp_err);
273
274 cotp_error_t err;
275 int64_t totp = otp_to_int (get_totp_at (K_base32, counter, 10, 30, SHA1, &err), &err);
276 cr_expect_eq (err, MISSING_LEADING_ZERO, "Expected %d to be equal to %d\n", err, MISSING_LEADING_ZERO);
277
278 free (K_base32);
279 }
280
281
282 Test(totp_int, test_err_invalid_input) {
283 const char *K = "12345678901234567890";
284
285 cotp_error_t cotp_err;
286 char *K_base32 = base32_encode ((const uchar *)K, strlen(K)+1, &cotp_err);
287
288 cotp_error_t err;
289 int64_t totp = otp_to_int ("124", &err);
290 cr_expect_eq (err, INVALID_USER_INPUT, "Expected %d to be equal to %d\n", err, INVALID_USER_INPUT);
291 cr_expect_eq (totp, -1, "Expected %ld to be equal to %d\n", totp, -1);
292
293 free (K_base32);
294 }
207 baseencode_error_t base_err;
208 char *secret_base32 = base32_encode(secret, strlen(secret)+1, &base_err);
209
210 cotp_error_t err;
211 char *totp = get_totp_at(secret_base32, 1111111109, 6, 60, SHA1, &err);
212 cr_expect_str_eq(totp, expected_totp, "Expected %s to be equal to %s\n", totp, expected_totp);
213
214 free(totp);
215 free(secret_base32);
216 }