someone know why this TOTP code doesn't generate the same OTP like google authenticator??? the SHA1-HASH function is working well, it's just the final OTP that dosen't generated correct. I tried to check everything and I couldn't find where my mistake. can someone help please? this code supposed to be a very basic code to generate TOTP.
#include <stdio.h>
#include <math.h>
#include <time.h>
#include "SHA_1.h"
void xor (char *arr, int length, int value)
{
int i;
for (i = 0; i < length; i++)
arr[i] ^= value;
}
int main()
{
int CT, i, j;
long int T;
const int T0 = 0, Tx = 30;
const char o_pad = 0x5C, i_pad = 0x36;
unsigned char result[20];
unsigned char counter[8];
unsigned char secretKey[64] = "abcdefghijklmnop";
// HMAC
unsigned char block[128];
unsigned char inner_key[64];
unsigned char outter_key[64];
//HOTP
int offset;
long int truncatedHash;
long int finalOTP;
T = (long int)(time(NULL));
CT = floor((T - T0) / Tx);
for (i = strlen(counter)-1; i >= 0; i--)
{
counter[i] = (unsigned char)(CT & 0xFF);
CT >>= 8;
}
// HMAC
// Inner Key = K xor 0x36
memset(inner_key, 0, 64);
memcpy(inner_key, secretKey, strlen(secretKey));
xor(inner_key, sizeof(inner_key), i_pad);
// Outter Key = K xor 0x5C
memset(outter_key, 0, 64);
memcpy(outter_key, secretKey, strlen(secretKey));
xor(outter_key, sizeof(outter_key), o_pad);
// Hash Inner
memset(block, 0, sizeof(block));
for (i = 0; i < 64; i++)
{
block[i] = inner_key[i];
}
for (i = 0; i < strlen(counter); i++)
{
block[i + 64] = counter[i];
}
SHA1_HASH(block, strlen(block), result);
// Hash Outter
memset(block, 0, sizeof(block));
for (i = 0; i < 64; i++)
{
block[i] = outter_key[i];
}
for (i = 0; i < strlen(result); i++)
{
block[i + 64] = result[i];
}
SHA1_HASH(block, strlen(block), result);
offset = result[19] & 0x0f;
truncatedHash = ((result[offset] & 0x7f) << 24) | ((result[offset + 1] & 0xff) << 16) | ((result[offset + 2] & 0xff) << 8) | (result[offset + 3] & 0xff);
finalOTP = (truncatedHash % (long int)pow(10, 6));
printf("%d", finalOTP);
return 0;
}
EDITED
My SHA_1 code:
#include <stdio.h>
#include <string.h>
// circular rotate function
unsigned int S(int n, unsigned int word)
{
return ((word << n) | (word >> (32 - n)));
}
// constant K
const unsigned int K[4] = {
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6};
// H values
unsigned int H[5] = {
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476,
0xC3D2E1F0};
void SHA1_HASH(unsigned char *input,int input_length, char *output)
{
// Variabels Declaration
unsigned char M[128];
unsigned int W[80];
unsigned int j, i, TEMP, A, B, C, D, E;
unsigned char HH[20], temp_HH[20];
int M_size;
// clear M
memset(M, 0, sizeof(M));
// Copy the messege into M
strcpy(M, input);
// add the length of the messege to the end of M
if (input_length > 55)
{
M[127] = (unsigned char)(input_length * 8);
M[126] = (unsigned char)(input_length * 8 >> 8);
M_size = 2;
}
else
{
M[63] = (unsigned char)(input_length * 8);
M[62] = (unsigned char)(input_length * 8 >> 8);
M_size = 1;
}
// set bit '1' after the messege
M[input_length] |= (unsigned char)0x80;
for(j=0; j<M_size; j++){
//init Words in W[0] - W[15]
for (i = 0; i < 16; i++)
{
W[i] = ((unsigned int)M[(4 * i)+(64*j)] << 24) | ((unsigned int)M[(1 + (4 * i))+(64*j)] << 16) | ((unsigned int)M[(2 + (4 * i))+(64*j)] << 8) | ((unsigned int)M[(3 + (4 * i))+(64*j)]);
}
for (i = 16; i < 80; i++)
{
W[i] = S(1, W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]);
}
//start of the SHA-1 alghoritem
A = H[0];
B = H[1];
C = H[2];
D = H[3];
E = H[4];
for (i = 0; i < 20; i++) // round 1
{
TEMP = S(5, A) + ((B & C) | ((~B) & D)) + E + W[i] + K[0];
E = D;
D = C;
C = S(30, B);
B = A;
A = TEMP;
}
for (i = 20; i < 40; i++) //round 2
{
TEMP = S(5, A) + (B ^ C ^ D) + E + W[i] + K[1];
E = D;
D = C;
C = S(30, B);
B = A;
A = TEMP;
}
for (i = 40; i < 60; i++) //round 3
{
TEMP = S(5, A) + ((B & C) | (B & D) | (C & D)) + E + W[i] + K[2];
E = D;
D = C;
C = S(30, B);
B = A;
A = TEMP;
}
for (i = 60; i < 80; i++) //round 4
{
TEMP = S(5, A) + (B ^ C ^ D) + E + W[i] + K[3];
E = D;
D = C;
C = S(30, B);
B = A;
A = TEMP;
}
H[0] += A;
H[1] += B;
H[2] += C;
H[3] += D;
H[4] += E;
}
sprintf(HH, "%x", H[0]);
for (i = 1; i < 5; i++)
{
sprintf(temp_HH, "%x", H[i]);
strcat(HH, temp_HH);
}
// end of SHA-1 alghoritem
// return the algoritem result
strcpy(output, HH);
}
You have some UB (undefined behavior).
You're doing strlen(counter) but counter is unitialized.
And, for crypto, you probably want sizeof(counter) as you've done in other places.
There are other places to use sizeof instead of strlen. In the code below, I changed all of them. That may or may not be correct, so you'll have to review that.
Here's a refactored version:
I've used cpp conditionals to denote old vs new code:
#if 0
// old code
#else
// new code
#endif
Here is the code. I didn't have access to your version of the SHA1 code, so I used openssl. If you use that, link the code with -lssl -lcrypto:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <time.h>
#if 0
#include "SHA_1.h"
#else
#include <openssl/sha.h>
void
SHA1_HASH(const unsigned char *buf,size_t len,unsigned char *result)
{
SHA_CTX ctx;
SHA1_Init(&ctx);
SHA1_Update(&ctx,buf,len);
SHA1_Final(result,&ctx);
}
#endif
void
xor(char *arr, int length, int value)
{
int i;
for (i = 0; i < length; i++)
arr[i] ^= value;
}
int
main()
{
int CT, i, j;
long int T;
const int T0 = 0,
Tx = 30;
const char o_pad = 0x5C,
i_pad = 0x36;
unsigned char result[20];
unsigned char counter[8];
unsigned char secretKey[64] = "abcdefghijklmnop";
// HMAC
unsigned char block[128];
unsigned char inner_key[64];
unsigned char outter_key[64];
// HOTP
int offset;
long int truncatedHash;
long int finalOTP;
T = (long int) (time(NULL));
CT = floor((T - T0) / Tx);
#if 0
for (i = strlen(counter) - 1; i >= 0; i--) {
#else
for (i = sizeof(counter) - 1; i >= 0; i--) {
#endif
counter[i] = (unsigned char) (CT & 0xFF);
CT >>= 8;
}
// HMAC
// Inner Key = K xor 0x36
#if 0
memset(inner_key, 0, 64);
memcpy(inner_key, secretKey, strlen(secretKey));
#else
memset(inner_key, 0, sizeof(inner_key));
strcpy(inner_key, secretKey);
#endif
xor(inner_key, sizeof(inner_key), i_pad);
// Outter Key = K xor 0x5C
#if 0
memset(outter_key, 0, 64);
memcpy(outter_key, secretKey, strlen(secretKey));
#else
memset(outter_key, 0, sizeof(outter_key));
strcpy(outter_key, secretKey);
#endif
xor(outter_key, sizeof(outter_key), o_pad);
// Hash Inner
memset(block, 0, sizeof(block));
#if 0
for (i = 0; i < 64; i++) {
block[i] = inner_key[i];
}
#else
for (i = 0; i < sizeof(inner_key); i++) {
block[i] = inner_key[i];
}
#endif
#if 0
for (i = 0; i < strlen(counter); i++) {
block[i + 64] = counter[i];
}
#else
for (i = 0; i < sizeof(counter); i++) {
block[i + sizeof(inner_key)] = counter[i];
}
#endif
#if 0
SHA1_HASH(block, strlen(block), result);
#else
SHA1_HASH(block, sizeof(block), result);
#endif
// Hash Outter
memset(block, 0, sizeof(block));
#if 0
for (i = 0; i < 64; i++) {
block[i] = outter_key[i];
}
#else
for (i = 0; i < sizeof(outter_key); i++) {
block[i] = outter_key[i];
}
#endif
#if 0
for (i = 0; i < strlen(result); i++) {
block[i + 64] = result[i];
}
#else
for (i = 0; i < sizeof(result); i++) {
block[i + sizeof(outter_key)] = result[i];
}
#endif
#if 0
SHA1_HASH(block, strlen(block), result);
#else
SHA1_HASH(block, sizeof(block), result);
#endif
offset = result[19] & 0x0f;
truncatedHash = ((result[offset] & 0x7f) << 24) |
((result[offset + 1] & 0xff) << 16) |
((result[offset + 2] & 0xff) << 8) |
(result[offset + 3] & 0xff);
finalOTP = (truncatedHash % (long int) pow(10, 6));
printf("%d\n", finalOTP);
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With