1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-01-09 12:54:23 +01:00
gnupg/agent/divert-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

219 lines
5.3 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 "agent.h"
#include "../common/i18n.h"
#include "../common/sexp-parse.h"
#include "tpm2.h"
int
divert_tpm2_pksign (ctrl_t ctrl, const char *desc_text,
const unsigned char *digest, size_t digestlen, int algo,
const unsigned char *shadow_info, unsigned char **r_sig,
size_t *r_siglen)
{
TSS_CONTEXT *tssc;
TPM_HANDLE key;
TPMI_ALG_PUBLIC type;
int ret;
ret = tpm2_start(&tssc);
if (ret)
return ret;
ret = tpm2_load_key(tssc, shadow_info, &key, &type);
if (ret)
goto out;
ret = tpm2_sign(ctrl, tssc, key, type, digest, digestlen, r_sig, r_siglen);
tpm2_flush_handle(tssc, key);
out:
tpm2_end(tssc);
return ret;
}
static unsigned char *
make_tpm2_shadow_info (uint32_t parent, const char *pub, int pub_len,
const char *priv, int priv_len)
{
gcry_sexp_t s_exp;
size_t len;
char *info;
gcry_sexp_build(&s_exp, NULL, "(%u%b%b)", parent, pub_len, pub, priv_len, priv);
len = gcry_sexp_sprint(s_exp, GCRYSEXP_FMT_CANON, NULL, 0);
info = xtrymalloc(len);
gcry_sexp_sprint(s_exp, GCRYSEXP_FMT_CANON, info, len);
gcry_sexp_release(s_exp);
return (unsigned char *)info;
}
static gpg_error_t
agent_write_tpm2_shadow_key (ctrl_t ctrl, const unsigned char *grip,
int parent, char *pub, int pub_len,
char *priv, int priv_len)
{
gpg_error_t err;
unsigned char *shadow_info;
unsigned char *shdkey;
unsigned char *pkbuf;
size_t len;
gcry_sexp_t s_pkey;
err = agent_public_key_from_file (ctrl, grip, &s_pkey);
len = gcry_sexp_sprint(s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
pkbuf = xtrymalloc (len);
gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, pkbuf, len);
gcry_sexp_release (s_pkey);
shadow_info = make_tpm2_shadow_info (parent, pub, pub_len, priv, priv_len);
if (!shadow_info) {
xfree (pkbuf);
return gpg_error_from_syserror ();
}
err = agent_shadow_key_type (pkbuf, shadow_info, "tpm2-v1", &shdkey);
xfree (shadow_info);
xfree (pkbuf);
if (err)
{
log_error ("shadowing the key failed: %s\n", gpg_strerror (err));
return err;
}
len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
err = agent_write_private_key (grip, shdkey, len, 1 /*force*/);
xfree (shdkey);
if (err)
log_error ("error writing key: %s\n", gpg_strerror (err));
return err;
}
int
divert_tpm2_writekey (ctrl_t ctrl, const unsigned char *grip,
gcry_sexp_t s_skey)
{
TSS_CONTEXT *tssc;
int ret, pub_len, priv_len;
/* priv is always shielded so no special handling required */
char pub[sizeof(TPM2B_PUBLIC)], priv[sizeof(TPM2B_PRIVATE)];
ret = tpm2_start(&tssc);
if (ret)
return ret;
ret = tpm2_import_key (ctrl, tssc, pub, &pub_len, priv, &priv_len, s_skey);
if (ret)
goto out;
ret = agent_write_tpm2_shadow_key (ctrl, grip, TPM2_PARENT, pub, pub_len,
priv, priv_len);
out:
tpm2_end(tssc);
return ret;
}
int
divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
const unsigned char *cipher,
const unsigned char *shadow_info,
char **r_buf, size_t *r_len, int *r_padding)
{
TSS_CONTEXT *tssc;
TPM_HANDLE key;
TPMI_ALG_PUBLIC type;
int ret;
const unsigned char *s;
size_t n;
*r_padding = -1;
(void)desc_text;
s = cipher;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
s++;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
if (!smatch (&s, n, "enc-val"))
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
if (*s != '(')
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
s++;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
if (smatch (&s, n, "rsa"))
{
*r_padding = 0;
if (*s != '(')
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
s++;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
if (!smatch (&s, n, "a"))
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
n = snext (&s);
}
else if (smatch (&s, n, "ecdh"))
{
if (*s != '(')
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
s++;
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
if (smatch (&s, n, "s"))
{
n = snext (&s);
s += n;
if (*s++ != ')')
return gpg_error (GPG_ERR_INV_SEXP);
if (*s++ != '(')
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
n = snext (&s);
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
}
if (!smatch (&s, n, "e"))
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
n = snext (&s);
}
else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
/* know we have RSA to decrypt at s,n */
ret = tpm2_start(&tssc);
if (ret)
return ret;
ret = tpm2_load_key(tssc, shadow_info, &key, &type);
if (ret)
goto out;
if (type == TPM_ALG_RSA)
ret = tpm2_rsa_decrypt(ctrl, tssc, key, s, n, r_buf, r_len);
else if (type == TPM_ALG_ECC)
ret = tpm2_ecc_decrypt(ctrl, tssc, key, s, n, r_buf, r_len);
tpm2_flush_handle(tssc, key);
out:
tpm2_end(tssc);
return ret;
}