otp.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 18 Jun 2017 19:40:30 -0400
changeset 56 a573346e6f7b
parent 55 0aaf56a96d21
child 57 4974e5368a29
permissions -rw-r--r--
Added One Time Password support.

This is only for time-based OTP for now ("TOPT" algorithm), but that's more
or less what one expects to see in the wild anyhow.

This is sort of a placeholder UI until I replace the entire existing UI with
something better.
icculus@55
     1
#include <stdio.h>
icculus@55
     2
#include <stdint.h>
icculus@55
     3
#include <string.h>
icculus@55
     4
#include <time.h>
icculus@55
     5
#include "sha1.h"
icculus@56
     6
#include "otp.h"
icculus@55
     7
icculus@55
     8
static int base32_decode(const char *src, const int srclen, uint8_t *dst, const int dstlen)
icculus@55
     9
{
icculus@55
    10
    const int len = srclen == -1 ? strlen((const char *) src) : srclen;
icculus@55
    11
    int retval = 0;
icculus@55
    12
    uint32_t accum = 0;
icculus@55
    13
    int shifter = 0;
icculus@55
    14
    int i;
icculus@55
    15
icculus@55
    16
    for (i = 0; i < len; i++) {
icculus@55
    17
        const uint8_t ch = (uint8_t) src[i];
icculus@55
    18
        uint8_t val;
icculus@55
    19
icculus@55
    20
        if ((ch >= 'A') && (ch <= 'Z')) {
icculus@55
    21
            val = ch - 'A';
icculus@55
    22
        } else if ((ch >= '2') && (ch <= '7')) {
icculus@55
    23
            val = (ch - '2') + 26;
icculus@55
    24
        } else if (ch == '=') {
icculus@55
    25
            val = 0;
icculus@55
    26
icculus@55
    27
        /* these are illegal in base32, but GAuth keys might have them. */
icculus@55
    28
        } else if (ch == ' ') {
icculus@55
    29
            continue;  /* skip these. */
icculus@55
    30
        } else if ((ch >= 'a') && (ch <= 'z')) {
icculus@55
    31
            val = ch - 'a'; /* treat like uppercase. */
icculus@55
    32
icculus@55
    33
        } else {
icculus@55
    34
            return -1;  /* invalid string. */
icculus@55
    35
        }
icculus@55
    36
icculus@55
    37
        accum = (accum << 5) | ((uint32_t) val);
icculus@55
    38
        shifter += 5;
icculus@55
    39
        if (shifter >= 8) {
icculus@55
    40
            if (retval > dstlen) {
icculus@55
    41
                return -1;  /* dst too small */
icculus@55
    42
            }
icculus@55
    43
            dst[retval] = (uint8_t) ((accum >> (shifter - 8)) & 0xFF);
icculus@55
    44
            retval++;
icculus@55
    45
            shifter -= 8;
icculus@55
    46
        }
icculus@55
    47
    }
icculus@55
    48
icculus@55
    49
    if (shifter > 0) {
icculus@55
    50
        if (retval > dstlen) {
icculus@55
    51
            return -1;  /* dst too small */
icculus@55
    52
        }
icculus@55
    53
        dst[retval] = (uint8_t) (accum & 0xFF);
icculus@55
    54
        retval++;
icculus@55
    55
    }
icculus@55
    56
icculus@55
    57
    return retval;
icculus@55
    58
}
icculus@55
    59
icculus@56
    60
int totp(const char *base32_secret, char *dst, int dstlen)
icculus@55
    61
{
icculus@55
    62
    uint8_t decoded[64];
icculus@55
    63
    int decodedlen;
icculus@55
    64
    uint64_t secs;
icculus@55
    65
    uint8_t timebytes[8];
icculus@55
    66
    unsigned char digest[SHA1_DIGEST_LENGTH];
icculus@55
    67
    uint8_t *bytes;
icculus@55
    68
    uint32_t val;
icculus@55
    69
icculus@55
    70
    decodedlen = base32_decode(base32_secret, -1, decoded, sizeof (decoded));
icculus@55
    71
    if (decodedlen == -1) {
icculus@55
    72
        return -1;
icculus@55
    73
    }
icculus@55
    74
icculus@55
    75
    secs = ((uint64_t) time(NULL)) / 30;
icculus@55
    76
    for (int i = 0; i < 8; i++) {
icculus@55
    77
        timebytes[i] = (uint8_t) (secs >> ((7-i) * 8));
icculus@55
    78
    }
icculus@55
    79
icculus@55
    80
    SHA1Hmac(decoded, decodedlen, timebytes, 8, digest);
icculus@55
    81
icculus@55
    82
    bytes = (uint8_t *) (digest + (digest[SHA1_DIGEST_LENGTH-1] & 0xF));
icculus@55
    83
    val = (((uint32_t) bytes[0]) << 24) | (((uint32_t) bytes[1]) << 16) |
icculus@55
    84
          (((uint32_t) bytes[2]) << 8) | (((uint32_t) bytes[3]));
icculus@55
    85
    val &= 0x7FFFFFFF; /* drop most significant bit. */
icculus@55
    86
    val %= 1000000;  /* make it six digits long. */
icculus@55
    87
icculus@55
    88
    snprintf(dst, dstlen, "%06u", (unsigned int) val);
icculus@55
    89
    return 0;
icculus@55
    90
}
icculus@55
    91
icculus@55
    92
/* end of otp.c ... */
icculus@55
    93