Rebased from upstream / out of band repository.
[librecmc/librecmc.git] / package / firmware / wireless-regdb / patches / 100-regdb-write-firmware-file-format-version-code-20.patch
1 From: Johannes Berg <johannes.berg@intel.com>
2 Date: Mon, 9 Oct 2017 11:50:57 +0200
3 Subject: [PATCH] regdb: write firmware file format (version code 20)
4
5 TODO: clean up the Makefile stuff ...
6
7 Signed-off-by: Johannes Berg <johannes.berg@intel.com>
8 ---
9  create mode 100755 db2fw.py
10
11 --- a/Makefile
12 +++ b/Makefile
13 @@ -1,7 +1,5 @@
14  # Install prefix
15  PREFIX ?= /usr
16 -CRDA_PATH ?= $(PREFIX)/lib/crda
17 -CRDA_KEY_PATH ?= $(CRDA_PATH)/pubkeys
18  
19  MANDIR ?= $(PREFIX)/share/man/
20  
21 @@ -30,39 +28,47 @@ REGDB_AUTHOR ?= $(shell if [ -f $(DISTRO
22                 fi)
23  
24  REGDB_PRIVKEY ?= ~/.wireless-regdb-$(REGDB_AUTHOR).key.priv.pem
25 -REGDB_PUBKEY ?= $(REGDB_AUTHOR).key.pub.pem
26 -
27 -REGDB_UPSTREAM_PUBKEY ?= sforshee.key.pub.pem
28 +REGDB_PUBCERT ?= $(REGDB_AUTHOR).x509.pem
29  
30  REGDB_CHANGED = $(shell $(SHA1SUM) -c --status sha1sum.txt >/dev/null 2>&1; \
31          if [ $$? -ne 0 ]; then \
32 -                echo maintainer-clean $(REGDB_PUBKEY); \
33 +                echo maintainer-clean $(REGDB_PUBCERT); \
34          fi)
35  
36  .PHONY: all clean mrproper install maintainer-clean install-distro-key
37  
38 -all: $(REGDB_CHANGED) regulatory.bin sha1sum.txt
39 +all: $(REGDB_CHANGED) regulatory.db.p7s sha1sum.txt
40  
41  clean:
42         @rm -f *.pyc *.gz
43  
44  maintainer-clean: clean
45 -       @rm -f regulatory.bin
46 +       @rm -f regulatory.db regulatory.db.p7s
47  
48  mrproper: clean maintainer-clean
49 -       @echo Removed public key, regulatory.bin and compresed man pages
50 -       @rm -f $(REGDB_PUBKEY) .custom
51 +       @echo Removed public key, regulatory.db* and compressed man pages
52 +       @rm -f $(REGDB_PUBCERT) .custom
53  
54 -regulatory.bin: db.txt $(REGDB_PRIVKEY) $(REGDB_PUBKEY)
55 -       @echo Generating $@ digitally signed by $(REGDB_AUTHOR)...
56 -       ./db2bin.py regulatory.bin db.txt $(REGDB_PRIVKEY)
57 +regulatory.db: db.txt db2fw.py
58 +       @echo "Generating $@"
59 +       ./db2fw.py regulatory.db db.txt
60 +
61 +regulatory.db.p7s: regulatory.db $(REGDB_PRIVKEY) $(REGDB_PUBCERT)
62 +       @echo "Signing regulatory.db (by $(REGDB_AUTHOR))..."
63 +       @openssl smime -sign \
64 +               -signer $(REGDB_PUBCERT) \
65 +               -inkey $(REGDB_PRIVKEY) \
66 +               -in $< -nosmimecap -binary \
67 +               -outform DER -out $@
68  
69  sha1sum.txt: db.txt
70         sha1sum $< > $@
71  
72 -$(REGDB_PUBKEY): $(REGDB_PRIVKEY)
73 -       @echo "Generating public key for $(REGDB_AUTHOR)..."
74 -       openssl rsa -in $(REGDB_PRIVKEY) -out $(REGDB_PUBKEY) -pubout -outform PEM
75 +$(REGDB_PUBCERT): $(REGDB_PRIVKEY)
76 +       @echo "Generating certificate for $(REGDB_AUTHOR)..."
77 +       @openssl req -config regulatory.openssl.conf \
78 +               -key $(REGDB_PRIVKEY) -days 36500 -utf8 -nodes -batch \
79 +               -x509 -outform PEM -out $(REGDB_PUBCERT)
80         @echo $(REGDB_PUBKEY) > .custom
81  
82  
83 @@ -97,16 +103,7 @@ install-distro-key: maintainer-clean $(D
84  #      make maintainer-clean
85  #      make
86  #      sudo make install
87 -install: regulatory.bin.5.gz
88 -       install -m 755 -d $(DESTDIR)/$(CRDA_PATH)
89 -       install -m 755 -d $(DESTDIR)/$(CRDA_KEY_PATH)
90 -       if [ -f .custom ]; then \
91 -               install -m 644 -t $(DESTDIR)/$(CRDA_KEY_PATH)/ $(shell cat .custom); \
92 -       fi
93 -       install -m 644 -t $(DESTDIR)/$(CRDA_KEY_PATH)/ $(REGDB_UPSTREAM_PUBKEY)
94 -       install -m 644 -t $(DESTDIR)/$(CRDA_PATH)/ regulatory.bin
95 +install: regulatory.db.5.gz
96 +       install -m 644 -t $(DESTDIR)/$(CRDA_PATH)/ regulatory.db
97         install -m 755 -d $(DESTDIR)/$(MANDIR)/man5/
98 -       install -m 644 -t $(DESTDIR)/$(MANDIR)/man5/ regulatory.bin.5.gz
99 -
100 -uninstall:
101 -       rm -rf $(DESTDIR)/$(CRDA_PATH)/
102 +       install -m 644 -t $(DESTDIR)/$(MANDIR)/man5/ regulatory.db.5.gz
103 --- a/README
104 +++ b/README
105 @@ -18,8 +18,8 @@ python module is used by the web viewer
106  implemented as a MoinMoin macro (and used on http://wireless.kernel.org)
107  to allow viewing the database for verification.
108  
109 -The dbparse module is also used by db2bin.py, the `compiler', which
110 -compiles and signs the binary database.
111 +The dbparse module is also used by db2bin.py and db2fw.py, the `compilers'
112 +that compile the database to its binary formats.
113  
114  For more information, please see the CRDA git repository:
115  
116 --- /dev/null
117 +++ b/db2fw.py
118 @@ -0,0 +1,133 @@
119 +#!/usr/bin/env python
120 +
121 +from cStringIO import StringIO
122 +import struct
123 +import hashlib
124 +from dbparse import DBParser
125 +import sys
126 +
127 +MAGIC = 0x52474442
128 +VERSION = 20
129 +
130 +if len(sys.argv) < 3:
131 +    print 'Usage: %s output-file input-file' % sys.argv[0]
132 +    sys.exit(2)
133 +
134 +def create_rules(countries):
135 +    result = {}
136 +    for c in countries.itervalues():
137 +        for rule in c.permissions:
138 +            result[rule] = 1
139 +    return result.keys()
140 +
141 +def create_collections(countries):
142 +    result = {}
143 +    for c in countries.itervalues():
144 +        result[(c.permissions, c.dfs_region)] = 1
145 +    return result.keys()
146 +
147 +
148 +def be32(output, val):
149 +    output.write(struct.pack('>I', val))
150 +def be16(output, val):
151 +    output.write(struct.pack('>H', val))
152 +
153 +class PTR(object):
154 +    def __init__(self, output):
155 +        self._output = output
156 +        self._pos = output.tell()
157 +        be16(output, 0)
158 +        self._written = False
159 +
160 +    def set(self, val=None):
161 +        if val is None:
162 +            val = self._output.tell()
163 +        assert val & 3 == 0
164 +        self._offset = val
165 +        pos = self._output.tell()
166 +        self._output.seek(self._pos)
167 +        be16(self._output, val >> 2)
168 +        self._output.seek(pos)
169 +        self._written = True
170 +
171 +    def get(self):
172 +        return self._offset
173 +
174 +    @property
175 +    def written(self):
176 +        return self._written
177 +
178 +p = DBParser()
179 +countries = p.parse(file(sys.argv[2]))
180 +rules = create_rules(countries)
181 +rules.sort(cmp=lambda x, y: cmp(x.freqband, y.freqband))
182 +collections = create_collections(countries)
183 +collections.sort(cmp=lambda x, y: cmp(x[0][0].freqband, y[0][0].freqband))
184 +
185 +output = StringIO()
186 +
187 +# struct regdb_file_header
188 +be32(output, MAGIC)
189 +be32(output, VERSION)
190 +
191 +country_ptrs = {}
192 +countrynames = countries.keys()
193 +countrynames.sort()
194 +for alpha2 in countrynames:
195 +    coll = countries[alpha2]
196 +    output.write(struct.pack('>cc', str(alpha2[0]), str(alpha2[1])))
197 +    country_ptrs[alpha2] = PTR(output)
198 +output.write('\x00' * 4)
199 +
200 +reg_rules = {}
201 +flags = 0
202 +for reg_rule in rules:
203 +    freq_range, power_rule = reg_rule.freqband, reg_rule.power
204 +    reg_rules[reg_rule] = output.tell()
205 +    assert power_rule.max_ant_gain == 0
206 +    flags = 0
207 +    # convert to new rule flags
208 +    assert reg_rule.flags & ~0x899 == 0
209 +    if reg_rule.flags & 1<<0:
210 +        flags |= 1<<0
211 +    if reg_rule.flags & 1<<3:
212 +        flags |= 1<<1
213 +    if reg_rule.flags & 1<<4:
214 +        flags |= 1<<2
215 +    if reg_rule.flags & 1<<7:
216 +        flags |= 1<<3
217 +    if reg_rule.flags & 1<<11:
218 +        flags |= 1<<4
219 +    rule_len = 16
220 +    cac_timeout = 0 # TODO
221 +    if not (flags & 1<<2):
222 +        cac_timeout = 0
223 +    if cac_timeout:
224 +        rule_len += 2
225 +    output.write(struct.pack('>BBHIII', rule_len, flags, power_rule.max_eirp * 100,
226 +                             freq_range.start * 1000, freq_range.end * 1000, freq_range.maxbw * 1000,
227 +                             ))
228 +    if cac_timeout:
229 +        output.write(struct.pack('>H', cac_timeout))
230 +    while rule_len % 4:
231 +        output.write('\0')
232 +        rule_len += 1
233 +
234 +for coll in collections:
235 +    for alpha2 in countrynames:
236 +        if (countries[alpha2].permissions, countries[alpha2].dfs_region) == coll:
237 +            assert not country_ptrs[alpha2].written
238 +            country_ptrs[alpha2].set()
239 +    slen = 3
240 +    output.write(struct.pack('>BBBx', slen, len(list(coll[0])), coll[1]))
241 +    coll = list(coll[0])
242 +    for regrule in coll:
243 +        be16(output, reg_rules[regrule] >> 2)
244 +    if len(coll) % 2:
245 +        be16(output, 0)
246 +
247 +for alpha2 in countrynames:
248 +    assert country_ptrs[alpha2].written
249 +
250 +outfile = open(sys.argv[1], 'w')
251 +outfile.write(output.getvalue())