/* * %CopyrightBegin% * * Copyright Ericsson AB 2010-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * %CopyrightEnd% */ #include "aead.h" #include "aes.h" ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type,Key,Iv,AAD,In) */ #if defined(HAVE_AEAD) EVP_CIPHER_CTX *ctx = NULL; const EVP_CIPHER *cipher = NULL; ErlNifBinary key, iv, aad, in; 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; type = argv[0]; if (argc != 6) goto bad_arg; if (!enif_is_atom(env, type)) goto bad_arg; if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) goto bad_arg; if (!enif_inspect_binary(env, argv[2], &iv)) goto bad_arg; if (!enif_inspect_iolist_as_binary(env, argv[3], &aad)) goto bad_arg; if (!enif_inspect_iolist_as_binary(env, argv[4], &in)) goto bad_arg; if (!enif_get_uint(env, argv[5], &tag_len)) goto bad_arg; if (tag_len > INT_MAX || iv.size > INT_MAX || in.size > INT_MAX || aad.size > INT_MAX) goto bad_arg; /* Use cipher_type some day. Must check block_encrypt|decrypt first */ #if defined(HAVE_GCM) if (type == atom_aes_gcm) { if (iv.size == 0) goto bad_arg; if (tag_len < 1 || tag_len > 16) goto bad_arg; ctx_ctrl_set_ivlen = EVP_CTRL_GCM_SET_IVLEN; ctx_ctrl_get_tag = EVP_CTRL_GCM_GET_TAG; switch (key.size) { case 16: cipher = EVP_aes_128_gcm(); break; case 24: cipher = EVP_aes_192_gcm(); break; case 32: cipher = EVP_aes_256_gcm(); break; default: goto bad_arg; } } else #endif #if defined(HAVE_CCM) if (type == atom_aes_ccm) { if (iv.size < 7 || iv.size > 13) goto bad_arg; if (tag_len < 4 || tag_len > 16) goto bad_arg; if ((tag_len & 1) != 0) goto bad_arg; ctx_ctrl_set_ivlen = EVP_CTRL_CCM_SET_IVLEN; ctx_ctrl_get_tag = EVP_CTRL_CCM_GET_TAG; switch (key.size) { case 16: cipher = EVP_aes_128_ccm(); break; case 24: cipher = EVP_aes_192_ccm(); break; case 32: cipher = EVP_aes_256_ccm(); break; default: goto bad_arg; } } else #endif #if defined(HAVE_CHACHA20_POLY1305) if (type == atom_chacha20_poly1305) { if (key.size != 32) goto bad_arg; if (iv.size < 1 || iv.size > 16) goto bad_arg; if (tag_len != 16) goto bad_arg; ctx_ctrl_set_ivlen = EVP_CTRL_AEAD_SET_IVLEN; ctx_ctrl_get_tag = EVP_CTRL_AEAD_GET_TAG; cipher = EVP_chacha20_poly1305(); } else #endif return enif_raise_exception(env, atom_notsup); if ((ctx = EVP_CIPHER_CTX_new()) == NULL) goto err; if (EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) goto err; if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, (int)iv.size, NULL) != 1) goto err; #if defined(HAVE_CCM) if (type == atom_aes_ccm) { if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, (int)tag_len, NULL) != 1) goto err; if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto err; if (EVP_EncryptUpdate(ctx, NULL, &len, NULL, (int)in.size) != 1) goto err; } else #endif { if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto err; } if (EVP_EncryptUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1) goto err; if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL) goto err; if (EVP_EncryptUpdate(ctx, outp, &len, in.data, (int)in.size) != 1) goto err; if (EVP_EncryptFinal_ex(ctx, outp/*+len*/, &len) != 1) goto err; if ((tagp = enif_make_new_binary(env, tag_len, &out_tag)) == NULL) goto err; if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_get_tag, (int)tag_len, tagp) != 1) goto err; CONSUME_REDS(env, in); ret = enif_make_tuple2(env, out, out_tag); goto done; bad_arg: ret = enif_make_badarg(env); goto done; err: ret = atom_error; done: if (ctx) EVP_CIPHER_CTX_free(ctx); return ret; #else return enif_raise_exception(env, atom_notsup); #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) 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; if (argc != 6) goto bad_arg; 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)) goto bad_arg; if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) goto bad_arg; if (!enif_inspect_binary(env, argv[2], &iv)) goto bad_arg; if (!enif_inspect_iolist_as_binary(env, argv[3], &aad)) goto bad_arg; if (!enif_inspect_iolist_as_binary(env, argv[4], &in)) goto bad_arg; if (!enif_inspect_iolist_as_binary(env, argv[5], &tag)) goto bad_arg; if (tag.size > INT_MAX || key.size > INT_MAX || iv.size > INT_MAX || in.size > INT_MAX || aad.size > INT_MAX) goto bad_arg; /* Use cipher_type some day. Must check block_encrypt|decrypt first */ #if defined(HAVE_GCM) if (type == atom_aes_gcm) { if (iv.size == 0) goto bad_arg; ctx_ctrl_set_ivlen = EVP_CTRL_GCM_SET_IVLEN; ctx_ctrl_set_tag = EVP_CTRL_GCM_SET_TAG; switch (key.size) { case 16: cipher = EVP_aes_128_gcm(); break; case 24: cipher = EVP_aes_192_gcm(); break; case 32: cipher = EVP_aes_256_gcm(); break; default: goto bad_arg; } } else #endif #if defined(HAVE_CCM) if (type == atom_aes_ccm) { if (iv.size == 0) goto bad_arg; ctx_ctrl_set_ivlen = EVP_CTRL_CCM_SET_IVLEN; ctx_ctrl_set_tag = EVP_CTRL_CCM_SET_TAG; switch (key.size) { case 16: cipher = EVP_aes_128_ccm(); break; case 24: cipher = EVP_aes_192_ccm(); break; case 32: cipher = EVP_aes_256_ccm(); break; default: goto bad_arg; } } else #endif #if defined(HAVE_CHACHA20_POLY1305) if (type == atom_chacha20_poly1305) { if (key.size != 32) goto bad_arg; if (iv.size < 1 || iv.size > 16) goto bad_arg; if (tag.size != 16) goto bad_arg; ctx_ctrl_set_ivlen = EVP_CTRL_AEAD_SET_IVLEN; ctx_ctrl_set_tag = EVP_CTRL_AEAD_SET_TAG; cipher = EVP_chacha20_poly1305(); } else #endif return enif_raise_exception(env, atom_notsup); if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL) goto err; if ((ctx = EVP_CIPHER_CTX_new()) == NULL) goto err; if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) goto err; if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, (int)iv.size, NULL) != 1) goto err; #if defined(HAVE_CCM) if (type == atom_aes_ccm) { if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, (int)tag.size, tag.data) != 1) goto err; } #endif if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto err; #if defined(HAVE_CCM) if (type == atom_aes_ccm) { if (EVP_DecryptUpdate(ctx, NULL, &len, NULL, (int)in.size) != 1) goto err; } #endif if (EVP_DecryptUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1) goto err; if (EVP_DecryptUpdate(ctx, outp, &len, in.data, (int)in.size) != 1) goto err; #if defined(HAVE_GCM) || defined(HAVE_CHACHA20_POLY1305) 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; } #endif CONSUME_REDS(env, in); ret = out; goto done; bad_arg: ret = enif_make_badarg(env); goto done; err: ret = atom_error; done: if (ctx) EVP_CIPHER_CTX_free(ctx); return ret; #else return enif_raise_exception(env, atom_notsup); #endif }