changed
README.md
|
@@ -1,39 +1,39 @@
|
1
|
- # HKDF
|
2
|
-
|
3
|
- Provides a simple Hashed Message Authentication Code (HMAC)-based
|
4
|
- key derivation function (HKDF).
|
5
|
-
|
6
|
- Based on the algorithm defined in [rfc 5859](https://tools.ietf.org/html/rfc5869).
|
7
|
-
|
8
|
- ## Usage
|
9
|
-
|
10
|
- Derive key:
|
11
|
- ```elixir
|
12
|
- HKDF.derive(:sha256, "some input", 42, "optional salt", "optional secret message")
|
13
|
- ```
|
14
|
-
|
15
|
- Expand pseudorandom key:
|
16
|
- ```elixir
|
17
|
- HKDF.extract(:sha256, "some input", "optional salt")
|
18
|
- ```
|
19
|
-
|
20
|
- Extract output key material:
|
21
|
- ```elixir
|
22
|
- prk = HKDF.extract(:sha256, "some input", "optional salt")
|
23
|
- HKDF.expand(:sha256, prk, 16, "optional secret message")
|
24
|
- ```
|
25
|
-
|
26
|
- ## Installation
|
27
|
-
|
28
|
- If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
29
|
- by adding `hkdf` to your list of dependencies in `mix.exs`:
|
30
|
-
|
31
|
- ```elixir
|
32
|
- def deps do
|
33
|
- [{:hkdf, "~> 0.1.0"}]
|
34
|
- end
|
35
|
- ```
|
36
|
-
|
37
|
- Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
38
|
- and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
39
|
- be found at [https://hexdocs.pm/hkdf](https://hexdocs.pm/hkdf).
|
1
|
+ # HKDF
|
2
|
+
|
3
|
+ Provides a simple Hashed Message Authentication Code (HMAC)-based
|
4
|
+ key derivation function (HKDF).
|
5
|
+
|
6
|
+ Based on the algorithm defined in [rfc 5859](https://tools.ietf.org/html/rfc5869).
|
7
|
+
|
8
|
+ ## Usage
|
9
|
+
|
10
|
+ Derive key:
|
11
|
+ ```elixir
|
12
|
+ HKDF.derive(:sha256, "some input", 42, "optional salt", "optional secret message")
|
13
|
+ ```
|
14
|
+
|
15
|
+ Expand pseudorandom key:
|
16
|
+ ```elixir
|
17
|
+ HKDF.extract(:sha256, "some input", "optional salt")
|
18
|
+ ```
|
19
|
+
|
20
|
+ Extract output key material:
|
21
|
+ ```elixir
|
22
|
+ prk = HKDF.extract(:sha256, "some input", "optional salt")
|
23
|
+ HKDF.expand(:sha256, prk, 16, "optional secret message")
|
24
|
+ ```
|
25
|
+
|
26
|
+ ## Installation
|
27
|
+
|
28
|
+ If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
29
|
+ by adding `hkdf` to your list of dependencies in `mix.exs`:
|
30
|
+
|
31
|
+ ```elixir
|
32
|
+ def deps do
|
33
|
+ [{:hkdf, "~> 0.2.0"}]
|
34
|
+ end
|
35
|
+ ```
|
36
|
+
|
37
|
+ Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
38
|
+ and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
39
|
+ be found at [https://hexdocs.pm/hkdf](https://hexdocs.pm/hkdf).
|
changed
hex_metadata.config
|
@@ -1,11 +1,11 @@
|
1
1
|
{<<"app">>,<<"hkdf">>}.
|
2
2
|
{<<"build_tools">>,[<<"mix">>]}.
|
3
3
|
{<<"description">>,<<"HMAC-based key derivation function.">>}.
|
4
|
- {<<"elixir">>,<<"~> 1.4">>}.
|
5
|
- {<<"files">>,[<<"lib/hkdf.ex">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>]}.
|
4
|
+ {<<"elixir">>,<<"~> 1.13">>}.
|
5
|
+ {<<"files">>,
|
6
|
+ [<<"lib">>,<<"lib/hkdf.ex">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>]}.
|
6
7
|
{<<"licenses">>,[<<"MIT">>]}.
|
7
|
- {<<"links">>,[{<<"GitHub">>,<<"https://github.com/sschneider1207/hkdf">>}]}.
|
8
|
- {<<"maintainers">>,[<<"Sam Schneider">>]}.
|
8
|
+ {<<"links">>,[{<<"GitHub">>,<<"https://github.com/jschneider1207/hkdf">>}]}.
|
9
9
|
{<<"name">>,<<"hkdf">>}.
|
10
10
|
{<<"requirements">>,[]}.
|
11
|
- {<<"version">>,<<"0.1.0">>}.
|
11
|
+ {<<"version">>,<<"0.2.0">>}.
|
changed
lib/hkdf.ex
|
@@ -1,101 +1,99 @@
|
1
|
- defmodule HKDF do
|
2
|
- @moduledoc """
|
3
|
- Provides a simple Hashed Message Authentication Code (HMAC)-based
|
4
|
- key derivation function (HKDF).
|
5
|
-
|
6
|
- ## Process
|
7
|
-
|
8
|
- Keys are derived in two steps:
|
9
|
-
|
10
|
- 1. Extract - a pseudorandom key is extracted from an input key material
|
11
|
- and optional salt.
|
12
|
- 2. Expand - an output key material of a specific length is expanded from
|
13
|
- hashes of the pseudorandom key and an optional info message.
|
14
|
-
|
15
|
- ## Source
|
16
|
-
|
17
|
- Defined in [rfc 5859](https://tools.ietf.org/html/rfc5869)
|
18
|
- """
|
19
|
- @type hash_fun :: :md5 | :sha | :sha224 | :sha256 | :sha384 | :sha512
|
20
|
- @type input_key_material :: binary
|
21
|
- @type salt :: binary
|
22
|
- @type pseudorandom_key :: binary
|
23
|
- @type length :: non_neg_integer
|
24
|
- @type info :: binary
|
25
|
- @type output_key_material :: binary
|
26
|
-
|
27
|
- @doc """
|
28
|
- Dervice a key of a specific length using the specified hash function.
|
29
|
-
|
30
|
- An optional salt (extract phase) and/or info message (expand phase)
|
31
|
- can be supplied.
|
32
|
-
|
33
|
- ## Example
|
34
|
-
|
35
|
- iex> HKDF.derive(:sha256, "some input", 16)
|
36
|
- <<47, 231, 129, 75, 82, 47, 198, 78, 55, 31, 167, 66, 15, 128, 63, 243>>
|
37
|
-
|
38
|
- iex> HKDF.derive(:sha256, "some input", 16, "salt", "secret message")
|
39
|
- <<28, 213, 201, 204, 16, 226, 160, 120, 69, 47, 46, 58, 15, 255, 54, 52>>
|
40
|
-
|
41
|
- """
|
42
|
- @spec derive(hash_fun, input_key_material, length, salt, info) :: output_key_material
|
43
|
- def derive(hash_fun, ikm, len, salt \\ "", info \\ "") do
|
44
|
- prk = extract(hash_fun, ikm, salt)
|
45
|
- expand(hash_fun, prk, len, info)
|
46
|
- end
|
47
|
-
|
48
|
- @doc """
|
49
|
- Extract a psuedorandom key from an input key material.
|
50
|
-
|
51
|
- ## Example
|
52
|
-
|
53
|
- iex> HKDF.extract(:sha256, "some input")
|
54
|
- <<130, 6, 35, 29, 160, 13, 100, 90, 127, 71, 104, 2, 139, 88, 204, 124, 201,
|
55
|
- 141, 22, 223, 95, 189, 60, 4, 147, 6, 19, 196, 66, 139, 65, 153>>
|
56
|
-
|
57
|
- iex> HKDF.extract(:sha256, "some input", "salt")
|
58
|
- <<165, 68, 136, 223, 19, 149, 73, 161, 172, 133, 175, 129, 14, 46, 132, 27, 219,
|
59
|
- 137, 155, 191, 199, 9, 251, 100, 155, 173, 33, 97, 201, 250, 19, 92>>
|
60
|
-
|
61
|
- """
|
62
|
- @spec extract(hash_fun, input_key_material, salt) :: pseudorandom_key
|
63
|
- def extract(hash_fun, ikm, salt \\ "") do
|
64
|
- :crypto.hmac(hash_fun, salt, ikm)
|
65
|
- end
|
66
|
-
|
67
|
- @doc """
|
68
|
- Expands a pseudorandom key to an output key material of a defined length.
|
69
|
-
|
70
|
- ## Example
|
71
|
-
|
72
|
- iex(1)> prk = HKDF.extract(:sha256, "some input", "salt")
|
73
|
- iex(2)> HKDF.expand(:sha256, prk, 16)
|
74
|
- <<227, 13, 8, 99, 198, 12, 203, 171, 124, 253, 132, 131, 59, 202, 95, 24>>
|
75
|
-
|
76
|
- iex(1)> prk = HKDF.extract(:sha256, "some input", "salt")
|
77
|
- iex(2)> HKDF.expand(:sha256, prk, 16, "secret message")
|
78
|
- <<28, 213, 201, 204, 16, 226, 160, 120, 69, 47, 46, 58, 15, 255, 54, 52>>
|
79
|
-
|
80
|
- """
|
81
|
- @spec expand(hash_fun, pseudorandom_key, length, info) :: output_key_material
|
82
|
- def expand(hash_fun, prk, len, info \\ "") do
|
83
|
- hash_len = hash_length(hash_fun)
|
84
|
- n = Float.ceil(len/hash_len) |> round()
|
85
|
- full =
|
86
|
- Enum.scan(1..n, "", fn index, prev ->
|
87
|
- data = prev <> info <> <<index>>
|
88
|
- :crypto.hmac(hash_fun, prk, data)
|
89
|
- end)
|
90
|
- |> Enum.reduce("", &Kernel.<>(&2, &1))
|
91
|
- <<output :: unit(8)-size(len), _ :: binary>> = full
|
92
|
- <<output :: unit(8)-size(len)>>
|
93
|
- end
|
94
|
-
|
95
|
- for fun <- ~w(md5 sha sha224 sha256 sha384 sha512)a do
|
96
|
- len = fun |> :crypto.hash("") |> byte_size()
|
97
|
- defp hash_length(unquote(fun)) do
|
98
|
- unquote(len)
|
99
|
- end
|
100
|
- end
|
101
|
- end
|
1
|
+ defmodule HKDF do
|
2
|
+ @moduledoc """
|
3
|
+ Provides a simple Hashed Message Authentication Code (HMAC)-based
|
4
|
+ key derivation function (HKDF).
|
5
|
+
|
6
|
+ ## Process
|
7
|
+
|
8
|
+ Keys are derived in two steps:
|
9
|
+
|
10
|
+ 1. Extract - a pseudorandom key is extracted from an input key material and optional salt.
|
11
|
+ 2. Expand - an output key material of a specific length is expanded from hashes of the pseudorandom key and an optional info message.
|
12
|
+
|
13
|
+ ## Source
|
14
|
+
|
15
|
+ Defined in [rfc 5859](https://tools.ietf.org/html/rfc5869)
|
16
|
+ """
|
17
|
+ @type hash_fun :: :md5 | :sha | :sha224 | :sha256 | :sha384 | :sha512
|
18
|
+ @type input_key_material :: binary
|
19
|
+ @type salt :: binary
|
20
|
+ @type pseudorandom_key :: binary
|
21
|
+ @type length :: non_neg_integer
|
22
|
+ @type info :: binary
|
23
|
+ @type output_key_material :: binary
|
24
|
+
|
25
|
+ @doc """
|
26
|
+ Derives a key of a specific length using the specified hash function.
|
27
|
+
|
28
|
+ An optional salt (extract phase) and/or info message (expand phase)
|
29
|
+ can be supplied.
|
30
|
+
|
31
|
+ ## Example
|
32
|
+
|
33
|
+ iex> HKDF.derive(:sha256, "some input", 16)
|
34
|
+ <<47, 231, 129, 75, 82, 47, 198, 78, 55, 31, 167, 66, 15, 128, 63, 243>>
|
35
|
+
|
36
|
+ iex> HKDF.derive(:sha256, "some input", 16, "salt", "secret message")
|
37
|
+ <<28, 213, 201, 204, 16, 226, 160, 120, 69, 47, 46, 58, 15, 255, 54, 52>>
|
38
|
+
|
39
|
+ """
|
40
|
+ @spec derive(hash_fun, input_key_material, length, salt, info) :: output_key_material
|
41
|
+ def derive(hash_fun, ikm, len, salt \\ "", info \\ "") do
|
42
|
+ prk = extract(hash_fun, ikm, salt)
|
43
|
+ expand(hash_fun, prk, len, info)
|
44
|
+ end
|
45
|
+
|
46
|
+ @doc """
|
47
|
+ Extract a psuedorandom key from an input key material.
|
48
|
+
|
49
|
+ ## Example
|
50
|
+
|
51
|
+ iex> HKDF.extract(:sha256, "some input")
|
52
|
+ <<130, 6, 35, 29, 160, 13, 100, 90, 127, 71, 104, 2, 139, 88, 204, 124, 201,
|
53
|
+ 141, 22, 223, 95, 189, 60, 4, 147, 6, 19, 196, 66, 139, 65, 153>>
|
54
|
+
|
55
|
+ iex> HKDF.extract(:sha256, "some input", "salt")
|
56
|
+ <<165, 68, 136, 223, 19, 149, 73, 161, 172, 133, 175, 129, 14, 46, 132, 27, 219,
|
57
|
+ 137, 155, 191, 199, 9, 251, 100, 155, 173, 33, 97, 201, 250, 19, 92>>
|
58
|
+
|
59
|
+ """
|
60
|
+ @spec extract(hash_fun, input_key_material, salt) :: pseudorandom_key
|
61
|
+ def extract(hash_fun, ikm, salt \\ "") do
|
62
|
+ :crypto.mac(:hmac, hash_fun, salt, ikm)
|
63
|
+ end
|
64
|
+
|
65
|
+ @doc """
|
66
|
+ Expands a pseudorandom key to an output key material of a defined length.
|
67
|
+
|
68
|
+ ## Example
|
69
|
+
|
70
|
+ iex(1)> prk = HKDF.extract(:sha256, "some input", "salt")
|
71
|
+ iex(2)> HKDF.expand(:sha256, prk, 16)
|
72
|
+ <<227, 13, 8, 99, 198, 12, 203, 171, 124, 253, 132, 131, 59, 202, 95, 24>>
|
73
|
+
|
74
|
+ iex(1)> prk = HKDF.extract(:sha256, "some input", "salt")
|
75
|
+ iex(2)> HKDF.expand(:sha256, prk, 16, "secret message")
|
76
|
+ <<28, 213, 201, 204, 16, 226, 160, 120, 69, 47, 46, 58, 15, 255, 54, 52>>
|
77
|
+
|
78
|
+ """
|
79
|
+ @spec expand(hash_fun, pseudorandom_key, length, info) :: output_key_material
|
80
|
+ def expand(hash_fun, prk, len, info \\ "") do
|
81
|
+ hash_len = hash_length(hash_fun)
|
82
|
+ n = Float.ceil(len/hash_len) |> round()
|
83
|
+ full =
|
84
|
+ Enum.scan(1..n, "", fn index, prev ->
|
85
|
+ data = prev <> info <> <<index>>
|
86
|
+ :crypto.mac(:hmac, hash_fun, prk, data)
|
87
|
+ end)
|
88
|
+ |> Enum.reduce("", &Kernel.<>(&2, &1))
|
89
|
+ <<output :: unit(8)-size(len), _ :: binary>> = full
|
90
|
+ <<output :: unit(8)-size(len)>>
|
91
|
+ end
|
92
|
+
|
93
|
+ for fun <- ~w(md5 sha sha224 sha256 sha384 sha512)a do
|
94
|
+ len = fun |> :crypto.hash("") |> byte_size()
|
95
|
+ defp hash_length(unquote(fun)) do
|
96
|
+ unquote(len)
|
97
|
+ end
|
98
|
+ end
|
99
|
+ end
|
changed
mix.exs
|
@@ -1,40 +1,40 @@
|
1
|
- defmodule HKDF.Mixfile do
|
2
|
- use Mix.Project
|
3
|
-
|
4
|
- def project do
|
5
|
- [app: :hkdf,
|
6
|
- version: "0.1.0",
|
7
|
- build_path: "../../_build",
|
8
|
- config_path: "../../config/config.exs",
|
9
|
- deps_path: "../../deps",
|
10
|
- lockfile: "../../mix.lock",
|
11
|
- elixir: "~> 1.4",
|
12
|
- build_embedded: Mix.env == :prod,
|
13
|
- start_permanent: Mix.env == :prod,
|
14
|
- description: description(),
|
15
|
- package: package(),
|
16
|
- deps: deps()]
|
17
|
- end
|
18
|
-
|
19
|
- def application do
|
20
|
- [extra_applications: [:logger]]
|
21
|
- end
|
22
|
-
|
23
|
- defp deps do
|
24
|
- [{:ex_doc, "~> 0.14.5", only: :dev}]
|
25
|
- end
|
26
|
-
|
27
|
- defp description do
|
28
|
- """
|
29
|
- HMAC-based key derivation function.
|
30
|
- """
|
31
|
- end
|
32
|
-
|
33
|
- defp package do
|
34
|
- [name: :hkdf,
|
35
|
- files: ["lib", "mix.exs", "README*", "LICENSE*"],
|
36
|
- maintainers: ["Sam Schneider"],
|
37
|
- licenses: ["MIT"],
|
38
|
- links: %{"GitHub" => "https://github.com/sschneider1207/hkdf"}]
|
39
|
- end
|
40
|
- end
|
1
|
+ defmodule HKDF.Mixfile do
|
2
|
+ use Mix.Project
|
3
|
+
|
4
|
+ def project do
|
5
|
+ [app: :hkdf,
|
6
|
+ version: "0.2.0",
|
7
|
+ build_path: "_build",
|
8
|
+ config_path: "config/config.exs",
|
9
|
+ deps_path: "deps",
|
10
|
+ lockfile: "mix.lock",
|
11
|
+ elixir: "~> 1.13",
|
12
|
+ build_embedded: Mix.env == :prod,
|
13
|
+ start_permanent: Mix.env == :prod,
|
14
|
+ description: description(),
|
15
|
+ package: package(),
|
16
|
+ deps: deps()]
|
17
|
+ end
|
18
|
+
|
19
|
+ def application do
|
20
|
+ [extra_applications: [:logger, :crypto]]
|
21
|
+ end
|
22
|
+
|
23
|
+ defp deps do
|
24
|
+ [{:ex_doc, ">= 0.0.0", only: :dev, runtime: false}]
|
25
|
+ end
|
26
|
+
|
27
|
+ defp description do
|
28
|
+ """
|
29
|
+ HMAC-based key derivation function.
|
30
|
+ """
|
31
|
+ end
|
32
|
+
|
33
|
+ defp package do
|
34
|
+ [name: :hkdf,
|
35
|
+ files: ["lib", "mix.exs", "README*", "LICENSE*"],
|
36
|
+ maintainers: ["Jessica Schneider"],
|
37
|
+ licenses: ["MIT"],
|
38
|
+ links: %{"GitHub" => "https://github.com/jschneider1207/hkdf"}]
|
39
|
+ end
|
40
|
+ end
|