parsav  Artifact [530b761d29]

Artifact 530b761d29be40b4319b9d4cd1cdf134eb10fb51049d99a80885a8737d8045dc:


-- vim: ft=terra
local const = {
	keybits = 2048;
	sighash = lib.md.MBEDTLS_MD_SHA256;
}
local err = {
	rawcode = terra(code: int)
		if code < 0 then code = -code end
		return code and 0xFF80
	end;
	toobig = -lib.pk.MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE;
}
const.maxpemsz = math.floor((const.keybits / 8)*6.4) + 128 -- idk why this formula works but it basically seems to
const.maxdersz = const.maxpemsz -- FIXME this is a safe value but obvs not the correct one

local ctx = lib.pk.mbedtls_pk_context
terra ctx:free() lib.pk.mbedtls_pk_free(self) end

local struct hashalg { id: uint8 bytes: intptr }
local m = {
	pemfile = int8[const.maxpemsz];
	derfile = uint8[const.maxdersz];
	const = const;
	algsz = {
		sha1 =   160/8;
		sha256 = 256/8;
		sha512 = 512/8;
		sha384 = 384/8;
		sha224 = 224/8;
	}
}
m.alg = {
	sha1 =   `hashalg {id = lib.md.MBEDTLS_MD_SHA1;   bytes = m.algsz.sha1};
	sha256 = `hashalg {id = lib.md.MBEDTLS_MD_SHA256; bytes = m.algsz.sha256};
	sha512 = `hashalg {id = lib.md.MBEDTLS_MD_SHA512; bytes = m.algsz.sha512};
	sha384 = `hashalg {id = lib.md.MBEDTLS_MD_SHA384; bytes = m.algsz.sha384};
	sha224 = `hashalg {id = lib.md.MBEDTLS_MD_SHA224; bytes = m.algsz.sha224};
	-- md5 = {id = lib.md.MBEDTLS_MD_MD5};-- !!!
};
local callbacks = {}
if config.feat.randomizer == 'kern' then
	local rnd = terralib.externfunction('getrandom', {&opaque, intptr, uint} -> ptrdiff);
	terra m.spray(dest: &uint8, sz: intptr): int
		return rnd(dest, sz, 0)
	end
elseif config.feat.randomizer == 'devfs' then
	terra m.spray(dest: &uint8, sz: intptr): int
		var gen = lib.io.open("/dev/urandom",0)
		lib.io.read(gen, dest, sz)
		lib.io.close(gen)
		return sz
	end
elseif config.feat.randomizer == 'libc' then
	local rnd = terralib.externfunction('rand', {} -> int);
	local srnd = terralib.externfunction('srand', uint -> int);
	local time = terralib.includec 'time.h'
	lib.init[#lib.init + 1] = quote srnd(time.time(nil)) end
	print '(warn) using libc soft-rand function for cryptographic purposes, this is very bad!'
	terra m.spray(dest: &uint8, sz: intptr): int
		for i=0,sz do dest[i] = [uint8](rnd()) end
		return sz
	end
end

m.random = macro(function(typ, from, to)
	local ty = typ:astype()
	from = from or 0
	to = to or ty:max()
	return quote
		var v: ty
		m.spray([&uint8](&v), sizeof(ty))
		v = v % (to - from) + from -- only works with unsigned!!
	in v end
end)

terra callbacks.randomize(ctx: &opaque, dest: &uint8, sz: intptr)
	return m.spray(dest,sz) end

terra m.pem(pub: bool, key: &ctx, buf: &int8): bool
	if pub then
		return lib.pk.mbedtls_pk_write_pubkey_pem(key, [&uint8](buf), const.maxpemsz) == 0
	else
		return lib.pk.mbedtls_pk_write_key_pem(key, [&uint8](buf), const.maxpemsz) == 0
	end
end

local binblob = lib.mem.ptr(uint8)
terra m.der(pub: bool, key: &ctx, buf: &uint8): binblob
	var ofs: ptrdiff
	if pub then
		ofs = lib.pk.mbedtls_pk_write_pubkey_der(key, buf, const.maxdersz)
	else
		ofs = lib.pk.mbedtls_pk_write_key_der(key, buf, const.maxdersz)
	end
	if ofs < 0 then return binblob.null() end
	return binblob {
		ptr = buf + (const.maxdersz - ofs);
		ct = ofs;
	}
end

m.destroy = lib.dispatch {
	[ctx] = function(v) return `lib.pk.mbedtls_pk_free(&v) end;

	[false] = function(ptr) return `ptr:free() end;
}

terra m.genkp(): lib.pk.mbedtls_pk_context
	lib.dbg('generating new keypair')

	var pk: ctx
	lib.pk.mbedtls_pk_init(&pk)
	lib.pk.mbedtls_pk_setup(&pk, lib.pk.mbedtls_pk_info_from_type(lib.pk.MBEDTLS_PK_RSA))
	var rsa = [&lib.rsa.mbedtls_rsa_context](pk.pk_ctx)
	lib.rsa.mbedtls_rsa_gen_key(rsa, callbacks.randomize, nil, const.keybits, 65537)

	return pk
end

local binblob = lib.mem.ptr(uint8)
terra m.loadpriv(buf: binblob): lib.stat(ctx)
	lib.dbg('parsing saved private key')

	var pk: ctx
	lib.pk.mbedtls_pk_init(&pk)
	var rt = lib.pk.mbedtls_pk_parse_key(&pk, buf.ptr, buf.ct, nil, 0)
	if rt == 0 then
		return [lib.stat(ctx)] { ok = true, val = pk }
	else
		lib.pk.mbedtls_pk_free(&pk)
		return [lib.stat(ctx)] { ok = false, error = rt }
	end
end

terra m.loadpub(buf: binblob): lib.stat(ctx)
	lib.dbg('parsing saved key')

	var pk: ctx
	lib.pk.mbedtls_pk_init(&pk)
	var rt = lib.pk.mbedtls_pk_parse_public_key(&pk, buf.ptr, buf.ct)
	if rt == 0 then
		return [lib.stat(ctx)] { ok = true, val = pk }
	else
		lib.pk.mbedtls_pk_free(&pk)
		return [lib.stat(ctx)] { ok = false, error = rt }
	end
end

terra m.sign(pk: &ctx, txt: rawstring, len: intptr)
	lib.dbg('signing message')
	var osz: intptr = 0
	var sig = lib.mem.heapa(int8, 2048)
	var hash: uint8[32]
	
	lib.dbg('(1/2) hashing message')
	if lib.md.mbedtls_md(lib.md.mbedtls_md_info_from_type(const.sighash), [&uint8](txt), len, hash) ~= 0 then
		lib.bail('could not hash message')
	end

	lib.dbg('(2/2) signing hash')
	var ret = lib.pk.mbedtls_pk_sign(pk, const.sighash, hash, 0, [&uint8](sig.ptr), &osz, callbacks.randomize, nil)
	if ret ~= 0 then lib.bail('could not sign message hash')
	else sig:resize(osz) end

	return sig
end

terra m.verify(pk: &ctx, txt: rawstring, len: intptr,
                        sig: &uint8, siglen: intptr): {bool, uint8}
	lib.dbg('verifying signature')
	var osz: intptr = 0
	var hash: uint8[64]

	-- there does not appear to be any way to extract the hash algorithm
	-- from the message, so we just have to try likely algorithms until
	-- we find one that fits or give up. a security level is attached
	-- to indicate our relative confidence in the hash; 0 == 'likely forgeable'
	var algs = array(
		-- common hashes
		{lib.md.MBEDTLS_MD_SHA256, 'sha256', 2},
		{lib.md.MBEDTLS_MD_SHA512, 'sha512', 3},
		{lib.md.MBEDTLS_MD_SHA1,   'sha1',   1},
		-- uncommon hashes
		{lib.md.MBEDTLS_MD_SHA384, 'sha384', 2},
		{lib.md.MBEDTLS_MD_SHA224, 'sha224', 2},
		-- bad hashes
		{lib.md.MBEDTLS_MD_MD5,   'md5', 0}
		--{lib.md.MBEDTLS_MD_MD4,   'md4', 0},
		--{lib.md.MBEDTLS_MD_MD2,   'md2', 0}
	)
	
	for i = 0, [algs.type.N] do
		var hk, aname, secl = algs[i]

		lib.dbg('(1/2) trying hash algorithm ',aname)
		if lib.md.mbedtls_md(lib.md.mbedtls_md_info_from_type(hk), [&uint8](txt), len, hash) ~= 0 then
			lib.bail('could not hash message')
		end

		lib.dbg('(2/2) verifying hash')
		if lib.pk.mbedtls_pk_verify(pk, hk, hash, 0, [&uint8](sig), siglen) == 0 then
			return true, secl
		end
	end
	lib.dbg('all hash algorithms failed')
	return false, 0
end

terra m.hmac(alg: hashalg, key: lib.mem.ptr(uint8), txt: lib.mem.ptr(int8), buf: &uint8)
	lib.md.mbedtls_md_hmac(
			lib.md.mbedtls_md_info_from_type(alg.id), 
			key.ptr, key.ct,
			[&uint8](txt.ptr), txt.ct,
			buf) -- sz(buf) >= hash output size
end

terra m.hmaca(alg: hashalg, key: lib.mem.ptr(uint8), txt: lib.mem.ptr(int8))
	var buf = lib.mem.heapa(uint8, alg.bytes)
	m.hmac(alg, key, txt, buf.ptr)
	return buf
end

terra m.hmacp(p: &lib.mem.pool, alg: hashalg, key: lib.mem.ptr(uint8), txt: lib.mem.ptr(int8))
	var buf = p:alloc(uint8, alg.bytes)
	m.hmac(alg, key, txt, buf.ptr)
	return buf
end

terra m.hotp(key: &(uint8[10]), counter: uint64)
	var hmac: uint8[20]
	var ctr = [lib.mem.ptr(int8)]{ptr = [&int8](&counter), ct = 8}
	m.hmac(m.alg.sha1,
		[lib.mem.ptr(uint8)]{ptr = [&uint8](key), ct = 10},
		ctr, hmac)
	
	var ofs = hmac[19] and 0x0F
	var p: uint8[4]
	for i=0,4 do p[i] = hmac[ofs + i] end

	return (@[&uint32](&p)) and 0x7FFFFFFF -- one hopes it's that easy
end

local splitwords = macro(function(str)
	local words = {}
	for w in str:asvalue():gmatch('(%g+)') do words[#words + 1] = w end
	return `arrayof(lib.str.t, [words])
end)

terra m.cryptogram(a: &lib.str.acc, len: intptr)
	var words = splitwords [[
		alpha beta gamma delta epsilon psi eta nu omicron omega
		red crimson green verdant golden silver blue cyan navy
		carnelian opal sapphire amethyst ruby jade emerald
		chalice peacock cabernet windmill saxony tunnel waterspout
	]]
	for i = 0, len do
		a:ppush(words[m.random(intptr,0,[words.type.N])]):lpush '-'
	end
	a:ipush(m.random(uint32,0,99999))
end

return m