File 1351-crypto-Add-support-for-ML-DSA-and-ML-KEM.patch of Package erlang
From 6675b73ff8e0597451e8240ecee1e3355eb9a130 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson <sverker@erlang.org>
Date: Mon, 24 Feb 2025 17:04:39 +0100
Subject: [PATCH] crypto: Add support for ML-DSA and ML-KEM
Requires OpenSSL 3.5
---
lib/crypto/c_src/algorithms.c | 18 ++-
lib/crypto/c_src/algorithms.h | 1 +
lib/crypto/c_src/atoms.c | 26 ++++
lib/crypto/c_src/atoms.h | 13 ++
lib/crypto/c_src/bn.c | 2 +-
lib/crypto/c_src/bn.h | 2 +-
lib/crypto/c_src/cipher.c | 1 +
lib/crypto/c_src/common.h | 1 +
lib/crypto/c_src/crypto.c | 7 +-
lib/crypto/c_src/evp.c | 130 ++++++++++++++++-
lib/crypto/c_src/evp.h | 2 +
lib/crypto/c_src/openssl_config.h | 8 ++
lib/crypto/c_src/pkey.c | 232 +++++++++++++++++++++++++++---
lib/crypto/c_src/pkey.h | 16 +++
lib/crypto/src/crypto.erl | 78 +++++++++-
lib/crypto/test/crypto_SUITE.erl | 75 +++++++++-
16 files changed, 581 insertions(+), 31 deletions(-)
diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c
index 8fab065932..2e1465e15e 100644
--- a/lib/crypto/c_src/algorithms.c
+++ b/lib/crypto/c_src/algorithms.c
@@ -193,10 +193,26 @@ void init_pubkey_types(ErlNifEnv* env) {
algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "eddh");
#endif
algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp");
-
+#ifdef HAVE_ML_DSA
+ algo_pubkey[algo_pubkey_cnt++] = atom_mldsa44;
+ algo_pubkey[algo_pubkey_cnt++] = atom_mldsa65;
+ algo_pubkey[algo_pubkey_cnt++] = atom_mldsa87;
+#endif
ASSERT(algo_pubkey_cnt <= sizeof(algo_pubkey)/sizeof(ERL_NIF_TERM));
}
+ERL_NIF_TERM kem_algorithms_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+#ifdef HAVE_ML_KEM
+ return enif_make_list3(env,
+ atom_mlkem512,
+ atom_mlkem768,
+ atom_mlkem1024);
+#else
+ return enif_make_list(env, 0);
+#endif
+}
+
/*================================================================
Cipher key algorithms
diff --git a/lib/crypto/c_src/algorithms.h b/lib/crypto/c_src/algorithms.h
index 2a1e6deb49..7a2c75106b 100644
--- a/lib/crypto/c_src/algorithms.h
+++ b/lib/crypto/c_src/algorithms.h
@@ -31,6 +31,7 @@ void init_algorithms_types(ErlNifEnv* env);
ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM kem_algorithms_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c
index cbd81de3ca..41e171afbc 100644
--- a/lib/crypto/c_src/atoms.c
+++ b/lib/crypto/c_src/atoms.c
@@ -151,6 +151,20 @@ ERL_NIF_TERM atom_key_id;
ERL_NIF_TERM atom_password;
#endif
+#ifdef HAVE_ML_DSA
+ERL_NIF_TERM atom_mldsa44;
+ERL_NIF_TERM atom_mldsa65;
+ERL_NIF_TERM atom_mldsa87;
+ERL_NIF_TERM atom_seed;
+ERL_NIF_TERM atom_expandedkey;
+#endif
+
+#ifdef HAVE_ML_KEM
+ERL_NIF_TERM atom_mlkem512;
+ERL_NIF_TERM atom_mlkem768;
+ERL_NIF_TERM atom_mlkem1024;
+#endif
+
int init_atoms(ErlNifEnv *env) {
atom_true = enif_make_atom(env,"true");
atom_false = enif_make_atom(env,"false");
@@ -274,5 +288,17 @@ int init_atoms(ErlNifEnv *env) {
atom_password = enif_make_atom(env,"password");
#endif
+#ifdef HAVE_ML_DSA
+ atom_mldsa44 = enif_make_atom(env,"mldsa44");
+ atom_mldsa65 = enif_make_atom(env,"mldsa65");
+ atom_mldsa87 = enif_make_atom(env,"mldsa87");
+ atom_seed = enif_make_atom(env,"seed");
+ atom_expandedkey = enif_make_atom(env,"expandedkey");
+#endif
+#ifdef HAVE_ML_KEM
+ atom_mlkem512 = enif_make_atom(env,"mlkem512");
+ atom_mlkem768 = enif_make_atom(env,"mlkem768");
+ atom_mlkem1024 = enif_make_atom(env,"mlkem1024");
+#endif
return 1;
}
diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h
index c8d88fcf75..4ee6a56057 100644
--- a/lib/crypto/c_src/atoms.h
+++ b/lib/crypto/c_src/atoms.h
@@ -150,6 +150,19 @@ extern ERL_NIF_TERM atom_key_id;
extern ERL_NIF_TERM atom_password;
#endif
+#ifdef HAVE_ML_DSA
+extern ERL_NIF_TERM atom_mldsa44;
+extern ERL_NIF_TERM atom_mldsa65;
+extern ERL_NIF_TERM atom_mldsa87;
+extern ERL_NIF_TERM atom_seed;
+extern ERL_NIF_TERM atom_expandedkey;
+#endif
+#ifdef HAVE_ML_KEM
+extern ERL_NIF_TERM atom_mlkem512;
+extern ERL_NIF_TERM atom_mlkem768;
+extern ERL_NIF_TERM atom_mlkem1024;
+#endif
+
int init_atoms(ErlNifEnv *env);
#endif /* E_ATOMS_H__ */
diff --git a/lib/crypto/c_src/bn.c b/lib/crypto/c_src/bn.c
index 7bd9529c4e..c7bdb1c5c7 100644
--- a/lib/crypto/c_src/bn.c
+++ b/lib/crypto/c_src/bn.c
@@ -198,7 +198,7 @@ ERL_NIF_TERM bn2term(ErlNifEnv* env, size_t size, const BIGNUM *bn)
#ifdef HAS_3_0_API
-int get_ossl_octet_string_param_from_bin(ErlNifEnv* env, char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest)
+int get_ossl_octet_string_param_from_bin(ErlNifEnv* env, const char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest)
{
ErlNifBinary tmp;
diff --git a/lib/crypto/c_src/bn.h b/lib/crypto/c_src/bn.h
index 44994f8358..e0ad63ab76 100644
--- a/lib/crypto/c_src/bn.h
+++ b/lib/crypto/c_src/bn.h
@@ -37,7 +37,7 @@ int get_bn_from_bin(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp);
int get_bn_from_bin_sz(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp, size_t* binsize);
#ifdef HAS_3_0_API
-int get_ossl_octet_string_param_from_bin(ErlNifEnv* env, char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest);
+int get_ossl_octet_string_param_from_bin(ErlNifEnv* env, const char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest);
int get_ossl_BN_param_from_bin(ErlNifEnv* env, char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest);
int get_ossl_BN_param_from_bin_sz(ErlNifEnv* env, char* key, ERL_NIF_TERM bin, OSSL_PARAM *dest, size_t *size);
int get_ossl_BN_param_from_bn(ErlNifEnv* env, char* key, const BIGNUM* bn, OSSL_PARAM *dest);
diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c
index 307dddc4c8..b57563b3a7 100644
--- a/lib/crypto/c_src/cipher.c
+++ b/lib/crypto/c_src/cipher.c
@@ -22,6 +22,7 @@
#include "cipher.h"
#include "info.h"
+#include "evp.h"
#define NOT_AEAD {{0,0,0}}
#define AEAD_CTRL {{EVP_CTRL_AEAD_SET_IVLEN,EVP_CTRL_AEAD_GET_TAG,EVP_CTRL_AEAD_SET_TAG}}
diff --git a/lib/crypto/c_src/common.h b/lib/crypto/c_src/common.h
index 92fb31c38b..2c5c306c9d 100644
--- a/lib/crypto/c_src/common.h
+++ b/lib/crypto/c_src/common.h
@@ -32,6 +32,7 @@
#include <string.h>
#include <limits.h>
#include <stdint.h>
+#include <stdbool.h>
#include <erl_nif.h>
#include "openssl_config.h"
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index af493a2cdb..daff2a4d51 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -104,6 +104,9 @@ static ErlNifFunc nif_funcs[] = {
{"pkey_sign_nif", 5, pkey_sign_nif, 0},
{"pkey_verify_nif", 6, pkey_verify_nif, 0},
{"pkey_crypt_nif", 6, pkey_crypt_nif, 0},
+ {"encapsulate_key_nif", 2, encapsulate_key_nif, 0},
+ {"decapsulate_key_nif", 3, decapsulate_key_nif, 0},
+ {"kem_algorithms_nif", 0, kem_algorithms_nif, 0},
{"rsa_generate_key_nif", 2, rsa_generate_key_nif, 0},
{"dh_generate_key_nif", 4, dh_generate_key_nif, 0},
{"dh_compute_key_nif", 3, dh_compute_key_nif, 0},
@@ -266,12 +269,14 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
ret = __LINE__; goto done;
}
if (!(prov[prov_cnt++] = OSSL_PROVIDER_load(NULL, "base"))) {
- ret = __LINE__; goto done;
+ ret = __LINE__; goto done;
}
if ((prov[prov_cnt] = OSSL_PROVIDER_load(NULL, "legacy"))) {
/* Don't fail loading if the legacy provider is missing */
prov_cnt++;
}
+ prefetched_sign_algo_init();
+
#endif
if (!init_atoms(env)) {
diff --git a/lib/crypto/c_src/evp.c b/lib/crypto/c_src/evp.c
index 30870ef5ad..f09eedb00e 100644
--- a/lib/crypto/c_src/evp.c
+++ b/lib/crypto/c_src/evp.c
@@ -19,8 +19,114 @@
*
* %CopyrightEnd%
*/
+#include <ctype.h>
#include "evp.h"
+#include "bn.h"
+#include "pkey.h"
+
+/* (Type, OthersPublicKey) -> {Secret, EncapSecret}
+ */
+ERL_NIF_TERM encapsulate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+#ifdef HAVE_ML_KEM
+ EVP_PKEY_CTX *ctx = NULL;
+ EVP_PKEY *peer_pkey = NULL;
+ size_t encaps_len, secret_len;
+ unsigned char *encaps_data, *secret_data;
+ ERL_NIF_TERM encaps_bin, secret_bin;
+ ERL_NIF_TERM ret;
+
+ if (!get_pkey_from_octet_string(env, argv[0], argv[1], PKEY_PUB,
+ &peer_pkey, &ret)) {
+ goto err;
+ }
+
+ ctx = EVP_PKEY_CTX_new_from_pkey(NULL, peer_pkey, NULL);
+ if (!ctx) {
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't create PKEY_CTX from key"));
+ }
+
+ if (EVP_PKEY_encapsulate_init(ctx, NULL) != 1) {
+ ERR_print_errors_fp(stderr);
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't encapsulate_init"));
+ }
+ if (EVP_PKEY_encapsulate(ctx, NULL, &encaps_len, NULL, &secret_len) != 1) {
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't get encapsulate sizes"));
+ }
+ encaps_data = enif_make_new_binary(env, encaps_len, &encaps_bin);
+ secret_data = enif_make_new_binary(env, secret_len, &secret_bin);
+ if (EVP_PKEY_encapsulate(ctx, encaps_data, &encaps_len, secret_data, &secret_len) != 1) {
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't encapsulate"));
+ }
+
+ ret = enif_make_tuple2(env, secret_bin, encaps_bin);
+
+err:
+ if (peer_pkey) {
+ EVP_PKEY_free(peer_pkey);
+ }
+ if (ctx) {
+ EVP_PKEY_CTX_free(ctx);
+ }
+ return ret;
+#else
+ return RAISE_NOTSUP(env);
+#endif
+}
+
+/* (Type, MyPrivKey, EncapSecret) -> Secret
+ */
+ERL_NIF_TERM decapsulate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+#ifdef HAVE_ML_KEM
+ EVP_PKEY_CTX *ctx = NULL;
+ EVP_PKEY *my_pkey = NULL;
+ size_t secret_len;
+ unsigned char *secret_data;
+ ERL_NIF_TERM secret_bin;
+ ErlNifBinary encaps;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_binary(env, argv[2], &encaps)) {
+ assign_goto(ret, err, EXCP_ERROR_N(env, 2, "Invalid encapsulated secret"));
+ }
+ if (!get_pkey_from_octet_string(env, argv[0], argv[1], PKEY_PRIV,
+ &my_pkey, &ret)) {
+ goto err;
+ }
+
+ ctx = EVP_PKEY_CTX_new_from_pkey(NULL, my_pkey, NULL);
+ if (!ctx) {
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't create PKEY_CTX from key"));
+ }
+
+ if (EVP_PKEY_decapsulate_init(ctx, NULL) != 1) {
+ ERR_print_errors_fp(stderr);
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't decapsulate_init"));
+ }
+ if (EVP_PKEY_decapsulate(ctx, NULL, &secret_len, encaps.data, encaps.size) != 1) {
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't get encapsulate sizes"));
+ }
+ secret_data = enif_make_new_binary(env, secret_len, &secret_bin);
+ if (EVP_PKEY_decapsulate(ctx, secret_data, &secret_len, encaps.data, encaps.size) != 1) {
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't encapsulate"));
+ }
+
+ ret = secret_bin;
+
+err:
+ if (my_pkey) {
+ EVP_PKEY_free(my_pkey);
+ }
+ if (ctx) {
+ EVP_PKEY_CTX_free(ctx);
+ }
+ return ret;
+#else
+ return RAISE_NOTSUP(env);
+#endif
+}
ERL_NIF_TERM evp_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
/* (Curve, PeerBin, MyBin) */
@@ -136,8 +242,27 @@ ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
else if (argv[0] == atom_ed448)
type = EVP_PKEY_ED448;
#endif
- else
- assign_goto(ret, bad_arg, EXCP_BADARG_N(env, 0, "Bad curve"));
+#ifdef HAVE_ML_DSA
+ else if (argv[0] == atom_mldsa44) {
+ type = EVP_PKEY_ML_DSA_44;
+ } else if (argv[0] == atom_mldsa65) {
+ type = EVP_PKEY_ML_DSA_65;
+ } else if (argv[0] == atom_mldsa87) {
+ type = EVP_PKEY_ML_DSA_87;
+ }
+#endif
+#ifdef HAVE_ML_KEM
+ else if (argv[0] == atom_mlkem512) {
+ type = NID_ML_KEM_512;
+ } else if (argv[0] == atom_mlkem768) {
+ type = NID_ML_KEM_768;
+ } else if (argv[0] == atom_mlkem1024) {
+ type = NID_ML_KEM_1024;
+ }
+#endif
+ else {
+ assign_goto(ret, err, EXCP_BADARG_N(env, 0, "Bad key type"));
+ }
if (argv[1] == atom_undefined) {
if ((ctx = EVP_PKEY_CTX_new_id(type, NULL)) == NULL)
@@ -170,7 +295,6 @@ ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
ret = enif_make_tuple2(env, ret_pub, ret_prv);
goto done;
- bad_arg:
err:
done:
if (pkey)
diff --git a/lib/crypto/c_src/evp.h b/lib/crypto/c_src/evp.h
index 35674b0eb0..9ca11eaba3 100644
--- a/lib/crypto/c_src/evp.h
+++ b/lib/crypto/c_src/evp.h
@@ -25,6 +25,8 @@
#include "common.h"
+ERL_NIF_TERM encapsulate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM decapsulate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM evp_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
diff --git a/lib/crypto/c_src/openssl_config.h b/lib/crypto/c_src/openssl_config.h
index f65f1858a4..9d7ef23a60 100644
--- a/lib/crypto/c_src/openssl_config.h
+++ b/lib/crypto/c_src/openssl_config.h
@@ -379,6 +379,14 @@
#endif
#endif
+#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(3,5,0)
+# ifndef OPENSSL_NO_ML_KEM
+# define HAVE_ML_KEM
+# endif
+# ifndef OPENSSL_NO_ML_DSA
+# define HAVE_ML_DSA
+# endif
+#endif
#if defined(HAS_ENGINE_SUPPORT)
# include <openssl/engine.h>
diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c
index 0b1e1bbb2c..f10998baff 100644
--- a/lib/crypto/c_src/pkey.c
+++ b/lib/crypto/c_src/pkey.c
@@ -46,6 +46,7 @@ typedef struct PKeySignOptions {
static int check_pkey_algorithm_type(ErlNifEnv *env,
int alg_arg_num, ERL_NIF_TERM algorithm,
+ int allow_unknown,
ERL_NIF_TERM *err_return);
static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm,
int type_arg_num, ERL_NIF_TERM type,
@@ -81,8 +82,42 @@ static int get_pkey_crypt_options(ErlNifEnv *env,
static size_t size_of_RSA(EVP_PKEY *pkey);
#endif
+#ifdef HAVE_ML_DSA
+static EVP_SIGNATURE* the_mldsa44_algo;
+static EVP_SIGNATURE* the_mldsa65_algo;
+static EVP_SIGNATURE* the_mldsa87_algo;
+#endif
+
+void prefetched_sign_algo_init(void)
+{
+#ifdef HAVE_ML_DSA
+ the_mldsa44_algo = EVP_SIGNATURE_fetch(NULL, "mldsa44", NULL);
+ the_mldsa65_algo = EVP_SIGNATURE_fetch(NULL, "mldsa65", NULL);
+ the_mldsa87_algo = EVP_SIGNATURE_fetch(NULL, "mldsa87", NULL);
+#endif
+}
+
+#ifdef HAS_3_0_API
+static EVP_SIGNATURE* get_prefetched_sign_algo(ERL_NIF_TERM alg_atom)
+{
+#ifdef HAVE_ML_DSA
+ if (alg_atom == atom_mldsa44) {
+ return the_mldsa44_algo;
+ }
+ else if (alg_atom == atom_mldsa65) {
+ return the_mldsa65_algo;
+ }
+ else if (alg_atom == atom_mldsa87) {
+ return the_mldsa87_algo;
+ }
+#endif
+ return NULL;
+}
+#endif
+
static int check_pkey_algorithm_type(ErlNifEnv *env,
int alg_arg_num, ERL_NIF_TERM algorithm,
+ int allow_unknown,
ERL_NIF_TERM *err_return)
{
if (
@@ -106,7 +141,8 @@ static int check_pkey_algorithm_type(ErlNifEnv *env,
assign_goto(*err_return, err, EXCP_NOTSUP_N(env, alg_arg_num, "Unsupported algorithm in FIPS mode"));
#endif
- if ((algorithm != atom_rsa) &&
+ if (!allow_unknown &&
+ (algorithm != atom_rsa) &&
(algorithm != atom_dss) &&
(algorithm != atom_ecdsa) &&
(algorithm != atom_eddsa)
@@ -128,8 +164,18 @@ static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm,
struct digest_type_t *digp = NULL;
*md = NULL;
- if (type == atom_none && algorithm == atom_rsa)
- return 1;
+ if (type == atom_none) {
+ if (algorithm == atom_rsa) {
+ return 1;
+ }
+#ifdef HAVE_ML_DSA
+ if (algorithm == atom_mldsa44 ||
+ algorithm == atom_mldsa65 ||
+ algorithm == atom_mldsa87) {
+ return 1;
+ }
+#endif
+ }
if (algorithm == atom_eddsa) /* Type was skipped for eddsa in < OTP-25
For eddsa the RFC 8032 mandates sha512 in
@@ -173,8 +219,10 @@ static int get_pkey_sign_digest(ErlNifEnv *env,
tbs = *tbsp;
tbslen = *tbslenp;
- if (!check_pkey_algorithm_type(env, algorithm_arg_num, argv[algorithm_arg_num], err_return))
+ if (!check_pkey_algorithm_type(env, algorithm_arg_num, argv[algorithm_arg_num],
+ 1, err_return)) {
goto err; /* An exception is present in ret */
+ }
if (!get_pkey_digest_type(env, argv[algorithm_arg_num],
type_arg_num, argv[type_arg_num],
@@ -329,10 +377,11 @@ static int get_pkey_sign_options(ErlNifEnv *env,
}
static int get_pkey_private_key(ErlNifEnv *env,
- const ERL_NIF_TERM argv[],
- int algorithm_arg_num, int key_arg_num,
- EVP_PKEY **pkey,
- ERL_NIF_TERM *err_return)
+ const ERL_NIF_TERM argv[],
+ int algorithm_arg_num,
+ int key_arg_num,
+ EVP_PKEY **pkey,
+ ERL_NIF_TERM *err_return)
{
char *id = NULL;
char *password = NULL;
@@ -385,8 +434,39 @@ static int get_pkey_private_key(ErlNifEnv *env,
assign_goto(*err_return, err, EXCP_NOTSUP_N(env, algorithm_arg_num, "DSA not supported"));
#endif
- } else
- assign_goto(*err_return, err, EXCP_BADARG_N(env, algorithm_arg_num, "Bad algorithm"));
+ } else {
+#ifndef HAS_3_0_API
+ assign_goto(*err_return, err, EXCP_BADARG_N(env, algorithm_arg_num, "Bad algorithm"));
+#else
+ enum pkey_format_t pkey_format = PKEY_PRIV;
+ ERL_NIF_TERM key_bin = argv[key_arg_num];
+
+# ifdef HAVE_ML_DSA
+ if (argv[algorithm_arg_num] == atom_mldsa44 ||
+ argv[algorithm_arg_num] == atom_mldsa65 ||
+ argv[algorithm_arg_num] == atom_mldsa87) {
+ int tpl_arity;
+ const ERL_NIF_TERM* tpl_array;
+
+ if (!enif_get_tuple(env, argv[key_arg_num], &tpl_arity, &tpl_array)
+ || tpl_arity != 2) {
+ assign_goto(*err_return, err, EXCP_BADARG_N(env, key_arg_num, "MLDSA key not 2-tuple"));
+ }
+ if (tpl_array[0] == atom_seed) {
+ pkey_format = PKEY_PRIV_SEED;
+ }
+ else if (tpl_array[0] != atom_expandedkey) {
+ assign_goto(*err_return, err, EXCP_BADARG_N(env, key_arg_num, "Invalid MLDSA key tuple"));
+ }
+ key_bin = tpl_array[1];
+ }
+# endif
+ if (!get_pkey_from_octet_string(env, argv[algorithm_arg_num], key_bin,
+ pkey_format, pkey, err_return)) {
+ goto err;
+ }
+#endif
+ }
ret = 1;
done:
@@ -405,6 +485,79 @@ static int get_pkey_private_key(ErlNifEnv *env,
goto done;
}
+#ifdef HAS_3_0_API
+int get_pkey_from_octet_string(ErlNifEnv *env,
+ ERL_NIF_TERM alg_atom,
+ ERL_NIF_TERM key_bin,
+ enum pkey_format_t pkey_format,
+ EVP_PKEY **pkey_p,
+ ERL_NIF_TERM *ret_p)
+{
+ char type[40];
+ OSSL_PARAM params[2];
+ int i = 0;
+ EVP_PKEY *pkey = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ ErlNifBinary keyb;
+ const char* key_type;
+ int selection;
+
+ if (!enif_get_atom(env, alg_atom, type, sizeof(type), ERL_NIF_UTF8)) {
+ assign_goto(*ret_p, err, EXCP_ERROR_N(env, 0, "Invalid key type"));
+ }
+ if (!enif_inspect_binary(env, key_bin, &keyb)) {
+ assign_goto(*ret_p, err, EXCP_ERROR_N(env, 1, "Invalid public key"));
+ }
+ switch (pkey_format) {
+#ifdef HAVE_ML_DSA
+ case PKEY_PRIV_SEED:
+ key_type = "seed"; // OSSL_PKEY_PARAM_ML_DSA_SEED
+ selection = EVP_PKEY_KEYPAIR;
+ break;
+#endif
+ case PKEY_PRIV:
+ key_type = "priv"; // OSSL_PKEY_PARAM_PRIV_KEY;
+ selection = EVP_PKEY_KEYPAIR;
+ break;
+ default:
+ ASSERT(pkey_format == PKEY_PUB);
+ key_type = "pub"; // OSSL_PKEY_PARAM_PUB_KEY;
+ selection = EVP_PKEY_PUBLIC_KEY;
+ break;
+ }
+
+ params[i++] = OSSL_PARAM_construct_octet_string(key_type, keyb.data, keyb.size);
+ params[i++] = OSSL_PARAM_construct_end();
+
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, type, NULL);
+ if (!ctx) {
+ assign_goto(*ret_p, err, EXCP_ERROR(env, "Can't create PKEY_CTX from name"));
+ }
+
+ if (EVP_PKEY_fromdata_init(ctx) <= 0) {
+ assign_goto(*ret_p, err, EXCP_ERROR(env, "Can't init fromdata"));
+ }
+
+ if (EVP_PKEY_fromdata(ctx, &pkey, selection, params) <= 0) {
+ assign_goto(*ret_p, err, EXCP_ERROR(env, "Can't create PKEY fromdata"));
+ }
+
+ /*
+ ** The caller might need an EVP_PKEY_CTX, but the one we have here isn't
+ ** good enough for EVP_PKEY_encapsulate_init and friends for some reason!?
+ */
+ EVP_PKEY_CTX_free(ctx);
+
+ *pkey_p = pkey;
+
+ return 1;
+
+err:
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(pkey);
+ return 0;
+}
+#endif // HAS_3_0_API
static int get_pkey_public_key(ErlNifEnv *env,
const ERL_NIF_TERM argv[],
@@ -462,8 +615,17 @@ static int get_pkey_public_key(ErlNifEnv *env,
#else
assign_goto(*err_return, err, EXCP_NOTSUP_N(env, algorithm_arg_num, "DSA not supported"));
#endif
- } else
+ } else {
+#ifdef HAS_3_0_API
+ if (!get_pkey_from_octet_string(env, argv[algorithm_arg_num], argv[key_arg_num],
+ PKEY_PUB, pkey, err_return)) {
+ goto err;
+ }
+#else
assign_goto(*err_return, err, EXCP_BADARG_N(env, algorithm_arg_num, "Bad algorithm"));
+#endif
+ }
+
ret = 1;
@@ -528,8 +690,27 @@ ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
assign_goto(ret, err, EXCP_ERROR(env, "Can't allocate new EVP_PKEY_CTX"));
if (argv[0] != atom_eddsa) {
- if (EVP_PKEY_sign_init(ctx) != 1)
+#ifdef HAS_3_0_API
+ EVP_SIGNATURE *sig_alg = get_prefetched_sign_algo(argv[0]);
+
+ if (!sig_alg) {
+ if (EVP_PKEY_sign_init(ctx) != 1) {
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign_init"));
+ }
+ }
+ else {
+# ifdef HAVE_ML_DSA
+ if (EVP_PKEY_sign_message_init(ctx, sig_alg, NULL) != 1)
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign_message_init"));
+# else
+ assign_goto(ret, err, EXCP_ERROR(env, "Unknown signature algorithm"));
+# endif
+ }
+#else
+ if (EVP_PKEY_sign_init(ctx) != 1) {
assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign_init"));
+ }
+#endif
if (md != NULL) {
if (EVP_PKEY_CTX_set_signature_md(ctx, md) != 1)
assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_CTX_set_signature_md"));
@@ -764,8 +945,27 @@ ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]
assign_goto(ret, err, EXCP_ERROR(env, "Can't allocate new EVP_PKEY_CTX"));
if (argv[0] != atom_eddsa) {
- if (EVP_PKEY_verify_init(ctx) != 1)
- assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign_init"));
+#ifdef HAS_3_0_API
+ EVP_SIGNATURE *sig_alg = get_prefetched_sign_algo(argv[0]);
+
+ if (!sig_alg) {
+ if (EVP_PKEY_verify_init(ctx) != 1) {
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_verify_init"));
+ }
+ }
+ else {
+# ifdef HAVE_ML_DSA
+ if (EVP_PKEY_verify_message_init(ctx, sig_alg, NULL) != 1)
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_verify_message_init"));
+# else
+ assign_goto(ret, err, EXCP_ERROR(env, "Unknown signature algorithm"));
+# endif
+ }
+#else
+ if (EVP_PKEY_verify_init(ctx) != 1) {
+ assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_verify_init"));
+ }
+#endif
if (md != NULL) {
if (EVP_PKEY_CTX_set_signature_md(ctx, md) != 1)
assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_CTX_set_signature_md"));
@@ -1042,7 +1242,7 @@ ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
is_private = (argv[4] == atom_true);
is_encrypt = (argv[5] == atom_true);
- if (!check_pkey_algorithm_type(env, 0, argv[0], &ret))
+ if (!check_pkey_algorithm_type(env, 0, argv[0], 0, &ret))
goto err; /* An exception is present in ret */
if (!enif_inspect_binary(env, argv[1], &in_bin))
@@ -1294,7 +1494,7 @@ ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
ASSERT(argc == 2);
- if (!check_pkey_algorithm_type(env, 0, argv[0], &ret))
+ if (!check_pkey_algorithm_type(env, 0, argv[0], 0, &ret))
goto err; /* An exception is present in ret */
if (!get_pkey_private_key(env, argv, 0, 1, &pkey, &ret)) // handles engine
diff --git a/lib/crypto/c_src/pkey.h b/lib/crypto/c_src/pkey.h
index a4eac62118..257a755aef 100644
--- a/lib/crypto/c_src/pkey.h
+++ b/lib/crypto/c_src/pkey.h
@@ -25,6 +25,22 @@
#include "common.h"
+void prefetched_sign_algo_init(void);
+
+#ifdef HAS_3_0_API
+enum pkey_format_t {
+ PKEY_PUB = 0,
+ PKEY_PRIV = 1,
+ PKEY_PRIV_SEED = 2
+};
+int get_pkey_from_octet_string(ErlNifEnv*,
+ ERL_NIF_TERM alg_atom,
+ ERL_NIF_TERM key_bin,
+ enum pkey_format_t,
+ EVP_PKEY **pkey_p,
+ ERL_NIF_TERM *ret_p);
+#endif
+
ERL_NIF_TERM pkey_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM pkey_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index a2c66bad54..938b898118 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -157,6 +157,7 @@ end
-export([hash/2, hash_xof/3, hash_init/1, hash_update/2, hash_final/1, hash_final_xof/2]).
-export([sign/4, sign/5, verify/5, verify/6]).
-export([generate_key/2, generate_key/3, compute_key/4]).
+-export([encapsulate_key/2, decapsulate_key/3]).
-export([exor/2, strong_rand_bytes/1, mod_pow/3]).
-export([rand_seed/0, rand_seed_alg/1, rand_seed_alg/2]).
-export([rand_seed_s/0, rand_seed_alg_s/1, rand_seed_alg_s/2]).
@@ -268,6 +269,7 @@ end
-nifs([info_nif/0, info_lib/0, info_fips/0, enable_fips_mode_nif/1,
hash_algorithms/0, pubkey_algorithms/0, cipher_algorithms/0,
+ kem_algorithms_nif/0,
mac_algorithms/0, curve_algorithms/0, rsa_opts_algorithms/0,
hash_info/1, hash_nif/2, hash_init_nif/1, hash_update_nif/2,
hash_final_nif/1, hash_final_xof_nif/2, mac_nif/4, mac_init_nif/3, mac_update_nif/2,
@@ -277,6 +279,7 @@ end
strong_rand_bytes_nif/1, strong_rand_range_nif/1, rand_uniform_nif/2,
mod_exp_nif/4, do_exor/2, hash_equals_nif/2, pbkdf2_hmac_nif/5,
pkey_sign_nif/5, pkey_verify_nif/6, pkey_crypt_nif/6,
+ encapsulate_key_nif/2, decapsulate_key_nif/3,
rsa_generate_key_nif/2, dh_generate_key_nif/4, dh_compute_key_nif/3,
evp_compute_key_nif/3, evp_generate_key_nif/2, privkey_to_pubkey_nif/2,
srp_value_B_nif/5, srp_user_secret_nif/7, srp_host_secret_nif/5,
@@ -343,6 +346,10 @@ end
-doc(#{title => <<"Keys">>}).
-type key_integer() :: integer() | binary(). % Always binary() when used as return value
+-doc "Key encapsulation mechanisms.".
+-doc(#{title => <<"Key Encapsulation Mechanism">>}).
+-type kem() :: mlkem512 | mlkem768 | mlkem1024.
+
%%% Keys
-doc(#{title => <<"Public/Private Keys">>,equiv => rsa_params()}).
-type rsa_public() :: [key_integer()] . % [E, N]
@@ -808,19 +815,22 @@ stop() ->
-spec supports() -> [Support]
when Support :: {hashs, Hashs}
| {ciphers, Ciphers}
+ | {kems, KEMs}
| {public_keys, PKs}
| {macs, Macs}
| {curves, Curves}
| {rsa_opts, RSAopts},
Hashs :: [sha1() | sha2() | sha3() | sha3_xof() | blake2() | ripemd160 | sm3 | compatibility_only_hash()],
Ciphers :: [cipher()],
- PKs :: [rsa | dss | ecdsa | dh | ecdh | eddh | ec_gf2m],
+ KEMs :: [kem()],
+ PKs :: [rsa | dss | ecdsa | dh | ecdh | eddh | ec_gf2m | mldsa()],
Macs :: [hmac | cmac | poly1305],
Curves :: [ec_named_curve() | edwards_curve_dh() | edwards_curve_ed()],
RSAopts :: [rsa_sign_verify_opt() | rsa_opt()] .
supports() ->
[{hashs, supports(hashs)},
- {ciphers, supports(ciphers)}
+ {ciphers, supports(ciphers)},
+ {kems, supports(kems)}
| [{T,supports(T)} || T <- [public_keys,
macs,
curves,
@@ -840,18 +850,21 @@ algorithms.
-spec supports(Type) -> Support
when Type :: hashs
| ciphers
+ | kems
| public_keys
| macs
| curves
| rsa_opts,
Support :: Hashs
| Ciphers
+ | KEMs
| PKs
| Macs
| Curves
| RSAopts,
Hashs :: [sha1() | sha2() | sha3() | sha3_xof() | blake2() | ripemd160 | compatibility_only_hash()],
Ciphers :: [cipher()],
+ KEMs :: [kem()],
PKs :: [rsa | dss | ecdsa | dh | ecdh | eddh | ec_gf2m],
Macs :: [hmac | cmac | poly1305],
Curves :: [ec_named_curve() | edwards_curve_dh() | edwards_curve_ed()],
@@ -863,6 +876,7 @@ algorithms.
supports(hashs) -> hash_algorithms();
supports(public_keys) -> pubkey_algorithms();
supports(ciphers) -> add_cipher_aliases(cipher_algorithms());
+supports(kems) -> kem_algorithms_nif();
supports(macs) -> mac_algorithms();
supports(curves) -> curve_algorithms();
supports(rsa_opts) -> rsa_opts_algorithms().
@@ -2424,7 +2438,10 @@ rand_seed_nif(_Seed) -> ?nif_stub.
%%%================================================================
-doc "Algorithms for sign and verify.".
-doc(#{title => <<"Public Key Sign and Verify">>}).
--type pk_sign_verify_algs() :: rsa | dss | ecdsa | eddsa .
+-type pk_sign_verify_algs() :: rsa | dss | ecdsa | eddsa | mldsa().
+-type mldsa() :: mldsa44 | mldsa65 | mldsa87.
+-type mldsa_private() :: {seed | expandedkey, binary()}.
+-type mldsa_public() :: binary().
-doc(#{title => <<"Public Key Sign and Verify">>,
equiv => rsa_sign_verify_padding()}).
@@ -2470,6 +2487,7 @@ Options for sign and verify.
| dss_private()
| [ecdsa_private() | ecdsa_params()]
| [eddsa_private() | eddsa_params()]
+ | mldsa_private()
| engine_key_ref(),
Signature :: binary() .
@@ -2503,6 +2521,7 @@ See also `public_key:sign/3`.
| dss_private()
| [ecdsa_private() | ecdsa_params()]
| [eddsa_private() | eddsa_params()]
+ | mldsa_private()
| engine_key_ref(),
Options :: pk_sign_verify_opts(),
Signature :: binary() .
@@ -2534,6 +2553,7 @@ pkey_sign_nif(_Algorithm, _Type, _Digest, _Key, _Options) -> ?nif_stub.
| dss_public()
| [ecdsa_public() | ecdsa_params()]
| [eddsa_public() | eddsa_params()]
+ | mldsa_public()
| engine_key_ref(),
Result :: boolean().
@@ -2567,6 +2587,7 @@ See also `public_key:verify/4`.
| dss_public()
| [ecdsa_public() | ecdsa_params()]
| [eddsa_public() | eddsa_params()]
+ | mldsa_public()
| engine_key_ref(),
Options :: pk_sign_verify_opts(),
Result :: boolean().
@@ -2877,11 +2898,59 @@ generate_key(eddsa, Curve, PrivKey) when Curve == ed448 ;
?nif_call(evp_generate_key_nif(Curve, ensure_int_as_bin(PrivKey)),
{2, 3},
[eddsa, Curve, PrivKey]
- ).
+ );
+generate_key(Type, [], PrivKey) ->
+ ?nif_call(evp_generate_key_nif(Type, PrivKey)).
+
evp_generate_key_nif(_Curve, _PrivKey) -> ?nif_stub.
+-doc """
+Generate encapsulated shared secret from the other party's public key.
+
+Returns both a shared secret for encryption/decryption by local party and an
+encapsulated format of the same secret to be safely sent to the other
+party. With its private key, the other party can decapsulate the received secret
+(with `decapsulate_key/3` for example) to regenerate the same shared secret.
+
+Supported encapsulation methods can be obtained with
+[`supports(kems)`](`supports/1`).
+""".
+-doc(#{group => ~b"Key API",
+ since => ~b"OTP @OTP-19657@"}).
+-spec encapsulate_key(Type, OthersPublicKey) -> {Secret, EncapSecret}
+ when Type :: kem(),
+ OthersPublicKey :: binary(),
+ Secret :: binary(),
+ EncapSecret :: binary().
+encapsulate_key(Type, OthersPublicKey) ->
+ encapsulate_key_nif(Type, OthersPublicKey).
+
+encapsulate_key_nif(_Type, _OthersPublicKey) -> ?nif_stub.
+
+
+-doc """
+Regenerate shared secret from encapsulated secret and private key.
+
+Returns a shared secret for encryption/decryption by local party.
+
+Supported encapsulation methods can be obtained with
+[`supports(kems)`](`supports/1`).
+""".
+-doc(#{group => ~b"Key API",
+ since => ~b"OTP @OTP-19657@"}).
+-spec decapsulate_key(Type, MyPrivKey, EncapSecret) -> Secret
+ when Type :: kem(),
+ MyPrivKey :: binary(),
+ EncapSecret :: binary(),
+ Secret :: binary().
+decapsulate_key(Type, MyPrivKey, EncapSecret) ->
+ decapsulate_key_nif(Type, MyPrivKey, EncapSecret).
+
+decapsulate_key_nif(_Type, _MyPrivKey, _EncapSecret) -> ?nif_stub.
+
+
-doc """
Compute the shared secret from the private key and the other party's public
key.
@@ -3822,6 +3891,7 @@ hash_equals_nif(_A, _B) -> ?nif_stub.
hash_algorithms() -> ?nif_stub.
pubkey_algorithms() -> ?nif_stub.
cipher_algorithms() -> ?nif_stub.
+kem_algorithms_nif() -> ?nif_stub.
mac_algorithms() -> ?nif_stub.
curve_algorithms() -> ?nif_stub.
rsa_opts_algorithms() -> ?nif_stub.
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index dfdf8d8ffb..5be5da69bb 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -73,6 +73,7 @@
compute_bug/1,
crypto_load/1,
crypto_load_and_call/1,
+ encapsulate/1,
exor/0,
exor/1,
generate/0,
@@ -125,6 +126,7 @@
hash_equals/1,
sign_verify/0,
sign_verify/1,
+ sign_verify_oqs/1,
ec_key_padding/1,
use_all_ec_sign_verify/1,
use_all_ecdh_generate_compute/1,
@@ -219,6 +221,8 @@ all() ->
ec_key_padding,
node_supports_cache,
mod_pow,
+ encapsulate,
+ sign_verify_oqs,
exor,
rand_uniform,
rand_threads,
@@ -695,8 +699,6 @@ simple_cipher_test(Cipher) ->
error
end.
-
-
enc_dec(Cipher, Key, 0, _Mode, Plain) ->
case crypto:crypto_one_time(Cipher, Key, Plain, true) of
Encrypted when is_binary(Encrypted) ->
@@ -1242,6 +1244,62 @@ compute(Config) when is_list(Config) ->
end,
lists:foreach(fun do_compute/1, Gen).
+%%--------------------------------------------------------------------
+encapsulate(_Config) ->
+ case openssl_version() of
+ V when V < {3,5,0} ->
+ {skip, "Requires OpenSSL 3.5 "};
+ _ ->
+ KEM_algs = [mlkem512, mlkem768, mlkem1024],
+ Supported = crypto:supports(kems),
+ [begin
+ true = lists:member(Alg, Supported),
+ encap_decap(Alg)
+ end || Alg <- KEM_algs],
+ ok
+ end.
+
+encap_decap(Alg) ->
+ io:format("Alg = ~p\n", [Alg]),
+ {Pub, Priv} = crypto:generate_key(Alg, []),
+ {Pub, Priv} = crypto:generate_key(Alg, [], Priv),
+ {Secret, Encap} = crypto:encapsulate_key(Alg, Pub),
+ Secret2 = crypto:decapsulate_key(Alg, Priv, Encap),
+ {Secret2,Secret} = {Secret,Secret2},
+ ok.
+
+%%--------------------------------------------------------------------
+sign_verify_oqs(_Config) ->
+ case openssl_version() of
+ V when V < {3,5,0} ->
+ {skip, "Requires OpenSSL 3.5"};
+ _ ->
+ Supported = crypto:supports(public_keys),
+ [begin
+ true = lists:member(Alg, Supported),
+ sign_verify_oqs_do(Alg)
+ end
+ || Alg <- mldsa_sign_ciphers()],
+ ok
+ end.
+
+sign_verify_oqs_do(Alg) ->
+ io:format("Alg = ~p\n", [Alg]),
+ {Pub, Priv} = crypto:generate_key(Alg, []),
+ {Pub, Priv} = crypto:generate_key(Alg, [], Priv),
+ Msg = "Hejsan",
+ [begin
+ io:format("Hash = ~p\n", [Hash]),
+ Sign = crypto:sign(Alg, Hash, Msg, {expandedkey,Priv}),
+ true = crypto:verify(Alg, Hash, Msg, Sign, Pub)
+ end
+ || Hash <- [none, sha]],
+ ok.
+
+%% Supported by OpenSSL 3.5
+mldsa_sign_ciphers() ->
+ [mldsa44, mldsa65, mldsa87].
+
%%--------------------------------------------------------------------
use_all_ec_sign_verify(_Config) ->
Msg = <<"hello world!">>,
@@ -4908,13 +4966,13 @@ bad_sign_name(_Config) ->
?chk_api_name(crypto:sign(rsa, foobar, "nothing", <<1:1024>>),
error:{badarg, {"pkey.c",_}, "Bad digest type"++_}),
?chk_api_name(crypto:sign(foobar, sha, "nothing", <<1:1024>>),
- error:{badarg, {"pkey.c",_}, "Bad algorithm"++_}).
+ error:{_, {"pkey.c",_}, _}).
bad_verify_name(_Config) ->
?chk_api_name(crypto:verify(rsa, foobar, "nothing", <<"nothing">>, <<1:1024>>),
error:{badarg,{"pkey.c",_},"Bad digest type"++_}),
?chk_api_name(crypto:verify(foobar, sha, "nothing", <<"nothing">>, <<1:1024>>),
- error:{badarg, {"pkey.c",_}, "Bad algorithm"++_}).
+ error:{_, {"pkey.c",_}, _}).
%%%----------------------------------------------------------------
@@ -5077,3 +5135,12 @@ get_priv_pub({Type, undefined=_Hash, Private, Public, _Msg, _Signature}, Acc) ->
get_priv_pub({Type, _Hash, Public, Private, _Msg}, Acc) -> [{Type,Private,Public} | Acc];
get_priv_pub({Type, _Hash, Public, Private, _Msg, _Options}, Acc) -> [{Type,Private,Public} | Acc];
get_priv_pub(_, Acc) -> Acc.
+
+openssl_version() ->
+ case crypto:info_lib() of
+ [{<<"OpenSSL">>,Ver,<<"OpenSSL",_/binary>>}] ->
+ <<Maj,Min,Patch>> = <<(Ver bsr 12):24/integer>>,
+ {Maj,Min,Patch};
+ _ ->
+ undefined
+ end.
--
2.43.0