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
 	};