-
Notifications
You must be signed in to change notification settings - Fork 8
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
PKCS11 Wrapper #14
base: main
Are you sure you want to change the base?
PKCS11 Wrapper #14
Conversation
Signed-off-by: jsmoon <[email protected]>
Signed-off-by: jsmoon <[email protected]>
Some parts of the code are commented for future improvements. In case of algorithm which would requires more inputs to know the IV length, the IsIvNeeded can return "0". Clearly I'm not very fan of it, but I don't know if giving access to the PKCS#11 session is better. Signed-off-by: Jérôme GLATIGNY <[email protected]>
Signed-off-by: Jérôme GLATIGNY <[email protected]>
* Moving PKCS11 client into the struct * Creating the client during configuration loading * Support of Finalize to destroy the client * Adding tools functions Signed-off-by: Jérôme GLATIGNY <[email protected]>
* Move client into a specific file * Support of setting: token label * Encrypt do not wrap anymore * Decrypt do not unwrap anymore * Encrypt store the key identifier in the KeyInfo * Decrypt and read the key identifier from the KeyInfo * Client Encrypt and Decrypt return and take a key identifier * Pkcs11Key struct to store the key identifier (label and id) Signed-off-by: Jérôme GLATIGNY <[email protected]>
Signed-off-by: Jérôme GLATIGNY <[email protected]>
Signed-off-by: Jérôme GLATIGNY <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry about the delay! This is looking fairly good, thank you! I've left a few comments here and there.
wrappers/pkcs11/pkcs11.go
Outdated
k.keyId = client.GetCurrentKey().String() | ||
|
||
// Send a value to test the wrapper and to set the current key id | ||
if _, err := k.Encrypt(context.Background(), []byte("a")); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we think this will always work? For CKM_AES_GCM
and CKM_RSA_PKCS_OAEP
I think it will, but not for CKM_AES_CBC
, which Vault Enterprise supports.
Since this is a fresh start, I suppose we could forbid insecure mechanisms and require AEAD from our mechanism... It is 2024, I don't really see the need to encourage use of CBC any more. Perhaps a note why we think this is fine would be good?
Do you want to take a stab at denying insecure mechanisms? I don't know how we'd do that generally, e.g., if someone wanted to substitute Korean or Japanese algorithms here for instance... I don't really want to maintain the encrypt-then-MAC construction either, tbh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'll remove the encryption test here. The PKCS11 client already test the session.
Regarding the mechanisms, of course AEAD are better.
Something currently wrong in the implementation is the parameter hmac_mechanism
is not handled and should be required for CBC and CBC PAD.
At first, I was thinking that SoftHSM2 did not support AES GCM, but it's just the documentation which is not up-to-date.
There is still the issue with YubiHSM2 but yes, it might be interesting to review the list of supported mechanisms !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not inclined to support hmac parameter and just require an AEAD mech.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, of course.
It just like you wrote 3 days ago: to support encrypt-then-mac it requires to add the HMAC mechanism parameter.
wrapConfig := new(wrapping.WrapperConfig) | ||
wrapConfig.Metadata = make(map[string]string) | ||
wrapConfig.Metadata["lib"] = lib | ||
wrapConfig.Metadata["key_label"] = keyLabel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably know better than I, but I was reading the PKCS#11v3.0 spec and I noticed it very carefully doesn't say CKA_LABEL
is guaranteed unique. OpenSC/OpenSC#1613 seems to reference this as a place where it has happened in the wild...
Do we want to allow CKA_UNIQUE_ID
as an identifier to compensate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed the Key Label (CKA_LABEL
) and Key ID (CKA_ID
) are not unique ; the user is free to choose them (and edit them).
The Unique ID is unique by definition and generated by the Token/HSM itself ; which means that if you import your secret the Unique ID will change.
That's why, I'm not a fan of using the Unique ID in the configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's why, I'm not a fan of using the Unique ID in the configuration.
Hmm, but if we error like in https://github.com/openbao/go-kms-wrapping/pull/14/files#r1863149975, we're essentially then saying there's no way to use HSMs configured with multiple keys with the same CKA_LABEL
and/or CKA_ID
values?
I think we're fine with that, but just making sure I understand the implications of this. It seems like we'd prefer a way to disambiguate the keys, given the FindObjects(...)
return order may not be deterministic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Label + ID + KeyType muse be unique yes.
Now, if the ID is not provide during encryption and we found one key for that label, we can retrieve the ID to store it in the metadata, so during the decryption, we will be able to find the right key even if another one has been created with the same label.
If there is more than one key, the fonction FindKey will return an error.
case api.ReadBaoVariable(EnvHsmWrapperKeyId) != "" && !opts.Options.WithDisallowEnvVars: | ||
keyId = api.ReadBaoVariable(EnvHsmWrapperKeyId) | ||
case opts.withKeyId != "": | ||
keyId = opts.withKeyId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this comment might still apply? https://github.com/openbao/go-kms-wrapping/pull/8/files/45ad5ab66ef39ae498a8a736b1a190d34ca1c397#r1718338953
if err := c.client.FindObjectsInit(session, template); err != nil { | ||
return nil, fmt.Errorf("failed to pkcs11 FindObjectsInit: %s", err) | ||
} | ||
obj, _, err := c.client.FindObjects(session, 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do we want to handle multiple results? I think right now, given the above comment about uniqueness, we're at the mercy of the library as to what order it returns results in, which might be random.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a part in Encrypt
and Decrypt
that I'll move into FindKey
: (and I will change the text too).
if len(obj) != 1 {
return nil, nil, fmt.Errorf("expected 1 object, got %d", len(obj))
}
Since we're asking for 2 objects max, we can detect if there is more than 1 and then return an error.
wrappers/pkcs11/pkcs11_client.go
Outdated
return nil, nil, err | ||
} | ||
|
||
var iv []byte |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@glatigny I think you need to correctly handle mechanism parameters here. See https://pkg.go.dev/github.com/miekg/pkcs11#NewGCMParams or https://pkg.go.dev/github.com/miekg/pkcs11#NewOAEPParams.
Otherwise, this fails for me:
pkcs11_acc_test.go:32: err : failed to pkcs11 EncryptInit: pkcs11: 0x7: CKR_ARGUMENTS_BAD
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up with a Dockerfile:
FROM registry.fedoraproject.org/fedora:latest
COPY . /go-kms-wrapping
# pkcs11-tool --module "$BAO_HSM_LIB" --token-label "$BAO_HSM_TOKEN_LABEL" --so-pin 1234 --pin 4321 --keygen --key-type aes:32 --label "$BAO_HSM_KEY_LABEL" && \
# Set up SoftHSM, generate a key, and run tests.
RUN true && \
dnf update --refresh -y && \
dnf install -y softhsm opensc golang && \
mkdir /softhsm && \
export SOFTHSM2_CONF=/go-kms-wrapping/wrappers/pkcs11/softhsm2.conf && \
softhsm2-util --init-token --slot 0 --label "OpenBao g-k-w" --so-pin 1234 --pin 4321 && \
softhsm2-util --show-slots && \
export BAO_HSM_TOKEN_LABEL="OpenBao g-k-w" && \
export BAO_HSM_PIN=4321 && \
export BAO_HSM_LIB=/usr/lib64/softhsm/libsofthsm.so && \
export BAO_HSM_KEY_LABEL=bao-root-key && \
pkcs11-tool --module "$BAO_HSM_LIB" --token-label "$BAO_HSM_TOKEN_LABEL" --so-pin 1234 --pin 4321 --keypairgen --key-type rsa:4096 --label "$BAO_HSM_KEY_LABEL" && \
pkcs11-tool --module "$BAO_HSM_LIB" --token-label "$BAO_HSM_TOKEN_LABEL" --so-pin 1234 --pin 4321 --show-info && \
cd /go-kms-wrapping/wrappers/pkcs11 && \
export VAULT_ACC=true && \
go test -v ./... && \
true
with config (wrappers/pkcs11/softhsm2.conf
):
directories.tokendir = /softhsm
objectstore.backend = file
log.level = DEBUG
and run via:
$ podman build -f wrappers/pkcs11/Dockerfile .
wrappers/pkcs11/pkcs11_client.go
Outdated
switch mech { | ||
case pkcs11.CKM_AES_GCM: | ||
return true, 16 | ||
case pkcs11.CKM_AES_CBC_PAD: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed my mind, let's deny this mechanism as well if you don't mind and only do CKM_AES_GCM
. Otherwise, we'd need to implement an encrypt-then-mac construct with an additional HMAC key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, we should probably implement CKM_AES_CCM
; it looks like YubiHSM2 doesn't support GCM mode per https://downloads.yubico.com/support/YubiHSM%20Manual_1_5_0.pdf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CKM_AES_CCM
requires some modifications in the current structure.
jsmoon started with mechanisms which requires an IV
while CCM
uses a nonce and some other parameters (like the MAC Len).
AAD
can also be used with GCM or CCM ; I don't think that we want to use it but, if so, we need to store it.
For the moment, the IV
is stored as a prefix ; we add it during encryption and we extract it from the ciphertext during decryption.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bad news... It is not possible to add CCM
support for the moment.
The PKCS11 lib doesn't have the function to create the CCM parameters:
https://github.com/miekg/pkcs11/blob/master/params.go
but the struct is available in the headers:
https://github.com/miekg/pkcs11/blob/master/pkcs11t.h#L1888
So it needs a small PR to add the wrapping struct and functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@glatigny Hmm.... Sadly it kinda looks like miekg/pkcs11
is getting less active. Things seemed to stall after attempting the PKCS#11 v3.0 port: miekg/pkcs11#148 ... Though, I'm sure he'd be welcoming of a PR for CCM support.
I'm not aware of any good alternatives to this library, so perhaps we should start trying to get involved upstream?
That said, I'm fine leaving off CCM for now.
I am prepared to test this with Thales USB HSM, NitroKey HSM and Yubico 2 HSM in December. |
* Add rsa_oaep_hash parameter. * New functions for the creation of PKCS11 encrypt/decrypt parameters. * FindKey now return only one key handle. Signed-off-by: Jérôme GLATIGNY <[email protected]>
wrappers/pkcs11/pkcs11_client.go
Outdated
var params interface{} | ||
switch mechanism { | ||
case pkcs11.CKM_AES_GCM: | ||
params = pkcs11.NewGCMParams(prefix, nil, 128) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, check out this note: https://pkg.go.dev/github.com/miekg/pkcs11#NewGCMParams
Note that some HSMs, like CloudHSM, will ignore the IV you pass in and write their own. As a result, to support all libraries, memory is not freed automatically, so that after the EncryptInit/Encrypt operation the HSM's IV can be read back out. It is up to the caller to ensure that Free() is called on the GCMParams object at an appropriate time, which is after Encrypt/Decrypt. As an example:
.... so we likely want to make sure we save params
here and update our copy of prefix
from this value before doing the append in Encrypt(...)
.
* Do not concat the IV with the encrypted content anymore, use the BlobInfo instead. * Split AES GCM and RSA OAEP into different functions. * Handle Specific cases for GCM (some HSM don't read the IV/nonce and generate it instead). * Use the PKCS#11 random generator and propose an interface for it. * Move constants into a dedicated section. Signed-off-by: Jérôme GLATIGNY <[email protected]>
Hey @cipherboy I wanted to make the constants a bit cleaner so I moved them into a dedicated section. While spliting the Encrypt/Decrypt functions for AES GCM and RSA OAEP, I saw how the IV could be handle by CloudHSM (and maybe others) and also saw that BlobInfo can embed the IV. I also remove the usage of |
Continuation of #8
Tested with SoftHSM2 (AES_CBC_PAD & RSA_PKCS keys)