Hello all,

Attached is my second go at a patch for pinning public keys with a hash.
 It now supports all backends public key pinning supported except for
GSKit, which simply needs a sha256 function (can someone help me out
here?  By writing/testing preferably, or pointing me to docs?).

Here is the status of the backend support (all compiled/tested under
Linux, Ubuntu 14.04):
OpenSSL/GnuTLS-with-gcrypt: Written/Compiled/Tested
NSS: Written/Compiled
CyaSSL(WolfSSL)/GnuTLS-with-nettle: Written
GSKit: No Support

Still waiting to hear back on this question:

On 05/29/2015 03:31 PM, moparisthebest wrote:
> 1. Is it safe to re-use the existing curlopt, the code treats it as a
> hash only if it starts with "sha256/" and nothing else, and then will
> not look on the filesystem for a file at all.  I suppose this could
> break systems where a der/pem is in a folder named 'sha256/' with no
> leading path parts, but I feel like that's a safe bet?  (And by 'break'
> I mean fail-closed, it'll fail to connect with 'curl: (90) SSL: public
> key does not match pinned public key!')

After which I can update documentation, write tests, and hopefully have
a final patch.

Thanks much,
Travis Burtrum
From 30c7c8d281c41ebb0a586fb1db0b8b28ec8d4985 Mon Sep 17 00:00:00 2001
From: moparisthebest <[email protected]>
Date: Sun, 31 May 2015 22:39:36 -0400
Subject: [PATCH] SSL: Pinned public key hash support, for comments

---
 lib/vtls/cyassl.c  | 13 +++++++++++
 lib/vtls/cyassl.h  |  5 ++++
 lib/vtls/gtls.c    | 20 ++++++++++++++++
 lib/vtls/gtls.h    |  5 ++++
 lib/vtls/nss.c     | 13 +++++++++++
 lib/vtls/nssg.h    |  6 +++++
 lib/vtls/openssl.c | 12 ++++++++++
 lib/vtls/openssl.h |  5 ++++
 lib/vtls/vtls.c    | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/vtls/vtls.h    |  4 ++++
 10 files changed, 150 insertions(+)

diff --git a/lib/vtls/cyassl.c b/lib/vtls/cyassl.c
index 40dbbe1..3ded7f1 100644
--- a/lib/vtls/cyassl.c
+++ b/lib/vtls/cyassl.c
@@ -67,6 +67,7 @@ and that's a problem since options.h hasn't been included yet. */
 #include <cyassl/error.h>
 #endif
 #include <cyassl/ctaocrypt/random.h>
+#include <cyassl/ctaocrypt/sha256.h>
 
 /* The last #include files should be: */
 #include "curl_memory.h"
@@ -770,4 +771,16 @@ int Curl_cyassl_random(struct SessionHandle *data,
   return 0;
 }
 
+void Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */
+                      size_t tmplen,
+                      unsigned char *sha256sum /* output */,
+                      size_t unused)
+{
+  Sha256 SHA256pw;
+  (void)unused;
+  InitSha256(&SHA256pw);
+  Sha256Update(&SHA256pw, tmp, tmplen);
+  Sha256Final(&SHA256pw, sha256sum);
+}
+
 #endif
