]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
dcrypt: Add signature format
authorAki Tuomi <aki.tuomi@open-xchange.com>
Mon, 2 Sep 2019 09:54:12 +0000 (12:54 +0300)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Mon, 23 Sep 2019 05:47:57 +0000 (08:47 +0300)
Needed to implement RFC7515

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

index 88a48fd0c51164814485cf1fd2ee442a87d2a444..350d0020617eab6e644cf1b8c344aeaa83ec274b 100644 (file)
@@ -3141,11 +3141,94 @@ dcrypt_openssl_private_key_id(struct dcrypt_private_key *key,
        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;
+       }
+       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;
@@ -3192,13 +3275,78 @@ dcrypt_openssl_sign(struct dcrypt_private_key *key, const char *algorithm,
        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)
+{
+       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;
index 834ed7fab2f05317b056b8e642da572a0a5997dc..13da99e9006c68014cd354b23dad9cb0eadf15b8 100644 (file)
@@ -184,9 +184,11 @@ struct dcrypt_vfs {
        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,
index ef55c69e4df8ccce0b34f2b854737d39175566dd..a79c889c15a7c94faff244a4fe67f63423469770 100644 (file)
@@ -589,6 +589,7 @@ void dcrypt_key_set_usage_private(struct dcrypt_private_key *key,
 }
 
 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 @@ bool dcrypt_sign(struct dcrypt_private_key *key, const char *algorithm,
                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 @@ bool dcrypt_verify(struct dcrypt_public_key *key, const char *algorithm,
                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);
 }
index 62fa1949dbbdbc5d7d3dd2a834bff09019a8badf..0f976ebf9462166dca4fba92eefa75c8fb057e06 100644 (file)
@@ -57,6 +57,11 @@ enum dcrypt_key_usage {
        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 @@ bool dcrypt_ecdh_derive_secret_peer(struct dcrypt_public_key *peer_key,
 
 /* 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,
index 2f5267eef0cb3d118458c55979f62ebc7d397c91..ce43791a290467026daa358306fa72a0c06fa1b7 100644 (file)
@@ -1051,11 +1051,11 @@ static void test_sign_verify_rsa(void)
        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 @@ static void test_sign_verify_ecdsa(void)
        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 @@ static void test_static_verify_ecdsa(void)
        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 @@ static void test_jwk_keys(void)
          "\"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 @@ static void test_static_verify_rsa(void)
        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 @@ static void test_static_verify_rsa(void)
        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 @@ int main(void)
                test_sign_verify_ecdsa,
                test_static_verify_ecdsa,
                test_static_verify_rsa,
+               test_static_verify_ecdsa_x962,
                NULL
        };