Expand description
A zero-copy library to parse and validate PKCS#11 URIs in accordance to RFC7512 specifications.
§Examples
Using a sample URI from the specification:
use pk11_uri_parser::{parse, PK11URIError};
fn main() -> Result<(), PK11URIError> {
let pk11_uri = "pkcs11:token=The%20Software%20PKCS%2311%20Softtoken;
manufacturer=Snake%20Oil,%20Inc.;
model=1.0;
object=my-certificate;
type=cert;
id=%69%95%3E%5C%F4%BD%EC%91;
serial=
?pin-source=file:/etc/token_pin";
let mapping = parse(pk11_uri)?;
println!("{mapping:?}");
Ok(())
}
Will effectively print:
PK11URIMapping { token: Some("The%20Software%20PKCS%2311%20Softtoken"), manufacturer: Some("Snake%20Oil,%20Inc."), serial: Some(""), model: Some("1.0"), library_manufacturer: None, library_version: None, library_description: None, object: Some("my-certificate"), type: Some("cert"), id: Some("%69%95%3E%5C%F4%BD%EC%91"), slot_description: None, slot_manufacturer: None, slot_id: None, pin_source: Some("file:/etc/token_pin"), pin_value: None, module_name: None, module_path: None, vendor: {} }
The parse Result
’s type is a PK11URIMapping. Users of the library do not need to be intimately
familiar with specification rules regarding what attributes belong to the path-component or the
query-component, or to be knowledgeable about the various vendor-specific attribute rules: the PK11URIMapping
provides appropriately named methods for retrieving standard component values and an intuitive
vendor method for retrieving vendor-specific attribute values.
let pk11_uri = "pkcs11:vendor-attribute=my_vendor_attribte?pin-source=|/usr/lib/pinomatic";
let mapping = pk11_uri_parser::parse(pk11_uri).expect("mapping should be valid");
if let Some(pin_source) = mapping.pin_source() {
// do something with `pin_source`...
}
// see whether we've got `vendor-attribute` values:
if let Some(vendor_values) = mapping.vendor("vendor-attribute") {
// do something with `vendor_values`...
}
It’s worth reiterating that vendor-specific attributes may have multiple values so therefore the vendor
method’s Option
return type is &Vec<&'a str>
.
§Errors
At least initially, PKCS#11 URIs will likely be derived from invoking exploratory commands in tools such as
p11tool
or pkcs11-tool
. While parsing URIs from these tools is pretty much guaranteed to be successful,
it’s often not necessary to provide such verbose values in order to properly identify your targeted resource.
It’s also generally beyond the scope of those tools to include query-components (such as pin-value
or pin-source
).
In the interest of making your life a little bit easier (and code more readable), a bit of exploration can result
in a considerably shorter (and potentially more portable) URI.
Let’s say for example you are in need of utilizing an HSM-bound private key (and read “somewhere on the internet”):
// note: this isn't a valid pkcs11 uri
let pk11_uri = "pkcs11:object=Private key for Card Authentication;pin-value=123456";
#[cfg(feature = "validation")]
println!("{err:?}", err=pk11_uri_parser::parse(pk11_uri).expect_err("empty spaces in value violation"));
Attempting to parse that uri will result in a PK11URIError.
PK11URIError { pk11_uri: "pkcs11:object=Private key for Card Authentication;pin-value=123456", error_span: (7, 49), violation: "Invalid component value: Appendix A of [RFC3986] specifies component values may not contain empty spaces.", help: "Replace `Private key for Card Authentication` with `Private%20key%20for%20Card%20Authentication`." }
Or if you’d prefer a fancier output, simply display the PK11URIError (not using :?
debug):
// note: this isn't a valid pkcs11 uri
let pk11_uri = "pkcs11:object=Private key for Card Authentication;pin-value=123456";
#[cfg(feature = "validation")]
println!("{err}", err=pk11_uri_parser::parse(pk11_uri).expect_err("empty spaces in value violation"))
pkcs11:object=Private key for Card Authentication;pin-value=123456
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid component value: Appendix A of [RFC3986] specifies component values may not contain empty spaces.
help: Replace `Private key for Card Authentication` with `Private%20key%20for%20Card%20Authentication`.
Great! Based on the “help” text, it’s a simple fix:
// note: again, this isn't a valid pkcs11 uri
let pk11_uri = "pkcs11:object=Private%20key%20for%20Card%20Authentication;pin-value=123456";
#[cfg(feature = "validation")]
println!("{err}", err=pk11_uri_parser::parse(pk11_uri).expect_err("query component naming collision violation"));
This will once again fail to parse and brings up the fact that this library will fail-quickly (ie, short-circuit further parsing) if any violation is found.
pkcs11:object=Private%20key%20for%20Card%20Authentication;pin-value=123456
^^^^^^^^^^^^^^^^ Naming collision with standard query component.
help: Move `pin-value` and its value to the PKCS#11 URI query.
In this case, pin-value
is a standard query-component attribute name so its current location as a path attribute is a violation.
The “help” section again offers a simple solution.
let pk11_uri = "pkcs11:object=Private%20key%20for%20Card%20Authenciation?pin-value=123456";
pk11_uri_parser::parse(pk11_uri).expect("mapping should be valid");
Which finally yields a valid mapping.
§Warnings
The RFC7512 specification uses terminology such as SHOULD
and SHOULD NOT
to indicate optional,
best-practice type treatment for attribute values. This library embraces these optional rules, but will only
emit warning messages to the terminal and only provide such warnings for non-optimized builds. Likewise,
violations of such optional rules will never result in a PK11URIError. The messages printed to the terminal
begin with pkcs11 warning:
.
Assuming a debug build:
let pk11_uri = "pkcs11:x-muppet=cookie<^^>monster!";
let mapping = pk11_uri_parser::parse(pk11_uri).expect("mapping should be valid");
let x_muppet = mapping.vendor("x-muppet").expect("valid x-muppet vendor-attribute");
println!("x-muppet: {:?}", x_muppet);
prints
pkcs11 warning: per RFC7512, the previously used convention of starting vendor attributes with an "x-" prefix is now deprecated. Identified: `x-muppet`.
pkcs11 warning: the `<` identified at offset 6 in `cookie<^^>monster!` of component `x-muppet=cookie<^^>monster!` SHOULD be percent-encoded.
pkcs11 warning: the `^` identified at offset 7 in `cookie<^^>monster!` of component `x-muppet=cookie<^^>monster!` SHOULD be percent-encoded.
pkcs11 warning: the `^` identified at offset 8 in `cookie<^^>monster!` of component `x-muppet=cookie<^^>monster!` SHOULD be percent-encoded.
pkcs11 warning: the `>` identified at offset 9 in `cookie<^^>monster!` of component `x-muppet=cookie<^^>monster!` SHOULD be percent-encoded.
x-muppet: ["cookie<^^>monster!"]
Any warning related code is explicitly not included in --release
builds.
§Crate feature flags
As alluded to above, the crate’s default feature set is to always perform validation and for
debug builds, emit pkcs11 warning:
messages when values do not comply with RFC7512 “SHOULD/
SHOULD NOT” guidelines.
“But sir, I implore you, I’ve thoroughly tested my input!”
I hear you barking, big dog! It’s perfectly reasonable to not want validation (and/or warnings). You
can eliminate that slight bit of runtime overhead by utilizing the default-features=false
treatment
on your dependency:
[dependencies]
pk11-uri-parser = {version = "0.1.4", default-features = false}
It’s important to note, however, that doing so will introduce expect("my expectation")
calls to perform
unwrap functionality required in the parsing.
Structs§
- PK11URI
Error - Issued when parsing a PKCS#11 URI is found to be in violation of RFC7512 specifications.
- PK11URI
Mapping - Encapsulates the result of successfully parsing a PKCS#11 URI.
Functions§
- parse
- Parses and verifies the contents of the given
pk11_uri
&str, making parsed values available through a PK11URIMapping. Violations to RFC7512 specifications will result in issuing a PK11URIError.