diff --git a/lib/vtls/cyassl.h b/lib/vtls/cyassl.h
index 12638a7..447816b 100644
--- a/lib/vtls/cyassl.h
+++ b/lib/vtls/cyassl.h
@@ -42,6 +42,10 @@ CURLcode Curl_cyassl_connect_nonblocking(struct connectdata *conn,
 int Curl_cyassl_random(struct SessionHandle *data,
                        unsigned char *entropy,
                        size_t length);
+void Curl_cyassl_sha256sum(unsigned char *tmp, /* input */
+                     size_t tmplen,
+                     unsigned char *sha256sum, /* output */
+                     size_t unused);
 
 /* Set the API backend definition to Schannel */
 #define CURL_SSL_BACKEND CURLSSLBACKEND_CYASSL
@@ -65,6 +69,7 @@ int Curl_cyassl_random(struct SessionHandle *data,
 #define curlssl_check_cxn(x) ((void)x, -1)
 #define curlssl_data_pending(x,y) Curl_cyassl_data_pending(x,y)
 #define curlssl_random(x,y,z) Curl_cyassl_random(x,y,z)
+#define curlssl_sha256sum(a,b,c,d) Curl_cyassl_sha256sum(a,b,c,d)
 
 #endif /* USE_CYASSL */
 #endif /* HEADER_CURL_CYASSL_H */
diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
index d6eb6c4..cbe6542 100644
--- a/lib/vtls/gtls.c
+++ b/lib/vtls/gtls.c
@@ -39,6 +39,7 @@
 #ifdef USE_GNUTLS_NETTLE
 #include <gnutls/crypto.h>
 #include <nettle/md5.h>
+#include <nettle/sha2.h>
 #else
 #include <gcrypt.h>
 #endif
@@ -1557,6 +1558,25 @@ void Curl_gtls_md5sum(unsigned char *tmp, /* input */
 #endif
 }
 
+void Curl_gtls_sha256sum(const unsigned char *tmp, /* input */
+                      size_t tmplen,
+                      unsigned char *sha256sum, /* output */
+                      size_t sha256len)
+{
+#if defined(USE_GNUTLS_NETTLE)
+  struct sha256_ctx SHA256pw;
+  sha256_init(&SHA256pw);
+  sha256_update(&SHA256pw, (unsigned int)tmplen, tmp);
+  sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum);
+#elif defined(USE_GNUTLS)
+  gcry_md_hd_t SHA256pw;
+  gcry_md_open(&SHA256pw, GCRY_MD_SHA256, 0);
+  gcry_md_write(SHA256pw, tmp, tmplen);
+  memcpy(sha256sum, gcry_md_read (SHA256pw, 0), sha256len);
+  gcry_md_close(SHA256pw);
+#endif
+}
+
 bool Curl_gtls_cert_status_request(void)
 {
 #ifdef HAS_OCSP
diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h
index dcae442..0afd9b9 100644
--- a/lib/vtls/gtls.h
+++ b/lib/vtls/gtls.h
@@ -48,6 +48,10 @@ void Curl_gtls_md5sum(unsigned char *tmp, /* input */
                       size_t tmplen,
                       unsigned char *md5sum, /* output */
                       size_t md5len);
+void Curl_gtls_sha256sum(const unsigned char *tmp, /* input */
+                      size_t tmplen,
+                      unsigned char *sha256sum, /* output */
+                      size_t sha256len);
 
 bool Curl_gtls_cert_status_request(void);
 
@@ -77,6 +81,7 @@ bool Curl_gtls_cert_status_request(void);
 #define curlssl_data_pending(x,y) ((void)x, (void)y, 0)
 #define curlssl_random(x,y,z) Curl_gtls_random(x,y,z)
 #define curlssl_md5sum(a,b,c,d) Curl_gtls_md5sum(a,b,c,d)
+#define curlssl_sha256sum(a,b,c,d) Curl_gtls_sha256sum(a,b,c,d)
 #define curlssl_cert_status_request() Curl_gtls_cert_status_request()
 
 #endif /* USE_GNUTLS */
diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c
index 5434ce3..91727c7 100644
--- a/lib/vtls/nss.c
+++ b/lib/vtls/nss.c
@@ -2041,6 +2041,19 @@ void Curl_nss_md5sum(unsigned char *tmp, /* input */
   PK11_DestroyContext(MD5pw, PR_TRUE);
 }
 
+void Curl_nss_sha256sum(const unsigned char *tmp, /* input */
+                     size_t tmplen,
+                     unsigned char *sha256sum, /* output */
+                     size_t sha256len)
+{
+  PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256);
+  unsigned int SHA256out;
+
+  PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen));
+  PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len));
+  PK11_DestroyContext(SHA256pw, PR_TRUE);
+}
+
 bool Curl_nss_cert_status_request(void)
 {
 #ifdef SSL_ENABLE_OCSP_STAPLING
diff --git a/lib/vtls/nssg.h b/lib/vtls/nssg.h
index d0e7412..5fd7275 100644
--- a/lib/vtls/nssg.h
+++ b/lib/vtls/nssg.h
@@ -56,6 +56,11 @@ void Curl_nss_md5sum(unsigned char *tmp, /* input */
                      unsigned char *md5sum, /* output */
                      size_t md5len);
 
+void Curl_nss_sha256sum(const unsigned char *tmp, /* input */
+                     size_t tmplen,
+                     unsigned char *sha256sum, /* output */
+                     size_t sha256len);
+
 bool Curl_nss_cert_status_request(void);
 
 bool Curl_nss_false_start(void);
@@ -89,6 +94,7 @@ bool Curl_nss_false_start(void);
 #define curlssl_data_pending(x,y) ((void)x, (void)y, 0)
 #define curlssl_random(x,y,z) Curl_nss_random(x,y,z)
 #define curlssl_md5sum(a,b,c,d) Curl_nss_md5sum(a,b,c,d)
+#define curlssl_sha256sum(a,b,c,d) Curl_nss_sha256sum(a,b,c,d)
 #define curlssl_cert_status_request() Curl_nss_cert_status_request()
 #define curlssl_false_start() Curl_nss_false_start()
 
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index 96a7d6e..8931e2c 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -3237,6 +3237,18 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */
   MD5_Final(md5sum, &MD5pw);
 }
 
+void Curl_ossl_sha256sum(const unsigned char *tmp, /* input */
+                      size_t tmplen,
+                      unsigned char *sha256sum /* output */,
+                      size_t unused)
+{
+  SHA256_CTX SHA256pw;
+  (void)unused;
+  SHA256_Init(&SHA256pw);
+  SHA256_Update(&SHA256pw, tmp, tmplen);
+  SHA256_Final(sha256sum, &SHA256pw);
+}
+
 bool Curl_ossl_cert_status_request(void)
 {
 #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h
index 499b4fe..ebac542 100644
--- a/lib/vtls/openssl.h
+++ b/lib/vtls/openssl.h
@@ -72,6 +72,10 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */
                       size_t tmplen,
                       unsigned char *md5sum /* output */,
                       size_t unused);
