-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Revamp pkcs11key #330
Revamp pkcs11key #330
Conversation
Fixes cloudflare#329 Fixes cloudflare#325 Fixes cloudflare#322 Incorporates cloudflare#321 pkcs11key used to log in and log out for each Sign request. This created a performance problem, because logins are slow. It also created a concurrency problem. Logged in / logged out state is a property of a PKCS#11 application, not a session. So it was possible for two Sign operations on separate threads to log in at the same time, and for one Sign operation to finish and call Logout before the other Sign operation called SignInit. This would result in a CKR_OBJECT_HANDLE_INVALID error. To fix both of these issues, I made the session a persistent property of a PKCS11Key, and protected it with a mutex. Now each PKCS11Key maintains a single session throughout its lifetime, increasing signing performance. Also, some of the field names for PKCS11Key were incorrect, and there was no provision to select by token label, only slot label. In general, token label is more unique, and the better choice. This change incorporates the elements of cloudflare#321 fixing that. That is, it renames Token to SlotDescription, Label to PrivateKeyLabel, and adds a new field TokenLabel. Note for people updating configs: In your configs, don't rename Token to TokenLabel. The value you currently have in Token is actually a SlotDescription, and should be named such. You should add a name field TokenLabel, whose value you can find with pkcs11-tool --list-slots. I also changed where the PKCS11Key Config object is defined, to make it more straightforward to share between different components, and added object label support to pkcs11uri, so private keys can be selected by label.
// Login | ||
if err = ps.module.Login(session, pkcs11.CKU_USER, ps.pin); err != nil { | ||
return session, err | ||
if slotInfo.SlotDescription != ps.slotDescription { |
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.
Isn't slot description just some made up stuff from the manufacturer and not ever guaranteed to be unique? This seems like an unnecessary complication for something that won't be useful for folks. They'll just think they need to have it when they don't.
// Note: Logged-in status is application-wide, not per session. But in | ||
// practice it appears to be okay to login to a token multiple times with the same | ||
// credentials. | ||
if err = ps.module.Login(session, pkcs11.CKU_USER, ps.pin); 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've done this same thing before with other PKCS11 modules and it's worked fine.
I agree with the decision to remove SlotDescription, for what it's worth. I'm only qualified to review the changes to |
And update the benchmark test to use it. In the process, introduce a flag allowing selection of the session count independently of GOMAXPROCS.
for j := 0; j < i; j++ { | ||
signers[j].Destroy() | ||
} | ||
return nil, fmt.Errorf("Problem making PKCS11Key: %s", err) |
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.
As a style thing, this error should be "pkcs11key: problem making PKCS11Key".
@jsha It would be useful to have some PKCS11 docs in the top-level docs dir. Is that something that could be included with this PR? It's not strictly necessary, but I think it would be useful for people to get started. |
@kisom: Sure, will do. |
Okay, added docs. |
Tested locally with a yubikey neo. LGTM. |
If the private key has a CKA_ALWAYS_AUTHENTICATE attribute, it means we have to Login before each sign attempt, and that would be terribly slow. Note: This means that a default-setup Yubikey Neo in PIV mode will be rejected. I'm planning to see if it's possible to set the attribute to false.
@kisom: Thanks! This works great with my SoftHSM instance. I had some trouble with my Yubikey, which I tracked to the CKA_ALWAYS_AUTHENTICATE attribute on my private key object. Can you run this command and tell me if the Private Key Object has
The problem I was having was that each session would succeed at signing once, and then fail on the second sign attempt. I can see now why the original version of this code did a login and logout on each sign attempt! It appears to be necessary for Yubikeys. I just pushed an update to this branch that checks for the CKA_ALWAYS_AUTHENTICATE attribute and fails if it's present. However, this probably breaks usage with most Yubikeys. Try running the pkcs11_bench_test against your Yubikey and let me know what the result is? My next steps is to see if I can clear the CKA_ALWAYS_AUTHENTICATE attribute, in which case we wouldn't need any special casing for Yubikeys. If I can't clear the attribute, and support for Yubikeys is important, we'll have to add some special case code that will do a logout/login after each signature when CKA_ALWAYS_AUTHENTICATE is present. I'd rather avoid the special casing because it will be messy: we would also have to make sure we disallow creation of multiple pkcs11key.Key's, because their behavior would be undefined. So let me know if you consider Yubikey support optional and it'll save me some work! |
Actually, I went ahead and implemented the login/logout when CKA_ALWAYS_AUTHENTICATE is true, so Yubikeys should work properly. |
@jsha I'll have some time to do more extensive testing over the weekend. Thanks again for this PR! |
We need to take a global lock when doing the logout/login and when doing the sign. Moving those calls to the top of Sign allows us to do that.
@kisom: Thanks! Let me know what you find. |
After jumping through a number of hoops figuring out how to stand up a new CA on a Neo-N, and a much more pleasant (in relative PKCS11 terms) time with SoftHSM, this LGTM. |
Excellent, thanks for the review & merge! |
This pulls in the pkcs11key change from cloudflare/cfssl#330, and updates the Boulder code to match. Note: This change overwrites the local changes to our vendored CFSSL made in letsencrypt#784. That's intentional: The upstream changes in cloudflare/cfssl#330 accomplish the same thing, more cleanly.
Fixes #329
Fixes #325
Fixes #322
Incorporates #321
pkcs11key used to log in and log out for each Sign request. This created a
performance problem, because logins are slow. It also created a concurrency
problem.
Logged in / logged out state is a property of a PKCS#11 application, not a
session. So it was possible for two Sign operations on separate threads to log
in at the same time, and for one Sign operation to finish and call Logout before
the other Sign operation called SignInit. This would result in a
CKR_OBJECT_HANDLE_INVALID error.
To fix both of these issues, I made the session a persistent property of a
PKCS11Key, and protected it with a mutex. Now each PKCS11Key maintains a single
session throughout its lifetime, increasing signing performance.
Also, some of the field names for PKCS11Key were incorrect, and there was no
provision to select by token label, only slot label. In general, token label is
more unique, and the better choice. This change incorporates the elements of
#321 fixing that. That is, it renames
Token to SlotDescription, Label to PrivateKeyLabel, and adds a new field
TokenLabel. Note for people updating configs: In your configs, don't rename
Token to TokenLabel. The value you currently have in Token is actually a
SlotDescription, and should be named such. You should add a field named
TokenLabel, whose value you can find with pkcs11-tool --list-slots.
I also changed where the PKCS11Key Config object is defined, to make it more
straightforward to share between different components, and added object label
support to pkcs11uri, so private keys can be selected by label.