repo: tlswrap
action: commit
revision: 
path_from: 
revision_from: 2d0a4485eb028d224682f73ec9440b53250dc52f:
path_to: 
revision_to: 
git.thebackupbox.net
tlswrap
git clone git://git.thebackupbox.net/tlswrap
commit 2d0a4485eb028d224682f73ec9440b53250dc52f
Author: epoch 
Date:   Sat Apr 19 09:16:15 2025 +0000

    moved the ja3 code into its own file

diff --git a/Makefile b/Makefile
index d2d79331ffd720317210b632b02098c78b1ffdd1..
index ..2cec71083e454c600dee5b96d892eb56bc64a3df 100644
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,7 @@ tlswrap: LDLIBS=-lssl -lcrypto

 tlswrap-ja3: CFLAGS=-DJA3 -pedantic -Wall
 tlswrap-ja3: LDLIBS=-lssl -lcrypto
+tlswrap-ja3: tlswrap-ja3.o ja3.o

 tlswrap-no-shutdown: CFLAGS=-DNOSHUTDOWN -pedantic -Wall
 tlswrap-no-shutdown: LDLIBS=-lssl -lcrypto
diff --git a/ja3.c b/ja3.c
new file mode 100644
index 0000000000000000000000000000000000000000..2ee62235f78c6fe13fbc73d545a7c58a4c8e19e6
--- /dev/null
+++ b/ja3.c
@@ -0,0 +1,217 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "ja3.h"
+
+/*
+   hey, epoch, if you ever feel like finishing the JA3 shit, link below
+   https://github.com/fooinha/nginx-ssl-ja3/tree/master/src
+ */
+
+static const int nid_list[] = {
+    NID_sect163k1,        /* sect163k1 (1) */
+    NID_sect163r1,        /* sect163r1 (2) */
+    NID_sect163r2,        /* sect163r2 (3) */
+    NID_sect193r1,        /* sect193r1 (4) */
+    NID_sect193r2,        /* sect193r2 (5) */
+    NID_sect233k1,        /* sect233k1 (6) */
+    NID_sect233r1,        /* sect233r1 (7) */
+    NID_sect239k1,        /* sect239k1 (8) */
+    NID_sect283k1,        /* sect283k1 (9) */
+    NID_sect283r1,        /* sect283r1 (10) */
+    NID_sect409k1,        /* sect409k1 (11) */
+    NID_sect409r1,        /* sect409r1 (12) */
+    NID_sect571k1,        /* sect571k1 (13) */
+    NID_sect571r1,        /* sect571r1 (14) */
+    NID_secp160k1,        /* secp160k1 (15) */
+    NID_secp160r1,        /* secp160r1 (16) */
+    NID_secp160r2,        /* secp160r2 (17) */
+    NID_secp192k1,        /* secp192k1 (18) */
+    NID_X9_62_prime192v1, /* secp192r1 (19) */
+    NID_secp224k1,        /* secp224k1 (20) */
+    NID_secp224r1,        /* secp224r1 (21) */
+    NID_secp256k1,        /* secp256k1 (22) */
+    NID_X9_62_prime256v1, /* secp256r1 (23) */
+    NID_secp384r1,        /* secp384r1 (24) */
+    NID_secp521r1,        /* secp521r1 (25) */
+    NID_brainpoolP256r1,  /* brainpoolP256r1 (26) */
+    NID_brainpoolP384r1,  /* brainpoolP384r1 (27) */
+    NID_brainpoolP512r1,  /* brainpool512r1 (28) */
+    NID_X25519,           /* X25519 (29) */
+    NID_X448,             /* X448 (30) */
+};
+
+
+static unsigned short
+tlswrap_ssl_ja3_nid_to_cid(int nid)
+{
+    unsigned char i;
+    unsigned char sz = (sizeof(nid_list) / sizeof(nid_list[0]));
+
+    for (i = 0; i < sz; i++) {
+        if (nid == nid_list[i]) {
+            return i+1;
+        }
+    }
+
+    if (nid == NID_ffdhe2048) {
+        return 0x100;
+    }
+    if (nid == NID_ffdhe3072) {
+        return 0x101;
+    }
+    if (nid == NID_ffdhe4096) {
+        return 0x102;
+    }
+    if (nid == NID_ffdhe6144) {
+        return 0x103;
+    }
+    if (nid == NID_ffdhe8192) {
+        return 0x104;
+    }
+
+    return nid;
+}
+
+
+// I probably could just export these values to env vars and do the actual concatenation and md5 externally
+void ja3_shit(struct ja3 *j) { // how do we get ssl version?
+  int sz=4096;
+  char *s = malloc(sz);
+  int offset=snprintf(s,sz,"%u,",j->version);
+  int i;
+
+  for(i=0;i < j->ciphers_sz;i++) offset += snprintf(s+offset,sz,"%u-",ntohs(j->ciphers[i]));
+  if(j->ciphers_sz) { offset-- ; } s[offset]=','; offset++;
+
+  for(i=0;i < j->extensions_sz;i++) offset += snprintf(s+offset,sz,"%u-",j->extensions[i]);
+  if(j->extensions_sz) { offset-- ; } s[offset]=','; offset++;
+
+  for(i=0;i < j->curves_sz;i++) offset += snprintf(s+offset,sz,"%u-",j->curves[i]);
+  if(j->curves_sz) { offset-- ; } s[offset]=','; offset++;
+
+  for(i=0;i < j->point_formats_sz;i++) offset += snprintf(s+offset,sz,"%u-",j->point_formats[i]);
+  if(j->point_formats_sz) { offset-- ; } s[offset]='\0'; offset++;
+  // I tested the hashing code. it works compared to https://github.com/salesforce/ja3/tree/master/python
+
+
+  // these functions are deprecated too.
+  /*MD5_CTX c;
+  unsigned char p[16];
+  MD5_Init(&c);
+  MD5_Update(&c, s, strlen(s));
+  MD5_Final(p, &c);*/
+
+  // looked up what the non-deprecated way was supposed to be.
+  // https://github.com/openssl/openssl/discussions/23493
+#if OPENSSL_VERSION_MAJOR == 3
+  unsigned char p[16];
+  if(!EVP_Q_digest( NULL, "MD5", NULL, s, strlen(s), p, NULL )) {
+    syslog(LOG_DAEMON|LOG_ERR, "MD5 hashing failed: %s",strerror(errno));
+  }
+#else
+  // MD5() is deprecated.
+  unsigned char *p=MD5((unsigned char *)s,strlen(s),NULL);
+#endif
+
+  char q[33];
+  for(i=0;i<16;i++) {
+    snprintf(q+(i*2),sizeof(q),"%02x",p[i]);
+  }
+  q[33]=0;
+  //printf("input: %s\n",s);
+  //printf("md5sum: %s\n",q);
+//  syslog(LOG_DAEMON|LOG_CRIT,"ja3:%s",s);
+//  syslog(LOG_DAEMON|LOG_CRIT,"ja3 hashed: %s",q); //let's put these into env vars. :D
+  setenv("SSL_JA3",s,1);
+  setenv("SSL_JA3_DIGEST",q,1);
+  // MD5 the string
+}
+
+void tlswrap_SSL_client_features(struct ja3 *j, SSL *s) {
+
+    unsigned short                *ciphers_out = NULL;
+    int                           *curves_out = NULL;
+    int                           *point_formats_out = NULL;
+    size_t                         i = 0;
+    size_t                         len = 0, sz = 0;
+
+    if (j == NULL) return;
+    j->version = SSL_version(s);
+
+    /* Cipher suites */
+    j->ciphers = NULL;
+    j->ciphers_sz = SSL_get0_raw_cipherlist(s, &ciphers_out);
+    j->ciphers_sz /= 2;
+
+    if (j->ciphers_sz && ciphers_out) {
+        len = j->ciphers_sz * sizeof(unsigned short);
+        j->ciphers = malloc(len);
+        memcpy(j->ciphers, ciphers_out, len);
+    }
+
+    /* Elliptic curve points */
+
+    j->curves_sz = 0;
+    sz = SSL_get1_curves(s, NULL);
+    if (sz) {
+        len = sz * sizeof(unsigned int);
+        curves_out = malloc(len);
+        if (curves_out != NULL) {
+            memset(curves_out, 0, len);
+            SSL_get1_curves(s, curves_out);
+//	    for(i=0 ; i < sz; i++ ){
+//                if( ! (curves_out[i] & 0x1000000)) { //unknown. skip.
+//                     j->curves_sz++;
+//		}
+//	    }
+            j->curves_sz = sz;
+            len = j->curves_sz * sizeof(unsigned short);
+            j->curves = malloc(len);
+            if (j->curves != NULL) {
+                for (i = 0; i < sz; i++) {
+                     //if( ! (curves_out[i] & 0x1000000)) { //unknown. skip?
+                         j->curves[i] = (unsigned short) tlswrap_ssl_ja3_nid_to_cid( curves_out[i]);
+		         //syslog(LOG_DAEMON|LOG_CRIT,"%d",curves_out[i]);
+		     //}
+                }
+            }
+            free(curves_out);
+        }
+    }
+
+    /* Elliptic curve point formats */
+    j->point_formats_sz = SSL_get0_ec_point_formats(s, &point_formats_out);
+    if (j->point_formats_sz && point_formats_out != NULL) {
+        len = j->point_formats_sz * sizeof(unsigned char);
+        j->point_formats = malloc(len);
+        if (j->point_formats != NULL) {
+            memcpy(j->point_formats, point_formats_out, len);
+        }
+    }
+}
+
+int tlswrap_SSL_early_cb_fn(SSL *s, int *al, void *arg) {
+    int                            got_extensions;
+    int                           *ext_out;
+    size_t                         ext_len;
+    struct ja3 *j = arg;
+    //syslog(LOG_DAEMON|LOG_CRIT,"got into the early callback.");
+    got_extensions = SSL_client_hello_get1_extensions_present(s,
+                                                       &ext_out,
+                                                       &ext_len);
+    if (!got_extensions) return 1;
+    if (!ext_out) return 1;
+    if (!ext_len) return 1;
+    j->extensions = malloc(sizeof(int) * ext_len);
+    if (j->extensions != NULL) {
+        j->extensions_sz = ext_len;
+        memcpy(j->extensions, ext_out, sizeof(int) * ext_len);
+    }
+    OPENSSL_free(ext_out);
+    //syslog(LOG_DAEMON|LOG_CRIT,"got to the end of the early callback.");
+    return 1;
+}
diff --git a/ja3.h b/ja3.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b1d51db8b556c9f0990629dce571cd08c994b0d
--- /dev/null
+++ b/ja3.h
@@ -0,0 +1,24 @@
+#ifndef _TLSWRAP_JA3_H_
+#define _TLSWRAP_JA3_H_
+
+//
+
+struct ja3 {
+    int version;
+
+    size_t ciphers_sz;
+    unsigned short *ciphers;
+
+    size_t extensions_sz;
+    unsigned int *extensions;
+
+    size_t curves_sz;
+    unsigned short *curves;
+
+    size_t point_formats_sz;
+    unsigned char *point_formats;
+};
+
+int tlswrap_SSL_early_cb_fn(SSL *s, int *al, void *arg);
+void ja3_shit(struct ja3 *j);
+#endif
diff --git a/tlswrap.c b/tlswrap.c
index f96eb93e6db3abd00c1b1c1d08f36d067767fd30..
index ..eebc30968b76a003858aaec17958cc96e9e12889 100644
--- a/tlswrap.c
+++ b/tlswrap.c
@@ -22,6 +22,10 @@
 #include 
 #include 

