From 338637ac08c19708eb35523894b44bbe3c726cfa Mon Sep 17 00:00:00 2001
From: quentin <quentin@minster.io>
Date: Mon, 2 Apr 2018 18:07:50 +0200
Subject: [PATCH] crda: Fix for OpenSSL 1.1.0: BIGNUM now opaque

OpenSSL 1.1.0 makes most of OpenSSL's structures opaque, and provides
functions to manipulate them. This means it's no longer possible to
construct an OpenSSL BIGNUM directly from scratch, as was done in
keys-ssl.c.

Use BN_bin2bn() (available since OpenSSL 0.9.8) to build the bignum from
its big-endian representation as a byte array.

This also allows factoring the code in utils/key2pub.py as it's now the
same mechanism as with libgcrypt.

This was tested with OpenSSL 1.1.0g.

Signed-off-by: Quentin Minster <quentin@minster.io>
---
 Makefile         |  12 +++----
 reglib.c         |  44 +++++++++++++++++------
 utils/key2pub.py | 107 ++++++-------------------------------------------------
 3 files changed, 49 insertions(+), 114 deletions(-)

diff --git a/Makefile b/Makefile
index a3ead30..a4e7373 100644
--- a/Makefile
+++ b/Makefile
@@ -38,18 +38,16 @@ all: all_noverify verify
 
 all_noverify: $(LIBREG) crda intersect regdbdump db2rd optimize
 
+$(LIBREG): keys.c
+
 ifeq ($(USE_OPENSSL),1)
 CFLAGS += -DUSE_OPENSSL -DPUBKEY_DIR=\"$(RUNTIME_PUBKEY_DIR)\" `pkg-config --cflags openssl`
 LDLIBS += `pkg-config --libs openssl`
 
-$(LIBREG): keys-ssl.c
-
 else
 CFLAGS += -DUSE_GCRYPT
 LDLIBS += -lgcrypt
 
-$(LIBREG): keys-gcrypt.c
-
 endif
 MKDIR ?= mkdir -p
 INSTALL ?= install
@@ -109,10 +107,10 @@ $(REG_BIN):
 	$(NQ)
 	$(Q) exit 1
 
