Mercurial > dovecot > core-2.3
changeset 26373:f962d5e2d1ce
dcrypt: Add signature format
Needed to implement RFC7515
author | Aki Tuomi <aki.tuomi@open-xchange.com> |
---|---|
date | Mon, 02 Sep 2019 12:54:12 +0300 |
parents | d0c3492bce55 |
children | d84b55aba44f |
files | src/lib-dcrypt/dcrypt-openssl.c src/lib-dcrypt/dcrypt-private.h src/lib-dcrypt/dcrypt.c src/lib-dcrypt/dcrypt.h src/lib-dcrypt/test-crypto.c |
diffstat | 5 files changed, 215 insertions(+), 14 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-dcrypt/dcrypt-openssl.c Thu Aug 29 17:09:55 2019 +0300 +++ b/src/lib-dcrypt/dcrypt-openssl.c Mon Sep 02 12:54:12 2019 +0300 @@ -3142,10 +3142,93 @@ } 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; + } + 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; + + /* 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; + const BIGNUM *s; + + ECDSA_SIG_get0(ec_sig, &r, &s); + + /* write r */ + int bytes = BN_num_bytes(r); + unsigned char *buf = buffer_append_space_unsafe(signature_r, bytes); + if (BN_bn2bin(r, buf) != bytes) { + ret = dcrypt_openssl_error(error_r); + } else { + bytes = BN_num_bytes(s); + buf = buffer_append_space_unsafe(signature_r, bytes); + if (BN_bn2bin(s, buf) != bytes) { + 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) { + *error_r = "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; @@ -3193,12 +3276,77 @@ } 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) +{ + 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(); + /* 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(); + ECDSA_SIG_set0(ec_sig, r, 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) { + *error_r = "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;
--- a/src/lib-dcrypt/dcrypt-private.h Thu Aug 29 17:09:55 2019 +0300 +++ b/src/lib-dcrypt/dcrypt-private.h Mon Sep 02 12:54:12 2019 +0300 @@ -184,9 +184,11 @@ 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,
--- a/src/lib-dcrypt/dcrypt.c Thu Aug 29 17:09:55 2019 +0300 +++ b/src/lib-dcrypt/dcrypt.c Mon Sep 02 12:54:12 2019 +0300 @@ -589,6 +589,7 @@ } 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) { @@ -599,11 +600,12 @@ return FALSE; } - return dcrypt_vfs->sign(key, algorithm, data, data_len, + 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, @@ -616,7 +618,7 @@ return FALSE; } - return dcrypt_vfs->verify(key, algorithm, data, data_len, + return dcrypt_vfs->verify(key, algorithm, format, data, data_len, signature, signature_len, valid_r, padding, error_r); }
--- a/src/lib-dcrypt/dcrypt.h Thu Aug 29 17:09:55 2019 +0300 +++ b/src/lib-dcrypt/dcrypt.h Mon Sep 02 12:54:12 2019 +0300 @@ -57,6 +57,11 @@ 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). @@ -233,12 +238,14 @@ /* 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,
--- a/src/lib-dcrypt/test-crypto.c Thu Aug 29 17:09:55 2019 +0300 +++ b/src/lib-dcrypt/test-crypto.c Mon Sep 02 12:54:12 2019 +0300 @@ -1051,11 +1051,11 @@ 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", data, strlen(data), signature, 0, &error)); + 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", data, strlen(data), + 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); @@ -1087,11 +1087,11 @@ 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", data, strlen(data), signature, 0, &error)); + 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", data, strlen(data), signature->data, + 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); @@ -1134,7 +1134,8 @@ 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", input, strlen(input), + test_assert(dcrypt_verify(pair.pub, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, + input, strlen(input), sig, sizeof(sig), &valid, 0, &error) && valid == TRUE); @@ -1150,8 +1151,8 @@ "\"crv\":\"P-256\"," "\"x\":\"Kp0Y4-Wpt-D9t_2XenFIj0LmvaZByLG69yOisek4aMI\"," "\"y\":\"wjEPB5BhH5SRPw1cCN5grWrLCphrW19fCFR8p7c9O5o\"," - "\"use\":\"sig\"," - "\"kid\":\"123\"," + "\"use\":\"sig\"," + "\"kid\":\"123\"," "\"d\":\"Po2z9rs86J2Qb_xWprr4idsWNPlgKf3G8-mftnE2ync\"}"; /* Acquired using another tool */ const char *pem_key = @@ -1217,7 +1218,8 @@ 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", data, strlen(data), + 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); @@ -1225,6 +1227,45 @@ 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) { struct dcrypt_settings set = { @@ -1261,6 +1302,7 @@ test_sign_verify_ecdsa, test_static_verify_ecdsa, test_static_verify_rsa, + test_static_verify_ecdsa_x962, NULL };