diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib-otp/otp-parse.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/src/lib-otp/otp-parse.c b/src/lib-otp/otp-parse.c new file mode 100644 index 0000000..70b6940 --- /dev/null +++ b/src/lib-otp/otp-parse.c @@ -0,0 +1,253 @@ +/* + * OTP extended response parser. + * + * Copyright (c) 2006 Andrey Panin <pazke@donpac.ru> + * + * This software is released under the MIT license. + */ + +#include "lib.h" +#include "buffer.h" +#include "str.h" +#include "strfuncs.h" +#include "hex-binary.h" + +#include "otp.h" + +#include <ctype.h> + +#define IS_LWS(c) ((c) == ' ' || (c) == '\t') + +static inline const char *otp_skip_lws(const char *data) +{ + while (*data != '\0' && IS_LWS(*data)) + data++; + return data; +} + +static inline bool otp_check_tail(const char *data) +{ + data = otp_skip_lws(data); + + return *data != 0; +} + +int otp_read_hex(const char *data, const char **endptr, unsigned char *hash) +{ + string_t *str; + buffer_t buf; + unsigned int i = 0; + + if (data == NULL) + return -1; + + str = t_str_new(18); + buffer_create_from_data(&buf, hash, OTP_HASH_SIZE); + + while (*data != '\0') { + char c = *data; + + if (i_isxdigit(c)) { + str_append_c(str, c); + if (++i == OTP_HASH_SIZE * 2) { + data++; + break; + } + } else if (!IS_LWS(c)) { + *endptr = data; + return -1; + } + data++; + } + + *endptr = data; + + if (i < OTP_HASH_SIZE * 2) + return -1; + + return hex_to_binary(str_c(str), &buf); +} + +#define add_word() do { \ + tmp = otp_lookup_word(str_c(word)); \ + buffer_append(&buf, &tmp, sizeof(tmp)); \ + count++; \ +} while (0) + +int ATTR_NO_SANITIZE_INTEGER +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 != '\0' && (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 != NULL) + *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 ? 1 : 0; +} + +int otp_read_new_params(const char *data, const char **endptr, + struct otp_state *state) +{ + const char *p, *s; + 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; + if (str_parse_int(s, &state->seq, &p) < 0 || !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) ? 1 : 0; +} + +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)) { + *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)); +} |