Repository navigation
C_GetAttributeValue returns CKR_FUNCTION_FAILED for CKA_CHECK_VALUE on a secret key whose length is not an AES key length #245
Description
Summary
wolfPKCS11 computes CKA_CHECK_VALUE for every CKO_SECRET_KEY object by encrypting a zero block with AES-ECB and taking the first three bytes of the result. That works only when the key is 16, 24, or 32 bytes long. For other lengths, AES key setup fails and C_GetAttributeValue returns CKR_FUNCTION_FAILED.
The failure also stops processing the attribute template. As a result, a request for CKA_CHECK_VALUE can prevent a consumer from reading any of the other attributes in that template.
For example, a 48-byte CKK_GENERIC_SECRET key (such as an HMAC-SHA384 key or a P-384 ECDH shared secret) and a 64-byte key (such as an HMAC-SHA512 key) cannot be read by a consumer that includes the common secret-key attribute CKA_CHECK_VALUE in its template. The length query succeeds and reports a length of 3, so the problem appears only when the consumer asks for the value.
I found this in wolfPKCS11 2.1.0 at commit caeaaa5693ad7b4253d6bc585e642381033a6a87 (2026-06-25). I also reproduced it on the head of pull request #243 (101fe0a, the stacked series #227 to #244), with #222 merged. Both builds were made from source on macOS arm64 against wolfSSL 5.9.2.
What the specification says
PKCS#11 Cryptoki Interface Base Specification Version 3.2, section 4.11 (Secret key objects), Table 30, lists CKA_CHECK_VALUE (“Key checksum”) as an attribute that secret-key objects may carry. A consumer reading a secret key may therefore include it in the requested attributes.
Section 6.8.2 (Generic secret key objects) defines the check value for this key type: "the first three bytes of the SHA-1 hash of the generic secret key object's CKA_VALUE attribute". Section 6.10.2 (AES secret key objects) defines the AES form, the ECB encryption of a block of null bytes. The HMAC key types define none, so for them the attribute is unavailable.
Section 5.7, C_GetAttributeValue, says:
Otherwise, if the specified value for the object is invalid (the object does not possess such an attribute), then the ulValueLen field in that triple is modified to hold the value CK_UNAVAILABLE_INFORMATION.
It also says:
Note that the error codes CKR_ATTRIBUTE_SENSITIVE, CKR_ATTRIBUTE_TYPE_INVALID, and CKR_BUFFER_TOO_SMALL do not denote true errors for C_GetAttributeValue. If a call to C_GetAttributeValue returns any of these three values, then the call MUST nonetheless have processed every attribute in the template supplied to C_GetAttributeValue.
CKR_FUNCTION_FAILED indicates a function failure; it is not the appropriate way to report that an attribute is unavailable or cannot be computed for a particular key type.
Steps to reproduce
Initialize a token with a user PIN, set WOLFPKCS11_TOKEN_PATH to its store, and run the program below against the module:
repro <path to libwolfpkcs11> <user pin>
The program creates session CKK_GENERIC_SECRET keys of different lengths with C_CreateObject, then reads CKA_CHECK_VALUE twice: first as a length query, then into a three-byte buffer.
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#define CK_PTR *
#define CK_DECLARE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name)
#define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name)
#define NULL_PTR 0
#include "pkcs11.h"
static CK_RV read_check_value(CK_FUNCTION_LIST_PTR fl, CK_SESSION_HANDLE s, CK_ULONG length)
{
CK_OBJECT_CLASS cls = CKO_SECRET_KEY;
CK_KEY_TYPE type = CKK_GENERIC_SECRET;
CK_BBOOL yes = CK_TRUE, no = CK_FALSE;
CK_BYTE value[64];
CK_BYTE kcv[3];
CK_OBJECT_HANDLE h;
CK_RV rv;
CK_ATTRIBUTE make[] = {
{ CKA_CLASS, &cls, sizeof(cls) }, { CKA_KEY_TYPE, &type, sizeof(type) },
{ CKA_TOKEN, &no, sizeof(no) }, { CKA_SIGN, &yes, sizeof(yes) }, { CKA_VERIFY, &yes, sizeof(yes) },
{ CKA_SENSITIVE, &no, sizeof(no) }, { CKA_EXTRACTABLE, &yes, sizeof(yes) },
{ CKA_VALUE, value, length },
};
CK_ATTRIBUTE query = { CKA_CHECK_VALUE, NULL, 0 };
CK_ATTRIBUTE read = { CKA_CHECK_VALUE, kcv, sizeof(kcv) };
memset(value, 0x5a, sizeof(value));
rv = fl->C_CreateObject(s, make, 8, &h);
if (rv != CKR_OK) {
printf("%2lu bytes: C_CreateObject -> 0x%lx\n", (unsigned long)length, (unsigned long)rv);
return rv;
}
rv = fl->C_GetAttributeValue(s, h, &query, 1);
printf("%2lu bytes: length query -> 0x%lx, ulValueLen %ld\n", (unsigned long)length, (unsigned long)rv, (long)query.ulValueLen);
rv = fl->C_GetAttributeValue(s, h, &read, 1);
printf("%2lu bytes: value read -> 0x%lx\n", (unsigned long)length, (unsigned long)rv);
fl->C_DestroyObject(s, h);
return rv;
}
int main(int argc, char** argv)
{
void* lib;
CK_RV (*get_list)(CK_FUNCTION_LIST_PTR_PTR);
CK_FUNCTION_LIST_PTR fl;
CK_SLOT_ID slot;
CK_ULONG count = 1;
CK_SESSION_HANDLE s;
if (argc != 3) {
fprintf(stderr, "usage: repro <module> <user pin>\n");
return 2;
}
lib = dlopen(argv[1], RTLD_NOW);
get_list = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR))dlsym(lib, "C_GetFunctionList");
get_list(&fl);
fl->C_Initialize(NULL);
fl->C_GetSlotList(CK_TRUE, &slot, &count);
fl->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &s);
fl->C_Login(s, CKU_USER, (CK_UTF8CHAR_PTR)argv[2], strlen(argv[2]));
read_check_value(fl, s, 32);
read_check_value(fl, s, 48);
read_check_value(fl, s, 64);
fl->C_Logout(s);
fl->C_CloseSession(s);
fl->C_Finalize(NULL);
return 0;
}Output on wolfPKCS11 2.1.0 (macOS arm64, wolfSSL 5.9.2, --enable-aesecb); 0x6 is CKR_FUNCTION_FAILED:
32 bytes: length query -> 0x0, ulValueLen 3
32 bytes: value read -> 0x0
48 bytes: length query -> 0x0, ulValueLen 3
48 bytes: value read -> 0x6
64 bytes: length query -> 0x0, ulValueLen 3
64 bytes: value read -> 0x6
The same result occurs on the head of pull request #243 with #222 merged.
A 32-byte CKK_AES key reads successfully, as expected. A 32-byte generic secret also reads successfully, but the value is wrong: it is the AES-256 check value, where section 6.8.2 defines the SHA-1 form.
The problem is not limited to callers requesting CKA_CHECK_VALUE by itself. If the template also includes attributes such as CKA_CLASS, CKA_KEY_TYPE, or CKA_VALUE_LEN, the call still returns CKR_FUNCTION_FAILED and leaves those attributes unfilled. A consumer using the common secret-key attributes therefore cannot read a 48- or 64-byte key, or enumerate a token containing one, if its template includes CKA_CHECK_VALUE.
Where it is in the source
In src/internal.c (wolfPKCS11 2.1.0 line numbers):
- 11448–11459,
WP11_Object_GetAttr,case CKA_CHECK_VALUE: ForCKO_SECRET_KEY, the code callsGetEcbCheckValue(object, data, len)underHAVE_AESECB, regardless of the secret key's type. - 11233,
GetEcbCheckValue: With a NULL buffer, it reportsPKCS11_CHECK_VALUE_SIZEand returns. With a buffer, it encrypts a zero block usingWP11_AesEcb_Encrypt(input, inLen, hash, &inLen, secret, secret->session), passing the secret-key object itself as the AES key. If that call fails, the function returnsCKR_FUNCTION_FAILED. wolfCrypt's AES key setup rejects 48- and 64-byte keys. src/crypto.c,C_GetAttributeValue: The final per-attribute result-mapping branch handles any other nonzero return withCKR_FUNCTION_FAILEDand returns immediately, stopping processing of the template.
Suggested fix
Select the algorithm by key type: CKK_AES keeps the ECB check value; CKK_GENERIC_SECRET takes the first three bytes of SHA-1 over the key value, which GetSha1CheckValue in the same file already computes for certificates; the HMAC key types report NOT_AVAILABLE_E, which C_GetAttributeValue maps to CK_UNAVAILABLE_INFORMATION.
At a minimum, a failed check-value computation should be reported as an unavailable attribute rather than CKR_FUNCTION_FAILED, so the rest of the template can still be processed.
Why it matters
A PKCS#11 consumer that requests the common secret-key attributes cannot read HMAC-SHA384 or HMAC-SHA512 keys, or P-384 or P-521 ECDH shared secrets, if its template includes CKA_CHECK_VALUE. It can also fail to enumerate a token containing one of these keys.
The length query succeeds, so the failure is easy to miss until the consumer tries to read the value. The underlying issue is that an attribute request for a check value can prevent the caller from retrieving otherwise valid attributes from the same object.
Activity
pdb0102 commentedon Oct 10, 2026
I'm not comfortable enough to provide a PR (not sure about the rules and style and commenting, and some projects are picky) but the following fix worked for me:
LinuxJedi commentedon Oct 12, 2026
Many thanks for the report and the diff. You are welcome to contribute a PR for this, but we would need to get you to sign a CLA for it. If you are happy with that, please open a PR and the team will walk you through how to do it.
Alternatively, I can work on a fix later this week.
Compute CKA_CHECK_VALUE by secret key type