in pkcs11/pkcs11/src/lib.rs [105:208]
fn from_str(s: &str) -> Result<Self, Self::Err> {
// Ref https://tools.ietf.org/html/rfc7512#section-2.3
//
// Only object-label, slot-id, token-label and pin-value are parsed from the URL. If both slot-id and token are provided, token is ignored.
enum PathComponentKey {
Object,
SlotId,
Token,
}
enum QueryComponentKey {
PinValue,
}
fn parse_key_value_pair<'a, F, T>(
s: &'a str,
mut key_discriminant: F,
) -> Result<Option<(T, std::borrow::Cow<'a, str>)>, ParsePkcs11UriError>
where
F: FnMut(&[u8]) -> Option<T>,
{
let (key, value) = s.split_once('=').unwrap_or((s, ""));
let key = percent_encoding::percent_decode(key.as_bytes());
let key: std::borrow::Cow<'a, _> = key.into();
let Some(typed_key) = key_discriminant(&key) else {
return Ok(None);
};
let value = percent_encoding::percent_decode(value.as_bytes());
match value.decode_utf8() {
Ok(value) => Ok(Some((typed_key, value))),
Err(err) => Err(ParsePkcs11UriError::InvalidUtf8(
key.into_owned(),
err.into(),
)),
}
}
let mut object_label = None;
let mut token_label = None;
let mut slot_id = None;
let mut pin = None;
let s = s
.strip_prefix("pkcs11:")
.ok_or(ParsePkcs11UriError::InvalidScheme)?;
let (path, query) = s.split_once('?').unwrap_or((s, ""));
let path_components = path.split(';');
for path_component in path_components {
let key_value_pair = parse_key_value_pair(path_component, |key| match key {
b"object" => Some(PathComponentKey::Object),
b"slot-id" => Some(PathComponentKey::SlotId),
b"token" => Some(PathComponentKey::Token),
_ => None,
})?;
if let Some((key, value)) = key_value_pair {
match key {
PathComponentKey::Object => {
object_label = Some(value.into_owned());
}
PathComponentKey::SlotId => {
let value = value.parse::<pkcs11_sys::CK_SLOT_ID>().map_err(|err| {
ParsePkcs11UriError::MalformedSlotId(value.into_owned(), err)
})?;
slot_id = Some(value);
}
PathComponentKey::Token => {
token_label = Some(value.into_owned());
}
}
}
}
let query_components = query.split('&');
for query_component in query_components {
let key_value_pair = parse_key_value_pair(query_component, |key| match key {
b"pin-value" => Some(QueryComponentKey::PinValue),
_ => None,
})?;
if let Some((key, value)) = key_value_pair {
match key {
QueryComponentKey::PinValue => {
pin = Some(value.into_owned());
}
}
}
}
let slot_identifier = match (token_label, slot_id) {
(_, Some(slot_id)) => UriSlotIdentifier::SlotId(slot_id),
(Some(token_label), _) => UriSlotIdentifier::Label(token_label),
(None, None) => return Err(ParsePkcs11UriError::NeitherSlotIdNorTokenSpecified),
};
Ok(Uri {
slot_identifier,
object_label,
pin,
})
}