Crate pk11_uri_parser

Source
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§

PK11URIError
Issued when parsing a PKCS#11 URI is found to be in violation of RFC7512 specifications.
PK11URIMapping
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.