-keys-%.c: utils/key2pub.py $(wildcard $(PUBKEY_DIR)/*.pem)
+keys.c: utils/key2pub.py $(wildcard $(PUBKEY_DIR)/*.pem)
 	$(NQ) '  GEN ' $@
 	$(NQ) '  Trusted pubkeys:' $(wildcard $(PUBKEY_DIR)/*.pem)
-	$(Q)./utils/key2pub.py --$* $(wildcard $(PUBKEY_DIR)/*.pem) $@
+	$(Q)./utils/key2pub.py $(wildcard $(PUBKEY_DIR)/*.pem) $@
 
 $(LIBREG): regdb.h reglib.h reglib.c
 	$(NQ) '  CC  ' $@
@@ -187,5 +185,5 @@ install: install-libreg install-libreg-headers crda crda.8.gz regdbdump.8.gz
 
 clean:
 	$(Q)rm -f $(LIBREG) crda regdbdump intersect db2rd optimize \
-		*.o *~ *.pyc keys-*.c *.gz \
+		*.o *~ *.pyc keys.c *.gz \
 	udev/$(UDEV_LEVEL)regulatory.rules udev/regulatory.rules.parsed
diff --git a/reglib.c b/reglib.c
index e00e9b8..00f7f56 100644
--- a/reglib.c
+++ b/reglib.c
@@ -22,6 +22,7 @@
 #include <openssl/rsa.h>
 #include <openssl/sha.h>
 #include <openssl/pem.h>
+#include <openssl/bn.h>
 #endif
 
 #ifdef USE_GCRYPT
@@ -30,12 +31,8 @@
 
 #include "reglib.h"
 
-#ifdef USE_OPENSSL
-#include "keys-ssl.c"
-#endif
-
-#ifdef USE_GCRYPT
-#include "keys-gcrypt.c"
+#if defined(USE_OPENSSL) || defined(USE_GCRYPT)
+#include "keys.c"
 #endif
 
 int debug = 0;
@@ -81,7 +78,8 @@ reglib_array_len(size_t baselen, unsigned int elemcount, size_t elemlen)
 #ifdef USE_OPENSSL
 int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
 {
-	RSA *rsa;
+	RSA *rsa = NULL;
+	BIGNUM *rsa_e = NULL, *rsa_n = NULL;
 	uint8_t hash[SHA_DIGEST_LENGTH];
 	unsigned int i;
 	int ok = 0;
@@ -102,15 +100,35 @@ int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
 			goto out;
 		}
 
-		rsa->e = &keys[i].e;
-		rsa->n = &keys[i].n;
+		rsa_e = BN_bin2bn(keys[i].e, keys[i].len_e, NULL);
+		if (!rsa_e) {
+			fprintf(stderr, "Failed to convert value for RSA e.\n");
+			goto out;
+		}
+		rsa_n = BN_bin2bn(keys[i].n, keys[i].len_n, NULL);
+		if (!rsa_n) {
+			fprintf(stderr, "Failed to convert value for RSA n.\n");
+			goto out;
+		}
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+		rsa->e = rsa_e;
+		rsa->n = rsa_n;
+#else
+		if (RSA_set0_key(rsa, rsa_n, rsa_e, NULL) != 1) {
+			fprintf(stderr, "Failed to set RSA key.\n");
+			goto out;
+		}
+#endif
+		/* BIGNUMs now owned by the RSA object */
+		rsa_e = NULL;
+		rsa_n = NULL;
 
 		ok = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH,
 				db + dblen, siglen, rsa) == 1;
 
-		rsa->e = NULL;
-		rsa->n = NULL;
 		RSA_free(rsa);
+		rsa = NULL;
 	}
 	if (!ok && (pubkey_dir = opendir(PUBKEY_DIR))) {
 		while (!ok && (nextfile = readdir(pubkey_dir))) {
@@ -123,6 +141,7 @@ int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
 					ok = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH,
 						db + dblen, siglen, rsa) == 1;
 				RSA_free(rsa);
+				rsa = NULL;
 				fclose(keyfile);
 			}
 		}
@@ -133,6 +152,9 @@ int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
 		fprintf(stderr, "Database signature verification failed.\n");
 
 out:
+	RSA_free(rsa);
+	BN_free(rsa_e);
+	BN_free(rsa_n);
 	return ok;
 }
 #endif /* USE_OPENSSL */
diff --git a/utils/key2pub.py b/utils/key2pub.py
index 9bb04cd..1919270 100755
--- a/utils/key2pub.py
+++ b/utils/key2pub.py
@@ -9,84 +9,7 @@ except ImportError, e:
        sys.stderr.write('On Debian GNU/Linux the package is called "python-m2crypto".\n')
        sys.exit(1)
 
