From c7cab680b80ef9220832151ed2c8c23a5d590b8b Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Thu, 31 Jan 2019 10:27:23 +0100 Subject: crypto: New experimental api The new files api_ng.h and api_ng.c implements an api using EVP. The api is not by any mean new, except for the crypto application in Erlang/OTP. The aims at using the block api in a stream manor, that is 1) call crypto_init/4 2..N) call crypto_update/{2,3} The purpose is to simplify and hopefully optimize the SSL and SSH applications. By keeping the crypto state in C in an enif_resource the costful state copying in SSL and SSH is reduced with 1-2 per message sent or received. Changes in other files are for adaptation like FIPS etc since many functions uses the central get_cipher_type() function. --- lib/crypto/c_src/Makefile.in | 1 + lib/crypto/c_src/aead.c | 188 +++++----------------- lib/crypto/c_src/aes.c | 36 +++-- lib/crypto/c_src/aes.h | 5 + lib/crypto/c_src/algorithms.c | 59 +------ lib/crypto/c_src/api_ng.c | 223 +++++++++++++++++++++++++ lib/crypto/c_src/api_ng.h | 29 ++++ lib/crypto/c_src/atoms.c | 70 +------- lib/crypto/c_src/atoms.h | 37 ----- lib/crypto/c_src/block.c | 40 +++-- lib/crypto/c_src/cipher.c | 194 ++++++++++++++++------ lib/crypto/c_src/cipher.h | 31 +++- lib/crypto/c_src/cmac.c | 6 +- lib/crypto/c_src/crypto.c | 5 +- lib/crypto/c_src/engine.c | 17 +- lib/crypto/c_src/pkey.c | 6 +- lib/crypto/doc/src/crypto.xml | 18 ++- lib/crypto/src/crypto.erl | 366 +++++++++++++++++++++++++++++++++--------- 18 files changed, 849 insertions(+), 482 deletions(-) create mode 100644 lib/crypto/c_src/api_ng.c create mode 100644 lib/crypto/c_src/api_ng.h diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index 508e1c40ee..e1e7f71538 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -75,6 +75,7 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/aead$(TYPEMARKER).o \ $(OBJDIR)/aes$(TYPEMARKER).o \ $(OBJDIR)/algorithms$(TYPEMARKER).o \ + $(OBJDIR)/api_ng$(TYPEMARKER).o \ $(OBJDIR)/atoms$(TYPEMARKER).o \ $(OBJDIR)/block$(TYPEMARKER).o \ $(OBJDIR)/bn$(TYPEMARKER).o \ diff --git a/lib/crypto/c_src/aead.c b/lib/crypto/c_src/aead.c index c6f4cf52b1..3ee04f1be9 100644 --- a/lib/crypto/c_src/aead.c +++ b/lib/crypto/c_src/aead.c @@ -20,17 +20,19 @@ #include "aead.h" #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) */ #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; 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; + int len, ctx_ctrl_set_ivlen, ctx_ctrl_get_tag, ctx_ctrl_set_tag; type = argv[0]; @@ -55,77 +57,19 @@ ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) || 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 + if ((cipherp = get_cipher_type(type, key.size)) == NULL) + goto bad_arg; + if (cipherp->flags & NON_EVP_CIPHER) + goto bad_arg; + if (! (cipherp->flags & AEAD_CIPHER) ) + goto bad_arg; + if ((cipher = cipherp->cipher.p) == NULL) return enif_raise_exception(env, atom_notsup); - + + 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 ((ctx = EVP_CIPHER_CTX_new()) == NULL) goto err; @@ -136,7 +80,7 @@ ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #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) + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, (int)tag_len, NULL) != 1) goto err; if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto err; @@ -144,10 +88,10 @@ ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto err; } else #endif - { - if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) - goto err; - } + { + 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; @@ -190,6 +134,7 @@ ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 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; @@ -225,70 +170,18 @@ ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) || 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 + if ((cipherp = get_cipher_type(type, key.size)) == NULL) + goto bad_arg; + if (cipherp->flags & NON_EVP_CIPHER) + goto bad_arg; + if ( !(cipherp->flags & AEAD_CIPHER) ) + goto bad_arg; + if ((cipher = cipherp->cipher.p) == NULL) return enif_raise_exception(env, atom_notsup); + ctx_ctrl_set_ivlen = cipherp->extra.aead.ctx_ctrl_set_ivlen; + ctx_ctrl_set_tag = cipherp->extra.aead.ctx_ctrl_set_tag; + if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL) goto err; @@ -301,27 +194,26 @@ ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #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) + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, (int)tag.size, tag.data) != 1) + goto err; + if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.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; } + else #endif + { + if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) + goto err; + } 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 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; diff --git a/lib/crypto/c_src/aes.c b/lib/crypto/c_src/aes.c index 2f30ec8a58..ee2bb70fb7 100644 --- a/lib/crypto/c_src/aes.c +++ b/lib/crypto/c_src/aes.c @@ -166,8 +166,6 @@ ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv } -/* Initializes state for ctr streaming (de)encryption -*/ #ifdef HAVE_EVP_AES_CTR ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Key, IVec) */ @@ -279,27 +277,31 @@ ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Key, IVec) */ + ASSERT(argc == 2); + + return aes_ctr_stream_init_compat(env, argv[0], argv[1]); +} + + +ERL_NIF_TERM aes_ctr_stream_init_compat(ErlNifEnv* env, const ERL_NIF_TERM key_term, const ERL_NIF_TERM iv_term) +{ ErlNifBinary key_bin, ivec_bin; ERL_NIF_TERM ecount_bin; unsigned char *outp; - - ASSERT(argc == 2); - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)) + + if (!enif_inspect_iolist_as_binary(env, key_term, &key_bin)) goto bad_arg; if (key_bin.size != 16 && key_bin.size != 24 && key_bin.size != 32) goto bad_arg; - if (!enif_inspect_binary(env, argv[1], &ivec_bin)) + if (!enif_inspect_binary(env, iv_term, &ivec_bin)) goto bad_arg; if (ivec_bin.size != 16) goto bad_arg; - if ((outp = enif_make_new_binary(env, AES_BLOCK_SIZE, &ecount_bin)) == NULL) goto err; - memset(outp, 0, AES_BLOCK_SIZE); - return enif_make_tuple4(env, argv[0], argv[1], ecount_bin, enif_make_int(env, 0)); + return enif_make_tuple4(env, key_term, iv_term, ecount_bin, enif_make_int(env, 0)); bad_arg: err: @@ -307,6 +309,14 @@ ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar } ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ASSERT(argc == 2); + + return aes_ctr_stream_encrypt_compat(env, argv[0], argv[1]); +} + + +ERL_NIF_TERM aes_ctr_stream_encrypt_compat(ErlNifEnv* env, const ERL_NIF_TERM state_arg, const ERL_NIF_TERM data_arg) {/* ({Key, IVec, ECount, Num}, Data) */ ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin; AES_KEY aes_key; @@ -318,9 +328,7 @@ ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM unsigned char * ecount2_buf; unsigned char *outp; - ASSERT(argc == 2); - - if (!enif_get_tuple(env, argv[0], &state_arity, &state_term)) + if (!enif_get_tuple(env, state_arg, &state_arity, &state_term)) goto bad_arg; if (state_arity != 4) goto bad_arg; @@ -338,7 +346,7 @@ ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM goto bad_arg; if (!enif_get_uint(env, state_term[3], &num)) goto bad_arg; - if (!enif_inspect_iolist_as_binary(env, argv[1], &text_bin)) + if (!enif_inspect_iolist_as_binary(env, data_arg, &text_bin)) goto bad_arg; /* NOTE: This function returns 0 on success unlike most OpenSSL functions */ diff --git a/lib/crypto/c_src/aes.h b/lib/crypto/c_src/aes.h index 09c984f84a..527d041410 100644 --- a/lib/crypto/c_src/aes.h +++ b/lib/crypto/c_src/aes.h @@ -26,8 +26,13 @@ ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +#if !defined(HAVE_EVP_AES_CTR) +ERL_NIF_TERM aes_ctr_stream_init_compat(ErlNifEnv* env, const ERL_NIF_TERM key_term, const ERL_NIF_TERM iv_term); +ERL_NIF_TERM aes_ctr_stream_encrypt_compat(ErlNifEnv* env, const ERL_NIF_TERM state_arg, const ERL_NIF_TERM data_arg); +#endif #ifdef HAVE_GCM_EVP_DECRYPT_BUG ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index a5bf248ea0..06cd109fc1 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -19,13 +19,12 @@ */ #include "algorithms.h" +#include "cipher.h" static unsigned int algo_hash_cnt, algo_hash_fips_cnt; static ERL_NIF_TERM algo_hash[14]; /* increase when extending the list */ static unsigned int algo_pubkey_cnt, algo_pubkey_fips_cnt; static ERL_NIF_TERM algo_pubkey[12]; /* increase when extending the list */ -static unsigned int algo_cipher_cnt, algo_cipher_fips_cnt; -static ERL_NIF_TERM algo_cipher[25]; /* increase when extending the list */ static unsigned int algo_mac_cnt, algo_mac_fips_cnt; static ERL_NIF_TERM algo_mac[3]; /* increase when extending the list */ static unsigned int algo_curve_cnt, algo_curve_fips_cnt; @@ -92,56 +91,7 @@ void init_algorithms_types(ErlNifEnv* env) #endif algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp"); - // Validated algorithms first - algo_cipher_cnt = 0; -#ifndef OPENSSL_NO_DES - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des_ede3"); -#ifdef HAVE_DES_ede3_cfb_encrypt - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbf"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cfb"); -#endif -#endif - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc128"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb8"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb128"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc256"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_ctr"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_ecb"); -#if defined(HAVE_GCM) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_gcm"); -#endif -#if defined(HAVE_CCM) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ccm"); -#endif - // Non-validated algorithms follow - algo_cipher_fips_cnt = algo_cipher_cnt; -#ifdef HAVE_AES_IGE - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ige256"); -#endif -#ifndef OPENSSL_NO_DES - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cfb"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_ecb"); -#endif - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cfb64"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ofb64"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ecb"); -#ifndef OPENSSL_NO_RC2 - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc2_cbc"); -#endif -#ifndef OPENSSL_NO_RC4 - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc4"); -#endif -#if defined(HAVE_CHACHA20_POLY1305) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"chacha20_poly1305"); -#endif -#if defined(HAVE_CHACHA20) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"chacha20"); -#endif - + // Validated algorithms first algo_mac_cnt = 0; algo_mac[algo_mac_cnt++] = enif_make_atom(env,"hmac"); @@ -290,7 +240,6 @@ void init_algorithms_types(ErlNifEnv* env) // Check that the max number of algos is updated ASSERT(algo_hash_cnt <= sizeof(algo_hash)/sizeof(ERL_NIF_TERM)); ASSERT(algo_pubkey_cnt <= sizeof(algo_pubkey)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_cipher_cnt <= sizeof(algo_cipher)/sizeof(ERL_NIF_TERM)); ASSERT(algo_mac_cnt <= sizeof(algo_mac)/sizeof(ERL_NIF_TERM)); ASSERT(algo_curve_cnt <= sizeof(algo_curve)/sizeof(ERL_NIF_TERM)); ASSERT(algo_rsa_opts_cnt <= sizeof(algo_rsa_opts)/sizeof(ERL_NIF_TERM)); @@ -303,14 +252,12 @@ ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) unsigned int hash_cnt = fips_mode ? algo_hash_fips_cnt : algo_hash_cnt; unsigned int pubkey_cnt = fips_mode ? algo_pubkey_fips_cnt : algo_pubkey_cnt; - unsigned int cipher_cnt = fips_mode ? algo_cipher_fips_cnt : algo_cipher_cnt; unsigned int mac_cnt = fips_mode ? algo_mac_fips_cnt : algo_mac_cnt; unsigned int curve_cnt = fips_mode ? algo_curve_fips_cnt : algo_curve_cnt; unsigned int rsa_opts_cnt = fips_mode ? algo_rsa_opts_fips_cnt : algo_rsa_opts_cnt; #else unsigned int hash_cnt = algo_hash_cnt; unsigned int pubkey_cnt = algo_pubkey_cnt; - unsigned int cipher_cnt = algo_cipher_cnt; unsigned int mac_cnt = algo_mac_cnt; unsigned int curve_cnt = algo_curve_cnt; unsigned int rsa_opts_cnt = algo_rsa_opts_cnt; @@ -318,7 +265,7 @@ ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return enif_make_tuple6(env, enif_make_list_from_array(env, algo_hash, hash_cnt), enif_make_list_from_array(env, algo_pubkey, pubkey_cnt), - enif_make_list_from_array(env, algo_cipher, cipher_cnt), + cipher_types_as_list(env), enif_make_list_from_array(env, algo_mac, mac_cnt), enif_make_list_from_array(env, algo_curve, curve_cnt), enif_make_list_from_array(env, algo_rsa_opts, rsa_opts_cnt) diff --git a/lib/crypto/c_src/api_ng.c b/lib/crypto/c_src/api_ng.c new file mode 100644 index 0000000000..c4114d1626 --- /dev/null +++ b/lib/crypto/c_src/api_ng.c @@ -0,0 +1,223 @@ +/* + * %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 "api_ng.h" +#include "aes.h" +#include "cipher.h" + +/* + * A unified set of functions for encryption/decryption. + * + * EXPERIMENTAL!! + * + */ +ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + + + +/* Try better error messages in new functions */ +#define ERROR_Term(Env, ReasonTerm) enif_make_tuple2((Env), atom_error, (ReasonTerm)) +#define ERROR_Str(Env, ReasonString) ERROR_Term((Env), enif_make_string((Env),(ReasonString),(ERL_NIF_LATIN1))) + +/* Initializes state for (de)encryption + */ +ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Cipher, Key, IVec, Encrypt) % if no IV for the Cipher, set IVec = <<>> + */ + ErlNifBinary key_bin, ivec_bin; + unsigned char *iv = NULL; + struct evp_cipher_ctx *ctx; + const struct cipher_type_t *cipherp; + const EVP_CIPHER *cipher; + ERL_NIF_TERM enc_flg_arg, ret; + int enc; + unsigned iv_len; + + enc_flg_arg = argv[argc-1]; + if (enc_flg_arg == atom_true) + enc = 1; + else if (enc_flg_arg == atom_false) + enc = 0; + else if (enc_flg_arg == atom_undefined) + /* For compat funcs in crypto.erl */ + enc = -1; + else + return ERROR_Str(env, "Bad enc flag"); + + if (!enif_inspect_binary(env, argv[1], &key_bin)) + return ERROR_Str(env, "Bad key"); + + if (!(cipherp = get_cipher_type(argv[0], key_bin.size))) + return ERROR_Str(env, "Unknown cipher or bad key size"); + + if (FORBIDDEN_IN_FIPS(cipherp)) + return enif_raise_exception(env, atom_notsup); + + if (enc == -1) + return atom_undefined; + + if (!(cipher = cipherp->cipher.p)) { +#if !defined(HAVE_EVP_AES_CTR) + if (cipherp->flags & AES_CTR_COMPAT) + return aes_ctr_stream_init_compat(env, argv[1], argv[2]); + else +#endif + return enif_raise_exception(env, atom_notsup); + } + +#ifdef HAVE_ECB_IVEC_BUG + if (cipherp->flags & ECB_BUG_0_9_8L) + iv_len = 0; /* <= 0.9.8l returns faulty ivec length */ + else +#endif + iv_len = EVP_CIPHER_iv_length(cipher); + + if (iv_len) { + if (!enif_inspect_binary(env, argv[2], &ivec_bin)) + return ERROR_Str(env, "Bad iv type"); + + if (iv_len != ivec_bin.size) + return ERROR_Str(env, "Bad iv size"); + + iv = ivec_bin.data; + } + + if ((ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL) + return ERROR_Str(env, "Can't allocate resource"); + + ctx->ctx = EVP_CIPHER_CTX_new(); + if (! ctx->ctx) + return ERROR_Str(env, "Can't allocate context"); + + if (!EVP_CipherInit_ex(ctx->ctx, cipher, NULL, NULL, NULL, enc)) { + enif_release_resource(ctx); + return ERROR_Str(env, "Can't initialize context, step 1"); + } + + if (!EVP_CIPHER_CTX_set_key_length(ctx->ctx, (int)key_bin.size)) { + enif_release_resource(ctx); + return ERROR_Str(env, "Can't initialize context, key_length"); + } + + if (EVP_CIPHER_type(cipher) == NID_rc2_cbc) { + if (key_bin.size > INT_MAX / 8) { + enif_release_resource(ctx); + return ERROR_Str(env, "To large rc2_cbc key"); + } + if (!EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_SET_RC2_KEY_BITS, (int)key_bin.size * 8, NULL)) { + enif_release_resource(ctx); + return ERROR_Str(env, "ctrl rc2_cbc key"); + } + } + + if (!EVP_CipherInit_ex(ctx->ctx, NULL, NULL, key_bin.data, iv, enc)) { + enif_release_resource(ctx); + return ERROR_Str(env, "Can't initialize key and/or iv"); + } + + EVP_CIPHER_CTX_set_padding(ctx->ctx, 0); + + ret = enif_make_resource(env, ctx); + enif_release_resource(ctx); + return ret; +} + +ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) + (Context, Data, IV) */ + struct evp_cipher_ctx *ctx; + ErlNifBinary in_data_bin, ivec_bin, out_data_bin; + int out_len, block_size; + +#if !defined(HAVE_EVP_AES_CTR) + const ERL_NIF_TERM *state_term; + int state_arity; + + if (enif_get_tuple(env, argv[0], &state_arity, &state_term) && (state_arity == 4)) { + return aes_ctr_stream_encrypt_compat(env, argv[0], argv[1]); + } +#endif + + if (!enif_get_resource(env, argv[0], evp_cipher_ctx_rtype, (void**)&ctx)) + return ERROR_Str(env, "Bad 1:st arg"); + + if (!enif_inspect_binary(env, argv[1], &in_data_bin) ) + return ERROR_Str(env, "Bad 2:nd arg"); + + /* arg[1] was checked by the caller */ + ASSERT(in_data_bin.size =< INT_MAX); + + block_size = EVP_CIPHER_CTX_block_size(ctx->ctx); + if (in_data_bin.size % (size_t)block_size != 0) + return ERROR_Str(env, "Data not a multiple of block size"); + + if (argc==3) { + if (!enif_inspect_iolist_as_binary(env, argv[2], &ivec_bin)) + return ERROR_Str(env, "Not binary IV"); + + if (ivec_bin.size > INT_MAX) + return ERROR_Str(env, "Too big IV"); + + if (!EVP_CipherInit_ex(ctx->ctx, NULL, NULL, NULL, ivec_bin.data, -1)) + return ERROR_Str(env, "Can't set IV"); + } + + if (!enif_alloc_binary((size_t)in_data_bin.size+block_size, &out_data_bin)) + return ERROR_Str(env, "Can't allocate outdata"); + + if (!EVP_CipherUpdate(ctx->ctx, out_data_bin.data, &out_len, in_data_bin.data, in_data_bin.size)) + return ERROR_Str(env, "Can't update"); + + if (!enif_realloc_binary(&out_data_bin, (size_t)out_len)) + return ERROR_Str(env, "Can't reallocate"); + + CONSUME_REDS(env, in_data_bin); + return enif_make_binary(env, &out_data_bin); +} + + +ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) + (Context, Data, IV) */ + int i; + ErlNifBinary data_bin; + ERL_NIF_TERM new_argv[3]; + + ASSERT(argc =< 3); + + if (!enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) + return ERROR_Str(env, "iodata expected as data"); + + if (data_bin.size > INT_MAX) + return ERROR_Str(env, "to long data"); + + for (i=0; i MAX_BYTES_TO_NIF) { + return enif_schedule_nif(env, "ng_crypto_update", + ERL_NIF_DIRTY_JOB_CPU_BOUND, + ng_crypto_update, argc, new_argv); + } + + return ng_crypto_update(env, argc, new_argv); +} + diff --git a/lib/crypto/c_src/api_ng.h b/lib/crypto/c_src/api_ng.h new file mode 100644 index 0000000000..a3b40fe7fc --- /dev/null +++ b/lib/crypto/c_src/api_ng.h @@ -0,0 +1,29 @@ +/* + * %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% + */ + +#ifndef E_API_NG_H__ +#define E_API_NG_H__ 1 + +#include "common.h" + +ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_AES_H__ */ diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c index 5f19327197..2e417da7f4 100644 --- a/lib/crypto/c_src/atoms.c +++ b/lib/crypto/c_src/atoms.c @@ -31,12 +31,6 @@ ERL_NIF_TERM atom_signature_md; ERL_NIF_TERM atom_undefined; ERL_NIF_TERM atom_ok; -ERL_NIF_TERM atom_not_prime; -ERL_NIF_TERM atom_not_strong_prime; -ERL_NIF_TERM atom_unable_to_check_generator; -ERL_NIF_TERM atom_not_suitable_generator; -ERL_NIF_TERM atom_check_failed; -ERL_NIF_TERM atom_unknown; ERL_NIF_TERM atom_none; ERL_NIF_TERM atom_notsup; ERL_NIF_TERM atom_digest; @@ -48,7 +42,6 @@ ERL_NIF_TERM atom_not_supported; #endif #if defined(HAVE_EC) -ERL_NIF_TERM atom_ec; ERL_NIF_TERM atom_prime_field; ERL_NIF_TERM atom_characteristic_two_field; ERL_NIF_TERM atom_tpbasis; @@ -64,14 +57,6 @@ ERL_NIF_TERM atom_aes_gcm; #ifdef HAVE_CCM ERL_NIF_TERM atom_aes_ccm; #endif -#ifdef HAVE_CHACHA20_POLY1305 -ERL_NIF_TERM atom_chacha20_poly1305; -#endif -#ifdef HAVE_ECB_IVEC_BUG -ERL_NIF_TERM atom_aes_ecb; -ERL_NIF_TERM atom_des_ecb; -ERL_NIF_TERM atom_blowfish_ecb; -#endif ERL_NIF_TERM atom_rsa; ERL_NIF_TERM atom_dss; @@ -99,16 +84,6 @@ ERL_NIF_TERM atom_rsa_sslv23_padding; #endif ERL_NIF_TERM atom_rsa_x931_padding; ERL_NIF_TERM atom_rsa_pss_saltlen; -ERL_NIF_TERM atom_sha224; -ERL_NIF_TERM atom_sha256; -ERL_NIF_TERM atom_sha384; -ERL_NIF_TERM atom_sha512; -ERL_NIF_TERM atom_sha3_224; -ERL_NIF_TERM atom_sha3_256; -ERL_NIF_TERM atom_sha3_384; -ERL_NIF_TERM atom_sha3_512; -ERL_NIF_TERM atom_md5; -ERL_NIF_TERM atom_ripemd160; #ifdef HAVE_BLAKE2 ERL_NIF_TERM atom_blake2b; @@ -116,14 +91,6 @@ ERL_NIF_TERM atom_blake2s; #endif #ifdef HAS_ENGINE_SUPPORT -ERL_NIF_TERM atom_bad_engine_method; -ERL_NIF_TERM atom_bad_engine_id; -ERL_NIF_TERM atom_ctrl_cmd_failed; -ERL_NIF_TERM atom_engine_init_failed; -ERL_NIF_TERM atom_register_engine_failed; -ERL_NIF_TERM atom_add_engine_failed; -ERL_NIF_TERM atom_remove_engine_failed; -ERL_NIF_TERM atom_engine_method_not_supported; ERL_NIF_TERM atom_engine_method_rsa; ERL_NIF_TERM atom_engine_method_dsa; @@ -169,18 +136,11 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM atom_signature_md = enif_make_atom(env,"signature_md"); atom_undefined = enif_make_atom(env,"undefined"); atom_ok = enif_make_atom(env,"ok"); - atom_not_prime = enif_make_atom(env,"not_prime"); - atom_not_strong_prime = enif_make_atom(env,"not_strong_prime"); - atom_unable_to_check_generator = enif_make_atom(env,"unable_to_check_generator"); - atom_not_suitable_generator = enif_make_atom(env,"not_suitable_generator"); - atom_check_failed = enif_make_atom(env,"check_failed"); - atom_unknown = enif_make_atom(env,"unknown"); atom_none = enif_make_atom(env,"none"); atom_notsup = enif_make_atom(env,"notsup"); atom_digest = enif_make_atom(env,"digest"); #if defined(HAVE_EC) - atom_ec = enif_make_atom(env,"ec"); atom_prime_field = enif_make_atom(env,"prime_field"); atom_characteristic_two_field = enif_make_atom(env,"characteristic_two_field"); atom_tpbasis = enif_make_atom(env,"tpbasis"); @@ -196,14 +156,6 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM #ifdef HAVE_CCM atom_aes_ccm = enif_make_atom(env, "aes_ccm"); #endif -#ifdef HAVE_CHACHA20_POLY1305 - atom_chacha20_poly1305 = enif_make_atom(env,"chacha20_poly1305"); -#endif -#ifdef HAVE_ECB_IVEC_BUG - atom_aes_ecb = enif_make_atom(env, "aes_ecb"); - atom_des_ecb = enif_make_atom(env, "des_ecb"); - atom_blowfish_ecb = enif_make_atom(env, "blowfish_ecb"); -#endif #ifdef FIPS_SUPPORT atom_enabled = enif_make_atom(env,"enabled"); @@ -214,6 +166,7 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM atom_rsa = enif_make_atom(env,"rsa"); atom_dss = enif_make_atom(env,"dss"); atom_ecdsa = enif_make_atom(env,"ecdsa"); + #ifdef HAVE_ED_CURVE_DH atom_x25519 = enif_make_atom(env,"x25519"); atom_x448 = enif_make_atom(env,"x448"); @@ -234,29 +187,8 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM #endif atom_rsa_x931_padding = enif_make_atom(env,"rsa_x931_padding"); atom_rsa_pss_saltlen = enif_make_atom(env,"rsa_pss_saltlen"); - atom_sha224 = enif_make_atom(env,"sha224"); - atom_sha256 = enif_make_atom(env,"sha256"); - atom_sha384 = enif_make_atom(env,"sha384"); - atom_sha512 = enif_make_atom(env,"sha512"); - atom_sha3_224 = enif_make_atom(env,"sha3_224"); - atom_sha3_256 = enif_make_atom(env,"sha3_256"); - atom_sha3_384 = enif_make_atom(env,"sha3_384"); - atom_sha3_512 = enif_make_atom(env,"sha3_512"); - atom_md5 = enif_make_atom(env,"md5"); - atom_ripemd160 = enif_make_atom(env,"ripemd160"); -#ifdef HAVE_BLAKE2 - atom_blake2b = enif_make_atom(env,"blake2b"); - atom_blake2s = enif_make_atom(env,"blake2s"); -#endif #ifdef HAS_ENGINE_SUPPORT - atom_bad_engine_method = enif_make_atom(env,"bad_engine_method"); - atom_bad_engine_id = enif_make_atom(env,"bad_engine_id"); - atom_ctrl_cmd_failed = enif_make_atom(env,"ctrl_cmd_failed"); - atom_engine_init_failed = enif_make_atom(env,"engine_init_failed"); - atom_engine_method_not_supported = enif_make_atom(env,"engine_method_not_supported"); - atom_add_engine_failed = enif_make_atom(env,"add_engine_failed"); - atom_remove_engine_failed = enif_make_atom(env,"remove_engine_failed"); atom_engine_method_rsa = enif_make_atom(env,"engine_method_rsa"); atom_engine_method_dsa = enif_make_atom(env,"engine_method_dsa"); diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h index 32f5ec856c..f15523d865 100644 --- a/lib/crypto/c_src/atoms.h +++ b/lib/crypto/c_src/atoms.h @@ -35,12 +35,6 @@ extern ERL_NIF_TERM atom_signature_md; extern ERL_NIF_TERM atom_undefined; extern ERL_NIF_TERM atom_ok; -extern ERL_NIF_TERM atom_not_prime; -extern ERL_NIF_TERM atom_not_strong_prime; -extern ERL_NIF_TERM atom_unable_to_check_generator; -extern ERL_NIF_TERM atom_not_suitable_generator; -extern ERL_NIF_TERM atom_check_failed; -extern ERL_NIF_TERM atom_unknown; extern ERL_NIF_TERM atom_none; extern ERL_NIF_TERM atom_notsup; extern ERL_NIF_TERM atom_digest; @@ -52,7 +46,6 @@ extern ERL_NIF_TERM atom_not_supported; #endif #if defined(HAVE_EC) -extern ERL_NIF_TERM atom_ec; extern ERL_NIF_TERM atom_prime_field; extern ERL_NIF_TERM atom_characteristic_two_field; extern ERL_NIF_TERM atom_tpbasis; @@ -68,14 +61,6 @@ extern ERL_NIF_TERM atom_aes_gcm; #ifdef HAVE_CCM extern ERL_NIF_TERM atom_aes_ccm; #endif -#ifdef HAVE_CHACHA20_POLY1305 -extern ERL_NIF_TERM atom_chacha20_poly1305; -#endif -#ifdef HAVE_ECB_IVEC_BUG -extern ERL_NIF_TERM atom_aes_ecb; -extern ERL_NIF_TERM atom_des_ecb; -extern ERL_NIF_TERM atom_blowfish_ecb; -#endif extern ERL_NIF_TERM atom_rsa; extern ERL_NIF_TERM atom_dss; @@ -103,30 +88,8 @@ extern ERL_NIF_TERM atom_rsa_sslv23_padding; #endif extern ERL_NIF_TERM atom_rsa_x931_padding; extern ERL_NIF_TERM atom_rsa_pss_saltlen; -extern ERL_NIF_TERM atom_sha224; -extern ERL_NIF_TERM atom_sha256; -extern ERL_NIF_TERM atom_sha384; -extern ERL_NIF_TERM atom_sha512; -extern ERL_NIF_TERM atom_sha3_224; -extern ERL_NIF_TERM atom_sha3_256; -extern ERL_NIF_TERM atom_sha3_384; -extern ERL_NIF_TERM atom_sha3_512; -extern ERL_NIF_TERM atom_md5; -extern ERL_NIF_TERM atom_ripemd160; -#ifdef HAVE_BLAKE2 -extern ERL_NIF_TERM atom_blake2b; -extern ERL_NIF_TERM atom_blake2s; -#endif #ifdef HAS_ENGINE_SUPPORT -extern ERL_NIF_TERM atom_bad_engine_method; -extern ERL_NIF_TERM atom_bad_engine_id; -extern ERL_NIF_TERM atom_ctrl_cmd_failed; -extern ERL_NIF_TERM atom_engine_init_failed; -extern ERL_NIF_TERM atom_register_engine_failed; -extern ERL_NIF_TERM atom_add_engine_failed; -extern ERL_NIF_TERM atom_remove_engine_failed; -extern ERL_NIF_TERM atom_engine_method_not_supported; extern ERL_NIF_TERM atom_engine_method_rsa; extern ERL_NIF_TERM atom_engine_method_dsa; diff --git a/lib/crypto/c_src/block.c b/lib/crypto/c_src/block.c index d88ee8dba7..0a4fd72623 100644 --- a/lib/crypto/c_src/block.c +++ b/lib/crypto/c_src/block.c @@ -24,7 +24,7 @@ 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; + const struct cipher_type_t *cipherp; const EVP_CIPHER *cipher; ErlNifBinary key, ivec, text; EVP_CIPHER_CTX *ctx = NULL; @@ -41,36 +41,42 @@ ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] goto bad_arg; if ((cipherp = get_cipher_type(argv[0], key.size)) == NULL) goto bad_arg; + if (cipherp->flags & (NON_EVP_CIPHER | AEAD_CIPHER)) + goto bad_arg; if (!enif_inspect_iolist_as_binary(env, argv[argc - 2], &text)) goto bad_arg; if (text.size > INT_MAX) goto bad_arg; + if (FORBIDDEN_IN_FIPS(cipherp)) + return enif_raise_exception(env, atom_notsup); if ((cipher = cipherp->cipher.p) == NULL) return enif_raise_exception(env, atom_notsup); - if (argv[0] == atom_aes_cfb8 - && (key.size == 24 || key.size == 32)) { - /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? - * Fall back on low level API - */ - return aes_cfb_8_crypt(env, argc-1, argv+1); + if (cipherp->flags & AES_CFBx) { + if (argv[0] == atom_aes_cfb8 + && (key.size == 24 || key.size == 32)) { + /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? + * Fall back on low level API + */ + return aes_cfb_8_crypt(env, argc-1, argv+1); + } + else if (argv[0] == atom_aes_cfb128 + && (key.size == 24 || key.size == 32)) { + /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? + * Fall back on low level API + */ + return aes_cfb_128_crypt_nif(env, argc-1, argv+1); + } } - else if (argv[0] == atom_aes_cfb128 - && (key.size == 24 || key.size == 32)) { - /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? - * Fall back on low level API - */ - return aes_cfb_128_crypt_nif(env, argc-1, argv+1); - } ivec_size = EVP_CIPHER_iv_length(cipher); #ifdef HAVE_ECB_IVEC_BUG - if (argv[0] == atom_aes_ecb || argv[0] == atom_blowfish_ecb || - argv[0] == atom_des_ecb) - ivec_size = 0; /* 0.9.8l returns faulty ivec_size */ + if (cipherp->flags & ECB_BUG_0_9_8L) + ivec_size = 0; /* 0.9.8l returns faulty ivec_size */ #endif + if (ivec_size < 0) goto bad_arg; diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c index 449e636037..f8e44b228a 100644 --- a/lib/crypto/c_src/cipher.c +++ b/lib/crypto/c_src/cipher.c @@ -28,51 +28,122 @@ static struct cipher_type_t cipher_types[] = { - {{"rc2_cbc"}, #ifndef OPENSSL_NO_RC2 - {&EVP_rc2_cbc} + {{"rc2_cbc"}, {&EVP_rc2_cbc}, 0, NO_FIPS_CIPHER}, #else - {NULL} + {{"rc2_cbc"}, {NULL}, 0, NO_FIPS_CIPHER}, #endif - ,0}, - {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}, 0}, - {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}, 0}, - {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}, 0}, - {{"des_ede3_cbc"}, {COND_NO_DES_PTR(&EVP_des_ede3_cbc)}, 0}, - {{"des_ede3_cbf"}, /* Misspelled, retained */ -#ifdef HAVE_DES_ede3_cfb_encrypt - {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} +#ifndef OPENSSL_NO_RC4 + {{"rc4"}, {&EVP_rc4}, 0, NO_FIPS_CIPHER}, #else - {NULL} + {{"rc4"}, {NULL}, 0, NO_FIPS_CIPHER}, #endif - ,0}, - {{"des_ede3_cfb"}, + {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}, 0, NO_FIPS_CIPHER}, + {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}, 0, NO_FIPS_CIPHER}, + {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}, 0, NO_FIPS_CIPHER | ECB_BUG_0_9_8L}, + + {{"des_ede3_cbc"}, {COND_NO_DES_PTR(&EVP_des_ede3_cbc)}, 0, 0}, + #ifdef HAVE_DES_ede3_cfb_encrypt - {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} + {{"des_ede3_cfb"}, {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)}, 0, 0}, +#else + {{"des_ede3_cfb"}, {NULL}, 0, 0}, +#endif + + {{"blowfish_cbc"}, {&EVP_bf_cbc}, 0, NO_FIPS_CIPHER}, + {{"blowfish_cfb64"}, {&EVP_bf_cfb64}, 0, NO_FIPS_CIPHER}, + {{"blowfish_ofb64"}, {&EVP_bf_ofb}, 0, NO_FIPS_CIPHER}, + {{"blowfish_ecb"}, {&EVP_bf_ecb}, 0, NO_FIPS_CIPHER | ECB_BUG_0_9_8L}, + + {{"aes_cbc"}, {&EVP_aes_128_cbc}, 16, 0}, + {{"aes_cbc"}, {&EVP_aes_192_cbc}, 24, 0}, + {{"aes_cbc"}, {&EVP_aes_256_cbc}, 32, 0}, + + {{"aes_128_cbc"}, {&EVP_aes_128_cbc}, 16, 0}, + {{"aes_192_cbc"}, {&EVP_aes_192_cbc}, 24, 0}, + {{"aes_256_cbc"}, {&EVP_aes_256_cbc}, 32, 0}, + + {{"aes_cfb8"}, {&EVP_aes_128_cfb8}, 16, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb8"}, {&EVP_aes_192_cfb8}, 24, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb8"}, {&EVP_aes_256_cfb8}, 32, NO_FIPS_CIPHER | AES_CFBx}, + + {{"aes_cfb128"}, {&EVP_aes_128_cfb128}, 16, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb128"}, {&EVP_aes_192_cfb128}, 24, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb128"}, {&EVP_aes_256_cfb128}, 32, NO_FIPS_CIPHER | AES_CFBx}, + + {{"aes_ecb"}, {&EVP_aes_128_ecb}, 16, ECB_BUG_0_9_8L}, + {{"aes_ecb"}, {&EVP_aes_192_ecb}, 24, ECB_BUG_0_9_8L}, + {{"aes_ecb"}, {&EVP_aes_256_ecb}, 32, ECB_BUG_0_9_8L}, + +#if defined(HAVE_EVP_AES_CTR) + {{"aes_128_ctr"}, {&EVP_aes_128_ctr}, 16, 0}, + {{"aes_192_ctr"}, {&EVP_aes_192_ctr}, 24, 0}, + {{"aes_256_ctr"}, {&EVP_aes_256_ctr}, 32, 0}, + {{"aes_ctr"}, {&EVP_aes_128_ctr}, 16, 0}, + {{"aes_ctr"}, {&EVP_aes_192_ctr}, 24, 0}, + {{"aes_ctr"}, {&EVP_aes_256_ctr}, 32, 0}, +#else + {{"aes_128_ctr"}, {NULL}, 16, AES_CTR_COMPAT}, + {{"aes_192_ctr"}, {NULL}, 24, AES_CTR_COMPAT}, + {{"aes_256_ctr"}, {NULL}, 32, AES_CTR_COMPAT}, + {{"aes_ctr"}, {NULL}, 0, AES_CTR_COMPAT}, +#endif + +#if defined(HAVE_CHACHA20) + {{"chacha20"}, {&EVP_chacha20}, 32, NO_FIPS_CIPHER}, +#else + {{"chacha20"}, {NULL}, 0, NO_FIPS_CIPHER}, +#endif + + /*==== AEAD ciphers ====*/ +#if defined(HAVE_CHACHA20_POLY1305) + {{"chacha20_poly1305"}, {&EVP_chacha20_poly1305}, 0, NO_FIPS_CIPHER | AEAD_CIPHER, {{EVP_CTRL_AEAD_SET_IVLEN,EVP_CTRL_AEAD_GET_TAG,EVP_CTRL_AEAD_SET_TAG}}}, +#else + {{"chacha20_poly1305"}, {NULL}, 0, NO_FIPS_CIPHER | AEAD_CIPHER, {{0,0,0}}}, +#endif + +#if defined(HAVE_GCM) + {{"aes_gcm"}, {&EVP_aes_128_gcm}, 16, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_gcm"}, {&EVP_aes_192_gcm}, 24, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_gcm"}, {&EVP_aes_256_gcm}, 32, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_128_gcm"}, {&EVP_aes_128_gcm}, 16, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_192_gcm"}, {&EVP_aes_192_gcm}, 24, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_256_gcm"}, {&EVP_aes_256_gcm}, 32, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, +#else + {{"aes_gcm"}, {NULL}, 0, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_128_gcm"}, {NULL}, 16, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_192_gcm"}, {NULL}, 24, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_256_gcm"}, {NULL}, 32, AEAD_CIPHER, {{0,0,0}}}, +#endif + +#if defined(HAVE_CCM) + {{"aes_ccm"}, {&EVP_aes_128_ccm}, 16, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_ccm"}, {&EVP_aes_192_ccm}, 24, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_ccm"}, {&EVP_aes_256_ccm}, 32, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_128_ccm"}, {&EVP_aes_128_ccm}, 16, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_192_ccm"}, {&EVP_aes_192_ccm}, 24, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_256_ccm"}, {&EVP_aes_256_ccm}, 32, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, #else - {NULL} + {{"aes_ccm"}, {NULL}, 0, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_128_ccm"}, {NULL}, 16, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_192_ccm"}, {NULL}, 24, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_256_ccm"}, {NULL}, 32, AEAD_CIPHER, {{0,0,0}}}, +#endif + + /*==== Specialy handled ciphers, only for inclusion in algorithm's list ====*/ +#ifdef HAVE_AES_IGE + {{"aes_ige256"}, {NULL}, 0, NO_FIPS_CIPHER | NON_EVP_CIPHER}, #endif - ,0}, - {{"blowfish_cbc"}, {&EVP_bf_cbc}, 0}, - {{"blowfish_cfb64"}, {&EVP_bf_cfb64}, 0}, - {{"blowfish_ofb64"}, {&EVP_bf_ofb}, 0}, - {{"blowfish_ecb"}, {&EVP_bf_ecb}, 0}, - {{"aes_cbc"}, {&EVP_aes_128_cbc}, 16}, - {{"aes_cbc"}, {&EVP_aes_192_cbc}, 24}, - {{"aes_cbc"}, {&EVP_aes_256_cbc}, 32}, - {{"aes_cbc128"}, {&EVP_aes_128_cbc}, 0}, - {{"aes_cbc256"}, {&EVP_aes_256_cbc}, 0}, - {{"aes_cfb8"}, {&EVP_aes_128_cfb8}, 0}, - {{"aes_cfb128"}, {&EVP_aes_128_cfb128}, 0}, - {{"aes_ecb"}, {&EVP_aes_128_ecb}, 16}, - {{"aes_ecb"}, {&EVP_aes_192_ecb}, 24}, - {{"aes_ecb"}, {&EVP_aes_256_ecb}, 32}, - {{NULL},{NULL},0} + + /*==== End of list ==== */ + + {{NULL},{NULL},0,0} }; -#ifdef HAVE_EVP_AES_CTR ErlNifResourceType* evp_cipher_ctx_rtype; +static size_t num_cipher_types = 0; + static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) { if (ctx == NULL) return; @@ -80,46 +151,79 @@ static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) { if (ctx->ctx) EVP_CIPHER_CTX_free(ctx->ctx); } -#endif int init_cipher_ctx(ErlNifEnv *env) { -#ifdef HAVE_EVP_AES_CTR evp_cipher_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_CIPHER_CTX", (ErlNifResourceDtor*) evp_cipher_ctx_dtor, ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, NULL); if (evp_cipher_ctx_rtype == NULL) goto err; -#endif return 1; -#ifdef HAVE_EVP_AES_CTR err: PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_CIPHER_CTX'"); return 0; -#endif } void init_cipher_types(ErlNifEnv* env) { struct cipher_type_t* p = cipher_types; + num_cipher_types = 0; for (p = cipher_types; p->type.str; p++) { + num_cipher_types++; p->type.atom = enif_make_atom(env, p->type.str); if (p->cipher.funcp) p->cipher.p = p->cipher.funcp(); } p->type.atom = atom_false; /* end marker */ + + qsort(cipher_types, num_cipher_types, sizeof(cipher_types[0]), cmp_cipher_types); } -struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len) +const struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len) { - struct cipher_type_t* p = NULL; - for (p = cipher_types; p->type.atom != atom_false; p++) { - if (type == p->type.atom && (!p->key_len || key_len == p->key_len)) { - return p; - } + struct cipher_type_t key; + + key.type.atom = type; + key.key_len = key_len; + + return bsearch(&key, cipher_types, num_cipher_types, sizeof(cipher_types[0]), cmp_cipher_types); +} + + +int cmp_cipher_types(const void *keyp, const void *elemp) { + const struct cipher_type_t *key = keyp; + const struct cipher_type_t *elem = elemp; + + if (key->type.atom < elem->type.atom) return -1; + else if (key->type.atom > elem->type.atom) return 1; + else /* key->type.atom == elem->type.atom */ + if (!elem->key_len || key->key_len == elem->key_len) return 0; + else if (key->key_len < elem->key_len) return -1; + else return 1; +} + + +ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env) +{ + struct cipher_type_t* p; + ERL_NIF_TERM prev, hd; + + hd = enif_make_list(env, 0); + prev = atom_undefined; + + for (p = cipher_types; (p->type.atom & (p->type.atom != atom_false)); p++) { + if ((prev != p->type.atom) && + ((p->cipher.p != NULL) || + (p->flags & (NON_EVP_CIPHER|AES_CTR_COMPAT)) ) && /* Special handling. Bad indeed... */ + ! FORBIDDEN_IN_FIPS(p) + ) + hd = enif_make_list_cell(env, p->type.atom, hd); + prev = p->type.atom; } - return NULL; + + return hd; } diff --git a/lib/crypto/c_src/cipher.h b/lib/crypto/c_src/cipher.h index 3fb27f0ba3..6b43afea99 100644 --- a/lib/crypto/c_src/cipher.h +++ b/lib/crypto/c_src/cipher.h @@ -32,19 +32,42 @@ struct cipher_type_t { const EVP_CIPHER* (*funcp)(void); /* before init, NULL if notsup */ const EVP_CIPHER* p; /* after init, NULL if notsup */ }cipher; - const size_t key_len; /* != 0 to also match on key_len */ + size_t key_len; /* != 0 to also match on key_len */ + unsigned flags; + union { + struct aead_ctrl {int ctx_ctrl_set_ivlen, ctx_ctrl_get_tag, ctx_ctrl_set_tag;} aead; + } extra; }; -#ifdef HAVE_EVP_AES_CTR +/* masks in the flags field if cipher_type_t */ +#define NO_FIPS_CIPHER 1 +#define AES_CFBx 2 +#define ECB_BUG_0_9_8L 4 +#define AEAD_CIPHER 8 +#define NON_EVP_CIPHER 16 +#define AES_CTR_COMPAT 32 + + +#ifdef FIPS_SUPPORT +/* May have FIPS support, must check dynamically if it is enabled */ +# define FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_CIPHER) && FIPS_mode()) +#else +/* No FIPS support since the symbol FIPS_SUPPORT is undefined */ +# define FORBIDDEN_IN_FIPS(P) 0 +#endif + extern ErlNifResourceType* evp_cipher_ctx_rtype; struct evp_cipher_ctx { EVP_CIPHER_CTX* ctx; }; -#endif int init_cipher_ctx(ErlNifEnv *env); void init_cipher_types(ErlNifEnv* env); -struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len); +const struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len); + +int cmp_cipher_types(const void *keyp, const void *elemp); + +ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env); #endif /* E_CIPHER_H__ */ diff --git a/lib/crypto/c_src/cmac.c b/lib/crypto/c_src/cmac.c index 196b7476e3..49e67ccf29 100644 --- a/lib/crypto/c_src/cmac.c +++ b/lib/crypto/c_src/cmac.c @@ -24,7 +24,7 @@ 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 struct cipher_type_t *cipherp; const EVP_CIPHER *cipher; CMAC_CTX *ctx = NULL; ErlNifBinary key; @@ -40,9 +40,13 @@ ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto bad_arg; if ((cipherp = get_cipher_type(argv[0], key.size)) == NULL) goto bad_arg; + if (cipherp->flags & (NON_EVP_CIPHER | AEAD_CIPHER)) + goto bad_arg; if (!enif_inspect_iolist_as_binary(env, argv[2], &data)) goto bad_arg; + if (FORBIDDEN_IN_FIPS(cipherp)) + return enif_raise_exception(env, atom_notsup); if ((cipher = cipherp->cipher.p) == NULL) return enif_raise_exception(env, atom_notsup); diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 03f11c9059..06439c34b2 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -28,6 +28,7 @@ #include "aead.h" #include "aes.h" #include "algorithms.h" +#include "api_ng.h" #include "block.h" #include "bn.h" #include "chacha20.h" @@ -83,6 +84,9 @@ static ErlNifFunc nif_funcs[] = { {"aes_ctr_stream_init", 2, aes_ctr_stream_init, 0}, {"aes_ctr_stream_encrypt", 2, aes_ctr_stream_encrypt, 0}, {"aes_ctr_stream_decrypt", 2, aes_ctr_stream_encrypt, 0}, + {"ng_crypto_init_nif", 4, ng_crypto_init_nif, 0}, + {"ng_crypto_update_nif", 2, ng_crypto_update_nif, 0}, + {"ng_crypto_update_nif", 3, ng_crypto_update_nif, 0}, {"strong_rand_bytes_nif", 1, strong_rand_bytes_nif, 0}, {"strong_rand_range_nif", 1, strong_rand_range_nif, 0}, {"rand_uniform_nif", 2, rand_uniform_nif, 0}, @@ -132,7 +136,6 @@ static ErlNifFunc nif_funcs[] = { {"engine_get_id_nif", 1, engine_get_id_nif, 0}, {"engine_get_name_nif", 1, engine_get_name_nif, 0}, {"engine_get_all_methods_nif", 0, engine_get_all_methods_nif, 0} - }; ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload) diff --git a/lib/crypto/c_src/engine.c b/lib/crypto/c_src/engine.c index 6692ccd734..7ffbb9e70d 100644 --- a/lib/crypto/c_src/engine.c +++ b/lib/crypto/c_src/engine.c @@ -26,6 +26,9 @@ struct engine_ctx { char *id; }; +#define ERROR_Term(Env, ReasonTerm) enif_make_tuple2((Env), atom_error, (ReasonTerm)) +#define ERROR_Atom(Env, ReasonString) ERROR_Term((Env), enif_make_atom((Env),(ReasonString))) + static ErlNifResourceType* engine_ctx_rtype; static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i); @@ -136,7 +139,7 @@ ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ if ((engine = ENGINE_by_id(engine_id)) == NULL) { PRINTF_ERR0("engine_by_id_nif Leaved: {error, bad_engine_id}"); - ret = enif_make_tuple2(env, atom_error, atom_bad_engine_id); + ret = ERROR_Atom(env, "bad_engine_id"); goto done; } @@ -179,7 +182,7 @@ ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] goto bad_arg; if (!ENGINE_init(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_engine_init_failed); + return ERROR_Atom(env, "engine_init_failed"); return atom_ok; @@ -306,7 +309,7 @@ ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF goto done; cmd_failed: - ret = enif_make_tuple2(env, atom_error, atom_ctrl_cmd_failed); + ret = ERROR_Atom(env, "ctrl_cmd_failed"); done: if (cmds_loaded) { @@ -344,7 +347,7 @@ ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return enif_make_badarg(env); failed: - return enif_make_tuple2(env, atom_error, atom_add_engine_failed); + return ERROR_Atom(env, "add_engine_failed"); #else return atom_notsup; @@ -371,7 +374,7 @@ ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv return enif_make_badarg(env); failed: - return enif_make_tuple2(env, atom_error, atom_remove_engine_failed); + return ERROR_Atom(env, "remove_engine_failed"); #else return atom_notsup; #endif @@ -466,7 +469,7 @@ ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar break; #endif default: - return enif_make_tuple2(env, atom_error, atom_engine_method_not_supported); + return ERROR_Atom(env, "engine_method_not_supported"); } return atom_ok; @@ -475,7 +478,7 @@ ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar return enif_make_badarg(env); failed: - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + return ERROR_Atom(env, "register_engine_failed"); #else return atom_notsup; diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c index 4e76f817bc..567e8df08a 100644 --- a/lib/crypto/c_src/pkey.c +++ b/lib/crypto/c_src/pkey.c @@ -110,7 +110,7 @@ static int get_pkey_sign_digest(ErlNifEnv *env, ERL_NIF_TERM algorithm, goto bad_arg; if (tpl_terms[0] != atom_digest) goto bad_arg; - if (!enif_inspect_binary(env, tpl_terms[1], &tbs_bin)) + if (!enif_inspect_iolist_as_binary(env, tpl_terms[1], &tbs_bin)) goto bad_arg; if (tbs_bin.size > INT_MAX) goto bad_arg; @@ -123,14 +123,14 @@ static int get_pkey_sign_digest(ErlNifEnv *env, ERL_NIF_TERM algorithm, tbs = tbs_bin.data; tbslen = tbs_bin.size; } else if (md == NULL) { - if (!enif_inspect_binary(env, data, &tbs_bin)) + if (!enif_inspect_iolist_as_binary(env, data, &tbs_bin)) goto bad_arg; /* md == NULL, that is no hashing because DigestType argument was atom_none */ tbs = tbs_bin.data; tbslen = tbs_bin.size; } else { - if (!enif_inspect_binary(env, data, &tbs_bin)) + if (!enif_inspect_iolist_as_binary(env, data, &tbs_bin)) goto bad_arg; /* We have the cleartext in tbs_bin and the hash algo info in md */ diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index e0794a080e..83e10c4c78 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -193,10 +193,12 @@ Ciphers + +

