Skip to content

Run CI against NSS softokn #269

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

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
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
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,32 @@ jobs:
RUST_BACKTRACE=1 cargo test


tests-softokn:
name: Run tests against Softokn
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install dependencies
run: sudo apt-get -yq install libnss3 libnss3-tools libnss3-dev
- name: Test script
env:
NSS_LIB_PARAMS: configDir=/tmp/nssdb/
TEST_PKCS11_MODULE: /usr/lib/x86_64-linux-gnu/libsoftokn3.so
TEST_TOKEN_LABEL: NSS Certificate DB
TEST_SKIP_TOKEN_INIT: 1
run: |
mkdir /tmp/nssdb/ &&
echo "fedcba" > /tmp/nssdb/pinfile &&
certutil -N -d /tmp/nssdb/ -f /tmp/nssdb/pinfile &&
RUST_BACKTRACE=1 cargo build &&
RUST_BACKTRACE=1 cargo build --all-features &&
RUST_BACKTRACE=1 cargo test


build-msrv:
name: MSRV - Execute CI script
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions cryptoki/src/session/decryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ impl Session {
.into_result(Function::DecryptFinal)?;
}

// Some pkcs11 modules might finalize the operation when there
// no more output even if we pass in NULL.
if data_len == 0 {
return Ok(Vec::new());
}

let mut data = vec![0; data_len.try_into()?];

