-- vim: ft=terra
-- sessions are implemented so as to avoid any local data storage. they
-- are tracked by storing an encrypted cookie which contains an authid,
-- a login epoch time, and a truncated hmac code authenticating both, all
-- encoded using Shorthand. we need functions to generate and parse these

local m = {
	maxlen = lib.math.shorthand.maxlen*3 + 2;
	maxage = 16 * 60 * 60; -- 16 hours
	cookiename = 'auth';
}

terra m.cookie_gen(secret: lib.mem.ptr(int8), authid: uint64, time: uint64, out: &int8): intptr
	var ptr = out
	ptr = ptr + lib.math.shorthand.gen(authid, ptr)
	@ptr = @'.' ptr = ptr + 1
	ptr = ptr + lib.math.shorthand.gen(time, ptr)
	@ptr = @'.' ptr = ptr + 1
	var len = ptr - out
	var hash: uint8[lib.crypt.algsz.sha256]
	lib.crypt.hmac(lib.crypt.alg.sha256,
		[lib.mem.ptr(uint8)] {ptr = [&uint8](secret.ptr), ct = secret.ct},
		[lib.mem.ptr( int8)] {ptr = out, ct = len},
	&hash[0])
	ptr = ptr + lib.math.shorthand.gen(lib.math.truncate64(hash, [hash.type.N]), ptr)
	return ptr - out
end

terra m.cookie_interpret(secret: lib.mem.ptr(int8), c: lib.mem.ptr(int8), now: uint64)
 -- returns either 0,0 or a valid {authid, timepoint}
	var authid_sz = lib.str.cspan(c.ptr, '.', c.ct)
	if authid_sz == 0 then return 0,0 end
	if authid_sz + 1 > c.ct then return 0,0 end
	var time_sz = lib.str.cspan(c.ptr+authid_sz+1, '.', c.ct - (authid_sz+1))
	if time_sz == 0 then return 0,0 end
	if (authid_sz + time_sz + 2) > c.ct then return 0,0 end
	var hash_sz = c.ct - (authid_sz + time_sz + 2)

	var knownhash: uint8[lib.crypt.algsz.sha256]
	lib.crypt.hmac(lib.crypt.alg.sha256,
		[lib.mem.ptr(uint8)] {ptr = [&uint8](secret.ptr), ct = secret.ct},
		[lib.mem.ptr( int8)] {ptr = c.ptr, ct = c.ct - hash_sz},
	&knownhash[0])

	var authid, authok = lib.math.shorthand.parse(c.ptr, authid_sz)
	var time, timeok = lib.math.shorthand.parse(c.ptr + authid_sz + 1, time_sz)
	var hash, hashok = lib.math.shorthand.parse(c.ptr + c.ct - hash_sz, hash_sz)
	if not (timeok and authok and hashok) then return 0,0 end
	if lib.math.truncate64(knownhash, [knownhash.type.N]) ~= hash then return 0,0 end
	if now - time > m.maxage then return 0,0 end

	return authid, time
end

return m