Stream ciphers for - stream_encrypt/2 and - stream_decrypt/2 . + stream_init/3 and + stream_init/2 .

@@ -213,6 +215,18 @@ + + + + +

Names that are replaced by more common names. They may deprecated in futer releases.

+

des3_cbc and des_ede3 should be replaced by des_ede3_cbc

+

des_ede3_cbf, des3_cbf and des3_cfb should be replaced by des_ede3_cfb.

+

aes_cbc128 should be replaced by aes_128_cbc.

+

aes_cbc256 should be replaced by aes_256_cbc.

+
+
+ diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index de8cfac9a2..fe8390c5b8 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -47,6 +47,19 @@ -export([privkey_to_pubkey/2]). -export([ec_curve/1, ec_curves/0]). -export([rand_seed/1]). + +%% Experiment +-export([crypto_init/4, + crypto_update/2, crypto_update/3, + %% Emulates old api: + crypto_stream_init/2, crypto_stream_init/3, + crypto_stream_encrypt/2, + crypto_stream_decrypt/2, + crypto_block_encrypt/3, crypto_block_encrypt/4, + crypto_block_decrypt/3, crypto_block_decrypt/4 + ]). + + %% Engine -export([ engine_get_all_methods/0, @@ -89,7 +102,8 @@ -export_type([ stream_state/0, hmac_state/0, - hash_state/0 + hash_state/0, + crypto_state/0 ]). %% Private. For tests. @@ -261,17 +275,36 @@ %%% -type block_cipher_with_iv() :: cbc_cipher() | cfb_cipher() - | aes_cbc128 - | aes_cbc256 | aes_ige256 | blowfish_ofb64 - | des3_cbf % cfb misspelled - | des_ede3 | rc2_cbc . --type cbc_cipher() :: des_cbc | des3_cbc | aes_cbc | blowfish_cbc . --type aead_cipher() :: aes_gcm | aes_ccm | chacha20_poly1305 . --type cfb_cipher() :: aes_cfb128 | aes_cfb8 | blowfish_cfb64 | des3_cfb | des_cfb . +-type cbc_cipher() :: des_cbc | des_ede3_cbc + | blowfish_cbc + | aes_cbc | aes_128_cbc | aes_192_cbc | aes_256_cbc + | alias_cbc() . +-type alias_cbc() :: des3_cbc | des_ede3 + | aes_cbc128 | aes_cbc256 . + +-type aead_cipher() :: aes_gcm + | aes_128_gcm + | aes_192_gcm + | aes_256_gcm + | aes_ccm + | aes_128_ccm + | aes_192_ccm + | aes_256_ccm + | chacha20_poly1305 . + +-type cfb_cipher() :: aes_cfb8 + | aes_cfb128 + | blowfish_cfb64 + | des_cfb + | des_ede3_cfb + | alias_cfb() . +-type alias_cfb() :: des_ede3_cbf | des3_cbf + | des3_cfb . + -type block_cipher_without_iv() :: ecb_cipher() . -type ecb_cipher() :: des_ecb | blowfish_ecb | aes_ecb . @@ -342,7 +375,7 @@ stop() -> supports()-> {Hashs, PubKeys, Ciphers, Macs, Curves, RsaOpts} = algorithms(), [{hashs, Hashs}, - {ciphers, Ciphers}, + {ciphers, prepend_cipher_aliases(Ciphers)}, {public_keys, PubKeys}, {macs, Macs}, {curves, Curves}, @@ -473,7 +506,7 @@ hmac_final_n(Context, HashLen) -> Data :: iodata(), Mac :: binary(). cmac(Type, Key, Data) -> - notsup_to_error(cmac_nif(Type, Key, Data)). + notsup_to_error(cmac_nif(alias(Type), Key, Data)). -spec cmac(Type, Key, Data, MacLength) -> Mac when Type :: ?CMAC_CIPHER_ALGORITHM, @@ -481,8 +514,9 @@ cmac(Type, Key, Data) -> Data :: iodata(), MacLength :: integer(), Mac :: binary(). + cmac(Type, Key, Data, MacLength) -> - erlang:binary_part(cmac(Type, Key, Data), 0, MacLength). + erlang:binary_part(cmac(alias(Type), Key, Data), 0, MacLength). %%%---- POLY1305 @@ -499,91 +533,80 @@ poly1305(Key, Data) -> %%%---- Block ciphers +%%%---------------------------------------------------------------- -spec block_encrypt(Type::block_cipher_with_iv(), Key::key()|des3_key(), Ivec::binary(), PlainText::iodata()) -> binary(); (Type::aead_cipher(), Key::iodata(), Ivec::binary(), {AAD::binary(), PlainText::iodata()}) -> {binary(), binary()}; (aes_gcm | aes_ccm, Key::iodata(), Ivec::binary(), {AAD::binary(), PlainText::iodata(), TagLength::1..16}) -> {binary(), binary()}. -block_encrypt(Type, Key, Ivec, PlainText) when Type =:= des_cbc; - Type =:= des_cfb; - Type =:= blowfish_cbc; - Type =:= blowfish_cfb64; - Type =:= blowfish_ofb64; - Type =:= aes_cbc128; - Type =:= aes_cfb8; - Type =:= aes_cfb128; - Type =:= aes_cbc256; - Type =:= aes_cbc; - Type =:= rc2_cbc -> - block_crypt_nif(Type, Key, Ivec, PlainText, true); -block_encrypt(Type, Key0, Ivec, PlainText) when Type =:= des3_cbc; - Type =:= des_ede3 -> - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbc, Key, Ivec, PlainText, true); -block_encrypt(des3_cbf, Key0, Ivec, PlainText) -> % cfb misspelled - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbf, Key, Ivec, PlainText, true); -block_encrypt(des3_cfb, Key0, Ivec, PlainText) -> + +block_encrypt(Type, Key, Ivec, Data) -> + do_block_encrypt(alias(Type), Key, Ivec, Data). + +do_block_encrypt(Type, Key0, Ivec, Data) when Type =:= des_ede3_cbc; + Type =:= des_ede3_cfb -> Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cfb, Key, Ivec, PlainText, true); -block_encrypt(aes_ige256, Key, Ivec, PlainText) -> + block_crypt_nif(Type, Key, Ivec, Data, true); + +do_block_encrypt(Type, Key, Ivec, PlainText) when Type =:= aes_ige256 -> notsup_to_error(aes_ige_crypt_nif(Key, Ivec, PlainText, true)); -block_encrypt(Type, Key, Ivec, {AAD, PlainText}) when Type =:= aes_gcm; - Type =:= aes_ccm -> - aead_encrypt(Type, Key, Ivec, AAD, PlainText); -block_encrypt(Type, Key, Ivec, {AAD, PlainText, TagLength}) when Type =:= aes_gcm; - Type =:= aes_ccm -> - aead_encrypt(Type, Key, Ivec, AAD, PlainText, TagLength); -block_encrypt(chacha20_poly1305=Type, Key, Ivec, {AAD, PlainText}) -> - aead_encrypt(Type, Key, Ivec, AAD, PlainText, 16). +do_block_encrypt(Type, Key, Ivec, {AAD, PlainText}) when Type =:= chacha20_poly1305 -> + aead_encrypt(Type, Key, Ivec, AAD, PlainText, 16); +do_block_encrypt(Type, Key, Ivec, Data) when Type =:= aes_gcm; + Type =:= aes_ccm -> + case Data of + {AAD, PlainText} -> + aead_encrypt(Type, Key, Ivec, AAD, PlainText); + {AAD, PlainText, TagLength} -> + aead_encrypt(Type, Key, Ivec, AAD, PlainText, TagLength) + end; + +do_block_encrypt(Type, Key, Ivec, PlainText) -> + block_crypt_nif(Type, Key, Ivec, PlainText, true). + + + +-spec block_encrypt(Type::block_cipher_without_iv(), Key::key(), PlainText::iodata()) -> binary(). + +block_encrypt(Type, Key, PlainText) -> + block_crypt_nif(alias(Type), Key, PlainText, true). + +%%%---------------------------------------------------------------- +%%%---------------------------------------------------------------- -spec block_decrypt(Type::block_cipher_with_iv(), Key::key()|des3_key(), Ivec::binary(), Data::iodata()) -> binary(); (Type::aead_cipher(), Key::iodata(), Ivec::binary(), {AAD::binary(), Data::iodata(), Tag::binary()}) -> binary() | error. -block_decrypt(Type, Key, Ivec, Data) when Type =:= des_cbc; - Type =:= des_cfb; - Type =:= blowfish_cbc; - Type =:= blowfish_cfb64; - Type =:= blowfish_ofb64; - Type =:= aes_cbc; - Type =:= aes_cbc128; - Type =:= aes_cfb8; - Type =:= aes_cfb128; - Type =:= aes_cbc256; - Type =:= rc2_cbc -> - block_crypt_nif(Type, Key, Ivec, Data, false); -block_decrypt(Type, Key0, Ivec, Data) when Type =:= des3_cbc; - Type =:= des_ede3 -> - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbc, Key, Ivec, Data, false); -block_decrypt(des3_cbf, Key0, Ivec, Data) -> % cfb misspelled - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, false); -block_decrypt(des3_cfb, Key0, Ivec, Data) -> + +block_decrypt(Type, Key, Ivec, Data) -> + do_block_decrypt(alias(Type), Key, Ivec, Data). + +do_block_decrypt(Type, Key0, Ivec, Data) when Type =:= des_ede3_cbc; + Type =:= des_ede3_cfb -> Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, false); -block_decrypt(aes_ige256, Key, Ivec, Data) -> + block_crypt_nif(Type, Key, Ivec, Data, false); + +do_block_decrypt(aes_ige256, Key, Ivec, Data) -> notsup_to_error(aes_ige_crypt_nif(Key, Ivec, Data, false)); -block_decrypt(Type, Key, Ivec, {AAD, Data, Tag}) when Type =:= aes_gcm; + +do_block_decrypt(Type, Key, Ivec, {AAD, Data, Tag}) when Type =:= aes_gcm; Type =:= aes_ccm; Type =:= chacha20_poly1305 -> - aead_decrypt(Type, Key, Ivec, AAD, Data, Tag). - + aead_decrypt(Type, Key, Ivec, AAD, Data, Tag); --spec block_encrypt(Type::block_cipher_without_iv(), Key::key(), PlainText::iodata()) -> binary(). +do_block_decrypt(Type, Key, Ivec, Data) -> + block_crypt_nif(Type, Key, Ivec, Data, false). -block_encrypt(Type, Key, PlainText) -> - block_crypt_nif(Type, Key, PlainText, true). -spec block_decrypt(Type::block_cipher_without_iv(), Key::key(), Data::iodata()) -> binary(). block_decrypt(Type, Key, Data) -> - block_crypt_nif(Type, Key, Data, false). - + block_crypt_nif(alias(Type), Key, Data, false). +%%%---------------------------------------------------------------- -spec next_iv(Type:: cbc_cipher(), Data) -> NextIVec when % Type :: cbc_cipher(), %des_cbc | des3_cbc | aes_cbc | aes_ige, Data :: iodata(), NextIVec :: binary(). @@ -614,18 +637,30 @@ next_iv(Type, Data, _Ivec) -> -opaque stream_state() :: {stream_cipher(), reference()}. --type stream_cipher() :: rc4 | aes_ctr | chacha20 . +-type stream_cipher() :: stream_cipher_iv() | stream_cipher_no_iv() . +-type stream_cipher_no_iv() :: rc4 . +-type stream_cipher_iv() :: aes_ctr + | aes_128_ctr + | aes_192_ctr + | aes_256_ctr + | chacha20 . --spec stream_init(Type, Key, IVec) -> State when Type :: aes_ctr | chacha20, +-spec stream_init(Type, Key, IVec) -> State when Type :: stream_cipher_iv(), Key :: iodata(), IVec :: binary(), State :: stream_state() . stream_init(aes_ctr, Key, Ivec) -> {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; +stream_init(aes_128_ctr, Key, Ivec) -> + {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; +stream_init(aes_192_ctr, Key, Ivec) -> + {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; +stream_init(aes_256_ctr, Key, Ivec) -> + {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; stream_init(chacha20, Key, Ivec) -> {chacha20, chacha20_stream_init(Key,Ivec)}. --spec stream_init(Type, Key) -> State when Type :: rc4, +-spec stream_init(Type, Key) -> State when Type :: stream_cipher_no_iv(), Key :: iodata(), State :: stream_state() . stream_init(rc4, Key) -> @@ -932,7 +967,7 @@ rand_seed_nif(_Seed) -> ?nif_stub. DigestType :: rsa_digest_type() | dss_digest_type() | ecdsa_digest_type(), - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Key :: rsa_private() | dss_private() | [ecdsa_private() | ecdsa_params()] @@ -951,7 +986,7 @@ sign(Algorithm, Type, Data, Key) -> | dss_digest_type() | ecdsa_digest_type() | none, - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Key :: rsa_private() | dss_private() | [ecdsa_private() | ecdsa_params()] @@ -980,7 +1015,7 @@ pkey_sign_nif(_Algorithm, _Type, _Digest, _Key, _Options) -> ?nif_stub. | dss_digest_type() | ecdsa_digest_type() | none, - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Signature :: binary(), Key :: rsa_public() | dss_public() @@ -998,7 +1033,7 @@ verify(Algorithm, Type, Data, Signature, Key) -> DigestType :: rsa_digest_type() | dss_digest_type() | ecdsa_digest_type(), - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Signature :: binary(), Key :: rsa_public() | dss_public() @@ -2180,3 +2215,178 @@ check_otp_test_engine(LibDir) -> {error, notexist} end end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% +%%% Experimental NG +%%% + +%%% -> {ok,State::ref()} | {error,Reason} + +-opaque crypto_state() :: reference() | {any(),any(),any(),any()}. + + +%%%---------------------------------------------------------------- +%%% +%%% Create and initialize a new state for encryption or decryption +%%% + +-spec crypto_init(Cipher, Key, IV, EncryptFlag) -> {ok,State} | {error,term()} | undefined + when Cipher :: stream_cipher() + | block_cipher_with_iv() + | block_cipher_without_iv() , + Key :: iodata(), + IV :: binary(), + EncryptFlag :: boolean() | undefined, + State :: crypto_state() . + +crypto_init(Cipher, Key, IV, EncryptFlag) when is_atom(Cipher), + is_binary(Key), + is_binary(IV), + is_atom(EncryptFlag) -> + case ng_crypto_init_nif(alias(Cipher), Key, IV, EncryptFlag) of + {error,Error} -> + {error,Error}; + undefined -> % For compatibility function crypto_stream_init/3 + undefined; + Ref when is_reference(Ref) -> + {ok,Ref}; + State when is_tuple(State), + size(State)==4 -> + {ok,State} % compatibility with old cryptolibs < 1.0.1 + end. + + +%%%---------------------------------------------------------------- +%%% +%%% Encrypt/decrypt a sequence of bytes. The sum of the sizes +%%% of all blocks must be an integer multiple of the crypto's +%%% blocksize. +%%% + +-spec crypto_update(State, Data) -> {ok,Result} | {error,term()} + when State :: crypto_state(), + Data :: iodata(), + Result :: binary() | {crypto_state(),binary()}. +crypto_update(State, Data) -> + mk_ret(ng_crypto_update_nif(State, Data)). + +%%%---------------------------------------------------------------- +%%% +%%% Encrypt/decrypt a sequence of bytes but change the IV first. +%%% Not applicable for all modes. +%%% + +-spec crypto_update(State, Data, IV) -> {ok,Result} | {error,term()} + when State :: crypto_state(), + Data :: iodata(), + IV :: binary(), + Result :: binary() | {crypto_state(),binary()}. +crypto_update(State, Data, IV) -> + mk_ret(ng_crypto_update_nif(State, Data, IV)). + +%%%---------------------------------------------------------------- +%%% Helpers +mk_ret(R) -> mk_ret(R, []). + +mk_ret({error,Error}, _) -> + {error,Error}; +mk_ret(Bin, Acc) when is_binary(Bin) -> + {ok, iolist_to_binary(lists:reverse([Bin|Acc]))}; +mk_ret({State1,Bin}, Acc) when is_tuple(State1), + size(State1) == 4, + is_binary(Bin) -> + %% compatibility with old cryptolibs < 1.0.1 + {ok, {State1, iolist_to_binary(lists:reverse([Bin|Acc]))}}. + +%%%---------------------------------------------------------------- +%%% NIFs +ng_crypto_init_nif(_Cipher, _Key, _IVec, _EncryptFlg) -> ?nif_stub. +ng_crypto_update_nif(_State, _Data) -> ?nif_stub. +ng_crypto_update_nif(_State, _Data, _IV) -> ?nif_stub. + +%%%================================================================ +%%% Compatibility functions to be called by "old" api functions. + +%%%-------------------------------- +%%%---- block encrypt/decrypt +crypto_block_encrypt(Cipher, Key, Data) -> crypto_block_encrypt(Cipher, Key, <<>>, Data). +crypto_block_decrypt(Cipher, Key, Data) -> crypto_block_decrypt(Cipher, Key, <<>>, Data). + +crypto_block_encrypt(Cipher, Key, Ivec, Data) -> crypto_block(Cipher, Key, Ivec, Data, true). +crypto_block_decrypt(Cipher, Key, Ivec, Data) -> crypto_block(Cipher, Key, Ivec, Data, false). + +%% AEAD: use old funcs + +%%%---- helper +crypto_block(Cipher, Key, IV, Data, EncryptFlag) -> + case crypto_init(Cipher, iolist_to_binary(Key), iolist_to_binary(IV), EncryptFlag) of + {ok, Ref} -> + case crypto_update(Ref, Data) of + {ok, {_,Bin}} when is_binary(Bin) -> Bin; + {ok, Bin} when is_binary(Bin) -> Bin; + {error,_} -> error(badarg) + end; + + {error,_} -> error(badarg) + end. + +%%%-------------------------------- +%%%---- stream init, encrypt/decrypt + +crypto_stream_init(Cipher, Key) -> + crypto_stream_init(Cipher, Key, <<>>). + +crypto_stream_init(Cipher, Key0, IV0) -> + Key = iolist_to_binary(Key0), + IV = iolist_to_binary(IV0), + %% First check the argumensts: + case crypto_init(Cipher, Key, IV, undefined) of + undefined -> + {Cipher, {Key, IV}}; + {error,_} -> + {error,badarg} + end. + +crypto_stream_encrypt(State, PlainText) -> + crypto_stream_emulate(State, PlainText, true). + +crypto_stream_decrypt(State, CryptoText) -> + crypto_stream_emulate(State, CryptoText, false). + + +%%%---- helper +crypto_stream_emulate({Cipher,{Key,IV}}, Data, EncryptFlag) -> + case crypto_init(Cipher, Key, IV, EncryptFlag) of + {ok,State} -> + crypto_stream_emulate({Cipher,State}, Data, EncryptFlag); + {error,_} -> + error(badarg) + end; +crypto_stream_emulate({Cipher,State}, Data, _) -> + case crypto_update(State, Data) of + {ok, {State1,Bin}} when is_binary(Bin) -> {{Cipher,State1},Bin}; + {ok,Bin} when is_binary(Bin) -> {{Cipher,State},Bin}; + {error,_} -> error(badarg) + end. + + +%%%================================================================ + +prepend_cipher_aliases(L) -> + [des3_cbc, des_ede3, des_ede3_cbf, des3_cbf, des3_cfb, aes_cbc128, aes_cbc256 | L]. + + +%%%---- des_ede3_cbc +alias(des3_cbc) -> des_ede3_cbc; +alias(des_ede3) -> des_ede3_cbc; +%%%---- des_ede3_cfb +alias(des_ede3_cbf) -> des_ede3_cfb; +alias(des3_cbf) -> des_ede3_cfb; +alias(des3_cfb) -> des_ede3_cfb; +%%%---- aes_*_cbc +alias(aes_cbc128) -> aes_128_cbc; +alias(aes_cbc256) -> aes_256_cbc; + +alias(Alg) -> Alg. -- cgit v1.2.3