-def print_ssl_64(output, name, val):
-    while val[0] == '\0':
-        val = val[1:]
-    while len(val) % 8:
-        val = '\0' + val
-    vnew = []
-    while len(val):
-        vnew.append((val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]))
-        val = val[8:]
-    vnew.reverse()
-    output.write('static BN_ULONG %s[%d] = {\n' % (name, len(vnew)))
-    idx = 0
-    for v1, v2, v3, v4, v5, v6, v7, v8 in vnew:
-        if not idx:
-            output.write('\t')
-        output.write('0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x, ' % (ord(v1), ord(v2), ord(v3), ord(v4), ord(v5), ord(v6), ord(v7), ord(v8)))
-        idx += 1
-        if idx == 2:
-            idx = 0
-            output.write('\n')
-    if idx:
-        output.write('\n')
-    output.write('};\n\n')
-
-def print_ssl_32(output, name, val):
-    while val[0] == '\0':
-        val = val[1:]
-    while len(val) % 4:
-        val = '\0' + val
-    vnew = []
-    while len(val):
-        vnew.append((val[0], val[1], val[2], val[3], ))
-        val = val[4:]
-    vnew.reverse()
-    output.write('static BN_ULONG %s[%d] = {\n' % (name, len(vnew)))
-    idx = 0
-    for v1, v2, v3, v4 in vnew:
-        if not idx:
-            output.write('\t')
-        output.write('0x%.2x%.2x%.2x%.2x, ' % (ord(v1), ord(v2), ord(v3), ord(v4)))
-        idx += 1
-        if idx == 4:
-            idx = 0
-            output.write('\n')
-    if idx:
-        output.write('\n')
-    output.write('};\n\n')
-
-def print_ssl(output, name, val):
-    import struct
-    output.write('#include <stdint.h>\n')
-    if len(struct.pack('@L', 0)) == 8:
-        return print_ssl_64(output, name, val)
-    else:
-        return print_ssl_32(output, name, val)
-
-def print_ssl_keys(output, n):
-    output.write(r'''
-struct pubkey {
-	struct bignum_st e, n;
-};
-
-#define KEY(data) {				\
-	.d = data,				\
-	.top = sizeof(data)/sizeof(data[0]),	\
-}
-
-#define KEYS(e,n)	{ KEY(e), KEY(n), }
-
-static struct pubkey keys[] = {
-''')
-    for n in xrange(n + 1):
-        output.write('	KEYS(e_%d, n_%d),\n' % (n, n))
-    output.write('};\n')
-    pass
-
-def print_gcrypt(output, name, val):
-    output.write('#include <stdint.h>\n')
+def print_bignum(output, name, val):
     while val[0] == '\0':
         val = val[1:]
     output.write('static const uint8_t %s[%d] = {\n' % (name, len(val)))
@@ -103,11 +26,11 @@ def print_gcrypt(output, name, val):
         output.write('\n')
     output.write('};\n\n')
 
-def print_gcrypt_keys(output, n):
+def print_keys(output, n):
     output.write(r'''
 struct key_params {
 	const uint8_t *e, *n;
-	uint32_t len_e, len_n;
+	const uint32_t len_e, len_n;
 };
 
 #define KEYS(_e, _n) {			\
@@ -120,25 +43,17 @@ static const struct key_params __attribute__ ((unused)) keys[] = {
     for n in xrange(n + 1):
         output.write('	KEYS(e_%d, n_%d),\n' % (n, n))
     output.write('};\n')
-    
 
-modes = {
-    '--ssl': (print_ssl, print_ssl_keys),
-    '--gcrypt': (print_gcrypt, print_gcrypt_keys),
-}
 
-try:
-    mode = sys.argv[1]
-    files = sys.argv[2:-1]
-    outfile = sys.argv[-1]
-except IndexError:
-    mode = None
+files = sys.argv[1:-1]
+outfile = sys.argv[-1]
 
-if not mode in modes:
-    print 'Usage: %s [%s] input-file... output-file' % (sys.argv[0], '|'.join(modes.keys()))
+if len(files) == 0:
+    print 'Usage: %s input-file... output-file' % (sys.argv[0], )
     sys.exit(2)
 
 output = open(outfile, 'w')
+output.write('#include <stdint.h>\n\n\n')
 
 # load key
 idx = 0
@@ -148,8 +63,8 @@ for f in files:
     except RSA.RSAError:
         key = RSA.load_key(f)
 
-    modes[mode][0](output, 'e_%d' % idx, key.e[4:])
-    modes[mode][0](output, 'n_%d' % idx, key.n[4:])
+    print_bignum(output, 'e_%d' % idx, key.e[4:])
+    print_bignum(output, 'n_%d' % idx, key.n[4:])
     idx += 1
 
-modes[mode][1](output, idx - 1)
+print_keys(output, idx - 1)
-- 
2.16.2

