5
5
6
6
defmodule Scenic.Cache.Hash do
7
7
@ moduledoc """
8
- Ssimple functions to load a file, following the hashing rules
8
+ Helper functions to work with hash signatures.
9
+
10
+ Both the [`Cache.File`](Scenic.Cache.File.html) and [`Cache.Term`](Scenic.Cache.Term.html)
11
+ modules use cryptographic hash signatures to verify that files are valid before using
12
+ the data they contain.
13
+
14
+ This modules provides a collection of helper functions that make it easy to use, generate
15
+ and validate these hashes.
16
+
17
+ Any time one of these functions asks for a type of hash, the supported types are:
18
+ `:sha`, `:sha224`, `:sha256`, `:sha384`, `:sha512`, and `:ripemd160`
9
19
"""
10
- # import IEx
11
20
12
21
@ hash_types [ :sha , :sha224 , :sha256 , :sha384 , :sha512 , :ripemd160 ]
13
22
@ default_hash :sha
14
23
24
+ @ type hash_type ::
25
+ :sha
26
+ | :sha224
27
+ | :sha256
28
+ | :sha384
29
+ | :sha512
30
+ | :ripemd160
31
+
32
+ @ type type_error :: { :error , :invalid_hash_type }
33
+ @ type hash_error :: { :error , :hash_failure }
34
+
15
35
# ===========================================================================
16
36
defmodule Error do
17
37
@ moduledoc false
@@ -20,40 +40,56 @@ defmodule Scenic.Cache.Hash do
20
40
end
21
41
22
42
# --------------------------------------------------------
43
+ @ doc false
44
+ @ deprecated "Cache.valid_hash_types/0 will be removed in 0.10.0"
23
45
@ spec valid_hash_types ( ) :: [ :ripemd160 | :sha | :sha224 | :sha256 | :sha384 | :sha512 , ... ]
24
46
def valid_hash_types ( ) , do: @ hash_types
47
+
25
48
# --------------------------------------------------------
26
- @ spec valid_hash_type? ( any ( ) ) :: boolean ( )
27
- def valid_hash_type? ( hash_type ) , do: Enum . member? ( @ hash_types , hash_type )
49
+ @ spec valid_hash_type? ( type :: hash_type ) :: boolean ( )
50
+ defp valid_hash_type? ( type ) , do: Enum . member? ( @ hash_types , type )
51
+
28
52
# --------------------------------------------------------
29
- @ spec valid_hash_type! ( any ( ) ) :: any ( ) | no_return
30
- def valid_hash_type! ( hash_type ) do
31
- case Enum . member? ( @ hash_types , hash_type ) do
53
+ @ spec valid_hash_type! ( type :: hash_type ) :: hash_type | no_return
54
+ defp valid_hash_type! ( type ) do
55
+ case Enum . member? ( @ hash_types , type ) do
32
56
true ->
33
- hash_type
57
+ type
34
58
35
59
false ->
36
- msg = "Invalid hash type: #{ hash_type } \r \n " <> "Must be one of: #{ inspect ( @ hash_types ) } "
60
+ msg = "Invalid hash type: #{ type } \r \n " <> "Must be one of: #{ inspect ( @ hash_types ) } "
37
61
raise Error , message: msg
38
62
end
39
63
end
40
64
41
65
# --------------------------------------------------------
42
- @ spec binary ( any ( ) , any ( ) ) :: { :error , :invalid_hash_type } | { :ok , binary ( ) }
43
- def binary ( data , hash_type ) do
44
- case valid_hash_type? ( hash_type ) do
45
- true -> { :ok , hash_type |> :crypto . hash ( data ) |> Base . url_encode64 ( padding: false ) }
66
+ @ doc """
67
+ Calculate the hash of binary data
68
+
69
+ Returns the hash wrapped in a `{:ok, hash}` tuple.
70
+ """
71
+ @ spec binary ( data :: binary , type :: hash_type ) :: { :ok , bitstring ( ) } | type_error
72
+ def binary ( data , type ) do
73
+ case valid_hash_type? ( type ) do
74
+ true -> { :ok , type |> :crypto . hash ( data ) |> Base . url_encode64 ( padding: false ) }
46
75
false -> { :error , :invalid_hash_type }
47
76
end
48
77
end
49
78
50
- def binary! ( data , hash_type ) do
51
- valid_hash_type! ( hash_type )
79
+ @ doc """
80
+ Calculate the hash of binary data
81
+
82
+ Returns the hash directly.
83
+ """
84
+ @ spec binary! ( data :: binary , type :: hash_type ) :: bitstring ( )
85
+ def binary! ( data , type ) do
86
+ valid_hash_type! ( type )
52
87
|> :crypto . hash ( data )
53
88
|> Base . url_encode64 ( padding: false )
54
89
end
55
90
56
91
# --------------------------------------------------------
92
+ @ spec file ( path :: bitstring , type :: hash_type ) :: { :ok , bitstring ( ) } | type_error
57
93
def file ( path , hash_type ) do
58
94
do_compute_file (
59
95
path ,
@@ -62,6 +98,7 @@ defmodule Scenic.Cache.Hash do
62
98
)
63
99
end
64
100
101
+ @ spec file! ( path :: bitstring , type :: hash_type ) :: bitstring ( )
65
102
def file! ( path , hash_type ) do
66
103
# start the hash context
67
104
hash_context =
@@ -103,6 +140,13 @@ defmodule Scenic.Cache.Hash do
103
140
end
104
141
105
142
# --------------------------------------------------------
143
+ @ doc """
144
+ Verify that the given data conforms to the given hash.
145
+
146
+ If the verification passes, returns `{:ok, data}`
147
+ If it fails, returns `{:error, :hash_failure}`
148
+ """
149
+ @ spec verify ( data :: binary , hash :: bitstring , type :: hash_type ) :: { :ok , binary } | hash_error
106
150
def verify ( data , hash , hash_type ) do
107
151
case binary ( data , hash_type ) do
108
152
{ :ok , ^ hash } -> { :ok , data }
@@ -111,6 +155,13 @@ defmodule Scenic.Cache.Hash do
111
155
end
112
156
113
157
# --------------------------------------------------------
158
+ @ doc """
159
+ Verify that the given data conforms to the given hash.
160
+
161
+ If the verification passes, returns the data unchanged.
162
+ If it fails, raises an error
163
+ """
164
+ @ spec verify! ( data :: binary , hash :: bitstring , type :: hash_type ) :: binary | no_return
114
165
def verify! ( data , hash , hash_type ) do
115
166
case binary! ( data , hash_type ) == hash do
116
167
true -> data
@@ -119,9 +170,13 @@ defmodule Scenic.Cache.Hash do
119
170
end
120
171
121
172
# --------------------------------------------------------
122
- def verify_file ( path_data ) , do: path_params ( path_data ) |> do_verify_file ( )
173
+ @ doc """
174
+ Verify that the data in a file conforms to the given hash.
123
175
124
- defp do_verify_file ( { path , hash , hash_type } ) do
176
+ If the verification passes, returns `{:ok, hash}`
177
+ If it fails, returns `{:error, :hash_failure}`
178
+ """
179
+ def verify_file ( path , hash , hash_type ) do
125
180
case file ( path , hash_type ) do
126
181
{ :ok , computed_hash } ->
127
182
case computed_hash == hash do
@@ -135,42 +190,46 @@ defmodule Scenic.Cache.Hash do
135
190
end
136
191
137
192
# --------------------------------------------------------
138
- def verify_file! ( path_data ) , do: path_params ( path_data ) |> do_verify_file! ( )
193
+ @ doc """
194
+ Verify that the data in a file conforms to the given hash.
139
195
140
- defp do_verify_file! ( { path , hash , hash_type } ) do
196
+ If the verification passes, returns the hash unchanged.
197
+ If it fails, raises an error
198
+ """
199
+ def verify_file! ( path , hash , hash_type ) do
141
200
case file! ( path , hash_type ) == hash do
142
201
true -> hash
143
202
false -> raise Error
144
203
end
145
204
end
146
205
147
- # --------------------------------------------------------
148
- def from_path ( path ) do
149
- path
150
- |> String . split ( "." )
151
- |> List . last ( )
152
- end
206
+ # # --------------------------------------------------------
207
+ # defp from_path(path) do
208
+ # path
209
+ # |> String.split(".")
210
+ # |> List.last()
211
+ # end
153
212
154
- # --------------------------------------------------------
155
- def path_params ( path )
213
+ # # --------------------------------------------------------
214
+ # defp path_params(path)
156
215
157
- def path_params ( path ) when is_bitstring ( path ) do
158
- hash = from_path ( path )
159
- path_params ( { path , hash , @ default_hash } )
160
- end
216
+ # defp path_params(path) when is_bitstring(path) do
217
+ # hash = from_path(path)
218
+ # path_params({path, hash, @default_hash})
219
+ # end
161
220
162
- def path_params ( { path , hash_type } ) when is_atom ( hash_type ) do
163
- hash = from_path ( path )
164
- path_params ( { path , hash , hash_type } )
165
- end
221
+ # defp path_params({path, hash_type}) when is_atom(hash_type) do
222
+ # hash = from_path(path)
223
+ # path_params({path, hash, hash_type})
224
+ # end
166
225
167
- def path_params ( { path_or_data , hash } ) , do: path_params ( { path_or_data , hash , @ default_hash } )
226
+ # defp path_params({path_or_data, hash}), do: path_params({path_or_data, hash, @default_hash})
168
227
169
- def path_params ( { path_or_data , hash , hash_type } )
170
- when is_binary ( path_or_data ) and is_bitstring ( hash ) and is_atom ( hash_type ) do
171
- { path_or_data , hash , valid_hash_type! ( hash_type ) }
172
- end
228
+ # defp path_params({path_or_data, hash, hash_type})
229
+ # when is_binary(path_or_data) and is_bitstring(hash) and is_atom(hash_type) do
230
+ # {path_or_data, hash, valid_hash_type!(hash_type)}
231
+ # end
173
232
174
- def path_params ( path_or_data , hash_or_type ) , do: path_params ( { path_or_data , hash_or_type } )
175
- def path_params ( path_or_data , hash , hash_type ) , do: path_params ( { path_or_data , hash , hash_type } )
233
+ # defp path_params(path_or_data, hash_or_type), do: path_params({path_or_data, hash_or_type})
234
+ # defp path_params(path_or_data, hash, hash_type), do: path_params({path_or_data, hash, hash_type})
176
235
end
0 commit comments