From a2d613ade54068d7ea44ecce9718023ca53eefde Mon Sep 17 00:00:00 2001 From: guowentao Date: Fri, 16 Jun 2023 11:39:29 +0800 Subject: [PATCH] add SM2 sign function for openssl engine Signed-off-by: guowentao --- src/tpm2-tss-engine-common.h | 1 + src/tpm2-tss-engine-ecc.c | 8 ++ src/tpm2-tss-engine-err.h | 1 + src/tpm2-tss-engine-sm2.c | 241 +++++++++++++++++++++++++++++++++++ src/tpm2-tss-engine.c | 7 +- 5 files changed, 257 insertions(+), 1 deletion(-) diff --git a/src/tpm2-tss-engine-common.h b/src/tpm2-tss-engine-common.h index 14c98a0..e15e9bd 100755 --- a/src/tpm2-tss-engine-common.h +++ b/src/tpm2-tss-engine-common.h @@ -51,6 +51,7 @@ extern char *tcti_nameconf; int init_ecc(ENGINE *e); int init_rand(ENGINE *e); int init_rsa(ENGINE *e); +int init_sm2(ENGINE *e); TSS2_RC esys_ctx_init (ESYS_CONTEXT **esys_ctx); diff --git a/src/tpm2-tss-engine-ecc.c b/src/tpm2-tss-engine-ecc.c index c1c5864..ec1882f 100644 --- a/src/tpm2-tss-engine-ecc.c +++ b/src/tpm2-tss-engine-ecc.c @@ -300,6 +300,9 @@ populate_ecc(EC_KEY *key) case TPM2_ECC_NIST_P384: nid = EC_curve_nist2nid("P-384"); break; + case TPM2_ECC_SM2_P256: + nid = NID_sm2; + break; default: nid = -1; } @@ -393,6 +396,11 @@ tpm2tss_ecc_makekey(TPM2_DATA *tpm2Data) goto error; } + if (!tpm2tss_sm2_setappdata(eckey, tpm2Data)) { + ERR(tpm2tss_ecc_makekey, TPM2TSS_R_GENERAL_FAILURE); + goto error; + } + if (!populate_ecc(eckey)) goto error; diff --git a/src/tpm2-tss-engine-err.h b/src/tpm2-tss-engine-err.h index e6a7219..f9e0513 100644 --- a/src/tpm2-tss-engine-err.h +++ b/src/tpm2-tss-engine-err.h @@ -98,6 +98,7 @@ void ERR_error(int function, int reason, const char *file, int line); /* tpm2-tss-engine-rsa.c */ #define TPM2TSS_F_tpm2tss_sm2_genkey 144 #define TPM2TSS_F_populate_sm2 145 +#define TPM2TSS_F_tpm_pkey_sm2_sign 146 /* Reason codes */ #define TPM2TSS_R_TPM2DATA_READ_FAILED 100 diff --git a/src/tpm2-tss-engine-sm2.c b/src/tpm2-tss-engine-sm2.c index 679b36c..8a3908f 100644 --- a/src/tpm2-tss-engine-sm2.c +++ b/src/tpm2-tss-engine-sm2.c @@ -43,6 +43,10 @@ static int ec_sm2_key_app_data = -1; +static int (*sm2_pkey_orig_sign)(EVP_PKEY_CTX * ctx, unsigned char * sig, size_t * siglen, const unsigned char * tbs, size_t tbslen); +static int (*sm2_pkey_orig_verify)(EVP_PKEY_CTX * ctx, const unsigned char * sig, size_t siglen, const unsigned char * tbs, size_t tbslen); +static int (*sm2_pkey_orig_digest_custom)(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx); + static TPM2B_DATA allOutsideInfo = { .size = 0, }; @@ -84,6 +88,181 @@ static TPM2B_PUBLIC keyEcTemplate = { } }; +static int +tpm_pkey_sm2_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen) +{ + DBG("[%s][IN] ctx:%p sig:%p siglen:%ld tbs:%p tbslen:%ld\n", __func__, ctx, sig, *siglen, tbs, tbslen); + ECDSA_SIG *s = NULL; + int sigleni; + BIGNUM *bns = NULL, *bnr = NULL; + + EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx); + EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey); + const int sig_sz = ECDSA_size(eckey); + TPM2_DATA *tpm2Data = tpm2tss_sm2_getappdata(eckey); + TPM2_ALG_ID hash_alg; + + /* If this is not a TPM2 key, fall through to software functions */ + if (tpm2Data == NULL) { + DBG("origin sign funciton is called\n"); + sm2_pkey_orig_sign(ctx, sig, siglen, tbs, tbslen); + } + DBG("tbs content (size=%ld):\n", tbslen); + DBGBUF(tbs, (int)tbslen); + + TSS2_RC r = -1; + ESYS_CONTEXT *esys_ctx = NULL; + ESYS_TR keyHandle = ESYS_TR_NONE; + TPMT_SIGNATURE *sig_tpm = NULL; + + TPMT_TK_HASHCHECK validation = { .tag = TPM2_ST_HASHCHECK, + .hierarchy = TPM2_RH_NULL, + .digest.size = 0 }; + + TPMT_SIG_SCHEME inScheme = { .scheme = TPM2_ALG_SM2 }; + + if (sig_sz <= 0) { + return 0; + } + + if (sig == NULL) { + *siglen = (size_t)sig_sz; + DBG("siglen is %ld\n", *siglen); + return 1; + } + + if (*siglen < (size_t)sig_sz) { + DBG("error:SM2_F_PKEY_SM2_SIGN, SM2_R_BUFFER_TOO_SMALL"); + return 0; + } + /* + * ECDSA signatures truncate the incoming hash to fit the curve, + * and the signature mechanism is the same regardless of the + * hash being used. + * + * The TPM bizarrely wants to be told the hash algorithm, and + * either it or the TSS will validate that the digest length + * matches the hash that it's told, despite it having no business + * caring about such things. + * + * So, we can truncate the digest any pretend it's any smaller + * digest that the TPM actually does support, as long as that + * digest is larger than the size of the curve. + */ + int curve_len = (EC_GROUP_order_bits(EC_KEY_get0_group(eckey)) + 7) / 8; + /* If we couldn't work it out, don't truncate */ + if (!curve_len) + curve_len = tbslen; + if (tbslen == SHA256_DIGEST_LENGTH || + (curve_len <= SHA256_DIGEST_LENGTH && tbslen > SHA256_DIGEST_LENGTH)) { + hash_alg = TPM2_ALG_SM3_256; + inScheme.details.ecdsa.hashAlg = hash_alg; + tbslen = SHA256_DIGEST_LENGTH; + } else { + ERR(tpm_pkey_sm2_sign, TPM2TSS_R_PADDING_UNKNOWN); + goto error; + } + DBG("hash alg is %d\n", hash_alg); + TPM2B_DIGEST digest = { .size = tbslen }; + if (digest.size > sizeof(digest.buffer)) { + ERR(ecdsa_sign, TPM2TSS_R_DIGEST_TOO_LARGE); + goto error; + } + memcpy(&digest.buffer[0], tbs, tbslen); + + r = init_tpm_key(&esys_ctx, &keyHandle, tpm2Data); + ERRchktss(tpm_pkey_sm2_sign, r, goto error); + + r = Esys_Sign(esys_ctx, keyHandle, + ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, + &digest, &inScheme, &validation, &sig_tpm); + ERRchktss(tpm_pkey_sm2_sign, r, goto error); + + s = ECDSA_SIG_new(); + if (s == NULL) { + ERR(ecdsa_sign, ERR_R_MALLOC_FAILURE); + goto error; + } + + bns = BN_bin2bn(&sig_tpm->signature.ecdsa.signatureS.buffer[0], + sig_tpm->signature.ecdsa.signatureS.size, NULL); + bnr = BN_bin2bn(&sig_tpm->signature.ecdsa.signatureR.buffer[0], + sig_tpm->signature.ecdsa.signatureR.size, NULL); + if (!bns || !bnr) { + ERR(tpm_pkey_sm2_sign, ERR_R_MALLOC_FAILURE); + goto error; + } + + ECDSA_SIG_set0(s, bnr, bns); + + #ifndef NDEBUG + unsigned char *sig_debug = sig; + #endif + sigleni = i2d_ECDSA_SIG(s, &sig); + if (sigleni < 0) { + ERR(tpm_pkey_sm2_sign, ERR_R_MALLOC_FAILURE); + goto error; + } + *siglen = (unsigned int)sigleni; + DBG("sig content (size=%ld):\n", *siglen); + DBGBUF(sig_debug, *siglen); + DBG("[%s][OUT] ctx:%p sig:%p siglen:%ld tbs:%p tbslen:%ld\n", __func__, ctx, sig, *siglen, tbs, tbslen); + + goto out; +error: + r = -1; +out: + if (keyHandle != ESYS_TR_NONE) { + if (tpm2Data->privatetype == KEY_TYPE_HANDLE) { + Esys_TR_Close(esys_ctx, &keyHandle); + } else { + Esys_FlushContext(esys_ctx, keyHandle); + } + } + if (r != TSS2_RC_SUCCESS && s != NULL) { + if (bns) + BN_free(bns); + if (bnr) + BN_free(bnr); + } + if (r != TSS2_RC_SUCCESS) { + if (s) + ECDSA_SIG_free(s); + r = -1; + } + + esys_ctx_free(&esys_ctx); + return (r == TSS2_RC_SUCCESS) ? 1 : r; +} + +static int +tpm_pkey_sm2_verify(EVP_PKEY_CTX * ctx, const unsigned char * sig, size_t siglen, const unsigned char * tbs, size_t tbslen) +{ + int ret = 0; + DBG("[%s] ctx:%p sig:%p siglen:%ld tbs:%p tbslen:%ld\n", __func__, ctx, sig, siglen, tbs, tbslen); + DBG("tbs content (size=%ld):\n", tbslen); + DBGBUF(tbs, (int)tbslen); + DBG("sig content (size=%ld):\n", siglen); + DBGBUF(sig, siglen); + if(sm2_pkey_orig_verify != NULL) + ret = sm2_pkey_orig_verify(ctx, sig, siglen, tbs, tbslen); + DBG("[%s] ret is %d\n", __func__, ret); + return ret; +} + +/* called for digest & sign init, after message digest algorithm set */ +static int +tpm_pkey_sm2_digest_custom(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx) +{ + DBG("[%s] ctx:%p mctx:%p \n", __func__, ctx, mctx); + int ret = 0; + if(sm2_pkey_orig_digest_custom != NULL) + ret = sm2_pkey_orig_digest_custom(ctx, mctx); + DBG("[%s] ret is %d\n", __func__, ret); + return ret; +} + /** Retrieve app data * * Since the ECC api (opposed to the RSA api) does not provide a standardized @@ -96,6 +275,11 @@ static TPM2B_PUBLIC keyEcTemplate = { TPM2_DATA * tpm2tss_sm2_getappdata(EC_KEY *key) { + if (ec_sm2_key_app_data == -1) { + DBG("Module uninitialized\n"); + return NULL; + } + return EC_KEY_get_ex_data(key, ec_sm2_key_app_data); } @@ -112,6 +296,11 @@ tpm2tss_sm2_getappdata(EC_KEY *key) int tpm2tss_sm2_setappdata(EC_KEY *key, TPM2_DATA *tpm2Data) { + if (ec_sm2_key_app_data == -1) { + DBG("Module uninitialized\n"); + return 0; + } + return EC_KEY_set_ex_data(key, ec_sm2_key_app_data, tpm2Data); } @@ -182,6 +371,23 @@ populate_sm2(EC_KEY *key) return 1; } +static void +free_sm2_appdata(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, + long argl, void *argp) +{ + TPM2_DATA *tpm2Data = ptr; + + (void)parent; + (void)ad; + (void)idx; + (void)argl; + (void)argp; + + if (!ptr) + return; + + OPENSSL_free(tpm2Data); +} /** Generate a tpm2tss sm2 key object. * @@ -287,3 +493,38 @@ tpm2tss_sm2_genkey(EC_KEY *key, TPMI_ECC_CURVE curve, const char *password, return (r == TSS2_RC_SUCCESS); } +int +init_sm2(ENGINE *e) +{ + (void)(e); + + if (ec_sm2_key_app_data == -1) + ec_sm2_key_app_data = EC_KEY_get_ex_new_index(0, NULL, NULL, NULL, free_sm2_appdata); + + EVP_PKEY_METHOD *pkey_sm2_methods; + + pkey_sm2_methods = EVP_PKEY_meth_new(EVP_PKEY_SM2, 0); + if (pkey_sm2_methods == NULL) + return 0; + + const EVP_PKEY_METHOD *pkey_orig_sm2_methods = + EVP_PKEY_meth_find(EVP_PKEY_SM2); + if (pkey_orig_sm2_methods == NULL) + return 0; + EVP_PKEY_meth_copy(pkey_sm2_methods, pkey_orig_sm2_methods); + /* + * save originals since we only override some of the pkey + * functionality, rather than reimplementing all of it + */ + EVP_PKEY_meth_get_sign(pkey_sm2_methods, NULL, &sm2_pkey_orig_sign); + EVP_PKEY_meth_get_verify(pkey_sm2_methods, NULL, &sm2_pkey_orig_verify); + EVP_PKEY_meth_get_digest_custom((EVP_PKEY_METHOD *)pkey_orig_sm2_methods, &sm2_pkey_orig_digest_custom); + + EVP_PKEY_meth_set_sign(pkey_sm2_methods, NULL, tpm_pkey_sm2_sign); + EVP_PKEY_meth_set_verify(pkey_sm2_methods, NULL, tpm_pkey_sm2_verify); + EVP_PKEY_meth_set_digest_custom(pkey_sm2_methods, tpm_pkey_sm2_digest_custom); + EVP_PKEY_meth_add0(pkey_sm2_methods); + + return 1; +} + diff --git a/src/tpm2-tss-engine.c b/src/tpm2-tss-engine.c index 824f538..8a5fb52 100644 --- a/src/tpm2-tss-engine.c +++ b/src/tpm2-tss-engine.c @@ -293,7 +293,12 @@ init_engine(ENGINE *e) { ERR(init_engine, TPM2TSS_R_SUBINIT_FAILED); return rc; } - + + rc = init_sm2(e); + if (rc != 1) { + ERR(init_engine, TPM2TSS_R_SUBINIT_FAILED); + return rc; + } initialized = 1; return 1; } -- 2.27.0