Track liveness stateReject updates to finalized generichash states.

This commit is contained in:
Jesper Louis Andersen 2020-01-17 16:06:15 +01:00
parent f5918c0156
commit df1b134f73
2 changed files with 35 additions and 13 deletions

View File

@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
following the style of the Erlang/OTP `crypto` library. While here, make sure following the style of the Erlang/OTP `crypto` library. While here, make sure
we clean up correctly and that we don't accidentally mis-ref-count data. The we clean up correctly and that we don't accidentally mis-ref-count data. The
code is a bit more goto heavy, but this style is surprisingly common in C code. code is a bit more goto heavy, but this style is surprisingly common in C code.
- The code now rejects updates to generichash states which were already finalized.
### Fixes ### Fixes
- Fix a resource leak in generichash/sign init/update/final. - Fix a resource leak in generichash/sign init/update/final.

View File

@ -78,19 +78,23 @@ ERL_NIF_TERM enif_crypto_generichash_KEYBYTES_MAX(ErlNifEnv *env, int argc,
ERL_NIF_TERM enacl_crypto_generichash(ErlNifEnv *env, int argc, ERL_NIF_TERM enacl_crypto_generichash(ErlNifEnv *env, int argc,
ERL_NIF_TERM const argv[]) { ERL_NIF_TERM const argv[]) {
ErlNifBinary hash, message, key; ErlNifBinary hash, message, key;
unsigned hashSize; unsigned hash_size;
ERL_NIF_TERM ret; ERL_NIF_TERM ret;
// Validate the arguments // Validate the arguments
if ((argc != 3) || (!enif_get_uint(env, argv[0], &hashSize)) || if (argc != 3)
(!enif_inspect_binary(env, argv[1], &message)) || goto bad_arg;
(!enif_inspect_binary(env, argv[2], &key))) if (!enif_get_uint(env, argv[0], &hash_size))
goto bad_arg;
if (!enif_inspect_binary(env, argv[1], &message))
goto bad_arg;
if (!enif_inspect_binary(env, argv[2], &key))
goto bad_arg; goto bad_arg;
// Verify that hash size is // Verify that hash size is
// crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX // crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
if ((hashSize <= crypto_generichash_BYTES_MIN) || if ((hash_size <= crypto_generichash_BYTES_MIN) ||
(hashSize >= crypto_generichash_BYTES_MAX)) { (hash_size >= crypto_generichash_BYTES_MAX)) {
ret = nacl_error_tuple(env, "invalid_hash_size"); ret = nacl_error_tuple(env, "invalid_hash_size");
goto done; goto done;
} }
@ -106,7 +110,7 @@ ERL_NIF_TERM enacl_crypto_generichash(ErlNifEnv *env, int argc,
} }
// allocate memory for hash // allocate memory for hash
if (!enif_alloc_binary(hashSize, &hash)) { if (!enif_alloc_binary(hash_size, &hash)) {
ret = nacl_error_tuple(env, "alloc_failed"); ret = nacl_error_tuple(env, "alloc_failed");
goto done; goto done;
} }
@ -140,13 +144,14 @@ ERL_NIF_TERM enacl_crypto_generichash_init(ErlNifEnv *env, int argc,
ERL_NIF_TERM ret; ERL_NIF_TERM ret;
// Validate the arguments // Validate the arguments
if ((argc != 2) || (!enif_get_uint(env, argv[0], &hash_size)) || if (argc != 2)
(!enif_inspect_binary(env, argv[1], &key))) { goto bad_arg;
if (!enif_get_uint(env, argv[0], &hash_size))
goto bad_arg;
if (!enif_inspect_binary(env, argv[1], &key))
goto bad_arg; goto bad_arg;
}
// Verify that hash size is // Verify that hash size is valid
// crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
if ((hash_size <= crypto_generichash_BYTES_MIN) || if ((hash_size <= crypto_generichash_BYTES_MIN) ||
(hash_size >= crypto_generichash_BYTES_MAX)) { (hash_size >= crypto_generichash_BYTES_MAX)) {
ret = nacl_error_tuple(env, "invalid_hash_size"); ret = nacl_error_tuple(env, "invalid_hash_size");
@ -184,7 +189,7 @@ ERL_NIF_TERM enacl_crypto_generichash_init(ErlNifEnv *env, int argc,
// Call the library function // Call the library function
if (0 != crypto_generichash_init(obj->ctx, k, key.size, hash_size)) { if (0 != crypto_generichash_init(obj->ctx, k, key.size, hash_size)) {
ret = nacl_error_tuple(env, "hash_init_error"); ret = nacl_error_tuple(env, "hash_init_error");
goto done; goto err;
} }
// Create return values // Create return values
@ -202,6 +207,7 @@ err:
if (obj != NULL) { if (obj != NULL) {
if (obj->alive) { if (obj->alive) {
sodium_free(obj->ctx); sodium_free(obj->ctx);
obj->alive = 0; // Maintain the invariant consistently
} }
} }
done: done:
@ -230,6 +236,11 @@ ERL_NIF_TERM enacl_crypto_generichash_update(ErlNifEnv *env, int argc,
if (!enif_inspect_binary(env, argv[2], &data)) if (!enif_inspect_binary(env, argv[2], &data))
goto bad_arg; goto bad_arg;
if (!obj->alive) {
ret = nacl_error_tuple(env, "finalized");
goto done;
}
// Update hash state // Update hash state
if (0 != crypto_generichash_update(obj->ctx, data.data, data.size)) { if (0 != crypto_generichash_update(obj->ctx, data.data, data.size)) {
ret = nacl_error_tuple(env, "hash_update_error"); ret = nacl_error_tuple(env, "hash_update_error");
@ -264,6 +275,11 @@ ERL_NIF_TERM enacl_crypto_generichash_final(ErlNifEnv *env, int argc,
(void **)&obj)) (void **)&obj))
goto bad_arg; goto bad_arg;
if (!obj->alive) {
ret = nacl_error_tuple(env, "finalized");
goto done;
}
if ((hash_size <= crypto_generichash_BYTES_MIN) || if ((hash_size <= crypto_generichash_BYTES_MIN) ||
(hash_size >= crypto_generichash_BYTES_MAX)) { (hash_size >= crypto_generichash_BYTES_MAX)) {
ret = nacl_error_tuple(env, "invalid_hash_size"); ret = nacl_error_tuple(env, "invalid_hash_size");
@ -280,6 +296,11 @@ ERL_NIF_TERM enacl_crypto_generichash_final(ErlNifEnv *env, int argc,
goto release; goto release;
} }
// Finalize the object such that it cannot be reused by accident
if (obj->ctx)
sodium_free(obj->ctx);
obj->alive = 0;
ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK); ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK);
ERL_NIF_TERM h = enif_make_binary(env, &hash); ERL_NIF_TERM h = enif_make_binary(env, &hash);