Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ jobs:
uses: ./.github/workflows/build-workflow.yml
with:
config: --disable-concatenate
no_derive_length_guard:
uses: ./.github/workflows/build-workflow.yml
with:
config: --disable-derive-length-guard
no_derive_secret_key:
uses: ./.github/workflows/build-workflow.yml
with:
Expand Down
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,16 @@ if(WOLFPKCS11_CONCATENATE)
list(APPEND WOLFPKCS11_DEFINITIONS "-DWOLFPKCS11_CONCATENATE")
endif()

# Derive length guard
add_option("WOLFPKCS11_DERIVE_LENGTH_GUARD"
"Refuse protected derived keys shorter than 16 bytes (default: enabled)"
"yes" "yes;no")

if(NOT WOLFPKCS11_DERIVE_LENGTH_GUARD)
set(WOLFPKCS11_NO_DERIVE_LENGTH_GUARD 1)
list(APPEND WOLFPKCS11_DEFINITIONS "-DWOLFPKCS11_NO_DERIVE_LENGTH_GUARD")
endif()


# PBKDF2
add_option("WOLFPKCS11_PBKDF2"
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ this macro does not change which objects can be copied; objects created with
with `CKA_COPYABLE=CK_FALSE` are still rejected by `C_CopyObject` with
`CKR_ACTION_PROHIBITED`.

#### Define WOLFPKCS11_NO_DERIVE_LENGTH_GUARD

Allows `C_DeriveKey` to create sensitive or non-extractable keys shorter than
16 bytes from protected keys, and `CKM_CONCATENATE_*` results that end inside
a protected key, as PKCS#11 permits. Such keys let a caller recover a secret
one byte at a time, so define this only where strict specification
compliance is required. `--disable-derive-length-guard` and the CMake option
`WOLFPKCS11_DERIVE_LENGTH_GUARD=no` define it.

### Behavior changes for PKCS#11 spec compliance

Several pre-existing gaps were closed; applications upgrading from earlier
Expand All @@ -181,6 +190,23 @@ versions may need to update templates or error-handling:
`CKM_TLS12_KEY_AND_MAC_DERIVE` this is new, as its keys used the defaults
before. NSS builds and builds with `WOLFPKCS11_LEGACY_DERIVE_NO_INHERIT`
are unchanged.
- `C_DeriveKey` returns `CKR_ATTRIBUTE_VALUE_INVALID` and creates no key when
a key derived from a sensitive or non-extractable key would be shorter than
16 bytes. A `CKM_TLS12_KEY_AND_MAC_DERIVE` MAC or key size that short
returns `CKR_MECHANISM_PARAM_INVALID`. The `CKM_CONCATENATE_*` mechanisms
refuse a `CKA_VALUE_LEN` that ends inside a sensitive or non-extractable
input key. PKCS#11 allows both, but a caller could then recover the secret
a byte at a time, for example from `CKA_CHECK_VALUE`. Only the
concatenation check applies to NSS builds and builds with
`WOLFPKCS11_LEGACY_DERIVE_NO_INHERIT`, where other derived keys can be made
readable anyway. 16 bytes of a protected derived secret stay unknown, but
with `CKM_CONCATENATE_*` enabled a caller can still find its other bytes
by comparing a longer derived key with a shorter one plus a guessed byte.
Build with `--disable-concatenate` where that matters. See
`WOLFPKCS11_NO_DERIVE_LENGTH_GUARD` to restore the specification behavior.
- `C_DeriveKey` returns `CKR_TEMPLATE_INCONSISTENT` for a template with
`CKA_VALUE`, which PKCS#11 does not allow for a derived key. Its length was
used to truncate the derived value.
- `C_CopyObject` enforces `CKA_COPYABLE` (returns `CKR_ACTION_PROHIBITED`
on `CK_FALSE`).
- `C_DestroyObject` enforces `CKA_DESTROYABLE` (returns
Expand Down Expand Up @@ -362,6 +388,7 @@ cmake -DCMAKE_PREFIX_PATH=/path/to/wolfssl/install ..
| `WOLFPKCS11_HMAC` | `yes` | HMAC support |
| `WOLFPKCS11_HKDF` | `yes` | HKDF support |
| `WOLFPKCS11_CONCATENATE` | `yes` | `CKM_CONCATENATE_*` key derivation |
| `WOLFPKCS11_DERIVE_LENGTH_GUARD` | `yes` | Refuse protected derived keys shorter than 16 bytes |
| `WOLFPKCS11_PBKDF2` | `no` | PBKDF2 for PIN hashing |
| `PBKDF2_ITERATIONS` | `600000` | PBKDF2 iteration count (used when `WOLFPKCS11_PBKDF2=yes`) |
| `WOLFPKCS11_MD5` | `yes` | MD5 support |
Expand Down
2 changes: 2 additions & 0 deletions cmake/options.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ extern "C" {
#cmakedefine WOLFPKCS11_HKDF
#undef WOLFPKCS11_CONCATENATE
#cmakedefine WOLFPKCS11_CONCATENATE
#undef WOLFPKCS11_NO_DERIVE_LENGTH_GUARD
#cmakedefine WOLFPKCS11_NO_DERIVE_LENGTH_GUARD
#undef WOLFPKCS11_PBKDF2
#cmakedefine WOLFPKCS11_PBKDF2
#undef PBKDF2_ITERATIONS
Expand Down
11 changes: 11 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,16 @@ then
AM_CFLAGS="$AM_CFLAGS -DWOLFPKCS11_CONCATENATE"
fi

AC_ARG_ENABLE([derive-length-guard],
[AS_HELP_STRING([--enable-derive-length-guard],[Refuse protected derived keys shorter than 16 bytes (default: enabled)])],
[ ENABLED_DERIVE_LENGTH_GUARD=$enableval ],
[ ENABLED_DERIVE_LENGTH_GUARD=yes ]
)
if test "$ENABLED_DERIVE_LENGTH_GUARD" = "no"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFPKCS11_NO_DERIVE_LENGTH_GUARD"
fi

AC_ARG_ENABLE([pbkdf2],
[AS_HELP_STRING([--enable-pbkdf2],[Enable internal PBKDF2 for PIN hashing (default: disabled)])],
[ ENABLED_PBKDF2=$enableval ],
Expand Down Expand Up @@ -874,6 +884,7 @@ echo " * DH: $ENABLED_DH"
echo " * ECC: $ENABLED_ECC"
echo " * HKDF: $ENABLED_HKDF"
echo " * Concatenate derive: $ENABLED_CONCATENATE"
echo " * Derive length guard: $ENABLED_DERIVE_LENGTH_GUARD"
echo " * ML-DSA: $ENABLED_MLDSA"
echo " * ML-KEM: $ENABLED_MLKEM"
echo " * LMS/HSS verify: $ENABLED_LMS"
Expand Down
44 changes: 44 additions & 0 deletions src/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@
#define WP11_DERIVE_SECRET_KEY
#endif

/* Shortest protected key a derive may create. */
#define WP11_DERIVE_MIN_KEY_SZ 16

/* Check that a CK_ULONG value fits in word32 with room for overhead such as
* authentication tags, key wrap blocks, or padding. On LP64 platforms CK_ULONG
* is 64-bit but wolfCrypt functions use word32/int for lengths. */
Expand Down Expand Up @@ -10723,6 +10726,15 @@ static int SymmKeyLen(WP11_Object* obj, word32 len, CK_ULONG* symmKeyLen)

return ret;
}

#ifndef WOLFPKCS11_NO_DERIVE_LENGTH_GUARD
static int DerivedKeyTooShort(CK_BBOOL baseSensitive, CK_BBOOL baseExtractable,
CK_ULONG len)
{
return (baseSensitive == CK_TRUE || baseExtractable == CK_FALSE) &&
len > 0 && len < WP11_DERIVE_MIN_KEY_SZ;
}
#endif
#endif

#ifdef WOLFSSL_HAVE_PRF
Expand Down Expand Up @@ -10752,6 +10764,14 @@ static int SetKeyExtract(WP11_Session* session, byte* ptr, CK_ULONG length,
}

ret = SymmKeyLen(secret, (word32)length, &symmKeyLen);
#ifndef WOLFPKCS11_NO_DERIVE_LENGTH_GUARD
if (ret == 0 && DerivedKeyTooShort(baseSensitive, baseExtractable,
symmKeyLen)) {
WP11_Object_Free(secret);
return (length < WP11_DERIVE_MIN_KEY_SZ) ? CKR_MECHANISM_PARAM_INVALID :
CKR_ATTRIBUTE_VALUE_INVALID;
}
#endif
if (ret == 0) {
/* Only use the bottom part of the secret for the key. */
secretKeyData[0] = (unsigned char*)&symmKeyLen;
Expand Down Expand Up @@ -10891,6 +10911,10 @@ static CK_RV ConcatKeyRv(WP11_Session* session, int ret)
return CKR_FUNCTION_FAILED;
}

#endif

#if defined(WOLFPKCS11_CONCATENATE) && \
!defined(WOLFPKCS11_NO_DERIVE_LENGTH_GUARD)
/* A result that ends inside a protected key lets each of its bytes be found
* by brute force, so it must hold all or none of such a key. */
static int ConcatCutsProtectedKey(WP11_Object* key, CK_ULONG off,
Expand Down Expand Up @@ -10955,6 +10979,7 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession,
CK_RV rv = CKR_OK;
WP11_Session* session;
WP11_Object* obj = NULL;
CK_ATTRIBUTE* valueAttr = NULL;
#ifdef WP11_DERIVE_SECRET_KEY
WP11_Object* otherKey = NULL;
byte* derivedKey = NULL;
Expand All @@ -10972,6 +10997,9 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession,
int inherit = 1;
#endif
int dataOnly = 0;
#ifndef WOLFPKCS11_NO_DERIVE_LENGTH_GUARD
int concat = 0;
#endif
CK_BBOOL wrapTrusted = CK_FALSE;
CK_BBOOL baseAlwaysSensitive = CK_FALSE;
CK_BBOOL baseNeverExtractable = CK_FALSE;
Expand Down Expand Up @@ -11063,6 +11091,11 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession,
CKO_SECRET_KEY);
if (rv == CKR_OK)
rv = CheckSecretKeyTemplate(pTemplate, ulAttributeCount);
if (rv == CKR_OK) {
FindAttributeType(pTemplate, ulAttributeCount, CKA_VALUE, &valueAttr);
if (valueAttr != NULL)
rv = CKR_TEMPLATE_INCONSISTENT;
}
if (rv != CKR_OK)
return rv;

Expand Down Expand Up @@ -11517,12 +11550,14 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession,
XMEMCPY(&reqLen, lenAttr->pValue, sizeof(CK_ULONG));
if (reqLen == 0 || reqLen > totalLen)
return CKR_ATTRIBUTE_VALUE_INVALID;
#ifndef WOLFPKCS11_NO_DERIVE_LENGTH_GUARD
if (ConcatCutsProtectedKey(obj, (pMechanism->mechanism ==
CKM_CONCATENATE_DATA_AND_BASE) ? addLen : 0, baseLen,
reqLen) || (otherKey != NULL &&
ConcatCutsProtectedKey(otherKey, baseLen, addLen,
reqLen)))
return CKR_ATTRIBUTE_VALUE_INVALID;
#endif
}
else {
FindAttributeType(pTemplate, ulAttributeCount, CKA_KEY_TYPE,
Expand All @@ -11534,6 +11569,9 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession,
if (reqLen == 0 || reqLen > WP11_MAX_SYM_KEY_SZ)
return CKR_KEY_SIZE_RANGE;
inherit = 1;
#ifndef WOLFPKCS11_NO_DERIVE_LENGTH_GUARD
concat = 1;
#endif
dataOnly = pMechanism->mechanism == CKM_CONCATENATE_DATA_AND_BASE &&
reqLen <= addLen;
bLen = sizeof(CK_BBOOL);
Expand Down Expand Up @@ -11615,6 +11653,12 @@ static CK_RV wp11_C_DeriveKey(CK_SESSION_HANDLE hSession,
}
if (reqValueLen > keyLen)
rv = CKR_ATTRIBUTE_VALUE_INVALID;
#ifndef WOLFPKCS11_NO_DERIVE_LENGTH_GUARD
if (rv == CKR_OK && !concat && WP11_Object_GetClass(obj) != CKO_DATA &&
DerivedKeyTooShort(baseSensitive, baseExtractable,
(reqValueLen > 0) ? reqValueLen : keyLen))
rv = CKR_ATTRIBUTE_VALUE_INVALID;
#endif
#ifndef WOLFPKCS11_LEGACY_DERIVE_NO_INHERIT
if (rv == CKR_OK) {
rv = CheckDeriveProtection(pTemplate, ulAttributeCount,
Expand Down
Loading
Loading