Symmetric state layer
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
, encrypt_with_ad/3
|
||||
, has_key/1
|
||||
, init/2
|
||||
, key/1
|
||||
, rekey/1
|
||||
, set_key/2
|
||||
, set_nonce/2
|
||||
@@ -70,3 +71,7 @@ rekey(CState = #noise_cs{ k = K, cipher = Cipher }) ->
|
||||
-spec cipher(CState :: state()) -> noise_cipher().
|
||||
cipher(#noise_cs{ cipher = Cipher }) ->
|
||||
Cipher.
|
||||
|
||||
-spec key(CState :: state()) -> key().
|
||||
key(#noise_cs{ k = K }) ->
|
||||
K.
|
||||
|
||||
+36
-5
@@ -6,7 +6,7 @@
|
||||
|
||||
-include("enoise.hrl").
|
||||
|
||||
-export([decrypt/5, encrypt/5, rekey/2, hash/2, pad/3, hashlen/1, dhlen/1, new_key_pair/1, hkdf/3, dh/3]).
|
||||
-export([decrypt/5, encrypt/5, rekey/2, hash/2, pad/3, hashlen/1, dhlen/1, new_key_pair/1, hmac/3, hkdf/3, dh/3]).
|
||||
|
||||
new_key_pair(dh25519) ->
|
||||
KeyPair = enacl:crypto_sign_ed25519_keypair(),
|
||||
@@ -16,7 +16,19 @@ new_key_pair(dh25519) ->
|
||||
dh(dh25519, KeyPair, PubKey) ->
|
||||
enacl:curve25519_scalarmult(KeyPair#key_pair.pik, PubKey).
|
||||
|
||||
hkdf(_, _, _) -> [].
|
||||
hmac(Hash, Key, Data) ->
|
||||
BLen = blocklen(blake2b),
|
||||
Block1 = hmac_format_key(Hash, Key, 16#36, BLen),
|
||||
Hash1 = hash(Hash, <<Block1/binary, Data/binary>>),
|
||||
Block2 = hmac_format_key(Hash, Key, 16#5C, BLen),
|
||||
hash(Hash, <<Block2/binary, Hash1/binary>>).
|
||||
|
||||
hkdf(Hash, Key, Data) ->
|
||||
TempKey = hmac(Hash, Key, Data),
|
||||
Output1 = hmac(Hash, TempKey, <<1:8>>),
|
||||
Output2 = hmac(Hash, TempKey, <<Output1/binary, 2:8>>),
|
||||
Output3 = hmac(Hash, TempKey, <<Output2/binary, 3:8>>),
|
||||
[Output1, Output2, Output3].
|
||||
|
||||
rekey(Cipher, K) ->
|
||||
encrypt(Cipher, K, ?MAX_NONCE, <<>>, <<0:(32*8)>>).
|
||||
@@ -32,9 +44,9 @@ decrypt('ChaChaPoly', K, N, Ad, CipherText) ->
|
||||
enacl:aead_chacha20poly1305_decrypt(K, N, Ad, CipherText).
|
||||
|
||||
hash(blake2b, Data) ->
|
||||
enacl:generichash(64, Data);
|
||||
hash(blake2s, Data) ->
|
||||
enacl:generichash(32, Data).
|
||||
{ok, Hash} = enacl:generichash(64, Data), Hash;
|
||||
hash(Hash, _Data) ->
|
||||
error({hash_not_implemented_yet, Hash}).
|
||||
|
||||
pad(Data, MinSize, PadByte) ->
|
||||
case byte_size(Data) of
|
||||
@@ -50,5 +62,24 @@ hashlen(sha512) -> 64;
|
||||
hashlen(blake2s) -> 32;
|
||||
hashlen(blake2b) -> 64.
|
||||
|
||||
blocklen(sha256) -> 64;
|
||||
blocklen(sha512) -> 128;
|
||||
blocklen(blake2s) -> 64;
|
||||
blocklen(blake2b) -> 128.
|
||||
|
||||
dhlen(dh25519) -> 32;
|
||||
dhlen(dh448) -> 56.
|
||||
|
||||
%%% Local implementations
|
||||
|
||||
|
||||
hmac_format_key(Hash, Key0, Pad, BLen) ->
|
||||
Key1 =
|
||||
case byte_size(Key0) =< BLen of
|
||||
true -> Key0;
|
||||
false -> hash(Hash, Key0)
|
||||
end,
|
||||
Key2 = pad(Key1, BLen, 0),
|
||||
<<PadWord:32>> = <<Pad:8, Pad:8, Pad:8, Pad:8>>,
|
||||
<< <<(Word bxor PadWord):32>> || <<Word:32>> <= Key2 >>.
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(enoise_protocol).
|
||||
|
||||
-include("enoise.hrl").
|
||||
|
||||
-export([to_name/1]).
|
||||
|
||||
to_name(_Protocol) ->
|
||||
<<"Noise_XK_25519_ChaChaPoly_BLAKE2b">>.
|
||||
@@ -0,0 +1,101 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @copyright (C) 2018, Aeternity Anstalt
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(enoise_sym_state).
|
||||
|
||||
-export([ cipher_state/1
|
||||
, ck/1
|
||||
, decrypt_and_hash/2
|
||||
, encrypt_and_hash/2
|
||||
, h/1
|
||||
, hash/1
|
||||
, init/1
|
||||
, mix_hash/2
|
||||
, mix_key/2
|
||||
, mix_key_and_hash/2
|
||||
, split/1
|
||||
]).
|
||||
|
||||
-include("enoise.hrl").
|
||||
|
||||
-type noise_hash() :: sha256 | sha512 | blake2s | blake2b.
|
||||
|
||||
-record(noise_ss, { cs :: enoise_cipher_state:state()
|
||||
, ck = <<>> :: binary()
|
||||
, h = <<>> :: binary()
|
||||
, hash = blake2b :: noise_hash() }).
|
||||
|
||||
-opaque state() :: #noise_ss{}.
|
||||
-export_type([noise_hash/0, state/0]).
|
||||
|
||||
-spec init(Protocol :: #noise_protocol{}) -> state().
|
||||
init(Protocol = #noise_protocol{ hash = Hash, cipher = Cipher }) ->
|
||||
Name = enoise_protocol:to_name(Protocol),
|
||||
HashLen = enoise_crypto:hashlen(Hash),
|
||||
H1 =
|
||||
case byte_size(Name) > HashLen of
|
||||
true -> enoise_crypto:hash(Hash, Name);
|
||||
false -> enoise_crypto:pad(Name, HashLen, 16#00)
|
||||
end,
|
||||
#noise_ss{ h = H1
|
||||
, ck = H1
|
||||
, hash = Hash
|
||||
, cs = enoise_cipher_state:init(empty, Cipher) }.
|
||||
|
||||
-spec mix_key(SState :: state(), InputKeyMaterial :: binary()) -> state().
|
||||
mix_key(SState = #noise_ss{ hash = Hash, ck = CK0, cs = CS0 }, InputKeyMaterial) ->
|
||||
[CK1, <<TempK:32/binary, _/binary>> | _] =
|
||||
enoise_crypto:hkdf(Hash, CK0, InputKeyMaterial),
|
||||
CS1 = enoise_cipher_state:set_key(CS0, TempK),
|
||||
SState#noise_ss{ ck = CK1, cs = CS1 }.
|
||||
|
||||
-spec mix_hash(SState :: state(), Data :: binary()) -> state().
|
||||
mix_hash(SState = #noise_ss{ hash = Hash, h = H0 }, Data) ->
|
||||
H1 = enoise_crypto:hash(Hash, <<H0/binary, Data/binary>>),
|
||||
SState#noise_ss{ h = H1 }.
|
||||
|
||||
-spec mix_key_and_hash(SState :: state(), InputKeyMaterial :: binary()) -> state().
|
||||
mix_key_and_hash(SState = #noise_ss{ hash = Hash, ck = CK0, cs = CS0 }, InputKeyMaterial) ->
|
||||
[CK1, TempH, <<TempK:32/binary, _/binary>>] =
|
||||
enoise_crypto:hkdf(Hash, CK0, InputKeyMaterial),
|
||||
CS1 = enoise_cipher_state:set_key(CS0, TempK),
|
||||
mix_hash(SState#noise_ss{ ck = CK1, cs = CS1 }, TempH).
|
||||
|
||||
-spec encrypt_and_hash(SState :: state(), PlainText :: binary()) -> {ok, state(), binary()}.
|
||||
encrypt_and_hash(SState = #noise_ss{ cs = CS0, h = H }, PlainText) ->
|
||||
{ok, CS1, CipherText} = enoise_cipher_state:encrypt_with_ad(CS0, H, PlainText),
|
||||
{ok, mix_hash(SState#noise_ss{ cs = CS1 }, CipherText), CipherText}.
|
||||
|
||||
-spec decrypt_and_hash(SState :: state(), CipherText :: binary()) ->
|
||||
{ok, state(), binary()} | {error, term()}.
|
||||
decrypt_and_hash(SState = #noise_ss{ cs = CS0, h = H }, CipherText) ->
|
||||
case enoise_cipher_state:decrypt_with_ad(CS0, H, CipherText) of
|
||||
Err = {error, _} ->
|
||||
Err;
|
||||
{ok, CS1, PlainText} ->
|
||||
{ok, mix_hash(SState#noise_ss{ cs = CS1 }, CipherText), PlainText}
|
||||
end.
|
||||
|
||||
-spec split(SState :: state()) -> {enoise_cipher_state:state(), enoise_cipher_state:state()}.
|
||||
split(#noise_ss{ hash = Hash, ck = CK, cs = CS }) ->
|
||||
[<<TempK1:32/binary, _/binary>>, <<TempK2:32/binary, _/binary>>, _] =
|
||||
enoise_crypto:hkdf(Hash, CK, <<>>),
|
||||
{enoise_cipher_state:set_key(CS, TempK1),
|
||||
enoise_cipher_state:set_key(CS, TempK2)}.
|
||||
|
||||
-spec cipher_state(SState :: state()) -> enoise_cipher_state:state().
|
||||
cipher_state(#noise_ss{ cs = CS }) ->
|
||||
CS.
|
||||
|
||||
-spec ck(SState :: state()) -> binary().
|
||||
ck(#noise_ss{ ck = CK }) ->
|
||||
CK.
|
||||
|
||||
-spec h(SState :: state()) -> binary().
|
||||
h(#noise_ss{ h = H }) ->
|
||||
H.
|
||||
|
||||
-spec hash(SState :: state()) -> noise_hash().
|
||||
hash(#noise_ss{ hash = Hash }) ->
|
||||
Hash.
|
||||
Reference in New Issue
Block a user