updating miekg/pkcs11 for go 1.6

Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
This commit is contained in:
David Lawrence 2016-02-22 17:53:24 -08:00
parent 332fc35dc5
commit 9db2ae3ed6
7 changed files with 515 additions and 151 deletions

4
Godeps/Godeps.json generated
View File

@ -171,6 +171,10 @@
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
"Rev": "d0c3fe89de86839aecf2e0579c40ba3bb336a453"
},
{
"ImportPath": "github.com/miekg/pkcs11",
"Rev": "df8ae6ca730422dba20c768ff38ef7d79077a59f"
},
{
"ImportPath": "github.com/mitchellh/go-homedir",
"Rev": "df55a15e5ce646808815381b3db47a8c66ea62f4"

View File

@ -1,13 +1,11 @@
# PKCS#11
# PKCS#11 [![Build Status](https://travis-ci.org/miekg/pkcs11.png?branch=master)](https://travis-ci.org/miekg/pkcs11)
This is a Go implementation of the PKCS#11 API. It wraps the library closely, but uses Go idiom
were it makes sense. It has been tested with SoftHSM.
## SoftHSM
* Make it use a custom configuration file
export SOFTHSM_CONF=$PWD/softhsm.conf
* Make it use a custom configuration file `export SOFTHSM_CONF=$PWD/softhsm.conf`
* Then use `softhsm` to init it
@ -22,16 +20,37 @@ were it makes sense. It has been tested with SoftHSM.
A skeleton program would look somewhat like this (yes, pkcs#11 is verbose):
p := pkcs11.New("/usr/lib/softhsm/libsofthsm.so")
p.Initialize()
err := p.Initialize()
if err != nil {
panic(err)
}
defer p.Destroy()
defer p.Finalize()
slots, _ := p.GetSlotList(true)
session, _ := p.OpenSession(slots[0], pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
slots, err := p.GetSlotList(true)
if err != nil {
panic(err)
}
session, err := p.OpenSession(slots[0], pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
if err != nil {
panic(err)
}
defer p.CloseSession(session)
p.Login(session, pkcs11.CKU_USER, "1234")
err = p.Login(session, pkcs11.CKU_USER, "1234")
if err != nil {
panic(err)
}
defer p.Logout(session)
p.DigestInit(session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_SHA_1, nil)})
hash, err := p.Digest(session, []byte("this is a string"))
if err != nil {
panic(err)
}
for _, d := range hash {
fmt.Printf("%x", d)
}
@ -41,8 +60,5 @@ Further examples are included in the tests.
# TODO
* Fix/double check endian stuff, see types.go NewAttribute();
* Kill C.Sizeof in that same function.
* Look at the memory copying in fast functions (sign, hash etc).
* Fix inconsistencies in naming?
* Add tests -- there are way too few
* Fix/double check endian stuff, see types.go NewAttribute()
* Look at the memory copying in fast functions (sign, hash etc)

View File

@ -23,9 +23,9 @@ const (
CKO_VENDOR_DEFINED uint = 0x80000000
)
// Generated with: awk '/#define CK[AFKMR]/{ print $2 "=" $3 }' pkcs11t.h
// Generated with: awk '/#define CK[AFKMRC]/{ print $2 "=" $3 }' pkcs11t.h
// All the flag (CKF_), attribute (CKA_), error code (CKR_), key type (CKK_) and
// All the flag (CKF_), attribute (CKA_), error code (CKR_), key type (CKK_), certificate type (CKC_) and
// mechanism (CKM_) constants as defined in PKCS#11.
const (
CKF_TOKEN_PRESENT = 0x00000001
@ -83,6 +83,10 @@ const (
CKK_CAMELLIA = 0x00000025
CKK_ARIA = 0x00000026
CKK_VENDOR_DEFINED = 0x80000000
CKC_X_509 = 0x00000000
CKC_X_509_ATTR_CERT = 0x00000001
CKC_WTLS = 0x00000002
CKC_VENDOR_DEFINED = 0x80000000
CKF_ARRAY_ATTRIBUTE = 0x40000000
CKA_CLASS = 0x00000000
CKA_TOKEN = 0x00000001
@ -117,11 +121,11 @@ const (
CKA_VERIFY = 0x0000010A
CKA_VERIFY_RECOVER = 0x0000010B
CKA_DERIVE = 0x0000010C
CKA_START_DATE = 0x00000110 // Use time.Time as a value.
CKA_END_DATE = 0x00000111 // Use time.Time as a value.
CKA_START_DATE = 0x00000110
CKA_END_DATE = 0x00000111
CKA_MODULUS = 0x00000120
CKA_MODULUS_BITS = 0x00000121
CKA_PUBLIC_EXPONENT = 0x00000122 // Use []byte slice as a value.
CKA_PUBLIC_EXPONENT = 0x00000122
CKA_PRIVATE_EXPONENT = 0x00000123
CKA_PRIME_1 = 0x00000124
CKA_PRIME_2 = 0x00000125

View File

@ -0,0 +1,237 @@
package pkcs11
// A test of using several pkcs11 sessions in parallel for signing across
// multiple goroutines. Access to the PKCS11 module is thread-safe because of
// the C.CKF_OS_LOCKING_OK param and nil mutex functions that the pkcs11
// package passes to C.Initialize, which indicate that the module should use OS
// locking primitives on its own.
//
// Note that while access to the module is thread-safe, sessions are not thread
// safe, and each session must be protected from simultaneous use by some
// synchronization mechanism. In this case we use a cache of sessions (as
// embodied by the `signer` struct), protected by a condition variable. So long
// as there is an available signer in the cache, it is popped off and used. If
// there are no signers available, the caller blocks until there is one
// available.
//
// Please set the appropiate env variables. See the init function.
import (
"fmt"
"log"
"os"
"sync"
"testing"
)
var (
module = "/usr/lib/softhsm/libsofthsm.so"
tokenLabel = "softhsm token"
privateKeyLabel = "my key"
pin = "1234"
)
func init() {
if x := os.Getenv("SOFTHSM_LIB"); x != "" {
module = x
}
if x := os.Getenv("SOFTHSM_TOKENLABEL"); x != "" {
tokenLabel = x
}
if x := os.Getenv("SOFTHSM_PRIVKEYLABEL"); x != "" {
privateKeyLabel = x
}
if x := os.Getenv("SOFTHSM_PIN"); x != "" {
pin = x
}
wd, _ := os.Getwd()
os.Setenv("SOFTHSM_CONF", wd+"/softhsm.conf")
}
func initPKCS11Context(modulePath string) (*Ctx, error) {
context := New(modulePath)
if context == nil {
return nil, fmt.Errorf("unable to load PKCS#11 module")
}
err := context.Initialize()
return context, err
}
func getSlot(p *Ctx, label string) (uint, error) {
slots, err := p.GetSlotList(true)
if err != nil {
return 0, err
}
for _, slot := range slots {
_, err := p.GetSlotInfo(slot)
if err != nil {
return 0, err
}
tokenInfo, err := p.GetTokenInfo(slot)
if err != nil {
return 0, err
}
if tokenInfo.Label == label {
return slot, nil
}
}
return 0, fmt.Errorf("Slot not found: %s", label)
}
func getPrivateKey(context *Ctx, session SessionHandle, label string) (ObjectHandle, error) {
var noKey ObjectHandle
template := []*Attribute{
NewAttribute(CKA_CLASS, CKO_PRIVATE_KEY),
NewAttribute(CKA_LABEL, label),
}
if err := context.FindObjectsInit(session, template); err != nil {
return noKey, err
}
objs, _, err := context.FindObjects(session, 2)
if err != nil {
return noKey, err
}
if err = context.FindObjectsFinal(session); err != nil {
return noKey, err
}
if len(objs) == 0 {
err = fmt.Errorf("private key not found")
return noKey, err
}
return objs[0], nil
}
type signer struct {
context *Ctx
session SessionHandle
privateKey ObjectHandle
}
func makeSigner(context *Ctx) (*signer, error) {
slot, err := getSlot(context, tokenLabel)
if err != nil {
return nil, err
}
session, err := context.OpenSession(slot, CKF_SERIAL_SESSION)
if err != nil {
return nil, err
}
if err = context.Login(session, CKU_USER, pin); err != nil {
context.CloseSession(session)
return nil, err
}
privateKey, err := getPrivateKey(context, session, privateKeyLabel)
if err != nil {
context.CloseSession(session)
return nil, err
}
return &signer{context, session, privateKey}, nil
}
func (s *signer) sign(input []byte) ([]byte, error) {
mechanism := []*Mechanism{NewMechanism(CKM_RSA_PKCS, nil)}
if err := s.context.SignInit(s.session, mechanism, s.privateKey); err != nil {
log.Fatalf("SignInit: %s", err)
}
signed, err := s.context.Sign(s.session, input)
if err != nil {
log.Fatalf("Sign: %s", err)
}
return signed, nil
}
type cache struct {
signers []*signer
// this variable signals the condition that there are signers available to be
// used.
cond *sync.Cond
}
func newCache(signers []*signer) cache {
var mutex sync.Mutex
return cache{
signers: signers,
cond: sync.NewCond(&mutex),
}
}
func (c *cache) get() *signer {
c.cond.L.Lock()
for len(c.signers) == 0 {
c.cond.Wait()
}
instance := c.signers[len(c.signers)-1]
c.signers = c.signers[:len(c.signers)-1]
c.cond.L.Unlock()
return instance
}
func (c *cache) put(instance *signer) {
c.cond.L.Lock()
c.signers = append(c.signers, instance)
c.cond.Signal()
c.cond.L.Unlock()
}
func (c *cache) sign(input []byte) ([]byte, error) {
instance := c.get()
defer c.put(instance)
return instance.sign(input)
}
// TODO(miek): disabled for now. Fill out the correct values in hsm.db so we can use it.
func testParallel(t *testing.T) {
if module == "" || tokenLabel == "" || pin == "" || privateKeyLabel == "" {
t.Fatal("Must pass all flags: module, tokenLabel, pin, and privateKeyLabel")
return
}
context, err := initPKCS11Context(module)
if err != nil {
t.Fatal(err)
}
defer func() {
context.Finalize()
context.Destroy()
}()
const nSigners = 100
const nSignatures = 1000
signers := make([]*signer, nSigners)
for i := 0; i < nSigners; i++ {
signers[i], err = makeSigner(context)
if err != nil {
t.Fatalf("Problem making signer: %s", err)
}
}
pool := newCache(signers)
output := make(chan []byte, nSignatures)
for i := 0; i < nSignatures; i++ {
go func() {
result, err := pool.sign([]byte("hi"))
if err != nil {
t.Fatal(err)
}
output <- result
}()
}
for i := 0; i < nSignatures; i++ {
// Consume the output of the signers, but do nothing with it.
<-output
}
for i := 0; i < nSigners; i++ {
// Note: It is not necessary to call context.Logout. Closing the last
// session will automatically log out, per PKCS#11 API.
context.CloseSession(signers[i].session)
}
}

View File

@ -207,6 +207,9 @@ CK_RV SetOperationState(struct ctx * c, CK_SESSION_HANDLE session,
CK_RV Login(struct ctx *c, CK_SESSION_HANDLE session, CK_USER_TYPE userType,
char *pin, CK_ULONG pinLen)
{
if (pinLen == 0) {
pin = NULL;
}
CK_RV e =
c->sym->C_Login(session, userType, (CK_UTF8CHAR_PTR) pin, pinLen);
return e;
@ -997,7 +1000,8 @@ func (c *Ctx) Logout(sh SessionHandle) error {
/* CreateObject creates a new object. */
func (c *Ctx) CreateObject(sh SessionHandle, temp []*Attribute) (ObjectHandle, error) {
var obj C.CK_OBJECT_HANDLE
t, tcount := cAttributeList(temp)
arena, t, tcount := cAttributeList(temp)
defer arena.Free()
e := C.CreateObject(c.ctx, C.CK_SESSION_HANDLE(sh), t, tcount, C.CK_OBJECT_HANDLE_PTR(&obj))
e1 := toError(e)
if e1 == nil {
@ -1009,7 +1013,8 @@ func (c *Ctx) CreateObject(sh SessionHandle, temp []*Attribute) (ObjectHandle, e
/* CopyObject copies an object, creating a new object for the copy. */
func (c *Ctx) CopyObject(sh SessionHandle, o ObjectHandle, temp []*Attribute) (ObjectHandle, error) {
var obj C.CK_OBJECT_HANDLE
t, tcount := cAttributeList(temp)
arena, t, tcount := cAttributeList(temp)
defer arena.Free()
e := C.CopyObject(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_OBJECT_HANDLE(o), t, tcount, C.CK_OBJECT_HANDLE_PTR(&obj))
e1 := toError(e)
@ -1059,7 +1064,8 @@ func (c *Ctx) GetAttributeValue(sh SessionHandle, o ObjectHandle, a []*Attribute
/* SetAttributeValue modifies the value of one or more object attributes */
func (c *Ctx) SetAttributeValue(sh SessionHandle, o ObjectHandle, a []*Attribute) error {
pa, palen := cAttributeList(a)
arena, pa, palen := cAttributeList(a)
defer arena.Free()
e := C.SetAttributeValue(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_OBJECT_HANDLE(o), pa, palen)
return toError(e)
}
@ -1067,7 +1073,8 @@ func (c *Ctx) SetAttributeValue(sh SessionHandle, o ObjectHandle, a []*Attribute
// FindObjectsInit initializes a search for token and session
// objects that match a template.
func (c *Ctx) FindObjectsInit(sh SessionHandle, temp []*Attribute) error {
t, tcount := cAttributeList(temp)
arena, t, tcount := cAttributeList(temp)
defer arena.Free()
e := C.FindObjectsInit(c.ctx, C.CK_SESSION_HANDLE(sh), t, tcount)
return toError(e)
}
@ -1103,7 +1110,8 @@ func (c *Ctx) FindObjectsFinal(sh SessionHandle) error {
/* EncryptInit initializes an encryption operation. */
func (c *Ctx) EncryptInit(sh SessionHandle, m []*Mechanism, o ObjectHandle) error {
mech, _ := cMechanismList(m)
arena, mech, _ := cMechanismList(m)
defer arena.Free()
e := C.EncryptInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(o))
return toError(e)
}
@ -1155,7 +1163,8 @@ func (c *Ctx) EncryptFinal(sh SessionHandle) ([]byte, error) {
/* DecryptInit initializes a decryption operation. */
func (c *Ctx) DecryptInit(sh SessionHandle, m []*Mechanism, o ObjectHandle) error {
mech, _ := cMechanismList(m)
arena, mech, _ := cMechanismList(m)
defer arena.Free()
e := C.DecryptInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(o))
return toError(e)
}
@ -1207,7 +1216,8 @@ func (c *Ctx) DecryptFinal(sh SessionHandle) ([]byte, error) {
/* DigestInit initializes a message-digesting operation. */
func (c *Ctx) DigestInit(sh SessionHandle, m []*Mechanism) error {
mech, _ := cMechanismList(m)
arena, mech, _ := cMechanismList(m)
defer arena.Free()
e := C.DigestInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech)
return toError(e)
}
@ -1267,7 +1277,8 @@ func (c *Ctx) DigestFinal(sh SessionHandle) ([]byte, error) {
// the data, and plaintext cannot be recovered from the
// signature.
func (c *Ctx) SignInit(sh SessionHandle, m []*Mechanism, o ObjectHandle) error {
mech, _ := cMechanismList(m) // Only the first is used, but still use a list.
arena, mech, _ := cMechanismList(m) // Only the first is used, but still use a list.
defer arena.Free()
e := C.SignInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(o))
return toError(e)
}
@ -1314,7 +1325,8 @@ func (c *Ctx) SignFinal(sh SessionHandle) ([]byte, error) {
// SignRecoverInit initializes a signature operation, where
// the data can be recovered from the signature.
func (c *Ctx) SignRecoverInit(sh SessionHandle, m []*Mechanism, key ObjectHandle) error {
mech, _ := cMechanismList(m)
arena, mech, _ := cMechanismList(m)
defer arena.Free()
e := C.SignRecoverInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(key))
return toError(e)
}
@ -1339,7 +1351,8 @@ func (c *Ctx) SignRecover(sh SessionHandle, data []byte) ([]byte, error) {
// signature is an appendix to the data, and plaintext cannot
// be recovered from the signature (e.g. DSA).
func (c *Ctx) VerifyInit(sh SessionHandle, m []*Mechanism, key ObjectHandle) error {
mech, _ := cMechanismList(m) // only use one here
arena, mech, _ := cMechanismList(m) // only use one here
defer arena.Free()
e := C.VerifyInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(key))
return toError(e)
}
@ -1370,7 +1383,8 @@ func (c *Ctx) VerifyFinal(sh SessionHandle, signature []byte) error {
// VerifyRecoverInit initializes a signature verification
// operation, where the data is recovered from the signature.
func (c *Ctx) VerifyRecoverInit(sh SessionHandle, m []*Mechanism, key ObjectHandle) error {
mech, _ := cMechanismList(m)
arena, mech, _ := cMechanismList(m)
defer arena.Free()
e := C.VerifyRecoverInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(key))
return toError(e)
}
@ -1455,8 +1469,10 @@ func (c *Ctx) DecryptVerifyUpdate(sh SessionHandle, cipher []byte) ([]byte, erro
/* GenerateKey generates a secret key, creating a new key object. */
func (c *Ctx) GenerateKey(sh SessionHandle, m []*Mechanism, temp []*Attribute) (ObjectHandle, error) {
var key C.CK_OBJECT_HANDLE
t, tcount := cAttributeList(temp)
mech, _ := cMechanismList(m)
attrarena, t, tcount := cAttributeList(temp)
defer attrarena.Free()
mecharena, mech, _ := cMechanismList(m)
defer mecharena.Free()
e := C.GenerateKey(c.ctx, C.CK_SESSION_HANDLE(sh), mech, t, tcount, C.CK_OBJECT_HANDLE_PTR(&key))
e1 := toError(e)
if e1 == nil {
@ -1471,9 +1487,12 @@ func (c *Ctx) GenerateKeyPair(sh SessionHandle, m []*Mechanism, public, private
pubkey C.CK_OBJECT_HANDLE
privkey C.CK_OBJECT_HANDLE
)
pub, pubcount := cAttributeList(public)
priv, privcount := cAttributeList(private)
mech, _ := cMechanismList(m)
pubarena, pub, pubcount := cAttributeList(public)
defer pubarena.Free()
privarena, priv, privcount := cAttributeList(private)
defer privarena.Free()
mecharena, mech, _ := cMechanismList(m)
defer mecharena.Free()
e := C.GenerateKeyPair(c.ctx, C.CK_SESSION_HANDLE(sh), mech, pub, pubcount, priv, privcount, C.CK_OBJECT_HANDLE_PTR(&pubkey), C.CK_OBJECT_HANDLE_PTR(&privkey))
e1 := toError(e)
if e1 == nil {
@ -1488,7 +1507,8 @@ func (c *Ctx) WrapKey(sh SessionHandle, m []*Mechanism, wrappingkey, key ObjectH
wrappedkey C.CK_BYTE_PTR
wrappedkeylen C.CK_ULONG
)
mech, _ := cMechanismList(m)
arena, mech, _ := cMechanismList(m)
defer arena.Free()
e := C.WrapKey(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(wrappingkey), C.CK_OBJECT_HANDLE(key), &wrappedkey, &wrappedkeylen)
if toError(e) != nil {
return nil, toError(e)
@ -1501,8 +1521,10 @@ func (c *Ctx) WrapKey(sh SessionHandle, m []*Mechanism, wrappingkey, key ObjectH
/* UnwrapKey unwraps (decrypts) a wrapped key, creating a new key object. */
func (c *Ctx) UnwrapKey(sh SessionHandle, m []*Mechanism, unwrappingkey ObjectHandle, wrappedkey []byte, a []*Attribute) (ObjectHandle, error) {
var key C.CK_OBJECT_HANDLE
ac, aclen := cAttributeList(a)
mech, _ := cMechanismList(m)
attrarena, ac, aclen := cAttributeList(a)
defer attrarena.Free()
mecharena, mech, _ := cMechanismList(m)
defer mecharena.Free()
e := C.UnwrapKey(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(unwrappingkey), C.CK_BYTE_PTR(unsafe.Pointer(&wrappedkey[0])), C.CK_ULONG(len(wrappedkey)), ac, aclen, &key)
return ObjectHandle(key), toError(e)
}
@ -1510,8 +1532,10 @@ func (c *Ctx) UnwrapKey(sh SessionHandle, m []*Mechanism, unwrappingkey ObjectHa
// DeriveKey derives a key from a base key, creating a new key object. */
func (c *Ctx) DeriveKey(sh SessionHandle, m []*Mechanism, basekey ObjectHandle, a []*Attribute) (ObjectHandle, error) {
var key C.CK_OBJECT_HANDLE
ac, aclen := cAttributeList(a)
mech, _ := cMechanismList(m)
attrarena, ac, aclen := cAttributeList(a)
defer attrarena.Free()
mecharena, mech, _ := cMechanismList(m)
defer mecharena.Free()
e := C.DeriveKey(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(basekey), ac, aclen, &key)
return ObjectHandle(key), toError(e)
}

View File

@ -9,49 +9,91 @@ package pkcs11
import (
"fmt"
"log"
"math/big"
"os"
"testing"
)
/*
This test supports the following environment variables:
* SOFTHSM_LIB: complete path to libsofthsm.so
* SOFTHSM_TOKENLABEL
* SOFTHSM_PRIVKEYLABEL
* SOFTHSM_PIN
*/
func setenv(t *testing.T) *Ctx {
wd, _ := os.Getwd()
os.Setenv("SOFTHSM_CONF", wd+"/softhsm.conf")
p := New("/usr/lib/softhsm/libsofthsm.so") //p := New("/home/miek/libsofthsm.so")
lib := "/usr/lib/softhsm/libsofthsm.so"
if x := os.Getenv("SOFTHSM_LIB"); x != "" {
lib = x
}
t.Logf("loading %s", lib)
p := New(lib)
if p == nil {
t.Fatal("Failed to init lib")
}
return p
}
func TestSetenv(t *testing.T) {
wd, _ := os.Getwd()
os.Setenv("SOFTHSM_CONF", wd+"/softhsm.conf")
lib := "/usr/lib/softhsm/libsofthsm.so"
if x := os.Getenv("SOFTHSM_LIB"); x != "" {
lib = x
}
p := New(lib)
if p == nil {
t.Fatal("Failed to init pkcs11")
}
p.Destroy()
return
}
func getSession(p *Ctx, t *testing.T) SessionHandle {
if e := p.Initialize(); e != nil {
t.Fatalf("init error %s\n", e.Error())
t.Fatalf("init error %s\n", e)
}
slots, e := p.GetSlotList(true)
if e != nil {
t.Fatalf("slots %s\n", e.Error())
t.Fatalf("slots %s\n", e)
}
session, e := p.OpenSession(slots[0], CKF_SERIAL_SESSION)
if e != nil {
t.Fatalf("session %s\n", e.Error())
t.Fatalf("session %s\n", e)
}
if e := p.Login(session, CKU_USER, "1234"); e != nil {
t.Fatal("user pin %s\n", e.Error())
if e := p.Login(session, CKU_USER, pin); e != nil {
t.Fatalf("user pin %s\n", e)
}
return session
}
func TestInitialize(t *testing.T) {
p := setenv(t)
if e := p.Initialize(); e != nil {
t.Fatalf("init error %s\n", e)
}
p.Finalize()
p.Destroy()
}
func finishSession(p *Ctx, session SessionHandle) {
p.Logout(session)
p.CloseSession(session)
p.Finalize()
p.Destroy()
}
func TestGetInfo(t *testing.T) {
p := setenv(t)
session := getSession(p, t)
defer p.Logout(session)
defer p.CloseSession(session)
defer p.Finalize()
defer p.Destroy()
defer finishSession(p, session)
info, err := p.GetInfo()
if err != nil {
t.Fatalf("Non zero error %s\n", err.Error())
t.Fatalf("non zero error %s\n", err)
}
if info.ManufacturerID != "SoftHSM" {
t.Fatal("ID should be SoftHSM")
@ -62,21 +104,18 @@ func TestGetInfo(t *testing.T) {
func TestFindObject(t *testing.T) {
p := setenv(t)
session := getSession(p, t)
defer p.Logout(session)
defer p.CloseSession(session)
defer p.Finalize()
defer p.Destroy()
defer finishSession(p, session)
// There are 2 keys in the db with this tag
template := []*Attribute{NewAttribute(CKA_LABEL, "MyFirstKey")}
if e := p.FindObjectsInit(session, template); e != nil {
t.Fatalf("Failed to init: %s\n", e.Error())
t.Fatalf("failed to init: %s\n", e)
}
obj, b, e := p.FindObjects(session, 2)
if e != nil {
t.Fatalf("Failed to find: %s %v\n", e.Error(), b)
t.Fatalf("failed to find: %s %v\n", e, b)
}
if e := p.FindObjectsFinal(session); e != nil {
t.Fatalf("Failed to finalize: %s\n", e.Error())
t.Fatalf("failed to finalize: %s\n", e)
}
if len(obj) != 2 {
t.Fatal("should have found two objects")
@ -86,10 +125,7 @@ func TestFindObject(t *testing.T) {
func TestGetAttributeValue(t *testing.T) {
p := setenv(t)
session := getSession(p, t)
defer p.Logout(session)
defer p.Destroy()
defer p.Finalize()
defer p.CloseSession(session)
defer finishSession(p, session)
// There are at least two RSA keys in the hsm.db, objecthandle 1 and 2.
template := []*Attribute{
NewAttribute(CKA_PUBLIC_EXPONENT, nil),
@ -100,14 +136,14 @@ func TestGetAttributeValue(t *testing.T) {
// ObjectHandle two is the public key
attr, err := p.GetAttributeValue(session, ObjectHandle(2), template)
if err != nil {
t.Fatalf("err %s\n", err.Error())
t.Fatalf("err %s\n", err)
}
for i, a := range attr {
t.Logf("Attr %d, type %d, valuelen %d", i, a.Type, len(a.Value))
t.Logf("attr %d, type %d, valuelen %d", i, a.Type, len(a.Value))
if a.Type == CKA_MODULUS {
mod := big.NewInt(0)
mod.SetBytes(a.Value)
t.Logf("Modulus %s\n", mod.String())
t.Logf("modulus %s\n", mod.String())
}
}
}
@ -115,18 +151,15 @@ func TestGetAttributeValue(t *testing.T) {
func TestDigest(t *testing.T) {
p := setenv(t)
session := getSession(p, t)
defer p.Logout(session)
defer p.CloseSession(session)
defer p.Finalize()
defer p.Destroy()
defer finishSession(p, session)
e := p.DigestInit(session, []*Mechanism{NewMechanism(CKM_SHA_1, nil)})
if e != nil {
t.Fatalf("DigestInit: %s\n", e.Error())
t.Fatalf("DigestInit: %s\n", e)
}
hash, e := p.Digest(session, []byte("this is a string"))
if e != nil {
t.Fatalf("Digest: %s\n", e.Error())
t.Fatalf("digest: %s\n", e)
}
hex := ""
for _, d := range hash {
@ -141,22 +174,19 @@ func TestDigest(t *testing.T) {
func TestDigestUpdate(t *testing.T) {
p := setenv(t)
session := getSession(p, t)
defer p.Logout(session)
defer p.CloseSession(session)
defer p.Finalize()
defer p.Destroy()
defer finishSession(p, session)
if e := p.DigestInit(session, []*Mechanism{NewMechanism(CKM_SHA_1, nil)}); e != nil {
t.Fatalf("DigestInit: %s\n", e.Error())
t.Fatalf("DigestInit: %s\n", e)
}
if e := p.DigestUpdate(session, []byte("this is ")); e != nil {
t.Fatalf("DigestUpdate: %s\n", e.Error())
t.Fatalf("DigestUpdate: %s\n", e)
}
if e := p.DigestUpdate(session, []byte("a string")); e != nil {
t.Fatalf("DigestUpdate: %s\n", e.Error())
t.Fatalf("DigestUpdate: %s\n", e)
}
hash, e := p.DigestFinal(session)
if e != nil {
t.Fatalf("DigestFinal: %s\n", e.Error())
t.Fatalf("DigestFinal: %s\n", e)
}
hex := ""
for _, d := range hash {
@ -166,47 +196,95 @@ func TestDigestUpdate(t *testing.T) {
if hex != "517592df8fec3ad146a79a9af153db2a4d784ec5" {
t.Fatalf("wrong digest: %s", hex)
}
}
func generateRSAKeyPair(t *testing.T, p *Ctx, session SessionHandle) (ObjectHandle, ObjectHandle) {
publicKeyTemplate := []*Attribute{
NewAttribute(CKA_KEY_TYPE, CKO_PUBLIC_KEY),
NewAttribute(CKA_TOKEN, false),
NewAttribute(CKA_VERIFY, true),
NewAttribute(CKA_PUBLIC_EXPONENT, []byte{1, 0, 1}),
NewAttribute(CKA_MODULUS_BITS, 2048),
NewAttribute(CKA_LABEL, "TestPbk"),
}
privateKeyTemplate := []*Attribute{
NewAttribute(CKA_TOKEN, false),
NewAttribute(CKA_SIGN, true),
NewAttribute(CKA_LABEL, "TestPvk"),
NewAttribute(CKA_SENSITIVE, true),
NewAttribute(CKA_EXTRACTABLE, true),
}
pbk, pvk, e := p.GenerateKeyPair(session,
[]*Mechanism{NewMechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, nil)},
publicKeyTemplate, privateKeyTemplate)
if e != nil {
t.Fatalf("failed to generate keypair: %s\n", e)
}
return pbk, pvk
}
func TestGenerateKeyPair(t *testing.T) {
p := setenv(t)
session := getSession(p, t)
defer finishSession(p, session)
generateRSAKeyPair(t, p, session)
}
func TestSign(t *testing.T) {
p := setenv(t)
session := getSession(p, t)
defer finishSession(p, session)
_, pvk := generateRSAKeyPair(t, p, session)
p.SignInit(session, []*Mechanism{NewMechanism(CKM_SHA1_RSA_PKCS, nil)}, pvk)
_, e := p.Sign(session, []byte("Sign me!"))
if e != nil {
t.Fatalf("failed to sign: %s\n", e)
}
}
func testDestroyObject(t *testing.T) {
p := setenv(t)
session := getSession(p, t)
defer p.Logout(session)
defer p.CloseSession(session)
defer p.Finalize()
defer p.Destroy()
defer finishSession(p, session)
p.Logout(session) // log out the normal user
if e := p.Login(session, CKU_SO, "1234"); e != nil {
t.Fatal("security officer pin %s\n", e.Error())
t.Fatalf("security officer pin %s\n", e)
}
// Looking the int values is tricky because they are stored in 64 bits in hsm.db,
// this means looking up stuff on 32 bits will not found them.
template := []*Attribute{
NewAttribute(CKA_LABEL, "MyFirstKey")}
if e := p.FindObjectsInit(session, template); e != nil {
t.Fatalf("Failed to init: %s\n", e.Error())
t.Fatalf("failed to init: %s\n", e)
}
obj, _, e := p.FindObjects(session, 1)
if e != nil || len(obj) == 0 {
t.Fatalf("Failed to find objects\n")
t.Fatalf("failed to find objects\n")
}
if e := p.FindObjectsFinal(session); e != nil {
t.Fatalf("Failed to finalize: %s\n", e.Error())
t.Fatalf("failed to finalize: %s\n", e)
}
if err := p.DestroyObject(session, obj[0]); err != nil {
t.Fatal("DestroyObject failed" + err.Error())
if e := p.DestroyObject(session, obj[0]); e != nil {
t.Fatal("DestroyObject failed: %s\n", e)
}
}
// ExampleSign show how to sign some data with a private key.
// Note: error correction is not implemented in this function.
// ExampleSign shows how to sign some data with a private key.
// Note: error correction is not implemented in this example.
func ExampleSign() {
p := setenv(nil)
lib := "/usr/lib/softhsm/libsofthsm.so"
if x := os.Getenv("SOFTHSM_LIB"); x != "" {
lib = x
}
p := New(lib)
if p == nil {
log.Fatal("Failed to init lib")
}
p.Initialize()
defer p.Destroy()
defer p.Finalize()
@ -217,7 +295,7 @@ func ExampleSign() {
defer p.Logout(session)
publicKeyTemplate := []*Attribute{
NewAttribute(CKA_KEY_TYPE, CKO_PUBLIC_KEY),
NewAttribute(CKA_TOKEN, true),
NewAttribute(CKA_TOKEN, false),
NewAttribute(CKA_ENCRYPT, true),
NewAttribute(CKA_PUBLIC_EXPONENT, []byte{3}),
NewAttribute(CKA_MODULUS_BITS, 1024),
@ -225,18 +303,26 @@ func ExampleSign() {
}
privateKeyTemplate := []*Attribute{
NewAttribute(CKA_KEY_TYPE, CKO_PRIVATE_KEY),
NewAttribute(CKA_TOKEN, true),
NewAttribute(CKA_TOKEN, false),
NewAttribute(CKA_PRIVATE, true),
NewAttribute(CKA_SIGN, true),
NewAttribute(CKA_LABEL, "MyFirstKey"),
}
pub, priv, _ := p.GenerateKeyPair(session,
_, priv, err := p.GenerateKeyPair(session,
[]*Mechanism{NewMechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, nil)},
publicKeyTemplate, privateKeyTemplate)
if err != nil {
log.Fatal(err)
}
p.SignInit(session, []*Mechanism{NewMechanism(CKM_SHA1_RSA_PKCS, nil)}, priv)
// Sign something with the private key.
data := []byte("Lets sign this data")
sig, _ := p.Sign(session, data)
fmt.Printf("%v validate with %v\n", sig, pub)
_, err = p.Sign(session, data)
if err != nil {
log.Fatal(err)
}
fmt.Printf("It works!")
// Output: It works!
}

View File

@ -15,17 +15,13 @@ package pkcs11
#define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name)
#include <stdlib.h>
#include <string.h>
#include "pkcs11.h"
CK_ULONG Index(CK_ULONG_PTR array, CK_ULONG i)
{
return array[i];
}
CK_ULONG Sizeof()
{
return sizeof(CK_ULONG);
}
*/
import "C"
@ -35,6 +31,21 @@ import (
"unsafe"
)
type arena []unsafe.Pointer
func (a *arena) Allocate(obj []byte) (C.CK_VOID_PTR, C.CK_ULONG) {
cobj := C.calloc(C.size_t(len(obj)), 1)
*a = append(*a, cobj)
C.memmove(cobj, unsafe.Pointer(&obj[0]), C.size_t(len(obj)))
return C.CK_VOID_PTR(cobj), C.CK_ULONG(len(obj))
}
func (a arena) Free() {
for _, p := range a {
C.free(p)
}
}
// toList converts from a C style array to a []uint.
func toList(clist C.CK_ULONG_PTR, size C.CK_ULONG) []uint {
l := make([]uint, int(size))
@ -53,6 +64,11 @@ func cBBool(x bool) C.CK_BBOOL {
return C.CK_BBOOL(C.CK_FALSE)
}
func uintToBytes(x uint64) []byte {
ul := C.CK_ULONG(x)
return C.GoBytes(unsafe.Pointer(&ul), C.int(unsafe.Sizeof(ul)))
}
// Error represents an PKCS#11 error.
type Error uint
@ -156,46 +172,23 @@ func NewAttribute(typ uint, x interface{}) *Attribute {
if x == nil {
return a
}
switch x.(type) {
case bool: // create bbool
if x.(bool) {
switch v := x.(type) {
case bool:
if v {
a.Value = []byte{1}
break
}
} else {
a.Value = []byte{0}
case uint, int:
var y uint
if _, ok := x.(int); ok {
y = uint(x.(int))
}
if _, ok := x.(uint); ok {
y = x.(uint)
}
// TODO(miek): ugly!
switch int(C.Sizeof()) {
case 4:
a.Value = make([]byte, 4)
a.Value[0] = byte(y)
a.Value[1] = byte(y >> 8)
a.Value[2] = byte(y >> 16)
a.Value[3] = byte(y >> 24)
case 8:
a.Value = make([]byte, 8)
a.Value[0] = byte(y)
a.Value[1] = byte(y >> 8)
a.Value[2] = byte(y >> 16)
a.Value[3] = byte(y >> 24)
a.Value[4] = byte(y >> 32)
a.Value[5] = byte(y >> 40)
a.Value[6] = byte(y >> 48)
a.Value[7] = byte(y >> 56)
}
case int:
a.Value = uintToBytes(uint64(v))
case uint:
a.Value = uintToBytes(uint64(v))
case string:
a.Value = []byte(x.(string))
case []byte: // just copy
a.Value = x.([]byte)
a.Value = []byte(v)
case []byte:
a.Value = v
case time.Time: // for CKA_DATE
a.Value = cDate(x.(time.Time))
a.Value = cDate(v)
default:
panic("pkcs11: unhandled attribute type")
}
@ -203,9 +196,10 @@ func NewAttribute(typ uint, x interface{}) *Attribute {
}
// cAttribute returns the start address and the length of an attribute list.
func cAttributeList(a []*Attribute) (C.CK_ATTRIBUTE_PTR, C.CK_ULONG) {
func cAttributeList(a []*Attribute) (arena, C.CK_ATTRIBUTE_PTR, C.CK_ULONG) {
var arena arena
if len(a) == 0 {
return nil, 0
return nil, nil, 0
}
pa := make([]C.CK_ATTRIBUTE, len(a))
for i := 0; i < len(a); i++ {
@ -213,10 +207,9 @@ func cAttributeList(a []*Attribute) (C.CK_ATTRIBUTE_PTR, C.CK_ULONG) {
if a[i].Value == nil {
continue
}
pa[i].pValue = C.CK_VOID_PTR((&a[i].Value[0]))
pa[i].ulValueLen = C.CK_ULONG(len(a[i].Value))
pa[i].pValue, pa[i].ulValueLen = arena.Allocate(a[i].Value)
}
return C.CK_ATTRIBUTE_PTR(&pa[0]), C.CK_ULONG(len(a))
return arena, C.CK_ATTRIBUTE_PTR(&pa[0]), C.CK_ULONG(len(a))
}
func cDate(t time.Time) []byte {
@ -250,9 +243,10 @@ func NewMechanism(mech uint, x interface{}) *Mechanism {
return m
}
func cMechanismList(m []*Mechanism) (C.CK_MECHANISM_PTR, C.CK_ULONG) {
func cMechanismList(m []*Mechanism) (arena, C.CK_MECHANISM_PTR, C.CK_ULONG) {
var arena arena
if len(m) == 0 {
return nil, 0
return nil, nil, 0
}
pm := make([]C.CK_MECHANISM, len(m))
for i := 0; i < len(m); i++ {
@ -260,10 +254,9 @@ func cMechanismList(m []*Mechanism) (C.CK_MECHANISM_PTR, C.CK_ULONG) {
if m[i].Parameter == nil {
continue
}
pm[i].pParameter = C.CK_VOID_PTR(&(m[i].Parameter[0]))
pm[i].ulParameterLen = C.CK_ULONG(len(m[i].Parameter))
pm[i].pParameter, pm[i].ulParameterLen = arena.Allocate(m[i].Parameter)
}
return C.CK_MECHANISM_PTR(&pm[0]), C.CK_ULONG(len(m))
return arena, C.CK_MECHANISM_PTR(&pm[0]), C.CK_ULONG(len(m))
}
// MechanismInfo provides information about a particular mechanism.