parsav  crypt.t at [93aea04a05]

File crypt.t artifact f5b057e4fa part of check-in 93aea04a05


-- 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

local struct hashalg { id: uint8 bytes: intptr }
local m = {
	pemfile = uint8[const.maxpemsz];
	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: &uint8): bool
	if pub then
		return lib.pk.mbedtls_pk_write_pubkey_pem(key, buf, const.maxpemsz) == 0
	else
		return lib.pk.mbedtls_pk_write_key_pem(key, buf, const.maxpemsz) == 0
	end
end

terra m.der(pub: bool, key: &ctx, buf: &uint8): intptr
	if pub then
		return lib.pk.mbedtls_pk_write_pubkey_der(key, buf, const.maxdersz)
	else
		return lib.pk.mbedtls_pk_write_key_der(key, buf, const.maxdersz)
	end
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

terra m.loadpriv(buf: &uint8, len: intptr): ctx
	lib.dbg('parsing saved keypair')

	var pk: ctx
	lib.pk.mbedtls_pk_init(&pk)
	lib.pk.mbedtls_pk_parse_key(&pk, buf, len + 1, nil, 0)
	return pk
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: rawstring, 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.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

return m