From 830f14c37605ab58a11aa4c83046f2e6459dfaff Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 11 May 2021 21:49:33 +0100 Subject: [PATCH] Tell TPMv2 the hash type based on size The TPM doesn't really have any business knowing this, and the only thing that matters is the size. Which is truncated to the curve bit length even for larger hashes. Signed-off-by: David Woodhouse --- gnutls_tpm2.c | 8 ++++---- gnutls_tpm2_esys.c | 24 +++++++++++++++++------- gnutls_tpm2_ibm.c | 25 ++++++++++++++++++------- openconnect-internal.h | 2 ++ 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/gnutls_tpm2.c b/gnutls_tpm2.c index 467cd1d1..a3ba3cdb 100644 --- a/gnutls_tpm2.c +++ b/gnutls_tpm2.c @@ -84,10 +84,10 @@ static int tpm2_ec_sign_fn(gnutls_privkey_t key, void *_certinfo, gnutls_sign_algorithm_t algo; switch (data->size) { - case 20: algo = GNUTLS_SIGN_ECDSA_SHA1; break; - case 32: algo = GNUTLS_SIGN_ECDSA_SHA256; break; - case 48: algo = GNUTLS_SIGN_ECDSA_SHA384; break; - case 64: algo = GNUTLS_SIGN_ECDSA_SHA512; break; + case SHA1_SIZE: algo = GNUTLS_SIGN_ECDSA_SHA1; break; + case SHA256_SIZE: algo = GNUTLS_SIGN_ECDSA_SHA256; break; + case SHA384_SIZE: algo = GNUTLS_SIGN_ECDSA_SHA384; break; + case SHA512_SIZE: algo = GNUTLS_SIGN_ECDSA_SHA512; break; default: vpn_progress(vpninfo, PRG_ERR, _("Unknown TPM2 EC digest size %d\n"), diff --git a/gnutls_tpm2_esys.c b/gnutls_tpm2_esys.c index f462222f..a57b843f 100644 --- a/gnutls_tpm2_esys.c +++ b/gnutls_tpm2_esys.c @@ -482,15 +482,25 @@ int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo, _("TPM2 EC sign function called for %d bytes.\n"), data->size); - switch (algo) { - case GNUTLS_SIGN_ECDSA_SHA1: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA1; break; - case GNUTLS_SIGN_ECDSA_SHA256: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA256; break; - case GNUTLS_SIGN_ECDSA_SHA384: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA384; break; - case GNUTLS_SIGN_ECDSA_SHA512: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA512; break; + /* FIPS-186-4 §6.4 says "When the length of the output of the hash + * function is greater than the bit length of n, then the leftmost + * n bits of the hash function output block shall be used in any + * calculation using the hash function output during the generation + * or verification of a digital signature." + * + * So GnuTLS is expected to *truncate* a larger hash to fit the + * curve bit length, and then we lie to the TPM about which hash + * it was because the TPM only really cares about the size of the + * data anyway. */ + switch (data->size) { + case SHA1_SIZE: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA1; break; + case SHA256_SIZE: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA256; break; + case SHA384_SIZE: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA384; break; + case SHA512_SIZE: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA512; break; default: vpn_progress(vpninfo, PRG_ERR, - _("Unknown TPM2 EC digest size %d\n"), - data->size); + _("Unknown TPM2 EC digest size %d for algo 0x%x\n"), + data->size, algo); return GNUTLS_E_PK_SIGN_FAILED; } diff --git a/gnutls_tpm2_ibm.c b/gnutls_tpm2_ibm.c index 0232e314..fdf6fe56 100644 --- a/gnutls_tpm2_ibm.c +++ b/gnutls_tpm2_ibm.c @@ -420,17 +420,28 @@ int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo, memset(&in, 0, sizeof(in)); - switch (algo) { - case GNUTLS_SIGN_ECDSA_SHA1: in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA1; break; - case GNUTLS_SIGN_ECDSA_SHA256: in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA256; break; - case GNUTLS_SIGN_ECDSA_SHA384: in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA384; break; + + /* FIPS-186-4 §6.4 says "When the length of the output of the hash + * function is greater than the bit length of n, then the leftmost + * n bits of the hash function output block shall be used in any + * calculation using the hash function output during the generation + * or verification of a digital signature." + * + * So GnuTLS is expected to *truncate* a larger hash to fit the + * curve bit length, and then we lie to the TPM about which hash + * it was because the TPM only really cares about the size of the + * data anyway. */ + switch (data->size) { + case SHA1_SIZE: in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA1; break; + case SHA256_SIZE: in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA256; break; + case SHA384_SIZE: in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA384; break; #ifdef TPM_ALG_SHA512 - case GNUTLS_SIGN_ECDSA_SHA512: in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA512; break; + case SHA512_SIZE: in.inScheme.details.ecdsa.hashAlg = TPM_ALG_SHA512; break; #endif default: vpn_progress(vpninfo, PRG_ERR, - _("Unknown TPM2 EC digest size %d\n"), - data->size); + _("Unknown TPM2 EC digest size %d for algo 0x%x\n"), + data->size, algo); return GNUTLS_E_PK_SIGN_FAILED; } diff --git a/openconnect-internal.h b/openconnect-internal.h index 5869ec8a..1ba62fbd 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -120,6 +120,8 @@ #include +#define SHA512_SIZE 64 +#define SHA384_SIZE 48 #define SHA256_SIZE 32 #define SHA1_SIZE 20 #define MD5_SIZE 16 -- 2.50.1