openssl: Add support for verifying RSASSA-PSS signatures

This commit is contained in:
Tobias Brunner
2017-11-08 16:48:10 +01:00
parent 51dd2fd2db
commit 2f95d7195d
2 changed files with 142 additions and 3 deletions
@@ -626,6 +626,7 @@ METHOD(plugin_t, get_features, int,
PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_NULL),
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PSS),
PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PSS),
#endif
#ifndef OPENSSL_NO_SHA1
PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA1),
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2008-2017 Tobias Brunner
* Copyright (C) 2009 Martin Willi
* Copyright (C) 2008 Tobias Brunner
* Hochschule fuer Technik Rapperswil
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -19,9 +19,11 @@
#ifndef OPENSSL_NO_RSA
#include "openssl_rsa_public_key.h"
#include "openssl_hasher.h"
#include "openssl_util.h"
#include <utils/debug.h>
#include <credentials/keys/signature_params.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
@@ -54,8 +56,138 @@ struct private_openssl_rsa_public_key_t {
refcount_t ref;
};
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
/**
* Verification of an EMPSA PKCS1 signature described in PKCS#1
* Verify RSA signature
*/
static bool verify_signature(private_openssl_rsa_public_key_t *this,
const EVP_MD *md, rsa_pss_params_t *pss,
chunk_t data, chunk_t signature)
{
EVP_PKEY_CTX *pctx = NULL;
EVP_MD_CTX *mctx = NULL;
EVP_PKEY *key;
int rsa_size = RSA_size(this->rsa);
bool valid = FALSE;
/* OpenSSL expects a signature of exactly RSA size (no leading 0x00) */
if (signature.len > rsa_size)
{
signature = chunk_skip(signature, signature.len - rsa_size);
}
mctx = EVP_MD_CTX_create();
key = EVP_PKEY_new();
if (!mctx || !key)
{
goto error;
}
if (!EVP_PKEY_set1_RSA(key, this->rsa))
{
goto error;
}
if (EVP_DigestVerifyInit(mctx, &pctx, md, NULL, key) <= 0)
{
goto error;
}
if (pss)
{
const EVP_MD *mgf1md = openssl_get_md(pss->mgf1_hash);
int slen = EVP_MD_size(md);
if (pss->salt_len > RSA_PSS_SALT_LEN_DEFAULT)
{
slen = pss->salt_len;
}
if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0 ||
EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, slen) <= 0 ||
EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1md) <= 0)
{
goto error;
}
}
if (EVP_DigestVerifyUpdate(mctx, data.ptr, data.len) <= 0)
{
goto error;
}
valid = (EVP_DigestVerifyFinal(mctx, signature.ptr, signature.len) == 1);
error:
if (key)
{
EVP_PKEY_free(key);
}
if (mctx)
{
EVP_MD_CTX_destroy(mctx);
}
return valid;
}
/**
* Verification of a signature without hashing
*/
static bool verify_plain_signature(private_openssl_rsa_public_key_t *this,
chunk_t data, chunk_t signature)
{
char *buf;
int len, rsa_size = RSA_size(this->rsa);
bool valid = FALSE;
/* OpenSSL expects a signature of exactly RSA size (no leading 0x00) */
if (signature.len > rsa_size)
{
signature = chunk_skip(signature, signature.len - rsa_size);
}
buf = malloc(rsa_size);
len = RSA_public_decrypt(signature.len, signature.ptr, buf, this->rsa,
RSA_PKCS1_PADDING);
if (len != -1)
{
valid = chunk_equals_const(data, chunk_create(buf, len));
}
free(buf);
return valid;
}
/**
* Verification of an EMSA PKCS1 signature described in PKCS#1
*/
static bool verify_emsa_pkcs1_signature(private_openssl_rsa_public_key_t *this,
int type, chunk_t data, chunk_t signature)
{
const EVP_MD *md;
if (type == NID_undef)
{
return verify_plain_signature(this, data, signature);
}
md = EVP_get_digestbynid(type);
return md && verify_signature(this, md, NULL, data, signature);
}
/**
* Verification of an EMSA PSS signature described in PKCS#1
*/
static bool verify_emsa_pss_signature(private_openssl_rsa_public_key_t *this,
rsa_pss_params_t *params, chunk_t data,
chunk_t signature)
{
const EVP_MD *md;
if (!params)
{
return FALSE;
}
md = openssl_get_md(params->hash);
return md && verify_signature(this, md, params, data, signature);
}
#else /* OPENSSL_VERSION_NUMBER < 1.0 */
/**
* Verification of an EMSA PKCS1 signature described in PKCS#1
*/
static bool verify_emsa_pkcs1_signature(private_openssl_rsa_public_key_t *this,
int type, chunk_t data, chunk_t signature)
@@ -129,6 +261,8 @@ error:
return valid;
}
#endif /* OPENSSL_VERSION_NUMBER < 1.0 */
METHOD(public_key_t, get_type, key_type_t,
private_openssl_rsa_public_key_t *this)
{
@@ -155,6 +289,10 @@ METHOD(public_key_t, verify, bool,
return verify_emsa_pkcs1_signature(this, NID_sha1, data, signature);
case SIGN_RSA_EMSA_PKCS1_MD5:
return verify_emsa_pkcs1_signature(this, NID_md5, data, signature);
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
case SIGN_RSA_EMSA_PSS:
return verify_emsa_pss_signature(this, params, data, signature);
#endif
default:
DBG1(DBG_LIB, "signature scheme %N not supported in RSA",
signature_scheme_names, scheme);