Skip to content

Commit

Permalink
Merge pull request #260 from smallstep/p11-kit-proxy
Browse files Browse the repository at this point in the history
Add automatic support for p11-kit
  • Loading branch information
maraino authored Jun 12, 2023
2 parents 541f830 + 572bfb7 commit 1e0726a
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 27 deletions.
80 changes: 62 additions & 18 deletions kms/pkcs11/pkcs11.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import (
"encoding/hex"
"fmt"
"math/big"
"runtime"
"strconv"
"sync"

"github.com/ThalesIgnite/crypto11"
"github.com/pkg/errors"

"go.step.sm/crypto/kms/apiv1"
"go.step.sm/crypto/kms/uri"
)
Expand Down Expand Up @@ -50,34 +52,66 @@ type PKCS11 struct {
closed sync.Once
}

// New returns a new PKCS11 KMS.
// New returns a new PKCS#11 KMS. To initialize it, you need to provide a URI
// with the following format:
//
// - pkcs11:token=smallstep?pin-value=password
// - pkcs11:serial=1a2b3c4d5e6f?pin-source=/path/to/pin.txt
// - pkcs11:slot-id=5?pin-value=password
// - pkcs11:module-path=/path/to/module.so;token=smallstep?pin-value=password
//
// The scheme is "pkcs11"; "token", "serial", or "slot-id" defines the
// cryptographic device to use. "module-path" is the path of the PKCS#11 module
// to use. It will default to the proxy module of the p11-kit project if none is
// specified (p11-kit-proxy.so). "pin-value" provides the user's PIN, and
// "pin-source" defines a file that contains the PIN.
//
// A cryptographic key or object is identified by its "id" or "object"
// attributes. The "id" is the key identifier for the object, it's a hexadecimal
// string, and it will set the CKA_ID attribute of the object. The "object" is
// the name of the object, and it will set the CKA_LABEL attribute. Only one
// attribute is required to identify a key, but this package requires both to
// create a new key. The complete URI for a key looks like this:
//
// - pkcs11:token=smallstep;id=0a10;object=ec-key?pin-value=password
// - pkcs11:token=smallstep;id=%0a%10?pin-source=/path/to/pin.txt
// - pkcs11:token=smallstep;object=ec-key?pin-value=password
func New(ctx context.Context, opts apiv1.Options) (*PKCS11, error) {
if opts.URI == "" {
return nil, errors.New("kms uri is required")
}

var config crypto11.Config
if opts.URI != "" {
u, err := uri.ParseWithScheme(Scheme, opts.URI)
u, err := uri.ParseWithScheme(Scheme, opts.URI)
if err != nil {
return nil, err
}

config.TokenLabel = u.Get("token")
config.TokenSerial = u.Get("serial")
if v := u.Get("slot-id"); v != "" {
n, err := strconv.Atoi(v)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "kms uri 'slot-id' is not valid")
}
config.SlotNumber = &n
}

config.Pin = u.Pin()
config.Path = u.Get("module-path")
config.TokenLabel = u.Get("token")
config.TokenSerial = u.Get("serial")
if v := u.Get("slot-id"); v != "" {
n, err := strconv.Atoi(v)
if err != nil {
return nil, errors.Wrap(err, "kms uri 'slot-id' is not valid")
}
config.SlotNumber = &n
}
// Get module or default to use p11-kit-proxy.so.
//
// pkcs11.New(module string) will use dlopen that will look for the
// given library in the appropriate paths, so there's no need to provide
// the full path.
if config.Path = u.Get("module-path"); config.Path == "" {
config.Path = defaultModule
}

config.Pin = u.Pin()
if config.Pin == "" && opts.Pin != "" {
config.Pin = opts.Pin
}

switch {
case config.Path == "":
return nil, errors.New("kms uri 'module-path' are required")
case config.TokenLabel == "" && config.TokenSerial == "" && config.SlotNumber == nil:
return nil, errors.New("kms uri 'token', 'serial' or 'slot-id' are required")
case config.Pin == "":
Expand All @@ -100,13 +134,23 @@ func New(ctx context.Context, opts apiv1.Options) (*PKCS11, error) {
}, nil
}

// defaultModule defines the defaultModule used, in this case is the
// p11-kit-proxy provided by p11-kit.
var defaultModule = "p11-kit-proxy.so"

func init() {
switch runtime.GOOS {
case "darwin":
defaultModule = "p11-kit-proxy.dylib"
case "windows":
defaultModule = "p11-kit-proxy.dll"
}
apiv1.Register(apiv1.PKCS11, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) {
return New(ctx, opts)
})
}

