]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-dcrypt: Support ED448 and ED25519 signatures
authorAki Tuomi <aki.tuomi@open-xchange.com>
Thu, 7 Dec 2023 09:40:18 +0000 (11:40 +0200)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 17 Jan 2024 08:29:09 +0000 (10:29 +0200)
src/lib-dcrypt/dcrypt-openssl1.c
src/lib-dcrypt/dcrypt-openssl3.c
src/lib-dcrypt/test-crypto.c

index da603ff6bec29fbd29cb266f0e86872673b0bffb..1a5ccf2cb7a3d7b039a7802bb6b4b767e896bb1d 100644 (file)
 #  define HAVE_X25519
 #  define IS_XD_CURVE(nid) \
        ((nid) == NID_X25519 || (nid) == NID_X448)
+# define IS_ED_CURVE(nid) \
+       ((nid) == NID_ED25519 || (nid) == NID_ED448)
 #endif
 
 struct dcrypt_context_symmetric {
@@ -1029,9 +1031,8 @@ dcrypt_openssl_generate_keypair(struct dcrypt_keypair *pair_r,
                        return FALSE;
                }
 #ifdef HAVE_X25519
-               if (IS_XD_CURVE(nid)) {
-                       if (!dcrypt_openssl_generate_xd_key(nid, &pkey,
-                                                           error_r))
+               if (IS_XD_CURVE(nid) || IS_ED_CURVE(nid)) {
+                       if (!dcrypt_openssl_generate_xd_key(nid, &pkey, error_r))
                                return dcrypt_openssl_error(error_r);
                } else
 #endif
@@ -1471,7 +1472,7 @@ dcrypt_openssl_load_private_key_dovecot_v2(struct dcrypt_private_key **key_r,
                (*key_r)->key = pkey;
                (*key_r)->ref++;
 #ifdef HAVE_X25519
-       } else if (IS_XD_CURVE(nid)) {
+       } else if (IS_XD_CURVE(nid) || IS_ED_CURVE(nid)) {
                size_t len;
                const unsigned char *ptr = buffer_get_data(key_data, &len);
                EVP_PKEY *pkey =
@@ -3094,7 +3095,7 @@ dcrypt_openssl_private_to_public_key(struct dcrypt_private_key *priv_key,
                EVP_PKEY_set1_RSA(pk, rsa);
                RSA_free(rsa);
 #ifdef HAVE_X25519
-       } else if (IS_XD_CURVE(nid)) {
+       } else if (IS_XD_CURVE(nid) || IS_ED_CURVE(nid)) {
                unsigned char buffer[128];
                size_t len = 128;
                EVP_PKEY_get_raw_public_key(pkey, buffer, &len);
@@ -3727,6 +3728,12 @@ dcrypt_openssl_sign(struct dcrypt_private_key *key, const char *algorithm,
                return FALSE;
        }
 
+#ifdef HAVE_X25519
+       if (EVP_PKEY_base_id(key->key) == NID_ED25519 ||
+           EVP_PKEY_base_id(key->key) == NID_ED448)
+               md = NULL;
+#endif
+
        dctx = EVP_MD_CTX_create();
 
        /* NB! Padding is set only on RSA signatures
@@ -3845,6 +3852,12 @@ dcrypt_openssl_verify(struct dcrypt_public_key *key, const char *algorithm,
                return FALSE;
        }
 
+#ifdef HAVE_X25519
+       if (EVP_PKEY_base_id(key->key) == NID_ED25519 ||
+           EVP_PKEY_base_id(key->key) == NID_ED448)
+               md = NULL;
+#endif
+
        dctx = EVP_MD_CTX_create();
 
        /* NB! Padding is set only on RSA signatures
index e230568376e1521ec1f76417cfeea46cfee83676..3f9842cb153ab1ca28db738905bef6056d1fd9c9 100644 (file)
        ERR_get_error_line_data(NULL, NULL, data, flags)
 #endif
 
-#define IS_XD_CURVE(nid) ((nid) == NID_X25519 || (nid) == NID_X448)
+#define IS_XD_CURVE(nid) \
+       ((nid) == NID_X25519 || (nid) == NID_X448)
+#define IS_ED_CURVE(nid) \
+       ((nid) == NID_ED25519 || (nid) == NID_ED448)
 
 struct dcrypt_context_symmetric {
        pool_t pool;
@@ -1086,9 +1089,8 @@ dcrypt_openssl_generate_keypair(struct dcrypt_keypair *pair_r,
                        *error_r = t_strdup_printf("Unknown EC curve %s", curve);
                        return FALSE;
                }
-               if (IS_XD_CURVE(nid)) {
-                       if (!dcrypt_openssl_generate_xd_key(curve, &pkey,
-                                                           error_r))
+               if (IS_XD_CURVE(nid) || IS_ED_CURVE(nid)) {
+                       if (!dcrypt_openssl_generate_xd_key(curve, &pkey, error_r))
                                return dcrypt_openssl_error(error_r);
                } else if (!dcrypt_openssl_generate_ec_key(nid, &pkey,
                                                           error_r)) {
@@ -1477,9 +1479,10 @@ dcrypt_openssl_load_private_key_dovecot_v2(struct dcrypt_private_key **key_r,
                *key_r = i_new(struct dcrypt_private_key, 1);
                (*key_r)->key = pkey;
                (*key_r)->ref++;
-       } else if (IS_XD_CURVE(nid)) {
-               EVP_PKEY *pkey = EVP_PKEY_new_raw_private_key(
-                       nid, NULL, key_data->data, key_data->used);
+       } else if (IS_XD_CURVE(nid) || IS_ED_CURVE(nid)) {
+               EVP_PKEY *pkey =
+                       EVP_PKEY_new_raw_private_key(nid, NULL, key_data->data,
+                                                    key_data->used);
                if (pkey == NULL)
                        return dcrypt_openssl_error(error_r);
 
@@ -2665,7 +2668,8 @@ dcrypt_openssl_store_private_key_dovecot(struct dcrypt_private_key *key,
                ptr = buffer_append_space_unsafe(buf, len);
                BN_bn2mpi(pk, ptr);
                BN_free(pk);
-       } else if (IS_XD_CURVE(EVP_PKEY_base_id(pkey))) {
+       } else if (IS_XD_CURVE(EVP_PKEY_base_id(pkey)) ||
+                  IS_ED_CURVE(EVP_PKEY_base_id(pkey))) {
                size_t len;
                unsigned char *ptr = buffer_append_space_unsafe(buf, 64);
                EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY,
@@ -3305,7 +3309,7 @@ dcrypt_openssl_private_key_type(struct dcrypt_private_key *key)
        int id = EVP_PKEY_base_id(priv);
        if (id == EVP_PKEY_RSA)
                return DCRYPT_KEY_RSA;
-       else if (id == EVP_PKEY_EC || IS_XD_CURVE(id))
+       else if (id == EVP_PKEY_EC || IS_XD_CURVE(id) || IS_ED_CURVE(id))
                return DCRYPT_KEY_EC;
        else i_unreached();
 }
@@ -3318,7 +3322,7 @@ dcrypt_openssl_public_key_type(struct dcrypt_public_key *key)
        int id = EVP_PKEY_base_id(pub);
        if (id == EVP_PKEY_RSA)
                return DCRYPT_KEY_RSA;
-       else if (id == EVP_PKEY_EC || IS_XD_CURVE(id))
+       else if (id == EVP_PKEY_EC || IS_XD_CURVE(id) || IS_ED_CURVE(id))
                return DCRYPT_KEY_EC;
        else i_unreached();
 }
@@ -3521,6 +3525,9 @@ dcrypt_openssl_sign(struct dcrypt_private_key *key, const char *algorithm,
        if (pad == -1)
                return FALSE;
 
+       if (IS_ED_CURVE(EVP_PKEY_base_id(key->key)))
+               algorithm = NULL;
+
        EVP_MD_CTX *dctx = EVP_MD_CTX_create();
        /* do not preallocate - will cause memory leak */
        EVP_PKEY_CTX *pctx = NULL;
@@ -3594,10 +3601,16 @@ dcrypt_openssl_verify(struct dcrypt_public_key *key, const char *algorithm,
        if (pad == -1)
                return FALSE;
 
+       if (IS_ED_CURVE(EVP_PKEY_base_id(key->key)))
+               algorithm = NULL;
+
        EVP_MD_CTX *dctx = EVP_MD_CTX_create();
        /* do not preallocate, causes memory leak */
        EVP_PKEY_CTX *pctx = NULL;
 
+       if (IS_ED_CURVE(EVP_PKEY_base_id(key->key)))
+               algorithm = NULL;
+
        /* NB! Padding is set only on RSA signatures
           ECDSA signatures use whatever is default */
        if (EVP_DigestVerifyInit_ex(dctx, &pctx, algorithm, NULL, NULL,
index 3b21d9ac4561400551ec9ca9119063821fba0c20..ff86cd0925d7e76276feee995e451f41c531cd28 100644 (file)
@@ -1725,6 +1725,131 @@ static void test_static_verify_ecdsa_x962(void)
 }
 
 #ifdef HAVE_X25519
+static void test_sign_verify_ed25519(void)
+{
+       const char *error = NULL;
+       bool valid, ret;
+       struct dcrypt_keypair pair;
+       buffer_t *signature =
+               buffer_create_dynamic(pool_datastack_create(), 128);
+       const char *data = "signed data";
+
+       test_begin("sign and verify (ed25519)");
+
+       ret = dcrypt_keypair_generate(&pair, DCRYPT_KEY_EC, 0, "ED25519", &error);
+       if (!ret)
+               i_panic("%s", error);
+
+       test_assert(dcrypt_sign(pair.priv, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+                data, strlen(data), signature, 0, &error));
+       /* verify signature */
+       test_assert(dcrypt_verify(pair.pub, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+                data, strlen(data),
+                signature->data, signature->used, &valid, 0, &error) && valid);
+
+       dcrypt_keypair_unref(&pair);
+
+       test_end();
+}
+
+static void test_static_verify_ed25519(void)
+{
+       const char *error = NULL;
+       bool valid, ret;
+       struct dcrypt_public_key *pub;
+       const char *data = "signed data";
+       const unsigned char sig[] = {
+               0xf6,0xc5,0xf9,0x6e,0x6c,0x42,0x6d,0xa8,0xbc,0x9f,0xc8,0xbe,
+               0x87,0x17,0x38,0x40,0xd9,0x5b,0x4b,0xee,0xba,0x64,0x1a,0xba,
+               0xb2,0xac,0x94,0x8c,0x25,0xd5,0x2b,0x1f,0x98,0x73,0x98,0x40,
+               0x85,0x33,0xfa,0xb9,0x40,0xd6,0x75,0x61,0x29,0xaa,0xcb,0xf7,
+               0x69,0xa4,0x93,0x21,0xa2,0x64,0x9b,0xc2,0xcd,0x62,0x95,0x42,
+               0xea,0x93,0x6f,0x07,
+       };
+       const char *key =
+"-----BEGIN PUBLIC KEY-----\n"
+"MCowBQYDK2VwAyEAPfeu6ItbVmVPUV/nYYHvV/aVheD7iVUWO9/POgcCyYM=\n"
+"-----END PUBLIC KEY-----";
+       test_begin("static verify (ed25519)");
+
+       ret = dcrypt_key_load_public(&pub, key, &error);
+       if (!ret)
+               i_panic("%s", error);
+
+       /* verify signature */
+       test_assert(dcrypt_verify(pub, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+                data, strlen(data),
+                sig, sizeof(sig), &valid, 0, &error) && valid);
+       dcrypt_key_unref_public(&pub);
+
+       test_end();
+}
+
+static void test_static_verify_ed448(void)
+{
+       const char *error = NULL;
+       bool valid, ret;
+       struct dcrypt_public_key *pub;
+       const char *data = "signed data";
+       const unsigned char sig[] = {
+               0x9d,0x90,0x9b,0xe9,0x40,0xf5,0xee,0x4e,0x42,0x34,0xa5,0x90,
+               0x85,0x0d,0x6a,0xea,0x60,0xae,0xd2,0x49,0x39,0x5f,0x61,0x64,
+               0x16,0x22,0x36,0xa4,0x14,0x8b,0x61,0x4e,0x4e,0xfa,0x74,0x45,
+               0xcb,0xf8,0x8b,0x15,0xc1,0x59,0x99,0xaa,0x26,0x20,0x45,0x32,
+               0x8a,0xa0,0xed,0x21,0xce,0x39,0xa4,0x06,0x00,0x75,0xb1,0x70,
+               0x6e,0xe6,0xb6,0x89,0x94,0x72,0xcb,0x3a,0xe8,0x1d,0xaf,0x18,
+               0x01,0xde,0x58,0xbe,0x1f,0x72,0x5b,0x0c,0xc4,0x98,0xfb,0xba,
+               0x51,0x26,0x89,0x01,0xc3,0xea,0xa7,0xe6,0xb2,0xf6,0xe5,0xee,
+               0xa2,0x5a,0x72,0x84,0xc4,0xfc,0x81,0x1f,0x48,0x45,0x9f,0xd1,
+               0x44,0x1f,0x77,0x5c,0x3b,0x00,
+       };
+       const char *key =
+"-----BEGIN PUBLIC KEY-----\n"
+"MEMwBQYDK2VxAzoAnWMyXj/1VTCDWIyx0IKbezYsUI0dl80fAkQ3IK+U5+SqR2gw\n"
+"zhHZ7ewPFgiEiP/KY3qKLWJHSDcA\n"
+"-----END PUBLIC KEY-----";
+       test_begin("static verify (ed448)");
+
+       ret = dcrypt_key_load_public(&pub, key, &error);
+       if (!ret)
+               i_panic("%s", error);
+
+       /* verify signature */
+       test_assert(dcrypt_verify(pub, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+                data, strlen(data),
+                sig, sizeof(sig), &valid, 0, &error) && valid);
+       dcrypt_key_unref_public(&pub);
+
+       test_end();
+}
+
+static void test_sign_verify_ed448(void)
+{
+       const char *error = NULL;
+       bool valid, ret;
+       struct dcrypt_keypair pair;
+       buffer_t *signature =
+               buffer_create_dynamic(pool_datastack_create(), 128);
+       const char *data = "signed data";
+
+       test_begin("sign and verify (ed448)");
+
+       ret = dcrypt_keypair_generate(&pair, DCRYPT_KEY_EC, 0, "ED448", &error);
+       if (!ret)
+               i_panic("%s", error);
+
+       test_assert(dcrypt_sign(pair.priv, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+                data, strlen(data), signature, 0, &error));
+       /* verify signature */
+       test_assert(dcrypt_verify(pair.pub, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+                data, strlen(data),
+                signature->data, signature->used, &valid, 0, &error) && valid);
+
+       dcrypt_keypair_unref(&pair);
+
+       test_end();
+}
+
 static void test_xd25519_keypair(void)
 {
        test_begin("X25519 key exchange");
@@ -1821,6 +1946,10 @@ int main(void)
                test_static_verify_rsa,
                test_static_verify_ecdsa_x962,
 #ifdef HAVE_X25519
+               test_sign_verify_ed25519,
+               test_sign_verify_ed448,
+               test_static_verify_ed25519,
+               test_static_verify_ed448,
                test_xd25519_keypair,
                test_xd448_keypair,
 #endif