Skip to content

Commit

Permalink
Fix RSA padding selection (#536)
Browse files Browse the repository at this point in the history
IB-7756

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma authored Jun 26, 2023
1 parent 767326d commit c8cfe9b
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 190 deletions.
14 changes: 10 additions & 4 deletions libdigidocpp.dox
Original file line number Diff line number Diff line change
Expand Up @@ -1538,10 +1538,16 @@ Postal code of the place where the signature is created.</td></tr>
Country of origin. ISO 3166-type 2-character country codes are used (e.g. EE)</td></tr>
<tr><td>\-\-role= </td><td>Optional</td><td>
Signer’s role(s). The option can occur multiple times.</td></tr>
<tr><td>\-\-sha(1,224,256,384,512) </td><td>Optional</td><td>
Used for testing purposes. Specifies the hash function that is used when calculating digest values, supported only in case of BDOC documents. If not specified then SHA-256 is used by default.</td></tr>
<tr><td>\-\-sigsha(1,224,256,384,512) </td><td>Optional</td><td>
Used for testing purposes. Specifies the hash function that is used for calculating the hash that is being signed, supported only in case of BDOC format. If not specified then SHA-256 is used by default.</td></tr>
<tr><td>\-\-sha(224,256,384,512) </td><td>Optional</td><td>
Used for testing purposes. Specifies the hash function that is used when calculating digest values. If not specified then SHA-256 is used by default.</td></tr>
<tr><td>\-\-sigsha(224,256,384,512) </td><td>Optional</td><td>
Used for testing purposes. Specifies the hash function that is used for calculating the hash that is being signed. If not specified then SHA-256 is used by default.</td></tr>
<tr><td>\-\-sigpsssha(224,256,384,512) </td><td>Optional</td><td>
Used for testing purposes. With RSA keys RSA-PSS padding is used. Specifies the hash function that is used for calculating the hash that is being signed. If not specified then SHA-256 is used by default. Same as \-\-sigsha* with \-\-rsapss</td></tr>
<tr><td>\-\-rsapkcs15 </td><td>Optional</td><td>
Option to change RSA Signature padding (RSA PKCS1.5).</td></tr>
<tr><td>\-\-rsapss </td><td>Optional</td><td>
Option to change RSA Signature padding (RSA PSS).</td></tr>
<tr><td>\-\-tsurl </td><td>Optional</td><td>
Option to change TS URL.</td></tr>
<tr><td>\-\-dontValidate </td><td>Optional</td><td>
Expand Down
159 changes: 50 additions & 109 deletions src/crypto/Digest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,42 +26,31 @@
#include <openssl/x509.h>

using namespace std;

namespace digidoc
{
class Digest::Private: public vector<unsigned char>
{
public:
EVP_MD_CTX *ctx {};
int method = -1;
};
}

using namespace digidoc;

/**
* Initializes OpenSSL digest calculator.
*
* @param uri digest method URI (e.g. 'http://www.w3.org/2000/09/xmldsig#sha1' for SHA1).
* @throws IOException throws exception if the digest calculator initialization failed.
* @param uri digest method URI (e.g. 'http://www.w3.org/2001/04/xmlenc#sha256' for SHA256).
* @throws Exception throws exception if the digest calculator initialization failed.
*/
Digest::Digest(const string &uri)
: d(make_unique<Private>())
: d(SCOPE_PTR(EVP_MD_CTX, EVP_MD_CTX_new()))
{
reset(uri);
if(uri.empty() && Conf::instance()->digestUri() == URI_SHA1)
THROW("Unsupported digest method %s", uri.c_str());
int method = toMethod(uri.empty() ? Conf::instance()->digestUri() : uri);
if(EVP_DigestInit(d.get(), EVP_get_digestbynid(method)) != 1)
THROW_OPENSSLEXCEPTION("Failed to initialize %s digest calculator", uri.c_str());
}

/**
* Destroys OpenSSL digest calculator.
*/
Digest::~Digest()
{
EVP_MD_CTX_free(d->ctx);
}
Digest::~Digest() = default;

vector<unsigned char> Digest::addDigestInfo(const vector<unsigned char> &digest, const string &uri)
vector<unsigned char> Digest::addDigestInfo(vector<unsigned char> digest, string_view uri)
{
vector<unsigned char> result = digest;
vector<unsigned char> oid;
switch(toMethod(uri))
{
Expand All @@ -73,8 +62,8 @@ vector<unsigned char> Digest::addDigestInfo(const vector<unsigned char> &digest,
default: break;
}
if(!oid.empty())
result.insert(result.begin(), oid.begin(), oid.end());
return result;
digest.insert(digest.begin(), oid.begin(), oid.end());
return digest;
}

vector<unsigned char> Digest::digestInfoDigest(const std::vector<unsigned char> &digest)
Expand All @@ -83,7 +72,7 @@ vector<unsigned char> Digest::digestInfoDigest(const std::vector<unsigned char>
SCOPE(X509_SIG, sig, d2i_X509_SIG(nullptr, &p, long(digest.size())));
if(!sig)
return {};
const ASN1_OCTET_STRING *value = nullptr;
const ASN1_OCTET_STRING *value {};
X509_SIG_get0(sig.get(), nullptr, &value);
return { value->data, value->data + value->length };
}
Expand All @@ -94,7 +83,7 @@ string Digest::digestInfoUri(const std::vector<unsigned char> &digest)
SCOPE(X509_SIG, sig, d2i_X509_SIG(nullptr, &p, long(digest.size())));
if(!sig)
return {};
const X509_ALGOR *algor = nullptr;
const X509_ALGOR *algor {};
X509_SIG_get0(sig.get(), &algor, nullptr);
return toUri(OBJ_obj2nid(algor->algorithm));
}
Expand All @@ -105,45 +94,24 @@ string Digest::digestInfoUri(const std::vector<unsigned char> &digest)
*/
string Digest::uri() const
{
return toUri(d->method);
return toUri(EVP_MD_CTX_type(d.get()));
}

/**
*
*/
void Digest::reset(const string &uri)
bool Digest::isRsaPssUri(string_view uri)
{
if(uri.empty() && Conf::instance()->digestUri() == URI_SHA1)
THROW("Unsupported digest method");

if(d->ctx)
EVP_MD_CTX_free(d->ctx);
d->ctx = EVP_MD_CTX_new();
int result = -1;
switch(d->method = toMethod(uri.empty() ? Conf::instance()->digestUri() : uri))
{
case NID_sha1: result = EVP_DigestInit(d->ctx, EVP_sha1()); break;
case NID_sha224: result = EVP_DigestInit(d->ctx, EVP_sha224()); break;
case NID_sha256: result = EVP_DigestInit(d->ctx, EVP_sha256()); break;
case NID_sha384: result = EVP_DigestInit(d->ctx, EVP_sha384()); break;
case NID_sha512: result = EVP_DigestInit(d->ctx, EVP_sha512()); break;
return
uri == URI_RSA_PSS_SHA224 ||
uri == URI_RSA_PSS_SHA256 ||
uri == URI_RSA_PSS_SHA384 ||
uri == URI_RSA_PSS_SHA512 ||
#ifndef LIBRESSL_VERSION_NUMBER
case NID_sha3_224: result = EVP_DigestInit(d->ctx, EVP_sha3_224()); break;
case NID_sha3_256: result = EVP_DigestInit(d->ctx, EVP_sha3_256()); break;
case NID_sha3_384: result = EVP_DigestInit(d->ctx, EVP_sha3_384()); break;
case NID_sha3_512: result = EVP_DigestInit(d->ctx, EVP_sha3_512()); break;
uri == URI_RSA_PSS_SHA3_224 ||
uri == URI_RSA_PSS_SHA3_256 ||
uri == URI_RSA_PSS_SHA3_384 ||
uri == URI_RSA_PSS_SHA3_512;
#else
false;
#endif
default: break;
}
d->clear();
if(result != 1)
THROW_OPENSSLEXCEPTION("Failed to initialize %s digest calculator", uri.c_str());
}

bool Digest::isRsaPssUri(const std::string &uri)
{
return uri == URI_RSA_PSS_SHA224 || uri == URI_RSA_PSS_SHA256 || uri == URI_RSA_PSS_SHA384 || uri == URI_RSA_PSS_SHA512 ||
uri == URI_RSA_PSS_SHA3_224 || uri == URI_RSA_PSS_SHA3_256 || uri == URI_RSA_PSS_SHA3_384 || uri == URI_RSA_PSS_SHA3_512;
}

/**
Expand All @@ -152,14 +120,15 @@ bool Digest::isRsaPssUri(const std::string &uri)
* For available method URIs see:
* <li>
* <ul><b>W3C XML Encryption Syntax and Processing</b> (10 December 2005) http://www.w3.org/TR/xmlenc-core/</ul>
* <ul><b>RFC 4051</b> http://www.ietf.org/rfc/rfc4051.txt</ul>
* <ul><b>RFC 4051</b> https://www.ietf.org/rfc/rfc4051.txt</ul>
* <ul><b>RFC 6931</b> https://www.ietf.org/rfc/rfc6931.txt</ul>
* </li>
*
* @param uri digest method URI (e.g. 'http://www.w3.org/2000/09/xmldsig#sha1' for SHA1).
* @return returns digest OpenSSL method id.
* @throws IOException throws exception if digest method is not supported.
* @throws Exception throws exception if digest method is not supported.
*/
int Digest::toMethod(const string &uri)
int Digest::toMethod(string_view uri)
{
if(uri == URI_SHA1 || uri == URI_RSA_SHA1 || uri == URI_ECDSA_SHA1) return NID_sha1;
if(uri == URI_SHA224 || uri == URI_RSA_SHA224 || uri == URI_RSA_PSS_SHA224 || uri == URI_ECDSA_SHA224) return NID_sha224;
Expand All @@ -172,11 +141,12 @@ int Digest::toMethod(const string &uri)
if(uri == URI_SHA3_384 || uri == URI_RSA_PSS_SHA3_384) return NID_sha3_384;
if(uri == URI_SHA3_512 || uri == URI_RSA_PSS_SHA3_512) return NID_sha3_512;
#endif
THROW( "Digest method URI '%s' is not supported.", uri.c_str() );
THROW("Digest method URI '%.*s' is not supported.", uri.size(), uri.data());
}

string Digest::toRsaUri(const string &uri)
{
DEBUG("method %s", uri.c_str());
if(uri == URI_SHA1) return URI_RSA_SHA1;
if(uri == URI_SHA224) return URI_RSA_SHA224;
if(uri == URI_SHA256) return URI_RSA_SHA256;
Expand All @@ -186,33 +156,25 @@ string Digest::toRsaUri(const string &uri)
uri == URI_RSA_SHA224 ||
uri == URI_RSA_SHA256 ||
uri == URI_RSA_SHA384 ||
uri == URI_RSA_SHA512 ||
uri == URI_RSA_PSS_SHA224 ||
uri == URI_RSA_PSS_SHA256 ||
uri == URI_RSA_PSS_SHA384 ||
uri == URI_RSA_PSS_SHA512 ||
#ifndef LIBRESSL_VERSION_NUMBER
uri == URI_RSA_PSS_SHA3_224 ||
uri == URI_RSA_PSS_SHA3_256 ||
uri == URI_RSA_PSS_SHA3_384 ||
uri == URI_RSA_PSS_SHA3_512)
#else
0)
#endif
uri == URI_RSA_SHA512)
return uri;
return {};
return toRsaPssUri(uri);
}

