Category Archives: Amateur Radio

HamFox: Forking Firefox for Fun and no Profit

Null Cipher

The radio amateur service does not allow encryption of communications. This is a fundamental rule meant to ensure that the amateur service is not used for unintended purposes, such as commercial ones.

As a consequence, we cannot transmit passwords securely over the amateur frequencies. But we can use cryptographic authentication. Just not anything that would be used to hide the contents of the communications.

Cryptographic authentication is something that HTTPS/TLS allows out-of-the-box. We just need to make sure that we are not using encryption. In this article, we’ll see how to do this with Mozilla Firefox.

Forking Firefox

The code base of Firefox is an unwieldy Mercurial repository. Maintaining a fork would be cumbersome.

Git has won. And learning Mercurial for a small project adds quite a bit of friction, and I could not host on GitHub or GitLab. Of course, I can use git-remote-hg, but this adds another layer of abstraction, and another source of problems. I tried both, but it turned out it did not matter.

Since the official build process assumes that you are using the official repository with the official URLs and so forth, forking the repository itself would require also patching the build process. And I really did not want to touch that.

Another approach is to make a repository that just contains the patches to apply to the Firefox codebase, and a script that fetches the Firefox repository, applies the patches, and builds the project. This is gluon.

If you look into the HamFox repository, you will see that it contains just a few small files. The most important one is the null cipher patch itself.

The Patch

Grepping through the code for the TLS names of the cipher, we uncover an array sCipherPrefs that lists the supported ciphers. Here is an extract:

static const CipherPref sCipherPrefs[] = {

    {"security.ssl3.ecdhe_rsa_aes_128_gcm_sha256",
     TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
     StaticPrefs::security_ssl3_ecdhe_rsa_aes_128_gcm_sha256},

    {"security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256",
     TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
     StaticPrefs::security_ssl3_ecdhe_ecdsa_aes_128_gcm_sha256},

    //...
};

Each entry in the array is a CipherPref. The definition is given right above the array:

// Table of pref names and SSL cipher ID
typedef struct {
  const char* pref;
  int32_t id;
  bool (*prefGetter)();
} CipherPref;

The first field is the string used to toggle the cipher in the about:config page. There is no point is creating a toggle for this cipher, so we’ll just leave it empty.

The second field is the identifier of the cipher; we find that the macros are defined in security/nss/lib/ssl/sslproto.h. When looking for the null ciphers, we find:

$ grep NULL security/nss/lib/ssl/sslproto.h
#define SSL_NULL_WITH_NULL_NULL                TLS_NULL_WITH_NULL_NULL
#define SSL_RSA_WITH_NULL_MD5                  TLS_RSA_WITH_NULL_MD5
#define SSL_RSA_WITH_NULL_SHA                  TLS_RSA_WITH_NULL_SHA
#define TLS_NULL_WITH_NULL_NULL                 0x0000
#define TLS_RSA_WITH_NULL_MD5                   0x0001
#define TLS_RSA_WITH_NULL_SHA                   0x0002
#define TLS_RSA_WITH_NULL_SHA256                0x003B
#define TLS_ECDH_ECDSA_WITH_NULL_SHA            0xC001
#define TLS_ECDHE_ECDSA_WITH_NULL_SHA           0xC006
#define TLS_ECDH_RSA_WITH_NULL_SHA              0xC00B
#define TLS_ECDHE_RSA_WITH_NULL_SHA             0xC010
#define TLS_ECDH_anon_WITH_NULL_SHA             0xC015
#define SRTP_NULL_HMAC_SHA1_80                  0x0005
#define SRTP_NULL_HMAC_SHA1_32                  0x0006
#define SSL_FORTEZZA_DMS_WITH_NULL_SHA          0x001c

The SSL_* ones are just aliases for the TLS_* ones. We do want authentication. So, this eliminates TLS_NULL_WITH_NULL_NULL. We then have the choice between MD5, SHA-1 and SHA-256, and I opted for the last one, so TLS_RSA_WITH_NULL_SHA256.

The third one is a pointer to the function that tells us whether the toggle is enabled or not. There is no toggle. But we can just write a function that always returns true.

We replace the contents of the array with a single entry containing these fields, and we’re good!

To minimize the risk of conflict when the upstream code changes, I have put the other ciphers in a block comment instead of removing them altogether from the code. With this, we get:

diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp
index 5844ffecfd..e2da79480a 100644
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -1054,9 +1054,15 @@ typedef struct {
   bool (*prefGetter)();
 } CipherPref;
 
+bool AlwaysTrue(void) {
+  return true;
+}
+
 // Update the switch statement in AccumulateCipherSuite in nsNSSCallbacks.cpp
 // when you add/remove cipher suites here.
 static const CipherPref sCipherPrefs[] = {
+    {"", TLS_RSA_WITH_NULL_SHA256, AlwaysTrue},
+    /*
     {"security.ssl3.ecdhe_rsa_aes_128_gcm_sha256",
      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
      StaticPrefs::security_ssl3_ecdhe_rsa_aes_128_gcm_sha256},
@@ -1103,6 +1109,7 @@ static const CipherPref sCipherPrefs[] = {
      StaticPrefs::security_ssl3_rsa_aes_128_sha},
     {"security.ssl3.rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA,
      StaticPrefs::security_ssl3_rsa_aes_256_sha},
+    */
 };
 
 // These ciphersuites can only be enabled if deprecated versions of TLS are