From 60c32fd85e3f944a31ba93a368e0dda27ee397c8 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Tue, 30 May 2017 15:39:52 +0200 Subject: abstract API: introduced new signing functions That is, the gnutls_privkey_sign_data2() and gnutls_privkey_sign_hash2(). The new functions perform signing with input the signature algorithm instead of the hash algorithm; that allows to use algorithms where the hash algorithm is not used, or the public key algorithm may be different than the key's. Signed-off-by: Nikos Mavrogiannopoulos --- lib/algorithms.h | 15 +++++ lib/algorithms/sign.c | 30 +++++----- lib/includes/gnutls/abstract.h | 13 +++++ lib/libgnutls.map | 2 + lib/privkey.c | 130 ++++++++++++++++++++++++++++++++++++++--- 5 files changed, 166 insertions(+), 24 deletions(-) diff --git a/lib/algorithms.h b/lib/algorithms.h index b666668cf0..3d8c95e910 100644 --- a/lib/algorithms.h +++ b/lib/algorithms.h @@ -292,6 +292,21 @@ enum encipher_type _gnutls_kx_encipher_type(gnutls_kx_algorithm_t algorithm); /* Functions for sign algorithms. */ + +struct gnutls_sign_entry_st { + const char *name; + const char *oid; + gnutls_sign_algorithm_t id; + gnutls_pk_algorithm_t pk; + gnutls_digest_algorithm_t hash; + /* See RFC 5246 HashAlgorithm and SignatureAlgorithm + for values to use in aid struct. */ + const sign_algorithm_st aid; +}; +typedef struct gnutls_sign_entry_st gnutls_sign_entry_st; + +const gnutls_sign_entry_st *_gnutls_sign_to_entry(gnutls_sign_algorithm_t sign); + gnutls_pk_algorithm_t _gnutls_x509_sign_to_pk(gnutls_sign_algorithm_t sign); const char *_gnutls_x509_sign_to_oid(gnutls_pk_algorithm_t, diff --git a/lib/algorithms/sign.c b/lib/algorithms/sign.c index 1c19cc86fb..b64221bcfb 100644 --- a/lib/algorithms/sign.c +++ b/lib/algorithms/sign.c @@ -28,17 +28,6 @@ /* signature algorithms; */ -struct gnutls_sign_entry { - const char *name; - const char *oid; - gnutls_sign_algorithm_t id; - gnutls_pk_algorithm_t pk; - gnutls_digest_algorithm_t mac; - /* See RFC 5246 HashAlgorithm and SignatureAlgorithm - for values to use in aid struct. */ - const sign_algorithm_st aid; -}; -typedef struct gnutls_sign_entry gnutls_sign_entry; #define TLS_SIGN_AID_UNKNOWN {255, 255} static const sign_algorithm_st unknown_tls_aid = TLS_SIGN_AID_UNKNOWN; @@ -46,7 +35,7 @@ static const sign_algorithm_st unknown_tls_aid = TLS_SIGN_AID_UNKNOWN; /* Signature algorithms may be listed twice with a different PK algorithm, * e.g., RSA-PSS-SHA256 can be generated by GNUTLS_PK_RSA or GNUTLS_PK_RSA_PSS. */ -static const gnutls_sign_entry sign_algorithms[] = { +static const gnutls_sign_entry_st sign_algorithms[] = { {"RSA-SHA1", SIG_RSA_SHA1_OID, GNUTLS_SIGN_RSA_SHA1, GNUTLS_PK_RSA, GNUTLS_DIG_SHA1, {2, 1}}, {"RSA-SHA1", ISO_SIG_RSA_SHA1_OID, GNUTLS_SIGN_RSA_SHA1, @@ -146,7 +135,7 @@ static const gnutls_sign_entry sign_algorithms[] = { #define GNUTLS_SIGN_LOOP(b) \ do { \ - const gnutls_sign_entry *p; \ + const gnutls_sign_entry_st *p; \ for(p = sign_algorithms; p->name != NULL; p++) { b ; } \ } while (0) @@ -185,7 +174,7 @@ int gnutls_sign_is_secure(gnutls_sign_algorithm_t algorithm) gnutls_digest_algorithm_t dig = GNUTLS_DIG_UNKNOWN; /* avoid prefix */ - GNUTLS_SIGN_ALG_LOOP(dig = p->mac); + GNUTLS_SIGN_ALG_LOOP(dig = p->hash); if (dig != GNUTLS_DIG_UNKNOWN) return _gnutls_digest_is_secure(hash_to_entry(dig)); @@ -289,7 +278,7 @@ gnutls_pk_to_sign(gnutls_pk_algorithm_t pk, gnutls_digest_algorithm_t hash) gnutls_sign_algorithm_t ret = 0; GNUTLS_SIGN_LOOP( - if (pk == p->pk && hash == p->mac) { + if (pk == p->pk && hash == p->hash) { ret = p->id; break; } @@ -336,7 +325,7 @@ gnutls_sign_get_hash_algorithm(gnutls_sign_algorithm_t sign) { gnutls_digest_algorithm_t ret = GNUTLS_DIG_UNKNOWN; - GNUTLS_SIGN_ALG_LOOP(ret = p->mac); + GNUTLS_SIGN_ALG_LOOP(ret = p->hash); return ret; } @@ -422,3 +411,12 @@ const sign_algorithm_st *_gnutls_sign_to_tls_aid(gnutls_sign_algorithm_t return ret; } + +const gnutls_sign_entry_st *_gnutls_sign_to_entry(gnutls_sign_algorithm_t sign) +{ + const gnutls_sign_entry_st *ret = NULL; + + GNUTLS_SIGN_ALG_LOOP(ret = p); + + return ret; +} diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h index 5acc6bc555..94bb9b9042 100644 --- a/lib/includes/gnutls/abstract.h +++ b/lib/includes/gnutls/abstract.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2010-2012 Free Software Foundation, Inc. + * Copyright (C) 2015-2017 Red Hat, Inc. * * Author: Nikos Mavrogiannopoulos * @@ -404,6 +405,12 @@ int gnutls_privkey_sign_data(gnutls_privkey_t signer, const gnutls_datum_t * data, gnutls_datum_t * signature); +int gnutls_privkey_sign_data2(gnutls_privkey_t signer, + gnutls_sign_algorithm_t algo, + unsigned int flags, + const gnutls_datum_t * data, + gnutls_datum_t * signature); + #define gnutls_privkey_sign_raw_data(key, flags, data, sig) \ gnutls_privkey_sign_hash ( key, 0, GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA, data, sig) @@ -413,6 +420,12 @@ int gnutls_privkey_sign_hash(gnutls_privkey_t signer, const gnutls_datum_t * hash_data, gnutls_datum_t * signature); +int gnutls_privkey_sign_hash2(gnutls_privkey_t signer, + gnutls_sign_algorithm_t algo, + unsigned int flags, + const gnutls_datum_t * hash_data, + gnutls_datum_t * signature); + int gnutls_privkey_decrypt_data(gnutls_privkey_t key, unsigned int flags, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 672c55bdc1..d32f482e95 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1156,6 +1156,8 @@ GNUTLS_3_4 gnutls_x509_crq_set_pk_algorithm; gnutls_x509_privkey_get_pk_algorithm3; gnutls_sign_supports_pk_algorithm; + gnutls_privkey_sign_hash2; + gnutls_privkey_sign_data2; local: *; }; diff --git a/lib/privkey.c b/lib/privkey.c index d114160011..1c61bfe317 100644 --- a/lib/privkey.c +++ b/lib/privkey.c @@ -2,6 +2,7 @@ * GnuTLS PKCS#11 support * Copyright (C) 2010-2014 Free Software Foundation, Inc. * Copyright (C) 2012-2015 Nikos Mavrogiannopoulos + * Copyright (C) 2016-2017 Red Hat, Inc. * * Author: Nikos Mavrogiannopoulos * @@ -42,7 +43,7 @@ static int privkey_sign_hash(gnutls_privkey_t signer, const gnutls_datum_t * hash_data, gnutls_datum_t * signature, - gnutls_x509_spki_st * params); + gnutls_x509_spki_st * params, unsigned flags); static int _gnutls_privkey_sign_raw_data(gnutls_privkey_t key, @@ -1227,6 +1228,117 @@ gnutls_privkey_sign_data(gnutls_privkey_t signer, return privkey_sign_data(signer, data, signature, ¶ms); } +/** + * gnutls_privkey_sign_data2: + * @signer: Holds the key + * @algo: The signature algorithm used + * @flags: Zero or one of %gnutls_privkey_flags_t + * @data: holds the data to be signed + * @signature: will contain the signature allocated with gnutls_malloc() + * + * This function will sign the given data using the specified signature + * algorithm. This function is an enhancement of gnutls_privkey_sign_data(), + * as it allows utilizing a alternative signature algorithm where possible + * (e.g, use an RSA key with RSA-PSS). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.0 + **/ +int +gnutls_privkey_sign_data2(gnutls_privkey_t signer, + gnutls_sign_algorithm_t algo, + unsigned int flags, + const gnutls_datum_t * data, + gnutls_datum_t * signature) +{ + int ret; + gnutls_x509_spki_st params; + const gnutls_sign_entry_st *e; + + if (flags & GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + e = _gnutls_sign_to_entry(algo); + if (e == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = _gnutls_privkey_get_sign_params(signer, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_privkey_update_sign_params(signer, e->pk, e->hash, + flags, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return privkey_sign_data(signer, data, signature, ¶ms); +} + +/** + * gnutls_privkey_sign_hash2: + * @signer: Holds the signer's key + * @algo: The signature algorithm used + * @flags: Zero or one of %gnutls_privkey_flags_t + * @hash_data: holds the data to be signed + * @signature: will contain newly allocated signature + * + * This function will sign the given hashed data using a signature algorithm + * supported by the private key. Signature algorithms are always used + * together with a hash functions. Different hash functions may be + * used for the RSA algorithm, but only SHA-XXX for the DSA keys. + * + * You may use gnutls_pubkey_get_preferred_hash_algorithm() to determine + * the hash algorithm. + * + * The flags may be %GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA or %GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS. + * In the former case this function will ignore @hash_algo and perform a raw PKCS1 signature, + * and in the latter an RSA-PSS signature will be generated. + * + * Note that, not all algorithm support signing already signed data. When + * signing with Ed25519, gnutls_privkey_sign_data() should be used. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.0 + **/ +int +gnutls_privkey_sign_hash2(gnutls_privkey_t signer, + gnutls_sign_algorithm_t algo, + unsigned int flags, + const gnutls_datum_t * hash_data, + gnutls_datum_t * signature) +{ + int ret; + gnutls_x509_spki_st params; + const gnutls_sign_entry_st *e; + + e = _gnutls_sign_to_entry(algo); + if (e == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = _gnutls_privkey_get_sign_params(signer, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_privkey_update_sign_params(signer, e->pk, e->hash, + flags, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return privkey_sign_hash(signer, hash_data, signature, ¶ms, flags); +} + int privkey_sign_data(gnutls_privkey_t signer, const gnutls_datum_t * data, @@ -1267,6 +1379,7 @@ privkey_sign_data(gnutls_privkey_t signer, return ret; } + /** * gnutls_privkey_sign_hash: * @signer: Holds the signer's key @@ -1314,23 +1427,24 @@ gnutls_privkey_sign_hash(gnutls_privkey_t signer, return ret; } - if (flags & GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA) - return _gnutls_privkey_sign_raw_data(signer, - hash_data, signature, - ¶ms); - - return privkey_sign_hash(signer, hash_data, signature, ¶ms); + return privkey_sign_hash(signer, hash_data, signature, ¶ms, flags); } static int privkey_sign_hash(gnutls_privkey_t signer, const gnutls_datum_t * hash_data, gnutls_datum_t * signature, - gnutls_x509_spki_st * params) + gnutls_x509_spki_st * params, + unsigned flags) { int ret; gnutls_datum_t digest; + if (flags & GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA) + return _gnutls_privkey_sign_raw_data(signer, + hash_data, signature, + params); + digest.data = gnutls_malloc(hash_data->size); if (digest.data == NULL) { gnutls_assert(); -- cgit v1.2.1