aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Weissberg <[email protected]>2017-12-21 13:33:23 +0100
committerFabian Weissberg <[email protected]>2017-12-22 09:13:22 +0100
commit53b8e128121c6f845e2196d79bce3dd39bd83228 (patch)
tree8e8ad207fec7db3373eec5fd1ab3850ff5b02898
parentc9940f43eb54c1e02cf07454bedcccf7c0873eed (diff)
Enable signing X509 structures with rsa-pss
-rw-r--r--doc/manual/cli.rst23
-rw-r--r--doc/manual/x509.rst24
-rw-r--r--src/build-data/oids.txt6
-rw-r--r--src/cli/x509.cpp11
-rw-r--r--src/lib/pk_pad/emsa.h19
-rw-r--r--src/lib/pk_pad/emsa1/emsa1.cpp36
-rw-r--r--src/lib/pk_pad/emsa1/emsa1.h5
-rw-r--r--src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp25
-rw-r--r--src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h14
-rw-r--r--src/lib/pk_pad/emsa_pssr/pssr.cpp52
-rw-r--r--src/lib/pk_pad/emsa_pssr/pssr.h7
-rw-r--r--src/lib/pk_pad/emsa_raw/emsa_raw.h4
-rw-r--r--src/lib/pk_pad/emsa_x931/emsa_x931.h3
-rw-r--r--src/lib/pk_pad/info.txt4
-rw-r--r--src/lib/pk_pad/iso9796/iso9796.cpp17
-rw-r--r--src/lib/pk_pad/iso9796/iso9796.h4
-rw-r--r--src/lib/pk_pad/padding.cpp42
-rw-r--r--src/lib/pk_pad/padding.h36
-rw-r--r--src/lib/x509/x509_ca.cpp88
-rw-r--r--src/lib/x509/x509_ca.h36
-rw-r--r--src/lib/x509/x509opt.cpp7
-rw-r--r--src/lib/x509/x509self.cpp11
-rw-r--r--src/lib/x509/x509self.h7
-rwxr-xr-xsrc/scripts/oids.py78
-rw-r--r--src/tests/data/x509/misc/rsa_key.pem40
-rw-r--r--src/tests/test_pk_pad.cpp95
-rw-r--r--src/tests/unit_x509.cpp191
27 files changed, 821 insertions, 64 deletions
diff --git a/doc/manual/cli.rst b/doc/manual/cli.rst
index 85b93d5e1..ecebe74e5 100644
--- a/doc/manual/cli.rst
+++ b/doc/manual/cli.rst
@@ -95,23 +95,34 @@ Public Key Cryptography
X.509
----------------------------------------------
-``gen_pkcs10 key CN --country= --organization= --email= --key-pass= --hash=SHA-256``
+<<<<<<< HEAD
+``gen_pkcs10 key CN --country= --organization= --email= --key-pass= --hash=SHA-256 --emsa=``
Generate a PKCS #10 certificate signing request (CSR) using the passed PKCS #8
private key *key*. If the private key is encrypted, the decryption passphrase
- *key-pass* has to be passed.
+ *key-pass* has to be passed.*emsa* specifies the padding scheme to be used
+ when calculating the signature.
+
+ - For RSA keys EMSA4 (RSA-PSS) is the default scheme.
+ - For ECDSA, DSA, ECGDSA, ECKCDSA and GOST-34.10 keys *emsa* defaults to EMSA1.
-``gen_self_signed key CN --country= --dns= --organization= --email= --key-pass= --ca --hash=SHA-256``
+``gen_self_signed key CN --country= --dns= --organization= --email= --key-pass= --ca --hash=SHA-256 --emsa=``
Generate a self signed X.509 certificate using the PKCS #8 private key
*key*. If the private key is encrypted, the decryption passphrase *key-pass*
has to be passed. If *ca* is passed, the certificate is marked for certificate
- authority (CA) usage.
+ authority (CA) usage. *emsa* specifies the padding scheme to be used when
+ calculating the signature.
-``sign_cert --ca-key-pass= --hash=SHA-256 --duration=365 ca_cert ca_key pkcs10_req``
+ - For RSA keys EMSA4 (RSA-PSS) is the default scheme.
+ - For ECDSA, DSA, ECGDSA, ECKCDSA and GOST-34.10 keys *emsa* defaults to EMSA1.
+
+``sign_cert --ca-key-pass= --hash=SHA-256 --duration=365 --emsa= ca_cert ca_key pkcs10_req``
Create a CA signed X.509 certificate from the information contained in the
PKCS #10 CSR *pkcs10_req*. The CA certificate is passed as *ca_cert* and the
respective PKCS #8 private key as *ca_key*. If the private key is encrypted,
the decryption passphrase *ca-key-pass* has to be passed. The created
- certificate has a validity period of *duration* days.
+ certificate has a validity period of *duration* days. *emsa* specifies the
+ padding scheme to be used when calculating the signature. *emsa* defaults to
+ the padding scheme used in the CA certificate.
``ocsp_check subject issuer``
Verify an X.509 certificate against the issuers OCSP responder. Pass the
diff --git a/doc/manual/x509.rst b/doc/manual/x509.rst
index daf154c9a..f9431c362 100644
--- a/doc/manual/x509.rst
+++ b/doc/manual/x509.rst
@@ -531,6 +531,21 @@ The private ``key`` is the private key corresponding to the public key in the
CA's certificate. ``hash_fn`` is the name of the hash function to use
for signing, e.g., `SHA-256`. ``rng`` is queried for random during signing.
+There is an alternative constructor that lets you set additional options, namely
+the padding scheme that will be used by the X509_CA object to sign certificates
+and certificate revocation lists. If the padding is not set explicitly, the CA
+will use the padding scheme that was used when signing the CA certificate.
+
+.. cpp:function:: X509_CA::X509_CA(const X509_Certificate& cert, \
+ const Private_Key& key, \
+ const std::map<std::string,std::string>& opts, \
+ const std::string& hash_fn, \
+ RandomNumberGenerator& rng)
+
+The only option valid at this moment is "padding". The supported padding schemes
+can be found in src/lib/pubkey/padding.cpp. Some alternative names for the
+padding schemes are understood, as well.
+
Requests for new certificates are supplied to a CA in the form of PKCS
#10 certificate requests (called a ``PKCS10_Request`` object in
Botan). These are decoded in a similar manner to
@@ -668,6 +683,15 @@ to want to use is to create (or request) a CA certificate, which can
be done by calling the member function ``CA_key``. This should only be
used when needed.
+Moreover, you can specify the padding scheme to be used when digital signatures
+are computed by calling function ``set_padding_scheme`` with a string
+representing the padding scheme. This way, you can control the padding scheme
+for self-signed certificates and PKCS #10 requests. The padding scheme used by
+a CA when building a certificate or a certificate revocation list can be set in
+the ``X509_CA`` constructor. The supported padding schemes can be found in
+src/lib/pubkey/padding.cpp. Some alternative names for the padding schemes are
+understood, as well.
+
Other constraints can be set by calling the member functions
``add_constraints`` and ``add_ex_constraints``. The first takes a
``Key_Constraints`` value, and replaces any previously set value. If
diff --git a/src/build-data/oids.txt b/src/build-data/oids.txt
index 6584f7e6d..089523e69 100644
--- a/src/build-data/oids.txt
+++ b/src/build-data/oids.txt
@@ -1,4 +1,8 @@
-# Regenerate with ./src/scripts/oids.py oids > src/lib/asn1/oids.cpp AND ./src/scripts/oids.py dn_ub > src/lib/x509/x509_dn_ub.cpp
+# Regenerate with ./src/scripts/oids.py oids > src/lib/asn1/oids.cpp
+# AND ./src/scripts/oids.py dn_ub > src/lib/x509/x509_dn_ub.cpp
+# (if you modified something under [dn]
+# AND ./src/scripts/oids.py pads > src/lib/pk_pad/padding.cpp
+# (if you modified something under [signature]
# Public key types
[pubkey]
diff --git a/src/cli/x509.cpp b/src/cli/x509.cpp
index 10e7a1c7f..0feaad003 100644
--- a/src/cli/x509.cpp
+++ b/src/cli/x509.cpp
@@ -29,7 +29,7 @@ class Sign_Cert final : public Command
public:
Sign_Cert()
: Command("sign_cert --ca-key-pass= --hash=SHA-256 "
- "--duration=365 ca_cert ca_key pkcs10_req") {}
+ "--duration=365 --emsa= ca_cert ca_key pkcs10_req") {}
void go() override
{
@@ -51,7 +51,8 @@ class Sign_Cert final : public Command
throw CLI_Error("Failed to load key from " + get_arg("ca_key"));
}
- Botan::X509_CA ca(ca_cert, *key, get_arg("hash"), rng());
+ Botan::X509_CA ca(ca_cert, *key,
+ {{"padding",get_arg_or("emsa", "EMSA4")}}, get_arg("hash"), rng());
Botan::PKCS10_Request req(get_arg("pkcs10_req"));
@@ -186,7 +187,7 @@ class Gen_Self_Signed final : public Command
public:
Gen_Self_Signed()
: Command("gen_self_signed key CN --country= --dns= "
- "--organization= --email= --key-pass= --ca --hash=SHA-256") {}
+ "--organization= --email= --key-pass= --ca --hash=SHA-256 --emsa=") {}
void go() override
{
@@ -204,6 +205,7 @@ class Gen_Self_Signed final : public Command
opts.organization = get_arg("organization");
opts.email = get_arg("email");
opts.dns = get_arg("dns");
+ opts.set_padding_scheme(get_arg_or("emsa", "EMSA4"));
if(flag_set("ca"))
{
@@ -223,7 +225,7 @@ class Generate_PKCS10 final : public Command
public:
Generate_PKCS10()
: Command("gen_pkcs10 key CN --country= --organization= "
- "--email= --key-pass= --hash=SHA-256") {}
+ "--email= --key-pass= --hash=SHA-256 --emsa=") {}
void go() override
{
@@ -240,6 +242,7 @@ class Generate_PKCS10 final : public Command
opts.country = get_arg("country");
opts.organization = get_arg("organization");
opts.email = get_arg("email");
+ opts.set_padding_scheme(get_arg_or("emsa", "EMSA4"));
Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *key, get_arg("hash"), rng());
diff --git a/src/lib/pk_pad/emsa.h b/src/lib/pk_pad/emsa.h
index d45185f30..db835f1bd 100644
--- a/src/lib/pk_pad/emsa.h
+++ b/src/lib/pk_pad/emsa.h
@@ -9,6 +9,8 @@
#define BOTAN_PUBKEY_EMSA_H_
#include <botan/secmem.h>
+#include <botan/alg_id.h>
+#include <botan/pk_keys.h>
namespace Botan {
@@ -56,12 +58,29 @@ class BOTAN_PUBLIC_API(2,0) EMSA
const secure_vector<uint8_t>& raw,
size_t key_bits) = 0;
+ /**
+ * Prepare sig_algo for use in choose_sig_format for x509 certs,
+ * return padding info string
+ * @param sig_algo's oid and parameters will be set properly
+ * @param key used for checking compatibility with the encoding scheme
+ * @param cert_hash_name is checked to equal the hash for the encoding
+ * @return padding string to be consumed by PK_signer
+ */
+ virtual AlgorithmIdentifier config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const
+ { throw Not_Implemented("Encoding " + name() + " not supported for signing X509 objects"); }
+
virtual ~EMSA() = default;
/**
* @return a new object representing the same encoding method as *this
*/
virtual EMSA* clone() = 0;
+
+ /**
+ * @return the SCAN name of the encoding/padding scheme
+ */
+ virtual std::string name() const = 0;
};
/**
diff --git a/src/lib/pk_pad/emsa1/emsa1.cpp b/src/lib/pk_pad/emsa1/emsa1.cpp
index db9f2432f..76f668f83 100644
--- a/src/lib/pk_pad/emsa1/emsa1.cpp
+++ b/src/lib/pk_pad/emsa1/emsa1.cpp
@@ -7,6 +7,8 @@
#include <botan/emsa1.h>
#include <botan/exceptn.h>
+#include <botan/oids.h>
+#include <botan/internal/padding.h>
namespace Botan {
@@ -94,4 +96,38 @@ bool EMSA1::verify(const secure_vector<uint8_t>& input,
}
}
+AlgorithmIdentifier EMSA1::config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const
+ {
+ if(cert_hash_name != m_hash->name())
+ throw Invalid_Argument("Hash function from opts and hash_fn argument"
+ " need to be identical");
+ // check that the signature algorithm and the padding scheme fit
+ if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA1"))
+ {
+ throw Invalid_Argument("Encoding scheme with canonical name EMSA1"
+ " not supported for signature algorithm " + key.algo_name());
+ }
+
+ AlgorithmIdentifier sig_algo;
+ sig_algo.oid = OIDS::lookup( key.algo_name() + "/" + name() );
+
+ std::string algo_name = key.algo_name();
+ if(algo_name == "DSA" ||
+ algo_name == "ECDSA" ||
+ algo_name == "ECGDSA" ||
+ algo_name == "ECKCDSA" ||
+ algo_name == "GOST-34.10")
+ {
+ // for DSA, ECDSA, GOST parameters "SHALL" be empty
+ sig_algo.parameters = {};
+ }
+ else
+ {
+ sig_algo.parameters = key.algorithm_identifier().parameters;
+ }
+
+ return sig_algo;
+ }
+
}
diff --git a/src/lib/pk_pad/emsa1/emsa1.h b/src/lib/pk_pad/emsa1/emsa1.h
index 702db8f80..35071675f 100644
--- a/src/lib/pk_pad/emsa1/emsa1.h
+++ b/src/lib/pk_pad/emsa1/emsa1.h
@@ -27,6 +27,11 @@ class BOTAN_PUBLIC_API(2,0) EMSA1 final : public EMSA
EMSA* clone() override;
+ virtual std::string name() const override
+ { return "EMSA1(" + m_hash->name() + ")"; };
+
+ AlgorithmIdentifier config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const override;
private:
size_t hash_output_length() const { return m_hash->output_length(); }
diff --git a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
index 4175fe4b3..5e5024806 100644
--- a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
+++ b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
@@ -8,6 +8,8 @@
#include <botan/emsa_pkcs1.h>
#include <botan/hash_id.h>
#include <botan/exceptn.h>
+#include <botan/oids.h>
+#include <botan/internal/padding.h>
namespace Botan {
@@ -81,6 +83,28 @@ bool EMSA_PKCS1v15::verify(const secure_vector<uint8_t>& coded,
}
}
+AlgorithmIdentifier EMSA_PKCS1v15::config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const
+ {
+ if(cert_hash_name != m_hash->name())
+ throw Invalid_Argument("Hash function from opts and hash_fn argument"
+ " need to be identical");
+ // check that the signature algorithm and the padding scheme fit
+ if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA3"))
+ {
+ throw Invalid_Argument("Encoding scheme with canonical name EMSA3"
+ " not supported for signature algorithm " + key.algo_name());
+ }
+
+
+ AlgorithmIdentifier sig_algo;
+ sig_algo.oid = OIDS::lookup( key.algo_name() + "/" + name() );
+ // for RSA PKCSv1.5 parameters "SHALL" be NULL as configured by
+ // RSA_PublicKey::algorithm_identifier()
+ sig_algo.parameters = key.algorithm_identifier().parameters;
+ return sig_algo;
+ }
+
EMSA_PKCS1v15::EMSA_PKCS1v15(HashFunction* hash) : m_hash(hash)
{
m_hash_id = pkcs_hash_id(m_hash->name());
@@ -92,6 +116,7 @@ EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(const std::string& hash_algo)
{
m_hash_id = pkcs_hash_id(hash_algo);
std::unique_ptr<HashFunction> hash(HashFunction::create(hash_algo));
+ m_hash_name = hash->name();
m_hash_output_len = hash->output_length();
}
else
diff --git a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h
index b58ccb385..31032320e 100644
--- a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h
+++ b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h
@@ -37,6 +37,12 @@ class BOTAN_PUBLIC_API(2,0) EMSA_PKCS1v15 final : public EMSA
bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&,
size_t) override;
+
+ std::string name() const override
+ { return "EMSA3(" + m_hash->name() + ")"; }
+
+ AlgorithmIdentifier config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const override;
private:
std::unique_ptr<HashFunction> m_hash;
std::vector<uint8_t> m_hash_id;
@@ -67,8 +73,16 @@ class BOTAN_PUBLIC_API(2,0) EMSA_PKCS1v15_Raw final : public EMSA
* included in the signature.
*/
EMSA_PKCS1v15_Raw(const std::string& hash_algo = "");
+
+ std::string name() const override
+ {
+ if(m_hash_name.empty()) return "EMSA3(Raw)";
+ else return "EMSA3(Raw," + m_hash_name + ")";
+ }
+
private:
size_t m_hash_output_len = 0;
+ std::string m_hash_name;
std::vector<uint8_t> m_hash_id;
secure_vector<uint8_t> m_message;
};
diff --git a/src/lib/pk_pad/emsa_pssr/pssr.cpp b/src/lib/pk_pad/emsa_pssr/pssr.cpp
index 941da7d99..510d84a99 100644
--- a/src/lib/pk_pad/emsa_pssr/pssr.cpp
+++ b/src/lib/pk_pad/emsa_pssr/pssr.cpp
@@ -10,6 +10,9 @@
#include <botan/rng.h>
#include <botan/mgf1.h>
#include <botan/internal/bit_ops.h>
+#include <botan/oids.h>
+#include <botan/der_enc.h>
+#include <botan/internal/padding.h>
namespace Botan {
@@ -160,6 +163,50 @@ bool PSSR::verify(const secure_vector<uint8_t>& coded,
return pss_verify(*m_hash, coded, raw, key_bits);
}
+std::string PSSR::name() const
+ {
+ return "EMSA4(" + m_hash->name() + ",MGF1," + std::to_string(m_SALT_SIZE) + ")";
+ }
+
+AlgorithmIdentifier PSSR::config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const
+ {
+ if(cert_hash_name != m_hash->name())
+ throw Invalid_Argument("Hash function from opts and hash_fn argument"
+ " need to be identical");
+ // check that the signature algorithm and the padding scheme fit
+ if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA4"))
+ {
+ throw Invalid_Argument("Encoding scheme with canonical name EMSA4"
+ " not supported for signature algorithm " + key.algo_name());
+ }
+
+ AlgorithmIdentifier sig_algo;
+ // hardcoded as RSA is the only valid algorithm for EMSA4 at the moment
+ sig_algo.oid = OIDS::lookup( "RSA/EMSA4" );
+
+ sig_algo.parameters = DER_Encoder()
+ .start_cons( SEQUENCE )
+ .start_cons( ASN1_Tag(0), CONTEXT_SPECIFIC )
+ .encode( AlgorithmIdentifier( cert_hash_name, AlgorithmIdentifier::USE_NULL_PARAM ) )
+ .end_cons()
+ .start_cons( ASN1_Tag(1), CONTEXT_SPECIFIC )
+ .encode( AlgorithmIdentifier( "MGF1", DER_Encoder()
+ .encode( AlgorithmIdentifier( cert_hash_name, AlgorithmIdentifier::USE_NULL_PARAM ) )
+ .get_contents_unlocked() ) )
+ .end_cons()
+ .start_cons( ASN1_Tag(2), CONTEXT_SPECIFIC )
+ .encode( size_t( m_SALT_SIZE ) )
+ .end_cons()
+ .start_cons( ASN1_Tag(3), CONTEXT_SPECIFIC )
+ .encode( size_t( 1 ) ) // trailer field
+ .end_cons()
+ .end_cons()
+ .get_contents_unlocked();
+
+ return sig_algo;
+ }
+
PSSR_Raw::PSSR_Raw(HashFunction* h) :
m_hash(h), m_SALT_SIZE(m_hash->output_length())
{
@@ -210,4 +257,9 @@ bool PSSR_Raw::verify(const secure_vector<uint8_t>& coded,
return pss_verify(*m_hash, coded, raw, key_bits);
}
+std::string PSSR_Raw::name() const
+ {
+ return "PSSR_Raw(" + m_hash->name() + ",MGF1," + std::to_string(m_SALT_SIZE) + ")";
+ }
+
}
diff --git a/src/lib/pk_pad/emsa_pssr/pssr.h b/src/lib/pk_pad/emsa_pssr/pssr.h
index 85dfefc2c..61a4b9448 100644
--- a/src/lib/pk_pad/emsa_pssr/pssr.h
+++ b/src/lib/pk_pad/emsa_pssr/pssr.h
@@ -32,6 +32,11 @@ class BOTAN_PUBLIC_API(2,0) PSSR final : public EMSA
PSSR(HashFunction* hash, size_t salt_size);
EMSA* clone() override { return new PSSR(m_hash->clone(), m_SALT_SIZE); }
+
+ std::string name() const override;
+
+ AlgorithmIdentifier config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const override;
private:
void update(const uint8_t input[], size_t length) override;
@@ -69,6 +74,8 @@ class BOTAN_DLL PSSR_Raw final : public EMSA
PSSR_Raw(HashFunction* hash, size_t salt_size);
EMSA* clone() override { return new PSSR_Raw(m_hash->clone(), m_SALT_SIZE); }
+
+ std::string name() const override;
private:
void update(const uint8_t input[], size_t length) override;
diff --git a/src/lib/pk_pad/emsa_raw/emsa_raw.h b/src/lib/pk_pad/emsa_raw/emsa_raw.h
index f85595cdf..5f2b994f5 100644
--- a/src/lib/pk_pad/emsa_raw/emsa_raw.h
+++ b/src/lib/pk_pad/emsa_raw/emsa_raw.h
@@ -24,6 +24,10 @@ class BOTAN_PUBLIC_API(2,0) EMSA_Raw final : public EMSA
explicit EMSA_Raw(size_t expected_hash_size = 0) :
m_expected_size(expected_hash_size) {}
+ virtual std::string name() const override
+ { if(m_expected_size > 0)
+ return "Raw(" + std::to_string(m_expected_size) + ")";
+ else return "Raw"; }
private:
void update(const uint8_t[], size_t) override;
secure_vector<uint8_t> raw_data() override;
diff --git a/src/lib/pk_pad/emsa_x931/emsa_x931.h b/src/lib/pk_pad/emsa_x931/emsa_x931.h
index 0f186deb2..6ce9e339a 100644
--- a/src/lib/pk_pad/emsa_x931/emsa_x931.h
+++ b/src/lib/pk_pad/emsa_x931/emsa_x931.h
@@ -27,6 +27,9 @@ class BOTAN_PUBLIC_API(2,0) EMSA_X931 final : public EMSA
explicit EMSA_X931(HashFunction* hash);
EMSA* clone() override { return new EMSA_X931(m_hash->clone()); }
+
+ virtual std::string name() const override
+ { return "EMSA2(" + m_hash->name() + ")"; };
private:
void update(const uint8_t[], size_t) override;
secure_vector<uint8_t> raw_data() override;
diff --git a/src/lib/pk_pad/info.txt b/src/lib/pk_pad/info.txt
index 4c25e4371..513c4d0ad 100644
--- a/src/lib/pk_pad/info.txt
+++ b/src/lib/pk_pad/info.txt
@@ -8,6 +8,10 @@ load_on auto
rng
</requires>
+<header:internal>
+padding.h
+</header:internal>
+
<header:public>
eme.h
emsa.h
diff --git a/src/lib/pk_pad/iso9796/iso9796.cpp b/src/lib/pk_pad/iso9796/iso9796.cpp
index b8375af68..5b74319e0 100644
--- a/src/lib/pk_pad/iso9796/iso9796.cpp
+++ b/src/lib/pk_pad/iso9796/iso9796.cpp
@@ -250,6 +250,15 @@ bool ISO_9796_DS2::verify(const secure_vector<uint8_t>& const_coded,
}
/*
+ * Return the SCAN name
+ */
+std::string ISO_9796_DS2::name() const
+ {
+ return "ISO_9796_DS2(" + m_hash->name() + ","
+ + (m_implicit ? "imp" : "exp") + "," + std::to_string(m_SALT_SIZE) + ")";
+ }
+
+/*
* ISO-9796-2 signature scheme 3
* DS 3 is deterministic and equals DS2 without salt
*/
@@ -286,4 +295,12 @@ bool ISO_9796_DS3::verify(const secure_vector<uint8_t>& const_coded,
{
return iso9796_verification(const_coded, raw, key_bits, m_hash, 0);
}
+/*
+ * Return the SCAN name
+ */
+std::string ISO_9796_DS3::name() const
+ {
+ return "ISO_9796_DS3(" + m_hash->name() + "," +
+ (m_implicit ? "imp" : "exp") + ")";
+ }
}
diff --git a/src/lib/pk_pad/iso9796/iso9796.h b/src/lib/pk_pad/iso9796/iso9796.h
index 692ca1300..994295f0f 100644
--- a/src/lib/pk_pad/iso9796/iso9796.h
+++ b/src/lib/pk_pad/iso9796/iso9796.h
@@ -36,6 +36,8 @@ class BOTAN_PUBLIC_API(2,0) ISO_9796_DS2 final : public EMSA
EMSA* clone() override
{return new ISO_9796_DS2(m_hash->clone(), m_implicit, m_SALT_SIZE);}
+
+ virtual std::string name() const override;
private:
void update(const uint8_t input[], size_t length) override;
@@ -70,6 +72,8 @@ class BOTAN_PUBLIC_API(2,0) ISO_9796_DS3 final : public EMSA
EMSA* clone() override
{return new ISO_9796_DS3(m_hash->clone(), m_implicit);}
+
+ virtual std::string name() const override;
private:
void update(const uint8_t input[], size_t length) override;
diff --git a/src/lib/pk_pad/padding.cpp b/src/lib/pk_pad/padding.cpp
new file mode 100644
index 000000000..134bb4101
--- /dev/null
+++ b/src/lib/pk_pad/padding.cpp
@@ -0,0 +1,42 @@
+/*
+* Sets of allowed padding schemes for public key types
+*
+* This file was automatically generated by ./src/scripts/oids.py on 2017-12-20
+*
+* All manual edits to this file will be lost. Edit the script
+* then regenerate this source file.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/padding.h>
+#include <map>
+#include <vector>
+#include <string>
+#include <algorithm>
+
+namespace Botan {
+
+const std::map<const std::string, std::vector<std::string>> allowed_signature_paddings =
+ {
+ { "DSA", {"EMSA1"} },
+ { "ECDSA", {"EMSA1"} },
+ { "ECGDSA", {"EMSA1"} },
+ { "ECKCDSA", {"EMSA1"} },
+ { "GOST-34.10", {"EMSA1"} },
+ { "RSA", {"EMSA4", "EMSA3"} },
+ };
+
+const std::vector<std::string> get_sig_paddings(const std::string algo)
+ {
+ if(allowed_signature_paddings.count(algo) > 0)
+ return allowed_signature_paddings.at(algo);
+ return {};
+ }
+
+bool sig_algo_and_pad_ok(const std::string algo, const std::string padding)
+ {
+ std::vector<std::string> pads = get_sig_paddings(algo);
+ return std::find(pads.begin(), pads.end(), padding) != pads.end();
+ }
+}
diff --git a/src/lib/pk_pad/padding.h b/src/lib/pk_pad/padding.h
new file mode 100644
index 000000000..ed05ec381
--- /dev/null
+++ b/src/lib/pk_pad/padding.h
@@ -0,0 +1,36 @@
+/*
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PADDING_H_
+#define BOTAN_PADDING_H_
+
+#include <botan/build.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Returns the allowed padding schemes when using the given
+* algorithm (key type) for creating digital signatures.
+*
+* @param algo the algorithm for which to look up supported padding schemes
+* @return a vector of supported padding schemes
+*/
+BOTAN_TEST_API const std::vector<std::string> get_sig_paddings(const std::string algo);
+
+/**
+* Returns true iff the given padding scheme is valid for the given
+* signature algorithm (key type).
+*
+* @param algo the signature algorithm to be used
+* @param padding the padding scheme to be used
+*/
+bool sig_algo_and_pad_ok(const std::string algo, const std::string padding);
+
+}
+
+#endif
diff --git a/src/lib/x509/x509_ca.cpp b/src/lib/x509/x509_ca.cpp
index f8daaf79a..cba3b9816 100644
--- a/src/lib/x509/x509_ca.cpp
+++ b/src/lib/x509/x509_ca.cpp
@@ -6,6 +6,7 @@
*/
#include <botan/x509_ca.h>
+#include <botan/x509self.h>
#include <botan/pkcs10.h>
#include <botan/pubkey.h>
#include <botan/der_enc.h>
@@ -14,8 +15,11 @@
#include <botan/oids.h>
#include <botan/hash.h>
#include <botan/key_constraint.h>
+#include <botan/emsa.h>
+#include <botan/scan_name.h>
#include <algorithm>
#include <iterator>
+#include <map>
namespace Botan {
@@ -32,7 +36,32 @@ X509_CA::X509_CA(const X509_Certificate& c,
if(!m_ca_cert.is_CA_cert())
throw Invalid_Argument("X509_CA: This certificate is not for a CA");
- m_signer.reset(choose_sig_format(key, rng, m_hash_fn, m_ca_sig_algo));
+ std::map<std::string,std::string> opts;
+ // constructor without additional options: use the padding used in the CA certificate
+ // sig_oid_str = <sig_alg>/<padding>, so padding with all its options will look
+ // like a cipher mode to the scanner
+ std::string sig_oid_str = OIDS::lookup(c.signature_algorithm().oid);
+ SCAN_Name scanner(sig_oid_str);
+ std::string pad = scanner.cipher_mode();
+ if(!pad.empty())
+ opts.insert({"padding",pad});
+
+ m_signer.reset(choose_sig_format(key, opts, rng, hash_fn, m_ca_sig_algo));
+ }
+
+/*
+* Load the certificate and private key, and additional options
+*/
+X509_CA::X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng) : m_ca_cert(ca_certificate), m_hash_fn(hash_fn)
+ {
+ if(!m_ca_cert.is_CA_cert())
+ throw Invalid_Argument("X509_CA: This certificate is not for a CA");
+
+ m_signer.reset(choose_sig_format(key, opts, rng, hash_fn, m_ca_sig_algo));
}
/*
@@ -242,7 +271,20 @@ X509_Certificate X509_CA::ca_certificate() const
/*
* Choose a signing format for the key
*/
+
+PK_Signer* choose_sig_format(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& sig_algo)
+ {
+ return choose_sig_format(key, {}, rng, hash_fn, sig_algo);
+ }
+
+/*
+* Choose a signing format for the key
+*/
PK_Signer* choose_sig_format(const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
RandomNumberGenerator& rng,
const std::string& hash_fn,
AlgorithmIdentifier& sig_algo)
@@ -250,14 +292,14 @@ PK_Signer* choose_sig_format(const Private_Key& key,
const std::string algo_name = key.algo_name();
std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_fn));
+ std::string hash_name = hash->name();
+ // check algo_name and set default
std::string padding;
- std::vector<uint8_t> algo_params;
if(algo_name == "RSA")
{
- padding = "EMSA3";
- // for RSA PKCSv1.5 parameters "SHALL" be NULL
- algo_params = key.algorithm_identifier().get_parameters();
+ // set to EMSA3 for compatibility reasons, originally it was the only option
+ padding = "EMSA3(" + hash_name + ")";
}
else if(algo_name == "DSA" ||
algo_name == "ECDSA" ||
@@ -265,21 +307,45 @@ PK_Signer* choose_sig_format(const Private_Key& key,
algo_name == "ECKCDSA" ||
algo_name == "GOST-34.10")
{
- // for DSA, ECDSA, GOST parameters "SHALL" be empty
- padding = "EMSA1";
+ padding = "EMSA1(" + hash_name + ")";
}
else
{
throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name);
}
- const Signature_Format format = (key.message_parts() > 1) ? DER_SEQUENCE : IEEE_1363;
+ if(opts.count("padding") > 0 && !opts.at("padding").empty())
+ {
+ padding = opts.at("padding");
+ }
- padding = padding + "(" + hash->name() + ")";
+ // try to construct an EMSA object from the padding options or default
+ std::unique_ptr<EMSA> emsa = nullptr;
+ try
+ {
+ emsa.reset(get_emsa(padding));
+ }
+ /*
+ * get_emsa will throw if opts contains {"padding",<valid_padding>} but
+ * <valid_padding> does not specify a hash function.
+ * Omitting it is valid since it needs to be identical to hash_fn.
+ * If it still throws, something happened that we cannot repair here,
+ * e.g. the algorithm/padding combination is not supported.
+ */
+ catch(...)
+ {
+ emsa.reset(get_emsa(padding + "(" + hash_fn + ")"));
+ }
+ if(emsa == nullptr)
+ {
+ throw Invalid_Argument("Could not parse padding scheme " + padding);
+ }
+
+ const Signature_Format format = (key.message_parts() > 1) ? DER_SEQUENCE : IEEE_1363;
- sig_algo = AlgorithmIdentifier(OIDS::lookup(algo_name + "/" + padding), algo_params);
+ sig_algo = emsa->config_for_x509(key, hash_name);
- return new PK_Signer(key, rng, padding, format);
+ return new PK_Signer(key, rng, emsa->name(), format);
}
}
diff --git a/src/lib/x509/x509_ca.h b/src/lib/x509/x509_ca.h
index 49005f530..4f1da51fa 100644
--- a/src/lib/x509/x509_ca.h
+++ b/src/lib/x509/x509_ca.h
@@ -132,6 +132,20 @@ class BOTAN_PUBLIC_API(2,0) X509_CA final
const std::string& hash_fn,
RandomNumberGenerator& rng);
+ /**
+ * Create a new CA object.
+ * @param ca_certificate the certificate of the CA
+ * @param key the private key of the CA
+ * @param opts additional options, e.g. padding, as key value pairs
+ * @param hash_fn name of a hash function to use for signing
+ * @param rng the random generator to use
+ */
+ X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
#if defined(BOTAN_HAS_SYSTEM_RNG)
BOTAN_DEPRECATED("Use version taking RNG object")
X509_CA(const X509_Certificate& ca_certificate,
@@ -178,6 +192,28 @@ BOTAN_PUBLIC_API(2,0) PK_Signer* choose_sig_format(const Private_Key& key,
const std::string& hash_fn,
AlgorithmIdentifier& alg_id);
+/**
+* Choose the default signature format for a certain public key signature
+* scheme.
+*
+* The only option recognized by opts at this moment is "padding"
+* Find an entry from src/build-data/oids.txt under [signature] of the form
+* <sig_algo>/<padding>[(<hash_algo>)] and add {"padding",<padding>}
+* to opts.
+*
+* @param key will be the key to choose a padding scheme for
+* @param opts contains additional options for building the certificate
+* @param rng the random generator to use
+* @param hash_fn is the desired hash function
+* @param alg_id will be set to the chosen scheme
+* @return A PK_Signer object for generating signatures
+*/
+ PK_Signer* choose_sig_format(const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& alg_id);
+
}
#endif
diff --git a/src/lib/x509/x509opt.cpp b/src/lib/x509/x509opt.cpp
index 79c735a0f..e31ead91f 100644
--- a/src/lib/x509/x509opt.cpp
+++ b/src/lib/x509/x509opt.cpp
@@ -61,6 +61,11 @@ void X509_Cert_Options::CA_key(size_t limit)
path_limit = limit;
}
+void X509_Cert_Options::set_padding_scheme(const std::string& scheme)
+ {
+ padding_scheme = scheme;
+ }
+
/*
* Initialize the certificate options
*/
@@ -70,6 +75,8 @@ X509_Cert_Options::X509_Cert_Options(const std::string& initial_opts,
is_CA = false;
path_limit = 0;
constraints = NO_CONSTRAINTS;
+ // use default for chosen algorithm
+ padding_scheme = "";
auto now = std::chrono::system_clock::now();
diff --git a/src/lib/x509/x509self.cpp b/src/lib/x509/x509self.cpp
index ad0e9af94..108e0496b 100644
--- a/src/lib/x509/x509self.cpp
+++ b/src/lib/x509/x509self.cpp
@@ -33,7 +33,6 @@ void load_info(const X509_Cert_Options& opts, X509_DN& subject_dn,
subject_alt.add_othername(OIDS::lookup("PKIX.XMPPAddr"),
opts.xmpp, UTF8_STRING);
}
-
}
namespace X509 {
@@ -50,8 +49,11 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts,
X509_DN subject_dn;
AlternativeName subject_alt;
+ // for now, only the padding option is used
+ std::map<std::string,std::string> sig_opts = { {"padding",opts.padding_scheme} };
+
std::vector<uint8_t> pub_key = X509::BER_encode(key);
- std::unique_ptr<PK_Signer> signer(choose_sig_format(key, rng, hash_fn, sig_algo));
+ std::unique_ptr<PK_Signer> signer(choose_sig_format(key, sig_opts, rng, hash_fn, sig_algo));
load_info(opts, subject_dn, subject_alt);
Key_Constraints constraints;
@@ -102,8 +104,11 @@ PKCS10_Request create_cert_req(const X509_Cert_Options& opts,
X509_DN subject_dn;
AlternativeName subject_alt;
+ // for now, only the padding option is used
+ std::map<std::string,std::string> sig_opts = { {"padding",opts.padding_scheme} };
+
std::vector<uint8_t> pub_key = X509::BER_encode(key);
- std::unique_ptr<PK_Signer> signer(choose_sig_format(key, rng, hash_fn, sig_algo));
+ std::unique_ptr<PK_Signer> signer(choose_sig_format(key, sig_opts, rng, hash_fn, sig_algo));
load_info(opts, subject_dn, subject_alt);
const size_t PKCS10_VERSION = 0;
diff --git a/src/lib/x509/x509self.h b/src/lib/x509/x509self.h
index 069403814..0cc12e98e 100644
--- a/src/lib/x509/x509self.h
+++ b/src/lib/x509/x509self.h
@@ -108,6 +108,8 @@ class BOTAN_PUBLIC_API(2,0) X509_Cert_Options final
*/
size_t path_limit;
+ std::string padding_scheme;
+
/**
* The key constraints for the subject public key
*/
@@ -130,6 +132,11 @@ class BOTAN_PUBLIC_API(2,0) X509_Cert_Options final
void CA_key(size_t limit = 1);
/**
+ * Choose a padding scheme different from the default for the key used.
+ */
+ void set_padding_scheme(const std::string& scheme);
+
+ /**
* Set the notBefore of the certificate.
* @param time the notBefore value of the certificate
*/
diff --git a/src/scripts/oids.py b/src/scripts/oids.py
index 7a6caa368..a307d579b 100755
--- a/src/scripts/oids.py
+++ b/src/scripts/oids.py
@@ -10,6 +10,8 @@ Botan is released under the Simplified BSD License (see license.txt)
import sys
import datetime
import re
+from collections import defaultdict
+
def format_map(m, for_oid = False):
s = ''
@@ -190,25 +192,90 @@ size_t lookup_ub(const OID& oid)
""" % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"),
format_dn_ub_map(dn_ub,oid2str))
+
+def format_set_map(m):
+ s = ''
+ for k in sorted(m.keys()):
+ v = m[k]
+
+ if len(s) > 0:
+ s += ' '
+
+ s += '{ "%s", {' % k
+ for pad in v:
+ s += '"%s", ' % pad
+ if len(v) is not 0:
+ s = s[:-2]
+ s += '} },\n'
+ s = s[:-1]
+ return s
+
+
+def format_pads_as_map(sig_dict):
+ return """/*
+* Sets of allowed padding schemes for public key types
+*
+* This file was automatically generated by %s on %s
+*
+* All manual edits to this file will be lost. Edit the script
+* then regenerate this source file.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/padding.h>
+#include <map>
+#include <vector>
+#include <string>
+#include <algorithm>
+
+namespace Botan {
+
+const std::map<const std::string, std::vector<std::string>> allowed_signature_paddings =
+ {
+ %s
+ };
+
+__attribute__((visibility("default"))) const std::vector<std::string> get_sig_paddings(const std::string algo)
+ {
+ if(allowed_signature_paddings.count(algo) > 0)
+ return allowed_signature_paddings.at(algo);
+ return {};
+ }
+
+bool sig_algo_and_pad_ok(const std::string algo, std::string padding)
+ {
+ std::vector<std::string> pads = get_sig_paddings(algo);
+ return std::find(pads.begin(), pads.end(), padding) != pads.end();
+ }
+}
+""" % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"),
+ format_set_map(sig_dict))
+
+
def main(args = None):
""" Print header files (oids.cpp, dn_ub.cpp) depending on the first argument and on srs/build-data/oids.txt
Choose 'oids' to print oids.cpp, needs to be written to src/lib/asn1/oids.cpp
Choose 'dn_ub' to print dn_ub.cpp, needs to be written to src/lib/x509/X509_dn_ub.cpp
+ Choose 'pads' to print padding.cpp, needs to be written to src/lib/pk_pad/padding.cpp
"""
if args is None:
args = sys.argv
if len(args) < 2:
- raise Exception("Use either 'oids' or 'dn_ub' as first argument")
+ raise Exception("Use either 'oids', 'dn_ub', 'pads' as first argument")
- oid_lines = open('src/build-data/oids.txt').readlines()
+ oid_lines = open('./src/build-data/oids.txt').readlines()
oid_re = re.compile("^([1-9][0-9.]+) = ([A-Za-z0-9_\./\(\), -]+)(?: = )?([0-9]+)?$")
hdr_re = re.compile("^\[([a-z0-9_]+)\]$")
+ pad_re = re.compile("^([A-Za-z0-9_\., -]+)/([A-Za-z0-9_-]+)[A-Za-z0-9_\.\(\), -]*$")
oid2str = {}
str2oid = {}
dn_ub = {}
+ sig2pads = defaultdict(set)
+ enc2pads = defaultdict(set)
cur_hdr = None
for line in oid_lines:
@@ -242,6 +309,11 @@ def main(args = None):
if match.lastindex < 3:
raise Exception("Could not find an upper bound for DN " + match.group(1))
dn_ub[oid] = match.group(3)
+ # parse signature paddings
+ elif cur_hdr == "signature":
+ pad_match = pad_re.search(nam)
+ if pad_match is not None:
+ sig2pads[pad_match.group(1)].add(pad_match.group(2))
if nam in str2oid:
#print "Duplicated name", nam, oid, str2oid[nam]
@@ -254,6 +326,8 @@ def main(args = None):
print format_as_ifs(oid2str, str2oid)
elif args[1] == "dn_ub":
print format_dn_ub_as_map(dn_ub,oid2str)
+ elif args[1] == "pads":
+ print format_pads_as_map(sig2pads)
if __name__ == '__main__':
diff --git a/src/tests/data/x509/misc/rsa_key.pem b/src/tests/data/x509/misc/rsa_key.pem
new file mode 100644
index 000000000..cf84f4274
--- /dev/null
+++ b/src/tests/data/x509/misc/rsa_key.pem
@@ -0,0 +1,40 @@
+-----BEGIN PRIVATE KEY-----
+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
+ezTQp0HUwoiHK+bS2l+FQg==
+-----END PRIVATE KEY-----
diff --git a/src/tests/test_pk_pad.cpp b/src/tests/test_pk_pad.cpp
index bd33dde6c..ecfdba5d5 100644
--- a/src/tests/test_pk_pad.cpp
+++ b/src/tests/test_pk_pad.cpp
@@ -69,6 +69,101 @@ class EME_Decoding_Tests final : public Text_Based_Test
BOTAN_REGISTER_TEST("pk_pad_eme", EME_Decoding_Tests);
+class EMSA_unit_tests final : public Test
+ {
+ public:
+ std::vector<Test::Result> run() override
+ {
+ Test::Result name_tests("EMSA_name_tests");
+
+ std::vector<std::string> pads_need_hash =
+ {
+#if BOTAN_HAS_EMSA1
+ "EMSA1",
+#endif
+#if BOTAN_HAS_EMSA_X931
+ "EMSA2",
+#endif
+#if BOTAN_HAS_EMSA_PKCS1
+ "EMSA3",
+#endif
+#if BOTAN_HAS_EMSA_PSSR
+ "EMSA4",
+ "PSSR_Raw",
+#endif
+#if BOTAN_HAS_ISO_9796
+ "ISO_9796_DS2",
+ "ISO_9796_DS3",
+#endif
+ };
+
+ std::vector<std::string> pads_no_hash =
+ {
+#if BOTAN_HAS_EMSA_RAW
+ "Raw",
+#endif
+#if BOTAN_HAS_EMSA_PKCS1
+ "EMSA3(Raw)",
+ "EMSA3(Raw,SHA-512)",
+#endif
+ };
+
+ for(auto pad : pads_need_hash)
+ {
+ try
+ {
+ std::unique_ptr<Botan::EMSA> emsa_1(
+ Botan::get_emsa(pad + "(" + Botan::hash_for_emsa(pad) + ")"));
+ std::unique_ptr<Botan::EMSA> emsa_2(Botan::get_emsa(emsa_1->name()));
+ name_tests.test_eq("EMSA_name_test for " + pad,
+ emsa_1->name(), emsa_2->name());
+ }
+ catch(const std::exception& e)
+ {
+ name_tests.test_failure("EMSA_name_test for " + pad + ": " + e.what());
+ }
+ }
+
+ for(auto pad : pads_need_hash)
+ {
+ std::string algo_name = pad + "(YYZ)";
+ try
+ {
+ std::unique_ptr<Botan::EMSA> emsa(
+ Botan::get_emsa(algo_name));
+ name_tests.test_failure("EMSA_name_test for " + pad + ": " +
+ "Could create EMSA with fantasy hash YYZ");
+ }
+ catch(const std::exception& e)
+ {
+ name_tests.test_eq("EMSA_name_test for " + pad,
+ e.what(),
+ "Could not find any algorithm named \"" + algo_name + "\"");
+ }
+ }
+
+ for(auto pad : pads_no_hash)
+ {
+ try
+ {
+ std::unique_ptr<Botan::EMSA> emsa_1(Botan::get_emsa(pad));
+ std::unique_ptr<Botan::EMSA> emsa_2(Botan::get_emsa(emsa_1->name()));
+ name_tests.test_eq("EMSA_name_test for " + pad,
+ emsa_1->name(), emsa_2->name());
+ }
+ catch(const std::exception& e)
+ {
+ name_tests.test_failure("EMSA_name_test for " + pad + ": " + e.what());
+ }
+ }
+
+
+ return { name_tests };
+ }
+ };
+
+BOTAN_REGISTER_TEST("pk_pad_emsa_unit", EMSA_unit_tests);
+
#endif
}
diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp
index a8849292a..11f2932a8 100644
--- a/src/tests/unit_x509.cpp
+++ b/src/tests/unit_x509.cpp
@@ -19,6 +19,7 @@
#include <botan/ber_dec.h>
#include <botan/der_enc.h>
#include <botan/oids.h>
+ #include <botan/internal/padding.h>
#endif
@@ -35,26 +36,28 @@ Botan::X509_Time from_date(const int y, const int m, const int d)
}
/* Return some option sets */
-Botan::X509_Cert_Options ca_opts()
+Botan::X509_Cert_Options ca_opts(const std::string& sig_padding = "")
{
Botan::X509_Cert_Options opts("Test CA/US/Botan Project/Testing");
opts.uri = "https://botan.randombit.net";
opts.dns = "botan.randombit.net";
opts.email = "[email protected]";
+ opts.set_padding_scheme(sig_padding);
opts.CA_key(1);
return opts;
}
-Botan::X509_Cert_Options req_opts1(const std::string& algo)
+Botan::X509_Cert_Options req_opts1(const std::string& algo, const std::string& sig_padding = "")
{
Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
opts.uri = "https://botan.randombit.net";
opts.dns = "botan.randombit.net";
opts.email = "[email protected]";
+ opts.set_padding_scheme(sig_padding);
opts.not_before("160101200000Z");
opts.not_after("300101200000Z");
@@ -73,13 +76,14 @@ Botan::X509_Cert_Options req_opts1(const std::string& algo)
return opts;
}
-Botan::X509_Cert_Options req_opts2()
+Botan::X509_Cert_Options req_opts2(const std::string& sig_padding = "")
{
Botan::X509_Cert_Options opts("Test User 2/US/Botan Project/Testing");
opts.uri = "https://botan.randombit.net";
opts.dns = "botan.randombit.net";
opts.email = "[email protected]";
+ opts.set_padding_scheme(sig_padding);
opts.add_ex_constraint("PKIX.EmailProtection");
@@ -461,7 +465,7 @@ Test::Result test_x509_bmpstring()
return result;
}
-Test::Result test_x509_cert(const std::string& sig_algo, const std::string& hash_fn = "SHA-256")
+Test::Result test_x509_cert(const std::string& sig_algo, const std::string& sig_padding = "", const std::string& hash_fn = "SHA-256")
{
Test::Result result("X509 Unit");
@@ -476,7 +480,7 @@ Test::Result test_x509_cert(const std::string& sig_algo, const std::string& hash
}
/* Create the self-signed cert */
- const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, Test::rng());
+ const auto ca_cert = Botan::X509::create_self_signed_cert(ca_opts(sig_padding), *ca_key, hash_fn, Test::rng());
{
const auto constraints = Botan::Key_Constraints(Botan::KEY_CERT_SIGN | Botan::CRL_SIGN);
@@ -487,7 +491,7 @@ Test::Result test_x509_cert(const std::string& sig_algo, const std::string& hash
std::unique_ptr<Botan::Private_Key> user1_key(make_a_private_key(sig_algo));
Botan::PKCS10_Request user1_req =
- Botan::X509::create_cert_req(req_opts1(sig_algo),
+ Botan::X509::create_cert_req(req_opts1(sig_algo, sig_padding),
*user1_key,
hash_fn,
Test::rng());
@@ -499,13 +503,13 @@ Test::Result test_x509_cert(const std::string& sig_algo, const std::string& hash
std::unique_ptr<Botan::Private_Key> user2_key(make_a_private_key(sig_algo));
Botan::PKCS10_Request user2_req =
- Botan::X509::create_cert_req(req_opts2(),
+ Botan::X509::create_cert_req(req_opts2(sig_padding),
*user2_key,
hash_fn,
Test::rng());
/* Create the CA object */
- Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, Test::rng());
+ Botan::X509_CA ca(ca_cert, *ca_key, {{"padding",sig_padding}}, hash_fn, Test::rng());
/* Sign the requests to create the certs */
Botan::X509_Certificate user1_cert =
@@ -519,7 +523,8 @@ Test::Result test_x509_cert(const std::string& sig_algo, const std::string& hash
from_date(2033, 01, 01));
// user#1 creates a self-signed cert on the side
- const auto user1_ss_cert = Botan::X509::create_self_signed_cert(req_opts1(sig_algo), *user1_key, hash_fn, Test::rng());
+ const auto user1_ss_cert =
+ Botan::X509::create_self_signed_cert(req_opts1(sig_algo, sig_padding), *user1_key, hash_fn, Test::rng());
{
auto constrains = req_opts1(sig_algo).constraints;
@@ -718,7 +723,7 @@ Test::Result test_usage(const std::string& sig_algo, const std::string& hash_fn
return result;
}
-Test::Result test_self_issued(const std::string& sig_algo, const std::string& hash_fn = "SHA-256")
+Test::Result test_self_issued(const std::string& sig_algo, const std::string& sig_padding = "", const std::string& hash_fn = "SHA-256")
{
using Botan::Key_Constraints;
@@ -736,10 +741,10 @@ Test::Result test_self_issued(const std::string& sig_algo, const std::string& ha
// create the self-signed cert
const Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(
- ca_opts(), *ca_key, hash_fn, Test::rng());
+ ca_opts(sig_padding), *ca_key, hash_fn, Test::rng());
/* Create the CA object */
- const Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, Test::rng());
+ const Botan::X509_CA ca(ca_cert, *ca_key, {{"padding",sig_padding}}, hash_fn, Test::rng());
std::unique_ptr<Botan::Private_Key> user_key(make_a_private_key(sig_algo));
@@ -747,6 +752,7 @@ Test::Result test_self_issued(const std::string& sig_algo, const std::string& ha
// but signed by a CA, not signed by it's own private key
Botan::X509_Cert_Options opts = ca_opts();
opts.constraints = Key_Constraints::DIGITAL_SIGNATURE;
+ opts.set_padding_scheme(sig_padding);
const Botan::PKCS10_Request self_issued_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, Test::rng());
@@ -1063,7 +1069,7 @@ class String_Extension final : public Botan::Certificate_Extension
std::string m_contents;
};
-Test::Result test_x509_extensions(const std::string& sig_algo, const std::string& hash_fn = "SHA-256")
+Test::Result test_x509_extensions(const std::string& sig_algo, const std::string& sig_padding = "", const std::string& hash_fn = "SHA-256")
{
using Botan::Key_Constraints;
@@ -1080,10 +1086,11 @@ Test::Result test_x509_extensions(const std::string& sig_algo, const std::string
}
/* Create the self-signed cert */
- Botan::X509_Certificate ca_cert = Botan::X509::create_self_signed_cert(ca_opts(), *ca_key, hash_fn, Test::rng());
+ Botan::X509_Certificate ca_cert =
+ Botan::X509::create_self_signed_cert(ca_opts(sig_padding), *ca_key, hash_fn, Test::rng());
/* Create the CA object */
- Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, Test::rng());
+ Botan::X509_CA ca(ca_cert, *ca_key, {{"padding",sig_padding}}, hash_fn, Test::rng());
std::unique_ptr<Botan::Private_Key> user_key(make_a_private_key(sig_algo));
@@ -1096,6 +1103,7 @@ Test::Result test_x509_extensions(const std::string& sig_algo, const std::string
const Botan::OID ku_oid = Botan::OIDS::lookup("X509v3.KeyUsage");
req_extensions.add(new String_Extension("AAAAAAAAAAAAAABCDEF"), false);
opts.extensions = req_extensions;
+ opts.set_padding_scheme(sig_padding);
/* Create a self-signed certificate */
const Botan::X509_Certificate self_signed_cert = Botan::X509::create_self_signed_cert(
@@ -1220,6 +1228,99 @@ Test::Result test_hashes(const std::string& algo, const std::string& hash_fn = "
return result;
}
+/*
+ * @brief checks the configurability of the EMSA4(RSA-PSS) signature scheme
+ *
+ * For the other algorithms than RSA, only one padding is supported right now.
+ */
+Test::Result test_padding_config() {
+ // Throughout the test, some synonyms for EMSA4 are used, e.g. PSSR, EMSA-PSS
+ Test::Result test_result("X509 Padding Config");
+
+ std::unique_ptr<Botan::Private_Key> sk(Botan::PKCS8::load_key(
+ Test::data_file("x509/misc/rsa_key.pem"), Test::rng()));
+
+ // Create X509 CA certificate; EMSA3 is used for signing by default
+ Botan::X509_Cert_Options opt("TESTCA");
+ opt.CA_key();
+ Botan::X509_Certificate ca_cert_def = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
+ test_result.test_eq("CA certificate signature algorithm (default)",
+ Botan::OIDS::lookup(ca_cert_def.signature_algorithm().oid),"RSA/EMSA3(SHA-512)");
+
+ // Create X509 CA certificate; RSA-PSS is explicitly set
+ opt.set_padding_scheme("PSSR");
+ Botan::X509_Certificate ca_cert_exp = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
+ test_result.test_eq("CA certificate signature algorithm (explicit)",
+ Botan::OIDS::lookup(ca_cert_exp.signature_algorithm().oid),"RSA/EMSA4");
+
+ // Try to set a padding scheme that is not supported for signing with the given key type
+ opt.set_padding_scheme("EMSA1");
+ try
+ {
+ Botan::X509_Certificate ca_cert_wrong = Botan::X509::create_self_signed_cert(opt, (*sk), "SHA-512", Test::rng());
+ test_result.test_failure("Could build CA certitiface with invalid encoding scheme EMSA1 for key type " + sk->algo_name());
+ }
+ catch (const Botan::Invalid_Argument& e)
+ {
+ test_result.test_eq("Build CA certitiface with invalid encoding scheme EMSA1 for key type " +
+ sk->algo_name(), e.what(),
+ "Invalid argument Encoding scheme with canonical name EMSA1 not supported for signature algorithm RSA");
+ }
+ test_result.test_eq("CA certificate signature algorithm (explicit)",
+ Botan::OIDS::lookup(ca_cert_exp.signature_algorithm().oid),"RSA/EMSA4");
+
+ // Prepare a signing request for the end certificate
+ Botan::X509_Cert_Options req_opt("endpoint");
+ req_opt.set_padding_scheme("EMSA4(SHA-512,MGF1,64)");
+ auto not_before = Botan::calendar_point(2017, 1, 1, 1, 1,
+ 1).to_std_timepoint();
+ auto not_after = Botan::calendar_point(2018, 1, 1, 1, 1,
+ 1).to_std_timepoint();
+ Botan::PKCS10_Request end_req = Botan::X509::create_cert_req(req_opt, (*sk), "SHA-512", Test::rng());
+ test_result.test_eq("Certificate request signature algorithm", Botan::OIDS::lookup(end_req.signature_algorithm().oid),"RSA/EMSA4");
+
+ // Create X509 CA object: will fail as the chosen hash functions differ
+ try
+ {
+ Botan::X509_CA ca_fail(ca_cert_exp, (*sk), {{"padding","EMSA4(SHA-256)"}},"SHA-512", Test::rng());
+ test_result.test_failure("Configured conflicting hash functions for CA");
+ }
+ catch(const Botan::Invalid_Argument& e)
+ {
+ test_result.test_eq("Configured conflicting hash functions for CA",
+ e.what(),
+ "Invalid argument Hash function from opts and hash_fn argument need to be identical");
+ }
+
+ // Create X509 CA object: its signer will use the padding scheme from the CA certificate, i.e. EMSA3
+ Botan::X509_CA ca_def(ca_cert_def, (*sk), "SHA-512", Test::rng());
+ Botan::X509_Certificate end_cert_emsa3 = ca_def.sign_request(end_req, Test::rng(), Botan::X509_Time(not_before), Botan::X509_Time(not_after));
+ test_result.test_eq("End certificate signature algorithm", Botan::OIDS::lookup(end_cert_emsa3.signature_algorithm().oid), "RSA/EMSA3(SHA-512)");
+
+ // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is different from the CA certificate's scheme
+ Botan::X509_CA ca_diff(ca_cert_def, (*sk), {{"padding","EMSA-PSS"}}, "SHA-512", Test::rng());
+ Botan::X509_Certificate end_cert_diff_emsa4 = ca_diff.sign_request(end_req, Test::rng(), Botan::X509_Time(not_before), Botan::X509_Time(not_after));
+ test_result.test_eq("End certificate signature algorithm", Botan::OIDS::lookup(end_cert_diff_emsa4.signature_algorithm().oid), "RSA/EMSA4");
+
+ // Create X509 CA object: its signer will use the explicitly configured padding scheme, which is identical to the CA certificate's scheme
+ Botan::X509_CA ca_exp(ca_cert_exp, (*sk), {{"padding","EMSA4(SHA-512,MGF1,64)"}},"SHA-512", Test::rng());
+ Botan::X509_Certificate end_cert_emsa4= ca_exp.sign_request(end_req, Test::rng(), Botan::X509_Time(not_before), Botan::X509_Time(not_after));
+ test_result.test_eq("End certificate signature algorithm", Botan::OIDS::lookup(end_cert_emsa4.signature_algorithm().oid), "RSA/EMSA4");
+
+ // Check CRL signature algorithm
+ Botan::X509_CRL crl = ca_exp.new_crl(Test::rng());
+ test_result.test_eq("CRL signature algorithm", Botan::OIDS::lookup(crl.signature_algorithm().oid), "RSA/EMSA4");
+
+ // sanity check for verification, the heavy lifting is done in the other unit tests
+ const Botan::Certificate_Store_In_Memory trusted(ca_exp.ca_certificate());
+ const Botan::Path_Validation_Restrictions restrictions(false, 80);
+ const Botan::Path_Validation_Result validation_result = Botan::x509_path_validate(
+ end_cert_emsa4, restrictions, trusted);
+ test_result.confirm("EMSA4-signed certificate validates", validation_result.successful_validation());
+
+ return test_result;
+}
+
class X509_Cert_Unit_Tests final : public Test
{
public:
@@ -1239,16 +1340,17 @@ class X509_Cert_Unit_Tests final : public Test
if(algo == "RSA")
continue;
#endif
-
- try
- {
- cert_result.merge(test_x509_cert(algo));
- }
- catch(std::exception& e)
+ for(auto padding_scheme : Botan::get_sig_paddings(algo))
{
- cert_result.test_failure("test_x509_cert " + algo, e.what());
+ try
+ {
+ cert_result.merge(test_x509_cert(algo, padding_scheme));
+ }
+ catch(std::exception& e)
+ {
+ cert_result.test_failure("test_x509_cert " + algo, e.what());
+ }
}
-
try
{
usage_result.merge(test_usage(algo));
@@ -1257,23 +1359,27 @@ class X509_Cert_Unit_Tests final : public Test
{
usage_result.test_failure("test_usage " + algo, e.what());
}
-
- try
- {
- self_issued_result.merge(test_self_issued(algo));
- }
- catch(std::exception& e)
- {
- self_issued_result.test_failure("test_self_issued " + algo, e.what());
- }
-
- try
+ for(auto padding_scheme : Botan::get_sig_paddings(algo))
{
- extensions_result.merge(test_x509_extensions(algo));
+ try
+ {
+ self_issued_result.merge(test_self_issued(algo, padding_scheme));
+ }
+ catch(std::exception& e)
+ {
+ self_issued_result.test_failure("test_self_issued " + algo, e.what());
+ }
}
- catch(std::exception& e)
+ for(auto padding_scheme : Botan::get_sig_paddings(algo))
{
- extensions_result.test_failure("test_extensions " + algo, e.what());
+ try
+ {
+ extensions_result.merge(test_x509_extensions(algo, padding_scheme));
+ }
+ catch(std::exception& e)
+ {
+ extensions_result.test_failure("test_extensions " + algo, e.what());
+ }
}
}
@@ -1282,6 +1388,17 @@ class X509_Cert_Unit_Tests final : public Test
results.push_back(self_issued_result);
results.push_back(extensions_result);
+ Test::Result pad_config_result("X509 Padding Config");
+ try
+ {
+ pad_config_result.merge(test_padding_config());
+ }
+ catch(const std::exception& e)
+ {
+ pad_config_result.test_failure("test_padding_config", e.what());
+ }
+ results.push_back(pad_config_result);
+
const std::vector<std::string> pk_algos
{
"DH", "ECDH", "RSA", "ElGamal", "GOST-34.10",