aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/divert-tpm2.c41
-rw-r--r--agent/tpm2.c251
-rw-r--r--agent/tpm2.h13
3 files changed, 280 insertions, 25 deletions
diff --git a/agent/divert-tpm2.c b/agent/divert-tpm2.c
index dc3110d0b..deb655a47 100644
--- a/agent/divert-tpm2.c
+++ b/agent/divert-tpm2.c
@@ -22,15 +22,16 @@ divert_tpm2_pksign (ctrl_t ctrl, const char *desc_text,
{
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);
+ ret = tpm2_load_key(tssc, shadow_info, &key, &type);
if (ret)
goto out;
- ret = tpm2_sign(ctrl, tssc, key, digest, digestlen, r_sig, r_siglen);
+ ret = tpm2_sign(ctrl, tssc, key, type, digest, digestlen, r_sig, r_siglen);
tpm2_flush_handle(tssc, key);
@@ -130,11 +131,12 @@ divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
{
TSS_CONTEXT *tssc;
TPM_HANDLE key;
+ TPMI_ALG_PUBLIC type;
int ret;
const unsigned char *s;
size_t n;
- *r_padding = 0;
+ *r_padding = -1;
(void)desc_text;
@@ -155,6 +157,7 @@ divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
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++;
@@ -165,6 +168,30 @@ divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
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);
@@ -173,10 +200,14 @@ divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
ret = tpm2_start(&tssc);
if (ret)
return ret;
- ret = tpm2_load_key(tssc, shadow_info, &key);
+ ret = tpm2_load_key(tssc, shadow_info, &key, &type);
if (ret)
goto out;
- ret = tpm2_decrypt(ctrl, tssc, key, s, n, r_buf, r_len);
+
+ 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);
diff --git a/agent/tpm2.c b/agent/tpm2.c
index 734f0fe74..ff5756038 100644
--- a/agent/tpm2.c
+++ b/agent/tpm2.c
@@ -313,7 +313,7 @@ parse_tpm2_shadow_info (const unsigned char *shadow_info,
int
tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
- TPM_HANDLE *key)
+ TPM_HANDLE *key, TPMI_ALG_PUBLIC *type)
{
uint32_t parent;
Load_In in;
@@ -339,6 +339,8 @@ tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
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,
@@ -358,7 +360,8 @@ tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
int
tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
- const unsigned char *digest, size_t digestlen,
+ TPMI_ALG_PUBLIC type,
+ const unsigned char *digest, size_t digestlen,
unsigned char **r_sig, size_t *r_siglen)
{
Sign_In in;
@@ -367,7 +370,6 @@ tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
/* The TPM insists on knowing the digest type, so
* calculate that from the size */
- in.inScheme.scheme = TPM_ALG_RSASSA;
switch (digestlen) {
case 20:
in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA1;
@@ -394,22 +396,181 @@ tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
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;
- *r_siglen = out.signature.signature.rsassa.sig.t.size;
+ 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;
- memcpy(*r_sig, out.signature.signature.rsassa.sig.t.buffer, *r_siglen);
+ 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(TPMT_SENSITIVE *s, gcry_sexp_t key)
+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;
@@ -433,7 +594,7 @@ sexp_to_tpm2_sensitive(TPMT_SENSITIVE *s, gcry_sexp_t key)
}
static int
-sexp_to_tpm2_public(TPMT_PUBLIC *p, gcry_sexp_t key)
+sexp_to_tpm2_public_rsa(TPMT_PUBLIC *p, gcry_sexp_t key)
{
gcry_mpi_t n, e;
gcry_sexp_t l;
@@ -506,12 +667,18 @@ sexp_to_tpm2(TPMT_PUBLIC *p, TPMT_SENSITIVE *s, gcry_sexp_t s_skey)
return rc;
l2 = gcry_sexp_find_token (l1, "rsa", 0);
- if (!l2)
- goto out;
-
- rc = sexp_to_tpm2_public(p, l2);
- if (!rc)
- rc = sexp_to_tpm2_sensitive(s, l2);
+ 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);
@@ -757,9 +924,61 @@ tpm2_import_key(ctrl_t ctrl, TSS_CONTEXT *tssc, char *pub, int *pub_len,
}
int
-tpm2_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
- const char *ciphertext, int ciphertext_len,
- char **decrypt, size_t *decrypt_len)
+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;
diff --git a/agent/tpm2.h b/agent/tpm2.h
index 2e168032f..7a63aab88 100644
--- a/agent/tpm2.h
+++ b/agent/tpm2.h
@@ -10,13 +10,18 @@ int tpm2_start(TSS_CONTEXT **tssc);
void tpm2_end(TSS_CONTEXT *tssc);
void tpm2_flush_handle(TSS_CONTEXT *tssc, TPM_HANDLE h);
int tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
- TPM_HANDLE *key);
+ TPM_HANDLE *key, TPMI_ALG_PUBLIC *type);
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);
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);
-int tpm2_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
- const char *ciphertext, int ciphertext_len,
- char **decrypt, size_t *decrypt_len);
+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);
+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);
+
#endif