Skip to content
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

Investigate PKCS#11 front-end compatibility layer for Parsec #291

Open
paulhowardarm opened this issue Nov 27, 2020 · 9 comments
Open

Investigate PKCS#11 front-end compatibility layer for Parsec #291

paulhowardarm opened this issue Nov 27, 2020 · 9 comments
Labels
ecosystem Issues related to building or improving compatibility layers to enhance Parsec's ecosystem position

Comments

@paulhowardarm
Copy link
Collaborator

Summary

There are a number of existing systems that use PKCS#11 as their interface of choice for calling out to hardware security functions - it is a well-established interface with a lot of industry adoption. If Parsec could be accessed and driven through this interface, it would allow those systems to gain the benefits of the Parsec platform abstraction without having to code to any Parsec-specific front-end API or client library. Existing PKCS#11 client code would automatically "just work" with Parsec. Hence it would be a useful ecosystem enabler.

This request is to investigate and define the required work.

Design Considerations

A non-exhaustive list of design considerations for this investigation:

  • What is the general mechanism via which a Rust-based PKCS#11 library should be built? The interface needs to be C-callable using the Oasis-defined headers, and it needs to compile to a dynamic (shared object) library exposing this interface. But the bulk of the implementation would likely be Rust and hook up to the existing Rust Parsec client library, Is it useful to have a generic PKCS#11 "skin" that provides the interface and the C/Rust interop layer? Should this be part of Parsec, or an enhancement of other Rust PKCS#11 ecosystem projects? How would it bridge to the Parsec client library? Should we try to capture the PKCS#11 contracts as simplified Rust traits, so that anyone could create a Rust-based PKCS#11 library with reduced effort?
  • What should the slot and slot access models look like? Does it make sense to model the Parsec service in terms of its providers, and perhaps have a slot per provider? What about slot PINs? Does it make sense to expose these? What would that mean? What if the back-end is also PKCS#11 and has multiple slots? Should we try to reflect the back-end slots into front-end slots? That could get messy, and is also breaking the abstraction that Parsec is trying to provide. There is benefit to keeping back-end slots "hidden" within the Parsec service, because then things like slot PINs can be managed by the Parsec admin and do not need to be leaked out to any other part of the system.
  • What's the subset of the PKCS#11 standard that we need to cover?
  • Related to the above, are the existing Parsec API (based on PSA Crypto) sufficient? Do we need PSA storage APIs as well? (The answer to this may depend on how many client systems we want to try to integrate with).
  • The C_FindObjects API could prove a challenge. Parsec currently has a ListKeys opcode, but it's not clear whether that is comprehensive enough to allow objects to be found by PKCS#11-style key/value pair templates.

Definition of Done

This is a ticket for investigation, so the expected end result would be a more detailed specification and scope for the work, perhaps with a breakdown into individual issues for concrete engineering items. Part of the DoD should also be a specification (perhaps as a PR against parsec-book) for the subset of PKCS#11 standard that would be implemented - this should aim to resolve some of the questions about slot modelling noted above. A good first target would be to enable PKCS#11-compatible access to the Parsec features that exist at time of writing (November 2020) for the provisioning of asymmetric keys, sign/verify and asymmetric crypto primitives.

@paulhowardarm paulhowardarm added the ecosystem Issues related to building or improving compatibility layers to enhance Parsec's ecosystem position label Nov 27, 2020
@ionut-arm
Copy link
Member

Does it make sense to model the Parsec service in terms of its providers, and perhaps have a slot per provider? What about slot PINs?

My vote would be to have a slot per provider and an empty PIN for each one (i.e. we just reject the call in the frontend if any PIN is supplied), it seems like a reasonable mapping.

What if the back-end is also PKCS#11 and has multiple slots? Should we try to reflect the back-end slots into front-end slots?

Unless we want to implement some PKCS11-specific operations to support this, I don't think it's do-able. There are a lot of complications that I can think of, it'd be better to discuss this one separately if we think it might be useful, but overall we'd have to use the current operations in a hacky way to achieve this (hacky including in the provider, not just in the frontend...).

