diff --git a/sm/decrypt.c b/sm/decrypt.c index 3689ed388..e4ead49e8 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -78,7 +78,7 @@ string_from_gcry_buffer (gcry_buffer_t *buffer) * entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL, * suppPubInfo [2] EXPLICIT OCTET STRING } * as described in RFC-5753, 7.2. */ -gpg_error_t +static gpg_error_t hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd, const char *wrap_algo_str, unsigned int keylen, const void *ukm, unsigned int ukmlen) @@ -135,6 +135,63 @@ hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd, const char *wrap_algo_str, } + +/* Derive a KEK (key wrapping key) using (SECRET,SECRETLEN), an + * optional (UKM,ULMLEN), the wrap algorithm WRAP_ALGO_STR in decimal + * dotted form, and the hash algorithm HASH_ALGO. On success a key of + * length KEYLEN is stored at KEY. */ +gpg_error_t +ecdh_derive_kek (unsigned char *key, unsigned int keylen, + int hash_algo, const char *wrap_algo_str, + const void *secret, unsigned int secretlen, + const void *ukm, unsigned int ukmlen) +{ + gpg_error_t err = 0; + unsigned int hashlen; + gcry_md_hd_t hash_hd; + unsigned char counter; + unsigned int n, ncopy; + + hashlen = gcry_md_get_algo_dlen (hash_algo); + if (!hashlen) + return gpg_error (GPG_ERR_INV_ARG); + + err = gcry_md_open (&hash_hd, hash_algo, 0); + if (err) + return err; + + /* According to SEC1 3.6.1 we should check that + * SECRETLEN + UKMLEN + 4 < maxhashlen + * However, we have no practical limit on the hash length and thus + * there is no point in checking this. The second check that + * KEYLEN < hashlen*(2^32-1) + * is obviously also not needed. + */ + for (n=0, counter=1; n < keylen; counter++) + { + if (counter > 1) + gcry_md_reset (hash_hd); + gcry_md_write (hash_hd, secret, secretlen); + gcry_md_write (hash_hd, "\x00\x00\x00", 3); /* MSBs of counter */ + gcry_md_write (hash_hd, &counter, 1); + err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, + ukm, ukmlen); + if (err) + break; + gcry_md_final (hash_hd); + if (n + hashlen > keylen) + ncopy = keylen - n; + else + ncopy = hashlen; + memcpy (key+n, gcry_md_read (hash_hd, 0), ncopy); + n += ncopy; + } + + gcry_md_close (hash_hd); + return err; +} + + /* This function will modify SECRET. NBITS is the size of the curve * which which we took from the certificate. */ static gpg_error_t @@ -151,7 +208,7 @@ ecdh_decrypt (unsigned char *secret, size_t secretlen, unsigned int ukmlen; const unsigned char *data; /* Alias for ioarray[3]. */ unsigned int datalen; - unsigned int keylen, hashlen; + unsigned int keylen; unsigned char key[32]; gcry_cipher_hd_t cipher_hd = NULL; unsigned char *result = NULL; @@ -234,19 +291,21 @@ ecdh_decrypt (unsigned char *secret, size_t secretlen, { /* dhSinglePass-stdDH-sha256kdf-scheme */ hash_algo = GCRY_MD_SHA256; - hashlen = 32; } else if (!strcmp (encr_algo_str, "1.3.132.1.11.2")) { /* dhSinglePass-stdDH-sha384kdf-scheme */ hash_algo = GCRY_MD_SHA384; - hashlen = 48; } else if (!strcmp (encr_algo_str, "1.3.132.1.11.3")) { /* dhSinglePass-stdDH-sha512kdf-scheme */ hash_algo = GCRY_MD_SHA512; - hashlen = 64; + } + else if (!strcmp (encr_algo_str, "1.3.133.16.840.63.0.2")) + { + /* dhSinglePass-stdDH-sha1kdf-scheme */ + hash_algo = GCRY_MD_SHA1; } else { @@ -275,33 +334,10 @@ ecdh_decrypt (unsigned char *secret, size_t secretlen, goto leave; } - /* Derive a KEK (key wrapping key) using MESSAGE and SECRET_X. - * According to SEC1 3.6.1 we should check that - * SECRETLEN + UKMLEN + 4 < maxhashlen - * However, we have no practical limit on the hash length and thus - * there is no point in checking this. The second check that - * KEYLEN < hashlen*(2^32-1) - * is obviously also not needed. Because with our allowed - * parameters KEYLEN is always less or equal to HASHLEN so that we - * do not need to iterate at all. - */ - log_assert (gcry_md_get_algo_dlen (hash_algo) == hashlen); - { - gcry_md_hd_t hash_hd; - err = gcry_md_open (&hash_hd, hash_algo, 0); - if (err) - goto leave; - gcry_md_write(hash_hd, secret, secretlen); - gcry_md_write(hash_hd, "\x00\x00\x00\x01", 4); /* counter */ - err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, - ukm, ukmlen); - gcry_md_final (hash_hd); - log_assert (keylen <= sizeof key && keylen <= hashlen); - memcpy (key, gcry_md_read (hash_hd, 0), keylen); - gcry_md_close (hash_hd); - if (err) - goto leave; - } + err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str, + secret, secretlen, ukm, ukmlen); + if (err) + goto leave; if (DBG_CRYPTO) log_printhex (key, keylen, "KEK .....:"); diff --git a/sm/encrypt.c b/sm/encrypt.c index 865b103c1..76bcfa23f 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -185,7 +185,7 @@ ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) const char *encr_algo_str; const char *wrap_algo_str; int hash_algo, cipher_algo; - unsigned int keylen, hashlen; + unsigned int keylen; unsigned char key[32]; gcry_sexp_t s_data = NULL; gcry_sexp_t s_encr = NULL; @@ -230,20 +230,28 @@ ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) curvebuf = NULL; /* Our mapping matches the recommended algorithms from RFC-5753 but - * not supporing the short curves which would require 3DES. */ + * not supporting the short curves which would require 3DES. */ if (curvebits < 255) { err = gpg_error (GPG_ERR_UNKNOWN_CURVE); log_error ("%s: curve '%s' is not supported\n", __func__, curve); goto leave; } + else if (opt.force_ecdh_sha1kdf) + { + /* dhSinglePass-stdDH-sha1kdf-scheme */ + encr_algo_str = "1.3.133.16.840.63.0.2"; + wrap_algo_str = "2.16.840.1.101.3.4.1.45"; + hash_algo = GCRY_MD_SHA1; + cipher_algo = GCRY_CIPHER_AES256; + keylen = 32; + } else if (curvebits <= 256) { /* dhSinglePass-stdDH-sha256kdf-scheme */ encr_algo_str = "1.3.132.1.11.1"; wrap_algo_str = "2.16.840.1.101.3.4.1.5"; hash_algo = GCRY_MD_SHA256; - hashlen = 32; cipher_algo = GCRY_CIPHER_AES128; keylen = 16; } @@ -253,7 +261,6 @@ ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) encr_algo_str = "1.3.132.1.11.2"; wrap_algo_str = "2.16.840.1.101.3.4.1.25"; hash_algo = GCRY_MD_SHA384; - hashlen = 48; cipher_algo = GCRY_CIPHER_AES256; keylen = 24; } @@ -263,7 +270,6 @@ ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) encr_algo_str = "1.3.132.1.11.3"; wrap_algo_str = "2.16.840.1.101.3.4.1.45"; hash_algo = GCRY_MD_SHA512; - hashlen = 64; cipher_algo = GCRY_CIPHER_AES256; keylen = 32; } @@ -338,32 +344,10 @@ ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) if (DBG_CRYPTO) log_printhex (secret, secretlen, "ECDH X ..:"); - /* Derive a KEK (key wrapping key) using MESSAGE and SECRET_X. - * According to SEC1 3.6.1 we should check that - * SECRETLEN + UKMLEN + 4 < maxhashlen - * However, we have no practical limit on the hash length and thus - * there is no point in checking this. The second check that - * KEYLEN < hashlen*(2^32-1) - * is obviously also not needed. Because with our allowed - * parameters KEYLEN is always less or equal to HASHLEN so that we - * do not need to iterate at all. - */ - log_assert (gcry_md_get_algo_dlen (hash_algo) == hashlen); - { - gcry_md_hd_t hash_hd; - err = gcry_md_open (&hash_hd, hash_algo, 0); - if (err) - goto leave; - gcry_md_write(hash_hd, secret, secretlen); - gcry_md_write(hash_hd, "\x00\x00\x00\x01", 4); /* counter */ - err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, NULL, 0); - gcry_md_final (hash_hd); - log_assert (keylen <= sizeof key && keylen <= hashlen); - memcpy (key, gcry_md_read (hash_hd, 0), keylen); - gcry_md_close (hash_hd); - if (err) - goto leave; - } + err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str, + secret, secretlen, NULL, 0); + if (err) + goto leave; if (DBG_CRYPTO) log_printhex (key, keylen, "KEK .....:"); diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 057ef50a1..541f904df 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -106,6 +106,7 @@ enum cmd_and_opt_values { oDebugAllowCoreDump, oDebugNoChainValidation, oDebugIgnoreExpiration, + oDebugForceECDHSHA1KDF, oLogFile, oNoLogFile, oAuditLog, @@ -270,6 +271,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"), ARGPARSE_s_n (oDebugNoChainValidation, "debug-no-chain-validation", "@"), ARGPARSE_s_n (oDebugIgnoreExpiration, "debug-ignore-expiration", "@"), + ARGPARSE_s_n (oDebugForceECDHSHA1KDF, "debug-force-ecdh-sha1kdf", "@"), ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write server mode logs to FILE")), ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"), @@ -1355,6 +1357,7 @@ main ( int argc, char **argv) break; case oDebugNoChainValidation: opt.no_chain_validation = 1; break; case oDebugIgnoreExpiration: opt.ignore_expiration = 1; break; + case oDebugForceECDHSHA1KDF: opt.force_ecdh_sha1kdf = 1; break; case oStatusFD: ctrl.status_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 1); diff --git a/sm/gpgsm.h b/sm/gpgsm.h index c4df8496e..9e4694a64 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -106,6 +106,8 @@ struct int forced_digest_algo; /* User forced hash algorithm. */ + int force_ecdh_sha1kdf; /* Only for debugging and testing. */ + char *def_recipient; /* userID of the default recipient */ int def_recipient_self; /* The default recipient is the default key */ @@ -392,10 +394,10 @@ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int in_fd, estream_t out_fp); /*-- decrypt.c --*/ -gpg_error_t hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd, - const char *wrap_algo_str, - unsigned int keylen, - const void *ukm, unsigned int ukmlen); +gpg_error_t ecdh_derive_kek (unsigned char *key, unsigned int keylen, + int hash_algo, const char *wrap_algo_str, + const void *secret, unsigned int secretlen, + const void *ukm, unsigned int ukmlen); int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp); /*-- certreqgen.c --*/