From 1cdaf0a6fd8dbbf08fe88dd148424df4da683f48 Mon Sep 17 00:00:00 2001 From: Petr Gotthard Date: Sat, 30 Jul 2016 10:51:47 -0700 Subject: crypto:cmac calculating the Cipher-based Message Authentication Code The ERL-82 issue requests a way to calculate a CMAC in Erlang. The AES128 CMAC is standartized in RFC 4493 and used e.g. for message authentication in the LoRaWAN networks. The CMAC is implemented by OpenSSL since v1.0.1, but as @IngelaAndin stated in response to the ERL-82, the current crypto implementation does not include functions that call those OpenSSL cryptolib functions. This commit introduces a new function `crypto:cmac` that calls the corresponding OpenSSL functions and calculates the CMAC. Only the cmac_nif is implemented. The incremental functions (init, update, final) are not provided because the current OpenSSL does not allow custom memory allocators like `enif_alloc_resource`. The Erlang user guide states that at least OpenSSL 0.9.8 is required, so I added few #ifdefs so the code is compatible with all versions. However, the OpenSSL pages say that the pre-1.0.1 versions (0.9.8 and 1.0.0) are no longer maintained. Even the 1.0.1 will be retired by Dec 2016. Hence I believe that adding a 1.0.1-only function like CMAC should be OK. --- lib/crypto/c_src/crypto.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'lib/crypto/c_src') diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 7183c395ae..240bfc8341 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -108,6 +108,7 @@ #if OPENSSL_VERSION_NUMBER >= OpenSSL_version_plain(1,0,1) # define HAVE_EVP_AES_CTR # define HAVE_GCM +# define HAVE_CMAC # if OPENSSL_VERSION_NUMBER < OpenSSL_version(1,0,1,'d') # define HAVE_GCM_EVP_DECRYPT_BUG # endif @@ -121,6 +122,10 @@ # define HAVE_ECB_IVEC_BUG #endif +#if defined(HAVE_CMAC) +#include +#endif + #if defined(HAVE_EC) #include #include @@ -224,6 +229,7 @@ static ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] static ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -294,6 +300,7 @@ static ErlNifFunc nif_funcs[] = { {"hmac_update_nif", 2, hmac_update_nif}, {"hmac_final_nif", 1, hmac_final_nif}, {"hmac_final_nif", 2, hmac_final_nif}, + {"cmac_nif", 3, cmac_nif}, {"block_crypt_nif", 5, block_crypt_nif}, {"block_crypt_nif", 4, block_crypt_nif}, {"aes_ige_crypt_nif", 4, aes_ige_crypt_nif}, @@ -1346,6 +1353,53 @@ static ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM return ret; } +static ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Key, Data) */ +#if defined(HAVE_CMAC) + struct cipher_type_t *cipherp = NULL; + const EVP_CIPHER *cipher; + CMAC_CTX *ctx; + ErlNifBinary key; + ErlNifBinary data; + ERL_NIF_TERM ret; + unsigned ret_size; + + if (!enif_inspect_iolist_as_binary(env, argv[1], &key) + || !(cipherp = get_cipher_type(argv[0], key.size)) + || !enif_inspect_iolist_as_binary(env, argv[2], &data)) { + return enif_make_badarg(env); + } + cipher = cipherp->cipher.p; + if (!cipher) { + return enif_raise_exception(env, atom_notsup); + } + + ctx = CMAC_CTX_new(); + if (!CMAC_Init(ctx, key.data, key.size, cipher, NULL)) { + CMAC_CTX_free(ctx); + return atom_notsup; + } + + if (!CMAC_Update(ctx, data.data, data.size) || + !CMAC_Final(ctx, + enif_make_new_binary(env, EVP_CIPHER_block_size(cipher), &ret), + &ret_size)) { + CMAC_CTX_free(ctx); + return atom_notsup; + } + ASSERT(ret_size == (unsigned)EVP_CIPHER_block_size(cipher)); + + CMAC_CTX_free(ctx); + CONSUME_REDS(env, data); + return ret; +#else + /* The CMAC functionality was introduced in OpenSSL 1.0.1 + * Although OTP requires at least version 0.9.8, the versions 0.9.8 and 1.0.0 are + * no longer maintained. */ + return atom_notsup; +#endif +} + static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type, Key, Ivec, Text, IsEncrypt) or (Type, Key, Text, IsEncrypt) */ struct cipher_type_t *cipherp = NULL; -- cgit v1.2.3 From 9a98a20987253410e096292b07b877e1cbd62b7f Mon Sep 17 00:00:00 2001 From: Petr Gotthard Date: Sat, 30 Jul 2016 12:21:39 -0700 Subject: Fix building crypto/cmac_nif on 64-bit machines. --- lib/crypto/c_src/crypto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/crypto/c_src') diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 240bfc8341..5270c9131e 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -1362,7 +1362,7 @@ static ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] ErlNifBinary key; ErlNifBinary data; ERL_NIF_TERM ret; - unsigned ret_size; + size_t ret_size; if (!enif_inspect_iolist_as_binary(env, argv[1], &key) || !(cipherp = get_cipher_type(argv[0], key.size)) -- cgit v1.2.3