Skip to content
This repository has been archived by the owner on Apr 1, 2020. It is now read-only.

starbelly/soda

Repository files navigation

Soda Soda Hex Version Gitlab-CI Travis-CI License


This project is no longer maintained

Please use and contribute to enacl

therecanonlybeone

Libsodium bindings for Erlang

About

Soda provides Erlang bindings to the Sodium Crypto Library (libsodium).

Working with Soda is simple in both Erlang and Elixr.

  • Erlang
1> N = soda:nonce(aead_xchacha20poly1305_ietf).
<<115,97,120,157,28,208,118,165,137,95,122,152,195,49,52,
  188,73,136,216,201,77,183,29,144>>
2> {ok, H} = soda:password_hash("foo").
{ok,<<"$argon2id$v=19$m=65536,t=2,p=1$Isq7U9BICzjQKL7HhrpdtA$8WEFXpc6a3ef+DMZELmmxA23xTCQq9CpN6/NPHXBUPg">>}
2> true = soda:password_verify(H, "foo").
true
  • Elixir
iex(1)> n = :soda.nonce(:aead_xchacha20poly1305_ietf)
<<115,97,120,157,28,208,118,165,137,95,122,152,195,49,52,
  188,73,136,216,201,77,183,29,144>>
iex(2)> {:ok, h} = :soda.password_hash("foo")
{:ok,<<"$argon2id$v=19$m=65536,t=2,p=1$Isq7U9BICzjQKL7HhrpdtA$8WEFXpc6a3ef+DMZELmmxA23xTCQq9CpN6/NPHXBUPg">>}
iex(3)> true = :soda.password_verify(h, "foo")
true

Installation

Rebar3

{deps, [{soda, "1.1.1"}]}

Mix

def deps do
  [{:soda, "~> 1.1"}]
end

Usage

Soda

nonce/1

1> N = soda:nonce(aead_xchacha20poly1305_ietf).
<<115,97,120,157,28,208,118,165,137,95,122,152,195,49,52,
  188,73,136,216,201,77,183,29,144>>

rand/1

soda:rand(42).
<<83,247,61,202,83,171,99,56,51,108,141,82,255,186,41,26,
  215,4,229,148,72,204,131,248,8,86,196,104,95,...>>

password_hash/1 and password_verify/2

1> {ok, H} = soda:password_hash("thuper thecret").
{ok,<<"$argon2id$v=19$m=65536,t=2,p=1$rPQCfeJLuKMoLei+d5o9uA$7LsyBNEnYVq2JOpTgD2cil+swou5gvewoEjcuQznYq0">>}
2> true = soda:password_verify(H, "thuper thecret").
true

aead_encrypt/2 and aead_decrypt/4

1> {ok, Ciphered, Nonce, Key} = soda:aead_encrypt(<<"Secret Msg">>, <<"Additional Data">>).
{ok,<<183,123,21,95,51,26,85,197,41,226,96,91,26,28,16,
      110,85,123,18,239,29,57,11,30,228,61>>,
    <<238,32,82,106,137,223,37,173,68,177,216,210,169,168,126,
      245,10,247,27,161,127,195,217,24>>,
    <<8,108,139,115,235,95,95,44,128,74,18,186,2,207,2,83,79,
      53,115,239,12,118,198,100,198,...>>}
2> soda:aead_decrypt(Ciphered, <<"Additional Data">>, Nonce, Key).
{ok,<<"Secret Msg">>}

Generic Hashing

Single part

Without a key

1> {ok, Hash} = soda_api:hash(<<"Every book is a children's book if the kid can read!">>).
{ok,<<110,213,155,8,43,187,167,80,146,177,214,102,226,62,
      107,0,197,188,250,227,26,127,216,211,82,111,20,...>>}

With a key

  1> Key = soda:rand(16).
  <<210,27,222,99,117,191,249,46,192,189,137,37,59,128,142,38>>
  2> Data = <<"Every book is a children's book if the kid can read!">>.
  <<"Every book is a children's book if the kid can read!">>
  2> {ok, Hash} = soda_api:hash(Data, Key).
  {ok,<<46,245,69,115,197,167,214,2,175,251,181,113,130,
      234,222,98,228,42,249,24,59,129,29,107,213,124,46,...>>}
Multi-part

Without a key