string Digest::toRsaPssUri(const string &uri)
string Digest::toRsaPssUri(string uri)
{
if(uri == URI_SHA224) return URI_RSA_PSS_SHA224;
if(uri == URI_SHA256) return URI_RSA_PSS_SHA256;
if(uri == URI_SHA384) return URI_RSA_PSS_SHA384;
if(uri == URI_SHA512) return URI_RSA_PSS_SHA512;
#ifndef LIBRESSL_VERSION_NUMBER
if(uri == URI_SHA3_224) return URI_RSA_PSS_SHA3_224;
if(uri == URI_SHA3_256) return URI_RSA_PSS_SHA3_256;
if(uri == URI_SHA3_384) return URI_RSA_PSS_SHA3_384;
if(uri == URI_SHA3_512) return URI_RSA_PSS_SHA3_512;
#endif
if(isRsaPssUri(uri))
return uri;
return {};
}

Expand Down Expand Up @@ -252,61 +214,40 @@ std::string Digest::toUri(int nid)
}

/**
* Add data for digest calculation.
*
* @param data data to add for digest calculation.
* @throws IOException throws exception if SHA1 update failed.
* @see update(const unsigned char* data, unsigned long length)
*/
void Digest::update(const vector<unsigned char> &data)
{
update(data.data(), data.size());
}

