From 9db2ae3ed687b4d71d01fd1fde3da7f7e489ef1c Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Mon, 22 Feb 2016 17:53:24 -0800 Subject: [PATCH] updating miekg/pkcs11 for go 1.6 Signed-off-by: David Lawrence (github: endophage) --- Godeps/Godeps.json | 4 + .../src/github.com/miekg/pkcs11/README.md | 42 +++- .../src/github.com/miekg/pkcs11/const.go | 14 +- .../github.com/miekg/pkcs11/parallel_test.go | 237 ++++++++++++++++++ .../src/github.com/miekg/pkcs11/pkcs11.go | 66 +++-- .../github.com/miekg/pkcs11/pkcs11_test.go | 208 ++++++++++----- .../src/github.com/miekg/pkcs11/types.go | 95 ++++--- 7 files changed, 515 insertions(+), 151 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/miekg/pkcs11/parallel_test.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index cc76ebc9b0..8c54fadaf3 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -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" diff --git a/Godeps/_workspace/src/github.com/miekg/pkcs11/README.md b/Godeps/_workspace/src/github.com/miekg/pkcs11/README.md index 6e7e550c21..f41c474394 100644 --- a/Godeps/_workspace/src/github.com/miekg/pkcs11/README.md +++ b/Godeps/_workspace/src/github.com/miekg/pkcs11/README.md @@ -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) diff --git a/Godeps/_workspace/src/github.com/miekg/pkcs11/const.go b/Godeps/_workspace/src/github.com/miekg/pkcs11/const.go index 79a25e345d..5901b602cd 100644 --- a/Godeps/_workspace/src/github.com/miekg/pkcs11/const.go +++ b/Godeps/_workspace/src/github.com/miekg/pkcs11/const.go @@ -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 diff --git a/Godeps/_workspace/src/github.com/miekg/pkcs11/parallel_test.go b/Godeps/_workspace/src/github.com/miekg/pkcs11/parallel_test.go new file mode 100644 index 0000000000..887d846136 --- /dev/null +++ b/Godeps/_workspace/src/github.com/miekg/pkcs11/parallel_test.go @@ -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) + } +} diff --git a/Godeps/_workspace/src/github.com/miekg/pkcs11/pkcs11.go b/Godeps/_workspace/src/github.com/miekg/pkcs11/pkcs11.go index 44023caf9c..424e74980a 100644 --- a/Godeps/_workspace/src/github.com/miekg/pkcs11/pkcs11.go +++ b/Godeps/_workspace/src/github.com/miekg/pkcs11/pkcs11.go @@ -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) } diff --git a/Godeps/_workspace/src/github.com/miekg/pkcs11/pkcs11_test.go b/Godeps/_workspace/src/github.com/miekg/pkcs11/pkcs11_test.go index 0ac02a2d5c..c06b78a2ce 100644 --- a/Godeps/_workspace/src/github.com/miekg/pkcs11/pkcs11_test.go +++ b/Godeps/_workspace/src/github.com/miekg/pkcs11/pkcs11_test.go @@ -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! } diff --git a/Godeps/_workspace/src/github.com/miekg/pkcs11/types.go b/Godeps/_workspace/src/github.com/miekg/pkcs11/types.go index 9a709cd851..e789bf1560 100644 --- a/Godeps/_workspace/src/github.com/miekg/pkcs11/types.go +++ b/Godeps/_workspace/src/github.com/miekg/pkcs11/types.go @@ -15,17 +15,13 @@ package pkcs11 #define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name) #include +#include #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 - } - 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) + } else { + a.Value = []byte{0} } + 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.