From: Helen Zhang Date: Fri, 21 Nov 2025 19:11:26 +0000 (+0000) Subject: Add SNMPKDF implementation X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1b035166bdb2d86e718ad51a55caffc8bc9504a7;p=thirdparty%2Fopenssl.git Add SNMPKDF implementation In compliance with SP800-135 and RFC7860 Reviewed-by: Shane Lontis Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/29195) --- diff --git a/CHANGES.md b/CHANGES.md index 562ee1112fa..8a04fadc9c5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -84,6 +84,10 @@ OpenSSL 4.0 *Milan Broz*, *Neil Horman*, *Norbert Pocs* + * Added SNMP KDF (EVP_KDF_SNMPKDF) to EVP_KDF + + *Barry Fussell and Helen Zhang* + OpenSSL 3.6 ----------- diff --git a/build.info b/build.info index 1faec4d8de0..f2d6da6c8bf 100644 --- a/build.info +++ b/build.info @@ -67,6 +67,7 @@ DEPEND[]=include/openssl/asn1.h \ providers/implementations/kdfs/pkcs12kdf.inc \ providers/implementations/kdfs/pvkkdf.inc \ providers/implementations/kdfs/scrypt.inc \ + providers/implementations/kdfs/snmpkdf.inc \ providers/implementations/kdfs/sshkdf.inc \ providers/implementations/kdfs/sskdf.inc \ providers/implementations/kdfs/tls1_prf.inc \ @@ -184,6 +185,7 @@ DEPEND[providers/implementations/asymciphers/rsa_enc.inc \ providers/implementations/kdfs/pkcs12kdf.inc \ providers/implementations/kdfs/pvkkdf.inc \ providers/implementations/kdfs/scrypt.inc \ + providers/implementations/kdfs/snmpkdf.inc \ providers/implementations/kdfs/sshkdf.inc \ providers/implementations/kdfs/sskdf.inc \ providers/implementations/kdfs/tls1_prf.inc \ @@ -291,6 +293,8 @@ GENERATE[providers/implementations/kdfs/pvkkdf.inc]=\ providers/implementations/kdfs/pvkkdf.inc.in GENERATE[providers/implementations/kdfs/scrypt.inc]=\ providers/implementations/kdfs/scrypt.inc.in +GENERATE[providers/implementations/kdfs/snmpkdf.inc]=\ + providers/implementations/kdfs/snmpkdf.inc.in GENERATE[providers/implementations/kdfs/sshkdf.inc]=\ providers/implementations/kdfs/sshkdf.inc.in GENERATE[providers/implementations/kdfs/sskdf.inc]=\ diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 568b6bd5aa4..8bd83215dd0 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -1101,6 +1101,7 @@ PROV_R_MISSING_CEK_ALG:144:missing cek alg PROV_R_MISSING_CIPHER:155:missing cipher PROV_R_MISSING_CONFIG_DATA:213:missing config data PROV_R_MISSING_CONSTANT:156:missing constant +PROV_R_MISSING_EID:255:missing eid PROV_R_MISSING_KEY:128:missing key PROV_R_MISSING_MAC:150:missing mac PROV_R_MISSING_MESSAGE_DIGEST:129:missing message digest diff --git a/doc/build.info b/doc/build.info index ed6b7d1afed..d7ca9723b16 100644 --- a/doc/build.info +++ b/doc/build.info @@ -4643,6 +4643,10 @@ DEPEND[html/man7/EVP_KDF-SCRYPT.html]=man7/EVP_KDF-SCRYPT.pod GENERATE[html/man7/EVP_KDF-SCRYPT.html]=man7/EVP_KDF-SCRYPT.pod DEPEND[man/man7/EVP_KDF-SCRYPT.7]=man7/EVP_KDF-SCRYPT.pod GENERATE[man/man7/EVP_KDF-SCRYPT.7]=man7/EVP_KDF-SCRYPT.pod +DEPEND[html/man7/EVP_KDF-SNMPKDF.html]=man7/EVP_KDF-SNMPKDF.pod +GENERATE[html/man7/EVP_KDF-SNMPKDF.html]=man7/EVP_KDF-SNMPKDF.pod +DEPEND[man/man7/EVP_KDF-SNMPKDF.7]=man7/EVP_KDF-SNMPKDF.pod +GENERATE[man/man7/EVP_KDF-SNMPKDF.7]=man7/EVP_KDF-SNMPKDF.pod DEPEND[html/man7/EVP_KDF-SS.html]=man7/EVP_KDF-SS.pod GENERATE[html/man7/EVP_KDF-SS.html]=man7/EVP_KDF-SS.pod DEPEND[man/man7/EVP_KDF-SS.7]=man7/EVP_KDF-SS.pod @@ -5213,6 +5217,7 @@ html/man7/EVP_KDF-PBKDF2.html \ html/man7/EVP_KDF-PKCS12KDF.html \ html/man7/EVP_KDF-PVKKDF.html \ html/man7/EVP_KDF-SCRYPT.html \ +html/man7/EVP_KDF-SNMPKDF.html \ html/man7/EVP_KDF-SS.html \ html/man7/EVP_KDF-SSHKDF.html \ html/man7/EVP_KDF-TLS13_KDF.html \ @@ -5373,6 +5378,7 @@ man/man7/EVP_KDF-PBKDF2.7 \ man/man7/EVP_KDF-PKCS12KDF.7 \ man/man7/EVP_KDF-PVKKDF.7 \ man/man7/EVP_KDF-SCRYPT.7 \ +man/man7/EVP_KDF-SNMPKDF.7 \ man/man7/EVP_KDF-SS.7 \ man/man7/EVP_KDF-SSHKDF.7 \ man/man7/EVP_KDF-TLS13_KDF.7 \ diff --git a/doc/man1/openssl-kdf.pod.in b/doc/man1/openssl-kdf.pod.in index 6eed74d70d4..ae153b4b817 100644 --- a/doc/man1/openssl-kdf.pod.in +++ b/doc/man1/openssl-kdf.pod.in @@ -141,7 +141,7 @@ This option is identical to the B<-mac> option. Specifies the name of a supported KDF algorithm which will be used. The supported algorithms names include TLS1-PRF, HKDF, SSKDF, PBKDF2, -SSHKDF, X942KDF-ASN1, X942KDF-CONCAT, X963KDF and SCRYPT. +SNMPKDF, SSHKDF, X942KDF-ASN1, X942KDF-CONCAT, X963KDF and SCRYPT. =back @@ -175,6 +175,12 @@ Use SSKDF with Hash to create a hex-encoded derived key from a secret key, salt -kdfopt hexkey:6dbdc23f045488 \ -kdfopt hexinfo:a1b2c3d4 SSKDF +Use SNMPKDF to create a hex-encoded derived key from an engine ID, hash and password: + + openssl kdf -keylen 20 -kdfopt digest:SHA1 \ + -kdfopt pass:IFUcNbMl \ + -kdfopt hexeid:800002b805123456789abcdef0123456789abcdef0123456789abcdef0123456 SNMPKDF + Use SSHKDF to create a hex-encoded derived key from a secret key, hash and session_id: openssl kdf -keylen 16 -kdfopt digest:SHA2-256 \ @@ -208,6 +214,7 @@ L, L, L, L, +L, L, L, L, diff --git a/doc/man7/EVP_KDF-SNMPKDF.pod b/doc/man7/EVP_KDF-SNMPKDF.pod new file mode 100644 index 00000000000..c0bcb70deeb --- /dev/null +++ b/doc/man7/EVP_KDF-SNMPKDF.pod @@ -0,0 +1,110 @@ +=pod + +=head1 NAME + +EVP_KDF-SNMPKDF - The SNMPKDF EVP_KDF implementation + +=head1 DESCRIPTION + +Support for computing the B KDF through the B API. + +The EVP_KDF-SNMPKDF algorithm implements the SNMPKDF key derivation function. +It is defined in RFC 3414, appendix A.2.2 and is used by SNMP to derive +encryption keys. +Three inputs are required to perform key derivation: The hashing function +(for example SHA1), the engine ID, and the password + +=head2 Identity + +"SNMPKDF" is the name for this implementation; it +can be used with the EVP_KDF_fetch() function. + +=head2 Supported parameters + +The supported parameters are: + +=over 4 + +=item "properties" (B) + +=item "digest" (B) + +=item "pass" (B) + +These parameters works as described in L. + +=item "eid" (B) + +This parameter sets the snmpEngineID value for the KDF. +If a value is already set, the contents are replaced. + +=back + +=head1 NOTES + +A context for SNMPKDF can be obtained by calling: + + EVP_KDF *kdf = EVP_KDF_fetch(NULL, "SNMPKDF", NULL); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + +The expected length of the SNMPKDF derivation output is the size of the I. +Since the SNMPKDF output length can vary depending on the digest used, the caller should allocate a buffer of the +digest size and pass that buffer in the L function along with the expected length. + +=head1 EXAMPLES + +This example derives an 8 byte IV using SHA1 with a 1K "key" and appropriate +"xcghash" and "session_id" values: + + EVP_KDF *kdf; + EVP_KDF_CTX *kctx; + unsigned char eid[1024] = "01234..."; + unsigned char pass[32] = "012345..."; + unsigned char out[20]; + size_t outlen = sizeof(out); + OSSL_PARAM params[6], *p = params; + + kdf = EVP_KDF_fetch(NULL, "SNMPKDF", NULL); + kctx = EVP_KDF_CTX_new(kdf); + EVP_KDF_free(kdf); + + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + SN_sha1, strlen(SN_sha1)); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASS, + pass, sizeof(pass)); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SNMPKDF_EID, + eid, sizeof(eid)); + *p = OSSL_PARAM_construct_end(); + if (EVP_KDF_derive(kctx, out, outlen, params) <= 0) + /* Error */ + + +=head1 CONFORMING TO + +RFC 3414, RFC 7860, NIST SP800-135 + +=head1 SEE ALSO + +L, +L, +L, +L, +L, +L, +L, +L + +=head1 HISTORY + +This functionality was added in OpenSSL 4.0. + +=head1 COPYRIGHT + +Copyright 2025 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/doc/man7/OSSL_PROVIDER-FIPS.pod b/doc/man7/OSSL_PROVIDER-FIPS.pod index 8ebf0c5cbd7..b18140f5546 100644 --- a/doc/man7/OSSL_PROVIDER-FIPS.pod +++ b/doc/man7/OSSL_PROVIDER-FIPS.pod @@ -117,6 +117,8 @@ KECCAK-KMAC is only used internally as a sub algorithm of KMAC. =item PBKDF2, see L +=item SNMPKDF, see L + =item SSHKDF, see L =item TLS1-PRF, see L @@ -483,6 +485,8 @@ Key agreement tests used with the "KAT_KA" type. =item "PBKDF2" (B) +=item "SNMPKDF" (B) + =item "SSHKDF" (B) =item "TLS12_PRF" (B) diff --git a/doc/man7/OSSL_PROVIDER-default.pod b/doc/man7/OSSL_PROVIDER-default.pod index f54815bd1cc..f0743d99d75 100644 --- a/doc/man7/OSSL_PROVIDER-default.pod +++ b/doc/man7/OSSL_PROVIDER-default.pod @@ -139,6 +139,8 @@ The OpenSSL default provider supports these operations and algorithms: =item PKCS12KDF, see L +=item SNMPKDF, see L + =item SSHKDF, see L =item TLS1-PRF, see L diff --git a/include/openssl/core_names.h.in b/include/openssl/core_names.h.in index 779acf02c04..e0a4c5e6325 100644 --- a/include/openssl/core_names.h.in +++ b/include/openssl/core_names.h.in @@ -75,6 +75,7 @@ extern "C" { #define OSSL_KDF_NAME_PBKDF1 "PBKDF1" #define OSSL_KDF_NAME_PBKDF2 "PBKDF2" #define OSSL_KDF_NAME_SCRYPT "SCRYPT" +#define OSSL_KDF_NAME_SNMPKDF "SNMPKDF" #define OSSL_KDF_NAME_SSHKDF "SSHKDF" #define OSSL_KDF_NAME_SSKDF "SSKDF" #define OSSL_KDF_NAME_TLS1_PRF "TLS1-PRF" diff --git a/include/openssl/proverr.h b/include/openssl/proverr.h index a8799f6757d..fdfa8916a28 100644 --- a/include/openssl/proverr.h +++ b/include/openssl/proverr.h @@ -97,6 +97,7 @@ #define PROV_R_MISSING_CIPHER 155 #define PROV_R_MISSING_CONFIG_DATA 213 #define PROV_R_MISSING_CONSTANT 156 +#define PROV_R_MISSING_EID 255 #define PROV_R_MISSING_KEY 128 #define PROV_R_MISSING_MAC 150 #define PROV_R_MISSING_MESSAGE_DIGEST 129 diff --git a/include/openssl/self_test.h b/include/openssl/self_test.h index 6e671c1a985..8dab9b6f998 100644 --- a/include/openssl/self_test.h +++ b/include/openssl/self_test.h @@ -83,6 +83,7 @@ extern "C" { #define OSSL_SELF_TEST_DESC_KDF_X963KDF "X963KDF" #define OSSL_SELF_TEST_DESC_KDF_X942KDF "X942KDF" #define OSSL_SELF_TEST_DESC_KDF_PBKDF2 "PBKDF2" +#define OSSL_SELF_TEST_DESC_KDF_SNMPKDF "SNMPKDF" #define OSSL_SELF_TEST_DESC_KDF_SSHKDF "SSHKDF" #define OSSL_SELF_TEST_DESC_KDF_TLS12_PRF "TLS12_PRF" #define OSSL_SELF_TEST_DESC_KDF_KBKDF "KBKDF" diff --git a/providers/common/provider_err.c b/providers/common/provider_err.c index cf956168b32..6cdb7283331 100644 --- a/providers/common/provider_err.c +++ b/providers/common/provider_err.c @@ -142,6 +142,7 @@ static const ERR_STRING_DATA PROV_str_reasons[] = { { ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_CONFIG_DATA), "missing config data" }, { ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_CONSTANT), "missing constant" }, + { ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_EID), "missing eid" }, { ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_KEY), "missing key" }, { ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_MAC), "missing mac" }, { ERR_PACK(ERR_LIB_PROV, 0, PROV_R_MISSING_MESSAGE_DIGEST), diff --git a/providers/defltprov.c b/providers/defltprov.c index 9cc14ff7ec6..50397f21892 100644 --- a/providers/defltprov.c +++ b/providers/defltprov.c @@ -366,6 +366,7 @@ static const OSSL_ALGORITHM deflt_kdfs[] = { { PROV_NAMES_SSKDF, "provider=default", ossl_kdf_sskdf_functions }, { PROV_NAMES_PBKDF2, "provider=default", ossl_kdf_pbkdf2_functions }, { PROV_NAMES_PKCS12KDF, "provider=default", ossl_kdf_pkcs12_functions }, + { PROV_NAMES_SNMPKDF, "provider=default", ossl_kdf_snmpkdf_functions }, { PROV_NAMES_SSHKDF, "provider=default", ossl_kdf_sshkdf_functions }, { PROV_NAMES_X963KDF, "provider=default", ossl_kdf_x963_kdf_functions }, { PROV_NAMES_TLS1_PRF, "provider=default", ossl_kdf_tls1_prf_functions }, diff --git a/providers/fips/fipsprov.c b/providers/fips/fipsprov.c index d613740194e..8967a21e9a3 100644 --- a/providers/fips/fipsprov.c +++ b/providers/fips/fipsprov.c @@ -428,6 +428,7 @@ static const OSSL_ALGORITHM fips_macs_internal[] = { ossl_kdf_tls1_3_kdf_functions }, \ { PROV_NAMES_SSKDF, FIPS_DEFAULT_PROPERTIES, ossl_kdf_sskdf_functions }, \ { PROV_NAMES_PBKDF2, FIPS_DEFAULT_PROPERTIES, ossl_kdf_pbkdf2_functions }, \ + { PROV_NAMES_SNMPKDF, FIPS_DEFAULT_PROPERTIES, ossl_kdf_snmpkdf_functions }, \ { PROV_NAMES_SSHKDF, FIPS_DEFAULT_PROPERTIES, ossl_kdf_sshkdf_functions }, \ { PROV_NAMES_X963KDF, FIPS_DEFAULT_PROPERTIES, \ ossl_kdf_x963_kdf_functions }, \ diff --git a/providers/fips/self_test_data.inc b/providers/fips/self_test_data.inc index b4073243146..2442038eb17 100644 --- a/providers/fips/self_test_data.inc +++ b/providers/fips/self_test_data.inc @@ -512,6 +512,25 @@ static const ST_KAT_PARAM hkdf_params[] = { ST_KAT_PARAM_END() }; +static const char snmpkdf_digest[] = "SHA1"; +static const unsigned char snmpkdf_eid[] = { + 0x80, 0x00, 0x02, 0xb8, 0x05, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, + 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, + 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56 +}; +static const unsigned char snmpkdf_password[] = { 0x74, 0x63, 0x6f, 0x54, 0x49, 0x48, 0x6d, 0x77, + 0x63, 0x46, 0x6c, 0x50, 0x52, 0x65, 0x52, 0x4a }; +static const unsigned char snmpkdf_expected[] = { + 0x3c, 0xd1, 0x21, 0xcb, 0xf3, 0xe8, 0xbf, 0x9e, 0x6d, 0x80, 0xf5, 0xf0, + 0x53, 0x83, 0x5b, 0x3c, 0x24, 0x1c, 0xd2, 0x1e +}; +static const ST_KAT_PARAM snmpkdf_params[] = { + ST_KAT_PARAM_UTF8STRING(OSSL_KDF_PARAM_DIGEST, snmpkdf_digest), + ST_KAT_PARAM_OCTET(OSSL_KDF_PARAM_SNMPKDF_EID, snmpkdf_eid), + ST_KAT_PARAM_OCTET(OSSL_KDF_PARAM_PASSWORD, snmpkdf_password), + ST_KAT_PARAM_END() +}; + static const char sskdf_digest[] = "SHA256"; static const unsigned char sskdf_secret[] = { 0x6d, 0xbd, 0xc2, 0x3f, 0x04, 0x54, 0x88, 0xe4, @@ -824,6 +843,13 @@ static const ST_KAT_KDF st_kat_kdf_tests[] = hkdf_params, ITM(hkdf_expected) }, + { + OSSL_SELF_TEST_DESC_KDF_SNMPKDF, + OSSL_KDF_NAME_SNMPKDF, + 0, + snmpkdf_params, + ITM(snmpkdf_expected) + }, { OSSL_SELF_TEST_DESC_KDF_SSKDF, OSSL_KDF_NAME_SSKDF, diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h index 3ecf4581829..70fff1b7974 100644 --- a/providers/implementations/include/prov/implementations.h +++ b/providers/implementations/include/prov/implementations.h @@ -290,6 +290,7 @@ extern const OSSL_DISPATCH ossl_kdf_hkdf_sha256_functions[]; extern const OSSL_DISPATCH ossl_kdf_hkdf_sha384_functions[]; extern const OSSL_DISPATCH ossl_kdf_hkdf_sha512_functions[]; extern const OSSL_DISPATCH ossl_kdf_tls1_3_kdf_functions[]; +extern const OSSL_DISPATCH ossl_kdf_snmpkdf_functions[]; extern const OSSL_DISPATCH ossl_kdf_sshkdf_functions[]; extern const OSSL_DISPATCH ossl_kdf_sskdf_functions[]; extern const OSSL_DISPATCH ossl_kdf_x963_kdf_functions[]; diff --git a/providers/implementations/include/prov/names.h b/providers/implementations/include/prov/names.h index e5cab7f1bbf..bc98202879b 100644 --- a/providers/implementations/include/prov/names.h +++ b/providers/implementations/include/prov/names.h @@ -289,6 +289,7 @@ #define PROV_NAMES_PBKDF1 "PBKDF1" #define PROV_NAMES_PBKDF2 "PBKDF2:1.2.840.113549.1.5.12" #define PROV_NAMES_PVKKDF "PVKKDF" +#define PROV_NAMES_SNMPKDF "SNMPKDF" #define PROV_NAMES_SSHKDF "SSHKDF" #define PROV_NAMES_X963KDF "X963KDF:X942KDF-CONCAT" #define PROV_NAMES_X942KDF_ASN1 "X942KDF-ASN1:X942KDF" diff --git a/providers/implementations/kdfs/build.info b/providers/implementations/kdfs/build.info index 17f9e6542da..b41a730e574 100644 --- a/providers/implementations/kdfs/build.info +++ b/providers/implementations/kdfs/build.info @@ -11,6 +11,7 @@ $PVKKDF_GOAL=../../liblegacy.a $PKCS12KDF_GOAL=../../libdefault.a $SSKDF_GOAL=../../libdefault.a ../../libfips.a $SCRYPT_GOAL=../../libdefault.a +$SNMPKDF_GOAL=../../libdefault.a ../../libfips.a $SSHKDF_GOAL=../../libdefault.a ../../libfips.a $X942KDF_GOAL=../../libdefault.a ../../libfips.a $HMAC_DRBG_KDF_GOAL=../../libdefault.a ../../libfips.a @@ -35,6 +36,7 @@ SOURCE[$PKCS12KDF_GOAL]=pkcs12kdf.c SOURCE[$SSKDF_GOAL]=sskdf.c SOURCE[$SCRYPT_GOAL]=scrypt.c +SOURCE[$SNMPKDF_GOAL]=snmpkdf.c SOURCE[$SSHKDF_GOAL]=sshkdf.c SOURCE[$X942KDF_GOAL]=x942kdf.c DEPEND[x942kdf.o]=../../common/include/prov/der_wrap.h diff --git a/providers/implementations/kdfs/snmpkdf.c b/providers/implementations/kdfs/snmpkdf.c new file mode 100644 index 00000000000..9a55b90b335 --- /dev/null +++ b/providers/implementations/kdfs/snmpkdf.c @@ -0,0 +1,326 @@ +/* + * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal/cryptlib.h" +#include "internal/numbers.h" +#include "crypto/evp.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_util.h" +#include "providers/implementations/kdfs/snmpkdf.inc" + +#define KDF_SNMP_PASSWORD_HASH_AMOUNT (1024 * 1024) +#define KDF_SNMP_MIN_PASSWORD_LEN 8 + +/* See RFC 3414, Appendix A.2.2 */ +/* See NIST SP800-135 Section 6.8 */ +static OSSL_FUNC_kdf_newctx_fn kdf_snmpkdf_new; +static OSSL_FUNC_kdf_dupctx_fn kdf_snmpkdf_dup; +static OSSL_FUNC_kdf_freectx_fn kdf_snmpkdf_free; +static OSSL_FUNC_kdf_reset_fn kdf_snmpkdf_reset; +static OSSL_FUNC_kdf_derive_fn kdf_snmpkdf_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_snmpkdf_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn kdf_snmpkdf_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_snmpkdf_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kdf_snmpkdf_get_ctx_params; + +static int SNMPKDF(const EVP_MD *evp_md, + const unsigned char *eid, size_t eid_len, + unsigned char *password, size_t password_len, + unsigned char *key, size_t keylen); + +typedef struct { + /* Warning: Any changes to this structure may require you to update kdf_snmpkdf_dup */ + void *provctx; + PROV_DIGEST digest; + unsigned char *eid; + size_t eid_len; + unsigned char *password; + size_t password_len; +} KDF_SNMPKDF; + +static void *kdf_snmpkdf_new(void *provctx) +{ + KDF_SNMPKDF *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL) + ctx->provctx = provctx; + return ctx; +} + +static void *kdf_snmpkdf_dup(void *vctx) +{ + const KDF_SNMPKDF *src = (const KDF_SNMPKDF *)vctx; + KDF_SNMPKDF *dest; + + dest = kdf_snmpkdf_new(src->provctx); + if (dest != NULL) { + if (!ossl_prov_memdup(src->eid, src->eid_len, + &dest->eid, &dest->eid_len) + || !ossl_prov_memdup(src->password, src->password_len, + &dest->password, &dest->password_len) + || !ossl_prov_digest_copy(&dest->digest, &src->digest)) + goto err; + } + return dest; + + err: + kdf_snmpkdf_free(dest); + return NULL; +} + +static void kdf_snmpkdf_free(void *vctx) +{ + KDF_SNMPKDF *ctx = (KDF_SNMPKDF *)vctx; + + if (ctx != NULL) { + kdf_snmpkdf_reset(ctx); + OPENSSL_free(ctx); + } +} + +static void kdf_snmpkdf_reset(void *vctx) +{ + KDF_SNMPKDF *ctx = (KDF_SNMPKDF *)vctx; + void *provctx = ctx->provctx; + + ossl_prov_digest_reset(&ctx->digest); + OPENSSL_clear_free(ctx->eid, ctx->eid_len); + OPENSSL_clear_free(ctx->password, ctx->password_len); + memset(ctx, 0, sizeof(*ctx)); + ctx->provctx = provctx; +} + +static int snmpkdf_set_membuf(unsigned char **dst, size_t *dst_len, + const OSSL_PARAM *p) +{ + OPENSSL_clear_free(*dst, *dst_len); + *dst = NULL; + *dst_len = 0; + return OSSL_PARAM_get_octet_string(p, (void **)dst, 0, dst_len); +} + +static int kdf_snmpkdf_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_SNMPKDF *ctx = (KDF_SNMPKDF *)vctx; + const EVP_MD *md; + + if (!ossl_prov_is_running() || !kdf_snmpkdf_set_ctx_params(ctx, params)) + return 0; + + md = ossl_prov_digest_md(&ctx->digest); + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + if (ctx->eid == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_EID); + return 0; + } + if (ctx->password == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS); + return 0; + } + + return SNMPKDF(md, ctx->eid, ctx->eid_len, + ctx->password, ctx->password_len, + key, keylen); +} + +static int kdf_snmpkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + struct snmp_set_ctx_params_st p; + KDF_SNMPKDF *ctx = vctx; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); +#ifdef FIPS_MODULE + const EVP_MD *md = NULL; +#endif + + if (params == NULL) + return 1; + + if (ctx == NULL || !snmp_set_ctx_params_decoder(params, &p)) + return 0; + + if (p.digest != NULL) { + if (!ossl_prov_digest_load(&ctx->digest, p.digest, p.propq, libctx)) + return 0; +#ifdef FIPS_MODULE + md = ossl_prov_digest_md(&ctx->digest); + if (!EVP_MD_is_a(md, SN_sha1)) + return 0; +#endif + } + + if (p.pw != NULL) { + if (!snmpkdf_set_membuf(&ctx->password, &ctx->password_len, p.pw)) + return 0; + if ((ctx->password_len > KDF_SNMP_PASSWORD_HASH_AMOUNT) || + (ctx->password_len < KDF_SNMP_MIN_PASSWORD_LEN)) + return 0; + } + + if (p.eid != NULL && !snmpkdf_set_membuf(&ctx->eid, &ctx->eid_len, p.eid)) + return 0; + + return 1; +} + +static const OSSL_PARAM *kdf_snmpkdf_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *p_ctx) +{ + return snmp_set_ctx_params_list; +} + +static size_t kdf_snmpkdf_size(KDF_SNMPKDF *ctx) +{ + int len; + const EVP_MD *md = NULL; + + md = ossl_prov_digest_md(&ctx->digest); + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + len = EVP_MD_get_size(md); + return (len <= 0) ? 0 : (size_t)len; +} + +static int kdf_snmpkdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + struct snmp_get_ctx_params_st p; + KDF_SNMPKDF *ctx = vctx; + + if (ctx == NULL || !snmp_get_ctx_params_decoder(params, &p)) + return 0; + + if (p.size != NULL) { + size_t sz = kdf_snmpkdf_size(ctx); + + if (sz == 0) + return 0; + if (!OSSL_PARAM_set_size_t(p.size, sz)) + return 0; + } + return 1; +} + +static const OSSL_PARAM *kdf_snmpkdf_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *p_ctx) +{ + return snmp_get_ctx_params_list; +} + +const OSSL_DISPATCH ossl_kdf_snmpkdf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_snmpkdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_snmpkdf_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_snmpkdf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_snmpkdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_snmpkdf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kdf_snmpkdf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_snmpkdf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kdf_snmpkdf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_snmpkdf_get_ctx_params }, + { 0, NULL } +}; + +/* + * SNMPKDF - In compliance with SP800-135 and RFC7860, calculate + * a master key using the engine ID and password. + * + * Denote engineLength and passwordlen to be the lengths (in bytes) of an + * snmpEngineID and a password, respectively. + * + * Let N = (1024*1024)/passwordlen + * + * Expanded_password = the leftmost 1048576 bytes of the string of N + * repetitions of the password. + * + * Derived_password = SHA-1 (Expanded_password). The Derived_password + * is the output of hashing the Expanded_password by SHA-1. + * + * Let Shared_key to be the key that the user shares with the authoritative + * SNMP engine with ID snmpEngineID. The Shared_key is generated as follows: + * + * Shared_key = SHA-1(Derived_password || snmpEngineID || Derived_password). + * + * e_id - engine ID(eid) + * e_len - engineID length + * password - password + * password_len - password length + * okey - pointer to key output, FIPS testing limited to SHA-1. + * okeylen - key output length + * return - 1 pass 0 for error + */ +static int SNMPKDF(const EVP_MD *evp_md, + const unsigned char *e_id, size_t e_len, + unsigned char *password, size_t password_len, + unsigned char *okey, size_t okeylen) +{ + EVP_MD_CTX *md = NULL; + unsigned char digest[EVP_MAX_MD_SIZE]; + size_t mdsize = 0, len = 0; + unsigned int md_len = 0; + int ret = 0; + + /* Limited to SHA-1 and SHA-2 hashes presently */ + if (okey == NULL || okeylen == 0) + return 0; + + md = EVP_MD_CTX_new(); + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + goto err; + } + + mdsize = EVP_MD_get_size(evp_md); + if (mdsize <= 0 || mdsize < okeylen) + goto err; + + if (!EVP_DigestInit_ex(md, evp_md, NULL)) + goto err; + + for (len = 0; len < KDF_SNMP_PASSWORD_HASH_AMOUNT - password_len; len += password_len) { + if (!EVP_DigestUpdate(md, password, password_len)) + goto err; + } + + if (!EVP_DigestUpdate(md, password, KDF_SNMP_PASSWORD_HASH_AMOUNT - len) + || !EVP_DigestFinal_ex(md, digest, &md_len) + || !EVP_DigestInit_ex(md, evp_md, NULL) + || !EVP_DigestUpdate(md, digest, mdsize) + || !EVP_DigestUpdate(md, e_id, e_len) + || !EVP_DigestUpdate(md, digest, mdsize) + || !EVP_DigestFinal_ex(md, digest, &md_len)) + goto err; + + memcpy(okey, digest, okeylen); + + ret = 1; + +err: + EVP_MD_CTX_free(md); + OPENSSL_cleanse(digest, EVP_MAX_MD_SIZE); + return ret; +} diff --git a/providers/implementations/kdfs/snmpkdf.inc.in b/providers/implementations/kdfs/snmpkdf.inc.in new file mode 100644 index 00000000000..c14cd209fbb --- /dev/null +++ b/providers/implementations/kdfs/snmpkdf.inc.in @@ -0,0 +1,23 @@ +/* + * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the \"License\"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +{- +use OpenSSL::paramnames qw(produce_param_decoder); +-} + +{- produce_param_decoder('snmp_set_ctx_params', + (['OSSL_KDF_PARAM_PROPERTIES', 'propq', 'utf8_string'], + ['OSSL_KDF_PARAM_DIGEST', 'digest', 'utf8_string'], + ['OSSL_KDF_PARAM_SNMPKDF_EID', 'eid', 'octet_string'], + ['OSSL_KDF_PARAM_PASSWORD', 'pw', 'octet_string'], + )); -} + +{- produce_param_decoder('snmp_get_ctx_params', + (['OSSL_KDF_PARAM_SIZE', 'size', 'size_t'], + )); -} diff --git a/test/recipes/30-test_evp.t b/test/recipes/30-test_evp.t index 2ac6ab70d93..98af32086d8 100644 --- a/test/recipes/30-test_evp.t +++ b/test/recipes/30-test_evp.t @@ -56,6 +56,7 @@ my @files = qw( evpkdf_kbkdf_kmac.txt evpkdf_pbkdf1.txt evpkdf_pbkdf2.txt + evpkdf_snmp.txt evpkdf_ss.txt evpkdf_ssh.txt evpkdf_tls12_prf.txt diff --git a/test/recipes/30-test_evp_data/evpkdf_snmp.txt b/test/recipes/30-test_evp_data/evpkdf_snmp.txt new file mode 100644 index 00000000000..1c062eb55ea --- /dev/null +++ b/test/recipes/30-test_evp_data/evpkdf_snmp.txt @@ -0,0 +1,107 @@ +# +# Copyright 2025 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +# Tests start with one of these keywords +# Cipher Decrypt Derive Digest Encoding KDF MAC PBE +# PrivPubKeyPair Sign Verify VerifyRecover +# and continue until a blank line. Lines starting with a pound sign are ignored. + +Title = SNMPKDF tests (from NIST ACVP 1.0 test vectors) + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:SHA1 +Ctrl.eid = hexeid:800002b805123456789abcdef0123456789abcdef0123456789abcdef0123456 +Ctrl.pass = pass:IFUcNbMl +Output = 6ab11fc4cada4ce96f4cc5cf8f37acfb18c1d992 + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:SHA1 +Ctrl.eid = hexeid:800002b805123456789abcdef0123456789abcdef0123456789abcdef0123456 +Ctrl.pass = pass:bAuDCHTs +Output = 907a086a6c038749fef2cbdadc1108bba649c02f + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:SHA1 +Ctrl.eid = hexeid:800002b805123456789abcdef0123456789abcdef0123456789abcdef0123456 +Ctrl.pass = pass:jXCaBOBTSnQkYseOecvUCyAiMOMjNuZx +Output = 8db38c0266e1cf1edd724e1c7fe9de73dca8acb6 + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:SHA1 +Ctrl.eid = hexeid:800002b805123456789abcdef0123456789abcdef0123456789abcdef0123456 +Ctrl.pass = pass:WgXMvXGNpSoaeEmIqBCbSzsfygkxzdMK +Output = cf50fac5db4f84281ce4a0569ded11beeefc2596 + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:SHA1 +Ctrl.eid = hexeid:000002b87766554433221100 +Ctrl.pass = pass:nozniMRj +Output = 90e6cd5df0618e117df1fde6f01e22e010489311 + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:SHA1 +Ctrl.eid = hexeid:000002b87766554433221100 +Ctrl.pass = pass:WmSlorrf +Output = a13096b909ae1c6034337d203eca7ab53d3da47e + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:SHA1 +Ctrl.eid = hexeid:000002B87766554433221100 +Ctrl.pass = pass:DnlIshNwRflhNzGYiNUOHSktxjntPiOB +Output = e1c4636f8fe00a90012489312a0f778ab001f3ff + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:SHA1 +Ctrl.eid = hexeid:000002B87766554433221100 +Ctrl.pass = pass:gdRkZksOndMLQCLmDdrWCMFBZQrerfTX +Output = BB1640B442255E9C1C6357F64230506B011DCD0D + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.eid = hexeid:000002B87766554433221100 +Ctrl.pass = pass:gdRkZksOndMLQCLmDdrWCMFBZQrerfTX +Output = BB1640B442255E9C1C6357F64230506B011DCD0D +Result = KDF_DERIVE_ERROR + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:SHA1 +Ctrl.pass = pass:gdRkZksOndMLQCLmDdrWCMFBZQrerfTX +Output = BB1640B442255E9C1C6357F64230506B011DCD0D +Result = KDF_DERIVE_ERROR + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:SHA1 +Ctrl.eid = hexeid:000002B87766554433221100 +Output = BB1640B442255E9C1C6357F64230506B011DCD0D +Result = KDF_DERIVE_ERROR + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:FAIL +Ctrl.eid = hexeid:000002B87766554433221100 +Ctrl.pass = pass:gdRkZksOndMLQCLmDdrWCMFBZQrerfTX +Output = BB1640B442255E9C1C6357F64230506B011DCD0D +Result = KDF_CTRL_ERROR + +FIPSversion = >=4.0.0 +KDF = SNMPKDF +Ctrl.digest = digest:SHA1 +Ctrl.eid = hexeid:800002b805123456789abcdef0123456789abcdef0123456789abcdef0123456 +Ctrl.pass = pass:IFUcNbM +Output = 6ab11fc4cada4ce96f4cc5cf8f37acfb18c1d992 +Result = KDF_CTRL_ERROR diff --git a/util/perl/OpenSSL/paramnames.pm b/util/perl/OpenSSL/paramnames.pm index 744edb10e36..24c339c9449 100644 --- a/util/perl/OpenSSL/paramnames.pm +++ b/util/perl/OpenSSL/paramnames.pm @@ -222,6 +222,7 @@ my %params = ( 'OSSL_KDF_PARAM_SCRYPT_MAXMEM' => "maxmem_bytes", # uint64_t 'OSSL_KDF_PARAM_INFO' => "info", # octet string 'OSSL_KDF_PARAM_SEED' => "seed", # octet string + 'OSSL_KDF_PARAM_SNMPKDF_EID' => "eid", # octet string 'OSSL_KDF_PARAM_SSHKDF_XCGHASH' => "xcghash", # octet string 'OSSL_KDF_PARAM_SSHKDF_SESSION_ID' => "session_id", # octet string 'OSSL_KDF_PARAM_SSHKDF_TYPE' => "type", # int