Current section
23 Versions
Jump to
Current section
23 Versions
Compare versions
4
files changed
+89
additions
-58
deletions
| @@ -12,4 +12,4 @@ | |
| 12 12 | {<<"maintainers">>,[<<"Matt Miller">>]}. |
| 13 13 | {<<"name">>,<<"ed25519">>}. |
| 14 14 | {<<"requirements">>,[]}. |
| 15 | - {<<"version">>,<<"1.1.0">>}. |
| 15 | + {<<"version">>,<<"1.2.0">>}. |
| @@ -1,5 +1,6 @@ | |
| 1 1 | defmodule Ed25519 do |
| 2 2 | use Bitwise |
| 3 | + |
| 3 4 | @moduledoc """ |
| 4 5 | Ed25519 signature functions |
| 5 6 | |
| @@ -44,10 +45,13 @@ defmodule Ed25519 do | |
| 44 45 | defp xrecover(y) do |
| 45 46 | xx = (y * y - 1) * inv(@d * y * y + 1) |
| 46 47 | x = expmod(xx, div(@p + 3, 8), @p) |
| 47 | - x = case (x * x - xx) |> mod(@p) do |
| 48 | - 0 -> x |
| 49 | - _ -> mod(x * @i, @p) |
| 50 | - end |
| 48 | + |
| 49 | + x = |
| 50 | + case (x * x - xx) |> mod(@p) do |
| 51 | + 0 -> x |
| 52 | + _ -> mod(x * @i, @p) |
| 53 | + end |
| 54 | + |
| 51 55 | case x |> mod(2) do |
| 52 56 | 0 -> @p - x |
| 53 57 | _ -> x |
| @@ -66,50 +70,59 @@ defmodule Ed25519 do | |
| 66 70 | # :crypto.mod_pow chokes on negative inputs, so we feed it positive values |
| 67 71 | # only and patch up the result if necessary |
| 68 72 | defp expmod(_b, 0, _m), do: 1 |
| 73 | + |
| 69 74 | defp expmod(b, e, m) when b > 0 do |
| 70 | - b |> :crypto.mod_pow(e, m) |> :binary.decode_unsigned |
| 75 | + b |> :crypto.mod_pow(e, m) |> :binary.decode_unsigned() |
| 71 76 | end |
| 77 | + |
| 72 78 | defp expmod(b, e, m) do |
| 73 | - i = b |> abs() |> :crypto.mod_pow(e, m) |> :binary.decode_unsigned |
| 79 | + i = b |> abs() |> :crypto.mod_pow(e, m) |> :binary.decode_unsigned() |
| 80 | + |
| 74 81 | cond do |
| 75 | - mod(e, 2) == 0 -> i |
| 76 | - i == 0 -> i |
| 77 | - true -> m - i |
| 82 | + mod(e, 2) == 0 -> i |
| 83 | + i == 0 -> i |
| 84 | + true -> m - i |
| 78 85 | end |
| 79 86 | end |
| 80 87 | |
| 81 88 | defp inv(x), do: x |> expmod(@p - 2, @p) |
| 82 89 | |
| 83 90 | defp edwards({x1, y1}, {x2, y2}) do |
| 84 | - x = (x1 * y2 + x2 * y1) * inv(1 + @d * x1 * x2 * y1 * y2) |
| 85 | - y = (y1 * y2 + x1 * x2) * inv(1 - @d * x1 * x2 * y1 * y2) |
| 86 | - {mod(x, @p), mod(y, @p)} |
| 91 | + x = (x1 * y2 + x2 * y1) * inv(1 + @d * x1 * x2 * y1 * y2) |
| 92 | + y = (y1 * y2 + x1 * x2) * inv(1 - @d * x1 * x2 * y1 * y2) |
| 93 | + {mod(x, @p), mod(y, @p)} |
| 87 94 | end |
| 88 95 | |
| 89 96 | defp encodeint(x), do: x |> :binary.encode_unsigned(:little) |
| 97 | + |
| 90 98 | defp encodepoint({x, y}) do |
| 91 | - y |> band(0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) |
| 92 | - |> bor((x &&& 1) <<< 255) |
| 93 | - |> encodeint |
| 99 | + y |
| 100 | + |> band(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) |
| 101 | + |> bor((x &&& 1) <<< 255) |
| 102 | + |> encodeint |
| 94 103 | end |
| 95 104 | |
| 96 105 | defp decodeint(x), do: x |> :binary.decode_unsigned(:little) |
| 106 | + |
| 97 107 | defp decodepoint(n) do |
| 98 108 | decoded = n |> :binary.decode_unsigned(:little) |
| 99 109 | xc = decoded |> bsr(255) |
| 100 | - y = decoded |> band(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) |
| 101 | - x = xrecover(y) |
| 102 | - point = case (x &&& 1) do |
| 103 | - ^xc -> {x, y} |
| 104 | - _ -> {@p - x, y} |
| 105 | - end |
| 110 | + y = decoded |> band(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) |
| 111 | + x = xrecover(y) |
| 112 | + |
| 113 | + point = |
| 114 | + case x &&& 1 do |
| 115 | + ^xc -> {x, y} |
| 116 | + _ -> {@p - x, y} |
| 117 | + end |
| 118 | + |
| 106 119 | if isoncurve(point), do: point, else: raise("Point off curve") |
| 107 120 | end |
| 108 121 | |
| 109 122 | defp isoncurve({x, y}), do: (-x * x + y * y - 1 - @d * x * x * y * y) |> mod(@p) == 0 |
| 110 123 | |
| 111 124 | defp rightsize(n, s) when byte_size(n) == s, do: n |
| 112 | - defp rightsize(n, s) when byte_size(n) < s, do: rightsize(n <> <<0>>, s) |
| 125 | + defp rightsize(n, s) when byte_size(n) < s, do: rightsize(n <> <<0>>, s) |
| 113 126 | |
| 114 127 | @doc """ |
| 115 128 | Sign a message |
| @@ -120,34 +133,39 @@ defmodule Ed25519 do | |
| 120 133 | @spec signature(binary, key, key) :: signature |
| 121 134 | def signature(m, sk, pk \\ nil) |
| 122 135 | def signature(m, sk, nil), do: signature(m, sk, derive_public_key(sk)) |
| 136 | + |
| 123 137 | def signature(m, sk, pk) do |
| 124 138 | h = hash(sk) |
| 125 139 | a = a_from_hash(h) |
| 126 140 | r = hashint(:binary.part(h, 32, 32) <> m) |
| 127 141 | bigr = r |> scalarmult(@base) |> encodepoint |
| 128 142 | s = mod(r + hashint(bigr <> pk <> m) * a, @l) |
| 129 | - bigr <> encodeint(s) |> rightsize(64) |
| 143 | + (bigr <> encodeint(s)) |> rightsize(64) |
| 130 144 | end |
| 131 145 | |
| 132 146 | defp a_from_hash(h) do |
| 133 | - @t254 + (h |> :binary.part(0, 32) |> :binary.decode_unsigned(:little) |> band(0xf3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8)) |
| 147 | + @t254 + |
| 148 | + (h |> :binary.part(0, 32) |> :binary.decode_unsigned(:little) |
| 149 | + |> band(0xF3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8)) |
| 134 150 | end |
| 135 151 | |
| 136 152 | defp scalarmult(0, _pair), do: {0, 1} |
| 153 | + |
| 137 154 | defp scalarmult(e, p) do |
| 138 | - q = e |> div(2) |> scalarmult(p) |
| 139 | - q = edwards(q, q) |
| 140 | - case (e &&& 1) do |
| 141 | - 1 -> edwards(q, p) |
| 142 | - _ -> q |
| 143 | - end |
| 155 | + q = e |> div(2) |> scalarmult(p) |
| 156 | + q = edwards(q, q) |
| 157 | + |
| 158 | + case e &&& 1 do |
| 159 | + 1 -> edwards(q, p) |
| 160 | + _ -> q |
| 161 | + end |
| 144 162 | end |
| 145 163 | |
| 146 164 | @doc """ |
| 147 165 | validate a signed message |
| 148 166 | """ |
| 149 167 | @spec valid_signature?(signature, binary, key) :: boolean |
| 150 | - def valid_signature?(s, m, pk) when byte_size(s) == 64 and byte_size(pk) == 32 do |
| 168 | + def valid_signature?(s, m, pk) when byte_size(s) == 64 and byte_size(pk) == 32 do |
| 151 169 | <<for_r::binary-size(32), for_s::binary-size(32)>> = s |
| 152 170 | r = decodepoint(for_r) |
| 153 171 | a = decodepoint(pk) |
| @@ -155,6 +173,7 @@ defmodule Ed25519 do | |
| 155 173 | h = hashint(encodepoint(r) <> pk <> m) |
| 156 174 | scalarmult(s, @base) == edwards(r, scalarmult(h, a)) |
| 157 175 | end |
| 176 | + |
| 158 177 | def valid_signature?(_s, _m_, _pk), do: false |
| 159 178 | |
| 160 179 | @doc """ |
| @@ -168,15 +187,25 @@ defmodule Ed25519 do | |
| 168 187 | {secret, derive_public_key(secret)} |
| 169 188 | end |
| 170 189 | |
| 190 | + @doc """ |
| 191 | + Generate a secret/public key pair from supplied secret |
| 192 | + |
| 193 | + Returned tuple contains `{secret_key, derived_public_key}` |
| 194 | + """ |
| 195 | + @spec generate_key_pair(key) :: {key, key} |
| 196 | + def generate_key_pair(secret) do |
| 197 | + {secret, derive_public_key(secret)} |
| 198 | + end |
| 199 | + |
| 171 200 | @doc """ |
| 172 201 | derive the public signing key from the secret key |
| 173 202 | """ |
| 174 203 | @spec derive_public_key(key) :: key |
| 175 204 | def derive_public_key(sk) do |
| 176 | - sk |> hash |
| 177 | - |> a_from_hash |
| 178 | - |> scalarmult(@base) |
| 179 | - |> encodepoint |
| 205 | + sk |
| 206 | + |> hash |
| 207 | + |> a_from_hash |
| 208 | + |> scalarmult(@base) |
| 209 | + |> encodepoint |
| 180 210 | end |
| 181 | - |
| 182 211 | end |
| @@ -1,11 +1,11 @@ | |
| 1 1 | defmodule Ed25519.Hash do |
| 2 2 | @moduledoc false |
| 3 3 | defmacro __using__(_) do |
| 4 | - {mod, fun, pre_args, post_args} = Application.get_env(:ed25519, :hash_fn, |
| 5 | - {:crypto, :hash, [:sha512], []}) |
| 4 | + {mod, fun, pre_args, post_args} = |
| 5 | + Application.get_env(:ed25519, :hash_fn, {:crypto, :hash, [:sha512], []}) |
| 6 6 | |
| 7 7 | quote do |
| 8 | - defp hash (unquote(Macro.var(:m, __MODULE__))) do |
| 8 | + defp hash(unquote(Macro.var(:m, __MODULE__))) do |
| 9 9 | unquote(mod).unquote(fun)( |
| 10 10 | unquote_splicing(pre_args), |
| 11 11 | unquote(Macro.var(:m, __MODULE__)), |
| @@ -2,16 +2,18 @@ defmodule Ed25519.Mixfile do | |
| 2 2 | use Mix.Project |
| 3 3 | |
| 4 4 | def project do |
| 5 | - [app: :ed25519, |
| 6 | - version: "1.1.0", |
| 7 | - elixir: "~> 1.5", |
| 8 | - name: "Ed25519", |
| 9 | - source_url: "https://github.com/mwmiller/ed25519_ex", |
| 10 | - build_embedded: Mix.env == :prod, |
| 11 | - start_permanent: Mix.env == :prod, |
| 12 | - description: description(), |
| 13 | - package: package(), |
| 14 | - deps: deps()] |
| 5 | + [ |
| 6 | + app: :ed25519, |
| 7 | + version: "1.2.0", |
| 8 | + elixir: "~> 1.5", |
| 9 | + name: "Ed25519", |
| 10 | + source_url: "https://github.com/mwmiller/ed25519_ex", |
| 11 | + build_embedded: Mix.env() == :prod, |
| 12 | + start_permanent: Mix.env() == :prod, |
| 13 | + description: description(), |
| 14 | + package: package(), |
| 15 | + deps: deps() |
| 16 | + ] |
| 15 17 | end |
| 16 18 | |
| 17 19 | def application do |
| @@ -22,7 +24,7 @@ defmodule Ed25519.Mixfile do | |
| 22 24 | [ |
| 23 25 | {:earmark, "~> 1.0", only: :dev}, |
| 24 26 | {:ex_doc, "~> 0.18", only: :dev}, |
| 25 | - {:credo, "~> 0.8", only: [:dev, :test]}, |
| 27 | + {:credo, "~> 0.8", only: [:dev, :test]} |
| 26 28 | ] |
| 27 29 | end |
| 28 30 | |
| @@ -34,13 +36,13 @@ defmodule Ed25519.Mixfile do | |
| 34 36 | |
| 35 37 | defp package do |
| 36 38 | [ |
| 37 | - files: ["lib", "mix.exs", "README*", "LICENSE*", ], |
| 38 | - maintainers: ["Matt Miller"], |
| 39 | - licenses: ["MIT"], |
| 40 | - links: %{"GitHub" => "https://github.com/mwmiller/ed25519_ex", |
| 41 | - "Info" => "http://ed25519.cr.yp.to", |
| 42 | - } |
| 39 | + files: ["lib", "mix.exs", "README*", "LICENSE*"], |
| 40 | + maintainers: ["Matt Miller"], |
| 41 | + licenses: ["MIT"], |
| 42 | + links: %{ |
| 43 | + "GitHub" => "https://github.com/mwmiller/ed25519_ex", |
| 44 | + "Info" => "http://ed25519.cr.yp.to" |
| 45 | + } |
| 43 46 | ] |
| 44 47 | end |
| 45 | - |
| 46 48 | end |