]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
tls-crypto: Add support for RSA-PSS signatures
authorTobias Brunner <tobias@strongswan.org>
Fri, 28 Aug 2020 06:59:37 +0000 (08:59 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 12 Feb 2021 10:45:47 +0000 (11:45 +0100)
PKCS#1 v1.5 signatures are not defined for use with TLS 1.3 (they can
only appear in certificates, we now send a signature_algorithms_cert
extension to indicate support for them).  So for RSA certificates, we
must support RSA-PSS signatures.

There are two sets of schemes, that are differentiated by the type of
RSA key used for the signature, one is for classic RSA keys (rsaEncryption
OID), which can also be used with PKCS#1 when using TLS 1.2, the other
is for RSA-PSS keys (RSASSA-PSS OID), which are not yet commonly
used (and can't be generated by our pki tool).  According to the RFC,
PSS must also be supported for TLS 1.2 if the schemes are included in
the signature_algorithms extension (e.g. OpenSSL does not use PKCS#1 v1.5
anymore if PSS is proposed).

This changes how these schemes are stored and enumerated (they are not
treated as combination of hash algo and key type anymore).

Legacy schemes (MD5/SHA-1) are removed.

src/libtls/tls_crypto.c
src/libtls/tls_crypto.h
src/libtls/tls_peer.c
src/libtls/tls_server.c

index 0aab3616d4a4b2f725087a871d2ed0e767381561..906c99baa2d40dd278bfe1c3d996ff8eea38bc86 100644 (file)
@@ -263,11 +263,42 @@ ENUM(tls_hash_algorithm_names, TLS_HASH_NONE, TLS_HASH_SHA512,
        "SHA512",
 );
 
-ENUM(tls_signature_algorithm_names, TLS_SIG_RSA, TLS_SIG_ECDSA,
-       "RSA",
-       "DSA",
-       "ECDSA",
+ENUM_BEGIN(tls_signature_scheme_names,
+                  TLS_SIG_RSA_PKCS1_SHA1, TLS_SIG_RSA_PKCS1_SHA1,
+       "RSA_PKCS1_SHA1");
+ENUM_NEXT(tls_signature_scheme_names,
+                 TLS_SIG_ECDSA_SHA1, TLS_SIG_ECDSA_SHA1, TLS_SIG_RSA_PKCS1_SHA1,
+       "ECDSA_SHA1");
+ENUM_NEXT(tls_signature_scheme_names,
+                 TLS_SIG_RSA_PKCS1_SHA256, TLS_SIG_RSA_PKCS1_SHA256, TLS_SIG_ECDSA_SHA1,
+       "RSA_PKCS1_SHA256");
+ENUM_NEXT(tls_signature_scheme_names,
+                 TLS_SIG_ECDSA_SHA256, TLS_SIG_ECDSA_SHA256, TLS_SIG_RSA_PKCS1_SHA256,
+       "ECDSA_SHA256");
+ENUM_NEXT(tls_signature_scheme_names,
+                 TLS_SIG_RSA_PKCS1_SHA384, TLS_SIG_RSA_PKCS1_SHA384, TLS_SIG_ECDSA_SHA256,
+       "RSA_PKCS1_SHA384");
+ENUM_NEXT(tls_signature_scheme_names,
+                 TLS_SIG_ECDSA_SHA384, TLS_SIG_ECDSA_SHA384, TLS_SIG_RSA_PKCS1_SHA384,
+       "ECDSA_SHA384");
+ENUM_NEXT(tls_signature_scheme_names,
+                 TLS_SIG_RSA_PKCS1_SHA512, TLS_SIG_RSA_PKCS1_SHA512, TLS_SIG_ECDSA_SHA384,
+       "RSA_PKCS1_SHA512");
+ENUM_NEXT(tls_signature_scheme_names,
+                 TLS_SIG_ECDSA_SHA512, TLS_SIG_ECDSA_SHA512, TLS_SIG_RSA_PKCS1_SHA512,
+       "ECDSA_SHA512");
+ENUM_NEXT(tls_signature_scheme_names,
+                 TLS_SIG_RSA_PSS_RSAE_SHA256, TLS_SIG_RSA_PSS_PSS_SHA512, TLS_SIG_ECDSA_SHA512,
+       "RSA_PSS_RSAE_SHA256",
+       "RSA_PSS_RSAE_SHA384",
+       "RSA_PSS_RSAE_SHA512",
+       "ED25519",
+       "ED448",
+       "RSA_PSS_PSS_SHA256",
+       "RSA_PSS_PSS_SHA384",
+       "RSA_PSS_PSS_SHA512",
 );
+ENUM_END(tls_signature_scheme_names, TLS_SIG_RSA_PSS_PSS_SHA512);
 
 ENUM_BEGIN(tls_client_certificate_type_names,
                   TLS_RSA_SIGN, TLS_DSS_EPHEMERAL_DH,
@@ -1372,60 +1403,84 @@ METHOD(tls_crypto_t, get_dh_group, diffie_hellman_group_t,
 }
 
 /**
- * Map signature schemes to TLS key types and hashes, ordered by preference
+ * Parameters for RSA/PSS signature schemes
+ */
+#define PSS_PARAMS(bits) static rsa_pss_params_t pss_params_sha##bits = { \
+       .hash = HASH_SHA##bits, \
+       .mgf1_hash = HASH_SHA##bits, \
+       .salt_len = HASH_SIZE_SHA##bits, \
+}
+
+PSS_PARAMS(256);
+PSS_PARAMS(384);
+PSS_PARAMS(512);
+
+/**
+ * Map TLS signature schemes, ordered by preference
  */
 static struct {
-       tls_signature_algorithm_t sig;
-       tls_hash_algorithm_t hash;
-       signature_scheme_t scheme;
+       tls_signature_scheme_t sig;
+       signature_params_t params;
+       /* min/max versions for use in CertificateVerify */
+       tls_version_t min_version;
+       tls_version_t max_version;
 } schemes[] = {
-       { TLS_SIG_ECDSA,        TLS_HASH_SHA256,        SIGN_ECDSA_WITH_SHA256_DER   },
-       { TLS_SIG_ECDSA,        TLS_HASH_SHA384,        SIGN_ECDSA_WITH_SHA384_DER   },
-       { TLS_SIG_ECDSA,        TLS_HASH_SHA512,        SIGN_ECDSA_WITH_SHA512_DER   },
-       { TLS_SIG_ECDSA,        TLS_HASH_SHA1,          SIGN_ECDSA_WITH_SHA1_DER     },
-       { TLS_SIG_RSA,          TLS_HASH_SHA256,        SIGN_RSA_EMSA_PKCS1_SHA2_256 },
-       { TLS_SIG_RSA,          TLS_HASH_SHA384,        SIGN_RSA_EMSA_PKCS1_SHA2_384 },
-       { TLS_SIG_RSA,          TLS_HASH_SHA512,        SIGN_RSA_EMSA_PKCS1_SHA2_512 },
-       { TLS_SIG_RSA,          TLS_HASH_SHA224,        SIGN_RSA_EMSA_PKCS1_SHA2_224 },
-       { TLS_SIG_RSA,          TLS_HASH_SHA1,          SIGN_RSA_EMSA_PKCS1_SHA1     },
-       { TLS_SIG_RSA,          TLS_HASH_MD5,           SIGN_RSA_EMSA_PKCS1_MD5      },
+       { TLS_SIG_ECDSA_SHA256, { .scheme = SIGN_ECDSA_WITH_SHA256_DER },
+               TLS_1_0, TLS_1_3 },
+       { TLS_SIG_ECDSA_SHA384, { .scheme = SIGN_ECDSA_WITH_SHA384_DER },
+               TLS_1_0, TLS_1_3 },
+       { TLS_SIG_ECDSA_SHA512, { .scheme = SIGN_ECDSA_WITH_SHA512_DER },
+               TLS_1_0, TLS_1_3 },
+       { TLS_SIG_ED25519, { .scheme = SIGN_ED25519 },
+               TLS_1_3, TLS_1_3 },
+       { TLS_SIG_ED448, { .scheme = SIGN_ED448 },
+               TLS_1_3, TLS_1_3 },
+       { TLS_SIG_RSA_PSS_RSAE_SHA256, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha256, },
+               TLS_1_2, TLS_1_3 },
+       { TLS_SIG_RSA_PSS_RSAE_SHA384, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha384, },
+               TLS_1_2, TLS_1_3 },
+       { TLS_SIG_RSA_PSS_RSAE_SHA512, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha512, },
+               TLS_1_2, TLS_1_3 },
+       /* the parameters for the next three should actually be taken from the
+        * public key, we currently don't have an API for that, so assume defaults */
+       { TLS_SIG_RSA_PSS_PSS_SHA256, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha256, },
+               TLS_1_2, TLS_1_3 },
+       { TLS_SIG_RSA_PSS_PSS_SHA384, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha384, },
+               TLS_1_2, TLS_1_3 },
+       { TLS_SIG_RSA_PSS_PSS_SHA512, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha512, },
+               TLS_1_2, TLS_1_3 },
+       { TLS_SIG_RSA_PKCS1_SHA256, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_256 },
+               TLS_1_0, TLS_1_2 },
+       { TLS_SIG_RSA_PKCS1_SHA384, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_384 },
+               TLS_1_0, TLS_1_2 },
+       { TLS_SIG_RSA_PKCS1_SHA512, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_512 },
+               TLS_1_0, TLS_1_2 },
 };
 
 METHOD(tls_crypto_t, get_signature_algorithms, void,
-       private_tls_crypto_t *this, bio_writer_t *writer)
+       private_tls_crypto_t *this, bio_writer_t *writer, bool cert)
 {
        bio_writer_t *supported;
-       tls_version_t version;
+       tls_version_t min_version, max_version;
        int i;
 
        supported = bio_writer_create(32);
-       version = this->tls->get_version_max(this->tls);
+
+       if (!cert)
+       {
+               min_version = this->tls->get_version_min(this->tls);
+               max_version = this->tls->get_version_max(this->tls);
+       }
 
        for (i = 0; i < countof(schemes); i++)
        {
-               if (schemes[i].sig == TLS_SIG_RSA && !this->rsa)
-               {
-                       continue;
-               }
-               if (schemes[i].sig == TLS_SIG_ECDSA && !this->ecdsa)
+               if ((cert || (schemes[i].min_version <= max_version &&
+                                         schemes[i].max_version >= min_version)) &&
+                       lib->plugins->has_feature(lib->plugins,
+                                               PLUGIN_PROVIDE(PUBKEY_VERIFY, schemes[i].params.scheme)))
                {
-                       continue;
-               }
-               if (schemes[i].hash == TLS_HASH_MD5 && version >= TLS_1_3)
-               {
-                       continue;
-               }
-               if (schemes[i].hash == TLS_HASH_SHA224 && version >= TLS_1_3)
-               {
-                       continue;
+                       supported->write_uint16(supported, schemes[i].sig);
                }
-               if (!lib->plugins->has_feature(lib->plugins,
-                                               PLUGIN_PROVIDE(PUBKEY_VERIFY, schemes[i].scheme)))
-               {
-                       continue;
-               }
-               supported->write_uint8(supported, schemes[i].hash);
-               supported->write_uint8(supported, schemes[i].sig);
        }
 
        supported->wrap16(supported);
@@ -1434,26 +1489,20 @@ METHOD(tls_crypto_t, get_signature_algorithms, void,
 }
 
 /**
- * Get the signature scheme from a TLS 1.2 hash/sig algorithm pair
+ * Get the signature parameters from a TLS signature scheme
  */
-static signature_scheme_t hashsig_to_scheme(key_type_t type,
-                                                                                       tls_hash_algorithm_t hash,
-                                                                                       tls_signature_algorithm_t sig)
+static signature_params_t *params_for_scheme(tls_signature_scheme_t sig)
 {
        int i;
 
-       if ((sig == TLS_SIG_RSA && type == KEY_RSA) ||
-               (sig == TLS_SIG_ECDSA && type == KEY_ECDSA))
+       for (i = 0; i < countof(schemes); i++)
        {
-               for (i = 0; i < countof(schemes); i++)
+               if (schemes[i].sig == sig)
                {
-                       if (schemes[i].sig == sig && schemes[i].hash == hash)
-                       {
-                               return schemes[i].scheme;
-                       }
+                       return &schemes[i].params;
                }
        }
-       return SIGN_UNKNOWN;
+       return NULL;
 }
 
 /**
@@ -1629,11 +1678,12 @@ METHOD(tls_crypto_t, sign, bool,
 {
        if (this->tls->get_version_max(this->tls) >= TLS_1_2)
        {
-               const chunk_t hashsig_def = chunk_from_chars(
-                                       TLS_HASH_SHA1, TLS_SIG_RSA, TLS_HASH_SHA1, TLS_SIG_ECDSA);
-               signature_scheme_t scheme;
+               /* fallback to SHA1/RSA and SHA1/ECDSA */
+               const chunk_t hashsig_def = chunk_from_chars(0x02, 0x01, 0x02, 0x03);
+               signature_params_t *params;
+               key_type_t type;
+               uint16_t scheme;
                bio_reader_t *reader;
-               uint8_t hash, alg;
                chunk_t sig;
                bool done = FALSE;
 
@@ -1641,15 +1691,16 @@ METHOD(tls_crypto_t, sign, bool,
                {       /* fallback if none given */
                        hashsig = hashsig_def;
                }
+               type = key->get_type(key);
                reader = bio_reader_create(hashsig);
                while (reader->remaining(reader) >= 2)
                {
-                       if (reader->read_uint8(reader, &hash) &&
-                               reader->read_uint8(reader, &alg))
+                       if (reader->read_uint16(reader, &scheme))
                        {
-                               scheme = hashsig_to_scheme(key->get_type(key), hash, alg);
-                               if (scheme != SIGN_UNKNOWN &&
-                                       key->sign(key, scheme, NULL, data, &sig))
+                               params = params_for_scheme(scheme);
+                               if (params &&
+                                       type == key_type_from_signature_scheme(params->scheme) &&
+                                       key->sign(key, params->scheme, params->params, data, &sig))
                                {
                                        done = TRUE;
                                        break;
@@ -1662,10 +1713,9 @@ METHOD(tls_crypto_t, sign, bool,
                        DBG1(DBG_TLS, "none of the proposed hash/sig algorithms supported");
                        return FALSE;
                }
-               DBG2(DBG_TLS, "created signature with %N/%N",
-                        tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg);
-               writer->write_uint8(writer, hash);
-               writer->write_uint8(writer, alg);
+               DBG2(DBG_TLS, "created signature with %N", tls_signature_scheme_names,
+                        scheme);
+               writer->write_uint16(writer, scheme);
                writer->write_data16(writer, sig);
                free(sig.ptr);
        }
@@ -1712,23 +1762,21 @@ METHOD(tls_crypto_t, verify, bool,
 {
        if (this->tls->get_version_max(this->tls) >= TLS_1_2)
        {
-               signature_scheme_t scheme = SIGN_UNKNOWN;
-               uint8_t hash, alg;
+               signature_params_t *params;
+               uint16_t scheme;
                chunk_t sig;
 
-               if (!reader->read_uint8(reader, &hash) ||
-                       !reader->read_uint8(reader, &alg) ||
+               if (!reader->read_uint16(reader, &scheme) ||
                        !reader->read_data16(reader, &sig))
                {
                        DBG1(DBG_TLS, "received invalid signature");
                        return FALSE;
                }
-               scheme = hashsig_to_scheme(key->get_type(key), hash, alg);
-               if (scheme == SIGN_UNKNOWN)
+               params = params_for_scheme(scheme);
+               if (!params)
                {
-                       DBG1(DBG_TLS, "signature algorithms %N/%N not supported",
-                                tls_hash_algorithm_names, hash,
-                                tls_signature_algorithm_names, alg);
+                       DBG1(DBG_TLS, "signature algorithms %N not supported",
+                                tls_signature_scheme_names, scheme);
                        return FALSE;
                }
                if (this->tls->get_version_max(this->tls) == TLS_1_3)
@@ -1743,15 +1791,14 @@ METHOD(tls_crypto_t, verify, bool,
 
                        data = chunk_cata("cm", tls13_sig_data_server, transcript_hash);
                }
-               if (!key->verify(key, scheme, NULL, data, sig))
+               if (!key->verify(key, params->scheme, params->params, data, sig))
                {
-                       DBG1(DBG_TLS, "signature verification with %N/%N failed",
-                                tls_hash_algorithm_names, hash, tls_signature_algorithm_names,
-                                alg);
+                       DBG1(DBG_TLS, "signature verification with %N failed",
+                                tls_signature_scheme_names, scheme);
                        return FALSE;
                }
-               DBG2(DBG_TLS, "verified signature with %N/%N",
-                        tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg);
+               DBG2(DBG_TLS, "verified signature with %N",
+                        tls_signature_scheme_names, scheme);
        }
        else
        {
index de6e56bade8c242a8a62add129d05b21a51fc971..b5dd4f930505d70984d0c0ce3e143aaa66d7f427 100644 (file)
@@ -29,7 +29,7 @@
 typedef struct tls_crypto_t tls_crypto_t;
 typedef enum tls_cipher_suite_t tls_cipher_suite_t;
 typedef enum tls_hash_algorithm_t tls_hash_algorithm_t;
-typedef enum tls_signature_algorithm_t tls_signature_algorithm_t;
+typedef enum tls_signature_scheme_t tls_signature_scheme_t;
 typedef enum tls_client_certificate_type_t tls_client_certificate_type_t;
 typedef enum tls_ecc_curve_type_t tls_ecc_curve_type_t;
 typedef enum tls_named_group_t tls_named_group_t;
@@ -287,18 +287,36 @@ enum tls_hash_algorithm_t {
 extern enum_name_t *tls_hash_algorithm_names;
 
 /**
- * TLS SignatureAlgorithm identifiers
+ * TLS SignatureScheme identifiers
  */
-enum tls_signature_algorithm_t {
-       TLS_SIG_RSA =           1,
-       TLS_SIG_DSA =           2,
-       TLS_SIG_ECDSA =         3,
+enum tls_signature_scheme_t {
+       /* legacy schemes compatible with TLS 1.2 (first byte is the hash algorithm,
+        * second the key type) */
+       TLS_SIG_RSA_PKCS1_SHA1 =                0x0201,
+       TLS_SIG_ECDSA_SHA1 =                    0x0203,
+       TLS_SIG_RSA_PKCS1_SHA256 =              0x0401,
+       TLS_SIG_ECDSA_SHA256 =                  0x0403,
+       TLS_SIG_RSA_PKCS1_SHA384 =              0x0501,
+       TLS_SIG_ECDSA_SHA384 =                  0x0503,
+       TLS_SIG_RSA_PKCS1_SHA512 =              0x0601,
+       TLS_SIG_ECDSA_SHA512 =                  0x0603,
+       /* RSASSA-PSS for public keys with OID rsaEncryption */
+       TLS_SIG_RSA_PSS_RSAE_SHA256 =   0x0804,
+       TLS_SIG_RSA_PSS_RSAE_SHA384 =   0x0805,
+       TLS_SIG_RSA_PSS_RSAE_SHA512 =   0x0806,
+       /* EdDSA */
+       TLS_SIG_ED25519 =                               0x0807,
+       TLS_SIG_ED448 =                                 0x0808,
+       /* RSASSA-PSS for public keys with OID RSASSA-PSS */
+       TLS_SIG_RSA_PSS_PSS_SHA256 =    0x0809,
+       TLS_SIG_RSA_PSS_PSS_SHA384 =    0x080a,
+       TLS_SIG_RSA_PSS_PSS_SHA512 =    0x080b,
 };
 
 /**
- * Enum names for tls_signature_algorithm_t
+ * Enum names for tls_signature_scheme_t
  */
-extern enum_name_t *tls_signature_algorithm_names;
+extern enum_name_t *tls_signature_scheme_names;
 
 /**
  * TLS ClientCertificateType
@@ -443,11 +461,14 @@ struct tls_crypto_t {
        diffie_hellman_group_t (*get_dh_group)(tls_crypto_t *this);
 
        /**
-        * Write the list of supported hash/sig algorithms to writer.
+        * Write the list of supported signature schemes, either for certificates
+        * or for CertificateVerify messages, to writer.
         *
-        * @param writer                writer to write supported hash/sig algorithms
+        * @param writer                writer to write supported signature schemes
+        * @param cert                  TRUE to return signature schemes supported in certs
         */
-       void (*get_signature_algorithms)(tls_crypto_t *this, bio_writer_t *writer);
+       void (*get_signature_algorithms)(tls_crypto_t *this, bio_writer_t *writer,
+                                                                        bool cert);
 
        /**
         * Create an enumerator over supported ECDH groups.
index 04c0072897dd4b51805eb0c2e8a63939b0373295..b5d5bef71395722c59b66c22ca8b53ef97ae3826 100644 (file)
@@ -1331,7 +1331,11 @@ static status_t send_client_hello(private_tls_peer_t *this,
        DBG2(DBG_TLS, "sending extension: %N",
                 tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS);
        extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS);
-       this->crypto->get_signature_algorithms(this->crypto, extensions);
+       this->crypto->get_signature_algorithms(this->crypto, extensions, FALSE);
+       DBG2(DBG_TLS, "sending extension: %N",
+                tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS_CERT);
+       extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS_CERT);
+       this->crypto->get_signature_algorithms(this->crypto, extensions, TRUE);
 
        if (this->dh)
        {
index f0bbb1a590e658b76caa102129e6cb4cb99ea6f1..8675dc20ec0c0ceb9703ef0fb61658bb1a026ddd 100644 (file)
@@ -777,7 +777,7 @@ static status_t send_certificate_request(private_tls_server_t *this,
        supported->destroy(supported);
        if (this->tls->get_version_max(this->tls) >= TLS_1_2)
        {
-               this->crypto->get_signature_algorithms(this->crypto, writer);
+               this->crypto->get_signature_algorithms(this->crypto, writer, TRUE);
        }
 
        authorities = bio_writer_create(64);