util  Check-in [4e2b17fce2]

Overview
Comment:add rd-key
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 4e2b17fce2c5086be6d1c53bbed3d9c07692db2047f51351eca24aaeb67050be
User & Date: lexi on 2021-09-11 22:18:39
Other Links: manifest | tags
Context
2022-04-25
21:01
add first parvan revision check-in: bf5f4fd9ca user: lexi tags: trunk
2021-09-11
22:18
add rd-key check-in: 4e2b17fce2 user: lexi tags: trunk
2019-12-16
06:16
fix newtab to keep userscript sockets/fifos from tripping it up check-in: 4cecd72436 user: lexi tags: trunk
Changes

Added rd-key.lua version [4ea4dcd59f].

            1  +-- [ʞ] rd-key.lua
            2  +--  ~ lexi hale
            3  +--  © GNU AGPLv3
            4  +--  ? utility for generating and applying 64bit key
            5  +--    schedules to RD-5R "codeplug" provisioning profiles
            6  +--  > lua rd-key.lua gen sched.kch
            7  +--  > lua rd-key.lua apply sched.kch device.img
            8  +--  > dmrconfig -w device.img
            9  +
           10  +function B(b, ...)
           11  +	if not b then return "" end
           12  +	return string.pack("I1", b) .. B(...)
           13  +end
           14  +
           15  +local const = {
           16  +	kch_header = B(0x9a, 0x1e, 0x2f) .. "kCH";
           17  +	maxkeys = 16;
           18  +	keylen = 64/8;
           19  +
           20  +	plug = {
           21  +		kch_start = 0x1370;
           22  +		kch_end   = 0x13f8;
           23  +		bank_chns = 128;
           24  +		chn_sz    = 56;
           25  +		bank_sz   = 16 + (56 * 128);
           26  +		chn_regions = {
           27  +			{top = 128,  start = 0x3780};
           28  +			{top = 1023, start = 0xb1b0};
           29  +		};
           30  +
           31  +		mode = {
           32  +			analog = 0;
           33  +			digital = 1;
           34  +		};
           35  +	};
           36  +}
           37  +
           38  +math.randomseed(os.time())
           39  +function log(...) print(string.format(...)) end
           40  +function keymask(num)
           41  +	local mask = 0
           42  +	for i = 0, num-1 do
           43  +		mask = mask | 1 << i
           44  +	end
           45  +
           46  +	return string.pack("<I2", mask)
           47  +end
           48  +
           49  +function strdec(str)
           50  +	for i = 1, #str do
           51  +		if string.byte(str, i) == 0xFF then
           52  +			return str:sub(1, i-1)
           53  +		end
           54  +	end
           55  +	return str
           56  +end
           57  +
           58  +function chans(mem)
           59  +	local i = 1
           60  +	local region = 1
           61  +	return function()
           62  +		local r = const.plug.chn_regions[region]
           63  +		::tryagain::
           64  +		if i > r.top then
           65  +			region = region + 1
           66  +			r = const.plug.chn_regions[region]
           67  +		end
           68  +		if r == nil then return nil end
           69  +		local bottom = region > 1 and const.plug.chn_regions[region-1].top or 1
           70  +
           71  +		local bank = (i - bottom) // const.plug.bank_chns
           72  +		local bo = r.start + bank * const.plug.bank_sz
           73  +		local bankpos = (i - bottom) % const.plug.bank_chns
           74  +
           75  +		i = i + 1
           76  +
           77  +		-- local bitmap = mem:sub(bo, bo + 16)
           78  +		-- local bitmap_b = string.byte(bitmap, 1 + bankpos // 8)
           79  +		-- if bitmap_b & (1 << (bankpos % 8)) == 0 then  --- doesn't work
           80  +		-- 	goto tryagain
           81  +		-- end
           82  +
           83  +		local chans = bo + 16
           84  +		local ent = chans + bankpos * const.plug.chn_sz
           85  +
           86  +		local rec = mem:sub(1+ent,1+ent + const.plug.chn_sz)
           87  +
           88  +
           89  +		return rec, i-1, 1+ent
           90  +	end
           91  +end
           92  +
           93  +function strchg(str, ofs, sz, new)
           94  +	local head = string.sub(str, 1, ofs-1)
           95  +	local tail = string.sub(str, ofs+sz)
           96  +	local n = head .. new .. tail
           97  +	if #n ~= #str then
           98  +		print("old str") dumpline(string.sub(str, ofs, ofs + (sz-1)))
           99  +		print("new str should be", sz, #new) dumpline(new)
          100  +		error("bad str chg")
          101  +	end
          102  +	return n
          103  +end
          104  +
          105  +function dumpline(str)
          106  +	local function dump(line)
          107  +		local top="\t"
          108  +		local bottom="\t"
          109  +
          110  +		for i=1,#line do
          111  +			local val = string.byte(line, i)
          112  +			if val >= 0x20 and val <= 0x7e then
          113  +				top = top .. string.format(" %c ", val)
          114  +			else
          115  +				top = top .. " : "
          116  +			end
          117  +			bottom = bottom .. string.format("%02x ", val)
          118  +		end
          119  +		print(top)
          120  +		print(bottom)
          121  +	end
          122  +	local cpl = 32
          123  +	for i = 0, #str // cpl do
          124  +		local st = 1 + i*cpl
          125  +		dump(str:sub(st, st+cpl))
          126  +	end
          127  +end
          128  +function applykeychain(chain, file)
          129  +	local h = io.open(file, 'rb')
          130  +	if not h then error("could not open file") end
          131  +	local pl = h:read('a') h:close()
          132  +
          133  +	local keylist = B(1,0) -- basic mode
          134  +		.. keymask(const.maxkeys) -- number of keys
          135  +		.. B(0,0, 0,0) -- padding??
          136  +		.. table.concat(chain)
          137  +	pl = strchg(pl, const.plug.kch_start+1, #keylist, keylist)
          138  +	
          139  +	for ch, idx, ofs in chans(pl) do
          140  +		if string.byte(ch, 1) ~= 0xFF then -- is channel defined?
          141  +			local name = strdec(ch:sub(1,16))
          142  +			local mode = string.byte(ch, 25)
          143  +			local function say(fmt, ...)
          144  +				log("\x1b[1mchannel [%04u] “%s”\x1b[m: " .. fmt, idx, name, ...)
          145  +			end
          146  +			local newch
          147  +			if mode == const.plug.mode.digital then
          148  +				local privg = string.byte(ch, 42)
          149  +				local chkey = idx % const.maxkeys
          150  +				if privg == 0 then
          151  +					say("marked clear, comms on this channel will NOT be encrypted")
          152  +				elseif privg ~= chkey then
          153  +					say("changing key from #%u to #%u", privg, chkey)
          154  +					newch = strchg(ch, 42, 1, B(chkey))
          155  +					dirty = true
          156  +				-- else
          157  +				-- 	say("already keyed correctly")
          158  +				end
          159  +			else
          160  +				say("analog channel, cannot be encrypted")
          161  +			end
          162  +
          163  +			if newch then
          164  +				pl=strchg(pl, ofs, 1+const.plug.chn_sz, newch)
          165  +			end
          166  +		end
          167  +	end
          168  +
          169  +	h = io.open(file, 'w+b')
          170  +	h:write(pl)
          171  +	h:close()
          172  +end
          173  +
          174  +function loadkeychain(file)
          175  +	local f = io.open(file, "r")
          176  +	if f:read(#const.kch_header) ~= const.kch_header then
          177  +		error "not a valid keychain file"
          178  +	end
          179  +	local ch = {}
          180  +	for i=1, const.maxkeys do
          181  +		ch[i] = f:read(const.keylen)
          182  +	end
          183  +	local comment = f:read('a')
          184  +	f:close()
          185  +	log("using keychain for %s", comment)
          186  +	return ch
          187  +end
          188  +
          189  +function savekeychain(file, kch)
          190  +	local f = io.open(file, "w+b")
          191  +	f:write(const.kch_header)
          192  +	for i=1, const.maxkeys do
          193  +		f:write(kch[i])
          194  +	end
          195  +	f:write(os.date())
          196  +	f:close()
          197  +end
          198  +
          199  +function genkeychain()
          200  +	local ch = {}
          201  +	local rng = io.open("/dev/urandom") or io.open("/dev/random")
          202  +	function rb(n)
          203  +		if rng then return rng:read(n) else
          204  +			log('warning, cannot read /dev/[u]random, random bytes may be low-quality')
          205  +			local str = ""
          206  +			for i = 1,n do str = str .. B(math.random(0,0xff)) end
          207  +			return str
          208  +		end
          209  +	end
          210  +	for i=1,const.maxkeys do
          211  +		ch[i] = rb(64/8)
          212  +	end
          213  +	if rng then rng:close() end
          214  +	return ch
          215  +end
          216  +
          217  +local cmd, keychain, target = ...
          218  +if not cmd then
          219  +	print("usage: lua rd-key.lua (gen|apply|extract) keychain [codeplug]")
          220  +else
          221  +	if keychain == nil or file == "" then keychain = "chain.kch" end
          222  +	if target == nil or target == "" then target = "device.img" end
          223  +	if cmd == "gen" then
          224  +		log("writing new keychain to %s", keychain)
          225  +		local ch = genkeychain()
          226  +		savekeychain(keychain, ch)
          227  +	elseif cmd == "apply" then
          228  +		log("applying keychain %s to codeplug %s", keychain, target)
          229  +		local ch = loadkeychain(keychain)
          230  +		applykeychain(ch, target)
          231  +	elseif cmd == "extract" then
          232  +		log("extracting keychain %s from codeplug %s", keychain, target)
          233  +		log(" -- unimplemented --")
          234  +	end
          235  +end

Modified tenki/make.sh from [f7f3655bf1] to [3702e74b7e].

     1         -#!/bin/bash
            1  +#!/usr/bin/env bash
     2      2   if test -e apikey; then
     3      3   	cc -Ofast -Dds_apikey="\"$(cat apikey)\"" tenki.c -lssl -otenki
     4      4   else
     5      5   	echo "tenki requires an API key. get one from dark sky and put it in a file in this directory named 'apikey' before you try to build"
     6      6   fi