From 9dca0ec666884bb6ae2ba6b7cd63354688a64a1f Mon Sep 17 00:00:00 2001 From: Andrey Cherepanov Date: Wed, 12 Apr 2017 12:34:20 +0300 Subject: [PATCH 01/12] Add support for GOST certificates Thanks Max Kosmach for patch --- src/common/cert_vfy.c | 44 ++++++++++++++++++++++++++++++++++--- src/common/cert_vfy.h | 3 +++ src/common/pkcs11_lib.c | 34 ++++++++++++++++++++++------ src/common/rsaref/pkcs11t.h | 20 +++++++++++++++++ src/pam_pkcs11/pam_config.c | 8 +++++++ src/pam_pkcs11/pam_pkcs11.c | 7 +++++- 6 files changed, 105 insertions(+), 11 deletions(-) diff --git a/src/common/cert_vfy.c b/src/common/cert_vfy.c index 6016ca0b..e22bc694 100644 --- a/src/common/cert_vfy.c +++ b/src/common/cert_vfy.c @@ -480,16 +480,28 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, int rv; EVP_PKEY *pubkey; EVP_MD_CTX *md_ctx = NULL; - + const EVP_MD* md; + int nid; /* get the public-key */ pubkey = X509_get_pubkey(x509); if (pubkey == NULL) { set_error("X509_get_pubkey() failed: %s", ERR_error_string(ERR_get_error(), NULL)); return -1; } - md_ctx = EVP_MD_CTX_new(); + + nid = OBJ_obj2nid(x509->cert_info->key->algor->algorithm); + if( NID_id_GostR3410_2001 == nid ) + md = EVP_get_digestbyname("md_gost94"); + else + md = EVP_sha1(); + if (!md) { + set_error("unsupported key algorithm, nid: %d", nid); + return -1; + } + + md_ctx = EVP_MD_CTX_create(); /* verify the signature */ - EVP_VerifyInit(md_ctx, EVP_sha1()); + 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); @@ -501,4 +513,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) == extobj->length + && 0 == memcmp(extobj->data, id_kp_sc_logon, sizeof(id_kp_sc_logon)) ) + { + rv = 1; + break; + } + } + EXTENDED_KEY_USAGE_free(eku); + } + return rv; +} + #endif diff --git a/src/common/cert_vfy.h b/src/common/cert_vfy.h index 657b212d..335bc44d 100644 --- a/src/common/cert_vfy.h +++ b/src/common/cert_vfy.h @@ -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 @@ -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, int signature_length); +CERTVFY_EXTERN int verify_eku_sc_logon(X509 * x509); + #undef CERTVFY_EXTERN #endif /* __CERT_VFY_H_ */ diff --git a/src/common/pkcs11_lib.c b/src/common/pkcs11_lib.c index a291baa9..e29d3882 100644 --- a/src/common/pkcs11_lib.c +++ b/src/common/pkcs11_lib.c @@ -32,7 +32,7 @@ #include "error.h" #include "cert_info.h" #include "pkcs11_lib.h" - +#include /* * this functions is completely common between both implementation. @@ -985,6 +985,7 @@ int crypto_init(cert_policy *policy) { /* arg is ignored for OPENSSL */ (void)policy; + OPENSSL_config(NULL); OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); return 0; @@ -1677,6 +1678,7 @@ cert_object_t **get_certificate_list(pkcs11_handle_t *h, int *ncerts) int get_private_key(pkcs11_handle_t *h, cert_object_t *cert) { CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; CK_BBOOL key_sign = CK_TRUE; + CK_ATTRIBUTE attr; CK_ATTRIBUTE key_template[] = { {CKA_CLASS, &key_class, sizeof(key_class)} , @@ -1724,7 +1726,16 @@ int get_private_key(pkcs11_handle_t *h, cert_object_t *cert) { } cert->private_key = object; - cert->key_type = CKK_RSA; + attr.type = CKA_KEY_TYPE; + attr.ulValueLen = sizeof(cert->key_type); + attr.pValue = &(cert->key_type); + rv = h->fl->C_GetAttributeValue(h->session, object, &attr,1); + if (rv != CKR_OK) { + set_error("C_GetAttributeValue() failed: 0x%08lX", rv); + return -1; + } + + DBG1("C_GetAttributeValue keytype: %x",cert->key_type); return 0; @@ -1765,29 +1776,38 @@ int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, case CKK_RSA: mechanism.mechanism = CKM_RSA_PKCS; break; + case CKK_GOSTR3410: + mechanism.mechanism = CKM_GOSTR3410_WITH_GOSTR3411; + break; default: set_error("unsupported key type %d", cert->type); return -1; } /* compute hash-value */ - SHA1(data, length, &hash[15]); - DBG5("hash[%ld] = [...:%02x:%02x:%02x:...:%02x]", sizeof(hash), - hash[15], hash[16], hash[17], hash[sizeof(hash) - 1]); + if( CKK_RSA == cert->key_type ) { + SHA1(data, length, &hash[15]); + DBG5("hash[%ld] = [...:%02x:%02x:%02x:...:%02x]", sizeof(hash), + hash[15], hash[16], hash[17], hash[sizeof(hash) - 1]); + } /* sign the token */ + DBG2("C_SignInit: mech: %x, keytype: %x", 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: 0x%08lX", rv); return -1; } *signature = NULL; - *signature_length = 128; + *signature_length = 256; while (*signature == NULL) { *signature = malloc(*signature_length); if (*signature == NULL) { set_error("not enough free memory available"); return -1; } - rv = h->fl->C_Sign(h->session, hash, sizeof(hash), *signature, signature_length); + if( CKK_RSA == cert->key_type ) + rv = h->fl->C_Sign(h->session, hash, sizeof(hash), *signature, signature_length); + else + rv = h->fl->C_Sign(h->session, data, length, *signature, signature_length); if (rv == CKR_BUFFER_TOO_SMALL) { /* increase signature length as long as it it to short */ free(*signature); diff --git a/src/common/rsaref/pkcs11t.h b/src/common/rsaref/pkcs11t.h index a80482bf..37375eab 100644 --- a/src/common/rsaref/pkcs11t.h +++ b/src/common/rsaref/pkcs11t.h @@ -383,6 +383,11 @@ typedef CK_ULONG CK_KEY_TYPE; #define CKK_CDMF 0x0000001E #define CKK_AES 0x0000001F +/* Elvis */ +#define CKK_GOSTR3410 0x00000030 +#define CKK_GOSTR3411 0x00000031 +#define CKK_GOST28147 0x00000032 + #define CKK_VENDOR_DEFINED 0x80000000 @@ -774,6 +779,21 @@ typedef CK_ULONG CK_MECHANISM_TYPE; #define CKM_DH_PKCS_PARAMETER_GEN 0x00002001 #define CKM_X9_42_DH_PARAMETER_GEN 0x00002002 +/* Elvis */ +#define CKM_GOSTR3410_KEY_PAIR_GEN 0x00001200 +#define CKM_GOSTR3410 0x00001201 +#define CKM_GOSTR3410_WITH_GOSTR3411 0x00001202 +#define CKM_GOSTR3410_KEY_WRAP 0x00001203 +#define CKM_GOSTR3410_DERIVE 0x00001204 +#define CKM_GOSTR3411 0x00001210 +#define CKM_GOSTR3411_HMAC 0x00001211 +#define CKM_GOST28147_KEY_GEN 0x00001220 +#define CKM_GOST28147_ECB 0x00001221 +#define CKM_GOST28147 0x00001222 +#define CKM_GOST28147_MAC 0x00001223 +#define CKM_GOST28147_KEY_WRAP 0x00001224 + + #define CKM_VENDOR_DEFINED 0x80000000 typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR; diff --git a/src/pam_pkcs11/pam_config.c b/src/pam_pkcs11/pam_config.c index 6739deff..f8fd0606 100644 --- a/src/pam_pkcs11/pam_config.c +++ b/src/pam_pkcs11/pam_config.c @@ -90,6 +90,7 @@ static void display_config (void) { DBG1("signature_policy %d",configuration.policy.signature_policy); DBG1("ocsp_policy %d",configuration.policy.ocsp_policy); DBG1("err_display_time %d", configuration.err_display_time); + DBG1("eku_sc_logon_policy %d",configuration.policy.eku_sc_logon_policy); } #endif @@ -186,6 +187,7 @@ static void parse_config_file(void) { configuration.policy.ocsp_policy=OCSP_NONE; configuration.policy.ca_policy=0; configuration.policy.signature_policy=0; + configuration.policy.eku_sc_logon_policy=0; break; } else if ( !strcmp(policy_list->data,"crl_auto") ) { configuration.policy.crl_policy=CRLP_AUTO; @@ -199,6 +201,8 @@ static void parse_config_file(void) { configuration.policy.ca_policy=1; } else if ( !strcmp(policy_list->data,"signature") ) { configuration.policy.signature_policy=1; + } else if ( !strcmp(policy_list->data,"eku_sclogon") ) { + configuration.policy.eku_sc_logon_policy=1; } else { DBG1("Invalid CRL policy: %s",policy_list->data); } @@ -324,6 +328,7 @@ struct configuration_st *pk_configure( int argc, const char **argv ) { configuration.policy.ca_policy=0; configuration.policy.signature_policy=0; configuration.policy.ocsp_policy=OCSP_NONE; + configuration.policy.eku_sc_logon_policy=0; } if (strstr(argv[i],"crl_online")) { configuration.policy.crl_policy=CRLP_ONLINE; @@ -343,6 +348,9 @@ struct configuration_st *pk_configure( int argc, const char **argv ) { if (strstr(argv[i],"signature")) { configuration.policy.signature_policy=1; } + if (strstr(argv[i],"eku_sclogon")) { + configuration.policy.eku_sc_logon_policy=1; + } continue; } diff --git a/src/pam_pkcs11/pam_pkcs11.c b/src/pam_pkcs11/pam_pkcs11.c index 3f2b6abb..5b2893dd 100644 --- a/src/pam_pkcs11/pam_pkcs11.c +++ b/src/pam_pkcs11/pam_pkcs11.c @@ -579,7 +579,12 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, cons if (!configuration->quiet) { pam_prompt(pamh, PAM_TEXT_INFO, NULL, _("verifying certificate")); } - + if (configuration->policy.eku_sc_logon_policy) { + if (!verify_eku_sc_logon(x509)) { + DBG("Certificate does not contain EKU Smart Card Logon"); + continue; /* try next certificate */ + } + } /* verify certificate (date, signature, CRL, ...) */ rv = verify_certificate(x509,&configuration->policy); if (rv < 0) { From 3a9f7f9063918a51b54ff8c235620e2fa31db4b8 Mon Sep 17 00:00:00 2001 From: Paul Wolneykien Date: Fri, 1 Sep 2017 13:25:57 +0300 Subject: [PATCH 02/12] Rely on 'sslconf' patch for OpenSSL config --- src/common/pkcs11_lib.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/pkcs11_lib.c b/src/common/pkcs11_lib.c index e29d3882..e8c63d86 100644 --- a/src/common/pkcs11_lib.c +++ b/src/common/pkcs11_lib.c @@ -985,7 +985,6 @@ int crypto_init(cert_policy *policy) { /* arg is ignored for OPENSSL */ (void)policy; - OPENSSL_config(NULL); OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); return 0; From 065ddcfba8c32e93af813143b192c4ffb99e5eed Mon Sep 17 00:00:00 2001 From: Paul Wolneykien Date: Thu, 9 Jul 2020 19:06:51 +0300 Subject: [PATCH 03/12] Update for OpenSSL 1.1 (thx Oleg Solovyov) See 3b88db14e4a6a3e2e6e11cd274d054e1a92956b5. --- src/common/cert_vfy.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/common/cert_vfy.c b/src/common/cert_vfy.c index e22bc694..b102e706 100644 --- a/src/common/cert_vfy.c +++ b/src/common/cert_vfy.c @@ -482,6 +482,7 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, EVP_MD_CTX *md_ctx = NULL; const EVP_MD* md; int nid; + ASN1_OBJECT **algorithm; /* get the public-key */ pubkey = X509_get_pubkey(x509); if (pubkey == NULL) { @@ -489,7 +490,8 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, return -1; } - nid = OBJ_obj2nid(x509->cert_info->key->algor->algorithm); + X509_PUBKEY_get0_param(&algorithm, NULL, NULL, NULL, X509_get_X509_PUBKEY(x509)); + nid = OBJ_obj2nid(algorithm); if( NID_id_GostR3410_2001 == nid ) md = EVP_get_digestbyname("md_gost94"); else @@ -527,8 +529,8 @@ int verify_eku_sc_logon(X509 * x509) ASN1_OBJECT* extobj = sk_ASN1_OBJECT_value( eku, i ); if( NULL == extobj ) continue; - if( sizeof(id_kp_sc_logon) == extobj->length - && 0 == memcmp(extobj->data, id_kp_sc_logon, sizeof(id_kp_sc_logon)) ) + 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; From 87ac5256ad6f46dbafffbb4aada60b0a65327ec6 Mon Sep 17 00:00:00 2001 From: Paul Wolneykien Date: Fri, 14 Aug 2020 17:00:27 +0300 Subject: [PATCH 04/12] Fixed #endif for SHA md types --- src/common/cert_vfy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/cert_vfy.c b/src/common/cert_vfy.c index 9ac78695..fe134e22 100644 --- a/src/common/cert_vfy.c +++ b/src/common/cert_vfy.c @@ -532,7 +532,7 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, #else DBG("hashing with SHA256"); md = EVP_sha256(); - +#endif if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) { ECDSA_SIG* ec_sig; int rs_len; From 356cde9406e2a4ff013e5dee1de2e24659d6ead0 Mon Sep 17 00:00:00 2001 From: Paul Wolneykien Date: Fri, 14 Aug 2020 17:12:09 +0300 Subject: [PATCH 05/12] Fixed ASN.1 object reference type error Signed-off-by: Paul Wolneykien --- src/common/cert_vfy.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common/cert_vfy.c b/src/common/cert_vfy.c index fe134e22..0251db3a 100644 --- a/src/common/cert_vfy.c +++ b/src/common/cert_vfy.c @@ -506,7 +506,12 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, EVP_MD_CTX *md_ctx = NULL; const EVP_MD* md = NULL; int nid; + +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) ASN1_OBJECT **algorithm; +#else + ASN1_OBJECT *algorithm; +#endif /* get the public-key */ pubkey = X509_get_pubkey(x509); From 0002cc47379baa7e878cd1ab10512bc03baac678 Mon Sep 17 00:00:00 2001 From: Paul Wolneykien Date: Sat, 15 Aug 2020 22:34:39 +0300 Subject: [PATCH 06/12] Fix: Remove unused `attr` in `get_private_key()` --- src/common/pkcs11_lib.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/pkcs11_lib.c b/src/common/pkcs11_lib.c index f4752fc9..f414e7e7 100644 --- a/src/common/pkcs11_lib.c +++ b/src/common/pkcs11_lib.c @@ -1675,7 +1675,6 @@ cert_object_t **get_certificate_list(pkcs11_handle_t *h, int *ncerts) int get_private_key(pkcs11_handle_t *h, cert_object_t *cert) { CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; CK_BBOOL key_sign = CK_TRUE; - CK_ATTRIBUTE attr; CK_ATTRIBUTE key_template[] = { {CKA_CLASS, &key_class, sizeof(key_class)} , From e0f85c4c91880dfe70788919bc62b6b237c44fbb Mon Sep 17 00:00:00 2001 From: Paul Wolneykien Date: Sat, 15 Aug 2020 22:39:00 +0300 Subject: [PATCH 07/12] Fixed the debug format used for mechanism and key type values --- src/common/pkcs11_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/pkcs11_lib.c b/src/common/pkcs11_lib.c index f414e7e7..69255232 100644 --- a/src/common/pkcs11_lib.c +++ b/src/common/pkcs11_lib.c @@ -1812,7 +1812,7 @@ int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, } /* sign the token */ - DBG2("C_SignInit: mech: %x, keytype: %x", mechanism.mechanism, cert->key_type); + 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: 0x%08lX", rv); From b8863808958b809ef13b7f511544dd6501924ea7 Mon Sep 17 00:00:00 2001 From: Paul Wolneykien Date: Wed, 2 Sep 2020 18:40:09 +0300 Subject: [PATCH 08/12] Added support for GOST-2012 --- src/common/cert_vfy.c | 60 ++++++++------ src/common/pkcs11_lib.c | 158 ++++++++++++++++++++++++++++-------- src/common/rsaref/pkcs11t.h | 6 +- 3 files changed, 163 insertions(+), 61 deletions(-) diff --git a/src/common/cert_vfy.c b/src/common/cert_vfy.c index 0251db3a..3474bf35 100644 --- a/src/common/cert_vfy.c +++ b/src/common/cert_vfy.c @@ -505,13 +505,9 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, EVP_PKEY *pubkey; EVP_MD_CTX *md_ctx = NULL; const EVP_MD* md = NULL; - int nid; - -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - ASN1_OBJECT **algorithm; -#else + const char* md_name = NULL; ASN1_OBJECT *algorithm; -#endif + int nid; /* get the public-key */ pubkey = X509_get_pubkey(x509); @@ -528,9 +524,24 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, switch ( nid ) { case NID_id_GostR3410_2001: - md = EVP_get_digestbyname("md_gost94"); + 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; - default: + } + + 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"); md = EVP_sha1(); @@ -538,22 +549,6 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, DBG("hashing with SHA256"); md = EVP_sha256(); #endif - if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) { - ECDSA_SIG* ec_sig; - int rs_len; - unsigned char *p = NULL; - - rs_len = *signature_length / 2; - ec_sig = ECDSA_SIG_new(); - BN_bin2bn(*signature, rs_len, ECDSA_SIG_get0_r(ec_sig)); - BN_bin2bn(*signature + rs_len, rs_len, ECDSA_SIG_get0_s(ec_sig)); - *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); - } } if (!md) { @@ -561,6 +556,23 @@ int verify_signature(X509 * x509, unsigned char *data, int data_length, return -1; } + if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) { + ECDSA_SIG* ec_sig; + int rs_len; + unsigned char *p = NULL; + + rs_len = *signature_length / 2; + ec_sig = ECDSA_SIG_new(); + BN_bin2bn(*signature, rs_len, ECDSA_SIG_get0_r(ec_sig)); + BN_bin2bn(*signature + rs_len, rs_len, ECDSA_SIG_get0_s(ec_sig)); + *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); diff --git a/src/common/pkcs11_lib.c b/src/common/pkcs11_lib.c index 69255232..fbbd086c 100644 --- a/src/common/pkcs11_lib.c +++ b/src/common/pkcs11_lib.c @@ -1756,82 +1756,161 @@ const 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; +} + 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_GOSTR3410: - mechanism.mechanism = CKM_GOSTR3410_WITH_GOSTR3411; - 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; + 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; + *signature_length = 512; + 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; } - if( CKK_GOSTR3410 != cert->key_type ) { - /* 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]); + 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 + } 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); - /* sign the token */ + 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: 0x%08lX", rv); - return -1; + goto error; } + *signature = NULL; - *signature_length = 256; while (*signature == NULL) { *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; } - if( CKK_GOSTR3410 != cert->key_type ) - rv = h->fl->C_Sign(h->session, hash + h_offset, sizeof(hash) - h_offset, *signature, signature_length); - else - rv = h->fl->C_Sign(h->session, data, length, *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; DBG1("increased signature buffer-length to %ld", *signature_length); @@ -1839,11 +1918,18 @@ int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, free(*signature); *signature = NULL; set_error("C_Sign() failed: 0x%08lX", 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 */ diff --git a/src/common/rsaref/pkcs11t.h b/src/common/rsaref/pkcs11t.h index 37375eab..9281da25 100644 --- a/src/common/rsaref/pkcs11t.h +++ b/src/common/rsaref/pkcs11t.h @@ -383,10 +383,14 @@ typedef CK_ULONG CK_KEY_TYPE; #define CKK_CDMF 0x0000001E #define CKK_AES 0x0000001F -/* Elvis */ +#define CK_VENDOR_PKCS11_RU_TEAM_TC26 0xD4321000 /* 0x80000000 | 0x54321000 */ + +/* Elvis, BaseALT */ #define CKK_GOSTR3410 0x00000030 #define CKK_GOSTR3411 0x00000031 #define CKK_GOST28147 0x00000032 +#define CKK_GOSTR3410_256 CKK_GOSTR3410 +#define CKK_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TC26 | 0x003) #define CKK_VENDOR_DEFINED 0x80000000 From 0821aad06fd94de92b88435ac15002c9a283dd57 Mon Sep 17 00:00:00 2001 From: Paul Wolneykien Date: Thu, 3 Sep 2020 13:19:43 +0300 Subject: [PATCH 09/12] Fixed RSA signature with prefix --- src/common/pkcs11_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/pkcs11_lib.c b/src/common/pkcs11_lib.c index fbbd086c..4a010f36 100644 --- a/src/common/pkcs11_lib.c +++ b/src/common/pkcs11_lib.c @@ -1908,7 +1908,7 @@ int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, set_error("not enough free memory available"); goto error; } - rv = h->fl->C_Sign(h->session, hash + h_offset, md_size, *signature, signature_length); + rv = h->fl->C_Sign(h->session, hash, h_offset + md_size, *signature, signature_length); if (rv == CKR_BUFFER_TOO_SMALL) { /* FIXME: Is *signature_length already increased by C_Sign()? */ free(*signature); From 5e553db1e4f55fd3a21ed4ffa8065c0a9295bd89 Mon Sep 17 00:00:00 2001 From: Paul Wolneykien Date: Thu, 3 Sep 2020 13:20:36 +0300 Subject: [PATCH 10/12] Fix: Use CKM_GOSTR3410_512 for GOST.R-3410-2012 --- src/common/pkcs11_lib.c | 2 +- src/common/rsaref/pkcs11t.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/pkcs11_lib.c b/src/common/pkcs11_lib.c index 4a010f36..47643011 100644 --- a/src/common/pkcs11_lib.c +++ b/src/common/pkcs11_lib.c @@ -1830,8 +1830,8 @@ int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, } break; case CKK_GOSTR3410_512: - mechanism.mechanism = CKM_GOSTR3410; *signature_length = 512; + mechanism.mechanism = CKM_GOSTR3410_512; md_name = SN_id_GostR3411_2012_512; break; case CKK_ECDSA: diff --git a/src/common/rsaref/pkcs11t.h b/src/common/rsaref/pkcs11t.h index 9281da25..0fe1798b 100644 --- a/src/common/rsaref/pkcs11t.h +++ b/src/common/rsaref/pkcs11t.h @@ -796,6 +796,7 @@ typedef CK_ULONG CK_MECHANISM_TYPE; #define CKM_GOST28147 0x00001222 #define CKM_GOST28147_MAC 0x00001223 #define CKM_GOST28147_KEY_WRAP 0x00001224 +#define CKM_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TC26 | 0x006) #define CKM_VENDOR_DEFINED 0x80000000 From f20f636d497afc795f70e084feda5afd1e82f6c3 Mon Sep 17 00:00:00 2001 From: Paul Wolneykien Date: Thu, 3 Sep 2020 13:21:34 +0300 Subject: [PATCH 11/12] Use 64 and 128 byte signatures for GOST --- src/common/pkcs11_lib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/pkcs11_lib.c b/src/common/pkcs11_lib.c index 47643011..e24ae382 100644 --- a/src/common/pkcs11_lib.c +++ b/src/common/pkcs11_lib.c @@ -1811,6 +1811,7 @@ int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, 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"); @@ -1830,8 +1831,8 @@ int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, } break; case CKK_GOSTR3410_512: - *signature_length = 512; mechanism.mechanism = CKM_GOSTR3410_512; + *signature_length = 128; md_name = SN_id_GostR3411_2012_512; break; case CKK_ECDSA: From 4091bb28edd52af2d4959c026fe82240c0a806ac Mon Sep 17 00:00:00 2001 From: Paul Wolneykien Date: Thu, 3 Sep 2020 13:24:49 +0300 Subject: [PATCH 12/12] Output the hash value without initial "..." for debug --- src/common/pkcs11_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/pkcs11_lib.c b/src/common/pkcs11_lib.c index e24ae382..c1a2d76f 100644 --- a/src/common/pkcs11_lib.c +++ b/src/common/pkcs11_lib.c @@ -1890,7 +1890,7 @@ int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, if (h_prefix) memcpy(hash, h_prefix, h_offset); - DBG5("hash[%u] = [...:%02x:%02x:%02x:...:%02x]", md_size, + 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]);