Add SNMPKDF implementation

In compliance with SP800-135 and RFC7860

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/29195)
This commit is contained in:
Helen Zhang 2025-11-21 19:11:26 +00:00 committed by Pauli
parent 9fb44b527e
commit 1b035166bd
23 changed files with 633 additions and 1 deletions

View file

@ -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
-----------

View file

@ -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]=\

View file

@ -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

View file

@ -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 \

View file

@ -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<EVP_KDF-SCRYPT(7)>,
L<EVP_KDF-TLS1_PRF(7)>,
L<EVP_KDF-PBKDF2(7)>,
L<EVP_KDF-HKDF(7)>,
L<EVP_KDF-SNMPKDF(7)>,
L<EVP_KDF-SS(7)>,
L<EVP_KDF-SSHKDF(7)>,
L<EVP_KDF-X942-ASN1(7)>,

View file

@ -0,0 +1,110 @@
=pod
=head1 NAME
EVP_KDF-SNMPKDF - The SNMPKDF EVP_KDF implementation
=head1 DESCRIPTION
Support for computing the B<SNMPKDF> KDF through the B<EVP_KDF> 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<OSSL_KDF_PARAM_PROPERTIES>) <UTF8 string>
=item "digest" (B<OSSL_KDF_PARAM_DIGEST>) <UTF8 string>
=item "pass" (B<OSSL_KDF_PARAM_PASS>) <octet string>
These parameters works as described in L<EVP_KDF(3)/PARAMETERS>.
=item "eid" (B<OSSL_KDF_PARAM_SNMPKDF_EID>) <octet string>
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<digest>.
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<EVP_KDF_derive(3)> 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<EVP_KDF(3)>,
L<EVP_KDF_CTX_new(3)>,
L<EVP_KDF_CTX_dup(3)>,
L<EVP_KDF_CTX_free(3)>,
L<EVP_KDF_CTX_set_params(3)>,
L<EVP_KDF_CTX_get_kdf_size(3)>,
L<EVP_KDF_derive(3)>,
L<EVP_KDF(3)/PARAMETERS>
=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<https://www.openssl.org/source/license.html>.
=cut

View file

@ -117,6 +117,8 @@ KECCAK-KMAC is only used internally as a sub algorithm of KMAC.
=item PBKDF2, see L<EVP_KDF-PBKDF2(7)>
=item SNMPKDF, see L<EVP_KDF-SNMPKDF(7)>
=item SSHKDF, see L<EVP_KDF-SSHKDF(7)>
=item TLS1-PRF, see L<EVP_KDF-TLS1_PRF(7)>
@ -483,6 +485,8 @@ Key agreement tests used with the "KAT_KA" type.
=item "PBKDF2" (B<OSSL_SELF_TEST_DESC_KDF_PBKDF2>)
=item "SNMPKDF" (B<OSSL_SELF_TEST_DESC_KDF_SNMPKDF>)
=item "SSHKDF" (B<OSSL_SELF_TEST_DESC_KDF_SSHKDF>)
=item "TLS12_PRF" (B<OSSL_SELF_TEST_DESC_KDF_TLS12_PRF>)

View file

@ -139,6 +139,8 @@ The OpenSSL default provider supports these operations and algorithms:
=item PKCS12KDF, see L<EVP_KDF-PKCS12KDF(7)>
=item SNMPKDF, see L<EVP_KDF-SNMPKDF(7)>
=item SSHKDF, see L<EVP_KDF-SSHKDF(7)>
=item TLS1-PRF, see L<EVP_KDF-TLS1_PRF(7)>

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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),

View file

@ -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 },

View file

@ -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 }, \

View file

@ -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,

View file

@ -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[];

View file

@ -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"

View file

@ -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

View file

@ -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 <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/sha.h>
#include <openssl/core_names.h>
#include <openssl/proverr.h>
#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;
}

View file

@ -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'],
)); -}

View file

@ -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

View file

@ -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

View file

@ -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