Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Devel/gost #53

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 95 additions & 31 deletions src/common/cert_vfy.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,9 +519,10 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length,
int rv;
EVP_PKEY *pubkey;
EVP_MD_CTX *md_ctx = NULL;
ECDSA_SIG* ec_sig;
int rs_len;
unsigned char *p = NULL;
const EVP_MD* md = NULL;
const char* md_name = NULL;
ASN1_OBJECT *algorithm;
int nid;

/* get the public-key */
pubkey = X509_get_pubkey(x509);
Expand All @@ -533,39 +534,76 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length,
DBG1("public key type: 0x%08x", EVP_PKEY_base_id(pubkey));
DBG1("public key bits: 0x%08x", EVP_PKEY_bits(pubkey));

if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) {
// FIXME: Why not to use d2i_ECDSA_SIG() ???
rs_len = *signature_length / 2;
ec_sig = ECDSA_SIG_new();
BIGNUM *r = BN_bin2bn(*signature, rs_len, NULL);
BIGNUM *s = BN_bin2bn(*signature + rs_len, rs_len, NULL);
if (!r || !s) {
set_error("Unable to parse r+s EC signature numbers: %s",
ERR_error_string(ERR_get_error(), NULL));
return -1;
}
if (1 != ECDSA_SIG_set0(ec_sig, r, s)) {
set_error("Unable to write r+s numbers to the signature structure: %s",
ERR_error_string(ERR_get_error(), NULL));
return -1;
}
*signature_length = i2d_ECDSA_SIG(ec_sig, &p);
free(*signature);
*signature = malloc(*signature_length);
p = *signature;
*signature_length = i2d_ECDSA_SIG(ec_sig, &p);
ECDSA_SIG_free(ec_sig);
X509_PUBKEY_get0_param(&algorithm, NULL, NULL, NULL, X509_get_X509_PUBKEY(x509));
nid = OBJ_obj2nid(algorithm);

switch ( nid ) {
case NID_id_GostR3410_2001:
md_name = SN_id_GostR3411_94;
break;
case NID_id_GostR3410_2012_256:
md_name = SN_id_GostR3411_2012_256;
break;
case NID_id_GostR3410_2012_512:
md_name = SN_id_GostR3411_2012_512;
break;
}

md_ctx = EVP_MD_CTX_new();
/* verify the signature */
if (md_name) {
md = EVP_get_digestbyname(md_name);
if (!md) {
set_error("unsupported digest %s", md_name);
return -1;
}
DBG1("hashing with %s", md_name);
} else {
#ifdef USE_HASH_SHA1
DBG("hashing with SHA1");
EVP_VerifyInit(md_ctx, EVP_sha1());
DBG("hashing with SHA1");
md = EVP_sha1();
#else
DBG("hashing with SHA256");
EVP_VerifyInit(md_ctx, EVP_sha256());
DBG("hashing with SHA256");
md = EVP_sha256();
#endif
}

if (!md) {
set_error("unsupported key algorithm, nid: %d", nid);
return -1;
}

if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) {
// FIXME: Why not to use d2i_ECDSA_SIG() ???
ECDSA_SIG* ec_sig;
int rs_len;
unsigned char *p = NULL;

rs_len = *signature_length / 2;
ec_sig = ECDSA_SIG_new();

BIGNUM *r = BN_bin2bn(*signature, rs_len, NULL);
BIGNUM *s = BN_bin2bn(*signature + rs_len, rs_len, NULL);
if (!r || !s) {
set_error("Unable to parse r+s EC signature numbers: %s",
ERR_error_string(ERR_get_error(), NULL));
return -1;
}
if (1 != ECDSA_SIG_set0(ec_sig, r, s)) {
set_error("Unable to write r+s numbers to the signature structure: %s",
ERR_error_string(ERR_get_error(), NULL));
return -1;
}

*signature_length = i2d_ECDSA_SIG(ec_sig, &p);
free(*signature);
*signature = malloc(*signature_length);
p = *signature;
*signature_length = i2d_ECDSA_SIG(ec_sig, &p);
ECDSA_SIG_free(ec_sig);
}

/* verify the signature */
md_ctx = EVP_MD_CTX_new();
EVP_VerifyInit(md_ctx, md);
EVP_VerifyUpdate(md_ctx, data, data_length);
rv = EVP_VerifyFinal(md_ctx, *signature, *signature_length, pubkey);
EVP_PKEY_free(pubkey);
Expand All @@ -577,4 +615,30 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length,
DBG("signature is valid");
return 0;
}

int verify_eku_sc_logon(X509 * x509)
{
static unsigned char id_kp_sc_logon[] = {0x2b, 6, 1, 4, 1, 0x82, 0x37, 20, 2, 2}; // 1.3.6.1.4.1.311.20.2.2
int rv = 0;
EXTENDED_KEY_USAGE* eku = X509_get_ext_d2i(x509, NID_ext_key_usage, NULL, NULL);
if( NULL != eku )
{
int i = 0, n = sk_ASN1_OBJECT_num(eku);
for( ; i < n; ++i )
{
ASN1_OBJECT* extobj = sk_ASN1_OBJECT_value( eku, i );
if( NULL == extobj )
continue;
if( sizeof(id_kp_sc_logon) == OBJ_length(extobj)
&& 0 == memcmp(OBJ_get0_data(extobj), id_kp_sc_logon, sizeof(id_kp_sc_logon)) )
{
rv = 1;
break;
}
}
EXTENDED_KEY_USAGE_free(eku);
}
return rv;
}

#endif
3 changes: 3 additions & 0 deletions src/common/cert_vfy.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ struct cert_policy_st {
const char *crl_dir;
const char *nss_dir;
int ocsp_policy;
int eku_sc_logon_policy;
};

#ifndef __CERT_VFY_C
Expand Down Expand Up @@ -80,6 +81,8 @@ CERTVFY_EXTERN int verify_certificate(X509 * x509, cert_policy *policy);
*/
CERTVFY_EXTERN int verify_signature(X509 * x509, unsigned char *data, int data_length, unsigned char **signature, unsigned long *signature_length);

CERTVFY_EXTERN int verify_eku_sc_logon(X509 * x509);

#undef CERTVFY_EXTERN

#endif /* __CERT_VFY_H_ */
159 changes: 129 additions & 30 deletions src/common/pkcs11_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
#include "error.h"
#include "cert_info.h"
#include "pkcs11_lib.h"

#include <openssl/conf.h>

/*
* this functions is completely common between both implementation.
Expand Down Expand Up @@ -1757,74 +1757,166 @@ X509 *get_X509_certificate(cert_object_t *cert)
return cert->x509;
}

static int get_pubkey_algo(X509 *x509)
{
ASN1_OBJECT *algorithm = NULL;
int nid;

/* get the public-key */
EVP_PKEY *pubkey = X509_get_pubkey(x509);
if (pubkey == NULL) {
set_error("X509_get_pubkey() failed: %s", ERR_error_string(ERR_get_error(), NULL));
return -1;
}

DBG1("public key type: 0x%08x", EVP_PKEY_base_id(pubkey));
DBG1("public key bits: 0x%08x", EVP_PKEY_bits(pubkey));

X509_PUBKEY_get0_param(&algorithm, NULL, NULL, NULL, X509_get_X509_PUBKEY(x509));
nid = OBJ_obj2nid(algorithm);
EVP_PKEY_free(pubkey);

return nid;
}

#define MAX_SIGNATURE_LENGTH 65536

int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data,
CK_ULONG length, CK_BYTE **signature, CK_ULONG *signature_length)
{
int rv;
int h_offset = 0;
#ifdef USE_HASH_SHA1
CK_BYTE hash[15 + SHA_DIGEST_LENGTH] =
"\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14";
#else
CK_BYTE hash[19 + SHA256_DIGEST_LENGTH] =
"\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20";
#endif
int rv = -1;
unsigned char *h_prefix = NULL;
unsigned long h_offset = 0;
CK_MECHANISM mechanism = { 0, NULL, 0 };

const EVP_MD* md = NULL;
const char *md_name = NULL;
EVP_MD_CTX *md_ctx = NULL;
unsigned char* hash = NULL;

if (get_private_key(h, cert) == -1) {
set_error("Couldn't find private key for certificate");
return -1;
}

*signature_length = 256;

/* set mechanism */
switch (cert->key_type) {
case CKK_RSA:
mechanism.mechanism = CKM_RSA_PKCS;
break;
case CKK_ECDSA:
mechanism.mechanism = CKM_ECDSA;
#ifdef USE_HASH_SHA1
h_prefix = "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14";
h_offset = 15;
#else
h_prefix = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20";
h_offset = 19;
#endif
break;
case CKK_GOSTR3410: {
mechanism.mechanism = CKM_GOSTR3410;
*signature_length = 64;
int nid = get_pubkey_algo(get_X509_certificate(cert));
if (nid < 0) {
set_error("unable to get the public key algorithm");
return -1;
}
switch (nid) {
case NID_id_GostR3410_2001:
md_name = SN_id_GostR3411_94;
break;
case NID_id_GostR3410_2012_256:
md_name = SN_id_GostR3411_2012_256;
break;
default:
set_error("unexpected public key algorithm: 0x%08X", nid);
return -1;
}
}
break;
case CKK_GOSTR3410_512:
mechanism.mechanism = CKM_GOSTR3410_512;
*signature_length = 128;
md_name = SN_id_GostR3411_2012_512;
break;
case CKK_ECDSA:
mechanism.mechanism = CKM_ECDSA;
break;
default:
set_error("unsupported private key type 0x%08X", cert->key_type);
return -1;
}
/* compute hash-value */

if (!md_name) {
#ifdef USE_HASH_SHA1
DBG("hashing with SHA1");
SHA1(data, length, &hash[15]);
DBG5("hash[%ld] = [...:%02x:%02x:%02x:...:%02x]", sizeof(hash),
hash[15], hash[16], hash[17], hash[sizeof(hash) - 1]);
DBG("hashing with SHA1");
md = EVP_sha1();
#else
SHA256(data, length, &hash[19]);
DBG5("hash[%ld] = [...:%02x:%02x:%02x:...:%02x]", sizeof(hash),
hash[19], hash[20], hash[21], hash[sizeof(hash) - 1]);
DBG("hashing with SHA256");
md = EVP_sha256();
#endif
/* sign the token */
} else {
md = EVP_get_digestbyname(md_name);
if (!md) {
set_error("unsupported digest %s", md_name);
return -1;
}
DBG1("hashing with %s", md_name);
}

/* compute hash-value */
md_ctx = EVP_MD_CTX_new();
if (!md_ctx) {
set_error("unable to create a digest context");
goto error;
}
if (1 != EVP_DigestInit (md_ctx, md)) {
set_error("unable to initialize the digest context");
goto error;
}
if (1 != EVP_DigestUpdate (md_ctx, data, length)) {
set_error("unable to update the digest context");
goto error;
}

unsigned int md_size = EVP_MD_size(md);
hash = malloc(h_offset + md_size);

if (!hash) {
set_error("unable to allocate the digest buffer");
goto error;
}
if (1 != EVP_DigestFinal_ex(md_ctx, hash + h_offset, &md_size)) {
set_error("unable to calculate the digest");
goto error;
}

if (h_prefix)
memcpy(hash, h_prefix, h_offset);

DBG5("hash[%u] = [%02x:%02x:%02x:...:%02x]", md_size,
hash[h_offset], hash[h_offset+1], hash[h_offset+2],
hash[h_offset + md_size - 1]);

/* sign the hash */
DBG2("C_SignInit: mech: %lx, keytype: %lx", mechanism.mechanism, cert->key_type);
rv = h->fl->C_SignInit(h->session, &mechanism, cert->private_key);
if (rv != CKR_OK) {
set_error("C_SignInit() failed: %i", rv);
return -1;
goto error;
}

*signature = NULL;
*signature_length = 1024;
while (*signature == NULL) {
CK_ULONG current_signature_length = *signature_length;
*signature = malloc(*signature_length);
if (*signature == NULL) {
set_error("not enough free memory available");
return -1;
set_error("not enough free memory available");
goto error;
}
rv = h->fl->C_Sign(h->session, hash + h_offset, sizeof(hash) - h_offset, *signature, signature_length);
rv = h->fl->C_Sign(h->session, hash, h_offset + md_size, *signature, signature_length);
if (rv == CKR_BUFFER_TOO_SMALL) {
/* increase signature length as long as it it to short */
/* FIXME: Is *signature_length already increased by C_Sign()? */
free(*signature);
*signature = NULL;
if (current_signature_length >= *signature_length) {
Expand All @@ -1841,11 +1933,18 @@ int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data,
free(*signature);
*signature = NULL;
set_error("C_Sign() failed: %i", rv);
return -1;
goto error;
}
}
DBG5("signature[%ld] = [%02x:%02x:%02x:...:%02x]", *signature_length,
(*signature)[0], (*signature)[1], (*signature)[2], (*signature)[*signature_length - 1]);
return 0;

rv = 0;

error:
EVP_MD_CTX_free(md_ctx);
free(hash);

return rv;
}
#endif /* HAVE_NSS */
Loading