Skip to content

Instantly share code, notes, and snippets.

@Flix01
Forked from syzdek/totp.c
Last active February 26, 2024 11:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Flix01/8fd8218f96c265b82b7a22f29c9c3d73 to your computer and use it in GitHub Desktop.
Save Flix01/8fd8218f96c265b82b7a22f29c9c3d73 to your computer and use it in GitHub Desktop.
totp.c
// Original code: https://gist.github.com/syzdek/eba233ca33e1b5a45a99
// Original code license:
/*
* TOTP: Time-Based One-Time Password Algorithm
* Copyright (c) 2015, David M. Syzdek <david@syzdek.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Keys are entered in base32 encodings
*
* Compile with: gcc -Wall -o totp totp.c -lcrypto
*/
// My fork: https://gist.github.com/Flix01/8fd8218f96c265b82b7a22f29c9c3d73 (same license)
/*
* Compile with: gcc -O3 -fstack-protector-all -march=native -Wall -o totp -I /usr/local/ssl totp.c -L /usr/lib/ssl -lcrypto
*/
// Handy links/alternatives:
/*
// Docs:
https://www.rfc-editor.org/rfc/rfc6238
https://en.wikipedia.org/wiki/Time-based_one-time_password
// Useful links:
https://stackoverflow.com/questions/73392097/totp-implementation-using-c-and-openssl
https://security.stackexchange.com/questions/47979/how-to-write-a-rock-solid-totp-implementation
// (Better?) alternatives:
https://github.com/msantos/totp.c
https://github.com/fmount/c_otp
https://github.com/h9lopez/totp-c
https://github.com/paolostivanin/libcotp
https://github.com/Netthaw/TOTP-MCU
https://github.com/mricon/totp-cgi
// Very useful online free TOTP token generator (to check results):
ex. key: "MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB" (base32)
Link: https://totp.danhersam.com/?key=MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB
Link with different params: https://totp.danhersam.com/?digits=6&period=30&algorithm=SHA512&key=MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB
*/
#include <time.h>
#include <openssl/hmac.h>
#include <openssl/buffer.h>
static const int8_t base32_vals[256] =
{
// This map cheats and interprets:
// - the numeral zero as the letter "O" as in oscar
// - the numeral one as the letter "L" as in lima
// - the numeral eight as the letter "B" as in bravo
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x20
14, 11, 26, 27, 28, 29, 30, 31, 1, -1, -1, -1, -1, 0, -1, -1, // 0x30
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 0x40
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 0x50
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 0x60
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, -1, // 0x70
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x80
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x90
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xA0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xB0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xC0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xD0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xE0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xF0
};
//static const char * base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=";
int main(int argc, char * argv[]);
int main(int argc, char * argv[])
{
size_t pos;
size_t len;
size_t keylen;
uint32_t endianness;
time_t time_now; // if 0 defaults to time(NULL)
time_t t0; // Unix time to start counting time step
uint64_t x; // step in seconds
uint64_t t; // number of steps
uint64_t offset;
uint8_t * md_value;
uint32_t bin_code;
uint32_t totp;
uint8_t * k; // user's secret key
uint8_t num_output_digits;
BIO* bio = NULL;
EVP_MD_CTX *ctx = NULL;
EVP_PKEY* pkey = NULL;
int rv = 1;
const char* sha_name = "sha1"; /* "sha1","sha256","sha512","sha3-256","sha3-512",etc. */
const EVP_MD* md = NULL;
size_t md_len = 0;
uint32_t digits_mod = 1;
time_t time_valid_for_seconds=0;
const char* key_encodings[3]={"b32","b64","raw"};
const char* key_encoding="b32";
int key_encoding_idx = 0;
int owned_k = 0;
x = 30;
t0 = 0;
num_output_digits = 6;
time_now = 0;
# ifndef MAX_K_LEN
# define MAX_K_LEN (EVP_MAX_MD_SIZE*4)
# endif
bio = BIO_new_fp(stdout,BIO_NOCLOSE);OPENSSL_assert(bio); /* not sure if BIO_NOCLOSE is correct */
switch(argc)
{
case 8:
time_now = strtoll(argv[7], NULL, 0);
// fall through
case 7:
t0 = strtoll(argv[6], NULL, 0);
// fall through
case 6:
x = strtoll(argv[5], NULL, 0);
// fall through
case 5:
sha_name = argv[4];
// fall through
case 4:
num_output_digits = strtoll(argv[3], NULL, 0);
// fall through
case 3:
key_encoding = argv[2];
// fall through
case 2:
k = (uint8_t *)argv[1];
break;
default:
BIO_printf(bio, "Usage: %s <key> [<key_encoding> [<num_output_digits> [<sha_name> [<interval> [<start> [<time_now>]]]]]]\n", argv[0]);
BIO_printf(bio, "where:\n");
BIO_printf(bio, "\t<key_encoding> can be: b32, b64 or raw (default:b32)\n");
BIO_printf(bio, "\t<num_output_digits> is typically 6 or 8 (default:6)\n");
BIO_printf(bio, "\t<sha_name> can be: sha1 sha256, sha512, etc. (default:sha1)\n");
BIO_printf(bio, "\t<interval> is the output validity in seconds (default:30)\n");
BIO_printf(bio, "\t<start> is usually 0 (default:0)\n");
BIO_printf(bio, "\t<time_now> is the number of seconds from the Unix epoc start time (default:time(NULL))\n");
goto cleanup;
break;
};
if (num_output_digits<1 || num_output_digits>24) num_output_digits=6;
for (int i=0;i<num_output_digits;i++) digits_mod*=10;
len = OPENSSL_strnlen(argv[1],MAX_K_LEN);
if (len==0) {BIO_printf(bio,"Error: inserted key is empty\n");goto cleanup;}
for (key_encoding_idx=0;key_encoding_idx<3;key_encoding_idx++) {if (CRYPTO_memcmp(key_encoding,key_encodings[key_encoding_idx],3)==0) break;}
if (key_encoding_idx<0 || key_encoding_idx>=3) {BIO_printf(bio,"Error: key_encoding=%s is not supported (try: b32, b64 or raw)\n",key_encoding);goto cleanup;}
if (key_encoding_idx==0) {
// validates base32 key
if (((len & 0xF) != 0) && ((len & 0xF) != 8)) {BIO_printf(bio, "%s: invalid base32 secret\n", argv[0]);goto cleanup;};
for(pos = 0; (pos < len); pos++)
{
if (base32_vals[k[pos]] == -1)
{
BIO_printf(bio, "%s: invalid base32 secret\n", argv[0]);
goto cleanup;
};
if (k[pos] == '=')
{
if (((pos & 0xF) == 0) || ((pos & 0xF) == 8))
{
BIO_printf(bio, "%s: invalid base32 secret\n", argv[0]);
goto cleanup;
}
if ((len - pos) > 6)
{
BIO_printf(bio, "%s: invalid base32 secret\n", argv[0]);
goto cleanup;
};
switch(pos%8)
{
case 2:
case 4:
case 5:
case 7:
break;
default:
BIO_printf(bio, "%s: invalid base32 secret\n", argv[0]);
rv = 1;goto cleanup;
};
for(; (pos < len); pos++)
{
if (k[pos] != '=')
{
BIO_printf(bio, "%s: invalid base32 secret\n", argv[0]);
rv = 1;goto cleanup;
};
};
};
};
// decodes base32 secret key
keylen = 0;
for(pos = 0; pos <= (len - 8); pos += 8)
{
// MSB is Most Significant Bits (0x80 == 10000000 ~= MSB)
// MB is middle bits (0x7E == 01111110 ~= MB)
// LSB is Least Significant Bits (0x01 == 00000001 ~= LSB)
// byte 0
k[keylen+0] = (base32_vals[k[pos+0]] << 3) & 0xF8; // 5 MSB
k[keylen+0] |= (base32_vals[k[pos+1]] >> 2) & 0x07; // 3 LSB
if (k[pos+2] == '=')
{
keylen += 1;
break;
};
// byte 1
k[keylen+1] = (base32_vals[k[pos+1]] << 6) & 0xC0; // 2 MSB
k[keylen+1] |= (base32_vals[k[pos+2]] << 1) & 0x3E; // 5 MB
k[keylen+1] |= (base32_vals[k[pos+3]] >> 4) & 0x01; // 1 LSB
if (k[pos+4] == '=')
{
keylen += 2;
break;
};
// byte 2
k[keylen+2] = (base32_vals[k[pos+3]] << 4) & 0xF0; // 4 MSB
k[keylen+2] |= (base32_vals[k[pos+4]] >> 1) & 0x0F; // 4 LSB
if (k[pos+5] == '=')
{
keylen += 3;
break;
};
// byte 3
k[keylen+3] = (base32_vals[k[pos+4]] << 7) & 0x80; // 1 MSB
k[keylen+3] |= (base32_vals[k[pos+5]] << 2) & 0x7C; // 5 MB
k[keylen+3] |= (base32_vals[k[pos+6]] >> 3) & 0x03; // 2 LSB
if (k[pos+7] == '=')
{
keylen += 4;
break;
};
// byte 4
k[keylen+4] = (base32_vals[k[pos+6]] << 5) & 0xE0; // 3 MSB
k[keylen+4] |= (base32_vals[k[pos+7]] >> 0) & 0x1F; // 5 LSB
keylen += 5;
};
k[keylen] = 0;
}
else if (key_encoding_idx==1) {
/* base64 decoding (TODO: How can we get conversion errors?) */
BIO *bmem,*b64;BUF_MEM *buf_mem;
for (size_t i=0;i<len;i++) {if (!((k[i]>='\\' && k[i]<='9') || (k[i]>='A' && k[i]<='Z') || (k[i]>='a' && k[i]<='z') || k[i]=='+' || k[i]=='=')) {BIO_printf(bio,"Error: invalid base64 secret\n");goto cleanup;}}
bmem = BIO_new(BIO_s_mem());
b64 = BIO_new(BIO_f_base64());
BIO_push(b64, bmem);
BIO_puts(b64,(const char*) k);
BIO_flush(b64);
BIO_get_mem_ptr(bmem, &buf_mem);
owned_k = 1;
k = (uint8_t*) CRYPTO_strndup(buf_mem->data, buf_mem->length,__FILE__,__LINE__);
keylen = buf_mem->length;
BIO_set_close(bmem, BIO_CLOSE);
BIO_free_all(b64);
}
else {/* raw mode */ keylen = OPENSSL_strnlen((char*)k,MAX_K_LEN);}
if (keylen==0) {BIO_printf(bio,"Error: decoded key is empty\n");goto cleanup;}
if (time_now==0) time_now = time(NULL);
t = (time_now - t0) / x;
time_valid_for_seconds = x-(time_now-t*x);
// converts T to big endian if system is little endian
endianness = 0xdeadbeef;
if ((*(const uint8_t *)&endianness) == 0xef)
{
t = ((t & 0x00000000ffffffff) << 32) | ((t & 0xffffffff00000000) >> 32);
t = ((t & 0x0000ffff0000ffff) << 16) | ((t & 0xffff0000ffff0000) >> 16);
t = ((t & 0x00ff00ff00ff00ff) << 8) | ((t & 0xff00ff00ff00ff00) >> 8);
};
// init OpenSSL fields
ctx = EVP_MD_CTX_new();OPENSSL_assert(ctx);
pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, k, keylen);OPENSSL_assert(pkey);
md = EVP_get_digestbyname(sha_name);if (!md ) {BIO_printf(bio,"Error: sha_name: \"%s\" not found\n",sha_name);goto cleanup;}
// determines hash
rv = EVP_DigestInit_ex(ctx, md, NULL);if (rv!=1) {BIO_printf(bio,"Error: EVP_DigestInit_ex(...) failed\n");rv=1;goto cleanup;}
rv = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);if (rv!=1) {BIO_printf(bio,"Error: EVP_DigestSignInit(...) failed\n");rv=1;goto cleanup;}
rv = EVP_DigestSignUpdate(ctx, (const unsigned char *)&t, sizeof(t));if (rv!=1) {BIO_printf(bio,"Error: EVP_DigestSignUpdate(...) failed\n");rv=1;goto cleanup;}
rv = EVP_DigestSignFinal(ctx, NULL, &md_len);if (rv!=1) {BIO_printf(bio,"Error: EVP_DigestSignFinal(...) failed\n");rv=1;goto cleanup;}
md_value = OPENSSL_secure_zalloc(md_len);if (!md_value) {BIO_printf(bio,"Error: OPENSSL_secure_zalloc(...) failed\n");rv=1;goto cleanup;}
rv = EVP_DigestSignFinal(ctx, md_value, &md_len);
if (rv!=1) {
OPENSSL_secure_free(md_value);md_value=NULL;
BIO_printf(bio,"Error: EVP_DigestSignFinal(...) failed\n");
rv=1;goto cleanup;
}
// dynamically truncates hash
offset = md_value[md_len-1] & 0x0f; /* 'md_len' or 'keylen' ? */
bin_code = (md_value[offset] & 0x7f) << 24
| (md_value[offset+1] & 0xff) << 16
| (md_value[offset+2] & 0xff) << 8
| (md_value[offset+3] & 0xff) ;
OPENSSL_secure_free(md_value);md_value=NULL;
// truncates code to 'num_output_digits'
totp = bin_code % digits_mod;
BIO_printf(bio,"totp[%s,%s]: %.*u (valid for %02ld seconds)\n",key_encoding,sha_name,num_output_digits,totp,time_valid_for_seconds);
rv = 0;
cleanup:
if (owned_k) {CRYPTO_secure_free(k,__FILE__,__LINE__);k=NULL;}
if (bio) {BIO_flush(bio);}
EVP_PKEY_free(pkey);pkey=NULL;
EVP_MD_CTX_free(ctx);ctx=NULL;
if (bio) {BIO_free(bio);bio=NULL;}
k=NULL;keylen=0;
return rv;
}
#!/bin/bash
FILE=./totp
if [ -f "$FILE" ]; then
echo "$FILE exists. Skipping compilation."
else
echo "$FILE does not exist. Compiling..."
gcc -O3 -march=native -Wall -o totp -I /usr/local/ssl totp.c -L /usr/lib/ssl -lcrypto
fi
if [ -f "$FILE" ]; then
test_times=(59 1111111109 1111111111 1234567890 2000000000 20000000000)
test_expected_sha1_results=(94287082 07081804 14050471 89005924 69279037 65353130)
test_expected_sha256_results=(46119246 68084774 67062674 91819424 90698825 77737706)
test_expected_sha512_results=(90693936 25091201 99943326 93441116 38618901 47863826)
num_passed=0
#echo "sha1 test:"
for ((i=0; i<6; i++))
do
test_time=${test_times[i]}
test_result=${test_expected_sha1_results[i]}
#echo "$i) $test_time $test_result"
topt_output=$(./totp 12345678901234567890 raw 8 sha1 30 0 $test_time | cut -d ' ' -f2)
#echo $topt_output
if [ "$test_result" = "$topt_output" ]; then
num_passed=$((num_passed + 1))
fi
done
#echo "sha256 test:"
for ((i=0; i<6; i++))
do
test_time=${test_times[i]}
test_result=${test_expected_sha256_results[i]}
#echo "$i) $test_time $test_result"
topt_output=$(./totp 12345678901234567890123456789012 raw 8 sha256 30 0 $test_time | cut -d ' ' -f2)
#echo $topt_output
if [ "$test_result" = "$topt_output" ]; then
num_passed=$((num_passed + 1))
fi
done
#echo "sha512 test:"
for ((i=0; i<6; i++))
do
test_time=${test_times[i]}
test_result=${test_expected_sha512_results[i]}
#echo "$i) $test_time $test_result"
topt_output=$(./totp 1234567890123456789012345678901234567890123456789012345678901234 raw 8 sha512 30 0 $test_time | cut -d ' ' -f2)
#echo $topt_output
if [ "$test_result" = "$topt_output" ]; then
num_passed=$((num_passed + 1))
fi
done
if (( num_passed == 18 )); then
echo "TEST PASSED [rfc6238 test vector (8 digits,sha1,sha256 and sha512)]."
else
echo "TEST FAILED $((18 - num_passed))/18 tests [rfc6238 test vector (8 digits,sha1,sha256 and sha512)]."
fi
fi
echo Press any key to quit.
read key
// Original code: https://gist.github.com/syzdek/eba233ca33e1b5a45a99
// Original code license:
/*
* TOTP: Time-Based One-Time Password Algorithm
* Copyright (c) 2015, David M. Syzdek <david@syzdek.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Keys are entered in base32 encodings
*
* Compile with: gcc -Wall -o totp totp.c -lcrypto
*/
// My fork: https://gist.github.com/Flix01/8fd8218f96c265b82b7a22f29c9c3d73 (same license)
/*
* Compile with: gcc -O3 -fstack-protector-all -march=native -Wall -o totp_standalone totp_standalone.c
*
* -> no more libopenssl/libcrypto dependency (replaced by picohash.h: https://github.com/kazuho/picohash)
* -> dropped sha512 support (missing in picohash.h)
* -> replaced base64 decoder from libopenssl/libcrypto with a custom one (code adapted from Polfosol's snippet here: https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c/13935718)
*/
// Handy links/alternatives:
/*
// Docs:
https://www.rfc-editor.org/rfc/rfc6238
https://en.wikipedia.org/wiki/Time-based_one-time_password
// Useful links:
https://stackoverflow.com/questions/73392097/totp-implementation-using-c-and-openssl
https://security.stackexchange.com/questions/47979/how-to-write-a-rock-solid-totp-implementation
// (Better?) alternatives:
https://github.com/msantos/totp.c
https://github.com/fmount/c_otp
https://github.com/h9lopez/totp-c
https://github.com/paolostivanin/libcotp
https://github.com/Netthaw/TOTP-MCU
https://github.com/mricon/totp-cgi
// Very useful online free TOTP token generator (to check results):
ex. key: "MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB" (base32)
Link: https://totp.danhersam.com/?key=MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB
Link with different params: https://totp.danhersam.com/?digits=6&period=30&algorithm=SHA512&key=MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB
*/
#include <time.h>
#include <stdio.h> // printf
#include <stdlib.h> // strtoll
#include <string.h>
#include "totp_standalone_picohash.h"
static const int8_t base32_vals[256] =
{
// This map cheats and interprets:
// - the numeral zero as the letter "O" as in oscar
// - the numeral one as the letter "L" as in lima
// - the numeral eight as the letter "B" as in bravo
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x20
14, 11, 26, 27, 28, 29, 30, 31, 1, -1, -1, -1, -1, 0, -1, -1, // 0x30
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 0x40
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 0x50
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 0x60
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, -1, // 0x70
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x80
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x90
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xA0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xB0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xC0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xD0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xE0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xF0
};
//static const char * base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=";
// Caller must free return value using free()
char* b64decode(const char* __restrict__ data, size_t len, size_t* out_len) {
// Code adapted from Polfosol's snippet here: https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c/13935718
// ['A'-'Z'] ['a'-'z'] ['0'-'9'] '+' '/'
// '=' used for padding
const int B64index[123] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
unsigned char* __restrict__ p = (unsigned char*)data;assert(data);
if (len==0) len=strnlen(data,2048);
int pad = len > 0 && (len % 4 || p[len - 1] == '=');
const size_t L = ((len + 3) / 4 - pad) * 4;
size_t str_len = L / 4 * 3 + pad;size_t str_cap = str_len+1;
char* __restrict__ str = calloc(1,str_cap);
for (size_t i = 0, j = 0; i < L; i += 4) {
int n = B64index[p[i]] << 18 | B64index[p[i + 1]] << 12 | B64index[p[i + 2]] << 6 | B64index[p[i + 3]];
str[j++] = n >> 16;
str[j++] = n >> 8 & 0xFF;
str[j++] = n & 0xFF;
}
if (pad) {
assert(p[L]<123 && p[L + 1]<123); // Wrong 'len' argument
int n = B64index[p[L]] << 18 | B64index[p[L + 1]] << 12;
str[str_len-1] = n >> 16;
if (len > L + 2 && p[L + 2] != '=')
{
n |= B64index[p[L + 2]] << 6;
assert(str_len<str_cap);
str[str_len++] = (n >> 8 & 0xFF);
}
}
if (out_len) *out_len=str_len;
return str;
}
int main(int argc, char * argv[]);
int main(int argc, char * argv[])
{
static const struct picohash_sha_data_t {
char* name;size_t name_len;
size_t digest_len;
void (*initf)(picohash_ctx_t *);
} supported_sha_data[] = {{"sha1",4,20,picohash_init_sha1},{"sha224",6,28,picohash_init_sha224},{"sha256",6,32,picohash_init_sha256}};
# ifndef MAX_DIGEST_LEN
# define MAX_DIGEST_LEN PICOHASH_SHA256_DIGEST_LENGTH
# endif
# ifndef MAX_K_LEN
# define MAX_K_LEN (MAX_DIGEST_LEN*4)
# endif
size_t pos;
size_t len;
size_t keylen;
uint32_t endianness;
time_t time_now; // if 0 defaults to time(NULL)
time_t t0; // Unix time to start counting time step
uint64_t x; // step in seconds
uint64_t t; // number of steps
uint64_t offset;
uint8_t md_value[MAX_K_LEN];
uint32_t bin_code;
uint32_t totp;
uint8_t * k = NULL; // user's secret key
uint8_t num_output_digits;
int k_owned = 0;
picohash_ctx_t ctx={0};
int rv = 1;
static const char* supported_key_encodings[] = {"b32","b64","raw"};
static const size_t supported_key_encodings_size = sizeof(supported_key_encodings)/sizeof(supported_key_encodings[0]);
static const size_t supported_sha_names_size = sizeof(supported_sha_data)/sizeof(supported_sha_data[0]);
const char* sha_name = "sha1";size_t sha_name_idx = 0;const struct picohash_sha_data_t* sha_data = &supported_sha_data[0];
size_t md_len = 0;
uint32_t digits_mod = 1;
time_t time_valid_for_seconds=0;
const char* key_encoding="b32";size_t key_encoding_idx = 0;
//int owned_k = 0;
x = 30;
t0 = 0;
num_output_digits = 6;
time_now = 0;
switch(argc)
{
case 8:
time_now = strtoll(argv[7], NULL, 0);
// fall through
case 7:
t0 = strtoll(argv[6], NULL, 0);
// fall through
case 6:
x = strtoll(argv[5], NULL, 0);
// fall through
case 5:
sha_name = argv[4];
// fall through
case 4:
num_output_digits = strtoll(argv[3], NULL, 0);
// fall through
case 3:
key_encoding = argv[2];
// fall through
case 2:
k = (uint8_t *)argv[1];
break;
default:
printf("Usage: %s <key> [<key_encoding> [<num_output_digits> [<sha_name> [<interval> [<start> [<time_now>]]]]]]\n", argv[0]);
printf("where:\n");
printf("\t<key_encoding> can be: b32, b64 or raw (default:b32)\n");
printf("\t<num_output_digits> is typically 6 or 8 (default:6)\n");
printf("\t<sha_name> can be: sha1, sha224 or sha256 (default:sha1)\n");
printf("\t<interval> is the output validity in seconds (default:30)\n");
printf("\t<start> is usually 0 (default:0)\n");
printf("\t<time_now> is the number of seconds from the Unix epoc start time (default:time(NULL))\n");
goto cleanup;
break;
};
if (num_output_digits<1 || num_output_digits>24) num_output_digits=6;
for (int i=0;i<num_output_digits;i++) digits_mod*=10;
len = strnlen(argv[1],MAX_K_LEN);
if (len==0) {printf("Error: inserted key is empty\n");goto cleanup;}
for (key_encoding_idx=0;key_encoding_idx<supported_key_encodings_size;key_encoding_idx++) {
if (memcmp(key_encoding,supported_key_encodings[key_encoding_idx],3)==0) break;
}
if (key_encoding_idx>=supported_key_encodings_size) {printf("Error: key_encoding=%s is not supported (try: b32, b64 or raw)\n",key_encoding);goto cleanup;}
for (sha_name_idx=0;sha_name_idx<supported_sha_names_size;sha_name_idx++) {
sha_data = &supported_sha_data[sha_name_idx];
if (memcmp(sha_name,sha_data->name,sha_data->name_len)==0) {
break;
}
}
if (sha_name_idx>=supported_sha_names_size) {printf("Error: sha_name=%s is not supported (try: sha1, sha224 or sha256)\n",sha_name);goto cleanup;}
if (key_encoding_idx==0) {
// validates base32 key
if (((len & 0xF) != 0) && ((len & 0xF) != 8)) {printf("%s: invalid base32 secret\n", argv[0]);goto cleanup;};
for(pos = 0; (pos < len); pos++)
{
if (base32_vals[k[pos]] == -1)
{
printf("%s: invalid base32 secret\n", argv[0]);
goto cleanup;
};
if (k[pos] == '=')
{
if (((pos & 0xF) == 0) || ((pos & 0xF) == 8))
{
printf("%s: invalid base32 secret\n", argv[0]);
goto cleanup;
}
if ((len - pos) > 6)
{
printf("%s: invalid base32 secret\n", argv[0]);
goto cleanup;
};
switch(pos%8)
{
case 2:
case 4:
case 5:
case 7:
break;
default:
printf("%s: invalid base32 secret\n", argv[0]);
rv = 1;goto cleanup;
};
for(; (pos < len); pos++)
{
if (k[pos] != '=')
{
printf("%s: invalid base32 secret\n", argv[0]);
rv = 1;goto cleanup;
};
};
};
};
// decodes base32 secret key
keylen = 0;
for(pos = 0; pos <= (len - 8); pos += 8)
{
// MSB is Most Significant Bits (0x80 == 10000000 ~= MSB)
// MB is middle bits (0x7E == 01111110 ~= MB)
// LSB is Least Significant Bits (0x01 == 00000001 ~= LSB)
// byte 0
k[keylen+0] = (base32_vals[k[pos+0]] << 3) & 0xF8; // 5 MSB
k[keylen+0] |= (base32_vals[k[pos+1]] >> 2) & 0x07; // 3 LSB
if (k[pos+2] == '=')
{
keylen += 1;
break;
};
// byte 1
k[keylen+1] = (base32_vals[k[pos+1]] << 6) & 0xC0; // 2 MSB
k[keylen+1] |= (base32_vals[k[pos+2]] << 1) & 0x3E; // 5 MB
k[keylen+1] |= (base32_vals[k[pos+3]] >> 4) & 0x01; // 1 LSB
if (k[pos+4] == '=')
{
keylen += 2;
break;
};
// byte 2
k[keylen+2] = (base32_vals[k[pos+3]] << 4) & 0xF0; // 4 MSB
k[keylen+2] |= (base32_vals[k[pos+4]] >> 1) & 0x0F; // 4 LSB
if (k[pos+5] == '=')
{
keylen += 3;
break;
};
// byte 3
k[keylen+3] = (base32_vals[k[pos+4]] << 7) & 0x80; // 1 MSB
k[keylen+3] |= (base32_vals[k[pos+5]] << 2) & 0x7C; // 5 MB
k[keylen+3] |= (base32_vals[k[pos+6]] >> 3) & 0x03; // 2 LSB
if (k[pos+7] == '=')
{
keylen += 4;
break;
};
// byte 4
k[keylen+4] = (base32_vals[k[pos+6]] << 5) & 0xE0; // 3 MSB
k[keylen+4] |= (base32_vals[k[pos+7]] >> 0) & 0x1F; // 5 LSB
keylen += 5;
};
k[keylen] = 0;
}
else if (key_encoding_idx==1) {
/* b64 encoding */
for (size_t i=0;i<len;i++) {if (!((k[i]>='\\' && k[i]<='9') || (k[i]>='A' && k[i]<='Z') || (k[i]>='a' && k[i]<='z') || k[i]=='+' || k[i]=='=')) {printf("%s: invalid base64 secret\n",argv[0]);goto cleanup;}}
k = (uint8_t*) b64decode((const char*)k,len,&keylen);
if (!k) {printf("%s: secret is not a valid base64-formatted string\n",argv[0]);goto cleanup;}
k_owned = 1;
}
else {/* raw mode */ keylen = strnlen((char*)k,MAX_K_LEN);}
if (keylen==0) {printf("%s: decoded key is empty\n",argv[0]);goto cleanup;}
if (time_now==0) time_now = time(NULL);
t = (time_now - t0) / x;
time_valid_for_seconds = x-(time_now-t*x);
// converts T to big endian if system is little endian
endianness = 0xdeadbeef;
if ((*(const uint8_t *)&endianness) == 0xef)
{
t = ((t & 0x00000000ffffffff) << 32) | ((t & 0xffffffff00000000) >> 32);
t = ((t & 0x0000ffff0000ffff) << 16) | ((t & 0xffff0000ffff0000) >> 16);
t = ((t & 0x00ff00ff00ff00ff) << 8) | ((t & 0xff00ff00ff00ff00) >> 8);
};
// calculate hash
picohash_init_hmac(&ctx, sha_data->initf,k,keylen);
picohash_update(&ctx, (const unsigned char *)&t, sizeof(t));
picohash_final(&ctx, md_value);
md_len = sha_data->digest_len;
// dynamically truncates hash
offset = md_value[md_len-1] & 0x0f; /* 'md_len' or 'keylen' ? */
bin_code = (md_value[offset] & 0x7f) << 24
| (md_value[offset+1] & 0xff) << 16
| (md_value[offset+2] & 0xff) << 8
| (md_value[offset+3] & 0xff) ;
// truncates code to 'num_output_digits'
totp = bin_code % digits_mod;
printf("totp[%s,%s]: %.*u (valid for %02ld seconds)\n",key_encoding,sha_data->name,num_output_digits,totp,time_valid_for_seconds);
rv = 0;
cleanup:
if (k_owned) {free(k);k=NULL;keylen=0;}
if (k) memset(k,0,MAX_K_LEN);
return rv;
}
#!/bin/bash
FILE=./totp_standalone
if [ -f "$FILE" ]; then
echo "$FILE exists. Skipping compilation."
else
echo "$FILE does not exist. Compiling..."
gcc -O3 -fstack-protector-all -march=native -Wall -o totp_standalone totp_standalone.c
fi
if [ -f "$FILE" ]; then
test_times=(59 1111111109 1111111111 1234567890 2000000000 20000000000)
test_expected_sha1_results=(94287082 07081804 14050471 89005924 69279037 65353130)
test_expected_sha256_results=(46119246 68084774 67062674 91819424 90698825 77737706)
test_expected_sha512_results=(90693936 25091201 99943326 93441116 38618901 47863826)
num_passed=0
#echo "sha1 test:"
key=12345678901234567890
for ((i=0; i<6; i++))
do
test_time=${test_times[i]}
test_result=${test_expected_sha1_results[i]}
#echo "$i) $test_time $test_result"
topt_output=$(./totp_standalone $key raw 8 sha1 30 0 $test_time | cut -d ' ' -f2)
#echo $topt_output
if [ "$test_result" = "$topt_output" ]; then
num_passed=$((num_passed + 1))
fi
done
#echo "sha256 test:"
key=12345678901234567890123456789012
for ((i=0; i<6; i++))
do
test_time=${test_times[i]}
test_result=${test_expected_sha256_results[i]}
#echo "$i) $test_time $test_result"
topt_output=$(./totp_standalone $key raw 8 sha256 30 0 $test_time | cut -d ' ' -f2)
#echo $topt_output
if [ "$test_result" = "$topt_output" ]; then
num_passed=$((num_passed + 1))
fi
done
if (( num_passed == 12 )); then
echo "TEST PASSED [rfc6238 test vector (8 digits,sha1 and sha256 only)]."
else
echo "TEST FAILED $((12 - num_passed))/12 tests [rfc6238 test vector (8 digits,sha1 and sha256 only)]."
fi
fi
echo Press any key to quit.
read key
/*
* The code is placed under public domain by Kazuho Oku <kazuhooku@gmail.com>.
*
* The MD5 implementation is based on a public domain implementation written by
* Solar Designer <solar@openwall.com> in 2001, which is used by Dovecot.
*
* The SHA1 implementation is based on a public domain implementation written
* by Wei Dai and other contributors for libcrypt, used also in liboauth.
*
* The SHA224/SHA256 implementation is based on a public domain implementation
* by Sam Hocevar <sam@hocevar.net> for LibTomCrypt.
*/
/* Github repo: https://github.com/kazuho/picohash */
/*
* Calculating Hash
picohash_ctx_t ctx;
char digest[PICOHASH_MD5_DIGEST_LENGTH];
picohash_init_md5(&ctx);
picohash_update(&ctx, "hello", 5);
picohash_final(&ctx, digest);
Replace md5 with sha1, sha224, sha256 for your need.
* Calculating HMAC
picohash_ctx_t ctx;
char digest[PICOHASH_SHA1_DIGEST_LENGTH];
picohash_init_hmac(&ctx, picohash_init_sha1, "my secret", strlen("my secret"));
picohash_update(&ctx, "hello", 5);
picohash_final(&ctx, digest);
Replace md5 with sha1, sha224, sha256 for your need.
*/
#ifndef _picohash_h_
#define _picohash_h_
#include <assert.h>
#include <inttypes.h>
#include <string.h>
#ifdef __BIG_ENDIAN__
#define _PICOHASH_BIG_ENDIAN
#elif defined __LITTLE_ENDIAN__
/* override */
#elif defined __BYTE_ORDER
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define _PICOHASH_BIG_ENDIAN
#endif
#elif !defined(_WIN32)
#include <endian.h> // machine/endian.h
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define _PICOHASH_BIG_ENDIAN
#endif
#endif
#define PICOHASH_MD5_BLOCK_LENGTH 64
#define PICOHASH_MD5_DIGEST_LENGTH 16
typedef struct _picohash_md5_ctx_t {
uint_fast32_t lo, hi;
uint_fast32_t a, b, c, d;
unsigned char buffer[64];
uint_fast32_t block[PICOHASH_MD5_DIGEST_LENGTH];
const void *(*_body)(struct _picohash_md5_ctx_t *ctx, const void *data, size_t size);
} _picohash_md5_ctx_t;
static void _picohash_md5_init(_picohash_md5_ctx_t *ctx);
static void _picohash_md5_update(_picohash_md5_ctx_t *ctx, const void *data, size_t size);
static void _picohash_md5_final(_picohash_md5_ctx_t *ctx, void *digest);
#define PICOHASH_SHA1_BLOCK_LENGTH 64
#define PICOHASH_SHA1_DIGEST_LENGTH 20
typedef struct {
uint32_t buffer[PICOHASH_SHA1_BLOCK_LENGTH / 4];
uint32_t state[PICOHASH_SHA1_DIGEST_LENGTH / 4];
uint64_t byteCount;
uint8_t bufferOffset;
} _picohash_sha1_ctx_t;
static void _picohash_sha1_init(_picohash_sha1_ctx_t *ctx);
static void _picohash_sha1_update(_picohash_sha1_ctx_t *ctx, const void *input, size_t len);
static void _picohash_sha1_final(_picohash_sha1_ctx_t *ctx, void *digest);
#define PICOHASH_SHA256_BLOCK_LENGTH 64
#define PICOHASH_SHA256_DIGEST_LENGTH 32
#define PICOHASH_SHA224_BLOCK_LENGTH PICOHASH_SHA256_BLOCK_LENGTH
#define PICOHASH_SHA224_DIGEST_LENGTH 28
typedef struct {
uint64_t length;
uint32_t state[PICOHASH_SHA256_DIGEST_LENGTH / 4];
uint32_t curlen;
unsigned char buf[PICOHASH_SHA256_BLOCK_LENGTH];
} _picohash_sha256_ctx_t;
static void _picohash_sha256_init(_picohash_sha256_ctx_t *ctx);
static void _picohash_sha256_update(_picohash_sha256_ctx_t *ctx, const void *data, size_t len);
static void _picohash_sha256_final(_picohash_sha256_ctx_t *ctx, void *digest);
static void _picohash_sha224_init(_picohash_sha256_ctx_t *ctx);
static void _picohash_sha224_final(_picohash_sha256_ctx_t *ctx, void *digest);
#define PICOHASH_MAX_BLOCK_LENGTH 64
#define PICOHASH_MAX_DIGEST_LENGTH 32
typedef struct {
union {
_picohash_md5_ctx_t _md5;
_picohash_sha1_ctx_t _sha1;
_picohash_sha256_ctx_t _sha256;
};
size_t block_length;
size_t digest_length;
void (*_reset)(void *ctx);
void (*_update)(void *ctx, const void *input, size_t len);
void (*_final)(void *ctx, void *digest);
struct {
unsigned char key[PICOHASH_MAX_BLOCK_LENGTH];
void (*hash_reset)(void *ctx);
void (*hash_final)(void *ctx, void *digest);
} _hmac;
} picohash_ctx_t;
static void picohash_init_md5(picohash_ctx_t *ctx);
static void picohash_init_sha1(picohash_ctx_t *ctx);
static void picohash_init_sha224(picohash_ctx_t *ctx);
static void picohash_init_sha256(picohash_ctx_t *ctx);
static void picohash_update(picohash_ctx_t *ctx, const void *input, size_t len);
static void picohash_final(picohash_ctx_t *ctx, void *digest);
static void picohash_reset(picohash_ctx_t *ctx);
static void picohash_init_hmac(picohash_ctx_t *ctx, void (*initf)(picohash_ctx_t *), const void *key, size_t key_len);
/* following are private definitions */
/*
* The basic MD5 functions.
*
* F is optimized compared to its RFC 1321 definition just like in Colin
* Plumb's implementation.
*/
#define _PICOHASH_MD5_F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define _PICOHASH_MD5_G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
#define _PICOHASH_MD5_H(x, y, z) ((x) ^ (y) ^ (z))
#define _PICOHASH_MD5_I(x, y, z) ((y) ^ ((x) | ~(z)))
/*
* The MD5 transformation for all four rounds.
*/
#define _PICOHASH_MD5_STEP(f, a, b, c, d, x, t, s) \
(a) += f((b), (c), (d)) + (x) + (t); \
(a) = (((a) << (s)) | (((a)&0xffffffff) >> (32 - (s)))); \
(a) += (b);
/*
* SET reads 4 input bytes in little-endian byte order and stores them
* in a properly aligned word in host byte order.
*
* The check for little-endian architectures which tolerate unaligned
* memory accesses is just an optimization. Nothing will break if it
* doesn't work.
*/
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
#define _PICOHASH_MD5_SET(n) (*(const uint32_t *)&ptr[(n)*4])
#define _PICOHASH_MD5_GET(n) _PICOHASH_MD5_SET(n)
#else
#define _PICOHASH_MD5_SET(n) \
(ctx->block[(n)] = (uint_fast32_t)ptr[(n)*4] | ((uint_fast32_t)ptr[(n)*4 + 1] << 8) | ((uint_fast32_t)ptr[(n)*4 + 2] << 16) | \
((uint_fast32_t)ptr[(n)*4 + 3] << 24))
#define _PICOHASH_MD5_GET(n) (ctx->block[(n)])
#endif
/*
* This processes one or more 64-byte data blocks, but does NOT update
* the bit counters. There're no alignment requirements.
*/
static const void *_picohash_md5_body(_picohash_md5_ctx_t *ctx, const void *data, size_t size)
{
const unsigned char *ptr;
uint_fast32_t a, b, c, d;
uint_fast32_t saved_a, saved_b, saved_c, saved_d;
ptr = data;
a = ctx->a;
b = ctx->b;
c = ctx->c;
d = ctx->d;
do {
saved_a = a;
saved_b = b;
saved_c = c;
saved_d = d;
/* Round 1 */
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, a, b, c, d, _PICOHASH_MD5_SET(0), 0xd76aa478, 7)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, d, a, b, c, _PICOHASH_MD5_SET(1), 0xe8c7b756, 12)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, c, d, a, b, _PICOHASH_MD5_SET(2), 0x242070db, 17)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, b, c, d, a, _PICOHASH_MD5_SET(3), 0xc1bdceee, 22)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, a, b, c, d, _PICOHASH_MD5_SET(4), 0xf57c0faf, 7)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, d, a, b, c, _PICOHASH_MD5_SET(5), 0x4787c62a, 12)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, c, d, a, b, _PICOHASH_MD5_SET(6), 0xa8304613, 17)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, b, c, d, a, _PICOHASH_MD5_SET(7), 0xfd469501, 22)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, a, b, c, d, _PICOHASH_MD5_SET(8), 0x698098d8, 7)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, d, a, b, c, _PICOHASH_MD5_SET(9), 0x8b44f7af, 12)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, c, d, a, b, _PICOHASH_MD5_SET(10), 0xffff5bb1, 17)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, b, c, d, a, _PICOHASH_MD5_SET(11), 0x895cd7be, 22)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, a, b, c, d, _PICOHASH_MD5_SET(12), 0x6b901122, 7)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, d, a, b, c, _PICOHASH_MD5_SET(13), 0xfd987193, 12)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, c, d, a, b, _PICOHASH_MD5_SET(14), 0xa679438e, 17)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_F, b, c, d, a, _PICOHASH_MD5_SET(15), 0x49b40821, 22)
/* Round 2 */
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, a, b, c, d, _PICOHASH_MD5_GET(1), 0xf61e2562, 5)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, d, a, b, c, _PICOHASH_MD5_GET(6), 0xc040b340, 9)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, c, d, a, b, _PICOHASH_MD5_GET(11), 0x265e5a51, 14)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, b, c, d, a, _PICOHASH_MD5_GET(0), 0xe9b6c7aa, 20)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, a, b, c, d, _PICOHASH_MD5_GET(5), 0xd62f105d, 5)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, d, a, b, c, _PICOHASH_MD5_GET(10), 0x02441453, 9)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, c, d, a, b, _PICOHASH_MD5_GET(15), 0xd8a1e681, 14)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, b, c, d, a, _PICOHASH_MD5_GET(4), 0xe7d3fbc8, 20)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, a, b, c, d, _PICOHASH_MD5_GET(9), 0x21e1cde6, 5)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, d, a, b, c, _PICOHASH_MD5_GET(14), 0xc33707d6, 9)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, c, d, a, b, _PICOHASH_MD5_GET(3), 0xf4d50d87, 14)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, b, c, d, a, _PICOHASH_MD5_GET(8), 0x455a14ed, 20)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, a, b, c, d, _PICOHASH_MD5_GET(13), 0xa9e3e905, 5)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, d, a, b, c, _PICOHASH_MD5_GET(2), 0xfcefa3f8, 9)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, c, d, a, b, _PICOHASH_MD5_GET(7), 0x676f02d9, 14)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_G, b, c, d, a, _PICOHASH_MD5_GET(12), 0x8d2a4c8a, 20)
/* Round 3 */
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, a, b, c, d, _PICOHASH_MD5_GET(5), 0xfffa3942, 4)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, d, a, b, c, _PICOHASH_MD5_GET(8), 0x8771f681, 11)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, c, d, a, b, _PICOHASH_MD5_GET(11), 0x6d9d6122, 16)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, b, c, d, a, _PICOHASH_MD5_GET(14), 0xfde5380c, 23)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, a, b, c, d, _PICOHASH_MD5_GET(1), 0xa4beea44, 4)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, d, a, b, c, _PICOHASH_MD5_GET(4), 0x4bdecfa9, 11)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, c, d, a, b, _PICOHASH_MD5_GET(7), 0xf6bb4b60, 16)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, b, c, d, a, _PICOHASH_MD5_GET(10), 0xbebfbc70, 23)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, a, b, c, d, _PICOHASH_MD5_GET(13), 0x289b7ec6, 4)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, d, a, b, c, _PICOHASH_MD5_GET(0), 0xeaa127fa, 11)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, c, d, a, b, _PICOHASH_MD5_GET(3), 0xd4ef3085, 16)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, b, c, d, a, _PICOHASH_MD5_GET(6), 0x04881d05, 23)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, a, b, c, d, _PICOHASH_MD5_GET(9), 0xd9d4d039, 4)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, d, a, b, c, _PICOHASH_MD5_GET(12), 0xe6db99e5, 11)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, c, d, a, b, _PICOHASH_MD5_GET(15), 0x1fa27cf8, 16)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_H, b, c, d, a, _PICOHASH_MD5_GET(2), 0xc4ac5665, 23)
/* Round 4 */
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, a, b, c, d, _PICOHASH_MD5_GET(0), 0xf4292244, 6)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, d, a, b, c, _PICOHASH_MD5_GET(7), 0x432aff97, 10)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, c, d, a, b, _PICOHASH_MD5_GET(14), 0xab9423a7, 15)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, b, c, d, a, _PICOHASH_MD5_GET(5), 0xfc93a039, 21)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, a, b, c, d, _PICOHASH_MD5_GET(12), 0x655b59c3, 6)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, d, a, b, c, _PICOHASH_MD5_GET(3), 0x8f0ccc92, 10)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, c, d, a, b, _PICOHASH_MD5_GET(10), 0xffeff47d, 15)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, b, c, d, a, _PICOHASH_MD5_GET(1), 0x85845dd1, 21)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, a, b, c, d, _PICOHASH_MD5_GET(8), 0x6fa87e4f, 6)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, d, a, b, c, _PICOHASH_MD5_GET(15), 0xfe2ce6e0, 10)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, c, d, a, b, _PICOHASH_MD5_GET(6), 0xa3014314, 15)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, b, c, d, a, _PICOHASH_MD5_GET(13), 0x4e0811a1, 21)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, a, b, c, d, _PICOHASH_MD5_GET(4), 0xf7537e82, 6)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, d, a, b, c, _PICOHASH_MD5_GET(11), 0xbd3af235, 10)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, c, d, a, b, _PICOHASH_MD5_GET(2), 0x2ad7d2bb, 15)
_PICOHASH_MD5_STEP(_PICOHASH_MD5_I, b, c, d, a, _PICOHASH_MD5_GET(9), 0xeb86d391, 21)
a += saved_a;
b += saved_b;
c += saved_c;
d += saved_d;
ptr += 64;
} while (size -= 64);
ctx->a = a;
ctx->b = b;
ctx->c = c;
ctx->d = d;
return ptr;
}
inline void _picohash_md5_init(_picohash_md5_ctx_t *ctx)
{
ctx->a = 0x67452301;
ctx->b = 0xefcdab89;
ctx->c = 0x98badcfe;
ctx->d = 0x10325476;
ctx->lo = 0;
ctx->hi = 0;
ctx->_body = _picohash_md5_body;
}
inline void _picohash_md5_update(_picohash_md5_ctx_t *ctx, const void *data, size_t size)
{
uint_fast32_t saved_lo;
unsigned long used, free;
saved_lo = ctx->lo;
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
ctx->hi++;
ctx->hi += size >> 29;
used = saved_lo & 0x3f;
if (used) {
free = 64 - used;
if (size < free) {
memcpy(&ctx->buffer[used], data, size);
return;
}
memcpy(&ctx->buffer[used], data, free);
data = (const unsigned char *)data + free;
size -= free;
ctx->_body(ctx, ctx->buffer, 64);
}
if (size >= 64) {
data = ctx->_body(ctx, data, size & ~(unsigned long)0x3f);
size &= 0x3f;
}
memcpy(ctx->buffer, data, size);
}
inline void _picohash_md5_final(_picohash_md5_ctx_t *ctx, void *_digest)
{
unsigned char *digest = _digest;
unsigned long used, free;
used = ctx->lo & 0x3f;
ctx->buffer[used++] = 0x80;
free = 64 - used;
if (free < 8) {
memset(&ctx->buffer[used], 0, free);
ctx->_body(ctx, ctx->buffer, 64);
used = 0;
free = 64;
}
memset(&ctx->buffer[used], 0, free - 8);
ctx->lo <<= 3;
ctx->buffer[56] = ctx->lo;
ctx->buffer[57] = ctx->lo >> 8;
ctx->buffer[58] = ctx->lo >> 16;
ctx->buffer[59] = ctx->lo >> 24;
ctx->buffer[60] = ctx->hi;
ctx->buffer[61] = ctx->hi >> 8;
ctx->buffer[62] = ctx->hi >> 16;
ctx->buffer[63] = ctx->hi >> 24;
ctx->_body(ctx, ctx->buffer, 64);
digest[0] = ctx->a;
digest[1] = ctx->a >> 8;
digest[2] = ctx->a >> 16;
digest[3] = ctx->a >> 24;
digest[4] = ctx->b;
digest[5] = ctx->b >> 8;
digest[6] = ctx->b >> 16;
digest[7] = ctx->b >> 24;
digest[8] = ctx->c;
digest[9] = ctx->c >> 8;
digest[10] = ctx->c >> 16;
digest[11] = ctx->c >> 24;
digest[12] = ctx->d;
digest[13] = ctx->d >> 8;
digest[14] = ctx->d >> 16;
digest[15] = ctx->d >> 24;
memset(ctx, 0, sizeof(*ctx));
}
#define _PICOHASH_SHA1_K0 0x5a827999
#define _PICOHASH_SHA1_K20 0x6ed9eba1
#define _PICOHASH_SHA1_K40 0x8f1bbcdc
#define _PICOHASH_SHA1_K60 0xca62c1d6
static inline uint32_t _picohash_sha1_rol32(uint32_t number, uint8_t bits)
{
return ((number << bits) | (number >> (32 - bits)));
}
static inline void _picohash_sha1_hash_block(_picohash_sha1_ctx_t *s)
{
uint8_t i;
uint32_t a, b, c, d, e, t;
a = s->state[0];
b = s->state[1];
c = s->state[2];
d = s->state[3];
e = s->state[4];
for (i = 0; i < 80; i++) {
if (i >= 16) {
t = s->buffer[(i + 13) & 15] ^ s->buffer[(i + 8) & 15] ^ s->buffer[(i + 2) & 15] ^ s->buffer[i & 15];
s->buffer[i & 15] = _picohash_sha1_rol32(t, 1);
}
if (i < 20) {
t = (d ^ (b & (c ^ d))) + _PICOHASH_SHA1_K0;
} else if (i < 40) {
t = (b ^ c ^ d) + _PICOHASH_SHA1_K20;
} else if (i < 60) {
t = ((b & c) | (d & (b | c))) + _PICOHASH_SHA1_K40;
} else {
t = (b ^ c ^ d) + _PICOHASH_SHA1_K60;
}
t += _picohash_sha1_rol32(a, 5) + e + s->buffer[i & 15];
e = d;
d = c;
c = _picohash_sha1_rol32(b, 30);
b = a;
a = t;
}
s->state[0] += a;
s->state[1] += b;
s->state[2] += c;
s->state[3] += d;
s->state[4] += e;
}
static inline void _picohash_sha1_add_uncounted(_picohash_sha1_ctx_t *s, uint8_t data)
{
uint8_t *const b = (uint8_t *)s->buffer;
#ifdef _PICOHASH_BIG_ENDIAN
b[s->bufferOffset] = data;
#else
b[s->bufferOffset ^ 3] = data;
#endif
s->bufferOffset++;
if (s->bufferOffset == PICOHASH_SHA1_BLOCK_LENGTH) {
_picohash_sha1_hash_block(s);
s->bufferOffset = 0;
}
}
inline void _picohash_sha1_init(_picohash_sha1_ctx_t *s)
{
s->state[0] = 0x67452301;
s->state[1] = 0xefcdab89;
s->state[2] = 0x98badcfe;
s->state[3] = 0x10325476;
s->state[4] = 0xc3d2e1f0;
s->byteCount = 0;
s->bufferOffset = 0;
}
inline void _picohash_sha1_update(_picohash_sha1_ctx_t *s, const void *_data, size_t len)
{
const uint8_t *data = _data;
for (; len != 0; --len) {
++s->byteCount;
_picohash_sha1_add_uncounted(s, *data++);
}
}
inline void _picohash_sha1_final(_picohash_sha1_ctx_t *s, void *digest)
{
// Pad with 0x80 followed by 0x00 until the end of the block
_picohash_sha1_add_uncounted(s, 0x80);
while (s->bufferOffset != 56)
_picohash_sha1_add_uncounted(s, 0x00);
// Append length in the last 8 bytes
_picohash_sha1_add_uncounted(s, s->byteCount >> 53); // Shifting to multiply by 8
_picohash_sha1_add_uncounted(s, s->byteCount >> 45); // as SHA-1 supports bitstreams as well as
_picohash_sha1_add_uncounted(s, s->byteCount >> 37); // byte.
_picohash_sha1_add_uncounted(s, s->byteCount >> 29);
_picohash_sha1_add_uncounted(s, s->byteCount >> 21);
_picohash_sha1_add_uncounted(s, s->byteCount >> 13);
_picohash_sha1_add_uncounted(s, s->byteCount >> 5);
_picohash_sha1_add_uncounted(s, s->byteCount << 3);
#ifndef SHA_BIG_ENDIAN
{ // Swap byte order back
int i;
for (i = 0; i < 5; i++) {
s->state[i] = (((s->state[i]) << 24) & 0xff000000) | (((s->state[i]) << 8) & 0x00ff0000) |
(((s->state[i]) >> 8) & 0x0000ff00) | (((s->state[i]) >> 24) & 0x000000ff);
}
}
#endif
memcpy(digest, s->state, sizeof(s->state));
}
#define _picohash_sha256_ch(x, y, z) (z ^ (x & (y ^ z)))
#define _picohash_sha256_maj(x, y, z) (((x | y) & z) | (x & y))
#define _picohash_sha256_s(x, y) \
(((((uint32_t)(x)&0xFFFFFFFFUL) >> (uint32_t)((y)&31)) | ((uint32_t)(x) << (uint32_t)(32 - ((y)&31)))) & 0xFFFFFFFFUL)
#define _picohash_sha256_r(x, n) (((x)&0xFFFFFFFFUL) >> (n))
#define _picohash_sha256_sigma0(x) (_picohash_sha256_s(x, 2) ^ _picohash_sha256_s(x, 13) ^ _picohash_sha256_s(x, 22))
#define _picohash_sha256_sigma1(x) (_picohash_sha256_s(x, 6) ^ _picohash_sha256_s(x, 11) ^ _picohash_sha256_s(x, 25))
#define _picohash_sha256_gamma0(x) (_picohash_sha256_s(x, 7) ^ _picohash_sha256_s(x, 18) ^ _picohash_sha256_r(x, 3))
#define _picohash_sha256_gamma1(x) (_picohash_sha256_s(x, 17) ^ _picohash_sha256_s(x, 19) ^ _picohash_sha256_r(x, 10))
#define _picohash_sha256_rnd(a, b, c, d, e, f, g, h, i) \
t0 = h + _picohash_sha256_sigma1(e) + _picohash_sha256_ch(e, f, g) + K[i] + W[i]; \
t1 = _picohash_sha256_sigma0(a) + _picohash_sha256_maj(a, b, c); \
d += t0; \
h = t0 + t1;
static inline void _picohash_sha256_compress(_picohash_sha256_ctx_t *ctx, unsigned char *buf)
{
static const uint32_t K[64] = {
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL};
uint32_t S[8], W[64], t, t0, t1;
int i;
/* copy state into S */
for (i = 0; i < 8; i++)
S[i] = ctx->state[i];
/* copy the state into 512-bits into W[0..15] */
for (i = 0; i < 16; i++)
W[i] =
(uint32_t)buf[4 * i] << 24 | (uint32_t)buf[4 * i + 1] << 16 | (uint32_t)buf[4 * i + 2] << 8 | (uint32_t)buf[4 * i + 3];
/* fill W[16..63] */
for (i = 16; i < 64; i++)
W[i] = _picohash_sha256_gamma1(W[i - 2]) + W[i - 7] + _picohash_sha256_gamma0(W[i - 15]) + W[i - 16];
/* Compress */
for (i = 0; i < 64; ++i) {
_picohash_sha256_rnd(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
t = S[7];
S[7] = S[6];
S[6] = S[5];
S[5] = S[4];
S[4] = S[3];
S[3] = S[2];
S[2] = S[1];
S[1] = S[0];
S[0] = t;
}
/* feedback */
for (i = 0; i < 8; i++)
ctx->state[i] = ctx->state[i] + S[i];
}
static inline void _picohash_sha256_do_final(_picohash_sha256_ctx_t *ctx, void *digest, size_t len)
{
unsigned char *out = digest;
size_t i;
/* increase the length of the message */
ctx->length += ctx->curlen * 8;
/* append the '1' bit */
ctx->buf[ctx->curlen++] = (unsigned char)0x80;
/* if the length is currently above 56 bytes we append zeros
* then compress. Then we can fall back to padding zeros and length
* encoding like normal.
*/
if (ctx->curlen > 56) {
while (ctx->curlen < 64) {
ctx->buf[ctx->curlen++] = (unsigned char)0;
}
_picohash_sha256_compress(ctx, ctx->buf);
ctx->curlen = 0;
}
/* pad upto 56 bytes of zeroes */
while (ctx->curlen < 56) {
ctx->buf[ctx->curlen++] = (unsigned char)0;
}
/* store length */
for (i = 0; i != 8; ++i)
ctx->buf[56 + i] = ctx->length >> (56 - 8 * i);
_picohash_sha256_compress(ctx, ctx->buf);
/* copy output */
for (i = 0; i != len / 4; ++i) {
out[i * 4] = ctx->state[i] >> 24;
out[i * 4 + 1] = ctx->state[i] >> 16;
out[i * 4 + 2] = ctx->state[i] >> 8;
out[i * 4 + 3] = ctx->state[i];
}
}
inline void _picohash_sha256_init(_picohash_sha256_ctx_t *ctx)
{
ctx->curlen = 0;
ctx->length = 0;
ctx->state[0] = 0x6A09E667UL;
ctx->state[1] = 0xBB67AE85UL;
ctx->state[2] = 0x3C6EF372UL;
ctx->state[3] = 0xA54FF53AUL;
ctx->state[4] = 0x510E527FUL;
ctx->state[5] = 0x9B05688CUL;
ctx->state[6] = 0x1F83D9ABUL;
ctx->state[7] = 0x5BE0CD19UL;
}
inline void _picohash_sha256_update(_picohash_sha256_ctx_t *ctx, const void *data, size_t len)
{
const unsigned char *in = data;
size_t n;
while (len > 0) {
if (ctx->curlen == 0 && len >= PICOHASH_SHA256_BLOCK_LENGTH) {
_picohash_sha256_compress(ctx, (unsigned char *)in);
ctx->length += PICOHASH_SHA256_BLOCK_LENGTH * 8;
in += PICOHASH_SHA256_BLOCK_LENGTH;
len -= PICOHASH_SHA256_BLOCK_LENGTH;
} else {
n = PICOHASH_SHA256_BLOCK_LENGTH - ctx->curlen;
if (n > len)
n = len;
memcpy(ctx->buf + ctx->curlen, in, (size_t)n);
ctx->curlen += n;
in += n;
len -= n;
if (ctx->curlen == 64) {
_picohash_sha256_compress(ctx, ctx->buf);
ctx->length += 8 * PICOHASH_SHA256_BLOCK_LENGTH;
ctx->curlen = 0;
}
}
}
}
inline void _picohash_sha256_final(_picohash_sha256_ctx_t *ctx, void *digest)
{
_picohash_sha256_do_final(ctx, digest, PICOHASH_SHA256_DIGEST_LENGTH);
}
inline void _picohash_sha224_init(_picohash_sha256_ctx_t *ctx)
{
ctx->curlen = 0;
ctx->length = 0;
ctx->state[0] = 0xc1059ed8UL;
ctx->state[1] = 0x367cd507UL;
ctx->state[2] = 0x3070dd17UL;
ctx->state[3] = 0xf70e5939UL;
ctx->state[4] = 0xffc00b31UL;
ctx->state[5] = 0x68581511UL;
ctx->state[6] = 0x64f98fa7UL;
ctx->state[7] = 0xbefa4fa4UL;
}
inline void _picohash_sha224_final(_picohash_sha256_ctx_t *ctx, void *digest)
{
_picohash_sha256_do_final(ctx, digest, PICOHASH_SHA224_DIGEST_LENGTH);
}
inline void picohash_init_md5(picohash_ctx_t *ctx)
{
ctx->block_length = PICOHASH_MD5_BLOCK_LENGTH;
ctx->digest_length = PICOHASH_MD5_DIGEST_LENGTH;
ctx->_reset = (void *)_picohash_md5_init;
ctx->_update = (void *)_picohash_md5_update;
ctx->_final = (void *)_picohash_md5_final;
_picohash_md5_init(&ctx->_md5);
}
inline void picohash_init_sha1(picohash_ctx_t *ctx)
{
ctx->block_length = PICOHASH_SHA1_BLOCK_LENGTH;
ctx->digest_length = PICOHASH_SHA1_DIGEST_LENGTH;
ctx->_reset = (void *)_picohash_sha1_init;
ctx->_update = (void *)_picohash_sha1_update;
ctx->_final = (void *)_picohash_sha1_final;
_picohash_sha1_init(&ctx->_sha1);
}
inline void picohash_init_sha224(picohash_ctx_t *ctx)
{
ctx->block_length = PICOHASH_SHA224_BLOCK_LENGTH;
ctx->digest_length = PICOHASH_SHA224_DIGEST_LENGTH;
ctx->_reset = (void *)_picohash_sha224_init;
ctx->_update = (void *)_picohash_sha256_update;
ctx->_final = (void *)_picohash_sha224_final;
_picohash_sha224_init(&ctx->_sha256);
}
inline void picohash_init_sha256(picohash_ctx_t *ctx)
{
ctx->block_length = PICOHASH_SHA256_BLOCK_LENGTH;
ctx->digest_length = PICOHASH_SHA256_DIGEST_LENGTH;
ctx->_reset = (void *)_picohash_sha256_init;
ctx->_update = (void *)_picohash_sha256_update;
ctx->_final = (void *)_picohash_sha256_final;
_picohash_sha256_init(&ctx->_sha256);
}
inline void picohash_update(picohash_ctx_t *ctx, const void *input, size_t len)
{
ctx->_update(ctx, input, len);
}
inline void picohash_final(picohash_ctx_t *ctx, void *digest)
{
ctx->_final(ctx, digest);
}
inline void picohash_reset(picohash_ctx_t *ctx)
{
ctx->_reset(ctx);
}
static inline void _picohash_hmac_apply_key(picohash_ctx_t *ctx, unsigned char delta)
{
size_t i;
for (i = 0; i != ctx->block_length; ++i)
ctx->_hmac.key[i] ^= delta;
picohash_update(ctx, ctx->_hmac.key, ctx->block_length);
for (i = 0; i != ctx->block_length; ++i)
ctx->_hmac.key[i] ^= delta;
}
static void _picohash_hmac_final(picohash_ctx_t *ctx, void *digest)
{
unsigned char inner_digest[PICOHASH_MAX_DIGEST_LENGTH];
ctx->_hmac.hash_final(ctx, inner_digest);
ctx->_hmac.hash_reset(ctx);
_picohash_hmac_apply_key(ctx, 0x5c);
picohash_update(ctx, inner_digest, ctx->digest_length);
memset(inner_digest, 0, ctx->digest_length);
ctx->_hmac.hash_final(ctx, digest);
}
static inline void _picohash_hmac_reset(picohash_ctx_t *ctx)
{
ctx->_hmac.hash_reset(ctx);
_picohash_hmac_apply_key(ctx, 0x36);
}
inline void picohash_init_hmac(picohash_ctx_t *ctx, void (*initf)(picohash_ctx_t *), const void *key, size_t key_len)
{
initf(ctx);
memset(ctx->_hmac.key, 0, ctx->block_length);
if (key_len > ctx->block_length) {
/* hash the key if it is too long */
picohash_update(ctx, key, key_len);
picohash_final(ctx, ctx->_hmac.key);
ctx->_hmac.hash_reset(ctx);
} else {
memcpy(ctx->_hmac.key, key, key_len);
}
/* replace reset and final function */
ctx->_hmac.hash_reset = ctx->_reset;
ctx->_hmac.hash_final = ctx->_final;
ctx->_reset = (void *)_picohash_hmac_reset;
ctx->_final = (void *)_picohash_hmac_final;
/* start calculating the inner hash */
_picohash_hmac_apply_key(ctx, 0x36);
}
#endif
@Flix01
Copy link
Author

Flix01 commented Feb 26, 2024

Forked from gist syzdek/totp.c

topt.c

  • still depends on libopenssl/libcrypto (now it uses the high-level API).
  • added support for base64 and raw input key encoding (so now it supports: b32, b64 and raw encodings).
  • added support for different sha algorithms (depending on libopenssl/libcrypto support, typically: sha1, sha224, sha256, sha512, etc.).
  • added totp_compile_and_test_linux.sh to compile, run and test the program on Linux using the original test vectors from rfc6238.

topt_standalone.c

  • does not depend on libopenssl/libcrypto (replaced by picohash.h, included in this gist).
  • added support for base64 and raw input key encoding (so now it supports: b32, b64 and raw encodings).
  • added support for different sha algorithms (only sha1, sha224 and sha256 are supported. sha512 is not supported).
  • added totp_standalone_compile_and_test_linux.sh to compile, run and test the program on Linux using the original test vectors from rfc6238 (without the sha512 tests).

No security approach has been taken to protect the programs from hackers! Use them at your own risk!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment