Skip to content

Commit 54cc197

Browse files
authored
Merge pull request #241 from ionut-arm/soft-pkcs11
Allow software operations in PKCS11 provider
2 parents 40bf768 + 61d207b commit 54cc197

File tree

14 files changed

+244
-15
lines changed

14 files changed

+244
-15
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ features = ["docs"]
6666
default = []
6767
no-parsec-user-and-clients-group = []
6868
mbed-crypto-provider = ["psa-crypto"]
69-
pkcs11-provider = ["pkcs11", "picky-asn1-der", "picky-asn1", "picky-asn1-x509"]
69+
pkcs11-provider = ["pkcs11", "picky-asn1-der", "picky-asn1", "picky-asn1-x509", "psa-crypto"]
7070
tpm-provider = ["tss-esapi", "picky-asn1-der", "picky-asn1", "picky-asn1-x509"]
7171
all-providers = ["tpm-provider", "pkcs11-provider", "mbed-crypto-provider"]
7272
docs = ["pkcs11-provider", "tpm-provider", "tss-esapi/docs", "mbed-crypto-provider"]

ci.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ pgrep -f target/debug/parsec >/dev/null
137137
if [ "$PROVIDER_NAME" = "all" ]; then
138138
echo "Execute all-providers tests"
139139
RUST_BACKTRACE=1 cargo test $TEST_FEATURES --manifest-path ./e2e_tests/Cargo.toml all_providers
140-
RUST_BACKTRACE=1 cargo test $TEST_FEATURES --manifest-path ./e2e_tests/Cargo.toml config
140+
RUST_BACKTRACE=1 cargo test $TEST_FEATURES --manifest-path ./e2e_tests/Cargo.toml config -- --test-threads=1
141141
else
142142
# Per provider tests
143143
echo "Execute normal tests"

config.toml

+3
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ key_info_manager = "on-disk-manager"
8080
# (Optional) User pin for authentication with the specific slot. If not set, no authentication will
8181
# be used.
8282
#user_pin = "123456"
83+
# (Optional) Control whether missing public key operation (such as verifying signatures or asymmetric
84+
# encryption) are fully performed in software.
85+
#software_public_operations = false
8386

8487
# Example of a TPM provider configuration
8588
#[[provider]]

e2e_tests/provider_cfg/pkcs11/Dockerfile

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ FROM ubuntu:18.04
22

33
RUN apt-get update && \
44
apt-get install -y wget automake autoconf libtool pkg-config && \
5-
apt-get install -y curl libssl-dev libgcc1
5+
apt-get install -y curl libssl-dev libgcc1 && \
6+
apt-get install -y git make gcc python3 python curl wget libgcc1 cmake && \
7+
# These libraries are needed for bindgen as it uses libclang.so
8+
apt-get install -y clang libclang-dev && \
9+
# Needed for Open SSL
10+
apt-get install -y pkg-config libssl-dev
611

712
WORKDIR /tmp
813
RUN wget https://github.com/opendnssec/SoftHSMv2/archive/2.5.0.tar.gz

e2e_tests/provider_cfg/pkcs11/config.toml

+1
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ provider_type = "Pkcs11"
2222
key_info_manager = "on-disk-manager"
2323
library_path = "/usr/local/lib/softhsm/libsofthsm2.so"
2424
user_pin = "123456"
25+
software_public_operations = false
2526
# The slot_number mandatory field is going to replace the following line with a valid number
2627
# slot_number

e2e_tests/tests/config/mod.rs

+53
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,56 @@ fn list_providers() {
7474
]
7575
);
7676
}
77+
78+
#[cfg(feature = "pkcs11-provider")]
79+
#[test]
80+
fn pkcs11_verify_software() {
81+
use sha2::{Digest, Sha256};
82+
set_config("pkcs11_software.toml");
83+
reload_service();
84+
85+
let mut client = TestClient::new();
86+
let key_name = String::from("pkcs11_verify_software");
87+
88+
let mut hasher = Sha256::new();
89+
hasher.update(b"Bob wrote this message.");
90+
let hash = hasher.finalize().to_vec();
91+
92+
client.generate_rsa_sign_key(key_name.clone()).unwrap();
93+
94+
let signature = client
95+
.sign_with_rsa_sha256(key_name.clone(), hash.clone())
96+
.unwrap();
97+
client
98+
.verify_with_rsa_sha256(key_name, hash, signature)
99+
.unwrap();
100+
}
101+
102+
#[cfg(feature = "pkcs11-provider")]
103+
#[test]
104+
fn pkcs11_encrypt_software() {
105+
set_config("pkcs11_software.toml");
106+
reload_service();
107+
108+
let mut client = TestClient::new();
109+
let key_name = String::from("pkcs11_verify_software");
110+
let plaintext_msg = [
111+
0x69, 0x3E, 0xDB, 0x1B, 0x22, 0x79, 0x03, 0xF4, 0xC0, 0xBF, 0xD6, 0x91, 0x76, 0x37, 0x84,
112+
0xA2, 0x94, 0x8E, 0x92, 0x50, 0x35, 0xC2, 0x8C, 0x5C, 0x3C, 0xCA, 0xFE, 0x18, 0xE8, 0x81,
113+
0x37, 0x78,
114+
];
115+
client
116+
.generate_rsa_encryption_keys_rsaoaep_sha1(key_name.clone())
117+
.unwrap();
118+
let ciphertext = client
119+
.asymmetric_encrypt_message_with_rsaoaep_sha1(
120+
key_name.clone(),
121+
plaintext_msg.to_vec(),
122+
vec![],
123+
)
124+
.unwrap();
125+
let plaintext = client
126+
.asymmetric_decrypt_message_with_rsaoaep_sha1(key_name, ciphertext, vec![])
127+
.unwrap();
128+
assert_eq!(&plaintext_msg[..], &plaintext[..]);
129+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[core_settings]
2+
# The CI already timestamps the logs
3+
log_timestamp = false
4+
log_error_details = true
5+
6+
# The container runs the Parsec service as root, so make sure we disable root
7+
# checks.
8+
allow_root = true
9+
10+
[listener]
11+
listener_type = "DomainSocket"
12+
# The timeout needs to be smaller than the test client timeout (five seconds) as it is testing
13+
# that the service does not hang for very big values of body or authentication length.
14+
timeout = 3000 # in milliseconds
15+
16+
[[key_manager]]
17+
name = "on-disk-manager"
18+
manager_type = "OnDisk"
19+
20+
[[provider]]
21+
provider_type = "Pkcs11"
22+
key_info_manager = "on-disk-manager"
23+
library_path = "/usr/local/lib/softhsm/libsofthsm2.so"
24+
user_pin = "123456"
25+
software_public_operations = true
26+
# The slot_number mandatory field is going to replace the following line with a valid number
27+
# slot_number

e2e_tests/tests/per_provider/normal_tests/asym_encryption.rs

-4
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,6 @@ fn simple_asym_encrypt_rsa_oaep_pkcs11() {
8585
let key_name = String::from("simple_asym_encrypt_rsa_oaep_pkcs11");
8686
let mut client = TestClient::new();
8787

88-
if !client.is_operation_supported(Opcode::PsaAsymmetricEncrypt) {
89-
return;
90-
}
91-
9288
client
9389
.generate_rsa_encryption_keys_rsaoaep_sha1(key_name.clone())
9490
.unwrap();

src/providers/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub enum ProviderConfig {
4747
slot_number: usize,
4848
/// User Pin
4949
user_pin: Option<String>,
50+
/// Control whether public key operations are performed in software
51+
software_public_operations: Option<bool>,
5052
},
5153
/// TPM provider configuration
5254
Tpm {

src/providers/pkcs11_provider/asym_encryption.rs

+42-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::key_info_managers::KeyTriple;
77
use log::{info, trace};
88
use parsec_interface::operations::psa_algorithm::Algorithm;
99
use parsec_interface::operations::{psa_asymmetric_decrypt, psa_asymmetric_encrypt};
10-
use parsec_interface::requests::{ProviderID, Result};
10+
use parsec_interface::requests::{ProviderID, ResponseStatus, Result};
1111
use std::convert::TryFrom;
1212
use utils::CkMechanism;
1313

@@ -108,4 +108,45 @@ impl Pkcs11Provider {
108108
}
109109
}
110110
}
111+
112+
pub(super) fn software_psa_asymmetric_encrypt_internal(
113+
&self,
114+
app_name: ApplicationName,
115+
op: psa_asymmetric_encrypt::Operation,
116+
) -> Result<psa_asymmetric_encrypt::Result> {
117+
let key_triple = KeyTriple::new(app_name, ProviderID::Pkcs11, op.key_name.clone());
118+
let (_, key_attributes) = self.get_key_info(&key_triple)?;
119+
120+
op.validate(key_attributes)?;
121+
122+
let alg = op.alg;
123+
let salt_buff = op.salt.as_ref().map(|salt| salt.as_slice());
124+
let buffer_size = key_attributes.asymmetric_encrypt_output_size(alg)?;
125+
let mut ciphertext = vec![0u8; buffer_size];
126+
let pub_key_id = self.move_pub_key_to_psa_crypto(&key_triple)?;
127+
128+
info!("Encrypting plaintext with PSA Crypto");
129+
let res = match psa_crypto::operations::asym_encryption::encrypt(
130+
pub_key_id,
131+
alg,
132+
&op.plaintext,
133+
salt_buff,
134+
&mut ciphertext,
135+
) {
136+
Ok(output_size) => {
137+
ciphertext.resize(output_size, 0);
138+
Ok(psa_asymmetric_encrypt::Result {
139+
ciphertext: ciphertext.into(),
140+
})
141+
}
142+
Err(error) => {
143+
let error = ResponseStatus::from(error);
144+
format_error!("Asymmetric encryption failed", error);
145+
Err(error)
146+
}
147+
};
148+
149+
let _ = self.remove_psa_crypto_pub_key(pub_key_id);
150+
res
151+
}
111152
}

