diff options
Diffstat (limited to 'lib/crypto/c_src/aead.c')
-rw-r--r-- | lib/crypto/c_src/aead.c | 248 |
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 } + + |