Skip to content
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
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ TESTS_SHELL = test/list.sh \
test/ecdsa_genpkey_sign_auth.sh \
test/ecdsa_genpkey_sign_rawin.sh \
test/ecdsa_createak_sign_handle.sh \
test/ecdsa_policy_pcr_x509_csr.sh \
test/ecdh_genpkey_keyexch.sh \
test/ecdh_create_keyexch_index.sh \
test/ec_createak_x509_cert.sh \
Expand Down
144 changes: 121 additions & 23 deletions src/tpm2-provider-signature.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: BSD-3-Clause */

#include <stdlib.h>
#include <string.h>

#include <openssl/core_dispatch.h>
Expand All @@ -26,6 +27,8 @@ struct tpm2_signature_ctx_st {
TPM2_PKEY *pkey;
TPMT_SIG_SCHEME signScheme;
TPMT_SIGNATURE *signature;
TPML_PCR_SELECTION policy_pcr;
int policy_pcr_set;
};

static OSSL_FUNC_signature_newctx_fn tpm2_signature_newctx;
Expand Down Expand Up @@ -89,6 +92,8 @@ tpm2_signature_dupctx(void *ctx)
sctx->capability = src->capability;
sctx->pkey = src->pkey;
sctx->signScheme = src->signScheme;
sctx->policy_pcr = src->policy_pcr;
sctx->policy_pcr_set = src->policy_pcr_set;
if (src->signature) {
sctx->signature = OPENSSL_malloc(sizeof(TPMT_SIGNATURE));
if (!sctx->signature)
Expand Down Expand Up @@ -322,13 +327,115 @@ set_signature_buffer(TPMT_SIGNATURE *signature,
return 0;
}

static int
parse_policy_pcr(TPML_PCR_SELECTION *selection, const char *value)
{
const char *p;
unsigned long pcr;
char *end;

if (strncmp(value, "sha256:", 7) != 0)
return 0;
p = value + 7;

memset(selection, 0, sizeof(*selection));
selection->count = 1;
selection->pcrSelections[0].hash = TPM2_ALG_SHA256;
selection->pcrSelections[0].sizeofSelect = 3;

while (1) {
if (*p < '0' || *p > '9')
return 0;
pcr = strtoul(p, &end, 10);
if (pcr > 23 || (*end != ',' && *end != '\0'))
return 0;
if (selection->pcrSelections[0].pcrSelect[pcr / 8] & (1 << (pcr % 8)))
return 0;
selection->pcrSelections[0].pcrSelect[pcr / 8] |= (1 << (pcr % 8));
if (*end == '\0')
break;
p = end + 1;
}

return 1;
}

static int
set_policy_pcr_param(TPM2_SIGNATURE_CTX *sctx, const OSSL_PARAM params[])
{
const OSSL_PARAM *p = OSSL_PARAM_locate_const(params, TPM2_PKEY_PARAM_POLICY_PCR);
char *value = NULL;
int ok;

if (p == NULL)
return 1;

sctx->policy_pcr_set = 0;
ok = OSSL_PARAM_get_utf8_string(p, &value, 0) &&
parse_policy_pcr(&sctx->policy_pcr, value);
OPENSSL_free(value);
if (!ok) {
TPM2_ERROR_raise(sctx->core, TPM2_ERR_INPUT_CORRUPTED);
return 0;
}
sctx->policy_pcr_set = 1;

return 1;
}

static int
tpm2_signature_sign_tpm(TPM2_SIGNATURE_CTX *sctx,
const TPM2B_DIGEST *digest,
const TPMT_TK_HASHCHECK *validation)
{
static const TPMT_SYM_DEF symmetric = { .algorithm = TPM2_ALG_NULL };
static const TPM2B_DIGEST empty_pcr_digest = { .size = 0 };
ESYS_TR session = ESYS_TR_PASSWORD;
TSS2_RC r, fr;

if (!tpm2_semaphore_lock(sctx->esys_lock))
return 0;

if (sctx->policy_pcr_set) {
session = ESYS_TR_NONE;
r = Esys_StartAuthSession(sctx->esys_ctx, ESYS_TR_NONE, ESYS_TR_NONE,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
NULL, TPM2_SE_POLICY, &symmetric,
sctx->pkey->data.pub.publicArea.nameAlg, &session);
if (r)
goto out;
r = Esys_TRSess_SetAttributes(sctx->esys_ctx, session,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this causes a deadlock with older OpenSSL versions, probably the way it does signature calls differs?

The hang is the first CSR call:

 openssl req -provider tpm2 -provider default \ 
     -propquery '?provider=tpm2' \
     -new -subj '/CN=policy-test' \
     -key "handle:${KEY_HANDLE}" \
     -sigopt 'policy-pcr:sha256:0,1,7' \
     -out policy-test.csr 

TPMA_SESSION_CONTINUESESSION,
TPMA_SESSION_CONTINUESESSION);
if (r)
goto out;
r = Esys_PolicyPCR(sctx->esys_ctx, session,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
&empty_pcr_digest, &sctx->policy_pcr);
if (r)
goto out;
}

r = Esys_Sign(sctx->esys_ctx, sctx->pkey->object,
session, ESYS_TR_NONE, ESYS_TR_NONE,
digest, &sctx->signScheme, validation, &sctx->signature);
out:
if (session != ESYS_TR_PASSWORD && session != ESYS_TR_NONE) {
fr = Esys_FlushContext(sctx->esys_ctx, session);
if (!r)
r = fr;
}
tpm2_semaphore_unlock(sctx->esys_lock);
TPM2_CHECK_RC(sctx->core, r, TPM2_ERR_CANNOT_SIGN, return 0);
return 1;
}

static int
tpm2_signature_sign(void *ctx, unsigned char *sig, size_t *siglen, size_t sigsize,
const unsigned char *tbs, size_t tbslen)
{
TPM2_SIGNATURE_CTX *sctx = ctx;
TPM2B_DIGEST digest;
TSS2_RC r;

TPMT_TK_HASHCHECK empty_validation = {
.tag = TPM2_ST_HASHCHECK,
Expand All @@ -355,13 +462,8 @@ tpm2_signature_sign(void *ctx, unsigned char *sig, size_t *siglen, size_t sigsiz
digest.size = tbslen;
memcpy(digest.buffer, tbs, tbslen);

if (!tpm2_semaphore_lock(sctx->esys_lock))
if (!tpm2_signature_sign_tpm(sctx, &digest, &empty_validation))
return 0;
r = Esys_Sign(sctx->esys_ctx, sctx->pkey->object,
ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
&digest, &sctx->signScheme, &empty_validation, &sctx->signature);
tpm2_semaphore_unlock(sctx->esys_lock);
TPM2_CHECK_RC(sctx->core, r, TPM2_ERR_CANNOT_SIGN, return 0);

if (!get_signature_buffer(sctx->signature, sig, siglen, sigsize))
return 0;
Expand Down Expand Up @@ -424,7 +526,6 @@ tpm2_signature_digest_update(void *ctx,
static int
digest_sign_calculate(TPM2_SIGNATURE_CTX *sctx)
{
TSS2_RC r;
TPM2B_DIGEST *digest = NULL;
TPMT_TK_HASHCHECK *validation = NULL;

Expand All @@ -435,15 +536,13 @@ digest_sign_calculate(TPM2_SIGNATURE_CTX *sctx)
if (validation->digest.size == 0)
DBG("SIGN DIGEST_SIGN_CALCULATE zero size ticket\n");

if (!tpm2_semaphore_lock(sctx->esys_lock))
if (!tpm2_signature_sign_tpm(sctx, digest, validation)) {
free(digest);
free(validation);
return 0;
r = Esys_Sign(sctx->esys_ctx, sctx->pkey->object,
ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
digest, &sctx->signScheme, validation, &sctx->signature);
}
free(digest);
free(validation);
tpm2_semaphore_unlock(sctx->esys_lock);
TPM2_CHECK_RC(sctx->core, r, TPM2_ERR_CANNOT_SIGN, return 0);

return 1;
}
Expand Down Expand Up @@ -474,7 +573,6 @@ static int
tpm2_signature_digest_sign(void *ctx, unsigned char *sig, size_t *siglen,
size_t sigsize, const unsigned char *data, size_t datalen)
{
TSS2_RC r;
TPM2_SIGNATURE_CTX *sctx = ctx;
TPM2B_DIGEST *digest = NULL;
TPMT_TK_HASHCHECK *validation = NULL;
Expand All @@ -500,15 +598,13 @@ tpm2_signature_digest_sign(void *ctx, unsigned char *sig, size_t *siglen,
if (validation->digest.size == 0)
DBG("SIGN DIGEST_SIGN zero size ticket\n");

if (!tpm2_semaphore_lock(sctx->esys_lock))
if (!tpm2_signature_sign_tpm(sctx, digest, validation)) {
free(digest);
free(validation);
return 0;
r = Esys_Sign(sctx->esys_ctx, sctx->pkey->object,
ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
digest, &sctx->signScheme, validation, &sctx->signature);
}
free(digest);
free(validation);
tpm2_semaphore_unlock(sctx->esys_lock);
TPM2_CHECK_RC(sctx->core, r, TPM2_ERR_CANNOT_SIGN, return 0);

if (!get_signature_buffer(sctx->signature, sig, siglen, sigsize))
return 0;
Expand Down Expand Up @@ -633,7 +729,7 @@ tpm2_rsa_signature_set_ctx_params(void *ctx, const OSSL_PARAM params[])
return 0;
}

return 1;
return set_policy_pcr_param(sctx, params);
}

static const OSSL_PARAM *
Expand All @@ -644,6 +740,7 @@ tpm2_rsa_signature_settable_ctx_params(void *ctx, void *provctx)
OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, NULL, 0),
OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, NULL, 0),
OSSL_PARAM_utf8_string(TPM2_PKEY_PARAM_POLICY_PCR, NULL, 0),
OSSL_PARAM_END
};

Expand All @@ -670,14 +767,15 @@ tpm2_ecdsa_signature_set_ctx_params(void *ctx, const OSSL_PARAM params[])
}
}

