]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Add OpenSSL crypto support for HKPE
authorDavid Woodhouse <dwmw2@infradead.org>
Sat, 9 Apr 2022 21:47:53 +0000 (22:47 +0100)
committerDavid Woodhouse <dwmw2@infradead.org>
Mon, 11 Apr 2022 13:50:18 +0000 (14:50 +0100)
We need ECDH derivation, HKDF-SHA256, and AES-256-GCM decryption.

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
configure.ac
openconnect-internal.h
openssl.c

index 8d6d343ff082681e068a658f62dd96eb2d2a88de..8fe5ddf16668413b51680d91bcb82f9e59b05ca2 100644 (file)
@@ -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 <openssl/ssl.h>
+                                        #include <openssl/kdf.h>],
+                                       [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])
index a14a825275de76282750be135b47421fa9058647..449d29403854fb178cd18dac892cf006fe5fe17d 100644 (file)
@@ -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);
index 7085429cc742a906a7c0cc3fcd2e0d83c0d8790d..e8f57c97f4eadb9c6ada5d2631b547e1c649db28 100644 (file)
--- 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 <openssl/kdf.h>
+
+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 */