in pkcs11/pkcs11/src/context.rs [65:259]
fn load_inner(lib_path: &std::path::Path) -> Result<Self, LoadContextError> {
unsafe {
let mut library =
crate::dl::Library::load(lib_path).map_err(LoadContextError::LoadLibrary)?;
let C_GetFunctionList: pkcs11_sys::CK_C_GetFunctionList = *library
.symbol(std::ffi::CStr::from_bytes_with_nul(b"C_GetFunctionList\0").unwrap())
.map_err(LoadContextError::LoadGetFunctionListSymbol)?;
let mut function_list = std::ptr::null();
let result = C_GetFunctionList(&mut function_list);
if result != pkcs11_sys::CKR_OK {
return Err(LoadContextError::GetFunctionListFailed(
format!("C_GetFunctionList failed with {result}").into(),
));
}
if function_list.is_null() {
return Err(LoadContextError::GetFunctionListFailed(
"C_GetFunctionList succeeded but function list is still NULL".into(),
));
}
let version = {
// We don't know if `*function_list` is actually a valid `CK_FUNCTION_LIST` yet,
// because we need to look at its `.version` first.
//
// Miri considers it UB to read the `.version` from a `CK_FUNCTION_LIST` pointer directly
// in case the pointee is smaller than a `CK_FUNCTION_LIST`, even if the pointee still has
// `version` as its first field. Miri considers it safe if we cast the pointer to
// the `version` field's type first and dereference just that.
//
// Note that C requires there to be no padding before the first field of a struct,
// so this cast is valid in that regard.
let version = function_list.cast::<pkcs11_sys::CK_VERSION>();
let version = *version;
if version.major != 2 || version.minor < 1 {
// We require 2.20 or higher. However opensc-pkcs11spy self-reports as v2.11 in the initial `CK_FUNCTION_LIST` version,
// and at least one smartcard vendor's library self-reports as v2.01 in the initial `CK_FUNCTION_LIST` version.
// Both of these report the real version in the `C_GetInfo` call (in opensc-pkcs11spy's case, it forwards `C_GetInfo` to
// the underlying PKCS#11 library), so we check the result of that later.
//
// Here the check here is a more lax v2.01 check, just to be sure that the pointer we have
// is to a `CK_FUNCTION_LIST` with the same structure that we expect. All PKCS#11 2.x versions
// have the same `CK_FUNCTION_LIST` structure with the same fields, so even if the final version from `C_GetInfo`
// turns out to be lower than 2.20, the `function_list` pointer is valid.
return Err(LoadContextError::UnsupportedPkcs11Version {
expected: pkcs11_sys::CK_VERSION { major: 2, minor: 1 },
actual: version,
});
}
version
};
let C_CloseSession = (*function_list)
.C_CloseSession
.ok_or(LoadContextError::MissingFunction("C_CloseSession"))?;
let C_CreateObject = (*function_list)
.C_CreateObject
.ok_or(LoadContextError::MissingFunction("C_CreateObject"))?;
let C_Decrypt = (*function_list)
.C_Decrypt
.ok_or(LoadContextError::MissingFunction("C_Decrypt"))?;
let C_DecryptInit = (*function_list)
.C_DecryptInit
.ok_or(LoadContextError::MissingFunction("C_DecryptInit"))?;
let C_DestroyObject = (*function_list)
.C_DestroyObject
.ok_or(LoadContextError::MissingFunction("C_DestroyObject"))?;
let C_Encrypt = (*function_list)
.C_Encrypt
.ok_or(LoadContextError::MissingFunction("C_Encrypt"))?;
let C_EncryptInit = (*function_list)
.C_EncryptInit
.ok_or(LoadContextError::MissingFunction("C_EncryptInit"))?;
let C_Finalize = (*function_list).C_Finalize;
let C_FindObjects = (*function_list)
.C_FindObjects
.ok_or(LoadContextError::MissingFunction("C_FindObjects"))?;
let C_FindObjectsFinal = (*function_list)
.C_FindObjectsFinal
.ok_or(LoadContextError::MissingFunction("C_FindObjectsFinal"))?;
let C_FindObjectsInit = (*function_list)
.C_FindObjectsInit
.ok_or(LoadContextError::MissingFunction("C_FindObjectsInit"))?;
let C_GenerateKey = (*function_list)
.C_GenerateKey
.ok_or(LoadContextError::MissingFunction("C_GenerateKey"))?;
let C_GenerateKeyPair = (*function_list)
.C_GenerateKeyPair
.ok_or(LoadContextError::MissingFunction("C_GenerateKeyPair"))?;
let C_GetAttributeValue = (*function_list)
.C_GetAttributeValue
.ok_or(LoadContextError::MissingFunction("C_GetAttributeValue"))?;
let C_SetAttributeValue = (*function_list)
.C_SetAttributeValue
.ok_or(LoadContextError::MissingFunction("C_SetAttributeValue"))?;
let C_GetInfo = (*function_list).C_GetInfo;
let C_GetSessionInfo = (*function_list)
.C_GetSessionInfo
.ok_or(LoadContextError::MissingFunction("C_GetSessionInfo"))?;
let C_GetSlotList = (*function_list)
.C_GetSlotList
.ok_or(LoadContextError::MissingFunction("C_GetSlotList"))?;
let C_GetTokenInfo = (*function_list)
.C_GetTokenInfo
.ok_or(LoadContextError::MissingFunction("C_GetTokenInfo"))?;
let C_Login = (*function_list)
.C_Login
.ok_or(LoadContextError::MissingFunction("C_Login"))?;
let C_OpenSession = (*function_list)
.C_OpenSession
.ok_or(LoadContextError::MissingFunction("C_OpenSession"))?;
let C_Sign = (*function_list)
.C_Sign
.ok_or(LoadContextError::MissingFunction("C_Sign"))?;
let C_SignInit = (*function_list)
.C_SignInit
.ok_or(LoadContextError::MissingFunction("C_SignInit"))?;
let C_Verify = (*function_list)
.C_Verify
.ok_or(LoadContextError::MissingFunction("C_Verify"))?;
let C_VerifyInit = (*function_list)
.C_VerifyInit
.ok_or(LoadContextError::MissingFunction("C_VerifyInit"))?;
let C_Initialize = (*function_list)
.C_Initialize
.ok_or(LoadContextError::MissingFunction("C_Initialize"))?;
let initialize_args = pkcs11_sys::CK_C_INITIALIZE_ARGS {
CreateMutex: create_mutex,
DestroyMutex: destroy_mutex,
LockMutex: lock_mutex,
UnlockMutex: unlock_mutex,
flags: pkcs11_sys::CKF_LIBRARY_CANT_CREATE_OS_THREADS
| pkcs11_sys::CKF_OS_LOCKING_OK,
pReserved: std::ptr::null_mut(),
};
let result = C_Initialize(&initialize_args);
if result != pkcs11_sys::CKR_OK {
return Err(LoadContextError::InitializeFailed(result));
}
// Now that `C_Initialize` has succeeded, create the `Context` value before doing anything else,
// so that its `Drop` will run `C_Finalize` in case anything else fails later.
let context = Context {
sessions: Default::default(),
_library: library,
C_CloseSession,
C_CreateObject,
C_Decrypt,
C_DecryptInit,
C_DestroyObject,
C_Encrypt,
C_EncryptInit,
C_Finalize,
C_FindObjects,
C_FindObjectsFinal,
C_FindObjectsInit,
C_GenerateKey,
C_GenerateKeyPair,
C_GetAttributeValue,
C_SetAttributeValue,
C_GetInfo,
C_GetSessionInfo,
C_GetSlotList,
C_GetTokenInfo,
C_Login,
C_OpenSession,
C_Sign,
C_SignInit,
C_Verify,
C_VerifyInit,
};
let version = context.info().map_or(
version, // Doesn't support C_GetInfo, so the initial version in the CK_FUNCTION_LIST is all we have.
|info| info.cryptokiVersion,
);
if version.major != 2 || version.minor < 20 {
return Err(LoadContextError::UnsupportedPkcs11Version {
expected: pkcs11_sys::CK_VERSION {
major: 2,
minor: 20,
},
actual: version,
});
}
Ok(context)
}
}