-- 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 = 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
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
terra m.loadpriv(buf: &uint8, len: intptr): 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, len + 1, 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 }
end
end
terra m.loadpub(buf: &uint8, len: intptr): 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, len)
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