/**
* Add data for digest calculation. After calling <code>getDigest()</code> SHA context
* Add data for digest calculation. After calling <code>result()</code> SHA context
* is uninitialized and this method should not be called.
*
* @param data data to add for digest calculation.
* @param length length of the data.
* @throws IOException throws exception if update failed.
* @see getDigest()
* @throws Exception throws exception if update failed.
* @see result()
*/
void Digest::update(const unsigned char *data, size_t length)
{
if(!data)
THROW("Can not update digest value from NULL pointer.");
if(!d->empty())
THROW("Digest is already finalized, can not update it.");
if(EVP_DigestUpdate(d->ctx, data, length) != 1)
if(EVP_DigestUpdate(d.get(), data, length) != 1)
THROW_OPENSSLEXCEPTION("Failed to update %s digest value", uri().c_str());
}

/**
* Calculate message digest. SHA context will be invalid after this call.
* For calculating an other digest you must create new SHA1Digest class.
* For calculating an other digest you must create new Digest class.
*
* @return returns the calculated digest.
* @throws IOException throws exception if update failed.
* @throws Exception throws exception if update failed.
*/
vector<unsigned char> Digest::result() const
{
if(!d->empty())
return *d;
unsigned int size = 0;
d->resize(size_t(EVP_MD_CTX_size(d->ctx)));
if(EVP_DigestFinal(d->ctx, d->data(), &size) != 1)
vector<unsigned char> result(size_t(EVP_MD_CTX_size(d.get())), 0);
if(EVP_DigestFinal_ex(d.get(), result.data(), &size) != 1)
THROW_OPENSSLEXCEPTION("Failed to create %s digest", uri().c_str());
return *d;
return result;
}

