Current section

23 Versions

Jump to

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