+void Curl_ossl_sha256sum(const unsigned char *tmp, /* input */
+                      size_t tmplen,
+                      unsigned char *sha256sum /* output */,
+                      size_t unused);
 
 bool Curl_ossl_cert_status_request(void);
 
@@ -104,6 +108,7 @@ bool Curl_ossl_cert_status_request(void);
 #define curlssl_data_pending(x,y) Curl_ossl_data_pending(x,y)
 #define curlssl_random(x,y,z) Curl_ossl_random(x,y,z)
 #define curlssl_md5sum(a,b,c,d) Curl_ossl_md5sum(a,b,c,d)
+#define curlssl_sha256sum(a,b,c,d) Curl_ossl_sha256sum(a,b,c,d)
 #define curlssl_cert_status_request() Curl_ossl_cert_status_request()
 
 #define DEFAULT_CIPHER_SELECTION \
diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
index 42a2b58..a38a1a5 100644
--- a/lib/vtls/vtls.c
+++ b/lib/vtls/vtls.c
@@ -774,12 +774,79 @@ CURLcode Curl_pin_peer_pubkey(const char *pinnedpubkey,
   size_t size, pem_len;
   CURLcode pem_read;
   CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+#ifdef curlssl_sha256sum
+  size_t pinkeylen;
+  char *pinkeycopy, *begin_pos, *end_pos;
+  unsigned char *sha256sumdigest = NULL, *expectedsha256sumdigest = NULL;
+#endif
 
   /* if a path wasn't specified, don't pin */
   if(!pinnedpubkey)
     return CURLE_OK;
   if(!pubkey || !pubkeylen)
     return result;
+
+#ifdef curlssl_sha256sum
+  /* only do this if pinnedpubkey starts with "sha256/" */
+  begin_pos = strstr(pinnedpubkey, "sha256/");
+  if(begin_pos && 0 == (begin_pos - pinnedpubkey)) {
+    /* compute sha256sum of public key */
+    sha256sumdigest = malloc(SHA256_DIGEST_LENGTH);
+    if(!sha256sumdigest)
+      return CURLE_OUT_OF_MEMORY;
+    curlssl_sha256sum(pubkey, pubkeylen,
+                      sha256sumdigest, SHA256_DIGEST_LENGTH);
+
+    /* it starts with sha256/, copy so we can modify it */
+    pinkeylen = strlen(pinnedpubkey) + 1;
+    pinkeycopy = malloc(pinkeylen);
+    if(!pinkeycopy) {
+      Curl_safefree(sha256sumdigest);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    memcpy(pinkeycopy, pinnedpubkey, pinkeylen);
+    /* point begin_pos to the copy, and start extracting keys */
+    begin_pos = pinkeycopy;
+    do {
+      end_pos = strstr(begin_pos, ";sha256/");
+      /*
+       * if there is an end_pos, null terminate,
+       * otherwise it'll go to the end of the original string
+       */
+      if(end_pos)
+        end_pos[0] = '\0';
+
+      /* decode base64 pinnedpubkey, 7 is length of "sha256/" */
+      pem_read = Curl_base64_decode(begin_pos + 7,
+                                    &expectedsha256sumdigest, &size);
+      /* if not valid base64, don't bother comparing or freeing */
+      if(!pem_read) {
+        /* compare sha256 digests directly */
+        if(SHA256_DIGEST_LENGTH == size &&
+           !memcmp(sha256sumdigest, expectedsha256sumdigest,
+                   SHA256_DIGEST_LENGTH)) {
+          result = CURLE_OK;
+          Curl_safefree(expectedsha256sumdigest);
+          break;
+        }
+        Curl_safefree(expectedsha256sumdigest);
+      }
+
+      /*
+       * change back the null-terminator we changed earlier,
+       * and look for next begin
+       */
+      if(end_pos) {
+        end_pos[0] = ';';
+        begin_pos = strstr(end_pos, "sha256/");
+      }
+    } while(end_pos && begin_pos);
+    Curl_safefree(sha256sumdigest);
+    Curl_safefree(pinkeycopy);
+    return result;
+  }
+#endif
+
   fp = fopen(pinnedpubkey, "rb");
   if(!fp)
     return result;
diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h
index b9741d7..2349e5b 100644
--- a/lib/vtls/vtls.h
+++ b/lib/vtls/vtls.h
@@ -41,6 +41,10 @@
 #define MD5_DIGEST_LENGTH 16 /* fixed size */
 #endif
 
+#ifndef SHA256_DIGEST_LENGTH
+#define SHA256_DIGEST_LENGTH 32 /* fixed size */
+#endif
+
 /* see http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-04 */
 #define ALPN_HTTP_1_1_LENGTH 8
 #define ALPN_HTTP_1_1 "http/1.1"
-- 
1.9.2

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html

Reply via email to