return 1;
return set_policy_pcr_param(sctx, params);
}

static const OSSL_PARAM *
tpm2_ecdsa_signature_settable_ctx_params(void *ctx, void *provctx)
{
static OSSL_PARAM settable[] = {
OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
OSSL_PARAM_utf8_string(TPM2_PKEY_PARAM_POLICY_PCR, NULL, 0),
OSSL_PARAM_END
};

Expand Down
1 change: 1 addition & 0 deletions src/tpm2-provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define TPM2_PKEY_PARAM_PARENT "parent"
#define TPM2_PKEY_PARAM_PARENT_AUTH "parent-auth"
#define TPM2_PKEY_PARAM_USER_AUTH "user-auth"
#define TPM2_PKEY_PARAM_POLICY_PCR "policy-pcr"

typedef struct tpm2_provider_ctx_st TPM2_PROVIDER_CTX;

Expand Down
55 changes: 55 additions & 0 deletions test/ecdsa_policy_pcr_x509_csr.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: BSD-3-Clause
set -eufx

PARENT_HANDLE=""
KEY_HANDLE=""

flush_ctx_object() {
local want h
want=$(tpm2_readpublic -c "$1" 2>/dev/null | sed -n 's/^name: //p') || true
[ -n "$want" ] || return 0
for h in $(tpm2_getcap handles-transient | sed -n 's/^- //p'); do
if [ "$(tpm2_readpublic -c "$h" 2>/dev/null | sed -n 's/^name: //p')" = "$want" ]; then
tpm2_flushcontext "$h"
fi
done
}

cleanup() {
flush_ctx_object key.ctx || true
flush_ctx_object primary.ctx || true
if [ -n "$KEY_HANDLE" ]; then
tpm2_evictcontrol -C o -c "$KEY_HANDLE" || true
fi
if [ -n "$PARENT_HANDLE" ]; then
tpm2_evictcontrol -C o -c "$PARENT_HANDLE" || true
fi
rm -f primary.ctx key.pub key.priv key.ctx policy.digest eventdata \
policy-test.csr policy-test-changed.csr
}
trap cleanup EXIT

tpm2_createprimary -C o -c primary.ctx
PARENT_HANDLE=$(tpm2_evictcontrol -C o -c primary.ctx | cut -d ' ' -f 2 | head -n 1)
flush_ctx_object primary.ctx
tpm2_createpolicy --policy-pcr -l sha256:0,1,7 -L policy.digest
tpm2_create -C "$PARENT_HANDLE" -G ecc -g sha256 \
-a 'fixedtpm|fixedparent|sensitivedataorigin|sign' \
-L policy.digest -u key.pub -r key.priv
tpm2_load -C "$PARENT_HANDLE" -u key.pub -r key.priv -c key.ctx
KEY_HANDLE=$(tpm2_evictcontrol -C o -c key.ctx | cut -d ' ' -f 2 | head -n 1)
flush_ctx_object key.ctx

openssl req -provider tpm2 -provider default -propquery '?provider=tpm2' \
-new -subj '/CN=policy-test' -key "handle:${KEY_HANDLE}" \
-sigopt 'policy-pcr:sha256:0,1,7' -out policy-test.csr
openssl req -in policy-test.csr -noout -verify

echo -n "policy-state-change" > eventdata
tpm2_pcrevent 7 eventdata

! openssl req -provider tpm2 -provider default -propquery '?provider=tpm2' \
-new -subj '/CN=policy-test' -key "handle:${KEY_HANDLE}" \
-sigopt 'policy-pcr:sha256:0,1,7' -out policy-test-changed.csr
! openssl req -in policy-test-changed.csr -noout -verify 2>/dev/null
Loading