1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
package p11
import (
"errors"
"sync"
"github.com/miekg/pkcs11"
)
// Session represents a PKCS#11 session.
type Session interface {
// Login logs into the token as a regular user. Note: According to PKCS#11,
// logged-in state is a property of an application, rather than a session, but
// you can only log in via a session. Keep this in mind when using multiple
// sessions on the same token. Logging in to a token in any session will log
// in all sessions on that token, and logging out will do the same. This is
// particularly relevant for private keys with CKA_ALWAYS_AUTHENTICATE set
// (like Yubikeys in PIV mode). See
// https://github.com/letsencrypt/pkcs11key/blob/master/key.go for an example
// of managing login state with a mutex.
Login(pin string) error
// LoginSecurityOfficer logs into the token as the security officer.
LoginSecurityOfficer(pin string) error
// LoginAs logs into the token with the given user type.
LoginAs(userType uint, pin string) error
// Logout logs out all sessions from the token (see Login).
Logout() error
// Close closes the session.
Close() error
// CreateObject creates an object on the token with the given attributes.
CreateObject(template []*pkcs11.Attribute) (Object, error)
// FindObject finds a single object in the token that matches the attributes in
// the template. It returns error if there is not exactly one result, or if
// there was an error during the find calls.
FindObject(template []*pkcs11.Attribute) (Object, error)
// FindObjects finds any objects in the token matching the template.
FindObjects(template []*pkcs11.Attribute) ([]Object, error)
// GenerateKeyPair generates a public/private key pair. It takes
// GenerateKeyPairRequest instead of individual arguments so that attributes for
// public and private keys can't be accidentally switched around.
GenerateKeyPair(request GenerateKeyPairRequest) (*KeyPair, error)
// GenerateRandom returns random bytes generated by the token.
GenerateRandom(length int) ([]byte, error)
// InitPIN initialize's the normal user's PIN.
InitPIN(pin string) error
// SetPIN modifies the PIN of the logged-in user. "old" should contain the
// current PIN, and "new" should contain the new PIN to be set.
SetPIN(old, new string) error
}
type sessionImpl struct {
sync.Mutex
ctx *pkcs11.Ctx
handle pkcs11.SessionHandle
}
func (s *sessionImpl) FindPrivateKey(label string) (PrivateKey, error) {
obj, err := s.findObjectWithClassAndLabel(pkcs11.CKO_PRIVATE_KEY, label)
if err != nil {
return PrivateKey(obj), err
}
return PrivateKey(obj), nil
}
func (s *sessionImpl) FindPublicKey(label string) (PublicKey, error) {
obj, err := s.findObjectWithClassAndLabel(pkcs11.CKO_PUBLIC_KEY, label)
if err != nil {
return PublicKey(obj), err
}
return PublicKey(obj), nil
}
func (s *sessionImpl) FindSecretKey(label string) (SecretKey, error) {
obj, err := s.findObjectWithClassAndLabel(pkcs11.CKO_SECRET_KEY, label)
if err != nil {
return SecretKey(obj), err
}
return SecretKey(obj), nil
}
func (s *sessionImpl) findObjectWithClassAndLabel(class uint, label string) (Object, error) {
return s.FindObject([]*pkcs11.Attribute{
pkcs11.NewAttribute(pkcs11.CKA_CLASS, class),
pkcs11.NewAttribute(pkcs11.CKA_LABEL, label),
})
}
func (s *sessionImpl) FindObject(template []*pkcs11.Attribute) (Object, error) {
objects, err := s.FindObjects(template)
if err != nil {
return Object{}, err
}
if len(objects) > 1 {
return Object{}, errors.New("too many objects matching template")
}
return objects[0], nil
}
func (s *sessionImpl) FindObjects(template []*pkcs11.Attribute) ([]Object, error) {
s.Lock()
defer s.Unlock()
if err := s.ctx.FindObjectsInit(s.handle, template); err != nil {
return nil, err
}
var results []Object
for {
objectHandles, _, err := s.ctx.FindObjects(s.handle, 100)
if err != nil {
_ = s.ctx.FindObjectsFinal(s.handle)
return nil, err
} else if len(objectHandles) == 0 {
break
}
i := len(results)
results = append(results, make([]Object, len(objectHandles))...)
for j, objectHandle := range objectHandles {
results[i+j] = Object{
session: s,
objectHandle: objectHandle,
}
}
}
if err := s.ctx.FindObjectsFinal(s.handle); err != nil {
return nil, err
} else if len(results) == 0 {
return nil, errors.New("no objects found")
}
return results, nil
}
func (s *sessionImpl) Close() error {
s.Lock()
defer s.Unlock()
return s.ctx.CloseSession(s.handle)
}
func (s *sessionImpl) Login(pin string) error {
return s.LoginAs(pkcs11.CKU_USER, pin)
}
func (s *sessionImpl) LoginSecurityOfficer(pin string) error {
return s.LoginAs(pkcs11.CKU_SO, pin)
}
func (s *sessionImpl) LoginAs(userType uint, pin string) error {
s.Lock()
defer s.Unlock()
return s.ctx.Login(s.handle, userType, pin)
}
func (s *sessionImpl) Logout() error {
s.Lock()
defer s.Unlock()
return s.ctx.Logout(s.handle)
}
func (s *sessionImpl) GenerateRandom(length int) ([]byte, error) {
s.Lock()
defer s.Unlock()
return s.ctx.GenerateRandom(s.handle, length)
}
func (s *sessionImpl) CreateObject(template []*pkcs11.Attribute) (Object, error) {
s.Lock()
defer s.Unlock()
oh, err := s.ctx.CreateObject(s.handle, template)
if err != nil {
return Object{}, err
}
return Object{
session: s,
objectHandle: oh,
}, nil
}
func (s *sessionImpl) InitPIN(pin string) error {
s.Lock()
defer s.Unlock()
return s.ctx.InitPIN(s.handle, pin)
}
func (s *sessionImpl) SetPIN(old, new string) error {
s.Lock()
defer s.Unlock()
return s.ctx.SetPIN(s.handle, old, new)
}
// KeyPair contains two Objects: one for a public key and one for a private key.
// It represents these as PublicKey and PrivateKey types so they can by used for
// appropriate cryptographic operations.
type KeyPair struct {
Public PublicKey
Private PrivateKey
}
// GenerateKeyPairRequest contains the fields used to generate a key pair.
type GenerateKeyPairRequest struct {
Mechanism pkcs11.Mechanism
PublicKeyAttributes []*pkcs11.Attribute
PrivateKeyAttributes []*pkcs11.Attribute
}
func (s *sessionImpl) GenerateKeyPair(request GenerateKeyPairRequest) (*KeyPair, error) {
s.Lock()
defer s.Unlock()
pubHandle, privHandle, err := s.ctx.GenerateKeyPair(s.handle,
[]*pkcs11.Mechanism{&request.Mechanism},
request.PublicKeyAttributes,
request.PrivateKeyAttributes)
if err != nil {
return nil, err
}
return &KeyPair{
Public: PublicKey(Object{
session: s,
objectHandle: pubHandle,
}),
Private: PrivateKey(Object{
session: s,
objectHandle: privHandle,
}),
}, nil
}
|