diff --git a/agent/Makefile.am b/agent/Makefile.am index 39e69cdd0..290ef1288 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -51,6 +51,7 @@ gpg_agent_SOURCES = \ protect.c \ trustlist.c \ divert-scd.c \ + divert-tpm2.c \ tpm2.c \ cvt-openpgp.c cvt-openpgp.h \ call-scd.c \ diff --git a/agent/agent.h b/agent/agent.h index e13ee1fb9..7a77eb629 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -417,6 +417,7 @@ gpg_error_t agent_public_key_from_file (ctrl_t ctrl, gcry_sexp_t *result); int agent_is_dsa_key (gcry_sexp_t s_key); int agent_is_eddsa_key (gcry_sexp_t s_key); +int agent_is_tpm2_key(gcry_sexp_t s_key); int agent_key_available (const unsigned char *grip); gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, int *r_keytype, @@ -533,6 +534,18 @@ gpg_error_t agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag); void agent_reload_trustlist (void); +/*-- divert-tpm2.c --*/ +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); +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); +int divert_tpm2_writekey (ctrl_t ctrl, const unsigned char *grip, + gcry_sexp_t s_skey); + /*-- divert-scd.c --*/ int divert_pksign (ctrl_t ctrl, const char *desc_text, diff --git a/agent/command.c b/agent/command.c index 32c12d93f..c439aa5f7 100644 --- a/agent/command.c +++ b/agent/command.c @@ -1192,6 +1192,11 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx, if (err) goto leave; } + else if (strcmp (shadow_info_type, "tpm2-v1") == 0) + { + serialno = xstrdup("TPM-Protected"); + idstr = NULL; + } else { log_error ("Unrecognised shadow key type %s\n", shadow_info_type); @@ -2577,6 +2582,57 @@ cmd_keytocard (assuan_context_t ctx, char *line) } + +static const char hlp_keytotpm[] = + "KEYTOTPM \n" + "\n"; +static gpg_error_t +cmd_keytotpm (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + unsigned char grip[20]; + gcry_sexp_t s_skey; + unsigned char *shadow_info = NULL; + + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + + err = parse_keygrip (ctx, line, grip); + if (err) + goto leave; + + if (agent_key_available (grip)) + { + err =gpg_error (GPG_ERR_NO_SECKEY); + goto leave; + } + + err = agent_key_from_file (ctrl, NULL, ctrl->server_local->keydesc, grip, + &shadow_info, CACHE_MODE_IGNORE, NULL, + &s_skey, NULL); + if (err) + { + xfree (shadow_info); + goto leave; + } + if (shadow_info) + { + /* Key is on a TPM or smartcard already. */ + xfree (shadow_info); + gcry_sexp_release (s_skey); + err = gpg_error (GPG_ERR_UNUSABLE_SECKEY); + goto leave; + } + + err = divert_tpm2_writekey (ctrl, grip, s_skey); + gcry_sexp_release (s_skey); + + leave: + return leave_cmd (ctx, err); +} + + static const char hlp_getval[] = "GETVAL \n" @@ -3243,6 +3299,7 @@ register_commands (assuan_context_t ctx) { "RELOADAGENT", cmd_reloadagent,hlp_reloadagent }, { "GETINFO", cmd_getinfo, hlp_getinfo }, { "KEYTOCARD", cmd_keytocard, hlp_keytocard }, + { "KEYTOTPM", cmd_keytotpm, hlp_keytotpm }, { NULL } }; int i, rc; diff --git a/agent/divert-tpm2.c b/agent/divert-tpm2.c new file mode 100644 index 000000000..dc3110d0b --- /dev/null +++ b/agent/divert-tpm2.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + int ret; + + ret = tpm2_start(&tssc); + if (ret) + return ret; + ret = tpm2_load_key(tssc, shadow_info, &key); + if (ret) + goto out; + ret = tpm2_sign(ctrl, tssc, key, 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; + int ret; + const unsigned char *s; + size_t n; + + *r_padding = 0; + + (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")) + { + 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 + 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); + if (ret) + goto out; + ret = tpm2_decrypt(ctrl, tssc, key, s, n, r_buf, r_len); + + tpm2_flush_handle(tssc, key); + + out: + tpm2_end(tssc); + return ret; + +} diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index 06a8e0b6f..6f766ca63 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -86,8 +86,12 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, goto leave; } - rc = divert_pkdecrypt (ctrl, desc_text, ciphertext, shadow_info, - &buf, &len, r_padding); + if (agent_is_tpm2_key (s_skey)) + rc = divert_tpm2_pkdecrypt (ctrl, desc_text, ciphertext, shadow_info, + &buf, &len, r_padding); + else + rc = divert_pkdecrypt (ctrl, desc_text, ciphertext, shadow_info, + &buf, &len, r_padding); if (rc) { log_error ("smartcard decryption failed: %s\n", gpg_strerror (rc)); diff --git a/agent/pksign.c b/agent/pksign.c index f54af0817..dae2638f4 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -353,10 +353,16 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, if (desc_text) agent_modify_description (desc_text, NULL, s_skey, &desc2); - err = divert_pksign (ctrl, desc2? desc2 : desc_text, - data, datalen, - ctrl->digest.algo, - shadow_info, &buf, &len); + if (agent_is_tpm2_key (s_skey)) + err = divert_tpm2_pksign (ctrl, desc2? desc2 : desc_text, + data, datalen, + ctrl->digest.algo, + shadow_info, &buf, &len); + else + err = divert_pksign (ctrl, desc2? desc2 : desc_text, + data, datalen, + ctrl->digest.algo, + shadow_info, &buf, &len); xfree (desc2); } if (err)