Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recognise a PKCS11 hardware token with its serial number instead of slot number #608

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
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ This file aims to acknowledge the specific contributors referred to in the "Cont
* Nicolas Stalder (@nickray)
* Edmund Grimley Evans (@egrimley-arm)
* Matt Davis (@MattDavis00)
* Mohamed Omar Asaker (@mohamedasaker-arm)
3 changes: 3 additions & 0 deletions ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ cleanup () {
if [ -n "$TPM_MC_SRV_PID" ]; then kill $TPM_MC_SRV_PID || true; fi
# Remove the slot_number line added earlier
find e2e_tests -name "*toml" -not -name "Cargo.toml" -exec sed -i 's/^slot_number =.*/# slot_number/' {} \;
find e2e_tests -name "*toml" -not -name "Cargo.toml" -exec sed -i 's/^serial_number =.*/# serial_number/' {} \;
# Remove fake mapping and temp files
rm -rf "mappings"
rm -f "NVChip"
Expand Down Expand Up @@ -208,8 +209,10 @@ if [ "$PROVIDER_NAME" = "pkcs11" ] || [ "$PROVIDER_NAME" = "all" ] || [ "$PROVID
# This command suppose that the slot created by the container will be the first one that appears
# when printing all the available slots.
SLOT_NUMBER=`softhsm2-util --show-slots | head -n2 | tail -n1 | cut -d " " -f 2`
SERIAL_NUMBER=`softhsm2-util --show-slots | grep "Serial number:*" | head -n1 | egrep -ow "[0-9a-zA-Z]+" | tail -n1`
# Find all TOML files in the directory (except Cargo.toml) and replace the commented slot number with the valid one
find . -name "*toml" -not -name "Cargo.toml" -exec sed -i "s/^# slot_number.*$/slot_number = $SLOT_NUMBER/" {} \;
find . -name "*toml" -not -name "Cargo.toml" -exec sed -i "s/^# serial_number.*$/serial_number = \"$SERIAL_NUMBER\"/" {} \;
popd
fi

Expand Down
5 changes: 4 additions & 1 deletion config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ key_info_manager = "sqlite-manager"
# (Required for this provider) Path to the location of the dynamic library loaded by this provider.
# For the PKCS 11 provider, this library implements the PKCS 11 API on the target platform.
#library_path = "/usr/local/lib/softhsm/libsofthsm2.so"
# (Optional) PKCS 11 slot that will be used by Parsec.
# (Optional) PKCS 11 serial number of the token that will be used by Parsec.
# If the token serial number is entered, then the slot that has the provided serial number will be used. Otherwise, if both `serial_number` and `slot_number` are given but do not match, a warning is issued and serial number takes precedence.
#serial_number = "0123456789abcdef"
# (Optional) PKCS 11 slot that will be used by Parsec If Token serial number is not entered. i.e, serial_number is preferred
# If the slot number is not entered and there is only one slot available - with a valid token - it will be automatically used
#slot_number = 123456789
# (Optional) User pin for authentication with the specific slot. If not set, the sessions will not
Expand Down
4 changes: 3 additions & 1 deletion e2e_tests/provider_cfg/pkcs11/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,7 @@ key_info_manager = "on-disk-manager"
library_path = "/usr/local/lib/softhsm/libsofthsm2.so"
user_pin = "123456"
software_public_operations = false
# The slot_number mandatory field is going to replace the following line with a valid number
# The slot_number optional field is going to replace the following line with a valid number
# slot_number
# The token serial_number optional field is going to replace the following line with a valid number
# serial_number
37 changes: 34 additions & 3 deletions e2e_tests/tests/all_providers/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,40 @@ fn no_user_pin() {
}

#[test]
fn no_slot_number() {
set_config("no_slot_number.toml");
// The service should still start, without the slot number.
fn no_serial_or_slot_number() {
set_config("no_serial_or_slot_number.toml");
// The service should still start, without the serial number or the slot number.
reload_service();

let mut client = TestClient::new();
let _ = client.ping().unwrap();
}

#[test]
fn slot_number_only() {
set_config("slot_number_only.toml");
// The service should still start, using the slot number only.
reload_service();

let mut client = TestClient::new();
let _ = client.ping().unwrap();
}

#[test]
fn serial_number_only() {
set_config("serial_number_only.toml");
// The service should still start, using the serial number only.
reload_service();

let mut client = TestClient::new();
let _ = client.ping().unwrap();
}

#[test]
fn slot_numbers_mismatch() {
set_config("slot_numbers_mismatch.toml");
// The service should still start, while the slot number that has
// the token of interest doesn't match the slot number in configuration.
reload_service();

let mut client = TestClient::new();
Expand Down
31 changes: 31 additions & 0 deletions e2e_tests/tests/all_providers/config/tomls/serial_number_only.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[core_settings]
# The CI already timestamps the logs
log_timestamp = false
log_error_details = true

# The container runs the Parsec service as root, so make sure we disable root
# checks.
allow_root = true

[listener]
listener_type = "DomainSocket"
# The timeout needs to be smaller than the test client timeout (five seconds) as it is testing
# that the service does not hang for very big values of body or authentication length.
timeout = 3000 # in milliseconds
socket_path = "/tmp/parsec.sock"

[authenticator]
auth_type = "Direct"

[[key_manager]]
name = "sqlite-manager"
manager_type = "SQLite"
database_path = "./kim-mappings/sqlite/sqlite-key-info-manager.sqlite3"

[[provider]]
provider_type = "Pkcs11"
key_info_manager = "sqlite-manager"
library_path = "/usr/local/lib/softhsm/libsofthsm2.so"
user_pin = "123456"
# The serial number optional field is going to replace the following line with a valid number
# serial_number
31 changes: 31 additions & 0 deletions e2e_tests/tests/all_providers/config/tomls/slot_number_only.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[core_settings]
# The CI already timestamps the logs
log_timestamp = false
log_error_details = true

# The container runs the Parsec service as root, so make sure we disable root
# checks.
allow_root = true

[listener]
listener_type = "DomainSocket"
# The timeout needs to be smaller than the test client timeout (five seconds) as it is testing
# that the service does not hang for very big values of body or authentication length.
timeout = 3000 # in milliseconds
socket_path = "/tmp/parsec.sock"

[authenticator]
auth_type = "Direct"

[[key_manager]]
name = "sqlite-manager"
manager_type = "SQLite"
database_path = "./kim-mappings/sqlite/sqlite-key-info-manager.sqlite3"

[[provider]]
provider_type = "Pkcs11"
key_info_manager = "sqlite-manager"
library_path = "/usr/local/lib/softhsm/libsofthsm2.so"
user_pin = "123456"
# The slot_number optional field is going to replace the following line with a valid number
# slot_number
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[core_settings]
# The CI already timestamps the logs
log_timestamp = false
log_error_details = true

# The container runs the Parsec service as root, so make sure we disable root
# checks.
allow_root = true

[listener]
listener_type = "DomainSocket"
# The timeout needs to be smaller than the test client timeout (five seconds) as it is testing
# that the service does not hang for very big values of body or authentication length.
timeout = 3000 # in milliseconds
socket_path = "/tmp/parsec.sock"

[authenticator]
auth_type = "Direct"

[[key_manager]]
name = "sqlite-manager"
manager_type = "SQLite"
database_path = "./kim-mappings/sqlite/sqlite-key-info-manager.sqlite3"

[[provider]]
provider_type = "Pkcs11"
key_info_manager = "sqlite-manager"
library_path = "/usr/local/lib/softhsm/libsofthsm2.so"
user_pin = "123456"
# A dummy slot_number
slot_number=12345678
# The serial number optional field is going to replace the following line with a valid number
# serial_number
81 changes: 67 additions & 14 deletions src/providers/pkcs11/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ pub struct ProviderBuilder {
key_info_store: Option<KeyInfoManagerClient>,
pkcs11_library_path: Option<String>,
slot_number: Option<u64>,
serial_number: Option<String>,
user_pin: Option<SecretString>,
software_public_operations: Option<bool>,
allow_export: Option<bool>,
Expand All @@ -394,6 +395,7 @@ impl ProviderBuilder {
key_info_store: None,
pkcs11_library_path: None,
slot_number: None,
serial_number: None,
user_pin: None,
software_public_operations: None,
allow_export: None,
Expand Down Expand Up @@ -428,6 +430,12 @@ impl ProviderBuilder {
self
}

/// Specify the token serial number used
pub fn with_serial_number(mut self, serial_number: Option<String>) -> ProviderBuilder {
self.serial_number = serial_number;
self
}

/// Specify the user pin
pub fn with_user_pin(mut self, mut user_pin: Option<String>) -> ProviderBuilder {
self.user_pin = match user_pin {
Expand Down Expand Up @@ -479,27 +487,72 @@ impl ProviderBuilder {
Error::new(ErrorKind::InvalidData, "error initializing PKCS 11 context")
})?;

let slot_number = match self.slot_number {
Some(i) => {
let slot = Slot::try_from(i).or_else(|_| {
let slots = backend.get_slots_with_initialized_token().map_err(|e| {
format_error!(
"Failed retrieving a valid slot with an initialized token",
e
);
Error::new(
ErrorKind::InvalidData,
"Failed retrieving a valid slot with an initialized token",
)
})?;
let slot_number = match (self.serial_number, self.slot_number) {
(Some(serial_number), given_slot) => {
let mut slot = None;
for current_slot in slots {
let current_token = backend.get_token_info(current_slot).map_err(|e| {
format_error!("Failed parsing token info", e);
Error::new(ErrorKind::InvalidData, "Failed parsing token info")
})?;
let sn =
String::from_utf8(current_token.serialNumber.to_vec()).map_err(|e| {
format_error!("Failed parsing token serial number", e);
Error::new(ErrorKind::InvalidData, "Failed parsing token serial number")
})?;
if sn == serial_number {
slot = Some(current_slot);
break;
}
}
match slot {
Some(slot) => {
if let Some(slot_number) = given_slot {
if slot.id() != slot_number {
warn!("Provided slot number mismatch!");
warn!("Token is attached to slot {}", slot.id())
}
}
slot
}
None => {
return Err(Error::new(
ErrorKind::InvalidData,
"No token with the provided serial number",
))
}
}
}
(None, Some(slot_number)) => {
let slot = Slot::try_from(slot_number).or_else(|_| {
Err(Error::new(
ErrorKind::InvalidData,
"cannot convert slot value",
))
})?;
if !slots.contains(&slot) {
return Err(Error::new(
ErrorKind::InvalidInput,
"No available slot with the given number",
));
}
warn!(
"Slot number {} will be used. However, It is preferred to use serial_number as the slot number might change during replug or OS reboot.",
slot_number
);
slot
}
None => {
let slots = backend.get_slots_with_initialized_token().map_err(|e| {
format_error!(
"Failed retrieving a valid slot with an initialized token",
e
);
Error::new(
ErrorKind::InvalidData,
"failed retrieving a valid slot with an initialized token",
)
})?;
(None, None) => {
if slots.len() == 1 {
slots[0]
} else {
Expand Down
2 changes: 2 additions & 0 deletions src/utils/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ pub enum ProviderConfig {
library_path: String,
/// Slot number to use
slot_number: Option<u64>,
/// Token serial number to use
serial_number: Option<String>,
/// User Pin
user_pin: Option<String>,
/// Control whether public key operations are performed in software
Expand Down
2 changes: 2 additions & 0 deletions src/utils/service_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ unsafe fn get_provider(
ProviderConfig::Pkcs11 {
library_path,
slot_number,
serial_number,
user_pin,
software_public_operations,
allow_export,
Expand All @@ -335,6 +336,7 @@ unsafe fn get_provider(
.with_provider_name(config.provider_name()?)
.with_pkcs11_library_path(library_path.clone())
.with_slot_number(*slot_number)
.with_serial_number(serial_number.clone())
.with_user_pin(user_pin.clone())
.with_software_public_operations(*software_public_operations)
.with_allow_export(*allow_export)
Expand Down