xref: /trunk/ns_server/deps/enacl/c_src/generichash.c (revision e35c584a)
1 #include <sodium.h>
2 
3 #include <erl_nif.h>
4 
5 #include "enacl.h"
6 #include "generichash.h"
7 
8 typedef struct enacl_generichash_ctx {
9   ErlNifMutex *mtx;
10   crypto_generichash_state *ctx; // Underlying hash state from sodium
11   int alive;  // Is the context still valid for updates/finalizes?
12   int outlen; // Final size of the hash
13 
14 } enacl_generichash_ctx;
15 
16 static ErlNifResourceType *enacl_generic_hash_ctx_rtype;
17 
18 static void enacl_generic_hash_ctx_dtor(ErlNifEnv *env,
19                                         enacl_generichash_ctx *);
20 
enacl_init_generic_hash_ctx(ErlNifEnv * env)21 int enacl_init_generic_hash_ctx(ErlNifEnv *env) {
22   enacl_generic_hash_ctx_rtype =
23       enif_open_resource_type(env, NULL, "enacl_generichash_context",
24                               (ErlNifResourceDtor *)enacl_generic_hash_ctx_dtor,
25                               ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, NULL);
26 
27   if (enacl_generic_hash_ctx_rtype == NULL)
28     return 0;
29 
30   return 1;
31 }
32 
enacl_generic_hash_ctx_dtor(ErlNifEnv * env,enacl_generichash_ctx * obj)33 static void enacl_generic_hash_ctx_dtor(ErlNifEnv *env,
34                                         enacl_generichash_ctx *obj) {
35   if (!obj->alive) {
36     return;
37   }
38 
39   if (obj->ctx)
40     sodium_free(obj->ctx);
41 
42   if (obj->mtx != NULL)
43     enif_mutex_destroy(obj->mtx);
44 
45   return;
46 }
47 
48 /*
49  * Generic hash
50  */
enacl_crypto_generichash_BYTES(ErlNifEnv * env,int argc,ERL_NIF_TERM const argv[])51 ERL_NIF_TERM enacl_crypto_generichash_BYTES(ErlNifEnv *env, int argc,
52                                             ERL_NIF_TERM const argv[]) {
53   return enif_make_int64(env, crypto_generichash_BYTES);
54 }
55 
enacl_crypto_generichash_BYTES_MIN(ErlNifEnv * env,int argc,ERL_NIF_TERM const argv[])56 ERL_NIF_TERM enacl_crypto_generichash_BYTES_MIN(ErlNifEnv *env, int argc,
57                                                 ERL_NIF_TERM const argv[]) {
58   return enif_make_int64(env, crypto_generichash_BYTES_MIN);
59 }
60 
enacl_crypto_generichash_BYTES_MAX(ErlNifEnv * env,int argc,ERL_NIF_TERM const argv[])61 ERL_NIF_TERM enacl_crypto_generichash_BYTES_MAX(ErlNifEnv *env, int argc,
62                                                 ERL_NIF_TERM const argv[]) {
63   return enif_make_int64(env, crypto_generichash_BYTES_MAX);
64 }
65 
enacl_crypto_generichash_KEYBYTES(ErlNifEnv * env,int argc,ERL_NIF_TERM const argv[])66 ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES(ErlNifEnv *env, int argc,
67                                                ERL_NIF_TERM const argv[]) {
68   return enif_make_int64(env, crypto_generichash_KEYBYTES);
69 }
70 
enacl_crypto_generichash_KEYBYTES_MIN(ErlNifEnv * env,int argc,ERL_NIF_TERM const argv[])71 ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MIN(ErlNifEnv *env, int argc,
72                                                    ERL_NIF_TERM const argv[]) {
73   return enif_make_int64(env, crypto_generichash_KEYBYTES_MIN);
74 }
75 
enacl_crypto_generichash_KEYBYTES_MAX(ErlNifEnv * env,int argc,ERL_NIF_TERM const argv[])76 ERL_NIF_TERM enacl_crypto_generichash_KEYBYTES_MAX(ErlNifEnv *env, int argc,
77                                                    ERL_NIF_TERM const argv[]) {
78   return enif_make_int64(env, crypto_generichash_KEYBYTES_MAX);
79 }
80 
enacl_crypto_generichash(ErlNifEnv * env,int argc,ERL_NIF_TERM const argv[])81 ERL_NIF_TERM enacl_crypto_generichash(ErlNifEnv *env, int argc,
82                                       ERL_NIF_TERM const argv[]) {
83   ErlNifBinary hash, message, key;
84   unsigned hash_size;
85   ERL_NIF_TERM ret;
86 
87   // Validate the arguments
88   if (argc != 3)
89     goto bad_arg;
90   if (!enif_get_uint(env, argv[0], &hash_size))
91     goto bad_arg;
92   if (!enif_inspect_binary(env, argv[1], &message))
93     goto bad_arg;
94   if (!enif_inspect_binary(env, argv[2], &key))
95     goto bad_arg;
96 
97   // Verify that hash size is
98   // crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
99   if ((hash_size < crypto_generichash_BYTES_MIN) ||
100       (hash_size > crypto_generichash_BYTES_MAX)) {
101     goto bad_arg;
102   }
103 
104   // validate key size
105   unsigned char *k = key.data;
106   if (0 == key.size) {
107     k = NULL;
108   } else if (key.size < crypto_generichash_KEYBYTES_MIN ||
109              key.size > crypto_generichash_KEYBYTES_MAX) {
110     goto bad_arg;
111   }
112 
113   // allocate memory for hash
114   if (!enif_alloc_binary(hash_size, &hash)) {
115     goto err;
116   }
117 
118   // calculate hash
119   if (0 != crypto_generichash(hash.data, hash.size, message.data, message.size,
120                               k, key.size)) {
121     goto release;
122   }
123 
124   ret = enif_make_binary(env, &hash);
125   goto done;
126 
127 bad_arg:
128   return enif_make_badarg(env);
129 release:
130   enif_release_binary(&hash);
131 err:
132   ret = enacl_internal_error(env);
133 done:
134   return ret;
135 }
136 
enacl_crypto_generichash_init(ErlNifEnv * env,int argc,ERL_NIF_TERM const argv[])137 ERL_NIF_TERM enacl_crypto_generichash_init(ErlNifEnv *env, int argc,
138                                            ERL_NIF_TERM const argv[]) {
139   ErlNifBinary key;
140   unsigned hash_size;
141   enacl_generichash_ctx *obj = NULL;
142   ERL_NIF_TERM ret;
143 
144   // Validate the arguments
145   if (argc != 2)
146     goto bad_arg;
147   if (!enif_get_uint(env, argv[0], &hash_size))
148     goto bad_arg;
149   if (!enif_inspect_binary(env, argv[1], &key))
150     goto bad_arg;
151 
152   // Verify that hash size is valid
153   if ((hash_size < crypto_generichash_BYTES_MIN) ||
154       (hash_size > crypto_generichash_BYTES_MAX)) {
155     goto bad_arg;
156   }
157 
158   // validate key size
159   unsigned char *k = key.data;
160   if (0 == key.size) {
161     k = NULL;
162   } else if (key.size < crypto_generichash_KEYBYTES_MIN ||
163              key.size > crypto_generichash_KEYBYTES_MAX) {
164     goto bad_arg;
165   }
166 
167   // Create the resource
168   if ((obj = enif_alloc_resource(enacl_generic_hash_ctx_rtype,
169                                  sizeof(enacl_generichash_ctx))) == NULL) {
170     goto err;
171   }
172 
173   // Allocate the state context via libsodium
174   // Note that this ensures a 64byte alignment for the resource
175   // And also protects the resource via guardpages
176   obj->mtx = NULL;
177   obj->ctx = NULL;
178   obj->alive = 0;
179   obj->outlen = 0;
180 
181   obj->ctx = (crypto_generichash_state *)sodium_malloc(
182       crypto_generichash_statebytes());
183   if (obj->ctx == NULL) {
184     goto err;
185   }
186   obj->alive = 1;
187   obj->outlen = hash_size;
188 
189   if ((obj->mtx = enif_mutex_create("enacl.generichash")) == NULL) {
190     ret = enacl_error_tuple(env, "mutex_create");
191     goto err;
192   }
193 
194   // Call the library function
195   if (0 != crypto_generichash_init(obj->ctx, k, key.size, obj->outlen)) {
196     ret = enacl_error_tuple(env, "hash_init_error");
197     goto err;
198   }
199 
200   ret = enif_make_resource(env, obj);
201   goto done;
202 
203 bad_arg:
204   return enif_make_badarg(env);
205 err:
206   ret = enacl_internal_error(env);
207   if (obj != NULL) {
208     if (obj->alive) {
209       sodium_free(obj->ctx);
210       obj->alive = 0; // Maintain the invariant consistently
211     }
212   }
213 done:
214   if (obj != NULL) {
215     enif_release_resource(obj);
216   }
217   return ret;
218 }
219 
enacl_crypto_generichash_update(ErlNifEnv * env,int argc,ERL_NIF_TERM const argv[])220 ERL_NIF_TERM enacl_crypto_generichash_update(ErlNifEnv *env, int argc,
221                                              ERL_NIF_TERM const argv[]) {
222   ERL_NIF_TERM ret;
223   ErlNifBinary data;
224   unsigned int data_size;
225   enacl_generichash_ctx *obj = NULL;
226 
227   // Validate the arguments
228   if (argc != 2)
229     goto bad_arg;
230   if (!enif_get_resource(env, argv[0],
231                          (ErlNifResourceType *)enacl_generic_hash_ctx_rtype,
232                          (void **)&obj))
233     goto bad_arg;
234   if (!enif_inspect_binary(env, argv[1], &data))
235     goto bad_arg;
236 
237   enif_mutex_lock(obj->mtx);
238   if (!obj->alive) {
239     goto err;
240   }
241 
242   // Update hash state
243   if (0 != crypto_generichash_update(obj->ctx, data.data, data.size)) {
244     goto err;
245   }
246 
247   ret = argv[0];
248   goto done;
249 
250 bad_arg:
251   return enif_make_badarg(env);
252 err:
253   ret = enacl_error_finalized(env);
254 done:
255   enif_mutex_unlock(obj->mtx);
256   return ret;
257 }
258 
enacl_crypto_generichash_final(ErlNifEnv * env,int argc,ERL_NIF_TERM const argv[])259 ERL_NIF_TERM enacl_crypto_generichash_final(ErlNifEnv *env, int argc,
260                                             ERL_NIF_TERM const argv[]) {
261   ERL_NIF_TERM ret;
262   ErlNifBinary hash;
263   enacl_generichash_ctx *obj = NULL;
264 
265   if (argc != 1)
266     goto bad_arg;
267   if (!enif_get_resource(env, argv[0], enacl_generic_hash_ctx_rtype,
268                          (void **)&obj))
269     goto bad_arg;
270 
271   enif_mutex_lock(obj->mtx);
272   if (!obj->alive) {
273     ret = enacl_error_finalized(env);
274     goto done;
275   }
276 
277   if (!enif_alloc_binary(obj->outlen, &hash)) {
278     goto err;
279   }
280 
281   if (0 != crypto_generichash_final(obj->ctx, hash.data, hash.size)) {
282     goto release;
283   }
284 
285   // Finalize the object such that it cannot be reused by accident
286   if (obj->ctx)
287     sodium_free(obj->ctx);
288   obj->alive = 0;
289 
290   ret = enif_make_binary(env, &hash);
291   goto done;
292 
293 bad_arg:
294   return enif_make_badarg(env);
295 release:
296   enif_release_binary(&hash);
297 err:
298   ret = enacl_internal_error(env);
299 done:
300   enif_mutex_unlock(obj->mtx);
301   return ret;
302 }
303