bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "lib.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "base64.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "hex-binary.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "str.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "hash-method.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen#include "hash-format.h"
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenenum hash_encoding {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen HASH_ENCODING_HEX,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen HASH_ENCODING_HEX_SHORT,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen HASH_ENCODING_BASE64
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen};
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstruct hash_format_list {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen struct hash_format_list *next;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const struct hash_method *method;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen void *context;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen unsigned int bits;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen enum hash_encoding encoding;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen};
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstruct hash_format {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen pool_t pool;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const char *str;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen struct hash_format_list *list, **pos;
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen unsigned char *digest;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen};
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstatic int
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenhash_format_parse(const char *str, unsigned int *idxp,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const struct hash_method **method_r,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen unsigned int *bits_r, const char **error_r)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const char *name, *end, *bitsp;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen unsigned int bits, i = *idxp;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen /* we should have "hash_name}" or "hash_name:bits}" */
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen end = strchr(str+i, '}');
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (end == NULL) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *error_r = "Missing '}'";
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return -1;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *idxp = end - str;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen name = t_strdup_until(str+i, end);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen bitsp = strchr(name, ':');
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (bitsp != NULL)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen name = t_strdup_until(name, bitsp++);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *method_r = hash_method_lookup(name);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (*method_r == NULL) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *error_r = t_strconcat("Unknown hash method: ", name, NULL);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return -1;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen bits = (*method_r)->digest_size * 8;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (bitsp != NULL) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (str_to_uint(bitsp, &bits) < 0 ||
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen bits == 0 || bits > (*method_r)->digest_size*8) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *error_r = t_strconcat("Invalid :bits number: ",
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen bitsp, NULL);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return -1;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if ((bits % 8) != 0) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *error_r = t_strconcat(
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen "Currently :bits must be divisible by 8: ",
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen bitsp, NULL);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return -1;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *bits_r = bits;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return 0;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstatic int
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenhash_format_string_analyze(struct hash_format *format, const char *str,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const char **error_r)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen struct hash_format_list *list;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen unsigned int i;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen for (i = 0; str[i] != '\0'; i++) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (str[i] != '%')
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen continue;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i++;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen list = p_new(format->pool, struct hash_format_list, 1);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen list->encoding = HASH_ENCODING_HEX;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *format->pos = list;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen format->pos = &list->next;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (str[i] == 'B') {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen list->encoding = HASH_ENCODING_BASE64;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i++;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen } else if (str[i] == 'X') {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen list->encoding = HASH_ENCODING_HEX_SHORT;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i++;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (str[i++] != '{') {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *error_r = "No '{' after '%'";
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return -1;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (hash_format_parse(str, &i, &list->method,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen &list->bits, error_r) < 0)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return -1;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen list->context = p_malloc(format->pool,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen list->method->context_size);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen list->method->init(list->context);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return 0;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenint hash_format_init(const char *format_string, struct hash_format **format_r,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const char **error_r)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen struct hash_format *format;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen pool_t pool;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen int ret;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen pool = pool_alloconly_create("hash format", 1024);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen format = p_new(pool, struct hash_format, 1);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen format->pool = pool;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen format->str = p_strdup(pool, format_string);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen format->pos = &format->list;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen T_BEGIN {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen ret = hash_format_string_analyze(format, format_string,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen error_r);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (ret < 0)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *error_r = p_strdup(format->pool, *error_r);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen } T_END;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (ret < 0) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *error_r = t_strdup(*error_r);
c971768955f826fb965d8ffbb13dac93c9bbead8Phil Carmody pool_unref(&pool);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return -1;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *format_r = format;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen return 0;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenvoid hash_format_loop(struct hash_format *format,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const void *data, size_t size)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen struct hash_format_list *list;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen for (list = format->list; list != NULL; list = list->next)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen list->method->loop(list->context, data, size);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainenvoid hash_format_reset(struct hash_format *format)
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen{
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen struct hash_format_list *list;
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen for (list = format->list; list != NULL; list = list->next) {
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen memset(list->context, 0, list->method->context_size);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen list->method->init(list->context);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen }
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen}
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenstatic void
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenhash_format_digest(string_t *dest, const struct hash_format_list *list,
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const unsigned char *digest)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen unsigned int i, orig_len, size = list->bits / 8;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i_assert(list->bits % 8 == 0);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen switch (list->encoding) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen case HASH_ENCODING_HEX:
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen binary_to_hex_append(dest, digest, size);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen break;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen case HASH_ENCODING_HEX_SHORT:
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen orig_len = str_len(dest);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen binary_to_hex_append(dest, digest, size);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen /* drop leading zeros, except if it's the only one */
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen for (i = orig_len; i < str_len(dest); i++) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (str_data(dest)[i] != '0')
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen break;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (i == str_len(dest)) i--;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen str_delete(dest, orig_len, i-orig_len);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen break;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen case HASH_ENCODING_BASE64:
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen orig_len = str_len(dest);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen base64_encode(digest, size, dest);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen /* drop trailing '=' chars */
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen while (str_len(dest) > orig_len &&
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen str_data(dest)[str_len(dest)-1] == '=')
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen str_truncate(dest, str_len(dest)-1);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen break;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainenvoid hash_format_write(struct hash_format *format, string_t *dest)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen struct hash_format_list *list;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen const char *p;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen unsigned int i, max_digest_size = 0;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen for (list = format->list; list != NULL; list = list->next) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (max_digest_size < list->method->digest_size)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen max_digest_size = list->method->digest_size;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen if (format->digest == NULL)
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen format->digest = p_malloc(format->pool, max_digest_size);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen list = format->list;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen for (i = 0; format->str[i] != '\0'; i++) {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen if (format->str[i] != '%') {
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen str_append_c(dest, format->str[i]);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen continue;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen /* we already verified that the string is ok */
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i_assert(list != NULL);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen list->method->result(list->context, format->digest);
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen hash_format_digest(dest, list, format->digest);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen list = list->next;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen p = strchr(format->str+i, '}');
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i_assert(p != NULL);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen i = p - format->str;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen }
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen}
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainenvoid hash_format_deinit(struct hash_format **_format, string_t *dest)
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen{
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen struct hash_format *format = *_format;
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen *_format = NULL;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
ddd1adf19cacc6186fbc713d255e1e82086d7751Timo Sirainen hash_format_write(format, dest);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen pool_unref(&format->pool);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainenvoid hash_format_deinit_free(struct hash_format **_format)
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen{
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen struct hash_format *format = *_format;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen *_format = NULL;
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen pool_unref(&format->pool);
37bd98570b30513255a19cc52de16594bc4256e0Timo Sirainen}