Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AES stream codecs #19

Closed
wants to merge 6 commits into from
Closed

Add AES stream codecs #19

wants to merge 6 commits into from

Conversation

jeanparpaillon
Copy link

When encoding (large files), working with binaries can rapidly kill the VM.

This patch adds 2 functions for encoding and decoding streams with AES (CTR mode)

For instance:

alias Kryptonite.AES
{key, iv} = {AES.generate_key!(), Random.bytes!(16)}
File.write!("/tmp/mysecret.txt", "This is a secret")
File.stream!("./mysecret.txt") |> AES.stream_encrypt(key, iv) |> Stream.into(File.stream!("./mysecret.enc")) |> Stream.run()
"This is a secret" = File.stream!("./mysecret.enc") |> AES.stream_decrypt(key, iv) |> Enum.to_list() |> :erlang.iolist_to_binary

## Examples

iex> {key, iv} = {generate_key!(), Random.bytes!(16)}
iex> cypher = 'This is a secret' |> stream_encrypt(key, iv) |> Enum.to_list()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Readability:

iex> cypher = "This is a secret"
...>    |> stream_encrypt(key, iv)
...>    |> Enum.to_list()

iex> {key, iv} = {generate_key!(), Random.bytes!(16)}
iex> msg = "This is a secret..."
iex> cypher = msg |> String.to_charlist() |> stream_encrypt(key, iv) |> Enum.to_list()
iex> msg == cypher |> stream_decrypt(key, iv) |> Enum.to_list() |> :erlang.iolist_to_binary
Copy link
Member

@hickscorp hickscorp Sep 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! Readability:

iex> msg == cypher
...>    |> stream_decrypt(key, iv)
...>    |> Enum.to_list()
...>    |> :erlang.iolist_to_binary

## Examples

iex> {key, iv} = {generate_key!(), Random.bytes!(16)}
iex> File.write!("/tmp/plain.txt", "This is a secret")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Race condition here when running parallel builds. I'd probably add a timestamp based identifier to the filename. WDYT?

iex> {:ok, tag} =
...> "/tmp/plain.txt"
...> |> File.stream!()
...> |> stream_encrypt(File.stream!("/tmp/secret.aes"), key, iv, "Auth...")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Race condition - maybe reuse the same timestamp / random identifier here?

@doc """
Check integrity then decrypts a stream encrypted with `stream_encrypt/5`

Raise `Kryptonite.AES.StreamIntegrityError` in case of integrity checking error.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love it! 👍

|> Stream.concat(in_stream)
|> stream_tag(ad)
|> case do
^tag ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really cool.

@@ -244,4 +346,9 @@ defmodule Kryptonite.AES do

@spec cut_key(binary) :: binary
defp cut_key(<<key::binary-size(@key_byte_size), _::binary>>), do: key

defp do_stream_encrypt(elem, acc) do
Copy link
Member

@hickscorp hickscorp Sep 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could have this a def instead of defp so later we could unit-test it in isolation and have a @spec that properly guards weird changes from happening?

@@ -244,4 +346,9 @@ defmodule Kryptonite.AES do

@spec cut_key(binary) :: binary
defp cut_key(<<key::binary-size(@key_byte_size), _::binary>>), do: key

defp do_stream_encrypt(elem, acc) do
{acc, cypher} = :crypto.stream_encrypt(acc, elem |> List.wrap())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish we could pipe stuff on 2nd argument... Wouldn't an infix library be awesome? Something that would allow:

{acc, cypher} =  elem
  |> List.wrap()
  2> :crypto.stream_encrypt(acc)

@hickscorp hickscorp mentioned this pull request Sep 16, 2018
@hickscorp
Copy link
Member

@jeanparpaillon I'm closing this, because I needed to be able to change it and I didn't have access to your repo. Instead I've opened a PR on a local branch that you also have access to. See #20

@hickscorp hickscorp closed this Sep 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants