1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-06 23:17:47 +02:00
gnupg/agent/tpm2.c
James Bottomley 72ece35fb7
tpm2: add handling for elliptic curve keys
* agent/divert-tpm2.c: Support ECC.

--
This adds handling for the way gnupg does elliptic keys, namely ECDSA
for signatures and using ECDH with an ephemeral key to generate an
encrypted message.  The main problem is that the TPM2 usually has a
very small list of built in curves and it won't handle any others.
Thanks to TCG mandates, all TPM2 systems in the USA should come with
NIST P-256, but do not come with the Bernstien curve 25519, so the
only way to use the TPM2 to protect an elliptic curve key is first to
create it with a compatible algorithm.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
2018-03-09 10:15:25 +01:00

1004 lines
26 KiB
C

#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <unistd.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <agent.h>
#include <tpm2.h>
#include "../common/i18n.h"
#include "../common/sexp-parse.h"
#include <tss2/tssutils.h>
#include <tss2/tssresponsecode.h>
#include <tss2/tssmarshal.h>
#include <tss2/Unmarshal_fp.h>
#include <tss2/tsscryptoh.h>
/* List of tss2 functions we use. This is macro jiggery-pokery:
* the F argument gives us the ability to run an arbitrary macro over
* the function list as for each function do macro F */
#define _TSS2_LIST(F) \
F(TSS_Create); \
F(TSS_SetProperty); \
F(TSS_Execute); \
F(TSS_ResponseCode_toString); \
F(TPM2B_PUBLIC_Unmarshal); \
F(TPM2B_PRIVATE_Unmarshal); \
F(TSS_TPM2B_PUBLIC_Marshal); \
F(TSS_TPMT_PUBLIC_Marshal); \
F(TSS_TPM2B_PRIVATE_Marshal); \
F(TSS_UINT16_Marshal); \
F(TSS_TPMT_SENSITIVE_Marshal); \
F(TSS_SetProperty); \
F(TSS_GetDigestSize); \
F(TSS_Hash_Generate); \
F(TSS_Delete);
/* create static declarations for the function pointers */
#define _DL_DECLARE(func) \
static typeof(func) *p##func
_TSS2_LIST(_DL_DECLARE);
static const char *tpm2_dir;
/* The TPM builds a small database of active files representing key
* parameters used for authentication and session encryption. Make sure
* they're contained in a separate directory to avoid stepping on any
* other application uses of the TPM */
static const char *
tpm2_set_unique_tssdir(void)
{
char *prefix = getenv("XDG_RUNTIME_DIR"), *template,
*dir;
int len = 0;
if (!prefix)
prefix = "/tmp";
len = snprintf(NULL, 0, "%s/tss2.XXXXXX", prefix);
if (len <= 0)
return NULL;
template = xtrymalloc(len + 1);
if (!template)
return NULL;
len++;
len = snprintf(template, len, "%s/tss2.XXXXXX", prefix);
dir = mkdtemp(template);
return dir;
}
/* now dynamically load the tss library (if it exists) and resolve the
* above symbols. This allows us simply to return 0 for tpm2_init on
* systems where there is no TPM library */
static int
tpm2_init(void)
{
static int inited = 0;
const char *sym;
void *dl;
if (inited)
return 0;
dl = dlopen(TSS2_LIB, RTLD_LAZY);
if (!dl)
{
log_error("opening of tss2 library failed %s\n", strerror(errno));
return GPG_ERR_CARD_NOT_PRESENT;
}
/* load each symbol pointer and check for existence */
# define _DL_SYM(func) \
sym = #func; \
p##func = dlsym(dl, #func); \
if (p##func == NULL) \
goto out_symfail
_TSS2_LIST(_DL_SYM);
tpm2_dir = tpm2_set_unique_tssdir();
if (!tpm2_dir)
/* make this non fatal */
log_error("Failed to set unique TPM directory\n");
inited = 1;
return 0;
out_symfail:
log_error("Failed to find symbol %s in tss2 library\n", sym);
return GPG_ERR_CARD_NOT_PRESENT;
}
static void
tpm2_error(TPM_RC rc, char *prefix)
{
const char *msg, *submsg, *num;
pTSS_ResponseCode_toString(&msg, &submsg, &num, rc);
log_error("%s gave TPM2 Error: %s%s%s", prefix, msg, submsg, num);
}
#define _TSS_CHECK(f) \
rc = f; \
if (rc != TPM_RC_SUCCESS) \
{ \
tpm2_error(rc, #f); \
return GPG_ERR_CARD; \
}
int
tpm2_start(TSS_CONTEXT **tssc)
{
TPM_RC rc;
int ret;
ret = tpm2_init();
if (ret)
return ret;
_TSS_CHECK(pTSS_Create(tssc));
_TSS_CHECK(pTSS_SetProperty(*tssc, TPM_DATA_DIR, tpm2_dir));
return 0;
}
void
tpm2_end(TSS_CONTEXT *tssc)
{
pTSS_Delete(tssc);
}
void
tpm2_flush_handle(TSS_CONTEXT *tssc, TPM_HANDLE h)
{
FlushContext_In in;
if (!h)
return;
in.flushHandle = h;
pTSS_Execute(tssc, NULL,
(COMMAND_PARAMETERS *)&in,
NULL,
TPM_CC_FlushContext,
TPM_RH_NULL, NULL, 0);
}
static int
tpm2_get_hmac_handle(TSS_CONTEXT *tssc, TPM_HANDLE *handle,
TPM_HANDLE salt_key)
{
TPM_RC rc;
StartAuthSession_In in;
StartAuthSession_Out out;
StartAuthSession_Extra extra;
memset(&in, 0, sizeof(in));
memset(&extra, 0 , sizeof(extra));
in.bind = TPM_RH_NULL;
in.sessionType = TPM_SE_HMAC;
in.authHash = TPM_ALG_SHA256;
in.tpmKey = TPM_RH_NULL;
in.symmetric.algorithm = TPM_ALG_AES;
in.symmetric.keyBits.aes = 128;
in.symmetric.mode.aes = TPM_ALG_CFB;
if (salt_key) {
ReadPublic_In rin;
ReadPublic_Out rout;
rin.objectHandle = salt_key;
rc = pTSS_Execute (tssc,
(RESPONSE_PARAMETERS *)&rout,
(COMMAND_PARAMETERS *)&rin,
NULL,
TPM_CC_ReadPublic,
TPM_RH_NULL, NULL, 0);
if (rc) {
tpm2_error(rc, "TPM2_ReadPublic");
return GPG_ERR_CARD;
}
/* don't care what rout returns, the purpose of the operation was
* to get the public key parameters into the tss so it can
* construct the salt */
in.tpmKey = salt_key;
}
rc = pTSS_Execute(tssc,
(RESPONSE_PARAMETERS *)&out,
(COMMAND_PARAMETERS *)&in,
(EXTRA_PARAMETERS *)&extra,
TPM_CC_StartAuthSession,
TPM_RH_NULL, NULL, 0);
if (rc) {
tpm2_error(rc, "TPM2_StartAuthSession");
return GPG_ERR_CARD;
}
*handle = out.sessionHandle;
return 0;
}
static int
tpm2_exec_with_auth(ctrl_t ctrl, TSS_CONTEXT *tssc, int cmd, char *cmd_str,
void *out, void *in)
{
TPM_HANDLE ah;
struct pin_entry_info_s *pi;
TPM_RC rc;
pi = gcry_xmalloc_secure(sizeof(*pi) + MAX_PASSPHRASE_LEN + 10);
pi->max_length = MAX_PASSPHRASE_LEN;
pi->min_digits = 0; /* want a real passphrase */
pi->max_digits = 16;
pi->max_tries = 3;
rc = agent_askpin(ctrl, NULL, "TPM Key Passphrase", NULL, pi, NULL, 0);
if (rc) {
gcry_free (pi);
return rc;
}
rc = tpm2_get_hmac_handle(tssc, &ah, 0);
if (rc)
return rc;
rc = pTSS_Execute(tssc, out, in, NULL,
cmd,
ah, pi->pin, 0,
TPM_RH_NULL, NULL, 0);
gcry_free (pi);
if (rc) {
tpm2_error(rc, cmd_str);
tpm2_flush_handle(tssc, ah);
switch (rc & 0xFF) {
case TPM_RC_BAD_AUTH:
case TPM_RC_AUTH_FAIL:
return GPG_ERR_BAD_PASSPHRASE;
default:
return GPG_ERR_CARD;
}
}
return 0;
}
static gpg_error_t
parse_tpm2_shadow_info (const unsigned char *shadow_info,
uint32_t *parent,
const char **pub, int *pub_len,
const char **priv, int *priv_len)
{
const unsigned char *s;
size_t n;
int i;
s = shadow_info;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
s++;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
*parent = 0;
for (i = 0; i < n; i++) {
*parent *= 10;
*parent += atoi_1(s+i);
}
s += n;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
*pub_len = n;
*pub = s;
s += n;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
*priv_len = n;
*priv = s;
return 0;
}
int
tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
TPM_HANDLE *key, TPMI_ALG_PUBLIC *type)
{
uint32_t parent;
Load_In in;
Load_Out out;
const char *pub, *priv;
int ret, pub_len, priv_len;
TPM_RC rc;
BYTE *buf;
uint32_t size;
ret = parse_tpm2_shadow_info (shadow_info, &parent, &pub, &pub_len,
&priv, &priv_len);
if (ret)
return ret;
in.parentHandle = parent;
buf = (BYTE *)priv;
size = priv_len;
pTPM2B_PRIVATE_Unmarshal(&in.inPrivate, &buf, &size);
buf = (BYTE *)pub;
size = pub_len;
pTPM2B_PUBLIC_Unmarshal(&in.inPublic, &buf, &size, FALSE);
*type = in.inPublic.publicArea.type;
rc = pTSS_Execute(tssc,
(RESPONSE_PARAMETERS *)&out,
(COMMAND_PARAMETERS *)&in,
NULL,
TPM_CC_Load,
TPM_RS_PW, NULL, 0,
TPM_RH_NULL, NULL, 0);
if (rc != TPM_RC_SUCCESS) {
tpm2_error(rc, "TPM2_Load");
return GPG_ERR_CARD;
}
*key = out.objectHandle;
return 0;
}
int
tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
TPMI_ALG_PUBLIC type,
const unsigned char *digest, size_t digestlen,
unsigned char **r_sig, size_t *r_siglen)
{
Sign_In in;
Sign_Out out;
int ret;
/* The TPM insists on knowing the digest type, so
* calculate that from the size */
switch (digestlen) {
case 20:
in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA1;
break;
case 32:
in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA256;
break;
case 48:
in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA384;
break;
#ifdef TPM_ALG_SHA512
case 64:
in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA512;
break;
#endif
default:
log_error("Unknown signature digest length, cannot deduce hash type for TPM\n");
return GPG_ERR_NO_SIGNATURE_SCHEME;
}
in.digest.t.size = digestlen;
memcpy(in.digest.t.buffer, digest, digestlen);
in.keyHandle = key;
in.validation.tag = TPM_ST_HASHCHECK;
in.validation.hierarchy = TPM_RH_NULL;
in.validation.digest.t.size = 0;
if (type == TPM_ALG_RSA)
in.inScheme.scheme = TPM_ALG_RSASSA;
else if (type == TPM_ALG_ECC)
in.inScheme.scheme = TPM_ALG_ECDSA;
else
return GPG_ERR_PUBKEY_ALGO;
ret = tpm2_exec_with_auth(ctrl, tssc, TPM_CC_Sign, "TPM2_Sign", &out, &in);
if (ret)
return ret;
if (type == TPM_ALG_RSA)
*r_siglen = out.signature.signature.rsassa.sig.t.size;
else if (type == TPM_ALG_ECC)
*r_siglen = out.signature.signature.ecdsa.signatureR.t.size
+ out.signature.signature.ecdsa.signatureS.t.size;
*r_sig = xtrymalloc(*r_siglen);
if (!r_sig)
return GPG_ERR_ENOMEM;
if (type == TPM_ALG_RSA)
{
memcpy(*r_sig, out.signature.signature.rsassa.sig.t.buffer, *r_siglen);
}
else if (type == TPM_ALG_ECC)
{
memcpy(*r_sig, out.signature.signature.ecdsa.signatureR.t.buffer,
out.signature.signature.ecdsa.signatureR.t.size);
memcpy(*r_sig + out.signature.signature.ecdsa.signatureR.t.size,
out.signature.signature.ecdsa.signatureS.t.buffer,
out.signature.signature.ecdsa.signatureS.t.size);
}
return 0;
}
static int
sexp_to_tpm2_sensitive_ecc(TPMT_SENSITIVE *s, gcry_sexp_t key)
{
gcry_mpi_t d;
gcry_sexp_t l;
int rc = -1;
size_t len;
s->sensitiveType = TPM_ALG_ECC;
s->seedValue.b.size = 0;
l = gcry_sexp_find_token (key, "d", 0);
if (!l)
return rc;
d = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l);
len = sizeof(s->sensitive.ecc.t.buffer);
rc = gcry_mpi_print (GCRYMPI_FMT_USG, s->sensitive.ecc.t.buffer, len, &len, d);
s->sensitive.ecc.t.size = len;
gcry_mpi_release (d);
return rc;
}
/* try to match the libgcrypt curve names to known TPM parameters.
*
* As of 2018 the TCG defined curves are only NIST
* (192,224,256,384,521) Barreto-Naehring (256,638) and the Chinese
* SM2 (256), which means only the NIST ones overlap with libgcrypt */
static struct {
const char *name;
TPMI_ECC_CURVE c;
} tpm2_curves[] = {
{ "NIST P-192", TPM_ECC_NIST_P192 },
{ "prime192v1", TPM_ECC_NIST_P192 },
{ "secp192r1", TPM_ECC_NIST_P192 },
{ "nistp192", TPM_ECC_NIST_P192 },
{ "NIST P-224", TPM_ECC_NIST_P224 },
{ "secp224r1", TPM_ECC_NIST_P224 },
{ "nistp224", TPM_ECC_NIST_P224 },
{ "NIST P-256", TPM_ECC_NIST_P256 },
{ "prime256v1", TPM_ECC_NIST_P256 },
{ "secp256r1", TPM_ECC_NIST_P256 },
{ "nistp256", TPM_ECC_NIST_P256 },
{ "NIST P-384", TPM_ECC_NIST_P384 },
{ "secp384r1", TPM_ECC_NIST_P384 },
{ "nistp384", TPM_ECC_NIST_P384 },
{ "NIST P-521", TPM_ECC_NIST_P521 },
{ "secp521r1", TPM_ECC_NIST_P521 },
{ "nistp521", TPM_ECC_NIST_P521 },
};
static int
tpm2_ecc_curve (const char *curve_name, TPMI_ECC_CURVE *c)
{
int i;
for (i = 0; i < DIM (tpm2_curves); i++)
if (strcmp (tpm2_curves[i].name, curve_name) == 0)
break;
if (i == DIM (tpm2_curves)) {
log_error ("curve %s does not match any available TPM curves\n", curve_name);
return GPG_ERR_UNKNOWN_CURVE;
}
*c = tpm2_curves[i].c;
return 0;
}
static int
sexp_to_tpm2_public_ecc(TPMT_PUBLIC *p, gcry_sexp_t key)
{
const char *q;
gcry_sexp_t l;
int rc = GPG_ERR_BAD_PUBKEY;
size_t len;
TPMI_ECC_CURVE curve;
char *curve_name;
l = gcry_sexp_find_token (key, "curve", 0);
if (!l)
return rc;
curve_name = gcry_sexp_nth_string (l, 1);
if (!curve_name)
goto out;
rc = tpm2_ecc_curve (curve_name, &curve);
gcry_free (curve_name);
if (rc)
goto out;
gcry_sexp_release(l);
l = gcry_sexp_find_token (key, "q", 0);
if (!l)
return rc;
q = gcry_sexp_nth_data (l, 1, &len);
/* This is a point representation, the first byte tells you what
* type. The only format we understand is uncompressed (0x04)
* which has layout 0x04 | x | y */
if (q[0] != 0x04)
{
log_error ("Point format for q is not uncompressed\n");
goto out;
}
q++;
len--;
/* now should have to equal sized big endian point numbers */
if ((len & 0x01) == 1)
{
log_error ("Point format for q has incorrect length\n");
goto out;
}
len >>= 1;
p->type = TPM_ALG_ECC;
p->nameAlg = TPM_ALG_SHA256;
p->objectAttributes.val = TPMA_OBJECT_NODA |
TPMA_OBJECT_SIGN |
TPMA_OBJECT_DECRYPT |
TPMA_OBJECT_USERWITHAUTH;
p->authPolicy.t.size = 0;
p->parameters.eccDetail.symmetric.algorithm = TPM_ALG_NULL;
p->parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
p->parameters.eccDetail.curveID = curve;
p->parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
memcpy(p->unique.ecc.x.t.buffer, q, len);
p->unique.ecc.x.t.size = len;
memcpy(p->unique.ecc.y.t.buffer, q + len, len);
p->unique.ecc.y.t.size = len;
out:
gcry_sexp_release (l);
return rc;
}
static int
sexp_to_tpm2_sensitive_rsa(TPMT_SENSITIVE *s, gcry_sexp_t key)
{
gcry_mpi_t p;
gcry_sexp_t l;
int rc = -1;
size_t len;
s->sensitiveType = TPM_ALG_RSA;
s->seedValue.b.size = 0;
l = gcry_sexp_find_token (key, "p", 0);
if (!l)
return rc;
p = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l);
len = sizeof(s->sensitive.rsa.t.buffer);
rc = gcry_mpi_print (GCRYMPI_FMT_USG, s->sensitive.rsa.t.buffer, len, &len, p);
s->sensitive.rsa.t.size = len;
gcry_mpi_release (p);
return rc;
}
static int
sexp_to_tpm2_public_rsa(TPMT_PUBLIC *p, gcry_sexp_t key)
{
gcry_mpi_t n, e;
gcry_sexp_t l;
int rc = -1, i;
size_t len;
/* longer than an int */
unsigned char ebuf[5];
uint32_t exp = 0;
p->type = TPM_ALG_RSA;
p->nameAlg = TPM_ALG_SHA256;
/* note: all our keys are decrypt only. This is because
* we use the TPM2_RSA_Decrypt operation for both signing
* and decryption (see e_tpm2.c for details) */
p->objectAttributes.val = TPMA_OBJECT_NODA |
TPMA_OBJECT_DECRYPT |
TPMA_OBJECT_SIGN |
TPMA_OBJECT_USERWITHAUTH;
p->authPolicy.t.size = 0;
p->parameters.rsaDetail.symmetric.algorithm = TPM_ALG_NULL;
p->parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
l = gcry_sexp_find_token (key, "n", 0);
if (!l)
return rc;
n = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l);
len = sizeof(p->unique.rsa.t.buffer);
p->parameters.rsaDetail.keyBits = gcry_mpi_get_nbits (n);
rc = gcry_mpi_print (GCRYMPI_FMT_USG, p->unique.rsa.t.buffer, len, &len, n);
p->unique.rsa.t.size = len;
gcry_mpi_release (n);
if (rc)
return rc;
rc = -1;
l = gcry_sexp_find_token (key, "e", 0);
if (!l)
return rc;
e = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l);
len = sizeof (ebuf);
rc = gcry_mpi_print (GCRYMPI_FMT_USG, ebuf, len, &len, e);
gcry_mpi_release (e);
if (rc)
return rc;
if (len > 4)
return -1;
/* MPI are simply big endian integers, so convert to uint32 */
for (i = 0; i < len; i++) {
exp <<= 8;
exp += ebuf[i];
}
if (exp == 0x10001)
p->parameters.rsaDetail.exponent = 0;
else
p->parameters.rsaDetail.exponent = exp;
return 0;
}
static int
sexp_to_tpm2(TPMT_PUBLIC *p, TPMT_SENSITIVE *s, gcry_sexp_t s_skey)
{
gcry_sexp_t l1, l2;
int rc = -1;
/* find the value of (private-key */
l1 = gcry_sexp_nth (s_skey, 1);
if (!l1)
return rc;
l2 = gcry_sexp_find_token (l1, "rsa", 0);
if (l2) {
rc = sexp_to_tpm2_public_rsa (p, l2);
if (!rc)
rc = sexp_to_tpm2_sensitive_rsa (s, l2);
} else {
l2 = gcry_sexp_find_token (l1, "ecc", 0);
if (!l2)
goto out;
rc = sexp_to_tpm2_public_ecc (p, l2);
if (!rc)
rc = sexp_to_tpm2_sensitive_ecc (s, l2);
}
gcry_sexp_release(l2);
out:
gcry_sexp_release(l1);
return rc;
}
/* copied from TPM implementation code */
static TPM_RC
tpm2_ObjectPublic_GetName(TPM2B_NAME *name,
TPMT_PUBLIC *tpmtPublic)
{
TPM_RC rc = 0;
uint16_t written = 0;
TPMT_HA digest;
uint32_t sizeInBytes;
uint8_t buffer[MAX_RESPONSE_SIZE];
/* marshal the TPMT_PUBLIC */
if (rc == 0) {
INT32 size = MAX_RESPONSE_SIZE;
uint8_t *buffer1 = buffer;
rc = pTSS_TPMT_PUBLIC_Marshal(tpmtPublic, &written, &buffer1, &size);
}
/* hash the public area */
if (rc == 0) {
sizeInBytes = pTSS_GetDigestSize(tpmtPublic->nameAlg);
digest.hashAlg = tpmtPublic->nameAlg; /* Name digest algorithm */
/* generate the TPMT_HA */
rc = pTSS_Hash_Generate(&digest,
written, buffer,
0, NULL);
}
if (rc == 0) {
TPMI_ALG_HASH nameAlgNbo;
/* copy the digest */
memcpy(name->t.name + sizeof(TPMI_ALG_HASH), (uint8_t *)&digest.digest, sizeInBytes);
/* copy the hash algorithm */
nameAlgNbo = htons(tpmtPublic->nameAlg);
memcpy(name->t.name, (uint8_t *)&nameAlgNbo, sizeof(TPMI_ALG_HASH));
/* set the size */
name->t.size = sizeInBytes + sizeof(TPMI_ALG_HASH);
}
return rc;
}
/*
* Cut down version of Part 4 Supporting Routines 7.6.3.10
*
* Hard coded to symmetrically encrypt with aes128 as the inner
* wrapper and no outer wrapper but with a prototype that allows
* drop in replacement with a tss equivalent
*/
TPM_RC tpm2_SensitiveToDuplicate(TPMT_SENSITIVE *s,
TPM2B_NAME *name,
TPM_ALG_ID nalg,
TPMT_SYM_DEF_OBJECT *symdef,
TPM2B_DATA *innerkey,
TPM2B_PRIVATE *p)
{
BYTE *buf = p->t.buffer;
p->t.size = 0;
memset(p, 0, sizeof(*p));
/* hard code AES CFB */
if (symdef->algorithm == TPM_ALG_AES
&& symdef->mode.aes == TPM_ALG_CFB) {
TPMT_HA hash;
const int hlen = pTSS_GetDigestSize(nalg);
TPM2B *digest = (TPM2B *)buf;
TPM2B *s2b;
int32_t size;
unsigned char null_iv[AES_128_BLOCK_SIZE_BYTES];
UINT16 bsize, written = 0;
gcry_cipher_hd_t hd;
/* WARNING: don't use the static null_iv trick here:
* the AES routines alter the passed in iv */
memset(null_iv, 0, sizeof(null_iv));
/* reserve space for hash before the encrypted sensitive */
bsize = sizeof(digest->size) + hlen;
buf += bsize;
p->t.size += bsize;
s2b = (TPM2B *)buf;
/* marshal the digest size */
buf = (BYTE *)&digest->size;
bsize = hlen;
size = 2;
pTSS_UINT16_Marshal(&bsize, &written, &buf, &size);
/* marshal the unencrypted sensitive in place */
size = sizeof(*s);
bsize = 0;
buf = s2b->buffer;
pTSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
buf = (BYTE *)&s2b->size;
size = 2;
pTSS_UINT16_Marshal(&bsize, &written, &buf, &size);
bsize = bsize + sizeof(s2b->size);
p->t.size += bsize;
/* compute hash of unencrypted marshalled sensitive and
* write to the digest buffer */
hash.hashAlg = nalg;
pTSS_Hash_Generate(&hash, bsize, s2b,
name->t.size, name->t.name,
0, NULL);
memcpy(digest->buffer, &hash.digest, hlen);
gcry_cipher_open (&hd, GCRY_CIPHER_AES128,
GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE);
gcry_cipher_setiv(hd, null_iv, sizeof(null_iv));
gcry_cipher_setkey(hd, innerkey->b.buffer, innerkey->b.size);
/* encrypt the hash and sensitive in-place */
gcry_cipher_encrypt(hd, p->t.buffer, p->t.size, NULL, 0);
gcry_cipher_close(hd);
} else if (symdef->algorithm == TPM_ALG_NULL) {
TPM2B *s2b = (TPM2B *)buf;
int32_t size = sizeof(*s);
UINT16 bsize = 0, written = 0;
buf = s2b->buffer;
/* marshal the unencrypted sensitive in place */
pTSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
buf = (BYTE *)&s2b->size;
size = 2;
pTSS_UINT16_Marshal(&bsize, &written, &buf, &size);
p->b.size += bsize + sizeof(s2b->size);
} else {
log_error ("Unknown symmetric algorithm\n");
return TPM_RC_SYMMETRIC;
}
return TPM_RC_SUCCESS;
}
static void
tpm2_encrypt_duplicate(Import_In *iin, TPMT_SENSITIVE *s)
{
TPM2B_NAME name;
TPMT_PUBLIC *p = &iin->objectPublic.publicArea;
const int aes_key_bits = 128;
const int aes_key_bytes = aes_key_bits/8;
tpm2_ObjectPublic_GetName(&name, p);
gcry_randomize(iin->encryptionKey.t.buffer,
aes_key_bytes, GCRY_STRONG_RANDOM);
iin->encryptionKey.t.size = aes_key_bytes;
/* set random iin.symSeed */
iin->inSymSeed.t.size = 0;
iin->symmetricAlg.algorithm = TPM_ALG_AES;
iin->symmetricAlg.keyBits.aes = aes_key_bits;
iin->symmetricAlg.mode.aes = TPM_ALG_CFB;
tpm2_SensitiveToDuplicate(s, &name, p->nameAlg, &iin->symmetricAlg,
&iin->encryptionKey, &iin->duplicate);
}
int
tpm2_import_key(ctrl_t ctrl, TSS_CONTEXT *tssc, char *pub, int *pub_len,
char *priv, int *priv_len, gcry_sexp_t s_skey)
{
Import_In iin;
Import_Out iout;
TPMT_SENSITIVE s;
TPM_HANDLE ah;
TPM_RC rc;
uint32_t size;
uint16_t len;
BYTE *buffer;
int ret;
char *passphrase;
iin.parentHandle = TPM2_PARENT;
ret = sexp_to_tpm2(&iin.objectPublic.publicArea, &s, s_skey);
if (ret) {
log_error("Failed to parse Key s-expression: key corrupt?\n");
return ret;
}
/* add an authorization password to the key which the TPM will check */
ret = agent_ask_new_passphrase (ctrl, _("Please enter the TPM Authorization passphrase for the key."), &passphrase);
if (ret)
return ret;
s.authValue.b.size = strlen(passphrase);
memcpy(s.authValue.b.buffer, passphrase, s.authValue.b.size);
/* We're responsible for securing the data in transmission to the
* TPM here. The TPM provides parameter encryption via a session,
* but only for the first parameter. For TPM2_Import, the first
* parameter is a symmetric key used to encrypt the sensitive data,
* so we must populate this key with random value and encrypt the
* sensitive data with it */
tpm2_encrypt_duplicate(&iin, &s);
/* use salted parameter encryption to hide the key. First we read
* the public parameters of the parent key and use them to agree an
* encryption for the first parameter */
rc = tpm2_get_hmac_handle(tssc, &ah, TPM2_PARENT);
if (rc)
return GPG_ERR_CARD;
rc = pTSS_Execute(tssc,
(RESPONSE_PARAMETERS *)&iout,
(COMMAND_PARAMETERS *)&iin,
NULL,
TPM_CC_Import,
ah, NULL, TPMA_SESSION_DECRYPT,
TPM_RH_NULL, NULL, 0);
if (rc) {
tpm2_error(rc, "TPM2_Import");
/* failure means auth handle is not flushed */
tpm2_flush_handle(tssc, ah);
return GPG_ERR_CARD;
}
size = sizeof(TPM2B_PUBLIC);
buffer = pub;
len = 0;
pTSS_TPM2B_PUBLIC_Marshal(&iin.objectPublic,
&len, &buffer, &size);
*pub_len = len;
size = sizeof(TPM2B_PRIVATE);
buffer = priv;
len = 0;
pTSS_TPM2B_PRIVATE_Marshal(&iout.outPrivate,
&len, &buffer, &size);
*priv_len = len;
return 0;
}
int
tpm2_ecc_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
const char *ciphertext, int ciphertext_len,
char **decrypt, size_t *decrypt_len)
{
ECDH_ZGen_In in;
ECDH_ZGen_Out out;
size_t len;
int ret;
/* This isn't really a decryption per se. The ciphertext actually
* contains an EC Point which we must multiply by the private key number.
*
* The reason is to generate a diffe helman agreement on a shared
* point. This shared point is then used to generate the per
* session encryption key.
*/
if (ciphertext[0] != 0x04)
{
log_error ("Decryption Shared Point format is not uncompressed\n");
return GPG_ERR_ENCODING_PROBLEM;
}
if ((ciphertext_len & 0x01) != 1)
{
log_error ("Decryption Shared Point has incorrect length\n");
return GPG_ERR_ENCODING_PROBLEM;
}
len = ciphertext_len >> 1;
in.keyHandle = key;
memcpy(in.inPoint.point.x.t.buffer, ciphertext + 1, len);
in.inPoint.point.x.t.size = len;
memcpy(in.inPoint.point.y.t.buffer, ciphertext + 1 + len, len);
in.inPoint.point.y.t.size = len;
ret = tpm2_exec_with_auth(ctrl, tssc, TPM_CC_ECDH_ZGen, "TPM2_ECDH_ZGen",
&out, &in);
if (ret)
return ret;
*decrypt_len = out.outPoint.point.x.t.size + out.outPoint.point.y.t.size + 1;
*decrypt = xtrymalloc(*decrypt_len);
(*decrypt)[0] = 0x04;
memcpy(*decrypt + 1, out.outPoint.point.x.t.buffer,
out.outPoint.point.x.t.size);
memcpy(*decrypt + 1 + out.outPoint.point.x.t.size,
out.outPoint.point.y.t.buffer,
out.outPoint.point.y.t.size);
return 0;
}
int
tpm2_rsa_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
const char *ciphertext, int ciphertext_len,
char **decrypt, size_t *decrypt_len)
{
RSA_Decrypt_In in;
RSA_Decrypt_Out out;
int ret;
in.keyHandle = key;
in.inScheme.scheme = TPM_ALG_RSAES;
in.cipherText.t.size = ciphertext_len;
memcpy (in.cipherText.t.buffer, ciphertext, ciphertext_len);
in.label.t.size = 0;
ret = tpm2_exec_with_auth(ctrl, tssc, TPM_CC_RSA_Decrypt, "TPM2_RSA_Decrypt",
&out, &in);
if (ret)
return ret;
*decrypt_len = out.message.t.size;
*decrypt = xtrymalloc(out.message.t.size);
memcpy (*decrypt, out.message.t.buffer, out.message.t.size);
return 0;
}