unsafe {
Expand Down
6 changes: 6 additions & 0 deletions cryptoki/src/session/encryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ impl Session {
.into_result(Function::EncryptFinal)?;
}

// Some pkcs11 modules might finalize the operation when there
// no more output even if we pass in NULL.
if encrypted_data_len == 0 {
return Ok(Vec::new());
}

let mut encrypted_data = vec![0; encrypted_data_len.try_into()?];

unsafe {
Expand Down
87 changes: 61 additions & 26 deletions cryptoki/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
mod common;

use crate::common::{get_pkcs11, is_softhsm, SO_PIN, USER_PIN};
use crate::common::{get_pkcs11, is_softhsm, is_softokn, SO_PIN, USER_PIN};
use common::init_pins;
use cryptoki::context::Function;
use cryptoki::error::{Error, RvError};
Expand Down Expand Up @@ -1086,7 +1086,11 @@ fn get_session_info_test() -> TestResult {
if let Err(cryptoki::error::Error::Pkcs11(rv_error, _)) =
session.login(UserType::So, Some(&AuthPin::new(SO_PIN.into())))
{
assert_eq!(rv_error, RvError::SessionReadOnlyExists)
if is_softokn() {
assert_eq!(rv_error, RvError::UserTypeInvalid)
} else {
assert_eq!(rv_error, RvError::SessionReadOnlyExists)
}
} else {
panic!("Should error when attempting to log in as CKU_SO on a read-only session");
}
Expand All @@ -1107,14 +1111,16 @@ fn get_session_info_test() -> TestResult {
assert_eq!(session_info.slot_id(), slot);
assert!(matches!(session_info.session_state(), SessionState::RwUser,));
session.logout()?;
session.login(UserType::So, Some(&AuthPin::new(SO_PIN.into())))?;
let session_info = session.get_session_info()?;
assert!(session_info.read_write());
assert_eq!(session_info.slot_id(), slot);
assert!(matches!(
session_info.session_state(),
SessionState::RwSecurityOfficer
));
if !is_softokn() {
session.login(UserType::So, Some(&AuthPin::new(SO_PIN.into())))?;
let session_info = session.get_session_info()?;
assert!(session_info.read_write());
assert_eq!(session_info.slot_id(), slot);
assert!(matches!(
session_info.session_state(),
SessionState::RwSecurityOfficer
));
}

Ok(())
}
Expand Down Expand Up @@ -1157,6 +1163,9 @@ fn set_pin_test() -> TestResult {
session.logout()?;
session.login(UserType::User, Some(&new_user_pin))?;

// return it back
session.set_pin(&new_user_pin, &user_pin)?;
session.logout()?;
Ok(())
}

Expand Down Expand Up @@ -1197,7 +1206,7 @@ fn get_attribute_info_test() -> TestResult {
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;

let pub_attribs = vec![AttributeType::PublicExponent, AttributeType::Modulus];
let mut priv_attribs = [
let priv_attribs = [
AttributeType::PublicExponent,
AttributeType::Modulus,
AttributeType::PrivateExponent,
Expand Down Expand Up @@ -1492,10 +1501,15 @@ fn session_copy_object() -> TestResult {
let copy = rw_session.copy_object(object, &copy_template)?;
rw_session.destroy_object(copy)?;

// try the copy with the insecure template. It should fail. Returning CKR_OK is considered a failure.
rw_session
.copy_object(object, &insecure_copy_template)
.unwrap_err();
let res = rw_session.copy_object(object, &insecure_copy_template);
if is_softokn() {
// Softokn considers all keys nonextractable
let copy = res?;
rw_session.destroy_object(copy)?;
} else {
// try the copy with the insecure template. It should fail. Returning CKR_OK is considered a failure.
res.unwrap_err();
}

// delete keys
rw_session.destroy_object(object)?;
Expand Down Expand Up @@ -1979,7 +1993,9 @@ fn rsa_pkcs_oaep_empty() -> TestResult {
let session = pkcs11.open_rw_session(slot)?;
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;

let public_exponent: Vec<u8> = vec![0x01, 0x00, 0x01];
let pub_key_template = [
Attribute::PublicExponent(public_exponent),
Attribute::ModulusBits(2048.into()),
Attribute::Encrypt(true),
];
Expand Down Expand Up @@ -2018,7 +2034,9 @@ fn rsa_pkcs_oaep_with_data() -> TestResult {
let session = pkcs11.open_rw_session(slot)?;
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;

let public_exponent: Vec<u8> = vec![0x01, 0x00, 0x01];
let pub_key_template = [
Attribute::PublicExponent(public_exponent),
Attribute::ModulusBits(2048.into()),
Attribute::Encrypt(true),
];
Expand Down Expand Up @@ -2094,14 +2112,17 @@ fn generate_generic_secret_key() -> TestResult {
Attribute::Token(true),
Attribute::Sensitive(true),
Attribute::Private(true),
Attribute::ValueLen(512.into()),
Attribute::ValueLen(256.into()),
key_label.clone(),
];

let key = session.generate_key(&Mechanism::GenericSecretKeyGen, &key_template)?;
let attributes_result = session.find_objects(&[key_label])?.remove(0);
assert_eq!(key, attributes_result);

// Delete keys
session.destroy_object(key)?;

Ok(())
}

Expand Down Expand Up @@ -2158,6 +2179,9 @@ fn ekdf_aes_cbc_encrypt_data() -> TestResult {
session.find_objects(&[derived_key_label])?.remove(0)
);

// delete keys
session.destroy_object(master_key)?;
session.destroy_object(derived_key)?;
Ok(())
}

Expand Down Expand Up @@ -2502,6 +2526,8 @@ fn kbkdf_additional_keys_counter_mode() -> TestResult {
Attribute::ValueLen((AES128_BLOCK_SIZE as u64).into()),
Attribute::Sign(true),
Attribute::Verify(true),
Attribute::Encrypt(false),
Attribute::Decrypt(false),
],
vec![
Attribute::Token(true),
Expand All @@ -2510,6 +2536,8 @@ fn kbkdf_additional_keys_counter_mode() -> TestResult {
Attribute::KeyType(KeyType::GENERIC_SECRET),
Attribute::ValueLen(1.into()),
Attribute::Derive(true),
Attribute::Encrypt(false),
Attribute::Decrypt(false),
],
];

Expand Down Expand Up @@ -2586,6 +2614,8 @@ fn kbkdf_additional_keys_counter_mode() -> TestResult {
Attribute::Sign(true),
Attribute::Verify(true),
Attribute::Derive(false),
Attribute::Encrypt(false),
Attribute::Decrypt(false),
],
vec![
Attribute::Class(ObjectClass::SECRET_KEY),
Expand All @@ -2596,14 +2626,19 @@ fn kbkdf_additional_keys_counter_mode() -> TestResult {
Attribute::Sign(false),
Attribute::Verify(false),
Attribute::Derive(true),
Attribute::Encrypt(false),
Attribute::Decrypt(false),
],
];

for (key, wanted_attributes) in derived_keys.iter().zip(wanted_attributes.iter().cycle()) {
let have_attributes = session.get_attributes(*key, &attributes_to_check)?;

for (value_wanted, value_have) in wanted_attributes.iter().zip(have_attributes.iter()) {
assert_eq!(value_wanted, value_have);
assert_eq!(
value_wanted, value_have,
"The generated key {key} has unexpected attribute value"
);
}
}

Expand Down Expand Up @@ -2658,6 +2693,8 @@ fn kbkdf_additional_keys_feedback_mode() -> TestResult {
Attribute::ValueLen((AES128_BLOCK_SIZE as u64).into()),
Attribute::Sign(true),
Attribute::Verify(true),
Attribute::Encrypt(false),
Attribute::Decrypt(false),
],
vec![
Attribute::Token(true),
Expand All @@ -2666,6 +2703,8 @@ fn kbkdf_additional_keys_feedback_mode() -> TestResult {
Attribute::KeyType(KeyType::GENERIC_SECRET),
Attribute::ValueLen(1.into()),
Attribute::Derive(true),
Attribute::Encrypt(false),
Attribute::Decrypt(false),
],
];

Expand Down Expand Up @@ -3697,13 +3736,7 @@ fn unique_id() -> TestResult {
];
let res = session.create_object(&key_template);
assert!(res.is_err());
assert!(matches!(
res,
Err(Error::Pkcs11(
RvError::AttributeTypeInvalid,
Function::CreateObject
))
));
assert!(matches!(res, Err(Error::Pkcs11(_, Function::CreateObject))));

let generate_template = vec![
Attribute::Token(true),
Expand All @@ -3716,8 +3749,9 @@ fn unique_id() -> TestResult {

// we can get the UniqueId attribute
let attrs = session.get_attributes(key, &[AttributeType::UniqueId])?;
if is_softhsm() {
if is_softhsm() || is_softokn() {
// SoftHSM does not support this attribute at all
// Softkn does not expose this attribute
assert_eq!(attrs.len(), 0);
} else {
assert!(matches!(attrs.first(), Some(Attribute::UniqueId(_))));
Expand All @@ -3727,8 +3761,9 @@ fn unique_id() -> TestResult {
let update_template = vec![Attribute::UniqueId(vec![0x01, 0x02, 0x03])];
let res = session.update_attributes(key, &update_template);
assert!(res.is_err());
if is_softhsm() {
if is_softhsm() || is_softokn() {
// SoftHSM does not support this attribute at all
// Softtokn supports it internally, but does not expose it
assert!(matches!(
res,
Err(Error::Pkcs11(
Expand Down
54 changes: 43 additions & 11 deletions cryptoki/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ pub static USER_PIN: &str = "fedcba";
// The default SO pin
pub static SO_PIN: &str = "abcdef";

fn get_token_label() -> Option<String> {
env::var("TEST_TOKEN_LABEL").ok()
}

fn skip_token_init() -> bool {
match env::var("TEST_SKIP_TOKEN_INIT") {
Ok(s) => s == "1",
Err(_) => false,
}
}

fn get_pkcs11_path() -> String {
env::var("TEST_PKCS11_MODULE")
.unwrap_or_else(|_| "/usr/local/lib/softhsm/libsofthsm2.so".to_string())
Expand All @@ -20,28 +31,49 @@ pub fn is_softhsm() -> bool {
get_pkcs11_path().contains("softhsm")
}

pub fn is_softokn() -> bool {
get_pkcs11_path().contains("softokn")
}

pub fn get_pkcs11() -> Pkcs11 {
Pkcs11::new(get_pkcs11_path()).unwrap()
}

fn get_slot(pkcs11: &Pkcs11) -> Slot {
// find a slot, get the first one or one with name specified in the environment variable
let mut slots = pkcs11.get_slots_with_token().unwrap();
match get_token_label() {
None => slots.remove(0),
Some(label) => {
for s in slots {
let ti = pkcs11.get_token_info(s).unwrap();
if ti.label() == label {
return s;
}
}
panic!("No token with Token Label `{label}` found");
}
}
}

pub fn init_pins() -> (Pkcs11, Slot) {
let pkcs11 = get_pkcs11();

// initialize the library
pkcs11.initialize(CInitializeArgs::OsThreads).unwrap();

// find a slot, get the first one
let slot = pkcs11.get_slots_with_token().unwrap().remove(0);

let so_pin = AuthPin::new(SO_PIN.into());
pkcs11.init_token(slot, &so_pin, "Test Token").unwrap();
let slot = get_slot(&pkcs11);

{
// open a session
let session = pkcs11.open_rw_session(slot).unwrap();
// log in the session
session.login(UserType::So, Some(&so_pin)).unwrap();
session.init_pin(&AuthPin::new(USER_PIN.into())).unwrap();
if !skip_token_init() {
let so_pin = AuthPin::new(SO_PIN.into());
let _ = pkcs11.init_token(slot, &so_pin, "Test Token");
{
// open a session
let session = pkcs11.open_rw_session(slot).unwrap();
// log in the session
session.login(UserType::So, Some(&so_pin)).unwrap();
session.init_pin(&AuthPin::new(USER_PIN.into())).unwrap();
}
}

(pkcs11, slot)
Expand Down
Loading