-- 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
local ctx = lib.pk.mbedtls_pk_context
local struct hashalg { id: uint8 bytes: intptr }
local m = {
pemfile = uint8[const.maxpemsz];
alg = {
sha1 = `hashalg {id = lib.md.MBEDTLS_MD_SHA1; bytes = 160/8};
sha256 = `hashalg {id = lib.md.MBEDTLS_MD_SHA256; bytes = 256/8};
sha512 = `hashalg {id = lib.md.MBEDTLS_MD_SHA512; bytes = 512/8};
sha384 = `hashalg {id = lib.md.MBEDTLS_MD_SHA384; bytes = 384/8};
sha224 = `hashalg {id = lib.md.MBEDTLS_MD_SHA224; bytes = 224/8};
-- 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 callbacks.randomize(ctx: &opaque, dest: &uint8, sz: intptr): int
return rnd(dest, sz, 0)
end
elseif config.feat.randomizer == 'devfs' then
terra callbacks.randomize(ctx: &opaque, 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 callbacks.randomize(ctx: &opaque, dest: &uint8, sz: intptr): int
for i=0,sz do dest[i] = [uint8](rnd()) end
return sz
end
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
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