2

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);
}

1 Answers1

1

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;
}
Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • i tried your solution but its still dosen't generate the correct OTP. I could try used the function from openssl but I dont know how to install it... – daniel danino Jan 15 '22 at 05:21