src/providers/pkcs11_provider/asym_sign.rs

+32-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::key_info_managers::KeyTriple;
77
use log::{info, trace};
88
use parsec_interface::operations::psa_algorithm::Algorithm;
99
use parsec_interface::operations::{psa_sign_hash, psa_verify_hash};
10-
use parsec_interface::requests::{ProviderID, Result};
10+
use parsec_interface::requests::{ProviderID, ResponseStatus, Result};
1111
use std::convert::TryFrom;
1212
use utils::CkMechanism;
1313

@@ -104,4 +104,35 @@ impl Pkcs11Provider {
104104
}
105105
}
106106
}
107+
108+
pub(super) fn software_psa_verify_hash_internal(
109+
&self,
110+
app_name: ApplicationName,
111+
op: psa_verify_hash::Operation,
112+
) -> Result<psa_verify_hash::Result> {
113+
let key_triple = KeyTriple::new(app_name, ProviderID::Pkcs11, op.key_name.clone());
114+
let (_, key_attributes) = self.get_key_info(&key_triple)?;
115+
116+
op.validate(key_attributes)?;
117+
118+
let pub_key_id = self.move_pub_key_to_psa_crypto(&key_triple)?;
119+
120+
info!("Verifying signature with PSA Crypto");
121+
let res = match psa_crypto::operations::asym_signature::verify_hash(
122+
pub_key_id,
123+
op.alg,
124+
&op.hash,
125+
&op.signature,
126+
) {
127+
Ok(()) => Ok(psa_verify_hash::Result {}),
128+
Err(error) => {
129+
let error = ResponseStatus::from(error);
130+
format_error!("Verify hash failed", error);
131+
Err(error)
132+
}
133+
};
134+
135+
let _ = self.remove_psa_crypto_pub_key(pub_key_id);
136+
res
137+
}
107138
}

src/providers/pkcs11_provider/key_management.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::{utils, KeyPairType, Pkcs11Provider, ReadWriteSession, Session};
44
use crate::authenticators::ApplicationName;
55
use crate::key_info_managers::KeyTriple;
66
use log::{error, info, trace};
7-
use parsec_interface::operations::psa_key_attributes::Type;
7+
use parsec_interface::operations::psa_key_attributes::{Id, Lifetime, Type};
88
use parsec_interface::operations::{
99
psa_destroy_key, psa_export_public_key, psa_generate_key, psa_import_key,
1010
};
@@ -62,6 +62,42 @@ impl Pkcs11Provider {
6262
}
6363
}
6464
}
65+
66+
pub(super) fn move_pub_key_to_psa_crypto(&self, key_triple: &KeyTriple) -> Result<Id> {
67+
info!("Attempting to export public key");
68+
let export_operation = psa_export_public_key::Operation {
69+
key_name: key_triple.key_name().to_owned(),
70+
};
71+
let psa_export_public_key::Result { data } =
72+
self.psa_export_public_key_internal(key_triple.app_name().clone(), export_operation)?;
73+
74+
info!("Importing public key into PSA Crypto");
75+
let (_, mut attributes) = self.get_key_info(key_triple)?;
76+
attributes.lifetime = Lifetime::Volatile;
77+
attributes.key_type = match attributes.key_type {
78+
Type::RsaKeyPair | Type::RsaPublicKey => Type::RsaPublicKey,
79+
Type::EccKeyPair { curve_family } | Type::EccPublicKey { curve_family } => {
80+
Type::EccPublicKey { curve_family }
81+
}
82+
Type::DhKeyPair { group_family } | Type::DhPublicKey { group_family } => {
83+
Type::DhPublicKey { group_family }
84+
}
85+
_ => return Err(ResponseStatus::PsaErrorInvalidArgument),
86+
};
87+
let id = psa_crypto::operations::key_management::import(attributes, None, &data)?;
88+
89+
Ok(id)
90+
}
91+
92+
pub(super) fn remove_psa_crypto_pub_key(&self, pub_key_id: Id) -> Result<()> {
93+
info!("Removing public key stored in PSA.");
94+
unsafe { psa_crypto::operations::key_management::destroy(pub_key_id) }.map_err(|e| {
95+
error!("Failed to remove public key from PSA Crypto.");
96+
e
97+
})?;
98+
Ok(())
99+
}
100+
65101
pub(super) fn psa_generate_key_internal(
66102
&self,
67103
app_name: ApplicationName,

0 commit comments

Comments
 (0)