// GetPublicKey returns the public key ....
// GetPublicKey returns the public key stored in the object identified by the name URI.
func (k *PKCS11) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey, error) {
if req.Name == "" {
return nil, errors.New("getPublicKeyRequest 'name' cannot be empty")
Expand Down
27 changes: 20 additions & 7 deletions kms/pkcs11/pkcs11_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ import (
)

func TestNew(t *testing.T) {
tmp := p11Configure
tmp0 := p11Configure
t.Cleanup(func() {
p11Configure = tmp
p11Configure = tmp0
})

k := mustPKCS11(t)
t.Cleanup(func() {
k.Close()
})

p11Configure = func(config *crypto11.Config) (P11, error) {
if strings.Contains(config.Path, "fail") {
return nil, errors.New("an error")
Expand Down Expand Up @@ -68,10 +69,13 @@ func TestNew(t *testing.T) {
URI: "pkcs11:module-path=/usr/local/lib/softhsm/libsofthsm2.so;token=pkcs11-test",
Pin: "passowrd",
}}, k, false},
{"fail missing module", args{context.Background(), apiv1.Options{
{"ok with missing module", args{context.Background(), apiv1.Options{
Type: "pkcs11",
URI: "pkcs11:token=pkcs11-test",
Pin: "passowrd",
}}, k, false},
{"fail missing uri", args{context.Background(), apiv1.Options{
Type: "pkcs11",
}}, nil, true},
{"fail missing pin", args{context.Background(), apiv1.Options{
Type: "pkcs11",
Expand Down Expand Up @@ -153,7 +157,7 @@ func TestPKCS11_GetPublicKey(t *testing.T) {
Name: "pkcs11:id=7373;object=ecdsa-p256-key",
}}, &ecdsa.PublicKey{}, false},
{"ECDSA by id", args{&apiv1.GetPublicKeyRequest{
Name: "pkcs11:id=7373",
Name: "pkcs11:id=%73%73",
}}, &ecdsa.PublicKey{}, false},
{"ECDSA by label", args{&apiv1.GetPublicKeyRequest{
Name: "pkcs11:object=ecdsa-p256-key",
Expand Down Expand Up @@ -219,6 +223,15 @@ func TestPKCS11_CreateKey(t *testing.T) {
SigningKey: testObject,
},
}, false},
{"default with percent URI", args{&apiv1.CreateKeyRequest{
Name: testObjectPercent,
}}, &apiv1.CreateKeyResponse{
Name: testObjectPercent,
PublicKey: &ecdsa.PublicKey{},
CreateSignerRequest: apiv1.CreateSignerRequest{
SigningKey: testObjectPercent,
},
}, false},
{"RSA SHA256WithRSA", args{&apiv1.CreateKeyRequest{
Name: testObject,
SignatureAlgorithm: apiv1.SHA256WithRSA,
Expand Down Expand Up @@ -424,7 +437,7 @@ func TestPKCS11_CreateSigner(t *testing.T) {
SigningKey: "pkcs11:id=7371;object=rsa-key",
}}, apiv1.SHA256WithRSA, crypto.SHA256, false},
{"RSA PSS", args{&apiv1.CreateSignerRequest{
SigningKey: "pkcs11:id=7372;object=rsa-pss-key",
SigningKey: "pkcs11:id=%73%72;object=rsa-pss-key",
}}, apiv1.SHA256WithRSAPSS, &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
Hash: crypto.SHA256,
Expand All @@ -433,7 +446,7 @@ func TestPKCS11_CreateSigner(t *testing.T) {
SigningKey: "pkcs11:id=7373;object=ecdsa-p256-key",
}}, apiv1.ECDSAWithSHA256, crypto.SHA256, false},
{"ECDSA P384", args{&apiv1.CreateSignerRequest{
SigningKey: "pkcs11:id=7374;object=ecdsa-p384-key",
SigningKey: "pkcs11:id=%73%74;object=ecdsa-p384-key",
}}, apiv1.ECDSAWithSHA384, crypto.SHA384, false},
{"ECDSA P521", args{&apiv1.CreateSignerRequest{
SigningKey: "pkcs11:id=7375;object=ecdsa-p521-key",
Expand Down Expand Up @@ -508,7 +521,7 @@ func TestPKCS11_CreateDecrypter(t *testing.T) {
DecryptionKey: "pkcs11:id=7371;object=rsa-key",
}}, false},
{"RSA PSS", args{&apiv1.CreateDecrypterRequest{
DecryptionKey: "pkcs11:id=7372;object=rsa-pss-key",
DecryptionKey: "pkcs11:id=%73%72;object=rsa-pss-key",
}}, false},
{"ECDSA P256", args{&apiv1.CreateDecrypterRequest{
DecryptionKey: "pkcs11:id=7373;object=ecdsa-p256-key",
Expand Down
5 changes: 3 additions & 2 deletions kms/pkcs11/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
var (
testModule = ""
testObject = "pkcs11:id=7370;object=test-name"
testObjectPercent = "pkcs11:id=%73%70;object=test-name"
testObjectAlt = "pkcs11:id=7377;object=alt-test-name"
testObjectByID = "pkcs11:id=7370"
testObjectByLabel = "pkcs11:object=test-name"
Expand All @@ -27,9 +28,9 @@ var (
Bits int
}{
{"pkcs11:id=7371;object=rsa-key", apiv1.SHA256WithRSA, 2048},
{"pkcs11:id=7372;object=rsa-pss-key", apiv1.SHA256WithRSAPSS, DefaultRSASize},
{"pkcs11:id=%73%72;object=rsa-pss-key", apiv1.SHA256WithRSAPSS, DefaultRSASize},
{"pkcs11:id=7373;object=ecdsa-p256-key", apiv1.ECDSAWithSHA256, 0},
{"pkcs11:id=7374;object=ecdsa-p384-key", apiv1.ECDSAWithSHA384, 0},
{"pkcs11:id=%73%74;object=ecdsa-p384-key", apiv1.ECDSAWithSHA384, 0},
{"pkcs11:id=7375;object=ecdsa-p521-key", apiv1.ECDSAWithSHA512, 0},
}

Expand Down

0 comments on commit 1e0726a

Please sign in to comment.