@hug-dev
Copy link
Member

hug-dev commented Jan 6, 2021

Concerning the bridge from the C PKCS11 API to the Rust Parsec Client one, I think we can do:

  • C PKCS11 types to Rust PKCS11 abstracted types using the rust-pkcs11 crate (some of which are already in this PR). Also probably the other way around for some operations.
  • Rust PKCS11 types to PSA Crypto Rust types adding TryFrom implementations in psa-crypto. The reverse was already done in the PR linked above.

I think the main type to convert is PKCS11 CK_MECHANISM to rust-pkcs11 Mechanism to psa-crypto Algorithm. Maybe for the key types as well but that's easier. Note that this also depends of the linked PR to be merged somewhere.

My vote would be to have a slot per provider and an empty PIN for each one (i.e. we just reject the call in the frontend if any PIN is supplied), it seems like a reasonable mapping.

Agreed as well, the problem remaining is how we map a provider to a specific slot ID. I propose that they can be listed, starting from 0 in a prioritised list (same order than what ListProviders returns) and that we can convert the ProviderInfo structure into a CK_SLOT_INFO one to give more information if needed when C_GetSlotInfo is called.

@ionut-arm
Copy link
Member

Agreed as well, the problem remaining is how we map a provider to a specific slot ID. I propose that they can be listed, starting from 0 in a prioritised list (same order than what ListProviders returns) and that we can convert the ProviderInfo structure into a CK_SLOT_INFO one to give more information if needed when C_GetSlotInfo is called.

Alternatively, we can use the provider UUID - it's essentially an 128-bit, base64 encoded number, so we can just take the lowest X bits and hope they don't collide.

@ionut-arm
Copy link
Member

We started investigating this topic a bit and found a few other questions and ideas to throw around.

  1. What version of the API should we expose? How do we decide? The work we put in for the pkcs11 crate was on the 2.40 version of the spec, but there's a version 3 out.
  2. How do we handle sessions? Presumably we can be stateful in the library and keep track of requests made per session (such as opening objects).
  3. We'll need to define a clear set of primitives that we need to implement and figure out if we can "partially" implement them (e.g. if we can support some type of keys for one operation, but not others).

We can start from the C headers of PKCS11 and build out the interface or use something like this and call our library from the methods we implement.

@hug-dev
Copy link
Member

hug-dev commented Feb 1, 2021

I have also had a look at the specs and can think of the following:

  • if we support multiple slots/tokens, we would have to store the login/logout information per token. All sessions open on a token store the same login/logout state. Might be easier to only support one token for now (the first provider returned by ListProviders?).
  • we will have to store which sessions are opened (possibly per token) and for each of them their state (for example read or read-write sessions). This is to return the correct error code in case something is wrong.
  • We will have to store persistently the mappings between the attributes of a key (CK_ATTRIBUTE: ID, sensitivity, etc) and the key name in Parsec (which will have to be generated). I don't think all CK_ATTRIBUTE map to our PSA KeyAttributes but maybe we can make simplifications.
  • we will have to store (no need to be persistent) the mappings between object handle (CK_OBJECT_HANDLE) and attributes/key names in order to use the correct keys
  • per session, we will have to store the state of the stateful operations: C_FindObjects, C_Sign, C_Verify, etc... As they have an Init equivalent function to be called before. Correct error codes need to be returned in case the order in which they should be called is not respected.
  • as this is C we are interfacing with and we will need to dereference pointers, we will have to check for user inputs, as much as we can
  • We will have to make sure we respect the logic expected by some operations: for example a lot of operations use the NULL pointer argument as a way to tell the PKCS11 function to return the number of element/size of something instead of the actual result.
  • If we want to be compliant, we will have to make sure that we return the correct error codes when things fail in a specified way. Clients might be relying on that.
  • We will have to make sure that we support the different CK_MECHANISM that are planning to be used.

Finally, we will have to think about security, PIN handling and authentication. As stated by the spec:

