Skip to content

Instantly share code, notes, and snippets.

@colesnicov
Forked from Flix01/totp.c
Created May 22, 2024 19:38
Show Gist options
  • Save colesnicov/8a121b29da1c758fe67a454723b66cc2 to your computer and use it in GitHub Desktop.
Save colesnicov/8a121b29da1c758fe67a454723b66cc2 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment