pkcs11: switch to RSA-OAEP SHA-256/SHA-1
RSA PKCS#1 v1.5 is vulnerable to Bleichenbacher-style padding oracle attacks, albeit very difficult and unlikely to actually happen in the real world. Still for hardedning, switch new enrollments to RSA-OAEP, with SHA-256 preferred and SHA-1 as fallback (probed at enrollment time, since e.g. SoftHSM only accepts SHA-1, and older token might as well). The actual padding scheme used to wrap a given key is recorded as a new optional 'pkcs11-padding' / 'padding' field in the LUKS2 token JSON and the homed user record. Decryption defaults to PKCS#1 v1.5 when absent so existing enrollments keep working.
This commit is contained in:
+24
-60
@@ -164,7 +164,7 @@ DLSYM_PROTOTYPE(EVP_PKEY_free) = NULL;
|
||||
DLSYM_PROTOTYPE(EVP_PKEY_fromdata) = NULL;
|
||||
DLSYM_PROTOTYPE(EVP_PKEY_fromdata_init) = NULL;
|
||||
static DLSYM_PROTOTYPE(EVP_PKEY_get1_encoded_public_key) = NULL;
|
||||
static DLSYM_PROTOTYPE(EVP_PKEY_get_base_id) = NULL;
|
||||
DLSYM_PROTOTYPE(EVP_PKEY_get_base_id) = NULL;
|
||||
static DLSYM_PROTOTYPE(EVP_PKEY_get_bits) = NULL;
|
||||
static DLSYM_PROTOTYPE(EVP_PKEY_get_bn_param) = NULL;
|
||||
static DLSYM_PROTOTYPE(EVP_PKEY_get_group_name) = NULL;
|
||||
@@ -1123,52 +1123,6 @@ int kdf_kb_hmac_derive(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsa_encrypt_bytes(
|
||||
EVP_PKEY *pkey,
|
||||
const void *decrypted_key,
|
||||
size_t decrypted_key_size,
|
||||
void **ret_encrypt_key,
|
||||
size_t *ret_encrypt_key_size) {
|
||||
|
||||
_cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL;
|
||||
_cleanup_free_ void *b = NULL;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
assert(ret_encrypt_key);
|
||||
assert(ret_encrypt_key_size);
|
||||
|
||||
r = dlopen_libcrypto(LOG_DEBUG);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
ctx = sym_EVP_PKEY_CTX_new(pkey, NULL);
|
||||
if (!ctx)
|
||||
return log_openssl_errors("Failed to allocate public key context");
|
||||
|
||||
if (sym_EVP_PKEY_encrypt_init(ctx) <= 0)
|
||||
return log_openssl_errors("Failed to initialize public key context");
|
||||
|
||||
if (sym_EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
|
||||
return log_openssl_errors("Failed to configure PKCS#1 padding");
|
||||
|
||||
if (sym_EVP_PKEY_encrypt(ctx, NULL, &l, decrypted_key, decrypted_key_size) <= 0)
|
||||
return log_openssl_errors("Failed to determine encrypted key size");
|
||||
|
||||
b = malloc(l);
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
|
||||
if (sym_EVP_PKEY_encrypt(ctx, b, &l, decrypted_key, decrypted_key_size) <= 0)
|
||||
return log_openssl_errors("Failed to determine encrypted key size");
|
||||
|
||||
*ret_encrypt_key = TAKE_PTR(b);
|
||||
*ret_encrypt_key_size = l;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Encrypt the key data using RSA-OAEP with the provided label and specified digest algorithm. Returns 0 on
|
||||
* success, -EOPNOTSUPP if the digest algorithm is not supported, or < 0 for any other error. */
|
||||
int rsa_oaep_encrypt_bytes(
|
||||
const EVP_PKEY *pkey,
|
||||
const char *digest_alg,
|
||||
@@ -1182,7 +1136,6 @@ int rsa_oaep_encrypt_bytes(
|
||||
|
||||
assert(pkey);
|
||||
assert(digest_alg);
|
||||
assert(label);
|
||||
assert(decrypted_key);
|
||||
assert(decrypted_key_size > 0);
|
||||
assert(ret_encrypt_key);
|
||||
@@ -1210,14 +1163,16 @@ int rsa_oaep_encrypt_bytes(
|
||||
if (sym_EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) <= 0)
|
||||
return log_openssl_errors("Failed to configure RSA-OAEP MD");
|
||||
|
||||
_cleanup_free_ char *duplabel = strdup(label);
|
||||
if (!duplabel)
|
||||
return log_oom_debug();
|
||||
if (label) {
|
||||
_cleanup_free_ char *duplabel = strdup(label);
|
||||
if (!duplabel)
|
||||
return log_oom_debug();
|
||||
|
||||
if (sym_EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, duplabel, strlen(duplabel) + 1) <= 0)
|
||||
return log_openssl_errors("Failed to configure RSA-OAEP label");
|
||||
/* ctx owns this now, don't free */
|
||||
TAKE_PTR(duplabel);
|
||||
if (sym_EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, duplabel, strlen(duplabel) + 1) <= 0)
|
||||
return log_openssl_errors("Failed to configure RSA-OAEP label");
|
||||
/* ctx owns this now, don't free */
|
||||
TAKE_PTR(duplabel);
|
||||
}
|
||||
|
||||
size_t size = 0;
|
||||
if (sym_EVP_PKEY_encrypt(ctx, NULL, &size, decrypted_key, decrypted_key_size) <= 0)
|
||||
@@ -1259,8 +1214,9 @@ int rsa_pkey_to_suitable_key_size(
|
||||
bits = sym_EVP_PKEY_get_bits(pkey);
|
||||
log_debug("Bits in RSA key: %i", bits);
|
||||
|
||||
/* We use PKCS#1 padding for the RSA cleartext, hence let's leave some extra space for it, hence only
|
||||
* generate a random key half the size of the RSA length */
|
||||
/* We use RSA-OAEP for the cleartext, which has 2*hash_len + 2 bytes of overhead (e.g. 66 bytes
|
||||
* for SHA-256, 42 for SHA-1). Leave plenty of headroom by only generating a key half the size of
|
||||
* the RSA modulus. */
|
||||
suitable_key_size = bits / 8 / 2;
|
||||
|
||||
if (suitable_key_size < 1)
|
||||
@@ -1862,6 +1818,7 @@ static int ecc_pkey_generate_volume_keys(
|
||||
|
||||
static int rsa_pkey_generate_volume_keys(
|
||||
EVP_PKEY *pkey,
|
||||
const char *digest_alg,
|
||||
void **ret_decrypted_key,
|
||||
size_t *ret_decrypted_key_size,
|
||||
void **ret_saved_key,
|
||||
@@ -1872,6 +1829,7 @@ static int rsa_pkey_generate_volume_keys(
|
||||
size_t decrypted_key_size, saved_key_size;
|
||||
int r;
|
||||
|
||||
assert(digest_alg);
|
||||
assert(ret_decrypted_key);
|
||||
assert(ret_decrypted_key_size);
|
||||
assert(ret_saved_key);
|
||||
@@ -1891,9 +1849,14 @@ static int rsa_pkey_generate_volume_keys(
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to generate random key: %m");
|
||||
|
||||
r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &saved_key, &saved_key_size);
|
||||
r = rsa_oaep_encrypt_bytes(
|
||||
pkey,
|
||||
digest_alg,
|
||||
/* label= */ NULL, /* matches PKCS#11 CKZ_DATA_SPECIFIED with empty source */
|
||||
decrypted_key, decrypted_key_size,
|
||||
&saved_key, &saved_key_size);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to encrypt random key: %m");
|
||||
return log_debug_errno(r, "Failed to RSA-OAEP encrypt random key: %m");
|
||||
|
||||
*ret_decrypted_key = TAKE_PTR(decrypted_key);
|
||||
*ret_decrypted_key_size = decrypted_key_size;
|
||||
@@ -1904,6 +1867,7 @@ static int rsa_pkey_generate_volume_keys(
|
||||
|
||||
int pkey_generate_volume_keys(
|
||||
EVP_PKEY *pkey,
|
||||
const char *rsa_oaep_digest_alg,
|
||||
void **ret_decrypted_key,
|
||||
size_t *ret_decrypted_key_size,
|
||||
void **ret_saved_key,
|
||||
@@ -1925,7 +1889,7 @@ int pkey_generate_volume_keys(
|
||||
switch (type) {
|
||||
|
||||
case EVP_PKEY_RSA:
|
||||
return rsa_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size);
|
||||
return rsa_pkey_generate_volume_keys(pkey, rsa_oaep_digest_alg ?: "SHA-1", ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size);
|
||||
|
||||
case EVP_PKEY_EC:
|
||||
return ecc_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size);
|
||||
|
||||
@@ -148,6 +148,7 @@ extern DLSYM_PROTOTYPE(EVP_PKEY_eq);
|
||||
extern DLSYM_PROTOTYPE(EVP_PKEY_free);
|
||||
extern DLSYM_PROTOTYPE(EVP_PKEY_fromdata_init);
|
||||
extern DLSYM_PROTOTYPE(EVP_PKEY_fromdata);
|
||||
extern DLSYM_PROTOTYPE(EVP_PKEY_get_base_id);
|
||||
extern DLSYM_PROTOTYPE(EVP_PKEY_get_id);
|
||||
extern DLSYM_PROTOTYPE(EVP_PKEY_keygen_init);
|
||||
extern DLSYM_PROTOTYPE(EVP_PKEY_keygen);
|
||||
@@ -348,8 +349,6 @@ int kdf_ss_derive(const char *digest, const void *key, size_t key_size, const vo
|
||||
|
||||
int kdf_kb_hmac_derive(const char *mode, const char *digest, const void *key, size_t key_size, const void *salt, size_t salt_size, const void *info, size_t info_size, const void *seed, size_t seed_size, size_t derive_size, void **ret);
|
||||
|
||||
int rsa_encrypt_bytes(EVP_PKEY *pkey, const void *decrypted_key, size_t decrypted_key_size, void **ret_encrypt_key, size_t *ret_encrypt_key_size);
|
||||
|
||||
int rsa_oaep_encrypt_bytes(const EVP_PKEY *pkey, const char *digest_alg, const char *label, const void *decrypted_key, size_t decrypted_key_size, void **ret_encrypt_key, size_t *ret_encrypt_key_size);
|
||||
|
||||
int rsa_pkey_to_suitable_key_size(EVP_PKEY *pkey, size_t *ret_suitable_key_size);
|
||||
@@ -366,7 +365,7 @@ int ecc_pkey_new(int curve_id, EVP_PKEY **ret);
|
||||
|
||||
int ecc_ecdh(const EVP_PKEY *private_pkey, const EVP_PKEY *peer_pkey, void **ret_shared_secret, size_t *ret_shared_secret_size);
|
||||
|
||||
int pkey_generate_volume_keys(EVP_PKEY *pkey, void **ret_decrypted_key, size_t *ret_decrypted_key_size, void **ret_saved_key, size_t *ret_saved_key_size);
|
||||
int pkey_generate_volume_keys(EVP_PKEY *pkey, const char *rsa_oaep_digest_alg, void **ret_decrypted_key, size_t *ret_decrypted_key_size, void **ret_saved_key, size_t *ret_saved_key_size);
|
||||
|
||||
int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size);
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "basic-forward.h"
|
||||
|
||||
typedef enum Pkcs11RsaPadding {
|
||||
PKCS11_RSA_PADDING_PKCS1V15, /* CKM_RSA_PKCS, RFC 8017 PKCS#1 v1.5 (legacy) */
|
||||
PKCS11_RSA_PADDING_OAEP_SHA1, /* CKM_RSA_PKCS_OAEP with SHA-1/MGF1-SHA-1 (more supported) */
|
||||
PKCS11_RSA_PADDING_OAEP_SHA256, /* CKM_RSA_PKCS_OAEP with SHA-256/MGF1-SHA-256 (preferred if supported) */
|
||||
_PKCS11_RSA_PADDING_MAX,
|
||||
_PKCS11_RSA_PADDING_INVALID = -EINVAL,
|
||||
} Pkcs11RsaPadding;
|
||||
|
||||
const char* pkcs11_rsa_padding_to_string(Pkcs11RsaPadding i);
|
||||
Pkcs11RsaPadding pkcs11_rsa_padding_from_string(const char *s);
|
||||
|
||||
static inline const char* pkcs11_rsa_padding_to_oaep_hash(Pkcs11RsaPadding p) {
|
||||
switch (p) {
|
||||
case PKCS11_RSA_PADDING_OAEP_SHA1:
|
||||
return "SHA-1";
|
||||
case PKCS11_RSA_PADDING_OAEP_SHA256:
|
||||
return "SHA-256";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
+166
-5
@@ -17,6 +17,7 @@
|
||||
#include "memory-util.h"
|
||||
#include "pkcs11-util.h"
|
||||
#include "random-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
@@ -41,6 +42,14 @@ bool pkcs11_uri_valid(const char *uri) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* const pkcs11_rsa_padding_table[_PKCS11_RSA_PADDING_MAX] = {
|
||||
[PKCS11_RSA_PADDING_PKCS1V15] = "pkcs1",
|
||||
[PKCS11_RSA_PADDING_OAEP_SHA1] = "oaep-sha1",
|
||||
[PKCS11_RSA_PADDING_OAEP_SHA256] = "oaep-sha256",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(pkcs11_rsa_padding, Pkcs11RsaPadding);
|
||||
|
||||
#if HAVE_P11KIT
|
||||
|
||||
DLSYM_PROTOTYPE(p11_kit_module_get_name) = NULL;
|
||||
@@ -1171,12 +1180,23 @@ static int pkcs11_token_decrypt_data_rsa(
|
||||
CK_OBJECT_HANDLE object,
|
||||
const void *encrypted_data,
|
||||
size_t encrypted_data_size,
|
||||
Pkcs11RsaPadding rsa_padding,
|
||||
void **ret_decrypted_data,
|
||||
size_t *ret_decrypted_data_size) {
|
||||
|
||||
static const CK_MECHANISM mechanism = {
|
||||
.mechanism = CKM_RSA_PKCS
|
||||
/* For RSA-OAEP we use SHA-256 or SHA-1 with the matching MGF1 and an empty (zero-length) label.
|
||||
* SHA-256 is preferred when the token supports it, but for example SoftHSM doesn't support it. */
|
||||
CK_RSA_PKCS_OAEP_PARAMS oaep_params_sha1 = {
|
||||
.hashAlg = CKM_SHA_1,
|
||||
.mgf = CKG_MGF1_SHA1,
|
||||
.source = CKZ_DATA_SPECIFIED,
|
||||
};
|
||||
CK_RSA_PKCS_OAEP_PARAMS oaep_params_sha256 = {
|
||||
.hashAlg = CKM_SHA256,
|
||||
.mgf = CKG_MGF1_SHA256,
|
||||
.source = CKZ_DATA_SPECIFIED,
|
||||
};
|
||||
CK_MECHANISM mechanism;
|
||||
_cleanup_(erase_and_freep) CK_BYTE *dbuffer = NULL;
|
||||
CK_ULONG dbuffer_size = 0;
|
||||
CK_RV rv;
|
||||
@@ -1184,7 +1204,36 @@ static int pkcs11_token_decrypt_data_rsa(
|
||||
assert(ret_decrypted_data);
|
||||
assert(ret_decrypted_data_size);
|
||||
|
||||
rv = m->C_DecryptInit(session, (CK_MECHANISM*) &mechanism, object);
|
||||
switch (rsa_padding) {
|
||||
|
||||
case PKCS11_RSA_PADDING_PKCS1V15:
|
||||
mechanism = (CK_MECHANISM) {
|
||||
.mechanism = CKM_RSA_PKCS,
|
||||
};
|
||||
break;
|
||||
|
||||
case PKCS11_RSA_PADDING_OAEP_SHA1:
|
||||
mechanism = (CK_MECHANISM) {
|
||||
.mechanism = CKM_RSA_PKCS_OAEP,
|
||||
.pParameter = &oaep_params_sha1,
|
||||
.ulParameterLen = sizeof(oaep_params_sha1),
|
||||
};
|
||||
break;
|
||||
|
||||
case PKCS11_RSA_PADDING_OAEP_SHA256:
|
||||
mechanism = (CK_MECHANISM) {
|
||||
.mechanism = CKM_RSA_PKCS_OAEP,
|
||||
.pParameter = &oaep_params_sha256,
|
||||
.ulParameterLen = sizeof(oaep_params_sha256),
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Unknown RSA padding scheme requested for PKCS#11 decryption.");
|
||||
}
|
||||
|
||||
rv = m->C_DecryptInit(session, &mechanism, object);
|
||||
if (rv != CKR_OK)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to initialize decryption on security token: %s", sym_p11_kit_strerror(rv));
|
||||
@@ -1208,7 +1257,8 @@ static int pkcs11_token_decrypt_data_rsa(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to decrypt key on security token: %s", sym_p11_kit_strerror(rv));
|
||||
|
||||
log_info("Successfully decrypted key with security token.");
|
||||
log_info("Successfully decrypted key with security token (%s padding).",
|
||||
pkcs11_rsa_padding_to_string(rsa_padding));
|
||||
|
||||
*ret_decrypted_data = TAKE_PTR(dbuffer);
|
||||
*ret_decrypted_data_size = dbuffer_size;
|
||||
@@ -1221,6 +1271,7 @@ int pkcs11_token_decrypt_data(
|
||||
CK_OBJECT_HANDLE object,
|
||||
const void *encrypted_data,
|
||||
size_t encrypted_data_size,
|
||||
Pkcs11RsaPadding rsa_padding,
|
||||
void **ret_decrypted_data,
|
||||
size_t *ret_decrypted_data_size) {
|
||||
|
||||
@@ -1241,7 +1292,7 @@ int pkcs11_token_decrypt_data(
|
||||
switch (key_type) {
|
||||
|
||||
case CKK_RSA:
|
||||
return pkcs11_token_decrypt_data_rsa(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size);
|
||||
return pkcs11_token_decrypt_data_rsa(m, session, object, encrypted_data, encrypted_data_size, rsa_padding, ret_decrypted_data, ret_decrypted_data_size);
|
||||
|
||||
case CKK_EC:
|
||||
return pkcs11_token_decrypt_data_ecc(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size);
|
||||
@@ -1529,6 +1580,7 @@ int pkcs11_find_token(
|
||||
struct pkcs11_acquire_public_key_callback_data {
|
||||
char *pin_used;
|
||||
EVP_PKEY *pkey;
|
||||
Pkcs11RsaPadding rsa_padding;
|
||||
const char *askpw_friendly_name, *askpw_icon, *askpw_credential;
|
||||
AskPasswordFlags askpw_flags;
|
||||
};
|
||||
@@ -1539,6 +1591,86 @@ static void pkcs11_acquire_public_key_callback_data_release(struct pkcs11_acquir
|
||||
sym_EVP_PKEY_free(data->pkey);
|
||||
}
|
||||
|
||||
static int pkcs11_token_try_oaep_mechanism(
|
||||
CK_FUNCTION_LIST *m,
|
||||
CK_SESSION_HANDLE session,
|
||||
CK_OBJECT_HANDLE private_key,
|
||||
CK_MECHANISM_TYPE hash_alg,
|
||||
CK_RSA_PKCS_MGF_TYPE mgf) {
|
||||
|
||||
CK_RSA_PKCS_OAEP_PARAMS params = {
|
||||
.hashAlg = hash_alg,
|
||||
.mgf = mgf,
|
||||
.source = CKZ_DATA_SPECIFIED,
|
||||
};
|
||||
CK_MECHANISM mechanism = {
|
||||
.mechanism = CKM_RSA_PKCS_OAEP,
|
||||
.pParameter = ¶ms,
|
||||
.ulParameterLen = sizeof(params),
|
||||
};
|
||||
CK_RV rv;
|
||||
|
||||
rv = m->C_DecryptInit(session, &mechanism, private_key);
|
||||
if (rv != CKR_OK)
|
||||
return -EIO;
|
||||
|
||||
/* Accepted: terminate the operation. Per PKCS#11 spec section 5.2 any cryptographic function
|
||||
* returning an error other than CKR_BUFFER_TOO_SMALL terminates the active operation, so feed
|
||||
* deliberately invalid (too-short) ciphertext to C_Decrypt to trigger a cancellation that's
|
||||
* likely to be portable across token implementations. */
|
||||
CK_BYTE in[1] = {};
|
||||
CK_BYTE out[1];
|
||||
CK_ULONG out_len = sizeof(out);
|
||||
(void) m->C_Decrypt(session, in, sizeof(in), out, &out_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pkcs11_token_probe_rsa_oaep_padding(
|
||||
CK_FUNCTION_LIST *m,
|
||||
CK_SESSION_HANDLE session,
|
||||
CK_OBJECT_HANDLE private_key,
|
||||
Pkcs11RsaPadding *ret) {
|
||||
|
||||
CK_KEY_TYPE key_type;
|
||||
CK_ATTRIBUTE key_type_template = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
|
||||
CK_RV rv;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(ret);
|
||||
|
||||
/* Probes whether the token will accept the OAEP-SHA256 or OAEP-SHA1 mechanism for decryption with
|
||||
* the given private key. On any failure (wrong key type, related-object lookup ambiguity yielded
|
||||
* an EC private key, both mechanisms rejected, etc.) we bail out, this can only be best-effort. */
|
||||
rv = m->C_GetAttributeValue(session, private_key, &key_type_template, /* ulCount= */ 1);
|
||||
if (rv != CKR_OK)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to retrieve private key type: %s", sym_p11_kit_strerror(rv));
|
||||
if (key_type != CKK_RSA)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Located private key is not RSA, skipping OAEP padding probe.");
|
||||
|
||||
/* For RSA-OAEP we use SHA-256 or SHA-1 with the matching MGF1 and an empty (zero-length) label.
|
||||
* SHA-256 is preferred when the token supports it, but for example SoftHSM doesn't support it, so
|
||||
* fall back to SHA-1 if it gets rejected. */
|
||||
r = pkcs11_token_try_oaep_mechanism(m, session, private_key, CKM_SHA256, CKG_MGF1_SHA256);
|
||||
if (r >= 0) {
|
||||
*ret = PKCS11_RSA_PADDING_OAEP_SHA256;
|
||||
log_debug("Token accepts OAEP-SHA256 for decryption.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = pkcs11_token_try_oaep_mechanism(m, session, private_key, CKM_SHA_1, CKG_MGF1_SHA1);
|
||||
if (r >= 0) {
|
||||
*ret = PKCS11_RSA_PADDING_OAEP_SHA1;
|
||||
log_debug("Token accepts OAEP-SHA1 for decryption.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Token rejected both OAEP-SHA256 and OAEP-SHA1.");
|
||||
}
|
||||
|
||||
static int pkcs11_acquire_public_key_callback(
|
||||
CK_FUNCTION_LIST *m,
|
||||
CK_SESSION_HANDLE session,
|
||||
@@ -1702,6 +1834,30 @@ success:
|
||||
* kernel's and the token's pool */
|
||||
(void) pkcs11_token_acquire_rng(m, session);
|
||||
|
||||
/* Decide whether the enrolled key needs RSA padding metadata at all based on the public key we
|
||||
* extracted (RSA vs EC). For RSA, default to OAEP-SHA1 which works on all known tokens (e.g.
|
||||
* SoftHSM only accepts SHA-1), then try to upgrade to OAEP-SHA256 by probing the matching private
|
||||
* key on the token. The probe is best-effort: if we cannot find the private key (e.g. token holds
|
||||
* only the certificate, or the related-object lookup is ambiguous because CKA_ID is missing or
|
||||
* doesn't match) we keep the OAEP-SHA1 default. For non-RSA keys (EC) we leave rsa_padding as
|
||||
* _INVALID, so no padding tag is recorded and the decrypt path uses ECDH instead. */
|
||||
data->rsa_padding = _PKCS11_RSA_PADDING_INVALID;
|
||||
|
||||
if (sym_EVP_PKEY_get_base_id(pkey) == EVP_PKEY_RSA) {
|
||||
CK_OBJECT_HANDLE prototype = public_key != CK_INVALID_HANDLE ? public_key : certificate;
|
||||
CK_OBJECT_HANDLE private_key = CK_INVALID_HANDLE;
|
||||
Pkcs11RsaPadding padding = PKCS11_RSA_PADDING_OAEP_SHA1; /* default to SHA1 unless we can do better */
|
||||
|
||||
if (pkcs11_token_find_related_object(m, session, prototype, CKO_PRIVATE_KEY, &private_key) >= 0) {
|
||||
r = pkcs11_token_probe_rsa_oaep_padding(m, session, private_key, &padding);
|
||||
if (r < 0)
|
||||
log_info_errno(r, "Token rejected both OAEP-SHA256 and OAEP-SHA1, defaulting RSA enrollment to OAEP-SHA1.");
|
||||
} else
|
||||
log_info("No matching PKCS#11 private key found, OAEP padding probe skipped, defaulting RSA enrollment to OAEP-SHA1.");
|
||||
|
||||
data->rsa_padding = padding;
|
||||
}
|
||||
|
||||
data->pin_used = TAKE_PTR(pin_used);
|
||||
data->pkey = TAKE_PTR(pkey);
|
||||
return 0;
|
||||
@@ -1714,6 +1870,7 @@ int pkcs11_acquire_public_key(
|
||||
const char *askpw_credential,
|
||||
AskPasswordFlags askpw_flags,
|
||||
EVP_PKEY **ret_pkey,
|
||||
Pkcs11RsaPadding *ret_rsa_padding,
|
||||
char **ret_pin_used) {
|
||||
|
||||
_cleanup_(pkcs11_acquire_public_key_callback_data_release) struct pkcs11_acquire_public_key_callback_data data = {
|
||||
@@ -1721,6 +1878,7 @@ int pkcs11_acquire_public_key(
|
||||
.askpw_icon = askpw_icon,
|
||||
.askpw_credential = askpw_credential,
|
||||
.askpw_flags = askpw_flags,
|
||||
.rsa_padding = _PKCS11_RSA_PADDING_INVALID,
|
||||
};
|
||||
int r;
|
||||
|
||||
@@ -1736,6 +1894,8 @@ int pkcs11_acquire_public_key(
|
||||
return r;
|
||||
|
||||
*ret_pkey = TAKE_PTR(data.pkey);
|
||||
if (ret_rsa_padding)
|
||||
*ret_rsa_padding = data.rsa_padding;
|
||||
if (ret_pin_used)
|
||||
*ret_pin_used = TAKE_PTR(data.pin_used);
|
||||
return 0;
|
||||
@@ -1978,6 +2138,7 @@ int pkcs11_crypt_device_callback(
|
||||
object,
|
||||
data->encrypted_key,
|
||||
data->encrypted_key_size,
|
||||
data->rsa_padding,
|
||||
&data->decrypted_key,
|
||||
&data->decrypted_key_size);
|
||||
if (r < 0)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "ask-password-api.h"
|
||||
#include "dlfcn-util.h"
|
||||
#include "pkcs11-padding.h"
|
||||
#include "shared-forward.h"
|
||||
|
||||
bool pkcs11_uri_valid(const char *uri);
|
||||
@@ -66,7 +67,7 @@ int pkcs11_token_read_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE se
|
||||
#endif
|
||||
|
||||
int pkcs11_token_find_private_key(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object);
|
||||
int pkcs11_token_decrypt_data(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, const void *encrypted_data, size_t encrypted_data_size, void **ret_decrypted_data, size_t *ret_decrypted_data_size);
|
||||
int pkcs11_token_decrypt_data(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, const void *encrypted_data, size_t encrypted_data_size, Pkcs11RsaPadding rsa_padding, void **ret_decrypted_data, size_t *ret_decrypted_data_size);
|
||||
|
||||
int pkcs11_token_acquire_rng(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session);
|
||||
|
||||
@@ -74,7 +75,7 @@ typedef int (*pkcs11_find_token_callback_t)(CK_FUNCTION_LIST *m, CK_SESSION_HAND
|
||||
int pkcs11_find_token(const char *pkcs11_uri, pkcs11_find_token_callback_t callback, void *userdata);
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
int pkcs11_acquire_public_key(const char *uri, const char *askpw_friendly_name, const char *askpw_icon, const char *askpw_credential, AskPasswordFlags askpw_flags, EVP_PKEY **ret_pkey, char **ret_pin_used);
|
||||
int pkcs11_acquire_public_key(const char *uri, const char *askpw_friendly_name, const char *askpw_icon, const char *askpw_credential, AskPasswordFlags askpw_flags, EVP_PKEY **ret_pkey, Pkcs11RsaPadding *ret_rsa_padding, char **ret_pin_used);
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
@@ -87,6 +88,7 @@ typedef struct {
|
||||
bool free_encrypted_key;
|
||||
const char *askpw_credential;
|
||||
AskPasswordFlags askpw_flags;
|
||||
Pkcs11RsaPadding rsa_padding;
|
||||
} pkcs11_crypt_device_callback_data;
|
||||
|
||||
void pkcs11_crypt_device_callback_data_release(pkcs11_crypt_device_callback_data *data);
|
||||
|
||||
@@ -729,6 +729,29 @@ static int dispatch_pkcs11_key_data(const char *name, sd_json_variant *variant,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispatch_pkcs11_padding(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
Pkcs11RsaPadding *p = ASSERT_PTR(userdata);
|
||||
Pkcs11RsaPadding v;
|
||||
|
||||
if (sd_json_variant_is_null(variant)) {
|
||||
*p = PKCS11_RSA_PADDING_PKCS1V15;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!sd_json_variant_is_string(variant))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
|
||||
"JSON field '%s' is not a string.", strna(name));
|
||||
|
||||
v = pkcs11_rsa_padding_from_string(sd_json_variant_string(variant));
|
||||
if (v < 0)
|
||||
return json_log(variant, flags, v,
|
||||
"JSON field '%s' has unsupported value '%s'.",
|
||||
strna(name), sd_json_variant_string(variant));
|
||||
|
||||
*p = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispatch_pkcs11_key(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
UserRecord *h = userdata;
|
||||
sd_json_variant *e;
|
||||
@@ -742,6 +765,7 @@ static int dispatch_pkcs11_key(const char *name, sd_json_variant *variant, sd_js
|
||||
{ "uri", SD_JSON_VARIANT_STRING, dispatch_pkcs11_uri, offsetof(Pkcs11EncryptedKey, uri), SD_JSON_MANDATORY },
|
||||
{ "data", SD_JSON_VARIANT_STRING, dispatch_pkcs11_key_data, 0, SD_JSON_MANDATORY },
|
||||
{ "hashedPassword", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(Pkcs11EncryptedKey, hashed_password), SD_JSON_MANDATORY },
|
||||
{ "padding", SD_JSON_VARIANT_STRING, dispatch_pkcs11_padding, offsetof(Pkcs11EncryptedKey, padding), 0 },
|
||||
{},
|
||||
};
|
||||
|
||||
@@ -752,7 +776,9 @@ static int dispatch_pkcs11_key(const char *name, sd_json_variant *variant, sd_js
|
||||
return log_oom();
|
||||
|
||||
Pkcs11EncryptedKey *k = h->pkcs11_encrypted_key + h->n_pkcs11_encrypted_key;
|
||||
*k = (Pkcs11EncryptedKey) {};
|
||||
*k = (Pkcs11EncryptedKey) {
|
||||
.padding = PKCS11_RSA_PADDING_PKCS1V15, /* legacy default if field is absent */
|
||||
};
|
||||
|
||||
r = sd_json_dispatch(e, pkcs11_key_dispatch_table, flags, k);
|
||||
if (r < 0) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "bitfield.h"
|
||||
#include "pkcs11-padding.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "shared-forward.h"
|
||||
|
||||
@@ -189,6 +190,10 @@ typedef struct Pkcs11EncryptedKey {
|
||||
/* Where to find the private key to decrypt the encrypted passphrase above */
|
||||
char *uri;
|
||||
|
||||
/* Which RSA padding scheme was used to wrap the encrypted passphrase. Defaults to PKCS#1 v1.5 for
|
||||
* legacy records that omit the field; new enrollments use RSA-OAEP with SHA-256 or SHA-1. */
|
||||
Pkcs11RsaPadding padding;
|
||||
|
||||
/* What to test the decrypted passphrase against to allow access (classic UNIX password hash). Note
|
||||
* that the decrypted passphrase is also used for unlocking LUKS and fscrypt, and if the account is
|
||||
* backed by LUKS or fscrypt the hashed password is only an additional layer of authentication, not
|
||||
|
||||
Reference in New Issue
Block a user