1> {ok, State} = soda:hash_init().
{ok,#Ref<0.3118750307.3589668872.246381>}
2> Msg1 = <<"Any sufficiently complicated concurrent program in another language
contains an ad hoc informally-specified ">>.
<<"Any sufficiently complicated concurrent program in another language contains an ad hoc informally-specified ">>
3> {ok, State1} = soda:hash_update(State, Msg1).
{ok,#Ref<0.3118750307.3589668872.246382>}
4> Msg2 = <<"bug-ridden slow implementation of half of Erlang.">>.
<<"bug-ridden slow implementation of half of Erlang.">>
5> {ok, State2} = soda:hash_update(State1, Msg2).
{ok,#Ref<0.3118750307.3589668872.246383>}
6> {ok, Hash} = soda:hash_final(State2).
{ok,<<213,253,49,236,84,178,244,57,188,147,14,175,172,74,
      105,61,99,4,143,138,246,208,235,82,205,74,211,...>>}
7> Hex = soda:bin2hex(Hash).
<<"d5fd31ec54b2f439bc930eafac4a693d63048f8af6d0eb52cd4ad353e90fbc01">>

With a key

1> Key = <<"Virding's first rule of programming">>,
<<"Virding's first rule of programming">>,
2> {ok, State} = soda:hash_init(Key).
{ok,#Ref<0.791790418.1979056136.39831>}
3> Msg1 = <<"Any sufficiently complicated concurrent program in another language
contains an ad hoc informally-specified ">>.
<<"Any sufficiently complicated concurrent program in another language contains an ad hoc informally-specified">>
4> {ok, State1} = soda:hash_update(State, Msg1).
{ok,#Ref<0.791790418.1979056136.39832>}
5> Msg2 = <<"bug-ridden slow implementation of half of Erlang.">>.
<<"bug-ridden slow implementation of half of Erlang.">>
6> {ok, State2} = soda:hash_update(State1, Msg2).
{ok,#Ref<0.791790418.1979056136.39833>}
7> {ok, Hash} = soda:hash_final(State2).
{ok,<<103,191,97,168,121,250,16,57,59,220,113,55,230,119,
    126,141,93,102,86,130,143,198,54,102,23,180,219,...>>}
8> Hex = soda_api:bin2hex(Hash).
<<"67bf61a879fa10393bdc7137e6777e8d5d6656828fc6366617b4db25e93bfe41">>

Soda API

For advanced usage soda provides soda_api

AEAD constructions

XChaCha20-Poly1305 construction
  1. Generate a public nonce
1> N = soda_api:randombytes(24).
<<115,97,120,157,28,208,118,165,137,95,122,152,195,49,52,
  188,73,136,216,201,77,183,29,144>>
  1. Generate a secret key using the soda_api module
2> K = soda_api:aead_xchacha20poly1305_ietf_keygen().
<<234,19,163,89,73,193,122,110,11,196,215,227,56,193,126,
  110,228,27,49,107,19,123,43,168,255,60,92,13,49,...>>
  1. Encrypt a message with some non-confidential additional data and our secret key and nonce
  3> C = soda_api:aead_xchacha20poly1305_ietf_encrypt(<<"Hello, Mike?">>, <<"Hello, Joe.">>, N, K ).
  <<218,218,199,94,171,25,110,199,107,224,186,225,52,248,
    185,1,53,39,16,167,91,24,155,31,143,195,89,87>>
  1. Decrypt the ciphered message using our additional data, nonce, and key
  4> D = soda_api:aead_xchacha20poly1305_ietf_decrypt(C, <<"Hello, Joe.">>, N, K).
  <<"Hello, Mike?">>

Generic Hashing

Single part

Without a key

1> {ok, Hash} = soda_api:generichash(<<"Every book is a children's book if the kid can read!">>).
{ok,<<110,213,155,8,43,187,167,80,146,177,214,102,226,62,
    107,0,197,188,250,227,26,127,216,211,82,111,20,...>>}

With a key

1> Key = soda_api:randombytes(16).
<<210,27,222,99,117,191,249,46,192,189,137,37,59,128,142,38>>
2> Data = <<"Every book is a children's book if the kid can read!">>.
<<"Every book is a children's book if the kid can read!">>
3> {ok, Hash} = soda_api:generichash(<<"Every book is a children's book if the kid can read!">>, Key).
{ok,<<46,245,69,115,197,167,214,2,175,251,181,113,130,
    234,222,98,228,42,249,24,59,129,29,107,213,124,46,...>>}
Multi-part

Without a key

1> {ok, State} = soda_api:generichash_init().
{ok,#Ref<0.3118750307.3589668872.246381>}
2> Msg1 = <<"Any sufficiently complicated concurrent program in another language
contains an ad hoc informally-specified ">>.
<<"Any sufficiently complicated concurrent program in another language contains an ad hoc informally-specified ">>
3> {ok, State1} = soda_api:generichash_update(State,  Msg1).
{ok,#Ref<0.3118750307.3589668872.246382>}
4> Msg2 = <<"bug-ridden slow implementation of half of Erlang.">>.
<<"bug-ridden slow implementation of half of Erlang.">>
5> {ok, State2} = soda_api:generichash_update(State1, Msg).
{ok,#Ref<0.3118750307.3589668872.246383>}
6> {ok, Hash} = soda_api:generichash_final(State2).
{ok,<<213,253,49,236,84,178,244,57,188,147,14,175,172,74,
    105,61,99,4,143,138,246,208,235,82,205,74,211,...>>}
7> Hex = soda_api:bin2hex(Hash).
<<"d5fd31ec54b2f439bc930eafac4a693d63048f8af6d0eb52cd4ad353e90fbc01">>

With a key

1> {ok, State} = soda_api:generichash_init(<<"Virding's first rule of programming">>).
{ok,#Ref<0.791790418.1979056136.39831>}
2> Msg1 = <<"Any sufficiently complicated concurrent program in another language
contains an ad hoc informally-specified ">>.
<<"Any sufficiently complicated concurrent program in another language contains an ad hoc informally-specified ">>
3> {ok, State1} = soda_api:generichash_update(State, Msg1).
{ok,#Ref<0.791790418.1979056136.39832>}
4> Msg2 = <<"bug-ridden slow implementation of half of Erlang.">>.
<<"bug-ridden slow implementation of half of Erlang.">>
5> {ok, State2} = soda_api:generichash_update(State1, Msg2).
{ok,#Ref<0.791790418.1979056136.39833>}
6> {ok, Hash} = soda:generichash_final(State2).
{ok,<<103,191,97,168,121,250,16,57,59,220,113,55,230,119,
    126,141,93,102,86,130,143,198,54,102,23,180,219,...>>}
7> Hex = soda_api:bin2hex(Hash).
<<"67bf61a879fa10393bdc7137e6777e8d5d6656828fc6366617b4db25e93bfe41">>

Reference

Inspirado