+#ifdef JA3
+#include "ja3.h"
+#endif
+
 //#define TRAFFIC_LOGGING "1.2.3.4" // use this to debug exact request and response data going on. no timestamps inside yet.
 //#define FORCE_SNI //whether SNI is required to connect to this server.

@@ -224,236 +228,6 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) {
   return 1;
 }

-//#define JA3
-#ifdef JA3
-
-/*
-   hey, epoch, if you ever feel like finishing the JA3 shit, link below
-   https://github.com/fooinha/nginx-ssl-ja3/tree/master/src
- */
-
-
-static const int nid_list[] = {
-    NID_sect163k1,        /* sect163k1 (1) */
-    NID_sect163r1,        /* sect163r1 (2) */
-    NID_sect163r2,        /* sect163r2 (3) */
-    NID_sect193r1,        /* sect193r1 (4) */
-    NID_sect193r2,        /* sect193r2 (5) */
-    NID_sect233k1,        /* sect233k1 (6) */
-    NID_sect233r1,        /* sect233r1 (7) */
-    NID_sect239k1,        /* sect239k1 (8) */
-    NID_sect283k1,        /* sect283k1 (9) */
-    NID_sect283r1,        /* sect283r1 (10) */
-    NID_sect409k1,        /* sect409k1 (11) */
-    NID_sect409r1,        /* sect409r1 (12) */
-    NID_sect571k1,        /* sect571k1 (13) */
-    NID_sect571r1,        /* sect571r1 (14) */
-    NID_secp160k1,        /* secp160k1 (15) */
-    NID_secp160r1,        /* secp160r1 (16) */
-    NID_secp160r2,        /* secp160r2 (17) */
-    NID_secp192k1,        /* secp192k1 (18) */
-    NID_X9_62_prime192v1, /* secp192r1 (19) */
-    NID_secp224k1,        /* secp224k1 (20) */
-    NID_secp224r1,        /* secp224r1 (21) */
-    NID_secp256k1,        /* secp256k1 (22) */
-    NID_X9_62_prime256v1, /* secp256r1 (23) */
-    NID_secp384r1,        /* secp384r1 (24) */
-    NID_secp521r1,        /* secp521r1 (25) */
-    NID_brainpoolP256r1,  /* brainpoolP256r1 (26) */
-    NID_brainpoolP384r1,  /* brainpoolP384r1 (27) */
-    NID_brainpoolP512r1,  /* brainpool512r1 (28) */
-    NID_X25519,           /* X25519 (29) */
-    NID_X448,             /* X448 (30) */
-};
-
-
-static unsigned short
-tlswrap_ssl_ja3_nid_to_cid(int nid)
-{
-    unsigned char i;
-    unsigned char sz = (sizeof(nid_list) / sizeof(nid_list[0]));
-
-    for (i = 0; i < sz; i++) {
-        if (nid == nid_list[i]) {
-            return i+1;
-        }
-    }
-
-    if (nid == NID_ffdhe2048) {
-        return 0x100;
-    }
-    if (nid == NID_ffdhe3072) {
-        return 0x101;
-    }
-    if (nid == NID_ffdhe4096) {
-        return 0x102;
-    }
-    if (nid == NID_ffdhe6144) {
-        return 0x103;
-    }
-    if (nid == NID_ffdhe8192) {
-        return 0x104;
-    }
-
-    return nid;
-}
-
-
-struct ja3 {
-    int version;
-
-    size_t ciphers_sz;
-    unsigned short *ciphers;
-
-    size_t extensions_sz;
-    unsigned int *extensions;
-
-    size_t curves_sz;
-    unsigned short *curves;
-
-    size_t point_formats_sz;
-    unsigned char *point_formats;
-};
-
-// I probably could just export these values to env vars and do the actual concatenation and md5 externally
-void ja3_shit(struct ja3 *j) { // how do we get ssl version?
-  int sz=4096;
-  char *s = malloc(sz);
-  int offset=snprintf(s,sz,"%u,",j->version);
-  int i;
-
-  for(i=0;i < j->ciphers_sz;i++) offset += snprintf(s+offset,sz,"%u-",ntohs(j->ciphers[i]));
-  if(j->ciphers_sz) { offset-- ; } s[offset]=','; offset++;
-
-  for(i=0;i < j->extensions_sz;i++) offset += snprintf(s+offset,sz,"%u-",j->extensions[i]);
-  if(j->extensions_sz) { offset-- ; } s[offset]=','; offset++;
-
-  for(i=0;i < j->curves_sz;i++) offset += snprintf(s+offset,sz,"%u-",j->curves[i]);
-  if(j->curves_sz) { offset-- ; } s[offset]=','; offset++;
-
-  for(i=0;i < j->point_formats_sz;i++) offset += snprintf(s+offset,sz,"%u-",j->point_formats[i]);
-  if(j->point_formats_sz) { offset-- ; } s[offset]='\0'; offset++;
-  // I tested the hashing code. it works compared to https://github.com/salesforce/ja3/tree/master/python
-
-
-  // these functions are deprecated too.
-  /*MD5_CTX c;
-  unsigned char p[16];
-  MD5_Init(&c);
-  MD5_Update(&c, s, strlen(s));
-  MD5_Final(p, &c);*/
-
-  // looked up what the non-deprecated way was supposed to be.
-  // https://github.com/openssl/openssl/discussions/23493
-#if OPENSSL_VERSION_MAJOR == 3
-  unsigned char p[16];
-  if(!EVP_Q_digest( NULL, "MD5", NULL, s, strlen(s), p, NULL )) {
-    syslog(LOG_DAEMON|LOG_ERR, "MD5 hashing failed: %s",strerror(errno));
-  }
-#else
-  // MD5() is deprecated.
-  unsigned char *p=MD5((unsigned char *)s,strlen(s),NULL);
-#endif
-
-  char q[33];
-  for(i=0;i<16;i++) {
-    snprintf(q+(i*2),sizeof(q),"%02x",p[i]);
-  }
-  q[33]=0;
-  //printf("input: %s\n",s);
-  //printf("md5sum: %s\n",q);
-//  syslog(LOG_DAEMON|LOG_CRIT,"ja3:%s",s);
-//  syslog(LOG_DAEMON|LOG_CRIT,"ja3 hashed: %s",q); //let's put these into env vars. :D
-  setenv("SSL_JA3",s,1);
-  setenv("SSL_JA3_DIGEST",q,1);
-  // MD5 the string
-}
-
-void tlswrap_SSL_client_features(struct ja3 *j, SSL *s) {
-
-    unsigned short                *ciphers_out = NULL;
-    int                           *curves_out = NULL;
-    int                           *point_formats_out = NULL;
-    size_t                         i = 0;
-    size_t                         len = 0, sz = 0;
-
-    if (j == NULL) return;
-    j->version = SSL_version(s);
-
-    /* Cipher suites */
-    j->ciphers = NULL;
-    j->ciphers_sz = SSL_get0_raw_cipherlist(s, &ciphers_out);
-    j->ciphers_sz /= 2;
-
-    if (j->ciphers_sz && ciphers_out) {
-        len = j->ciphers_sz * sizeof(unsigned short);
-        j->ciphers = malloc(len);
-        memcpy(j->ciphers, ciphers_out, len);
-    }
-
-    /* Elliptic curve points */
-
-    j->curves_sz = 0;
-    sz = SSL_get1_curves(s, NULL);
-    if (sz) {
-        len = sz * sizeof(unsigned int);
-        curves_out = malloc(len);
-        if (curves_out != NULL) {
-            memset(curves_out, 0, len);
-            SSL_get1_curves(s, curves_out);
-//	    for(i=0 ; i < sz; i++ ){
-//                if( ! (curves_out[i] & 0x1000000)) { //unknown. skip.
-//                     j->curves_sz++;
-//		}
-//	    }
-            j->curves_sz = sz;
-            len = j->curves_sz * sizeof(unsigned short);
-            j->curves = malloc(len);
-            if (j->curves != NULL) {
-                for (i = 0; i < sz; i++) {
-                     //if( ! (curves_out[i] & 0x1000000)) { //unknown. skip?
-                         j->curves[i] = (unsigned short) tlswrap_ssl_ja3_nid_to_cid( curves_out[i]);
-		         //syslog(LOG_DAEMON|LOG_CRIT,"%d",curves_out[i]);
-		     //}
-                }
-            }
-            free(curves_out);
-        }
-    }
-
-    /* Elliptic curve point formats */
-    j->point_formats_sz = SSL_get0_ec_point_formats(s, &point_formats_out);
-    if (j->point_formats_sz && point_formats_out != NULL) {
-        len = j->point_formats_sz * sizeof(unsigned char);
-        j->point_formats = malloc(len);
-        if (j->point_formats != NULL) {
-            memcpy(j->point_formats, point_formats_out, len);
-        }
-    }
-}
-
-int tlswrap_SSL_early_cb_fn(SSL *s, int *al, void *arg) {
-    int                            got_extensions;
-    int                           *ext_out;
-    size_t                         ext_len;
-    struct ja3 *j = arg;
-    //syslog(LOG_DAEMON|LOG_CRIT,"got into the early callback.");
-    got_extensions = SSL_client_hello_get1_extensions_present(s,
-                                                       &ext_out,
-                                                       &ext_len);
-    if (!got_extensions) return 1;
-    if (!ext_out) return 1;
-    if (!ext_len) return 1;
-    j->extensions = malloc(sizeof(int) * ext_len);
-    if (j->extensions != NULL) {
-        j->extensions_sz = ext_len;
-        memcpy(j->extensions, ext_out, sizeof(int) * ext_len);
-    }
-    OPENSSL_free(ext_out);
-    //syslog(LOG_DAEMON|LOG_CRIT,"got to the end of the early callback.");
-    return 1;
-}
-#endif
 #endif /* #ifndef NONTLS */

 void signal_handler(int sig) {

-----END OF PAGE-----