From 60c54dafbb172da90de8db3f6344730317d3f650 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 9 Apr 2022 22:47:53 +0100 Subject: [PATCH] Add OpenSSL crypto support for HKPE We need ECDH derivation, HKDF-SHA256, and AES-256-GCM decryption. Signed-off-by: David Woodhouse --- configure.ac | 14 ++++++++ openconnect-internal.h | 7 ++++ openssl.c | 77 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) diff --git a/configure.ac b/configure.ac index 8d6d343f..8fe5ddf1 100644 --- a/configure.ac +++ b/configure.ac @@ -378,6 +378,7 @@ AC_ARG_WITH([openssl], ssl_library= esp= dtls= +hpke= if test "$with_openssl" != "" -a "$with_openssl" != "no"; then if test "$with_gnutls" = ""; then @@ -579,6 +580,15 @@ perhaps consider building with GnuTLS instead.)]) AC_DEFINE(HAVE_SSL_CIPHER_FIND, [1], [OpenSSL has SSL_CIPHER_find() function])], [AC_MSG_RESULT(no)]) + AC_MSG_CHECKING([for HKDF support in OpenSSL]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include + #include ], + [EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + EVP_PKEY_CTX_hkdf_mode(ctx, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND);])], + [AC_MSG_RESULT(yes) + hpke=yes], + [AC_MSG_RESULT(no)]) + LIBS="${oldLIBS}" CFLAGS="${oldCFLAGS}" @@ -783,6 +793,9 @@ fi if test "$dtls" != ""; then AC_DEFINE(HAVE_DTLS, 1, [Build with DTLS support]) fi +if test "$hpke" != ""; then + AC_DEFINE(HAVE_HPKE_SUPPORT, 1, [Support Cisco external browser HPKE (ECDH+HKDF+AES-256-GCM)]) +fi AC_ARG_WITH(lz4, AS_HELP_STRING([--without-lz4], [disable support for LZ4 compression]), @@ -1370,6 +1383,7 @@ if test "$ssl_library" = "GnuTLS"; then fi SUMMARY([DTLS support], [$dtls]) SUMMARY([ESP support], [$esp]) +SUMMARY([HPKE support], [$hpke]) SUMMARY([libproxy support], [$libproxy_pkg]) SUMMARY([RSA SecurID support], [$libstoken_pkg]) SUMMARY([PSKC OATH file support], [$libpskc_pkg]) diff --git a/openconnect-internal.h b/openconnect-internal.h index a14a8252..449d2940 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -1433,6 +1433,13 @@ int openconnect_install_ctx_verify(struct openconnect_info *vpninfo, #endif void free_strap_keys(struct openconnect_info *vpninfo); int generate_strap_keys(struct openconnect_info *vpninfo); +int ecdh_compute_secp256r1(struct openconnect_info *vpninfo, const unsigned char *pubkey, + int pubkey_len, unsigned char *secret); +int hkdf_sha256_extract_expand(struct openconnect_info *vpninfo, unsigned char *buf, + const char *info, int infolen); +int aes_256_gcm_decrypt(struct openconnect_info *vpninfo, unsigned char *key, + unsigned char *data, int len, + unsigned char *iv, unsigned char *tag); /* mainloop.c */ int tun_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable, int did_work); diff --git a/openssl.c b/openssl.c index 7085429c..e8f57c97 100644 --- a/openssl.c +++ b/openssl.c @@ -2320,3 +2320,80 @@ void free_strap_keys(struct openconnect_info *vpninfo) vpninfo->strap_key = vpninfo->strap_dh_key = NULL; } + +#ifdef HAVE_HPKE_SUPPORT + +#include + +int ecdh_compute_secp256r1(struct openconnect_info *vpninfo, const unsigned char *pubkey, + int pubkey_len, unsigned char *secret) +{ + const EC_POINT *point; + EC_KEY *pkey; + int ret = 0; + + if (!(pkey = d2i_EC_PUBKEY(NULL, &pubkey, pubkey_len)) || + !(point = EC_KEY_get0_public_key(pkey))) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to decode server DH key\n")); + openconnect_report_ssl_errors(vpninfo); + ret = -EIO; + goto out; + + } + + /* Perform the DH secret derivation from our STRAP-DH key + * and the one the server returned to us in the payload. */ + if (ECDH_compute_key(secret, 32, point, vpninfo->strap_dh_key, NULL) <= 0) { + vpn_progress(vpninfo, PRG_ERR, _("Failed to compute DH secret\n")); + openconnect_report_ssl_errors(vpninfo); + ret = -EIO; + } + out: + EC_KEY_free(pkey); + return ret; +} + +int hkdf_sha256_extract_expand(struct openconnect_info *vpninfo, unsigned char *buf, + const char *info, int infolen) +{ + size_t buflen = 32; + int ret = 0; + + /* Next, use HKDF to generate the actual key used for encryption. */ + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (!ctx || !EVP_PKEY_derive_init(ctx) || + !EVP_PKEY_CTX_set_hkdf_md(ctx, EVP_sha256()) || + !EVP_PKEY_CTX_set1_hkdf_key(ctx, buf, buflen) || + !EVP_PKEY_CTX_hkdf_mode(ctx, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) || + !EVP_PKEY_CTX_add1_hkdf_info(ctx, info, infolen) || + EVP_PKEY_derive(ctx, buf, &buflen) != 1) { + vpn_progress(vpninfo, PRG_ERR, _("HKDF key derivation failed\n")); + openconnect_report_ssl_errors(vpninfo); + ret = -EINVAL; + } + EVP_PKEY_CTX_free(ctx); + return ret; +} + +int aes_256_gcm_decrypt(struct openconnect_info *vpninfo, unsigned char *key, + unsigned char *data, int len, + unsigned char *iv, unsigned char *tag) +{ + /* Finally, we actually decrypt the sso-token */ + EVP_CIPHER_CTX *cctx = EVP_CIPHER_CTX_new(); + int ret = 0, i = 0; + + if (!cctx || + !EVP_DecryptInit_ex(cctx, EVP_aes_256_gcm(), NULL, key, iv) || + !EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_SET_TAG, 12, tag) || + !EVP_DecryptUpdate(cctx, data, &len, data, len) || + !EVP_DecryptFinal(cctx, NULL, &i)) { + vpn_progress(vpninfo, PRG_ERR, _("SSO token decryption failed\n")); + openconnect_report_ssl_errors(vpninfo); + ret = -EINVAL; + } + EVP_CIPHER_CTX_free(cctx); + return ret; +} +#endif /* HAVE_HPKE_SUPPORT */ -- 2.49.0