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