Commit eeb36395 authored by Jaromil's avatar Jaromil
Browse files

refactor ECDH related examples and tests to work with strong crypto

also improved machine language and msgpack related code
still needs some simplification on usage of msgpack
parent 4b46b134
......@@ -14,8 +14,7 @@ keys = JSON.decode(KEYS)
-- this is our own secret key, combined with the recipient's public
-- key to obtain a session key
seckey = base64(keys.keyring.secret)
keyring:private(seckey)
keyring:private( base64(keys.keyring.secret) )
res = {}
......@@ -23,16 +22,11 @@ res = {}
for name,pubkey in pairs(keys.recipients) do
-- calculate the session key
pub = base64(pubkey)
k = keyring:session(pub)
if not k then
print( "Error: not a valid public key for recipient " .. name)
return
end
-- encrypt the message with the session key
enc = keyring:encrypt_weak_aes_cbc(k,secret)
enc = ECDH.encrypt(keyring,pub,secret,keyring:public())
-- insert results in final json array
res[name]= enc:base64()
res[name] = OCTET.msgpack( map(enc,base64) ):base64()
end
-- return the json array
......
......@@ -19,13 +19,13 @@ payload_schema = SCHEMA.Record {
data = read_json(DATA) -- TODO: data_schema validation
keys = read_json(KEYS, keys_schema)
header = MSG.unpack( base64(data.header) )
head = OCTET.msgunpack( base64(data.header) )
dashkey = ECDH.new()
dashkey:private( base64(keys.community_seckey) )
payload,ck = decrypt(dashkey,
base64( header.device_pubkey ),
payload,ck = ECDH.decrypt(dashkey,
base64( head.device_pubkey ),
map(data, base64))
-- validate the payload
......@@ -33,4 +33,4 @@ validate(payload, payload_schema)
-- print("Header:")
-- content(msgunpack(payload.header) )
print(JSON.encode(MSG.unpack(payload.text) ))
print(JSON.encode(OCTET.msgunpack(payload.text) ))
......@@ -32,17 +32,19 @@ own = ECDH.new()
own:private (base64 (keys.own_private) )
-- now calculate the session key between us and decidim
session = own:session(decidim_key)
-- session = own:session(decidim_key)
-- use a map() function in a somehow more efficient way to update all
-- first level k/v entries of the received data
content(data)
out = {}
LAMBDA.map(data,function(k,v)
out[k] = own:encrypt_weak_aes_cbc(
session,str(v))
:base64()
out = LAMBDA.map(data,function(k,v)
header = OCTET.msgpack({ key=k,
pubkey=own:public() })
enc = ECDH.encrypt(own,decidim_key,str(v), header)
oct = OCTET.msgpack( map(enc,base64) )
return oct:base64()
end)
-- print out result
print(JSON.encode(out))
......@@ -49,7 +49,7 @@ header['community_id'] = keys['community_id']
-- The output is a table with crypto contents which is standard for
-- zenroom's functions encrypt/decrypt: .checksum .header .iv .text
output = encrypt(devkey,
output = ECDH.encrypt(devkey,
base64(keys.community_pubkey),
MSG.pack(payload), MSG.pack(header))
......
......@@ -12,7 +12,7 @@ for i,name in ipairs(recipients) do
kk = ECDH.new()
kk:keygen()
keys[name] = kk:public():base64()
assert(kk:checkpub(kk:public()))
assert(ECDH.checkpub(kk))
end
......
......@@ -12,7 +12,7 @@ S = SCHEMA -- alias
RNG = require('zenroom_rng')
OCTET = require('zenroom_octet')
O = OCTET -- alias
ECDH = require('ecdh')
ECDH = require('zenroom_ecdh')
LAMBDA = require('functional')
L = LAMBDA -- alias
INSIDE = require('inspect')
......@@ -44,36 +44,6 @@ function content(var)
end
end
-- encrypt with default AES-GCM technique, returns base58 encoded
-- values into a table containing: .text .iv .checksum .header
function encrypt(alice, bob, msg, header)
local key = alice:session(bob)
local iv = RNG.new():octet(16)
-- convert strings to octets
local omsg, ohead
if(type(msg) == "string") then
omsg = str(msg) else omsg = msg end
if(type(header) == "string") then
ohead = str(header) else ohead = header end
cypher = {header = ohead, iv = iv}
cypher.text, cypher.checksum = ECDH.encrypt(key,omsg,iv,ohead)
return(cypher)
end
function decrypt(alice, bob, cypher)
key = alice:session(bob)
decode = {header = cypher.header}
decode.text, decode.checksum =
ECDH.decrypt(key,
cypher.text,
cypher.iv,
cypher.header)
if(cypher.checksum ~= decode.checksum) then
error("decrypt error: header checksum mismatch")
end
return(decode)
end
-- map values in place
function map(data, fun)
if(type(data) ~= "table") then
......
......@@ -51,14 +51,14 @@ octet.from_base64 = function(s)
end
-- msgpack returning octets
function msgpack(data)
if (type(data) == "zenroom.octet") then return str(MSG.pack(data:string())) end
function octet.msgpack(data)
if (type(data) == "zenroom.octet") then return str(MSG.pack(data:base64())) end
-- else
return str(MSG.pack(data))
end
-- msgunpack returning lua's tables or single types
function msgunpack(data)
function octet.msgunpack(data)
if (type(data) == "table") then error("unpack: argument is already a table") return
elseif(type(data) == "zenroom.octet") then return MSG.unpack(data:string())
elseif(type(data) == "string") then return MSG.unpack(data)
......
This diff is collapsed.
......@@ -207,52 +207,26 @@ static int ecdh_checkpub(lua_State *L) {
*/
static int ecdh_session(lua_State *L) {
HERE();
void *ud;
octet *pubkey;
ecdh *pk;
ecdh *e = ecdh_arg(L,1); SAFE(e);
HEREecdh(e);
SAFE(e->seckey);
// argument is another keyring
ud = luaL_testudata(L, 2, "zenroom.ecdh");
if(ud) {
HEREs("argument is an ecdh keyring");
pk = (ecdh*)ud;
HEREecdh(pk);
if(!pk->pubkey) {
lerror(L, "%s: public key not found in keyring",__func__);
return 0; }
pubkey = pk->pubkey; // take public key from keyring
func(L, "%s: public key found in ecdh keyring (%u bytes)",
__func__, pubkey->len);
goto finish;
}
ud = luaL_testudata(L, 2, "zenroom.octet");
if(ud) {
HEREs("argument is an octet");
pubkey = (octet*)ud; // take public key from octet
HEREoct(pubkey);
goto finish;
}
lerror(L, "%s: invalid key in argument",__func__);
return 0;
pubkey = o_arg(L,2); SAFE(pubkey);
finish:{
int res;
res = (*e->ECP__PUBLIC_KEY_VALIDATE)(pubkey);
if(res == ECDH_INVALID_PUBLIC_KEY) {
lerror(L, "%s: argument found, but is an invalid key",__func__);
return 0; }
octet *kdf = o_new(L,e->hash); SAFE(kdf);
octet *ses = o_new(L,e->keysize); SAFE(ses);
(*e->ECP__SVDP_DH)(e->seckey,pubkey,ses);
// here the NULL could be a salt (TODO: global?)
// its used as 'p' in the hash function
// ehashit(sha,z,counter,p,&H,0);
KDF2(e->hash,ses,NULL,e->hash,kdf);
return 2;
}
int res;
res = (*e->ECP__PUBLIC_KEY_VALIDATE)(pubkey);
if(res == ECDH_INVALID_PUBLIC_KEY) {
lerror(L, "%s: argument found, but is an invalid key",__func__);
return 0; }
octet *kdf = o_new(L,e->hash); SAFE(kdf);
octet *ses = o_new(L,e->keysize); SAFE(ses);
(*e->ECP__SVDP_DH)(e->seckey,pubkey,ses);
// here the NULL could be a salt (TODO: global?)
// its used as 'p' in the hash function
// ehashit(sha,z,counter,p,&H,0);
KDF2(e->hash,ses,NULL,e->hash,kdf);
return 2;
}
/**
......@@ -336,58 +310,6 @@ static int ecdh_private(lua_State *L) {
return 1;
}
/**
AES encrypts a plaintext to a ciphtertext. Function compatible with
IEEE-1363 `AES_CBC_IV0_ENCRYPT`. Encrypts in CBC mode with a zero
IV, pads necessary to create a full final block. This is weak
encryption and @{keyring:encrypt} should be preferred.
@param key AES key octet
@param message input text in an octet
@return a new octet containing the output ciphertext
@function keyring:encrypt_weak_aes_cbc(key, message)
*/
static int ecdh_encrypt_weak_aes_cbc(lua_State *L) {
HERE();
ecdh *e = ecdh_arg(L, 1); SAFE(e);
octet *k = o_arg(L, 2); SAFE(k);
octet *in = o_arg(L, 3); SAFE(in);
// output is padded to next word
octet *out = o_new(L, in->len+0x0f); SAFE(out);
HEREoct(k);
HEREoct(in);
AES_CBC_IV0_ENCRYPT(k,in,out);
HEREoct(out);
return 1;
}
/**
AES decrypts a plaintext to a ciphtertext. Function compabible
with IEEE-1363 specification for AES CBC using IV set to
zero. Decrypts a secret produced using
@{keyring:encrypt_weak_aes_cbc} in CBC mode.
@param key AES key octet
@param ciphertext input ciphertext octet
@return a new octet containing the decrypted plain text, or false when failed
@function keyring:decrypt_weak_aes_cbc(key, ciphertext)
*/
static int ecdh_decrypt_weak_aes_cbc(lua_State *L) {
HERE();
ecdh *e = ecdh_arg(L, 1); SAFE(e);
octet *k = o_arg(L, 2); SAFE(k);
octet *in = o_arg(L, 3); SAFE(in);
// output is padded to next word
octet *out = o_new(L, in->len+16); SAFE(out);
if(!AES_CBC_IV0_DECRYPT(k,in,out)) {
error(L, "%s: decryption failed.",__func__);
lua_pop(L, 1);
lua_pushboolean(L, 0);
}
return 1;
}
/**
AES-GCM encrypt with Additional Data (AEAD) encrypts and
......@@ -620,21 +542,19 @@ static int ecdh_random(lua_State *L) {
#define COMMON_METHODS \
{"session",ecdh_session}, \
{"public", ecdh_public}, \
{"private", ecdh_private}, \
{"encrypt_weak_aes_cbc", ecdh_encrypt_weak_aes_cbc}, \
{"decrypt_weak_aes_cbc", ecdh_decrypt_weak_aes_cbc}, \
{"hash", ecdh_hash}, \
{"hmac", ecdh_hmac}, \
{"kdf2", ecdh_kdf2}, \
{"pbkdf2", ecdh_pbkdf2}, \
{"checkpub", ecdh_checkpub}
{"private", ecdh_private}
int luaopen_ecdh(lua_State *L) {
const struct luaL_Reg ecdh_class[] = {
{"new",lua_new_ecdh},
{"keygen",ecdh_new_keygen},
{"encrypt", ecdh_aead_encrypt},
{"decrypt", ecdh_aead_decrypt},
{"aead_encrypt", ecdh_aead_encrypt},
{"aead_decrypt", ecdh_aead_decrypt},
{"hash", ecdh_hash},
{"hmac", ecdh_hmac},
{"kdf2", ecdh_kdf2},
{"pbkdf2", ecdh_pbkdf2},
{"checkpub", ecdh_checkpub},
COMMON_METHODS,
{NULL,NULL}};
const struct luaL_Reg ecdh_methods[] = {
......
......@@ -18,26 +18,25 @@ function test_curve (name)
assert(ask:hex() == alice:private():hex()) -- compare strings
assert(ask == alice:private()) -- compare octects
kdf = alice:session(bob)
-- AES-GCM encryption
iv = rng:octet(16)
-- iv = octet.hex('00000000000000000000000000000000')
header = octet.string('This is the header!')
ciphermsg, ck = ECDH.encrypt(kdf, secret, iv, header)
ciphermsg = ECDH.encrypt(alice, bob, secret, header)
print ('AES-GCM encrypt : ' .. ciphermsg:base64())
print ('AES-GCM checksum : ' .. ck:base64())
print ('AES-GCM encrypt : ' .. ciphermsg.text:base64())
print ('AES-GCM checksum : ' .. ciphermsg.checksum:base64())
decipher, ck2 = ECDH.decrypt(kdf, ciphermsg, iv, header)
decipher = ECDH.decrypt(alice, bob, ciphermsg)
print ('AES-GCM checksum : ' .. ck2:base64())
-- print ('AES-GCM checksum : ' .. ck2:base64())
assert(secret == decipher)
assert(secret == decipher.text)
assert(header == decipher.header)
print 'decipher message:'
print(header:string())
print(decipher:string())
print(decipher.header:string())
print(decipher.text:string())
print (' AES-GCM on ' .. name .. ' OK')
end
......
......@@ -9,7 +9,7 @@ function Test(t)
curve = ECDH.new('goldilocks')
print ("Test vector: " .. t.name)
out, tag_out = curve:encrypt(hex(t.key), hex(t.msg), hex(t.iv), hex(t.header))
out, tag_out = ECDH.aead_encrypt(hex(t.key), hex(t.msg), hex(t.iv), hex(t.header))
assert(hex(t.ciphermsg) == out)
print (' encrypt OK')
......
......@@ -45,7 +45,7 @@ recipient = ECDH.new('$curve')
recipient:public(hex(data['pubkey']))
sender = ECDH.new('$curve')
sender:private(hex(keys['private']))
enc,tag = encrypt(sender,recipient,str(data['message']),sender:public())
enc = ECDH.encrypt(sender,recipient,str(data['message']),sender:public())
print(JSON.encode(map(enc,hex)))
EOF
}
......@@ -60,7 +60,7 @@ recipient = ECDH.new('$curve')
recipient:private(hex(keys['private']))
sender = ECDH.new('$curve')
sender:public(hex(data['header']))
dec = decrypt(recipient,sender,map(data,hex))
dec = ECDH.decrypt(recipient,sender,map(data,hex))
print(dec.text:string())
EOF
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment