Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions include/wolfprovider/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,26 @@ int wp_read_pem_bio(WOLFPROV_CTX *provctx, OSSL_CORE_BIO *coreBio,
unsigned char** data, word32* len);
BIO* wp_corebio_get_bio(WOLFPROV_CTX* provCtx, OSSL_CORE_BIO *coreBio);

#ifdef HAVE_FIPS
/**
* Decide whether a decoder should skip key-object instantiation (FIPS only).
*
* Cold-CAST optimization: if the wrapped key's (SPKI/PKCS#8) AlgorithmIdentifier
* OID is recognized but not in allowedNids, return 1 so the caller bails before
* instantiating a key (which would fire the lazy CAST).
*
* @param [in] castType FIPS CAST id for this decoder's algorithm.
* @param [in] der DER-encoded key bytes.
* @param [in] len Length of der in bytes.
* @param [in] format Encoding format (WP_ENC_FORMAT_*).
* @param [in] allowedNids NIDs owned by this decoder.
* @param [in] nAllowed Number of entries in allowedNids.
* @return 1 to skip instantiation, 0 to proceed.
*/
int wp_decode_should_skip(int castType, const unsigned char* der, word32 len,
int format, const int* allowedNids, size_t nAllowed);
#endif /* HAVE_FIPS */

byte wp_ct_byte_mask_eq(byte a, byte b);
byte wp_ct_byte_mask_ne(byte a, byte b);
byte wp_ct_int_mask_gte(int a, int b);
Expand Down
24 changes: 21 additions & 3 deletions src/wp_dh_kmgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@

#ifdef WP_HAVE_DH

#ifdef HAVE_FIPS
#include <wolfssl/wolfcrypt/fips_test.h>

static const int wp_dh_decode_nids[] = {
NID_dhKeyAgreement,
NID_dhpublicnumber
};
#define WP_DH_DECODE_NIDS_CNT \
(sizeof(wp_dh_decode_nids) / sizeof(*wp_dh_decode_nids))
#endif /* HAVE_FIPS */

/** Supported selections (key parts) in this key manager for DH. */
#define WP_DH_POSSIBLE_SELECTIONS \
(OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS)
Expand Down Expand Up @@ -2307,15 +2318,22 @@ static int wp_dh_decode(wp_DhEncDecCtx* ctx, OSSL_CORE_BIO *cBio,

ctx->selection = selection;

if (ok && (!wp_read_der_bio(ctx->provCtx, cBio, &data, &len))) {
ok = 0;
}
#ifdef HAVE_FIPS
if (ok && wp_decode_should_skip(FIPS_CAST_DH_PRIMITIVE_Z, data, len,
ctx->format, wp_dh_decode_nids, WP_DH_DECODE_NIDS_CNT)) {
decoded = 0;
ok = 0;
}
#endif
if (ok) {
dh = wp_dh_new(ctx->provCtx);
if (dh == NULL) {
ok = 0;
}
}
if (ok && (!wp_read_der_bio(ctx->provCtx, cBio, &data, &len))) {
ok = 0;
}
if (ok && (ctx->format == WP_ENC_FORMAT_TYPE_SPECIFIC)) {
if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0){
if (!wp_dh_decode_pki(dh, data, len)) {
Expand Down
25 changes: 22 additions & 3 deletions src/wp_ecc_kmgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@

#ifdef WP_HAVE_ECC

#ifdef HAVE_FIPS
#include <wolfssl/wolfcrypt/fips_test.h>

static const int wp_ecc_decode_nids[] = {
NID_X9_62_id_ecPublicKey
};
#define WP_ECC_DECODE_NIDS_CNT \
(sizeof(wp_ecc_decode_nids) / sizeof(*wp_ecc_decode_nids))
#endif /* HAVE_FIPS */

/* Note: Explicit parameters are not supported. A predefined curve MUST be used.
*/

Expand Down Expand Up @@ -2307,13 +2317,22 @@ static int wp_ecc_decode(wp_EccEncDecCtx* ctx, OSSL_CORE_BIO *cBio,

ctx->selection = selection;

ecc = wp_ecc_new(ctx->provCtx);
if (ecc == NULL) {
if (ok && (!wp_read_der_bio(ctx->provCtx, cBio, &data, &len))) {
ok = 0;
}
if (ok && (!wp_read_der_bio(ctx->provCtx, cBio, &data, &len))) {
#ifdef HAVE_FIPS
if (ok && wp_decode_should_skip(FIPS_CAST_ECC_PRIMITIVE_Z, data, len,
ctx->format, wp_ecc_decode_nids, WP_ECC_DECODE_NIDS_CNT)) {
decoded = 0;
ok = 0;
}
#endif
if (ok) {
ecc = wp_ecc_new(ctx->provCtx);
if (ecc == NULL) {
ok = 0;
}
}
if (ok && ((ctx->format == WP_ENC_FORMAT_TYPE_SPECIFIC) ||
(ctx->format == WP_ENC_FORMAT_X9_62))) {
if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
Expand Down
124 changes: 124 additions & 0 deletions src/wp_internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@


#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/objects.h>
#include <openssl/err.h>

#include <wolfprovider/settings.h>
#include <wolfprovider/internal.h>
Expand All @@ -29,6 +32,9 @@

#include <wolfssl/wolfcrypt/rsa.h>
#include <wolfssl/wolfcrypt/pwdbased.h>
#ifdef HAVE_FIPS
#include <wolfssl/wolfcrypt/fips_test.h>
#endif

#ifndef WP_SINGLE_THREADED

Expand Down Expand Up @@ -197,6 +203,124 @@ int wp_init_cast(int algo)
#endif /* HAVE_FIPS */
#endif /* !WP_SINGLE_THREADED */

#ifdef HAVE_FIPS
/**
* Extract the AlgorithmIdentifier OID NID from a SubjectPublicKeyInfo DER.
*
* Not d2i_X509_PUBKEY: its custom d2i hook drives OSSL_DECODER, which re-enters
* this provider's decoders and recurses. ASN1_get_object + d2i_X509_ALGOR are
* plain templates and re-entry-safe.
*
* @param [in] der DER bytes.
* @param [in] len Length of der.
* @return Algorithm NID, or NID_undef if not a fully-consumed SPKI.
*/
static int wp_spki_alg_nid(const unsigned char* der, word32 len)
{
int nid = NID_undef;
const unsigned char* p = der;
long plen;
int tag, xclass, hdr;

ERR_set_mark();
hdr = ASN1_get_object(&p, &plen, &tag, &xclass, (long)len);
if (!(hdr & 0x80) && (tag == V_ASN1_SEQUENCE) &&
(xclass == V_ASN1_UNIVERSAL) &&
((word32)(p - der) + (word32)plen == len)) {
const unsigned char* seqEnd = p + plen;
X509_ALGOR* alg = d2i_X509_ALGOR(NULL, &p, plen);
if (alg != NULL) {
const unsigned char* bsp = p;
long bslen;
int bstag, bsclass, bshdr;

/* Require the trailing BIT STRING so a foreign SEQUENCE is not
* mistaken for an SPKI. */
bshdr = ASN1_get_object(&bsp, &bslen, &bstag, &bsclass,
(long)(seqEnd - p));
if ((alg->algorithm != NULL) && !(bshdr & 0x80) &&
(bstag == V_ASN1_BIT_STRING) &&
(bsclass == V_ASN1_UNIVERSAL) &&
(bsp + bslen == seqEnd)) {
nid = OBJ_obj2nid(alg->algorithm);
}
X509_ALGOR_free(alg);
}
}
ERR_pop_to_mark();

return nid;
}

/**
* Extract the AlgorithmIdentifier OID NID from a PKCS#8 PrivateKeyInfo DER.
*
* @param [in] der DER bytes.
* @param [in] len Length of der.
* @return Algorithm NID, or NID_undef if not a fully-consumed PKCS#8.
*/
static int wp_pki_alg_nid(const unsigned char* der, word32 len)
{
int nid = NID_undef;
const unsigned char* p = der;
PKCS8_PRIV_KEY_INFO* p8;

ERR_set_mark();
p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, &p, (long)len);
if (p8 != NULL) {
if (p == der + len) {
const ASN1_OBJECT* alg = NULL;
if ((PKCS8_pkey_get0(&alg, NULL, NULL, NULL, p8) == 1) &&
(alg != NULL)) {
nid = OBJ_obj2nid(alg);
}
}
PKCS8_PRIV_KEY_INFO_free(p8);
}
ERR_pop_to_mark();

return nid;
}

/**
* Decide whether a decoder should skip key-object instantiation.
*
* See declaration in internal.h for the full contract.
*/
int wp_decode_should_skip(int castType, const unsigned char* der, word32 len,
int format, const int* allowedNids, size_t nAllowed)
{
int nid;
size_t i;

if (wc_GetCastStatus_fips(castType) == FIPS_CAST_STATE_SUCCESS) {
return 0;
}
if ((format != WP_ENC_FORMAT_SPKI) && (format != WP_ENC_FORMAT_PKI)) {
return 0;
}

if (format == WP_ENC_FORMAT_SPKI) {
nid = wp_spki_alg_nid(der, len);
}
else {
nid = wp_pki_alg_nid(der, len);
}

/* Can't prove it foreign -> proceed; protects raw/unwrapped key material. */
if (nid == NID_undef) {
return 0;
}
for (i = 0; i < nAllowed; i++) {
if (nid == allowedNids[i]) {
return 0;
}
}

return 1;
}
#endif /* HAVE_FIPS */

/**
* Get the wolfSSL random number generator from the provider context.
*
Expand Down
27 changes: 24 additions & 3 deletions src/wp_rsa_kmgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@

#ifdef WP_HAVE_RSA

#ifdef HAVE_FIPS
#include <openssl/obj_mac.h>
#include <wolfssl/wolfcrypt/fips_test.h>

static const int wp_rsa_decode_nids[] = {
NID_rsaEncryption,
NID_rsassaPss
};
#define WP_RSA_DECODE_NIDS_CNT \
(sizeof(wp_rsa_decode_nids) / sizeof(*wp_rsa_decode_nids))
#endif /* HAVE_FIPS */

/* In 5.8.2 RSA_MIN_SIZE was changed from 1024 to 2048. We still need to
* allow 1024 in some cases, and have extended logic in place for it already.
* For FIPS 1024 bit keys, use existing checks and let wolfssl throw us back */
Expand Down Expand Up @@ -2577,12 +2589,21 @@ static int wp_rsa_decode(wp_RsaEncDecCtx* ctx, OSSL_CORE_BIO* cBio,

ctx->selection = selection;

rsa = wp_rsa_base_new(ctx->provCtx, ctx->type);
if (rsa == NULL) {
if (ok) {
ok = wp_read_der_bio(ctx->provCtx, cBio, &data, &len);
}
#ifdef HAVE_FIPS
if (ok && wp_decode_should_skip(FIPS_CAST_RSA_SIGN_PKCS1v15, data, len,
ctx->format, wp_rsa_decode_nids, WP_RSA_DECODE_NIDS_CNT)) {
decoded = 0;
ok = 0;
}
#endif
if (ok) {
ok = wp_read_der_bio(ctx->provCtx, cBio, &data, &len);
rsa = wp_rsa_base_new(ctx->provCtx, ctx->type);
if (rsa == NULL) {
ok = 0;
}
}
if (ok && (ctx->format == WP_ENC_FORMAT_SPKI)) {
if (!wp_rsa_decode_spki(rsa, data, len)) {
Expand Down
15 changes: 15 additions & 0 deletions test/standalone/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
noinst_HEADERS += test/standalone/test_common.h \
test/standalone/tests/fips_baseline/test_fips_baseline.h

# Process-isolated decoder OID-precheck regression test. Unlike the other
# standalone tests, this one IS part of 'make check' (added to check_PROGRAMS
# below) because its safety invariant must be guarded automatically. It runs in
# its own fresh process so the DH/ECC primitive-Z FIPS CASTs are cold (the
# shared unit.test warms them); see the file header for details.

# Standalone test programs
# Each test compiles to its own binary for isolated execution
# Note: These are NOT in check_PROGRAMS because they must be run through scripts, not directly
Expand Down Expand Up @@ -41,6 +47,15 @@ test_fips_baseline_test_SOURCES = test/standalone/tests/fips_baseline/test_fips_
test/standalone/tests/fips_baseline/test_fips_baseline_pbkdf2.c
test_fips_baseline_test_LDADD = $(STANDALONE_COMMON_LDADD)

# Decoder OID-precheck regression test - part of 'make check'
check_PROGRAMS += test/decode_oid_precheck.test
noinst_PROGRAMS += test/decode_oid_precheck.test
DISTCLEANFILES += test/.libs/decode_oid_precheck.test
test_decode_oid_precheck_test_CPPFLAGS = $(STANDALONE_COMMON_CPPFLAGS)
test_decode_oid_precheck_test_SOURCES = \
test/standalone/tests/decode_oid_precheck/test_decode_oid_precheck.c
test_decode_oid_precheck_test_LDADD = $(STANDALONE_COMMON_LDADD)

# Common test utilities are built automatically by automake

# Standalone tests are available for manual execution but not part of make check
Expand Down
Loading
Loading