235 lines
5.3 KiB
Go
235 lines
5.3 KiB
Go
// +build cgo
|
|
|
|
package pkcs11
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"math/big"
|
|
"runtime"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/ThalesIgnite/crypto11"
|
|
"github.com/smallstep/certificates/kms/apiv1"
|
|
)
|
|
|
|
var (
|
|
softHSM2Once sync.Once
|
|
yubiHSM2Once sync.Once
|
|
)
|
|
|
|
var (
|
|
testKeys = []struct {
|
|
Name string
|
|
SignatureAlgorithm apiv1.SignatureAlgorithm
|
|
Bits int
|
|
}{
|
|
{"pkcs11:id=7371;object=rsa-key", apiv1.SHA256WithRSA, 2048},
|
|
{"pkcs11:id=7372;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=7375;object=ecdsa-p521-key", apiv1.ECDSAWithSHA512, 0},
|
|
}
|
|
|
|
testCerts = []struct {
|
|
Name string
|
|
Key string
|
|
Certificates []*x509.Certificate
|
|
}{
|
|
{"pkcs11:id=7370;object=root", "pkcs11:id=7373;object=ecdsa-p256-key", nil},
|
|
}
|
|
)
|
|
|
|
func generateCertificate(pub crypto.PublicKey, signer crypto.Signer) (*x509.Certificate, error) {
|
|
now := time.Now()
|
|
template := &x509.Certificate{
|
|
Subject: pkix.Name{CommonName: "Test Root Certificate"},
|
|
Issuer: pkix.Name{CommonName: "Test Root Certificate"},
|
|
IsCA: true,
|
|
MaxPathLen: 1,
|
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
NotBefore: now,
|
|
NotAfter: now.Add(time.Hour),
|
|
SerialNumber: big.NewInt(100),
|
|
}
|
|
|
|
b, err := x509.CreateCertificate(rand.Reader, template, template, pub, signer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return x509.ParseCertificate(b)
|
|
}
|
|
|
|
func setup(t *testing.T, k *PKCS11) {
|
|
for _, tk := range testKeys {
|
|
_, err := k.CreateKey(&apiv1.CreateKeyRequest{
|
|
Name: tk.Name,
|
|
SignatureAlgorithm: tk.SignatureAlgorithm,
|
|
Bits: tk.Bits,
|
|
})
|
|
if err != nil && !errors.Is(errors.Cause(err), apiv1.ErrAlreadyExists{
|
|
Message: tk.Name + " already exists",
|
|
}) {
|
|
t.Errorf("PKCS11.GetPublicKey() error = %v", err)
|
|
}
|
|
}
|
|
|
|
for i, c := range testCerts {
|
|
signer, err := k.CreateSigner(&apiv1.CreateSignerRequest{
|
|
SigningKey: c.Key,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("PKCS11.CreateSigner() error = %v", err)
|
|
continue
|
|
}
|
|
cert, err := generateCertificate(signer.Public(), signer)
|
|
if err != nil {
|
|
t.Errorf("x509.CreateCertificate() error = %v", err)
|
|
continue
|
|
}
|
|
if err := k.StoreCertificate(&apiv1.StoreCertificateRequest{
|
|
Name: c.Name,
|
|
Certificate: cert,
|
|
}); err != nil {
|
|
t.Errorf("PKCS1.StoreCertificate() error = %v", err)
|
|
continue
|
|
}
|
|
testCerts[i].Certificates = append(testCerts[i].Certificates, cert)
|
|
}
|
|
}
|
|
|
|
func teardown(t *testing.T, k *PKCS11) {
|
|
for _, tk := range testKeys {
|
|
if err := k.DeleteKey(tk.Name); err != nil {
|
|
t.Errorf("PKCS11.DeleteKey() error = %v", err)
|
|
}
|
|
}
|
|
for _, tc := range testCerts {
|
|
if err := k.DeleteCertificate(tc.Name); err != nil {
|
|
t.Errorf("PKCS11.DeleteCertificate() error = %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
type setupFunc func(t *testing.T) *PKCS11
|
|
|
|
func setupFuncs(t *testing.T) (setupFunc, setupFunc) {
|
|
var sh2, yh2 *PKCS11
|
|
t.Cleanup(func() {
|
|
if sh2 != nil {
|
|
sh2.Close()
|
|
}
|
|
if yh2 != nil {
|
|
yh2.Close()
|
|
}
|
|
})
|
|
setupSoftHSM2 := func(t *testing.T) *PKCS11 {
|
|
if sh2 != nil {
|
|
return sh2
|
|
}
|
|
sh2 = softHSM2(t)
|
|
return sh2
|
|
}
|
|
setupYubiHSM2 := func(t *testing.T) *PKCS11 {
|
|
if yh2 != nil {
|
|
return yh2
|
|
}
|
|
yh2 = yubiHSM2(t)
|
|
return yh2
|
|
}
|
|
return setupSoftHSM2, setupYubiHSM2
|
|
}
|
|
|
|
// softHSM2 configures a *PKCS11 KMS to be used with softHSM2. To initialize
|
|
// this tests, we should run:
|
|
// softhsm2-util --init-token --free \
|
|
// --token pkcs11-test --label pkcs11-test \
|
|
// --so-pin password --pin password
|
|
//
|
|
// To delete we should run:
|
|
// softhsm2-util --delete-token --token pkcs11-test
|
|
func softHSM2(t *testing.T) *PKCS11 {
|
|
t.Helper()
|
|
if runtime.GOARCH != "amd64" {
|
|
t.Skipf("softHSM2 test skipped on %s:%s", runtime.GOOS, runtime.GOARCH)
|
|
}
|
|
|
|
var path string
|
|
switch runtime.GOOS {
|
|
case "darwin":
|
|
path = "/usr/local/lib/softhsm/libsofthsm2.so"
|
|
case "linux":
|
|
path = "/usr/lib/softhsm/libsofthsm2.so"
|
|
default:
|
|
t.Skipf("softHSM2 test skipped on %s", runtime.GOOS)
|
|
return nil
|
|
}
|
|
p11, err := crypto11.Configure(&crypto11.Config{
|
|
Path: path,
|
|
TokenLabel: "pkcs11-test",
|
|
Pin: "password",
|
|
})
|
|
if err != nil {
|
|
t.Skipf("softHSM test skipped on %s: %v", runtime.GOOS, err)
|
|
}
|
|
|
|
k := &PKCS11{
|
|
p11: p11,
|
|
}
|
|
|
|
// Setup
|
|
softHSM2Once.Do(func() {
|
|
teardown(t, k)
|
|
setup(t, k)
|
|
})
|
|
|
|
return k
|
|
}
|
|
|
|
// yubiHSM2 configures a *PKCS11 KMS to be used with YubiHSM2. To initialize
|
|
// this tests, we should run:
|
|
// yubihsm-connector -d
|
|
func yubiHSM2(t *testing.T) *PKCS11 {
|
|
t.Helper()
|
|
if runtime.GOARCH != "amd64" {
|
|
t.Skipf("yubiHSM2 test skipped on %s:%s", runtime.GOOS, runtime.GOARCH)
|
|
}
|
|
|
|
var path string
|
|
switch runtime.GOOS {
|
|
case "darwin":
|
|
path = "/usr/local/lib/pkcs11/yubihsm_pkcs11.dylib"
|
|
case "linux":
|
|
path = "/usr/lib/x86_64-linux-gnu/pkcs11/yubihsm_pkcs11.so"
|
|
default:
|
|
t.Skipf("yubiHSM2 test skipped on %s", runtime.GOOS)
|
|
return nil
|
|
}
|
|
p11, err := crypto11.Configure(&crypto11.Config{
|
|
Path: path,
|
|
TokenLabel: "YubiHSM",
|
|
Pin: "0001password",
|
|
})
|
|
if err != nil {
|
|
t.Skipf("yubiHSM2 test skipped on %s: %v", runtime.GOOS, err)
|
|
}
|
|
|
|
k := &PKCS11{
|
|
p11: p11,
|
|
}
|
|
|
|
// Setup
|
|
yubiHSM2Once.Do(func() {
|
|
teardown(t, k)
|
|
setup(t, k)
|
|
})
|
|
|
|
return k
|
|
}
|