879 lines
24 KiB
C
879 lines
24 KiB
C
/*
|
|
* p11test_case_pss_oaep.c: RSA-PSS and RSA-OAEP tests
|
|
*
|
|
* Copyright (C) 2017 Red Hat, Inc.
|
|
*
|
|
* Author: Jakub Jelen <jjelen@redhat.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "p11test_case_pss_oaep.h"
|
|
#include "libopensc/internal.h"
|
|
|
|
#include <openssl/sha.h>
|
|
#include <openssl/evp.h>
|
|
|
|
#define SHORT_MESSAGE_TO_SIGN "Simple message for signing & verifying. It needs to be little bit longer to fit also longer keys and allow the truncation.\n"
|
|
#define BUFFER_SIZE 4096
|
|
|
|
const unsigned char *global_message = (unsigned char *) SHORT_MESSAGE_TO_SIGN;
|
|
size_t global_message_length = sizeof(SHORT_MESSAGE_TO_SIGN);
|
|
|
|
const CK_MECHANISM_TYPE *
|
|
get_oaep_mechanism_hashes(CK_MECHANISM_TYPE mech)
|
|
{
|
|
static CK_MECHANISM_TYPE h[6];
|
|
|
|
switch (mech) {
|
|
case CKM_RSA_PKCS_OAEP:
|
|
h[0] = CKM_SHA_1;
|
|
h[1] = CKM_SHA224;
|
|
h[2] = CKM_SHA256;
|
|
h[3] = CKM_SHA384;
|
|
h[4] = CKM_SHA512;
|
|
h[5] = -1;
|
|
break;
|
|
|
|
default:
|
|
h[0] = -1;
|
|
break;
|
|
}
|
|
|
|
return h;
|
|
}
|
|
const CK_MECHANISM_TYPE *
|
|
get_pss_mechanism_hashes(CK_MECHANISM_TYPE mech)
|
|
{
|
|
static CK_MECHANISM_TYPE h[6];
|
|
|
|
switch (mech) {
|
|
case CKM_RSA_PKCS_PSS:
|
|
h[0] = CKM_SHA_1;
|
|
h[1] = CKM_SHA224;
|
|
h[2] = CKM_SHA256;
|
|
h[3] = CKM_SHA384;
|
|
h[4] = CKM_SHA512;
|
|
h[5] = -1;
|
|
break;
|
|
|
|
case CKM_SHA1_RSA_PKCS_PSS:
|
|
h[0] = CKM_SHA_1;
|
|
h[1] = -1;
|
|
break;
|
|
|
|
case CKM_SHA224_RSA_PKCS_PSS:
|
|
h[0] = CKM_SHA224;
|
|
h[1] = -1;
|
|
break;
|
|
|
|
case CKM_SHA256_RSA_PKCS_PSS:
|
|
h[0] = CKM_SHA256;
|
|
h[1] = -1;
|
|
break;
|
|
|
|
case CKM_SHA384_RSA_PKCS_PSS:
|
|
h[0] = CKM_SHA384;
|
|
h[1] = -1;
|
|
break;
|
|
|
|
case CKM_SHA512_RSA_PKCS_PSS:
|
|
h[0] = CKM_SHA512;
|
|
h[1] = -1;
|
|
break;
|
|
|
|
default:
|
|
h[0] = -1;
|
|
break;
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
const CK_MECHANISM_TYPE *
|
|
get_mechanism_hashes(CK_MECHANISM_TYPE mech)
|
|
{
|
|
if (mech == CKM_RSA_PKCS_OAEP)
|
|
return get_oaep_mechanism_hashes(mech);
|
|
else
|
|
return get_pss_mechanism_hashes(mech);
|
|
}
|
|
|
|
const CK_RSA_PKCS_MGF_TYPE *
|
|
get_mgfs(void)
|
|
{
|
|
static CK_RSA_PKCS_MGF_TYPE h[6];
|
|
h[0] = CKG_MGF1_SHA1;
|
|
h[1] = CKG_MGF1_SHA224;
|
|
h[2] = CKG_MGF1_SHA256;
|
|
h[3] = CKG_MGF1_SHA384;
|
|
h[4] = CKG_MGF1_SHA512;
|
|
h[5] = -1;
|
|
return h;
|
|
}
|
|
|
|
const EVP_MD *mgf_cryptoki_to_ossl(CK_RSA_PKCS_MGF_TYPE mgf)
|
|
{
|
|
switch (mgf) {
|
|
case CKG_MGF1_SHA224:
|
|
return EVP_sha224();
|
|
|
|
case CKG_MGF1_SHA256:
|
|
return EVP_sha256();
|
|
|
|
case CKG_MGF1_SHA384:
|
|
return EVP_sha384();
|
|
|
|
case CKG_MGF1_SHA512:
|
|
return EVP_sha512();
|
|
|
|
case CKG_MGF1_SHA1:
|
|
default:
|
|
return EVP_sha1();
|
|
|
|
}
|
|
}
|
|
|
|
const EVP_MD *md_cryptoki_to_ossl(CK_MECHANISM_TYPE hash)
|
|
{
|
|
/* Digest mechanisms */
|
|
switch (hash) {
|
|
case CKM_SHA224:
|
|
return EVP_sha224();
|
|
|
|
case CKM_SHA256:
|
|
return EVP_sha256();
|
|
|
|
case CKM_SHA384:
|
|
return EVP_sha384();
|
|
|
|
case CKM_SHA512:
|
|
return EVP_sha512();
|
|
|
|
case CKM_SHA_1:
|
|
default:
|
|
return EVP_sha1();
|
|
|
|
}
|
|
}
|
|
|
|
size_t get_hash_length(CK_MECHANISM_TYPE mech)
|
|
{
|
|
switch (mech) {
|
|
case CKM_SHA224:
|
|
return SHA224_DIGEST_LENGTH;
|
|
case CKM_SHA256:
|
|
return SHA256_DIGEST_LENGTH;
|
|
case CKM_SHA384:
|
|
return SHA384_DIGEST_LENGTH;
|
|
case CKM_SHA512:
|
|
return SHA512_DIGEST_LENGTH;
|
|
default:
|
|
case CKM_SHA_1:
|
|
return SHA_DIGEST_LENGTH;
|
|
}
|
|
}
|
|
|
|
CK_BYTE *hash_message(const CK_BYTE *message, size_t message_length,
|
|
CK_MECHANISM_TYPE hash)
|
|
{
|
|
switch (hash) {
|
|
case CKM_SHA224:
|
|
return SHA224(message, message_length, NULL);
|
|
|
|
case CKM_SHA256:
|
|
return SHA256(message, message_length, NULL);
|
|
|
|
case CKM_SHA384:
|
|
return SHA384(message, message_length, NULL);
|
|
|
|
case CKM_SHA512:
|
|
return SHA512(message, message_length, NULL);
|
|
|
|
case CKM_SHA_1:
|
|
default:
|
|
return SHA1(message, message_length, NULL);
|
|
|
|
}
|
|
}
|
|
|
|
int oaep_encrypt_message_openssl(test_cert_t *o, token_info_t *info, CK_BYTE *message,
|
|
CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message)
|
|
{
|
|
size_t enc_length = 0;
|
|
CK_RV rv = -1;
|
|
EVP_PKEY_CTX *pctx = NULL;
|
|
const EVP_MD *md = EVP_md_null();
|
|
const EVP_MD *mgf1_md = EVP_md_null();
|
|
EVP_PKEY *key = NULL;
|
|
|
|
md = md_cryptoki_to_ossl(mech->hash);
|
|
mgf1_md = mgf_cryptoki_to_ossl(mech->mgf);
|
|
|
|
if ((key = EVP_PKEY_new()) == NULL
|
|
|| RSA_up_ref(o->key.rsa) < 1
|
|
|| EVP_PKEY_set1_RSA(key, o->key.rsa) != 1) {
|
|
fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY. Error: %s\n",
|
|
o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
|
|
goto out;
|
|
}
|
|
|
|
if ((pctx = EVP_PKEY_CTX_new(key, NULL)) == NULL
|
|
|| EVP_PKEY_encrypt_init(pctx) != 1
|
|
|| EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_OAEP_PADDING) != 1
|
|
|| EVP_PKEY_CTX_set_rsa_oaep_md(pctx, md) != 1
|
|
|| EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1_md) != 1) {
|
|
fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY_CTX. Error: %s\n",
|
|
o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
|
|
goto out;
|
|
}
|
|
|
|
if (EVP_PKEY_encrypt(pctx, NULL, &enc_length, message, message_length) <= 0) {
|
|
fprintf(stderr, " [ ERROR %s ] Failed get signature length. Error: %s\n",
|
|
o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
|
|
goto out;
|
|
}
|
|
|
|
*enc_message = OPENSSL_malloc(enc_length);
|
|
|
|
rv = EVP_PKEY_encrypt(pctx, *enc_message, &enc_length, message, message_length);
|
|
if (rv <= 0) {
|
|
fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n",
|
|
o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
|
|
}
|
|
out:
|
|
EVP_PKEY_CTX_free(pctx);
|
|
EVP_PKEY_free(key);
|
|
return enc_length;
|
|
}
|
|
|
|
void fill_oaep_params(CK_RSA_PKCS_OAEP_PARAMS *oaep_params,
|
|
test_mech_t *mech)
|
|
{
|
|
oaep_params->hashAlg = mech->hash;
|
|
oaep_params->mgf = mech->mgf;
|
|
oaep_params->source = CKZ_DATA_SPECIFIED;
|
|
oaep_params->pSourceData = NULL;
|
|
oaep_params->ulSourceDataLen = 0;
|
|
|
|
}
|
|
|
|
int oaep_encrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *message,
|
|
CK_ULONG message_length, test_mech_t *mech, unsigned char **enc_message)
|
|
{
|
|
CK_RV rv;
|
|
CK_FUNCTION_LIST_PTR fp = info->function_pointer;
|
|
CK_MECHANISM enc_mechanism = { mech->mech, NULL_PTR, 0 };
|
|
CK_RSA_PKCS_OAEP_PARAMS oaep_params;
|
|
CK_ULONG enc_message_length;
|
|
static int encrypt_support = 1;
|
|
|
|
fill_oaep_params(&oaep_params, mech);
|
|
enc_mechanism.pParameter = &oaep_params;
|
|
enc_mechanism.ulParameterLen = sizeof(oaep_params);
|
|
|
|
if (!encrypt_support)
|
|
goto openssl_encrypt;
|
|
|
|
rv = fp->C_EncryptInit(info->session_handle, &enc_mechanism,
|
|
o->public_handle);
|
|
if (rv != CKR_OK) {
|
|
debug_print(" C_EncryptInit: rv = 0x%.8lX", rv);
|
|
encrypt_support = 0; /* avoid trying over and over again */
|
|
goto openssl_encrypt;
|
|
}
|
|
|
|
/* get the expected length */
|
|
rv = fp->C_Encrypt(info->session_handle, message, message_length,
|
|
NULL, &enc_message_length);
|
|
if (rv != CKR_OK) {
|
|
debug_print(" C_Encrypt: rv = 0x%.8lX", rv);
|
|
goto openssl_encrypt;
|
|
}
|
|
*enc_message = malloc(enc_message_length);
|
|
if (*enc_message == NULL) {
|
|
debug_print("malloc returned null");
|
|
return -1;
|
|
}
|
|
|
|
/* Do the actual encryption with allocated buffer */
|
|
rv = fp->C_Encrypt(info->session_handle, message, message_length,
|
|
*enc_message, &enc_message_length);
|
|
if (rv == CKR_OK) {
|
|
mech->result_flags |= FLAGS_DECRYPT_OPENSSL;
|
|
return enc_message_length;
|
|
}
|
|
debug_print(" C_Encrypt: rv = 0x%.8lX", rv);
|
|
|
|
openssl_encrypt:
|
|
debug_print(" [ KEY %s ] Falling back to openssl encryption", o->id_str);
|
|
return oaep_encrypt_message_openssl(o, info, message, message_length, mech,
|
|
enc_message);
|
|
}
|
|
|
|
int oaep_decrypt_message(test_cert_t *o, token_info_t *info, CK_BYTE *enc_message,
|
|
CK_ULONG enc_message_length, test_mech_t *mech, unsigned char **dec_message)
|
|
{
|
|
CK_RV rv;
|
|
CK_FUNCTION_LIST_PTR fp = info->function_pointer;
|
|
CK_MECHANISM dec_mechanism = { mech->mech, NULL_PTR, 0 };
|
|
CK_RSA_PKCS_OAEP_PARAMS oaep_params;
|
|
CK_ULONG dec_message_length = BUFFER_SIZE;
|
|
|
|
fill_oaep_params(&oaep_params, mech);
|
|
dec_mechanism.pParameter = &oaep_params;
|
|
dec_mechanism.ulParameterLen = sizeof(oaep_params);
|
|
|
|
rv = fp->C_DecryptInit(info->session_handle, &dec_mechanism,
|
|
o->private_handle);
|
|
if (rv == CKR_KEY_TYPE_INCONSISTENT) {
|
|
debug_print(" [SKIP %s ] Not allowed to decrypt with this key?", o->id_str);
|
|
return 0;
|
|
} else if (rv != CKR_OK) {
|
|
debug_print(" C_DecryptInit: rv = 0x%.8lX\n", rv);
|
|
return -1;
|
|
}
|
|
|
|
*dec_message = malloc(dec_message_length);
|
|
|
|
always_authenticate(o, info);
|
|
|
|
rv = fp->C_Decrypt(info->session_handle, enc_message,
|
|
enc_message_length, *dec_message, &dec_message_length);
|
|
if (rv != CKR_OK) {
|
|
free(*dec_message);
|
|
debug_print(" C_Decrypt: rv = 0x%.8lX\n", rv);
|
|
return -1;
|
|
}
|
|
return (int) dec_message_length;
|
|
}
|
|
|
|
/* Perform encryption and decryption of a message using private key referenced
|
|
* in the o object with mechanism defined by mech.
|
|
*
|
|
* NONE of the reasonable mechanisms support encryption/decryption
|
|
*
|
|
* Returns
|
|
* * 1 for successful Encrypt&Decrypt sequence
|
|
* * 0 for skipped test (unsupported mechanism, key, ...)
|
|
* * -1 otherwise.
|
|
* Serious errors terminate the execution.
|
|
*/
|
|
int oaep_encrypt_decrypt_test(test_cert_t *o, token_info_t *info, test_mech_t *mech)
|
|
{
|
|
CK_BYTE *message = (CK_BYTE *) SHORT_MESSAGE_TO_SIGN;
|
|
CK_BYTE *dec_message = NULL;
|
|
int dec_message_length = 0;
|
|
int message_length = 16;
|
|
unsigned char *enc_message = NULL;
|
|
int enc_message_length, rv;
|
|
|
|
if (o->private_handle == CK_INVALID_HANDLE) {
|
|
debug_print(" [SKIP %s ] Missing private key", o->id_str);
|
|
return 0;
|
|
}
|
|
|
|
if (o->type != EVP_PK_RSA) {
|
|
debug_print(" [ KEY %s ] Skip non-RSA key for encryption", o->id_str);
|
|
return 0;
|
|
}
|
|
|
|
if (mech->mech != CKM_RSA_PKCS_OAEP) {
|
|
mech->usage_flags &= ~CKF_DECRYPT;
|
|
debug_print(" [SKIP %s ] non RSA-OAEP mechanism", o->id_str);
|
|
return 0;
|
|
}
|
|
|
|
message_length = MIN((int)global_message_length,
|
|
(int)((o->bits+7)/8 - 2*get_hash_length(mech->hash) - 2));
|
|
|
|
/* will not work for 1024b RSA key and SHA512 hash: It has max size -2 */
|
|
if (message_length < 0) {
|
|
mech->usage_flags &= ~CKF_DECRYPT;
|
|
debug_print(" [SKIP %s ] Too small modulus (%ld bits)"
|
|
" or too large hash %s (%lu B) for OAEP", o->id_str,
|
|
o->bits, get_mechanism_name(mech->hash),
|
|
get_hash_length(mech->hash));
|
|
return 0;
|
|
}
|
|
|
|
debug_print(" [ KEY %s ] Encrypt message of length %d using CKM_%s, "
|
|
"hash CKM_%s, mgf=CKG_%s", o->id_str, (unsigned) message_length,
|
|
get_mechanism_name(mech->mech), get_mechanism_name(mech->hash),
|
|
get_mgf_name(mech->mgf));
|
|
enc_message_length = oaep_encrypt_message(o, info, message,
|
|
(unsigned) message_length, mech, &enc_message);
|
|
if (enc_message_length <= 0) {
|
|
return -1;
|
|
}
|
|
|
|
debug_print(" [ KEY %s ] Decrypt message", o->id_str);
|
|
dec_message_length = oaep_decrypt_message(o, info, enc_message,
|
|
enc_message_length, mech, &dec_message);
|
|
free(enc_message);
|
|
if (dec_message_length <= 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (memcmp(dec_message, message, dec_message_length) == 0
|
|
&& dec_message_length == message_length) {
|
|
debug_print(" [ OK %s ] Text decrypted successfully.", o->id_str);
|
|
mech->result_flags |= FLAGS_DECRYPT;
|
|
rv = 1;
|
|
} else {
|
|
dec_message[dec_message_length] = '\0';
|
|
debug_print(" [ ERROR %s ] Text decryption failed. Recovered text: %s",
|
|
o->id_str, dec_message);
|
|
rv = 0;
|
|
}
|
|
free(dec_message);
|
|
return rv;
|
|
}
|
|
|
|
static int get_max_salt_len(unsigned long bits, CK_MECHANISM_TYPE hash)
|
|
{
|
|
return (bits + 7)/8 - get_hash_length(hash) - 2;
|
|
}
|
|
|
|
int fill_pss_params(CK_RSA_PKCS_PSS_PARAMS *pss_params,
|
|
test_mech_t *mech, test_cert_t *o)
|
|
{
|
|
pss_params->hashAlg = mech->hash;
|
|
pss_params->mgf = mech->mgf;
|
|
switch (mech->salt){
|
|
case -2:
|
|
/* max possible ( modlen - hashlen -2 ) */
|
|
pss_params->sLen = get_max_salt_len(o->bits,mech->hash);
|
|
break;
|
|
case -1:
|
|
/* digest length */
|
|
/* will not work with SHA512 and 1024b keys (max is 62b!) */
|
|
if ((int) get_hash_length(mech->hash) > get_max_salt_len(o->bits, mech->hash)) {
|
|
return -1;
|
|
}
|
|
pss_params->sLen = get_hash_length(mech->hash);
|
|
break;
|
|
case 0:
|
|
default:
|
|
pss_params->sLen = 0;
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int pss_sign_message(test_cert_t *o, token_info_t *info, CK_BYTE *message,
|
|
CK_ULONG message_length, test_mech_t *mech, unsigned char **sign)
|
|
{
|
|
CK_RV rv;
|
|
CK_FUNCTION_LIST_PTR fp = info->function_pointer;
|
|
CK_MECHANISM sign_mechanism = { mech->mech, NULL_PTR, 0 };
|
|
CK_ULONG sign_length = 0;
|
|
CK_RSA_PKCS_PSS_PARAMS pss_params;
|
|
|
|
if (fill_pss_params(&pss_params, mech, o) != 1) {
|
|
debug_print(" [SKIP %s ] Impossible to use requested salt length", o->id_str);
|
|
return 0;
|
|
}
|
|
sign_mechanism.pParameter = &pss_params;
|
|
sign_mechanism.ulParameterLen = sizeof(pss_params);
|
|
|
|
rv = fp->C_SignInit(info->session_handle, &sign_mechanism,
|
|
o->private_handle);
|
|
if (rv == CKR_KEY_TYPE_INCONSISTENT) {
|
|
debug_print(" [SKIP %s ] Not allowed to sign with this key?", o->id_str);
|
|
return 0;
|
|
} else if (rv == CKR_MECHANISM_INVALID) {
|
|
debug_print(" [SKIP %s ] Bad mechanism. Not supported?", o->id_str);
|
|
return 0;
|
|
} else if (rv != CKR_OK) {
|
|
debug_print(" C_SignInit: rv = 0x%.8lX\n", rv);
|
|
return -1;
|
|
}
|
|
|
|
always_authenticate(o, info);
|
|
|
|
/* Call C_Sign with NULL argument to find out the real size of signature */
|
|
rv = fp->C_Sign(info->session_handle,
|
|
message, message_length, *sign, &sign_length);
|
|
if (rv != CKR_OK) {
|
|
fprintf(stderr, " C_Sign: rv = 0x%.8lX\n", rv);
|
|
return -1;
|
|
}
|
|
|
|
*sign = malloc(sign_length);
|
|
if (*sign == NULL) {
|
|
fprintf(stderr, "%s: malloc failed", __func__);
|
|
return -1;
|
|
}
|
|
|
|
/* Call C_Sign with allocated buffer to the actual signature */
|
|
rv = fp->C_Sign(info->session_handle,
|
|
message, message_length, *sign, &sign_length);
|
|
|
|
if (rv != CKR_OK) {
|
|
free(*sign);
|
|
fprintf(stderr, " C_Sign: rv = 0x%.8lX\n", rv);
|
|
return -1;
|
|
}
|
|
return sign_length;
|
|
}
|
|
|
|
int pss_verify_message_openssl(test_cert_t *o, token_info_t *info,
|
|
CK_BYTE *message, CK_ULONG message_length, test_mech_t *mech,
|
|
unsigned char *sign, CK_ULONG sign_length)
|
|
{
|
|
CK_RV rv = -1;
|
|
EVP_PKEY_CTX *pctx = NULL;
|
|
const CK_BYTE *my_message;
|
|
CK_ULONG my_message_length;
|
|
const EVP_MD *mgf_md = EVP_md_null();
|
|
const EVP_MD *md = EVP_md_null();
|
|
EVP_PKEY *key = NULL;
|
|
|
|
md = md_cryptoki_to_ossl(mech->hash);
|
|
mgf_md = mgf_cryptoki_to_ossl(mech->mgf);
|
|
|
|
if (mech->mech != CKM_RSA_PKCS_PSS) {
|
|
my_message = hash_message(message, message_length, mech->hash);
|
|
my_message_length = get_hash_length(mech->hash);
|
|
} else {
|
|
my_message = message;
|
|
my_message_length = message_length;
|
|
}
|
|
|
|
if ((key = EVP_PKEY_new()) == NULL
|
|
|| RSA_up_ref(o->key.rsa) < 1
|
|
|| EVP_PKEY_set1_RSA(key, o->key.rsa) != 1) {
|
|
fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY. Error: %s\n",
|
|
o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
|
|
goto out;
|
|
}
|
|
|
|
if ((pctx = EVP_PKEY_CTX_new(key, NULL)) == NULL
|
|
|| EVP_PKEY_verify_init(pctx) != 1
|
|
|| EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) != 1
|
|
|| EVP_PKEY_CTX_set_signature_md(pctx, md) != 1
|
|
|| EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, mech->salt) != 1
|
|
|| EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf_md) != 1) {
|
|
fprintf(stderr, " [ ERROR %s ] Failed to initialize EVP_PKEY_CTX. Error: %s\n",
|
|
o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
|
|
goto out;
|
|
}
|
|
|
|
rv = EVP_PKEY_verify(pctx, sign, sign_length, my_message, my_message_length);
|
|
if (rv == 1) {
|
|
debug_print(" [ OK %s ] Signature is valid.", o->id_str);
|
|
mech->result_flags |= FLAGS_SIGN_OPENSSL;
|
|
} else {
|
|
fprintf(stderr, " [ ERROR %s ] Signature is not valid. Error: %s\n",
|
|
o->id_str, ERR_error_string(ERR_peek_last_error(), NULL));
|
|
goto out;
|
|
}
|
|
out:
|
|
EVP_PKEY_CTX_free(pctx);
|
|
EVP_PKEY_free(key);
|
|
return rv;
|
|
}
|
|
|
|
int pss_verify_message(test_cert_t *o, token_info_t *info, CK_BYTE *message,
|
|
CK_ULONG message_length, test_mech_t *mech, unsigned char *sign,
|
|
CK_ULONG sign_length)
|
|
{
|
|
CK_RV rv;
|
|
CK_FUNCTION_LIST_PTR fp = info->function_pointer;
|
|
CK_MECHANISM sign_mechanism = { mech->mech, NULL_PTR, 0 };
|
|
CK_RSA_PKCS_PSS_PARAMS pss_params;
|
|
static int verify_support = 1;
|
|
|
|
if (!verify_support)
|
|
goto openssl_verify;
|
|
|
|
fill_pss_params(&pss_params, mech, o);
|
|
sign_mechanism.pParameter = &pss_params;
|
|
sign_mechanism.ulParameterLen = sizeof(pss_params);
|
|
|
|
/* try C_Verify() if it is supported */
|
|
rv = fp->C_VerifyInit(info->session_handle, &sign_mechanism,
|
|
o->public_handle);
|
|
if (rv != CKR_OK) {
|
|
debug_print(" C_VerifyInit: rv = 0x%.8lX", rv);
|
|
verify_support = 0; /* avoid trying over and over again */
|
|
goto openssl_verify;
|
|
}
|
|
|
|
rv = fp->C_Verify(info->session_handle,
|
|
message, message_length, sign, sign_length);
|
|
|
|
if (rv == CKR_OK) {
|
|
mech->result_flags |= FLAGS_SIGN;
|
|
debug_print(" [ OK %s ] Verification successful", o->id_str);
|
|
return 1;
|
|
}
|
|
debug_print(" C_Verify: rv = 0x%.8lX", rv);
|
|
verify_support = 0; /* avoid trying over and over again */
|
|
|
|
openssl_verify:
|
|
debug_print(" [ KEY %s ] Falling back to openssl verification", o->id_str);
|
|
return pss_verify_message_openssl(o, info, message, message_length, mech,
|
|
sign, sign_length);
|
|
}
|
|
|
|
/* Perform signature and verification of a message using private key referenced
|
|
* in the o object with mechanism defined by mech.
|
|
*
|
|
* Returns
|
|
* * 1 for successful Sign&Verify sequence
|
|
* * 0 for skipped test (unsupported mechanism, key, ...)
|
|
* * -1 otherwise.
|
|
* Serious errors terminate the execution.
|
|
*/
|
|
int pss_sign_verify_test(test_cert_t *o, token_info_t *info, test_mech_t *mech)
|
|
{
|
|
CK_BYTE *message = NULL;
|
|
size_t message_length = global_message_length;
|
|
CK_BYTE *sign = NULL;
|
|
CK_ULONG sign_length = 0;
|
|
int rv = 0;
|
|
|
|
if (o->private_handle == CK_INVALID_HANDLE) {
|
|
debug_print(" [SKIP %s ] Missing private key", o->id_str);
|
|
return 0;
|
|
}
|
|
|
|
if (o->type != EVP_PK_RSA) {
|
|
debug_print(" [SKIP %s ] Skip non-RSA key", o->id_str);
|
|
return 0;
|
|
}
|
|
|
|
if (!is_pss_mechanism(mech->mech)) {
|
|
mech->usage_flags &= ~CKF_SIGN;
|
|
debug_print(" [SKIP %s ] non RSA-PSS mechanism %s", o->id_str,
|
|
get_mechanism_name(mech->mech));
|
|
return 0;
|
|
}
|
|
|
|
if (mech->mech == CKM_RSA_PKCS_PSS) {
|
|
message = hash_message(global_message, global_message_length,
|
|
mech->hash);
|
|
message_length = get_hash_length(mech->hash);
|
|
} else {
|
|
message = (unsigned char *) SHORT_MESSAGE_TO_SIGN;
|
|
}
|
|
|
|
debug_print(" [ KEY %s ] Signing message using CKM_%s, CKM_%s,"
|
|
" CKG_%s, salt_len=%d", o->id_str,
|
|
get_mechanism_name(mech->mech), get_mechanism_name(mech->hash),
|
|
get_mgf_name(mech->mgf), mech->salt);
|
|
rv = pss_sign_message(o, info, message, message_length, mech, &sign);
|
|
if (rv <= 0) {
|
|
return rv;
|
|
}
|
|
sign_length = (unsigned long) rv;
|
|
|
|
debug_print(" [ KEY %s ] Verify message signature", o->id_str);
|
|
rv = pss_verify_message(o, info, message, message_length, mech,
|
|
sign, sign_length);
|
|
free(sign);
|
|
return rv;
|
|
}
|
|
|
|
/* ignore the prefilled mechanisms and list all combinations of mechanisms
|
|
* found, all reasonable hash functions, MGFs and salt lengths
|
|
*/
|
|
void fill_object_pss_mechanisms(token_info_t *info, test_cert_t *o)
|
|
{
|
|
const CK_MECHANISM_TYPE *h;
|
|
const CK_RSA_PKCS_MGF_TYPE *mgf;
|
|
int n = 0, s;
|
|
unsigned int j;
|
|
|
|
for (j = 0; j < token.num_rsa_mechs; j++) {
|
|
test_mech_t *source_mech = &token.rsa_mechs[j];
|
|
|
|
/* skip non-RSA-PSS mechs early */
|
|
if (!is_pss_mechanism(source_mech->mech) &&
|
|
source_mech->mech != CKM_RSA_PKCS_OAEP) {
|
|
continue;
|
|
}
|
|
|
|
h = get_mechanism_hashes(source_mech->mech);
|
|
for (; *h != (CK_MECHANISM_TYPE) -1; h++) {
|
|
mgf = get_mgfs();
|
|
for (; *mgf != (CK_RSA_PKCS_MGF_TYPE) -1; mgf++) {
|
|
/* OAEP does not have salt */
|
|
if (source_mech->mech == CKM_RSA_PKCS_OAEP)
|
|
s = 0;
|
|
else
|
|
s = -2;
|
|
|
|
for (; s <= 0; s++) {
|
|
test_mech_t *mech = &o->mechs[n++];
|
|
mech->mech = source_mech->mech;
|
|
mech->hash = *h;
|
|
mech->mgf = *mgf;
|
|
mech->salt = s;
|
|
mech->usage_flags =
|
|
source_mech->usage_flags;
|
|
mech->result_flags = 0;
|
|
if (n >= MAX_MECHS)
|
|
P11TEST_FAIL(info,
|
|
"Too many mechanisms (%d)",
|
|
MAX_MECHS);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
o->num_mechs = n;
|
|
}
|
|
|
|
int have_pss_oaep_mechanisms()
|
|
{
|
|
unsigned have = 0, i;
|
|
for (i = 0; i <= token.num_rsa_mechs; i++) {
|
|
if (is_pss_mechanism(token.rsa_mechs[i].mech) ||
|
|
token.rsa_mechs[i].mech == CKM_RSA_PKCS_OAEP) {
|
|
have++;
|
|
}
|
|
}
|
|
return have;
|
|
}
|
|
|
|
void pss_oaep_test(void **state) {
|
|
|
|
token_info_t *info = (token_info_t *) *state;
|
|
unsigned int i;
|
|
int used, j;
|
|
test_certs_t objects;
|
|
|
|
P11TEST_START(info);
|
|
|
|
if (have_pss_oaep_mechanisms() == 0) {
|
|
fprintf(stderr, "Token does not support any RSA-PSS or OAEP mechanisms. Skipping.\n");
|
|
skip();
|
|
}
|
|
|
|
objects.count = 0;
|
|
objects.data = NULL;
|
|
search_for_all_objects(&objects, info);
|
|
|
|
debug_print("\nCheck functionality of Sign&Verify and/or Encrypt&Decrypt");
|
|
for (i = 0; i < objects.count; i++) {
|
|
test_cert_t *o = &objects.data[i];
|
|
/* do the Sign&Verify and/or Encrypt&Decrypt */
|
|
used = 0;
|
|
if (o->private_handle == CK_INVALID_HANDLE) {
|
|
debug_print(" [SKIP %s ] Missing private key",
|
|
o->id_str);
|
|
continue;
|
|
}
|
|
fill_object_pss_mechanisms(info, o);
|
|
for (j = 0; j < o->num_mechs; j++)
|
|
if (o->mechs[j].mech != CKM_RSA_PKCS_OAEP)
|
|
used |= pss_sign_verify_test(o, info,
|
|
&(o->mechs[j]));
|
|
|
|
for (j = 0; j < o->num_mechs; j++)
|
|
if (o->mechs[j].mech == CKM_RSA_PKCS_OAEP)
|
|
used |= oaep_encrypt_decrypt_test(o, info,
|
|
&(o->mechs[j]));
|
|
|
|
if (!used) {
|
|
debug_print(" [ WARN %s ] Private key with unknown purpose T:%02lX",
|
|
o->id_str, o->key_type);
|
|
}
|
|
}
|
|
|
|
if (objects.count == 0) {
|
|
printf(" [WARN] No objects to display\n");
|
|
return;
|
|
}
|
|
|
|
/* print summary */
|
|
printf("[KEY ID] [LABEL]\n");
|
|
printf("[ TYPE ] [ SIZE ] [PUBLIC] [SIGN&VERIFY] [ENC&DECRYPT]\n");
|
|
printf("[ MECHANISM ] [ HASH ] [ MGF ] [SALT] [ WORKS ] [ WORKS ]\n");
|
|
P11TEST_DATA_ROW(info, 7,
|
|
's', "KEY ID",
|
|
's', "MECHANISM",
|
|
's', "HASH",
|
|
's', "MGF",
|
|
's', "SALT",
|
|
's', "SIGN&VERIFY WORKS",
|
|
's', "ENCRYPT&DECRYPT WORKS");
|
|
for (i = 0; i < objects.count; i++) {
|
|
test_cert_t *o = &objects.data[i];
|
|
|
|
/* Do not go through incomplete pairs */
|
|
if (o->private_handle == CK_INVALID_HANDLE)
|
|
continue;
|
|
|
|
/* Do not list non-RSA keys here */
|
|
if (o->type != EVP_PK_RSA)
|
|
continue;
|
|
|
|
printf("\n[%-6s] [%s]\n",
|
|
o->id_str,
|
|
o->label);
|
|
printf("[ %s ] [%6lu] [ %s ] [%s%s] [%s%s]\n",
|
|
o->key_type == CKK_RSA ? "RSA " :
|
|
o->key_type == CKK_EC ? " EC " : " ?? ",
|
|
o->bits,
|
|
o->verify_public == 1 ? " ./ " : " ",
|
|
o->sign ? "[./] " : "[ ] ",
|
|
o->verify ? " [./] " : " [ ] ",
|
|
o->encrypt ? "[./] " : "[ ] ",
|
|
o->decrypt ? " [./] " : " [ ] ");
|
|
if (!o->sign && !o->verify && !o->encrypt && !o->decrypt) {
|
|
printf(" no usable attributes found ... ignored\n");
|
|
continue;
|
|
}
|
|
for (j = 0; j < o->num_mechs; j++) {
|
|
test_mech_t *mech = &o->mechs[j];
|
|
printf(" [ %-20s ] [%-6s] [%-11s] [%4d] [ %s ] [ %s ]\n",
|
|
get_mechanism_name(mech->mech),
|
|
get_mechanism_name(mech->hash),
|
|
get_mgf_name(mech->mgf),
|
|
mech->salt,
|
|
mech->result_flags & FLAGS_SIGN_ANY
|
|
? "[./]" : " ",
|
|
mech->result_flags & FLAGS_DECRYPT_ANY
|
|
? "[./]" : " ");
|
|
if ((mech->result_flags & FLAGS_SIGN_ANY) == 0 &&
|
|
(mech->result_flags & FLAGS_DECRYPT_ANY) == 0)
|
|
continue; /* skip empty rows for export */
|
|
P11TEST_DATA_ROW(info, 7,
|
|
's', o->id_str,
|
|
's', get_mechanism_name(mech->mech),
|
|
's', get_mechanism_name(mech->hash),
|
|
's', get_mgf_name(mech->mgf),
|
|
'd', mech->salt,
|
|
's', mech->result_flags & FLAGS_SIGN_ANY
|
|
? "YES" : "",
|
|
's', mech->result_flags & FLAGS_DECRYPT_ANY
|
|
? "YES" : "");
|
|
}
|
|
}
|
|
printf(" Public == Cert ----------^ ^ ^ ^ ^ ^ ^\n");
|
|
printf(" Sign Attribute -------------------------------------------' | | | | |\n");
|
|
printf(" Sign&Verify functionality -----------------------------------' | | | |\n");
|
|
printf(" Verify Attribute -----------------------------------------------' | | |\n");
|
|
printf(" Encrypt Attribute ------------------------------------------------------' | |\n");
|
|
printf(" Encrypt & Decrypt functionality -------------------------------------------' |\n");
|
|
printf(" Decrypt Attribute ------------------------------------------------------------'\n");
|
|
|
|
clean_all_objects(&objects);
|
|
P11TEST_PASS(info);
|
|
}
|