1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-06 23:17:47 +02:00

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>
This commit is contained in:
James Bottomley 2018-03-05 11:18:15 -08:00 committed by Werner Koch
parent c4c7b7d7ba
commit 72ece35fb7
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 280 additions and 25 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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