aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--OTP_VERSION2
-rw-r--r--erts/emulator/beam/erl_process.c2
-rw-r--r--erts/emulator/hipe/hipe_amd64_bifs.m437
-rw-r--r--erts/emulator/hipe/hipe_bif0.tab1
-rw-r--r--erts/emulator/hipe/hipe_bif_list.m45
-rw-r--r--erts/emulator/hipe/hipe_debug.c8
-rw-r--r--erts/emulator/hipe/hipe_native_bif.c6
-rw-r--r--erts/emulator/hipe/hipe_native_bif.h2
-rw-r--r--erts/emulator/hipe/hipe_primops.h1
-rw-r--r--erts/emulator/test/process_SUITE.erl13
-rw-r--r--lib/crypto/c_src/Makefile.in25
-rw-r--r--lib/crypto/c_src/crypto.c1018
-rw-r--r--lib/crypto/c_src/otp_test_engine.c262
-rw-r--r--lib/crypto/doc/src/Makefile20
-rw-r--r--lib/crypto/doc/src/crypto.xml209
-rw-r--r--lib/crypto/doc/src/engine_load.xml110
-rw-r--r--lib/crypto/doc/src/usersguide.xml6
-rw-r--r--lib/crypto/src/Makefile18
-rw-r--r--lib/crypto/src/crypto.erl316
-rw-r--r--lib/crypto/test/Makefile7
-rw-r--r--lib/crypto/test/crypto_SUITE.erl2
-rw-r--r--lib/crypto/test/engine_SUITE.erl513
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/dsa_private_key.pem9
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/dsa_public_key.pem12
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_private_key.pem8
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_public_key.pem6
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key.pem28
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key_pwd.pem30
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key.pem9
-rw-r--r--lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key_pwd.pem9
-rw-r--r--lib/hipe/doc/src/hipe_app.xml66
-rw-r--r--lib/hipe/main/hipe.app.src2
-rw-r--r--lib/hipe/rtl/hipe_rtl_binary_construct.erl9
-rw-r--r--lib/inets/doc/src/notes.xml34
-rw-r--r--lib/inets/src/http_client/httpc_response.erl40
-rw-r--r--lib/inets/src/http_server/httpd_esi.erl25
-rw-r--r--lib/inets/src/http_server/httpd_example.erl5
-rw-r--r--lib/inets/src/http_server/mod_esi.erl88
-rw-r--r--lib/inets/test/http_format_SUITE.erl2
-rw-r--r--lib/inets/test/httpc_SUITE.erl24
-rw-r--r--lib/inets/test/httpd_mod.erl7
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/observer/src/cdv_bin_cb.erl2
-rw-r--r--lib/observer/src/crashdump_viewer.erl94
-rw-r--r--lib/observer/src/observer_html_lib.erl6
-rw-r--r--lib/observer/src/observer_lib.erl8
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl99
-rw-r--r--lib/ssh/doc/src/ssh_client_key_api.xml33
-rw-r--r--lib/ssh/doc/src/ssh_server_key_api.xml28
-rw-r--r--lib/ssh/src/ssh.erl6
-rw-r--r--lib/ssh/src/ssh_auth.erl5
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl58
-rw-r--r--lib/ssh/src/ssh_transport.erl45
-rw-r--r--lib/ssh/test/Makefile2
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl9
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl62
-rw-r--r--lib/ssh/test/ssh_engine_SUITE.erl141
-rw-r--r--lib/ssh/test/ssh_engine_SUITE_data/dsa_private_key.pem9
-rw-r--r--lib/ssh/test/ssh_engine_SUITE_data/ecdsa_private_key.pem8
-rw-r--r--lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key.pem28
-rw-r--r--lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key_pwd.pem30
-rw-r--r--lib/ssh/test/ssh_key_cb_engine_keys.erl62
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl5
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl16
-rw-r--r--otp_versions.table1
65 files changed, 3411 insertions, 344 deletions
diff --git a/OTP_VERSION b/OTP_VERSION
index 42ee6ac80e..286ad61f58 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-20.1.3
+20.1.4
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 1ce2b5071c..1f8a756305 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -11591,7 +11591,7 @@ request_system_task(Process *c_p, Eterm requester, Eterm target,
goto badarg;
req_type = tp[1];
req_id = tp[2];
- req_id_sz = is_immed(req_id) ? req_id : size_object(req_id);
+ req_id_sz = is_immed(req_id) ? 0 : size_object(req_id);
tot_sz = req_id_sz;
for (i = 0; i < ERTS_MAX_PROC_SYS_TASK_ARGS; i++) {
int tix = 3 + i;
diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4
index dca3887564..aff10f1528 100644
--- a/erts/emulator/hipe/hipe_amd64_bifs.m4
+++ b/erts/emulator/hipe/hipe_amd64_bifs.m4
@@ -463,6 +463,43 @@ ASYM($1):
#endif')
/*
+ * nogc_bif_interface_1(nbif_name, cbif_name)
+ *
+ * Generate native interface for a bif with implicit P
+ * The bif can fail but cannot do GC.
+ */
+
+define(nogc_bif_interface_1,
+`
+#ifndef HAVE_$1
+#`define' HAVE_$1
+ TEXT
+ .align 4
+ GLOBAL(ASYM($1))
+ASYM($1):
+ /* set up the parameters */
+ movq P, %rdi
+ NBIF_ARG(%rsi,1,0)
+
+ /* make the call on the C stack */
+ SWITCH_ERLANG_TO_C
+ pushq %rsi
+ movq %rsp, %rsi /* Eterm* BIF__ARGS */
+ sub $(8), %rsp /* stack frame 16-byte alignment */
+ CALL_BIF($2)
+ add $(1*8 + 8), %rsp
+ SWITCH_C_TO_ERLANG
+
+ /* throw exception if failure, otherwise return */
+ TEST_GOT_EXN
+ jz nbif_1_simple_exception
+ NBIF_RET(1)
+ SET_SIZE(ASYM($1))
+ TYPE_FUNCTION(ASYM($1))
+#endif')
+
+
+/*
* noproc_primop_interface_0(nbif_name, cbif_name)
* noproc_primop_interface_1(nbif_name, cbif_name)
* noproc_primop_interface_2(nbif_name, cbif_name)
diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab
index 4038ca7ef8..4f73770d24 100644
--- a/erts/emulator/hipe/hipe_bif0.tab
+++ b/erts/emulator/hipe/hipe_bif0.tab
@@ -140,3 +140,4 @@ atom bs_validate_unicode_retract
atom emulate_fpe
atom emasculate_binary
atom is_divisible
+atom is_unicode \ No newline at end of file
diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4
index f034c4700c..b86f2dafdc 100644
--- a/erts/emulator/hipe/hipe_bif_list.m4
+++ b/erts/emulator/hipe/hipe_bif_list.m4
@@ -223,6 +223,7 @@ standard_bif_interface_3(nbif_find_na_or_make_stub, hipe_find_na_or_make_stub)
standard_bif_interface_2(nbif_nonclosure_address, hipe_nonclosure_address)
nocons_nofail_primop_interface_0(nbif_fclearerror_error, hipe_fclearerror_error)
standard_bif_interface_2(nbif_is_divisible, hipe_is_divisible)
+noproc_primop_interface_1(nbif_is_unicode, hipe_is_unicode)
/*
* Mbox primops with implicit P parameter.
@@ -247,7 +248,11 @@ nofail_primop_interface_3(nbif_bs_get_float_2, erts_bs_get_float_2)
standard_bif_interface_3(nbif_bs_put_utf8, hipe_bs_put_utf8)
standard_bif_interface_3(nbif_bs_put_utf16be, hipe_bs_put_utf16be)
standard_bif_interface_3(nbif_bs_put_utf16le, hipe_bs_put_utf16le)
+ifdef(`nogc_bif_interface_1',`
+nogc_bif_interface_1(nbif_bs_validate_unicode, hipe_bs_validate_unicode)
+',`
standard_bif_interface_1(nbif_bs_validate_unicode, hipe_bs_validate_unicode)
+')
/*
* Bit-syntax primops without any P parameter.
diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c
index 222a11db3d..cfe60b379e 100644
--- a/erts/emulator/hipe/hipe_debug.c
+++ b/erts/emulator/hipe/hipe_debug.c
@@ -135,7 +135,9 @@ static void print_heap(Eterm *pos, Eterm *end)
printf("From: 0x%0*lx to 0x%0*lx\n\r",
2*(int)sizeof(long), (unsigned long)pos,
2*(int)sizeof(long), (unsigned long)end);
- printf(" | H E A P |\r\n");
+ printf(" | %*s H E A P %*s |\r\n",
+ 2*(int)sizeof(long)-1, "",
+ 2*(int)sizeof(long)-1, "");
printf(" | %*s | %*s |\r\n",
2+2*(int)sizeof(long), "Address",
2+2*(int)sizeof(long), "Contents");
@@ -158,8 +160,10 @@ static void print_heap(Eterm *pos, Eterm *end)
++pos;
--ari;
}
- } else
+ } else {
+ fflush(stdout);
erts_printf("%.30T", val);
+ }
printf("\r\n");
}
printf(" |%s|%s|\r\n", dashes, dashes);
diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c
index d8044fe6da..e1c22701d0 100644
--- a/erts/emulator/hipe/hipe_native_bif.c
+++ b/erts/emulator/hipe/hipe_native_bif.c
@@ -495,6 +495,12 @@ BIF_RETTYPE nbif_impl_hipe_bs_validate_unicode(NBIF_ALIST_1)
return NIL;
}
+Uint hipe_is_unicode(Eterm arg)
+{
+ return (Uint) validate_unicode(arg);
+}
+
+
int hipe_bs_validate_unicode_retract(ErlBinMatchBuffer* mb, Eterm arg)
{
if (!validate_unicode(arg)) {
diff --git a/erts/emulator/hipe/hipe_native_bif.h b/erts/emulator/hipe/hipe_native_bif.h
index 38f874888b..1127d4ac56 100644
--- a/erts/emulator/hipe/hipe_native_bif.h
+++ b/erts/emulator/hipe/hipe_native_bif.h
@@ -67,6 +67,7 @@ AEXTERN(Eterm,nbif_bs_put_utf16be,(Process*,Eterm,byte*,unsigned int));
AEXTERN(Eterm,nbif_bs_put_utf16le,(Process*,Eterm,byte*,unsigned int));
AEXTERN(Eterm,nbif_bs_get_utf16,(void));
AEXTERN(Eterm,nbif_bs_validate_unicode,(Process*,Eterm));
+AEXTERN(Uint,nbif_is_unicode,(Eterm));
AEXTERN(Eterm,nbif_bs_validate_unicode_retract,(void));
AEXTERN(void,nbif_is_divisible,(Process*,Uint,Uint));
@@ -92,6 +93,7 @@ Eterm hipe_bs_utf16_size(Eterm);
BIF_RETTYPE nbif_impl_hipe_bs_put_utf16be(NBIF_ALIST_3);
BIF_RETTYPE nbif_impl_hipe_bs_put_utf16le(NBIF_ALIST_3);
BIF_RETTYPE nbif_impl_hipe_bs_validate_unicode(NBIF_ALIST_1);
+Uint hipe_is_unicode(Eterm);
struct erl_bin_match_buffer;
int hipe_bs_validate_unicode_retract(struct erl_bin_match_buffer*, Eterm);
BIF_RETTYPE nbif_impl_hipe_is_divisible(NBIF_ALIST_2);
diff --git a/erts/emulator/hipe/hipe_primops.h b/erts/emulator/hipe/hipe_primops.h
index 4fcbc9df38..77f0dfe7e5 100644
--- a/erts/emulator/hipe/hipe_primops.h
+++ b/erts/emulator/hipe/hipe_primops.h
@@ -66,6 +66,7 @@ PRIMOP_LIST(am_bs_put_utf16be, &nbif_bs_put_utf16be)
PRIMOP_LIST(am_bs_put_utf16le, &nbif_bs_put_utf16le)
PRIMOP_LIST(am_bs_get_utf16, &nbif_bs_get_utf16)
PRIMOP_LIST(am_bs_validate_unicode, &nbif_bs_validate_unicode)
+PRIMOP_LIST(am_is_unicode, &nbif_is_unicode)
PRIMOP_LIST(am_bs_validate_unicode_retract, &nbif_bs_validate_unicode_retract)
PRIMOP_LIST(am_is_divisible, &nbif_is_divisible)
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index a9f20f9928..a8bcfac84d 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -2532,8 +2532,13 @@ system_task_on_suspended(Config) when is_list(Config) ->
end.
gc_request_when_gc_disabled(Config) when is_list(Config) ->
- Master = self(),
AIS = erts_debug:set_internal_state(available_internal_state, true),
+ gc_request_when_gc_disabled_do(ref),
+ gc_request_when_gc_disabled_do(immed),
+ erts_debug:set_internal_state(available_internal_state, AIS).
+
+gc_request_when_gc_disabled_do(ReqIdType) ->
+ Master = self(),
{P, M} = spawn_opt(fun () ->
true = erts_debug:set_internal_state(gc_state,
false),
@@ -2545,7 +2550,10 @@ gc_request_when_gc_disabled(Config) when is_list(Config) ->
receive after 100 -> ok end
end, [monitor, link]),
receive {P, gc_state, false} -> ok end,
- ReqId = make_ref(),
+ ReqId = case ReqIdType of
+ ref -> make_ref();
+ immed -> immed
+ end,
async = garbage_collect(P, [{async, ReqId}]),
receive
{garbage_collect, ReqId, Result} ->
@@ -2554,7 +2562,6 @@ gc_request_when_gc_disabled(Config) when is_list(Config) ->
ok
end,
receive {garbage_collect, ReqId, true} -> ok end,
- erts_debug:set_internal_state(available_internal_state, AIS),
receive {'DOWN', M, process, P, _Reason} -> ok end,
ok.
diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in
index af7c209c75..31124ba477 100644
--- a/lib/crypto/c_src/Makefile.in
+++ b/lib/crypto/c_src/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2016. All Rights Reserved.
+# Copyright Ericsson AB 1999-2017. 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.
@@ -78,12 +78,16 @@ CRYPTO_STATIC_OBJS = $(OBJDIR)/crypto_static$(TYPEMARKER).o\
NIF_ARCHIVE = $(LIBDIR)/crypto$(TYPEMARKER).a
+TEST_ENGINE_OBJS = $(OBJDIR)/otp_test_engine$(TYPEMARKER).o
+
ifeq ($(findstring win32,$(TARGET)), win32)
NIF_LIB = $(LIBDIR)/crypto$(TYPEMARKER).dll
CALLBACK_LIB = $(LIBDIR)/crypto_callback$(TYPEMARKER).dll
+TEST_ENGINE_LIB = $(LIBDIR)/otp_test_engine$(TYPEMARKER).dll
else
NIF_LIB = $(LIBDIR)/crypto$(TYPEMARKER).so
CALLBACK_LIB = $(LIBDIR)/crypto_callback$(TYPEMARKER).so
+TEST_ENGINE_LIB = $(LIBDIR)/otp_test_engine$(TYPEMARKER).so
endif
ifeq ($(HOST_OS),)
@@ -129,10 +133,22 @@ ALL_STATIC_CFLAGS = $(DED_STATIC_CFLAGS) $(INCLUDES)
_create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR))
-debug opt valgrind: $(NIF_LIB) $(CALLBACK_LIB)
+debug opt valgrind: $(NIF_LIB) $(CALLBACK_LIB) $(TEST_ENGINE_LIB)
static_lib: $(NIF_ARCHIVE)
+$(OBJDIR)/otp_test_engine$(TYPEMARKER).o: otp_test_engine.c
+ $(V_at)$(INSTALL_DIR) $(OBJDIR)
+ $(V_CC) -c -o $@ $(ALL_CFLAGS) $<
+
+$(LIBDIR)/otp_test_engine$(TYPEMARKER).so: $(TEST_ENGINE_OBJS)
+ $(V_at)$(INSTALL_DIR) $(LIBDIR)
+ $(V_LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(CRYPTO_LINK_LIB)
+
+$(LIBDIR)/otp_test_engine$(TYPEMARKER).dll: $(TEST_ENGINE_OBJS)
+ $(V_at)$(INSTALL_DIR) $(LIBDIR)
+ $(V_LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(TEST_ENGINE_OBJS) -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME)
+
$(OBJDIR)/%$(TYPEMARKER).o: %.c
$(V_at)$(INSTALL_DIR) $(OBJDIR)
$(V_CC) -c -o $@ $(ALL_CFLAGS) $<
@@ -170,6 +186,7 @@ ifeq ($(findstring win32,$(TARGET)), win32)
rm -f $(LIBDIR)/crypto.debug.dll
rm -f $(LIBDIR)/crypto_callback.dll
rm -f $(LIBDIR)/crypto_callback.debug.dll
+ rm -f $(LIBDIR)/otp_test_engine.dll
else
rm -f $(LIBDIR)/crypto.so
rm -f $(LIBDIR)/crypto.debug.so
@@ -177,6 +194,7 @@ else
rm -f $(LIBDIR)/crypto_callback.so
rm -f $(LIBDIR)/crypto_callback.debug.so
rm -f $(LIBDIR)/crypto_callback.valgrind.so
+ rm -f $(LIBDIR)/otp_test_engine.so
endif
rm -f $(OBJDIR)/crypto.o
rm -f $(OBJDIR)/crypto_static.o
@@ -187,6 +205,7 @@ endif
rm -f $(OBJDIR)/crypto_callback.o
rm -f $(OBJDIR)/crypto_callback.debug.o
rm -f $(OBJDIR)/crypto_callback.valgrind.o
+ rm -f $(OBJDIR)/otp_test_engine.o
rm -f core *~
docs:
@@ -206,6 +225,8 @@ ifeq ($(DYNAMIC_CRYPTO_LIB),yes)
$(INSTALL_PROGRAM) $(CALLBACK_OBJS) "$(RELSYSDIR)/priv/obj"
$(INSTALL_PROGRAM) $(CALLBACK_LIB) "$(RELSYSDIR)/priv/lib"
endif
+ $(INSTALL_PROGRAM) $(TEST_ENGINE_OBJS) "$(RELSYSDIR)/priv/obj"
+ $(INSTALL_PROGRAM) $(TEST_ENGINE_LIB) "$(RELSYSDIR)/priv/lib"
release_docs_spec:
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 53fe233790..b29c5082ba 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -1,4 +1,4 @@
-/*
+/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2010-2017. All Rights Reserved.
@@ -19,8 +19,8 @@
*/
/*
- * Purpose: Dynamically loadable NIF library for cryptography.
- * Based on OpenSSL.
+ * Purpose: Dynamically loadable NIF library for cryptography.
+ * Based on OpenSSL.
*/
#ifdef __WIN32__
@@ -60,6 +60,8 @@
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
/* Helper macro to construct a OPENSSL_VERSION_NUMBER.
* See openssl/opensslv.h
@@ -79,9 +81,9 @@
*
* Therefor works tests like this as intendend:
* OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0)
- * (The test is for example "2.4.2" >= "1.0.0" although the test
+ * (The test is for example "2.4.2" >= "1.0.0" although the test
* with the cloned OpenSSL test would be "1.0.1" >= "1.0.0")
- *
+ *
* But tests like this gives wrong result:
* OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
* (The test is false since "2.4.2" < "1.1.0". It should have been
@@ -119,6 +121,10 @@
#include <openssl/modes.h>
#endif
+#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'h')
+#define HAS_ENGINE_SUPPORT
+#endif
+
#include "crypto_callback.h"
#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \
@@ -240,7 +246,7 @@
/* This shall correspond to the similar macro in crypto.erl */
/* Current value is: erlang:system_info(context_reductions) * 10 */
-#define MAX_BYTES_TO_NIF 20000
+#define MAX_BYTES_TO_NIF 20000
#define CONSUME_REDS(NifEnv, Ibin) \
do { \
@@ -277,7 +283,7 @@ static HMAC_CTX *HMAC_CTX_new()
static void HMAC_CTX_free(HMAC_CTX *ctx)
{
HMAC_CTX_cleanup(ctx);
- return CRYPTO_free(ctx);
+ CRYPTO_free(ctx);
}
#define EVP_MD_CTX_new() EVP_MD_CTX_create()
@@ -342,6 +348,10 @@ static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const
static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key);
static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g);
+static INLINE void DSA_get0_pqg(const DSA *dsa,
+ const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
+static INLINE void DSA_get0_key(const DSA *dsa,
+ const BIGNUM **pub_key, const BIGNUM **priv_key);
static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
{
@@ -358,6 +368,23 @@ static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
return 1;
}
+static INLINE void
+DSA_get0_pqg(const DSA *dsa, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
+{
+ *p = dsa->p;
+ *q = dsa->q;
+ *g = dsa->g;
+}
+
+static INLINE void
+DSA_get0_key(const DSA *dsa, const BIGNUM **pub_key, const BIGNUM **priv_key)
+{
+ if (pub_key) *pub_key = dsa->pub_key;
+ if (priv_key) *priv_key = dsa->priv_key;
+}
+
+
+
static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key);
static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g);
static INLINE int DH_set_length(DH *dh, long length);
@@ -387,6 +414,8 @@ static INLINE int DH_set_length(DH *dh, long length)
return 1;
}
+
+
static INLINE void
DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
{
@@ -398,8 +427,8 @@ DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
static INLINE void
DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
{
- *pub_key = dh->pub_key;
- *priv_key = dh->priv_key;
+ if (pub_key) *pub_key = dh->pub_key;
+ if (priv_key) *priv_key = dh->priv_key;
}
#else /* End of compatibility definitions. */
@@ -448,6 +477,7 @@ static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const E
static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM srp_user_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM srp_host_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -466,6 +496,22 @@ static ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_N
static ERL_NIF_TERM chacha20_poly1305_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM chacha20_poly1305_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i);
+static ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
/* helpers */
static void init_algorithms_types(ErlNifEnv*);
static void init_digest_types(ErlNifEnv* env);
@@ -477,6 +523,10 @@ static int term2point(ErlNifEnv* env, ERL_NIF_TERM term,
#endif
static ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn);
+#ifdef HAS_ENGINE_SUPPORT
+static int zero_terminate(ErlNifBinary bin, char **buf);
+#endif
+
static int library_refc = 0; /* number of users of this dynamic library */
static ErlNifFunc nif_funcs[] = {
@@ -516,6 +566,7 @@ static ErlNifFunc nif_funcs[] = {
{"dh_check", 1, dh_check},
{"dh_generate_key_nif", 4, dh_generate_key_nif},
{"dh_compute_key_nif", 3, dh_compute_key_nif},
+ {"privkey_to_pubkey_nif", 2, privkey_to_pubkey_nif},
{"srp_value_B_nif", 5, srp_value_B_nif},
{"srp_user_secret_nif", 7, srp_user_secret_nif},
{"srp_host_secret_nif", 5, srp_host_secret_nif},
@@ -529,12 +580,27 @@ static ErlNifFunc nif_funcs[] = {
{"aes_gcm_decrypt", 5, aes_gcm_decrypt},
{"chacha20_poly1305_encrypt", 4, chacha20_poly1305_encrypt},
- {"chacha20_poly1305_decrypt", 5, chacha20_poly1305_decrypt}
+ {"chacha20_poly1305_decrypt", 5, chacha20_poly1305_decrypt},
+
+ {"engine_by_id_nif", 1, engine_by_id_nif},
+ {"engine_init_nif", 1, engine_init_nif},
+ {"engine_finish_nif", 1, engine_finish_nif},
+ {"engine_free_nif", 1, engine_free_nif},
+ {"engine_load_dynamic_nif", 0, engine_load_dynamic_nif},
+ {"engine_ctrl_cmd_strings_nif", 2, engine_ctrl_cmd_strings_nif},
+ {"engine_register_nif", 2, engine_register_nif},
+ {"engine_unregister_nif", 2, engine_unregister_nif},
+ {"engine_add_nif", 1, engine_add_nif},
+ {"engine_remove_nif", 1, engine_remove_nif},
+ {"engine_get_first_nif", 0, engine_get_first_nif},
+ {"engine_get_next_nif", 1, engine_get_next_nif},
+ {"engine_get_id_nif", 1, engine_get_id_nif},
+ {"engine_get_all_methods_nif", 0, engine_get_all_methods_nif}
+
};
ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload)
-
#define MD5_CTX_LEN (sizeof(MD5_CTX))
#define MD4_CTX_LEN (sizeof(MD4_CTX))
#define RIPEMD160_CTX_LEN (sizeof(RIPEMD160_CTX))
@@ -603,7 +669,33 @@ static ERL_NIF_TERM atom_sha512;
static ERL_NIF_TERM atom_md5;
static ERL_NIF_TERM atom_ripemd160;
-
+#ifdef HAS_ENGINE_SUPPORT
+static ERL_NIF_TERM atom_bad_engine_method;
+static ERL_NIF_TERM atom_bad_engine_id;
+static ERL_NIF_TERM atom_ctrl_cmd_failed;
+static ERL_NIF_TERM atom_engine_init_failed;
+static ERL_NIF_TERM atom_register_engine_failed;
+static ERL_NIF_TERM atom_add_engine_failed;
+static ERL_NIF_TERM atom_remove_engine_failed;
+static ERL_NIF_TERM atom_engine_method_not_supported;
+
+static ERL_NIF_TERM atom_engine_method_rsa;
+static ERL_NIF_TERM atom_engine_method_dsa;
+static ERL_NIF_TERM atom_engine_method_dh;
+static ERL_NIF_TERM atom_engine_method_rand;
+static ERL_NIF_TERM atom_engine_method_ecdh;
+static ERL_NIF_TERM atom_engine_method_ecdsa;
+static ERL_NIF_TERM atom_engine_method_ciphers;
+static ERL_NIF_TERM atom_engine_method_digests;
+static ERL_NIF_TERM atom_engine_method_store;
+static ERL_NIF_TERM atom_engine_method_pkey_meths;
+static ERL_NIF_TERM atom_engine_method_pkey_asn1_meths;
+static ERL_NIF_TERM atom_engine_method_ec;
+
+static ERL_NIF_TERM atom_engine;
+static ERL_NIF_TERM atom_key_id;
+static ERL_NIF_TERM atom_password;
+#endif
static ErlNifResourceType* hmac_context_rtype;
struct hmac_context
@@ -728,11 +820,13 @@ static struct cipher_type_t cipher_types[] =
static struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len);
+
/*
#define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n")
#define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1)
#define PRINTF_ERR2(FMT, A1, A2) enif_fprintf(stderr, FMT "\n", A1, A2)
*/
+
#define PRINTF_ERR0(FMT)
#define PRINTF_ERR1(FMT,A1)
#define PRINTF_ERR2(FMT,A1,A2)
@@ -758,6 +852,23 @@ static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) {
}
#endif
+// Engine
+#ifdef HAS_ENGINE_SUPPORT
+static ErlNifResourceType* engine_ctx_rtype;
+struct engine_ctx {
+ ENGINE *engine;
+ char *id;
+};
+static void engine_ctx_dtor(ErlNifEnv* env, struct engine_ctx* ctx) {
+ PRINTF_ERR0("engine_ctx_dtor");
+ if(ctx->id) {
+ PRINTF_ERR1(" non empty ctx->id=%s", ctx->id);
+ enif_free(ctx->id);
+ } else
+ PRINTF_ERR0(" empty ctx->id=NULL");
+}
+#endif
+
static int verify_lib_version(void)
{
const unsigned long libv = SSLeay();
@@ -793,7 +904,7 @@ static char crypto_callback_name[] = "crypto_callback";
static int change_basename(ErlNifBinary* bin, char* buf, int bufsz, const char* newfile)
{
int i;
-
+
for (i = bin->size; i > 0; i--) {
if (bin->data[i-1] == '/')
break;
@@ -869,12 +980,23 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
return __LINE__;
}
#endif
+#ifdef HAS_ENGINE_SUPPORT
+ engine_ctx_rtype = enif_open_resource_type(env, NULL, "ENGINE_CTX",
+ (ErlNifResourceDtor*) engine_ctx_dtor,
+ ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
+ NULL);
+ if (!engine_ctx_rtype) {
+ PRINTF_ERR0("CRYPTO: Could not open resource type 'ENGINE_CTX'");
+ return __LINE__;
+ }
+
if (library_refc > 0) {
/* Repeated loading of this library (module upgrade).
* Atoms and callbacks are already set, we are done.
*/
return 0;
}
+#endif
atom_true = enif_make_atom(env,"true");
atom_false = enif_make_atom(env,"false");
@@ -952,6 +1074,33 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
atom_md5 = enif_make_atom(env,"md5");
atom_ripemd160 = enif_make_atom(env,"ripemd160");
+#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");
+ atom_engine_method_dh = enif_make_atom(env,"engine_method_dh");
+ atom_engine_method_rand = enif_make_atom(env,"engine_method_rand");
+ atom_engine_method_ecdh = enif_make_atom(env,"engine_method_ecdh");
+ atom_engine_method_ecdsa = enif_make_atom(env,"engine_method_ecdsa");
+ atom_engine_method_store = enif_make_atom(env,"engine_method_store");
+ atom_engine_method_ciphers = enif_make_atom(env,"engine_method_ciphers");
+ atom_engine_method_digests = enif_make_atom(env,"engine_method_digests");
+ atom_engine_method_pkey_meths = enif_make_atom(env,"engine_method_pkey_meths");
+ atom_engine_method_pkey_asn1_meths = enif_make_atom(env,"engine_method_pkey_asn1_meths");
+ atom_engine_method_ec = enif_make_atom(env,"engine_method_ec");
+
+ atom_engine = enif_make_atom(env,"engine");
+ atom_key_id = enif_make_atom(env,"key_id");
+ atom_password = enif_make_atom(env,"password");
+#endif
+
init_digest_types(env);
init_cipher_types(env);
init_algorithms_types(env);
@@ -973,24 +1122,24 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
#else /* !HAVE_DYNAMIC_CRYPTO_LIB */
funcp = &get_crypto_callbacks;
#endif
-
+
#ifdef OPENSSL_THREADS
enif_system_info(&sys_info, sizeof(sys_info));
if (sys_info.scheduler_threads > 1) {
- nlocks = CRYPTO_num_locks();
+ nlocks = CRYPTO_num_locks();
}
/* else no need for locks */
#endif
-
+
ccb = (*funcp)(nlocks);
-
+
if (!ccb || ccb->sizeof_me != sizeof(*ccb)) {
PRINTF_ERR0("Invalid 'crypto_callbacks'");
return __LINE__;
}
-
+
CRYPTO_set_mem_functions(ccb->crypto_alloc, ccb->crypto_realloc, ccb->crypto_free);
-
+
#ifdef OPENSSL_THREADS
if (nlocks > 0) {
CRYPTO_set_locking_callback(ccb->locking_function);
@@ -1186,11 +1335,11 @@ static ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
* Version string is still from library though.
*/
- memcpy(enif_make_new_binary(env, name_sz, &name_term), libname, name_sz);
+ memcpy(enif_make_new_binary(env, name_sz, &name_term), libname, name_sz);
memcpy(enif_make_new_binary(env, ver_sz, &ver_term), ver, ver_sz);
return enif_make_list1(env, enif_make_tuple3(env, name_term,
- enif_make_int(env, ver_num),
+ enif_make_int(env, ver_num),
ver_term));
}
@@ -1225,6 +1374,8 @@ static ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, int argc, const ERL_NIF_TER
}
}
+
+#if defined(HAVE_EC)
static ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env)
{
ERL_NIF_TERM reason;
@@ -1233,6 +1384,7 @@ static ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env)
else
return enif_make_badarg(env);
}
+#endif
static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Type, Data) */
@@ -1668,7 +1820,7 @@ static ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
{/* (Context, Data) */
ErlNifBinary data;
struct hmac_context* obj;
-
+
if (!enif_get_resource(env, argv[0], hmac_context_rtype, (void**)&obj)
|| !enif_inspect_iolist_as_binary(env, argv[1], &data)) {
return enif_make_badarg(env);
@@ -1704,13 +1856,13 @@ static ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
enif_mutex_unlock(obj->mtx);
return enif_make_badarg(env);
}
-
+
HMAC_Final(obj->ctx, mac_buf, &mac_len);
HMAC_CTX_free(obj->ctx);
obj->alive = 0;
enif_mutex_unlock(obj->mtx);
- if (argc == 2 && req_len < mac_len) {
+ if (argc == 2 && req_len < mac_len) {
/* Only truncate to req_len bytes if asked. */
mac_len = req_len;
}
@@ -2021,7 +2173,7 @@ static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_
}
static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* ({Key, IVec, ECount, Num}, Data) */
+{/* ({Key, IVec, ECount, Num}, Data) */
ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin;
AES_KEY aes_key;
unsigned int num;
@@ -2042,14 +2194,14 @@ static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_N
return enif_make_badarg(env);
}
- ivec2_buf = enif_make_new_binary(env, ivec_bin.size, &ivec2_term);
+ ivec2_buf = enif_make_new_binary(env, ivec_bin.size, &ivec2_term);
ecount2_buf = enif_make_new_binary(env, ecount_bin.size, &ecount2_term);
-
+
memcpy(ivec2_buf, ivec_bin.data, 16);
memcpy(ecount2_buf, ecount_bin.data, ecount_bin.size);
AES_ctr128_encrypt((unsigned char *) text_bin.data,
- enif_make_new_binary(env, text_bin.size, &cipher_term),
+ enif_make_new_binary(env, text_bin.size, &cipher_term),
text_bin.size, &aes_key, ivec2_buf, ecount2_buf, &num);
num2_term = enif_make_uint(env, num);
@@ -2352,7 +2504,7 @@ out_err:
}
static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Bytes) */
+{/* (Bytes) */
unsigned bytes;
unsigned char* data;
ERL_NIF_TERM ret;
@@ -2446,7 +2598,7 @@ static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER
bn_to = BN_new();
BN_sub(bn_to, bn_rand, bn_from);
- BN_pseudo_rand_range(bn_rand, bn_to);
+ BN_pseudo_rand_range(bn_rand, bn_to);
BN_add(bn_rand, bn_rand, bn_from);
dlen = BN_num_bytes(bn_rand);
data = enif_make_new_binary(env, dlen+4, &ret);
@@ -2464,7 +2616,7 @@ static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
BIGNUM *bn_base=NULL, *bn_exponent=NULL, *bn_modulo=NULL, *bn_result;
BN_CTX *bn_ctx;
unsigned char* ptr;
- unsigned dlen;
+ unsigned dlen;
unsigned bin_hdr; /* return type: 0=plain binary, 4: mpint */
unsigned extra_byte;
ERL_NIF_TERM ret;
@@ -2485,7 +2637,7 @@ static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
dlen = BN_num_bytes(bn_result);
extra_byte = bin_hdr && BN_is_bit_set(bn_result, dlen*8-1);
ptr = enif_make_new_binary(env, bin_hdr+extra_byte+dlen, &ret);
- if (bin_hdr) {
+ if (bin_hdr) {
put_int32(ptr, extra_byte+dlen);
ptr[4] = 0; /* extra zeroed byte to ensure a positive mpint */
ptr += bin_hdr + extra_byte;
@@ -2545,6 +2697,7 @@ static struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len)
return NULL;
}
+
static ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Data1, Data2) */
ErlNifBinary d1, d2;
@@ -2578,7 +2731,7 @@ static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
return enif_make_badarg(env);
}
RC4_set_key((RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &ret),
- key.size, key.data);
+ key.size, key.data);
return ret;
#else
return enif_raise_exception(env, atom_notsup);
@@ -2846,7 +2999,7 @@ static ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF
static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (PrimeLen, Generator) */
int prime_len, generator;
- DH* dh_params;
+ DH* dh_params = NULL;
int p_len, g_len;
unsigned char *p_ptr, *g_ptr;
ERL_NIF_TERM ret_p, ret_g;
@@ -2857,8 +3010,8 @@ static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const E
return enif_make_badarg(env);
}
- dh_params = DH_generate_parameters(prime_len, generator, NULL, NULL);
- if (dh_params == NULL) {
+
+ if (DH_generate_parameters_ex(dh_params, prime_len, generator, NULL)) {
return atom_error;
}
DH_get0_pqg(dh_params, &dh_p, &dh_q, &dh_g);
@@ -2871,7 +3024,7 @@ static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const E
BN_bn2bin(dh_g, g_ptr);
ERL_VALGRIND_MAKE_MEM_DEFINED(p_ptr, p_len);
ERL_VALGRIND_MAKE_MEM_DEFINED(g_ptr, g_len);
- return enif_make_list2(env, ret_p, ret_g);
+ return enif_make_list2(env, ret_p, ret_g);
}
static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -2881,9 +3034,9 @@ static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
ERL_NIF_TERM ret, head, tail;
BIGNUM *dh_p, *dh_g;
- if (!enif_get_list_cell(env, argv[0], &head, &tail)
+ if (!enif_get_list_cell(env, argv[0], &head, &tail)
|| !get_bn_from_bin(env, head, &dh_p)
- || !enif_get_list_cell(env, tail, &head, &tail)
+ || !enif_get_list_cell(env, tail, &head, &tail)
|| !get_bn_from_bin(env, head, &dh_g)
|| !enif_is_empty_list(env,tail)) {
@@ -2900,12 +3053,12 @@ static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
else if (i & DH_NOT_SUITABLE_GENERATOR) ret = atom_not_suitable_generator;
else ret = enif_make_tuple2(env, atom_unknown, enif_make_uint(env, i));
}
- else { /* Check Failed */
+ else { /* Check Failed */
ret = enif_make_tuple2(env, atom_error, atom_check_failed);
}
DH_free(dh_params);
return ret;
-}
+}
static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (PrivKey|undefined, DHParams=[P,G], Mpint, Len|0) */
@@ -3007,7 +3160,7 @@ static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_T
i = DH_compute_key(ret_bin.data, other_pub_key, dh_params);
if (i > 0) {
if (i != ret_bin.size) {
- enif_realloc_binary(&ret_bin, i);
+ enif_realloc_binary(&ret_bin, i);
}
ret = enif_make_binary(env, &ret_bin);
}
@@ -3803,9 +3956,69 @@ static int get_pkey_sign_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF
}
+#ifdef HAS_ENGINE_SUPPORT
+static int get_engine_and_key_id(ErlNifEnv *env, ERL_NIF_TERM key, char ** id, ENGINE **e)
+{
+ ERL_NIF_TERM engine_res, key_id_term;
+ struct engine_ctx *ctx;
+ ErlNifBinary key_id_bin;
+
+ if (!enif_get_map_value(env, key, atom_engine, &engine_res) ||
+ !enif_get_resource(env, engine_res, engine_ctx_rtype, (void**)&ctx) ||
+ !enif_get_map_value(env, key, atom_key_id, &key_id_term) ||
+ !enif_inspect_binary(env, key_id_term, &key_id_bin)) {
+ return 0;
+ }
+ else {
+ *e = ctx->engine;
+ return zero_terminate(key_id_bin, id);
+ }
+}
+
+
+static char *get_key_password(ErlNifEnv *env, ERL_NIF_TERM key) {
+ ERL_NIF_TERM tmp_term;
+ ErlNifBinary pwd_bin;
+ char *pwd;
+ if (enif_get_map_value(env, key, atom_password, &tmp_term) &&
+ enif_inspect_binary(env, tmp_term, &pwd_bin) &&
+ zero_terminate(pwd_bin, &pwd)
+ ) return pwd;
+
+ return NULL;
+}
+
+static int zero_terminate(ErlNifBinary bin, char **buf) {
+ *buf = enif_alloc(bin.size+1);
+ if (!*buf)
+ return 0;
+ memcpy(*buf, bin.data, bin.size);
+ *(*buf+bin.size) = 0;
+ return 1;
+}
+#endif
+
static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, EVP_PKEY **pkey)
{
- if (algorithm == atom_rsa) {
+ if (enif_is_map(env, key)) {
+#ifdef HAS_ENGINE_SUPPORT
+ /* Use key stored in engine */
+ ENGINE *e;
+ char *id;
+ char *password;
+
+ if (!get_engine_and_key_id(env, key, &id, &e))
+ return PKEY_BADARG;
+ password = get_key_password(env, key);
+ *pkey = ENGINE_load_private_key(e, id, NULL, password);
+ if (!pkey)
+ return PKEY_BADARG;
+ enif_free(id);
+#else
+ return PKEY_BADARG;
+#endif
+ }
+ else if (algorithm == atom_rsa) {
RSA *rsa = RSA_new();
if (!get_rsa_private_key(env, key, rsa)) {
@@ -3866,7 +4079,24 @@ static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_
static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key,
EVP_PKEY **pkey)
{
- if (algorithm == atom_rsa) {
+ if (enif_is_map(env, key)) {
+#ifdef HAS_ENGINE_SUPPORT
+ /* Use key stored in engine */
+ ENGINE *e;
+ char *id;
+ char *password;
+
+ if (!get_engine_and_key_id(env, key, &id, &e))
+ return PKEY_BADARG;
+ password = get_key_password(env, key);
+ *pkey = ENGINE_load_public_key(e, id, NULL, password);
+ if (!pkey)
+ return PKEY_BADARG;
+ enif_free(id);
+#else
+ return PKEY_BADARG;
+#endif
+ } else if (algorithm == atom_rsa) {
RSA *rsa = RSA_new();
if (!get_rsa_public_key(env, key, rsa)) {
@@ -3924,7 +4154,7 @@ static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_T
}
static ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
-{/* (Algorithm, Type, Data|{digest,Digest}, Key, Options) */
+{/* (Algorithm, Type, Data|{digest,Digest}, Key|#{}, Options) */
int i;
const EVP_MD *md = NULL;
unsigned char md_value[EVP_MAX_MD_SIZE];
@@ -3944,6 +4174,13 @@ enif_get_atom(env,argv[0],buf,1024,ERL_NIF_LATIN1); printf("algo=%s ",buf);
enif_get_atom(env,argv[1],buf,1024,ERL_NIF_LATIN1); printf("hash=%s ",buf);
printf("\r\n");
*/
+
+#ifndef HAS_ENGINE_SUPPORT
+ if (enif_is_map(env, argv[3])) {
+ return atom_notsup;
+ }
+#endif
+
i = get_pkey_sign_digest(env, argv[0], argv[1], argv[2], md_value, &md, &tbs, &tbslen);
if (i != PKEY_OK) {
if (i == PKEY_NOTSUP)
@@ -3965,10 +4202,9 @@ printf("\r\n");
}
#ifdef HAS_EVP_PKEY_CTX
-/* printf("EVP interface\r\n");
- */
ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx) goto badarg;
+
if (EVP_PKEY_sign_init(ctx) <= 0) goto badarg;
if (md != NULL && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) goto badarg;
@@ -4070,6 +4306,12 @@ static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
unsigned char *tbs; /* data to be signed */
size_t tbslen;
+#ifndef HAS_ENGINE_SUPPORT
+ if (enif_is_map(env, argv[4])) {
+ return atom_notsup;
+ }
+#endif
+
if (!enif_inspect_binary(env, argv[3], &sig_bin)) {
return enif_make_badarg(env);
}
@@ -4095,7 +4337,7 @@ static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
}
#ifdef HAS_EVP_PKEY_CTX
-/* printf("EVP interface\r\n");
+/* printf("EVP interface\r\n");
*/
ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx) goto badarg;
@@ -4280,7 +4522,13 @@ static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
int algo_init = 0;
/* char algo[1024]; */
-
+
+#ifndef HAS_ENGINE_SUPPORT
+ if (enif_is_map(env, argv[2])) {
+ return atom_notsup;
+ }
+#endif
+
if (!enif_inspect_binary(env, argv[1], &in_bin)) {
return enif_make_badarg(env);
}
@@ -4542,6 +4790,83 @@ static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
/*--------------------------------*/
+static ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{ /* (Algorithm, PrivKey | KeyMap) */
+ EVP_PKEY *pkey;
+ ERL_NIF_TERM alg = argv[0];
+ ERL_NIF_TERM result[8];
+
+ if (get_pkey_private_key(env, alg, argv[1], &pkey) != PKEY_OK) {
+ return enif_make_badarg(env);
+ }
+
+ if (alg == atom_rsa) {
+ const BIGNUM *n = NULL, *e = NULL, *d = NULL;
+ RSA *rsa = EVP_PKEY_get1_RSA(pkey);
+ if (rsa) {
+ RSA_get0_key(rsa, &n, &e, &d);
+ result[0] = bin_from_bn(env, e); // Exponent E
+ result[1] = bin_from_bn(env, n); // Modulus N = p*q
+ EVP_PKEY_free(pkey);
+ return enif_make_list_from_array(env, result, 2);
+ }
+
+ } else if (argv[0] == atom_dss) {
+ const BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL;
+ DSA *dsa = EVP_PKEY_get1_DSA(pkey);
+ if (dsa) {
+ DSA_get0_pqg(dsa, &p, &q, &g);
+ DSA_get0_key(dsa, &pub_key, NULL);
+ result[0] = bin_from_bn(env, p);
+ result[1] = bin_from_bn(env, q);
+ result[2] = bin_from_bn(env, g);
+ result[3] = bin_from_bn(env, pub_key);
+ EVP_PKEY_free(pkey);
+ return enif_make_list_from_array(env, result, 4);
+ }
+
+ } else if (argv[0] == atom_ecdsa) {
+#if defined(HAVE_EC)
+ EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey);
+ if (ec) {
+ /* Example of result:
+ {
+ Curve = {Field, Prime, Point, Order, CoFactor} =
+ {
+ Field = {prime_field,<<255,...,255>>},
+ Prime = {<<255,...,252>>,
+ <<90,...,75>>,
+ <<196,...,144>>
+ },
+ Point = <<4,...,245>>,
+ Order = <<255,...,81>>,
+ CoFactor = <<1>>
+ },
+ Key = <<151,...,62>>
+ }
+ or
+ {
+ Curve =
+ {characteristic_two_field,
+ M,
+ Basis = {tpbasis, _}
+ | {ppbasis, k1, k2, k3}
+ },
+ Key
+ }
+ */
+ EVP_PKEY_free(pkey);
+ return atom_notsup;
+ }
+#else
+ EVP_PKEY_free(pkey);
+ return atom_notsup;
+#endif
+ }
+
+ if (pkey) EVP_PKEY_free(pkey);
+ return enif_make_badarg(env);
+}
/*================================================================*/
@@ -4554,3 +4879,598 @@ static ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
RAND_seed(seed_bin.data,seed_bin.size);
return atom_ok;
}
+
+/*================================================================*/
+/* Engine */
+/*================================================================*/
+static ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (EngineId) */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM ret;
+ ErlNifBinary engine_id_bin;
+ unsigned int engine_id_len = 0;
+ char *engine_id;
+ ENGINE *engine;
+ struct engine_ctx *ctx;
+
+ // Get Engine Id
+ if(!enif_inspect_binary(env, argv[0], &engine_id_bin)) {
+ PRINTF_ERR0("engine_by_id_nif Leaved: badarg");
+ return enif_make_badarg(env);
+ } else {
+ engine_id_len = engine_id_bin.size+1;
+ engine_id = enif_alloc(engine_id_len);
+ (void) memcpy(engine_id, engine_id_bin.data, engine_id_len);
+ engine_id[engine_id_len-1] = '\0';
+ }
+
+ engine = ENGINE_by_id(engine_id);
+ if(!engine) {
+ PRINTF_ERR0("engine_by_id_nif Leaved: {error, bad_engine_id}");
+ return enif_make_tuple2(env, atom_error, atom_bad_engine_id);
+ }
+
+ ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx));
+ ctx->engine = engine;
+ ctx->id = engine_id;
+
+ ret = enif_make_resource(env, ctx);
+ enif_release_resource(ctx);
+
+ return enif_make_tuple2(env, atom_ok, ret);
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM ret = atom_ok;
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_init_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+ if (!ENGINE_init(ctx->engine)) {
+ //ERR_print_errors_fp(stderr);
+ PRINTF_ERR0("engine_init_nif Leaved: {error, engine_init_failed}");
+ return enif_make_tuple2(env, atom_error, atom_engine_init_failed);
+ }
+
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_free_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ ENGINE_free(ctx->engine);
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_finish_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ ENGINE_finish(ctx->engine);
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* () */
+#ifdef HAS_ENGINE_SUPPORT
+ ENGINE_load_dynamic();
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine, Commands) */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM ret = atom_ok;
+ unsigned int cmds_len = 0;
+ char **cmds = NULL;
+ struct engine_ctx *ctx;
+ int i;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ PRINTF_ERR1("Engine Id: %s\r\n", ENGINE_get_id(ctx->engine));
+
+ // Get Command List
+ if(!enif_get_list_length(env, argv[1], &cmds_len)) {
+ PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Bad Command List");
+ return enif_make_badarg(env);
+ } else {
+ cmds_len *= 2; // Key-Value list from erlang
+ cmds = enif_alloc((cmds_len+1)*sizeof(char*));
+ if(get_engine_load_cmd_list(env, argv[1], cmds, 0)) {
+ PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Couldn't read Command List");
+ ret = enif_make_badarg(env);
+ goto error;
+ }
+ }
+
+ for(i = 0; i < cmds_len; i+=2) {
+ PRINTF_ERR2("Cmd: %s:%s\r\n",
+ cmds[i] ? cmds[i] : "(NULL)",
+ cmds[i+1] ? cmds[i+1] : "(NULL)");
+ if(!ENGINE_ctrl_cmd_string(ctx->engine, cmds[i], cmds[i+1], 0)) {
+ PRINTF_ERR2("Command failed: %s:%s\r\n",
+ cmds[i] ? cmds[i] : "(NULL)",
+ cmds[i+1] ? cmds[i+1] : "(NULL)");
+ //ENGINE_free(ctx->engine);
+ ret = enif_make_tuple2(env, atom_error, atom_ctrl_cmd_failed);
+ PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: {error, ctrl_cmd_failed}");
+ goto error;
+ }
+}
+
+ error:
+ for(i = 0; cmds != NULL && cmds[i] != NULL; i++)
+ enif_free(cmds[i]);
+ return ret;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_add_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ if (!ENGINE_add(ctx->engine)) {
+ PRINTF_ERR0("engine_add_nif Leaved: {error, add_engine_failed}");
+ return enif_make_tuple2(env, atom_error, atom_add_engine_failed);
+ }
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_remove_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ if (!ENGINE_remove(ctx->engine)) {
+ PRINTF_ERR0("engine_remove_nif Leaved: {error, remove_engine_failed}");
+ return enif_make_tuple2(env, atom_error, atom_remove_engine_failed);
+ }
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine, EngineMethod) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+ unsigned int method;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_register_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+ // Get Method
+ if (!enif_get_uint(env, argv[1], &method)) {
+ PRINTF_ERR0("engine_register_nif Leaved: Parameter Method not an uint");
+ return enif_make_badarg(env);
+ }
+
+ switch(method)
+ {
+#ifdef ENGINE_METHOD_RSA
+ case ENGINE_METHOD_RSA:
+ if (!ENGINE_register_RSA(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DSA
+ case ENGINE_METHOD_DSA:
+ if (!ENGINE_register_DSA(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DH
+ case ENGINE_METHOD_DH:
+ if (!ENGINE_register_DH(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_RAND
+ case ENGINE_METHOD_RAND:
+ if (!ENGINE_register_RAND(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_ECDH
+ case ENGINE_METHOD_ECDH:
+ if (!ENGINE_register_ECDH(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_ECDSA
+ case ENGINE_METHOD_ECDSA:
+ if (!ENGINE_register_ECDSA(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_STORE
+ case ENGINE_METHOD_STORE:
+ if (!ENGINE_register_STORE(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_CIPHERS
+ case ENGINE_METHOD_CIPHERS:
+ if (!ENGINE_register_ciphers(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DIGESTS
+ case ENGINE_METHOD_DIGESTS:
+ if (!ENGINE_register_digests(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_PKEY_METHS
+ case ENGINE_METHOD_PKEY_METHS:
+ if (!ENGINE_register_pkey_meths(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_PKEY_ASN1_METHS
+ case ENGINE_METHOD_PKEY_ASN1_METHS:
+ if (!ENGINE_register_pkey_asn1_meths(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+#ifdef ENGINE_METHOD_EC
+ case ENGINE_METHOD_EC:
+ if (!ENGINE_register_EC(ctx->engine))
+ return enif_make_tuple2(env, atom_error, atom_register_engine_failed);
+ break;
+#endif
+ default:
+ return enif_make_tuple2(env, atom_error, atom_engine_method_not_supported);
+ break;
+ }
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine, EngineMethod) */
+#ifdef HAS_ENGINE_SUPPORT
+ struct engine_ctx *ctx;
+ unsigned int method;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_unregister_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+ // Get Method
+ if (!enif_get_uint(env, argv[1], &method)) {
+ PRINTF_ERR0("engine_unregister_nif Leaved: Parameter Method not an uint");
+ return enif_make_badarg(env);
+ }
+
+ switch(method)
+ {
+#ifdef ENGINE_METHOD_RSA
+ case ENGINE_METHOD_RSA:
+ ENGINE_unregister_RSA(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DSA
+ case ENGINE_METHOD_DSA:
+ ENGINE_unregister_DSA(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DH
+ case ENGINE_METHOD_DH:
+ ENGINE_unregister_DH(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_RAND
+ case ENGINE_METHOD_RAND:
+ ENGINE_unregister_RAND(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_ECDH
+ case ENGINE_METHOD_ECDH:
+ ENGINE_unregister_ECDH(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_ECDSA
+ case ENGINE_METHOD_ECDSA:
+ ENGINE_unregister_ECDSA(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_STORE
+ case ENGINE_METHOD_STORE:
+ ENGINE_unregister_STORE(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_CIPHERS
+ case ENGINE_METHOD_CIPHERS:
+ ENGINE_unregister_ciphers(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_DIGESTS
+ case ENGINE_METHOD_DIGESTS:
+ ENGINE_unregister_digests(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_PKEY_METHS
+ case ENGINE_METHOD_PKEY_METHS:
+ ENGINE_unregister_pkey_meths(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_PKEY_ASN1_METHS
+ case ENGINE_METHOD_PKEY_ASN1_METHS:
+ ENGINE_unregister_pkey_asn1_meths(ctx->engine);
+ break;
+#endif
+#ifdef ENGINE_METHOD_EC
+ case ENGINE_METHOD_EC:
+ ENGINE_unregister_EC(ctx->engine);
+ break;
+#endif
+ default:
+ break;
+ }
+ return atom_ok;
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM ret;
+ ENGINE *engine;
+ ErlNifBinary engine_bin;
+ struct engine_ctx *ctx;
+
+ engine = ENGINE_get_first();
+ if(!engine) {
+ enif_alloc_binary(0, &engine_bin);
+ engine_bin.size = 0;
+ return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin));
+ }
+
+ ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx));
+ ctx->engine = engine;
+ ctx->id = NULL;
+
+ ret = enif_make_resource(env, ctx);
+ enif_release_resource(ctx);
+
+ return enif_make_tuple2(env, atom_ok, ret);
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM ret;
+ ENGINE *engine;
+ ErlNifBinary engine_bin;
+ struct engine_ctx *ctx, *next_ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_get_next_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+ engine = ENGINE_get_next(ctx->engine);
+ if (!engine) {
+ enif_alloc_binary(0, &engine_bin);
+ engine_bin.size = 0;
+ return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin));
+ }
+
+ next_ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx));
+ next_ctx->engine = engine;
+ next_ctx->id = NULL;
+
+ ret = enif_make_resource(env, next_ctx);
+ enif_release_resource(next_ctx);
+
+ return enif_make_tuple2(env, atom_ok, ret);
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+ ErlNifBinary engine_id_bin;
+ const char *engine_id;
+ int size;
+ struct engine_ctx *ctx;
+
+ // Get Engine
+ if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+ PRINTF_ERR0("engine_get_id_nif Leaved: Parameter not an engine resource object");
+ return enif_make_badarg(env);
+ }
+
+ engine_id = ENGINE_get_id(ctx->engine);
+ if (!engine_id) {
+ enif_alloc_binary(0, &engine_id_bin);
+ engine_id_bin.size = 0;
+ return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_id_bin));
+ }
+
+ size = strlen(engine_id);
+ enif_alloc_binary(size, &engine_id_bin);
+ engine_id_bin.size = size;
+ memcpy(engine_id_bin.data, engine_id, size);
+
+ return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_id_bin));
+#else
+ return atom_notsup;
+#endif
+}
+
+static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i)
+{
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM head, tail;
+ const ERL_NIF_TERM *tmp_tuple;
+ ErlNifBinary tmpbin;
+ int arity;
+ char* tmpstr;
+ int tmplen = 0;
+
+ if(!enif_is_empty_list(env, term)) {
+ if(!enif_get_list_cell(env, term, &head, &tail)) {
+ cmds[i] = NULL;
+ return -1;
+ } else {
+ if(!enif_get_tuple(env, head, &arity, &tmp_tuple) || arity != 2) {
+ cmds[i] = NULL;
+ return -1;
+ } else {
+ if(!enif_inspect_binary(env, tmp_tuple[0], &tmpbin)) {
+ cmds[i] = NULL;
+ return -1;
+ } else {
+ tmplen = tmpbin.size+1;
+ tmpstr = enif_alloc(tmplen);
+ (void) memcpy(tmpstr, tmpbin.data, tmplen);
+ tmpstr[tmplen-1] = '\0';
+ cmds[i++] = tmpstr;
+ }
+ if(!enif_inspect_binary(env, tmp_tuple[1], &tmpbin)) {
+ cmds[i] = NULL;
+ return -1;
+ } else {
+ if(tmpbin.size == 0)
+ cmds[i++] = NULL;
+ else {
+ tmplen = tmpbin.size+1;
+ tmpstr = enif_alloc(tmplen);
+ (void) memcpy(tmpstr, tmpbin.data, tmplen);
+ tmpstr[tmplen-1] = '\0';
+ cmds[i++] = tmpstr;
+ }
+ }
+ return get_engine_load_cmd_list(env, tail, cmds, i);
+ }
+ }
+ } else {
+ cmds[i] = NULL;
+ return 0;
+ }
+#else
+ return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* () */
+#ifdef HAS_ENGINE_SUPPORT
+ ERL_NIF_TERM method_array[12];
+ int i = 0;
+
+#ifdef ENGINE_METHOD_RSA
+ method_array[i++] = atom_engine_method_rsa;
+#endif
+#ifdef ENGINE_METHOD_DSA
+ method_array[i++] = atom_engine_method_dsa;
+#endif
+#ifdef ENGINE_METHOD_DH
+ method_array[i++] = atom_engine_method_dh;
+#endif
+#ifdef ENGINE_METHOD_RAND
+ method_array[i++] = atom_engine_method_rand;
+#endif
+#ifdef ENGINE_METHOD_ECDH
+ method_array[i++] = atom_engine_method_ecdh;
+#endif
+#ifdef ENGINE_METHOD_ECDSA
+ method_array[i++] = atom_engine_method_ecdsa;
+#endif
+#ifdef ENGINE_METHOD_STORE
+ method_array[i++] = atom_engine_method_store;
+#endif
+#ifdef ENGINE_METHOD_CIPHERS
+ method_array[i++] = atom_engine_method_ciphers;
+#endif
+#ifdef ENGINE_METHOD_DIGESTS
+ method_array[i++] = atom_engine_method_digests;
+#endif
+#ifdef ENGINE_METHOD_PKEY_METHS
+ method_array[i++] = atom_engine_method_pkey_meths;
+#endif
+#ifdef ENGINE_METHOD_PKEY_ASN1_METHS
+ method_array[i++] = atom_engine_method_pkey_asn1_meths;
+#endif
+#ifdef ENGINE_METHOD_EC
+ method_array[i++] = atom_engine_method_ec;
+#endif
+
+ return enif_make_list_from_array(env, method_array, i);
+#else
+ return atom_notsup;
+#endif
+}
diff --git a/lib/crypto/c_src/otp_test_engine.c b/lib/crypto/c_src/otp_test_engine.c
new file mode 100644
index 0000000000..a66bee2ddf
--- /dev/null
+++ b/lib/crypto/c_src/otp_test_engine.c
@@ -0,0 +1,262 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2017-2017. 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%
+ */
+
+#ifdef _WIN32
+#define OPENSSL_OPT_WINDLL
+#endif
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/engine.h>
+#include <openssl/md5.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+
+#define PACKED_OPENSSL_VERSION(MAJ, MIN, FIX, P) \
+ ((((((((MAJ << 8) | MIN) << 8 ) | FIX) << 8) | (P-'a'+1)) << 4) | 0xf)
+
+#define PACKED_OPENSSL_VERSION_PLAIN(MAJ, MIN, FIX) \
+ PACKED_OPENSSL_VERSION(MAJ,MIN,FIX,('a'-1))
+
+#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) \
+ || defined(LIBRESSL_VERSION_NUMBER)
+#define OLD
+#endif
+
+static const char *test_engine_id = "MD5";
+static const char *test_engine_name = "MD5 test engine";
+
+/* The callback that does the job of fetching keys on demand by the Engine */
+EVP_PKEY* test_key_load(ENGINE *er, const char *id, UI_METHOD *ui_method, void *callback_data);
+
+
+static int test_init(ENGINE *e) {
+ printf("OTP Test Engine Initializatzion!\r\n");
+
+ /* Load all digest and cipher algorithms. Needed for password protected private keys */
+ OpenSSL_add_all_algorithms();
+
+ return 111;
+}
+
+static void add_test_data(unsigned char *md, unsigned int len)
+{
+ unsigned int i;
+
+ for (i=0; i<len; i++) {
+ md[i] = (unsigned char)(i & 0xff);
+ }
+}
+
+/* MD5 part */
+#undef data
+#ifdef OLD
+#define data(ctx) ((MD5_CTX *)ctx->md_data)
+#endif
+
+static int test_engine_md5_init(EVP_MD_CTX *ctx) {
+ fprintf(stderr, "MD5 initialized\r\n");
+#ifdef OLD
+ return MD5_Init(data(ctx));
+#else
+ return 1;
+#endif
+}
+
+static int test_engine_md5_update(EVP_MD_CTX *ctx,const void *data, size_t count)
+{
+ fprintf(stderr, "MD5 update\r\n");
+#ifdef OLD
+ return MD5_Update(data(ctx), data, (size_t)count);
+#else
+ return 1;
+#endif
+}
+
+static int test_engine_md5_final(EVP_MD_CTX *ctx,unsigned char *md) {
+#ifdef OLD
+ int ret;
+
+ fprintf(stderr, "MD5 final size of EVP_MD: %lu\r\n", sizeof(EVP_MD));
+ ret = MD5_Final(md, data(ctx));
+
+ if (ret > 0) {
+ add_test_data(md, MD5_DIGEST_LENGTH);
+ }
+ return ret;
+#else
+ fprintf(stderr, "MD5 final\r\n");
+ add_test_data(md, MD5_DIGEST_LENGTH);
+ return 1;
+#endif
+}
+
+#ifdef OLD
+static EVP_MD test_engine_md5_method= {
+ NID_md5, /* The name ID for MD5 */
+ NID_undef, /* IGNORED: MD5 with private key encryption NID */
+ MD5_DIGEST_LENGTH, /* Size of MD5 result, in bytes */
+ 0, /* Flags */
+ test_engine_md5_init, /* digest init */
+ test_engine_md5_update, /* digest update */
+ test_engine_md5_final, /* digest final */
+ NULL, /* digest copy */
+ NULL, /* digest cleanup */
+ EVP_PKEY_NULL_method, /* IGNORED: pkey methods */
+ MD5_CBLOCK, /* Internal blocksize, see rfc1321/md5.h */
+ sizeof(EVP_MD *) + sizeof(MD5_CTX),
+ NULL, /* IGNORED: control function */
+};
+#endif
+
+static int test_digest_ids[] = {NID_md5};
+
+static int test_engine_digest_selector(ENGINE *e, const EVP_MD **digest,
+ const int **nids, int nid) {
+ int ok = 1;
+ if (!digest) {
+ *nids = test_digest_ids;
+ fprintf(stderr, "Digest is empty! Nid:%d\r\n", nid);
+ return 2;
+ }
+ fprintf(stderr, "Digest no %d requested\r\n",nid);
+ if (nid == NID_md5) {
+#ifdef OLD
+ *digest = &test_engine_md5_method;
+#else
+ EVP_MD *md = EVP_MD_meth_new(NID_md5, NID_undef);
+ if (!md ||
+ !EVP_MD_meth_set_result_size(md, MD5_DIGEST_LENGTH) ||
+ !EVP_MD_meth_set_flags(md, 0) ||
+ !EVP_MD_meth_set_init(md, test_engine_md5_init) ||
+ !EVP_MD_meth_set_update(md, test_engine_md5_update) ||
+ !EVP_MD_meth_set_final(md, test_engine_md5_final) ||
+ !EVP_MD_meth_set_copy(md, NULL) ||
+ !EVP_MD_meth_set_cleanup(md, NULL) ||
+ !EVP_MD_meth_set_input_blocksize(md, MD5_CBLOCK) ||
+ !EVP_MD_meth_set_app_datasize(md, sizeof(EVP_MD *) + sizeof(MD5_CTX)) ||
+ !EVP_MD_meth_set_ctrl(md, NULL))
+ {
+ ok = 0;
+ *digest = NULL;
+ } else
+ {
+ *digest = md;
+ }
+#endif
+ }
+ else {
+ ok = 0;
+ *digest = NULL;
+ }
+
+ return ok;
+}
+
+
+static int bind_helper(ENGINE * e, const char *id)
+{
+ if (!ENGINE_set_id(e, test_engine_id) ||
+ !ENGINE_set_name(e, test_engine_name) ||
+ !ENGINE_set_init_function(e, test_init) ||
+ !ENGINE_set_digests(e, &test_engine_digest_selector) ||
+ /* For testing of key storage in an Engine: */
+ !ENGINE_set_load_privkey_function(e, &test_key_load) ||
+ !ENGINE_set_load_pubkey_function(e, &test_key_load)
+ )
+ return 0;
+
+ return 1;
+}
+
+IMPLEMENT_DYNAMIC_CHECK_FN();
+
+IMPLEMENT_DYNAMIC_BIND_FN(bind_helper);
+
+/********************************************************
+ *
+ * Engine storage simulation
+ *
+ */
+int pem_passwd_cb_fun(char *buf, int size, int rwflag, void *password);
+
+EVP_PKEY* test_key_load(ENGINE *er, const char *id, UI_METHOD *ui_method, void *callback_data)
+{
+ EVP_PKEY *pkey = NULL;
+ FILE *f = fopen(id, "r");
+
+ if (!f) {
+ fprintf(stderr, "%s:%d fopen(%s) failed\r\n", __FILE__,__LINE__,id);
+ return NULL;
+ }
+
+ /* First try to read as a private key. If that fails, try to read as a public key: */
+ pkey = PEM_read_PrivateKey(f, NULL, pem_passwd_cb_fun, callback_data);
+ if (!pkey) {
+ /* ERR_print_errors_fp (stderr); */
+ fclose(f);
+ f = fopen(id, "r");
+ pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
+ }
+ fclose(f);
+
+ if (!pkey) {
+ fprintf(stderr, "%s:%d Key read from file failed. ", __FILE__,__LINE__);
+ if (callback_data)
+ fprintf(stderr, "Pwd = \"%s\". ", (char *)callback_data);
+ fprintf(stderr, "Contents of file \"%s\":\r\n",id);
+ f = fopen(id, "r");
+ { /* Print the contents of the key file */
+ char c;
+ while (!feof(f)) {
+ switch (c=fgetc(f)) {
+ case '\n':
+ case '\r': putc('\r',stdout); putc('\n',stdout); break;
+ default: putc(c, stdout);
+ }
+ }
+ }
+ fclose(f);
+ }
+
+ return pkey;
+}
+
+
+int pem_passwd_cb_fun(char *buf, int size, int rwflag, void *password)
+{
+ int i;
+
+ fprintf(stderr, "In pem_passwd_cb_fun\r\n");
+ if (!password)
+ return 0;
+
+ i = strlen(password);
+ if (i < size) {
+ /* whole pwd (incl terminating 0) fits */
+ fprintf(stderr, "Got FULL pwd %d(%d) chars\r\n", i, size);
+ memcpy(buf, (char*)password, i+1);
+ return i+1;
+ } else {
+ fprintf(stderr, "Got TO LONG pwd %d(%d) chars\r\n", i, size);
+ /* meaningless with a truncated password */
+ return 0;
+ }
+}
diff --git a/lib/crypto/doc/src/Makefile b/lib/crypto/doc/src/Makefile
index 9c503b8fe0..a902779383 100644
--- a/lib/crypto/doc/src/Makefile
+++ b/lib/crypto/doc/src/Makefile
@@ -9,11 +9,11 @@
# 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.
-#
+#
# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
# AB. All Rights Reserved.''
-#
+#
# $Id$
#
include $(ERL_TOP)/make/target.mk
@@ -38,13 +38,13 @@ XML_APPLICATION_FILES = ref_man.xml
XML_REF3_FILES = crypto.xml
XML_REF6_FILES = crypto_app.xml
-XML_PART_FILES = release_notes.xml usersguide.xml
-XML_CHAPTER_FILES = notes.xml licenses.xml fips.xml
+XML_PART_FILES = usersguide.xml
+XML_CHAPTER_FILES = notes.xml licenses.xml fips.xml engine_load.xml
BOOK_FILES = book.xml
XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) \
- $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
GIF_FILES =
@@ -63,9 +63,9 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
+XML_FLAGS +=
# ----------------------------------------------------
# Targets
@@ -73,7 +73,6 @@ XML_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-
docs: pdf html man
$(TOP_PDF_FILE): $(XML_FILES)
@@ -86,7 +85,7 @@ man: $(MAN3_FILES) $(MAN6_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt valgrind:
+debug opt valgrind:
clean clean_docs clean_tex:
rm -rf $(HTMLDIR)/*
@@ -97,7 +96,7 @@ clean clean_docs clean_tex:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_docs_spec: docs
@@ -114,4 +113,3 @@ release_docs_spec: docs
release_spec:
-
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 5b2c46a004..8e2d33c928 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,6 @@
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.
-
</legalnotice>
<title>crypto</title>
@@ -68,11 +67,11 @@
<section>
<title>DATA TYPES </title>
-
- <code>key_value() = integer() | binary() </code>
+
+ <code>key_value() = integer() | binary() </code>
<p>Always <c>binary()</c> when used as return value</p>
- <code>rsa_public() = [key_value()] = [E, N] </code>
+ <code>rsa_public() = [key_value()] = [E, N] </code>
<p> Where E is the public exponent and N is public modulus. </p>
<code>rsa_private() = [key_value()] = [E, N, D] | [E, N, D, P1, P2, E1, E2, C] </code>
@@ -85,7 +84,7 @@
<code>dss_public() = [key_value()] = [P, Q, G, Y] </code>
<p>Where P, Q and G are the dss parameters and Y is the public key.</p>
- <code>dss_private() = [key_value()] = [P, Q, G, X] </code>
+ <code>dss_private() = [key_value()] = [P, Q, G, X] </code>
<p>Where P, Q and G are the dss parameters and X is the private key.</p>
<code>srp_public() = key_value() </code>
@@ -109,15 +108,16 @@
<code>ecdh_private() = key_value() </code>
- <code>ecdh_params() = ec_named_curve() | ec_explicit_curve()</code>
+ <code>ecdh_params() = ec_named_curve() | ec_explicit_curve()</code>
<code>ec_explicit_curve() =
- {ec_field(), Prime :: key_value(), Point :: key_value(), Order :: integer(), CoFactor :: none | integer()} </code>
+ {ec_field(), Prime :: key_value(), Point :: key_value(), Order :: integer(),
+ CoFactor :: none | integer()} </code>
<code>ec_field() = {prime_field, Prime :: integer()} |
{characteristic_two_field, M :: integer(), Basis :: ec_basis()}</code>
- <code>ec_basis() = {tpbasis, K :: non_neg_integer()} |
+ <code>ec_basis() = {tpbasis, K :: non_neg_integer()} |
{ppbasis, K1 :: non_neg_integer(), K2 :: non_neg_integer(), K3 :: non_neg_integer()} |
onbasis</code>
@@ -136,16 +136,33 @@
See also <seealso marker="#supports-0">crypto:supports/0</seealso>
</p>
+ <code>engine_key_ref() = #{engine := engine_ref(),
+ key_id := key_id(),
+ password => password()}</code>
+
+ <code>engine_key_ref() = term()</code>
+ <p>The result of a call to <seealso marker="#engine_load-3">engine_load/3</seealso>.
+ </p>
+
+ <code>key_id() = string() | binary()</code>
+ <p>Identifies the key to be used. The format depends on the loaded engine. It is passed to
+ the <c>ENGINE_load_(private|public)_key</c> functions in libcrypto.
+ </p>
+
+ <code>password() = string() | binary()</code>
+ <p>The key's password
+ </p>
+
<code>stream_cipher() = rc4 | aes_ctr </code>
- <code>block_cipher() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ige256 | blowfish_cbc |
+ <code>block_cipher() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ige256 | blowfish_cbc |
blowfish_cfb64 | des_cbc | des_cfb | des3_cbc | des3_cfb | des_ede3 | rc2_cbc </code>
- <code>aead_cipher() = aes_gcm | chacha20_poly1305 </code>
+ <code>aead_cipher() = aes_gcm | chacha20_poly1305 </code>
- <code>stream_key() = aes_key() | rc4_key() </code>
+ <code>stream_key() = aes_key() | rc4_key() </code>
- <code>block_key() = aes_key() | blowfish_key() | des_key()| des3_key() </code>
+ <code>block_key() = aes_key() | blowfish_key() | des_key()| des3_key() </code>
<code>aes_key() = iodata() </code> <p>Key length is 128, 192 or 256 bits</p>
@@ -174,13 +191,17 @@
Note that both md4 and md5 are recommended only for compatibility with existing applications.
</p>
<code> cipher_algorithms() = aes_cbc | aes_cfb8 | aes_cfb128 | aes_ctr | aes_gcm |
- aes_ige256 | blowfish_cbc | blowfish_cfb64 | chacha20_poly1305 | des_cbc | des_cfb |
- des3_cbc | des3_cfb | des_ede3 | rc2_cbc | rc4 </code>
- <code> mac_algorithms() = hmac | cmac</code>
- <code> public_key_algorithms() = rsa |dss | ecdsa | dh | ecdh | ec_gf2m</code>
+ aes_ige256 | blowfish_cbc | blowfish_cfb64 | chacha20_poly1305 | des_cbc |
+ des_cfb | des3_cbc | des3_cfb | des_ede3 | rc2_cbc | rc4 </code>
+ <code> mac_algorithms() = hmac | cmac</code>
+ <code> public_key_algorithms() = rsa |dss | ecdsa | dh | ecdh | ec_gf2m</code>
<p>Note that ec_gf2m is not strictly a public key algorithm, but a restriction on what curves are supported
with ecdsa and ecdh.
</p>
+ <code>engine_method_type() = engine_method_rsa | engine_method_dsa | engine_method_dh |
+ engine_method_rand | engine_method_ecdh | engine_method_ecdsa |
+ engine_method_ciphers | engine_method_digests | engine_method_store |
+ engine_method_pkey_meths | engine_method_pkey_asn1_meths</code>
</section>
@@ -261,13 +282,13 @@
is not supported by the underlying OpenSSL implementation.</p>
</desc>
</func>
-
+
<func>
<name>bytes_to_integer(Bin) -> Integer </name>
<fsummary>Convert binary representation, of an integer, to an Erlang integer.</fsummary>
<type>
<v>Bin = binary() - as returned by crypto functions</v>
-
+
<v>Integer = integer() </v>
</type>
<desc>
@@ -439,7 +460,7 @@
</type>
<desc>
<p>Updates the HMAC represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c>
- must have been generated using an HMAC init function (such as
+ must have been generated using an HMAC init function (such as
<seealso marker="#hmac_init-2">hmac_init</seealso>). <c>Data</c> can be any length. <c>NewContext</c>
must be passed into the next call to <c>hmac_update</c>
or to one of the functions <seealso marker="#hmac_final-1">hmac_final</seealso> and
@@ -580,7 +601,7 @@
<type>
<v>Type = rsa</v>
<v>CipherText = binary()</v>
- <v>PrivateKey = rsa_private()</v>
+ <v>PrivateKey = rsa_private() | engine_key_ref()</v>
<v>Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding | rsa_no_padding</v>
<v>PlainText = binary()</v>
</type>
@@ -594,7 +615,22 @@
</p>
</desc>
</func>
-
+
+ <func>
+ <name>privkey_to_pubkey(Type, EnginePrivateKeyRef) -> PublicKey</name>
+ <fsummary>Fetches a public key from an Engine stored private key.</fsummary>
+ <type>
+ <v>Type = rsa | dss</v>
+ <v>EnginePrivateKeyRef = engine_key_ref()</v>
+ <v>PublicKey = rsa_public() | dss_public()</v>
+ </type>
+ <desc>
+ <p>Fetches the corresponding public key from a private key stored in an Engine.
+ The key must be of the type indicated by the Type parameter.
+ </p>
+ </desc>
+ </func>
+
<func>
<name>private_encrypt(Type, PlainText, PrivateKey, Padding) -> CipherText</name>
<fsummary>Encrypts PlainText using the private Key.</fsummary>
@@ -605,7 +641,7 @@
than <c>byte_size(N)-11</c> if <c>rsa_pkcs1_padding</c> is
used, and <c>byte_size(N)</c> if <c>rsa_no_padding</c> is
used, where N is public modulus of the RSA key.</d>
- <v>PrivateKey = rsa_private()</v>
+ <v>PrivateKey = rsa_private() | engine_key_ref()</v>
<v>Padding = rsa_pkcs1_padding | rsa_no_padding</v>
<v>CipherText = binary()</v>
</type>
@@ -624,7 +660,7 @@
<type>
<v>Type = rsa</v>
<v>CipherText = binary()</v>
- <v>PublicKey = rsa_public() </v>
+ <v>PublicKey = rsa_public() | engine_key_ref()</v>
<v>Padding = rsa_pkcs1_padding | rsa_no_padding</v>
<v>PlainText = binary()</v>
</type>
@@ -649,7 +685,7 @@
than <c>byte_size(N)-11</c> if <c>rsa_pkcs1_padding</c> is
used, and <c>byte_size(N)</c> if <c>rsa_no_padding</c> is
used, where N is public modulus of the RSA key.</d>
- <v>PublicKey = rsa_public()</v>
+ <v>PublicKey = rsa_public() | engine_key_ref()</v>
<v>Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding | rsa_no_padding</v>
<v>CipherText = binary()</v>
</type>
@@ -702,7 +738,7 @@
signed or it is the hashed value of "cleartext" i.e. the
digest (plaintext).</d>
<v>DigestType = rsa_digest_type() | dss_digest_type() | ecdsa_digest_type()</v>
- <v>Key = rsa_private() | dss_private() | [ecdh_private(),ecdh_params()]</v>
+ <v>Key = rsa_private() | dss_private() | [ecdh_private(),ecdh_params()] | engine_key_ref()</v>
<v>Options = sign_options()</v>
</type>
<desc>
@@ -893,7 +929,7 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre>
or it is the hashed value of "cleartext" i.e. the digest (plaintext).</d>
<v>DigestType = rsa_digest_type() | dss_digest_type() | ecdsa_digest_type()</v>
<v>Signature = binary()</v>
- <v>Key = rsa_public() | dss_public() | [ecdh_public(),ecdh_params()]</v>
+ <v>Key = rsa_public() | dss_public() | [ecdh_public(),ecdh_params()] | engine_key_ref()</v>
<v>Options = sign_options()</v>
</type>
<desc>
@@ -905,6 +941,124 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre>
</desc>
</func>
+ <!-- Engine functions -->
+ <func>
+ <name>engine_get_all_methods() -> Result</name>
+ <fsummary>Return list of all possible engine methods</fsummary>
+ <type>
+ <v>Result = [EngineMethod::atom()]</v>
+ </type>
+ <desc>
+ <p>
+ Returns a list of all possible engine methods.
+ </p>
+ <p>
+ May throw exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ <p>
+ See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ in the User's Guide.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>engine_load(EngineId, PreCmds, PostCmds) -> Result</name>
+ <fsummary>Dynamical load an encryption engine</fsummary>
+ <type>
+ <v>EngineId = unicode:chardata()</v>
+ <v>PreCmds, PostCmds = [{unicode:chardata(), unicode:chardata()}]</v>
+ <v>Result = {ok, Engine::term()} | {error, Reason::term()}</v>
+ </type>
+ <desc>
+ <p>
+ Loads the OpenSSL engine given by <c>EngineId</c> if it is available and then returns ok and
+ an engine handle. This function is the same as calling <c>engine_load/4</c> with
+ <c>EngineMethods</c> set to a list of all the possible methods. An error tuple is
+ returned if the engine can't be loaded.
+ </p>
+ <p>
+ The function throws a badarg if the parameters are in wrong format.
+ It may also throw the exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ <p>
+ See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ in the User's Guide.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>engine_load(EngineId, PreCmds, PostCmds, EngineMethods) -> Result</name>
+ <fsummary>Dynamical load an encryption engine</fsummary>
+ <type>
+ <v>EngineId = unicode:chardata()</v>
+ <v>PreCmds, PostCmds = [{unicode:chardata(), unicode:chardata()}]</v>
+ <v>EngineMethods = [engine_method_type()]</v>
+ <v>Result = {ok, Engine::term()} | {error, Reason::term()}</v>
+ </type>
+ <desc>
+ <p>
+ Loads the OpenSSL engine given by <c>EngineId</c> if it is available and then returns ok and
+ an engine handle. An error tuple is returned if the engine can't be loaded.
+ </p>
+ <p>
+ The function throws a badarg if the parameters are in wrong format.
+ It may also throw the exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ <p>
+ See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ in the User's Guide.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>engine_unload(Engine) -> Result</name>
+ <fsummary>Dynamical load an encryption engine</fsummary>
+ <type>
+ <v>Engine = term()</v>
+ <v>Result = ok | {error, Reason::term()}</v>
+ </type>
+ <desc>
+ <p>
+ Unloads the OpenSSL engine given by <c>EngineId</c>.
+ An error tuple is returned if the engine can't be unloaded.
+ </p>
+ <p>
+ The function throws a badarg if the parameter is in wrong format.
+ It may also throw the exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ <p>
+ See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ in the User's Guide.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>engine_list() -> Result</name>
+ <fsummary>List the known engine ids</fsummary>
+ <type>
+ <v>Result = [EngineId::unicode:chardata()]</v>
+ </type>
+ <desc>
+ <p>List the id's of all engines in OpenSSL's internal list.</p>
+ <p>
+ It may also throw the exception notsup in case there is
+ no engine support in the underlying OpenSSL implementation.
+ </p>
+ <p>
+ See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+ in the User's Guide.
+ </p>
+ </desc>
+ </func>
+
</funcs>
<!-- Maybe put this in the users guide -->
@@ -979,4 +1133,3 @@ _FloatValue = rand:uniform(). % [0.0; 1.0[</pre>
<!-- </p> -->
<!-- </section> -->
</erlref>
-
diff --git a/lib/crypto/doc/src/engine_load.xml b/lib/crypto/doc/src/engine_load.xml
new file mode 100644
index 0000000000..e5c3f5d561
--- /dev/null
+++ b/lib/crypto/doc/src/engine_load.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2017</year><year>2017</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+ </legalnotice>
+ <title>Engine Load</title>
+ <prepared>Lars Thorsén</prepared>
+ <date>2017-08-22</date>
+ <file>engine_load.xml</file>
+ </header>
+ <p>
+ <marker id="engine_load"></marker>
+ This chapter describes the support for loading encryption engines in the crypto application.
+ </p>
+
+ <section>
+ <title>Background</title>
+ <p>
+ OpenSSL exposes an Engine API, which makes it possible to plug in alternative
+ implementations for some or all of the cryptographic operations implemented by OpenSSL.
+ When configured appropriately, OpenSSL calls the engine's implementation of these
+ operations instead of its own.
+ </p>
+ <p>
+ Typically, OpenSSL engines provide a hardware implementation of specific cryptographic
+ operations. The hardware implementation usually offers improved performance over its
+ software-based counterpart, which is known as cryptographic acceleration.
+ </p>
+ </section>
+
+ <section>
+ <title>Use Cases</title>
+ <section>
+ <title>Dynamically load an engine from default directory</title>
+ <p>
+ If the engine is located in the OpenSSL/LibreSSL installation <c>engines</c> directory.
+ </p>
+ <code>
+1> {ok, Engine} = crypto:engine_load(&lt;&lt;"otp_test_engine">>, [], []).
+ {ok, #Ref}</code>
+ <note>
+ <p>The file name requirement on the engine dynamic library can differ between SSL versions.</p>
+ </note>
+ </section>
+
+ <section>
+ <title>Load an engine with the dynamic engine</title>
+ <p>
+ Load an engine with the help of the dynamic engine by giving the path to the library.
+ </p>
+ <code>
+ 2> {ok, Engine} = crypto:engine_load(&lt;&lt;"dynamic">>,
+ [{&lt;&lt;"SO_PATH">>,
+ &lt;&lt;"/some/path/otp_test_engine.so">>},
+ {&lt;&lt;"ID">>, &lt;&lt;"MD5">>},
+ &lt;&lt;"LOAD">>],
+ []).
+ {ok, #Ref}</code>
+ <note>
+ <p>The dynamic engine is not supported in LibreSSL from version 2.2.1</p>
+ </note>
+ </section>
+
+ <section>
+ <title>Load an engine and replace some methods</title>
+ <p>
+ Load an engine with the help of the dynamic engine and just
+ replace some engine methods.
+ </p>
+ <code>
+ 3> Methods = crypto:engine_get_all_methods() -- [engine_method_dh,engine_method_rand,
+engine_method_ciphers,engine_method_digests, engine_method_store,
+engine_method_pkey_meths, engine_method_pkey_asn1_meths].
+[engine_method_rsa,engine_method_dsa,
+ engine_method_ecdh,engine_method_ecdsa]
+ 4> {ok, Engine} = crypto:engine_load(&lt;&lt;"dynamic">>,
+ [{&lt;&lt;"SO_PATH">>,
+ &lt;&lt;"/some/path/otp_test_engine.so">>},
+ {&lt;&lt;"ID">>, &lt;&lt;"MD5">>},
+ &lt;&lt;"LOAD">>],
+ [],
+ Methods).
+ {ok, #Ref}</code>
+ </section>
+
+ <section>
+ <title>List all engines currently loaded</title>
+ <code>
+ 5> crypto:engine_list().
+[&lt;&lt;"dynamic">>, &lt;&lt;"MD5">>]</code>
+ </section>
+
+ </section>
+</chapter>
diff --git a/lib/crypto/doc/src/usersguide.xml b/lib/crypto/doc/src/usersguide.xml
index 7971aefff4..f637a1db79 100644
--- a/lib/crypto/doc/src/usersguide.xml
+++ b/lib/crypto/doc/src/usersguide.xml
@@ -11,7 +11,7 @@
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
@@ -19,7 +19,7 @@
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.
-
+
</legalnotice>
<title>Crypto User's Guide</title>
@@ -48,5 +48,5 @@
</description>
<xi:include href="licenses.xml"/>
<xi:include href="fips.xml"/>
+ <xi:include href="engine_load.xml"/>
</part>
-
diff --git a/lib/crypto/src/Makefile b/lib/crypto/src/Makefile
index aea8a5a71c..edad0e6b61 100644
--- a/lib/crypto/src/Makefile
+++ b/lib/crypto/src/Makefile
@@ -39,8 +39,7 @@ MODULES= \
crypto \
crypto_ec_curves
-HRL_FILES=
-
+HRL_FILES=
ERL_FILES= $(MODULES:%=%.erl)
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
@@ -56,16 +55,16 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += -DCRYPTO_VSN=\"$(VSN)\" -Werror
+ERL_COMPILE_FLAGS += -DCRYPTO_VSN=\"$(VSN)\" -Werror -I../include
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-debug opt valgrind: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+debug opt valgrind: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
- rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
@@ -78,7 +77,7 @@ docs:
# ----------------------------------------------------
# Release Target
-# ----------------------------------------------------
+# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
@@ -89,10 +88,3 @@ release_spec: opt
$(APPUP_TARGET) "$(RELSYSDIR)/ebin"
release_docs_spec:
-
-
-
-
-
-
-
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index f9c4f7b71d..0d39dcc76e 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -42,12 +42,26 @@
-export([public_encrypt/4, private_decrypt/4]).
-export([private_encrypt/4, public_decrypt/4]).
-export([dh_generate_parameters/2, dh_check/1]). %% Testing see
+-export([privkey_to_pubkey/2]).
-export([ec_curve/1, ec_curves/0]).
-export([rand_seed/1]).
+%% Engine
+-export([
+ engine_get_all_methods/0,
+ engine_load/3,
+ engine_load/4,
+ engine_unload/1,
+ engine_list/0
+ ]).
+
+-export_type([engine_ref/0,
+ key_id/0,
+ password/0
+ ]).
-%% Private. For tests.
--export([packed_openssl_version/4]).
+%% Private. For tests.
+-export([packed_openssl_version/4, engine_methods_convert_to_bitmask/2, get_test_engine/0]).
-deprecated({rand_uniform, 2, next_major_release}).
@@ -421,13 +435,24 @@ sign(Algorithm, Type, Data, Key, Options) ->
end.
+
+-type key_id() :: string() | binary() .
+-type password() :: string() | binary() .
+
+-type engine_key_ref() :: #{engine := engine_ref(),
+ key_id := key_id(),
+ password => password(),
+ term() => term()
+ }.
+
-type pk_algs() :: rsa | ecdsa | dss .
--type pk_opt() :: list() | rsa_padding() .
+-type pk_key() :: engine_key_ref() | [integer() | binary()] .
+-type pk_opt() :: list() | rsa_padding() .
--spec public_encrypt(pk_algs(), binary(), [binary()], pk_opt()) -> binary().
--spec public_decrypt(pk_algs(), binary(), [integer() | binary()], pk_opt()) -> binary().
--spec private_encrypt(pk_algs(), binary(), [integer() | binary()], pk_opt()) -> binary().
--spec private_decrypt(pk_algs(), binary(), [integer() | binary()], pk_opt()) -> binary().
+-spec public_encrypt(pk_algs(), binary(), pk_key(), pk_opt()) -> binary().
+-spec public_decrypt(pk_algs(), binary(), pk_key(), pk_opt()) -> binary().
+-spec private_encrypt(pk_algs(), binary(), pk_key(), pk_opt()) -> binary().
+-spec private_decrypt(pk_algs(), binary(), pk_key(), pk_opt()) -> binary().
public_encrypt(Algorithm, In, Key, Options) when is_list(Options) ->
case pkey_crypt_nif(Algorithm, In, format_pkey(Algorithm, Key), Options, false, true) of
@@ -568,10 +593,145 @@ compute_key(ecdh, Others, My, Curve) ->
nif_curve_params(Curve),
ensure_int_as_bin(My)).
+%%======================================================================
+%% Engine functions
+%%======================================================================
+%%----------------------------------------------------------------------
+%% Function: engine_get_all_methods/0
+%%----------------------------------------------------------------------
+-type engine_method_type() :: engine_method_rsa | engine_method_dsa | engine_method_dh |
+ engine_method_rand | engine_method_ecdh | engine_method_ecdsa |
+ engine_method_ciphers | engine_method_digests | engine_method_store |
+ engine_method_pkey_meths | engine_method_pkey_asn1_meths |
+ engine_method_ec.
+
+-type engine_ref() :: term().
+
+-spec engine_get_all_methods() ->
+ [engine_method_type()].
+engine_get_all_methods() ->
+ notsup_to_error(engine_get_all_methods_nif()).
+
+%%----------------------------------------------------------------------
+%% Function: engine_load/3
+%%----------------------------------------------------------------------
+-spec engine_load(EngineId::unicode:chardata(),
+ PreCmds::[{unicode:chardata(), unicode:chardata()}],
+ PostCmds::[{unicode:chardata(), unicode:chardata()}]) ->
+ {ok, Engine::engine_ref()} | {error, Reason::term()}.
+engine_load(EngineId, PreCmds, PostCmds) when is_list(PreCmds), is_list(PostCmds) ->
+ engine_load(EngineId, PreCmds, PostCmds, engine_get_all_methods()).
+
+%%----------------------------------------------------------------------
+%% Function: engine_load/4
+%%----------------------------------------------------------------------
+-spec engine_load(EngineId::unicode:chardata(),
+ PreCmds::[{unicode:chardata(), unicode:chardata()}],
+ PostCmds::[{unicode:chardata(), unicode:chardata()}],
+ EngineMethods::[engine_method_type()]) ->
+ {ok, Engine::term()} | {error, Reason::term()}.
+engine_load(EngineId, PreCmds, PostCmds, EngineMethods) when is_list(PreCmds),
+ is_list(PostCmds) ->
+ try
+ ok = notsup_to_error(engine_load_dynamic_nif()),
+ case notsup_to_error(engine_by_id_nif(ensure_bin_chardata(EngineId))) of
+ {ok, Engine} ->
+ ok = engine_load_1(Engine, PreCmds, PostCmds, EngineMethods),
+ {ok, Engine};
+ {error, Error1} ->
+ {error, Error1}
+ end
+ catch
+ throw:Error2 ->
+ Error2
+ end.
+
+engine_load_1(Engine, PreCmds, PostCmds, EngineMethods) ->
+ try
+ ok = engine_nif_wrapper(engine_ctrl_cmd_strings_nif(Engine, ensure_bin_cmds(PreCmds))),
+ ok = engine_nif_wrapper(engine_add_nif(Engine)),
+ ok = engine_nif_wrapper(engine_init_nif(Engine)),
+ engine_load_2(Engine, PostCmds, EngineMethods),
+ ok
+ catch
+ throw:Error ->
+ %% The engine couldn't initialise, release the structural reference
+ ok = engine_free_nif(Engine),
+ throw(Error)
+ end.
+
+engine_load_2(Engine, PostCmds, EngineMethods) ->
+ try
+ ok = engine_nif_wrapper(engine_ctrl_cmd_strings_nif(Engine, ensure_bin_cmds(PostCmds))),
+ [ok = engine_nif_wrapper(engine_register_nif(Engine, engine_method_atom_to_int(Method))) ||
+ Method <- EngineMethods],
+ ok
+ catch
+ throw:Error ->
+ %% The engine registration failed, release the functional reference
+ ok = engine_finish_nif(Engine),
+ throw(Error)
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: engine_unload/1
+%%----------------------------------------------------------------------
+-spec engine_unload(Engine::term()) ->
+ ok | {error, Reason::term()}.
+engine_unload(Engine) ->
+ engine_unload(Engine, engine_get_all_methods()).
+
+-spec engine_unload(Engine::term(), EngineMethods::[engine_method_type()]) ->
+ ok | {error, Reason::term()}.
+engine_unload(Engine, EngineMethods) ->
+ try
+ [ok = engine_nif_wrapper(engine_unregister_nif(Engine, engine_method_atom_to_int(Method))) ||
+ Method <- EngineMethods],
+ ok = engine_nif_wrapper(engine_remove_nif(Engine)),
+ %% Release the functional reference from engine_init_nif
+ ok = engine_nif_wrapper(engine_finish_nif(Engine)),
+ %% Release the structural reference from engine_by_id_nif
+ ok = engine_nif_wrapper(engine_free_nif(Engine))
+ catch
+ throw:Error ->
+ Error
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: engine_list/0
+%%----------------------------------------------------------------------
+-spec engine_list() ->
+ [EngineId::binary()].
+engine_list() ->
+ case notsup_to_error(engine_get_first_nif()) of
+ {ok, <<>>} ->
+ [];
+ {ok, Engine} ->
+ case notsup_to_error(engine_get_id_nif(Engine)) of
+ {ok, <<>>} ->
+ engine_list(Engine, []);
+ {ok, EngineId} ->
+ engine_list(Engine, [EngineId])
+ end
+ end.
+
+engine_list(Engine0, IdList) ->
+ case notsup_to_error(engine_get_next_nif(Engine0)) of
+ {ok, <<>>} ->
+ lists:reverse(IdList);
+ {ok, Engine1} ->
+ case notsup_to_error(engine_get_id_nif(Engine1)) of
+ {ok, <<>>} ->
+ engine_list(Engine1, IdList);
+ {ok, EngineId} ->
+ engine_list(Engine1, [EngineId |IdList])
+ end
+ end.
+
+
%%--------------------------------------------------------------------
%%% On load
%%--------------------------------------------------------------------
-
on_load() ->
LibBaseName = "crypto",
PrivDir = code:priv_dir(crypto),
@@ -631,12 +791,12 @@ path2bin(Path) when is_list(Path) ->
end.
%%--------------------------------------------------------------------
-%%% Internal functions
+%%% Internal functions
%%--------------------------------------------------------------------
max_bytes() ->
?MAX_BYTES_TO_NIF.
-notsup_to_error(notsup) ->
+notsup_to_error(notsup) ->
erlang:error(notsup);
notsup_to_error(Other) ->
Other.
@@ -760,7 +920,7 @@ do_stream_decrypt({rc4, State0}, Data) ->
%%
-%% AES - in counter mode (CTR) with state maintained for multi-call streaming
+%% AES - in counter mode (CTR) with state maintained for multi-call streaming
%%
-type ctr_state() :: { iodata(), binary(), binary(), integer() } | binary().
@@ -769,11 +929,11 @@ do_stream_decrypt({rc4, State0}, Data) ->
{ ctr_state(), binary() }.
-spec aes_ctr_stream_decrypt(ctr_state(), binary()) ->
{ ctr_state(), binary() }.
-
+
aes_ctr_stream_init(_Key, _IVec) -> ?nif_stub.
aes_ctr_stream_encrypt(_State, _Data) -> ?nif_stub.
aes_ctr_stream_decrypt(_State, _Cipher) -> ?nif_stub.
-
+
%%
%% RC4 - symmetric stream cipher
%%
@@ -858,22 +1018,22 @@ pkey_verify_nif(_Algorithm, _Type, _Data, _Signature, _Key, _Options) -> ?nif_st
rsa_generate_key_nif(_Bits, _Exp) -> ?nif_stub.
%% DH Diffie-Hellman functions
-%%
+%%
%% Generate (and check) Parameters is not documented because they are implemented
%% for testing (and offline parameter generation) only.
-%% From the openssl doc:
+%% From the openssl doc:
%% DH_generate_parameters() may run for several hours before finding a suitable prime.
-%% Thus dh_generate_parameters may in this implementation block
+%% Thus dh_generate_parameters may in this implementation block
%% the emulator for several hours.
%%
-%% usage: dh_generate_parameters(1024, 2 or 5) ->
+%% usage: dh_generate_parameters(1024, 2 or 5) ->
%% [Prime=mpint(), SharedGenerator=mpint()]
dh_generate_parameters(PrimeLen, Generator) ->
case dh_generate_parameters_nif(PrimeLen, Generator) of
error -> erlang:error(generation_failed, [PrimeLen,Generator]);
Ret -> Ret
- end.
+ end.
dh_generate_parameters_nif(_PrimeLen, _Generator) -> ?nif_stub.
@@ -899,6 +1059,16 @@ ec_curves() ->
ec_curve(X) ->
crypto_ec_curves:curve(X).
+
+privkey_to_pubkey(Alg, EngineMap) when Alg == rsa; Alg == dss; Alg == ecdsa ->
+ case privkey_to_pubkey_nif(Alg, format_pkey(Alg,EngineMap)) of
+ [_|_]=L -> map_ensure_bin_as_int(L);
+ X -> X
+ end.
+
+privkey_to_pubkey_nif(_Alg, _EngineMap) -> ?nif_stub.
+
+
%%
%% EC
%%
@@ -966,6 +1136,19 @@ ensure_int_as_bin(Int) when is_integer(Int) ->
ensure_int_as_bin(Bin) ->
Bin.
+map_ensure_bin_as_int(List) when is_list(List) ->
+ lists:map(fun ensure_bin_as_int/1, List).
+
+ensure_bin_as_int(Bin) when is_binary(Bin) ->
+ bin_to_int(Bin);
+ensure_bin_as_int(E) ->
+ E.
+
+format_pkey(_Alg, #{engine:=_, key_id:=T}=M) when is_binary(T) -> format_pwd(M);
+format_pkey(_Alg, #{engine:=_, key_id:=T}=M) when is_list(T) -> format_pwd(M#{key_id:=list_to_binary(T)});
+format_pkey(_Alg, #{engine:=_ }=M) -> error({bad_key_id, M});
+format_pkey(_Alg, #{}=M) -> error({bad_engine_map, M});
+%%%
format_pkey(rsa, Key) ->
map_ensure_int_as_bin(Key);
format_pkey(ecdsa, [Key, Curve]) ->
@@ -975,6 +1158,9 @@ format_pkey(dss, Key) ->
format_pkey(_, Key) ->
Key.
+format_pwd(#{password := Pwd}=M) when is_list(Pwd) -> M#{password := list_to_binary(Pwd)};
+format_pwd(M) -> M.
+
%%--------------------------------------------------------------------
%%
-type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' | 'rsa_no_padding'.
@@ -985,7 +1171,7 @@ pkey_crypt_nif(_Algorithm, _In, _Key, _Options, _IsPrivate, _IsEncrypt) -> ?nif_
%% MP representaion (SSH2)
mpint(X) when X < 0 -> mpint_neg(X);
mpint(X) -> mpint_pos(X).
-
+
-define(UINT32(X), X:32/unsigned-big-integer).
@@ -993,7 +1179,7 @@ mpint_neg(X) ->
Bin = int_to_bin_neg(X, []),
Sz = byte_size(Bin),
<<?UINT32(Sz), Bin/binary>>.
-
+
mpint_pos(X) ->
Bin = int_to_bin_pos(X, []),
<<MSB,_/binary>> = Bin,
@@ -1015,7 +1201,6 @@ erlint(<<MPIntSize:32/integer,MPIntValue/binary>>) ->
%%
mod_exp_nif(_Base,_Exp,_Mod,_bin_hdr) -> ?nif_stub.
-
%%%----------------------------------------------------------------
%% 9470495 == V(0,9,8,zh).
%% 268435615 == V(1,0,0,i).
@@ -1026,3 +1211,92 @@ packed_openssl_version(MAJ, MIN, FIX, P0) ->
P1 = atom_to_list(P0),
P = lists:sum([C-$a||C<-P1]),
((((((((MAJ bsl 8) bor MIN) bsl 8 ) bor FIX) bsl 8) bor (P+1)) bsl 4) bor 16#f).
+
+%%--------------------------------------------------------------------
+%% Engine nifs
+engine_by_id_nif(_EngineId) -> ?nif_stub.
+engine_init_nif(_Engine) -> ?nif_stub.
+engine_finish_nif(_Engine) -> ?nif_stub.
+engine_free_nif(_Engine) -> ?nif_stub.
+engine_load_dynamic_nif() -> ?nif_stub.
+engine_ctrl_cmd_strings_nif(_Engine, _Cmds) -> ?nif_stub.
+engine_add_nif(_Engine) -> ?nif_stub.
+engine_remove_nif(_Engine) -> ?nif_stub.
+engine_register_nif(_Engine, _EngineMethod) -> ?nif_stub.
+engine_unregister_nif(_Engine, _EngineMethod) -> ?nif_stub.
+engine_get_first_nif() -> ?nif_stub.
+engine_get_next_nif(_Engine) -> ?nif_stub.
+engine_get_id_nif(_Engine) -> ?nif_stub.
+engine_get_all_methods_nif() -> ?nif_stub.
+
+%%--------------------------------------------------------------------
+%% Engine internals
+engine_nif_wrapper(ok) ->
+ ok;
+engine_nif_wrapper(notsup) ->
+ erlang:error(notsup);
+engine_nif_wrapper({error, Error}) ->
+ throw({error, Error}).
+
+ensure_bin_chardata(CharData) when is_binary(CharData) ->
+ CharData;
+ensure_bin_chardata(CharData) ->
+ unicode:characters_to_binary(CharData).
+
+ensure_bin_cmds(CMDs) ->
+ ensure_bin_cmds(CMDs, []).
+
+ensure_bin_cmds([], Acc) ->
+ lists:reverse(Acc);
+ensure_bin_cmds([{Key, Value} |CMDs], Acc) ->
+ ensure_bin_cmds(CMDs, [{ensure_bin_chardata(Key), ensure_bin_chardata(Value)} | Acc]);
+ensure_bin_cmds([Key | CMDs], Acc) ->
+ ensure_bin_cmds(CMDs, [{ensure_bin_chardata(Key), <<"">>} | Acc]).
+
+engine_methods_convert_to_bitmask([], BitMask) ->
+ BitMask;
+engine_methods_convert_to_bitmask(engine_method_all, _BitMask) ->
+ 16#FFFF;
+engine_methods_convert_to_bitmask(engine_method_none, _BitMask) ->
+ 16#0000;
+engine_methods_convert_to_bitmask([M |Ms], BitMask) ->
+ engine_methods_convert_to_bitmask(Ms, BitMask bor engine_method_atom_to_int(M)).
+
+engine_method_atom_to_int(engine_method_rsa) -> 16#0001;
+engine_method_atom_to_int(engine_method_dsa) -> 16#0002;
+engine_method_atom_to_int(engine_method_dh) -> 16#0004;
+engine_method_atom_to_int(engine_method_rand) -> 16#0008;
+engine_method_atom_to_int(engine_method_ecdh) -> 16#0010;
+engine_method_atom_to_int(engine_method_ecdsa) -> 16#0020;
+engine_method_atom_to_int(engine_method_ciphers) -> 16#0040;
+engine_method_atom_to_int(engine_method_digests) -> 16#0080;
+engine_method_atom_to_int(engine_method_store) -> 16#0100;
+engine_method_atom_to_int(engine_method_pkey_meths) -> 16#0200;
+engine_method_atom_to_int(engine_method_pkey_asn1_meths) -> 16#0400;
+engine_method_atom_to_int(engine_method_ec) -> 16#0800;
+engine_method_atom_to_int(X) ->
+ erlang:error(badarg, [X]).
+
+get_test_engine() ->
+ Type = erlang:system_info(system_architecture),
+ LibDir = filename:join([code:priv_dir(crypto), "lib"]),
+ ArchDir = filename:join([LibDir, Type]),
+ case filelib:is_dir(ArchDir) of
+ true -> check_otp_test_engine(ArchDir);
+ false -> check_otp_test_engine(LibDir)
+ end.
+
+check_otp_test_engine(LibDir) ->
+ case filelib:wildcard("otp_test_engine*", LibDir) of
+ [] ->
+ {error, notexist};
+ [LibName] ->
+ LibPath = filename:join(LibDir,LibName),
+ case filelib:is_file(LibPath) of
+ true ->
+ {ok, unicode:characters_to_binary(LibPath)};
+ false ->
+ {error, notexist}
+ end
+ end.
+
diff --git a/lib/crypto/test/Makefile b/lib/crypto/test/Makefile
index 138081d386..e046a25338 100644
--- a/lib/crypto/test/Makefile
+++ b/lib/crypto/test/Makefile
@@ -7,7 +7,8 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES = \
blowfish_SUITE \
- crypto_SUITE
+ crypto_SUITE \
+ engine_SUITE
ERL_FILES= $(MODULES:%=%.erl)
@@ -27,7 +28,7 @@ RELSYSDIR = $(RELEASE_PATH)/crypto_test
# FLAGS
# ----------------------------------------------------
ERL_MAKE_FLAGS +=
-ERL_COMPILE_FLAGS +=
+ERL_COMPILE_FLAGS += +nowarn_export_all
EBIN = .
MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile)
@@ -77,7 +78,7 @@ release_spec:
release_tests_spec: $(TEST_TARGET)
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) crypto.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)"
- @tar cfh - crypto_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
+ @tar cfh - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
chmod -R u+w "$(RELSYSDIR)"
release_docs_spec:
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 69f02d3da6..6dab459df6 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -198,7 +198,7 @@ init_per_suite(Config) ->
%% This is NOT how you want to do seeding, it is just here
%% to make the tests pass. Check your OS manual for how you
%% really want to seed.
- {H,M,L} = erlang:now(),
+ {H,M,L} = erlang:timestamp(),
Bin = <<H:24,M:20,L:20>>,
crypto:rand_seed(<< <<Bin/binary>> || _ <- lists:seq(1,16) >>),
Config
diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl
new file mode 100644
index 0000000000..72bd59f8ab
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE.erl
@@ -0,0 +1,513 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017-2017. 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%
+%%
+%%
+
+-module(engine_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds, 10}}
+ ].
+
+all() ->
+ [
+ get_all_possible_methods,
+ engine_load_all_methods,
+ engine_load_some_methods,
+ bad_arguments,
+ unknown_engine,
+ pre_command_fail_bad_value,
+ pre_command_fail_bad_key,
+ failed_engine_init,
+ {group, engine_stored_key}
+ ].
+
+groups() ->
+ [{engine_stored_key, [],
+ [sign_verify_rsa,
+ sign_verify_dsa,
+ sign_verify_ecdsa,
+ sign_verify_rsa_pwd,
+ priv_encrypt_pub_decrypt_rsa,
+ priv_encrypt_pub_decrypt_rsa_pwd,
+ pub_encrypt_priv_decrypt_rsa,
+ pub_encrypt_priv_decrypt_rsa_pwd,
+ get_pub_from_priv_key_dsa,
+ get_pub_from_priv_key_ecdsa
+ ]}].
+
+
+init_per_suite(Config) ->
+ try crypto:start() of
+ ok ->
+ Config;
+ {error,{already_started,crypto}} ->
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+init_per_group(engine_stored_key, Config) ->
+ case load_storage_engine(Config) of
+ {ok, E} ->
+ KeyDir = key_dir(Config),
+ [{storage_engine,E}, {storage_dir,KeyDir} | Config];
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {error, notsup} ->
+ {skip, "Engine not supported on this OpenSSL version"};
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"};
+ Other ->
+ ct:log("Engine load failed: ~p",[Other]),
+ {fail, "Engine load failed"}
+ end;
+init_per_group(_Group, Config0) ->
+ Config0.
+
+end_per_group(engine_stored_key, Config) ->
+ case proplists:get_value(storage_engine, Config) of
+ undefined ->
+ ok;
+ E ->
+ ok = crypto:engine_unload(E)
+ end;
+end_per_group(_, _) ->
+ ok.
+
+%%--------------------------------------------------------------------
+init_per_testcase(_Case, Config) ->
+ Config.
+end_per_testcase(_Case, _Config) ->
+ ok.
+
+%%-------------------------------------------------------------------------
+%% Test cases starts here.
+%%-------------------------------------------------------------------------
+get_all_possible_methods() ->
+ [{doc, "Just fetch all possible engine methods supported."}].
+
+get_all_possible_methods(Config) when is_list(Config) ->
+ try
+ List = crypto:engine_get_all_methods(),
+ ct:log("crypto:engine_get_all_methods() -> ~p\n", [List]),
+ ok
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+engine_load_all_methods()->
+ [{doc, "Use a dummy md5 engine that does not implement md5"
+ "but rather returns a static binary to test that crypto:engine_load "
+ "functions works."}].
+
+engine_load_all_methods(Config) when is_list(Config) ->
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ try
+ Md5Hash1 = <<106,30,3,246,166,222,229,158,244,217,241,179,50,232,107,109>>,
+ Md5Hash1 = crypto:hash(md5, "Don't panic"),
+ Md5Hash2 = <<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ []) of
+ {ok, E} ->
+ case crypto:hash(md5, "Don't panic") of
+ Md5Hash1 ->
+ ct:fail(fail_to_load_still_original_engine);
+ Md5Hash2 ->
+ ok;
+ _ ->
+ ct:fail(fail_to_load_engine)
+ end,
+ ok = crypto:engine_unload(E),
+ case crypto:hash(md5, "Don't panic") of
+ Md5Hash2 ->
+ ct:fail(fail_to_unload_still_test_engine);
+ Md5Hash1 ->
+ ok;
+ _ ->
+ ct:fail(fail_to_unload_engine)
+ end;
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end
+ end.
+
+engine_load_some_methods()->
+ [{doc, "Use a dummy md5 engine that does not implement md5"
+ "but rather returns a static binary to test that crypto:engine_load "
+ "functions works."}].
+
+engine_load_some_methods(Config) when is_list(Config) ->
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ try
+ Md5Hash1 = <<106,30,3,246,166,222,229,158,244,217,241,179,50,232,107,109>>,
+ Md5Hash1 = crypto:hash(md5, "Don't panic"),
+ Md5Hash2 = <<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
+ EngineMethods = crypto:engine_get_all_methods() --
+ [engine_method_dh,engine_method_rand,
+ engine_method_ciphers, engine_method_store,
+ engine_method_pkey_meths, engine_method_pkey_asn1_meths],
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ [],
+ EngineMethods) of
+ {ok, E} ->
+ case crypto:hash(md5, "Don't panic") of
+ Md5Hash1 ->
+ ct:fail(fail_to_load_engine_still_original);
+ Md5Hash2 ->
+ ok;
+ _ ->
+ ct:fail(fail_to_load_engine)
+ end,
+ ok = crypto:engine_unload(E),
+ case crypto:hash(md5, "Don't panic") of
+ Md5Hash2 ->
+ ct:fail(fail_to_unload_still_test_engine);
+ Md5Hash1 ->
+ ok;
+ _ ->
+ ct:fail(fail_to_unload_engine)
+ end;
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end
+ end.
+
+%%-------------------------------------------------------------------------
+%% Error cases
+bad_arguments()->
+ [{doc, "Test different arguments in bad format."}].
+
+bad_arguments(Config) when is_list(Config) ->
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ try
+ try
+ crypto:engine_load(fail_engine, [], [])
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
+ crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ 1,
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ [])
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
+ crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ {'ID', <<"MD5">>},
+ <<"LOAD">>],
+ [])
+ catch
+ error:badarg ->
+ ok
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end
+ end.
+
+unknown_engine() ->
+ [{doc, "Try to load a non existent engine."}].
+
+unknown_engine(Config) when is_list(Config) ->
+ try
+ {error, bad_engine_id} = crypto:engine_load(<<"fail_engine">>, [], []),
+ ok
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+pre_command_fail_bad_value() ->
+ [{doc, "Test pre command due to bad value"}].
+
+pre_command_fail_bad_value(Config) when is_list(Config) ->
+ DataDir = unicode:characters_to_binary(code:priv_dir(crypto)),
+ try
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>,
+ <<DataDir/binary, <<"/libfail_engine.so">>/binary >>},
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ []) of
+ {error, ctrl_cmd_failed} ->
+ ok;
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+pre_command_fail_bad_key() ->
+ [{doc, "Test pre command due to bad key"}].
+
+pre_command_fail_bad_key(Config) when is_list(Config) ->
+ try
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_WRONG_PATH">>, Engine},
+ {<<"ID">>, <<"MD5">>},
+ <<"LOAD">>],
+ []) of
+ {error, ctrl_cmd_failed} ->
+ ok;
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+failed_engine_init()->
+ [{doc, "Test failing engine init due to missed pre command"}].
+
+failed_engine_init(Config) when is_list(Config) ->
+ try
+ case crypto:get_test_engine() of
+ {error, notexist} ->
+ {skip, "OTP Test engine not found"};
+ {ok, Engine} ->
+ case crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ {<<"ID">>, <<"MD5">>}],
+ []) of
+ {error, add_engine_failed} ->
+ ok;
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"}
+ end
+ end
+ catch
+ error:notsup ->
+ {skip, "Engine not supported on this OpenSSL version"}
+ end.
+
+%%%----------------------------------------------------------------
+%%% Pub/priv key storage tests. Thoose are for testing the crypto.erl
+%%% support for using priv/pub keys stored in an engine.
+
+sign_verify_rsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key.pem")},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key.pem")},
+ sign_verify(rsa, sha, Priv, Pub).
+
+sign_verify_dsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "dsa_private_key.pem")},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "dsa_public_key.pem")},
+ sign_verify(dss, sha, Priv, Pub).
+
+sign_verify_ecdsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "ecdsa_private_key.pem")},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "ecdsa_public_key.pem")},
+ sign_verify(ecdsa, sha, Priv, Pub).
+
+sign_verify_rsa_pwd(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key_pwd.pem"),
+ password => "password"},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key_pwd.pem")},
+ sign_verify(rsa, sha, Priv, Pub).
+
+priv_encrypt_pub_decrypt_rsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key.pem")},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key.pem")},
+ priv_enc_pub_dec(rsa, Priv, Pub, rsa_pkcs1_padding).
+
+priv_encrypt_pub_decrypt_rsa_pwd(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key_pwd.pem"),
+ password => "password"},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key_pwd.pem")},
+ priv_enc_pub_dec(rsa, Priv, Pub, rsa_pkcs1_padding).
+
+pub_encrypt_priv_decrypt_rsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key.pem")},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key.pem")},
+ pub_enc_priv_dec(rsa, Pub, Priv, rsa_pkcs1_padding).
+
+pub_encrypt_priv_decrypt_rsa_pwd(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key.pem"),
+ password => "password"},
+ Pub = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_public_key.pem")},
+ pub_enc_priv_dec(rsa, Pub, Priv, rsa_pkcs1_padding).
+
+get_pub_from_priv_key_rsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "rsa_private_key.pem")},
+ Pub = crypto:privkey_to_pubkey(rsa, Priv),
+ ct:log("rsa Pub = ~p",[Pub]),
+ sign_verify(rsa, sha, Priv, Pub).
+
+get_pub_from_priv_key_dsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "dsa_private_key.pem")},
+ Pub = crypto:privkey_to_pubkey(dss, Priv),
+ ct:log("dsa Pub = ~p",[Pub]),
+ sign_verify(dss, sha, Priv, Pub).
+
+get_pub_from_priv_key_ecdsa(Config) ->
+ Priv = #{engine => engine_ref(Config),
+ key_id => key_id(Config, "ecdsa_private_key.pem")},
+ Pub = crypto:privkey_to_pubkey(ecdsa, Priv),
+ case Pub of
+ notsup -> {skip, "ECDSA not implemented"};
+ _ ->
+ ct:log("ecdsa Pub = ~p",[Pub]),
+ sign_verify(ecdsa, sha, Priv, Pub)
+ end.
+
+%%%================================================================
+%%% Help for engine_stored_pub_priv_keys* test cases
+%%%
+load_storage_engine(_Config) ->
+ case crypto:get_test_engine() of
+ {ok, Engine} ->
+ try crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ <<"LOAD">>],
+ [])
+ catch
+ error:notsup ->
+ {error, notsup}
+ end;
+
+ {error, Error} ->
+ {error, Error}
+ end.
+
+
+key_dir(Config) ->
+ DataDir = unicode:characters_to_binary(proplists:get_value(data_dir, Config)),
+ filename:join(DataDir, "pkcs8").
+
+
+engine_ref(Config) ->
+ proplists:get_value(storage_engine, Config).
+
+key_id(Config, File) ->
+ filename:join(proplists:get_value(storage_dir,Config), File).
+
+pubkey_alg_supported(Alg) ->
+ lists:member(Alg,
+ proplists:get_value(public_keys, crypto:supports())).
+
+
+pub_enc_priv_dec(Alg, KeyEnc, KeyDec, Padding) ->
+ case pubkey_alg_supported(Alg) of
+ true ->
+ PlainText = <<"Hej pĂ¥ dig">>,
+ CryptoText = crypto:public_encrypt(Alg, PlainText, KeyEnc, Padding),
+ case crypto:private_decrypt(Alg, CryptoText, KeyDec, Padding) of
+ PlainText -> ok;
+ _ -> {fail, "Encrypt-decrypt error"}
+ end;
+ false ->
+ {skip, lists:concat([Alg," is not supported by cryptolib"])}
+ end.
+
+priv_enc_pub_dec(Alg, KeyEnc, KeyDec, Padding) ->
+ case pubkey_alg_supported(Alg) of
+ true ->
+ PlainText = <<"Hej pĂ¥ dig">>,
+ CryptoText = crypto:private_encrypt(Alg, PlainText, KeyEnc, Padding),
+ case crypto:public_decrypt(Alg, CryptoText, KeyDec, Padding) of
+ PlainText -> ok;
+ _ -> {fail, "Encrypt-decrypt error"}
+ end;
+ false ->
+ {skip, lists:concat([Alg," is not supported by cryptolib"])}
+ end.
+
+sign_verify(Alg, Sha, KeySign, KeyVerify) ->
+ case pubkey_alg_supported(Alg) of
+ true ->
+ PlainText = <<"Hej pĂ¥ dig">>,
+ Signature = crypto:sign(Alg, Sha, PlainText, KeySign),
+ case crypto:verify(Alg, Sha, PlainText, Signature, KeyVerify) of
+ true -> ok;
+ _ -> {fail, "Sign-verify error"}
+ end;
+ false ->
+ {skip, lists:concat([Alg," is not supported by cryptolib"])}
+ end.
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_private_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_private_key.pem
new file mode 100644
index 0000000000..778ffac675
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_private_key.pem
@@ -0,0 +1,9 @@
+-----BEGIN PRIVATE KEY-----
+MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAMyitTMR7vPbpqyAXJpqnB0AhFwQ
+F87IE+JKFl5bD/MSkhhRV5sM73HUU1ooXY0FjhZ+cdLUCATuZR5ta4ydANqWIcAB
+gX3IwF1B4zf5SXEKTWkUYneL9dOKtiZLtoG28swrk8xMxwX+0fLHkltCEj6FiTW9
+PFrv8GmIfV6DjcI9AhUAqXWbb3RtoN9Ld28fVMhGZrj3LJUCgYEAwnxGHGBMpJaF
+2w7zAw3jHjL8PMYlV6vnufGHQlwF0ZUXJxRsvagMb/X1qACTu2VPYEVoLQGM3cfH
+EhHoQmvSXGAyTfR7Bmn3gf1n/s/DcFbdZduUCZ/rAyIrfd0eSbc1I+kZk85UCsKK
+w/IYdlqcuYa4Cgm2TapT5uEMqH4jhzEEFgIULh8swEUWmU8aJNWsrWl4eCiuUUg=
+-----END PRIVATE KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_public_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_public_key.pem
new file mode 100644
index 0000000000..0fa5428828
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/dsa_public_key.pem
@@ -0,0 +1,12 @@
+-----BEGIN PUBLIC KEY-----
+MIIBtzCCASwGByqGSM44BAEwggEfAoGBAMyitTMR7vPbpqyAXJpqnB0AhFwQF87I
+E+JKFl5bD/MSkhhRV5sM73HUU1ooXY0FjhZ+cdLUCATuZR5ta4ydANqWIcABgX3I
+wF1B4zf5SXEKTWkUYneL9dOKtiZLtoG28swrk8xMxwX+0fLHkltCEj6FiTW9PFrv
+8GmIfV6DjcI9AhUAqXWbb3RtoN9Ld28fVMhGZrj3LJUCgYEAwnxGHGBMpJaF2w7z
+Aw3jHjL8PMYlV6vnufGHQlwF0ZUXJxRsvagMb/X1qACTu2VPYEVoLQGM3cfHEhHo
+QmvSXGAyTfR7Bmn3gf1n/s/DcFbdZduUCZ/rAyIrfd0eSbc1I+kZk85UCsKKw/IY
+dlqcuYa4Cgm2TapT5uEMqH4jhzEDgYQAAoGAXPygOFYdeKgfLmuIC303cESYXvic
+e2GNJomv8vaWLZmbLVVDfwA1fNsuF1hZkWw8f7aYaN9iZ3yl9u4Yk4TbJKkqfJqd
+dgVt288SUqvi+NMHODUzYi9KAOXxupXffZSvdu54gKRaDuFTZ5XNcRqIJWGYlJYg
+NVHF5FPZ4Bk2FYA=
+-----END PUBLIC KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_private_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_private_key.pem
new file mode 100644
index 0000000000..a45522064f
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_private_key.pem
@@ -0,0 +1,8 @@
+-----BEGIN PRIVATE KEY-----
+MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBparGjr0KcdNrVM2J
+G0mW5ltP1QyvxDqBMyWLWo3fruRZv6Qoohl5skd1u4O+KJoM/UrrSTOXI/MDR7NN
+i1yl7O+hgYkDgYYABAG8K2XVsK0ahG9+HIIPwCO0pJY8ulwSTXwIjkCGyB2lpglh
+8qJmRzuyGcfRTslv8wfv0sPlT9H9PKDvgrTUL7rvQQDdOODNgVPXSecUoXoPn+X+
+eqxs77bjx+A5x0t/i3m5PfkaNPh5MZ1H/bWuOOdj2ZXZw0R4rlVc0zVrgnPU8L8S
+BQ==
+-----END PRIVATE KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_public_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_public_key.pem
new file mode 100644
index 0000000000..6d22fe43fe
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/ecdsa_public_key.pem
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBvCtl1bCtGoRvfhyCD8AjtKSWPLpc
+Ek18CI5AhsgdpaYJYfKiZkc7shnH0U7Jb/MH79LD5U/R/Tyg74K01C+670EA3Tjg
+zYFT10nnFKF6D5/l/nqsbO+248fgOcdLf4t5uT35GjT4eTGdR/21rjjnY9mV2cNE
+eK5VXNM1a4Jz1PC/EgU=
+-----END PUBLIC KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key.pem
new file mode 100644
index 0000000000..ea0e3d3958
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCwwb0/ddXGXTFK
+4FLxXdV6a/WJMSoPPS55RvZIAHFsiTtvPLbJ8LxDsZ6wSVZLN0/UQ4wdWn9jftyj
+U5/IxBVG8XOtKimTMvm3/ZOzVLueGHBbrLYscRv9oL85ulTKHWgrZDu0lBX5JJTI
+v5UTCErzJRQbka9DG1GaBgDb1PlXfkzBWMwfsBZmwoC77KvCcIGCgbW/XCY03TP2
+3Tg8drvpByMStddP2FQ4fZ91qFUzPu8uhZEsqSQTFlmhgGEx7dLlky0xvu62RuAD
+RTpINpcWZtWDHTdssOqu653LwwqBY8lBopCZ/4Af8QR3ZYkQhen1YLEbVheXRuzI
+LSCZIiJNAgMBAAECggEBAJH4/fxpqQkvr2Shy33Pu1xlyhnpw01gfn/jrcKasxEq
+aC4eWup86E2TY3U8q4pkfIXU3uLi+O9HNpmflwargNLc1mY8uqb44ygiv5bLNEKE
+9k2PXcdoBfC4jxPyoNFl5cBn/7LK1TazEjiTl15na9ZPWcLG1pG5/vMPYCgsQ1sP
+8J3c4E3aaXIj9QceYxBprl490OCzieGyZlRipncz3g4UShRc/b4cycvDZOJpmAy4
+zbWTcBcSMPVPi5coF0K8UcimiqZkotfb/2RLc433i34IdsIXMM+brdq+g8rmjg5a
++oQPy02M6tFApBruEhAz8DGgaLtDY6MLtyZAt3SjXnUCgYEA1zLgamdTHOqrrmIi
+eIQBnAJiyIfcY8B9SX1OsLGYFCHiPVwgUY35B2c7MavMsGcExJhtE+uxU7o5djtM
+R6r9cRHOXJ6EQwa8OwzzPqbM17/YqNDeK39bc9WOFUqRWrhDhVMPy6z8rmZr73mG
+IUC7mBNx/1GBdVYXIlsXzC96dI8CgYEA0kUAhz6I5nyPa70NDEUYHLHf3IW1BCmE
+UoVbraSePJtIEY/IqFx7oDuFo30d4n5z+8ICCtyid1h/Cp3mf3akOiqltYUfgV1G
+JgcEjKKYWEnO7cfFyO7LB7Y3GYYDJNy6EzVWPiwTGk9ZTfFJEESmHC45Unxgd17m
+Dx/R58rFgWMCgYBQXQWFdtSI5fH7C1bIHrPjKNju/h2FeurOuObcAVZDnmu4cmD3
+U8d9xkVKxVeJQM99A1coq0nrdI3k4zwXP3mp8fZYjDHkPe2pN6rW6L9yiohEcsuk
+/siON1/5/4DMmidM8LnjW9R45HLGWWGHpX7oyco2iJ+Jy/6Tq+T1MX3PbQKBgQCm
+hdsbQJ0u3CrBSmFQ/E9SOlRt0r4+45pVuCOY6yweF2QF9HcXTtbhWQJHLclDHJ5C
+Ha18aKuKFN3XzKFFBPKe1jOSBDGlQ/dQGnKx5fr8wMdObM3oiaTlIJuWbRmEUgJT
+QARjDIi8Z2b0YUhZx+Q9oSXoe3PyVYehJrQX+/BavQKBgQCIr7Zp0rQPbfqcTL+M
+OYHUoNcb14f9f8hXeXHQOqVpsGwxGdRQAU9wbx/4+obKB5xIkzBsVNcJwavisNja
+hegnGjTB/9Hc4m+5bMGwH0bhS2eQO4o+YYM2ypDmFQqDLRfFUlZ5PVHffm/aA9+g
+GanNBCsmtoHtV6CJ1UZ7NmBuIA==
+-----END PRIVATE KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key_pwd.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key_pwd.pem
new file mode 100644
index 0000000000..501662fc35
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_private_key_pwd.pem
@@ -0,0 +1,30 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIh888Iq6gxuMCAggA
+MBQGCCqGSIb3DQMHBAic/11YZ8Nt5gSCBMjG/Jb4qiMoBS50iQvHXqcETPE+0NBr
+jhsn9w94LkdRBstMPAsoKmY98Er96Rnde/NfmqlU9CupKTkd7Ce5poBf72Y6KMED
+cPURyjbGRFsu6x9skXB2obhyKYEqAEF2oQAg4Qbe5v1qXBIgDuC/NgiJnM+w2zCZ
+LkHSZB2/NmcnvDzcgPF7TM8pTO23xCJ33m37qjfWvHsgocVqZmL9wQ4+wr/NMYjJ
+pJvX1OHW1vBsZsXh40WchalYRSB1VeO368QfsE8coRJztqbMzdce9EQdMB6Q6jlO
+cetd3moLIoMP4I7HW0/SgokbycTbRiYSvRyU1TGc2WbW6BrFZV24IckcnnVUFatf
+6HKUcaYLG68dJcRgs5QMGkcmgVvlddENHFmHZlo0eym/xSiUl/AT8/5odscm6ML8
+wW5sneax+TF4J2eYmiN7yjAUCodXVTNYNDVKo6uUhntlymbM0o4UitVIbPIfTDHl
+sxJAEZ7vpuPqeNMxUk6G6zipuEjqsVbnuFSBSZmgKiGYcifRPUmqqINa3DdS4WVx
+xaPWdHbHVRD//ze3h/FsA+1lIE5q2kUE0xXseJA1ISog++kJp14XeaaL2j/tx3Ob
+OsbcaOAD/IUw/ItDt9kn0qzfnar7sS0Wov8AmJQxHmH7Lm93jHTLM05yE0AR/eBr
+Mig2ZdC+9OqVC+GPuBkRjSs8NpltQIDroz6EV9IMwPwXm0szSYoyoPLmlHJUdnLs
+ZUef+au6hYkEJBrvuisagnq5eT/fCV3hsjD7yODebNU2CmBTo6X2PRx/xsBHRMWl
+QkoM9PBdSCnKv6HpHl4pchuoqU2NpFjN0BCaad6aHfZSTnqgzK4bEh1oO6dI8/rB
+/eh71JyFFG5J4xbpaqz5Su01V1iwU5leK5bDwqals4M4+ZGHGciou7qnXUmX2fJl
+r6DlMUa/xy+A2ZG0NuZR05yk2oB3+KVNMgp6zFty3XaxwoNtc8GTLtLnBnIh2rlP
+mE1+I65LRWwrNQalPeOAUrYuEzhyp2Df7a8Ykas5PUH7MGR/S0Ge/dLxtE2bJuK4
+znbLAsGhvo/SbNxYqIp6D4iDtd3va6yUGncy41paA/vTKFVvXZDrXcwJQYYCVOGT
+OwdzNuozU8Dc7oxsd8oakfC46kvmVaOrGvZbm56PFfprcaL/Hslska5xxEni/eZe
+WRxZbCBhAVqS1pn5zkDQVUe9uFlR/x39Qi01HIlKLBsjpSs6qQsFArMe8hgXmXLG
+xP+dyVuOE18NzSewdEjeqSRKIM7Qi8EOjZsI4HdSRBY7bh9VhmaVXDZiCSf33TTE
+3y8nimzQAeuGoYg6WqHmWWC2Qnpki2HlaIH/ayXEyQWkP/qvg61e8ovdg9Fy8JOO
+0AacXVt5zj0q00AW5bKx7usi4NIjZedi86hUm6H19aBm7r86BKjwYTEI/GOcdrbV
+9HC/8ayOimgwiAG3gq+aLioWym+Z6KnsbVd7XReVbvM/InQx54WA2y5im0A+/c67
+oQFFPV84XGX9waeqv/K4Wzkm6HW+qVAEM67482VGOf0PVrlQMno6dOotT/Y7ljoZ
+2iz0LmN9yylJnLPDrr1i6gzbs5OhhUgbF5LI2YP2wWdCZTl/DrKSIvQZWl8U+tw3
+ciA=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key.pem
new file mode 100644
index 0000000000..d3fb5a2cc9
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsMG9P3XVxl0xSuBS8V3V
+emv1iTEqDz0ueUb2SABxbIk7bzy2yfC8Q7GesElWSzdP1EOMHVp/Y37co1OfyMQV
+RvFzrSopkzL5t/2Ts1S7nhhwW6y2LHEb/aC/ObpUyh1oK2Q7tJQV+SSUyL+VEwhK
+8yUUG5GvQxtRmgYA29T5V35MwVjMH7AWZsKAu+yrwnCBgoG1v1wmNN0z9t04PHa7
+6QcjErXXT9hUOH2fdahVMz7vLoWRLKkkExZZoYBhMe3S5ZMtMb7utkbgA0U6SDaX
+FmbVgx03bLDqruudy8MKgWPJQaKQmf+AH/EEd2WJEIXp9WCxG1YXl0bsyC0gmSIi
+TQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key_pwd.pem b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key_pwd.pem
new file mode 100644
index 0000000000..f74361cead
--- /dev/null
+++ b/lib/crypto/test/engine_SUITE_data/pkcs8/rsa_public_key_pwd.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxquo1Na8C+kjeW0YESGm
+vE1bgNW9xh+SQjU1fv/97ePK8mQW2zO1h/vUNz23pfZAKjQu3rlFW/VgGJQ0LgCs
+8Gr/HbMwNcCJzuFMePUrnWn/qBeR7OKUZCJ3E1pp4kwsTdGDDO7jPtNzKf0bdKlg
+G2GHfZWhUediRX8NsRg12X1odVPuRGVRsyJ952YODk9PFjK7pro7Ynf3Icx7di9d
+PXL5vEcKSRdomXvt1rgM8XVHES94RQqoz60ZhfV2JnPfa9V8qu0KaGntpEr7p4rQ
+5BSiLFPjPOArjsD5tKyo8ldKCdQjLfisEp7AetfMjLPVVPw9o/SmCjDxsYWTVRQ2
+tQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/lib/hipe/doc/src/hipe_app.xml b/lib/hipe/doc/src/hipe_app.xml
index e489d155c3..9299c6d73f 100644
--- a/lib/hipe/doc/src/hipe_app.xml
+++ b/lib/hipe/doc/src/hipe_app.xml
@@ -47,6 +47,72 @@
Details on HiPE compiler options are given by <c>hipe:help_options()</c>.</p>
</description>
<section>
+ <title>Feature Limitations</title>
+ <p>
+ The HiPE compiler is in general compliant with the normal BEAM compiler,
+ with respect to semantic behavior. There are however features in the BEAM compiler
+ and the runtime system that have limited or no support for HiPE compiled modules.
+ </p>
+ <taglist>
+ <tag>Stack traces</tag>
+ <item><p>Stack traces returned from <seealso marker="erts:erlang#get_stacktrace/0">
+ <c>erlang:get_stacktrace/0</c></seealso> or as part of <c>'EXIT'</c> terms
+ can look incomplete if HiPE compiled functions are involved. Typically a stack trace
+ will contain only BEAM compiled functions or only HiPE compiled functions, depending
+ on where the exception was raised.</p>
+ <p>Source code line numbers in stack traces are also not supported by HiPE compiled functions.</p>
+ </item>
+
+ <tag>Tracing</tag>
+ <item><p>Erlang call trace is not supported by HiPE. Calling
+ <seealso marker="erts:erlang#trace_pattern/3"><c>erlang:trace_pattern({M,F,A}, ...)</c></seealso>
+ does not have any effect on HiPE compiled modules.</p>
+ </item>
+
+ <tag>NIFs</tag>
+ <item><p>Modules compiled with HiPE can not call <seealso marker="erts:erlang#load_nif-2">
+ <c>erlang:load_nif/2</c></seealso> to load NIFs.</p>
+ </item>
+
+ <tag>-on_load</tag>
+ <item><p>Modules compiled with HiPE can not use
+ <seealso marker="doc/reference_manual:code_loading#on_load"><c>-on_load()</c></seealso>
+ directives.</p>
+ </item>
+ </taglist>
+
+ </section>
+ <section>
+ <title>Performance Limitations</title>
+ <p>
+ The HiPE compiler does in general produce faster code than the
+ BEAM compiler. There are however some situation when HiPE
+ compiled code will perform worse than BEAM code.
+ </p>
+ <taglist>
+ <tag>Mode switches</tag>
+ <item><p>Every time a process changes from executing code in a
+ HiPE compiled module to a BEAM compiled module (or vice versa),
+ it will do a mode switch. This involves a certain amount of
+ CPU overhead which can have a negative net impact if the
+ process is switching back and forth without getting enough done in
+ each mode.</p>
+ </item>
+
+ <tag>Optimization for <c>receive</c> with unique references</tag>
+ <item><p>The BEAM compiler can do an optimization when
+ a <c>receive</c> statement is <em>only</em> waiting for messages
+ containing a reference created before the receive. All messages
+ that existed in the queue when the reference was created will be
+ bypassed, as they cannot possibly contain the reference. HiPE
+ does not implement this optimization.</p>
+ <p>An example of this is when
+ <c>gen_server:call()</c> waits for the reply message.</p>
+ </item>
+
+ </taglist>
+ </section>
+ <section>
<title>SEE ALSO</title>
<p>
<seealso marker="stdlib:c">c(3)</seealso>,
diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src
index 5b2280594f..fb750dd418 100644
--- a/lib/hipe/main/hipe.app.src
+++ b/lib/hipe/main/hipe.app.src
@@ -236,4 +236,4 @@
{applications, [kernel,stdlib]},
{env, []},
{runtime_dependencies, ["syntax_tools-1.6.14","stdlib-3.4","kernel-5.3",
- "erts-9.0","compiler-5.0"]}]}.
+ "erts-9.2","compiler-5.0"]}]}.
diff --git a/lib/hipe/rtl/hipe_rtl_binary_construct.erl b/lib/hipe/rtl/hipe_rtl_binary_construct.erl
index 52ea5db382..bc215e3abe 100644
--- a/lib/hipe/rtl/hipe_rtl_binary_construct.erl
+++ b/lib/hipe/rtl/hipe_rtl_binary_construct.erl
@@ -195,8 +195,13 @@ gen_rtl(BsOP, Dst, Args, TrueLblName, FalseLblName, SystemLimitLblName, ConstTab
bs_validate_unicode ->
[_Arg] = Args,
- [hipe_rtl:mk_call([], bs_validate_unicode, Args,
- TrueLblName, FalseLblName, not_remote)];
+ [IsUnicode] = create_regs(1),
+ RetLbl = hipe_rtl:mk_new_label(),
+ [hipe_rtl:mk_call([IsUnicode], is_unicode, Args,
+ hipe_rtl:label_name(RetLbl), [], not_remote),
+ RetLbl,
+ hipe_rtl:mk_branch(IsUnicode, ne, hipe_rtl:mk_imm(0),
+ TrueLblName, FalseLblName, 0.99)];
bs_final ->
Zero = hipe_rtl:mk_imm(0),
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 10ef84d7cf..1ff6aefaa7 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,39 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.4.2</title>
+ <section><title>Inets 6.4.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fix broken handling of POST requests</p>
+ <p>
+ New chunk mechanism of body data in POST requests added
+ in 5d01c70ca399edf28e99dc760506329689fab6ba broke
+ handling of POST body data not using the new mechanism.</p>
+ <p>
+ Own Id: OTP-14656</p>
+ </item>
+ <item>
+ <p>
+ Make sure ints:stop/2 of the service httpd is synchronous</p>
+ <p>
+ Own Id: OTP-14696</p>
+ </item>
+ <item>
+ <p>
+ Honor status code returned by ESI script and modernize
+ "location" header handling.</p>
+ <p>
+ Own Id: OTP-14716</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl
index b3b11b74ab..91638f5d2e 100644
--- a/lib/inets/src/http_client/httpc_response.erl
+++ b/lib/inets/src/http_client/httpc_response.erl
@@ -269,7 +269,7 @@ parse_headers(<<?LF,?LF,Body/binary>>, Header, Headers,
MaxHeaderSize, Result, Relaxed);
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers,
- MaxHeaderSize, Result, _) ->
+ MaxHeaderSize, Result, Relaxed) ->
HTTPHeaders = [lists:reverse(Header) | Headers],
Length = lists:foldl(fun(H, Acc) -> length(H) + Acc end,
0, HTTPHeaders),
@@ -277,8 +277,42 @@ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers,
true ->
ResponseHeaderRcord =
http_response:headers(HTTPHeaders, #http_response_h{}),
- {ok, list_to_tuple(
- lists:reverse([Body, ResponseHeaderRcord | Result]))};
+
+ %% RFC7230, Section 3.3.3
+ %% If a message is received with both a Transfer-Encoding and a
+ %% Content-Length header field, the Transfer-Encoding overrides the
+ %% Content-Length. Such a message might indicate an attempt to
+ %% perform request smuggling (Section 9.5) or response splitting
+ %% (Section 9.4) and ought to be handled as an error. A sender MUST
+ %% remove the received Content-Length field prior to forwarding such
+ %% a message downstream.
+ case ResponseHeaderRcord#http_response_h.'transfer-encoding' of
+ undefined ->
+ {ok, list_to_tuple(
+ lists:reverse([Body, ResponseHeaderRcord | Result]))};
+ Value ->
+ TransferEncoding = string:lowercase(Value),
+ ContentLength = ResponseHeaderRcord#http_response_h.'content-length',
+ if
+ %% Respond without error but remove Content-Length field in relaxed mode
+ (Relaxed =:= true)
+ andalso (TransferEncoding =:= "chunked")
+ andalso (ContentLength =/= "-1") ->
+ ResponseHeaderRcordFixed =
+ ResponseHeaderRcord#http_response_h{'content-length' = "-1"},
+ {ok, list_to_tuple(
+ lists:reverse([Body, ResponseHeaderRcordFixed | Result]))};
+ %% Respond with error in default (not relaxed) mode
+ (Relaxed =:= false)
+ andalso (TransferEncoding =:= "chunked")
+ andalso (ContentLength =/= "-1") ->
+ throw({error, {headers_conflict, {'content-length',
+ 'transfer-encoding'}}});
+ true ->
+ {ok, list_to_tuple(
+ lists:reverse([Body, ResponseHeaderRcord | Result]))}
+ end
+ end;
false ->
throw({error, {header_too_long, MaxHeaderSize,
MaxHeaderSize-Length}})
diff --git a/lib/inets/src/http_server/httpd_esi.erl b/lib/inets/src/http_server/httpd_esi.erl
index 9406b47802..fd50934d00 100644
--- a/lib/inets/src/http_server/httpd_esi.erl
+++ b/lib/inets/src/http_server/httpd_esi.erl
@@ -66,7 +66,7 @@ handle_headers("") ->
{ok, [], 200};
handle_headers(Headers) ->
NewHeaders = string:tokens(Headers, ?CRLF),
- handle_headers(NewHeaders, [], 200).
+ handle_headers(NewHeaders, [], 200, true).
%%%========================================================================
%%% Internal functions
@@ -80,21 +80,17 @@ parse_headers([?CR, ?LF, ?CR, ?LF | Rest], Acc) ->
parse_headers([Char | Rest], Acc) ->
parse_headers(Rest, [Char | Acc]).
-handle_headers([], NewHeaders, StatusCode) ->
+handle_headers([], NewHeaders, StatusCode, _) ->
{ok, NewHeaders, StatusCode};
-handle_headers([Header | Headers], NewHeaders, StatusCode) ->
+handle_headers([Header | Headers], NewHeaders, StatusCode, NoESIStatus) ->
{FieldName, FieldValue} = httpd_response:split_header(Header, []),
case FieldName of
"location" ->
- case http_request:is_absolut_uri(FieldValue) of
- true ->
- handle_headers(Headers,
- [{FieldName, FieldValue} | NewHeaders],
- 302);
- false ->
- {proceed, FieldValue}
- end;
+ handle_headers(Headers,
+ [{FieldName, FieldValue} | NewHeaders],
+ 302, NoESIStatus);
+
"status" ->
NewStatusCode =
case httpd_util:split(FieldValue," ",2) of
@@ -103,8 +99,9 @@ handle_headers([Header | Headers], NewHeaders, StatusCode) ->
_ ->
200
end,
- handle_headers(Headers, NewHeaders, NewStatusCode);
+ handle_headers(Headers, NewHeaders, NewStatusCode, false);
_ ->
handle_headers(Headers,
- [{FieldName, FieldValue}| NewHeaders], StatusCode)
- end.
+ [{FieldName, FieldValue}| NewHeaders], StatusCode,
+ NoESIStatus)
+ end.
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 45b6deba97..adbbf64685 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -20,7 +20,7 @@
%%
-module(httpd_example).
-export([print/1]).
--export([get/2, put/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]).
+-export([get/2, put/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2,new_status_and_location/2]).
-export([newformat/3, post_chunked/3]).
%% These are used by the inets test-suite
@@ -90,6 +90,9 @@ post(Env,Input) ->
yahoo(_Env,_Input) ->
"Location: http://www.yahoo.com\r\n\r\n".
+new_status_and_location(_Env,_Input) ->
+ "status:201\r\n Location: http://www.yahoo.com\r\n\r\n".
+
default(Env,Input) ->
[header(),
top("Default Example"),
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index 3a589ca5f0..3206d957d9 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -339,26 +339,21 @@ erl_scheme_webpage_whole(Mod, Func, Env, Input, ModData) ->
{Headers, Body} =
httpd_esi:parse_headers(lists:flatten(Response)),
Length = httpd_util:flatlength(Body),
- case httpd_esi:handle_headers(Headers) of
- {proceed, AbsPath} ->
- {proceed, [{real_name, httpd_util:split_path(AbsPath)}
- | ModData#mod.data]};
- {ok, NewHeaders, StatusCode} ->
- send_headers(ModData, StatusCode,
- [{"content-length",
- integer_to_list(Length)}| NewHeaders]),
- case ModData#mod.method of
- "HEAD" ->
- {proceed, [{response, {already_sent, 200, 0}} |
- ModData#mod.data]};
- _ ->
- httpd_response:send_body(ModData,
- StatusCode, Body),
- {proceed, [{response, {already_sent, 200,
- Length}} |
- ModData#mod.data]}
- end
- end
+ {ok, NewHeaders, StatusCode} = httpd_esi:handle_headers(Headers),
+ send_headers(ModData, StatusCode,
+ [{"content-length",
+ integer_to_list(Length)}| NewHeaders]),
+ case ModData#mod.method of
+ "HEAD" ->
+ {proceed, [{response, {already_sent, 200, 0}} |
+ ModData#mod.data]};
+ _ ->
+ httpd_response:send_body(ModData,
+ StatusCode, Body),
+ {proceed, [{response, {already_sent, 200,
+ Length}} |
+ ModData#mod.data]}
+ end
end.
%% New API that allows the dynamic wepage to be sent back to the client
@@ -398,29 +393,23 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) ->
{continue, _} = Continue ->
Continue;
{Headers, Body} ->
- case httpd_esi:handle_headers(Headers) of
- {proceed, AbsPath} ->
- {proceed, [{real_name, httpd_util:split_path(AbsPath)}
- | ModData#mod.data]};
- {ok, NewHeaders, StatusCode} ->
- IsDisableChunkedSend =
- httpd_response:is_disable_chunked_send(Db),
- case (ModData#mod.http_version =/= "HTTP/1.1") or
- (IsDisableChunkedSend) of
- true ->
- send_headers(ModData, StatusCode,
- [{"connection", "close"} |
- NewHeaders]);
- false ->
- send_headers(ModData, StatusCode,
- [{"transfer-encoding",
- "chunked"} | NewHeaders])
- end,
- handle_body(Pid, ModData, Body, Timeout, length(Body),
- IsDisableChunkedSend)
- end;
- timeout ->
- send_headers(ModData, 504, [{"connection", "close"}]),
+ {ok, NewHeaders, StatusCode} = httpd_esi:handle_headers(Headers),
+ IsDisableChunkedSend = httpd_response:is_disable_chunked_send(Db),
+ case (ModData#mod.http_version =/= "HTTP/1.1") or
+ (IsDisableChunkedSend) of
+ true ->
+ send_headers(ModData, StatusCode,
+ [{"connection", "close"} |
+ NewHeaders]);
+ false ->
+ send_headers(ModData, StatusCode,
+ [{"transfer-encoding",
+ "chunked"} | NewHeaders])
+ end,
+ handle_body(Pid, ModData, Body, Timeout, length(Body),
+ IsDisableChunkedSend);
+ timeout ->
+ send_headers(ModData, 504, [{"connection", "close"}]),
httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket),
{proceed,[{response, {already_sent, 200, 0}} | ModData#mod.data]}
end.
@@ -560,15 +549,10 @@ eval(#mod{method = Method} = ModData, ESIBody, Modules)
{ok, Response} ->
{Headers, _} =
httpd_esi:parse_headers(lists:flatten(Response)),
- case httpd_esi:handle_headers(Headers) of
- {ok, _, StatusCode} ->
- {proceed,[{response, {StatusCode, Response}} |
- ModData#mod.data]};
- {proceed, AbsPath} ->
- {proceed, [{real_name, AbsPath} |
- ModData#mod.data]}
- end
- end;
+ {ok, _, StatusCode} =httpd_esi:handle_headers(Headers),
+ {proceed,[{response, {StatusCode, Response}} |
+ ModData#mod.data]}
+ end;
false ->
{proceed,[{status,
{403, ModData#mod.request_uri,
diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl
index 4e10a97f58..9a13ed3d17 100644
--- a/lib/inets/test/http_format_SUITE.erl
+++ b/lib/inets/test/http_format_SUITE.erl
@@ -535,7 +535,7 @@ esi_parse_headers(Config) when is_list(Config) ->
{"location","http://foo.bar.se"}], 302} =
httpd_esi:handle_headers(Headers2),
- {proceed,"/foo/bar.html"} =
+ {ok,[{"location","/foo/bar.html"}], 302} =
httpd_esi:handle_headers("location:/foo/bar.html\r\n").
%%--------------------------------------------------------------------
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index a39e786c79..cc166d522e 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -115,6 +115,7 @@ only_simulated() ->
invalid_chunk_size,
headers_dummy,
headers_with_obs_fold,
+ headers_conflict_chunked_with_length,
empty_response_header,
remote_socket_close,
remote_socket_close_async,
@@ -978,7 +979,6 @@ headers_dummy(Config) when is_list(Config) ->
{"If-Range", "Sat, 29 Oct 1994 19:43:31 GMT"},
{"If-Match", "*"},
{"Content-Type", "text/plain"},
- {"Content-Encoding", "chunked"},
{"Content-Length", "6"},
{"Content-Language", "en"},
{"Content-Location", "http://www.foobar.se"},
@@ -1004,6 +1004,18 @@ headers_with_obs_fold(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
+headers_conflict_chunked_with_length(doc) ->
+ ["Test the code for handling headers with both Transfer-Encoding"
+ "and Content-Length which must receive error in default (not relaxed) mode"
+ "and must receive successful response in relaxed mode"];
+headers_conflict_chunked_with_length(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/headers_conflict_chunked_with_length.html", Config), []},
+ {error, {could_not_parse_as_http, _}} = httpc:request(get, Request, [{relaxed, false}], []),
+ {ok,{{_,200,_},_,_}} = httpc:request(get, Request, [{relaxed, true}], []),
+ ok.
+
+%%-------------------------------------------------------------------------
+
invalid_headers(Config) ->
Request = {url(group_name(Config), "/dummy.html", Config), [{"cookie", undefined}]},
{error, _} = httpc:request(get, Request, [], []).
@@ -1869,7 +1881,6 @@ handle_uri(_,"/dummy_headers.html",_,_,Socket,_) ->
%% user to evaluate. This is not a valid response
%% it only tests that the header handling code works.
Head = "HTTP/1.1 200 ok\r\n" ++
- "Content-Length:32\r\n" ++
"Pragma:1#no-cache\r\n" ++
"Via:1.0 fred, 1.1 nowhere.com (Apache/1.1)\r\n" ++
"Warning:1#pseudonym foobar\r\n" ++
@@ -1899,6 +1910,15 @@ handle_uri(_,"/obs_folded_headers.html",_,_,_,_) ->
" b\r\n\r\n"
"Hello";
+handle_uri(_,"/headers_conflict_chunked_with_length.html",_,_,Socket,_) ->
+ Head = "HTTP/1.1 200 ok\r\n"
+ "Content-Length:32\r\n"
+ "Transfer-Encoding:Chunked\r\n\r\n",
+ send(Socket, Head),
+ send(Socket, http_chunk:encode("<HTML><BODY>fo")),
+ send(Socket, http_chunk:encode("obar</BODY></HTML>")),
+ http_chunk:encode_last();
+
handle_uri(_,"/capital_transfer_encoding.html",_,_,Socket,_) ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Transfer-Encoding:Chunked\r\n\r\n",
diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl
index d9118aa1a4..2035b50248 100644
--- a/lib/inets/test/httpd_mod.erl
+++ b/lib/inets/test/httpd_mod.erl
@@ -779,9 +779,14 @@ esi(Type, Port, Host, Node) ->
[{statuscode, 200},
{no_header, "cache-control"},
{version, "HTTP/1.0"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/httpd_example:new_status_and_location"
+ " HTTP/1.1\r\n\r\n",
+ [{statuscode, 201},
+ {header, "Location"},
+ {version, "HTTP/1.1"}]),
ok.
-
%%--------------------------------------------------------------------
get(Type, Port, Host, Node) ->
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 34b6902747..108d259823 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 6.4.2
+INETS_VSN = 6.4.3
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/observer/src/cdv_bin_cb.erl b/lib/observer/src/cdv_bin_cb.erl
index 5502869973..a4a542297c 100644
--- a/lib/observer/src/cdv_bin_cb.erl
+++ b/lib/observer/src/cdv_bin_cb.erl
@@ -71,6 +71,8 @@ hex_binary_fun(Bin) ->
plain_html(io_lib:format("~s",[S]))
end.
+format_hex(<<>>,_) ->
+ [];
format_hex(<<B1:4,B2:4>>,_) ->
[integer_to_list(B1,16),integer_to_list(B2,16)];
format_hex(<<B1:4,B2:4,Bin/binary>>,0) ->
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index ddf2879a97..40450a2873 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -26,10 +26,25 @@
%% Tables
%% ------
%% cdv_dump_index_table: This table holds all tags read from the
-%% crashdump. Each tag indicates where the information about a
-%% specific item starts. The table entry for a tag includes the start
-%% position for this item-information. In a crash dump file, all tags
-%% start with a "=" at the beginning of a line.
+%% crashdump, except the 'binary' tag. Each tag indicates where the
+%% information about a specific item starts. The table entry for a
+%% tag includes the start position for this item-information. In a
+%% crash dump file, all tags start with a "=" at the beginning of a
+%% line.
+%%
+%% cdv_binary_index_table: This table holds all 'binary' tags. The hex
+%% address for each binary is converted to its integer value before
+%% storing Address -> Start Position in this table. The hex value of
+%% the address is never used for lookup.
+%%
+%% cdv_reg_proc_table: This table holds mappings between pid and
+%% registered name. This is used for timers and monitors.
+%%
+%% cdv_heap_file_chars: For each 'proc_heap' and 'literals' tag, this
+%% table contains the number of characters to read from the crash dump
+%% file. This is used for giving an indication in percent of the
+%% progress when parsing this data.
+%%
%%
%% Process state
%% -------------
@@ -73,6 +88,9 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
+%% Test support
+-export([get_dump_versions/0]).
+
%% Debug support
-export([debug/1,stop_debug/0]).
@@ -87,6 +105,7 @@
% line_head/1 function can return
-define(not_available,"N/A").
-define(binary_size_progress_limit,10000).
+-define(max_dump_version,[0,4]).
%% All possible tags - use macros in order to avoid misspelling in the code
@@ -294,6 +313,11 @@ port(Id) ->
expand_binary(Pos) ->
call({expand_binary,Pos}).
+%%%-----------------------------------------------------------------
+%%% For testing only - called from crashdump_viewer_SUITE
+get_dump_versions() ->
+ call(get_dump_versions).
+
%%====================================================================
%% Server functions
%%====================================================================
@@ -455,8 +479,9 @@ handle_call(index_tables,_From,State=#state{file=File}) ->
handle_call(schedulers,_From,State=#state{file=File}) ->
Schedulers=schedulers(File),
TW = truncated_warning([?scheduler]),
- {reply,{ok,Schedulers,TW},State}.
-
+ {reply,{ok,Schedulers,TW},State};
+handle_call(get_dump_versions,_From,State=#state{dump_vsn=DumpVsn}) ->
+ {reply,{ok,{?max_dump_version,DumpVsn}},State}.
%%--------------------------------------------------------------------
@@ -781,10 +806,8 @@ parse_vsn_str(Str,WS) ->
%%%-----------------------------------------------------------------
-%%% Traverse crash dump and insert index in table for each heading
-%%%
-%%% Progress is reported during the time and MUST be checked with
-%%% crashdump_viewer:get_progress/0 until it returns {ok,done}.
+%%% Traverse crash dump and insert index in table for each heading.
+%%% Progress is reported during the time.
do_read_file(File) ->
erase(?literals), %Clear literal cache.
put(truncated,false), %Not truncated (yet).
@@ -800,17 +823,21 @@ do_read_file(File) ->
{Tag,Id,Rest,N1} = tag(Fd,TagAndRest,1),
case Tag of
?erl_crash_dump ->
- reset_tables(),
- insert_index(Tag,Id,N1+1),
- put_last_tag(Tag,""),
- DumpVsn = [list_to_integer(L) ||
- L<-string:tokens(Id,".")],
- AddrAdj = get_bin_addr_adj(DumpVsn),
- indexify(Fd,AddrAdj,Rest,N1),
- end_progress(),
- check_if_truncated(),
- close(Fd),
- {ok,DumpVsn};
+ case check_dump_version(Id) of
+ {ok,DumpVsn} ->
+ reset_tables(),
+ insert_index(Tag,Id,N1+1),
+ put_last_tag(Tag,""),
+ AddrAdj = get_bin_addr_adj(DumpVsn),
+ indexify(Fd,AddrAdj,Rest,N1),
+ end_progress(),
+ check_if_truncated(),
+ close(Fd),
+ {ok,DumpVsn};
+ Error ->
+ close(Fd),
+ Error
+ end;
_Other ->
R = io_lib:format(
"~ts is not an Erlang crash dump~n",
@@ -838,6 +865,18 @@ do_read_file(File) ->
{error,R}
end.
+check_dump_version(Vsn) ->
+ DumpVsn = [list_to_integer(L) || L<-string:tokens(Vsn,".")],
+ if DumpVsn > ?max_dump_version ->
+ Info =
+ "This Crashdump Viewer is too old for the given "
+ "Erlang crash dump. Please use a newer version of "
+ "Crashdump Viewer.",
+ {error,Info};
+ true ->
+ {ok,DumpVsn}
+ end.
+
indexify(Fd,AddrAdj,Bin,N) ->
case binary:match(Bin,<<"\n=">>) of
{Start,Len} ->
@@ -1083,7 +1122,7 @@ get_proc_details(File,Pid,WS,DumpVsn) ->
{{Stack,MsgQ,Dict},TW} =
case truncated_warning([{?proc,Pid}]) of
[] ->
- {expand_memory(Fd,Pid,DumpVsn),[]};
+ expand_memory(Fd,Pid,DumpVsn);
TW0 ->
{{[],[],[]},TW0}
end,
@@ -1418,7 +1457,15 @@ expand_memory(Fd,Pid,DumpVsn) ->
read_messages(Fd,Pid,BinAddrAdj,Dict),
read_dictionary(Fd,Pid,BinAddrAdj,Dict)},
erase(fd),
- Expanded.
+ IncompleteWarning =
+ case erase(incomplete_heap) of
+ undefined ->
+ [];
+ true ->
+ ["WARNING: This process has an incomplete heap. "
+ "Some information might be missing."]
+ end,
+ {Expanded,IncompleteWarning}.
read_literals(Fd) ->
case lookup_index(?literals,[]) of
@@ -2745,6 +2792,7 @@ deref_ptr(Ptr, Line, BinAddrAdj, D0) ->
none ->
case get(fd) of
end_of_heap ->
+ put(incomplete_heap,true),
{['#CDVIncompleteHeap'],Line,D0};
Fd ->
case bytes(Fd) of
diff --git a/lib/observer/src/observer_html_lib.erl b/lib/observer/src/observer_html_lib.erl
index a85808a472..22b4714d63 100644
--- a/lib/observer/src/observer_html_lib.erl
+++ b/lib/observer/src/observer_html_lib.erl
@@ -355,11 +355,11 @@ href_proc_bin(From, T, Acc, LTB) ->
PreviewStr
end
end;
- [PreviewIntStr,SizeStr,Md5] when From =:= obs ->
+ [PreviewIntStr,PreviewBitSizeStr,SizeStr,Md5] when From =:= obs ->
Size = list_to_integer(SizeStr),
PreviewInt = list_to_integer(PreviewIntStr),
- PrevSize = (trunc(math:log2(PreviewInt)/8)+1)*8,
- PreviewStr = preview_string(Size,<<PreviewInt:PrevSize>>),
+ PreviewBitSize = list_to_integer(PreviewBitSizeStr),
+ PreviewStr = preview_string(Size,<<PreviewInt:PreviewBitSize>>),
if LTB ->
href("TARGET=\"expanded\"",
["#OBSBinary?key1="++PreviewIntStr++
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 29f4f9fabc..94d199e688 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -810,7 +810,7 @@ progress_dialog_destroy({Dialog,_,_}) ->
make_obsbin(Bin,Tab) ->
Size = byte_size(Bin),
- Preview =
+ {Preview,PreviewBitSize} =
try
%% The binary might be a unicode string, in which case we
%% don't want to split it in the middle of a grapheme
@@ -819,14 +819,14 @@ make_obsbin(Bin,Tab) ->
PB1 = string:slice(Bin,0,PL1),
PS1 = byte_size(PB1) * 8,
<<P1:PS1>> = PB1,
- P1
+ {P1,PS1}
catch _:_ ->
%% Probably not a string, so just split anywhere
PS2 = min(Size, 10) * 8,
<<P2:PS2, _/binary>> = Bin,
- P2
+ {P2,PS2}
end,
Hash = erlang:phash2(Bin),
Key = {Preview, Size, Hash},
ets:insert(Tab, {Key,Bin}),
- ['#OBSBin',Preview,Size,Hash].
+ ['#OBSBin',Preview,PreviewBitSize,Size,Hash].
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 86a60e15f4..29b9e406ae 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -25,7 +25,7 @@
%% Test functions
-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
start_stop/1,load_file/1,not_found_items/1,
- non_existing/1,not_a_crashdump/1,old_crashdump/1]).
+ non_existing/1,not_a_crashdump/1,old_crashdump/1,new_crashdump/1]).
-export([init_per_suite/1, end_per_suite/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -83,6 +83,7 @@ all() ->
non_existing,
not_a_crashdump,
old_crashdump,
+ new_crashdump,
load_file,
not_found_items
].
@@ -212,6 +213,25 @@ not_a_crashdump(Config) when is_list(Config) ->
ok = crashdump_viewer:stop().
+%% Try to load a file with newer version than this crashdump viewer can handle
+new_crashdump(Config) ->
+ Dump = hd(?config(dumps,Config)),
+ ok = start_backend(Dump),
+ {ok,{MaxVsn,CurrentVsn}} = crashdump_viewer:get_dump_versions(),
+ if MaxVsn =/= CurrentVsn ->
+ ct:fail("Current dump version is not equal to cdv's max version");
+ true ->
+ ok
+ end,
+ ok = crashdump_viewer:stop(),
+ NewerVsn = lists:join($.,[integer_to_list(X+1) || X <- MaxVsn]),
+ PrivDir = ?config(priv_dir,Config),
+ NewDump = filename:join(PrivDir,"new_erl_crash.dump"),
+ ok = file:write_file(NewDump,"=erl_crash_dump:"++NewerVsn++"\n"),
+ {error, Reason} = start_backend(NewDump),
+ "This Crashdump Viewer is too old" ++_ = Reason,
+ ok = crashdump_viewer:stop().
+
%% Load files into the tool and view all pages
load_file(Config) when is_list(Config) ->
case ?t:is_debug() of
@@ -328,7 +348,7 @@ browse_file(File) ->
io:format(" info read",[]),
- lookat_all_pids(Procs),
+ lookat_all_pids(Procs,is_truncated(File),incomplete_allowed(File)),
io:format(" pids ok",[]),
lookat_all_ports(Ports),
io:format(" ports ok",[]),
@@ -339,6 +359,21 @@ browse_file(File) ->
Procs. % used as second arg to special/2
+is_truncated(File) ->
+ case filename:extension(File) of
+ ".trunc"++_ ->
+ true;
+ _ ->
+ false
+ end.
+
+incomplete_allowed(File) ->
+ %% Incomplete heap is allowed for native libs, since some literals
+ %% are not dumped - and for pre OTP-20 (really pre 20.2) releases,
+ %% since literals were not dumped at all then.
+ Rel = get_rel_from_dump_name(File),
+ Rel < 20 orelse test_server:is_native(lists).
+
special(File,Procs) ->
case filename:extension(File) of
".full_dist" ->
@@ -528,7 +563,7 @@ special(File,Procs) ->
io:format(" process details ok",[]),
#proc{dict=Dict} = ProcDetails,
- io:format("~p\n", [Dict]),
+ %% io:format("~p\n", [Dict]),
Maps = crashdump_helper:create_maps(),
Maps = proplists:get_value(maps,Dict),
io:format(" maps ok",[]),
@@ -548,14 +583,25 @@ verify_binaries([Bin|T1], [['#CDVBin',Offset,Size,Pos]|T2]) ->
verify_binaries([], []) ->
ok.
-lookat_all_pids([]) ->
+lookat_all_pids([],_,_) ->
ok;
-lookat_all_pids([#proc{pid=Pid0}|Procs]) ->
+lookat_all_pids([#proc{pid=Pid0}|Procs],TruncAllowed,IncompAllowed) ->
Pid = pid_to_list(Pid0),
- {ok,_ProcDetails=#proc{},_ProcTW} = crashdump_viewer:proc_details(Pid),
- {ok,_Ets,_EtsTW} = crashdump_viewer:ets_tables(Pid),
- {ok,_Timers,_TimersTW} = crashdump_viewer:timers(Pid),
- lookat_all_pids(Procs).
+ {ok,_ProcDetails=#proc{},ProcTW} = crashdump_viewer:proc_details(Pid),
+ {ok,_Ets,EtsTW} = crashdump_viewer:ets_tables(Pid),
+ {ok,_Timers,TimersTW} = crashdump_viewer:timers(Pid),
+ case {ProcTW,EtsTW,TimersTW} of
+ {[],[],[]} ->
+ ok;
+ {["WARNING: This process has an incomplete heap."++_],[],[]}
+ when IncompAllowed ->
+ ok; % native libs, literals might not be included in dump
+ _ when TruncAllowed ->
+ ok; % truncated dump
+ TWs ->
+ ct:fail({unexpected_warning,TWs})
+ end,
+ lookat_all_pids(Procs,TruncAllowed,IncompAllowed).
lookat_all_ports([]) ->
ok;
@@ -603,13 +649,7 @@ do_create_dumps(DataDir,Rel) ->
current ->
CD3 = dump_with_args(DataDir,Rel,"instr","+Mim true"),
CD4 = dump_with_strange_module_name(DataDir,Rel,"strangemodname"),
- Tmp = dump_with_args(DataDir,Rel,"trunc_bytes",""),
- {ok,#file_info{size=Max}} = file:read_file_info(Tmp),
- ok = file:delete(Tmp),
- Bytes = max(15,rand:uniform(Max)),
- CD5 = dump_with_args(DataDir,Rel,"trunc_bytes",
- "-env ERL_CRASH_DUMP_BYTES " ++
- integer_to_list(Bytes)),
+ CD5 = dump_with_size_limit_reached(DataDir,Rel,"trunc_bytes"),
CD6 = dump_with_unicode_atoms(DataDir,Rel,"unicode"),
CD7 = dump_with_maps(DataDir,Rel,"maps"),
TruncatedDumps = truncate_dump(CD1),
@@ -722,6 +762,28 @@ dump_with_strange_module_name(DataDir,Rel,DumpName) ->
?t:stop_node(n1),
CD.
+dump_with_size_limit_reached(DataDir,Rel,DumpName) ->
+ Tmp = dump_with_args(DataDir,Rel,DumpName,""),
+ {ok,#file_info{size=Max}} = file:read_file_info(Tmp),
+ ok = file:delete(Tmp),
+ dump_with_size_limit_reached(DataDir,Rel,DumpName,Max).
+
+dump_with_size_limit_reached(DataDir,Rel,DumpName,Max) ->
+ Bytes = max(15,rand:uniform(Max)),
+ CD = dump_with_args(DataDir,Rel,DumpName,
+ "-env ERL_CRASH_DUMP_BYTES " ++
+ integer_to_list(Bytes)),
+ {ok,#file_info{size=Size}} = file:read_file_info(CD),
+ if Size < Bytes ->
+ %% This means that the dump was actually smaller than the
+ %% randomly selected truncation size, so we'll just do it
+ %% again with a smaller numer
+ ok = file:delete(CD),
+ dump_with_size_limit_reached(DataDir,Rel,DumpName,Size-3);
+ true ->
+ CD
+ end.
+
dump_with_unicode_atoms(DataDir,Rel,DumpName) ->
Opt = rel_opt(Rel),
Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"",
@@ -794,6 +856,11 @@ dump_prefix(current) ->
dump_prefix(Rel) ->
lists:concat(["r",Rel,"_dump."]).
+get_rel_from_dump_name(File) ->
+ Name = filename:basename(File),
+ ["r"++Rel|_] = string:split(Name,"_"),
+ list_to_integer(Rel).
+
compat_rel(current) ->
"";
compat_rel(Rel) ->
diff --git a/lib/ssh/doc/src/ssh_client_key_api.xml b/lib/ssh/doc/src/ssh_client_key_api.xml
index a1cd9d4b02..98a1676ca4 100644
--- a/lib/ssh/doc/src/ssh_client_key_api.xml
+++ b/lib/ssh/doc/src/ssh_client_key_api.xml
@@ -56,11 +56,17 @@
<tag><c>string() =</c></tag>
<item><p><c>[byte()]</c></p></item>
<tag><c>public_key() =</c></tag>
- <item><p><c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item>
+ <item><p><c>#'RSAPublicKey'{}
+ | {integer(),#'Dss-Parms'{}}
+ | {#'ECPoint'{},{namedCurve,Curve::string()}}</c></p></item>
<tag><c>private_key() =</c></tag>
- <item><p><c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item>
+ <item><p><c>#'RSAPrivateKey'{}
+ | #'DSAPrivateKey'{}
+ | #'ECPrivateKey'{}</c></p></item>
<tag><c>public_key_algorithm() =</c></tag>
- <item><p><c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item>
+ <item><p><c>'ssh-rsa' | 'ssh-dss'
+ | 'rsa-sha2-256' | 'rsa-sha2-384' | 'rsa-sha2-512'
+ | 'ecdsa-sha2-nistp256' | 'ecdsa-sha2-nistp384' | 'ecdsa-sha2-nistp521' </c></p></item>
</taglist>
</section>
@@ -73,10 +79,11 @@
<d>Description of the host that owns the <c>PublicKey</c>.</d>
<v>Key = public_key()</v>
- <d>Normally an RSA or DSA public key, but handling of other public keys can be added.</d>
+ <d>Normally an RSA, DSA or ECDSA public key, but handling of other public keys can be added.</d>
<v>ConnectOptions = proplists:proplist()</v>
- <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso></d>
+ <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso>. The option list given in
+ the <c>key_cb</c> option is available with the key <c>key_cb_private</c>.</d>
<v>Reason = term().</v>
</type>
<desc>
@@ -89,17 +96,17 @@
<fsummary>Checks if a host key is trusted.</fsummary>
<type>
<v>Key = public_key() </v>
- <d>Normally an RSA or DSA public key, but handling of other public keys can be added.</d>
+ <d>Normally an RSA, DSA or ECDSA public key, but handling of other public keys can be added.</d>
<v>Host = string()</v>
<d>Description of the host.</d>
<v>Algorithm = public_key_algorithm()</v>
- <d>Host key algorithm. Is to support <c>'ssh-rsa'| 'ssh-dss'</c>, but more algorithms
- can be handled.</d>
+ <d>Host key algorithm.</d>
<v>ConnectOptions = proplists:proplist() </v>
- <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso>.</d>
+ <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso>. The option list given in
+ the <c>key_cb</c> option is available with the key <c>key_cb_private</c>.</d>
<v>Result = boolean()</v>
</type>
@@ -110,15 +117,15 @@
<func>
<name>Module:user_key(Algorithm, ConnectOptions) ->
- {ok, PrivateKey} | {error, Reason}</name>
+ {ok, PrivateKey} | {error, Reason}</name>
<fsummary>Fetches the users <em>public key</em> matching the <c>Algorithm</c>.</fsummary>
<type>
<v>Algorithm = public_key_algorithm()</v>
- <d>Host key algorithm. Is to support <c>'ssh-rsa'| 'ssh-dss'</c> but more algorithms
- can be handled.</d>
+ <d>Host key algorithm.</d>
<v>ConnectOptions = proplists:proplist()</v>
- <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso></d>
+ <d>Options provided to <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso>. The option list given in
+ the <c>key_cb</c> option is available with the key <c>key_cb_private</c>.</d>
<v>PrivateKey = private_key()</v>
<d>Private key of the user matching the <c>Algorithm</c>.</d>
diff --git a/lib/ssh/doc/src/ssh_server_key_api.xml b/lib/ssh/doc/src/ssh_server_key_api.xml
index a0694ca8d9..c6808b95d1 100644
--- a/lib/ssh/doc/src/ssh_server_key_api.xml
+++ b/lib/ssh/doc/src/ssh_server_key_api.xml
@@ -57,11 +57,17 @@
<tag><c>string() =</c></tag>
<item><p><c>[byte()]</c></p></item>
<tag><c>public_key() =</c></tag>
- <item><p><c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item>
+ <item><p><c>#'RSAPublicKey'{}
+ | {integer(),#'Dss-Parms'{}}
+ | {#'ECPoint'{},{namedCurve,Curve::string()}}</c></p></item>
<tag><c>private_key() =</c></tag>
- <item><p><c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item>
+ <item><p><c>#'RSAPrivateKey'{}
+ | #'DSAPrivateKey'{}
+ | #'ECPrivateKey'{}</c></p></item>
<tag><c>public_key_algorithm() =</c></tag>
- <item><p><c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item>
+ <item><p><c>'ssh-rsa' | 'ssh-dss'
+ | 'rsa-sha2-256' | 'rsa-sha2-384' | 'rsa-sha2-512'
+ | 'ecdsa-sha2-nistp256' | 'ecdsa-sha2-nistp384' | 'ecdsa-sha2-nistp521' </c></p></item>
</taglist>
</section>
@@ -72,12 +78,13 @@
<fsummary>Fetches the host’s private key.</fsummary>
<type>
<v>Algorithm = public_key_algorithm()</v>
- <d>Host key algorithm. Is to support <c>'ssh-rsa' | 'ssh-dss'</c>, but more algorithms
- can be handled.</d>
+ <d>Host key algorithm.</d>
<v>DaemonOptions = proplists:proplist()</v>
- <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>.</d>
- <v>Key = private_key()</v>
- <d>Private key of the host matching the <c>Algorithm</c>.</d>
+ <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>. The option list given in
+ the <c>key_cb</c> option is available with the key <c>key_cb_private</c>.</d>
+ <v>Key = private_key() | crypto:engine_key_ref()</v>
+ <d>Private key of the host matching the <c>Algorithm</c>.
+ It may be a reference to a 'ssh-rsa', rsa-sha2-* or 'ssh-dss' (NOT ecdsa) key stored in a loaded Engine.</d>
<v>Reason = term()</v>
</type>
<desc>
@@ -90,11 +97,12 @@
<fsummary>Checks if the user key is authorized.</fsummary>
<type>
<v>Key = public_key()</v>
- <d>Normally an RSA or DSA public key, but handling of other public keys can be added</d>
+ <d>Normally an RSA, DSA or ECDSA public key, but handling of other public keys can be added</d>
<v>User = string()</v>
<d>User owning the public key.</d>
<v>DaemonOptions = proplists:proplist()</v>
- <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>.</d>
+ <d>Options provided to <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso>. The option list given in
+ the <c>key_cb</c> option is available with the key <c>key_cb_private</c>.</d>
<v>Result = boolean()</v>
</type>
<desc>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 1a5d48baca..032d87bdad 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -188,6 +188,7 @@ daemon(Port) ->
daemon(Socket, UserOptions) when is_port(Socket) ->
try
#{} = Options = ssh_options:handle_options(server, UserOptions),
+
case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
ok ->
{ok, {IP,Port}} = inet:sockname(Socket),
@@ -461,6 +462,9 @@ open_listen_socket(_Host0, Port0, Options0) ->
%%%----------------------------------------------------------------
finalize_start(Host, Port, Profile, Options0, F) ->
try
+ %% throws error:Error if no usable hostkey is found
+ ssh_connection_handler:available_hkey_algorithms(server, Options0),
+
sshd_sup:start_child(Host, Port, Profile, Options0)
of
{error, {already_started, _}} ->
@@ -470,6 +474,8 @@ finalize_start(Host, Port, Profile, Options0, F) ->
Result = {ok,_} ->
F(Options0, Result)
catch
+ error:{shutdown,Err} ->
+ {error,Err};
exit:{noproc, _} ->
{error, ssh_not_started}
end.
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index ac64a7bf14..894877f8bf 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -145,14 +145,17 @@ get_public_key(SigAlg, #ssh{opts = Opts}) ->
case KeyCb:user_key(KeyAlg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
{ok, PrivKey} ->
try
+ %% Check the key - the KeyCb may be a buggy plugin
+ true = ssh_transport:valid_key_sha_alg(PrivKey, KeyAlg),
Key = ssh_transport:extract_public_key(PrivKey),
public_key:ssh_encode(Key, ssh2_pubkey)
of
PubKeyBlob -> {ok,{PrivKey,PubKeyBlob}}
catch
_:_ ->
- not_ok
+ not_ok
end;
+
_Error ->
not_ok
end.
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 4158a52a27..802bf62570 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -46,6 +46,7 @@
%%% Internal application API
-export([start_connection/4,
+ available_hkey_algorithms/2,
open_channel/6,
request/6, request/7,
reply_request/3,
@@ -432,13 +433,12 @@ init_ssh_record(Role, Socket, Opts) ->
init_ssh_record(Role, Socket, PeerAddr, Opts).
init_ssh_record(Role, _Socket, PeerAddr, Opts) ->
- KeyCb = ?GET_OPT(key_cb, Opts),
AuthMethods = ?GET_OPT(auth_methods, Opts),
S0 = #ssh{role = Role,
- key_cb = KeyCb,
+ key_cb = ?GET_OPT(key_cb, Opts),
opts = Opts,
userauth_supported_methods = AuthMethods,
- available_host_keys = supported_host_keys(Role, KeyCb, Opts),
+ available_host_keys = available_hkey_algorithms(Role, Opts),
random_length_padding = ?GET_OPT(max_random_length_padding, Opts)
},
@@ -1544,44 +1544,42 @@ peer_role(client) -> server;
peer_role(server) -> client.
%%--------------------------------------------------------------------
-supported_host_keys(client, _, Options) ->
- try
- find_sup_hkeys(Options)
- of
- [] ->
+available_hkey_algorithms(Role, Options) ->
+ KeyCb = ?GET_OPT(key_cb, Options),
+ case [A || A <- available_hkey_algos(Options),
+ (Role==client) orelse available_host_key(KeyCb, A, Options)
+ ] of
+
+ [] when Role==client ->
error({shutdown, "No public key algs"});
- Algs ->
- [atom_to_list(A) || A<-Algs]
- catch
- exit:Reason ->
- error({shutdown, Reason})
- end;
-supported_host_keys(server, KeyCb, Options) ->
- [atom_to_list(A) || A <- find_sup_hkeys(Options),
- available_host_key(KeyCb, A, Options)
- ].
+ [] when Role==server ->
+ error({shutdown, "No host key available"});
-find_sup_hkeys(Options) ->
- case proplists:get_value(public_key,
- ?GET_OPT(preferred_algorithms,Options)
- )
- of
- undefined ->
- ssh_transport:default_algorithms(public_key);
- L ->
- NonSupported = L--ssh_transport:supported_algorithms(public_key),
- L -- NonSupported
+ Algs ->
+ [atom_to_list(A) || A<-Algs]
end.
+available_hkey_algos(Options) ->
+ SupAlgos = ssh_transport:supported_algorithms(public_key),
+ HKeys = proplists:get_value(public_key,
+ ?GET_OPT(preferred_algorithms,Options)
+ ),
+ NonSupported = HKeys -- SupAlgos,
+ AvailableAndSupported = HKeys -- NonSupported,
+ AvailableAndSupported.
+
%% Alg :: atom()
available_host_key({KeyCb,KeyCbOpts}, Alg, Opts) ->
UserOpts = ?GET_OPT(user_options, Opts),
case KeyCb:host_key(Alg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
- {ok,_} -> true;
- _ -> false
+ {ok,Key} ->
+ %% Check the key - the KeyCb may be a buggy plugin
+ ssh_transport:valid_key_sha_alg(Key, Alg);
+ _ ->
+ false
end.
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index e92c727559..892db6b64f 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -795,8 +795,14 @@ get_host_key(SSH, SignAlg) ->
#ssh{key_cb = {KeyCb,KeyCbOpts}, opts = Opts} = SSH,
UserOpts = ?GET_OPT(user_options, Opts),
case KeyCb:host_key(SignAlg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
- {ok, PrivHostKey} -> PrivHostKey;
- Result -> exit({error, {Result, unsupported_key_type}})
+ {ok, PrivHostKey} ->
+ %% Check the key - the KeyCb may be a buggy plugin
+ case valid_key_sha_alg(PrivHostKey, SignAlg) of
+ true -> PrivHostKey;
+ false -> exit({error, bad_hostkey})
+ end;
+ Result ->
+ exit({error, {Result, unsupported_key_type}})
end.
extract_public_key(#'RSAPrivateKey'{modulus = N, publicExponent = E}) ->
@@ -805,7 +811,15 @@ extract_public_key(#'DSAPrivateKey'{y = Y, p = P, q = Q, g = G}) ->
{Y, #'Dss-Parms'{p=P, q=Q, g=G}};
extract_public_key(#'ECPrivateKey'{parameters = {namedCurve,OID},
publicKey = Q}) ->
- {#'ECPoint'{point=Q}, {namedCurve,OID}}.
+ {#'ECPoint'{point=Q}, {namedCurve,OID}};
+extract_public_key(#{engine:=_, key_id:=_, algorithm:=Alg} = M) ->
+ case {Alg, crypto:privkey_to_pubkey(Alg, M)} of
+ {rsa, [E,N]} ->
+ #'RSAPublicKey'{modulus = N, publicExponent = E};
+ {dss, [P,Q,G,Y]} ->
+ {Y, #'Dss-Parms'{p=P, q=Q, g=G}}
+ end.
+
verify_host_key(#ssh{algorithms=Alg}=SSH, PublicKey, Digest, {AlgStr,Signature}) ->
@@ -1255,10 +1269,12 @@ payload(<<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>) ->
<<Payload:PayloadLen/binary, _/binary>> = PayloadAndPadding,
Payload.
+sign(SigData, HashAlg, #{algorithm:=dss} = Key) ->
+ mk_dss_sig(crypto:sign(dss, HashAlg, SigData, Key));
+sign(SigData, HashAlg, #{algorithm:=SigAlg} = Key) ->
+ crypto:sign(SigAlg, HashAlg, SigData, Key);
sign(SigData, HashAlg, #'DSAPrivateKey'{} = Key) ->
- DerSignature = public_key:sign(SigData, HashAlg, Key),
- #'Dss-Sig-Value'{r = R, s = S} = public_key:der_decode('Dss-Sig-Value', DerSignature),
- <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>;
+ mk_dss_sig(public_key:sign(SigData, HashAlg, Key));
sign(SigData, HashAlg, Key = #'ECPrivateKey'{}) ->
DerEncodedSign = public_key:sign(SigData, HashAlg, Key),
#'ECDSA-Sig-Value'{r=R, s=S} = public_key:der_decode('ECDSA-Sig-Value', DerEncodedSign),
@@ -1266,6 +1282,12 @@ sign(SigData, HashAlg, Key = #'ECPrivateKey'{}) ->
sign(SigData, HashAlg, Key) ->
public_key:sign(SigData, HashAlg, Key).
+
+mk_dss_sig(DerSignature) ->
+ #'Dss-Sig-Value'{r = R, s = S} = public_key:der_decode('Dss-Sig-Value', DerSignature),
+ <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>.
+
+
verify(PlainText, HashAlg, Sig, {_, #'Dss-Parms'{}} = Key) ->
case Sig of
<<R:160/big-unsigned-integer, S:160/big-unsigned-integer>> ->
@@ -1817,6 +1839,8 @@ kex_alg_dependent({Min, NBits, Max, Prime, Gen, E, F, K}) ->
%%%----------------------------------------------------------------
+valid_key_sha_alg(#{engine:=_, key_id:=_}, _Alg) -> true; % Engine key
+
valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-512') -> true;
valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-384') -> true;
valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-256') -> true;
@@ -1830,11 +1854,14 @@ valid_key_sha_alg(#'RSAPrivateKey'{}, 'ssh-rsa' ) -> true;
valid_key_sha_alg({_, #'Dss-Parms'{}}, 'ssh-dss') -> true;
valid_key_sha_alg(#'DSAPrivateKey'{}, 'ssh-dss') -> true;
-valid_key_sha_alg({#'ECPoint'{},{namedCurve,OID}}, Alg) -> sha(OID) == sha(Alg);
-valid_key_sha_alg(#'ECPrivateKey'{parameters = {namedCurve,OID}}, Alg) -> sha(OID) == sha(Alg);
+valid_key_sha_alg({#'ECPoint'{},{namedCurve,OID}}, Alg) -> valid_key_sha_alg_ec(OID, Alg);
+valid_key_sha_alg(#'ECPrivateKey'{parameters = {namedCurve,OID}}, Alg) -> valid_key_sha_alg_ec(OID, Alg);
valid_key_sha_alg(_, _) -> false.
-
+valid_key_sha_alg_ec(OID, Alg) ->
+ Curve = public_key:oid2ssh_curvename(OID),
+ Alg == list_to_atom("ecdsa-sha2-" ++ binary_to_list(Curve)).
+
public_algo(#'RSAPublicKey'{}) -> 'ssh-rsa'; % FIXME: Not right with draft-curdle-rsa-sha2
public_algo({_, #'Dss-Parms'{}}) -> 'ssh-dss';
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 5ea048a352..a18383d148 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -38,6 +38,7 @@ MODULES= \
ssh_basic_SUITE \
ssh_bench_SUITE \
ssh_connection_SUITE \
+ ssh_engine_SUITE \
ssh_protocol_SUITE \
ssh_property_test_SUITE \
ssh_sftp_SUITE \
@@ -49,6 +50,7 @@ MODULES= \
ssh_test_lib \
ssh_key_cb \
ssh_key_cb_options \
+ ssh_key_cb_engine_keys \
ssh_trpt_test_lib \
ssh_echo_server \
ssh_bench_dev_null \
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl
index c07140dc43..19e2754eba 100644
--- a/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl
+++ b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl
@@ -57,9 +57,9 @@
%%% Properties:
-prop_seq(_Config) ->
+prop_seq(Config) ->
{ok,Pid} = ssh_eqc_event_handler:add_report_handler(),
- {_, _, Port} = init_daemon(),
+ {_, _, Port} = init_daemon(Config),
numtests(1000,
?FORALL(Delay, choose(0,100),%% Micro seconds
try
@@ -86,7 +86,8 @@ any_relevant_error_report(Pid) ->
end, Reports).
%%%================================================================
-init_daemon() ->
+init_daemon(Config) ->
ok = begin ssh:stop(), ssh:start() end,
- ssh_test_lib:daemon([]).
+ DataDir = proplists:get_value(data_dir, Config),
+ ssh_test_lib:daemon([{system_dir,DataDir}]).
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index db2ae241e5..202b0afe57 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -46,6 +46,7 @@
exec_key_differs2/1,
exec_key_differs3/1,
exec_key_differs_fail/1,
+ fail_daemon_start/1,
idle_time_client/1,
idle_time_server/1,
inet6_option/1,
@@ -105,6 +106,7 @@ all() ->
{group, host_user_key_differs},
{group, key_cb},
{group, internal_error},
+ {group, rsa_host_key_is_actualy_ecdsa},
daemon_already_started,
double_close,
daemon_opt_fd,
@@ -121,6 +123,7 @@ groups() ->
{ecdsa_sha2_nistp256_key, [], basic_tests()},
{ecdsa_sha2_nistp384_key, [], basic_tests()},
{ecdsa_sha2_nistp521_key, [], basic_tests()},
+ {rsa_host_key_is_actualy_ecdsa, [], [fail_daemon_start]},
{host_user_key_differs, [], [exec_key_differs1,
exec_key_differs2,
exec_key_differs3,
@@ -180,6 +183,31 @@ init_per_group(rsa_key, Config) ->
false ->
{skip, unsupported_pub_key}
end;
+init_per_group(rsa_host_key_is_actualy_ecdsa, Config) ->
+ case
+ lists:member('ssh-rsa',
+ ssh_transport:default_algorithms(public_key)) and
+ lists:member('ecdsa-sha2-nistp256',
+ ssh_transport:default_algorithms(public_key))
+ of
+ true ->
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ ssh_test_lib:setup_ecdsa("256", DataDir, PrivDir),
+ %% The following sets up bad rsa keys:
+ begin
+ UserDir = PrivDir,
+ System = filename:join(UserDir, "system"),
+ file:copy(filename:join(DataDir, "id_rsa"), filename:join(UserDir, "id_rsa")),
+ file:rename(filename:join(System, "ssh_host_ecdsa_key"), filename:join(System, "ssh_host_rsa_key")),
+ file:rename(filename:join(System, "ssh_host_ecdsa_key.pub"), filename:join(System, "ssh_host_rsa_key.pub")),
+ ssh_test_lib:setup_rsa_known_host(DataDir, UserDir),
+ ssh_test_lib:setup_rsa_auth_keys(DataDir, UserDir)
+ end,
+ Config;
+ false ->
+ {skip, unsupported_pub_key}
+ end;
init_per_group(ecdsa_sha2_nistp256_key, Config) ->
case lists:member('ecdsa-sha2-nistp256',
ssh_transport:default_algorithms(public_key)) of
@@ -304,7 +332,8 @@ init_per_group(internal_error, Config) ->
DataDir = proplists:get_value(data_dir, Config),
PrivDir = proplists:get_value(priv_dir, Config),
ssh_test_lib:setup_dsa(DataDir, PrivDir),
- file:delete(filename:join(PrivDir, "system/ssh_host_dsa_key")),
+ %% In the test case the key will be deleted after the daemon start:
+ %% ... file:delete(filename:join(PrivDir, "system/ssh_host_dsa_key")),
Config;
init_per_group(dir_options, Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
@@ -868,12 +897,17 @@ key_callback_options(Config) when is_list(Config) ->
%%% Test that client does not hang if disconnects due to internal error
internal_error(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- SystemDir = filename:join(proplists:get_value(priv_dir, Config), system),
+ PrivDir = proplists:get_value(priv_dir, Config),
UserDir = proplists:get_value(priv_dir, Config),
+ SystemDir = filename:join(PrivDir, system),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
{failfun, fun ssh_test_lib:failfun/2}]),
+
+ %% Now provoke an error in the following connect:
+ file:delete(filename:join(PrivDir, "system/ssh_host_dsa_key")),
+
{error, Error} =
ssh:connect(Host, Port, [{silently_accept_hosts, true},
{user_dir, UserDir},
@@ -902,6 +936,17 @@ send(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
+%%%
+fail_daemon_start(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(proplists:get_value(priv_dir, Config), system),
+ UserDir = proplists:get_value(priv_dir, Config),
+
+ {error,_} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2}]).
+
+%%--------------------------------------------------------------------
%%% Test ssh:connection_info([peername, sockname])
peername_sockname(Config) when is_list(Config) ->
process_flag(trap_exit, true),
@@ -1300,14 +1345,11 @@ shell_exit_status(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%% Due to timing the error message may or may not be delivered to
%% the "tcp-application" before the socket closed message is recived
-check_error("Invalid state") ->
- ok;
-check_error("Connection closed") ->
- ok;
-check_error("Selection of key exchange algorithm failed"++_) ->
- ok;
-check_error(Error) ->
- ct:fail(Error).
+check_error("Invalid state") -> ok;
+check_error("Connection closed") -> ok;
+check_error("Selection of key exchange algorithm failed"++_) -> ok;
+check_error("No host key available") -> ok;
+check_error(Error) -> ct:fail(Error).
basic_test(Config) ->
ClientOpts = proplists:get_value(client_opts, Config),
diff --git a/lib/ssh/test/ssh_engine_SUITE.erl b/lib/ssh/test/ssh_engine_SUITE.erl
new file mode 100644
index 0000000000..035446932b
--- /dev/null
+++ b/lib/ssh/test/ssh_engine_SUITE.erl
@@ -0,0 +1,141 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2017. 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%
+%%
+
+%%
+
+-module(ssh_engine_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include("ssh_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,40}}].
+
+all() ->
+ [{group, dsa_key},
+ {group, rsa_key}
+ ].
+
+groups() ->
+ [{dsa_key, [], basic_tests()},
+ {rsa_key, [], basic_tests()}
+ ].
+
+basic_tests() ->
+ [simple_connect
+ ].
+
+
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ ssh:start(),
+ ?CHECK_CRYPTO(
+ case load_engine() of
+ {ok,E} ->
+ ssh_dbg:messages(fun ct:pal/2),
+ [{engine,E}|Config];
+ {error, notsup} ->
+ {skip, "Engine not supported on this OpenSSL version"};
+ {error, bad_engine_id} ->
+ {skip, "Dynamic Engine not supported"};
+ Other ->
+ ct:log("Engine load failed: ~p",[Other]),
+ {fail, "Engine load failed"}
+ end
+ ).
+
+end_per_suite(Config) ->
+ catch crypto:engine_unload( proplists:get_value(engine,Config) ),
+ ssh:stop().
+
+%%--------------------------------------------------------------------
+init_per_group(dsa_key, Config) ->
+ case lists:member('ssh-dss',
+ ssh_transport:default_algorithms(public_key)) of
+ true ->
+ start_daemon(Config, 'ssh-dss', "dsa_private_key.pem");
+ false ->
+ {skip, unsupported_pub_key}
+ end;
+init_per_group(rsa_key, Config) ->
+ case lists:member('ssh-rsa',
+ ssh_transport:default_algorithms(public_key)) of
+ true ->
+ start_daemon(Config, 'ssh-rsa', "rsa_private_key.pem");
+ false ->
+ {skip, unsupported_pub_key}
+ end.
+
+start_daemon(Config, KeyType, KeyId) ->
+ SystemDir = proplists:get_value(data_dir, Config),
+ FullKeyId = filename:join(SystemDir, KeyId),
+ KeyCBOpts = [{engine, proplists:get_value(engine,Config)},
+ {KeyType, FullKeyId}
+ ],
+ Opts = [{key_cb, {ssh_key_cb_engine_keys, KeyCBOpts}}],
+ {Pid, Host, Port} = ssh_test_lib:std_daemon(Config, Opts),
+ [{host_port,{Host,Port}}, {daemon_pid,Pid}| Config].
+
+
+end_per_group(_, Config) ->
+ catch ssh:stop_daemon(proplists:get_value(daemon_pid,Config)),
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+%% A simple exec call
+simple_connect(Config) ->
+ {Host,Port} = proplists:get_value(host_port, Config),
+ CRef = ssh_test_lib:std_connect(Config, Host, Port, []),
+ ssh:close(CRef).
+
+%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+load_engine() ->
+ case crypto:get_test_engine() of
+ {ok, Engine} ->
+ try crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, Engine},
+ <<"LOAD">>],
+ [])
+ catch
+ error:notsup ->
+ {error, notsup}
+ end;
+
+ {error, Error} ->
+ {error, Error}
+ end.
+
+start_std_daemon(Opts, Config) ->
+ ct:log("starting std_daemon",[]),
+ {Pid, Host, Port} = ssh_test_lib:std_daemon(Config, Opts),
+ ct:log("started ~p:~p ~p",[Host,Port,Opts]),
+ [{srvr_pid,Pid},{srvr_addr,{Host,Port}} | Config].
diff --git a/lib/ssh/test/ssh_engine_SUITE_data/dsa_private_key.pem b/lib/ssh/test/ssh_engine_SUITE_data/dsa_private_key.pem
new file mode 100644
index 0000000000..778ffac675
--- /dev/null
+++ b/lib/ssh/test/ssh_engine_SUITE_data/dsa_private_key.pem
@@ -0,0 +1,9 @@
+-----BEGIN PRIVATE KEY-----
+MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAMyitTMR7vPbpqyAXJpqnB0AhFwQ
+F87IE+JKFl5bD/MSkhhRV5sM73HUU1ooXY0FjhZ+cdLUCATuZR5ta4ydANqWIcAB
+gX3IwF1B4zf5SXEKTWkUYneL9dOKtiZLtoG28swrk8xMxwX+0fLHkltCEj6FiTW9
+PFrv8GmIfV6DjcI9AhUAqXWbb3RtoN9Ld28fVMhGZrj3LJUCgYEAwnxGHGBMpJaF
+2w7zAw3jHjL8PMYlV6vnufGHQlwF0ZUXJxRsvagMb/X1qACTu2VPYEVoLQGM3cfH
+EhHoQmvSXGAyTfR7Bmn3gf1n/s/DcFbdZduUCZ/rAyIrfd0eSbc1I+kZk85UCsKK
+w/IYdlqcuYa4Cgm2TapT5uEMqH4jhzEEFgIULh8swEUWmU8aJNWsrWl4eCiuUUg=
+-----END PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_engine_SUITE_data/ecdsa_private_key.pem b/lib/ssh/test/ssh_engine_SUITE_data/ecdsa_private_key.pem
new file mode 100644
index 0000000000..a45522064f
--- /dev/null
+++ b/lib/ssh/test/ssh_engine_SUITE_data/ecdsa_private_key.pem
@@ -0,0 +1,8 @@
+-----BEGIN PRIVATE KEY-----
+MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBparGjr0KcdNrVM2J
+G0mW5ltP1QyvxDqBMyWLWo3fruRZv6Qoohl5skd1u4O+KJoM/UrrSTOXI/MDR7NN
+i1yl7O+hgYkDgYYABAG8K2XVsK0ahG9+HIIPwCO0pJY8ulwSTXwIjkCGyB2lpglh
+8qJmRzuyGcfRTslv8wfv0sPlT9H9PKDvgrTUL7rvQQDdOODNgVPXSecUoXoPn+X+
+eqxs77bjx+A5x0t/i3m5PfkaNPh5MZ1H/bWuOOdj2ZXZw0R4rlVc0zVrgnPU8L8S
+BQ==
+-----END PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key.pem b/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key.pem
new file mode 100644
index 0000000000..ea0e3d3958
--- /dev/null
+++ b/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCwwb0/ddXGXTFK
+4FLxXdV6a/WJMSoPPS55RvZIAHFsiTtvPLbJ8LxDsZ6wSVZLN0/UQ4wdWn9jftyj
+U5/IxBVG8XOtKimTMvm3/ZOzVLueGHBbrLYscRv9oL85ulTKHWgrZDu0lBX5JJTI
+v5UTCErzJRQbka9DG1GaBgDb1PlXfkzBWMwfsBZmwoC77KvCcIGCgbW/XCY03TP2
+3Tg8drvpByMStddP2FQ4fZ91qFUzPu8uhZEsqSQTFlmhgGEx7dLlky0xvu62RuAD
+RTpINpcWZtWDHTdssOqu653LwwqBY8lBopCZ/4Af8QR3ZYkQhen1YLEbVheXRuzI
+LSCZIiJNAgMBAAECggEBAJH4/fxpqQkvr2Shy33Pu1xlyhnpw01gfn/jrcKasxEq
+aC4eWup86E2TY3U8q4pkfIXU3uLi+O9HNpmflwargNLc1mY8uqb44ygiv5bLNEKE
+9k2PXcdoBfC4jxPyoNFl5cBn/7LK1TazEjiTl15na9ZPWcLG1pG5/vMPYCgsQ1sP
+8J3c4E3aaXIj9QceYxBprl490OCzieGyZlRipncz3g4UShRc/b4cycvDZOJpmAy4
+zbWTcBcSMPVPi5coF0K8UcimiqZkotfb/2RLc433i34IdsIXMM+brdq+g8rmjg5a
++oQPy02M6tFApBruEhAz8DGgaLtDY6MLtyZAt3SjXnUCgYEA1zLgamdTHOqrrmIi
+eIQBnAJiyIfcY8B9SX1OsLGYFCHiPVwgUY35B2c7MavMsGcExJhtE+uxU7o5djtM
+R6r9cRHOXJ6EQwa8OwzzPqbM17/YqNDeK39bc9WOFUqRWrhDhVMPy6z8rmZr73mG
+IUC7mBNx/1GBdVYXIlsXzC96dI8CgYEA0kUAhz6I5nyPa70NDEUYHLHf3IW1BCmE
+UoVbraSePJtIEY/IqFx7oDuFo30d4n5z+8ICCtyid1h/Cp3mf3akOiqltYUfgV1G
+JgcEjKKYWEnO7cfFyO7LB7Y3GYYDJNy6EzVWPiwTGk9ZTfFJEESmHC45Unxgd17m
+Dx/R58rFgWMCgYBQXQWFdtSI5fH7C1bIHrPjKNju/h2FeurOuObcAVZDnmu4cmD3
+U8d9xkVKxVeJQM99A1coq0nrdI3k4zwXP3mp8fZYjDHkPe2pN6rW6L9yiohEcsuk
+/siON1/5/4DMmidM8LnjW9R45HLGWWGHpX7oyco2iJ+Jy/6Tq+T1MX3PbQKBgQCm
+hdsbQJ0u3CrBSmFQ/E9SOlRt0r4+45pVuCOY6yweF2QF9HcXTtbhWQJHLclDHJ5C
+Ha18aKuKFN3XzKFFBPKe1jOSBDGlQ/dQGnKx5fr8wMdObM3oiaTlIJuWbRmEUgJT
+QARjDIi8Z2b0YUhZx+Q9oSXoe3PyVYehJrQX+/BavQKBgQCIr7Zp0rQPbfqcTL+M
+OYHUoNcb14f9f8hXeXHQOqVpsGwxGdRQAU9wbx/4+obKB5xIkzBsVNcJwavisNja
+hegnGjTB/9Hc4m+5bMGwH0bhS2eQO4o+YYM2ypDmFQqDLRfFUlZ5PVHffm/aA9+g
+GanNBCsmtoHtV6CJ1UZ7NmBuIA==
+-----END PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key_pwd.pem b/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key_pwd.pem
new file mode 100644
index 0000000000..501662fc35
--- /dev/null
+++ b/lib/ssh/test/ssh_engine_SUITE_data/rsa_private_key_pwd.pem
@@ -0,0 +1,30 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIh888Iq6gxuMCAggA
+MBQGCCqGSIb3DQMHBAic/11YZ8Nt5gSCBMjG/Jb4qiMoBS50iQvHXqcETPE+0NBr
+jhsn9w94LkdRBstMPAsoKmY98Er96Rnde/NfmqlU9CupKTkd7Ce5poBf72Y6KMED
+cPURyjbGRFsu6x9skXB2obhyKYEqAEF2oQAg4Qbe5v1qXBIgDuC/NgiJnM+w2zCZ
+LkHSZB2/NmcnvDzcgPF7TM8pTO23xCJ33m37qjfWvHsgocVqZmL9wQ4+wr/NMYjJ
+pJvX1OHW1vBsZsXh40WchalYRSB1VeO368QfsE8coRJztqbMzdce9EQdMB6Q6jlO
+cetd3moLIoMP4I7HW0/SgokbycTbRiYSvRyU1TGc2WbW6BrFZV24IckcnnVUFatf
+6HKUcaYLG68dJcRgs5QMGkcmgVvlddENHFmHZlo0eym/xSiUl/AT8/5odscm6ML8
+wW5sneax+TF4J2eYmiN7yjAUCodXVTNYNDVKo6uUhntlymbM0o4UitVIbPIfTDHl
+sxJAEZ7vpuPqeNMxUk6G6zipuEjqsVbnuFSBSZmgKiGYcifRPUmqqINa3DdS4WVx
+xaPWdHbHVRD//ze3h/FsA+1lIE5q2kUE0xXseJA1ISog++kJp14XeaaL2j/tx3Ob
+OsbcaOAD/IUw/ItDt9kn0qzfnar7sS0Wov8AmJQxHmH7Lm93jHTLM05yE0AR/eBr
+Mig2ZdC+9OqVC+GPuBkRjSs8NpltQIDroz6EV9IMwPwXm0szSYoyoPLmlHJUdnLs
+ZUef+au6hYkEJBrvuisagnq5eT/fCV3hsjD7yODebNU2CmBTo6X2PRx/xsBHRMWl
+QkoM9PBdSCnKv6HpHl4pchuoqU2NpFjN0BCaad6aHfZSTnqgzK4bEh1oO6dI8/rB
+/eh71JyFFG5J4xbpaqz5Su01V1iwU5leK5bDwqals4M4+ZGHGciou7qnXUmX2fJl
+r6DlMUa/xy+A2ZG0NuZR05yk2oB3+KVNMgp6zFty3XaxwoNtc8GTLtLnBnIh2rlP
+mE1+I65LRWwrNQalPeOAUrYuEzhyp2Df7a8Ykas5PUH7MGR/S0Ge/dLxtE2bJuK4
+znbLAsGhvo/SbNxYqIp6D4iDtd3va6yUGncy41paA/vTKFVvXZDrXcwJQYYCVOGT
+OwdzNuozU8Dc7oxsd8oakfC46kvmVaOrGvZbm56PFfprcaL/Hslska5xxEni/eZe
+WRxZbCBhAVqS1pn5zkDQVUe9uFlR/x39Qi01HIlKLBsjpSs6qQsFArMe8hgXmXLG
+xP+dyVuOE18NzSewdEjeqSRKIM7Qi8EOjZsI4HdSRBY7bh9VhmaVXDZiCSf33TTE
+3y8nimzQAeuGoYg6WqHmWWC2Qnpki2HlaIH/ayXEyQWkP/qvg61e8ovdg9Fy8JOO
+0AacXVt5zj0q00AW5bKx7usi4NIjZedi86hUm6H19aBm7r86BKjwYTEI/GOcdrbV
+9HC/8ayOimgwiAG3gq+aLioWym+Z6KnsbVd7XReVbvM/InQx54WA2y5im0A+/c67
+oQFFPV84XGX9waeqv/K4Wzkm6HW+qVAEM67482VGOf0PVrlQMno6dOotT/Y7ljoZ
+2iz0LmN9yylJnLPDrr1i6gzbs5OhhUgbF5LI2YP2wWdCZTl/DrKSIvQZWl8U+tw3
+ciA=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_key_cb_engine_keys.erl b/lib/ssh/test/ssh_key_cb_engine_keys.erl
new file mode 100644
index 0000000000..fc9cbfd49b
--- /dev/null
+++ b/lib/ssh/test/ssh_key_cb_engine_keys.erl
@@ -0,0 +1,62 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2017. 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%
+%%
+
+%%
+%%----------------------------------------------------------------------
+
+%% Note: This module is used by ssh_basic_SUITE
+
+-module(ssh_key_cb_engine_keys).
+-behaviour(ssh_server_key_api).
+-compile(export_all).
+
+host_key(SshAlg, Options) ->
+ KBopts = proplists:get_value(key_cb_private, Options, []),
+ Engine = proplists:get_value(engine, KBopts),
+ case proplists:get_value(SshAlg, KBopts) of
+ undefined ->
+ {error, {unknown_alg,SshAlg}};
+ KeyId ->
+ case crypto_alg(SshAlg) of
+ undefined ->
+ {error, {unsupported_alg,SshAlg}};
+ CryptoAlg ->
+ PrivKey = #{engine => Engine,
+ key_id => KeyId,
+ algorithm => CryptoAlg},
+ %% Is there a key with this reference ?
+ case crypto:privkey_to_pubkey(CryptoAlg, PrivKey) of
+ [_|_] ->
+ {ok, PrivKey};
+ _ ->
+ {error, {no_hostkey,SshAlg}}
+ end
+ end
+ end.
+
+is_auth_key(_PublicUserKey, _User, _Options) ->
+ false.
+
+
+
+crypto_alg('ssh-rsa') -> rsa;
+crypto_alg('ssh-dss') -> dss;
+crypto_alg(_) -> undefined.
+
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index 74f802cf57..3e3e151781 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -630,11 +630,12 @@ client_handles_keyboard_interactive_0_pwds(Config) ->
%%%--------------------------------------------------------------------
-client_info_line(_Config) ->
+client_info_line(Config) ->
%% A client must not send an info-line. If it does, the server should handle
%% handle this gracefully
{ok,Pid} = ssh_eqc_event_handler:add_report_handler(),
- {_, _, Port} = ssh_test_lib:daemon([]),
+ DataDir = proplists:get_value(data_dir, Config),
+ {_, _, Port} = ssh_test_lib:daemon([{system_dir,DataDir}]),
%% Fake client:
{ok,S} = gen_tcp:connect("localhost",Port,[]),
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 5e9e03e410..949142ec77 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -7871,7 +7871,7 @@ run_test(Config, Extra, {cres, Body, Opts, ExpectedCompileReturn}) ->
{module, _} = code:load_abs(AbsFile, Mod),
Ms0 = erlang:process_info(self(),messages),
- Before = {{get(), lists:sort(ets:all()), Ms0}, pps()},
+ Before = {{lget(), lists:sort(ets:all()), Ms0}, pps()},
%% Prepare the check that the qlc module does not call qlc_pt.
_ = [unload_pt() || {file, Name} <- [code:is_loaded(qlc_pt)],
@@ -7903,7 +7903,7 @@ run_test(Config, Extra, Body) ->
wait_for_expected(R, {Strict0,PPS0}=Before, SourceFile, Wait) ->
Ms = erlang:process_info(self(),messages),
- After = {_,PPS1} = {{get(), lists:sort(ets:all()), Ms}, pps()},
+ After = {_,PPS1} = {{lget(), lists:sort(ets:all()), Ms}, pps()},
case {R, After} of
{ok, Before} ->
ok;
@@ -7931,6 +7931,18 @@ wait_for_expected(R, {Strict0,PPS0}=Before, SourceFile, Wait) ->
expected({ok,Before}, {R,After}, SourceFile)
end.
+%% The qlc modules uses the process dictionary for storing names of files.
+lget() ->
+ lists:sort([T || {K, _} = T <- get(), is_qlc_key(K)]).
+
+%% Copied from the qlc module.
+-define(LCACHE_FILE(Ref), {Ref, '$_qlc_cache_tmpfiles_'}).
+-define(MERGE_JOIN_FILE, '$_qlc_merge_join_tmpfiles_').
+
+is_qlc_key(?LCACHE_FILE(_)) -> true;
+is_qlc_key(?MERGE_JOIN_FILE) -> true;
+is_qlc_key(_) -> false.
+
unload_pt() ->
erlang:garbage_collect(), % get rid of references to qlc_pt...
_ = code:purge(qlc_pt),
diff --git a/otp_versions.table b/otp_versions.table
index ba3bccfe67..0cb8c9aa0b 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,4 @@
+OTP-20.1.4 : inets-6.4.3 # asn1-5.0.3 common_test-1.15.2 compiler-7.1.3 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.1 debugger-4.2.3 dialyzer-3.2.2 diameter-2.1.2 edoc-0.9.1 eldap-1.2.2 erl_docgen-0.7.1 erl_interface-3.10 erts-9.1.3 et-1.6.1 eunit-2.3.4 hipe-3.16.1 ic-4.4.2 jinterface-1.8 kernel-5.4 megaco-3.18.2 mnesia-4.15.1 observer-2.5 odbc-2.12 orber-3.8.3 os_mon-2.4.3 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.5 reltool-0.7.5 runtime_tools-1.12.2 sasl-3.1 snmp-5.2.8 ssh-4.6.1 ssl-8.2.1 stdlib-3.4.2 syntax_tools-2.1.3 tools-2.11 wx-1.8.2 xmerl-1.3.15 :
OTP-20.1.3 : diameter-2.1.2 erts-9.1.3 snmp-5.2.8 # asn1-5.0.3 common_test-1.15.2 compiler-7.1.3 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.1 debugger-4.2.3 dialyzer-3.2.2 edoc-0.9.1 eldap-1.2.2 erl_docgen-0.7.1 erl_interface-3.10 et-1.6.1 eunit-2.3.4 hipe-3.16.1 ic-4.4.2 inets-6.4.2 jinterface-1.8 kernel-5.4 megaco-3.18.2 mnesia-4.15.1 observer-2.5 odbc-2.12 orber-3.8.3 os_mon-2.4.3 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.5 reltool-0.7.5 runtime_tools-1.12.2 sasl-3.1 ssh-4.6.1 ssl-8.2.1 stdlib-3.4.2 syntax_tools-2.1.3 tools-2.11 wx-1.8.2 xmerl-1.3.15 :
OTP-20.1.2 : diameter-2.1.1 erts-9.1.2 # asn1-5.0.3 common_test-1.15.2 compiler-7.1.3 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.1 debugger-4.2.3 dialyzer-3.2.2 edoc-0.9.1 eldap-1.2.2 erl_docgen-0.7.1 erl_interface-3.10 et-1.6.1 eunit-2.3.4 hipe-3.16.1 ic-4.4.2 inets-6.4.2 jinterface-1.8 kernel-5.4 megaco-3.18.2 mnesia-4.15.1 observer-2.5 odbc-2.12 orber-3.8.3 os_mon-2.4.3 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.5 reltool-0.7.5 runtime_tools-1.12.2 sasl-3.1 snmp-5.2.7 ssh-4.6.1 ssl-8.2.1 stdlib-3.4.2 syntax_tools-2.1.3 tools-2.11 wx-1.8.2 xmerl-1.3.15 :
OTP-20.1.1 : compiler-7.1.3 erts-9.1.1 ssh-4.6.1 # asn1-5.0.3 common_test-1.15.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.1 debugger-4.2.3 dialyzer-3.2.2 diameter-2.1 edoc-0.9.1 eldap-1.2.2 erl_docgen-0.7.1 erl_interface-3.10 et-1.6.1 eunit-2.3.4 hipe-3.16.1 ic-4.4.2 inets-6.4.2 jinterface-1.8 kernel-5.4 megaco-3.18.2 mnesia-4.15.1 observer-2.5 odbc-2.12 orber-3.8.3 os_mon-2.4.3 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.5 reltool-0.7.5 runtime_tools-1.12.2 sasl-3.1 snmp-5.2.7 ssl-8.2.1 stdlib-3.4.2 syntax_tools-2.1.3 tools-2.11 wx-1.8.2 xmerl-1.3.15 :