From: David Woodhouse Date: Sat, 9 Apr 2022 21:49:56 +0000 (+0100) Subject: Add GnuTLS crypto support for HPKE X-Git-Tag: v9.00~56 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=02ca865dd3bdea5a4f8f0335880697f8ca43fc21;p=users%2Fdwmw2%2Fopenconnect.git Add GnuTLS crypto support for HPKE We'll need to explicitly link against libhogweed and maybe also libgmp. Signed-off-by: David Woodhouse --- diff --git a/configure.ac b/configure.ac index 8fe5ddf1..33b3e12f 100644 --- a/configure.ac +++ b/configure.ac @@ -631,6 +631,9 @@ perhaps consider building with GnuTLS instead.)]) pkcs11_support=GnuTLS AC_SUBST(P11KIT_PC, p11-kit-1)], [:])], []) + # From GnuTLS 3.6.13 + AC_CHECK_FUNC(gnutls_hkdf_expand, [have_hkdf=yes], [have_hkdf=no]) + LIBS="-ltspi $oldlibs" AC_MSG_CHECKING([for Trousers tss library]) AC_LINK_IFELSE([AC_LANG_PROGRAM([ @@ -643,9 +646,32 @@ perhaps consider building with GnuTLS instead.)]) AC_SUBST([TSS_CFLAGS], []) AC_DEFINE(HAVE_TROUSERS, 1, [Have Trousers TSS library])], [AC_MSG_RESULT(no)]) + LIBS="$oldlibs" CFLAGS="$oldcflags" + if test "$have_hkdf" = "yes"; then + PKG_CHECK_MODULES(HOGWEED, [hogweed], + [AC_MSG_CHECKING([For hogweed built-in mini-gmp]) + LIBS="$oldlibs $HOGWEED_LIBS" + CFLAGS="$oldcflags $HOGWEED_CFLAGS" + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [mpz_clear((void *)0);])], + [AC_MSG_RESULT(yes) + AC_SUBST(HPKE_CFLAGS, ['$(HOGWEED_FLAGS)']) + AC_SUBST(HPKE_LIBS, ['$(HOGWEED_LIBS)']) + hpke=yes], + [AC_MSG_RESULT(no) + PKG_CHECK_MODULES(GMP, [gmp], + [hpke=yes + AC_SUBST(HPKE_CFLAGS, ['$(HOGWEED_FLAGS) $(GMP_CFLAGS)']) + AC_SUBST(HPKE_LIBS, ['$(HOGWEED_LIBS) $(GMP_LIBS)'])], [:])]) + LIBS="$oldlibs" + CFLAGS="$oldcflags"], + [:]) + + fi + PKG_CHECK_MODULES(TASN1, [libtasn1], [have_tasn1=yes], [have_tasn1=no]) if test "$have_tasn1" = "yes"; then if test "$with_gnutls_tss2" = "yes" -o "$with_gnutls_tss2" = "tss2-esys" -o "$with_gnutls_tss2" = ""; then @@ -688,8 +714,8 @@ perhaps consider building with GnuTLS instead.)]) AC_DEFINE(OPENCONNECT_GNUTLS, 1, [Using GnuTLS]) AC_SUBST(SSL_PC, [gnutls]) - AC_SUBST(SSL_LIBS, ['$(GNUTLS_LIBS) $(TPM2_LIBS)']) - AC_SUBST(SSL_CFLAGS, ['$(GNUTLS_CFLAGS) $(TPM2_CFLAGS)']) + AC_SUBST(SSL_LIBS, ['$(GNUTLS_LIBS) $(TPM2_LIBS) $(HKPE_LIBS)']) + AC_SUBST(SSL_CFLAGS, ['$(GNUTLS_CFLAGS) $(TPM2_CFLAGS) $(HKPE_CFLAGS)']) ;; *) diff --git a/gnutls.c b/gnutls.c index 6d8abd79..c637262a 100644 --- a/gnutls.c +++ b/gnutls.c @@ -2887,3 +2887,163 @@ void free_strap_keys(struct openconnect_info *vpninfo) vpninfo->strap_key = vpninfo->strap_dh_key = NULL; } + +#ifdef HAVE_HPKE_SUPPORT + +#include +#include + +int ecdh_compute_secp256r1(struct openconnect_info *vpninfo, const unsigned char *pubkey_der, + int pubkey_len, unsigned char *secret) +{ + int err, ret = -EIO; + gnutls_pubkey_t pubkey; + gnutls_datum_t d = { (void *)pubkey_der, pubkey_len }; + + if ((err = gnutls_pubkey_init(&pubkey)) || + (err = gnutls_pubkey_import(pubkey, &d, GNUTLS_X509_FMT_DER))) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to decode server DH key: %s\n"), + gnutls_strerror(err)); + goto out_pubkey; + } + + /* Yay, we have to do ECDH for ourselves. */ + gnutls_datum_t pub_x, pub_y, priv_k; + gnutls_ecc_curve_t pub_curve, priv_curve; + + if ((err = gnutls_privkey_export_ecc_raw(vpninfo->strap_dh_key, &priv_curve, + NULL, NULL, &priv_k))) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to export DH private key parameters: %s\n"), + gnutls_strerror(err)); + goto out_pubkey; + } + if ((err = gnutls_pubkey_export_ecc_raw(pubkey, &pub_curve, &pub_x, &pub_y))) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to export server DH key parameters: %s\n"), + gnutls_strerror(err)); + goto out_priv_data; + } + + if (pub_curve != GNUTLS_ECC_CURVE_SECP256R1 || + priv_curve != GNUTLS_ECC_CURVE_SECP256R1) { + vpn_progress(vpninfo, PRG_ERR, + _("HPKE uses unsupported EC curve (%d, %d)\n"), + pub_curve, priv_curve); + goto out_pub_data; + } + + mpz_t mx, my; + nettle_mpz_init_set_str_256_u(mx, pub_x.size, pub_x.data); + nettle_mpz_init_set_str_256_u(my, pub_y.size, pub_y.data); + + struct ecc_point point; + ecc_point_init(&point, nettle_get_secp_256r1()); + if (!ecc_point_set(&point, mx, my)) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to create ECC public point for ECDH\n")); + goto out_point; + } + + mpz_t mk; + nettle_mpz_init_set_str_256_u(mk, priv_k.size, priv_k.data); + + struct ecc_scalar priv; + ecc_scalar_init(&priv, nettle_get_secp_256r1()); + ecc_scalar_set(&priv, mk); + + ecc_point_mul(&point, &priv, &point); + ecc_point_get(&point, mx, my); + + nettle_mpz_get_str_256(32, secret, mx); + + ret = 0; + + ecc_scalar_clear(&priv); + mpz_clear(mk); + out_point: + ecc_point_clear(&point); + mpz_clear(mx); + mpz_clear(my); + out_pub_data: + gnutls_free(pub_x.data); + gnutls_free(pub_y.data); + out_priv_data: + gnutls_free(priv_k.data); + out_pubkey: + gnutls_pubkey_deinit(pubkey); + + return ret; +} + +int hkdf_sha256_extract_expand(struct openconnect_info *vpninfo, unsigned char *buf, + const char *info, int infolen) +{ + gnutls_datum_t d; + d.data = buf; + d.size = SHA256_SIZE; + + int err = gnutls_hkdf_extract(GNUTLS_MAC_SHA256, &d, NULL, buf); + if (err) { + vpn_progress(vpninfo, PRG_ERR, + _("HKDF extract failed: %s\n"), + gnutls_strerror(err)); + return -EIO; + } + + gnutls_datum_t info_d; + info_d.data = (void *)info; + info_d.size = infolen; + + err = gnutls_hkdf_expand(GNUTLS_MAC_SHA256, &d, &info_d, d.data, d.size); + if (err) { + vpn_progress(vpninfo, PRG_ERR, + _("HKDF expand failed: %s\n"), + gnutls_strerror(err)); + return -EIO; + } + return 0; +} + +int aes_256_gcm_decrypt(struct openconnect_info *vpninfo, unsigned char *key, + unsigned char *data, int len, + unsigned char *iv, unsigned char *tag) + { + gnutls_cipher_hd_t h = NULL; + + gnutls_datum_t d = { key, SHA256_SIZE }; + gnutls_datum_t iv_d = { iv, 12 }; + + int err = gnutls_cipher_init(&h, GNUTLS_CIPHER_AES_256_GCM, &d, &iv_d); + if (err) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to init AES-256-GCM cipher: %s\n"), + gnutls_strerror(err)); + return -EIO; + } + + err = gnutls_cipher_decrypt(h, data, len); + if (err) { + dec_fail: + vpn_progress(vpninfo, PRG_ERR, + _("SSO token decryption failed: %s\n"), + gnutls_strerror(err)); + gnutls_cipher_deinit(h); + return -EIO; + } + + /* Reusing the key buffer to fetch the auth tag */ + err = gnutls_cipher_tag(h, d.data, 12); + if (err) + goto dec_fail; + + if (memcmp(d.data, tag, 12)) { + err = GNUTLS_E_MAC_VERIFY_FAILED; + goto dec_fail; + } + + gnutls_cipher_deinit(h); + return 0; +} +#endif /* HAVE_HPKE_SUPPORT */