Hello list,

[RFC] PKCS#11 key support provider & engine (rev. 2)
Addresses: https://github.com/haproxy/haproxy/issues/71

This RFC now supports provider(OpenSSL 3) and engine(OpenSSL 1.1.1)

The patch will follow in the next email.

Background

Here is the revised version of my original RFC[1] to support
PKCS#11 keys in master-worker mode by recreating
private keys in the worker.

First let me address some previous questions: from @wlallemand[2]

"Did you identify why the fork was causing an issue? We should probably
try to understand this before, it could be something stupid in haproxy's
code or in the pkcs11 provider."

This is a known PKCS#11 issue and not really a haproxy problem.
Several integrations, such as provider/engine, do try to mitigate this
but in general it is a tricky problem.

After fork some of these integrations will attempt to restore handles
but as GnuTLS says[3]

"Note that, PKCS #11 modules behave in a peculiar way after a fork;
they require a reinitialization of all the used PKCS #11 resources.
While GnuTLS automates that process, there are corner cases where
it is not possible to handle it correctly in an automated way.
For that, it is recommended not to mix fork() and PKCS #11
module usage. It is recommended to initialize and use any
PKCS #11 resources in a single process."

PKCS#11 v3 does have support for PKCS#11 modules with "fork tolerant
semantics"[4], but such modules are rare at the moment.

Revised version
Changes from [1]
- now support both provider(OpenSSL 3) and engine(OpenSSL 1.1.1);
  the exemplars are pkcs11-provider[5] and opensc pkcs11 engine[6]
- remove use of maps (ckch_data* -> PEM, SSL_CTX* -> ckch_data*)

What does this RFC do?
In master-worker mode it recreates the private key in the worker
and installs this private key in the SSL_CTX*, overriding the original
private key created in master.

How does it do this?
- extend struct ckch_data to store a copy of the PEM data used to
  create the cert / key
- (engine specific) extend struct ckch_data to store the engine name and
  key name
- thus - hash map (struct ckch_data -> PEM/engine data) is not needed
- when creating SSL_CTX* store a pointer to struct ckch_data with
  SSL_CTX_set_app_data(); thus - hash map (SSL_CTX* -> struct ckch_data)
  is no longer needed
- in the worker process, just before SSL_use_SSL_CTX(), we use the stored
PEM
  data to recreate the provider private key or (engine specific)
  use the stored engine/key name to recreate the engine private key
- the worker key is cached for future use

How are engine keys referenced?
- use crt "extra files" and store engine "key" in a specially formatted
  "xxxx.crt.key" file
- this is not a private key PEM file, it is simply a text file containing
  two lines:
  engine = pkcs11
  private_key =
pkcs11:token=SomePKCS11Token;object=SomePrivateKeyLabel;type=private
- repeat: an engine "xxxx.crt.key" file is not a PEM-"PRIVATE KEY" file that
  we are familiar with; it is a special text file with the name of the
engine
  and private key
- engine keys must live in a separate .key file as they are not PEM

How are pkcs11-provider keys referenced?
- like TPM2 pkcs11-provider can store keys in PEM objects with
  label "PKCS#11 PROVIDER URI"
- a provider key can be merged into the .crt file
- current glitch in pkcs11-provider PEM parser: a key in a merged file
  must be the first PEM object

References
1. https://www.mail-archive.com/haproxy@formilux.org/msg44736.html
2. https://www.mail-archive.com/haproxy@formilux.org/msg44741.html
3. https://www.gnutls.org/manual/html_node/PKCS11-Initialization.html
4.
https://docs.oasis-open.org/pkcs11/pkcs11-base/v3.0/csprd01/pkcs11-base-v3.0-csprd01.html
5. https://github.com/latchset/pkcs11-provider
6. https://github.com/OpenSC/libp11

Regards
Richard

Reply via email to