aboutsummaryrefslogtreecommitdiffstats
path: root/lib/crypto/c_src/aead.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/crypto/c_src/aead.c')
-rw-r--r--lib/crypto/c_src/aead.c248
1 files changed, 99 insertions, 149 deletions
diff --git a/lib/crypto/c_src/aead.c b/lib/crypto/c_src/aead.c
index 4ed16615a5..ab0e609130 100644
--- a/lib/crypto/c_src/aead.c
+++ b/lib/crypto/c_src/aead.c
@@ -22,36 +22,65 @@
#include "aes.h"
#include "cipher.h"
-ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Type,Key,Iv,AAD,In) */
+
+
+ERL_NIF_TERM aead_cipher(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/*
+ (Type,Key,Iv,AAD,In,TagLen,true)
+ (Type,Key,Iv,AAD,In,Tag,false)
+ */
#if defined(HAVE_AEAD)
const struct cipher_type_t *cipherp;
EVP_CIPHER_CTX *ctx = NULL;
const EVP_CIPHER *cipher = NULL;
- ErlNifBinary key, iv, aad, in;
+ ErlNifBinary key, iv, aad, in, tag;
unsigned int tag_len;
- unsigned char *outp, *tagp;
- ERL_NIF_TERM type, out, out_tag, ret;
- int len, ctx_ctrl_set_ivlen, ctx_ctrl_get_tag, ctx_ctrl_set_tag;
+ unsigned char *outp, *tagp, *tag_data;
+ ERL_NIF_TERM type, out, out_tag, ret, encflg_arg;
+ int len, encflg;
+
+ encflg_arg = argv[6];
+
+ /* Fetch the flag telling if we are going to encrypt (=true) or decrypt (=false) */
+ if (encflg_arg == atom_true)
+ encflg = 1;
+ else if (encflg_arg == atom_false)
+ encflg = 0;
+ else if (encflg_arg == atom_undefined)
+ /* For compat funcs in crypto.erl */
+ encflg = -1;
+ else
+ {
+ ret = EXCP_BADARG(env, "Bad enc flag");
+ goto done;
+ }
type = argv[0];
- ASSERT(argc == 6);
-
if (!enif_is_atom(env, type))
{ret = EXCP_BADARG(env, "non-atom cipher type"); goto done;}
if (!enif_inspect_iolist_as_binary(env, argv[1], &key))
{ret = EXCP_BADARG(env, "non-binary key"); goto done;}
- if (!enif_inspect_binary(env, argv[2], &iv))
+ if (!enif_inspect_iolist_as_binary(env, argv[2], &iv))
{ret = EXCP_BADARG(env, "non-binary iv"); goto done;}
- if (!enif_inspect_iolist_as_binary(env, argv[3], &aad))
- {ret = EXCP_BADARG(env, "non-binary AAD"); goto done;}
- if (!enif_inspect_iolist_as_binary(env, argv[4], &in))
+ if (!enif_inspect_iolist_as_binary(env, argv[3], &in))
{ret = EXCP_BADARG(env, "non-binary text"); goto done;}
- if (!enif_get_uint(env, argv[5], &tag_len))
- {ret = EXCP_BADARG(env, ""); goto done;}
+ if (!enif_inspect_iolist_as_binary(env, argv[4], &aad))
+ {ret = EXCP_BADARG(env, "non-binary AAD"); goto done;}
+
+ if (encflg) {
+ if (!enif_get_uint(env, argv[5], &tag_len))
+ {ret = EXCP_BADARG(env, "Bad Tag length"); goto done;}
+ tag_data = NULL;
+ } else {
+ if (!enif_inspect_iolist_as_binary(env, argv[5], &tag))
+ {ret = EXCP_BADARG(env, "non-binary Tag"); goto done;}
+ tag_len = tag.size;
+ tag_data = tag.data;
+ }
if (tag_len > INT_MAX
+ || key.size > INT_MAX
|| iv.size > INT_MAX
|| in.size > INT_MAX
|| aad.size > INT_MAX)
@@ -66,167 +95,88 @@ ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
if ((cipher = cipherp->cipher.p) == NULL)
{ret = EXCP_NOTSUP(env, "Cipher not supported in this libcrypto version"); goto done;}
- ctx_ctrl_set_ivlen = cipherp->extra.aead.ctx_ctrl_set_ivlen;
- ctx_ctrl_get_tag = cipherp->extra.aead.ctx_ctrl_get_tag;
- ctx_ctrl_set_tag = cipherp->extra.aead.ctx_ctrl_set_tag;
-
+#if defined(HAVE_GCM_EVP_DECRYPT_BUG)
+ if ( !encflg && (cipherp->flags & GCM_MODE))
+ return aes_gcm_decrypt_NO_EVP(env, argc, argv);
+#endif
+
if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
- {ret = EXCP_ERROR(env, ""); goto done;}
+ {ret = EXCP_ERROR(env, "Can't allocate ctx"); goto done;}
- if (EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
- if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, (int)iv.size, NULL) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
+ if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encflg) != 1)
+ {ret = EXCP_ERROR(env, "CipherInit failed"); goto done;}
+ if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_set_ivlen, (int)iv.size, NULL) != 1)
+ {ret = EXCP_BADARG(env, "Bad IV length"); goto done;}
#if defined(HAVE_CCM)
- if (type == atom_aes_ccm) {
- if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, (int)tag_len, NULL) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
- if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
- if (EVP_EncryptUpdate(ctx, NULL, &len, NULL, (int)in.size) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
+ if (cipherp->flags & CCM_MODE) {
+ if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_set_tag, (int)tag_len, tag_data) != 1)
+ {ret = EXCP_BADARG(env, "Can't set tag"); goto done;}
+ if (EVP_CipherInit_ex(ctx, NULL, NULL, key.data, iv.data, -1) != 1)
+ {ret = EXCP_BADARG(env, "Can't set key or iv"); goto done;}
+ if (EVP_CipherUpdate(ctx, NULL, &len, NULL, (int)in.size) != 1)
+ {ret = EXCP_BADARG(env, "Can't set text size"); goto done;}
} else
#endif
{
- if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
+ if (EVP_CipherInit_ex(ctx, NULL, NULL, key.data, iv.data, -1) != 1)
+ {ret = EXCP_BADARG(env, "Can't set key or iv"); goto done;}
}
- if (EVP_EncryptUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
+ if (EVP_CipherUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1)
+ {ret = EXCP_BADARG(env, "Can't set AAD"); goto done;}
if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL)
- {ret = EXCP_ERROR(env, ""); goto done;}
-
- if (EVP_EncryptUpdate(ctx, outp, &len, in.data, (int)in.size) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
- if (EVP_EncryptFinal_ex(ctx, outp/*+len*/, &len) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
-
- if ((tagp = enif_make_new_binary(env, tag_len, &out_tag)) == NULL)
- {ret = EXCP_ERROR(env, ""); goto done;}
+ {ret = EXCP_ERROR(env, "Can't make 'Out' binary"); goto done;}
- if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_get_tag, (int)tag_len, tagp) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
-
- CONSUME_REDS(env, in);
- ret = enif_make_tuple2(env, out, out_tag);
-
- done:
- if (ctx)
- EVP_CIPHER_CTX_free(ctx);
- return ret;
-
-#else
- return EXCP_NOTSUP(env, "");
-#endif
-}
-
-ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Type,Key,Iv,AAD,In,Tag) */
-#if defined(HAVE_AEAD)
- const struct cipher_type_t *cipherp;
- EVP_CIPHER_CTX *ctx = NULL;
- const EVP_CIPHER *cipher = NULL;
- ErlNifBinary key, iv, aad, in, tag;
- unsigned char *outp;
- ERL_NIF_TERM type, out, ret;
- int len, ctx_ctrl_set_ivlen, ctx_ctrl_set_tag;
-
- ASSERT(argc == 6);
-
- type = argv[0];
-#if defined(HAVE_GCM_EVP_DECRYPT_BUG)
- if (type == atom_aes_gcm)
- return aes_gcm_decrypt_NO_EVP(env, argc, argv);
-#endif
-
- if (!enif_is_atom(env, type))
- {ret = EXCP_BADARG(env, "non-atom cipher type"); goto done;}
- if (!enif_inspect_iolist_as_binary(env, argv[1], &key))
- {ret = EXCP_BADARG(env, "non-binary key"); goto done;}
- if (!enif_inspect_binary(env, argv[2], &iv))
- {ret = EXCP_BADARG(env, "non-binary iv"); goto done;}
- if (!enif_inspect_iolist_as_binary(env, argv[3], &aad))
- {ret = EXCP_BADARG(env, "non-binary AAD"); goto done;}
- if (!enif_inspect_iolist_as_binary(env, argv[4], &in))
- {ret = EXCP_BADARG(env, ""); goto done;}
- if (!enif_inspect_iolist_as_binary(env, argv[5], &tag))
- {ret = EXCP_BADARG(env, "non-binary text"); goto done;}
-
- if (tag.size > INT_MAX
- || key.size > INT_MAX
- || iv.size > INT_MAX
- || in.size > INT_MAX
- || aad.size > INT_MAX)
- {ret = EXCP_BADARG(env, "binary too long"); goto done;}
-
- if ((cipherp = get_cipher_type(type, key.size)) == NULL)
- {ret = EXCP_BADARG(env, "Unknown cipher"); goto done;}
- if (cipherp->flags & NON_EVP_CIPHER)
- {ret = EXCP_BADARG(env, "Bad cipher"); goto done;}
- if ( !(cipherp->flags & AEAD_CIPHER) )
- {ret = EXCP_BADARG(env, "Not aead cipher"); goto done;}
- if ((cipher = cipherp->cipher.p) == NULL)
- {ret = EXCP_NOTSUP(env, "Cipher not supported in this libcrypto version"); goto done;}
+ if (EVP_CipherUpdate(ctx, outp, &len, in.data, (int)in.size) != 1)
+ {
+ if (encflg)
+ ret = EXCP_BADARG(env, "Can't set in-text");
+ else
+ /* Decrypt error */
+ ret = atom_error;
+ goto done;
+ }
- ctx_ctrl_set_ivlen = cipherp->extra.aead.ctx_ctrl_set_ivlen;
- ctx_ctrl_set_tag = cipherp->extra.aead.ctx_ctrl_set_tag;
+ if (encflg)
+ {
+ if (EVP_CipherFinal_ex(ctx, outp/*+len*/, &len) != 1)
+ {ret = EXCP_ERROR(env, "Encrypt error"); goto done;}
- if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL)
- {ret = EXCP_ERROR(env, ""); goto done;}
+ if ((tagp = enif_make_new_binary(env, tag_len, &out_tag)) == NULL)
+ {ret = EXCP_ERROR(env, "Can't make 'Out' binary"); goto done;}
- if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
- {ret = EXCP_ERROR(env, ""); goto done;}
- if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
- if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, (int)iv.size, NULL) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
+ if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_get_tag, (int)tag_len, tagp) != 1)
+ {ret = EXCP_ERROR(env, "Can't get Tag"); goto done;}
-#if defined(HAVE_CCM)
- if (type == atom_aes_ccm) {
- if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, (int)tag.size, tag.data) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
- if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
- if (EVP_DecryptUpdate(ctx, NULL, &len, NULL, (int)in.size) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
- }
+ ret = enif_make_tuple2(env, out, out_tag);
+ }
else
-#endif
{
- if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
- }
-
- if (EVP_DecryptUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
- if (EVP_DecryptUpdate(ctx, outp, &len, in.data, (int)in.size) != 1)
- {ret = EXCP_ERROR(env, ""); goto done;}
-
#if defined(HAVE_GCM)
- if (type == atom_aes_gcm) {
- if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, (int)tag.size, tag.data) != 1)
- goto err;
- if (EVP_DecryptFinal_ex(ctx, outp+len, &len) != 1)
- goto err;
- }
+ if (cipherp->flags & GCM_MODE) {
+ if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_set_tag, (int)tag_len, tag.data) != 1)
+ /* Decrypt error */
+ {ret = atom_error; goto done;}
+ if (EVP_DecryptFinal_ex(ctx, outp+len, &len) != 1)
+ /* Decrypt error */
+ {ret = atom_error; goto done;}
+ }
#endif
- CONSUME_REDS(env, in);
- ret = out;
- goto done;
+ ret = out;
+ }
- err:
- /* Decrypt failed, that is, wrong tag */
- ret = atom_error;
+ CONSUME_REDS(env, in);
- done:
+done:
if (ctx)
EVP_CIPHER_CTX_free(ctx);
return ret;
#else
- return EXCP_NOTSUP(env, "");
+ return EXCP_NOTSUP(env, "Unsupported Cipher");
#endif
}
+
+