otp-parse.c revision d19412e30b27e413f99cbedab86c9f9396741638
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen/*
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen * OTP extended response parser.
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen *
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen * Copyright (c) 2006 Andrey Panin <pazke@donpac.ru>
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen *
e074ffeaee1ce283bd42f167c6810e3d013f8218Timo Sirainen * This software is released under the MIT license.
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen */
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen#include "buffer.h"
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen#include "str.h"
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen#include "strfuncs.h"
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "hex-binary.h"
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen#include "otp.h"
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen#include <stdlib.h>
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen#include <ctype.h>
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen#define IS_LWS(c) ((c) == ' ' || (c) == '\t')
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainenstatic inline const char *otp_skip_lws(const char *data)
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen{
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen while (*data && IS_LWS(*data))
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen data++;
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen return data;
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen}
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenstatic inline int otp_check_tail(const char *data)
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen{
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen data = otp_skip_lws(data);
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen return *data != 0;
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen}
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen
462b8c71015b2483cff3c59fa3668246dc72dfceAndrey Paninint otp_read_hex(const char *data, const char **endptr, unsigned char *hash)
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen{
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen string_t *str;
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen buffer_t buf;
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen unsigned int i = 0;
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen if (data == NULL)
1e3a608d8d0e08cb7d549718fbfbcc148fdb236fTimo Sirainen return -1;
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen str = t_str_new(18);
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen buffer_create_from_data(&buf, hash, OTP_HASH_SIZE);
1e3a608d8d0e08cb7d549718fbfbcc148fdb236fTimo Sirainen
1e3a608d8d0e08cb7d549718fbfbcc148fdb236fTimo Sirainen while (*data) {
1e3a608d8d0e08cb7d549718fbfbcc148fdb236fTimo Sirainen char c = *data;
1e3a608d8d0e08cb7d549718fbfbcc148fdb236fTimo Sirainen
1e3a608d8d0e08cb7d549718fbfbcc148fdb236fTimo Sirainen if (i_isxdigit(c)) {
1e3a608d8d0e08cb7d549718fbfbcc148fdb236fTimo Sirainen str_append_c(str, c);
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen if (++i == OTP_HASH_SIZE * 2) {
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen data++;
4ac5448461b63de9637de839fbc611a3d503287cTimo Sirainen break;
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen }
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen } else if (!IS_LWS(c)) {
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen *endptr = data;
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen return -1;
e03ec0b7b9d92551331bc509bcd86920544171d1Timo Sirainen }
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen data++;
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen }
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen *endptr = data;
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen if (i < OTP_HASH_SIZE * 2)
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen return -1;
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return hex_to_binary(str_c(str), &buf);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen}
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen#define add_word() do { \
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen tmp = otp_lookup_word(str_c(word)); \
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen buffer_append(&buf, &tmp, sizeof(tmp)); \
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen count++; \
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen} while (0)
int otp_read_words(const char *data, const char **endptr, unsigned char *hash)
{
bool space = FALSE;
unsigned int len = 0, count = 0;
unsigned int parity = 0, bits[OTP_WORDS_NUMBER], tmp;
string_t *word;
buffer_t buf;
if (data == NULL)
return -1;
word = t_str_new(8);
data = otp_skip_lws(data);
buffer_create_from_data(&buf, bits, sizeof(bits));
for (; *data && (count < OTP_WORDS_NUMBER); data++) {
char c = *data;
if (space) {
if (IS_LWS(c))
continue;
else if (i_isalpha(c)) {
str_append_c(word, c);
space = FALSE;
len = 1;
continue;
}
} else {
if (i_isalpha(c)) {
if (++len > OTP_MAX_WORD_LEN) {
count = 0;
break;
}
str_append_c(word, c);
continue;
} else if (IS_LWS(c)) {
add_word();
str_truncate(word, 0);
space = TRUE;
continue;
}
}
break;
}
if ((str_len(word) > 0) && (count == OTP_WORDS_NUMBER - 1))
add_word();
if (endptr)
*endptr = data;
if (count < OTP_WORDS_NUMBER)
return -1;
hash[0] = bits[0] >> 3;
hash[1] = ((bits[0] & 7) << 5) | (bits[1] >> 6);
hash[2] = ((bits[1] & 0x3f) << 2) | (bits[2] >> 9);
hash[3] = (bits[2] >> 1) & 0xff;
hash[4] = ((bits[2] & 3) << 7) | (bits[3] >> 4);
hash[5] = ((bits[3] & 15) << 4) | (bits[4] >> 7);
hash[6] = ((bits[4] & 0x7f) << 1) | (bits[5] >> 10);
hash[7] = (bits[5] >> 2) & 0xff;
parity = bits[5] & 3;
return otp_parity(hash) != parity;
}
int otp_read_new_params(const char *data, const char **endptr,
struct otp_state *state)
{
const char *p, *s;
char *end;
unsigned int i = 0;
int algo;
s = p = data;
while ((*p != 0) && !IS_LWS(*p)) p++;
if (*p == 0)
return -1;
algo = digest_find(t_strdup_until(s, p++));
if (algo < 0)
return -2;
state->algo = algo;
s = p;
state->seq = strtol(s, &end, 10); p = end;
if ((p == s) || !IS_LWS(*p))
return -3;
p++;
while (i_isalnum(*p) && (i < OTP_MAX_SEED_LEN))
state->seed[i++] = i_tolower(*p++);
state->seed[i] = 0;
*endptr = p;
return 0;
}
int otp_parse_response(const char *data, unsigned char *hash, bool hex)
{
const char *end;
int ret = hex ? otp_read_hex(data, &end, hash) :
otp_read_words(data, &end, hash);
if (ret < 0)
return ret;
return otp_check_tail(end);
}
int otp_parse_init_response(const char *data, struct otp_state *new_state,
unsigned char *hash, bool hex, const char **error)
{
const char *end;
int ret = hex ? otp_read_hex(data, &end, hash) :
otp_read_words(data, &end, hash);
if (ret < 0) {
*error = "invalid current OTP";
return ret;
}
end = otp_skip_lws(end);
if (*end++ != ':') {
*error = "missing colon";
return -1;
}
ret = otp_read_new_params(end, &end, new_state);
if (ret < 0) {
*error = "invalid OTP parameters";
return -1;
}
end = otp_skip_lws(end);
if (*end++ != ':') {
*error = "missing colon";
return -1;
}
ret = hex ? otp_read_hex(end, &end, new_state->hash) :
otp_read_words(end, &end, new_state->hash);
if (ret < 0) {
*error = "invalid new OTP";
return -1;
}
if (otp_check_tail(end) != 0) {
*error = "trailing garbage found";
return -1;
}
return 0;
}
int otp_parse_dbentry(const char *text, struct otp_state *state)
{
const char *end;
int ret;
ret = otp_read_new_params(text, &end, state);
if (ret != 0)
return ret;
if (*end++ != ' ')
return -1;
return otp_read_hex(end, &end, state->hash);
}
const char *otp_print_dbentry(const struct otp_state *state)
{
return t_strdup_printf("%s %d %s %s", digest_name(state->algo),
state->seq, state->seed,
binary_to_hex(state->hash, 8));
}