100-regdb-write-firmware-file-format-version-code-20.patch 7.4 KB

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