It is also possible for a token to have concurrent sessions with more than one application.

I think that it should be possible to access the same keys from multiple application in PKCS11. That would imply that the PKCS11 front end has a fixed, direct authentication name. That might lead to several problems.

All of these comments are for a general, compliant PKCS11 library. We could make simplifications if we put constraints on the use-case.

@areiter
Copy link

areiter commented Dec 1, 2021

Does this ticket represent the current state of the PKCS11 frontend discussion? (or did I miss something?)

@paulhowardarm
Copy link
Collaborator Author

Hi @areiter - this ticket accurately reflects the current status. There was some initial discussion and investigation, but recently the focus of the project has shifted more towards natively integrating Parsec rather than using a common shim. This issue is still open because I don't think we completely rule out the possibility of a shim. But at this point my personal view is that a shim would more likely get created if there is a use case that specifically requires it, rather than as a strategic compatibility exercise. The philosophy behind Parsec is to avoid shims wherever possible, and instead to create attractive and ergonomic API surfaces in a variety of programming languages. We would like developers to embrace Parsec as a new and updated developer experience, and we are very keen to engage with engineers and partners who are willing to join us in that quest. I would be very keen to hear more about your possible use case with Parsec and/or PKCS#11.

@areiter
Copy link

areiter commented Dec 1, 2021

Hi @paulhowardarm,
thanks for your quick response, and thanks for all your effort!

Let me just give you a short overview:
My system at hand is a multi tenant environment (not necessarily human users, let's call them apps), running on a single device. The system is already existing and many apps are already out there - using mainly pkcs#11 interface to perform different cryptographic operations but e.g. directly talk to the hardware --> Currently no abstraction layer is available.
Apps can be completely custom binaries, but typically are a composition of existing tools, libraries,...
When it comes to cryptographic operations most of these tools already support PKCS#11.

I'm now looking into different possibilities of how a "security abstraction layer" could look like, but also need to provide an easy migration path for existing components, without the need to rewrite all the existing components.
For my use case it would also be a viable option to provide multiple interfaces.

  • A PKCS#11 interface/library which can be plugged into existing components,, e.g. with a limited set of functionality
  • The full-fledged integration using e.g. PSA CryptoAPI for greenfield components/migration scenarios
  • A scenario-based API (not going into detail on that)

@paulhowardarm
Copy link
Collaborator Author

Thanks @areiter - this sounds extremely interesting.

The PKCS#11 front-end shim work hasn't progressed in any material way as mentioned above, but there is plenty of room in the project for community contribution, if this is something that you would consider. As you might gather from the discussion above, there would be quite a lot of detail to resolve to create a general-purpose PKCS#11 front end. But perhaps that's not what you need? A narrow and highly-specialised subset could be considered instead, and either contributed into the broader Parsec project, or just kept as a separate component maintained elsewhere for your own use. But I think you would need to look at contributing that as part of your brownfield scenarios, because as I mentioned we are highly focused at the moment on creating client library experiences that are compelling in their own right, so there isn't any plan right now to deliver this feature.

For your greenfield use cases, where the client language is C or C++, you certainly can use the PSA Crypto API as way to call Parsec. Parsec doesn't have a C client library as such, but you can use Mbed TLS and link in a component known as the Parsec Secure Element Driver, see: https://github.com/parallaxsecond/parsec-se-driver

This method allows you to make C calls to PSA Crypto, and have those calls routed through to the Parsec service/daemon. For some minimal example of that, take a look at the CI tests for the SE driver here: https://github.com/parallaxsecond/parsec-se-driver/tree/main/ci/c-tests

If you would like to engage with the project team in order to discuss your requirements in more detail, feel free to join our public Slack channel or come to one of our regular open project meetings. Details for all of these can be found in our community repo: https://github.com/parallaxsecond/community

I hope that you will be able to find Parsec useful in your systems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ecosystem Issues related to building or improving compatibility layers to enhance Parsec's ecosystem position
Projects
None yet
Development

No branches or pull requests

4 participants