vector<unsigned char> Digest::result(const vector<unsigned char> &data)
{
return result(data.data(), data.size());
}

vector<unsigned char> Digest::result(const unsigned char *data, size_t length)
{
update(data, length);
update(data.data(), data.size());
return result();
}
17 changes: 7 additions & 10 deletions src/crypto/Digest.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
#define URI_RSA_PSS_SHA256 "http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1"
#define URI_RSA_PSS_SHA384 "http://www.w3.org/2007/05/xmldsig-more#sha384-rsa-MGF1"
#define URI_RSA_PSS_SHA512 "http://www.w3.org/2007/05/xmldsig-more#sha512-rsa-MGF1"

#define URI_RSA_PSS_SHA3_224 "http://www.w3.org/2007/05/xmldsig-more#sha3-224-rsa-MGF1"
#define URI_RSA_PSS_SHA3_256 "http://www.w3.org/2007/05/xmldsig-more#sha3-256-rsa-MGF1"
#define URI_RSA_PSS_SHA3_384 "http://www.w3.org/2007/05/xmldsig-more#sha3-384-rsa-MGF1"
Expand All @@ -57,6 +56,8 @@
#define URI_ECDSA_SHA384 "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384"
#define URI_ECDSA_SHA512 "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512"

using EVP_MD_CTX = struct evp_md_ctx_st;

namespace digidoc
{
/**
Expand All @@ -67,28 +68,24 @@ namespace digidoc
public:
Digest(const std::string &uri = {});
~Digest();
void reset(const std::string &uri = {});
void update(const std::vector<unsigned char> &data);
void update(const unsigned char *data, size_t length);
std::vector<unsigned char> result(const std::vector<unsigned char> &data);
std::vector<unsigned char> result(const unsigned char *data, size_t length);
std::vector<unsigned char> result() const;
std::string uri() const;

static bool isRsaPssUri(const std::string &uri);
static bool isRsaPssUri(std::string_view uri);
static std::string toRsaUri(const std::string &uri);
static std::string toRsaPssUri(const std::string &uri);
static std::string toRsaPssUri(std::string uri);
static std::string toEcUri(const std::string &uri);
static int toMethod(const std::string &uri);
static int toMethod(std::string_view uri);
static std::string toUri(int nid);
static std::vector<unsigned char> addDigestInfo(const std::vector<unsigned char> &digest, const std::string &uri);
static std::vector<unsigned char> addDigestInfo(std::vector<unsigned char> digest, std::string_view uri);
static std::vector<unsigned char> digestInfoDigest(const std::vector<unsigned char> &digest);
static std::string digestInfoUri(const std::vector<unsigned char> &digest);

private:
DISABLE_COPY(Digest);
class Private;
std::unique_ptr<Private> d;
std::unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX*)> d;
};

}
Loading

0 comments on commit c8cfe9b

Please sign in to comment.