commit 232a2fbe05734fbfd36ef91d35d6934d89474829 Author: Jacob Welsh AuthorDate: Sat Nov 5 23:02:40 2022 +0000 Commit: Jacob Welsh CommitDate: Sat Nov 5 23:02:40 2022 +0000 Type: dead code removal [1/2] drop all files for dcrypt library and oauth2 which depended on it http://fixpoint.welshcomputing.com/2022/jwrd-logs-for-Nov-2022/#5307 diff --git a/doc/example-config/dovecot-oauth2.conf.ext b/doc/example-config/dovecot-oauth2.conf.ext deleted file mode 100644 index 4b3b8baf79..0000000000 --- a/doc/example-config/dovecot-oauth2.conf.ext +++ /dev/null @@ -1,69 +0,0 @@ -### OAuth2 password database configuration - -## url for verifying token validity. Token is appended to the URL -# tokeninfo_url = http://endpoint/oauth/tokeninfo?access_token= - -## introspection endpoint, used to gather extra fields and other information. -# introspection_url = http://endpoint/oauth/me - -## How introspection is made, valid values are -## auth = GET request with Bearer authentication -## get = GET request with token appended to URL -## post = POST request with token=bearer_token as content -## local = perform local validation only -# introspection_mode = auth - -## Force introspection even if tokeninfo contains wanted fields -## Set this to yes if you are using active_attribute -# force_introspection = no - -## Validation key dictionary (e.g. fs:posix:prefix=/etc/dovecot/keys/) -## Lookup key is /shared/// -# local_validation_key_dict = - -## A single wanted scope of validity (optional) -# scope = something - -## username attribute in response (default: email) -# username_attribute = email - -## username normalization format (default: %Lu) -# username_format = %Lu - -## Attribute name for checking whether account is disabled (optional) -# active_attribute = - -## Expected value in active_attribute (empty = require present, but anything goes) -# active_value = - -## Expected issuer(s) for the token (space separated list) -# issuers = - -## URL to RFC 7628 OpenID Provider Configuration Information schema -# openid_configuration_url = - -## Extra fields to set in passdb response (in passdb static style) -# pass_attrs = - -## Timeout in milliseconds -# timeout_msecs = 0 - -## Enable debug logging -# debug = no - -## Max parallel connections (how many simultaneous connections to open) -# max_parallel_connections = 10 - -## Max pipelined requests (how many requests to send per connection, requires server-side support) -# max_pipelined_requests = 1 - -## HTTP request raw log directory -# rawlog_dir = /tmp/oauth2 - -## TLS settings -# tls_ca_cert_file = /path/to/ca-certificates.txt -# tls_ca_cert_dir = /path/to/certs/ -# tls_cert_file = /path/to/client/cert -# tls_key_file = /path/to/client/key -# tls_cipher_suite = HIGH:!SSLv2 -# tls_allow_invalid_cert = FALSE diff --git a/src/auth/db-oauth2.c b/src/auth/db-oauth2.c deleted file mode 100644 index 3c6ef3acb9..0000000000 --- a/src/auth/db-oauth2.c +++ /dev/null @@ -1,885 +0,0 @@ -/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ - -#include "auth-common.h" -#include "array.h" -#include "str.h" -#include "var-expand.h" -#include "env-util.h" -#include "var-expand.h" -#include "settings.h" -#include "oauth2.h" -#include "http-client.h" -#include "http-url.h" -#include "iostream-ssl.h" -#include "auth-request.h" -#include "auth-settings.h" -#include "passdb.h" -#include "passdb-template.h" -#include "llist.h" -#include "db-oauth2.h" -#include "dcrypt.h" -#include "dict.h" - -#include - -struct passdb_oauth2_settings { - /* tokeninfo endpoint, format https://endpoint/somewhere?token= */ - const char *tokeninfo_url; - /* password grant endpoint, format https://endpoint/somewhere */ - const char *grant_url; - /* introspection endpoint, format https://endpoint/somewhere */ - const char *introspection_url; - /* expected scope, optional */ - const char *scope; - /* mode of introspection, one of get, get-auth, post - - get: append token to url - - get-auth: send token with header Authorization: Bearer token - - post: send token= as POST request - */ - const char *introspection_mode; - /* normalization var-expand template for username, defaults to %Lu */ - const char *username_format; - /* name of username attribute to lookup, mandatory */ - const char *username_attribute; - /* name of account is active attribute, optional */ - const char *active_attribute; - /* expected active value for active attribute, optional */ - const char *active_value; - /* client identificator for oauth2 server */ - const char *client_id; - /* not really used, but have to present by oauth2 specs */ - const char *client_secret; - /* template to expand into passdb */ - const char *pass_attrs; - /* template to expand into key path, turns on local validation support */ - const char *local_validation_key_dict; - /* valid token issuers */ - const char *issuers; - /* The URL for a document following the OpenID Provider Configuration - Information schema, see - - https://datatracker.ietf.org/doc/html/rfc7628#section-3.2.2 - */ - const char *openid_configuration_url; - - /* TLS options */ - const char *tls_ca_cert_file; - const char *tls_ca_cert_dir; - const char *tls_cert_file; - const char *tls_key_file; - const char *tls_cipher_suite; - - /* HTTP rawlog directory */ - const char *rawlog_dir; - - /* HTTP client options */ - unsigned int timeout_msecs; - unsigned int max_idle_time_msecs; - unsigned int max_parallel_connections; - unsigned int max_pipelined_requests; - bool tls_allow_invalid_cert; - - bool debug; - /* Should introspection be done even if not necessary */ - bool force_introspection; - /* Should we send service and local/remote endpoints as X-Dovecot-Auth headers */ - bool send_auth_headers; - bool use_grant_password; -}; - -struct db_oauth2 { - struct db_oauth2 *prev,*next; - - pool_t pool; - - const char *config_path; - struct passdb_oauth2_settings set; - struct http_client *client; - struct passdb_template *tmpl; - struct oauth2_settings oauth2_set; - - struct db_oauth2_request *head; - - unsigned int refcount; -}; - -static struct db_oauth2 *db_oauth2_head = NULL; - -#undef DEF_STR -#undef DEF_BOOL -#undef DEF_INT - -#define DEF_STR(name) DEF_STRUCT_STR(name, passdb_oauth2_settings) -#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, passdb_oauth2_settings) -#define DEF_INT(name) DEF_STRUCT_INT(name, passdb_oauth2_settings) - -static struct setting_def setting_defs[] = { - DEF_STR(tokeninfo_url), - DEF_STR(grant_url), - DEF_STR(introspection_url), - DEF_STR(scope), - DEF_BOOL(force_introspection), - DEF_STR(introspection_mode), - DEF_STR(username_format), - DEF_STR(username_attribute), - DEF_STR(pass_attrs), - DEF_STR(local_validation_key_dict), - DEF_STR(active_attribute), - DEF_STR(active_value), - DEF_STR(client_id), - DEF_STR(client_secret), - DEF_STR(issuers), - DEF_STR(openid_configuration_url), - DEF_INT(timeout_msecs), - DEF_INT(max_idle_time_msecs), - DEF_INT(max_parallel_connections), - DEF_INT(max_pipelined_requests), - DEF_BOOL(send_auth_headers), - DEF_BOOL(use_grant_password), - - DEF_STR(tls_ca_cert_file), - DEF_STR(tls_ca_cert_dir), - DEF_STR(tls_cert_file), - DEF_STR(tls_key_file), - DEF_STR(tls_cipher_suite), - DEF_BOOL(tls_allow_invalid_cert), - - DEF_STR(rawlog_dir), - - DEF_BOOL(debug), - - { 0, NULL, 0 } -}; - -static struct passdb_oauth2_settings default_oauth2_settings = { - .tokeninfo_url = "", - .grant_url = "", - .introspection_url = "", - .scope = "", - .force_introspection = FALSE, - .introspection_mode = "", - .username_format = "%Lu", - .username_attribute = "email", - .active_attribute = "active", - .active_value = "true", - .client_id = "", - .client_secret = "", - .issuers = "", - .openid_configuration_url = "", - .pass_attrs = "", - .local_validation_key_dict = "", - .rawlog_dir = "", - .timeout_msecs = 0, - .max_idle_time_msecs = 60000, - .max_parallel_connections = 10, - .max_pipelined_requests = 1, - .tls_ca_cert_file = NULL, - .tls_ca_cert_dir = NULL, - .tls_cert_file = NULL, - .tls_key_file = NULL, - .tls_cipher_suite = "HIGH:!SSLv2", - .tls_allow_invalid_cert = FALSE, - .send_auth_headers = FALSE, - .use_grant_password = FALSE, - .debug = FALSE, -}; - -static const char *parse_setting(const char *key, const char *value, - struct db_oauth2 *db) -{ - return parse_setting_from_defs(db->pool, setting_defs, - &db->set, key, value); -} - -struct db_oauth2 *db_oauth2_init(const char *config_path) -{ - struct db_oauth2 *db; - const char *error; - struct ssl_iostream_settings ssl_set; - struct http_client_settings http_set; - - for(db = db_oauth2_head; db != NULL; db = db->next) { - if (strcmp(db->config_path, config_path) == 0) { - db->refcount++; - return db; - } - } - - pool_t pool = pool_alloconly_create("db_oauth2", 128); - db = p_new(pool, struct db_oauth2, 1); - db->pool = pool; - db->refcount = 1; - db->config_path = p_strdup(pool, config_path); - db->set = default_oauth2_settings; - - if (!settings_read_nosection(config_path, parse_setting, db, &error)) - i_fatal("oauth2 %s: %s", config_path, error); - - db->tmpl = passdb_template_build(pool, db->set.pass_attrs); - - i_zero(&ssl_set); - i_zero(&http_set); - - ssl_set.cipher_list = db->set.tls_cipher_suite; - ssl_set.ca_file = db->set.tls_ca_cert_file; - ssl_set.ca_dir = db->set.tls_ca_cert_dir; - if (db->set.tls_cert_file != NULL && *db->set.tls_cert_file != '\0') { - ssl_set.cert.cert = db->set.tls_cert_file; - ssl_set.cert.key = db->set.tls_key_file; - } - ssl_set.prefer_server_ciphers = TRUE; - ssl_set.allow_invalid_cert = db->set.tls_allow_invalid_cert; - ssl_set.verbose = db->set.debug; - ssl_set.verbose_invalid_cert = db->set.debug; - http_set.ssl = &ssl_set; - - http_set.dns_client_socket_path = "dns-client"; - http_set.user_agent = "dovecot-oauth2-passdb/" DOVECOT_VERSION; - - if (*db->set.local_validation_key_dict == '\0' && - *db->set.tokeninfo_url == '\0' && - (*db->set.grant_url == '\0' || *db->set.client_id == '\0') && - *db->set.introspection_url == '\0') - i_fatal("oauth2: Password grant, tokeninfo, introspection URL or " - "validation key dictionary must be given"); - - if (*db->set.rawlog_dir != '\0') - http_set.rawlog_dir = db->set.rawlog_dir; - - http_set.max_idle_time_msecs = db->set.max_idle_time_msecs; - http_set.max_parallel_connections = db->set.max_parallel_connections; - http_set.max_pipelined_requests = db->set.max_pipelined_requests; - http_set.no_auto_redirect = FALSE; - http_set.no_auto_retry = TRUE; - http_set.debug = db->set.debug; - http_set.event_parent = auth_event; - - db->client = http_client_init(&http_set); - - i_zero(&db->oauth2_set); - db->oauth2_set.client = db->client; - db->oauth2_set.tokeninfo_url = db->set.tokeninfo_url, - db->oauth2_set.grant_url = db->set.grant_url, - db->oauth2_set.introspection_url = db->set.introspection_url; - db->oauth2_set.client_id = db->set.client_id; - db->oauth2_set.client_secret = db->set.client_secret; - db->oauth2_set.timeout_msecs = db->set.timeout_msecs; - db->oauth2_set.send_auth_headers = db->set.send_auth_headers; - db->oauth2_set.use_grant_password = db->set.use_grant_password; - db->oauth2_set.scope = db->set.scope; - - if (*db->set.active_attribute != '\0' && - *db->set.active_value == '\0') - i_fatal("oauth2: Cannot have empty active_value if active_attribute is set"); - if (*db->set.active_attribute == '\0' && - *db->set.active_value != '\0') - i_fatal("oauth2: Cannot have empty active_attribute is active_value is set"); - - if (*db->set.introspection_mode == '\0' || - strcmp(db->set.introspection_mode, "auth") == 0) { - db->oauth2_set.introspection_mode = INTROSPECTION_MODE_GET_AUTH; - } else if (strcmp(db->set.introspection_mode, "get") == 0) { - db->oauth2_set.introspection_mode = INTROSPECTION_MODE_GET; - } else if (strcmp(db->set.introspection_mode, "post") == 0) { - db->oauth2_set.introspection_mode = INTROSPECTION_MODE_POST; - } else if (strcmp(db->set.introspection_mode, "local") == 0) { - if (*db->set.local_validation_key_dict == '\0') - i_fatal("oauth2: local_validation_key_dict is required " - "for local introspection."); - db->oauth2_set.introspection_mode = INTROSPECTION_MODE_LOCAL; - } else { - i_fatal("oauth2: Invalid value '%s' for introspection mode, must be on auth, get, post or local", - db->set.introspection_mode); - } - - if (db->oauth2_set.introspection_mode == INTROSPECTION_MODE_LOCAL) { - struct dict_settings dict_set = { - .base_dir = global_auth_settings->base_dir, - .event_parent = auth_event, - }; - if (dict_init(db->set.local_validation_key_dict, &dict_set, - &db->oauth2_set.key_dict, &error) < 0) - i_fatal("Cannot initialize key dict: %s", error); - /* failure to initialize dcrypt is not fatal - we can still - validate HMAC based keys */ - (void)dcrypt_initialize(NULL, NULL, NULL); - /* initialize key cache */ - db->oauth2_set.key_cache = oauth2_validation_key_cache_init(); - } - - if (*db->set.issuers != '\0') - db->oauth2_set.issuers = (const char *const *) - p_strsplit_spaces(pool, db->set.issuers, " "); - - if (*db->set.openid_configuration_url != '\0') { - struct http_url *parsed_url ATTR_UNUSED; - if (http_url_parse(db->set.openid_configuration_url, NULL, 0, - pool_datastack_create(), &parsed_url, - &error) < 0) { - i_fatal("Invalid openid_configuration_url: %s", - error); - } - } - - DLLIST_PREPEND(&db_oauth2_head, db); - - return db; -} - -void db_oauth2_ref(struct db_oauth2 *db) -{ - i_assert(db->refcount > 0); - db->refcount++; -} - -void db_oauth2_unref(struct db_oauth2 **_db) -{ - struct db_oauth2 *ptr, *db = *_db; - i_assert(db->refcount > 0); - - if (--db->refcount > 0) return; - - for(ptr = db_oauth2_head; ptr != NULL; ptr = ptr->next) { - if (ptr == db) { - DLLIST_REMOVE(&db_oauth2_head, ptr); - break; - } - } - - i_assert(ptr != NULL && ptr == db); - - /* make sure all requests are aborted */ - while (db->head != NULL) - oauth2_request_abort(&db->head->req); - - http_client_deinit(&db->client); - if (db->oauth2_set.key_dict != NULL) - dict_deinit(&db->oauth2_set.key_dict); - oauth2_validation_key_cache_deinit(&db->oauth2_set.key_cache); - pool_unref(&db->pool); -} - -static void -db_oauth2_add_openid_config_url(struct db_oauth2_request *req) -{ - /* FIXME: HORRIBLE HACK - REMOVE ME!!! - It is because the mech has not been implemented properly - that we need to pass the config url in this strange way. - - This **must** be moved to mech-oauth2 once the validation - result et al is handled there. - */ - req->auth_request->openid_config_url = - p_strdup_empty(req->auth_request->pool, - req->db->set.openid_configuration_url); -} - -static bool -db_oauth2_have_all_fields(struct db_oauth2_request *req) -{ - unsigned int n,i; - unsigned int size,idx; - const char *const *args = passdb_template_get_args(req->db->tmpl, &n); - - if (req->fields == NULL) - return FALSE; - - for(i=1;ifields, ptr+7)) - return FALSE; - ptr = ptr+size; - } - } - } - - if (!auth_fields_exists(req->fields, req->db->set.username_attribute)) - return FALSE; - if (*req->db->set.active_attribute != '\0' && !auth_fields_exists(req->fields, req->db->set.active_attribute)) - return FALSE; - - return TRUE; -} - -static const char *field_get_default(const char *data) -{ - const char *p; - - p = strchr(data, ':'); - if (p == NULL) - return ""; - else { - /* default value given */ - return p+1; - } -} - -static int db_oauth2_var_expand_func_oauth2(const char *data, void *context, - const char **value_r, - const char **error_r ATTR_UNUSED) -{ - struct db_oauth2_request *ctx = context; - const char *field_name = t_strcut(data, ':'); - const char *value = NULL; - - if (ctx->fields != NULL) - value = auth_fields_find(ctx->fields, field_name); - *value_r = value != NULL ? value : field_get_default(data); - - return 1; -} - -static const char *escape_none(const char *value, const struct auth_request *req ATTR_UNUSED) -{ - return value; -} - -static const struct var_expand_table * -db_oauth2_value_get_var_expand_table(struct auth_request *auth_request, - const char *oauth2_value) -{ - struct var_expand_table *table; - unsigned int count = 1; - - table = auth_request_get_var_expand_table_full(auth_request, - auth_request->fields.user, NULL, &count); - table[0].key = '$'; - table[0].value = oauth2_value; - return table; -} - -static bool -db_oauth2_template_export(struct db_oauth2_request *req, - enum passdb_result *result_r, const char **error_r) -{ - /* var=$ expands into var=${oauth2:var} */ - const struct var_expand_func_table funcs_table[] = { - { "oauth2", db_oauth2_var_expand_func_oauth2 }, - { NULL, NULL } - }; - string_t *dest; - const char *const *args, *value, *error; - struct passdb_template *tmpl = req->db->tmpl; - unsigned int i, count; - - if (passdb_template_is_empty(tmpl)) - return TRUE; - - dest = t_str_new(256); - args = passdb_template_get_args(tmpl, &count); - i_assert((count % 2) == 0); - for (i = 0; i < count; i += 2) { - if (args[i+1] == NULL) - value = ""; - else { - str_truncate(dest, 0); - const struct var_expand_table * - table = db_oauth2_value_get_var_expand_table(req->auth_request, - auth_fields_find(req->fields, args[i])); - if (var_expand_with_funcs(dest, args[i+1], table, funcs_table, - req, &error) < 0) { - *error_r = t_strdup_printf( - "var_expand(%s) failed: %s", - args[i+1], error); - *result_r = PASSDB_RESULT_INTERNAL_FAILURE; - return FALSE; - } - value = str_c(dest); - } - - auth_request_set_field(req->auth_request, args[i], value, - STATIC_PASS_SCHEME); - } - return TRUE; -} - -static void db_oauth2_fields_merge(struct db_oauth2_request *req, - ARRAY_TYPE(oauth2_field) *fields) -{ - const struct oauth2_field *field; - - if (req->fields == NULL) - req->fields = auth_fields_init(req->pool); - - array_foreach(fields, field) { - e_debug(authdb_event(req->auth_request), - "Processing field %s", - field->name); - auth_fields_add(req->fields, field->name, field->value, 0); - } -} - -static const char * -db_oauth2_field_find(const ARRAY_TYPE(oauth2_field) *fields, const char *name) -{ - const struct oauth2_field *f; - - array_foreach(fields, f) { - if (strcmp(f->name, name) == 0) - return f->value; - } - return NULL; -} - -static void db_oauth2_callback(struct db_oauth2_request *req, - enum passdb_result result, - const char *error_prefix, const char *error) -{ - db_oauth2_lookup_callback_t *callback = req->callback; - req->callback = NULL; - - i_assert(result == PASSDB_RESULT_OK || error != NULL); - - if (result != PASSDB_RESULT_OK) - db_oauth2_add_openid_config_url(req); - - /* Successful lookups were logged by the caller. Failed lookups will be - logged either with e_error() or e_info() by the callback. */ - if (callback != NULL) { - DLLIST_REMOVE(&req->db->head, req); - if (result != PASSDB_RESULT_OK) - error = t_strconcat(error_prefix, error, NULL); - callback(req, result, error, req->context); - } -} - -static bool -db_oauth2_validate_username(struct db_oauth2_request *req, - enum passdb_result *result_r, const char **error_r) -{ - const char *error; - struct var_expand_table table[] = { - { 'u', NULL, "user" }, - { 'n', NULL, "username" }, - { 'd', NULL, "domain" }, - { '\0', NULL, NULL } - }; - const char *username_value = - auth_fields_find(req->fields, req->db->set.username_attribute); - - if (username_value == NULL) { - *result_r = PASSDB_RESULT_INTERNAL_FAILURE; - *error_r = "No username returned"; - return FALSE; - } - - table[0].value = username_value; - table[1].value = t_strcut(username_value, '@'); - table[2].value = i_strchr_to_next(username_value, '@'); - - string_t *username_req = t_str_new(32); - string_t *username_val = t_str_new(strlen(username_value)); - - if (auth_request_var_expand(username_req, req->db->set.username_format, req->auth_request, escape_none, &error) < 0 || - var_expand(username_val, req->db->set.username_format, table, &error) < 0) { - *error_r = t_strdup_printf("var_expand(%s) failed: %s", - req->db->set.username_format, error); - *result_r = PASSDB_RESULT_INTERNAL_FAILURE; - return FALSE; - } else if (!str_equals(username_req, username_val)) { - *error_r = t_strdup_printf("Username '%s' did not match '%s'", - str_c(username_req), str_c(username_val)); - *result_r = PASSDB_RESULT_USER_UNKNOWN; - return FALSE; - } else { - return TRUE; - } -} - -static bool -db_oauth2_user_is_enabled(struct db_oauth2_request *req, - enum passdb_result *result_r, const char **error_r) -{ - if (*req->db->set.active_attribute != '\0' && - *req->db->set.active_value != '\0') { - const char *active_value = - auth_fields_find(req->fields, req->db->set.active_attribute); - if (active_value != NULL && - strcmp(req->db->set.active_value, active_value) != 0) { - *error_r = "Provided token is not valid"; - *result_r = PASSDB_RESULT_PASSWORD_MISMATCH; - return FALSE; - } - } - return TRUE; -} - -static bool -db_oauth2_token_in_scope(struct db_oauth2_request *req, - enum passdb_result *result_r, const char **error_r) -{ - if (*req->db->set.scope != '\0') { - bool found = FALSE; - const char *value = auth_fields_find(req->fields, "scope"); - if (value == NULL) - value = auth_fields_find(req->fields, "aud"); - e_debug(authdb_event(req->auth_request), - "Token scope(s): %s", - value); - if (value != NULL) { - const char **wanted_scopes = - t_strsplit_spaces(req->db->set.scope, " "); - const char **scopes = t_strsplit_spaces(value, " "); - for (; !found && *wanted_scopes != NULL; wanted_scopes++) - found = str_array_find(scopes, *wanted_scopes); - } - if (!found) { - *error_r = t_strdup_printf("Token is not valid for scope '%s'", - req->db->set.scope); - *result_r = PASSDB_RESULT_USER_DISABLED; - return FALSE; - } - } - return TRUE; -} - -static void db_oauth2_process_fields(struct db_oauth2_request *req, - enum passdb_result *result_r, - const char **error_r) -{ - *error_r = NULL; - - if (db_oauth2_user_is_enabled(req, result_r, error_r) && - db_oauth2_validate_username(req, result_r, error_r) && - db_oauth2_token_in_scope(req, result_r, error_r) && - db_oauth2_template_export(req, result_r, error_r)) { - *result_r = PASSDB_RESULT_OK; - } else { - i_assert(*result_r != PASSDB_RESULT_OK && *error_r != NULL); - } -} - -static void -db_oauth2_introspect_continue(struct oauth2_request_result *result, - struct db_oauth2_request *req) -{ - enum passdb_result passdb_result; - const char *error; - - req->req = NULL; - - if (result->error != NULL) { - /* fail here */ - passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; - error = result->error; - } else { - e_debug(authdb_event(req->auth_request), - "Introspection succeeded"); - db_oauth2_fields_merge(req, result->fields); - db_oauth2_process_fields(req, &passdb_result, &error); - } - db_oauth2_callback(req, passdb_result, "Introspection failed: ", error); -} - -static void db_oauth2_lookup_introspect(struct db_oauth2_request *req) -{ - struct oauth2_request_input input; - i_zero(&input); - - e_debug(authdb_event(req->auth_request), - "Making introspection request to %s", - req->db->set.introspection_url); - input.token = req->token; - input.local_ip = req->auth_request->fields.local_ip; - input.local_port = req->auth_request->fields.local_port; - input.remote_ip = req->auth_request->fields.remote_ip; - input.remote_port = req->auth_request->fields.remote_port; - input.real_local_ip = req->auth_request->fields.real_local_ip; - input.real_local_port = req->auth_request->fields.real_local_port; - input.real_remote_ip = req->auth_request->fields.real_remote_ip; - input.real_remote_port = req->auth_request->fields.real_remote_port; - input.service = req->auth_request->fields.service; - - req->req = oauth2_introspection_start(&req->db->oauth2_set, &input, - db_oauth2_introspect_continue, req); -} - -static void db_oauth2_local_validation(struct db_oauth2_request *req, - const char *token) -{ - bool is_jwt ATTR_UNUSED; - const char *error = NULL; - enum passdb_result passdb_result; - ARRAY_TYPE(oauth2_field) fields; - t_array_init(&fields, 8); - if (oauth2_try_parse_jwt(&req->db->oauth2_set, token, - &fields, &is_jwt, &error) < 0) { - passdb_result = PASSDB_RESULT_PASSWORD_MISMATCH; - } else { - db_oauth2_fields_merge(req, &fields); - db_oauth2_process_fields(req, &passdb_result, &error); - } - if (passdb_result == PASSDB_RESULT_OK) { - e_debug(authdb_event(req->auth_request), - "Local validation succeeded"); - } - db_oauth2_callback(req, passdb_result, - "Local validation failed: ", error); -} - -static void -db_oauth2_lookup_continue_valid(struct db_oauth2_request *req, - ARRAY_TYPE(oauth2_field) *fields, - const char *error_prefix) -{ - enum passdb_result passdb_result; - const char *error; - - db_oauth2_fields_merge(req, fields); - if (db_oauth2_have_all_fields(req) && - !req->db->set.force_introspection) { - /* pass */ - } else if (req->db->oauth2_set.introspection_mode == - INTROSPECTION_MODE_LOCAL) { - e_debug(authdb_event(req->auth_request), - "Attempting to locally validate token"); - db_oauth2_local_validation(req, req->token); - return; - } else if (!db_oauth2_user_is_enabled(req, &passdb_result, &error)) { - db_oauth2_callback(req, passdb_result, - "Token is not valid: ", error); - return; - } else if (*req->db->set.introspection_url != '\0') { - db_oauth2_lookup_introspect(req); - return; - } - db_oauth2_process_fields(req, &passdb_result, &error); - db_oauth2_callback(req, passdb_result, error_prefix, error); -} - -static void -db_oauth2_lookup_continue(struct oauth2_request_result *result, - struct db_oauth2_request *req) -{ - i_assert(req->token != NULL); - req->req = NULL; - - if (result->error != NULL) { - db_oauth2_callback(req, PASSDB_RESULT_INTERNAL_FAILURE, - "Token validation failed: ", result->error); - } else if (!result->valid) { - db_oauth2_callback(req, PASSDB_RESULT_PASSWORD_MISMATCH, - "Token validation failed: ", - "Invalid token"); - } else { - e_debug(authdb_event(req->auth_request), - "Token validation succeeded"); - db_oauth2_lookup_continue_valid(req, result->fields, - "Token validation failed: "); - } -} - -static void -db_oauth2_lookup_passwd_grant(struct oauth2_request_result *result, - struct db_oauth2_request *req) -{ - enum passdb_result passdb_result; - const char *token, *error; - - i_assert(req->token == NULL); - req->req = NULL; - - if (result->valid) { - e_debug(authdb_event(req->auth_request), - "Password grant succeeded"); - token = db_oauth2_field_find(result->fields, "access_token"); - if (token == NULL) { - db_oauth2_callback(req, PASSDB_RESULT_INTERNAL_FAILURE, - "Password grant failed: ", - "OAuth2 token missing from reply"); - } else { - req->token = p_strdup(req->pool, token); - db_oauth2_lookup_continue_valid(req, result->fields, - "Password grant failed: "); - } - return; - } - - passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; - if (result->error != NULL) - error = result->error; - else { - passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; - error = db_oauth2_field_find(result->fields, "error"); - if (error == NULL) - error = "OAuth2 server returned failure without error field"; - else if (strcmp("invalid_grant", error) == 0) - passdb_result = PASSDB_RESULT_PASSWORD_MISMATCH; - } - db_oauth2_callback(req, passdb_result, - "Password grant failed: ", error); -} - -#undef db_oauth2_lookup -void db_oauth2_lookup(struct db_oauth2 *db, struct db_oauth2_request *req, - const char *token, struct auth_request *request, - db_oauth2_lookup_callback_t *callback, void *context) -{ - struct oauth2_request_input input; - i_zero(&input); - - req->db = db; - req->token = p_strdup(req->pool, token); - req->callback = callback; - req->context = context; - req->auth_request = request; - - input.token = token; - input.local_ip = req->auth_request->fields.local_ip; - input.local_port = req->auth_request->fields.local_port; - input.remote_ip = req->auth_request->fields.remote_ip; - input.remote_port = req->auth_request->fields.remote_port; - input.real_local_ip = req->auth_request->fields.real_local_ip; - input.real_local_port = req->auth_request->fields.real_local_port; - input.real_remote_ip = req->auth_request->fields.real_remote_ip; - input.real_remote_port = req->auth_request->fields.real_remote_port; - input.service = req->auth_request->fields.service; - - if (db->oauth2_set.introspection_mode == INTROSPECTION_MODE_LOCAL && - !db_oauth2_uses_password_grant(db)) { - /* try to validate token locally */ - e_debug(authdb_event(req->auth_request), - "Attempting to locally validate token"); - db_oauth2_local_validation(req, request->mech_password); - return; - - } - if (db->oauth2_set.use_grant_password) { - e_debug(authdb_event(req->auth_request), - "Making grant url request to %s", - db->set.grant_url); - /* There is no valid token until grant looks it up. */ - req->token = NULL; - req->req = oauth2_passwd_grant_start(&db->oauth2_set, &input, - request->fields.user, request->mech_password, - db_oauth2_lookup_passwd_grant, req); - } else if (*db->oauth2_set.tokeninfo_url == '\0') { - e_debug(authdb_event(req->auth_request), - "Making introspection request to %s", - db->set.introspection_url); - req->req = oauth2_introspection_start(&req->db->oauth2_set, &input, - db_oauth2_introspect_continue, req); - } else { - e_debug(authdb_event(req->auth_request), - "Making token validation lookup to %s", - db->oauth2_set.tokeninfo_url); - req->req = oauth2_token_validation_start(&db->oauth2_set, &input, - db_oauth2_lookup_continue, req); - } - i_assert(req->req != NULL); - DLLIST_PREPEND(&db->head, req); -} - -bool db_oauth2_uses_password_grant(const struct db_oauth2 *db) -{ - return db->set.use_grant_password; -} diff --git a/src/auth/db-oauth2.h b/src/auth/db-oauth2.h deleted file mode 100644 index cb653dbd05..0000000000 --- a/src/auth/db-oauth2.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef DB_OAUTH2_H -#define DB_OAUTH2_H 1 - -struct db_oauth2; -struct oauth2_request; -struct db_oauth2_request; - -typedef void db_oauth2_lookup_callback_t(struct db_oauth2_request *request, - enum passdb_result result, - const char *error, - void *context); -struct db_oauth2_request { - pool_t pool; - struct db_oauth2_request *prev,*next; - - struct db_oauth2 *db; - struct oauth2_request *req; - - /* username to match */ - const char *username; - /* token to use */ - const char *token; - - struct auth_request *auth_request; - struct auth_fields *fields; - - db_oauth2_lookup_callback_t *callback; - void *context; - verify_plain_callback_t *verify_callback; -}; - - -struct db_oauth2 *db_oauth2_init(const char *config_path); - -void db_oauth2_ref(struct db_oauth2 *); -void db_oauth2_unref(struct db_oauth2 **); - -bool db_oauth2_uses_password_grant(const struct db_oauth2 *db); - -void db_oauth2_lookup(struct db_oauth2 *db, struct db_oauth2_request *req, const char *token, struct auth_request *request, db_oauth2_lookup_callback_t *callback, void *context); -#define db_oauth2_lookup(db, req, token, request, callback, context) \ - db_oauth2_lookup(db, req, token - \ - CALLBACK_TYPECHECK(callback, void(*)(struct db_oauth2_request *, enum passdb_result, const char*, typeof(context))), \ - request, (db_oauth2_lookup_callback_t*)callback, (void*)context) - -#endif diff --git a/src/auth/mech-oauth2.c b/src/auth/mech-oauth2.c deleted file mode 100644 index 1b145674b3..0000000000 --- a/src/auth/mech-oauth2.c +++ /dev/null @@ -1,315 +0,0 @@ -/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ - -#include "auth-common.h" -#include "safe-memset.h" -#include "str.h" -#include "mech.h" -#include "passdb.h" -#include "oauth2.h" -#include "json-parser.h" -#include - -struct oauth2_auth_request { - struct auth_request auth; - bool failed; -}; - -/* RFC5801 based unescaping */ -static bool oauth2_unescape_username(const char *in, const char **username_r) -{ - string_t *out; - out = t_str_new(64); - for (; *in != '\0'; in++) { - if (in[0] == ',') - return FALSE; - if (in[0] == '=') { - if (in[1] == '2' && in[2] == 'C') - str_append_c(out, ','); - else if (in[1] == '3' && in[2] == 'D') - str_append_c(out, '='); - else - return FALSE; - in += 2; - } else { - str_append_c(out, *in); - } - } - *username_r = str_c(out); - return TRUE; -} - -static void oauth2_verify_callback(enum passdb_result result, - const char *const *error_fields, - struct auth_request *request) -{ - struct oauth2_auth_request *oauth2_req = - (struct oauth2_auth_request*)request; - - i_assert(result == PASSDB_RESULT_OK || error_fields != NULL); - switch (result) { - case PASSDB_RESULT_OK: - auth_request_success(request, "", 0); - break; - case PASSDB_RESULT_INTERNAL_FAILURE: - auth_request_internal_failure(request); - break; - default: - /* we could get new token after this */ - if (request->mech_password != NULL) - request->mech_password = NULL; - string_t *error = t_str_new(64); - str_append_c(error, '{'); - for (unsigned int i = 0; error_fields[i] != NULL; i += 2) { - i_assert(error_fields[i+1] != NULL); - if (i > 0) - str_append_c(error, ','); - str_append_c(error, '"'); - json_append_escaped(error, error_fields[i]); - str_append(error, "\":\""); - json_append_escaped(error, error_fields[i+1]); - str_append_c(error, '"'); - } - /* FIXME: HORRIBLE HACK - REMOVE ME!!! - It is because the mech has not been implemented properly - that we need to pass the config url in this strange way. - - This **must** be removed from here and db-oauth2 once the - validation result et al is handled here. - */ - if (request->openid_config_url != NULL) { - if (str_len(error) > 0) - str_append_c(error, ','); - str_printfa(error, "\"openid-configuration\":\""); - json_append_escaped(error, request->openid_config_url); - str_append_c(error, '"'); - } - str_append_c(error, '}'); - auth_request_handler_reply_continue(request, str_data(error), - str_len(error)); - oauth2_req->failed = TRUE; - break; - } -} - -static void -xoauth2_verify_callback(enum passdb_result result, struct auth_request *request) -{ - const char *const error_fields[] = { - "status", "401", - "schemes", "bearer", - "scope", "mail", - NULL - }; - oauth2_verify_callback(result, error_fields, request); -} - -static void -oauthbearer_verify_callback(enum passdb_result result, struct auth_request *request) -{ - const char *error_fields[] = { - "status", "invalid_token", - NULL - }; - oauth2_verify_callback(result, error_fields, request); -} - -/* Input syntax: - user=Username^Aauth=Bearer token^A^A -*/ -static void -mech_xoauth2_auth_continue(struct auth_request *request, - const unsigned char *data, - size_t data_size) -{ - struct oauth2_auth_request *oauth2_req = - (struct oauth2_auth_request*)request; - - /* Specification says that client is sent "invalid token" challenge - which the client is supposed to ack with empty response */ - if (oauth2_req->failed) { - auth_request_fail(request); - return; - } - - /* split the data from ^A */ - bool user_given = FALSE; - const char *error; - const char *token = NULL; - const char *const *ptr; - const char *username; - const char *const *fields = - t_strsplit(t_strndup(data, data_size), "\x01"); - for(ptr = fields; *ptr != NULL; ptr++) { - if (str_begins(*ptr, "user=")) { - /* xoauth2 does not require unescaping because the data - format does not contain anything to escape */ - username = (*ptr)+5; - user_given = TRUE; - } else if (str_begins(*ptr, "auth=")) { - const char *value = (*ptr)+5; - if (strncasecmp(value, "bearer ", 7) == 0 && - oauth2_valid_token(value+7)) { - token = value+7; - } else { - e_info(request->mech_event, - "Invalid continued data"); - auth_request_fail(request); - return; - } - } - /* do not fail on unexpected fields */ - } - - if (user_given && !auth_request_set_username(request, username, &error)) { - e_info(request->mech_event, - "%s", error); - auth_request_fail(request); - return; - } - - if (user_given && token != NULL) - auth_request_verify_plain(request, token, - xoauth2_verify_callback); - else { - e_info(request->mech_event, "Username or token missing"); - auth_request_fail(request); - } -} - -/* Input syntax for data: - gs2flag,a=username,^Afield=...^Afield=...^Aauth=Bearer token^A^A -*/ -static void -mech_oauthbearer_auth_continue(struct auth_request *request, - const unsigned char *data, - size_t data_size) -{ - struct oauth2_auth_request *oauth2_req = - (struct oauth2_auth_request*)request; - - if (oauth2_req->failed) { - auth_request_fail(request); - return; - } - - bool user_given = FALSE; - const char *error; - const char *username; - const char *const *ptr; - /* split the data from ^A */ - const char **fields = - t_strsplit(t_strndup(data, data_size), "\x01"); - const char *token = NULL; - /* ensure initial field is OK */ - if (*fields == NULL || *(fields[0]) == '\0') { - e_info(request->mech_event, - "Invalid continued data"); - auth_request_fail(request); - return; - } - - /* the first field is specified by RFC5801 as gs2-header */ - for(ptr = t_strsplit_spaces(fields[0], ","); *ptr != NULL; ptr++) { - switch(*ptr[0]) { - case 'f': - e_info(request->mech_event, - "Client requested non-standard mechanism"); - auth_request_fail(request); - return; - case 'p': - /* channel binding is not supported */ - e_info(request->mech_event, - "Client requested and used channel-binding"); - auth_request_fail(request); - return; - case 'n': - case 'y': - /* we don't need to use channel-binding */ - continue; - case 'a': /* authzid */ - if ((*ptr)[1] != '=' || - !oauth2_unescape_username((*ptr)+2, &username)) { - e_info(request->mech_event, - "Invalid username escaping"); - auth_request_fail(request); - return; - } else { - user_given = TRUE; - } - break; - default: - e_info(request->mech_event, - "Invalid gs2-header in request"); - auth_request_fail(request); - return; - } - } - - for(ptr = fields; *ptr != NULL; ptr++) { - if (str_begins(*ptr, "auth=")) { - const char *value = (*ptr)+5; - if (strncasecmp(value, "bearer ", 7) == 0 && - oauth2_valid_token(value+7)) { - token = value+7; - } else { - e_info(request->mech_event, - "Invalid continued data"); - auth_request_fail(request); - return; - } - } - /* do not fail on unexpected fields */ - } - if (user_given && !auth_request_set_username(request, username, &error)) { - e_info(request->mech_event, - "%s", error); - auth_request_fail(request); - return; - } - if (user_given && token != NULL) - auth_request_verify_plain(request, token, - oauthbearer_verify_callback); - else { - e_info(request->mech_event, "Missing username or token"); - auth_request_fail(request); - } -} - -static struct auth_request *mech_oauth2_auth_new(void) -{ - struct oauth2_auth_request *request; - pool_t pool; - - pool = pool_alloconly_create(MEMPOOL_GROWING"oauth2_auth_request", 2048); - request = p_new(pool, struct oauth2_auth_request, 1); - request->auth.pool = pool; - return &request->auth; -} - -const struct mech_module mech_oauthbearer = { - "OAUTHBEARER", - - /* while this does not transfer plaintext password, - the token is still considered as password */ - .flags = MECH_SEC_PLAINTEXT, - .passdb_need = 0, - - mech_oauth2_auth_new, - mech_generic_auth_initial, - mech_oauthbearer_auth_continue, - mech_generic_auth_free -}; - -const struct mech_module mech_xoauth2 = { - "XOAUTH2", - - .flags = MECH_SEC_PLAINTEXT, - .passdb_need = 0, - - mech_oauth2_auth_new, - mech_generic_auth_initial, - mech_xoauth2_auth_continue, - mech_generic_auth_free -}; - - diff --git a/src/auth/passdb-oauth2.c b/src/auth/passdb-oauth2.c deleted file mode 100644 index 000d1ec6e8..0000000000 --- a/src/auth/passdb-oauth2.c +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ - -#include "auth-common.h" -#include "passdb.h" -#include "db-oauth2.h" - -struct oauth2_passdb_module { - struct passdb_module module; - struct db_oauth2 *db; -}; - -static void -oauth2_verify_plain_continue(struct db_oauth2_request *req, - enum passdb_result result, const char *error, - struct auth_request *request) -{ - if (result == PASSDB_RESULT_INTERNAL_FAILURE) - e_error(authdb_event(request), "oauth2 failed: %s", - error); - else if (result != PASSDB_RESULT_OK) - e_info(authdb_event(request), "oauth2 failed: %s", - error); - else { - auth_request_set_field(request, "token", req->token, "PLAIN"); - } - req->verify_callback(result, request); - auth_request_unref(&request); -} - -static void -oauth2_verify_plain(struct auth_request *request, const char *password, - verify_plain_callback_t *callback) -{ - struct oauth2_passdb_module *module = - (struct oauth2_passdb_module *)request->passdb->passdb; - struct db_oauth2_request *req = - p_new(request->pool, struct db_oauth2_request, 1); - req->pool = request->pool; - req->verify_callback = callback; - - auth_request_ref(request); - - db_oauth2_lookup(module->db, req, password, request, oauth2_verify_plain_continue, request); -} - -static struct passdb_module * -oauth2_preinit(pool_t pool, const char *args) -{ - struct oauth2_passdb_module *module; - - module = p_new(pool, struct oauth2_passdb_module, 1); - module->db = db_oauth2_init(args); - module->module.default_pass_scheme = "PLAIN"; - - if (db_oauth2_uses_password_grant(module->db)) { - module->module.default_cache_key = "%u"; - } else { - module->module.default_cache_key = "%u%w"; - } - - return &module->module; -} - -static void oauth2_deinit(struct passdb_module *passdb) -{ - struct oauth2_passdb_module *module = (struct oauth2_passdb_module *)passdb; - db_oauth2_unref(&module->db); -} - -struct passdb_module_interface passdb_oauth2 = { - "oauth2", - - oauth2_preinit, - NULL, - oauth2_deinit, - - oauth2_verify_plain, - NULL, - NULL -}; diff --git a/src/doveadm/doveadm-dump-dcrypt-file.c b/src/doveadm/doveadm-dump-dcrypt-file.c deleted file mode 100644 index 3703bf488e..0000000000 --- a/src/doveadm/doveadm-dump-dcrypt-file.c +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "dcrypt.h" -#include "istream.h" -#include "istream-decrypt.h" -#include "dcrypt-iostream.h" -#include "doveadm-dump.h" -#include - -static int get_digest(const char *digest, - struct dcrypt_private_key **priv_key_r ATTR_UNUSED, - const char **error_r ATTR_UNUSED, - void *context) -{ - const char **digest_r = (const char**)context; - *digest_r = t_strdup(digest); - return 0; -} - -static void dcrypt_istream_dump_metadata(const struct istream *stream) -{ - enum io_stream_encrypt_flags flags = i_stream_encrypt_get_flags(stream); - if ((flags & IO_STREAM_ENC_INTEGRITY_HMAC) != 0) - printf("flags: IO_STREAM_ENC_INTEGRITY_HMAC\n"); - if ((flags & IO_STREAM_ENC_INTEGRITY_AEAD) != 0) - printf("flags: IO_STREAM_ENC_INTEGRITY_AEAD\n"); - if ((flags & IO_STREAM_ENC_INTEGRITY_NONE) != 0) - printf("flags: IO_STREAM_ENC_INTEGRITY_NONE\n"); - if ((flags & IO_STREAM_ENC_VERSION_1) != 0) - printf("flags: IO_STREAM_ENC_VERSION_1\n"); - - enum decrypt_istream_format format = i_stream_encrypt_get_format(stream); - switch (format) { - case DECRYPT_FORMAT_V1: - printf("format: DECRYPT_FORMAT_V1\n"); - break; - case DECRYPT_FORMAT_V2: - printf("format: DECRYPT_FORMAT_V2\n"); - break; - } -} - -static bool dcrypt_file_dump_metadata(const char *filename, bool print) -{ - bool ret = FALSE; - struct istream *is = i_stream_create_file(filename, IO_BLOCK_SIZE); - const char *key_digest = NULL; - struct istream *ds = i_stream_create_decrypt_callback(is, - get_digest, &key_digest); - - ssize_t size = i_stream_read(ds); - i_assert(size < 0); - - if (key_digest != NULL) { - ret = TRUE; - if (print) { - dcrypt_istream_dump_metadata(ds); - printf("decrypt key digest: %s\n", key_digest); - } - } else if (print && ds->stream_errno != 0) { - i_error("read(%s) failed: %s", - i_stream_get_name(ds), - i_stream_get_error(ds)); - } - - i_stream_unref(&ds); - i_stream_unref(&is); - return ret; -} - -static bool test_dump_dcrypt_file(const char *path) -{ - if (!dcrypt_initialize("openssl", NULL, NULL)) - return FALSE; - bool ret = dcrypt_file_dump_metadata(path, FALSE); - return ret; -} - -static void -cmd_dump_dcrypt_file(const char *path, const char *const *args ATTR_UNUSED) -{ - const char *error = NULL; - if (!dcrypt_initialize("openssl", NULL, &error)) - i_fatal("dcrypt_initialize failed: %s", error); - (void)dcrypt_file_dump_metadata(path, TRUE); -} - -struct doveadm_cmd_dump doveadm_cmd_dump_dcrypt_file = { - "dcrypt-file", - test_dump_dcrypt_file, - cmd_dump_dcrypt_file -}; diff --git a/src/doveadm/doveadm-dump-dcrypt-key.c b/src/doveadm/doveadm-dump-dcrypt-key.c deleted file mode 100644 index cecd27f3d2..0000000000 --- a/src/doveadm/doveadm-dump-dcrypt-key.c +++ /dev/null @@ -1,215 +0,0 @@ -/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "dcrypt.h" -#include "dcrypt-iostream.h" -#include "ostream-encrypt.h" -#include "istream-private.h" -#include "istream-decrypt.h" -#include "doveadm-dump.h" -#include "hex-binary.h" -#include "str.h" -#include -#include -#include -#include -#include - -#define KEY_BUF_SIZE 4096 - -static void dcrypt_dump_public_key_metadata(const char *buf) -{ - const char *error = NULL; - struct dcrypt_public_key *pub_key; - - bool ret = dcrypt_key_load_public(&pub_key, buf, &error); - if (ret == FALSE) { - i_error("dcrypt_key_load_public failed: %s", error); - return; - } - enum dcrypt_key_type key_type = dcrypt_key_type_public(pub_key); - if (key_type == DCRYPT_KEY_RSA) - printf("key type: DCRYPT_KEY_RSA\n"); - else if (key_type == DCRYPT_KEY_EC) - printf("key type: DCRYPT_KEY_EC\n"); - - string_t *hash = t_str_new(128); - if (!dcrypt_key_id_public(pub_key, "sha256", hash, &error)) { - i_error("dcrypt_key_id_public failed: %s", error); - } else { - const char *v2_hash = binary_to_hex(hash->data, hash->used); - printf("v2 hash: %s\n", v2_hash); - - if (key_type == DCRYPT_KEY_EC) { - buffer_set_used_size(hash, 0); - if (!dcrypt_key_id_public_old(pub_key, hash, &error)) { - i_error("dcrypt_key_id_public_old failed: %s", - error); - } else { - const char *v1_hash = binary_to_hex(hash->data, - hash->used); - printf("v1 hash: %s\n", v1_hash); - } - } - } - dcrypt_key_unref_public(&pub_key); -} - -static void dcrypt_dump_private_key_metadata(const char *buf) -{ - const char *error = NULL; - struct dcrypt_private_key *priv_key; - - bool ret = dcrypt_key_load_private(&priv_key, buf, NULL, NULL, - &error); - if (ret == FALSE) { - i_error("dcrypt_key_load_private failed: %s", error); - return; - } - enum dcrypt_key_type key_type = dcrypt_key_type_private(priv_key); - if (key_type == DCRYPT_KEY_RSA) - printf("key type: DCRYPT_KEY_RSA\n"); - else if (key_type == DCRYPT_KEY_EC) - printf("key type: DCRYPT_KEY_EC\n"); - - string_t *hash = t_str_new(128); - if (!dcrypt_key_id_private(priv_key, "sha256", hash, &error)) { - i_error("dcrypt_key_id_private failed: %s", error); - } else { - const char *v2_hash = binary_to_hex(hash->data, hash->used); - printf("v2 hash: %s\n", v2_hash); - - if (key_type == DCRYPT_KEY_EC) { - buffer_set_used_size(hash, 0); - if (!dcrypt_key_id_private_old(priv_key, hash, &error)) { - i_error("dcrypt_key_id_private_old failed: %s", error); - } else { - const char *v1_hash = binary_to_hex(hash->data, - hash->used); - printf("v1 hash: %s\n", v1_hash); - } - } - } - dcrypt_key_unref_private(&priv_key); -} - -static bool dcrypt_key_dump_metadata(const char *filename, bool print) -{ - bool ret = TRUE; - int fd = open(filename, O_RDONLY); - if (fd < 0) { - if (print) i_error("open(%s) failed: %m", filename); - return FALSE; - } - - char buf[KEY_BUF_SIZE+1]; - ssize_t res = read(fd, buf, KEY_BUF_SIZE); - if (res < 0) { - if (print) i_error("read(%s) failed: %m", filename); - i_close_fd(&fd); - return FALSE; - } - i_close_fd(&fd); - - buf[res] = '\0'; - enum dcrypt_key_format format; - enum dcrypt_key_version version; - enum dcrypt_key_kind kind; - enum dcrypt_key_encryption_type encryption_type; - const char *encryption_key_hash; - const char *key_hash; - const char *error; - - ret = dcrypt_key_string_get_info(buf, &format, &version, - &kind, &encryption_type, &encryption_key_hash, - &key_hash, &error); - if (ret == FALSE) { - if (print) i_error("dcrypt_key_string_get_info failed: %s", error); - return FALSE; - } - if (!print) return TRUE; - - switch (format) { - case DCRYPT_FORMAT_PEM: - printf("format: DCRYPT_FORMAT_PEM\n"); - break; - case DCRYPT_FORMAT_DOVECOT: - printf("format: DCRYPT_FORMAT_DOVECOT\n"); - break; - case DCRYPT_FORMAT_JWK: - printf("format: DCRYPT_FORMAT_JWK\n"); - } - - switch (version) { - case DCRYPT_KEY_VERSION_1: - printf("version: DCRYPT_KEY_VERSION_1\n"); - break; - case DCRYPT_KEY_VERSION_2: - printf("version: DCRYPT_KEY_VERSION_2\n"); - break; - case DCRYPT_KEY_VERSION_NA: - printf("version: DCRYPT_KEY_VERSION_NA\n"); - break; - } - - switch (kind) { - case DCRYPT_KEY_KIND_PUBLIC: - printf("kind: DCRYPT_KEY_KIND_PUBLIC\n"); - break; - case DCRYPT_KEY_KIND_PRIVATE: - printf("kind: DCRYPT_KEY_KIND_PRIVATE\n"); - break; - } - - switch (encryption_type) { - case DCRYPT_KEY_ENCRYPTION_TYPE_NONE: - printf("encryption_type: DCRYPT_KEY_ENCRYPTION_TYPE_NONE\n"); - break; - case DCRYPT_KEY_ENCRYPTION_TYPE_KEY: - printf("encryption_type: DCRYPT_KEY_ENCRYPTION_TYPE_KEY\n"); - break; - case DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD: - printf("encryption_type: DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD\n"); - break; - } - - if (encryption_key_hash != NULL) - printf("encryption_key_hash: %s\n", encryption_key_hash); - if (key_hash != NULL) - printf("key_hash: %s\n", key_hash); - - const char *data = t_str_rtrim(buf, "\r\n\t "); - switch (kind) { - case DCRYPT_KEY_KIND_PUBLIC: - dcrypt_dump_public_key_metadata(data); - break; - case DCRYPT_KEY_KIND_PRIVATE: - if (encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE) - dcrypt_dump_private_key_metadata(data); - break; - } - return TRUE; -} - -static bool test_dump_dcrypt_key(const char *path) -{ - if (!dcrypt_initialize("openssl", NULL, NULL)) - return FALSE; - bool ret = dcrypt_key_dump_metadata(path, FALSE); - return ret; -} - -static void -cmd_dump_dcrypt_key(const char *path, const char *const *args ATTR_UNUSED) -{ - const char *error = NULL; - if (!dcrypt_initialize("openssl", NULL, &error)) - i_fatal("dcrypt_initialize: %s", error); - (void)dcrypt_key_dump_metadata(path, TRUE); -} - -struct doveadm_cmd_dump doveadm_cmd_dump_dcrypt_key = { - "dcrypt-key", - test_dump_dcrypt_key, - cmd_dump_dcrypt_key -}; diff --git a/src/lib-dcrypt/Makefile.am b/src/lib-dcrypt/Makefile.am deleted file mode 100644 index aaf50261b3..0000000000 --- a/src/lib-dcrypt/Makefile.am +++ /dev/null @@ -1,72 +0,0 @@ -noinst_LTLIBRARIES = libdcrypt.la -pkglib_LTLIBRARIES = - -NOPLUGIN_LDFLAGS= - -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/lib \ - -I$(top_srcdir)/src/lib-test \ - -I$(top_srcdir)/src/lib-ssl-iostream \ - -libdcrypt_la_SOURCES = \ - dcrypt.c \ - istream-decrypt.c \ - ostream-encrypt.c - -libdcrypt_la_CFLAGS = $(AM_CPPFLAGS) \ - -if BUILD_DCRYPT_OPENSSL -pkglib_LTLIBRARIES += libdcrypt_openssl.la -libdcrypt_openssl_la_SOURCES = dcrypt-openssl.c -libdcrypt_openssl_la_LDFLAGS = -module -avoid-version ../lib-ssl-iostream/libssl_iostream_openssl.la -libdcrypt_openssl_la_LIBADD = $(SSL_LIBS) -libdcrypt_openssl_la_DEPENDENCIES = ../lib-ssl-iostream/libssl_iostream_openssl.la -libdcrypt_openssl_la_CFLAGS = $(AM_CPPFLAGS) \ - $(SSL_CFLAGS) -endif - -headers = \ - dcrypt.h \ - dcrypt-iostream.h \ - dcrypt-private.h \ - ostream-encrypt.h \ - istream-decrypt.h - -pkginc_libdir=$(pkgincludedir) -pkginc_lib_HEADERS = $(headers) - -EXTRA_DIST = \ - sample-v1.asc \ - sample-v1_short.asc \ - sample-v2.asc - -test_programs = test-crypto test-stream -noinst_PROGRAMS = $(test_programs) - -check-local: - for bin in $(test_programs); do \ - if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ - done - -LIBDOVECOT_TEST_DEPS = \ - ../lib-ssl-iostream/libssl_iostream.la \ - ../lib-test/libtest.la \ - ../lib/liblib.la -LIBDOVECOT_TEST = \ - $(LIBDOVECOT_TEST_DEPS) \ - -test_crypto_LDADD = $(LIBDOVECOT_TEST) -test_crypto_DEPENDENCIES = $(LIBDOVECOT_TEST_DEPS) -if HAVE_WHOLE_ARCHIVE -test_crypto_LDFLAGS = -Wl,$(LD_WHOLE_ARCHIVE),../lib-ssl-iostream/.libs/libssl_iostream.a,$(LD_NO_WHOLE_ARCHIVE) -endif -test_crypto_CFLAGS = $(AM_CPPFLAGS) -DDCRYPT_SRC_DIR=\"$(top_srcdir)/src/lib-dcrypt\" -test_crypto_SOURCES = $(libdcrypt_la_SOURCES) test-crypto.c - -test_stream_LDADD = $(LIBDOVECOT_TEST) -test_stream_DEPENDENCIES = $(LIBDOVECOT_TEST_DEPS) -if HAVE_WHOLE_ARCHIVE -test_stream_LDFLAGS = -Wl,$(LD_WHOLE_ARCHIVE),../lib-ssl-iostream/.libs/libssl_iostream.a,$(LD_NO_WHOLE_ARCHIVE) -endif -test_stream_CFLAGS = $(AM_CPPFLAGS) -DDCRYPT_SRC_DIR=\"$(top_srcdir)/src/lib-dcrypt\" -test_stream_SOURCES = $(libdcrypt_la_SOURCES) test-stream.c diff --git a/src/lib-dcrypt/dcrypt-gnutls.c b/src/lib-dcrypt/dcrypt-gnutls.c deleted file mode 100644 index 33a2a231a1..0000000000 --- a/src/lib-dcrypt/dcrypt-gnutls.c +++ /dev/null @@ -1,599 +0,0 @@ -/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "buffer.h" -#include "randgen.h" -#include "array.h" -#include "hash-method.h" -#include "pkcs5.h" -#include "module-dir.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dcrypt.h" -#include "dcrypt-private.h" - -struct dcrypt_context_symmetric { - pool_t pool; - gnutls_cipher_hd_t ctx; - gnutls_cipher_algorithm_t cipher; - gnutls_datum_t key; - gnutls_datum_t iv; - enum dcrypt_sym_mode mode; -}; - -struct dcrypt_context_hmac { - pool_t pool; - gnutls_hmac_hd_t ctx; - gnutls_mac_algorithm_t md; - gnutls_datum_t key; - size_t klen; -}; - -struct dcrypt_public_key { - void *ctx; -}; - -struct dcrypt_private_key { - void *ctx; -}; - -static int -dcrypt_gnutls_private_to_public_key(struct dcrypt_private_key *priv_key, - struct dcrypt_public_key **pub_key_r, - const char **error_r); - -static int dcrypt_gnutls_error(int ec, const char **error_r) -{ - i_assert(ec < 0); - if(error_r != NULL) { - *error_r = gnutls_strerror(ec); - } - return -1; -} - -static int -dcrypt_gnutls_ctx_sym_create(const char *algorithm, - enum dcrypt_sym_mode mode, - struct dcrypt_context_symmetric **ctx_r, - const char **error_r) -{ - gnutls_cipher_algorithm_t cipher = gnutls_cipher_get_id(algorithm); - struct dcrypt_context_symmetric *ctx; - pool_t pool; - - if(cipher == GNUTLS_CIPHER_UNKNOWN) - return dcrypt_gnutls_error(cipher, error_r); - - pool = pool_alloconly_create("dcrypt gnutls", 128); - ctx = p_new(pool, struct dcrypt_context_symmetric, 1); - ctx->pool = pool; - ctx->cipher = cipher; - ctx->mode = mode; - *ctx_r = ctx; - return 0; -} - -static int -dcrypt_gnutls_ctx_sym_destroy(struct dcrypt_context_symmetric **ctx) -{ - pool_t pool = (*ctx)->pool; - - gnutls_cipher_deinit((*ctx)->ctx); - pool_unref(&pool); - *ctx = NULL; - return 0; -} - -static void -dcrypt_gnutls_ctx_sym_set_key(struct dcrypt_context_symmetric *ctx, - const unsigned char *key, size_t key_len) -{ - if(ctx->key.data != NULL) - p_free(ctx->pool, ctx->key.data); - - ctx->key.size = I_MIN(key_len, - (size_t)gnutls_cipher_get_key_size(ctx->cipher)); - ctx->key.data = p_malloc(ctx->pool, ctx->key.size); - memcpy(ctx->key.data, key, ctx->key.size); -} - -static void -dcrypt_gnutls_ctx_sym_set_iv(struct dcrypt_context_symmetric *ctx, - const unsigned char *iv, size_t iv_len) -{ - if(ctx->iv.data != NULL) - p_free(ctx->pool, ctx->iv.data); - ctx->iv.size = I_MIN(iv_len, - (size_t)gnutls_cipher_get_iv_size(ctx->cipher)); - ctx->iv.data = p_malloc(ctx->pool, ctx->iv.size); - memcpy(ctx->iv.data, iv, ctx->iv.size); -} - -static void -dcrypt_gnutls_ctx_sym_set_key_iv_random(struct dcrypt_context_symmetric *ctx) -{ - if(ctx->key.data != NULL) - p_free(ctx->pool, ctx->key.data); - if(ctx->iv.data != NULL) - p_free(ctx->pool, ctx->iv.data); - - ctx->key.data = p_malloc(ctx->pool, - gnutls_cipher_get_key_size(ctx->cipher)); - random_fill(ctx->key.data, gnutls_cipher_get_key_size(ctx->cipher)); - ctx->key.size = gnutls_cipher_get_key_size(ctx->cipher); - ctx->iv.data = p_malloc(ctx->pool, - gnutls_cipher_get_iv_size(ctx->cipher)); - random_fill(ctx->iv.data, gnutls_cipher_get_iv_size(ctx->cipher)); - ctx->iv.size = gnutls_cipher_get_iv_size(ctx->cipher); -} - -static int -dcrypt_gnutls_ctx_sym_get_key(struct dcrypt_context_symmetric *ctx, - buffer_t *key) -{ - if(ctx->key.data == NULL) - return -1; - - buffer_append(key, ctx->key.data, ctx->key.size); - return 0; -} - -static int -dcrypt_gnutls_ctx_sym_get_iv(struct dcrypt_context_symmetric *ctx, buffer_t *iv) -{ - if(ctx->iv.data == NULL) - return -1; - - buffer_append(iv, ctx->iv.data, ctx->iv.size); - return 0; -} - -static int -dcrypt_gnutls_ctx_sym_get_key_length(struct dcrypt_context_symmetric *ctx) -{ - return gnutls_cipher_get_iv_size(ctx->cipher); -} - -static int -dcrypt_gnutls_ctx_sym_get_iv_length(struct dcrypt_context_symmetric *ctx) -{ - return gnutls_cipher_get_iv_size(ctx->cipher); -} - -static int -dcrypt_gnutls_ctx_sym_get_block_size(struct dcrypt_context_symmetric *ctx) -{ - return gnutls_cipher_get_block_size(ctx->cipher); -} - -static int -dcrypt_gnutls_ctx_sym_init(struct dcrypt_context_symmetric *ctx, - const char **error_r) -{ - int ec; - - ec = gnutls_cipher_init(&ctx->ctx, ctx->cipher, &ctx->key, &ctx->iv); - if(ec < 0) - return dcrypt_gnutls_error(ec, error_r); - return 0; -} - -static int -dcrypt_gnutls_ctx_sym_update(struct dcrypt_context_symmetric *ctx, - const unsigned char *data, size_t data_len, - buffer_t *result, const char **error_r) -{ - int ec; - size_t outl = gnutls_cipher_get_block_size(ctx->cipher); - unsigned char buf[outl]; - - ec = gnutls_cipher_encrypt2(ctx->ctx, data, data_len, buf, outl); - if(ec < 0) - return dcrypt_gnutls_error(ec, error_r); - - buffer_append(result, buf, outl); - return ec; -} - -static int -dcrypt_gnutls_ctx_sym_final(struct dcrypt_context_symmetric *ctx, - buffer_t *result, const char **error_r) -{ - return dcrypt_gnutls_ctx_sym_update(ctx, (const unsigned char*)"", 0, - result, error_r); -} - -static int -dcrypt_gnutls_ctx_hmac_create(const char *algorithm, - struct dcrypt_context_hmac **ctx_r, - const char **error_r) -{ - gnutls_mac_algorithm_t md = gnutls_mac_get_id(algorithm); - struct dcrypt_context_hmac *ctx; - pool_t pool; - - if (md == GNUTLS_MAC_UNKNOWN) - return dcrypt_gnutls_error(md, error_r); - - pool = pool_alloconly_create("dcrypt gnutls", 128); - ctx = p_new(pool, struct dcrypt_context_hmac, 1); - ctx->pool = pool; - ctx->md = md; - *ctx_r = ctx; - return 0; -} - -static int -dcrypt_gnutls_ctx_hmac_destroy(struct dcrypt_context_hmac **ctx) -{ - pool_t pool = (*ctx)->pool; - gnutls_hmac_deinit((*ctx)->ctx, NULL); - pool_unref(&pool); - *ctx = NULL; - return 0; -} - -static void -dcrypt_gnutls_ctx_hmac_set_key(struct dcrypt_context_hmac *ctx, - const unsigned char *key, size_t key_len) -{ - if(ctx->key.data != NULL) - p_free(ctx->pool, ctx->key.data); - - ctx->key.size = I_MIN(key_len,(size_t)gnutls_hmac_get_len(ctx->md)); - ctx->key.data = p_malloc(ctx->pool, ctx->key.size); - memcpy(ctx->key.data, key, ctx->key.size); -} - -static int -dcrypt_gnutls_ctx_hmac_get_key(struct dcrypt_context_hmac *ctx, buffer_t *key) -{ - if (ctx->key.data == NULL) - return -1; - - buffer_append(key, ctx->key.data, ctx->key.size); - return 0; -} - -static int -dcrypt_gnutls_ctx_hmac_init(struct dcrypt_context_hmac *ctx, - const char **error_r) -{ - int ec; - - ec = gnutls_hmac_init(&ctx->ctx, ctx->md, ctx->key.data, ctx->key.size); - if (ec < 0) - return dcrypt_gnutls_error(ec, error_r); - return 0; -} - -static int -dcrypt_gnutls_ctx_hmac_update(struct dcrypt_context_hmac *ctx, - const unsigned char *data, size_t data_len, - const char **error_r) -{ - int ec; - - if ((ec = gnutls_hmac(ctx->ctx, data, data_len)) != 0) - return dcrypt_gnutls_error(ec, error_r); - return 0; -} - -static int -dcrypt_gnutls_ctx_hmac_final(struct dcrypt_context_hmac *ctx, - buffer_t *result, const char **error_r) -{ - size_t hlen = gnutls_hmac_get_len(ctx->md); - unsigned char buf[hlen]; - - gnutls_hmac_output(ctx->ctx, buf); - buffer_append(result, buf, hlen); - return 0; -} - -static int -dcrypt_gnutls_ecdh_derive_secret(struct dcrypt_public_key *peer_key, - buffer_t *R, buffer_t *S, - const char **error_r) -{ - -} - -static int -dcrypt_gnutls_pbkdf2(const unsigned char *password, size_t password_len, - const unsigned char *salt, size_t salt_len, - const char *algorithm, unsigned int rounds, - buffer_t *result, unsigned int result_len, - const char **error_r) -{ - unsigned char buf[result_len]; - - /* only sha1 or sha256 is supported */ - if (strncasecmp(algorithm, "sha1", 4) == 0) { - pbkdf2_hmac_sha1(password_len, password, rounds, - salt_len, salt, result_len, buf); - } else if (strncasecmp(algorithm, "sha256", 6) == 0) { - pbkdf2_hmac_sha256(password_len, password, rounds, - salt_len, salt, result_len, buf); - } else if (strncasecmp(algorithm, "sha512", 6) == 0) { - struct hmac_sha512_ctx ctx; - - hmac_sha512_set_key(&ctx, password_len, password); - PBKDF2(&ctx, hmac_sha512_update, hmac_sha512_digest, 64, rounds, - salt_len, salt, result_len, buf); - i_zero(&ctx); - } else { - *error_r = "Unsupported algorithm"; - return -1; - } - - buffer_append(result, buf, result_len); - memset(buf, 0, sizeof(buf)); - return 0; -} - -static int -dcrypt_gnutls_generate_keypair(struct dcrypt_keypair *pair_r, - enum dcrypt_key_type kind, unsigned int bits, - const char *curve, const char **error_r) -{ - gnutls_pk_algorithm_t pk_algo; - gnutls_ecc_curve_t pk_curve; - gnutls_privkey_t priv; - int ec; - - if (kind == DCRYPT_KEY_EC) { - pk_curve = gnutls_ecc_curve_get_id(curve); - if (pk_curve == GNUTLS_ECC_CURVE_INVALID) { - *error_r = "Invalid curve"; - return -1; - } - bits = GNUTLS_CURVE_TO_BITS(pk_curve); -#if GNUTLS_VERSION_NUMBER >= 0x030500 - pk_algo = gnutls_curve_get_pk(pk_curve); -#else - pk_algo = GNUTLS_PK_EC; -#endif - } else if (kind == DCRYPT_KEY_RSA) { - pk_algo = gnutls_pk_get_id("RSA"); - } else { - *error_r = "Unsupported key type"; - return -1; - } - - if ((ec = gnutls_privkey_init(&priv)) != GNUTLS_E_SUCCESS) - return dcrypt_gnutls_error(ec, error_r); -#if GNUTLS_VERSION_NUMBER >= 0x030500 - gnutls_privkey_set_flags(priv, GNUTLS_PRIVKEY_FLAG_EXPORT_COMPAT); -#endif - ec = gnutls_privkey_generate(priv, pk_algo, bits, 0); - if (ec != GNUTLS_E_SUCCESS) { - gnutls_privkey_deinit(priv); - return dcrypt_gnutls_error(ec, error_r); - } - - pair_r->priv = (struct dcrypt_private_key*)priv; - - return dcrypt_gnutls_private_to_public_key(pair_r->priv, - &pair_r->pub, error_r); -} - -static int -dcrypt_gnutls_load_private_key(struct dcrypt_private_key **key_r, - const unsigned char *data, size_t data_len, - dcrypt_password_cb *cb, void *ctx, - const char **error_r) -{ - -} - -static int -dcrypt_gnutls_load_public_key(struct dcrypt_public_key **key_r, - const unsigned char *data, size_t data_len, - const char **error_r) -{ - -} - -static int -dcrypt_gnutls_store_private_key(struct dcrypt_private_key *key, - const char *cipher, buffer_t *destination, - dcrypt_password_cb *cb, void *ctx, - const char **error_r) -{ - gnutls_privkey_t priv = (gnutls_privkey_t)key; - gnutls_x509_privkey_t xkey; - size_t outl = 0; - - gnutls_privkey_export_x509(priv, &xkey); - /* then export PEM */ - gnutls_x509_privkey_export_pkcs8(xkey, GNUTLS_X509_FMT_PEM, NULL, 0, NULL, &outl); - char buffer[outl]; - gnutls_x509_privkey_export_pkcs8(xkey, GNUTLS_X509_FMT_PEM, NULL, 0, buffer, &outl); - buffer_append(destination, buffer, outl); - memset(buffer, 0, sizeof(buffer)); - return 0; -} - -static int -dcrypt_gnutls_store_public_key(struct dcrypt_public_key *key, buffer_t *destination, const char **error_r) -{ - gnutls_pubkey_t pub = (gnutls_pubkey_t)key; - size_t outl = 0; - - gnutls_pubkey_export(pub, GNUTLS_X509_FMT_PEM, NULL, &outl); - char buffer[outl]; - gnutls_pubkey_export(pub, GNUTLS_X509_FMT_PEM, buffer, &outl); - buffer_append(destination, buffer, outl); - return 0; -} - -static int -dcrypt_gnutls_private_to_public_key(struct dcrypt_private_key *priv_key, - struct dcrypt_public_key **pub_key_r, - const char **error_r) -{ - gnutls_privkey_t priv = (gnutls_privkey_t)priv_key; - int ec; - - if (gnutls_privkey_get_pk_algorithm(priv, NULL) == GNUTLS_PK_RSA) { - gnutls_datum_t m,e; - - /* do not extract anything we don't need */ - ec = gnutls_privkey_export_rsa_raw(priv, &m, &e, NULL, NULL, - NULL, NULL, NULL, NULL); - if (ec != GNUTLS_E_SUCCESS) - return dcrypt_gnutls_error(ec, error_r); - gnutls_pubkey_t pub; - gnutls_pubkey_init(&pub); - ec = gnutls_pubkey_import_rsa_raw(pub, &m, &e); - gnutls_free(m.data); - gnutls_free(e.data); - if (ec < 0) { - gnutls_pubkey_deinit(pub); - return dcrypt_gnutls_error(ec, error_r); - } - *pub_key_r = (struct dcrypt_public_key*)pub; - return 0; - } else if (gnutls_privkey_get_pk_algorithm(priv, NULL) == GNUTLS_PK_EC) { - gnutls_ecc_curve_t curve; - gnutls_datum_t x,y,k; - - ec = gnutls_privkey_export_ecc_raw(priv, &curve, &x, &y, NULL); - if (ec != GNUTLS_E_SUCCESS) - return dcrypt_gnutls_error(ec, error_r); - gnutls_pubkey_t pub; - gnutls_pubkey_init(&pub); - ec = gnutls_pubkey_import_ecc_raw(pub, curve, &x, &y); - gnutls_free(x.data); - gnutls_free(y.data); - if (ec < 0) { - gnutls_pubkey_deinit(pub); - return dcrypt_gnutls_error(ec, error_r); - } - *pub_key_r = (struct dcrypt_public_key*)pub; - return 0; - } - - return -1; -} - -static void -dcrypt_gnutls_free_public_key(struct dcrypt_public_key **key) -{ - gnutls_pubkey_deinit((gnutls_pubkey_t)*key); - *key = NULL; -} - -static void -dcrypt_gnutls_free_private_key(struct dcrypt_private_key **key) -{ - gnutls_privkey_deinit((gnutls_privkey_t)*key); - *key = NULL; -} - -static void -dcrypt_gnutls_free_keypair(struct dcrypt_keypair *keypair) -{ - dcrypt_gnutls_free_public_key(&keypair->pub); - dcrypt_gnutls_free_private_key(&keypair->priv); -} - -static int -dcrypt_gnutls_rsa_encrypt(struct dcrypt_public_key *key, - const unsigned char *data, size_t data_len, - buffer_t *result, const char **error_r) -{ - -} - -static int -dcrypt_gnutls_rsa_decrypt(struct dcrypt_private_key *key, - const unsigned char *data, size_t data_len, - buffer_t *result, const char **error_r) -{ - -} - -static int -dcrypt_gnutls_oid_keytype(const unsigned char *oid, size_t oid_len, - enum dcrypt_key_type *key_type, const char **error_r) -{ - -} - -static int -dcrypt_gnutls_keytype_oid(enum dcrypt_key_type key_type, buffer_t *oid, - const char **error_r) -{ - -} - -static const char * -dcrypt_gnutls_oid2name(const unsigned char *oid, size_t oid_len, - const char **error_r) -{ -} - -static int -dcrypt_gnutls_name2oid(const char *name, buffer_t *oid, const char **error_r) -{ - -} - -static struct dcrypt_vfs dcrypt_gnutls_vfs = { - .ctx_sym_create = dcrypt_gnutls_ctx_sym_create, - .ctx_sym_destroy = dcrypt_gnutls_ctx_sym_destroy, - .ctx_sym_set_key = dcrypt_gnutls_ctx_sym_set_key, - .ctx_sym_set_iv = dcrypt_gnutls_ctx_sym_set_iv, - .ctx_sym_set_key_iv_random = dcrypt_gnutls_ctx_sym_set_key_iv_random, - .ctx_sym_get_key = dcrypt_gnutls_ctx_sym_get_key, - .ctx_sym_get_iv = dcrypt_gnutls_ctx_sym_get_iv, - .ctx_sym_get_key_length = dcrypt_gnutls_ctx_sym_get_key_length, - .ctx_sym_get_iv_length = dcrypt_gnutls_ctx_sym_get_iv_length, - .ctx_sym_init = dcrypt_gnutls_ctx_sym_init, - .ctx_sym_update = dcrypt_gnutls_ctx_sym_update, - .ctx_sym_final = dcrypt_gnutls_ctx_sym_final, - .ctx_hmac_create = dcrypt_gnutls_ctx_hmac_create, - .ctx_hmac_destroy = dcrypt_gnutls_ctx_hmac_destroy, - .ctx_hmac_set_key = dcrypt_gnutls_ctx_hmac_set_key, - .ctx_hmac_get_key = dcrypt_gnutls_ctx_hmac_get_key, - .ctx_hmac_init = dcrypt_gnutls_ctx_hmac_init, - .ctx_hmac_update = dcrypt_gnutls_ctx_hmac_update, - .ctx_hmac_final = dcrypt_gnutls_ctx_hmac_final, -// .ecdh_derive_secret = dcrypt_gnutls_ecdh_derive_secret, - .pbkdf2 = dcrypt_gnutls_pbkdf2, - .generate_keypair = dcrypt_gnutls_generate_keypair, - .load_private_key = dcrypt_gnutls_load_private_key, - .load_public_key = dcrypt_gnutls_load_public_key, - .store_private_key = dcrypt_gnutls_store_private_key, - .store_public_key = dcrypt_gnutls_store_public_key, - .private_to_public_key = dcrypt_gnutls_private_to_public_key, - .free_keypair = dcrypt_gnutls_free_keypair, - .free_public_key = dcrypt_gnutls_free_public_key, - .free_private_key = dcrypt_gnutls_free_private_key, - .rsa_encrypt = dcrypt_gnutls_rsa_encrypt, - .rsa_decrypt = dcrypt_gnutls_rsa_decrypt, - .oid_keytype = dcrypt_gnutls_oid_keytype, - .keytype_oid = dcrypt_gnutls_keytype_oid, - .oid2name = dcrypt_gnutls_oid2name, - .name2oid = dcrypt_gnutls_name2oid -}; - -void dcrypt_gnutls_init(struct module *module ATTR_UNUSED) -{ - gnutls_global_init(); - dcrypt_set_vfs(&dcrypt_gnutls_vfs); -} - -void dcrypt_gnutls_deinit(void) -{ - gnutls_global_deinit(); -} diff --git a/src/lib-dcrypt/dcrypt-iostream.h b/src/lib-dcrypt/dcrypt-iostream.h deleted file mode 100644 index ec78978eb0..0000000000 --- a/src/lib-dcrypt/dcrypt-iostream.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef DCRYPT_IOSTREAM_H -#define DCRYPT_IOSTREAM_H 1 - -static const unsigned char IOSTREAM_CRYPT_MAGIC[] = - {'C','R','Y','P','T','E','D','\x03','\x07'}; -#define IOSTREAM_CRYPT_VERSION 2 -#define IOSTREAM_TAG_SIZE 16 - -enum io_stream_encrypt_flags { - IO_STREAM_ENC_INTEGRITY_HMAC = 0x1, - IO_STREAM_ENC_INTEGRITY_AEAD = 0x2, - IO_STREAM_ENC_INTEGRITY_NONE = 0x4, - IO_STREAM_ENC_VERSION_1 = 0x8, -}; - -#endif diff --git a/src/lib-dcrypt/dcrypt-openssl.c b/src/lib-dcrypt/dcrypt-openssl.c deleted file mode 100644 index 1a13eb5de5..0000000000 --- a/src/lib-dcrypt/dcrypt-openssl.c +++ /dev/null @@ -1,3779 +0,0 @@ -/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "buffer.h" -#include "base64.h" -#include "str.h" -#include "hex-binary.h" -#include "safe-memset.h" -#include "randgen.h" -#include "array.h" -#include "module-dir.h" -#include "istream.h" -#include "json-tree.h" -#include "dovecot-openssl-common.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "dcrypt.h" -#include "dcrypt-private.h" - -/** - - key format documentation: - ========================= - - v1 key - ------ - algo id = openssl NID - enctype = 0 = none, 1 = ecdhe, 2 = password - key id = sha256(hex encoded public point) - - public key - ---------- - 1algo idpublic point - - private key - ----------- - - enctype none - 1algo id0private pointkey id - - - enctype ecdh (algorithm AES-256-CTR, key = SHA256(shared secret), IV = \0\0\0...) - 1algo id1private pointephemeral public keyencryption key idkey id - - - enctype password (algorithm AES-256-CTR, key = PBKDF2(SHA1, 16, password, salt), IV = \0\0\0...) - 1algo id2private pointsaltkey id - - v2 key - ------ - algo oid = ASN1 OID of key algorithm (RSA or EC curve) - enctype = 0 = none, 1 = ecdhe, 2 = password - key id = SHA256(i2d_PUBKEY) - - public key - ---------- - 2HEX(i2d_PUBKEY)key id - - - enctype none - 2key algo oid0(RSA = i2d_PrivateKey, EC=Private Point)key id - - - enctype ecdh, key,iv = PBKDF2(hash algo, rounds, shared secret, salt) - 2key algo oid1symmetric algo namesalthash algoroundsE(RSA = i2d_PrivateKey, EC=Private Point)ephemeral public keyencryption key idkey id - - - enctype password, key,iv = PBKDF2(hash algo, rounds, password, salt) - 2key algo oid1symmetric algo namesalthash algoroundsE(RSA = i2d_PrivateKey, EC=Private Point)key id -**/ - -#ifndef HAVE_EVP_PKEY_get0 -#define EVP_PKEY_get0_EC_KEY(x) x->pkey.ec -#define EVP_PKEY_get0_RSA(x) x->pkey.rsa -#endif - -#ifndef HAVE_OBJ_LENGTH -#define OBJ_length(o) ((o)->length) -#endif - -#ifndef HAVE_HMAC_CTX_NEW -# define HMAC_Init_ex(ctx, key, key_len, md, impl) \ - HMAC_Init_ex(&(ctx), key, key_len, md, impl) -# define HMAC_Update(ctx, data, len) HMAC_Update(&(ctx), data, len) -# define HMAC_Final(ctx, md, len) HMAC_Final(&(ctx), md, len) -# define HMAC_CTX_free(ctx) HMAC_cleanup(&(ctx)) -#else -# define HMAC_CTX_free(ctx) \ - STMT_START { HMAC_CTX_free(ctx); (ctx) = NULL; } STMT_END -#endif - -/* Not always present */ -#ifndef HAVE_BN_SECURE_NEW -# define BN_secure_new BN_new -#endif - -/* openssl manual says this is OK */ -#define OID_TEXT_MAX_LEN 80 - -#define t_base64url_decode_str(x) t_base64url_decode_str(BASE64_DECODE_FLAG_IGNORE_PADDING, (x)) - -struct dcrypt_context_symmetric { - pool_t pool; - const EVP_CIPHER *cipher; - EVP_CIPHER_CTX *ctx; - unsigned char *key; - unsigned char *iv; - unsigned char *aad; - size_t aad_len; - unsigned char *tag; - size_t tag_len; - int padding; - int mode; -}; - -struct dcrypt_context_hmac { - pool_t pool; - const EVP_MD *md; -#ifdef HAVE_HMAC_CTX_NEW - HMAC_CTX *ctx; -#else - HMAC_CTX ctx; -#endif - unsigned char *key; - size_t klen; -}; - -struct dcrypt_public_key { - EVP_PKEY *key; - unsigned int ref; - enum dcrypt_key_usage usage; - char *key_id; -}; - -struct dcrypt_private_key { - EVP_PKEY *key; - unsigned int ref; - enum dcrypt_key_usage usage; - char *key_id; -}; - -#define DCRYPT_SET_ERROR(error) STMT_START { if (error_r != NULL) *error_r = (error); } STMT_END - -static bool -dcrypt_openssl_public_key_id(struct dcrypt_public_key *key, - const char *algorithm, buffer_t *result, - const char **error_r); -static bool -dcrypt_openssl_public_key_id_old(struct dcrypt_public_key *key, - buffer_t *result, const char **error_r); -static bool -dcrypt_openssl_private_key_id(struct dcrypt_private_key *key, - const char *algorithm, buffer_t *result, - const char **error_r); -static bool -dcrypt_openssl_private_key_id_old(struct dcrypt_private_key *key, - buffer_t *result, const char **error_r); -static void -dcrypt_openssl_private_to_public_key(struct dcrypt_private_key *priv_key, - struct dcrypt_public_key **pub_key_r); -static void -dcrypt_openssl_unref_private_key(struct dcrypt_private_key **key); -static void -dcrypt_openssl_unref_public_key(struct dcrypt_public_key **key); -static bool -dcrypt_openssl_rsa_decrypt(struct dcrypt_private_key *key, - const unsigned char *data, size_t data_len, - buffer_t *result, enum dcrypt_padding padding, - const char **error_r); -static bool -dcrypt_openssl_key_string_get_info(const char *key_data, - enum dcrypt_key_format *format_r, enum dcrypt_key_version *version_r, - enum dcrypt_key_kind *kind_r, - enum dcrypt_key_encryption_type *encryption_type_r, - const char **encryption_key_hash_r, const char **key_hash_r, - const char **error_r); - -static bool dcrypt_openssl_error(const char **error_r) -{ - unsigned long ec; - - if (error_r == NULL) { - /* caller is not really interested */ - return FALSE; - } - - ec = ERR_get_error(); - DCRYPT_SET_ERROR(t_strdup_printf("%s", ERR_error_string(ec, NULL))); - return FALSE; -} - -static int -dcrypt_openssl_padding_mode(enum dcrypt_padding padding, - bool sig, const char **error_r) -{ - switch (padding) { - case DCRYPT_PADDING_DEFAULT: - if (sig) return RSA_PKCS1_PSS_PADDING; - else return RSA_PKCS1_OAEP_PADDING; - case DCRYPT_PADDING_RSA_PKCS1_OAEP: - return RSA_PKCS1_OAEP_PADDING; - case DCRYPT_PADDING_RSA_PKCS1_PSS: - return RSA_PKCS1_PSS_PADDING; - case DCRYPT_PADDING_RSA_PKCS1: - return RSA_PKCS1_PADDING; - case DCRYPT_PADDING_RSA_NO: - return RSA_NO_PADDING; - default: - DCRYPT_SET_ERROR("Unsupported padding mode"); - return -1; - } - i_unreached(); -} - -static bool dcrypt_openssl_initialize(const struct dcrypt_settings *set, - const char **error_r) -{ - if (set->crypto_device != NULL && set->crypto_device[0] != '\0') { - if (dovecot_openssl_common_global_set_engine( - set->crypto_device, error_r) <= 0) - return FALSE; - } - return TRUE; -} - -/* legacy function for old formats that generates - hex encoded point from EC public key - */ -static char *ec_key_get_pub_point_hex(const EC_KEY *key) -{ - const EC_POINT *p; - const EC_GROUP *g; - - p = EC_KEY_get0_public_key(key); - g = EC_KEY_get0_group(key); - return EC_POINT_point2hex(g, p, POINT_CONVERSION_COMPRESSED, NULL); -} - -static bool -dcrypt_openssl_ctx_sym_create(const char *algorithm, enum dcrypt_sym_mode mode, - struct dcrypt_context_symmetric **ctx_r, - const char **error_r) -{ - struct dcrypt_context_symmetric *ctx; - pool_t pool; - const EVP_CIPHER *cipher; - - cipher = EVP_get_cipherbyname(algorithm); - if (cipher == NULL) { - DCRYPT_SET_ERROR(t_strdup_printf("Invalid cipher %s", - algorithm)); - return FALSE; - } - - /* allocate context */ - pool = pool_alloconly_create("dcrypt openssl", 1024); - ctx = p_new(pool, struct dcrypt_context_symmetric, 1); - ctx->pool = pool; - ctx->cipher = cipher; - ctx->padding = 1; - ctx->mode = (mode == DCRYPT_MODE_ENCRYPT ? 1 : 0); - *ctx_r = ctx; - return TRUE; -} - -static void -dcrypt_openssl_ctx_sym_destroy(struct dcrypt_context_symmetric **ctx) -{ - pool_t pool = (*ctx)->pool; - - if ((*ctx)->ctx != NULL) - EVP_CIPHER_CTX_free((*ctx)->ctx); - pool_unref(&pool); - *ctx = NULL; -} - -static void -dcrypt_openssl_ctx_sym_set_key(struct dcrypt_context_symmetric *ctx, - const unsigned char *key, size_t key_len) -{ - if (ctx->key != NULL) - p_free(ctx->pool, ctx->key); - ctx->key = p_malloc(ctx->pool, EVP_CIPHER_key_length(ctx->cipher)); - memcpy(ctx->key, key, I_MIN(key_len, - (size_t)EVP_CIPHER_key_length(ctx->cipher))); -} - -static void -dcrypt_openssl_ctx_sym_set_iv(struct dcrypt_context_symmetric *ctx, - const unsigned char *iv, size_t iv_len) -{ - if(ctx->iv != NULL) - p_free(ctx->pool, ctx->iv); - - ctx->iv = p_malloc(ctx->pool, EVP_CIPHER_iv_length(ctx->cipher)); - memcpy(ctx->iv, iv, I_MIN(iv_len, - (size_t)EVP_CIPHER_iv_length(ctx->cipher))); -} - -static void -dcrypt_openssl_ctx_sym_set_key_iv_random(struct dcrypt_context_symmetric *ctx) -{ - if(ctx->key != NULL) - p_free(ctx->pool, ctx->key); - if(ctx->iv != NULL) - p_free(ctx->pool, ctx->iv); - - ctx->key = p_malloc(ctx->pool, EVP_CIPHER_key_length(ctx->cipher)); - random_fill(ctx->key, EVP_CIPHER_key_length(ctx->cipher)); - ctx->iv = p_malloc(ctx->pool, EVP_CIPHER_iv_length(ctx->cipher)); - random_fill(ctx->iv, EVP_CIPHER_iv_length(ctx->cipher)); -} - -static void -dcrypt_openssl_ctx_sym_set_padding(struct dcrypt_context_symmetric *ctx, - bool padding) -{ - ctx->padding = (padding?1:0); -} - -static bool -dcrypt_openssl_ctx_sym_get_key(struct dcrypt_context_symmetric *ctx, - buffer_t *key) -{ - if(ctx->key == NULL) - return FALSE; - - buffer_append(key, ctx->key, EVP_CIPHER_key_length(ctx->cipher)); - return TRUE; -} - -static bool -dcrypt_openssl_ctx_sym_get_iv(struct dcrypt_context_symmetric *ctx, - buffer_t *iv) -{ - if(ctx->iv == NULL) - return FALSE; - - buffer_append(iv, ctx->iv, EVP_CIPHER_iv_length(ctx->cipher)); - return TRUE; -} - -static void -dcrypt_openssl_ctx_sym_set_aad(struct dcrypt_context_symmetric *ctx, - const unsigned char *aad, size_t aad_len) -{ - if (ctx->aad != NULL) - p_free(ctx->pool, ctx->aad); - - /* allow empty aad */ - ctx->aad = p_malloc(ctx->pool, I_MAX(1,aad_len)); - memcpy(ctx->aad, aad, aad_len); - ctx->aad_len = aad_len; -} - -static bool -dcrypt_openssl_ctx_sym_get_aad(struct dcrypt_context_symmetric *ctx, - buffer_t *aad) -{ - if (ctx->aad == NULL) - return FALSE; - - buffer_append(aad, ctx->aad, ctx->aad_len); - return TRUE; -} - -static void -dcrypt_openssl_ctx_sym_set_tag(struct dcrypt_context_symmetric *ctx, - const unsigned char *tag, size_t tag_len) -{ - if (ctx->tag != NULL) - p_free(ctx->pool, ctx->tag); - - /* unlike aad, tag cannot be empty */ - ctx->tag = p_malloc(ctx->pool, tag_len); - memcpy(ctx->tag, tag, tag_len); - ctx->tag_len = tag_len; -} - -static bool -dcrypt_openssl_ctx_sym_get_tag(struct dcrypt_context_symmetric *ctx, - buffer_t *tag) -{ - if (ctx->tag == NULL) - return FALSE; - - buffer_append(tag, ctx->tag, ctx->tag_len); - return TRUE; -} - -static unsigned int -dcrypt_openssl_ctx_sym_get_key_length(struct dcrypt_context_symmetric *ctx) -{ - return EVP_CIPHER_key_length(ctx->cipher); -} - -static unsigned int -dcrypt_openssl_ctx_sym_get_iv_length(struct dcrypt_context_symmetric *ctx) -{ - return EVP_CIPHER_iv_length(ctx->cipher); -} - -static unsigned int -dcrypt_openssl_ctx_sym_get_block_size(struct dcrypt_context_symmetric *ctx) -{ - return EVP_CIPHER_block_size(ctx->cipher); -} - -static bool -dcrypt_openssl_ctx_sym_init(struct dcrypt_context_symmetric *ctx, - const char **error_r) -{ - int ec; - int len; - - i_assert(ctx->key != NULL); - i_assert(ctx->iv != NULL); - i_assert(ctx->ctx == NULL); - - if((ctx->ctx = EVP_CIPHER_CTX_new()) == NULL) - return dcrypt_openssl_error(error_r); - - ec = EVP_CipherInit_ex(ctx->ctx, ctx->cipher, NULL, - ctx->key, ctx->iv, ctx->mode); - if (ec != 1) - return dcrypt_openssl_error(error_r); - - EVP_CIPHER_CTX_set_padding(ctx->ctx, ctx->padding); - len = 0; - if (ctx->aad != NULL) { - ec = EVP_CipherUpdate(ctx->ctx, NULL, &len, - ctx->aad, ctx->aad_len); - } - if (ec != 1) - return dcrypt_openssl_error(error_r); - return TRUE; -} - -static bool -dcrypt_openssl_ctx_sym_update(struct dcrypt_context_symmetric *ctx, - const unsigned char *data, size_t data_len, - buffer_t *result, const char **error_r) -{ - const size_t block_size = (size_t)EVP_CIPHER_block_size(ctx->cipher); - size_t buf_used = result->used; - unsigned char *buf; - int outl; - - i_assert(ctx->ctx != NULL); - - /* From `man 3 evp_cipherupdate`: - - EVP_EncryptUpdate() encrypts inl bytes from the buffer in and writes - the encrypted version to out. This function can be called multiple - times to encrypt successive blocks of data. The amount of data - written depends on the block alignment of the encrypted data: as a - result the amount of data written may be anything from zero bytes to - (inl + cipher_block_size - 1) so out should contain sufficient room. - The actual number of bytes written is placed in outl. - */ - - buf = buffer_append_space_unsafe(result, data_len + block_size); - outl = 0; - if (EVP_CipherUpdate - (ctx->ctx, buf, &outl, data, data_len) != 1) - return dcrypt_openssl_error(error_r); - buffer_set_used_size(result, buf_used + outl); - return TRUE; -} - -static bool -dcrypt_openssl_ctx_sym_final(struct dcrypt_context_symmetric *ctx, - buffer_t *result, const char **error_r) -{ - const size_t block_size = (size_t)EVP_CIPHER_block_size(ctx->cipher); - size_t buf_used = result->used; - unsigned char *buf; - int outl; - int ec; - - i_assert(ctx->ctx != NULL); - - /* From `man 3 evp_cipherupdate`: - - If padding is enabled (the default) then EVP_EncryptFinal_ex() - encrypts the "final" data, that is any data that remains in a partial - block. It uses standard block padding (aka PKCS padding). The - encrypted final data is written to out which should have sufficient - space for one cipher block. The number of bytes written is placed in - outl. After this function is called the encryption operation is - finished and no further calls to EVP_EncryptUpdate() should be made. - */ - - buf = buffer_append_space_unsafe(result, block_size); - outl = 0; - - /* when **DECRYPTING** set expected tag */ - if (ctx->mode == 0 && ctx->tag != NULL) { - ec = EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_SET_TAG, - ctx->tag_len, ctx->tag); - } else { - ec = 1; - } - - if (ec == 1) - ec = EVP_CipherFinal_ex(ctx->ctx, buf, &outl); - - if (ec == 1) { - buffer_set_used_size(result, buf_used + outl); - /* when **ENCRYPTING** recover tag */ - if (ctx->mode == 1 && ctx->aad != NULL) { - /* tag should be NULL here */ - i_assert(ctx->tag == NULL); - /* openssl claims taglen is always 16, go figure .. */ - ctx->tag = p_malloc(ctx->pool, EVP_GCM_TLS_TAG_LEN); - ec = EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_GET_TAG, - EVP_GCM_TLS_TAG_LEN, ctx->tag); - ctx->tag_len = EVP_GCM_TLS_TAG_LEN; - } - } - - if (ec == 0) - DCRYPT_SET_ERROR("data authentication failed"); - else if (ec < 0) - dcrypt_openssl_error(error_r); - - EVP_CIPHER_CTX_free(ctx->ctx); - ctx->ctx = NULL; - - return (ec == 1); -} - -static bool -dcrypt_openssl_ctx_hmac_create(const char *algorithm, - struct dcrypt_context_hmac **ctx_r, - const char **error_r) -{ - struct dcrypt_context_hmac *ctx; - pool_t pool; - const EVP_MD *md; - - md = EVP_get_digestbyname(algorithm); - if(md == NULL) { - DCRYPT_SET_ERROR(t_strdup_printf("Invalid digest %s", - algorithm)); - return FALSE; - } - - /* allocate context */ - pool = pool_alloconly_create("dcrypt openssl", 1024); - ctx = p_new(pool, struct dcrypt_context_hmac, 1); - ctx->pool = pool; - ctx->md = md; - *ctx_r = ctx; - return TRUE; -} - -static void -dcrypt_openssl_ctx_hmac_destroy(struct dcrypt_context_hmac **ctx) -{ - pool_t pool = (*ctx)->pool; - HMAC_CTX_free((*ctx)->ctx); - pool_unref(&pool); - *ctx = NULL; -} - -static void -dcrypt_openssl_ctx_hmac_set_key(struct dcrypt_context_hmac *ctx, - const unsigned char *key, size_t key_len) -{ - if (ctx->key != NULL) - p_free(ctx->pool, ctx->key); - - ctx->klen = I_MIN(key_len, HMAC_MAX_MD_CBLOCK); - ctx->key = p_malloc(ctx->pool, ctx->klen); - memcpy(ctx->key, key, ctx->klen); -} - -static bool -dcrypt_openssl_ctx_hmac_get_key(struct dcrypt_context_hmac *ctx, buffer_t *key) -{ - if (ctx->key == NULL) - return FALSE; - buffer_append(key, ctx->key, ctx->klen); - return TRUE; -} - -static void -dcrypt_openssl_ctx_hmac_set_key_random(struct dcrypt_context_hmac *ctx) -{ - ctx->klen = HMAC_MAX_MD_CBLOCK; - ctx->key = p_malloc(ctx->pool, ctx->klen); - random_fill(ctx->key, ctx->klen); -} - -static unsigned int -dcrypt_openssl_ctx_hmac_get_digest_length(struct dcrypt_context_hmac *ctx) -{ - return EVP_MD_size(ctx->md); -} - -static bool -dcrypt_openssl_ctx_hmac_init(struct dcrypt_context_hmac *ctx, - const char **error_r) -{ - int ec; - - i_assert(ctx->md != NULL); -#ifdef HAVE_HMAC_CTX_NEW - ctx->ctx = HMAC_CTX_new(); - if (ctx->ctx == NULL) - return dcrypt_openssl_error(error_r); -#endif - ec = HMAC_Init_ex(ctx->ctx, ctx->key, ctx->klen, ctx->md, NULL); - if (ec != 1) - return dcrypt_openssl_error(error_r); - return TRUE; -} - -static bool -dcrypt_openssl_ctx_hmac_update(struct dcrypt_context_hmac *ctx, - const unsigned char *data, size_t data_len, - const char **error_r) -{ - int ec; - - ec = HMAC_Update(ctx->ctx, data, data_len); - if (ec != 1) - return dcrypt_openssl_error(error_r); - return TRUE; -} - -static bool -dcrypt_openssl_ctx_hmac_final(struct dcrypt_context_hmac *ctx, buffer_t *result, - const char **error_r) -{ - int ec; - unsigned char buf[HMAC_MAX_MD_CBLOCK]; - unsigned int outl; - - ec = HMAC_Final(ctx->ctx, buf, &outl); - HMAC_CTX_free(ctx->ctx); - if (ec == 1) - buffer_append(result, buf, outl); - else - return dcrypt_openssl_error(error_r); - return TRUE; -} - -static bool -dcrypt_openssl_generate_ec_key(int nid, EVP_PKEY **key, const char **error_r) -{ - EVP_PKEY_CTX *pctx; - EVP_PKEY_CTX *ctx; - EVP_PKEY *params = NULL; - - /* generate parameters for EC */ - pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); - if (pctx == NULL || - EVP_PKEY_paramgen_init(pctx) < 1 || - EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) < 1 || - EVP_PKEY_paramgen(pctx, ¶ms) < 1) - { - dcrypt_openssl_error(error_r); - EVP_PKEY_CTX_free(pctx); - return FALSE; - } - - /* generate key from parameters */ - ctx = EVP_PKEY_CTX_new(params, NULL); - if (ctx == NULL || - EVP_PKEY_keygen_init(ctx) < 1 || - EVP_PKEY_keygen(ctx, key) < 1) - { - dcrypt_openssl_error(error_r); - EVP_PKEY_free(params); - EVP_PKEY_CTX_free(pctx); - EVP_PKEY_CTX_free(ctx); - return FALSE; - } - - EVP_PKEY_free(params); - EVP_PKEY_CTX_free(pctx); - EVP_PKEY_CTX_free(ctx); - EC_KEY_set_asn1_flag(EVP_PKEY_get0_EC_KEY((*key)), - OPENSSL_EC_NAMED_CURVE); - return TRUE; -} - -static bool -dcrypt_openssl_generate_rsa_key(int bits, EVP_PKEY **key, const char **error_r) -{ - i_assert(bits >= 256); - int ec = 0; - - EVP_PKEY_CTX *ctx; - ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); - if (ctx == NULL || - EVP_PKEY_keygen_init(ctx) < 1 || - EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) < 1 || - EVP_PKEY_keygen(ctx, key) < 1) { - dcrypt_openssl_error(error_r); - ec = -1; - } - - EVP_PKEY_CTX_free(ctx); - return ec == 0; -} - -static bool -dcrypt_openssl_ecdh_derive_secret(struct dcrypt_private_key *priv_key, - struct dcrypt_public_key *pub_key, - buffer_t *shared_secret, - const char **error_r) -{ - /* initialize */ - EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(priv_key->key, NULL); - if (pctx == NULL || - EVP_PKEY_derive_init(pctx) != 1 || - EVP_PKEY_derive_set_peer(pctx, pub_key->key) != 1) { - EVP_PKEY_CTX_free(pctx); - return dcrypt_openssl_error(error_r); - } - - /* derive */ - size_t len; - if (EVP_PKEY_derive(pctx, NULL, &len) != 1) { - EVP_PKEY_CTX_free(pctx); - return dcrypt_openssl_error(error_r); - } - unsigned char buf[len]; - if (EVP_PKEY_derive(pctx, buf, &len) != 1) { - EVP_PKEY_CTX_free(pctx); - return dcrypt_openssl_error(error_r); - } - - EVP_PKEY_CTX_free(pctx); - buffer_append(shared_secret, buf, len); - - return TRUE; -} - -static bool -dcrypt_openssl_ecdh_derive_secret_local(struct dcrypt_private_key *local_key, - buffer_t *R, buffer_t *S, - const char **error_r) -{ - bool ret; - i_assert(local_key != NULL && local_key->key != NULL); - - EVP_PKEY *local = local_key->key; - BN_CTX *bn_ctx = BN_CTX_new(); - if (bn_ctx == NULL) - return dcrypt_openssl_error(error_r); - - const EC_GROUP *grp = EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(local)); - EC_POINT *pub = EC_POINT_new(grp); - - /* convert ephemeral key data EC point */ - if (pub == NULL || - EC_POINT_oct2point(grp, pub, R->data, R->used, bn_ctx) != 1) - { - EC_POINT_free(pub); - BN_CTX_free(bn_ctx); - return dcrypt_openssl_error(error_r); - } - EC_KEY *ec_key = EC_KEY_new(); - - /* convert point to public key */ - int ec = 0; - if (ec_key == NULL || - EC_KEY_set_group(ec_key, grp) != 1 || - EC_KEY_set_public_key(ec_key, pub) != 1) - ec = -1; - else - EC_POINT_free(pub); - BN_CTX_free(bn_ctx); - - /* make sure it looks like a valid key */ - if (ec == -1 || EC_KEY_check_key(ec_key) != 1) { - EC_KEY_free(ec_key); - return dcrypt_openssl_error(error_r); - } - - EVP_PKEY *peer = EVP_PKEY_new(); - if (peer == NULL) { - EC_KEY_free(ec_key); - return dcrypt_openssl_error(error_r); - } - EVP_PKEY_set1_EC_KEY(peer, ec_key); - EC_KEY_free(ec_key); - - struct dcrypt_public_key pub_key; - i_zero(&pub_key); - pub_key.key = peer; - - ret = dcrypt_openssl_ecdh_derive_secret(local_key, &pub_key, S, error_r); - - EVP_PKEY_free(peer); - return ret; -} - -static bool -dcrypt_openssl_ecdh_derive_secret_peer(struct dcrypt_public_key *peer_key, - buffer_t *R, buffer_t *S, - const char **error_r) -{ - i_assert(peer_key != NULL && peer_key->key != NULL); - bool ret; - - /* ensure peer_key is EC key */ - EVP_PKEY *local = NULL; - EVP_PKEY *peer = peer_key->key; - if (EVP_PKEY_base_id(peer) != EVP_PKEY_EC) { - DCRYPT_SET_ERROR("Only ECC key can be used"); - return FALSE; - } - - /* generate another key from same group */ - int nid = EC_GROUP_get_curve_name( - EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(peer))); - if (!dcrypt_openssl_generate_ec_key(nid, &local, error_r)) - return FALSE; - - struct dcrypt_private_key priv_key; - i_zero(&priv_key); - priv_key.key = local; - - if (!(ret = dcrypt_openssl_ecdh_derive_secret(&priv_key, peer_key, S, - error_r))) { - EVP_PKEY_free(local); - return FALSE; - } - - /* get ephemeral key (=R) */ - BN_CTX *bn_ctx = BN_CTX_new(); - const EC_POINT *pub = EC_KEY_get0_public_key(EVP_PKEY_get0_EC_KEY(local)); - const EC_GROUP *grp = EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(local)); - size_t len = EC_POINT_point2oct(grp, pub, POINT_CONVERSION_UNCOMPRESSED, - NULL, 0, bn_ctx); - unsigned char R_buf[len]; - EC_POINT_point2oct(grp, pub, POINT_CONVERSION_UNCOMPRESSED, - R_buf, len, bn_ctx); - BN_CTX_free(bn_ctx); - buffer_append(R, R_buf, len); - EVP_PKEY_free(local); - - return ret; -} - -static bool -dcrypt_openssl_pbkdf2(const unsigned char *password, size_t password_len, - const unsigned char *salt, size_t salt_len, - const char *hash, unsigned int rounds, - buffer_t *result, unsigned int result_len, - const char **error_r) -{ - int ret; - i_assert(rounds > 0); - i_assert(result_len > 0); - i_assert(result != NULL); - /* determine MD */ - const EVP_MD* md = EVP_get_digestbyname(hash); - if (md == NULL) { - DCRYPT_SET_ERROR(t_strdup_printf("Invalid digest %s", hash)); - return FALSE; - } - - unsigned char buffer[result_len]; - if ((ret = PKCS5_PBKDF2_HMAC((const char*)password, password_len, - salt, salt_len, rounds, - md, result_len, buffer)) == 1) { - buffer_append(result, buffer, result_len); - } - if (ret != 1) - return dcrypt_openssl_error(error_r); - return TRUE; -} - -static bool -dcrypt_openssl_generate_keypair(struct dcrypt_keypair *pair_r, - enum dcrypt_key_type kind, unsigned int bits, - const char *curve, const char **error_r) -{ - EVP_PKEY *pkey = NULL; - - i_assert(pair_r != NULL); - i_zero(pair_r); - if (kind == DCRYPT_KEY_RSA) { - if (dcrypt_openssl_generate_rsa_key(bits, &pkey, error_r)) { - pair_r->priv = i_new(struct dcrypt_private_key, 1); - pair_r->priv->key = pkey; - pair_r->priv->ref++; - pair_r->pub = NULL; - dcrypt_openssl_private_to_public_key(pair_r->priv, - &pair_r->pub); - return TRUE; - } else { - return dcrypt_openssl_error(error_r); - } - } else if (kind == DCRYPT_KEY_EC) { - int nid = OBJ_sn2nid(curve); - if (nid == NID_undef) { - DCRYPT_SET_ERROR(t_strdup_printf("Unknown EC curve %s", - curve)); - return FALSE; - } - if (dcrypt_openssl_generate_ec_key(nid, &pkey, error_r)) { - pair_r->priv = i_new(struct dcrypt_private_key, 1); - pair_r->priv->key = pkey; - pair_r->priv->ref++; - pair_r->pub = NULL; - dcrypt_openssl_private_to_public_key(pair_r->priv, - &pair_r->pub); - return TRUE; - } else { - return dcrypt_openssl_error(error_r); - } - } - DCRYPT_SET_ERROR("Key type not supported in this build"); - return FALSE; -} - -static bool -dcrypt_openssl_decrypt_point_v1(buffer_t *data, buffer_t *key, BIGNUM **point_r, - const char **error_r) -{ - struct dcrypt_context_symmetric *dctx; - buffer_t *tmp = t_buffer_create(64); - - if (!dcrypt_openssl_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, - &dctx, error_r)) { - return FALSE; - } - - /* v1 KEYS have all-zero IV - have to use it ourselves too */ - dcrypt_openssl_ctx_sym_set_iv(dctx, (const unsigned char*) - "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16); - dcrypt_openssl_ctx_sym_set_key(dctx, key->data, key->used); - - if (!dcrypt_openssl_ctx_sym_init(dctx, error_r) || - !dcrypt_openssl_ctx_sym_update(dctx, data->data, data->used, - tmp, error_r) || - !dcrypt_openssl_ctx_sym_final(dctx, tmp, error_r)) { - dcrypt_openssl_ctx_sym_destroy(&dctx); - return FALSE; - } - - dcrypt_openssl_ctx_sym_destroy(&dctx); - - *point_r = BN_bin2bn(tmp->data, tmp->used, NULL); - safe_memset(buffer_get_modifiable_data(tmp, NULL), 0,tmp->used); - buffer_set_used_size(key, 0); - - if (*point_r == NULL) - return dcrypt_openssl_error(error_r); - return TRUE; -} - -static bool -dcrypt_openssl_decrypt_point_ec_v1(struct dcrypt_private_key *dec_key, - const char *data_hex, - const char *peer_key_hex, BIGNUM **point_r, - const char **error_r) -{ - buffer_t *peer_key, *data, key, *secret; - bool res; - - data = t_buffer_create(128); - peer_key = t_buffer_create(64); - - hex_to_binary(data_hex, data); - hex_to_binary(peer_key_hex, peer_key); - - secret = t_buffer_create(64); - - if (!dcrypt_openssl_ecdh_derive_secret_local(dec_key, peer_key, - secret, error_r)) - return FALSE; - - /* run it thru SHA256 once */ - unsigned char digest[SHA256_DIGEST_LENGTH]; - SHA256(secret->data, secret->used, digest); - safe_memset(buffer_get_modifiable_data(secret, NULL), 0, secret->used); - buffer_set_used_size(secret, 0); - buffer_create_from_const_data(&key, digest, SHA256_DIGEST_LENGTH); - - /* then use this as key */ - res = dcrypt_openssl_decrypt_point_v1(data, &key, point_r, error_r); - memset(digest, 0, sizeof(digest)); - safe_memset(digest, 0, SHA256_DIGEST_LENGTH); - - return res; -} - -static bool -dcrypt_openssl_decrypt_point_password_v1(const char *data_hex, - const char *password_hex, - const char *salt_hex, BIGNUM **point_r, - const char **error_r) -{ - buffer_t *salt, *data, *password, *key; - - data = t_buffer_create(128); - salt = t_buffer_create(16); - password = t_buffer_create(32); - key = t_buffer_create(32); - - hex_to_binary(data_hex, data); - hex_to_binary(salt_hex, salt); - hex_to_binary(password_hex, password); - - /* aes-256-ctr uses 32 byte key, and v1 uses all-zero IV */ - if (!dcrypt_openssl_pbkdf2(password->data, password->used, - salt->data, salt->used, - "sha256", 16, key, 32, error_r)) - return FALSE; - - return dcrypt_openssl_decrypt_point_v1(data, key, point_r, error_r); -} - -static bool -dcrypt_openssl_load_private_key_dovecot_v1(struct dcrypt_private_key **key_r, - int len, const char **input, - const char *password, - struct dcrypt_private_key *dec_key, - const char **error_r) -{ - int nid, ec, enctype; - BIGNUM *point = NULL; - - if (str_to_int(input[1], &nid) != 0) { - DCRYPT_SET_ERROR("Corrupted data"); - return FALSE; - } - - if (str_to_int(input[2], &enctype) != 0) { - DCRYPT_SET_ERROR("Corrupted data"); - return FALSE; - } - - /* decode and optionally decipher private key value */ - if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_NONE) { - point = BN_secure_new(); - if (point == NULL || BN_hex2bn(&point, input[3]) < 1) { - BN_free(point); - return dcrypt_openssl_error(error_r); - } - } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD) { - /* by password */ - if (password == NULL) { - DCRYPT_SET_ERROR("password missing"); - return FALSE; - } - const char *enc_priv_pt = input[3]; - const char *salt = input[4]; - if (!dcrypt_openssl_decrypt_point_password_v1( - enc_priv_pt, password, salt, &point, error_r)) { - return FALSE; - } - } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { - /* by key */ - if (dec_key == NULL) { - DCRYPT_SET_ERROR("decrypt key missing"); - return FALSE; - } - const char *enc_priv_pt = input[3]; - const char *peer_key = input[4]; - if (!dcrypt_openssl_decrypt_point_ec_v1( - dec_key, enc_priv_pt, peer_key, &point, error_r)) { - return FALSE; - } - } else { - DCRYPT_SET_ERROR("Invalid key data"); - return FALSE; - } - - EC_KEY *eckey = EC_KEY_new_by_curve_name(nid); - if (eckey == NULL) return dcrypt_openssl_error(error_r); - - /* assign private key */ - BN_CTX *bnctx = BN_CTX_new(); - if (bnctx == NULL) { - EC_KEY_free(eckey); - return dcrypt_openssl_error(error_r); - } - EC_KEY_set_private_key(eckey, point); - EC_KEY_precompute_mult(eckey, bnctx); - EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); - EC_POINT *pub = EC_POINT_new(EC_KEY_get0_group(eckey)); - if (pub == NULL) { - EC_KEY_free(eckey); - BN_CTX_free(bnctx); - return dcrypt_openssl_error(error_r); - } - /* calculate public key */ - ec = EC_POINT_mul(EC_KEY_get0_group(eckey), pub, point, - NULL, NULL, bnctx); - EC_KEY_set_public_key(eckey, pub); - BN_free(point); - EC_POINT_free(pub); - BN_CTX_free(bnctx); - - /* make sure it looks OK and is correct */ - if (ec == 1 && EC_KEY_check_key(eckey) == 1) { - unsigned char digest[SHA256_DIGEST_LENGTH]; - /* validate that the key was loaded correctly */ - char *id = ec_key_get_pub_point_hex(eckey); - if (id == NULL) { - EC_KEY_free(eckey); - return dcrypt_openssl_error(error_r); - } - SHA256((unsigned char*)id, strlen(id), digest); - OPENSSL_free(id); - const char *digest_hex = - binary_to_hex(digest, SHA256_DIGEST_LENGTH); - if (strcmp(digest_hex, input[len-1]) != 0) { - DCRYPT_SET_ERROR("Key id mismatch after load"); - EC_KEY_free(eckey); - return FALSE; - } - EVP_PKEY *key = EVP_PKEY_new(); - if (key == NULL) { - EC_KEY_free(eckey); - return dcrypt_openssl_error(error_r); - } - EVP_PKEY_set1_EC_KEY(key, eckey); - EC_KEY_free(eckey); - *key_r = i_new(struct dcrypt_private_key, 1); - (*key_r)->key = key; - (*key_r)->ref++; - return TRUE; - } - - EC_KEY_free(eckey); - - return dcrypt_openssl_error(error_r); -} - -/* encrypt/decrypt private keys */ -static bool -dcrypt_openssl_cipher_key_dovecot_v2(const char *cipher, - enum dcrypt_sym_mode mode, - buffer_t *input, buffer_t *secret, - buffer_t *salt, const char *digalgo, - unsigned int rounds, buffer_t *result_r, - const char **error_r) -{ - struct dcrypt_context_symmetric *dctx; - bool res; - - if (!dcrypt_openssl_ctx_sym_create(cipher, mode, &dctx, error_r)) { - return FALSE; - } - - /* generate encryption key/iv based on secret/salt */ - buffer_t *key_data = t_buffer_create(128); - res = dcrypt_openssl_pbkdf2(secret->data, secret->used, - salt->data, salt->used, digalgo, rounds, key_data, - dcrypt_openssl_ctx_sym_get_key_length(dctx) + - dcrypt_openssl_ctx_sym_get_iv_length(dctx), - error_r); - - if (!res) { - dcrypt_openssl_ctx_sym_destroy(&dctx); - return FALSE; - } - - buffer_t *tmp = t_buffer_create(128); - const unsigned char *kd = buffer_free_without_data(&key_data); - - /* perform ciphering */ - dcrypt_openssl_ctx_sym_set_key(dctx, kd, - dcrypt_openssl_ctx_sym_get_key_length(dctx)); - dcrypt_openssl_ctx_sym_set_iv(dctx, - kd + dcrypt_openssl_ctx_sym_get_key_length(dctx), - dcrypt_openssl_ctx_sym_get_iv_length(dctx)); - - if (!dcrypt_openssl_ctx_sym_init(dctx, error_r) || - !dcrypt_openssl_ctx_sym_update(dctx, input->data, - input->used, tmp, error_r) || - !dcrypt_openssl_ctx_sym_final(dctx, tmp, error_r)) { - res = FALSE; - } else { - /* provide result if succeeded */ - buffer_append_buf(result_r, tmp, 0, SIZE_MAX); - res = TRUE; - } - /* and ensure no data leaks */ - safe_memset(buffer_get_modifiable_data(tmp, NULL), 0, tmp->used); - - dcrypt_openssl_ctx_sym_destroy(&dctx); - return res; -} - -static bool -dcrypt_openssl_load_private_key_dovecot_v2(struct dcrypt_private_key **key_r, - int len, const char **input, - const char *password, - struct dcrypt_private_key *dec_key, - const char **error_r) -{ - int enctype; - buffer_t *key_data = t_buffer_create(256); - - /* check for encryption type */ - if (str_to_int(input[2], &enctype) != 0) { - DCRYPT_SET_ERROR("Corrupted data"); - return FALSE; - } - - if (enctype < 0 || enctype > 2) { - DCRYPT_SET_ERROR("Corrupted data"); - return FALSE; - } - - /* match encryption type to field counts */ - if ((enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_NONE && len != 5) || - (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD && len != 9) || - (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK && len != 11)) { - DCRYPT_SET_ERROR("Corrupted data"); - return FALSE; - } - - /* get key type */ - int nid = OBJ_txt2nid(input[1]); - - if (nid == NID_undef) - return dcrypt_openssl_error(error_r); - - /* decode and possibly decipher private key value */ - if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_NONE) { - if (hex_to_binary(input[3], key_data) != 0) { - DCRYPT_SET_ERROR("Corrupted data"); - } - } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { - if (dec_key == NULL) { - DCRYPT_SET_ERROR("decrypt key missing"); - return FALSE; - } - unsigned int rounds; - struct dcrypt_public_key *pubkey = NULL; - if (str_to_uint(input[6], &rounds) != 0) { - DCRYPT_SET_ERROR("Corrupted data"); - return FALSE; - } - - buffer_t *data = t_buffer_create(128); - - /* check that we have correct decryption key */ - dcrypt_openssl_private_to_public_key(dec_key, &pubkey); - if (!dcrypt_openssl_public_key_id(pubkey, "sha256", - data, error_r)) { - dcrypt_openssl_unref_public_key(&pubkey); - return FALSE; - } - - dcrypt_openssl_unref_public_key(&pubkey); - - if (strcmp(binary_to_hex(data->data, data->used), - input[9]) != 0) { - DCRYPT_SET_ERROR("No private key available"); - return FALSE; - } - - - buffer_t *salt, *peer_key, *secret; - salt = t_buffer_create(strlen(input[4])/2); - peer_key = t_buffer_create(strlen(input[8])/2); - secret = t_buffer_create(128); - - buffer_set_used_size(data, 0); - hex_to_binary(input[4], salt); - hex_to_binary(input[8], peer_key); - hex_to_binary(input[7], data); - - /* get us secret value to use for key/iv generation */ - if (EVP_PKEY_base_id((EVP_PKEY*)dec_key) == EVP_PKEY_RSA) { - if (!dcrypt_openssl_rsa_decrypt(dec_key, - peer_key->data, peer_key->used, secret, - DCRYPT_PADDING_RSA_PKCS1_OAEP, error_r)) - return FALSE; - } else { - /* perform ECDH */ - if (!dcrypt_openssl_ecdh_derive_secret_local( - dec_key, peer_key, secret, error_r)) - return FALSE; - } - /* decrypt key */ - if (!dcrypt_openssl_cipher_key_dovecot_v2(input[3], - DCRYPT_MODE_DECRYPT, data, secret, salt, - input[5], rounds, key_data, error_r)) { - return FALSE; - } - } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD) { - if (password == NULL) { - DCRYPT_SET_ERROR("password missing"); - return FALSE; - } - unsigned int rounds; - if (str_to_uint(input[6], &rounds) != 0) { - DCRYPT_SET_ERROR("Corrupted data"); - return FALSE; - } - - buffer_t *salt, secret, *data; - salt = t_buffer_create(strlen(input[4])/2); - buffer_create_from_const_data(&secret, password, strlen(password)); - data = t_buffer_create(strlen(input[7])/2); - if (hex_to_binary(input[4], salt) != 0 || - hex_to_binary(input[7], data) != 0) { - DCRYPT_SET_ERROR("Corrupted data"); - return FALSE; - } - - if (!dcrypt_openssl_cipher_key_dovecot_v2(input[3], - DCRYPT_MODE_DECRYPT, data, &secret, salt, - input[5], rounds, key_data, error_r)) { - return FALSE; - } - } - - /* decode actual key */ - if (EVP_PKEY_type(nid) == EVP_PKEY_RSA) { - RSA *rsa = RSA_new(); - const unsigned char *ptr = buffer_get_data(key_data, NULL); - if (rsa == NULL || - d2i_RSAPrivateKey(&rsa, &ptr, key_data->used) == NULL || - RSA_check_key(rsa) != 1) { - safe_memset(buffer_get_modifiable_data(key_data, NULL), - 0, key_data->used); - RSA_free(rsa); - return dcrypt_openssl_error(error_r); - } - safe_memset(buffer_get_modifiable_data(key_data, NULL), - 0, key_data->used); - buffer_set_used_size(key_data, 0); - EVP_PKEY *pkey = EVP_PKEY_new(); - if (pkey == NULL) { - RSA_free(rsa); - return dcrypt_openssl_error(error_r); - } - EVP_PKEY_set1_RSA(pkey, rsa); - RSA_free(rsa); - *key_r = i_new(struct dcrypt_private_key, 1); - (*key_r)->key = pkey; - (*key_r)->ref++; - } else { - int ec; - BIGNUM *point = BN_secure_new(); - if (point == NULL || - BN_mpi2bn(key_data->data, key_data->used, point) == NULL) { - safe_memset(buffer_get_modifiable_data(key_data, NULL), - 0, key_data->used); - BN_free(point); - return dcrypt_openssl_error(error_r); - } - EC_KEY *eckey = EC_KEY_new_by_curve_name(nid); - safe_memset(buffer_get_modifiable_data(key_data, NULL), - 0, key_data->used); - buffer_set_used_size(key_data, 0); - BN_CTX *bnctx = BN_CTX_new(); - if (eckey == NULL || bnctx == NULL) { - BN_free(point); - EC_KEY_free(eckey); - BN_CTX_free(bnctx); - return dcrypt_openssl_error(error_r); - } - EC_KEY_set_private_key(eckey, point); - EC_KEY_precompute_mult(eckey, bnctx); - EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); - EC_POINT *pub = EC_POINT_new(EC_KEY_get0_group(eckey)); - if (pub == NULL) - ec = -1; - else { - /* calculate public key */ - ec = EC_POINT_mul(EC_KEY_get0_group(eckey), pub, point, - NULL, NULL, bnctx); - EC_KEY_set_public_key(eckey, pub); - EC_POINT_free(pub); - } - BN_free(point); - BN_CTX_free(bnctx); - /* make sure the EC key is valid */ - EVP_PKEY *key = EVP_PKEY_new(); - if (ec == 1 && key != NULL && EC_KEY_check_key(eckey) == 1) { - EVP_PKEY_set1_EC_KEY(key, eckey); - EC_KEY_free(eckey); - *key_r = i_new(struct dcrypt_private_key, 1); - (*key_r)->key = key; - (*key_r)->ref++; - } else { - EVP_PKEY_free(key); - EC_KEY_free(eckey); - return dcrypt_openssl_error(error_r); - } - } - - /* finally compare key to key id */ - dcrypt_openssl_private_key_id(*key_r, "sha256", key_data, NULL); - - if (strcmp(binary_to_hex(key_data->data, key_data->used), - input[len-1]) != 0) { - dcrypt_openssl_unref_private_key(key_r); - DCRYPT_SET_ERROR("Key id mismatch after load"); - return FALSE; - } - - return TRUE; -} - -/* JWK Parameter names defined at https://www.iana.org/assignments/jose/jose.xhtml */ - -static const struct jwk_to_ssl_map_entry { - const char *jwk_curve; - int nid; -} jwk_to_ssl_curves[] = -{ - /* See https://tools.ietf.org/search/rfc8422#appendix-A */ - { .jwk_curve = "P-256", .nid = NID_X9_62_prime256v1 }, - { .jwk_curve = "P-384", .nid = NID_secp384r1 }, - { .jwk_curve = "P-521", .nid = NID_secp521r1 }, - { .jwk_curve = NULL, .nid = 0 } -}; - -static const char *key_usage_to_jwk_use(enum dcrypt_key_usage usage) -{ - switch(usage) { - case DCRYPT_KEY_USAGE_NONE: - return NULL; - case DCRYPT_KEY_USAGE_ENCRYPT: - return "enc"; - case DCRYPT_KEY_USAGE_SIGN: - return "sig"; - }; - i_unreached(); -} - -static enum dcrypt_key_usage jwk_use_to_key_usage(const char *use) -{ - if (strcmp(use, "enc") == 0) - return DCRYPT_KEY_USAGE_ENCRYPT; - if (strcmp(use, "sig") == 0) - return DCRYPT_KEY_USAGE_SIGN; - return DCRYPT_KEY_USAGE_NONE; -} - -static int jwk_curve_to_nid(const char *curve) -{ - /* use static mapping table to get correct input for OpenSSL */ - const struct jwk_to_ssl_map_entry *entry = jwk_to_ssl_curves; - for (;entry->jwk_curve != NULL;entry++) - if (strcmp(curve, entry->jwk_curve) == 0) - return entry->nid; - return 0; -} - -static const char *nid_to_jwk_curve(int nid) -{ - const struct jwk_to_ssl_map_entry *entry = jwk_to_ssl_curves; - for (;entry->jwk_curve != NULL;entry++) - if (nid == entry->nid) - return entry->jwk_curve; - return NULL; -} - -/* Loads both public and private key */ -static bool load_jwk_ec_key(EVP_PKEY **key_r, bool want_private_key, - const struct json_tree_node *root, - const char *password ATTR_UNUSED, - struct dcrypt_private_key *dec_key ATTR_UNUSED, - const char **error_r) -{ - i_assert(password == NULL && dec_key == NULL); - const char *crv, *x, *y, *d; - const struct json_tree_node *node; - - if ((node = json_tree_find_key(root, "crv")) == NULL || - (crv = json_tree_get_value_str(node)) == NULL) { - DCRYPT_SET_ERROR("Missing crv parameter"); - return FALSE; - } - - if ((node = json_tree_find_key(root, "x")) == NULL || - (x = json_tree_get_value_str(node)) == NULL) { - DCRYPT_SET_ERROR("Missing x parameter"); - return FALSE; - } - - if ((node = json_tree_find_key(root, "y")) == NULL || - (y = json_tree_get_value_str(node)) == NULL) { - DCRYPT_SET_ERROR("Missing y parameter"); - return FALSE; - } - - if ((node = json_tree_find_key(root, "d")) == NULL || - (d = json_tree_get_value_str(node)) == NULL) { - if (want_private_key) { - DCRYPT_SET_ERROR("Missing d parameter"); - return FALSE; - } - } - - /* base64 decode x and y */ - buffer_t *bx = t_base64url_decode_str(x); - buffer_t *by = t_base64url_decode_str(y); - - /* determine NID */ - int nid = jwk_curve_to_nid(crv); - if (nid == 0) { - DCRYPT_SET_ERROR(t_strdup_printf("Unsupported curve: %s", crv)); - return FALSE; - } - /* create key */ - EC_KEY *ec_key = EC_KEY_new_by_curve_name(nid); - if (ec_key == NULL) { - DCRYPT_SET_ERROR("Cannot allocate memory"); - return FALSE; - } - - BIGNUM *px = BN_new(); - BIGNUM *py = BN_new(); - - if (BN_bin2bn(bx->data, bx->used, px) == NULL || - BN_bin2bn(by->data, by->used, py) == NULL) { - EC_KEY_free(ec_key); - BN_free(px); - BN_free(py); - return dcrypt_openssl_error(error_r); - } - - int ret = EC_KEY_set_public_key_affine_coordinates(ec_key, px, py); - BN_free(px); - BN_free(py); - - if (ret != 1) { - EC_KEY_free(ec_key); - return dcrypt_openssl_error(error_r); - } - - /* FIXME: Support decryption */ - if (want_private_key) { - buffer_t *bd = t_base64url_decode_str(d); - BIGNUM *pd = BN_secure_new(); - if (BN_bin2bn(bd->data, bd->used, pd) == NULL) { - EC_KEY_free(ec_key); - return dcrypt_openssl_error(error_r); - } - ret = EC_KEY_set_private_key(ec_key, pd); - BN_free(pd); - if (ret != 1) { - EC_KEY_free(ec_key); - return dcrypt_openssl_error(error_r); - } - } - - if (EC_KEY_check_key(ec_key) != 1) { - EC_KEY_free(ec_key); - return dcrypt_openssl_error(error_r); - } - - EC_KEY_precompute_mult(ec_key, NULL); - EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE); - - /* return as EVP_PKEY */ - EVP_PKEY *pkey = EVP_PKEY_new(); - EVP_PKEY_set1_EC_KEY(pkey, ec_key); - EC_KEY_free(ec_key); - *key_r = pkey; - - return TRUE; -} - -/* RSA helpers */ -#if !defined(HAVE_RSA_SET0_KEY) -static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) -{ - if (n == NULL || e == NULL) { - RSAerr(0, ERR_R_PASSED_NULL_PARAMETER); - return 0; - } - BN_free(r->n); - r->n = n; - BN_free(r->e); - r->e = e; - BN_free(r->d); - r->d = d; - return 1; -} -#endif -#if !defined(HAVE_RSA_SET0_FACTORS) -static int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) -{ - if (p == NULL || q == NULL) { - RSAerr(0, ERR_R_PASSED_NULL_PARAMETER); - return 0; - } - BN_free(r->p); - r->p = p; - BN_free(r->q); - r->q = q; - return 1; -} -#endif -#if !defined(HAVE_RSA_SET0_CRT_PARAMS) -static int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) -{ - if (dmp1 == NULL || dmq1 == NULL || iqmp == NULL) { - RSAerr(0, ERR_R_PASSED_NULL_PARAMETER); - return 0; - } - BN_free(r->dmp1); - r->dmp1 = dmp1; - BN_free(r->dmq1); - r->dmq1 = dmq1; - BN_free(r->iqmp); - r->iqmp = iqmp; - return 1; -} -#endif - -/* Loads both public and private key */ -static bool load_jwk_rsa_key(EVP_PKEY **key_r, bool want_private_key, - const struct json_tree_node *root, - const char *password ATTR_UNUSED, - struct dcrypt_private_key *dec_key ATTR_UNUSED, - const char **error_r) -{ - const char *n, *e, *d = NULL, *p = NULL, *q = NULL, *dp = NULL; - const char *dq = NULL, *qi = NULL; - const struct json_tree_node *node; - - /* n and e must be present */ - if ((node = json_tree_find_key(root, "n")) == NULL || - (n = json_tree_get_value_str(node)) == NULL) { - DCRYPT_SET_ERROR("Missing n parameter"); - return FALSE; - } - - if ((node = json_tree_find_key(root, "e")) == NULL || - (e = json_tree_get_value_str(node)) == NULL) { - DCRYPT_SET_ERROR("Missing e parameter"); - return FALSE; - } - - if (want_private_key) { - if ((node = json_tree_find_key(root, "d")) == NULL || - (d = json_tree_get_value_str(node)) == NULL) { - DCRYPT_SET_ERROR("Missing d parameter"); - return FALSE; - } - - if ((node = json_tree_find_key(root, "p")) == NULL || - (p = json_tree_get_value_str(node)) == NULL) { - DCRYPT_SET_ERROR("Missing p parameter"); - return FALSE; - } - - if ((node = json_tree_find_key(root, "q")) == NULL || - (q = json_tree_get_value_str(node)) == NULL) { - DCRYPT_SET_ERROR("Missing q parameter"); - return FALSE; - } - - if ((node = json_tree_find_key(root, "dp")) == NULL || - (dp = json_tree_get_value_str(node)) == NULL) { - DCRYPT_SET_ERROR("Missing dp parameter"); - return FALSE; - } - - if ((node = json_tree_find_key(root, "dq")) == NULL || - (dq = json_tree_get_value_str(node)) == NULL) { - DCRYPT_SET_ERROR("Missing dq parameter"); - return FALSE; - } - - if ((node = json_tree_find_key(root, "qi")) == NULL || - (qi = json_tree_get_value_str(node)) == NULL) { - DCRYPT_SET_ERROR("Missing qi parameter"); - return FALSE; - } - } - - /* convert into BIGNUMs */ - BIGNUM *pn, *pe, *pd, *pp, *pq, *pdp, *pdq, *pqi; - buffer_t *bn = t_base64url_decode_str(n); - buffer_t *be = t_base64url_decode_str(e); - if (want_private_key) { - pd = BN_secure_new(); - buffer_t *bd = t_base64url_decode_str(d); - if (BN_bin2bn(bd->data, bd->used, pd) == NULL) { - BN_free(pd); - return dcrypt_openssl_error(error_r); - } - } else { - pd = NULL; - } - - pn = BN_new(); - pe = BN_new(); - - if (BN_bin2bn(bn->data, bn->used, pn) == NULL || - BN_bin2bn(be->data, be->used, pe) == NULL) { - if (pd != NULL) - BN_free(pd); - BN_free(pn); - BN_free(pe); - return dcrypt_openssl_error(error_r); - } - - RSA *rsa_key = RSA_new(); - if (rsa_key == NULL) { - if (pd != NULL) - BN_free(pd); - BN_free(pn); - BN_free(pe); - return dcrypt_openssl_error(error_r); - } - - if (RSA_set0_key(rsa_key, pn, pe, pd) != 1) { - if (pd != NULL) - BN_free(pd); - BN_free(pn); - BN_free(pe); - RSA_free(rsa_key); - return dcrypt_openssl_error(error_r); - } - - if (want_private_key) { - pp = BN_secure_new(); - pq = BN_secure_new(); - pdp = BN_secure_new(); - pdq = BN_secure_new(); - pqi = BN_secure_new(); - - buffer_t *bp = t_base64url_decode_str(p); - buffer_t *bq = t_base64url_decode_str(q); - buffer_t *bdp = t_base64url_decode_str(dp); - buffer_t *bdq = t_base64url_decode_str(dq); - buffer_t *bqi = t_base64url_decode_str(qi); - - if (BN_bin2bn(bp->data, bp->used, pp) == NULL || - BN_bin2bn(bq->data, bq->used, pq) == NULL || - BN_bin2bn(bdp->data, bdp->used, pdp) == NULL || - BN_bin2bn(bdq->data, bdq->used, pdq) == NULL || - BN_bin2bn(bqi->data, bqi->used, pqi) == NULL || - RSA_set0_factors(rsa_key, pp, pq) != 1) { - RSA_free(rsa_key); - BN_free(pp); - BN_free(pq); - BN_free(pdp); - BN_free(pdq); - BN_free(pqi); - return dcrypt_openssl_error(error_r); - } else if (RSA_set0_crt_params(rsa_key, pdp, pdq, pqi) != 1) { - RSA_free(rsa_key); - BN_free(pdp); - BN_free(pdq); - BN_free(pqi); - return dcrypt_openssl_error(error_r); - } - } - - /* return as EVP_PKEY */ - EVP_PKEY *pkey = EVP_PKEY_new(); - EVP_PKEY_set1_RSA(pkey, rsa_key); - RSA_free(rsa_key); - *key_r = pkey; - - return TRUE; -} - - -static bool -dcrypt_openssl_load_private_key_jwk(struct dcrypt_private_key **key_r, - const char *data, const char *password, - struct dcrypt_private_key *dec_key, - const char **error_r) -{ - const char *kty; - const char *error; - const struct json_tree_node *root, *node; - struct json_tree *key_tree; - EVP_PKEY *pkey; - bool ret; - - if (parse_jwk_key(data, &key_tree, &error) != 0) { - DCRYPT_SET_ERROR(t_strdup_printf("Cannot load JWK private key: %s", - error)); - return FALSE; - } - - root = json_tree_root(key_tree); - - /* check key type */ - if ((node = json_tree_find_key(root, "kty")) == NULL) { - DCRYPT_SET_ERROR("Cannot load JWK private key: no kty parameter"); - json_tree_deinit(&key_tree); - return FALSE; - } - - kty = json_tree_get_value_str(node); - - if (null_strcmp(kty, "EC") == 0) { - ret = load_jwk_ec_key(&pkey, TRUE, root, password, dec_key, &error); - } else if (strcmp(kty, "RSA") == 0) { - ret = load_jwk_rsa_key(&pkey, TRUE, root, password, dec_key, &error); - } else { - error = "Unsupported key type"; - ret = FALSE; - } - - i_assert(ret || error != NULL); - - if (!ret) - DCRYPT_SET_ERROR(t_strdup_printf("Cannot load JWK private key: %s", error)); - else if (ret) { - *key_r = i_new(struct dcrypt_private_key, 1); - (*key_r)->key = pkey; - (*key_r)->ref++; - /* check if kid is present */ - if ((node = json_tree_find_key(root, "kid")) != NULL) - (*key_r)->key_id = i_strdup_empty(json_tree_get_value_str(node)); - /* check if use is present */ - if ((node = json_tree_find_key(root, "use")) != NULL) - (*key_r)->usage = jwk_use_to_key_usage(json_tree_get_value_str(node)); - } - - json_tree_deinit(&key_tree); - - return ret; -} - -static bool -dcrypt_openssl_load_public_key_jwk(struct dcrypt_public_key **key_r, - const char *data, const char **error_r) -{ - const char *kty; - const char *error; - const struct json_tree_node *root, *node; - struct json_tree *key_tree; - EVP_PKEY *pkey; - bool ret; - - if (parse_jwk_key(data, &key_tree, &error) != 0) { - DCRYPT_SET_ERROR(t_strdup_printf("Cannot load JWK public key: %s", - error)); - return FALSE; - } - - root = json_tree_root(key_tree); - - /* check key type */ - if ((node = json_tree_find_key(root, "kty")) == NULL) { - DCRYPT_SET_ERROR("Cannot load JWK public key: no kty parameter"); - json_tree_deinit(&key_tree); - return FALSE; - } - - kty = json_tree_get_value_str(node); - - if (null_strcmp(kty, "EC") == 0) { - ret = load_jwk_ec_key(&pkey, FALSE, root, NULL, NULL, &error); - } else if (strcmp(kty, "RSA") == 0) { - ret = load_jwk_rsa_key(&pkey, FALSE, root, NULL, NULL, &error); - } else { - error = "Unsupported key type"; - ret = FALSE; - } - - i_assert(ret || error != NULL); - - if (!ret) - DCRYPT_SET_ERROR(t_strdup_printf("Cannot load JWK public key: %s", error)); - else if (ret) { - *key_r = i_new(struct dcrypt_public_key, 1); - (*key_r)->key = pkey; - (*key_r)->ref++; - /* check if kid is present */ - if ((node = json_tree_find_key(root, "kid")) != NULL) - (*key_r)->key_id = i_strdup_empty(json_tree_get_value_str(node)); - /* check if use is present */ - if ((node = json_tree_find_key(root, "use")) != NULL) - (*key_r)->usage = jwk_use_to_key_usage(json_tree_get_value_str(node)); - } - - json_tree_deinit(&key_tree); - - return ret; -} - - -static int bn2base64url(const BIGNUM *bn, string_t *dest) -{ - int len = BN_num_bytes(bn); - unsigned char *data = t_malloc_no0(len); - if (BN_bn2bin(bn, data) != len) - return -1; - base64url_encode(BASE64_ENCODE_FLAG_NO_PADDING, SIZE_MAX, data, len, dest); - return 0; -} - -/* FIXME: Add encryption support */ -/* FIXME: Add support for 'algo' field */ -static bool store_jwk_ec_key(EVP_PKEY *pkey, bool is_private_key, - enum dcrypt_key_usage usage, - const char *key_id, - const char *cipher ATTR_UNUSED, - const char *password ATTR_UNUSED, - struct dcrypt_public_key *enc_key ATTR_UNUSED, - string_t *dest, const char **error_r) -{ - i_assert(cipher == NULL && password == NULL && enc_key == NULL); - string_t *temp = t_str_new(256); - const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); - i_assert(ec_key != NULL); - - int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)); - const EC_POINT *public_point = EC_KEY_get0_public_key(ec_key); - BIGNUM *x, *y; - - x = BN_new(); - y = BN_new(); - if (EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key), public_point, - x, y, NULL) != 1) { - BN_free(x); - BN_free(y); - return dcrypt_openssl_error(error_r); - } - - const char *curve = nid_to_jwk_curve(nid); - const char *use = key_usage_to_jwk_use(usage); - - str_printfa(temp, "{\"kty\":\"EC\",\"crv\":\"%s\"", curve); - str_append(temp, ",\"x\":\""); - bn2base64url(x, temp); - str_append(temp, "\",\"y\":\""); - bn2base64url(y, temp); - - if (use != NULL) { - str_append(temp, "\",\"use\":\""); - json_append_escaped(temp, use); - } - if (key_id != NULL) { - str_append(temp, "\",\"kid\":\""); - json_append_escaped(temp, key_id); - } - BN_free(x); - BN_free(y); - - if (is_private_key) { - const BIGNUM *d = EC_KEY_get0_private_key(ec_key); - if (d == NULL) { - DCRYPT_SET_ERROR("No private key available"); - return FALSE; - } - str_append(temp, "\",\"d\":\""); - bn2base64url(d, temp); - } - str_append(temp, "\"}"); - str_append_str(dest, temp); - return TRUE; -} - -/* FIXME: Add RSA support */ - -static bool store_jwk_key(EVP_PKEY *pkey, bool is_private_key, - enum dcrypt_key_usage usage, - const char *key_id, - const char *cipher, - const char *password, - struct dcrypt_public_key *enc_key, - string_t *dest, const char **error_r) -{ - i_assert(cipher == NULL && password == NULL && enc_key == NULL); - if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { - return store_jwk_ec_key(pkey, is_private_key, usage, key_id, - cipher, password, enc_key, dest, error_r); - } - DCRYPT_SET_ERROR("Unsupported key type"); - return FALSE; -} - -static bool -dcrypt_openssl_load_private_key_dovecot(struct dcrypt_private_key **key_r, - const char *data, const char *password, - struct dcrypt_private_key *key, - enum dcrypt_key_version version, - const char **error_r) -{ - const char **input = t_strsplit(data, ":\t"); - size_t len = str_array_length(input); - - switch (version) { - case DCRYPT_KEY_VERSION_1: - return dcrypt_openssl_load_private_key_dovecot_v1( - key_r, len, input, password, key, error_r); - case DCRYPT_KEY_VERSION_2: - return dcrypt_openssl_load_private_key_dovecot_v2( - key_r, len, input, password, key, error_r); - case DCRYPT_KEY_VERSION_NA: - i_unreached(); - } - return FALSE; -} - -static bool -dcrypt_openssl_load_public_key_dovecot_v1(struct dcrypt_public_key **key_r, - int len, const char **input, - const char **error_r) -{ - int nid; - if (str_to_int(input[1], &nid) != 0) { - DCRYPT_SET_ERROR("Corrupted data"); - return FALSE; - } - - EC_KEY *eckey = EC_KEY_new_by_curve_name(nid); - if (eckey == NULL) { - dcrypt_openssl_error(error_r); - return FALSE; - } - - EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); - BN_CTX *bnctx = BN_CTX_new(); - - EC_POINT *point = EC_POINT_new(EC_KEY_get0_group(eckey)); - if (bnctx == NULL || point == NULL || - EC_POINT_hex2point(EC_KEY_get0_group(eckey), - input[2], point, bnctx) == NULL) { - BN_CTX_free(bnctx); - EC_KEY_free(eckey); - EC_POINT_free(point); - dcrypt_openssl_error(error_r); - return FALSE; - } - BN_CTX_free(bnctx); - - EC_KEY_set_public_key(eckey, point); - EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); - - EC_POINT_free(point); - - if (EC_KEY_check_key(eckey) == 1) { - EVP_PKEY *key = EVP_PKEY_new(); - EVP_PKEY_set1_EC_KEY(key, eckey); - EC_KEY_free(eckey); - /* make sure digest matches */ - buffer_t *dgst = t_buffer_create(32); - struct dcrypt_public_key tmp; - i_zero(&tmp); - tmp.key = key; - dcrypt_openssl_public_key_id_old(&tmp, dgst, NULL); - if (strcmp(binary_to_hex(dgst->data, dgst->used), - input[len-1]) != 0) { - DCRYPT_SET_ERROR("Key id mismatch after load"); - EVP_PKEY_free(key); - return FALSE; - } - *key_r = i_new(struct dcrypt_public_key, 1); - (*key_r)->key = key; - (*key_r)->ref++; - return TRUE; - } - - dcrypt_openssl_error(error_r); - return FALSE; -} - -static bool -dcrypt_openssl_load_public_key_dovecot_v2(struct dcrypt_public_key **key_r, - int len, const char **input, - const char **error_r) -{ - buffer_t tmp; - size_t keylen = strlen(input[1])/2; - unsigned char keybuf[keylen]; - const unsigned char *ptr; - buffer_create_from_data(&tmp, keybuf, keylen); - hex_to_binary(input[1], &tmp); - ptr = keybuf; - - EVP_PKEY *pkey = EVP_PKEY_new(); - if (pkey == NULL || d2i_PUBKEY(&pkey, &ptr, keylen)==NULL) { - EVP_PKEY_free(pkey); - dcrypt_openssl_error(error_r); - return FALSE; - } - - /* make sure digest matches */ - buffer_t *dgst = t_buffer_create(32); - struct dcrypt_public_key tmpkey; - i_zero(&tmpkey); - tmpkey.key = pkey; - dcrypt_openssl_public_key_id(&tmpkey, "sha256", dgst, NULL); - if (strcmp(binary_to_hex(dgst->data, dgst->used), input[len-1]) != 0) { - DCRYPT_SET_ERROR("Key id mismatch after load"); - EVP_PKEY_free(pkey); - return FALSE; - } - - *key_r = i_new(struct dcrypt_public_key, 1); - (*key_r)->key = pkey; - (*key_r)->ref++; - return TRUE; -} - -static bool -dcrypt_openssl_load_public_key_dovecot(struct dcrypt_public_key **key_r, - const char *data, - enum dcrypt_key_version version, - const char **error_r) -{ - const char **input = t_strsplit(data, ":\t"); - size_t len = str_array_length(input); - - switch (version) { - case DCRYPT_KEY_VERSION_1: - return dcrypt_openssl_load_public_key_dovecot_v1( - key_r, len, input, error_r); - break; - case DCRYPT_KEY_VERSION_2: - return dcrypt_openssl_load_public_key_dovecot_v2( - key_r, len, input, error_r); - break; - case DCRYPT_KEY_VERSION_NA: - i_unreached(); - } - return FALSE; -} - -static bool -dcrypt_openssl_encrypt_private_key_dovecot(buffer_t *key, int enctype, - const char *cipher, - const char *password, - struct dcrypt_public_key *enc_key, - buffer_t *destination, - const char **error_r) -{ - bool res; - unsigned char *ptr; - - unsigned char salt[8]; - buffer_t *peer_key = t_buffer_create(128); - buffer_t *secret = t_buffer_create(128); - cipher = t_str_lcase(cipher); - - str_append(destination, cipher); - str_append_c(destination, ':'); - random_fill(salt, sizeof(salt)); - binary_to_hex_append(destination, salt, sizeof(salt)); - buffer_t saltbuf; - buffer_create_from_const_data(&saltbuf, salt, sizeof(salt)); - - /* so we don't have to make new version if we ever upgrade these */ - str_append(destination, t_strdup_printf(":%s:%d:", - DCRYPT_DOVECOT_KEY_ENCRYPT_HASH, - DCRYPT_DOVECOT_KEY_ENCRYPT_ROUNDS)); - - if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { - if (EVP_PKEY_base_id(enc_key->key) == EVP_PKEY_RSA) { - size_t used = buffer_get_used_size(secret); - /* peer key, in this case, is encrypted secret, - which is 16 bytes of data */ - ptr = buffer_append_space_unsafe(secret, 16); - random_fill(ptr, 16); - buffer_set_used_size(secret, used+16); - if (!dcrypt_rsa_encrypt(enc_key, secret->data, - secret->used, peer_key, - DCRYPT_PADDING_RSA_PKCS1_OAEP, - error_r)) { - return FALSE; - } - } else if (EVP_PKEY_base_id(enc_key->key) == EVP_PKEY_EC) { - /* generate secret by ECDHE */ - if (!dcrypt_openssl_ecdh_derive_secret_peer( - enc_key, peer_key, secret, error_r)) { - return FALSE; - } - } else { - /* Loading the key should have failed */ - i_unreached(); - } - /* add encryption key id, reuse peer_key buffer */ - } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD) { - str_append(secret, password); - } - - /* encrypt key using secret and salt */ - buffer_t *tmp = t_buffer_create(128); - res = dcrypt_openssl_cipher_key_dovecot_v2(cipher, - DCRYPT_MODE_ENCRYPT, key, secret, &saltbuf, - DCRYPT_DOVECOT_KEY_ENCRYPT_HASH, - DCRYPT_DOVECOT_KEY_ENCRYPT_ROUNDS, tmp, error_r); - safe_memset(buffer_get_modifiable_data(secret, NULL), 0, secret->used); - binary_to_hex_append(destination, tmp->data, tmp->used); - - /* some additional fields or private key version */ - if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { - str_append_c(destination, ':'); - - /* for RSA, this is the actual encrypted secret */ - binary_to_hex_append(destination, - peer_key->data, peer_key->used); - str_append_c(destination, ':'); - - buffer_set_used_size(peer_key, 0); - if (!dcrypt_openssl_public_key_id(enc_key, "sha256", - peer_key, error_r)) - return FALSE; - binary_to_hex_append(destination, - peer_key->data, peer_key->used); - } - return res; -} - -static bool -dcrypt_openssl_store_private_key_dovecot(struct dcrypt_private_key *key, - const char *cipher, - buffer_t *destination, - const char *password, - struct dcrypt_public_key *enc_key, - const char **error_r) -{ - size_t dest_used = buffer_get_used_size(destination); - const char *cipher2 = NULL; - EVP_PKEY *pkey = key->key; - char objtxt[OID_TEXT_MAX_LEN]; - ASN1_OBJECT *obj; - - if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { - /* because otherwise we get wrong nid */ - obj = OBJ_nid2obj(EC_GROUP_get_curve_name( - EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey)))); - EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(pkey), - POINT_CONVERSION_COMPRESSED); - - } else { - obj = OBJ_nid2obj(EVP_PKEY_id(pkey)); - } - - int enctype = DCRYPT_KEY_ENCRYPTION_TYPE_NONE; - int len = OBJ_obj2txt(objtxt, sizeof(objtxt), obj, 1); - if (len < 1) - return dcrypt_openssl_error(error_r); - if (len > (int)sizeof(objtxt)) { - DCRYPT_SET_ERROR("Object identifier too long"); - return FALSE; - } - - buffer_t *buf = t_buffer_create(256); - - /* convert key to private key value */ - if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) { - unsigned char *ptr; - RSA *rsa = EVP_PKEY_get0_RSA(pkey); - int len = i2d_RSAPrivateKey(rsa, &ptr); - if (len < 1) - return dcrypt_openssl_error(error_r); - buffer_append(buf, ptr, len); - } else if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { - unsigned char *ptr; - EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey); - const BIGNUM *pk = EC_KEY_get0_private_key(eckey); - /* serialize to MPI which is portable */ - int len = BN_bn2mpi(pk, NULL); - ptr = buffer_append_space_unsafe(buf, len); - BN_bn2mpi(pk, ptr); - } else { - /* Loading the key should have failed */ - i_unreached(); - } - - /* see if we want ECDH based or password based encryption */ - if (cipher != NULL && strncasecmp(cipher, "ecdh-", 5) == 0) { - i_assert(enc_key != NULL); - i_assert(password == NULL); - enctype = DCRYPT_DOVECOT_KEY_ENCRYPT_PK; - cipher2 = cipher+5; - } else if (cipher != NULL) { - i_assert(enc_key == NULL); - i_assert(password != NULL); - enctype = DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD; - cipher2 = cipher; - } else if (enctype == DCRYPT_KEY_ENCRYPTION_TYPE_NONE) { - i_assert(enc_key == NULL && password == NULL); - } - - /* put in OID and encryption type */ - str_append(destination, t_strdup_printf("2:%s:%d:", - objtxt, enctype)); - - /* perform encryption if desired */ - if (enctype != DCRYPT_KEY_ENCRYPTION_TYPE_NONE) { - if (!dcrypt_openssl_encrypt_private_key_dovecot(buf, - enctype, cipher2, password, enc_key, destination, - error_r)) { - buffer_set_used_size(destination, dest_used); - return FALSE; - } - } else { - binary_to_hex_append(destination, buf->data, buf->used); - } - - /* append public key id */ - str_append_c(destination, ':'); - buffer_set_used_size(buf, 0); - bool res = dcrypt_openssl_private_key_id(key, "sha256", buf, error_r); - binary_to_hex_append(destination, buf->data, buf->used); - - if (!res) { - /* well, that didn't end well */ - buffer_set_used_size(destination, dest_used); - return FALSE; - } - return TRUE; -} - -static bool -dcrypt_openssl_store_public_key_dovecot(struct dcrypt_public_key *key, - buffer_t *destination, - const char **error_r) -{ - EVP_PKEY *pubkey = key->key; - unsigned char *tmp = NULL; - size_t dest_used = buffer_get_used_size(destination); - - if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) - EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(pubkey), - POINT_CONVERSION_COMPRESSED); - int rv = i2d_PUBKEY(pubkey, &tmp); - - if (tmp == NULL) - return dcrypt_openssl_error(error_r); - - /* then store it */ - str_append_c(destination, '2'); - str_append_c(destination, ':'); - binary_to_hex_append(destination, tmp, rv); - OPENSSL_free(tmp); - - /* append public key ID */ - str_append_c(destination, ':'); - - buffer_t *buf = t_buffer_create(32); - bool res = dcrypt_openssl_public_key_id(key, "sha256", buf, error_r); - - if (!res) { - buffer_set_used_size(destination, dest_used); - return FALSE; - } - - str_append(destination, binary_to_hex(buf->data, buf->used)); - return TRUE; -} - -static bool -dcrypt_openssl_load_private_key(struct dcrypt_private_key **key_r, - const char *data, const char *password, - struct dcrypt_private_key *dec_key, - const char **error_r) -{ - i_assert(key_r != NULL); - - enum dcrypt_key_format format; - enum dcrypt_key_version version; - enum dcrypt_key_kind kind; - if (!dcrypt_openssl_key_string_get_info(data, &format, &version, - &kind, NULL, NULL, NULL, error_r)) { - return FALSE; - } - if (kind != DCRYPT_KEY_KIND_PRIVATE) { - DCRYPT_SET_ERROR("key is not private"); - return FALSE; - } - - if (format == DCRYPT_FORMAT_JWK) - return dcrypt_openssl_load_private_key_jwk(key_r, data, password, - dec_key, error_r); - - if (format == DCRYPT_FORMAT_DOVECOT) - return dcrypt_openssl_load_private_key_dovecot(key_r, data, - password, dec_key, version, error_r); - - EVP_PKEY *key = NULL, *key2; - - BIO *key_in = BIO_new_mem_buf((void*)data, strlen(data)); - - key = EVP_PKEY_new(); - - key2 = PEM_read_bio_PrivateKey(key_in, &key, NULL, (void*)password); - - BIO_vfree(key_in); - - if (key2 == NULL) { - EVP_PKEY_free(key); - return dcrypt_openssl_error(error_r); - } - - if (EVP_PKEY_base_id(key) == EVP_PKEY_EC) { - EC_KEY_set_asn1_flag(EVP_PKEY_get0_EC_KEY(key), - OPENSSL_EC_NAMED_CURVE); - } - - *key_r = i_new(struct dcrypt_private_key, 1); - (*key_r)->key = key; - (*key_r)->ref++; - - return TRUE; -} - -static bool -dcrypt_openssl_load_public_key(struct dcrypt_public_key **key_r, - const char *data, const char **error_r) -{ - enum dcrypt_key_format format; - enum dcrypt_key_version version; - enum dcrypt_key_kind kind; - i_assert(key_r != NULL); - - if (!dcrypt_openssl_key_string_get_info(data, &format, &version, - &kind, NULL, NULL, NULL, - error_r)) { - return FALSE; - } - /* JWK private keys can be loaded as public */ - if (kind != DCRYPT_KEY_KIND_PUBLIC && format != DCRYPT_FORMAT_JWK) { - DCRYPT_SET_ERROR("key is not public"); - return FALSE; - } - - if (format == DCRYPT_FORMAT_JWK) - return dcrypt_openssl_load_public_key_jwk(key_r, data, error_r); - - if (format == DCRYPT_FORMAT_DOVECOT) - return dcrypt_openssl_load_public_key_dovecot(key_r, data, - version, error_r); - - EVP_PKEY *key = NULL; - BIO *key_in = BIO_new_mem_buf((void*)data, strlen(data)); - if (key_in == NULL) - return dcrypt_openssl_error(error_r); - - key = PEM_read_bio_PUBKEY(key_in, &key, NULL, NULL); - if (BIO_reset(key_in) <= 0) - i_unreached(); - if (key == NULL) { /* ec keys are bother */ - /* read the header */ - char buf[27]; /* begin public key */ - if (BIO_gets(key_in, buf, sizeof(buf)) != 1) { - BIO_vfree(key_in); - return dcrypt_openssl_error(error_r); - } - if (strcmp(buf, "-----BEGIN PUBLIC KEY-----") != 0) { - DCRYPT_SET_ERROR("Missing public key header"); - return FALSE; - } - BIO *b64 = BIO_new(BIO_f_base64()); - if (b64 == NULL) { - BIO_vfree(key_in); - return dcrypt_openssl_error(error_r); - } - EC_KEY *eckey = d2i_EC_PUBKEY_bio(b64, NULL); - if (eckey != NULL) { - EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); - key = EVP_PKEY_new(); - if (key != NULL) - EVP_PKEY_set1_EC_KEY(key, eckey); - EC_KEY_free(eckey); - } - } - - BIO_vfree(key_in); - - if (key == NULL) - return dcrypt_openssl_error(error_r); - - *key_r = i_new(struct dcrypt_public_key, 1); - (*key_r)->key = key; - (*key_r)->ref++; - - return TRUE; -} - -static bool -dcrypt_openssl_store_private_key(struct dcrypt_private_key *key, - enum dcrypt_key_format format, - const char *cipher, buffer_t *destination, - const char *password, - struct dcrypt_public_key *enc_key, - const char **error_r) -{ - i_assert(key != NULL && key->key != NULL); - - int ec; - if (format == DCRYPT_FORMAT_DOVECOT) { - bool ret; - ret = dcrypt_openssl_store_private_key_dovecot( - key, cipher, destination, password, enc_key, error_r); - return ret; - } - - EVP_PKEY *pkey = key->key; - - if (format == DCRYPT_FORMAT_JWK) { - bool ret; - ret = store_jwk_key(pkey, TRUE, key->usage, key->key_id, - cipher, password, enc_key, - destination, error_r); - return ret; - } - - if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) - EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(pkey), - POINT_CONVERSION_UNCOMPRESSED); - - BIO *key_out = BIO_new(BIO_s_mem()); - if (key_out == NULL) - return dcrypt_openssl_error(error_r); - - const EVP_CIPHER *algo = NULL; - if (cipher != NULL) { - algo = EVP_get_cipherbyname(cipher); - if (algo == NULL) { - DCRYPT_SET_ERROR(t_strdup_printf("Invalid cipher %s", - cipher)); - return FALSE; - } - } - - ec = PEM_write_bio_PrivateKey(key_out, pkey, algo, - NULL, 0, NULL, (void*)password); - - if (BIO_flush(key_out) <= 0) - ec = -1; - - if (ec != 1) { - BIO_vfree(key_out); - return dcrypt_openssl_error(error_r); - } - - long bs; - char *buf; - bs = BIO_get_mem_data(key_out, &buf); - buffer_append(destination, buf, bs); - BIO_vfree(key_out); - - return TRUE; -} - -static bool -dcrypt_openssl_store_public_key(struct dcrypt_public_key *key, - enum dcrypt_key_format format, - buffer_t *destination, const char **error_r) -{ - int ec; - - i_assert(key != NULL && key->key != NULL); - - if (format == DCRYPT_FORMAT_DOVECOT) { - return dcrypt_openssl_store_public_key_dovecot(key, destination, - error_r); - } - - EVP_PKEY *pkey = key->key; - - if (format == DCRYPT_FORMAT_JWK) { - bool ret; - ret = store_jwk_key(pkey, FALSE, key->usage, key->key_id, - NULL, NULL, NULL, - destination, error_r); - return ret; - } - - if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) - EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(pkey), - POINT_CONVERSION_UNCOMPRESSED); - - BIO *key_out = BIO_new(BIO_s_mem()); - if (key_out == NULL) - return dcrypt_openssl_error(error_r); - - BIO *b64; - if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) - ec = PEM_write_bio_PUBKEY(key_out, pkey); - else if ((b64 = BIO_new(BIO_f_base64())) == NULL) - ec = -1; - else { - (void)BIO_puts(key_out, "-----BEGIN PUBLIC KEY-----\n"); - (void)BIO_push(b64, key_out); - ec = i2d_EC_PUBKEY_bio(b64, EVP_PKEY_get0_EC_KEY(pkey)); - if (BIO_flush(b64) <= 0) - ec = -1; - (void)BIO_pop(b64); - BIO_vfree(b64); - if (BIO_puts(key_out, "-----END PUBLIC KEY-----") <= 0) - ec = -1; - } - - if (ec != 1) { - BIO_vfree(key_out); - return dcrypt_openssl_error(error_r); - } - - long bs; - char *buf; - bs = BIO_get_mem_data(key_out, &buf); - buffer_append(destination, buf, bs); - BIO_vfree(key_out); - - return TRUE; -} - -static void -dcrypt_openssl_private_to_public_key(struct dcrypt_private_key *priv_key, - struct dcrypt_public_key **pub_key_r) -{ - i_assert(priv_key != NULL && pub_key_r != NULL); - - EVP_PKEY *pkey = priv_key->key; - EVP_PKEY *pk; - - pk = EVP_PKEY_new(); - i_assert(pk != NULL); /* we shouldn't get malloc() failures */ - - if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) - { - RSA *rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(pkey)); - EVP_PKEY_set1_RSA(pk, rsa); - RSA_free(rsa); - } else if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { - EC_KEY* eck = EVP_PKEY_get1_EC_KEY(pkey); - EC_KEY_set_asn1_flag(eck, OPENSSL_EC_NAMED_CURVE); - EVP_PKEY_set1_EC_KEY(pk, eck); - EC_KEY_free(eck); - } else { - /* Loading the key should have failed */ - i_unreached(); - } - - *pub_key_r = i_new(struct dcrypt_public_key, 1); - (*pub_key_r)->key = pk; - (*pub_key_r)->ref++; -} - -static bool -dcrypt_openssl_key_string_get_info( - const char *key_data, enum dcrypt_key_format *format_r, - enum dcrypt_key_version *version_r, enum dcrypt_key_kind *kind_r, - enum dcrypt_key_encryption_type *encryption_type_r, - const char **encryption_key_hash_r, const char **key_hash_r, - const char **error_r) -{ - enum dcrypt_key_format format = DCRYPT_FORMAT_PEM; - enum dcrypt_key_version version = DCRYPT_KEY_VERSION_NA; - enum dcrypt_key_encryption_type encryption_type = - DCRYPT_KEY_ENCRYPTION_TYPE_NONE; - enum dcrypt_key_kind kind = DCRYPT_KEY_KIND_PUBLIC; - char *encryption_key_hash = NULL; - char *key_hash = NULL; - - i_assert(key_data != NULL); - - /* is it PEM key */ - if (str_begins(key_data, "-----BEGIN ")) { - format = DCRYPT_FORMAT_PEM; - version = DCRYPT_KEY_VERSION_NA; - key_data += 11; - if (str_begins(key_data, "RSA ")) { - DCRYPT_SET_ERROR("RSA private key format not supported, convert it to PKEY format with openssl pkey"); - return FALSE; - } - if (str_begins(key_data, "ENCRYPTED ")) { - encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD; - key_data += 10; - } - if (str_begins(key_data, "PRIVATE KEY-----")) - kind = DCRYPT_KEY_KIND_PRIVATE; - else if (str_begins(key_data, "PUBLIC KEY-----")) - kind = DCRYPT_KEY_KIND_PUBLIC; - else { - DCRYPT_SET_ERROR("Unknown/invalid PEM key type"); - return FALSE; - } - } else if (*key_data == '{') { - /* possibly a JWK key */ - format = DCRYPT_FORMAT_JWK; - version = DCRYPT_KEY_VERSION_NA; - struct json_tree *tree; - const struct json_tree_node *root, *node; - const char *value, *error; - if (parse_jwk_key(key_data, &tree, &error) != 0) { - DCRYPT_SET_ERROR("Unknown/invalid key data"); - return FALSE; - } - - /* determine key type */ - root = json_tree_root(tree); - if ((node = json_tree_find_key(root, "kty")) == NULL || - (value = json_tree_get_value_str(node)) == NULL) { - json_tree_deinit(&tree); - DCRYPT_SET_ERROR("Invalid JWK key: Missing kty parameter"); - return FALSE; - } else if (strcmp(value, "RSA") == 0) { - if (json_tree_find_key(root, "d") != NULL) - kind = DCRYPT_KEY_KIND_PRIVATE; - else - kind = DCRYPT_KEY_KIND_PUBLIC; - } else if (strcmp(value, "EC") == 0) { - if (json_tree_find_key(root, "d") != NULL) - kind = DCRYPT_KEY_KIND_PRIVATE; - else - kind = DCRYPT_KEY_KIND_PUBLIC; - } else { - json_tree_deinit(&tree); - DCRYPT_SET_ERROR("Unsupported JWK key type"); - return FALSE; - } - json_tree_deinit(&tree); - } else { - if (str_begins(key_data, "1:")) { - DCRYPT_SET_ERROR("Dovecot v1 key format uses tab to separate fields"); - return FALSE; - } else if (str_begins(key_data, "2\t")) { - DCRYPT_SET_ERROR("Dovecot v2 key format uses colon to separate fields"); - return FALSE; - } - const char **fields = t_strsplit(key_data, ":\t"); - int nfields = str_array_length(fields); - - if (nfields < 2) { - DCRYPT_SET_ERROR("Unknown key format"); - return FALSE; - } - - format = DCRYPT_FORMAT_DOVECOT; - - /* field 1 - version */ - if (strcmp(fields[0], "1") == 0) { - version = DCRYPT_KEY_VERSION_1; - if (nfields == 4) { - kind = DCRYPT_KEY_KIND_PUBLIC; - } else if (nfields == 5 && strcmp(fields[2],"0") == 0) { - kind = DCRYPT_KEY_KIND_PRIVATE; - encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_NONE; - } else if (nfields == 6 && strcmp(fields[2],"2") == 0) { - kind = DCRYPT_KEY_KIND_PRIVATE; - encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD; - } else if (nfields == 7 && strcmp(fields[2],"1") == 0) { - kind = DCRYPT_KEY_KIND_PRIVATE; - encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_KEY; - if (encryption_key_hash_r != NULL) - encryption_key_hash = i_strdup(fields[nfields-2]); - } else { - DCRYPT_SET_ERROR("Invalid dovecot v1 encoding"); - return FALSE; - } - } else if (strcmp(fields[0], "2") == 0) { - version = DCRYPT_KEY_VERSION_2; - if (nfields == 3) { - kind = DCRYPT_KEY_KIND_PUBLIC; - } else if (nfields == 5 && strcmp(fields[2],"0") == 0) { - kind = DCRYPT_KEY_KIND_PRIVATE; - encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_NONE; - } else if (nfields == 9 && strcmp(fields[2],"2") == 0) { - kind = DCRYPT_KEY_KIND_PRIVATE; - encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD; - } else if (nfields == 11 && strcmp(fields[2],"1") == 0) { - kind = DCRYPT_KEY_KIND_PRIVATE; - encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_KEY; - if (encryption_key_hash_r != NULL) - encryption_key_hash = i_strdup(fields[nfields-2]); - } else { - DCRYPT_SET_ERROR("Invalid dovecot v2 encoding"); - return FALSE; - } - } else { - DCRYPT_SET_ERROR("Invalid dovecot key version"); - return FALSE; - } - - /* last field is always key hash */ - if (key_hash_r != NULL) - key_hash = i_strdup(fields[nfields-1]); - } - - if (format_r != NULL) *format_r = format; - if (version_r != NULL) *version_r = version; - if (encryption_type_r != NULL) *encryption_type_r = encryption_type; - if (encryption_key_hash_r != NULL) { - *encryption_key_hash_r = t_strdup(encryption_key_hash); - i_free(encryption_key_hash); - } - if (kind_r != NULL) *kind_r = kind; - if (key_hash_r != NULL) { - *key_hash_r = t_strdup(key_hash); - i_free(key_hash); - } - return TRUE; -} - -static void dcrypt_openssl_ref_public_key(struct dcrypt_public_key *key) -{ - i_assert(key != NULL && key->ref > 0); - key->ref++; -} - -static void dcrypt_openssl_ref_private_key(struct dcrypt_private_key *key) -{ - i_assert(key != NULL && key->ref > 0); - key->ref++; -} - -static void dcrypt_openssl_unref_public_key(struct dcrypt_public_key **key) -{ - i_assert(key != NULL); - struct dcrypt_public_key *_key = *key; - if (_key == NULL) - return; - i_assert(_key->ref > 0); - *key = NULL; - if (--_key->ref > 0) return; - EVP_PKEY_free(_key->key); - i_free(_key->key_id); - i_free(_key); -} - -static void dcrypt_openssl_unref_private_key(struct dcrypt_private_key **key) -{ - i_assert(key != NULL); - struct dcrypt_private_key *_key = *key; - if (_key == NULL) - return; - i_assert(_key->ref > 0); - *key = NULL; - if (--_key->ref > 0) return; - EVP_PKEY_free(_key->key); - i_free(_key->key_id); - i_free(_key); -} - -static void dcrypt_openssl_unref_keypair(struct dcrypt_keypair *keypair) -{ - i_assert(keypair != NULL); - dcrypt_openssl_unref_public_key(&keypair->pub); - dcrypt_openssl_unref_private_key(&keypair->priv); -} - -static bool -dcrypt_openssl_rsa_encrypt(struct dcrypt_public_key *key, - const unsigned char *data, size_t data_len, - buffer_t *result, enum dcrypt_padding padding, - const char **error_r) -{ - i_assert(key != NULL && key->key != NULL); - int ec, pad = dcrypt_openssl_padding_mode(padding, FALSE, error_r); - if (pad == -1) - return FALSE; - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->key, NULL); - size_t outl = EVP_PKEY_size(key->key); - unsigned char buf[outl]; - - if (ctx == NULL || - EVP_PKEY_encrypt_init(ctx) < 1 || - EVP_PKEY_CTX_set_rsa_padding(ctx, pad) < 1 || - EVP_PKEY_encrypt(ctx, buf, &outl, data, data_len) < 1) { - dcrypt_openssl_error(error_r); - ec = -1; - } else { - buffer_append(result, buf, outl); - ec = 0; - } - - EVP_PKEY_CTX_free(ctx); - - return ec == 0; -} - -static bool -dcrypt_openssl_rsa_decrypt(struct dcrypt_private_key *key, - const unsigned char *data, size_t data_len, - buffer_t *result, enum dcrypt_padding padding, - const char **error_r) -{ - i_assert(key != NULL && key->key != NULL); - int ec, pad = dcrypt_openssl_padding_mode(padding, FALSE, error_r); - if (pad == -1) - return FALSE; - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->key, NULL); - size_t outl = EVP_PKEY_size(key->key); - unsigned char buf[outl]; - - if (ctx == NULL || - EVP_PKEY_decrypt_init(ctx) < 1 || - EVP_PKEY_CTX_set_rsa_padding(ctx, pad) < 1 || - EVP_PKEY_decrypt(ctx, buf, &outl, data, data_len) < 1) { - dcrypt_openssl_error(error_r); - ec = -1; - } else { - buffer_append(result, buf, outl); - ec = 0; - } - - EVP_PKEY_CTX_free(ctx); - - return ec == 0; -} - -static const char * -dcrypt_openssl_oid2name(const unsigned char *oid, size_t oid_len, - const char **error_r) -{ - const char *name; - i_assert(oid != NULL); - ASN1_OBJECT *obj = d2i_ASN1_OBJECT(NULL, &oid, oid_len); - if (obj == NULL) { - dcrypt_openssl_error(error_r); - return NULL; - } - name = OBJ_nid2sn(OBJ_obj2nid(obj)); - ASN1_OBJECT_free(obj); - return name; -} - -static bool -dcrypt_openssl_name2oid(const char *name, buffer_t *oid, const char **error_r) -{ - i_assert(name != NULL); - ASN1_OBJECT *obj = OBJ_txt2obj(name, 0); - if (obj == NULL) - return dcrypt_openssl_error(error_r); - - size_t len = OBJ_length(obj); - if (len == 0) - { - DCRYPT_SET_ERROR("Object has no OID assigned"); - return FALSE; - } - len = i2d_ASN1_OBJECT(obj, NULL); - unsigned char *bufptr = buffer_append_space_unsafe(oid, len); - i2d_ASN1_OBJECT(obj, &bufptr); - ASN1_OBJECT_free(obj); - if (bufptr != NULL) { - return TRUE; - } - return dcrypt_openssl_error(error_r); -} - -static enum dcrypt_key_type -dcrypt_openssl_private_key_type(struct dcrypt_private_key *key) -{ - i_assert(key != NULL && key->key != NULL); - EVP_PKEY *priv = key->key; - if (EVP_PKEY_base_id(priv) == EVP_PKEY_RSA) return DCRYPT_KEY_RSA; - else if (EVP_PKEY_base_id(priv) == EVP_PKEY_EC) return DCRYPT_KEY_EC; - else i_unreached(); -} - -static enum dcrypt_key_type -dcrypt_openssl_public_key_type(struct dcrypt_public_key *key) -{ - i_assert(key != NULL && key->key != NULL); - EVP_PKEY *pub = key->key; - if (EVP_PKEY_base_id(pub) == EVP_PKEY_RSA) return DCRYPT_KEY_RSA; - else if (EVP_PKEY_base_id(pub) == EVP_PKEY_EC) return DCRYPT_KEY_EC; - else i_unreached(); -} - -/** this is the v1 old legacy way of doing key id's **/ -static bool -dcrypt_openssl_public_key_id_old(struct dcrypt_public_key *key, - buffer_t *result, const char **error_r) -{ - unsigned char buf[SHA256_DIGEST_LENGTH]; - i_assert(key != NULL && key->key != NULL); - EVP_PKEY *pub = key->key; - - if (EVP_PKEY_base_id(pub) != EVP_PKEY_EC) { - DCRYPT_SET_ERROR("Only EC key supported"); - return FALSE; - } - - char *pub_pt_hex = ec_key_get_pub_point_hex(EVP_PKEY_get0_EC_KEY(pub)); - if (pub_pt_hex == NULL) - return dcrypt_openssl_error(error_r); - /* digest this */ - SHA256((const unsigned char*)pub_pt_hex, strlen(pub_pt_hex), buf); - buffer_append(result, buf, SHA256_DIGEST_LENGTH); - OPENSSL_free(pub_pt_hex); - return TRUE; -} - -static bool -dcrypt_openssl_private_key_id_old(struct dcrypt_private_key *key, - buffer_t *result, const char **error_r) -{ - unsigned char buf[SHA256_DIGEST_LENGTH]; - i_assert(key != NULL && key->key != NULL); - EVP_PKEY *priv = key->key; - - if (EVP_PKEY_base_id(priv) != EVP_PKEY_EC) { - DCRYPT_SET_ERROR("Only EC key supported"); - return FALSE; - } - - char *pub_pt_hex = ec_key_get_pub_point_hex(EVP_PKEY_get0_EC_KEY(priv)); - if (pub_pt_hex == NULL) - return dcrypt_openssl_error(error_r); - /* digest this */ - SHA256((const unsigned char*)pub_pt_hex, strlen(pub_pt_hex), buf); - buffer_append(result, buf, SHA256_DIGEST_LENGTH); - OPENSSL_free(pub_pt_hex); - return TRUE; -} - -/** this is the new which uses H(der formatted public key) **/ -static bool -dcrypt_openssl_public_key_id_evp(EVP_PKEY *key, - const EVP_MD *md, buffer_t *result, - const char **error_r) -{ - bool res = FALSE; - unsigned char buf[EVP_MD_size(md)], *ptr; - - if (EVP_PKEY_base_id(key) == EVP_PKEY_EC) { - EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(key), - POINT_CONVERSION_COMPRESSED); - } - BIO *b = BIO_new(BIO_s_mem()); - if (b == NULL || i2d_PUBKEY_bio(b, key) < 1) { - BIO_vfree(b); - return dcrypt_openssl_error(error_r); - } - long len = BIO_get_mem_data(b, &ptr); - unsigned int hlen = sizeof(buf); - /* then hash it */ - EVP_MD_CTX *ctx = EVP_MD_CTX_create(); - if (ctx == NULL || - EVP_DigestInit_ex(ctx, md, NULL) < 1 || - EVP_DigestUpdate(ctx, (const unsigned char*)ptr, len) < 1 || - EVP_DigestFinal_ex(ctx, buf, &hlen) < 1) { - res = dcrypt_openssl_error(error_r); - } else { - buffer_append(result, buf, hlen); - res = TRUE; - } - EVP_MD_CTX_destroy(ctx); - BIO_vfree(b); - - return res; -} - -static bool -dcrypt_openssl_public_key_id(struct dcrypt_public_key *key, - const char *algorithm, buffer_t *result, - const char **error_r) -{ - const EVP_MD *md = EVP_get_digestbyname(algorithm); - i_assert(key != NULL && key->key != NULL); - EVP_PKEY *pub = key->key; - - if (md == NULL) { - DCRYPT_SET_ERROR(t_strdup_printf("Unknown cipher %s", algorithm)); - return FALSE; - } - - return dcrypt_openssl_public_key_id_evp(pub, md, result, error_r); -} - -static bool -dcrypt_openssl_private_key_id(struct dcrypt_private_key *key, - const char *algorithm, buffer_t *result, - const char **error_r) -{ - const EVP_MD *md = EVP_get_digestbyname(algorithm); - i_assert(key != NULL && key->key != NULL); - EVP_PKEY *priv = key->key; - - if (md == NULL) { - DCRYPT_SET_ERROR(t_strdup_printf("Unknown cipher %s", algorithm)); - return FALSE; - } - - return dcrypt_openssl_public_key_id_evp(priv, md, result, error_r); -} - -static bool -dcrypt_openssl_digest(const char *algorithm, const void *data, size_t data_len, - buffer_t *digest_r, const char **error_r) -{ - bool ret; - EVP_MD_CTX *mdctx; - const EVP_MD *md = EVP_get_digestbyname(algorithm); - if (md == NULL) - return dcrypt_openssl_error(error_r); - unsigned int md_size = EVP_MD_size(md); - if ((mdctx = EVP_MD_CTX_create()) == NULL) - return dcrypt_openssl_error(error_r); - unsigned char *buf = buffer_append_space_unsafe(digest_r, md_size); - if (EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) != 1 || - EVP_DigestUpdate(mdctx, data, data_len) != 1 || - EVP_DigestFinal_ex(mdctx, buf, &md_size) != 1) { - ret = dcrypt_openssl_error(error_r); - } else { - ret = TRUE; - } - EVP_MD_CTX_destroy(mdctx); - return ret; -} - -static bool -dcrypt_openssl_sign_ecdsa(struct dcrypt_private_key *key, const char *algorithm, - const void *data, size_t data_len, buffer_t *signature_r, - const char **error_r) -{ - EVP_PKEY *pkey = key->key; - EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); - bool ret; - - int rs_len; - { - EC_GROUP *grp = EC_KEY_get0_group(ec_key); - BIGNUM *bn = BN_new(); - if (!bn || !EC_GROUP_get_order(grp, bn, NULL)) - return dcrypt_openssl_error(error_r); - rs_len = BN_num_bits(bn) / 8; - BN_free(bn); - } - - /* digest data */ - buffer_t *digest = t_buffer_create(64); - if (!dcrypt_openssl_digest(algorithm, data, data_len, digest, error_r)) - return FALSE; - - /* sign data */ - ECDSA_SIG *ec_sig; - if ((ec_sig = ECDSA_do_sign(digest->data, digest->used, ec_key)) == NULL) - return dcrypt_openssl_error(error_r); - - /* export signature */ - const BIGNUM *r = sig->r; - const BIGNUM *s = sig->s; - - int r_len = BN_num_bytes(r); - i_assert(rs_len >= r_len); - - /* write r */ - unsigned char *buf = buffer_append_space_unsafe(signature_r, rs_len); - if (BN_bn2bin(r, buf + (rs_len - r_len)) != r_len) { - ret = dcrypt_openssl_error(error_r); - } else { - buf = buffer_append_space_unsafe(signature_r, rs_len); - int s_len = BN_num_bytes(s); - i_assert(rs_len >= s_len); - if (BN_bn2bin(s, buf + (rs_len - s_len)) != s_len) { - ret = dcrypt_openssl_error(error_r); - } else { - ret = TRUE; - } - } - - ECDSA_SIG_free(ec_sig); - - return ret; -} - -static bool -dcrypt_openssl_sign(struct dcrypt_private_key *key, const char *algorithm, - enum dcrypt_signature_format format, - const void *data, size_t data_len, buffer_t *signature_r, - enum dcrypt_padding padding, const char **error_r) -{ - switch (format) { - case DCRYPT_SIGNATURE_FORMAT_DSS: - break; - case DCRYPT_SIGNATURE_FORMAT_X962: - if (EVP_PKEY_base_id(key->key) == EVP_PKEY_RSA) { - DCRYPT_SET_ERROR("Format does not support RSA"); - return FALSE; - } - return dcrypt_openssl_sign_ecdsa(key, algorithm, - data, data_len, signature_r, error_r); - default: - i_unreached(); - } - - EVP_PKEY_CTX *pctx = NULL; - EVP_MD_CTX *dctx; - bool ret; - const EVP_MD *md = EVP_get_digestbyname(algorithm); - size_t siglen; - int pad = dcrypt_openssl_padding_mode(padding, TRUE, error_r); - - if (pad == -1) - return FALSE; - - if (md == NULL) { - DCRYPT_SET_ERROR(t_strdup_printf("Unknown digest %s", algorithm)); - return FALSE; - } - - dctx = EVP_MD_CTX_create(); - - /* NB! Padding is set only on RSA signatures - ECDSA signatures use whatever is default */ - if (EVP_DigestSignInit(dctx, &pctx, md, NULL, key->key) != 1 || - (EVP_PKEY_base_id(key->key) == EVP_PKEY_RSA && - EVP_PKEY_CTX_set_rsa_padding(pctx, pad) != 1) || - EVP_DigestSignUpdate(dctx, data, data_len) != 1 || - EVP_DigestSignFinal(dctx, NULL, &siglen) != 1) { - ret = dcrypt_openssl_error(error_r); - } else { - i_assert(siglen > 0); - /* @UNSAFE */ - unsigned char *buf = - buffer_append_space_unsafe(signature_r, siglen); - if (EVP_DigestSignFinal(dctx, buf, &siglen) != 1) { - ret = dcrypt_openssl_error(error_r); - } else { - buffer_set_used_size(signature_r, siglen); - ret = TRUE; - } - } - - EVP_MD_CTX_destroy(dctx); - - return ret; -} - -static bool -dcrypt_openssl_verify_ecdsa(struct dcrypt_public_key *key, const char *algorithm, - const void *data, size_t data_len, - const unsigned char *signature, size_t signature_len, - bool *valid_r, const char **error_r) -{ - if ((signature_len % 2) != 0) { - DCRYPT_SET_ERROR("Truncated signature"); - return FALSE; - } - - EVP_PKEY *pkey = key->key; - EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); - int ec; - - /* digest data */ - buffer_t *digest = t_buffer_create(64); - if (!dcrypt_openssl_digest(algorithm, data, data_len, digest, error_r)) - return FALSE; - - BIGNUM *r = BN_new(); - BIGNUM *s = BN_new(); - if (!r || !s) - return dcrypt_openssl_error(error_r); - /* attempt to decode BIGNUMs */ - if (BN_bin2bn(signature, signature_len / 2, r) == NULL) { - BN_free(r); - BN_free(s); - return dcrypt_openssl_error(error_r); - } - /* then next */ - if (BN_bin2bn(CONST_PTR_OFFSET(signature, signature_len / 2), - signature_len / 2, s) == NULL) { - BN_free(r); - BN_free(s); - return dcrypt_openssl_error(error_r); - } - - /* reconstruct signature */ - ECDSA_SIG *ec_sig = ECDSA_SIG_new(); - if (!ec_sig) - return dcrypt_openssl_error(error_r); - BN_free(ec_sig->r); - BN_free(ec_sig->s); - ec_sig->r = r; - ec_sig->s = s; - - /* verify it */ - ec = ECDSA_do_verify(digest->data, digest->used, ec_sig, ec_key); - ECDSA_SIG_free(ec_sig); - - if (ec == 1) { - *valid_r = TRUE; - } else if (ec == 0) { - *valid_r = FALSE; - } else { - return dcrypt_openssl_error(error_r); - } - return TRUE; -} - -static bool -dcrypt_openssl_verify(struct dcrypt_public_key *key, const char *algorithm, - enum dcrypt_signature_format format, - const void *data, size_t data_len, - const unsigned char *signature, size_t signature_len, - bool *valid_r, enum dcrypt_padding padding, - const char **error_r) -{ - switch (format) { - case DCRYPT_SIGNATURE_FORMAT_DSS: - break; - case DCRYPT_SIGNATURE_FORMAT_X962: - if (EVP_PKEY_base_id(key->key) == EVP_PKEY_RSA) { - DCRYPT_SET_ERROR("Format does not support RSA"); - return FALSE; - } - return dcrypt_openssl_verify_ecdsa(key, algorithm, - data, data_len, signature, signature_len, - valid_r, error_r); - default: - i_unreached(); - } - - EVP_PKEY_CTX *pctx = NULL; - EVP_MD_CTX *dctx; - bool ret; - const EVP_MD *md = EVP_get_digestbyname(algorithm); - int rc, pad = dcrypt_openssl_padding_mode(padding, TRUE, error_r); - - if (pad == -1) - return FALSE; - - if (md == NULL) { - DCRYPT_SET_ERROR(t_strdup_printf("Unknown digest %s", algorithm)); - return FALSE; - } - - dctx = EVP_MD_CTX_create(); - - /* NB! Padding is set only on RSA signatures - ECDSA signatures use whatever is default */ - if (EVP_DigestVerifyInit(dctx, &pctx, md, NULL, key->key) != 1 || - (EVP_PKEY_base_id(key->key) == EVP_PKEY_RSA && - EVP_PKEY_CTX_set_rsa_padding(pctx, pad) != 1) || - EVP_DigestVerifyUpdate(dctx, data, data_len) != 1 || - (rc = EVP_DigestVerifyFinal(dctx, signature, signature_len)) < 0) { - ret = dcrypt_openssl_error(error_r); - } else { - /* return code 1 means valid signature, otherwise invalid */ - *valid_r = (rc == 1); - ret = TRUE; - } - - EVP_MD_CTX_destroy(dctx); - - return ret; -} - -static bool -dcrypt_openssl_key_store_private_raw(struct dcrypt_private_key *key, - pool_t pool, - enum dcrypt_key_type *type_r, - ARRAY_TYPE(dcrypt_raw_key) *keys_r, - const char **error_r) -{ - i_assert(key != NULL && key->key != NULL); - i_assert(array_is_created(keys_r)); - EVP_PKEY *priv = key->key; - ARRAY_TYPE(dcrypt_raw_key) keys; - t_array_init(&keys, 2); - - if (EVP_PKEY_base_id(priv) == EVP_PKEY_RSA) { - DCRYPT_SET_ERROR("Not implemented"); - return FALSE; - } else if (EVP_PKEY_base_id(priv) == EVP_PKEY_EC) { - /* store OID */ - EC_KEY *key = EVP_PKEY_get0_EC_KEY(priv); - EC_KEY_set_conv_form(key, POINT_CONVERSION_UNCOMPRESSED); - int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(key)); - ASN1_OBJECT *obj = OBJ_nid2obj(nid); - int len = OBJ_length(obj); - if (len == 0) { - DCRYPT_SET_ERROR("Object has no OID assigned"); - return FALSE; - } - len = i2d_ASN1_OBJECT(obj, NULL); - unsigned char *bufptr = p_malloc(pool, len); - struct dcrypt_raw_key *item = array_append_space(&keys); - item->parameter = bufptr; - item->len = i2d_ASN1_OBJECT(obj, &bufptr); - ASN1_OBJECT_free(obj); - /* store private key */ - const BIGNUM *b = EC_KEY_get0_private_key(key); - len = BN_num_bytes(b); - item = array_append_space(&keys); - bufptr = p_malloc(pool, len); - if (BN_bn2bin(b, bufptr) < len) - return dcrypt_openssl_error(error_r); - item->parameter = bufptr; - item->len = len; - *type_r = DCRYPT_KEY_EC; - } else { - DCRYPT_SET_ERROR("Key type unsupported"); - return FALSE; - } - - array_append_array(keys_r, &keys); - return TRUE; -} - -static bool -dcrypt_openssl_key_store_public_raw(struct dcrypt_public_key *key, - pool_t pool, - enum dcrypt_key_type *type_r, - ARRAY_TYPE(dcrypt_raw_key) *keys_r, - const char **error_r) -{ - i_assert(key != NULL && key->key != NULL); - EVP_PKEY *pub = key->key; - ARRAY_TYPE(dcrypt_raw_key) keys; - t_array_init(&keys, 2); - - if (EVP_PKEY_base_id(pub) == EVP_PKEY_RSA) { - DCRYPT_SET_ERROR("Not implemented"); - return FALSE; - } else if (EVP_PKEY_base_id(pub) == EVP_PKEY_EC) { - /* store OID */ - EC_KEY *key = EVP_PKEY_get0_EC_KEY(pub); - EC_KEY_set_conv_form(key, POINT_CONVERSION_UNCOMPRESSED); - int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(key)); - ASN1_OBJECT *obj = OBJ_nid2obj(nid); - int len = OBJ_length(obj); - if (len == 0) { - DCRYPT_SET_ERROR("Object has no OID assigned"); - return FALSE; - } - len = i2d_ASN1_OBJECT(obj, NULL); - unsigned char *bufptr = p_malloc(pool, len); - struct dcrypt_raw_key *item = array_append_space(&keys); - item->parameter = bufptr; - item->len = i2d_ASN1_OBJECT(obj, &bufptr); - ASN1_OBJECT_free(obj); - - /* store public key */ - const EC_POINT *point = EC_KEY_get0_public_key(key); - len = EC_POINT_point2oct(EC_KEY_get0_group(key), point, - POINT_CONVERSION_UNCOMPRESSED, - NULL, 0, NULL); - bufptr = p_malloc(pool, len); - item = array_append_space(&keys); - item->parameter = bufptr; - item->len = len; - if (EC_POINT_point2oct(EC_KEY_get0_group(key), point, - POINT_CONVERSION_UNCOMPRESSED, - bufptr, len, NULL) < (unsigned int)len) - return dcrypt_openssl_error(error_r); - *type_r = DCRYPT_KEY_EC; - } else { - DCRYPT_SET_ERROR("Key type unsupported"); - return FALSE; - } - - array_append_array(keys_r, &keys); - - return TRUE; -} - -static bool -dcrypt_openssl_key_load_private_raw(struct dcrypt_private_key **key_r, - enum dcrypt_key_type type, - const ARRAY_TYPE(dcrypt_raw_key) *keys, - const char **error_r) -{ - int ec; - i_assert(keys != NULL && array_is_created(keys) && array_count(keys) > 1); - const struct dcrypt_raw_key *item; - - if (type == DCRYPT_KEY_RSA) { - DCRYPT_SET_ERROR("Not implemented"); - return FALSE; - } else if (type == DCRYPT_KEY_EC) { - /* get curve */ - if (array_count(keys) < 2) { - DCRYPT_SET_ERROR("Invalid parameters"); - return FALSE; - } - item = array_idx(keys, 0); - const unsigned char *oid = item->parameter; - ASN1_OBJECT *obj = d2i_ASN1_OBJECT(NULL, &oid, item->len); - if (obj == NULL) - return dcrypt_openssl_error(error_r); - int nid = OBJ_obj2nid(obj); - ASN1_OBJECT_free(obj); - - /* load private point */ - item = array_idx(keys, 1); - BIGNUM *bn = BN_secure_new(); - if (BN_bin2bn(item->parameter, item->len, bn) == NULL) { - BN_free(bn); - return dcrypt_openssl_error(error_r); - } - - /* setup a key */ - EC_KEY *key = EC_KEY_new_by_curve_name(nid); - ec = EC_KEY_set_private_key(key, bn); - BN_free(bn); - - if (ec != 1) { - EC_KEY_free(key); - return dcrypt_openssl_error(error_r); - } - - /* calculate & assign public key */ - EC_POINT *pub = EC_POINT_new(EC_KEY_get0_group(key)); - if (pub == NULL) { - EC_KEY_free(key); - return dcrypt_openssl_error(error_r); - } - /* calculate public key */ - ec = EC_POINT_mul(EC_KEY_get0_group(key), pub, - EC_KEY_get0_private_key(key), - NULL, NULL, NULL); - if (ec == 1) - ec = EC_KEY_set_public_key(key, pub); - EC_POINT_free(pub); - - /* check the key */ - if (ec != 1 || EC_KEY_check_key(key) != 1) { - EC_KEY_free(key); - return dcrypt_openssl_error(error_r); - } - EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE); - - EVP_PKEY *pkey = EVP_PKEY_new(); - EVP_PKEY_set1_EC_KEY(pkey, key); - EC_KEY_free(key); - *key_r = i_new(struct dcrypt_private_key, 1); - (*key_r)->key = pkey; - (*key_r)->ref++; - return TRUE; - } else { - DCRYPT_SET_ERROR("Key type unsupported"); - } - - return FALSE; -} - -static bool -dcrypt_openssl_key_load_public_raw(struct dcrypt_public_key **key_r, - enum dcrypt_key_type type, - const ARRAY_TYPE(dcrypt_raw_key) *keys, - const char **error_r) -{ - int ec; - i_assert(keys != NULL && array_is_created(keys) && array_count(keys) > 1); - const struct dcrypt_raw_key *item; - - if (type == DCRYPT_KEY_RSA) { - DCRYPT_SET_ERROR("Not implemented"); - return FALSE; - } else if (type == DCRYPT_KEY_EC) { - /* get curve */ - if (array_count(keys) < 2) { - DCRYPT_SET_ERROR("Invalid parameters"); - return FALSE; - } - item = array_idx(keys, 0); - const unsigned char *oid = item->parameter; - ASN1_OBJECT *obj = d2i_ASN1_OBJECT(NULL, &oid, item->len); - if (obj == NULL) { - dcrypt_openssl_error(error_r); - return FALSE; - } - int nid = OBJ_obj2nid(obj); - ASN1_OBJECT_free(obj); - - /* set group */ - EC_GROUP *group = EC_GROUP_new_by_curve_name(nid); - if (group == NULL) { - dcrypt_openssl_error(error_r); - return FALSE; - } - - /* load point */ - item = array_idx(keys, 1); - EC_POINT *point = EC_POINT_new(group); - if (EC_POINT_oct2point(group, point, item->parameter, - item->len, NULL) != 1) { - EC_POINT_free(point); - EC_GROUP_free(group); - return dcrypt_openssl_error(error_r); - } - - EC_KEY *key = EC_KEY_new(); - ec = EC_KEY_set_group(key, group); - if (ec == 1) - ec = EC_KEY_set_public_key(key, point); - EC_POINT_free(point); - EC_GROUP_free(group); - - if (ec != 1 || EC_KEY_check_key(key) != 1) { - EC_KEY_free(key); - return dcrypt_openssl_error(error_r); - } - - EC_KEY_precompute_mult(key, NULL); - EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE); - EVP_PKEY *pkey = EVP_PKEY_new(); - EVP_PKEY_set1_EC_KEY(pkey, key); - EC_KEY_free(key); - *key_r = i_new(struct dcrypt_public_key, 1); - (*key_r)->key = pkey; - (*key_r)->ref++; - return TRUE; - } else { - DCRYPT_SET_ERROR("Key type unsupported"); - } - - return FALSE; -} - -static bool -dcrypt_openssl_key_get_curve_public(struct dcrypt_public_key *key, - const char **curve_r, const char **error_r) -{ - EVP_PKEY *pkey = key->key; - char objtxt[OID_TEXT_MAX_LEN]; - - if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { - DCRYPT_SET_ERROR("Unsupported key type"); - return FALSE; - } - - ASN1_OBJECT *obj = OBJ_nid2obj(EC_GROUP_get_curve_name( - EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey)))); - - int len = OBJ_obj2txt(objtxt, sizeof(objtxt), obj, 1); - ASN1_OBJECT_free(obj); - - if (len < 1) { - return dcrypt_openssl_error(error_r); - } else if ((unsigned int)len > sizeof(objtxt)) { - DCRYPT_SET_ERROR("Object name too long"); - return FALSE; - } - - *curve_r = t_strndup(objtxt, len); - return TRUE; -} - -static const char * -dcrypt_openssl_key_get_id_public(struct dcrypt_public_key *key) -{ - return key->key_id; -} - -static const char * -dcrypt_openssl_key_get_id_private(struct dcrypt_private_key *key) -{ - return key->key_id; -} - -static void -dcrypt_openssl_key_set_id_public(struct dcrypt_public_key *key, const char *id) -{ - i_free(key->key_id); - key->key_id = i_strdup_empty(id); -} - -static void -dcrypt_openssl_key_set_id_private(struct dcrypt_private_key *key, const char *id) -{ - i_free(key->key_id); - key->key_id = i_strdup_empty(id); -} - -static enum dcrypt_key_usage -dcrypt_openssl_key_get_usage_public(struct dcrypt_public_key *key) -{ - return key->usage; -} - -static enum dcrypt_key_usage -dcrypt_openssl_key_get_usage_private(struct dcrypt_private_key *key) -{ - return key->usage; -} - -static void -dcrypt_openssl_key_set_usage_public(struct dcrypt_public_key *key, - enum dcrypt_key_usage usage) -{ - key->usage = usage; -} - -static void -dcrypt_openssl_key_set_usage_private(struct dcrypt_private_key *key, - enum dcrypt_key_usage usage) -{ - key->usage = usage; -} - - -static struct dcrypt_vfs dcrypt_openssl_vfs = { - .initialize = dcrypt_openssl_initialize, - .ctx_sym_create = dcrypt_openssl_ctx_sym_create, - .ctx_sym_destroy = dcrypt_openssl_ctx_sym_destroy, - .ctx_sym_set_key = dcrypt_openssl_ctx_sym_set_key, - .ctx_sym_set_iv = dcrypt_openssl_ctx_sym_set_iv, - .ctx_sym_set_key_iv_random = dcrypt_openssl_ctx_sym_set_key_iv_random, - .ctx_sym_set_padding = dcrypt_openssl_ctx_sym_set_padding, - .ctx_sym_get_key = dcrypt_openssl_ctx_sym_get_key, - .ctx_sym_get_iv = dcrypt_openssl_ctx_sym_get_iv, - .ctx_sym_set_aad = dcrypt_openssl_ctx_sym_set_aad, - .ctx_sym_get_aad = dcrypt_openssl_ctx_sym_get_aad, - .ctx_sym_set_tag = dcrypt_openssl_ctx_sym_set_tag, - .ctx_sym_get_tag = dcrypt_openssl_ctx_sym_get_tag, - .ctx_sym_get_key_length = dcrypt_openssl_ctx_sym_get_key_length, - .ctx_sym_get_iv_length = dcrypt_openssl_ctx_sym_get_iv_length, - .ctx_sym_get_block_size = dcrypt_openssl_ctx_sym_get_block_size, - .ctx_sym_init = dcrypt_openssl_ctx_sym_init, - .ctx_sym_update = dcrypt_openssl_ctx_sym_update, - .ctx_sym_final = dcrypt_openssl_ctx_sym_final, - .ctx_hmac_create = dcrypt_openssl_ctx_hmac_create, - .ctx_hmac_destroy = dcrypt_openssl_ctx_hmac_destroy, - .ctx_hmac_set_key = dcrypt_openssl_ctx_hmac_set_key, - .ctx_hmac_set_key_random = dcrypt_openssl_ctx_hmac_set_key_random, - .ctx_hmac_get_digest_length = dcrypt_openssl_ctx_hmac_get_digest_length, - .ctx_hmac_get_key = dcrypt_openssl_ctx_hmac_get_key, - .ctx_hmac_init = dcrypt_openssl_ctx_hmac_init, - .ctx_hmac_update = dcrypt_openssl_ctx_hmac_update, - .ctx_hmac_final = dcrypt_openssl_ctx_hmac_final, - .ecdh_derive_secret_local = dcrypt_openssl_ecdh_derive_secret_local, - .ecdh_derive_secret_peer = dcrypt_openssl_ecdh_derive_secret_peer, - .pbkdf2 = dcrypt_openssl_pbkdf2, - .generate_keypair = dcrypt_openssl_generate_keypair, - .load_private_key = dcrypt_openssl_load_private_key, - .load_public_key = dcrypt_openssl_load_public_key, - .store_private_key = dcrypt_openssl_store_private_key, - .store_public_key = dcrypt_openssl_store_public_key, - .private_to_public_key = dcrypt_openssl_private_to_public_key, - .key_string_get_info = dcrypt_openssl_key_string_get_info, - .unref_keypair = dcrypt_openssl_unref_keypair, - .unref_public_key = dcrypt_openssl_unref_public_key, - .unref_private_key = dcrypt_openssl_unref_private_key, - .ref_public_key = dcrypt_openssl_ref_public_key, - .ref_private_key = dcrypt_openssl_ref_private_key, - .rsa_encrypt = dcrypt_openssl_rsa_encrypt, - .rsa_decrypt = dcrypt_openssl_rsa_decrypt, - .oid2name = dcrypt_openssl_oid2name, - .name2oid = dcrypt_openssl_name2oid, - .private_key_type = dcrypt_openssl_private_key_type, - .public_key_type = dcrypt_openssl_public_key_type, - .public_key_id = dcrypt_openssl_public_key_id, - .public_key_id_old = dcrypt_openssl_public_key_id_old, - .private_key_id = dcrypt_openssl_private_key_id, - .private_key_id_old = dcrypt_openssl_private_key_id_old, - .key_store_private_raw = dcrypt_openssl_key_store_private_raw, - .key_store_public_raw = dcrypt_openssl_key_store_public_raw, - .key_load_private_raw = dcrypt_openssl_key_load_private_raw, - .key_load_public_raw = dcrypt_openssl_key_load_public_raw, - .key_get_curve_public = dcrypt_openssl_key_get_curve_public, - .key_get_id_public = dcrypt_openssl_key_get_id_public, - .key_get_id_private = dcrypt_openssl_key_get_id_private, - .key_set_id_public = dcrypt_openssl_key_set_id_public, - .key_set_id_private = dcrypt_openssl_key_set_id_private, - .key_get_usage_public = dcrypt_openssl_key_get_usage_public, - .key_get_usage_private = dcrypt_openssl_key_get_usage_private, - .key_set_usage_public = dcrypt_openssl_key_set_usage_public, - .key_set_usage_private = dcrypt_openssl_key_set_usage_private, - .sign = dcrypt_openssl_sign, - .verify = dcrypt_openssl_verify, - .ecdh_derive_secret = dcrypt_openssl_ecdh_derive_secret, -}; - -void dcrypt_openssl_init(struct module *module ATTR_UNUSED) -{ - dovecot_openssl_common_global_ref(); - dcrypt_set_vfs(&dcrypt_openssl_vfs); -} - -void dcrypt_openssl_deinit(void) -{ - dovecot_openssl_common_global_unref(); -} diff --git a/src/lib-dcrypt/dcrypt-private.h b/src/lib-dcrypt/dcrypt-private.h deleted file mode 100644 index 13da99e900..0000000000 --- a/src/lib-dcrypt/dcrypt-private.h +++ /dev/null @@ -1,211 +0,0 @@ -#ifndef DCRYPT_PRIVATE_H -#define DCRYPT_PRIVATE_H - -#define DCRYPT_DOVECOT_KEY_ENCRYPT_HASH "sha256" -#define DCRYPT_DOVECOT_KEY_ENCRYPT_ROUNDS 2048 - -#define DCRYPT_DOVECOT_KEY_ENCRYPT_NONE 0 -#define DCRYPT_DOVECOT_KEY_ENCRYPT_PK 1 -#define DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD 2 - -struct dcrypt_vfs { - bool (*initialize)(const struct dcrypt_settings *set, - const char **error_r); - - bool (*ctx_sym_create)(const char *algorithm, - enum dcrypt_sym_mode mode, - struct dcrypt_context_symmetric **ctx_r, - const char **error_r); - void (*ctx_sym_destroy)(struct dcrypt_context_symmetric **ctx); - - void (*ctx_sym_set_key)(struct dcrypt_context_symmetric *ctx, - const unsigned char *key, size_t key_len); - void (*ctx_sym_set_iv)(struct dcrypt_context_symmetric *ctx, - const unsigned char *iv, size_t iv_len); - void (*ctx_sym_set_key_iv_random)(struct dcrypt_context_symmetric *ctx); - - void (*ctx_sym_set_padding)(struct dcrypt_context_symmetric *ctx, - bool padding); - - bool (*ctx_sym_get_key)(struct dcrypt_context_symmetric *ctx, - buffer_t *key); - bool (*ctx_sym_get_iv)(struct dcrypt_context_symmetric *ctx, - buffer_t *iv); - - void (*ctx_sym_set_aad)(struct dcrypt_context_symmetric *ctx, - const unsigned char *aad, size_t aad_len); - bool (*ctx_sym_get_aad)(struct dcrypt_context_symmetric *ctx, - buffer_t *aad); - void (*ctx_sym_set_tag)(struct dcrypt_context_symmetric *ctx, - const unsigned char *tag, size_t tag_len); - bool (*ctx_sym_get_tag)(struct dcrypt_context_symmetric *ctx, - buffer_t *tag); - - unsigned int (*ctx_sym_get_key_length)( - struct dcrypt_context_symmetric *ctx); - unsigned int (*ctx_sym_get_iv_length)( - struct dcrypt_context_symmetric *ctx); - unsigned int (*ctx_sym_get_block_size)( - struct dcrypt_context_symmetric *ctx); - - bool (*ctx_sym_init)(struct dcrypt_context_symmetric *ctx, - const char **error_r); - bool (*ctx_sym_update)(struct dcrypt_context_symmetric *ctx, - const unsigned char *data, size_t data_len, - buffer_t *result, const char **error_r); - bool (*ctx_sym_final)(struct dcrypt_context_symmetric *ctx, - buffer_t *result, const char **error_r); - - bool (*ctx_hmac_create)(const char *algorithm, - struct dcrypt_context_hmac **ctx_r, - const char **error_r); - void (*ctx_hmac_destroy)(struct dcrypt_context_hmac **ctx); - - void (*ctx_hmac_set_key)(struct dcrypt_context_hmac *ctx, - const unsigned char *key, size_t key_len); - bool (*ctx_hmac_get_key)(struct dcrypt_context_hmac *ctx, - buffer_t *key); - unsigned int (*ctx_hmac_get_digest_length)( - struct dcrypt_context_hmac *ctx); - void (*ctx_hmac_set_key_random)(struct dcrypt_context_hmac *ctx); - - bool (*ctx_hmac_init)(struct dcrypt_context_hmac *ctx, - const char **error_r); - bool (*ctx_hmac_update)(struct dcrypt_context_hmac *ctx, - const unsigned char *data, size_t data_len, - const char **error_r); - bool (*ctx_hmac_final)(struct dcrypt_context_hmac *ctx, - buffer_t *result, const char **error_r); - - bool (*ecdh_derive_secret_local)(struct dcrypt_private_key *local_key, - buffer_t *R, buffer_t *S, - const char **error_r); - bool (*ecdh_derive_secret_peer)(struct dcrypt_public_key *peer_key, - buffer_t *R, buffer_t *S, - const char **error_r); - bool (*pbkdf2)(const unsigned char *password, size_t password_len, - const unsigned char *salt, size_t salt_len, - const char *hash, unsigned int rounds, - buffer_t *result, unsigned int result_len, - const char **error_r); - - bool (*generate_keypair)(struct dcrypt_keypair *pair_r, - enum dcrypt_key_type kind, unsigned int bits, - const char *curve, const char **error_r); - - bool (*load_private_key)(struct dcrypt_private_key **key_r, - const char *data, const char *password, - struct dcrypt_private_key *dec_key, - const char **error_r); - bool (*load_public_key)(struct dcrypt_public_key **key_r, - const char *data, const char **error_r); - - bool (*store_private_key)(struct dcrypt_private_key *key, - enum dcrypt_key_format format, - const char *cipher, buffer_t *destination, - const char *password, - struct dcrypt_public_key *enc_key, - const char **error_r); - bool (*store_public_key)(struct dcrypt_public_key *key, - enum dcrypt_key_format format, - buffer_t *destination, const char **error_r); - - void (*private_to_public_key)(struct dcrypt_private_key *priv_key, - struct dcrypt_public_key **pub_key_r); - - bool (*key_string_get_info)( - const char *key_data, enum dcrypt_key_format *format_r, - enum dcrypt_key_version *version_r, - enum dcrypt_key_kind *kind_r, - enum dcrypt_key_encryption_type *encryption_type_r, - const char **encryption_key_hash_r, const char **key_hash_r, - const char **error_r); - - void (*unref_keypair)(struct dcrypt_keypair *keypair); - void (*unref_public_key)(struct dcrypt_public_key **key); - void (*unref_private_key)(struct dcrypt_private_key **key); - void (*ref_public_key)(struct dcrypt_public_key *key); - void (*ref_private_key)(struct dcrypt_private_key *key); - - bool (*rsa_encrypt)(struct dcrypt_public_key *key, - const unsigned char *data, size_t data_len, - buffer_t *result, enum dcrypt_padding padding, - const char **error_r); - bool (*rsa_decrypt)(struct dcrypt_private_key *key, - const unsigned char *data, size_t data_len, - buffer_t *result, enum dcrypt_padding padding, - const char **error_r); - - const char *(*oid2name)(const unsigned char *oid, - size_t oid_len, const char **error_r); - bool (*name2oid)(const char *name, buffer_t *oid, - const char **error_r); - - enum dcrypt_key_type (*private_key_type)(struct dcrypt_private_key *key); - enum dcrypt_key_type (*public_key_type)(struct dcrypt_public_key *key); - bool (*public_key_id)(struct dcrypt_public_key *key, - const char *algorithm, buffer_t *result, - const char **error_r); - bool (*public_key_id_old)(struct dcrypt_public_key *key, - buffer_t *result, const char **error_r); - bool (*private_key_id)(struct dcrypt_private_key *key, - const char *algorithm, buffer_t *result, - const char **error_r); - bool (*private_key_id_old)(struct dcrypt_private_key *key, - buffer_t *result, const char **error_r); - bool (*key_store_private_raw)(struct dcrypt_private_key *key, - pool_t pool, - enum dcrypt_key_type *key_type_r, - ARRAY_TYPE(dcrypt_raw_key) *keys_r, - const char **error_r); - bool (*key_store_public_raw)(struct dcrypt_public_key *key, - pool_t pool, - enum dcrypt_key_type *key_type_r, - ARRAY_TYPE(dcrypt_raw_key) *keys_r, - const char **error_r); - bool (*key_load_private_raw)(struct dcrypt_private_key **key_r, - enum dcrypt_key_type key_type, - const ARRAY_TYPE(dcrypt_raw_key) *keys, - const char **error_r); - bool (*key_load_public_raw)(struct dcrypt_public_key **key_r, - enum dcrypt_key_type key_type, - const ARRAY_TYPE(dcrypt_raw_key) *keys, - const char **error_r); - bool (*key_get_curve_public)(struct dcrypt_public_key *key, - const char **curve_r, const char **error_r); - const char *(*key_get_id_public)(struct dcrypt_public_key *key); - const char *(*key_get_id_private)(struct dcrypt_private_key *key); - void (*key_set_id_public)(struct dcrypt_public_key *key, const char *id); - void (*key_set_id_private)(struct dcrypt_private_key *key, const char *id); - enum dcrypt_key_usage (*key_get_usage_public)(struct dcrypt_public_key *key); - enum dcrypt_key_usage (*key_get_usage_private)(struct dcrypt_private_key *key); - void (*key_set_usage_public)(struct dcrypt_public_key *key, - enum dcrypt_key_usage usage); - void (*key_set_usage_private)(struct dcrypt_private_key *key, - enum dcrypt_key_usage usage); - bool (*sign)(struct dcrypt_private_key *key, const char *algorithm, - enum dcrypt_signature_format format, - const void *data, size_t data_len, buffer_t *signature_r, - enum dcrypt_padding padding, const char **error_r); - bool (*verify)(struct dcrypt_public_key *key, const char *algorithm, - enum dcrypt_signature_format format, - const void *data, size_t data_len, - const unsigned char *signature, size_t signature_len, - bool *valid_r, enum dcrypt_padding padding, - const char **error_r); - bool (*ecdh_derive_secret)(struct dcrypt_private_key *priv_key, - struct dcrypt_public_key *pub_key, - buffer_t *shared_secret, const char **error_r); -}; - -void dcrypt_set_vfs(struct dcrypt_vfs *vfs); - -void dcrypt_openssl_init(struct module *module ATTR_UNUSED); -void dcrypt_gnutls_init(struct module *module ATTR_UNUSED); -void dcrypt_openssl_deinit(void); -void dcrypt_gnutls_deinit(void); - -int parse_jwk_key(const char *key_data, struct json_tree **tree_r, - const char **error_r); - -#endif diff --git a/src/lib-dcrypt/dcrypt.c b/src/lib-dcrypt/dcrypt.c deleted file mode 100644 index b699fb3989..0000000000 --- a/src/lib-dcrypt/dcrypt.c +++ /dev/null @@ -1,647 +0,0 @@ -/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "module-dir.h" -#include "dcrypt.h" -#include "istream.h" -#include "json-tree.h" -#include "dcrypt-private.h" - -static struct module *dcrypt_module = NULL; -static struct dcrypt_vfs *dcrypt_vfs = NULL; -static const struct dcrypt_settings dcrypt_default_set = { NULL }; - -bool dcrypt_initialize(const char *backend, const struct dcrypt_settings *set, - const char **error_r) -{ - if (dcrypt_vfs != NULL) { - return TRUE; - } - if (backend == NULL) backend = "openssl"; /* default for now */ - if (set == NULL) - set = &dcrypt_default_set; - - const char *implementation = t_strconcat("dcrypt_",backend,NULL); - - module_dir_load_dcrypt_backend(&dcrypt_module, implementation); - module_dir_init(dcrypt_module); - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->initialize != NULL) { - if (!dcrypt_vfs->initialize(set, error_r)) { - dcrypt_deinitialize(); - return FALSE; - } - } - /* Destroy SSL module after(most of) the others. Especially lib-fs - backends may still want to access SSL module in their own - atexit-callbacks. */ - lib_atexit_priority(dcrypt_deinitialize, LIB_ATEXIT_PRIORITY_LOW); - return TRUE; -} - -void dcrypt_deinitialize(void) -{ - module_dir_unload(&dcrypt_module); - dcrypt_vfs = NULL; -} - -void dcrypt_set_vfs(struct dcrypt_vfs *vfs) -{ - dcrypt_vfs = vfs; -} - -bool dcrypt_is_initialized(void) -{ - return dcrypt_vfs != NULL; -} - -bool dcrypt_ctx_sym_create(const char *algorithm, enum dcrypt_sym_mode mode, - struct dcrypt_context_symmetric **ctx_r, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_sym_create(algorithm, mode, ctx_r, error_r); -} - -void dcrypt_ctx_sym_destroy(struct dcrypt_context_symmetric **ctx) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ctx_sym_destroy(ctx); -} - -void dcrypt_ctx_sym_set_key(struct dcrypt_context_symmetric *ctx, - const unsigned char *key, size_t key_len) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ctx_sym_set_key(ctx, key, key_len); -} - -void dcrypt_ctx_sym_set_iv(struct dcrypt_context_symmetric *ctx, - const unsigned char *iv, size_t iv_len) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ctx_sym_set_iv(ctx, iv, iv_len); -} - -void dcrypt_ctx_sym_set_key_iv_random(struct dcrypt_context_symmetric *ctx) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ctx_sym_set_key_iv_random(ctx); -} - -bool dcrypt_ctx_sym_get_key(struct dcrypt_context_symmetric *ctx, buffer_t *key) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_sym_get_key(ctx, key); -} -bool dcrypt_ctx_sym_get_iv(struct dcrypt_context_symmetric *ctx, buffer_t *iv) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_sym_get_iv(ctx, iv); -} - -unsigned int dcrypt_ctx_sym_get_key_length(struct dcrypt_context_symmetric *ctx) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_sym_get_key_length(ctx); -} - -unsigned int dcrypt_ctx_sym_get_iv_length(struct dcrypt_context_symmetric *ctx) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_sym_get_iv_length(ctx); -} - -void dcrypt_ctx_sym_set_aad(struct dcrypt_context_symmetric *ctx, - const unsigned char *aad, size_t aad_len) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ctx_sym_set_aad(ctx, aad, aad_len); -} - -bool dcrypt_ctx_sym_get_aad(struct dcrypt_context_symmetric *ctx, buffer_t *aad) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_sym_get_aad(ctx, aad); -} - -void dcrypt_ctx_sym_set_tag(struct dcrypt_context_symmetric *ctx, - const unsigned char *tag, size_t tag_len) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ctx_sym_set_tag(ctx, tag, tag_len); -} - -bool dcrypt_ctx_sym_get_tag(struct dcrypt_context_symmetric *ctx, buffer_t *tag) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_sym_get_tag(ctx, tag); -} - -unsigned int dcrypt_ctx_sym_get_block_size(struct dcrypt_context_symmetric *ctx) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_sym_get_block_size(ctx); -} - -bool dcrypt_ctx_sym_init(struct dcrypt_context_symmetric *ctx, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_sym_init(ctx, error_r); -} - -bool dcrypt_ctx_sym_update(struct dcrypt_context_symmetric *ctx, - const unsigned char *data, - size_t data_len, buffer_t *result, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_sym_update(ctx, data, data_len, result, error_r); -} - -bool dcrypt_ctx_sym_final(struct dcrypt_context_symmetric *ctx, - buffer_t *result, const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_sym_final(ctx, result, error_r); -} - -void dcrypt_ctx_sym_set_padding(struct dcrypt_context_symmetric *ctx, - bool padding) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ctx_sym_set_padding(ctx, padding); -} - -bool dcrypt_ctx_hmac_create(const char *algorithm, - struct dcrypt_context_hmac **ctx_r, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_hmac_create(algorithm, ctx_r, error_r); -} - -void dcrypt_ctx_hmac_destroy(struct dcrypt_context_hmac **ctx) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ctx_hmac_destroy(ctx); -} - -void dcrypt_ctx_hmac_set_key(struct dcrypt_context_hmac *ctx, - const unsigned char *key, size_t key_len) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ctx_hmac_set_key(ctx, key, key_len); -} - -bool dcrypt_ctx_hmac_get_key(struct dcrypt_context_hmac *ctx, buffer_t *key) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_hmac_get_key(ctx, key); -} - -void dcrypt_ctx_hmac_set_key_random(struct dcrypt_context_hmac *ctx) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ctx_hmac_set_key_random(ctx); -} - -unsigned int dcrypt_ctx_hmac_get_digest_length(struct dcrypt_context_hmac *ctx) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_hmac_get_digest_length(ctx); -} - -bool dcrypt_ctx_hmac_init(struct dcrypt_context_hmac *ctx, const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_hmac_init(ctx, error_r); -} - -bool dcrypt_ctx_hmac_update(struct dcrypt_context_hmac *ctx, - const unsigned char *data, size_t data_len, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_hmac_update(ctx, data, data_len, error_r); -} - -bool dcrypt_ctx_hmac_final(struct dcrypt_context_hmac *ctx, buffer_t *result, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ctx_hmac_final(ctx, result, error_r); -} - -bool dcrypt_ecdh_derive_secret(struct dcrypt_private_key *local_key, - struct dcrypt_public_key *pub_key, - buffer_t *shared_secret, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->ecdh_derive_secret == NULL) { - *error_r = "Not implemented"; - return FALSE; - } - return dcrypt_vfs->ecdh_derive_secret(local_key, pub_key, shared_secret, - error_r); -} - -bool dcrypt_ecdh_derive_secret_local(struct dcrypt_private_key *local_key, - buffer_t *R, buffer_t *S, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ecdh_derive_secret_local(local_key, R, S, error_r); -} -bool dcrypt_ecdh_derive_secret_peer(struct dcrypt_public_key *peer_key, - buffer_t *R, buffer_t *S, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->ecdh_derive_secret_peer(peer_key, R, S, error_r); -} - -bool dcrypt_pbkdf2(const unsigned char *password, size_t password_len, - const unsigned char *salt, size_t salt_len, - const char *hash, unsigned int rounds, buffer_t *result, - unsigned int result_len, const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->pbkdf2(password, password_len, salt, salt_len, - hash, rounds, result, result_len, error_r); -} - -bool dcrypt_keypair_generate(struct dcrypt_keypair *pair_r, - enum dcrypt_key_type kind, unsigned int bits, - const char *curve, const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - i_zero(pair_r); - return dcrypt_vfs->generate_keypair(pair_r, kind, bits, curve, error_r); -} - -bool dcrypt_key_load_private(struct dcrypt_private_key **key_r, - const char *data, const char *password, - struct dcrypt_private_key *dec_key, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->load_private_key(key_r, data, password, - dec_key, error_r); -} - -bool dcrypt_key_load_public(struct dcrypt_public_key **key_r, - const char *data, const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->load_public_key(key_r, data, error_r); -} - -bool dcrypt_key_store_private(struct dcrypt_private_key *key, - enum dcrypt_key_format format, - const char *cipher, buffer_t *destination, - const char *password, - struct dcrypt_public_key *enc_key, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->store_private_key(key, format, cipher, - destination, password, enc_key, - error_r); -} -bool dcrypt_key_store_public(struct dcrypt_public_key *key, - enum dcrypt_key_format format, - buffer_t *destination, const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->store_public_key(key, format, destination, error_r); -} - -void dcrypt_key_convert_private_to_public(struct dcrypt_private_key *priv_key, - struct dcrypt_public_key **pub_key_r) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->private_to_public_key(priv_key, pub_key_r); -} - -bool dcrypt_key_string_get_info(const char *key_data, - enum dcrypt_key_format *format_r, - enum dcrypt_key_version *version_r, - enum dcrypt_key_kind *kind_r, - enum dcrypt_key_encryption_type *encryption_type_r, - const char **encryption_key_hash_r, - const char **key_hash_r, const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs-> - key_string_get_info(key_data, format_r, version_r, kind_r, - encryption_type_r, encryption_key_hash_r, - key_hash_r, error_r); -} - -enum dcrypt_key_type dcrypt_key_type_private(struct dcrypt_private_key *key) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->private_key_type(key); -} - -enum dcrypt_key_type dcrypt_key_type_public(struct dcrypt_public_key *key) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->public_key_type(key); -} - -bool dcrypt_key_id_public(struct dcrypt_public_key *key, const char *algorithm, - buffer_t *result, const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->public_key_id(key, algorithm, result, error_r); -} - -bool dcrypt_key_id_public_old(struct dcrypt_public_key *key, buffer_t *result, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->public_key_id_old(key, result, error_r); -} - -bool dcrypt_key_id_private(struct dcrypt_private_key *key, - const char *algorithm, buffer_t *result, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->private_key_id(key, algorithm, result, error_r); -} - -bool dcrypt_key_id_private_old(struct dcrypt_private_key *key, buffer_t *result, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->private_key_id_old(key, result, error_r); -} - -void dcrypt_keypair_unref(struct dcrypt_keypair *keypair) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->unref_keypair(keypair); -} - -void dcrypt_key_ref_public(struct dcrypt_public_key *key) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ref_public_key(key); -} - -void dcrypt_key_ref_private(struct dcrypt_private_key *key) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->ref_private_key(key); -} - -void dcrypt_key_unref_public(struct dcrypt_public_key **key) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->unref_public_key(key); -} - -void dcrypt_key_unref_private(struct dcrypt_private_key **key) -{ - i_assert(dcrypt_vfs != NULL); - dcrypt_vfs->unref_private_key(key); -} - -bool dcrypt_rsa_encrypt(struct dcrypt_public_key *key, - const unsigned char *data, size_t data_len, - buffer_t *result, enum dcrypt_padding padding, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->rsa_encrypt(key, data, data_len, result, - padding, error_r); -} - -bool dcrypt_rsa_decrypt(struct dcrypt_private_key *key, - const unsigned char *data, size_t data_len, - buffer_t *result, enum dcrypt_padding padding, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->rsa_decrypt(key, data, data_len, result, - padding, error_r); -} - -const char *dcrypt_oid2name(const unsigned char *oid, size_t oid_len, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->oid2name(oid, oid_len, error_r); -} - -bool dcrypt_name2oid(const char *name, buffer_t *oid, const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - return dcrypt_vfs->name2oid(name, oid, error_r); -} - -bool dcrypt_key_store_private_raw(struct dcrypt_private_key *key, - pool_t pool, - enum dcrypt_key_type *key_type_r, - ARRAY_TYPE(dcrypt_raw_key) *keys_r, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_store_private_raw == NULL) { - *error_r = "Not implemented"; - return FALSE; - } - return dcrypt_vfs->key_store_private_raw(key, pool, key_type_r, keys_r, - error_r); -} - -bool dcrypt_key_store_public_raw(struct dcrypt_public_key *key, - pool_t pool, - enum dcrypt_key_type *key_type_r, - ARRAY_TYPE(dcrypt_raw_key) *keys_r, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_store_public_raw == NULL) { - *error_r = "Not implemented"; - return FALSE; - } - return dcrypt_vfs->key_store_public_raw(key, pool, key_type_r, keys_r, - error_r); -} - -bool dcrypt_key_load_private_raw(struct dcrypt_private_key **key_r, - enum dcrypt_key_type key_type, - const ARRAY_TYPE(dcrypt_raw_key) *keys, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_load_private_raw == NULL) { - *error_r = "Not implemented"; - return FALSE; - } - return dcrypt_vfs->key_load_private_raw(key_r, key_type, keys, - error_r); -} - -bool dcrypt_key_load_public_raw(struct dcrypt_public_key **key_r, - enum dcrypt_key_type key_type, - const ARRAY_TYPE(dcrypt_raw_key) *keys, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_load_public_raw == NULL) { - *error_r = "Not implemented"; - return FALSE; - } - return dcrypt_vfs->key_load_public_raw(key_r, key_type, keys, - error_r); -} - -bool dcrypt_key_get_curve_public(struct dcrypt_public_key *key, - const char **curve_r, const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_get_curve_public == NULL) { - *error_r = "Not implemented"; - return FALSE; - } - return dcrypt_vfs->key_get_curve_public(key, curve_r, error_r); -} - -const char *dcrypt_key_get_id_public(struct dcrypt_public_key *key) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_get_id_public == NULL) - return NULL; - return dcrypt_vfs->key_get_id_public(key); -} - -const char *dcrypt_key_get_id_private(struct dcrypt_private_key *key) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_get_id_private == NULL) - return NULL; - return dcrypt_vfs->key_get_id_private(key); -} - -void dcrypt_key_set_id_public(struct dcrypt_public_key *key, const char *id) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_set_id_public == NULL) - return; - dcrypt_vfs->key_set_id_public(key, id); -} - -void dcrypt_key_set_id_private(struct dcrypt_private_key *key, const char *id) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_set_id_private == NULL) - return; - dcrypt_vfs->key_set_id_private(key, id); -} - -enum dcrypt_key_usage dcrypt_key_get_usage_public(struct dcrypt_public_key *key) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_get_usage_public == NULL) - return DCRYPT_KEY_USAGE_NONE; - return dcrypt_vfs->key_get_usage_public(key); -} - -enum dcrypt_key_usage dcrypt_key_get_usage_private(struct dcrypt_private_key *key) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_get_usage_private == NULL) - return DCRYPT_KEY_USAGE_NONE; - return dcrypt_vfs->key_get_usage_private(key); -} - -void dcrypt_key_set_usage_public(struct dcrypt_public_key *key, - enum dcrypt_key_usage usage) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_set_usage_public == NULL) - return; - dcrypt_vfs->key_set_usage_public(key, usage); -} - -void dcrypt_key_set_usage_private(struct dcrypt_private_key *key, - enum dcrypt_key_usage usage) -{ - i_assert(dcrypt_vfs != NULL); - if (dcrypt_vfs->key_set_usage_private == NULL) - return; - dcrypt_vfs->key_set_usage_private(key, usage); -} - -bool dcrypt_sign(struct dcrypt_private_key *key, const char *algorithm, - enum dcrypt_signature_format format, - const void *data, size_t data_len, buffer_t *signature_r, - enum dcrypt_padding padding, const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - - if (dcrypt_vfs->sign == NULL) { - *error_r = "Not implemented"; - return FALSE; - } - - return dcrypt_vfs->sign(key, algorithm, format, data, data_len, - signature_r, padding, error_r); -} - -bool dcrypt_verify(struct dcrypt_public_key *key, const char *algorithm, - enum dcrypt_signature_format format, - const void *data, size_t data_len, - const unsigned char *signature, size_t signature_len, - bool *valid_r, enum dcrypt_padding padding, - const char **error_r) -{ - i_assert(dcrypt_vfs != NULL); - - if (dcrypt_vfs->verify == NULL) { - *error_r = "Not implemented"; - return FALSE; - } - - return dcrypt_vfs->verify(key, algorithm, format, data, data_len, - signature, signature_len, - valid_r, padding, error_r); -} - -int parse_jwk_key(const char *key_data, struct json_tree **tree_r, - const char **error_r) -{ - struct istream *is = i_stream_create_from_data(key_data, strlen(key_data)); - struct json_parser *parser = json_parser_init(is); - struct json_tree *tree = json_tree_init(); - const char *error; - enum json_type type; - const char *value; - int ret; - - i_stream_unref(&is); - - while ((ret = json_parse_next(parser, &type, &value)) == 1) - json_tree_append(tree, type, value); - - i_assert(ret == -1); - - if (json_parser_deinit(&parser, &error) != 0) { - json_tree_deinit(&tree); - *error_r = error; - if (error == NULL) - *error_r = "Truncated JSON"; - return -1; - } - - *tree_r = tree; - - return 0; -} diff --git a/src/lib-dcrypt/dcrypt.h b/src/lib-dcrypt/dcrypt.h deleted file mode 100644 index fffee831a5..0000000000 --- a/src/lib-dcrypt/dcrypt.h +++ /dev/null @@ -1,407 +0,0 @@ -#ifndef DCRYPT_H -#define DCRYPT_H 1 -#include "array.h" - -struct dcrypt_context_symmetric; -struct dcrypt_context_hmac; -struct dcrypt_public_key; -struct dcrypt_private_key; - -struct dcrypt_keypair { - struct dcrypt_public_key *pub; - struct dcrypt_private_key *priv; -}; - -enum dcrypt_sym_mode { - DCRYPT_MODE_ENCRYPT, - DCRYPT_MODE_DECRYPT -}; - -enum dcrypt_key_type { - DCRYPT_KEY_RSA = 0x1, - DCRYPT_KEY_EC = 0x2 -}; - -/** - * dovecot key format: - * version version-specific data - * v1: version tab nid tab raw ec private key (in hex) - * v2: version colon algorithm oid colon private-or-public-key-only (in hex) - */ -enum dcrypt_key_format { - DCRYPT_FORMAT_PEM, - DCRYPT_FORMAT_DOVECOT, - DCRYPT_FORMAT_JWK, /* JSON Web Key (JWK) [RFC7517] */ -}; - -enum dcrypt_key_encryption_type { - DCRYPT_KEY_ENCRYPTION_TYPE_NONE, - DCRYPT_KEY_ENCRYPTION_TYPE_KEY, - DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD -}; - -enum dcrypt_key_version { - DCRYPT_KEY_VERSION_1, - DCRYPT_KEY_VERSION_2, - DCRYPT_KEY_VERSION_NA /* not applicable, PEM key */ -}; - -enum dcrypt_key_kind { - DCRYPT_KEY_KIND_PUBLIC, - DCRYPT_KEY_KIND_PRIVATE -}; - -enum dcrypt_key_usage { - DCRYPT_KEY_USAGE_NONE, - DCRYPT_KEY_USAGE_ENCRYPT, - DCRYPT_KEY_USAGE_SIGN, -}; - -enum dcrypt_signature_format { - DCRYPT_SIGNATURE_FORMAT_DSS, - DCRYPT_SIGNATURE_FORMAT_X962, -}; - -/* this parameter makes sense with RSA only - default for RSA means either PSS (sign/verify) - or OAEP (encrypt/decrypt). - for ECDSA default can be used. -*/ -enum dcrypt_padding { - DCRYPT_PADDING_DEFAULT, - DCRYPT_PADDING_RSA_PKCS1_PSS, - DCRYPT_PADDING_RSA_PKCS1_OAEP, - DCRYPT_PADDING_RSA_PKCS1, /* for compatibility use only */ - DCRYPT_PADDING_RSA_NO, -}; - -struct dcrypt_settings { - /* OpenSSL engine to use */ - const char *crypto_device; -}; - -struct dcrypt_raw_key { - const void *parameter; - size_t len; -}; - -ARRAY_DEFINE_TYPE(dcrypt_raw_key, struct dcrypt_raw_key); - -/** - * load and initialize dcrypt backend, use either openssl or gnutls - */ -bool dcrypt_initialize(const char *backend, const struct dcrypt_settings *set, - const char **error_r); - -/** - * Returns TRUE if dcrypt has been initialized. - */ -bool dcrypt_is_initialized(void); - -/** - * deinitialize dcrypt. - * - * NOTE: Do not call this function if you are going to use dcrypt later on. - * Deinitializing the library using this will not allow it to be reinitialized - * when using OpenSSL. - */ -void dcrypt_deinitialize(void); - -/** - * create symmetric context - */ -bool dcrypt_ctx_sym_create(const char *algorithm, enum dcrypt_sym_mode mode, - struct dcrypt_context_symmetric **ctx_r, - const char **error_r); - -/** - * destroy symmetric context and free memory - */ -void dcrypt_ctx_sym_destroy(struct dcrypt_context_symmetric **ctx); - -/** - * key and IV manipulation functions - */ -void dcrypt_ctx_sym_set_key(struct dcrypt_context_symmetric *ctx, - const unsigned char *key, size_t key_len); -void dcrypt_ctx_sym_set_iv(struct dcrypt_context_symmetric *ctx, - const unsigned char *iv, size_t iv_len); -void dcrypt_ctx_sym_set_key_iv_random(struct dcrypt_context_symmetric *ctx); -bool dcrypt_ctx_sym_get_key(struct dcrypt_context_symmetric *ctx, buffer_t *key); -bool dcrypt_ctx_sym_get_iv(struct dcrypt_context_symmetric *ctx, buffer_t *iv); - -/** - * turn padding on/off (default: on) - */ -void dcrypt_ctx_sym_set_padding(struct dcrypt_context_symmetric *ctx, - bool padding); - -/** - * authentication data manipulation (use with GCM only) - */ -void dcrypt_ctx_sym_set_aad(struct dcrypt_context_symmetric *ctx, - const unsigned char *aad, size_t aad_len); -bool dcrypt_ctx_sym_get_aad(struct dcrypt_context_symmetric *ctx, - buffer_t *aad); -/** - * result tag from aead (use with GCM only) - */ -void dcrypt_ctx_sym_set_tag(struct dcrypt_context_symmetric *ctx, - const unsigned char *tag, size_t tag_len); -bool dcrypt_ctx_sym_get_tag(struct dcrypt_context_symmetric *ctx, - buffer_t *tag); - -/* get various lengths */ -unsigned int dcrypt_ctx_sym_get_key_length(struct dcrypt_context_symmetric *ctx); -unsigned int dcrypt_ctx_sym_get_iv_length(struct dcrypt_context_symmetric *ctx); -unsigned int dcrypt_ctx_sym_get_block_size(struct dcrypt_context_symmetric *ctx); - -/** - * initialize crypto - */ -bool dcrypt_ctx_sym_init(struct dcrypt_context_symmetric *ctx, - const char **error_r); - -/** - * update with data - */ -bool dcrypt_ctx_sym_update(struct dcrypt_context_symmetric *ctx, - const unsigned char *data, size_t data_len, - buffer_t *result, const char **error_r); - -/** - * perform final step (may or may not emit data) - */ -bool dcrypt_ctx_sym_final(struct dcrypt_context_symmetric *ctx, - buffer_t *result, const char **error_r); - -/** - * create HMAC context, algorithm is digest algorithm - */ -bool dcrypt_ctx_hmac_create(const char *algorithm, - struct dcrypt_context_hmac **ctx_r, - const char **error_r); -/** - * destroy HMAC context and free memory - */ -void dcrypt_ctx_hmac_destroy(struct dcrypt_context_hmac **ctx); - -/** - * hmac key manipulation - */ -void dcrypt_ctx_hmac_set_key(struct dcrypt_context_hmac *ctx, - const unsigned char *key, size_t key_len); -bool dcrypt_ctx_hmac_get_key(struct dcrypt_context_hmac *ctx, buffer_t *key); -void dcrypt_ctx_hmac_set_key_random(struct dcrypt_context_hmac *ctx); - -/** - * get digest length for HMAC - */ -unsigned int dcrypt_ctx_hmac_get_digest_length(struct dcrypt_context_hmac *ctx); - -/** - * initialize hmac - */ -bool dcrypt_ctx_hmac_init(struct dcrypt_context_hmac *ctx, - const char **error_r); - -/** - * update hmac context with data - */ -bool dcrypt_ctx_hmac_update(struct dcrypt_context_hmac *ctx, - const unsigned char *data, size_t data_len, - const char **error_r); - -/** - * perform final rounds and retrieve result - */ -bool dcrypt_ctx_hmac_final(struct dcrypt_context_hmac *ctx, buffer_t *result, - const char **error_r); - -/** - * Elliptic Curve based Diffie-Heffman shared secret derivation */ -bool dcrypt_ecdh_derive_secret(struct dcrypt_private_key *priv_key, - struct dcrypt_public_key *pub_key, - buffer_t *shared_secret, - const char **error_r); -/** - * Helpers for DCRYPT file format */ -bool dcrypt_ecdh_derive_secret_local(struct dcrypt_private_key *local_key, - buffer_t *R, buffer_t *S, - const char **error_r); -bool dcrypt_ecdh_derive_secret_peer(struct dcrypt_public_key *peer_key, - buffer_t *R, buffer_t *S, - const char **error_r); - -/** Signature functions - algorithm is name of digest algorithm to use, such as SHA256. - - both RSA and EC keys are supported. -*/ - -/* returns false on error, true on success */ -bool dcrypt_sign(struct dcrypt_private_key *key, const char *algorithm, - enum dcrypt_signature_format format, - const void *data, size_t data_len, buffer_t *signature_r, - enum dcrypt_padding padding, const char **error_r); - -/* check valid_r for signature validity - false return means it wasn't able to verify it for other reasons */ -bool dcrypt_verify(struct dcrypt_public_key *key, const char *algorithm, - enum dcrypt_signature_format format, - const void *data, size_t data_len, - const unsigned char *signature, size_t signature_len, - bool *valid_r, enum dcrypt_padding padding, - const char **error_r); - -/** - * generate cryptographic data from password and salt. Use 1000-10000 for rounds. - */ -bool dcrypt_pbkdf2(const unsigned char *password, size_t password_len, - const unsigned char *salt, size_t salt_len, - const char *hash, unsigned int rounds, - buffer_t *result, unsigned int result_len, - const char **error_r); - -bool dcrypt_keypair_generate(struct dcrypt_keypair *pair_r, - enum dcrypt_key_type kind, unsigned int bits, - const char *curve, const char **error_r); - -/** - * load loads key structure from external format. - * store stores key structure into external format. - * - * you can provide either PASSWORD or ENC_KEY, not both. - */ -bool dcrypt_key_load_private(struct dcrypt_private_key **key_r, - const char *data, const char *password, - struct dcrypt_private_key *dec_key, - const char **error_r); - -bool dcrypt_key_load_public(struct dcrypt_public_key **key_r, - const char *data, const char **error_r); - -/** - * When encrypting with public key, the cipher parameter here must begin with - * ecdh-, for example ecdh-aes-256-ctr. An example of a valid cipher for - * encrypting with password would be aes-256-ctr. - */ -bool dcrypt_key_store_private(struct dcrypt_private_key *key, - enum dcrypt_key_format format, const char *cipher, - buffer_t *destination, const char *password, - struct dcrypt_public_key *enc_key, - const char **error_r); - -bool dcrypt_key_store_public(struct dcrypt_public_key *key, - enum dcrypt_key_format format, - buffer_t *destination, const char **error_r); - -void dcrypt_key_convert_private_to_public(struct dcrypt_private_key *priv_key, - struct dcrypt_public_key **pub_key_r); - -void dcrypt_keypair_unref(struct dcrypt_keypair *keypair); -void dcrypt_key_ref_public(struct dcrypt_public_key *key); -void dcrypt_key_ref_private(struct dcrypt_private_key *key); -void dcrypt_key_unref_public(struct dcrypt_public_key **key); -void dcrypt_key_unref_private(struct dcrypt_private_key **key); - -enum dcrypt_key_type dcrypt_key_type_private(struct dcrypt_private_key *key); -enum dcrypt_key_type dcrypt_key_type_public(struct dcrypt_public_key *key); -/* return digest of key */ -bool dcrypt_key_id_public(struct dcrypt_public_key *key, const char *algorithm, - buffer_t *result, const char **error_r); -/* return SHA1 sum of key */ -bool dcrypt_key_id_public_old(struct dcrypt_public_key *key, buffer_t *result, - const char **error_r); -/* return digest of key */ -bool dcrypt_key_id_private(struct dcrypt_private_key *key, - const char *algorithm, buffer_t *result, - const char **error_r); -/* return SHA1 sum of key */ -bool dcrypt_key_id_private_old(struct dcrypt_private_key *key, - buffer_t *result, const char **error_r); - -/* return raw private key: - Only ECC supported currently - - returns OID bytes and private key in bigendian bytes -*/ -bool dcrypt_key_store_private_raw(struct dcrypt_private_key *key, - pool_t pool, - enum dcrypt_key_type *key_type_r, - ARRAY_TYPE(dcrypt_raw_key) *keys_r, - const char **error_r); - -/* return raw public key - Only ECC supported currently - - returns OID bytes and public key in compressed form (z||x) -*/ -bool dcrypt_key_store_public_raw(struct dcrypt_public_key *key, - pool_t pool, - enum dcrypt_key_type *key_type_r, - ARRAY_TYPE(dcrypt_raw_key) *keys_r, - const char **error_r); - -/* load raw private key: - Only ECC supported currently - - expects OID bytes and private key in bigendian bytes -*/ -bool dcrypt_key_load_private_raw(struct dcrypt_private_key **key_r, - enum dcrypt_key_type key_type, - const ARRAY_TYPE(dcrypt_raw_key) *keys, - const char **error_r); - -/* load raw public key - Only ECC supported currently - - expects OID bytes and public key bytes. -*/ -bool dcrypt_key_load_public_raw(struct dcrypt_public_key **key_r, - enum dcrypt_key_type key_type, - const ARRAY_TYPE(dcrypt_raw_key) *keys, - const char **error_r); - -/* for ECC only - return textual name or OID of used curve */ -bool dcrypt_key_get_curve_public(struct dcrypt_public_key *key, - const char **curve_r, const char **error_r); - -bool dcrypt_key_string_get_info(const char *key_data, - enum dcrypt_key_format *format_r, - enum dcrypt_key_version *version_r, - enum dcrypt_key_kind *kind_r, - enum dcrypt_key_encryption_type *encryption_type_r, - const char **encryption_key_hash_r, - const char **key_hash_r, const char **error_r); - -/* Get/Set key identifier, this is optional opaque string identifying the key. */ -const char *dcrypt_key_get_id_public(struct dcrypt_public_key *key); -const char *dcrypt_key_get_id_private(struct dcrypt_private_key *key); -void dcrypt_key_set_id_public(struct dcrypt_public_key *key, const char *id); -void dcrypt_key_set_id_private(struct dcrypt_private_key *key, const char *id); - -/* Get/Set key usage, optional. Defaults to NONE */ -enum dcrypt_key_usage dcrypt_key_get_usage_public(struct dcrypt_public_key *key); -enum dcrypt_key_usage dcrypt_key_get_usage_private(struct dcrypt_private_key *key); -void dcrypt_key_set_usage_public(struct dcrypt_public_key *key, - enum dcrypt_key_usage usage); -void dcrypt_key_set_usage_private(struct dcrypt_private_key *key, - enum dcrypt_key_usage usage); - -/* RSA stuff */ -bool dcrypt_rsa_encrypt(struct dcrypt_public_key *key, - const unsigned char *data, size_t data_len, - buffer_t *result, enum dcrypt_padding padding, - const char **error_r); -bool dcrypt_rsa_decrypt(struct dcrypt_private_key *key, - const unsigned char *data, size_t data_len, - buffer_t *result, enum dcrypt_padding padding, - const char **error_r); - -/* OID stuff */ -const char *dcrypt_oid2name(const unsigned char *oid, size_t oid_len, - const char **error_r); -bool dcrypt_name2oid(const char *name, buffer_t *oid, const char **error_r); - -#endif diff --git a/src/lib-dcrypt/istream-decrypt.c b/src/lib-dcrypt/istream-decrypt.c deleted file mode 100644 index be550fb1b4..0000000000 --- a/src/lib-dcrypt/istream-decrypt.c +++ /dev/null @@ -1,1146 +0,0 @@ -/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "buffer.h" -#include "randgen.h" -#include "safe-memset.h" -#include "hash-method.h" -#include "sha2.h" -#include "memarea.h" -#include "dcrypt.h" -#include "istream.h" -#include "istream-decrypt.h" -#include "istream-private.h" -#include "dcrypt-iostream.h" - -#include "hex-binary.h" - -#include - -#define ISTREAM_DECRYPT_READ_FIRST 15 - -struct decrypt_istream_snapshot { - struct istream_snapshot snapshot; - struct decrypt_istream *dstream; - buffer_t *buf; -}; - -struct decrypt_istream { - struct istream_private istream; - buffer_t *buf; - bool symmetric; - - i_stream_decrypt_get_key_callback_t *key_callback; - void *key_context; - - struct dcrypt_private_key *priv_key; - bool initialized; - bool finalized; - bool use_mac; - bool snapshot_pending; - - uoff_t ftr, pos; - enum io_stream_encrypt_flags flags; - - /* original iv, in case seeking is done, future feature */ - unsigned char *iv; - - struct dcrypt_context_symmetric *ctx_sym; - struct dcrypt_context_hmac *ctx_mac; - - enum decrypt_istream_format format; -}; - -static void i_stream_decrypt_reset(struct decrypt_istream *dstream) -{ - dstream->finalized = FALSE; - dstream->use_mac = FALSE; - - dstream->ftr = 0; - dstream->pos = 0; - dstream->flags = 0; - - if (!dstream->symmetric) { - dstream->initialized = FALSE; - if (dstream->ctx_sym != NULL) - dcrypt_ctx_sym_destroy(&dstream->ctx_sym); - if (dstream->ctx_mac != NULL) - dcrypt_ctx_hmac_destroy(&dstream->ctx_mac); - } - i_free(dstream->iv); - dstream->format = DECRYPT_FORMAT_V1; -} - -enum decrypt_istream_format -i_stream_encrypt_get_format(const struct istream *input) -{ - return ((const struct decrypt_istream*)input->real_stream)->format; -} - -enum io_stream_encrypt_flags -i_stream_encrypt_get_flags(const struct istream *input) -{ - return ((const struct decrypt_istream*)input->real_stream)->flags; -} - -static ssize_t -i_stream_decrypt_read_header_v1(struct decrypt_istream *stream, - const unsigned char *data, size_t mlen) -{ - const char *error = NULL; - size_t keydata_len = 0; - uint16_t len; - int ec, i = 0; - - const unsigned char *digest_pos = NULL, *key_digest_pos = NULL, - *key_ct_pos = NULL; - size_t digest_len = 0, key_ct_len = 0, key_digest_size = 0; - - buffer_t ephemeral_key; - buffer_t *secret = t_buffer_create(256); - buffer_t *key = t_buffer_create(256); - - if (mlen < 2) - return 0; - keydata_len = be16_to_cpu_unaligned(data); - if (mlen-2 < keydata_len) { - /* try to read more */ - return 0; - } - - data+=2; - mlen-=2; - - while (i < 4 && mlen > 2) { - memcpy(&len, data, 2); - len = ntohs(len); - if (len == 0 || len > mlen-2) - break; - data += 2; - mlen -= 2; - - switch(i++) { - case 0: - buffer_create_from_const_data(&ephemeral_key, - data, len); - break; - case 1: - /* public key id */ - digest_pos = data; - digest_len = len; - break; - case 2: - /* encryption key digest */ - key_digest_pos = data; - key_digest_size = len; - break; - case 3: - /* encrypted key data */ - key_ct_pos = data; - key_ct_len = len; - break; - } - data += len; - mlen -= len; - } - - if (i < 4) { - io_stream_set_error(&stream->istream.iostream, - "Invalid or corrupted header"); - /* was it consumed? */ - stream->istream.istream.stream_errno = - mlen > 2 ? EINVAL : EPIPE; - return -1; - } - - /* we don't have a private key */ - if (stream->priv_key == NULL) { - /* see if we can get one */ - if (stream->key_callback != NULL) { - const char *key_id = - binary_to_hex(digest_pos, digest_len); - int ret = stream->key_callback(key_id, - &stream->priv_key, &error, stream->key_context); - if (ret < 0) { - io_stream_set_error(&stream->istream.iostream, - "Private key not available: %s", - error); - return -1; - } - if (ret == 0) { - io_stream_set_error(&stream->istream.iostream, - "Private key not available"); - return -1; - } - } else { - io_stream_set_error(&stream->istream.iostream, - "Private key not available"); - return -1; - } - } - - buffer_t *check = t_buffer_create(32); - - if (!dcrypt_key_id_private_old(stream->priv_key, check, &error)) { - io_stream_set_error(&stream->istream.iostream, - "Cannot get public key hash: %s", error); - return -1; - } else { - if (memcmp(digest_pos, check->data, - I_MIN(digest_len,check->used)) != 0) { - io_stream_set_error(&stream->istream.iostream, - "Private key not available"); - return -1; - } - } - - /* derive shared secret */ - if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, - &ephemeral_key, secret, &error)) { - io_stream_set_error(&stream->istream.iostream, - "Cannot perform ECDH: %s", error); - return -1; - } - - /* run it thru SHA256 once */ - const struct hash_method *hash = &hash_method_sha256; - unsigned char hctx[hash->context_size]; - unsigned char hres[hash->digest_size]; - hash->init(hctx); - hash->loop(hctx, secret->data, secret->used); - hash->result(hctx, hres); - safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); - - /* NB! The old code was broken and used this kind of IV - it is not - correct, but we need to stay compatible with old data */ - - /* use it to decrypt the actual encryption key */ - struct dcrypt_context_symmetric *dctx; - if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, - &dctx, &error)) { - io_stream_set_error(&stream->istream.iostream, - "Key decryption error: %s", error); - return -1; - } - - ec = 0; - dcrypt_ctx_sym_set_iv(dctx, (const unsigned char*) - "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16); - dcrypt_ctx_sym_set_key(dctx, hres, hash->digest_size); - if (!dcrypt_ctx_sym_init(dctx, &error) || - !dcrypt_ctx_sym_update(dctx, key_ct_pos, key_ct_len, key, &error) || - !dcrypt_ctx_sym_final(dctx, key, &error)) { - io_stream_set_error(&stream->istream.iostream, - "Key decryption error: %s", error); - ec = -1; - } - dcrypt_ctx_sym_destroy(&dctx); - - if (ec != 0) { - io_stream_set_error(&stream->istream.iostream, - "Key decryption error: %s", error); - return -1; - } - - /* see if we got the correct key */ - hash->init(hctx); - hash->loop(hctx, key->data, key->used); - hash->result(hctx, hres); - - if (key_digest_size != sizeof(hres)) { - io_stream_set_error(&stream->istream.iostream, - "Key decryption error: " - "invalid digest length"); - return -1; - } - if (memcmp(hres, key_digest_pos, sizeof(hres)) != 0) { - io_stream_set_error(&stream->istream.iostream, - "Key decryption error: " - "decrypted key is invalid"); - return -1; - } - - /* prime context with key */ - if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, - &stream->ctx_sym, &error)) { - io_stream_set_error(&stream->istream.iostream, - "Decryption context create error: %s", - error); - return -1; - } - - /* Again, old code used this IV, so we have to use it too */ - dcrypt_ctx_sym_set_iv(stream->ctx_sym, (const unsigned char*) - "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16); - dcrypt_ctx_sym_set_key(stream->ctx_sym, key->data, key->used); - - safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); - - if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { - io_stream_set_error(&stream->istream.iostream, - "Decryption init error: %s", error); - return -1; - } - - stream->use_mac = FALSE; - stream->initialized = TRUE; - /* now we are ready to decrypt stream */ - - return sizeof(IOSTREAM_CRYPT_MAGIC) + 1 + 2 + keydata_len; -} - -static bool -get_msb32(const unsigned char **_data, const unsigned char *end, - uint32_t *num_r) -{ - const unsigned char *data = *_data; - if (end-data < 4) - return FALSE; - *num_r = be32_to_cpu_unaligned(data); - *_data += 4; - return TRUE; -} - -static bool -i_stream_decrypt_der(const unsigned char **_data, const unsigned char *end, - const char **str_r) -{ - const unsigned char *data = *_data; - unsigned int len; - - if (end-data < 2) - return FALSE; - /* get us DER encoded length */ - if ((data[1] & 0x80) != 0) { - /* two byte length */ - if (end-data < 3) - return FALSE; - len = ((data[1] & 0x7f) << 8) + data[2] + 3; - } else { - len = data[1] + 2; - } - if ((size_t)(end-data) < len) - return FALSE; - *str_r = dcrypt_oid2name(data, len, NULL); - *_data += len; - return TRUE; -} - -static ssize_t -i_stream_decrypt_key(struct decrypt_istream *stream, const char *malg, - unsigned int rounds, const unsigned char *data, - const unsigned char *end, buffer_t *key, size_t key_len) -{ - const char *error; - enum dcrypt_key_type ktype; - int keys; - bool have_key = FALSE; - unsigned char dgst[32]; - uint32_t val; - buffer_t buf; - - if (data == end) - return 0; - - keys = *data++; - - /* if we have a key, prefab the digest */ - if (stream->priv_key != NULL) { - buffer_create_from_data(&buf, dgst, sizeof(dgst)); - if (!dcrypt_key_id_private(stream->priv_key, "sha256", &buf, - &error)) { - io_stream_set_error(&stream->istream.iostream, - "Decryption error: " - "dcrypt_key_id_private failed: %s", - error); - return -1; - } - } else if (stream->key_callback == NULL) { - io_stream_set_error(&stream->istream.iostream, - "Decryption error: " - "no private key available"); - return -1; - } - - /* for each key */ - for(;keys>0;keys--) { - if ((size_t)(end-data) < 1 + (ssize_t)sizeof(dgst)) - return 0; - ktype = *data++; - - if (stream->priv_key != NULL) { - /* see if key matches to the one we have */ - if (memcmp(dgst, data, sizeof(dgst)) == 0) { - have_key = TRUE; - break; - } - } else if (stream->key_callback != NULL) { - const char *hexdgst = /* digest length */ - binary_to_hex(data, sizeof(dgst)); - if (stream->priv_key != NULL) - dcrypt_key_unref_private(&stream->priv_key); - /* hope you going to give us right key.. */ - int ret = stream->key_callback(hexdgst, - &stream->priv_key, &error, stream->key_context); - if (ret < 0) { - io_stream_set_error(&stream->istream.iostream, - "Private key not available: " - "%s", error); - return -1; - } - if (ret > 0) { - have_key = TRUE; - break; - } - } - data += sizeof(dgst); - - /* wasn't correct key, skip over some data */ - if (!get_msb32(&data, end, &val) || - !get_msb32(&data, end, &val)) - return 0; - } - - /* didn't find matching key */ - if (!have_key) { - io_stream_set_error(&stream->istream.iostream, - "Decryption error: " - "no private key available"); - return -1; - } - - data += sizeof(dgst); - - const unsigned char *ephemeral_key; - uint32_t ep_key_len; - const unsigned char *encrypted_key; - uint32_t eklen; - const unsigned char *ekhash; - uint32_t ekhash_len; - - /* read ephemeral key (can be missing for RSA) */ - if (!get_msb32(&data, end, &ep_key_len) || - (size_t)(end-data) < ep_key_len) - return 0; - ephemeral_key = data; - data += ep_key_len; - - /* read encrypted key */ - if (!get_msb32(&data, end, &eklen) || (size_t)(end-data) < eklen) - return 0; - encrypted_key = data; - data += eklen; - - /* read key data hash */ - if (!get_msb32(&data, end, &ekhash_len) || - (size_t)(end-data) < ekhash_len) - return 0; - ekhash = data; - data += ekhash_len; - - /* decrypt the seed */ - if (ktype == DCRYPT_KEY_RSA) { - if (!dcrypt_rsa_decrypt(stream->priv_key, encrypted_key, eklen, - key, DCRYPT_PADDING_RSA_PKCS1_OAEP, - &error)) { - io_stream_set_error(&stream->istream.iostream, - "key decryption error: %s", error); - return -1; - } - } else if (ktype == DCRYPT_KEY_EC) { - /* perform ECDHE */ - buffer_t *temp_key = t_buffer_create(256); - buffer_t *secret = t_buffer_create(256); - buffer_t peer_key; - buffer_create_from_const_data(&peer_key, - ephemeral_key, ep_key_len); - if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, - &peer_key, secret, &error)) { - io_stream_set_error(&stream->istream.iostream, - "Key decryption error: corrupted header"); - return -1; - } - - /* use shared secret and peer key to generate decryption key, - AES-256-CBC has 32 byte key and 16 byte IV */ - if (!dcrypt_pbkdf2(secret->data, secret->used, - peer_key.data, peer_key.used, - malg, rounds, temp_key, 32+16, &error)) { - safe_memset(buffer_get_modifiable_data(secret, 0), - 0, secret->used); - io_stream_set_error(&stream->istream.iostream, - "Key decryption error: %s", error); - return -1; - } - - safe_memset(buffer_get_modifiable_data(secret, 0), - 0, secret->used); - if (temp_key->used != 32+16) { - safe_memset(buffer_get_modifiable_data(temp_key, 0), - 0, temp_key->used); - io_stream_set_error(&stream->istream.iostream, - "Cannot perform key decryption: " - "invalid temporary key"); - return -1; - } - struct dcrypt_context_symmetric *dctx; - if (!dcrypt_ctx_sym_create("AES-256-CBC", DCRYPT_MODE_DECRYPT, - &dctx, &error)) { - safe_memset(buffer_get_modifiable_data(temp_key, 0), - 0, temp_key->used); - io_stream_set_error(&stream->istream.iostream, - "Key decryption error: %s", error); - return -1; - } - const unsigned char *ptr = temp_key->data; - - /* we use ephemeral_key for IV */ - dcrypt_ctx_sym_set_key(dctx, ptr, 32); - dcrypt_ctx_sym_set_iv(dctx, ptr+32, 16); - safe_memset(buffer_get_modifiable_data(temp_key, 0), - 0, temp_key->used); - - int ec = 0; - if (!dcrypt_ctx_sym_init(dctx, &error) || - !dcrypt_ctx_sym_update(dctx, encrypted_key, eklen, - key, &error) || - !dcrypt_ctx_sym_final(dctx, key, &error)) { - io_stream_set_error(&stream->istream.iostream, - "Cannot perform key decryption: %s", - error); - ec = -1; - } - - if (key->used != key_len) { - io_stream_set_error(&stream->istream.iostream, - "Cannot perform key decryption: " - "invalid key length"); - ec = -1; - } - - dcrypt_ctx_sym_destroy(&dctx); - if (ec != 0) return ec; - } else { - io_stream_set_error(&stream->istream.iostream, - "Decryption error: " - "unsupported key type 0x%02x", ktype); - return -1; - } - - /* make sure we were able to decrypt the encrypted key correctly */ - const struct hash_method *hash = hash_method_lookup(t_str_lcase(malg)); - if (hash == NULL) { - safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); - io_stream_set_error(&stream->istream.iostream, - "Decryption error: " - "unsupported hash algorithm: %s", malg); - return -1; - } - unsigned char hctx[hash->context_size]; - unsigned char hres[hash->digest_size]; - hash->init(hctx); - hash->loop(hctx, key->data, key->used); - hash->result(hctx, hres); - - for(int i = 1; i < 2049; i++) { - uint32_t i_msb = cpu32_to_be(i); - - hash->init(hctx); - hash->loop(hctx, hres, sizeof(hres)); - hash->loop(hctx, &i_msb, sizeof(i_msb)); - hash->result(hctx, hres); - } - - /* do the comparison */ - if (memcmp(ekhash, hres, I_MIN(ekhash_len, sizeof(hres))) != 0) { - safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); - io_stream_set_error(&stream->istream.iostream, - "Decryption error: " - "corrupted header ekhash"); - return -1; - } - return 1; -} - -static int -i_stream_decrypt_header_contents(struct decrypt_istream *stream, - const unsigned char *data, size_t size) -{ - const unsigned char *end = data + size; - bool failed = FALSE; - - /* read cipher OID */ - const char *calg; - if (!i_stream_decrypt_der(&data, end, &calg)) - return 0; - if (calg == NULL || - !dcrypt_ctx_sym_create(calg, DCRYPT_MODE_DECRYPT, - &stream->ctx_sym, NULL)) { - io_stream_set_error(&stream->istream.iostream, - "Decryption error: " - "unsupported/invalid cipher: %s", calg); - return -1; - } - - /* read MAC oid (MAC is used for PBKDF2 and key data digest, too) */ - const char *malg; - if (!i_stream_decrypt_der(&data, end, &malg)) - return 0; - if (malg == NULL || !dcrypt_ctx_hmac_create(malg, &stream->ctx_mac, NULL)) { - io_stream_set_error(&stream->istream.iostream, - "Decryption error: " - "unsupported/invalid MAC algorithm: %s", - malg); - return -1; - } - - /* read rounds (for PBKDF2) */ - uint32_t rounds; - if (!get_msb32(&data, end, &rounds)) - return 0; - /* read key data length */ - uint32_t kdlen; - if (!get_msb32(&data, end, &kdlen)) - return 0; - - size_t tagsize; - - if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == - IO_STREAM_ENC_INTEGRITY_HMAC) { - tagsize = IOSTREAM_TAG_SIZE; - } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == - IO_STREAM_ENC_INTEGRITY_AEAD) { - tagsize = IOSTREAM_TAG_SIZE; - } else { - tagsize = 0; - } - - /* how much key data we should be getting */ - size_t kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + - dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize; - buffer_t *keydata = t_buffer_create(kl); - - /* try to decrypt the keydata with a private key */ - int ret; - if ((ret = i_stream_decrypt_key(stream, malg, rounds, data, - end, keydata, kl)) <= 0) - return ret; - - /* oh, it worked! */ - const unsigned char *ptr = keydata->data; - if (keydata->used != kl) { - /* but returned wrong amount of data */ - io_stream_set_error(&stream->istream.iostream, - "Key decryption error: " - "Key data length mismatch"); - return -1; - } - - /* prime contexts */ - dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, - dcrypt_ctx_sym_get_key_length(stream->ctx_sym)); - ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym); - dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, - dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); - stream->iv = i_malloc(dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); - memcpy(stream->iv, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); - ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym); - - /* based on the chosen MAC, initialize HMAC or AEAD */ - if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == - IO_STREAM_ENC_INTEGRITY_HMAC) { - const char *error; - dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize); - if (!dcrypt_ctx_hmac_init(stream->ctx_mac, &error)) { - io_stream_set_error(&stream->istream.iostream, - "MAC error: %s", error); - stream->istream.istream.stream_errno = EINVAL; - failed = TRUE; - } - stream->ftr = dcrypt_ctx_hmac_get_digest_length(stream->ctx_mac); - stream->use_mac = TRUE; - } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == - IO_STREAM_ENC_INTEGRITY_AEAD) { - dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize); - stream->ftr = tagsize; - stream->use_mac = TRUE; - } else { - stream->use_mac = FALSE; - } - /* destroy private key data */ - safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used); - buffer_set_used_size(keydata, 0); - return failed ? -1 : 1; -} - -static ssize_t -i_stream_decrypt_read_header(struct decrypt_istream *stream, - const unsigned char *data, size_t mlen) -{ - const char *error; - const unsigned char *end = data + mlen; - - /* check magic */ - if (mlen < sizeof(IOSTREAM_CRYPT_MAGIC)) - return 0; - if (memcmp(data, IOSTREAM_CRYPT_MAGIC, sizeof(IOSTREAM_CRYPT_MAGIC)) != 0) { - io_stream_set_error(&stream->istream.iostream, - "Stream is not encrypted (invalid magic)"); - stream->istream.istream.stream_errno = EINVAL; - return -1; - } - data += sizeof(IOSTREAM_CRYPT_MAGIC); - - if (data >= end) - return 0; /* read more? */ - - /* check version */ - if (*data == '\x01') { - stream->format = DECRYPT_FORMAT_V1; - return i_stream_decrypt_read_header_v1(stream, data+1, - end - (data+1)); - } else if (*data != '\x02') { - io_stream_set_error(&stream->istream.iostream, - "Unsupported encrypted data 0x%02x", *data); - return -1; - } - - stream->format = DECRYPT_FORMAT_V2; - - data++; - - /* read flags */ - uint32_t flags; - if (!get_msb32(&data, end, &flags)) - return 0; - stream->flags = flags; - - /* get the total length of header */ - uint32_t hdr_len; - if (!get_msb32(&data, end, &hdr_len)) - return 0; - /* do not forget stream format */ - if ((size_t)(end-data)+1 < hdr_len) - return 0; - - int ret; - if ((ret = i_stream_decrypt_header_contents(stream, data, hdr_len)) < 0) - return -1; - else if (ret == 0) { - io_stream_set_error(&stream->istream.iostream, - "Decryption error: truncate header length"); - stream->istream.istream.stream_errno = EPIPE; - return -1; - } - stream->initialized = TRUE; - - /* if it all went well, try to initialize decryption context */ - if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { - io_stream_set_error(&stream->istream.iostream, - "Decryption init error: %s", error); - return -1; - } - return hdr_len; -} - -static void -i_stream_decrypt_realloc_buf_if_needed(struct decrypt_istream *dstream) -{ - if (!dstream->snapshot_pending) - return; - - /* buf exists in a snapshot. Leave it be and create a copy of it - that we modify. */ - buffer_t *old_buf = dstream->buf; - dstream->buf = buffer_create_dynamic(default_pool, - I_MAX(512, old_buf->used)); - buffer_append(dstream->buf, old_buf->data, old_buf->used); - dstream->snapshot_pending = FALSE; - - dstream->istream.buffer = dstream->buf->data; -} - -static ssize_t -i_stream_decrypt_read(struct istream_private *stream) -{ - struct decrypt_istream *dstream = - (struct decrypt_istream *)stream; - const unsigned char *data; - size_t size, decrypt_size; - const char *error = NULL; - int ret; - bool check_mac = FALSE; - - /* not if it's broken */ - if (stream->istream.stream_errno != 0) - return -1; - - i_stream_decrypt_realloc_buf_if_needed(dstream); - for (;;) { - /* remove skipped data from buffer */ - if (stream->skip > 0) { - i_assert(stream->skip <= dstream->buf->used); - buffer_delete(dstream->buf, 0, stream->skip); - stream->pos -= stream->skip; - stream->skip = 0; - } - - stream->buffer = dstream->buf->data; - - i_assert(stream->pos <= dstream->buf->used); - if (stream->pos >= dstream->istream.max_buffer_size) { - /* stream buffer still at maximum */ - return -2; - } - - /* if something is already decrypted, return as much of it as - we can */ - if (dstream->initialized && dstream->buf->used > 0) { - size_t new_pos, bytes; - - /* only return up to max_buffer_size bytes, even when - buffer actually has more, as not to confuse the - caller */ - if (dstream->buf->used <= - dstream->istream.max_buffer_size) { - new_pos = dstream->buf->used; - if (dstream->finalized) - stream->istream.eof = TRUE; - } else { - new_pos = dstream->istream.max_buffer_size; - } - - bytes = new_pos - stream->pos; - stream->pos = new_pos; - if (bytes > 0) - return (ssize_t)bytes; - } - if (dstream->finalized) { - /* all data decrypted */ - stream->istream.eof = TRUE; - return -1; - } - - /* need to read more input */ - ret = i_stream_read_memarea(stream->parent); - if (ret == 0) - return ret; - - data = i_stream_get_data(stream->parent, &size); - - if (ret == -1 && - (size == 0 || stream->parent->stream_errno != 0)) { - stream->istream.stream_errno = - stream->parent->stream_errno; - - /* file was empty */ - if (!dstream->initialized && - size == 0 && stream->parent->eof) { - stream->istream.eof = TRUE; - return -1; - } - - if (stream->istream.stream_errno != 0) - return -1; - - if (!dstream->initialized) { - io_stream_set_error(&stream->iostream, - "Decryption error: %s", - "Input truncated in decryption header"); - stream->istream.stream_errno = EPIPE; - return -1; - } - - /* final block */ - if (dcrypt_ctx_sym_final(dstream->ctx_sym, - dstream->buf, &error)) { - dstream->finalized = TRUE; - continue; - } - io_stream_set_error(&stream->iostream, - "MAC error: %s", error); - stream->istream.stream_errno = EINVAL; - return -1; - } - - if (!dstream->initialized) { - ssize_t hret; - - if ((hret=i_stream_decrypt_read_header( - dstream, data, size)) <= 0) { - if (hret < 0) { - if (stream->istream.stream_errno == 0) - /* assume temporary failure */ - stream->istream.stream_errno = EIO; - return -1; - } - - if (hret == 0 && stream->parent->eof) { - /* not encrypted by us */ - stream->istream.stream_errno = EPIPE; - io_stream_set_error(&stream->iostream, - "Truncated header"); - return -1; - } - } - - if (hret == 0) { - /* see if we can get more data */ - if (ret == -2) { - stream->istream.stream_errno = EINVAL; - io_stream_set_error(&stream->iostream, - "Header too large " - "(more than %zu bytes)", - size); - return -1; - } - continue; - } else { - /* clean up buffer */ - safe_memset(buffer_get_modifiable_data(dstream->buf, 0), - 0, dstream->buf->used); - buffer_set_used_size(dstream->buf, 0); - i_stream_skip(stream->parent, hret); - } - - data = i_stream_get_data(stream->parent, &size); - } - decrypt_size = size; - - if (dstream->use_mac) { - if (stream->parent->eof) { - if (decrypt_size < dstream->ftr) { - io_stream_set_error(&stream->iostream, - "Decryption error: " - "footer is longer than data"); - stream->istream.stream_errno = EINVAL; - return -1; - } - check_mac = TRUE; - } else { - /* ignore footer's length of data until we - reach EOF */ - size -= dstream->ftr; - } - decrypt_size -= dstream->ftr; - if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == - IO_STREAM_ENC_INTEGRITY_HMAC) { - if (!dcrypt_ctx_hmac_update(dstream->ctx_mac, - data, decrypt_size, &error)) { - io_stream_set_error(&stream->iostream, - "MAC error: %s", error); - stream->istream.stream_errno = EINVAL; - return -1; - } - } - } - - if (check_mac) { - if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == - IO_STREAM_ENC_INTEGRITY_HMAC) { - unsigned char dgst[dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)]; - buffer_t db; - buffer_create_from_data(&db, dgst, sizeof(dgst)); - if (!dcrypt_ctx_hmac_final(dstream->ctx_mac, &db, &error)) { - io_stream_set_error(&stream->iostream, - "Cannot verify MAC: %s", error); - stream->istream.stream_errno = EINVAL; - return -1; - } - if (memcmp(dgst, data + decrypt_size, - dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)) != 0) { - io_stream_set_error(&stream->iostream, - "Cannot verify MAC: mismatch"); - stream->istream.stream_errno = EINVAL; - return -1; - } - } else if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == - IO_STREAM_ENC_INTEGRITY_AEAD) { - dcrypt_ctx_sym_set_tag(dstream->ctx_sym, - data + decrypt_size, - dstream->ftr); - } - } - - if (!dcrypt_ctx_sym_update(dstream->ctx_sym, - data, decrypt_size, dstream->buf, &error)) { - io_stream_set_error(&stream->iostream, - "Decryption error: %s", error); - stream->istream.stream_errno = EINVAL; - return -1; - } - i_stream_skip(stream->parent, size); - } -} - -static void -i_stream_decrypt_seek(struct istream_private *stream, uoff_t v_offset, - bool mark ATTR_UNUSED) -{ - struct decrypt_istream *dstream = - (struct decrypt_istream *)stream; - - i_stream_decrypt_realloc_buf_if_needed(dstream); - - if (i_stream_nonseekable_try_seek(stream, v_offset)) - return; - - /* have to seek backwards - reset crypt state and retry */ - i_stream_decrypt_reset(dstream); - if (!i_stream_nonseekable_try_seek(stream, v_offset)) - i_unreached(); -} - -static void i_stream_decrypt_close(struct iostream_private *stream, - bool close_parent) -{ - struct decrypt_istream *dstream = - (struct decrypt_istream *)stream; - - if (close_parent) - i_stream_close(dstream->istream.parent); -} - -static void -i_stream_decrypt_snapshot_free(struct istream_snapshot *_snapshot) -{ - struct decrypt_istream_snapshot *snapshot = - container_of(_snapshot, struct decrypt_istream_snapshot, - snapshot); - - if (snapshot->dstream->buf != snapshot->buf) - buffer_free(&snapshot->buf); - else { - i_assert(snapshot->dstream->snapshot_pending); - snapshot->dstream->snapshot_pending = FALSE; - } - i_free(snapshot); -} - -static struct istream_snapshot * -i_stream_decrypt_snapshot(struct istream_private *stream, - struct istream_snapshot *prev_snapshot) -{ - struct decrypt_istream *dstream = - (struct decrypt_istream *)stream; - struct decrypt_istream_snapshot *snapshot; - - if (stream->buffer != dstream->buf->data) { - /* reading body */ - return i_stream_default_snapshot(stream, prev_snapshot); - } - - /* snapshot the header buffer */ - snapshot = i_new(struct decrypt_istream_snapshot, 1); - snapshot->dstream = dstream; - snapshot->buf = dstream->buf; - snapshot->snapshot.free = i_stream_decrypt_snapshot_free; - snapshot->snapshot.prev_snapshot = prev_snapshot; - dstream->snapshot_pending = TRUE; - return &snapshot->snapshot; -} - -static void i_stream_decrypt_destroy(struct iostream_private *stream) -{ - struct decrypt_istream *dstream = - (struct decrypt_istream *)stream; - - if (!dstream->snapshot_pending) - buffer_free(&dstream->buf); - else { - /* Clear buf to make sure i_stream_decrypt_snapshot_free() - frees it. */ - dstream->buf = NULL; - } - - if (dstream->iv != NULL) - i_free_and_null(dstream->iv); - if (dstream->ctx_sym != NULL) - dcrypt_ctx_sym_destroy(&dstream->ctx_sym); - if (dstream->ctx_mac != NULL) - dcrypt_ctx_hmac_destroy(&dstream->ctx_mac); - if (dstream->priv_key != NULL) - dcrypt_key_unref_private(&dstream->priv_key); - - i_stream_unref(&dstream->istream.parent); -} - -static struct decrypt_istream * -i_stream_create_decrypt_common(struct istream *input) -{ - struct decrypt_istream *dstream; - - i_assert(input->real_stream->max_buffer_size > 0); - - dstream = i_new(struct decrypt_istream, 1); - dstream->istream.max_buffer_size = input->real_stream->max_buffer_size; - dstream->istream.read = i_stream_decrypt_read; - dstream->istream.snapshot = i_stream_decrypt_snapshot; - if (input->seekable) - dstream->istream.seek = i_stream_decrypt_seek; - dstream->istream.iostream.close = i_stream_decrypt_close; - dstream->istream.iostream.destroy = i_stream_decrypt_destroy; - - dstream->istream.istream.readable_fd = FALSE; - dstream->istream.istream.blocking = input->blocking; - dstream->istream.istream.seekable = input->seekable; - - dstream->buf = buffer_create_dynamic(default_pool, 512); - - (void)i_stream_create(&dstream->istream, input, - i_stream_get_fd(input), 0); - return dstream; -} - -struct istream * -i_stream_create_decrypt(struct istream *input, - struct dcrypt_private_key *priv_key) -{ - struct decrypt_istream *dstream; - - dstream = i_stream_create_decrypt_common(input); - dcrypt_key_ref_private(priv_key); - dstream->priv_key = priv_key; - return &dstream->istream.istream; -} - -struct istream * -i_stream_create_sym_decrypt(struct istream *input, - struct dcrypt_context_symmetric *ctx) -{ - const char *error; - int ec; - struct decrypt_istream *dstream; - dstream = i_stream_create_decrypt_common(input); - dstream->use_mac = FALSE; - dstream->initialized = TRUE; - dstream->symmetric = TRUE; - - if (!dcrypt_ctx_sym_init(ctx, &error)) ec = -1; - else ec = 0; - - dstream->ctx_sym = ctx; - - if (ec != 0) { - io_stream_set_error(&dstream->istream.iostream, - "Cannot initialize decryption: %s", error); - dstream->istream.istream.stream_errno = EIO; - }; - - return &dstream->istream.istream; -} - -struct istream * -i_stream_create_decrypt_callback(struct istream *input, - i_stream_decrypt_get_key_callback_t *callback, - void *context) -{ - struct decrypt_istream *dstream; - - i_assert(callback != NULL); - - dstream = i_stream_create_decrypt_common(input); - dstream->key_callback = callback; - dstream->key_context = context; - return &dstream->istream.istream; -} diff --git a/src/lib-dcrypt/istream-decrypt.h b/src/lib-dcrypt/istream-decrypt.h deleted file mode 100644 index d531c004b8..0000000000 --- a/src/lib-dcrypt/istream-decrypt.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef ISTREAM_DECRYPT_H -#define ISTREAM_DECRYPT_H - -struct dcrypt_private_key; -struct dcrypt_context_symmetric; - -enum decrypt_istream_format { - DECRYPT_FORMAT_V1, - DECRYPT_FORMAT_V2 -}; - -/* Look for a private key for a specified public key digest and set it to - priv_key_r. Returns 1 if ok, 0 if key doesn't exist, -1 on internal error. - - Note that the private key will be unreferenced when the istream is - destroyed. If the callback is returning a persistent key, it must reference - the key first. (This is required, because otherwise a key newly created by - the callback couldn't be automatically freed.) */ -typedef int -i_stream_decrypt_get_key_callback_t(const char *pubkey_digest, - struct dcrypt_private_key **priv_key_r, - const char **error_r, void *context); - -struct istream * -i_stream_create_decrypt(struct istream *input, - struct dcrypt_private_key *priv_key); - -/* create stream for reading plain encrypted data with no header or MAC. - do not call dcrypt_ctx_sym_init - */ -struct istream * -i_stream_create_sym_decrypt(struct istream *input, - struct dcrypt_context_symmetric *ctx); - - -/* Decrypt the istream. When a private key is needed, the callback will be - called. This allows using multiple private keys for different mails. */ -struct istream * -i_stream_create_decrypt_callback(struct istream *input, - i_stream_decrypt_get_key_callback_t *callback, - void *context); - -enum decrypt_istream_format -i_stream_encrypt_get_format(const struct istream *input); -enum io_stream_encrypt_flags -i_stream_encrypt_get_flags(const struct istream *input); - -#endif diff --git a/src/lib-dcrypt/ostream-encrypt.c b/src/lib-dcrypt/ostream-encrypt.c deleted file mode 100644 index c6e67f51e5..0000000000 --- a/src/lib-dcrypt/ostream-encrypt.c +++ /dev/null @@ -1,800 +0,0 @@ -/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "buffer.h" -#include "randgen.h" -#include "dcrypt-iostream.h" -#include "ostream-encrypt.h" -#include "ostream-private.h" -#include "hash-method.h" -#include "sha2.h" -#include "safe-memset.h" -#include "dcrypt.h" - -#include - -/* file struct dcrypt_public_key syntax - * magic (14 bytes) - * version (1 bytes) - * flags (4 bytes) - * size of header (4 bytes) - * sha1 of key id (20 bytes) - * cipher oid - * mac oid - * rounds (4 bytes) - * key data size (4 bytes) - * key data - * cipher data - * mac data (mac specific bytes) - */ - -#define IO_STREAM_ENCRYPT_SEED_SIZE 32 -#define IO_STREAM_ENCRYPT_ROUNDS 2048 - -struct encrypt_ostream { - struct ostream_private ostream; - - struct dcrypt_context_symmetric *ctx_sym; - struct dcrypt_context_hmac *ctx_mac; - - enum io_stream_encrypt_flags flags; - struct dcrypt_public_key *pub; - - unsigned char *key_data; - size_t key_data_len; - - buffer_t *cipher_oid; - buffer_t *mac_oid; - size_t block_size; - - bool finalized; - bool failed; - bool prefix_written; -}; - -static int -o_stream_encrypt_send(struct encrypt_ostream *stream, - const unsigned char *data, size_t size) -{ - ssize_t ec; - - ec = o_stream_send(stream->ostream.parent, data, size); - if (ec == (ssize_t)size) - return 0; - else if (ec < 0) { - o_stream_copy_error_from_parent(&stream->ostream); - return -1; - } else { - io_stream_set_error(&stream->ostream.iostream, - "ostream-encrypt: " - "Unexpectedly short write to parent stream"); - stream->ostream.ostream.stream_errno = EINVAL; - return -1; - } -} - -static int -o_stream_encrypt_send_header_v1(struct encrypt_ostream *stream) -{ - unsigned char c; - unsigned short s; - - i_assert(!stream->prefix_written); - stream->prefix_written = TRUE; - - buffer_t *values = t_buffer_create(256); - buffer_append(values, IOSTREAM_CRYPT_MAGIC, - sizeof(IOSTREAM_CRYPT_MAGIC)); - /* version */ - c = 1; - buffer_append(values, &c, 1); - /* key data length */ - s = htons(stream->key_data_len); - buffer_append(values, &s, 2); - /* then write key data */ - buffer_append(values, stream->key_data, stream->key_data_len); - i_free_and_null(stream->key_data); - - /* then send it to stream */ - return o_stream_encrypt_send(stream, values->data, values->used); -} - -static int -o_stream_encrypt_send_header_v2(struct encrypt_ostream *stream) -{ - unsigned char c; - unsigned int i; - - i_assert(!stream->prefix_written); - stream->prefix_written = TRUE; - - buffer_t *values = t_buffer_create(256); - buffer_append(values, IOSTREAM_CRYPT_MAGIC, - sizeof(IOSTREAM_CRYPT_MAGIC)); - c = 2; - buffer_append(values, &c, 1); - i = cpu32_to_be(stream->flags); - buffer_append(values, &i, 4); - /* store total length of header - 9 = version + flags + length - 8 = rounds + key data length - */ - i = cpu32_to_be(sizeof(IOSTREAM_CRYPT_MAGIC) + 9 + - stream->cipher_oid->used + stream->mac_oid->used + - 8 + stream->key_data_len); - buffer_append(values, &i, 4); - - buffer_append_buf(values, stream->cipher_oid, 0, SIZE_MAX); - buffer_append_buf(values, stream->mac_oid, 0, SIZE_MAX); - i = cpu32_to_be(IO_STREAM_ENCRYPT_ROUNDS); - buffer_append(values, &i, 4); - i = cpu32_to_be(stream->key_data_len); - buffer_append(values, &i, 4); - buffer_append(values, stream->key_data, stream->key_data_len); - i_free_and_null(stream->key_data); - - return o_stream_encrypt_send(stream, values->data, values->used); -} - -static int -o_stream_encrypt_keydata_create_v1(struct encrypt_ostream *stream) -{ - buffer_t *encrypted_key, *ephemeral_key, *secret, *res, buf; - const char *error = NULL; - const struct hash_method *hash = &hash_method_sha256; - - /* various temporary buffers */ - unsigned char seed[IO_STREAM_ENCRYPT_SEED_SIZE]; - unsigned char pkhash[hash->digest_size]; - unsigned char ekhash[hash->digest_size]; - unsigned char hres[hash->digest_size]; - - unsigned char hctx[hash->context_size]; - - /* hash the public key first */ - buffer_create_from_data(&buf, pkhash, sizeof(pkhash)); - if (!dcrypt_key_id_public_old(stream->pub, &buf, &error)) { - io_stream_set_error(&stream->ostream.iostream, - "Key hash failed: %s", error); - return -1; - } - - /* hash the key base */ - hash->init(hctx); - hash->loop(hctx, seed, sizeof(seed)); - hash->result(hctx, ekhash); - - ephemeral_key = t_buffer_create(256); - encrypted_key = t_buffer_create(256); - secret = t_buffer_create(256); - - if (!dcrypt_ecdh_derive_secret_peer(stream->pub, ephemeral_key, - secret, &error)) { - io_stream_set_error(&stream->ostream.iostream, - "Cannot perform ECDH: %s", error); - return -1; - } - - /* hash the secret data */ - hash->init(hctx); - hash->loop(hctx, secret->data, secret->used); - hash->result(hctx, hres); - safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); - - /* use it to encrypt the actual encryption key */ - struct dcrypt_context_symmetric *dctx; - if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_ENCRYPT, - &dctx, &error)) { - io_stream_set_error(&stream->ostream.iostream, - "Key encryption error: %s", error); - return -1; - } - - random_fill(seed, sizeof(seed)); - hash->init(hctx); - hash->loop(hctx, seed, sizeof(seed)); - hash->result(hctx, ekhash); - - int ec = 0; - - /* NB! The old code was broken and used this kind of IV - it is not - correct, but we need to stay compatible with old data */ - dcrypt_ctx_sym_set_iv(dctx, (const unsigned char*) - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", 16); - dcrypt_ctx_sym_set_key(dctx, hres, sizeof(hres)); - - if (!dcrypt_ctx_sym_init(dctx, &error) || - !dcrypt_ctx_sym_update(dctx, seed, sizeof(seed), - encrypted_key, &error) || - !dcrypt_ctx_sym_final(dctx, encrypted_key, &error)) { - ec = -1; - } - dcrypt_ctx_sym_destroy(&dctx); - - if (ec != 0) { - safe_memset(seed, 0, sizeof(seed)); - io_stream_set_error(&stream->ostream.iostream, - "Key encryption error: %s", error); - return -1; - } - - /* same as above */ - dcrypt_ctx_sym_set_iv(stream->ctx_sym, (const unsigned char*) - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", 16); - dcrypt_ctx_sym_set_key(stream->ctx_sym, seed, sizeof(seed)); - safe_memset(seed, 0, sizeof(seed)); - - if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { - io_stream_set_error(&stream->ostream.iostream, - "Encryption init error: %s", error); - return -1; - } - - res = buffer_create_dynamic(default_pool, 256); - - /* ephemeral key */ - unsigned short s; - s = htons(ephemeral_key->used); - buffer_append(res, &s, 2); - buffer_append(res, ephemeral_key->data, ephemeral_key->used); - /* public key hash */ - s = htons(sizeof(pkhash)); - buffer_append(res, &s, 2); - buffer_append(res, pkhash, sizeof(pkhash)); - /* encrypted key hash */ - s = htons(sizeof(ekhash)); - buffer_append(res, &s, 2); - buffer_append(res, ekhash, sizeof(ekhash)); - /* encrypted key */ - s = htons(encrypted_key->used); - buffer_append(res, &s, 2); - buffer_append(res, encrypted_key->data, encrypted_key->used); - - stream->key_data_len = res->used; - stream->key_data = buffer_free_without_data(&res); - - return 0; -} - -static int -o_stream_encrypt_key_for_pubkey_v2(struct encrypt_ostream *stream, - const char *malg, const unsigned char *key, - size_t key_len, - struct dcrypt_public_key *pubkey, - buffer_t *res) -{ - enum dcrypt_key_type ktype; - const char *error; - buffer_t *encrypted_key, *ephemeral_key, *temp_key; - - ephemeral_key = t_buffer_create(256); - encrypted_key = t_buffer_create(256); - temp_key = t_buffer_create(48); - - ktype = dcrypt_key_type_public(pubkey); - - if (ktype == DCRYPT_KEY_RSA) { - /* encrypt key as R (as we don't need DH with RSA)*/ - if (!dcrypt_rsa_encrypt(pubkey, key, key_len, encrypted_key, - DCRYPT_PADDING_RSA_PKCS1_OAEP, - &error)) { - io_stream_set_error(&stream->ostream.iostream, - "Cannot encrypt key data: %s", - error); - return -1; - } - } else if (ktype == DCRYPT_KEY_EC) { - /* R = our ephemeral public key */ - buffer_t *secret = t_buffer_create(256); - - /* derive ephemeral key and shared secret */ - if (!dcrypt_ecdh_derive_secret_peer(pubkey, ephemeral_key, - secret, &error)) { - io_stream_set_error(&stream->ostream.iostream, - "Cannot perform ECDH: %s", error); - return -1; - } - - /* use shared secret and ephemeral key to generate encryption - key/iv */ - if (!dcrypt_pbkdf2(secret->data, secret->used, - ephemeral_key->data, ephemeral_key->used, - malg, IO_STREAM_ENCRYPT_ROUNDS, temp_key, - 48, &error)) { - safe_memset(buffer_get_modifiable_data(secret, 0), - 0, secret->used); - io_stream_set_error(&stream->ostream.iostream, - "Cannot perform key encryption: %s", - error); - } - safe_memset(buffer_get_modifiable_data(secret, 0), - 0, secret->used); - - /* encrypt key with shared secret */ - struct dcrypt_context_symmetric *dctx; - if (!dcrypt_ctx_sym_create("AES-256-CBC", DCRYPT_MODE_ENCRYPT, - &dctx, &error)) { - safe_memset(buffer_get_modifiable_data(temp_key, 0), - 0, temp_key->used); - io_stream_set_error(&stream->ostream.iostream, - "Cannot perform key encryption: %s", - error); - return -1; - } - - const unsigned char *ptr = temp_key->data; - i_assert(temp_key->used == 48); - - dcrypt_ctx_sym_set_key(dctx, ptr, 32); - dcrypt_ctx_sym_set_iv(dctx, ptr+32, 16); - safe_memset(buffer_get_modifiable_data(temp_key, 0), - 0, temp_key->used); - - int ec = 0; - if (!dcrypt_ctx_sym_init(dctx, &error) || - !dcrypt_ctx_sym_update(dctx, key, key_len, - encrypted_key, &error) || - !dcrypt_ctx_sym_final(dctx, encrypted_key, &error)) { - io_stream_set_error(&stream->ostream.iostream, - "Cannot perform key encryption: %s", - error); - ec = -1; - } - - dcrypt_ctx_sym_destroy(&dctx); - if (ec != 0) return ec; - } else { - io_stream_set_error(&stream->ostream.iostream, - "Unsupported key type"); - return -1; - } - - /* store key type */ - char kt = ktype; - buffer_append(res, &kt, 1); - /* store hash of public key as ID */ - dcrypt_key_id_public(stream->pub, "sha256", res, NULL); - /* store ephemeral key (if present) */ - unsigned int val = cpu32_to_be(ephemeral_key->used); - buffer_append(res, &val, 4); - buffer_append_buf(res, ephemeral_key, 0, SIZE_MAX); - /* store encrypted key */ - val = cpu32_to_be(encrypted_key->used); - buffer_append(res, &val, 4); - buffer_append_buf(res, encrypted_key, 0, SIZE_MAX); - - return 0; -} - -static int -o_stream_encrypt_keydata_create_v2(struct encrypt_ostream *stream, - const char *malg) -{ - const struct hash_method *hash = hash_method_lookup(malg); - const char *error; - size_t tagsize; - const unsigned char *ptr; - size_t kl; - unsigned int val; - - buffer_t *keydata, *res; - - if (hash == NULL) { - io_stream_set_error(&stream->ostream.iostream, - "Encryption init error: " - "Hash algorithm '%s' not supported", malg); - return -1; - } - - /* key data length for internal use */ - if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == - IO_STREAM_ENC_INTEGRITY_HMAC) { - tagsize = IOSTREAM_TAG_SIZE; - } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == - IO_STREAM_ENC_INTEGRITY_AEAD) { - tagsize = IOSTREAM_TAG_SIZE; - } else { - /* do not include MAC */ - tagsize = 0; - } - - /* generate keydata length of random data for key/iv/mac */ - kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + - dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize; - keydata = t_buffer_create(kl); - random_fill(buffer_append_space_unsafe(keydata, kl), kl); - buffer_set_used_size(keydata, kl); - ptr = keydata->data; - - res = buffer_create_dynamic(default_pool, 256); - - /* store number of public key(s) */ - buffer_append(res, "\1", 1); /* one key for now */ - - /* we can do multiple keys at this point, but do it only once now */ - if (o_stream_encrypt_key_for_pubkey_v2(stream, malg, ptr, kl, - stream->pub, res) != 0) { - buffer_free(&res); - return -1; - } - - /* create hash of the key data */ - unsigned char hctx[hash->context_size]; - unsigned char hres[hash->digest_size]; - hash->init(hctx); - hash->loop(hctx, ptr, kl); - hash->result(hctx, hres); - - for(int i = 1; i < 2049; i++) { - uint32_t i_msb = cpu32_to_be(i); - - hash->init(hctx); - hash->loop(hctx, hres, sizeof(hres)); - hash->loop(hctx, &i_msb, sizeof(i_msb)); - hash->result(hctx, hres); - } - - /* store key data hash */ - val = cpu32_to_be(sizeof(hres)); - buffer_append(res, &val, 4); - buffer_append(res, hres, sizeof(hres)); - - /* pick up key data that goes into stream */ - stream->key_data_len = res->used; - stream->key_data = buffer_free_without_data(&res); - - /* prime contexts */ - dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, - dcrypt_ctx_sym_get_key_length(stream->ctx_sym)); - ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym); - dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, - dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); - ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym); - - if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == - IO_STREAM_ENC_INTEGRITY_HMAC) { - dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize); - dcrypt_ctx_hmac_init(stream->ctx_mac, &error); - } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == - IO_STREAM_ENC_INTEGRITY_AEAD) { - dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize); - } - - /* clear out private key data */ - safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used); - - if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { - io_stream_set_error(&stream->ostream.iostream, - "Encryption init error: %s", error); - return -1; - } - return 0; -} - -static ssize_t -o_stream_encrypt_sendv(struct ostream_private *stream, - const struct const_iovec *iov, unsigned int iov_count) -{ - struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; - const char *error; - ssize_t ec,total = 0; - - /* not if finalized */ - i_assert(!estream->finalized); - - /* write prefix */ - if (!estream->prefix_written) { - T_BEGIN { - if ((estream->flags & IO_STREAM_ENC_VERSION_1) == - IO_STREAM_ENC_VERSION_1) - ec = o_stream_encrypt_send_header_v1(estream); - else - ec = o_stream_encrypt_send_header_v2(estream); - } T_END; - if (ec < 0) { - return -1; - } - } - - /* buffer for encrypted data */ - unsigned char ciphertext[IO_BLOCK_SIZE]; - buffer_t buf; - buffer_create_from_data(&buf, ciphertext, sizeof(ciphertext)); - - /* encrypt & send all blocks of data at max ciphertext buffer's - length */ - for(unsigned int i = 0; i < iov_count; i++) { - size_t bl, off = 0, len = iov[i].iov_len; - const unsigned char *ptr = iov[i].iov_base; - while(len > 0) { - buffer_set_used_size(&buf, 0); - /* update can emite twice the size of input */ - bl = I_MIN(sizeof(ciphertext)/2, len); - - if (!dcrypt_ctx_sym_update(estream->ctx_sym, ptr + off, - bl, &buf, &error)) { - io_stream_set_error(&stream->iostream, - "Encryption failure: %s", - error); - return -1; - } - if ((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == - IO_STREAM_ENC_INTEGRITY_HMAC) { - /* update mac */ - if (!dcrypt_ctx_hmac_update(estream->ctx_mac, - buf.data, buf.used, &error)) { - io_stream_set_error(&stream->iostream, - "MAC failure: %s", error); - return -1; - } - } - - /* hopefully upstream can accommodate */ - if (o_stream_encrypt_send(estream, buf.data, buf.used) < 0) { - return -1; - } - - len -= bl; - off += bl; - total += bl; - } - } - - stream->ostream.offset += total; - return total; -} - -static int -o_stream_encrypt_finalize(struct ostream_private *stream) -{ - const char *error; - struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; - - if (estream->finalized) { - /* we've already flushed the encrypted output. */ - return 0; - } - estream->finalized = TRUE; - - /* if nothing was written, we are done */ - if (!estream->prefix_written) return 0; - - /* acquire last block */ - buffer_t *buf = t_buffer_create( - dcrypt_ctx_sym_get_block_size(estream->ctx_sym)); - if (!dcrypt_ctx_sym_final(estream->ctx_sym, buf, &error)) { - io_stream_set_error(&estream->ostream.iostream, - "Encryption failure: %s", error); - return -1; - } - /* sometimes final does not emit anything */ - if (buf->used > 0) { - /* update mac */ - if (((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == - IO_STREAM_ENC_INTEGRITY_HMAC)) { - if (!dcrypt_ctx_hmac_update(estream->ctx_mac, buf->data, - buf->used, &error)) { - io_stream_set_error(&estream->ostream.iostream, - "MAC failure: %s", error); - return -1; - } - } - if (o_stream_encrypt_send(estream, buf->data, buf->used) < 0) { - return -1; - } - } - - /* write last mac bytes */ - buffer_set_used_size(buf, 0); - if ((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == - IO_STREAM_ENC_INTEGRITY_HMAC) { - if (!dcrypt_ctx_hmac_final(estream->ctx_mac, buf, &error)) { - io_stream_set_error(&estream->ostream.iostream, - "MAC failure: %s", error); - return -1; - } - } else if ((estream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == - IO_STREAM_ENC_INTEGRITY_AEAD) { - dcrypt_ctx_sym_get_tag(estream->ctx_sym, buf); - i_assert(buf->used > 0); - } - if (buf->used > 0 && - o_stream_encrypt_send(estream, buf->data, buf->used) < 0) { - return -1; - } - - return 0; -} - -static int -o_stream_encrypt_flush(struct ostream_private *stream) -{ - struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; - - if (stream->finished && estream->ctx_sym != NULL && - !estream->finalized) { - if (o_stream_encrypt_finalize(&estream->ostream) < 0) - return -1; - } - - return o_stream_flush_parent(stream); -} - -static void -o_stream_encrypt_close(struct iostream_private *stream, - bool close_parent) -{ - struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; - - i_assert(estream->finalized || estream->ctx_sym == NULL || - estream->ostream.ostream.stream_errno != 0); - if (close_parent) - o_stream_close(estream->ostream.parent); -} - -static void -o_stream_encrypt_destroy(struct iostream_private *stream) -{ - struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; - - /* release resources */ - if (estream->ctx_sym != NULL) - dcrypt_ctx_sym_destroy(&estream->ctx_sym); - if (estream->ctx_mac != NULL) - dcrypt_ctx_hmac_destroy(&estream->ctx_mac); - if (estream->key_data != NULL) - i_free(estream->key_data); - if (estream->cipher_oid != NULL) - buffer_free(&estream->cipher_oid); - if (estream->mac_oid != NULL) - buffer_free(&estream->mac_oid); - if (estream->pub != NULL) - dcrypt_key_unref_public(&estream->pub); - o_stream_unref(&estream->ostream.parent); -} - -static int -o_stream_encrypt_init(struct encrypt_ostream *estream, const char *algorithm) -{ - const char *error; - char *calg, *malg; - - if ((estream->flags & IO_STREAM_ENC_VERSION_1) == - IO_STREAM_ENC_VERSION_1) { - if (!dcrypt_ctx_sym_create("AES-256-CTR", DCRYPT_MODE_ENCRYPT, - &estream->ctx_sym, &error)) { - io_stream_set_error(&estream->ostream.iostream, - "Cannot create ostream-encrypt: %s", - error); - return -1; - } - /* disable MAC */ - estream->flags |= IO_STREAM_ENC_INTEGRITY_NONE; - /* then do keying */ - return o_stream_encrypt_keydata_create_v1(estream); - } else { - calg = t_strdup_noconst(algorithm); - malg = strrchr(calg, '-'); - - if (malg == NULL) { - io_stream_set_error(&estream->ostream.iostream, - "Invalid algorithm " - "(must be cipher-mac)"); - return -1; - } - (*malg++) = '\0'; - - if (!dcrypt_ctx_sym_create(calg, DCRYPT_MODE_ENCRYPT, - &estream->ctx_sym, &error)) { - io_stream_set_error(&estream->ostream.iostream, - "Cannot create ostream-encrypt: %s", - error); - return -1; - } - - /* create cipher and mac context, take note of OIDs */ - estream->cipher_oid = buffer_create_dynamic(default_pool, 12); - estream->block_size = - dcrypt_ctx_sym_get_block_size(estream->ctx_sym); - if (!dcrypt_name2oid(calg, estream->cipher_oid, &error)) { - io_stream_set_error(&estream->ostream.iostream, - "Cannot create ostream-encrypt: %s", - error); - return -1; - } - - /* mac context is optional */ - if ((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == - IO_STREAM_ENC_INTEGRITY_HMAC) { - if (!dcrypt_ctx_hmac_create(malg, &estream->ctx_mac, - &error)) { - io_stream_set_error(&estream->ostream.iostream, - "Cannot create ostream-encrypt: %s", - error); - return -1; - } - } - - estream->mac_oid = buffer_create_dynamic(default_pool, 12); - if (!dcrypt_name2oid(malg, estream->mac_oid, &error)) { - io_stream_set_error(&estream->ostream.iostream, - "Cannot create ostream-encrypt: %s", error); - return -1; - } - - /* MAC algorithm is used for PBKDF2 and keydata hashing */ - return o_stream_encrypt_keydata_create_v2(estream, malg); - } -} - -static struct encrypt_ostream * -o_stream_create_encrypt_common(enum io_stream_encrypt_flags flags) -{ - struct encrypt_ostream *estream; - - estream = i_new(struct encrypt_ostream, 1); - estream->ostream.sendv = o_stream_encrypt_sendv; - estream->ostream.flush = o_stream_encrypt_flush; - estream->ostream.iostream.close = o_stream_encrypt_close; - estream->ostream.iostream.destroy = o_stream_encrypt_destroy; - - estream->flags = flags; - - return estream; -} - -struct ostream * -o_stream_create_encrypt(struct ostream *output, const char *algorithm, - struct dcrypt_public_key *box_pub, enum io_stream_encrypt_flags flags) -{ - struct encrypt_ostream *estream = o_stream_create_encrypt_common(flags); - int ec; - - dcrypt_key_ref_public(box_pub); - estream->pub = box_pub; - - T_BEGIN { - ec = o_stream_encrypt_init(estream, algorithm); - } T_END; - - struct ostream *os = o_stream_create(&estream->ostream, output, - o_stream_get_fd(output)); - - if (ec != 0) { - os->stream_errno = EINVAL; - } - - return os; -} - -struct ostream * -o_stream_create_sym_encrypt(struct ostream *output, - struct dcrypt_context_symmetric *ctx) -{ - struct encrypt_ostream *estream = - o_stream_create_encrypt_common(IO_STREAM_ENC_INTEGRITY_NONE); - const char *error; - int ec; - - estream->prefix_written = TRUE; - - if (!dcrypt_ctx_sym_init(estream->ctx_sym, &error)) - ec = -1; - else - ec = 0; - - estream->ctx_sym = ctx; - - struct ostream *os = o_stream_create(&estream->ostream, output, - o_stream_get_fd(output)); - if (ec != 0) { - io_stream_set_error(&estream->ostream.iostream, - "Could not initialize stream: %s", - error); - os->stream_errno = EINVAL; - } - - return os; -} diff --git a/src/lib-dcrypt/ostream-encrypt.h b/src/lib-dcrypt/ostream-encrypt.h deleted file mode 100644 index 0b285476ed..0000000000 --- a/src/lib-dcrypt/ostream-encrypt.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef OSTREAM_ENCRYPT_H -#define OSTREAM_ENCRYPT_H - -struct dcrypt_public_key; -struct dcrypt_context_symmetric; - -/** - * algorithm is in form AES-256-CBC-SHA1, recommended - * AES-256-GCM-SHA256 - * - * Algorithms (both crypto and digest) *MUST* have OID to use it. - * - */ - -struct ostream * -o_stream_create_encrypt(struct ostream *output, const char *algorithm, - struct dcrypt_public_key *box_pub, - enum io_stream_encrypt_flags flags); - -/* create context for performing encryption with - preset crypto context. do not call ctx_sym_init. - - no header or mac is written, just plain crypto - data. - */ -struct ostream * -o_stream_create_sym_encrypt(struct ostream *output, - struct dcrypt_context_symmetric *ctx); - -#endif diff --git a/src/lib-dcrypt/sample-v1.asc b/src/lib-dcrypt/sample-v1.asc deleted file mode 100644 index c69fb50954..0000000000 --- a/src/lib-dcrypt/sample-v1.asc +++ /dev/null @@ -1,48 +0,0 @@ -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 -d/l7J6LsH25I/zsIV/0OHcrvMUEf2WzU3Q== diff --git a/src/lib-dcrypt/sample-v1_short.asc b/src/lib-dcrypt/sample-v1_short.asc deleted file mode 100644 index 4bc4c06d2a..0000000000 --- a/src/lib-dcrypt/sample-v1_short.asc +++ /dev/null @@ -1,4 +0,0 @@ -Q1JZUFRFRAMHAQCtAEMDAA+5INzRNr5OAicv3XI4jh//ufjZN9yYr7mElHKNGY+D -D2ziqHhPKVra6JzBzZvfySntDnDvdLAomafpDVlESMlkACB8mhA56i5P7XPoHdP/ -w/oi6kooNSk5rd57+OqFiwD6TwAgu4C6eZWV+Spojlk8eOAw784ySgMHK8gDrXhA -Jwg34GcAIPX4RmqXh+7QTAQWtGNHQvjqSygBxSUYkQ3KfPLMymKvAACMLy6/ diff --git a/src/lib-dcrypt/sample-v2.asc b/src/lib-dcrypt/sample-v2.asc deleted file mode 100644 index f9b2be3232..0000000000 --- a/src/lib-dcrypt/sample-v2.asc +++ /dev/null @@ -1,287 +0,0 @@ -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 diff --git a/src/lib-dcrypt/test-crypto.c b/src/lib-dcrypt/test-crypto.c deleted file mode 100644 index 11789f4652..0000000000 --- a/src/lib-dcrypt/test-crypto.c +++ /dev/null @@ -1,1311 +0,0 @@ -/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "array.h" -#include "buffer.h" -#include "str.h" -#include "dcrypt.h" -#include "dcrypt-iostream.h" -#include "ostream.h" -#include "ostream-encrypt.h" -#include "istream.h" -#include "iostream-temp.h" -#include "randgen.h" -#include "test-common.h" -#include "hex-binary.h" -#include "json-parser.h" -#include -#include -#include - -static void test_cipher_test_vectors(void) -{ - static const struct { - const char *key; - const char *iv; - const char *pt; - const char *ct; - } vectors[] = { - { - "2b7e151628aed2a6abf7158809cf4f3c", - "000102030405060708090a0b0c0d0e0f", - "6bc1bee22e409f96e93d7e117393172a", - "7649abac8119b246cee98e9b12e9197d" - }, { - "2b7e151628aed2a6abf7158809cf4f3c", - "7649ABAC8119B246CEE98E9B12E9197D", - "ae2d8a571e03ac9c9eb76fac45af8e51", - "5086cb9b507219ee95db113a917678b2" - } - }; - - - test_begin("test_cipher_test_vectors"); - - buffer_t *key,*iv,*pt,*ct,*res_enc,*res_dec; - - key = t_buffer_create(16); - iv = t_buffer_create(16); - pt = t_buffer_create(16); - ct = t_buffer_create(16); - - res_enc = t_buffer_create(32); - res_dec = t_buffer_create(32); - - for(size_t i = 0; i < N_ELEMENTS(vectors); i++) { - struct dcrypt_context_symmetric *ctx; - - buffer_set_used_size(key, 0); - buffer_set_used_size(iv, 0); - buffer_set_used_size(pt, 0); - buffer_set_used_size(ct, 0); - buffer_set_used_size(res_enc, 0); - buffer_set_used_size(res_dec, 0); - - hex_to_binary(vectors[i].key, key); - hex_to_binary(vectors[i].iv, iv); - hex_to_binary(vectors[i].pt, pt); - hex_to_binary(vectors[i].ct, ct); - - if (!dcrypt_ctx_sym_create("AES-128-CBC", DCRYPT_MODE_ENCRYPT, - &ctx, NULL)) { - test_assert_failed("dcrypt_ctx_sym_create", - __FILE__, __LINE__-1); - continue; - } - - dcrypt_ctx_sym_set_padding(ctx, FALSE); - - dcrypt_ctx_sym_set_key(ctx, key->data, key->used); - dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); - - test_assert_idx(dcrypt_ctx_sym_init(ctx, NULL), i); - - test_assert_idx(dcrypt_ctx_sym_update(ctx, - pt->data, pt->used, res_enc, NULL), i); - test_assert_idx(dcrypt_ctx_sym_final(ctx, res_enc, NULL), i); - - test_assert_idx(buffer_cmp(ct, res_enc), i); - - dcrypt_ctx_sym_destroy(&ctx); - - if (!dcrypt_ctx_sym_create("AES-128-CBC", DCRYPT_MODE_DECRYPT, - &ctx, NULL)) { - test_assert_failed("dcrypt_ctx_sym_create", - __FILE__, __LINE__-1); - continue; - } - - dcrypt_ctx_sym_set_padding(ctx, FALSE); - - dcrypt_ctx_sym_set_key(ctx, key->data, key->used); - dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); - - test_assert_idx(dcrypt_ctx_sym_init(ctx, NULL), i); - test_assert_idx(dcrypt_ctx_sym_update(ctx, - res_enc->data, res_enc->used, res_dec, NULL), i); - test_assert_idx(dcrypt_ctx_sym_final(ctx, res_dec, NULL), i); - - test_assert_idx(buffer_cmp(pt, res_dec), i); - - dcrypt_ctx_sym_destroy(&ctx); - } - - test_end(); -} - -static void test_cipher_aead_test_vectors(void) -{ - struct dcrypt_context_symmetric *ctx; - const char *error = NULL; - - test_begin("test_cipher_aead_test_vectors"); - - if (!dcrypt_ctx_sym_create("aes-128-gcm", DCRYPT_MODE_ENCRYPT, - &ctx, &error)) { - test_assert_failed("dcrypt_ctx_sym_create", - __FILE__, __LINE__-1); - return; - } - - buffer_t *key, *iv, *aad, *pt, *ct, *tag, *tag_res, *res; - - key = t_buffer_create(16); - iv = t_buffer_create(16); - aad = t_buffer_create(16); - pt = t_buffer_create(16); - ct = t_buffer_create(16); - tag = t_buffer_create(16); - res = t_buffer_create(16); - tag_res = t_buffer_create(16); - - hex_to_binary("feffe9928665731c6d6a8f9467308308", key); - hex_to_binary("cafebabefacedbaddecaf888", iv); - hex_to_binary("d9313225f88406e5a55909c5aff5269a" - "86a7a9531534f7da2e4c303d8a318a72" - "1c3c0c95956809532fcf0e2449a6b525" - "b16aedf5aa0de657ba637b391aafd255", pt); - hex_to_binary("42831ec2217774244b7221b784d0d49c" - "e3aa212f2c02a4e035c17e2329aca12e" - "21d514b25466931c7d8f6a5aac84aa05" - "1ba30b396a0aac973d58e091473f5985", ct); - hex_to_binary("4d5c2af327cd64a62cf35abd2ba6fab4", tag); - - dcrypt_ctx_sym_set_key(ctx, key->data, key->used); - dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); - dcrypt_ctx_sym_set_aad(ctx, aad->data, aad->used); - test_assert(dcrypt_ctx_sym_init(ctx, &error)); - test_assert(dcrypt_ctx_sym_update(ctx, pt->data, pt->used, res, &error)); - test_assert(dcrypt_ctx_sym_final(ctx, res, &error)); - test_assert(dcrypt_ctx_sym_get_tag(ctx, tag_res)); - - test_assert(buffer_cmp(ct, res) == TRUE); - test_assert(buffer_cmp(tag, tag_res) == TRUE); - - dcrypt_ctx_sym_destroy(&ctx); - - if (!dcrypt_ctx_sym_create("aes-128-gcm", DCRYPT_MODE_DECRYPT, - &ctx, &error)) { - test_assert_failed("dcrypt_ctx_sym_create", - __FILE__, __LINE__-1); - } else { - - buffer_set_used_size(res, 0); - - dcrypt_ctx_sym_set_key(ctx, key->data, key->used); - dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); - dcrypt_ctx_sym_set_aad(ctx, aad->data, aad->used); - dcrypt_ctx_sym_set_tag(ctx, tag->data, tag->used); - test_assert(dcrypt_ctx_sym_init(ctx, &error)); - test_assert(dcrypt_ctx_sym_update(ctx, - ct->data, ct->used, res, &error)); - test_assert(dcrypt_ctx_sym_final(ctx, res, &error)); - - test_assert(buffer_cmp(pt, res) == TRUE); - - dcrypt_ctx_sym_destroy(&ctx); - } - - test_end(); -} - -static void test_hmac_test_vectors(void) -{ - test_begin("test_hmac_test_vectors"); - - buffer_t *pt, *ct, *key, *res; - pt = t_buffer_create(50); - key = t_buffer_create(20); - ct = t_buffer_create(32); - res = t_buffer_create(32); - - hex_to_binary("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", key); - hex_to_binary("dddddddddddddddddddddddddddddddddddddddddddddddddd" - "dddddddddddddddddddddddddddddddddddddddddddddddddd", pt); - hex_to_binary("773ea91e36800e46854db8ebd09181a7" - "2959098b3ef8c122d9635514ced565fe", res); - - struct dcrypt_context_hmac *hctx; - if (!dcrypt_ctx_hmac_create("sha256", &hctx, NULL)) { - test_assert_failed("dcrypt_ctx_hmac_create", - __FILE__, __LINE__-1); - } else { - dcrypt_ctx_hmac_set_key(hctx, key->data, key->used); - test_assert(dcrypt_ctx_hmac_init(hctx, NULL)); - test_assert(dcrypt_ctx_hmac_update(hctx, - pt->data, pt->used, NULL)); - test_assert(dcrypt_ctx_hmac_final(hctx, ct, NULL)); - test_assert(buffer_cmp(ct, res)); - dcrypt_ctx_hmac_destroy(&hctx); - } - - test_end(); -} - -static void test_load_v1_keys(void) -{ - test_begin("test_load_v1_keys"); - - const char *error = NULL; - const char *data1 = - "1\t716\t1\t0567e6bf9579813ae967314423b0fceb14bda24" - "749303923de9a9bb9370e0026f995901a57e63113eeb2baf0c" - "940e978d00686cbb52bd5014bc318563375876255\t0300E46" - "DA2125427BE968EB3B649910CDC4C405E5FFDE18D433A97CAB" - "FEE28CEEFAE9EE356C792004FFB80981D67E741B8CC036A342" - "35A8D2E1F98D1658CFC963D07EB\td0cfaca5d335f9edc41c8" - "4bb47465184cb0e2ec3931bebfcea4dd433615e77a0\t7c9a1" - "039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea8" - "58b00fa4f"; - - enum dcrypt_key_format format; - enum dcrypt_key_version version; - enum dcrypt_key_kind kind; - enum dcrypt_key_encryption_type encryption_type; - const char *encryption_key_hash = NULL; - const char *key_hash = NULL; - - bool ret = dcrypt_key_string_get_info(data1, &format, &version, - &kind, &encryption_type, &encryption_key_hash, - &key_hash, &error); - - test_assert(ret == TRUE); - test_assert(error == NULL); - test_assert(format == DCRYPT_FORMAT_DOVECOT); - test_assert(version == DCRYPT_KEY_VERSION_1); - test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); - test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_KEY); - test_assert(strcmp(encryption_key_hash, - "d0cfaca5d335f9edc41c84bb47465184" - "cb0e2ec3931bebfcea4dd433615e77a0") == 0); - test_assert(strcmp(key_hash, - "7c9a1039ea2e4fed73e81dd3ffc3fa22" - "ea4a28352939adde7bf8ea858b00fa4f") == 0); - - const char* data2 = - "1\t716\t0301EB00973C4EFC8FCECA4EA33E941F50B561199A" - "5159BCB6C2EED9DD1D62D65E38A254979D89E28F0C28883E71" - "EE2AD264CD16B863FA094A8F6F69A56B62E8918040\t7c9a10" - "39ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea85" - "8b00fa4f"; - - error = NULL; - encryption_key_hash = NULL; - key_hash = NULL; - - ret = dcrypt_key_string_get_info(data2, &format, &version, - &kind, &encryption_type, &encryption_key_hash, - &key_hash, &error); - - test_assert(ret == TRUE); - test_assert(error == NULL); - test_assert(format == DCRYPT_FORMAT_DOVECOT); - test_assert(version == DCRYPT_KEY_VERSION_1); - test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); - test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); - test_assert(encryption_key_hash == NULL); - test_assert(strcmp(key_hash, - "7c9a1039ea2e4fed73e81dd3ffc3fa22" - "ea4a28352939adde7bf8ea858b00fa4f") == 0); - - /* This is the key that should be able to decrypt key1 */ - const char *data3 = - "1\t716\t0\t048FD04FD3612B22D32790C592CF21CEF417EFD" - "2EA34AE5F688FA5B51BED29E05A308B68DA78E16E90B47A11E" - "133BD9A208A2894FD01B0BEE865CE339EA3FB17AC\td0cfaca" - "5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd4336" - "15e77a0"; - - error = NULL; - encryption_key_hash = NULL; - key_hash = NULL; - - ret = dcrypt_key_string_get_info(data3, &format, &version, - &kind, &encryption_type, &encryption_key_hash, - &key_hash, &error); - test_assert(ret == TRUE); - test_assert(error == NULL); - test_assert(format == DCRYPT_FORMAT_DOVECOT); - test_assert(version == DCRYPT_KEY_VERSION_1); - test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); - test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); - test_assert(encryption_key_hash == NULL); - test_assert(strcmp(key_hash, - "d0cfaca5d335f9edc41c84bb47465184" - "cb0e2ec3931bebfcea4dd433615e77a0") == 0); - - /* key3's key_hash should and does match key1's encryption_key_hash */ - struct dcrypt_private_key *pkey = NULL; - struct dcrypt_private_key *pkey2 = NULL; - pkey = NULL; - error = NULL; - - ret = dcrypt_key_load_private(&pkey2, data3, NULL, NULL, &error); - test_assert(ret == TRUE); - test_assert(error == NULL); - - ret = dcrypt_key_load_private(&pkey, data1, NULL, pkey2, &error); - test_assert(ret == TRUE); - test_assert(error == NULL); - - dcrypt_key_unref_private(&pkey2); - dcrypt_key_unref_private(&pkey); - - test_end(); -} - -static void test_load_v1_key(void) -{ - test_begin("test_load_v1_key"); - - buffer_t *key_1 = t_buffer_create(128); - - struct dcrypt_private_key *pkey = NULL, *pkey2 = NULL; - const char *error = NULL; - - test_assert(dcrypt_key_load_private(&pkey, - "1\t716\t0\t048FD04FD3612B22D32790C592CF21CEF417EFD" - "2EA34AE5F688FA5B51BED29E05A308B68DA78E16E90B47A11E" - "133BD9A208A2894FD01B0BEE865CE339EA3FB17AC\td0cfaca" - "5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd4336" - "15e77a0", NULL, NULL, &error)); - if (pkey != NULL) { - buffer_set_used_size(key_1, 0); - /* check that key_id matches */ - struct dcrypt_public_key *pubkey = NULL; - dcrypt_key_convert_private_to_public(pkey, &pubkey); - test_assert(dcrypt_key_store_public(pubkey, - DCRYPT_FORMAT_DOVECOT, key_1, NULL)); - buffer_set_used_size(key_1, 0); - dcrypt_key_id_public(pubkey, "sha256", key_1, &error); - test_assert(strcmp("792caad4d38c9eb2134a0cbc844eae38" - "6116de096a0ccafc98479825fc99b6a1", - binary_to_hex(key_1->data, key_1->used)) - == 0); - - dcrypt_key_unref_public(&pubkey); - pkey2 = NULL; - - test_assert(dcrypt_key_load_private(&pkey2, - "1\t716\t1\t0567e6bf9579813ae967314423b0fceb14" - "bda24749303923de9a9bb9370e0026f995901a57e6311" - "3eeb2baf0c940e978d00686cbb52bd5014bc318563375" - "876255\t0300E46DA2125427BE968EB3B649910CDC4C4" - "05E5FFDE18D433A97CABFEE28CEEFAE9EE356C792004F" - "FB80981D67E741B8CC036A34235A8D2E1F98D1658CFC9" - "63D07EB\td0cfaca5d335f9edc41c84bb47465184cb0e" - "2ec3931bebfcea4dd433615e77a0\t7c9a1039ea2e4fe" - "d73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00" - "fa4f", NULL, pkey, &error)); - if (pkey2 != NULL) { - buffer_set_used_size(key_1, 0); - /* check that key_id matches */ - struct dcrypt_public_key *pubkey = NULL; - dcrypt_key_convert_private_to_public(pkey2, &pubkey); - test_assert(dcrypt_key_store_public(pubkey, - DCRYPT_FORMAT_DOVECOT, key_1, NULL)); - buffer_set_used_size(key_1, 0); - test_assert(dcrypt_key_id_public_old(pubkey, - key_1, &error)); - test_assert(strcmp( - "7c9a1039ea2e4fed73e81dd3ffc3fa22" - "ea4a28352939adde7bf8ea858b00fa4f", - binary_to_hex(key_1->data, key_1->used)) == 0); - - dcrypt_key_unref_public(&pubkey); - dcrypt_key_unref_private(&pkey2); - } - dcrypt_key_unref_private(&pkey); - } - - test_end(); -} - -static void test_load_v1_public_key(void) -{ - test_begin("test_load_v1_public_key"); - - const char* data1 = - "1\t716\t030131D8A5FD5167947A0AE9CB112ADED652665463" - "5AA5887051EE2364414B60FF32EBA8FA0BBE9485DBDE8794BB" - "BCB44BBFC0D662A4287A848BA570D4E5E45A11FE0F\td0cfac" - "a5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433" - "615e77a0"; - - const char* error = NULL; - const char* key_hash = NULL; - const char* encryption_key_hash = NULL; - - enum dcrypt_key_format format; - enum dcrypt_key_version version; - enum dcrypt_key_kind kind; - enum dcrypt_key_encryption_type encryption_type; - - bool ret = dcrypt_key_string_get_info(data1, &format, &version, - &kind, &encryption_type, &encryption_key_hash, - &key_hash, &error); - - test_assert(ret == TRUE); - test_assert(error == NULL); - test_assert(format == DCRYPT_FORMAT_DOVECOT); - test_assert(version == DCRYPT_KEY_VERSION_1); - test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); - test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); - test_assert(key_hash != NULL && - strcmp(key_hash, "d0cfaca5d335f9edc41c84bb47465184" - "cb0e2ec3931bebfcea4dd433615e77a0") == 0); - test_assert(encryption_key_hash == NULL); - - struct dcrypt_public_key *pub_key = NULL; - ret = dcrypt_key_load_public(&pub_key, data1, &error); - test_assert(ret == TRUE); - test_assert(error == NULL); - - test_assert(dcrypt_key_type_public(pub_key) == DCRYPT_KEY_EC); - - dcrypt_key_unref_public(&pub_key); - test_assert(pub_key == NULL); - - test_end(); -} - -static void test_load_v2_key(void) -{ - const char *keys[] = { - "-----BEGIN PRIVATE KEY-----\n" - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgtu" - "QJA+uboZWVwgHc\n" - "DciyVdrovAPwlMqshDK3s78IDDuhRANCAAQm0VEdzLB9PtD0HA" - "8JK1zifWnj8M00\n" - "FQzedfp9SQsWyA8dzs5/NFR5MTe6Xbh/ndKEs1zZH3vZ4FlNri" - "lZc0st\n" - "-----END PRIVATE KEY-----\n", - "2:1.2.840.10045.3.1.7:0:0000002100b6e40903eb9ba195" - "95c201dc0dc8b255dae8bc03f094caac8432b7b3bf080c3b:a" - "b13d251976dedab546b67354e7678821740dd534b749c2857f" - "66bf62bbaddfd", - "2:1.2.840.10045.3.1.7:2:aes-256-ctr:483bd74fd3d917" - "63:sha256:2048:d44ae35d3af7a2febcb15cde0c3693e7ed9" - "8595665ed655a97fa918d346d5c661a6e2339f4:ab13d25197" - "6dedab546b67354e7678821740dd534b749c2857f66bf62bba" - "ddfd", - "2:1.2.840.10045.3.1.7:1:aes-256-ctr:2574c10be28a4c" - "09:sha256:2048:a750ec9dea91999f108f943485a20f273f4" - "0f75c37fc9bcccdedda514c8243e550d69ce1bd:02237a199d" - "7d945aa6492275a02881071eceec5749caf2485da8c64fb601" - "229098:ab13d251976dedab546b67354e7678821740dd534b7" - "49c2857f66bf62bbaddfd:ab13d251976dedab546b67354e76" - "78821740dd534b749c2857f66bf62bbaddfd" - }; - - test_begin("test_load_v2_key"); - const char *error = NULL; - buffer_t *tmp = buffer_create_dynamic(default_pool, 256); - - struct dcrypt_private_key *priv,*priv2; - - test_assert_idx(dcrypt_key_load_private(&priv2, - keys[0], NULL, NULL, &error), 0); - test_assert_idx(dcrypt_key_store_private(priv2, - DCRYPT_FORMAT_PEM, NULL, tmp, NULL, NULL, &error), 0); - test_assert_idx(strcmp(str_c(tmp), keys[0])==0, 0); - buffer_set_used_size(tmp, 0); - - test_assert_idx(dcrypt_key_load_private(&priv, - keys[1], NULL, NULL, &error), 1); - test_assert_idx(dcrypt_key_store_private(priv, - DCRYPT_FORMAT_DOVECOT, NULL, tmp, NULL, NULL, &error), 1); - test_assert_idx(strcmp(str_c(tmp), keys[1])==0, 1); - buffer_set_used_size(tmp, 0); - dcrypt_key_unref_private(&priv); - - test_assert_idx(dcrypt_key_load_private(&priv, - keys[2], "This Is Sparta", NULL, &error), 2); - test_assert_idx(dcrypt_key_store_private(priv, - DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", tmp, - "This Is Sparta", NULL, &error), 2); - buffer_set_used_size(tmp, 0); - dcrypt_key_unref_private(&priv); - - struct dcrypt_public_key *pub = NULL; - dcrypt_key_convert_private_to_public(priv2, &pub); - test_assert_idx(dcrypt_key_load_private(&priv, - keys[3], NULL, priv2, &error), 3); - test_assert_idx(dcrypt_key_store_private(priv, - DCRYPT_FORMAT_DOVECOT, "ecdh-aes-256-ctr", tmp, - NULL, pub, &error), 3); - buffer_set_used_size(tmp, 0); - dcrypt_key_unref_private(&priv2); - dcrypt_key_unref_private(&priv); - dcrypt_key_unref_public(&pub); - - buffer_free(&tmp); - - if (error != NULL) error = NULL; - - test_end(); -} - -static void test_load_v2_public_key(void) -{ - struct dcrypt_public_key *pub = NULL; - const char *error; - - test_begin("test_load_v2_public_key"); - const char *key = - "2:3058301006072a8648ce3d020106052b810400230344000" - "301c50954e734dd8b410a607764a7057065a45510da52f2c6" - "e28e0cb353b9c389fa8cb786943ae991fce9befed78fb162f" - "bbc615415f06af06c8cc80c37f4e94ff6c7:185a721254278" - "2e239111f9c19d126ad55b18ddaf4883d66afe8d9627c3607" - "d8"; - - test_assert(dcrypt_key_load_public(&pub, key, &error)); - - buffer_t *tmp = buffer_create_dynamic(default_pool, 256); - - if (pub != NULL) { - test_assert(dcrypt_key_store_public(pub, - DCRYPT_FORMAT_DOVECOT, tmp, &error)); - test_assert(strcmp(key, str_c(tmp))==0); - buffer_free(&tmp); - dcrypt_key_unref_public(&pub); - } - - test_end(); -} - -static void test_get_info_v2_key(void) -{ - test_begin("test_get_info_v2_key"); - - const char *key = - "2:305e301006072a8648ce3d020106052b81040026034a0002" - "03fcc90034fa03d6fb79a0fc8b3b43c3398f68e76029307360" - "cdcb9e27bb7e84b3c19dfb7244763bc4d442d216f09b7b7945" - "ed9d182f3156550e9ee30b237a0217dbf79d28975f31:86706" - "b69d1f640011a65d26a42f2ba20a619173644e1cc7475eb1d9" - "0966e84dc"; - enum dcrypt_key_format format; - enum dcrypt_key_version version = DCRYPT_KEY_VERSION_NA; - enum dcrypt_key_kind kind; - enum dcrypt_key_encryption_type encryption_type; - const char *encryption_key_hash = NULL; - const char *key_hash = NULL; - const char *error = NULL; - - test_assert(dcrypt_key_string_get_info(key, &format, &version, - &kind, &encryption_type, &encryption_key_hash, - &key_hash, &error)); - test_assert(error == NULL); - test_assert(format == DCRYPT_FORMAT_DOVECOT); - test_assert(version == DCRYPT_KEY_VERSION_2); - - test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); - test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); - test_assert(encryption_key_hash == NULL); - test_assert(key_hash != NULL && strcmp(key_hash, - "86706b69d1f640011a65d26a42f2ba20" - "a619173644e1cc7475eb1d90966e84dc") == 0); - - test_end(); -} - -static void test_gen_and_get_info_rsa_pem(void) -{ - test_begin("test_gen_and_get_info_rsa_pem"); - - const char *error = NULL; - bool ret = FALSE; - struct dcrypt_keypair pair; - string_t* buf = str_new(default_pool, 4096); - - ret = dcrypt_keypair_generate(&pair, DCRYPT_KEY_RSA, 1024, NULL, NULL); - test_assert(ret == TRUE); - - /* test public key */ - enum dcrypt_key_format format; - enum dcrypt_key_version version; - enum dcrypt_key_kind kind; - enum dcrypt_key_encryption_type encryption_type; - const char *encryption_key_hash; - const char *key_hash; - - ret = dcrypt_key_store_public(pair.pub, DCRYPT_FORMAT_PEM, buf, - &error); - test_assert(ret == TRUE); - - ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, - &kind, &encryption_type, &encryption_key_hash, - &key_hash, &error); - test_assert(ret == TRUE); - test_assert(format == DCRYPT_FORMAT_PEM); - test_assert(version == DCRYPT_KEY_VERSION_NA); - - test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); - test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); - test_assert(encryption_key_hash == NULL); - test_assert(key_hash == NULL); - - /* test private key */ - buffer_set_used_size(buf, 0); - ret = dcrypt_key_store_private(pair.priv, DCRYPT_FORMAT_PEM, NULL, - buf, NULL, NULL, &error); - - test_assert(ret == TRUE); - - ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, - &kind, &encryption_type, &encryption_key_hash, - &key_hash, &error); - - test_assert(ret == TRUE); - test_assert(format == DCRYPT_FORMAT_PEM); - test_assert(version == DCRYPT_KEY_VERSION_NA); - - test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); - - test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); - test_assert(encryption_key_hash == NULL); - test_assert(key_hash == NULL); - - dcrypt_keypair_unref(&pair); - buffer_free(&buf); - - test_end(); -} - -static void test_get_info_rsa_private_key(void) -{ - test_begin("test_get_info_rsa_private_key"); - - const char *key = "-----BEGIN RSA PRIVATE KEY-----\n" -"MIICXQIBAAKBgQC89q02I9NezBLQ+otn5XLYE7S+GsKUz59ogr45DA/6MI9jey0W\n" -"56SeWQ1FJD1vDhAx/TRBMfOmhcIPsBjc5sakYOawPdoiqLjOIlO+iHwnbbmLuMsq\n" -"ue09vgvZsKjuTr2F5DOFQY43Bq/Nd+4bjHJItdOM58+xwA2I/8vDbtI8jwIDAQAB\n" -"AoGBAJCUrTMfdjqyKjN7f+6ewKBTc5eBIiB6O53ba3B6qj7jqNKVDIrZ8jq2KFEe\n" -"yWKPgBS/h5vafHKNJU6bjmp2qMUJPB7PTA876eDo0cq9PplUqihiTlXJFwNQYtF+\n" -"o27To5t25+5qdSAj657+lQfFT9Xn9fzYHDmotURxH10FgFkBAkEA+7Ny6lBTeb3W\n" -"LnP0UPfPzQLilEr8u81PLWe69RGtsEaMQHGpHOl4e+bvvVYbG1cgxwxI1m01uR9r\n" -"qpD3qLUdrQJBAMAw6UvN8R+opYTZzwqK7Nliil2QZMPmXM04SV1iFq26NM60w2Fm\n" -"HqOOh0EbpSWsFtIgxJFWoZOtrguxqCJuUqsCQF3EoXf3StHczhDqM8eCOpD2lTCH\n" -"qxXPy8JvlW+9EUbNUWykq0rRE4idJQ0VKe4KjHR6+Buh/dSkhvi5Hvpj1tUCQHRv\n" -"LWeXZLVhXqWVrzEb6VHpuRnmGKX2MdLCfu/sNQEbBlMUgCnJzFYaSybOsMaZ81lq\n" -"MKw8Z7coSYEcKFhzrfECQQD7l+4Bhy8Zuz6VoGGIZwIhxkJrImBFmaUwx8N6jg20\n" -"sgDRYwCoGkGd7B8uIHZLJoWzSSutHiu5i5PYUy5VT1yT\n" -"-----END RSA PRIVATE KEY-----\n"; - - const char *error = NULL; - - test_assert(!dcrypt_key_string_get_info(key, NULL, NULL, - NULL, NULL, NULL, NULL, &error)); - test_assert(error != NULL && strstr(error, "pkey") != NULL); - - test_end(); -} - -static void test_get_info_invalid_keys(void) -{ - test_begin("test_get_info_invalid_keys"); - - const char *key = - "1:716:030131D8A5FD5167947A0AE9CB112ADED6526654635A" - "A5887051EE2364414B60FF32EBA8FA0BBE9485DBDE8794BBBC" - "B44BBFC0D662A4287A848BA570D4E5E45A11FE0F:d0cfaca5d" - "335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615" - "e77a0"; - const char *error = NULL; - - test_assert(dcrypt_key_string_get_info(key, NULL, NULL, - NULL, NULL, NULL, NULL, &error) == FALSE); - test_assert(error != NULL && strstr(error, "tab") != NULL); - - key = - "2\t305e301006072a8648ce3d020106052b81040026034a000" - "203fcc90034fa03d6fb79a0fc8b3b43c3398f68e7602930736" - "0cdcb9e27bb7e84b3c19dfb7244763bc4d442d216f09b7b794" - "5ed9d182f3156550e9ee30b237a0217dbf79d28975f31\t867" - "06b69d1f640011a65d26a42f2ba20a619173644e1cc7475eb1" - "d90966e84dc"; - error = NULL; - - test_assert(dcrypt_key_string_get_info(key, NULL, NULL, - NULL, NULL, NULL, NULL, &error) == FALSE); - test_assert(error != NULL && strstr(error, "colon") != NULL); - - key = "2"; - error = NULL; - - test_assert(dcrypt_key_string_get_info(key, NULL, NULL, - NULL, NULL, NULL, NULL, &error) == FALSE); - test_assert(error != NULL && strstr(error, "Unknown") != NULL); - - test_end(); -} - -static void test_get_info_key_encrypted(void) -{ - test_begin("test_get_info_key_encrypted"); - - struct dcrypt_keypair p1, p2; - const char *error = NULL; - bool ret = dcrypt_keypair_generate(&p1, - DCRYPT_KEY_EC, 0, "secp521r1", &error); - test_assert(ret == TRUE); - ret = dcrypt_keypair_generate(&p2, - DCRYPT_KEY_EC, 0, "secp521r1", &error); - test_assert(ret == TRUE); - - string_t* buf = t_str_new(4096); - - buffer_set_used_size(buf, 0); - ret = dcrypt_key_store_private(p1.priv, - DCRYPT_FORMAT_DOVECOT, "ecdh-aes-256-ctr", buf, - NULL, p2.pub, &error); - test_assert(ret == TRUE); - - enum dcrypt_key_format format; - enum dcrypt_key_version version; - enum dcrypt_key_kind kind; - enum dcrypt_key_encryption_type enc_type; - const char *enc_hash; - const char *key_hash; - - ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, - &kind, &enc_type, &enc_hash, &key_hash, &error); - test_assert(ret == TRUE); - test_assert(format == DCRYPT_FORMAT_DOVECOT); - test_assert(version == DCRYPT_KEY_VERSION_2); - test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); - test_assert(enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_KEY); - test_assert(enc_hash != NULL); - test_assert(key_hash != NULL); - - dcrypt_keypair_unref(&p1); - dcrypt_keypair_unref(&p2); - - test_end(); -} - -static void test_get_info_pw_encrypted(void) -{ - test_begin("test_get_info_pw_encrypted"); - - struct dcrypt_keypair p1; - i_zero(&p1); - const char *error; - bool ret = dcrypt_keypair_generate(&p1, - DCRYPT_KEY_EC, 0, "secp521r1", &error); - test_assert(ret == TRUE); - - string_t* buf = t_str_new(4096); - ret = dcrypt_key_store_private(p1.priv, - DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", buf, "pw", NULL, &error); - test_assert(ret == TRUE); - - enum dcrypt_key_format format; - enum dcrypt_key_version version; - enum dcrypt_key_kind kind; - enum dcrypt_key_encryption_type enc_type; - const char *enc_hash; - const char *key_hash; - - ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, - &kind, &enc_type, &enc_hash, &key_hash, &error); - test_assert(ret == TRUE); - test_assert(format == DCRYPT_FORMAT_DOVECOT); - test_assert(version == DCRYPT_KEY_VERSION_2); - test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); - test_assert(enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD); - test_assert(enc_hash == NULL); - test_assert(key_hash != NULL); - - dcrypt_keypair_unref(&p1); - - test_end(); -} - -static void test_password_change(void) -{ - test_begin("test_password_change"); - - const char *pw1 = "first password"; - struct dcrypt_keypair orig; - const char *error = NULL; - - bool ret = dcrypt_keypair_generate(&orig, - DCRYPT_KEY_EC, 0, "secp521r1", &error); - test_assert(ret == TRUE); - - string_t *buf = t_str_new(4096); - ret = dcrypt_key_store_private(orig.priv, - DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", buf, pw1, NULL, &error); - test_assert(ret == TRUE); - - /* load the pw-encrypted key */ - struct dcrypt_private_key *k1_priv = NULL; - ret = dcrypt_key_load_private(&k1_priv, str_c(buf), pw1, NULL, &error); - test_assert(ret == TRUE); - - /* encrypt a key with the pw-encrypted key k1 */ - struct dcrypt_keypair k2; - ret = dcrypt_keypair_generate(&k2, - DCRYPT_KEY_EC, 0, "secp521r1", &error); - test_assert(ret == TRUE); - - string_t *buf2 = t_str_new(4096); - struct dcrypt_public_key *k1_pub = NULL; - dcrypt_key_convert_private_to_public(k1_priv, &k1_pub); - ret = dcrypt_key_store_private(k2.priv, - DCRYPT_FORMAT_DOVECOT, "ecdh-aes-256-ctr", buf2, - NULL, k1_pub, &error); - test_assert(ret == TRUE); - - /* change the password */ - const char *pw2 = "second password"; - string_t *buf3 = t_str_new(4096); - - /* encrypt k1 with pw2 */ - ret = dcrypt_key_store_private(k1_priv, - DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", buf3, pw2, NULL, &error); - test_assert(ret == TRUE); - - /* load the pw2 encrypted key */ - struct dcrypt_private_key *k2_priv = NULL; - ret = dcrypt_key_load_private(&k2_priv, str_c(buf3), pw2, NULL, &error); - test_assert(ret == TRUE); - - /* load the key that was encrypted with pw1 using the pw2 encrypted key */ - struct dcrypt_private_key *k3_priv = NULL; - ret = dcrypt_key_load_private(&k3_priv, - str_c(buf2), NULL, k2_priv, &error); - test_assert(ret == TRUE); - - dcrypt_key_unref_private(&k1_priv); - dcrypt_key_unref_public(&k1_pub); - dcrypt_key_unref_private(&k2_priv); - dcrypt_key_unref_private(&k3_priv); - dcrypt_keypair_unref(&orig); - dcrypt_keypair_unref(&k2); - - test_end(); -} - -static void test_load_invalid_keys(void) -{ - test_begin("test_load_invalid_keys"); - - const char *error = NULL; - const char *key = - "1:716:0301EB00973C4EFC8FCECA4EA33E941F50B561199A51" - "59BCB6C2EED9DD1D62D65E38A254979D89E28F0C28883E71EE" - "2AD264CD16B863FA094A8F6F69A56B62E8918040:7c9a1039e" - "a2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b0" - "0fa4f"; - struct dcrypt_public_key *pub_key = NULL; - - bool ret = dcrypt_key_load_public(&pub_key, key, &error); - test_assert(ret == FALSE); - test_assert(error != NULL); - - error = NULL; - key = - "2:305e301006072a8648ce3d020106052b81040026034a0002" - "03fcc90034fa03d6fb79a0fc8b3b43c3398f68e76029307360" - "cdcb9e27bb7e84b3c19dfb7244763bc4d442d216f09b7b7945" - "ed9d182f3156550e9ee30b237a0217dbf79d28975f31:86706" - "b69d1f640011a65d26a42f2ba20a619173644e1cc7475eb1d9" - "0966e84dc"; - struct dcrypt_private_key *priv_key = NULL; - - ret = dcrypt_key_load_private(&priv_key, key, NULL, NULL, &error); - test_assert(ret == FALSE); - test_assert(error != NULL); - - test_end(); -} - -static void test_raw_keys(void) -{ - - test_begin("test_raw_keys"); - - ARRAY_TYPE(dcrypt_raw_key) priv_key; - ARRAY_TYPE(dcrypt_raw_key) pub_key; - pool_t pool = pool_datastack_create(); - - enum dcrypt_key_type t; - - p_array_init(&priv_key, pool, 2); - p_array_init(&pub_key, pool, 2); - - /* generate ECC key */ - struct dcrypt_keypair pair; - i_assert(dcrypt_keypair_generate(&pair, DCRYPT_KEY_EC, 0, "prime256v1", NULL)); - - /* store it */ - test_assert(dcrypt_key_store_private_raw(pair.priv, pool, &t, &priv_key, - NULL)); - test_assert(dcrypt_key_store_public_raw(pair.pub, pool, &t, &pub_key, - NULL)); - dcrypt_keypair_unref(&pair); - - /* load it */ - test_assert(dcrypt_key_load_private_raw(&pair.priv, t, &priv_key, - NULL)); - test_assert(dcrypt_key_load_public_raw(&pair.pub, t, &pub_key, - NULL)); - - dcrypt_keypair_unref(&pair); - - /* test load known raw private key */ - const char *curve = "prime256v1"; - const unsigned char priv_key_data[] = { - 0x16, 0x9e, 0x62, 0x36, 0xaf, 0x9c, 0xae, 0x0e, 0x71, 0xda, - 0xf2, 0x63, 0xe2, 0xe0, 0x5d, 0xf1, 0xd5, 0x35, 0x8c, 0x2b, - 0x68, 0xf0, 0x2a, 0x69, 0xc4, 0x5d, 0x3d, 0x1c, 0xde, 0xa1, - 0x9b, 0xd3 - }; - - /* create buffers */ - struct dcrypt_raw_key *item; - ARRAY_TYPE(dcrypt_raw_key) static_key; - t_array_init(&static_key, 2); - - /* Add OID */ - buffer_t *buf = t_buffer_create(32); - test_assert(dcrypt_name2oid(curve, buf, NULL)); - item = array_append_space(&static_key); - item->parameter = buf->data; - item->len = buf->used; - - /* Add key data */ - item = array_append_space(&static_key); - item->parameter = priv_key_data; - item->len = sizeof(priv_key_data); - - /* Try load it */ - test_assert(dcrypt_key_load_private_raw(&pair.priv, t, - &static_key, NULL)); - - /* See what we got */ - buf = t_buffer_create(128); - test_assert(dcrypt_key_store_private(pair.priv, DCRYPT_FORMAT_DOVECOT, - NULL, buf, NULL, NULL, NULL)); - test_assert_strcmp(str_c(buf), - "2:1.2.840.10045.3.1.7:0:00000020169e6236af9cae0e71d" - "af263e2e05df1d5358c2b68f02a69c45d3d1cdea19bd3:21d11" - "6b7b3e5c52e81f0437a10b0116cfafc467fb1b96e48926d0216" - "68fc1bea"); - - /* try to load public key, too */ - const unsigned char pub_key_data[] = { - 0x04, 0xe8, 0x7c, 0x6d, 0xa0, 0x29, 0xfe, 0x5d, 0x16, 0x1a, - 0xd6, 0x6a, 0xc6, 0x1c, 0x78, 0x8a, 0x36, 0x0f, 0xfb, 0x64, - 0xe7, 0x7f, 0x58, 0x13, 0xb3, 0x80, 0x1f, 0x99, 0x45, 0xee, - 0xa9, 0x4a, 0xe2, 0xde, 0xf3, 0x88, 0xc6, 0x37, 0x72, 0x7f, - 0xbe, 0x97, 0x02, 0x94, 0xb2, 0x21, 0x60, 0xa4, 0x98, 0x4e, - 0xfb, 0x46, 0x19, 0x61, 0x4c, 0xc5, 0xe1, 0x9f, 0xe9, 0xb2, - 0xd2, 0x4d, 0xae, 0x83, 0x4b - }; - - array_clear(&static_key); - - /* Add OID */ - buf = t_buffer_create(32); - test_assert(dcrypt_name2oid(curve, buf, NULL)); - item = array_append_space(&static_key); - item->parameter = buf->data; - item->len = buf->used; - - /* Add key data */ - item = array_append_space(&static_key); - item->parameter = pub_key_data; - item->len = sizeof(pub_key_data); - - /* See what we got */ - test_assert(dcrypt_key_load_public_raw(&pair.pub, t, - &static_key, NULL)); - buf = t_buffer_create(128); - test_assert(dcrypt_key_store_public(pair.pub, DCRYPT_FORMAT_DOVECOT, - buf, NULL)); - test_assert_strcmp(str_c(buf), - "2:3039301306072a8648ce3d020106082a8648ce3d03010703220003e87c6d" - "a029fe5d161ad66ac61c788a360ffb64e77f5813b3801f9945eea94ae2:21d" - "116b7b3e5c52e81f0437a10b0116cfafc467fb1b96e48926d021668fc1bea"); - dcrypt_keypair_unref(&pair); - - test_end(); -} - -static void test_sign_verify_rsa(void) -{ - const char *error = NULL; - bool valid; - struct dcrypt_private_key *priv_key = NULL; - struct dcrypt_public_key *pub_key = NULL; - - buffer_t *signature = - buffer_create_dynamic(pool_datastack_create(), 128); - const char *data = "signed data"; - - test_begin("sign and verify (rsa)"); - const char *key = "-----BEGIN PRIVATE KEY-----\n" -"MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALz2rTYj017MEtD6\n" -"i2flctgTtL4awpTPn2iCvjkMD/owj2N7LRbnpJ5ZDUUkPW8OEDH9NEEx86aFwg+w\n" -"GNzmxqRg5rA92iKouM4iU76IfCdtuYu4yyq57T2+C9mwqO5OvYXkM4VBjjcGr813\n" -"7huMcki104znz7HADYj/y8Nu0jyPAgMBAAECgYEAkJStMx92OrIqM3t/7p7AoFNz\n" -"l4EiIHo7ndtrcHqqPuOo0pUMitnyOrYoUR7JYo+AFL+Hm9p8co0lTpuOanaoxQk8\n" -"Hs9MDzvp4OjRyr0+mVSqKGJOVckXA1Bi0X6jbtOjm3bn7mp1ICPrnv6VB8VP1ef1\n" -"/NgcOai1RHEfXQWAWQECQQD7s3LqUFN5vdYuc/RQ98/NAuKUSvy7zU8tZ7r1Ea2w\n" -"RoxAcakc6Xh75u+9VhsbVyDHDEjWbTW5H2uqkPeotR2tAkEAwDDpS83xH6ilhNnP\n" -"Cors2WKKXZBkw+ZczThJXWIWrbo0zrTDYWYeo46HQRulJawW0iDEkVahk62uC7Go\n" -"Im5SqwJAXcShd/dK0dzOEOozx4I6kPaVMIerFc/Lwm+Vb70RRs1RbKSrStETiJ0l\n" -"DRUp7gqMdHr4G6H91KSG+Lke+mPW1QJAdG8tZ5dktWFepZWvMRvpUem5GeYYpfYx\n" -"0sJ+7+w1ARsGUxSAKcnMVhpLJs6wxpnzWWowrDxntyhJgRwoWHOt8QJBAPuX7gGH\n" -"Lxm7PpWgYYhnAiHGQmsiYEWZpTDHw3qODbSyANFjAKgaQZ3sHy4gdksmhbNJK60e\n" -"K7mLk9hTLlVPXJM=\n" -"-----END PRIVATE KEY-----"; - - test_assert(dcrypt_key_load_private(&priv_key, - key, NULL, NULL, &error)); - if (priv_key == NULL) - i_fatal("%s", error); - dcrypt_key_convert_private_to_public(priv_key, &pub_key); - test_assert(dcrypt_sign(priv_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, - data, strlen(data), signature, 0, &error)); - /* verify signature */ - test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, - data, strlen(data), - signature->data, signature->used, &valid, 0, &error) && valid); - - dcrypt_key_unref_public(&pub_key); - dcrypt_key_unref_private(&priv_key); - - test_end(); -} - -static void test_sign_verify_ecdsa(void) -{ - const char *error = NULL; - bool valid; - struct dcrypt_private_key *priv_key = NULL; - struct dcrypt_public_key *pub_key = NULL; - - buffer_t *signature = - buffer_create_dynamic(pool_datastack_create(), 128); - const char *data = "signed data"; - - test_begin("sign and verify (ecdsa)"); - const char *key = "-----BEGIN PRIVATE KEY-----\n" -"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgZ4AMMyJ9XDl5lKM2\n" -"vusbT1OQ6VzBWBkB3/4syovaKtyhRANCAAQHTR+6L2qMh5fdcMZF+Y1rctBsq8Oy\n" -"7jZ4uV+MiuaoGNQ5sTxlcv6ETX/XrEDq4S/DUhFKzQ6u9VXYZImvRCT1\n" -"-----END PRIVATE KEY-----"; - - test_assert(dcrypt_key_load_private(&priv_key, - key, NULL, NULL, &error)); - if (priv_key == NULL) - i_fatal("%s", error); - dcrypt_key_convert_private_to_public(priv_key, &pub_key); - test_assert(dcrypt_sign(priv_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, - data, strlen(data), signature, 0, &error)); - /* verify signature */ - test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, - data, strlen(data), signature->data, - signature->used, &valid, 0, &error) && valid); - - dcrypt_key_unref_public(&pub_key); - dcrypt_key_unref_private(&priv_key); - - test_end(); -} - -static void test_static_verify_ecdsa(void) -{ - test_begin("static verify (ecdsa)"); - const char *input = "hello, world"; - const char *priv_key_pem = - "-----BEGIN PRIVATE KEY-----\n" - "MGcCAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcETTBLAgEBBCC25AkD65uhlZXCAdwN\n" - "yLJV2ui8A/CUyqyEMrezvwgMO6EkAyIAAybRUR3MsH0+0PQcDwkrXOJ9aePwzTQV\n" - "DN51+n1JCxbI\n" - "-----END PRIVATE KEY-----"; - const char *pub_key_pem = - "-----BEGIN PUBLIC KEY-----\n" - "MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADJtFRHcywfT7Q9BwPCStc4n1p4/DN\n" - "NBUM3nX6fUkLFsg=\n" - "-----END PUBLIC KEY-----"; - - const unsigned char sig[] = { - 0x30,0x45,0x02,0x20,0x2c,0x76,0x20,0x5e,0xfc,0xa6,0x9e,0x16, - 0x44,0xb3,0xbc,0xbf,0xcc,0x43,0xc1,0x08,0x76,0x4a,0xe8,0x60, - 0xc5,0x9b,0x99,0x20,0x5b,0x44,0x33,0x5c,0x38,0x84,0x63,0xcb, - 0x02,0x21,0x00,0xa3,0x67,0xed,0x57,0xbf,0x59,0x46,0xb7,0x0c, - 0x7b,0xec,0x4f,0x78,0x14,0xec,0xfa,0x8d,0xa2,0x85,0x48,0xea, - 0xe1,0xaf,0x9e,0xbf,0x04,0xac,0x0e,0x41,0xfe,0x84,0x0e - }; - - struct dcrypt_keypair pair; - bool valid; - const char *error; - - i_zero(&pair); - /* static key test */ - test_assert(dcrypt_key_load_public(&pair.pub, pub_key_pem, NULL)); - test_assert(dcrypt_key_load_private(&pair.priv, priv_key_pem, NULL, NULL, NULL)); - /* validate signature */ - test_assert(dcrypt_verify(pair.pub, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, - input, strlen(input), - sig, sizeof(sig), &valid, 0, &error) && - valid == TRUE); - - dcrypt_keypair_unref(&pair); - - test_end(); -} - -static void test_jwk_keys(void) -{ - /* Make sure this matches what comes out from store private */ - const char *jwk_key_json = "{\"kty\":\"EC\"," - "\"crv\":\"P-256\"," - "\"x\":\"Kp0Y4-Wpt-D9t_2XenFIj0LmvaZByLG69yOisek4aMI\"," - "\"y\":\"wjEPB5BhH5SRPw1cCN5grWrLCphrW19fCFR8p7c9O5o\"," - "\"use\":\"sig\"," - "\"kid\":\"123\"," - "\"d\":\"Po2z9rs86J2Qb_xWprr4idsWNPlgKf3G8-mftnE2ync\"}"; - /* Acquired using another tool */ - const char *pem_key = - "-----BEGIN PUBLIC KEY-----\n" - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKp0Y4+Wpt+D9t/2XenFIj0LmvaZB\n" - "yLG69yOisek4aMLCMQ8HkGEflJE/DVwI3mCtassKmGtbX18IVHyntz07mg==\n" - "-----END PUBLIC KEY-----"; - test_begin("test_jwk_keys"); - struct dcrypt_keypair pair; - buffer_t *pem = t_buffer_create(256); - i_zero(&pair); - - test_assert(dcrypt_key_load_public(&pair.pub, jwk_key_json, NULL)); - test_assert(dcrypt_key_load_private(&pair.priv, jwk_key_json, NULL, NULL, NULL)); - - /* test accessors */ - test_assert_strcmp(dcrypt_key_get_id_public(pair.pub), "123"); - test_assert(dcrypt_key_get_usage_public(pair.pub) == DCRYPT_KEY_USAGE_SIGN); - - /* make sure we got the right key */ - test_assert(dcrypt_key_store_public(pair.pub, DCRYPT_FORMAT_PEM, pem, NULL)); - test_assert_strcmp(str_c(pem), pem_key); - - str_truncate(pem, 0); - test_assert(dcrypt_key_store_private(pair.priv, DCRYPT_FORMAT_JWK, NULL, pem, NULL, NULL, NULL)); - test_assert_strcmp(str_c(pem), jwk_key_json); - - dcrypt_keypair_unref(&pair); - - test_end(); -} - -static void test_static_verify_rsa(void) -{ - const char *error = NULL; - bool valid; - struct dcrypt_public_key *pub_key = NULL; - - test_begin("static verify (rsa)"); - const char *data = "test signature input\n"; - const unsigned char sig[] = { - 0x6f,0x1b,0xfb,0xdd,0xdb,0xb1,0xcd,0x6f,0xf1,0x1b, - 0xb8,0xad,0x71,0x75,0x6c,0x87,0x22,0x11,0xe4,0xc3, - 0xe7,0xca,0x15,0x04,0xda,0x98,0xab,0x07,0x27,0xcc, - 0x5a,0x4d,0xab,0xac,0x37,0x7a,0xff,0xd2,0xdf,0x37, - 0x58,0x37,0x53,0x46,0xd5,0x6d,0x9d,0x73,0x83,0x90, - 0xea,0x5e,0x2c,0xc7,0x51,0x9e,0xc4,0xda,0xc5,0x7d, - 0xa5,0xcd,0xb7,0xd7,0x41,0x23,0x6d,0xb9,0x6d,0xe0, - 0x99,0xa1,0x63,0x6b,0x60,0x5f,0x15,0x5b,0xda,0x21, - 0x17,0x4c,0x37,0x68,0x67,0x7f,0x8e,0x02,0x93,0xd2, - 0x86,0xdd,0xe5,0xa7,0xc3,0xd9,0x93,0x8b,0x0c,0x56, - 0x1d,0x5c,0x60,0x63,0x3e,0x8b,0xbe,0x1f,0xb2,0xe7, - 0x7f,0xe5,0x66,0x6f,0xcd,0x2b,0x0c,0x02,0x2a,0x12, - 0x96,0x86,0x66,0x00,0xff,0x12,0x8a,0x79 - }; - const char *key = "-----BEGIN PUBLIC KEY-----\n" -"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC89q02I9NezBLQ+otn5XLYE7S+\n" -"GsKUz59ogr45DA/6MI9jey0W56SeWQ1FJD1vDhAx/TRBMfOmhcIPsBjc5sakYOaw\n" -"PdoiqLjOIlO+iHwnbbmLuMsque09vgvZsKjuTr2F5DOFQY43Bq/Nd+4bjHJItdOM\n" -"58+xwA2I/8vDbtI8jwIDAQAB\n" -"-----END PUBLIC KEY-----"; - - test_assert(dcrypt_key_load_public(&pub_key, key, &error)); - if (pub_key == NULL) - i_fatal("%s", error); - test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, - data, strlen(data), - sig, sizeof(sig), &valid, DCRYPT_PADDING_RSA_PKCS1, &error) && - valid); - dcrypt_key_unref_public(&pub_key); - - test_end(); -} - -/* Sample values from RFC8292 */ -static void test_static_verify_ecdsa_x962(void) -{ - const char *error = NULL; - bool valid; - struct dcrypt_public_key *pub_key = NULL; - - test_begin("static verify (ecdsa x9.62)"); - const char *data = - "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c" - "2guZXhhbXBsZS5uZXQiLCJleHAiOjE0NTM1MjM3NjgsInN1YiI6Im1haWx0bzp" - "wdXNoQGV4YW1wbGUuY29tIn0"; - const unsigned char sig[] = { - 0x8b,0x70,0x98,0x6f,0xbb,0x78,0xc5,0xfc,0x42,0x0e,0xab, - 0xa9,0xb4,0x53,0x9e,0xa4,0x2f,0x46,0x02,0xef,0xc7,0x2c, - 0x69,0x0c,0x94,0xcb,0x82,0x19,0x22,0xb6,0xae,0x98,0x94, - 0x7e,0x72,0xbd,0xa2,0x31,0x70,0x0d,0x76,0xf5,0x26,0xb1, - 0x2b,0xb6,0x6c,0xac,0x6b,0x33,0x63,0x8e,0xf5,0xb6,0x2f, - 0xd3,0xa4,0x49,0x21,0xf3,0xbe,0x80,0xf5,0xa0 - }; - const char *key = -"-----BEGIN PUBLIC KEY-----\n" -"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDUfHPKLVFQzVvnCPGyfucbECzPDa\n" -"7rWbXriLcysAjEcXpgrmHhINiJz51G5T9EI8J8Dlqr2iNLCTljYSYKUE+w==\n" -"-----END PUBLIC KEY-----"; - - test_assert(dcrypt_key_load_public(&pub_key, key, &error)); - if (pub_key == NULL) - i_fatal("%s", error); - test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_X962, - data, strlen(data), - sig, sizeof(sig), &valid, DCRYPT_PADDING_RSA_PKCS1, &error) && - valid); - dcrypt_key_unref_public(&pub_key); - - test_end(); -} - - -int main(void) -{ - const char *error; - - if (!dcrypt_initialize(NULL, NULL, &error)) { - i_error("No functional dcrypt backend found - " - "skipping tests: %s", error); - return 0; - } - - static void (*const test_functions[])(void) = { - test_cipher_test_vectors, - test_cipher_aead_test_vectors, - test_hmac_test_vectors, - test_load_v1_keys, - test_load_v1_key, - test_load_v1_public_key, - test_load_v2_key, - test_load_v2_public_key, - test_get_info_v2_key, - test_gen_and_get_info_rsa_pem, - test_get_info_rsa_private_key, - test_get_info_invalid_keys, - test_get_info_key_encrypted, - test_get_info_pw_encrypted, - test_password_change, - test_load_invalid_keys, - test_raw_keys, - test_jwk_keys, - test_sign_verify_rsa, - test_sign_verify_ecdsa, - test_static_verify_ecdsa, - test_static_verify_rsa, - test_static_verify_ecdsa_x962, - NULL - }; - - int ret = test_run(test_functions); - - dcrypt_deinitialize(); - - return ret; -} diff --git a/src/lib-dcrypt/test-stream.c b/src/lib-dcrypt/test-stream.c deleted file mode 100644 index 8c1a98528b..0000000000 --- a/src/lib-dcrypt/test-stream.c +++ /dev/null @@ -1,639 +0,0 @@ -/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "buffer.h" -#include "str.h" -#include "dcrypt.h" -#include "dcrypt-iostream.h" -#include "ostream.h" -#include "ostream-encrypt.h" -#include "istream.h" -#include "istream-decrypt.h" -#include "istream-hash.h" -#include "istream-base64.h" -#include "randgen.h" -#include "hash-method.h" -#include "test-common.h" -#include "hex-binary.h" - -#include -#include -#include - -static const char key_v1_priv[] = - "-----BEGIN PRIVATE KEY-----\n" - "MIGpAgEAMBAGByqGSM49AgEGBSuBBAAjBIGRMIGOAgEBBEGz2V2VMi/5s+Z+GJh7\n" - "4WfqZjZUpqqm+NJWojm6BbrZMY+9ZComlTGVcUZ007acFxV93oMmrfmtRUb5ynrb\n" - "MRFskKFGA0QAAwHrAJc8TvyPzspOoz6UH1C1YRmaUVm8tsLu2d0dYtZeOKJUl52J\n" - "4o8MKIg+ce4q0mTNFrhj+glKj29ppWti6JGAQA==\n" - "-----END PRIVATE KEY-----"; - -static const char key_v1_pub[] = - "-----BEGIN PUBLIC KEY-----\n" - "MFgwEAYHKoZIzj0CAQYFK4EEACMDRAADAesAlzxO/I/Oyk6jPpQfULVhGZpRWby2\n" - "wu7Z3R1i1l44olSXnYnijwwoiD5x7irSZM0WuGP6CUqPb2mla2LokYBA\n" - "-----END PUBLIC KEY-----"; - -static const char key_v2_priv[] = - "-----BEGIN PRIVATE KEY-----\n" - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgtuQJA+uboZWVwgHc\n" - "DciyVdrovAPwlMqshDK3s78IDDuhRANCAAQm0VEdzLB9PtD0HA8JK1zifWnj8M00\n" - "FQzedfp9SQsWyA8dzs5/NFR5MTe6Xbh/ndKEs1zZH3vZ4FlNrilZc0st\n" - "-----END PRIVATE KEY-----"; - -static const char key_v2_pub[] = - "-----BEGIN PUBLIC KEY-----\n" - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJtFRHcywfT7Q9BwPCStc4n1p4/DN\n" - "NBUM3nX6fUkLFsgPHc7OfzRUeTE3ul24f53ShLNc2R972eBZTa4pWXNLLQ==\n" - "-----END PUBLIC KEY-----"; - -static const char test_sample_v1_hash[] = - "1d7cc2cc1f1983f76241cc42389911e88590ad58cf9d54cafeb5b198d3723dd1"; -static const char test_sample_v1_short_hash[] = - "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"; -static const char test_sample_v2_hash[] = - "2e31218656dd34db65b321688bf418dee4ee785e99eb9c21e0d29b4af27a863e"; - -static struct dcrypt_keypair test_v1_kp; -static struct dcrypt_keypair test_v2_kp; - -static void test_static_v1_input(void) -{ - ssize_t siz; - const struct hash_method *hash = hash_method_lookup("sha256"); - unsigned char hash_ctx[hash->context_size]; - unsigned char hash_dgst[hash->digest_size]; - hash->init(hash_ctx); - - test_begin("test_static_v1_input"); - - struct istream *is_1 = - i_stream_create_file(DCRYPT_SRC_DIR"/sample-v1.asc", - IO_BLOCK_SIZE); - struct istream *is_2 = i_stream_create_base64_decoder(is_1); - i_stream_unref(&is_1); - struct istream *is_3 = i_stream_create_decrypt(is_2, test_v1_kp.priv); - i_stream_unref(&is_2); - struct istream *is_4 = i_stream_create_hash(is_3, hash, hash_ctx); - i_stream_unref(&is_3); - - while((siz = i_stream_read(is_4))>0) { i_stream_skip(is_4, siz); } - - if (is_4->stream_errno != 0) - i_debug("error: %s", i_stream_get_error(is_4)); - - test_assert(is_4->stream_errno == 0); - - i_stream_unref(&is_4); - - hash->result(hash_ctx, hash_dgst); - - test_assert(strcmp(test_sample_v1_hash, - binary_to_hex(hash_dgst, sizeof(hash_dgst))) == 0); - - test_end(); -} - -static void test_static_v1_input_short(void) -{ - ssize_t siz; - const struct hash_method *hash = hash_method_lookup("sha256"); - unsigned char hash_ctx[hash->context_size]; - unsigned char hash_dgst[hash->digest_size]; - hash->init(hash_ctx); - - test_begin("test_static_v1_input_short"); - - struct istream *is_1 = - i_stream_create_file(DCRYPT_SRC_DIR"/sample-v1_short.asc", - IO_BLOCK_SIZE); - struct istream *is_2 = i_stream_create_base64_decoder(is_1); - i_stream_unref(&is_1); - struct istream *is_3 = i_stream_create_decrypt(is_2, test_v1_kp.priv); - i_stream_unref(&is_2); - struct istream *is_4 = i_stream_create_hash(is_3, hash, hash_ctx); - i_stream_unref(&is_3); - - while((siz = i_stream_read(is_4))>0) { i_stream_skip(is_4, siz); } - - if (is_4->stream_errno != 0) - i_debug("error: %s", i_stream_get_error(is_4)); - - test_assert(is_4->stream_errno == 0); - - i_stream_unref(&is_4); - - hash->result(hash_ctx, hash_dgst); - - test_assert(strcmp(test_sample_v1_short_hash, - binary_to_hex(hash_dgst, sizeof(hash_dgst))) == 0); - - test_end(); -} - -static void test_static_v2_input(void) -{ - test_begin("test_static_v2_input"); - - ssize_t amt; - const struct hash_method *hash = hash_method_lookup("sha256"); - unsigned char hash_ctx[hash->context_size]; - unsigned char hash_dgst[hash->digest_size]; - hash->init(hash_ctx); - - struct istream *is_1 = - i_stream_create_file(DCRYPT_SRC_DIR"/sample-v2.asc", - IO_BLOCK_SIZE); - struct istream *is_2 = i_stream_create_base64_decoder(is_1); - i_stream_unref(&is_1); - struct istream *is_3 = i_stream_create_decrypt(is_2, test_v2_kp.priv); - i_stream_unref(&is_2); - struct istream *is_4 = i_stream_create_hash(is_3, hash, hash_ctx); - i_stream_unref(&is_3); - - while((amt = i_stream_read(is_4))>0) { i_stream_skip(is_4, amt); } - - if (is_4->stream_errno != 0) - i_debug("error: %s", i_stream_get_error(is_4)); - - test_assert(is_4->stream_errno == 0); - - i_stream_unref(&is_4); - - hash->result(hash_ctx, hash_dgst); - - test_assert(strcmp(test_sample_v2_hash, - binary_to_hex(hash_dgst, sizeof(hash_dgst))) == 0); - - test_end(); - -/** this code is left here to show how the sample file is created - struct istream *is = - i_stream_create_file("../lib-fts/udhr_fra.txt", 8192); - struct istream *is_2 = i_stream_create_hash(is, hash, hash_ctx); - int fd = open("sample-v2.bin", O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU); - struct ostream *os = o_stream_create_fd_file(fd, 0, TRUE); - struct ostream *os_2 = o_stream_create_encrypt(os, - "aes-256-gcm-sha256", test_v2_kp.pub, - IO_STREAM_ENC_INTEGRITY_AEAD); - const unsigned char *ptr; - size_t siz; - - while(i_stream_read_data(is_2, &ptr, &siz, 0)>0) { - o_stream_nsend(os_2, ptr, siz); - i_stream_skip(is_2, siz); - } - - i_assert(o_stream_finish(os_2) > 0); - - o_stream_close(os_2); - i_stream_close(is_2); - - hash->result(hash_ctx, hash_dgst); - printf("%s\n", binary_to_hex(hash_dgst, sizeof(hash_dgst))); -*/ -} - -static void test_write_read_v1(void) -{ - test_begin("test_write_read_v1"); - unsigned char payload[IO_BLOCK_SIZE]; - const unsigned char *ptr; - size_t pos = 0, siz; - random_fill(payload, IO_BLOCK_SIZE); - - buffer_t *buf = buffer_create_dynamic(default_pool, sizeof(payload)); - struct ostream *os = o_stream_create_buffer(buf); - struct ostream *os_2 = o_stream_create_encrypt(os, - "", test_v2_kp.pub, IO_STREAM_ENC_VERSION_1); - o_stream_nsend(os_2, payload, sizeof(payload)); - - if (os_2->stream_errno != 0) - i_debug("error: %s", o_stream_get_error(os_2)); - - test_assert(os_2->stream_errno == 0); - test_assert(o_stream_finish(os_2) > 0); - test_assert(os_2->stream_errno == 0); - - o_stream_unref(&os); - o_stream_unref(&os_2); - - struct istream *is = test_istream_create_data(buf->data, buf->used); - struct istream *is_2 = i_stream_create_decrypt(is, test_v2_kp.priv); - - size_t offset = 0; - test_istream_set_allow_eof(is, FALSE); - test_istream_set_size(is, 0); - while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { - if (offset == buf->used) - test_istream_set_allow_eof(is, TRUE); - else - test_istream_set_size(is, ++offset); - - test_assert_idx(pos + siz <= sizeof(payload), pos); - if (pos + siz > sizeof(payload)) - break; - test_assert_idx(siz == 0 || - memcmp(ptr, payload + pos, siz) == 0, pos); - i_stream_skip(is_2, siz); pos += siz; - } - - test_assert(is_2->stream_errno == 0); - - i_stream_unref(&is); - i_stream_unref(&is_2); - buffer_free(&buf); - - test_end(); -} - -static void test_write_read_v1_short(void) -{ - test_begin("test_write_read_v1_short"); - unsigned char payload[1]; - const unsigned char *ptr; - size_t pos = 0, siz; - random_fill(payload, 1); - - buffer_t *buf = buffer_create_dynamic(default_pool, 64); - struct ostream *os = o_stream_create_buffer(buf); - struct ostream *os_2 = o_stream_create_encrypt(os, - "", test_v2_kp.pub, IO_STREAM_ENC_VERSION_1); - o_stream_nsend(os_2, payload, sizeof(payload)); - - if (os_2->stream_errno != 0) - i_debug("error: %s", o_stream_get_error(os_2)); - - test_assert(os_2->stream_errno == 0); - test_assert(o_stream_finish(os_2) > 0); - test_assert(os_2->stream_errno == 0); - - o_stream_unref(&os); - o_stream_unref(&os_2); - - struct istream *is = test_istream_create_data(buf->data, buf->used); - struct istream *is_2 = i_stream_create_decrypt(is, test_v2_kp.priv); - - size_t offset = 0; - test_istream_set_allow_eof(is, FALSE); - test_istream_set_size(is, 0); - while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { - if (offset == buf->used) - test_istream_set_allow_eof(is, TRUE); - else - test_istream_set_size(is, ++offset); - - test_assert_idx(pos + siz <= sizeof(payload), pos); - if (siz > sizeof(payload) || pos + siz > sizeof(payload)) - break; - test_assert_idx(siz == 0 || - memcmp(ptr, payload + pos, siz) == 0, pos); - i_stream_skip(is_2, siz); pos += siz; - } - - test_assert(is_2->stream_errno == 0); - - i_stream_unref(&is); - i_stream_unref(&is_2); - buffer_free(&buf); - - test_end(); -} - -static void test_write_read_v1_empty(void) -{ - const unsigned char *ptr; - size_t siz; - test_begin("test_write_read_v1_empty"); - buffer_t *buf = buffer_create_dynamic(default_pool, 64); - struct ostream *os = o_stream_create_buffer(buf); - struct ostream *os_2 = o_stream_create_encrypt(os, - "", test_v1_kp.pub, IO_STREAM_ENC_VERSION_1); - test_assert(o_stream_finish(os_2) > 0); - if (os_2->stream_errno != 0) - i_debug("error: %s", o_stream_get_error(os_2)); - - o_stream_unref(&os); - o_stream_unref(&os_2); - /* this should've been enough */ - - struct istream *is = test_istream_create_data(buf->data, buf->used); - struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); - - /* read should not fail */ - test_istream_set_allow_eof(is, FALSE); - test_istream_set_size(is, 0); - size_t offset = 0; - ssize_t ret; - while ((ret = i_stream_read_data(is_2, &ptr, &siz, 0)) >= 0) { - test_assert(ret == 0); - if (offset == buf->used) - test_istream_set_allow_eof(is, TRUE); - else - test_istream_set_size(is, ++offset); - }; - - test_assert(is_2->stream_errno == 0); - if (is_2->stream_errno != 0) - i_debug("error: %s", i_stream_get_error(is_2)); - i_stream_unref(&is); - i_stream_unref(&is_2); - buffer_free(&buf); - test_end(); -} - -static void test_write_read_v2(void) -{ - test_begin("test_write_read_v2"); - unsigned char payload[IO_BLOCK_SIZE*10]; - const unsigned char *ptr; - size_t pos = 0, siz; - random_fill(payload, IO_BLOCK_SIZE*10); - - buffer_t *buf = buffer_create_dynamic(default_pool, sizeof(payload)); - struct ostream *os = o_stream_create_buffer(buf); - struct ostream *os_2 = o_stream_create_encrypt(os, - "aes-256-gcm-sha256", test_v1_kp.pub, - IO_STREAM_ENC_INTEGRITY_AEAD); - o_stream_nsend(os_2, payload, sizeof(payload)); - test_assert(o_stream_finish(os_2) > 0); - if (os_2->stream_errno != 0) - i_debug("error: %s", o_stream_get_error(os_2)); - - o_stream_unref(&os); - o_stream_unref(&os_2); - - struct istream *is = test_istream_create_data(buf->data, buf->used); - /* test regression where read fails due to incorrect behaviour - when buffer is full before going to decrypt code */ - i_stream_set_max_buffer_size(is, 8192); - test_assert(i_stream_read(is) > 0); - struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); - - size_t offset = 0; - test_istream_set_size(is, 0); - test_istream_set_allow_eof(is, FALSE); - while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { - if (offset == buf->used) - test_istream_set_allow_eof(is, TRUE); - else - test_istream_set_size(is, ++offset); - - test_assert_idx(pos + siz <= sizeof(payload), pos); - if (pos + siz > sizeof(payload)) break; - test_assert_idx(siz == 0 || - memcmp(ptr, payload + pos, siz) == 0, pos); - i_stream_skip(is_2, siz); pos += siz; - } - - test_assert(is_2->stream_errno == 0); - if (is_2->stream_errno != 0) - i_debug("error: %s", i_stream_get_error(is_2)); - - /* test seeking */ - for (size_t i = sizeof(payload)-100; i > 100; i -= 100) { - i_stream_seek(is_2, i); - test_assert_idx(i_stream_read_data(is_2, &ptr, &siz, 0) == 1, i); - test_assert_idx(memcmp(ptr, payload + i, siz) == 0, i); - } - i_stream_seek(is_2, 0); - test_assert(i_stream_read_data(is_2, &ptr, &siz, 0) == 1); - test_assert(memcmp(ptr, payload, siz) == 0); - - i_stream_unref(&is); - i_stream_unref(&is_2); - buffer_free(&buf); - - test_end(); -} - -static void test_write_read_v2_short(void) -{ - test_begin("test_write_read_v2_short"); - unsigned char payload[1]; - const unsigned char *ptr; - size_t pos = 0, siz; - random_fill(payload, 1); - - buffer_t *buf = buffer_create_dynamic(default_pool, 64); - struct ostream *os = o_stream_create_buffer(buf); - struct ostream *os_2 = o_stream_create_encrypt(os, - "aes-256-gcm-sha256", test_v1_kp.pub, - IO_STREAM_ENC_INTEGRITY_AEAD); - o_stream_nsend(os_2, payload, sizeof(payload)); - test_assert(o_stream_finish(os_2) > 0); - if (os_2->stream_errno != 0) - i_debug("error: %s", o_stream_get_error(os_2)); - - o_stream_unref(&os); - o_stream_unref(&os_2); - - struct istream *is = test_istream_create_data(buf->data, buf->used); - struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); - - size_t offset = 0; - test_istream_set_allow_eof(is, FALSE); - test_istream_set_size(is, 0); - while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { - if (offset == buf->used) - test_istream_set_allow_eof(is, TRUE); - test_istream_set_size(is, ++offset); - - test_assert_idx(pos + siz <= sizeof(payload), pos); - if (siz > sizeof(payload) || pos + siz > sizeof(payload)) - break; - test_assert_idx(siz == 0 || - memcmp(ptr, payload + pos, siz) == 0, pos); - i_stream_skip(is_2, siz); pos += siz; - } - - test_assert(is_2->stream_errno == 0); - if (is_2->stream_errno != 0) - i_debug("error: %s", i_stream_get_error(is_2)); - - i_stream_unref(&is); - i_stream_unref(&is_2); - buffer_free(&buf); - - test_end(); -} - -static void test_write_read_v2_empty(void) -{ - const unsigned char *ptr; - size_t siz; - test_begin("test_write_read_v2_empty"); - buffer_t *buf = buffer_create_dynamic(default_pool, 64); - struct ostream *os = o_stream_create_buffer(buf); - struct ostream *os_2 = o_stream_create_encrypt(os, - "aes-256-gcm-sha256", test_v1_kp.pub, - IO_STREAM_ENC_INTEGRITY_AEAD); - test_assert(o_stream_finish(os_2) > 0); - if (os_2->stream_errno != 0) - i_debug("error: %s", o_stream_get_error(os_2)); - - o_stream_unref(&os); - o_stream_unref(&os_2); - /* this should've been enough */ - - struct istream *is = test_istream_create_data(buf->data, buf->used); - struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); - - /* read should not fail */ - size_t offset = 0; - test_istream_set_allow_eof(is, FALSE); - test_istream_set_size(is, 0); - ssize_t ret; - while ((ret = i_stream_read_data(is_2, &ptr, &siz, 0)) >= 0) { - test_assert(ret == 0); - if (offset == buf->used) - test_istream_set_allow_eof(is, TRUE); - test_istream_set_size(is, ++offset); - }; - - test_assert(is_2->stream_errno == 0); - if (is_2->stream_errno != 0) - i_debug("error: %s", i_stream_get_error(is_2)); - i_stream_unref(&is); - i_stream_unref(&is_2); - buffer_free(&buf); - test_end(); -} - -static int -no_op_cb(const char *digest ATTR_UNUSED, - struct dcrypt_private_key **priv_key_r ATTR_UNUSED, - const char **error_r ATTR_UNUSED, - void *context ATTR_UNUSED) -{ - return 0; -} - -static void test_read_0_to_400_byte_garbage(void) -{ - test_begin("test_read_0_to_100_byte_garbage"); - - char data[512]; - memset(data, 0, sizeof(data)); - - for (size_t s = 0; s <= 400; ++s) { - struct istream *is = test_istream_create_data(data, s); - struct istream *ds = i_stream_create_decrypt_callback(is, - no_op_cb, NULL); - test_istream_set_size(is, 0); - test_istream_set_allow_eof(is, FALSE); - ssize_t siz = 0; - for (size_t offset = 0; offset <= s && siz == 0; offset++) { - if (offset == s) - test_istream_set_allow_eof(is, TRUE); - test_istream_set_size(is, offset); - siz = i_stream_read(ds); - } - test_assert_idx(siz < 0, s); - i_stream_unref(&ds); - i_stream_unref(&is); - } - - test_end(); -} - -static void test_read_large_header(void) -{ - test_begin("test_read_large_header"); - - struct istream *is = - test_istream_create_data(IOSTREAM_CRYPT_MAGIC, - sizeof(IOSTREAM_CRYPT_MAGIC)); - struct istream *ds = - i_stream_create_decrypt_callback(is, no_op_cb, NULL); - test_istream_set_allow_eof(is, FALSE); - test_istream_set_max_buffer_size(is, sizeof(IOSTREAM_CRYPT_MAGIC)); - - test_assert(i_stream_read(ds) == -1); - i_stream_unref(&ds); - i_stream_unref(&is); - - test_end(); -} - -static void test_read_increment(void) -{ - test_begin("test_read_increment"); - - ssize_t amt, total, i; - - struct istream *is_1 = i_stream_create_file( - DCRYPT_SRC_DIR"/sample-v2.asc", IO_BLOCK_SIZE); - struct istream *is_2 = i_stream_create_base64_decoder(is_1); - i_stream_unref(&is_1); - struct istream *is_3 = i_stream_create_decrypt(is_2, test_v2_kp.priv); - i_stream_unref(&is_2); - total = 0; - i = 500; - - i_stream_set_max_buffer_size(is_3, i); - while((amt = i_stream_read(is_3)) > 0) { - total += amt; - i_stream_set_max_buffer_size(is_3, ++i); - } - - /* make sure snapshotting works: */ - i_stream_skip(is_3, 1); - test_assert(i_stream_read(is_3) == -1); - - test_assert(total == 13534); - test_assert(is_3->stream_errno == 0); - test_assert(is_3->eof); - - i_stream_unref(&is_3); - - test_end(); -} - -static void test_free_keys() -{ - dcrypt_key_unref_private(&test_v1_kp.priv); - dcrypt_key_unref_public(&test_v1_kp.pub); - dcrypt_key_unref_private(&test_v2_kp.priv); - dcrypt_key_unref_public(&test_v2_kp.pub); -} - -int main(void) -{ - struct dcrypt_settings set = { - .module_dir = ".libs" - }; - const char *error; - - if (!dcrypt_initialize(NULL, &set, &error)) { - i_error("No functional dcrypt backend found - " - "skipping tests: %s", error); - return 0; - } - - test_assert(dcrypt_key_load_private(&test_v1_kp.priv, key_v1_priv, - NULL, NULL, NULL)); - test_assert(dcrypt_key_load_public(&test_v1_kp.pub, key_v1_pub, NULL)); - test_assert(dcrypt_key_load_private(&test_v2_kp.priv, key_v2_priv, - NULL, NULL, NULL)); - test_assert(dcrypt_key_load_public(&test_v2_kp.pub, key_v2_pub, NULL)); - - static void (*const test_functions[])(void) = { - test_static_v1_input, - test_static_v1_input_short, - test_static_v2_input, - test_read_increment, - test_write_read_v1, - test_write_read_v1_short, - test_write_read_v1_empty, - test_write_read_v2, - test_write_read_v2_short, - test_write_read_v2_empty, - test_free_keys, - test_read_0_to_400_byte_garbage, - test_read_large_header, - NULL - }; - - return test_run(test_functions); -} diff --git a/src/lib-oauth2/Makefile.am b/src/lib-oauth2/Makefile.am deleted file mode 100644 index 7ece5fc885..0000000000 --- a/src/lib-oauth2/Makefile.am +++ /dev/null @@ -1,70 +0,0 @@ -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/lib \ - -I$(top_srcdir)/src/lib-test \ - -I$(top_srcdir)/src/lib-http \ - -I$(top_srcdir)/src/lib-dcrypt \ - -I$(top_srcdir)/src/lib-dict \ - -I$(top_srcdir)/src/lib-settings - -noinst_LTLIBRARIES=liboauth2.la - -pkginc_libdir=$(pkgincludedir) -pkginc_lib_HEADERS = \ - oauth2.h - -noinst_HEADERS = \ - oauth2-private.h - -liboauth2_la_SOURCES = \ - oauth2.c \ - oauth2-request.c \ - oauth2-jwt.c \ - oauth2-key-cache.c - -test_programs = \ - test-oauth2-json \ - test-oauth2-jwt - -noinst_PROGRAMS = $(test_programs) - -test_libs = \ - $(noinst_LTLIBRARIES) \ - ../lib-dcrypt/libdcrypt.la \ - ../lib-http/libhttp.la \ - ../lib-dns/libdns.la \ - ../lib-ssl-iostream/libssl_iostream.la \ - ../lib-master/libmaster.la \ - ../lib-auth/libauth.la \ - ../lib-dict/libdict.la \ - ../lib-settings/libsettings.la \ - ../lib-test/libtest.la \ - ../lib/liblib.la -test_deps = \ - $(noinst_LTLIBRARIES) \ - ../lib-dcrypt/libdcrypt.la \ - ../lib-http/libhttp.la \ - ../lib-dns/libdns.la \ - ../lib-ssl-iostream/libssl_iostream.la \ - ../lib-master/libmaster.la \ - ../lib-auth/libauth.la \ - ../lib-dict/libdict.la \ - ../lib-settings/libsettings.la \ - ../lib-test/libtest.la \ - ../lib/liblib.la - -test_oauth2_json_SOURCES = test-oauth2-json.c -test_oauth2_json_LDADD = $(test_libs) -test_oauth2_json_DEPENDENCIES = $(test_deps) - -test_oauth2_jwt_SOURCES = test-oauth2-jwt.c -test_oauth2_jwt_LDADD = $(test_libs) -if HAVE_WHOLE_ARCHIVE -test_oauth2_jwt_LDFLAGS = -Wl,$(LD_WHOLE_ARCHIVE),../lib-ssl-iostream/.libs/libssl_iostream.a,$(LD_NO_WHOLE_ARCHIVE) -endif -test_oauth2_jwt_DEPENDENCIES = $(test_deps) - -check-local: - for bin in $(test_programs); do \ - if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ - done - diff --git a/src/lib-oauth2/oauth2-jwt.c b/src/lib-oauth2/oauth2-jwt.c deleted file mode 100644 index ec7ad46d4a..0000000000 --- a/src/lib-oauth2/oauth2-jwt.c +++ /dev/null @@ -1,489 +0,0 @@ -/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "buffer.h" -#include "str.h" -#include "hmac.h" -#include "array.h" -#include "hash-method.h" -#include "istream.h" -#include "iso8601-date.h" -#include "json-tree.h" -#include "array.h" -#include "base64.h" -#include "str-sanitize.h" -#include "dcrypt.h" -#include "var-expand.h" -#include "oauth2.h" -#include "oauth2-private.h" -#include "dict.h" - -#include - -static const char *get_field(const struct json_tree *tree, const char *key) -{ - const struct json_tree_node *root = json_tree_root(tree); - const struct json_tree_node *value_node = json_tree_find_key(root, key); - if (value_node == NULL || value_node->value_type == JSON_TYPE_OBJECT || - value_node->value_type == JSON_TYPE_ARRAY) - return NULL; - return json_tree_get_value_str(value_node); -} - -static int get_time_field(const struct json_tree *tree, const char *key, - int64_t *value_r) -{ - time_t tvalue; - const char *value = get_field(tree, key); - int tz_offset ATTR_UNUSED; - if (value == NULL) - return 0; - if (str_to_int64(value, value_r) == 0) { - if (*value_r < 0) - return -1; - return 1; - } else if (iso8601_date_parse((const unsigned char*)value, strlen(value), - &tvalue, &tz_offset)) { - if (tvalue < 0) - return -1; - *value_r = tvalue; - return 1; - } - return -1; -} - -/* Escapes '/' and '%' in identifier to %hex */ -static const char *escape_identifier(const char *identifier) -{ - size_t pos = strcspn(identifier, "/%"); - /* nothing to escape */ - if (identifier[pos] == '\0') - return identifier; - - size_t len = strlen(identifier); - string_t *new_id = t_str_new(len); - str_append_data(new_id, identifier, pos); - - for (size_t i = pos; i < len; i++) { - switch (identifier[i]) { - case '/': - str_append(new_id, "%2f"); - break; - case '%': - str_append(new_id, "%25"); - break; - default: - str_append_c(new_id, identifier[i]); - break; - } - } - return str_c(new_id); -} - -static int -oauth2_lookup_hmac_key(const struct oauth2_settings *set, const char *azp, - const char *alg, const char *key_id, - const buffer_t **hmac_key_r, const char **error_r) -{ - const char *base64_key; - const char *cache_key_id, *lookup_key; - int ret; - - cache_key_id = t_strconcat(azp, ".", alg, ".", key_id, NULL); - if (oauth2_validation_key_cache_lookup_hmac_key( - set->key_cache, cache_key_id, hmac_key_r) == 0) - return 0; - - - /* do a synchronous dict lookup */ - lookup_key = t_strconcat(DICT_PATH_SHARED, azp, "/", alg, "/", key_id, - NULL); - struct dict_op_settings dict_set = { - .username = NULL, - }; - if ((ret = dict_lookup(set->key_dict, &dict_set, pool_datastack_create(), - lookup_key, &base64_key, error_r)) < 0) { - return -1; - } else if (ret == 0) { - *error_r = t_strdup_printf("%s key '%s' not found", - alg, key_id); - return -1; - } - - /* decode key */ - buffer_t *key = t_base64_decode_str(base64_key); - if (key->used == 0) { - *error_r = "Invalid base64 encoded key"; - return -1; - } - oauth2_validation_key_cache_insert_hmac_key(set->key_cache, - cache_key_id, key); - *hmac_key_r = key; - return 0; -} - -static int -oauth2_validate_hmac(const struct oauth2_settings *set, const char *azp, - const char *alg, const char *key_id, - const char *const *blobs, const char **error_r) -{ - const struct hash_method *method; - - if (strcmp(alg, "HS256") == 0) - method = hash_method_lookup("sha256"); - else if (strcmp(alg, "HS384") == 0) - method = hash_method_lookup("sha384"); - else if (strcmp(alg, "HS512") == 0) - method = hash_method_lookup("sha512"); - else { - *error_r = t_strdup_printf("unsupported algorithm '%s'", alg); - return -1; - } - - const buffer_t *key; - if (oauth2_lookup_hmac_key(set, azp, alg, key_id, &key, error_r) < 0) - return -1; - - struct hmac_context ctx; - hmac_init(&ctx, key->data, key->used, method); - hmac_update(&ctx, blobs[0], strlen(blobs[0])); - hmac_update(&ctx, ".", 1); - hmac_update(&ctx, blobs[1], strlen(blobs[1])); - unsigned char digest[method->digest_size]; - - hmac_final(&ctx, digest); - - buffer_t *their_digest = - t_base64url_decode_str(BASE64_DECODE_FLAG_NO_PADDING, blobs[2]); - if (method->digest_size != their_digest->used || - !mem_equals_timing_safe(digest, their_digest->data, - method->digest_size)) { - *error_r = "Incorrect JWT signature"; - return -1; - } - return 0; -} - -static int -oauth2_lookup_pubkey(const struct oauth2_settings *set, const char *azp, - const char *alg, const char *key_id, - struct dcrypt_public_key **key_r, const char **error_r) -{ - const char *key_str; - const char *cache_key_id, *lookup_key; - int ret; - - cache_key_id = t_strconcat(azp, ".", alg, ".", key_id, NULL); - if (oauth2_validation_key_cache_lookup_pubkey( - set->key_cache, cache_key_id, key_r) == 0) - return 0; - - /* do a synchronous dict lookup */ - lookup_key = t_strconcat(DICT_PATH_SHARED, azp, "/", alg, "/", key_id, - NULL); - struct dict_op_settings dict_set = { - .username = NULL, - }; - if ((ret = dict_lookup(set->key_dict, &dict_set, pool_datastack_create(), - lookup_key, &key_str, error_r)) < 0) { - return -1; - } else if (ret == 0) { - *error_r = t_strdup_printf("%s key '%s' not found", - alg, key_id); - return -1; - } - - /* try to load key */ - struct dcrypt_public_key *pubkey; - const char *error; - if (!dcrypt_key_load_public(&pubkey, key_str, &error)) { - *error_r = t_strdup_printf("Cannot load key: %s", error); - return -1; - } - - /* cache key */ - oauth2_validation_key_cache_insert_pubkey(set->key_cache, cache_key_id, - pubkey); - *key_r = pubkey; - return 0; -} - -static int -oauth2_validate_rsa_ecdsa(const struct oauth2_settings *set, - const char *azp, const char *alg, const char *key_id, - const char *const *blobs, const char **error_r) -{ - const char *method; - enum dcrypt_padding padding; - enum dcrypt_signature_format sig_format; - - if (!dcrypt_is_initialized()) { - *error_r = "No crypto library loaded"; - return -1; - } - - if (str_begins(alg, "RS")) { - padding = DCRYPT_PADDING_RSA_PKCS1; - sig_format = DCRYPT_SIGNATURE_FORMAT_DSS; - } else if (str_begins(alg, "PS")) { - padding = DCRYPT_PADDING_RSA_PKCS1_PSS; - sig_format = DCRYPT_SIGNATURE_FORMAT_DSS; - } else if (str_begins(alg, "ES")) { - padding = DCRYPT_PADDING_DEFAULT; - sig_format = DCRYPT_SIGNATURE_FORMAT_X962; - } else { - /* this should be checked by caller */ - i_unreached(); - } - - if (strcmp(alg+2, "256") == 0) { - method = "sha256"; - } else if (strcmp(alg+2, "384") == 0) { - method = "sha384"; - } else if (strcmp(alg+2, "512") == 0) { - method = "sha512"; - } else { - *error_r = t_strdup_printf("Unsupported algorithm '%s'", alg); - return -1; - } - - buffer_t *signature = - t_base64url_decode_str(BASE64_DECODE_FLAG_NO_PADDING, blobs[2]); - - struct dcrypt_public_key *pubkey; - if (oauth2_lookup_pubkey(set, azp, alg, key_id, &pubkey, error_r) < 0) - return -1; - - /* data to verify */ - const char *data = t_strconcat(blobs[0], ".", blobs[1], NULL); - - /* verify signature */ - bool valid; - if (!dcrypt_verify(pubkey, method, sig_format, data, strlen(data), - signature->data, signature->used, &valid, padding, - error_r)) { - valid = FALSE; - } else if (!valid) { - *error_r = "Bad signature"; - } - - return valid ? 0 : -1; -} - -static int -oauth2_validate_signature(const struct oauth2_settings *set, const char *azp, - const char *alg, const char *key_id, - const char *const *blobs, const char **error_r) -{ - if (str_begins(alg, "HS")) { - return oauth2_validate_hmac(set, azp, alg, key_id, blobs, - error_r); - } else if (str_begins(alg, "RS") || str_begins(alg, "PS") || - str_begins(alg, "ES")) { - return oauth2_validate_rsa_ecdsa(set, azp, alg, key_id, blobs, - error_r); - } - - *error_r = t_strdup_printf("Unsupported algorithm '%s'", alg); - return -1; -} - -static void -oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2_field) *fields, struct json_tree *tree) -{ - pool_t pool = array_get_pool(fields); - ARRAY(const struct json_tree_node*) nodes; - const struct json_tree_node *root = json_tree_root(tree); - - t_array_init(&nodes, 1); - array_push_back(&nodes, &root); - - while (array_count(&nodes) > 0) { - const struct json_tree_node *const *pnode = array_front(&nodes); - const struct json_tree_node *node = *pnode; - array_pop_front(&nodes); - while (node != NULL) { - if (node->value_type == JSON_TYPE_OBJECT) { - root = node->value.child; - array_push_back(&nodes, &root); - } else if (node->key != NULL) { - struct oauth2_field *field = - array_append_space(fields); - field->name = p_strdup(pool, node->key); - field->value = p_strdup( - pool, json_tree_get_value_str(node)); - } - node = node->next; - } - } -} - -static int -oauth2_jwt_header_process(struct json_tree *tree, const char **alg_r, - const char **kid_r, const char **error_r) -{ - const char *typ = get_field(tree, "typ"); - const char *alg = get_field(tree, "alg"); - const char *kid = get_field(tree, "kid"); - - if (null_strcmp(typ, "JWT") != 0) { - *error_r = "Cannot find 'typ' field"; - return -1; - } - - if (alg == NULL) { - *error_r = "Cannot find 'alg' field"; - return -1; - } - - /* These are lost when tree is deinitialized. - Make sure algorithm is uppercased. */ - *alg_r = t_str_ucase(alg); - *kid_r = t_strdup(kid); - return 0; -} - -static int -oauth2_jwt_body_process(const struct oauth2_settings *set, const char *alg, - const char *kid, ARRAY_TYPE(oauth2_field) *fields, - struct json_tree *tree, const char *const *blobs, - const char **error_r) -{ - const char *sub = get_field(tree, "sub"); - - int ret; - int64_t t0 = time(NULL); - /* default IAT and NBF to now */ - int64_t iat, nbf, exp; - int tz_offset ATTR_UNUSED; - - if (sub == NULL) { - *error_r = "Missing 'sub' field"; - return -1; - } - - if ((ret = get_time_field(tree, "exp", &exp)) < 1) { - *error_r = t_strdup_printf("%s 'exp' field", - ret == 0 ? "Missing" : "Malformed"); - return -1; - } - - if ((ret = get_time_field(tree, "nbf", &nbf)) < 0) { - *error_r = "Malformed 'nbf' field"; - return -1; - } else if (ret == 0 || nbf == 0) - nbf = t0; - - if ((ret = get_time_field(tree, "iat", &iat)) < 0) { - *error_r = "Malformed 'iat' field"; - return -1; - } else if (ret == 0 || iat == 0) - iat = t0; - - if (nbf > t0) { - *error_r = "Token is not valid yet"; - return -1; - } - if (iat > t0) { - *error_r = "Token is issued in future"; - return -1; - } - if (exp < t0) { - *error_r = "Token has expired"; - return -1; - } - - /* ensure token dates are not conflicting */ - if (exp < iat || - exp < nbf) { - *error_r = "Token time values are conflicting"; - return -1; - } - - const char *iss = get_field(tree, "iss"); - if (set->issuers != NULL && *set->issuers != NULL) { - if (iss == NULL) { - *error_r = "Token is missing 'iss' field"; - return -1; - } - if (!str_array_find(set->issuers, iss)) { - *error_r = t_strdup_printf("Issuer '%s' is not allowed", - str_sanitize_utf8(iss, 128)); - return -1; - } - } - - /* see if there is azp */ - const char *azp = get_field(tree, "azp"); - if (azp == NULL) - azp = "default"; - else - azp = escape_identifier(azp); - - if (oauth2_validate_signature(set, azp, alg, kid, blobs, error_r) < 0) - return -1; - - oauth2_jwt_copy_fields(fields, tree); - return 0; -} - -int oauth2_try_parse_jwt(const struct oauth2_settings *set, - const char *token, ARRAY_TYPE(oauth2_field) *fields, - bool *is_jwt_r, const char **error_r) -{ - const char *const *blobs = t_strsplit(token, "."); - int ret; - - i_assert(set->key_dict != NULL); - - /* we don't know if it's JWT token yet */ - *is_jwt_r = FALSE; - - if (str_array_length(blobs) != 3) { - *error_r = "Not a JWT token"; - return -1; - } - - /* attempt to decode header */ - buffer_t *header = - t_base64url_decode_str(BASE64_DECODE_FLAG_NO_PADDING, blobs[0]); - - if (header->used == 0) { - *error_r = "Not a JWT token"; - return -1; - } - - struct json_tree *header_tree; - if (oauth2_json_tree_build(header, &header_tree, error_r) < 0) - return -1; - - const char *alg, *kid; - ret = oauth2_jwt_header_process(header_tree, &alg, &kid, error_r); - json_tree_deinit(&header_tree); - if (ret < 0) - return -1; - - /* it is now assumed to be a JWT token */ - *is_jwt_r = TRUE; - - if (kid == NULL) - kid = "default"; - else if (*kid == '\0') { - *error_r = "'kid' field is empty"; - return -1; - } else { - kid = escape_identifier(kid); - } - - /* parse body */ - struct json_tree *body_tree; - buffer_t *body = - t_base64url_decode_str(BASE64_DECODE_FLAG_NO_PADDING, blobs[1]); - if (oauth2_json_tree_build(body, &body_tree, error_r) == -1) - return -1; - ret = oauth2_jwt_body_process(set, alg, kid, fields, body_tree, blobs, - error_r); - json_tree_deinit(&body_tree); - - return ret; -} diff --git a/src/lib-oauth2/oauth2-key-cache.c b/src/lib-oauth2/oauth2-key-cache.c deleted file mode 100644 index e85c210842..0000000000 --- a/src/lib-oauth2/oauth2-key-cache.c +++ /dev/null @@ -1,158 +0,0 @@ -/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "array.h" -#include "llist.h" -#include "buffer.h" -#include "hash.h" -#include "dcrypt.h" -#include "oauth2.h" -#include "oauth2-private.h" - -struct oauth2_key_cache_entry { - const char *key_id; - struct dcrypt_public_key *pubkey; - buffer_t *hmac_key; - struct oauth2_key_cache_entry *prev, *next; -}; - -HASH_TABLE_DEFINE_TYPE(oauth2_key_cache, const char *, - struct oauth2_key_cache_entry *); - -struct oauth2_validation_key_cache { - pool_t pool; - HASH_TABLE_TYPE(oauth2_key_cache) keys; - struct oauth2_key_cache_entry *list_start; -}; - -struct oauth2_validation_key_cache *oauth2_validation_key_cache_init(void) -{ - pool_t pool = pool_alloconly_create( - MEMPOOL_GROWING"oauth2 key cache", 128); - struct oauth2_validation_key_cache *cache = - p_new(pool, struct oauth2_validation_key_cache, 1); - - cache->pool = pool; - hash_table_create(&cache->keys, pool, 8, str_hash, strcmp); - return cache; -} - -void oauth2_validation_key_cache_deinit( - struct oauth2_validation_key_cache **_cache) -{ - struct oauth2_validation_key_cache *cache = *_cache; - *_cache = NULL; - if (cache == NULL) - return; - - /* free resources */ - struct oauth2_key_cache_entry *entry = cache->list_start; - while (entry != NULL) { - if (entry->pubkey != NULL) - dcrypt_key_unref_public(&entry->pubkey); - entry = entry->next; - } - hash_table_destroy(&cache->keys); - pool_unref(&cache->pool); -} - -int oauth2_validation_key_cache_lookup_pubkey( - struct oauth2_validation_key_cache *cache, const char *key_id, - struct dcrypt_public_key **pubkey_r) -{ - if (cache == NULL) - return -1; - - struct oauth2_key_cache_entry *entry = - hash_table_lookup(cache->keys, key_id); - if (entry == NULL || entry->pubkey == NULL) - return -1; - - *pubkey_r = entry->pubkey; - return 0; -} - -int oauth2_validation_key_cache_lookup_hmac_key( - struct oauth2_validation_key_cache *cache, const char *key_id, - const buffer_t **hmac_key_r) -{ - if (cache == NULL) - return -1; - - struct oauth2_key_cache_entry *entry = - hash_table_lookup(cache->keys, key_id); - if (entry == NULL || entry->hmac_key == NULL || - entry->hmac_key->used == 0) - return -1; - - *hmac_key_r = entry->hmac_key; - return 0; -} - -void oauth2_validation_key_cache_insert_pubkey( - struct oauth2_validation_key_cache *cache, const char *key_id, - struct dcrypt_public_key *pubkey) -{ - if (cache == NULL) - return; - - struct oauth2_key_cache_entry *entry = - hash_table_lookup(cache->keys, key_id); - if (entry != NULL) { - dcrypt_key_unref_public(&entry->pubkey); - entry->pubkey = pubkey; - if (entry->hmac_key != NULL) - buffer_set_used_size(entry->hmac_key, 0); - return; - } - entry = p_new(cache->pool, struct oauth2_key_cache_entry, 1); - entry->key_id = p_strdup(cache->pool, key_id); - entry->pubkey = pubkey; - DLLIST_PREPEND(&cache->list_start, entry); - hash_table_insert(cache->keys, entry->key_id, entry); -} - -void oauth2_validation_key_cache_insert_hmac_key( - struct oauth2_validation_key_cache *cache, const char *key_id, - const buffer_t *hmac_key) -{ - if (cache == NULL) - return; - - struct oauth2_key_cache_entry *entry = - hash_table_lookup(cache->keys, key_id); - if (entry != NULL) { - dcrypt_key_unref_public(&entry->pubkey); - if (entry->hmac_key == NULL) { - entry->hmac_key = buffer_create_dynamic( - cache->pool, hmac_key->used); - } else { - buffer_set_used_size(entry->hmac_key, 0); - } - buffer_append(entry->hmac_key, hmac_key->data, hmac_key->used); - return; - } - entry = p_new(cache->pool, struct oauth2_key_cache_entry, 1); - entry->key_id = p_strdup(cache->pool, key_id); - entry->hmac_key = buffer_create_dynamic(cache->pool, hmac_key->used); - buffer_append(entry->hmac_key, hmac_key->data, hmac_key->used); - DLLIST_PREPEND(&cache->list_start, entry); - hash_table_insert(cache->keys, entry->key_id, entry); -} - -int oauth2_validation_key_cache_evict(struct oauth2_validation_key_cache *cache, - const char *key_id) -{ - if (cache == NULL) - return -1; - - struct oauth2_key_cache_entry *entry = - hash_table_lookup(cache->keys, key_id); - if (entry == NULL) - return -1; - if (entry->pubkey != NULL) - dcrypt_key_unref_public(&entry->pubkey); - DLLIST_REMOVE(&cache->list_start, entry); - hash_table_remove(cache->keys, key_id); - return 0; -} diff --git a/src/lib-oauth2/oauth2-private.h b/src/lib-oauth2/oauth2-private.h deleted file mode 100644 index 038f9e14a3..0000000000 --- a/src/lib-oauth2/oauth2-private.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef OAUTH2_PRIVATE_H -#define OAUTH2_PRIVATE_H - -struct json_tree; -struct dcrypt_public_key; - -struct oauth2_request { - pool_t pool; - - const struct oauth2_settings *set; - struct http_client_request *req; - struct json_parser *parser; - struct istream *is; - struct io *io; - - const char *delayed_error; - struct timeout *to_delayed_error; - - const char *username; - const char *key_file_template; - - void (*json_parsed_cb)(struct oauth2_request *, const char *error); - - ARRAY_TYPE(oauth2_field) fields; - char *field_name; - - oauth2_request_callback_t *req_callback; - void *req_context; - /* indicates whether token is valid */ - unsigned int response_status; -}; - -void oauth2_request_parse_json(struct oauth2_request *req); -int oauth2_json_tree_build(const buffer_t *json, struct json_tree **tree_r, - const char **error_r); - -int oauth2_validation_key_cache_lookup_pubkey( - struct oauth2_validation_key_cache *cache, const char *key_id, - struct dcrypt_public_key **pubkey_r); -int oauth2_validation_key_cache_lookup_hmac_key( - struct oauth2_validation_key_cache *cache, const char *key_id, - const buffer_t **hmac_key_r); -void oauth2_validation_key_cache_insert_pubkey( - struct oauth2_validation_key_cache *cache, const char *key_id, - struct dcrypt_public_key *pubkey); -void oauth2_validation_key_cache_insert_hmac_key( - struct oauth2_validation_key_cache *cache, const char *key_id, - const buffer_t *hmac_key); - -#endif diff --git a/src/lib-oauth2/oauth2-request.c b/src/lib-oauth2/oauth2-request.c deleted file mode 100644 index 032eff1630..0000000000 --- a/src/lib-oauth2/oauth2-request.c +++ /dev/null @@ -1,363 +0,0 @@ -/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "ioloop.h" -#include "istream.h" -#include "str.h" -#include "http-client.h" -#include "http-url.h" -#include "json-parser.h" -#include "oauth2.h" -#include "oauth2-private.h" - -static void oauth2_request_free(struct oauth2_request *req) -{ - timeout_remove(&req->to_delayed_error); - pool_unref(&req->pool); -} - -static void -oauth2_request_callback(struct oauth2_request *req, - struct oauth2_request_result *res) -{ - i_assert(req->req_callback != NULL); - oauth2_request_callback_t *callback = req->req_callback; - req->req_callback = NULL; - callback(res, req->req_context); - oauth2_request_free(req); -} - -static bool -oauth2_request_field_parse(const struct oauth2_field *field, - struct oauth2_request_result *res) -{ - if (strcasecmp(field->name, "expires_in") == 0) { - uint32_t expires_in = 0; - if (str_to_uint32(field->value, &expires_in) < 0) { - res->error = t_strdup_printf( - "Malformed number '%s' in expires_in", - field->value); - return FALSE; - } else { - res->expires_at = ioloop_time + expires_in; - } - } else if (strcasecmp(field->name, "token_type") == 0) { - if (strcasecmp(field->value, "bearer") != 0) { - res->error = t_strdup_printf( - "Expected Bearer token, got '%s'", - field->value); - return FALSE; - } - } - return TRUE; -} - -static void -oauth2_request_continue(struct oauth2_request *req, const char *error) -{ - struct oauth2_request_result res; - i_zero(&res); - - unsigned int status_hi = req->response_status/100; - i_assert(status_hi == 2 || status_hi == 4); - - if (error != NULL) - res.error = error; - else { - const struct oauth2_field *field; - /* see if we can figure out when it expires */ - array_foreach(&req->fields, field) { - if (!oauth2_request_field_parse(field, &res)) - break; - } - res.valid = (status_hi == 2) && res.error == NULL; - } - - res.fields = &req->fields; - - oauth2_request_callback(req, &res); -} - -void oauth2_request_parse_json(struct oauth2_request *req) -{ - enum json_type type; - const char *token, *error; - int ret; - - while((ret = json_parse_next(req->parser, &type, &token)) > 0) { - if (req->field_name == NULL) { - if (type != JSON_TYPE_OBJECT_KEY) break; - /* cannot use t_strdup because we might - have to read more */ - req->field_name = p_strdup(req->pool, token); - } else if (type < JSON_TYPE_STRING) { - /* this should be last allocation */ - p_free(req->pool, req->field_name); - json_parse_skip(req->parser); - } else { - if (!array_is_created(&req->fields)) - p_array_init(&req->fields, req->pool, 4); - struct oauth2_field *field = - array_append_space(&req->fields); - field->name = req->field_name; - req->field_name = NULL; - field->value = p_strdup(req->pool, token); - } - } - - /* read more */ - if (ret == 0) return; - - io_remove(&req->io); - - if (ret > 0) { - (void)json_parser_deinit(&req->parser, &error); - error = "Invalid response data"; - } else if (i_stream_read_eof(req->is) && - req->is->v_offset == 0 && req->is->stream_errno == 0) { - /* discard error, empty response is OK. */ - (void)json_parser_deinit(&req->parser, &error); - error = NULL; - } else if (json_parser_deinit(&req->parser, &error) == 0) { - error = NULL; - } else { - i_assert(error != NULL); - } - - i_stream_unref(&req->is); - - req->json_parsed_cb(req, error); -} - -static void -oauth2_request_response(const struct http_response *response, - struct oauth2_request *req) -{ - req->response_status = response->status; - unsigned int status_hi = req->response_status/100; - - if (status_hi != 2 && status_hi != 4) { - /* Unexpected internal error */ - struct oauth2_request_result res = { - .error = http_response_get_message(response), - }; - oauth2_request_callback(req, &res); - return; - } - - if (response->payload != NULL) { - req->is = response->payload; - i_stream_ref(req->is); - } else { - req->is = i_stream_create_from_data("", 0); - } - - p_array_init(&req->fields, req->pool, 1); - req->parser = json_parser_init(req->is); - req->json_parsed_cb = oauth2_request_continue; - req->io = io_add_istream(req->is, oauth2_request_parse_json, req); - oauth2_request_parse_json(req); -} - -static void -oauth2_request_set_headers(struct oauth2_request *req, - const struct oauth2_request_input *input) -{ - if (!req->set->send_auth_headers) - return; - if (input->service != NULL) { - http_client_request_add_header( - req->req, "X-Dovecot-Auth-Service", input->service); - } - if (input->local_ip.family != 0) { - const char *addr; - if (net_ipport2str(&input->local_ip, input->local_port, - &addr) == 0) { - http_client_request_add_header( - req->req, "X-Dovecot-Auth-Local", addr); - } - } - if (input->remote_ip.family != 0) { - const char *addr; - if (net_ipport2str(&input->remote_ip, input->remote_port, - &addr) == 0) { - http_client_request_add_header( - req->req, "X-Dovecot-Auth-Remote", addr); - } - } -} - -static struct oauth2_request * -oauth2_request_start(const struct oauth2_settings *set, - const struct oauth2_request_input *input, - oauth2_request_callback_t *callback, - void *context, - pool_t p, - const char *method, - const char *url, - const string_t *payload, - bool add_auth_bearer) -{ - i_assert(oauth2_valid_token(input->token)); - - pool_t pool = (p == NULL) ? - pool_alloconly_create_clean("oauth2 request", 1024) : p; - struct oauth2_request *req = - p_new(pool, struct oauth2_request, 1); - - req->pool = pool; - req->set = set; - req->req_callback = callback; - req->req_context = context; - - req->req = http_client_request_url_str(req->set->client, method, url, - oauth2_request_response, req); - - oauth2_request_set_headers(req, input); - - if (payload != NULL && strcmp(method, "POST") == 0) { - struct istream *is = i_stream_create_from_string(payload); - - http_client_request_add_header( - req->req, "Content-Type", - "application/x-www-form-urlencoded"); - - http_client_request_set_payload(req->req, is, FALSE); - i_stream_unref(&is); - } - if (add_auth_bearer && - http_client_request_get_origin_url(req->req)->user == NULL && - set->introspection_mode == INTROSPECTION_MODE_GET_AUTH) { - http_client_request_add_header(req->req, - "Authorization", - t_strdup_printf("Bearer %s", - input->token)); - } - http_client_request_set_timeout_msecs(req->req, - req->set->timeout_msecs); - http_client_request_submit(req->req); - - return req; -} - -#undef oauth2_refresh_start -struct oauth2_request * -oauth2_refresh_start(const struct oauth2_settings *set, - const struct oauth2_request_input *input, - oauth2_request_callback_t *callback, void *context) -{ - string_t *payload = t_str_new(128); - - str_append(payload, "client_secret="); - http_url_escape_param(payload, set->client_secret); - str_append(payload, "&grant_type=refresh_token&refresh_token="); - http_url_escape_param(payload, input->token); - str_append(payload, "&client_id="); - http_url_escape_param(payload, set->client_id); - - return oauth2_request_start(set, input, callback, context, NULL, - "POST", set->refresh_url, NULL, FALSE); -} - -#undef oauth2_introspection_start -struct oauth2_request * -oauth2_introspection_start(const struct oauth2_settings *set, - const struct oauth2_request_input *input, - oauth2_request_callback_t *callback, void *context) -{ - string_t *enc; - const char *url; - const char *method; - string_t *payload = NULL; - pool_t p = NULL; - - switch (set->introspection_mode) { - case INTROSPECTION_MODE_GET: - enc = t_str_new(64); - str_append(enc, set->introspection_url); - http_url_escape_param(enc, input->token); - str_append(enc, "&client_id="); - http_url_escape_param(enc, set->client_id); - str_append(enc, "&client_secret="); - http_url_escape_param(enc, set->client_secret); - url = str_c(enc); - method = "GET"; - break; - case INTROSPECTION_MODE_GET_AUTH: - url = set->introspection_url; - method = "GET"; - break; - case INTROSPECTION_MODE_POST: - p = pool_alloconly_create_clean("oauth2 request", 1024); - payload = str_new(p, strlen(input->token)+6); - str_append(payload, "token="); - http_url_escape_param(payload, input->token); - str_append(payload, "&client_id="); - http_url_escape_param(payload, set->client_id); - str_append(payload, "&client_secret="); - http_url_escape_param(payload, set->client_secret); - url = set->introspection_url; - method = "POST"; - break; - default: - i_unreached(); - break; - } - - return oauth2_request_start(set, input, callback, context, p, - method, url, payload, TRUE); -} - -#undef oauth2_token_validation_start -struct oauth2_request * -oauth2_token_validation_start(const struct oauth2_settings *set, - const struct oauth2_request_input *input, - oauth2_request_callback_t *callback, - void *context) -{ - string_t *enc = t_str_new(64); - - str_append(enc, set->tokeninfo_url); - http_url_escape_param(enc, input->token); - - return oauth2_request_start(set, input, callback, context, - NULL, "GET", str_c(enc), NULL, TRUE); -} - -#undef oauth2_passwd_grant_start -struct oauth2_request * -oauth2_passwd_grant_start(const struct oauth2_settings *set, - const struct oauth2_request_input *input, - const char *username, const char *password, - oauth2_request_callback_t *callback, void *context) -{ - pool_t pool = pool_alloconly_create_clean("oauth2 request", 1024); - string_t *payload = str_new(pool, 128); - - /* add token */ - str_append(payload, "grant_type=password&username="); - http_url_escape_param(payload, username); - str_append(payload, "&password="); - http_url_escape_param(payload, password); - str_append(payload, "&client_id="); - http_url_escape_param(payload, set->client_id); - str_append(payload, "&client_secret="); - http_url_escape_param(payload, set->client_secret); - if (set->scope[0] != '\0') { - str_append(payload, "&scope="); - http_url_escape_param(payload, set->scope); - } - - return oauth2_request_start(set, input, callback, context, - pool, "POST", set->grant_url, - payload, FALSE); -} - -void oauth2_request_abort(struct oauth2_request **_req) -{ - struct oauth2_request *req = *_req; - *_req = NULL; - - http_client_request_abort(&req->req); - oauth2_request_free(req); -} diff --git a/src/lib-oauth2/oauth2.c b/src/lib-oauth2/oauth2.c deleted file mode 100644 index b96995eab2..0000000000 --- a/src/lib-oauth2/oauth2.c +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "buffer.h" -#include "istream.h" -#include "json-tree.h" -#include "oauth2.h" -#include "oauth2-private.h" - -int oauth2_json_tree_build(const buffer_t *json, struct json_tree **tree_r, - const char **error_r) -{ - struct istream *is = i_stream_create_from_buffer(json); - struct json_parser *parser = json_parser_init(is); - struct json_tree *tree = json_tree_init(); - enum json_type type; - const char *value; - int ret; - - while ((ret = json_parse_next(parser, &type, &value)) > 0) { - /* this is safe to reuse here because it gets rewritten in while - loop */ - ret = json_tree_append(tree, type, value); - i_assert(ret == 0); - } - i_assert(ret != 0); - ret = json_parser_deinit(&parser, error_r); - i_stream_unref(&is); - if (ret != 0) - json_tree_deinit(&tree); - else - *tree_r = tree; - return ret; -} - -bool oauth2_valid_token(const char *token) -{ - if (token == NULL || *token == '\0' || strpbrk(token, "\r\n") != NULL) - return FALSE; - return TRUE; -} diff --git a/src/lib-oauth2/oauth2.h b/src/lib-oauth2/oauth2.h deleted file mode 100644 index 3d1d3ea6a1..0000000000 --- a/src/lib-oauth2/oauth2.h +++ /dev/null @@ -1,149 +0,0 @@ -#ifndef OAUTH2_H -#define OAUTH2_H - -#include "net.h" - -struct dict; -struct oauth2_request; -struct oauth2_validation_key_cache; - -struct oauth2_field { - const char *name; - const char *value; -}; - -ARRAY_DEFINE_TYPE(oauth2_field, struct oauth2_field); - -struct oauth2_settings { - struct http_client *client; - /* GET tokeninfo from this URL, token is appended to URL - http://some.host/path?access_token= */ - const char *tokeninfo_url; - /* POST grant password here, needs user credentials and client_* - settings */ - const char *grant_url; - /* GET more information from this URL, uses Bearer authentication */ - const char *introspection_url; - /* POST refresh here, needs refresh token and client_* settings */ - const char *refresh_url; - /* client identificator for oauth2 server */ - const char *client_id; - /* client secret for oauth2 server */ - const char *client_secret; - /* access request scope for oauth2 server (optional) */ - const char *scope; - /* key dict for looking up validation keys */ - struct dict *key_dict; - /* cache for validation keys */ - struct oauth2_validation_key_cache *key_cache; - /* valid issuer names */ - const char *const *issuers; - - enum { - INTROSPECTION_MODE_GET_AUTH, - INTROSPECTION_MODE_GET, - INTROSPECTION_MODE_POST, - INTROSPECTION_MODE_LOCAL, - } introspection_mode; - unsigned int timeout_msecs; - /* Should X-Dovecot-Auth-* headers be sent */ - bool send_auth_headers; - /* Should use grant password mechanism for authentication */ - bool use_grant_password; -}; - - -struct oauth2_request_result { - /* Oauth2 server response fields */ - ARRAY_TYPE(oauth2_field) *fields; - /* Non-NULL if there was an unexpected internal error. */ - const char *error; - /* timestamp token expires at */ - time_t expires_at; - /* User authenticated successfully. Implies that error==NULL. */ - bool valid:1; -}; - -struct oauth2_request_input { - const char *token; - const char *service; - struct ip_addr local_ip, real_local_ip, remote_ip, real_remote_ip; - in_port_t local_port, real_local_port, remote_port, real_remote_port; -}; - -typedef void -oauth2_request_callback_t(struct oauth2_request_result*, void*); - -bool oauth2_valid_token(const char *token); - -struct oauth2_request* -oauth2_passwd_grant_start(const struct oauth2_settings *set, - const struct oauth2_request_input *input, - const char *username, - const char *password, - oauth2_request_callback_t *callback, - void *context); -#define oauth2_passwd_grant_start(set, input, username, password, callback, \ - context) \ - oauth2_passwd_grant_start( \ - set, input - CALLBACK_TYPECHECK( \ - callback, void(*)(struct oauth2_request_result*, \ - typeof(context))), \ - username, password, \ - (oauth2_request_callback_t*)callback, (void*)context); - -struct oauth2_request* -oauth2_token_validation_start(const struct oauth2_settings *set, - const struct oauth2_request_input *input, - oauth2_request_callback_t *callback, - void *context); -#define oauth2_token_validation_start(set, input, callback, context) \ - oauth2_token_validation_start( \ - set, input - CALLBACK_TYPECHECK( \ - callback, void(*)(struct oauth2_request_result*, \ - typeof(context))), \ - (oauth2_request_callback_t*)callback, (void*)context); - -struct oauth2_request* -oauth2_introspection_start(const struct oauth2_settings *set, - const struct oauth2_request_input *input, - oauth2_request_callback_t *callback, - void *context); -#define oauth2_introspection_start(set, input, callback, context) \ - oauth2_introspection_start( \ - set, input - CALLBACK_TYPECHECK( \ - callback, void(*)(struct oauth2_request_result*, \ - typeof(context))), \ - (oauth2_request_callback_t*)callback, (void*)context); - -struct oauth2_request * -oauth2_refresh_start(const struct oauth2_settings *set, - const struct oauth2_request_input *input, - oauth2_request_callback_t *callback, - void *context); -#define oauth2_refresh_start(set, input, callback, context) \ - oauth2_refresh_start( \ - set, input - CALLBACK_TYPECHECK( \ - callback, void(*)(struct oauth2_request_result*, \ - typeof(context))), \ - (oauth2_request_callback_t*)callback, (void*)context); - -/* Abort without calling callback, use this to cancel the request */ -void oauth2_request_abort(struct oauth2_request **); - -int oauth2_try_parse_jwt(const struct oauth2_settings *set, - const char *token, ARRAY_TYPE(oauth2_field) *fields, - bool *is_jwt_r, const char **error_r); - -/* Initialize validation key cache */ -struct oauth2_validation_key_cache *oauth2_validation_key_cache_init(void); - -/* Evict given key ID from cache, returns 0 on successful eviction */ -int oauth2_validation_key_cache_evict(struct oauth2_validation_key_cache *cache, - const char *key_id); - -/* Deinitialize validation key cache */ -void oauth2_validation_key_cache_deinit( - struct oauth2_validation_key_cache **_cache); - -#endif diff --git a/src/lib-oauth2/test-oauth2-json.c b/src/lib-oauth2/test-oauth2-json.c deleted file mode 100644 index 87caedcb28..0000000000 --- a/src/lib-oauth2/test-oauth2-json.c +++ /dev/null @@ -1,104 +0,0 @@ -/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "array.h" -#include "json-parser.h" -#include "oauth2.h" -#include "oauth2-private.h" -#include "test-common.h" - -static void -test_oauth_json_valid_parsed(struct oauth2_request *req ATTR_UNUSED, - const char *error) -{ - test_assert(error == NULL); -} - -static void test_oauth2_json_valid(void) -{ - static const char *test_input = - "{\"access_token\":\"9a2dea3c-f8be-4271-b9c8-5b37da4f2f7e\"," - "\"grant_type\":\"authorization_code\"," - "\"openid\":\"\"," - "\"scope\":[\"openid\",\"profile\",\"email\"]," - "\"profile\":\"\"," - "\"realm\":\"/employees\"," - "\"token_type\":\"Bearer\"," - "\"expires_in\":2377," - "\"client_id\":\"mosaic\"," - "\"email\":\"\"," - "\"extensions\":" - "{\"algorithm\":\"cuttlefish\"," - "\"tentacles\":8" - "}" - "}"; - static const struct oauth2_field fields[] = { - { .name = "access_token", - .value = "9a2dea3c-f8be-4271-b9c8-5b37da4f2f7e" }, - { .name = "grant_type", - .value = "authorization_code" }, - { .name = "openid", - .value = "" }, - { .name = "profile", - .value = "" }, - { .name = "realm", - .value = "/employees" }, - { .name = "token_type", - .value = "Bearer" }, - { .name = "expires_in", - .value = "2377" }, - { .name = "client_id", - .value = "mosaic" }, - { .name = "email", - .value = "" }, - }; - static const unsigned int fields_count = N_ELEMENTS(fields); - struct oauth2_request *req; - const struct oauth2_field *pfields; - unsigned int count, i; - pool_t pool; - size_t pos; - - test_begin("oauth json skip"); - - /* Create mock request */ - pool = pool_alloconly_create_clean("oauth2 json test", 1024); - req = p_new(pool, struct oauth2_request, 1); - req->pool = pool; - p_array_init(&req->fields, req->pool, 1); - req->is = test_istream_create_data(test_input, strlen(test_input)); - req->parser = json_parser_init(req->is); - req->json_parsed_cb = test_oauth_json_valid_parsed; - - /* Parse the JSON response */ - for (pos = 0; pos <= strlen(test_input); pos +=2) { - test_istream_set_size(req->is, pos); - oauth2_request_parse_json(req); - if (req->is == NULL) - break; - } - - /* Verify the parsed fields */ - pfields = array_get(&req->fields, &count); - test_assert(count == fields_count); - if (count > fields_count) - count = fields_count; - for (i = 0; i < count; i++) { - test_assert(strcmp(pfields[i].name, fields[i].name) == 0); - test_assert(strcmp(pfields[i].value, fields[i].value) == 0); - } - - /* Clean up */ - pool_unref(&req->pool); - - test_end(); -} - -int main(void) -{ - static void (*const test_functions[])(void) = { - test_oauth2_json_valid, - NULL - }; - return test_run(test_functions); -} diff --git a/src/lib-oauth2/test-oauth2-jwt.c b/src/lib-oauth2/test-oauth2-jwt.c deleted file mode 100644 index 890712e48d..0000000000 --- a/src/lib-oauth2/test-oauth2-jwt.c +++ /dev/null @@ -1,919 +0,0 @@ -/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "buffer.h" -#include "str.h" -#include "ostream.h" -#include "hmac.h" -#include "sha2.h" -#include "base64.h" -#include "randgen.h" -#include "array.h" -#include "json-parser.h" -#include "iso8601-date.h" -#include "oauth2.h" -#include "oauth2-private.h" -#include "dcrypt.h" -#include "dict.h" -#include "dict-private.h" -#include "test-common.h" -#include "unlink-directory.h" - -#include -#include - -#define base64url_encode_str(str, dest) \ - base64url_encode(BASE64_ENCODE_FLAG_NO_PADDING, SIZE_MAX, (str), \ - strlen((str)), (dest)) - -/** - * Test keypair used only for this test. - */ -static const char *rsa_public_key = -"-----BEGIN PUBLIC KEY-----\n" -"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv\n" -"vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc\n" -"aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy\n" -"tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0\n" -"e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb\n" -"V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9\n" -"MwIDAQAB\n" -"-----END PUBLIC KEY-----"; -static const char *rsa_private_key = -"-----BEGIN PRIVATE KEY-----\n" -"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCfPKKzVmN80HRs\n" -"GAoUxK++RO3CW8GxomrtLnAD6TN5U5WlVbCRZ1WFrizfxcz+lr/Kvjtq/v7PdVOa\n" -"8NHIAdxpP3bCFEQWku/1yPmVN4lKJvKv8yub9i2MJlVaBo5giHCtfAouo+v/XWKd\n" -"awCR8jK28dZPFlgRxcuABcW5S5pLe4X2ASI1DDMZNTW/QWqSpMGvgHydbccI3jtd\n" -"S7S3xjR76V/izg7FBrBYPv0n3/l3dHLS9tXcCbUW0YmIm87BGwh9UKEOlhK1NwdM\n" -"Iyq29ZtXovXUFaSnMZdJbge/jepr4ZJg4PZBTrwxvn2hKTY4H4G04ukmh+ZsYQaC\n" -"+bDIIj0zAgMBAAECggEAKIBGrbCSW2O1yOyQW9nvDUkA5EdsS58Q7US7bvM4iWpu\n" -"DIBwCXur7/VuKnhn/HUhURLzj/JNozynSChqYyG+CvL+ZLy82LUE3ZIBkSdv/vFL\n" -"Ft+VvvRtf1EcsmoqenkZl7aN7HD7DJeXBoz5tyVQKuH17WW0fsi9StGtCcUl+H6K\n" -"zV9Gif0Kj0uLQbCg3THRvKuueBTwCTdjoP0PwaNADgSWb3hJPeLMm/yII4tIMGbO\n" -"w+xd9wJRl+ZN9nkNtQMxszFGdKjedB6goYLQuP0WRZx+YtykaVJdM75bDUvsQar4\n" -"9Pc21Fp7UVk/CN11DX/hX3TmTJAUtqYADliVKkTbCQKBgQDLU48tBxm3g1CdDM/P\n" -"ZIEmpA3Y/m7e9eX7M1Uo/zDh4G/S9a4kkX6GQY2dLFdCtOS8M4hR11Io7MceBKDi\n" -"djorTZ5zJPQ8+b9Rm+1GlaucGNwRW0cQk2ltT2ksPmJnQn2xvM9T8vE+a4A/YGzw\n" -"mZOfpoVGykWs/tbSzU2aTaOybQKBgQDIfRf6OmirGPh59l+RSuDkZtISF/51mCV/\n" -"S1M4DltWDwhjC2Y2T+meIsb/Mjtz4aVNz0EHB8yvn0TMGr94Uwjv4uBdpVSwz+xL\n" -"hHL7J4rpInH+i0gxa0N+rGwsPwI8wJG95wLY+Kni5KCuXQw55uX1cqnnsahpRZFZ\n" -"EerBXhjqHwKBgBmEjiaHipm2eEqNjhMoOPFBi59dJ0sCL2/cXGa9yEPA6Cfgv49F\n" -"V0zAM2azZuwvSbm4+fXTgTMzrDW/PPXPArPmlOk8jQ6OBY3XdOrz48q+b/gZrYyO\n" -"A6A9ZCSyW6U7+gxxds/BYLeFxF2v21xC2f0iZ/2faykv/oQMUh34en/tAoGACqVZ\n" -"2JexZyR0TUWf3X80YexzyzIq+OOTWicNzDQ29WLm9xtr2gZ0SUlfd72bGpQoyvDu\n" -"awkm/UxfwtbIxALkvpg1gcN9s8XWrkviLyPyZF7H3tRWiQlBFEDjnZXa8I7pLkRO\n" -"Cmdp3fp17cxTEeAI5feovfzZDH39MdWZuZrdh9ECgYBTEv8S7nK8wrxIC390kroV\n" -"52eBwzckQU2mWa0thUtaGQiU1EYPCSDcjkrLXwB72ft0dW57KyWtvrB6rt1ORgOL\n" -"eI5hFbwdGQhCHTrAR1vG3SyFPMAm+8JB+sGOD/fvjtZKx//MFNweKFNEF0C/o6Z2\n" -"FXj90PlgF8sCQut36ZfuIQ==\n" -"-----END PRIVATE KEY-----"; - -static buffer_t *hs_sign_key = NULL; - -static struct dict *keys_dict = NULL; - -static bool skip_dcrypt = FALSE; - -static struct oauth2_validation_key_cache *key_cache = NULL; - -static int parse_jwt_token(struct oauth2_request *req, const char *token, - bool *is_jwt_r, const char **error_r) -{ - struct oauth2_settings set; - - i_zero(&set); - set.scope = "mail"; - set.key_dict = keys_dict; - set.key_cache = key_cache; - i_zero(req); - req->pool = pool_datastack_create(); - req->set = &set; - t_array_init(&req->fields, 8); - return oauth2_try_parse_jwt(&set, token, &req->fields, is_jwt_r, - error_r); -} - -static void test_jwt_token(const char *token) -{ - /* then see what the parser likes it */ - struct oauth2_request req; - const char *error = NULL; - - bool is_jwt; - test_assert(parse_jwt_token(&req, token, &is_jwt, &error) == 0); - test_assert(is_jwt == TRUE); - test_assert(error == NULL); - - /* check fields */ - test_assert(array_is_created(&req.fields)); - if (array_is_created(&req.fields)) { - const struct oauth2_field *field; - bool got_sub = FALSE; - array_foreach(&req.fields, field) { - if (strcmp(field->name, "sub") == 0) { - test_assert_strcmp(field->value, "testuser"); - got_sub = TRUE; - } - } - test_assert(got_sub == TRUE); - } - - if (error != NULL) - i_error("%s", error); -} - -static buffer_t *create_jwt_token_kid(const char *algo, const char *kid) -{ - /* make a token */ - buffer_t *tokenbuf = t_buffer_create(64); - - /* header */ - base64url_encode_str( - t_strdup_printf( - "{\"alg\":\"%s\",\"typ\":\"JWT\",\"kid\":\"%s\"}", - algo, kid), - tokenbuf); - buffer_append(tokenbuf, ".", 1); - - /* body */ - base64url_encode_str( - t_strdup_printf("{\"sub\":\"testuser\","\ - "\"iat\":%"PRIdTIME_T"," - "\"exp\":%"PRIdTIME_T"}", - time(NULL), time(NULL)+600), - tokenbuf); - return tokenbuf; -} - -static buffer_t *create_jwt_token(const char *algo) -{ - /* make a token */ - buffer_t *tokenbuf = t_buffer_create(64); - - /* header */ - base64url_encode_str( - t_strdup_printf("{\"alg\":\"%s\",\"typ\":\"JWT\"}", algo), - tokenbuf); - buffer_append(tokenbuf, ".", 1); - - /* body */ - base64url_encode_str( - t_strdup_printf("{\"sub\":\"testuser\","\ - "\"iat\":%"PRIdTIME_T"," - "\"exp\":%"PRIdTIME_T"}", - time(NULL), time(NULL)+600), - tokenbuf); - return tokenbuf; -} - -static void -append_key_value(string_t *dest, const char *key, const char *value, bool str) -{ - str_append_c(dest, '"'); - json_append_escaped(dest, key); - str_append(dest, "\":"); - if (str) - str_append_c(dest, '"'); - json_append_escaped(dest, value); - if (str) - str_append_c(dest, '"'); - -} - -#define create_jwt_token_fields(algo, exp, iat, nbf, fields) \ - create_jwt_token_fields_kid(algo, "default", exp, iat, nbf, fields) -static buffer_t * -create_jwt_token_fields_kid(const char *algo, const char *kid, time_t exp, time_t iat, - time_t nbf, ARRAY_TYPE(oauth2_field) *fields) -{ - const struct oauth2_field *field; - buffer_t *tokenbuf = t_buffer_create(64); - string_t *hdr = t_str_new(32); - str_printfa(hdr, "{\"alg\":\"%s\",\"typ\":\"JWT\"", algo); - if (kid != NULL && *kid != '\0') { - str_append(hdr, ",\"kid\":\""); - json_append_escaped(hdr, kid); - str_append_c(hdr, '"'); - } - str_append(hdr, "}"); - base64url_encode_str(str_c(hdr), tokenbuf); - buffer_append(tokenbuf, ".", 1); - - string_t *bodybuf = t_str_new(64); - str_append_c(bodybuf, '{'); - if (exp > 0) { - append_key_value(bodybuf, "exp", dec2str(exp), FALSE); - } - if (iat > 0) { - if (exp > 0) - str_append_c(bodybuf, ','); - append_key_value(bodybuf, "iat", dec2str(iat), FALSE); - } - if (nbf > 0) { - if (exp > 0 || iat > 0) - str_append_c(bodybuf, ','); - append_key_value(bodybuf, "nbf", dec2str(nbf), FALSE); - } - array_foreach(fields, field) { - if (str_data(bodybuf)[bodybuf->used-1] != '{') - str_append_c(bodybuf, ','); - append_key_value(bodybuf, field->name, field->value, TRUE); - } - str_append_c(bodybuf, '}'); - base64url_encode_str(str_c(bodybuf), tokenbuf); - - return tokenbuf; -} - -#define save_key(algo, key) save_key_to(algo, "default", (key)) -#define save_key_to(algo, name, key) save_key_azp_to(algo, "default", name, (key)) -static void save_key_azp_to(const char *algo, const char *azp, - const char *name, const char *keydata) -{ - const char *error; - struct dict_op_settings set = { - .username = "testuser", - }; - struct dict_transaction_context *ctx = - dict_transaction_begin(keys_dict, &set); - algo = t_str_ucase(algo); - dict_set(ctx, t_strconcat(DICT_PATH_SHARED, azp, "/", algo, "/", - name, NULL), - keydata); - if (dict_transaction_commit(&ctx, &error) < 0) - i_error("dict_set(%s) failed: %s", name, error); -} - -static void sign_jwt_token_hs256(buffer_t *tokenbuf, buffer_t *key) -{ - i_assert(key != NULL); - buffer_t *sig = t_hmac_buffer(&hash_method_sha256, key->data, key->used, - tokenbuf); - buffer_append(tokenbuf, ".", 1); - base64url_encode(BASE64_ENCODE_FLAG_NO_PADDING, SIZE_MAX, - sig->data, sig->used, tokenbuf); -} - -static void sign_jwt_token_hs384(buffer_t *tokenbuf, buffer_t *key) -{ - i_assert(key != NULL); - buffer_t *sig = t_hmac_buffer(&hash_method_sha384, key->data, key->used, - tokenbuf); - buffer_append(tokenbuf, ".", 1); - base64url_encode(BASE64_ENCODE_FLAG_NO_PADDING, SIZE_MAX, - sig->data, sig->used, tokenbuf); -} - -static void sign_jwt_token_hs512(buffer_t *tokenbuf, buffer_t *key) -{ - i_assert(key != NULL); - buffer_t *sig = t_hmac_buffer(&hash_method_sha512, key->data, key->used, - tokenbuf); - buffer_append(tokenbuf, ".", 1); - base64url_encode(BASE64_ENCODE_FLAG_NO_PADDING, SIZE_MAX, - sig->data, sig->used, tokenbuf); -} - -static void test_jwt_hs_token(void) -{ - test_begin("JWT HMAC token"); - - buffer_t *sign_key_384 = t_buffer_create(384/8); - void *ptr = buffer_append_space_unsafe(sign_key_384, 384/8); - random_fill(ptr, 384/8); - buffer_t *b64_key = t_base64_encode(0, SIZE_MAX, - sign_key_384->data, - sign_key_384->used); - save_key_to("HS384", "default", str_c(b64_key)); - buffer_t *sign_key_512 = t_buffer_create(512/8); - ptr = buffer_append_space_unsafe(sign_key_512, 512/8); - random_fill(ptr, 512/8); - b64_key = t_base64_encode(0, SIZE_MAX, - sign_key_512->data, - sign_key_512->used); - save_key_to("HS512", "default", str_c(b64_key)); - /* make a token */ - buffer_t *tokenbuf = create_jwt_token("HS256"); - /* sign it */ - sign_jwt_token_hs256(tokenbuf, hs_sign_key); - test_jwt_token(str_c(tokenbuf)); - - tokenbuf = create_jwt_token("HS384"); - sign_jwt_token_hs384(tokenbuf, sign_key_384); - test_jwt_token(str_c(tokenbuf)); - - tokenbuf = create_jwt_token("HS512"); - sign_jwt_token_hs512(tokenbuf, sign_key_512); - test_jwt_token(str_c(tokenbuf)); - - test_end(); -} - -static void test_jwt_token_escape(void) -{ - struct test_case { - const char *azp; - const char *alg; - const char *kid; - const char *esc_azp; - const char *esc_kid; - } test_cases[] = { - { "", "hs256", "", "default", "default" }, - { "", "hs256", "test", "default", "test" }, - { "test", "hs256", "test", "test", "test" }, - { - "http://test.unit/local%key", - "hs256", - "http://test.unit/local%key", - "http:%2f%2ftest.unit%2flocal%25key", - "http:%2f%2ftest.unit%2flocal%25key" - }, - { "../", "hs256", "../", "..%2f", "..%2f" }, - }; - - test_begin("JWT token escaping"); - - buffer_t *b64_key = - t_base64_encode(0, SIZE_MAX, hs_sign_key->data, hs_sign_key->used); - ARRAY_TYPE(oauth2_field) fields; - t_array_init(&fields, 8); - - for (size_t i = 0; i < N_ELEMENTS(test_cases); i++) { - const struct test_case *test_case = &test_cases[i]; - array_clear(&fields); - struct oauth2_field *field = array_append_space(&fields); - field->name = "sub"; - field->value = "testuser"; - if (*test_case->azp != '\0') { - field = array_append_space(&fields); - field->name = "azp"; - field->value = test_case->azp; - } - if (*test_case->kid != '\0') { - field = array_append_space(&fields); - field->name = "kid"; - field->value = test_case->kid; - } - save_key_azp_to(test_case->alg, test_case->esc_azp, test_case->esc_kid, - str_c(b64_key)); - buffer_t *token = create_jwt_token_fields_kid(test_case->alg, - test_case->kid, - time(NULL)+500, - time(NULL)-500, - 0, &fields); - sign_jwt_token_hs256(token, hs_sign_key); - test_jwt_token(str_c(token)); - } - - test_end(); -} - -static void test_jwt_broken_token(void) -{ - struct test_cases { - const char *token; - bool is_jwt; - } test_cases[] = { - { /* empty token */ - .token = "", - .is_jwt = FALSE - }, - { /* not base64 */ - .token = "{\"alg\":\"HS256\":\"typ\":\"JWT\"}", - .is_jwt = FALSE - }, - { /* not jwt */ - .token = "aGVsbG8sIHdvcmxkCg", - .is_jwt = FALSE - }, - { /* no alg field */ - .token = "eyJ0eXAiOiAiSldUIn0", - .is_jwt = FALSE - }, - { /* no typ field */ - .token = "eyJhbGciOiAiSFMyNTYifQ", - .is_jwt = FALSE - }, - { /* typ field is wrong */ - .token = "eyJ0eXAiOiAiand0IiwgImFsZyI6ICJIUzI1NiJ9." - "eyJhbGdvIjogIldURiIsICJ0eXAiOiAiSldUIn0." - "q2wwwWWJVJxqw-J3uQ0DdlIyWfoZ7Z0QrdzvMW_B-jo", - .is_jwt = FALSE - }, - { /* unknown algorithm */ - .token = "eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJXVEYifQ." - "eyJhbGdvIjogIldURiIsICJ0eXAiOiAiSldUIn0." - "q2wwwWWJVJxqw-J3uQ0DdlIyWfoZ7Z0QrdzvMW_B-jo", - .is_jwt = TRUE - }, - { /* truncated base64 */ - .token = "yJhbGciOiJIUzI1NiIsInR5", - .is_jwt = FALSE - }, - { /* missing body and signature */ - .token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", - .is_jwt = FALSE - }, - { /* empty body and signature */ - .token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..", - .is_jwt = TRUE - }, - { /* empty signature */ - .token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." - "eyJleHAiOjE1ODEzMzA3OTN9.", - .is_jwt = TRUE - }, - { /* bad signature */ - .token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." - "eyJleHAiOjE1ODEzMzA3OTN9." - "q2wwwWWJVJxqw-J3uQ0DdlIyWfoZ7Z0QrdzvMW_B-jo", - .is_jwt = TRUE - }, - }; - - test_begin("JWT broken tokens"); - - for (size_t i = 0; i < N_ELEMENTS(test_cases); i++) T_BEGIN { - struct test_cases *test_case = &test_cases[i]; - struct oauth2_request req; - const char *error = NULL; - bool is_jwt; - test_assert_idx(parse_jwt_token(&req, test_case->token, - &is_jwt, &error) != 0, i); - test_assert_idx(test_case->is_jwt == is_jwt, i); - test_assert_idx(error != NULL, i); - } T_END; - - test_end(); -} - -static void test_jwt_bad_valid_token(void) -{ - test_begin("JWT bad token tests"); - time_t now = time(NULL); - - struct test_cases { - time_t exp; - time_t iat; - time_t nbf; - const char *key_values[20]; - const char *error; - } test_cases[] = - { - { /* "empty" token */ - .exp = 0, - .iat = 0, - .nbf = 0, - .key_values = { NULL }, - .error = "Missing 'sub' field", - }, - { /* missing sub field */ - .exp = now+500, - .iat = 0, - .nbf = 0, - .key_values = { NULL }, - .error = "Missing 'sub' field", - }, - { /* no expiration */ - .key_values = { - "sub", "testuser", - NULL - }, - .error = "Missing 'exp' field", - }, - { /* non-ISO date as iat */ - .exp = now+500, - .iat = 0, - .nbf = 0, - .key_values = { "sub", "testuser", "iat", - "1.1.2019 16:00", NULL }, - .error = "Malformed 'iat' field" - }, - { /* expired token */ - .exp = now-500, - .iat = 0, - .nbf = 0, - .key_values = { "sub", "testuser", NULL }, - .error = "Token has expired", - }, - { /* future token */ - .exp = now+1000, - .iat = now+500, - .nbf = 0, - .key_values = { "sub", "testuser", NULL }, - .error = "Token is issued in future", - }, - { /* token not valid yet */ - .exp = now+500, - .iat = now, - .nbf = now+250, - .key_values = { "sub", "testuser", NULL }, - .error = "Token is not valid yet", - }, - }; - - for (size_t i = 0; i < N_ELEMENTS(test_cases); i++) T_BEGIN { - const struct test_cases *test_case = &test_cases[i]; - const char *key = NULL; - ARRAY_TYPE(oauth2_field) fields; - - t_array_init(&fields, 8); - for (const char *const *value = test_case->key_values; - *value != NULL; value++) { - if (key == NULL) { - key = *value; - } else { - struct oauth2_field *field = - array_append_space(&fields); - field->name = key; - field->value = *value; - key = NULL; - } - } - - buffer_t *tokenbuf = - create_jwt_token_fields("HS256", test_case->exp, - test_case->iat, test_case->nbf, - &fields); - sign_jwt_token_hs256(tokenbuf, hs_sign_key); - - struct oauth2_request req; - const char *error = NULL; - bool is_jwt; - - test_assert_idx(parse_jwt_token(&req, str_c(tokenbuf), - &is_jwt, &error) != 0, i); - test_assert_idx(is_jwt == TRUE, i); - if (test_case->error != NULL) { - test_assert_strcmp(test_case->error, error); - } - test_assert(error != NULL); - } T_END; - - test_end(); -} - -static void test_jwt_valid_token(void) -{ - test_begin("JWT valid token tests"); - time_t now = time(NULL); - - struct test_cases { - time_t exp; - time_t iat; - time_t nbf; - const char *key_values[20]; - } test_cases[] = { - { /* valid token */ - .exp = now + 500, - .key_values = { - "sub", "testuser", - NULL - }, - }, - { - .exp = now + 500, - .nbf = now - 500, - .iat = now - 250, - .key_values = { - "sub", "testuser", - NULL - }, - }, - { /* token issued in advance */ - .exp = now + 500, - .nbf = now - 500, - .iat = now - 3600, - .key_values = { - "sub", "testuser", - NULL, - }, - }, - }; - - for (size_t i = 0; i < N_ELEMENTS(test_cases); i++) T_BEGIN { - const struct test_cases *test_case = &test_cases[i]; - ARRAY_TYPE(oauth2_field) fields; - - t_array_init(&fields, 8); - for (unsigned int i = 0; test_case->key_values[i] != NULL; i += 2) { - struct oauth2_field *field = array_append_space(&fields); - field->name = test_case->key_values[i]; - field->value = test_case->key_values[i+1]; - } - - buffer_t *tokenbuf = - create_jwt_token_fields("HS256", test_case->exp, - test_case->iat, test_case->nbf, - &fields); - sign_jwt_token_hs256(tokenbuf, hs_sign_key); - - struct oauth2_request req; - const char *error = NULL; - bool is_jwt; - - test_assert_idx(parse_jwt_token(&req, str_c(tokenbuf), - &is_jwt, &error) == 0, i); - test_assert_idx(is_jwt == TRUE, i); - test_assert_idx(error == NULL, i); - if (error != NULL) - i_error("JWT validation error: %s", error); - } T_END; - - test_end(); -} - -static void test_jwt_dates(void) -{ - test_begin("JWT Token dates"); - - /* simple check to make sure ISO8601 dates work too */ - ARRAY_TYPE(oauth2_field) fields; - t_array_init(&fields, 8); - struct oauth2_field *field; - struct tm tm_b; - struct tm *tm; - time_t now = time(NULL); - time_t exp = now+500; - time_t nbf = now-250; - time_t iat = now-500; - - field = array_append_space(&fields); - field->name = "sub"; - field->value = "testuser"; - field = array_append_space(&fields); - field->name = "exp"; - tm = gmtime_r(&exp, &tm_b); - field->value = iso8601_date_create_tm(tm, INT_MAX); - field = array_append_space(&fields); - field->name = "nbf"; - tm = gmtime_r(&nbf, &tm_b); - field->value = iso8601_date_create_tm(tm, INT_MAX); - field = array_append_space(&fields); - field->name = "iat"; - tm = gmtime_r(&iat, &tm_b); - field->value = iso8601_date_create_tm(tm, INT_MAX); - buffer_t *tokenbuf = create_jwt_token_fields("HS256", 0, 0, 0, &fields); - sign_jwt_token_hs256(tokenbuf, hs_sign_key); - test_jwt_token(str_c(tokenbuf)); - - str_truncate(tokenbuf, 0); - base64url_encode_str("{\"alg\":\"HS256\",\"typ\":\"JWT\"}", tokenbuf); - str_append_c(tokenbuf, '.'); - base64url_encode_str(t_strdup_printf("{\"sub\":\"testuser\"," - "\"exp\":%"PRIdTIME_T"," - "\"nbf\":0,\"iat\":%"PRIdTIME_T"}", - exp, iat), - tokenbuf); - sign_jwt_token_hs256(tokenbuf, hs_sign_key); - test_jwt_token(str_c(tokenbuf)); - - test_end(); -} - -static void test_jwt_key_files(void) -{ - test_begin("JWT key id"); - /* write HMAC secrets */ - struct oauth2_request req; - bool is_jwt; - const char *error = NULL; - - buffer_t *secret = t_buffer_create(32); - void *ptr = buffer_append_space_unsafe(secret, 32); - random_fill(ptr, 32); - buffer_t *b64_key = t_base64_encode(0, SIZE_MAX, - secret->data, secret->used); - save_key_to("HS256", "first", str_c(b64_key)); - buffer_t *secret2 = t_buffer_create(32); - ptr = buffer_append_space_unsafe(secret2, 32); - random_fill(ptr, 32); - b64_key = t_base64_encode(0, SIZE_MAX, secret2->data, secret2->used); - save_key_to("HS256", "second", str_c(b64_key)); - - /* create and sign token */ - buffer_t *token_1 = create_jwt_token_kid("HS256", "first"); - buffer_t *token_2 = create_jwt_token_kid("HS256", "second"); - buffer_t *token_3 = create_jwt_token_kid("HS256", "missing"); - buffer_t *token_4 = create_jwt_token_kid("HS256", ""); - - sign_jwt_token_hs256(token_1, secret); - sign_jwt_token_hs256(token_2, secret2); - sign_jwt_token_hs256(token_3, secret); - sign_jwt_token_hs256(token_4, secret); - - test_jwt_token(str_c(token_1)); - test_jwt_token(str_c(token_2)); - - test_assert(parse_jwt_token(&req, str_c(token_3), &is_jwt, &error) != 0); - test_assert(is_jwt == TRUE); - test_assert_strcmp(error, "HS256 key 'missing' not found"); - test_assert(parse_jwt_token(&req, str_c(token_4), &is_jwt, &error) != 0); - test_assert(is_jwt == TRUE); - test_assert_strcmp(error, "'kid' field is empty"); - - test_end(); -} - -static void test_jwt_kid_escape(void) -{ - test_begin("JWT kid escape"); - /* save a token */ - buffer_t *secret = t_buffer_create(32); - void *ptr = buffer_append_space_unsafe(secret, 32); - random_fill(ptr, 32); - buffer_t *b64_key = t_base64_encode(0, SIZE_MAX, - secret->data, secret->used); - save_key_to("HS256", "hello.world%2f%25", str_c(b64_key)); - /* make a token */ - buffer_t *tokenbuf = create_jwt_token_kid("HS256", "hello.world/%"); - /* sign it */ - sign_jwt_token_hs256(tokenbuf, secret); - test_jwt_token(str_c(tokenbuf)); - test_end(); -} - -static void test_jwt_rs_token(void) -{ - const char *error; - - if (skip_dcrypt) - return; - - test_begin("JWT RSA token"); - /* write public key to file */ - oauth2_validation_key_cache_evict(key_cache, "default"); - save_key("RS256", rsa_public_key); - - buffer_t *tokenbuf = create_jwt_token("RS256"); - - /* sign token */ - buffer_t *sig = t_buffer_create(64); - struct dcrypt_private_key *key; - if (!dcrypt_key_load_private(&key, rsa_private_key, NULL, NULL, - &error) || - !dcrypt_sign(key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, - tokenbuf->data, tokenbuf->used, sig, - DCRYPT_PADDING_RSA_PKCS1, &error)) { - i_error("dcrypt signing failed: %s", error); - lib_exit(1); - } - dcrypt_key_unref_private(&key); - - /* convert to base64 */ - buffer_append(tokenbuf, ".", 1); - base64url_encode(BASE64_ENCODE_FLAG_NO_PADDING, SIZE_MAX, - sig->data, sig->used, tokenbuf); - - test_jwt_token(str_c(tokenbuf)); - - test_end(); -} - -static void test_jwt_ps_token(void) -{ - const char *error; - - if (skip_dcrypt) - return; - - test_begin("JWT RSAPSS token"); - /* write public key to file */ - oauth2_validation_key_cache_evict(key_cache, "default"); - save_key("PS256", rsa_public_key); - - buffer_t *tokenbuf = create_jwt_token("PS256"); - - /* sign token */ - buffer_t *sig = t_buffer_create(64); - struct dcrypt_private_key *key; - if (!dcrypt_key_load_private(&key, rsa_private_key, NULL, NULL, - &error) || - !dcrypt_sign(key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, - tokenbuf->data, tokenbuf->used, sig, - DCRYPT_PADDING_RSA_PKCS1_PSS, &error)) { - i_error("dcrypt signing failed: %s", error); - lib_exit(1); - } - dcrypt_key_unref_private(&key); - - /* convert to base64 */ - buffer_append(tokenbuf, ".", 1); - base64url_encode(BASE64_ENCODE_FLAG_NO_PADDING, SIZE_MAX, - sig->data, sig->used, tokenbuf); - - test_jwt_token(str_c(tokenbuf)); - - test_end(); -} - -static void test_jwt_ec_token(void) -{ - const char *error; - - if (skip_dcrypt) - return; - - test_begin("JWT ECDSA token"); - struct dcrypt_keypair pair; - i_zero(&pair); - if (!dcrypt_keypair_generate(&pair, DCRYPT_KEY_EC, 0, - "prime256v1", &error)) { - i_error("dcrypt keypair generate failed: %s", error); - lib_exit(1); - } - /* export public key */ - buffer_t *keybuf = t_buffer_create(256); - if (!dcrypt_key_store_public(pair.pub, DCRYPT_FORMAT_PEM, keybuf, - &error)) { - i_error("dcrypt key store failed: %s", error); - lib_exit(1); - } - oauth2_validation_key_cache_evict(key_cache, "default"); - save_key("ES256", str_c(keybuf)); - - buffer_t *tokenbuf = create_jwt_token("ES256"); - - /* sign token */ - buffer_t *sig = t_buffer_create(64); - if (!dcrypt_sign(pair.priv, "sha256", DCRYPT_SIGNATURE_FORMAT_X962, - tokenbuf->data, tokenbuf->used, sig, - DCRYPT_PADDING_DEFAULT, &error)) { - i_error("dcrypt signing failed: %s", error); - lib_exit(1); - } - dcrypt_keypair_unref(&pair); - - /* convert to base64 */ - buffer_append(tokenbuf, ".", 1); - base64url_encode(BASE64_ENCODE_FLAG_NO_PADDING, SIZE_MAX, - sig->data, sig->used, tokenbuf); - test_jwt_token(str_c(tokenbuf)); - - test_end(); -} - -static void test_do_init(void) -{ - const char *error; - struct dcrypt_settings dcrypt_set = { - .module_dir = "../lib-dcrypt/.libs", - }; - struct dict_settings dict_set = { - .base_dir = ".", - }; - - i_unlink_if_exists(".keys"); - dict_driver_register(&dict_driver_file); - if (dict_init("file:.keys", &dict_set, &keys_dict, &error) < 0) - i_fatal("dict_init(file:.keys): %s", error); - if (!dcrypt_initialize(NULL, &dcrypt_set, &error)) { - i_error("No functional dcrypt backend found - " - "skipping some tests: %s", error); - skip_dcrypt = TRUE; - } - key_cache = oauth2_validation_key_cache_init(); - - /* write HMAC secret */ - hs_sign_key =buffer_create_dynamic(default_pool, 32); - void *ptr = buffer_append_space_unsafe(hs_sign_key, 32); - random_fill(ptr, 32); - buffer_t *b64_key = t_base64_encode(0, SIZE_MAX, - hs_sign_key->data, - hs_sign_key->used); - save_key("HS256", str_c(b64_key)); -} - -static void test_do_deinit(void) -{ - dict_deinit(&keys_dict); - dict_driver_unregister(&dict_driver_file); - oauth2_validation_key_cache_deinit(&key_cache); - i_unlink(".keys"); - buffer_free(&hs_sign_key); - dcrypt_deinitialize(); -} - -int main(void) -{ - static void (*test_functions[])(void) = { - test_do_init, - test_jwt_hs_token, - test_jwt_token_escape, - test_jwt_valid_token, - test_jwt_bad_valid_token, - test_jwt_broken_token, - test_jwt_dates, - test_jwt_key_files, - test_jwt_kid_escape, - test_jwt_rs_token, - test_jwt_ps_token, - test_jwt_ec_token, - test_do_deinit, - NULL - }; - int ret; - ret = test_run(test_functions); - return ret; -} diff --git a/src/lib-sasl/mech-oauthbearer.c b/src/lib-sasl/mech-oauthbearer.c deleted file mode 100644 index 62ec6bc7ae..0000000000 --- a/src/lib-sasl/mech-oauthbearer.c +++ /dev/null @@ -1,201 +0,0 @@ -/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "str.h" -#include "net.h" -#include "json-parser.h" -#include "istream.h" -#include "dsasl-client-private.h" - -struct oauthbearer_dsasl_client { - struct dsasl_client client; - const char *host; - const char *status; - in_port_t port; - bool output_sent; -}; - -static int -mech_oauthbearer_input(struct dsasl_client *_client, - const unsigned char *input, size_t input_len, - const char **error_r) -{ - struct oauthbearer_dsasl_client *client = - (struct oauthbearer_dsasl_client *)_client; - - if (!client->output_sent) { - if (input_len > 0) { - *error_r = "Server sent non-empty initial response"; - return -1; - } - } else { - client->status = ""; - /* if response is empty, authentication has *SUCCEEDED* */ - if (input_len == 0) - return 0; - - /* authentication has failed, try parse status. - we are only interested in extracting status if possible - so we don't really need to much error handling. */ - struct istream *is = i_stream_create_from_data(input, input_len); - const char *status = NULL, *value; - const char *error = NULL; - enum json_type jtype; - bool found_status = FALSE; - struct json_parser *parser = json_parser_init(is); - while (json_parse_next(parser, &jtype, &value)>0) { - if (found_status && status == NULL) { - if (jtype == JSON_TYPE_STRING || - jtype == JSON_TYPE_NUMBER) - status = t_strdup(value); - break; - } else if (jtype == JSON_TYPE_OBJECT_KEY && - strcmp(value, "status") == 0) { - found_status = TRUE; - } else json_parse_skip_next(parser); - } - - /* deinitialize json parser */ - int ret = json_parser_deinit(&parser, &error); - i_stream_unref(&is); - - if (status != NULL) - client->status = p_strdup(_client->pool, status); - else { - ret = -1; - if (error == NULL) - error = "Status value missing"; - } - if (ret < 0) - *error_r = t_strdup_printf("Error parsing JSON reply: %s", - error); - else - *error_r = t_strdup_printf("Failed to authenticate: %s", - client->status); - return -1; - } - return 0; -} - -static int -mech_oauthbearer_output(struct dsasl_client *_client, - const unsigned char **output_r, size_t *output_len_r, - const char **error_r) -{ - struct oauthbearer_dsasl_client *client = - (struct oauthbearer_dsasl_client *)_client; - string_t *str; - - if (_client->set.authid == NULL) { - *error_r = "authid not set"; - return -1; - } - if (_client->password == NULL) { - *error_r = "password not set"; - return -1; - } - - str = str_new(_client->pool, 64); - - str_printfa(str, "n,a=%s,\x01", _client->set.authid); - if (client->host != NULL && *client->host != '\0') - str_printfa(str, "host=%s\x01", client->host); - if (client->port > 0) - str_printfa(str, "port=%u\x01", client->port); - str_printfa(str, "auth=Bearer %s\x01", _client->password); - str_append_c(str, '\x01'); - - *output_r = str_data(str); - *output_len_r = str_len(str); - client->output_sent = TRUE; - return 0; -} - -static int -mech_xoauth2_output(struct dsasl_client *_client, - const unsigned char **output_r, size_t *output_len_r, - const char **error_r) -{ - struct oauthbearer_dsasl_client *client = - (struct oauthbearer_dsasl_client *)_client; - string_t *str; - - if (_client->set.authid == NULL) { - *error_r = "authid not set"; - return -1; - } - if (_client->password == NULL) { - *error_r = "password not set"; - return -1; - } - - str = str_new(_client->pool, 64); - - str_printfa(str, "user=%s\x01", _client->set.authid); - str_printfa(str, "auth=Bearer %s\x01", _client->password); - str_append_c(str, '\x01'); - - *output_r = str_data(str); - *output_len_r = str_len(str); - client->output_sent = TRUE; - return 0; -} - -static int -mech_oauthbearer_set_parameter(struct dsasl_client *_client, const char *key, - const char *value, const char **error_r) -{ - struct oauthbearer_dsasl_client *client = - (struct oauthbearer_dsasl_client *)_client; - if (strcmp(key, "host") == 0) { - if (value != NULL) - client->host = p_strdup(_client->pool, value); - else - client->host = NULL; - return 1; - } else if (strcmp(key, "port") == 0) { - if (value == NULL) { - client->port = 0; - } else if (net_str2port(value, &client->port) < 0) { - *error_r = "Invalid port value"; - return -1; - } - return 1; - } - return 0; -} - -static int -mech_oauthbearer_get_result(struct dsasl_client *_client, const char *key, - const char **value_r, const char **error_r ATTR_UNUSED) -{ - struct oauthbearer_dsasl_client *client = - (struct oauthbearer_dsasl_client *)_client; - if (strcmp(key, "status") == 0) { - /* this is set to value after login attempt */ - i_assert(client->status != NULL); - *value_r = client->status; - return 1; - } - return 0; -} - -const struct dsasl_client_mech dsasl_client_mech_oauthbearer = { - .name = "OAUTHBEARER", - .struct_size = sizeof(struct oauthbearer_dsasl_client), - - .input = mech_oauthbearer_input, - .output = mech_oauthbearer_output, - .set_parameter = mech_oauthbearer_set_parameter, - .get_result = mech_oauthbearer_get_result, -}; - -const struct dsasl_client_mech dsasl_client_mech_xoauth2 = { - .name = "XOAUTH2", - .struct_size = sizeof(struct oauthbearer_dsasl_client), - - .input = mech_oauthbearer_input, - .output = mech_xoauth2_output, - .set_parameter = mech_oauthbearer_set_parameter, - .get_result = mech_oauthbearer_get_result, -}; diff --git a/src/lib/module-dir-load-dcrypt-backend.c b/src/lib/module-dir-load-dcrypt-backend.c deleted file mode 100644 index 052225c91d..0000000000 --- a/src/lib/module-dir-load-dcrypt-backend.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "module-dir.h" -#include "../lib-dcrypt/dcrypt-private.h" - -void module_dir_load_dcrypt_backend(struct module **modules, const char *mod_names) -{ - T_BEGIN { - const char **names = module_parse_names(mod_names); - - module_load_static(modules, names, "dcrypt_openssl", - dcrypt_openssl_init, - dcrypt_openssl_deinit); - } T_END; -}