otp.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 09 Apr 2020 02:15:46 -0400
changeset 60 a0629a9e3ee6
parent 57 4974e5368a29
permissions -rw-r--r--
otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
55
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
     1
#include <stdio.h>
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
     2
#include <stdint.h>
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
     3
#include <string.h>
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
     4
#include <time.h>
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
     5
#include "sha1.h"
56
a573346e6f7b Added One Time Password support.
Ryan C. Gordon <icculus@icculus.org>
parents: 55
diff changeset
     6
#include "otp.h"
55
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
     7
60
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
     8
static uint8_t sanitize_base32_input(const char ch)
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
     9
{
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
    10
    /* Google Authenticator checks for these values and corrects them,
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
    11
       assuming this was a human error */
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
    12
    if (ch == '0') return (uint8_t) 'O';
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
    13
    else if (ch == '1') return (uint8_t) 'L';
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
    14
    else if (ch == '8') return (uint8_t) 'B';
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
    15
    return (uint8_t) ch;
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
    16
}
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
    17
55
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    18
static int base32_decode(const char *src, const int srclen, uint8_t *dst, const int dstlen)
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    19
{
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    20
    const int len = srclen == -1 ? strlen((const char *) src) : srclen;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    21
    int retval = 0;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    22
    uint32_t accum = 0;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    23
    int shifter = 0;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    24
    int i;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    25
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    26
    for (i = 0; i < len; i++) {
60
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
    27
        const uint8_t ch = sanitize_base32_input(src[i]);
55
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    28
        uint8_t val;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    29
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    30
        if ((ch >= 'A') && (ch <= 'Z')) {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    31
            val = ch - 'A';
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    32
        } else if ((ch >= '2') && (ch <= '7')) {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    33
            val = (ch - '2') + 26;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    34
        } else if (ch == '=') {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    35
            val = 0;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    36
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    37
        /* these are illegal in base32, but GAuth keys might have them. */
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    38
        } else if (ch == ' ') {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    39
            continue;  /* skip these. */
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    40
        } else if ((ch >= 'a') && (ch <= 'z')) {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    41
            val = ch - 'a'; /* treat like uppercase. */
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    42
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    43
        } else {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    44
            return -1;  /* invalid string. */
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    45
        }
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    46
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    47
        accum = (accum << 5) | ((uint32_t) val);
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    48
        shifter += 5;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    49
        if (shifter >= 8) {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    50
            if (retval > dstlen) {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    51
                return -1;  /* dst too small */
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    52
            }
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    53
            dst[retval] = (uint8_t) ((accum >> (shifter - 8)) & 0xFF);
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    54
            retval++;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    55
            shifter -= 8;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    56
        }
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    57
    }
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    58
60
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
    59
#if 0   // Apparently for Google Authenticator, we just drop extra bits...?
55
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    60
    if (shifter > 0) {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    61
        if (retval > dstlen) {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    62
            return -1;  /* dst too small */
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    63
        }
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    64
        dst[retval] = (uint8_t) (accum & 0xFF);
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    65
        retval++;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    66
    }
60
a0629a9e3ee6 otp: Some base32-decoding fixes to match what Google Authenticator expects.
Ryan C. Gordon <icculus@icculus.org>
parents: 57
diff changeset
    67
#endif
55
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    68
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    69
    return retval;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    70
}
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    71
56
a573346e6f7b Added One Time Password support.
Ryan C. Gordon <icculus@icculus.org>
parents: 55
diff changeset
    72
int totp(const char *base32_secret, char *dst, int dstlen)
55
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    73
{
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    74
    uint8_t decoded[64];
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    75
    int decodedlen;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    76
    uint64_t secs;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    77
    uint8_t timebytes[8];
57
4974e5368a29 Minor type cleanup ("unsigned char" -> "uint8_t").
Ryan C. Gordon <icculus@icculus.org>
parents: 56
diff changeset
    78
    uint8_t digest[SHA1_DIGEST_LENGTH];
55
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    79
    uint8_t *bytes;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    80
    uint32_t val;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    81
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    82
    decodedlen = base32_decode(base32_secret, -1, decoded, sizeof (decoded));
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    83
    if (decodedlen == -1) {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    84
        return -1;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    85
    }
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    86
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    87
    secs = ((uint64_t) time(NULL)) / 30;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    88
    for (int i = 0; i < 8; i++) {
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    89
        timebytes[i] = (uint8_t) (secs >> ((7-i) * 8));
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    90
    }
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    91
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    92
    SHA1Hmac(decoded, decodedlen, timebytes, 8, digest);
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    93
57
4974e5368a29 Minor type cleanup ("unsigned char" -> "uint8_t").
Ryan C. Gordon <icculus@icculus.org>
parents: 56
diff changeset
    94
    bytes = digest + (digest[SHA1_DIGEST_LENGTH-1] & 0xF);
55
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    95
    val = (((uint32_t) bytes[0]) << 24) | (((uint32_t) bytes[1]) << 16) |
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    96
          (((uint32_t) bytes[2]) << 8) | (((uint32_t) bytes[3]));
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    97
    val &= 0x7FFFFFFF; /* drop most significant bit. */
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    98
    val %= 1000000;  /* make it six digits long. */
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
    99
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
   100
    snprintf(dst, dstlen, "%06u", (unsigned int) val);
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
   101
    return 0;
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
   102
}
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
   103
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
   104
/* end of otp.c ... */
0aaf56a96d21 Added initial code for producing time-based One Time Passwords.
Ryan C. Gordon <icculus@icculus.org>
parents:
diff changeset
   105