1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package p11token
18
19 import (
20 "errors"
21 "fmt"
22 "io"
23 "runtime"
24 "sync"
25
26 "github.com/miekg/pkcs11"
27 "github.com/sassoftware/relic/config"
28 "github.com/sassoftware/relic/lib/passprompt"
29 "github.com/sassoftware/relic/signers/sigerrors"
30 "github.com/sassoftware/relic/token"
31 )
32
33 const (
34 CKS_RO_PUBLIC_SESSION = 0
35 CKS_RO_USER_FUNCTIONS = 1
36 CKS_RW_PUBLIC_SESSION = 2
37 CKS_RW_USER_FUNCTIONS = 3
38 CKS_RW_SO_FUNCTIONS = 4
39
40 CKA_ID = pkcs11.CKA_ID
41 CKA_LABEL = pkcs11.CKA_LABEL
42 CKA_SERIAL_NUMBER = pkcs11.CKA_SERIAL_NUMBER
43
44 CKK_RSA = pkcs11.CKK_RSA
45 CKK_ECDSA = pkcs11.CKK_ECDSA
46 )
47
48 func init() {
49 token.Openers["pkcs11"] = open
50 token.Listers["pkcs11"] = List
51 }
52
53 var providerMap map[string]*pkcs11.Ctx
54 var providerMutex sync.Mutex
55
56 type Token struct {
57 config *config.Config
58 tokenConf *config.TokenConfig
59 ctx *pkcs11.Ctx
60 sh pkcs11.SessionHandle
61 mutex sync.Mutex
62 }
63
64 func List(provider string, output io.Writer) error {
65 ctx := pkcs11.New(provider)
66 if ctx == nil {
67 return errors.New("Failed to initialize pkcs11 provider")
68 }
69 defer ctx.Destroy()
70 if err := ctx.Initialize(); err != nil {
71 return err
72 }
73 slots, err := ctx.GetSlotList(false)
74 if err != nil {
75 return err
76 }
77 for _, slot := range slots {
78 info, err := ctx.GetTokenInfo(slot)
79 if rv, ok := err.(pkcs11.Error); ok && rv == pkcs11.CKR_TOKEN_NOT_PRESENT {
80 continue
81 }
82 fmt.Fprintf(output, "slot %d:\n manuf: %s\n model: %s\n label: %s\n serial: %s\n", slot, info.ManufacturerID, info.Model, info.Label, info.SerialNumber)
83 }
84 return nil
85 }
86
87
88 func Open(config *config.Config, tokenName string, pinProvider passprompt.PasswordGetter) (*Token, error) {
89 tokenConf, err := config.GetToken(tokenName)
90 if err != nil {
91 return nil, err
92 }
93 ctx, err := openLib(tokenConf, true)
94 if err != nil {
95 return nil, err
96 }
97 tok := &Token{
98 ctx: ctx,
99 config: config,
100 tokenConf: tokenConf,
101 }
102 runtime.SetFinalizer(tok, (*Token).Close)
103 slot, err := tok.findSlot()
104 if err != nil {
105 tok.Close()
106 return nil, err
107 }
108 mode := uint(pkcs11.CKF_SERIAL_SESSION | pkcs11.CKF_RW_SESSION)
109 sh, err := tok.ctx.OpenSession(slot, mode)
110 if err != nil {
111 tok.Close()
112 return nil, err
113 }
114 tok.sh = sh
115 err = tok.autoLogIn(pinProvider)
116 if err != nil {
117 tok.Close()
118 return nil, err
119 }
120 return tok, nil
121 }
122
123
124 func open(cfg *config.Config, tokenName string, prompt passprompt.PasswordGetter) (token.Token, error) {
125 return Open(cfg, tokenName, prompt)
126 }
127
128 func openLib(tokenConf *config.TokenConfig, write bool) (*pkcs11.Ctx, error) {
129 if tokenConf.Provider == "" {
130 return nil, errors.New("Missing attribute \"provider\" in token configuration")
131 }
132 providerMutex.Lock()
133 defer providerMutex.Unlock()
134 if providerMap == nil {
135 providerMap = make(map[string]*pkcs11.Ctx)
136 }
137 ctx, ok := providerMap[tokenConf.Provider]
138 if ok {
139 return ctx, nil
140 }
141 ctx = pkcs11.New(tokenConf.Provider)
142 if ctx == nil {
143 return nil, errors.New("Failed to initialize pkcs11 provider")
144 }
145 err := ctx.Initialize()
146 if err != nil {
147 ctx.Destroy()
148 return nil, err
149 }
150 providerMap[tokenConf.Provider] = ctx
151 return ctx, nil
152 }
153
154
155 func (tok *Token) Close() error {
156 tok.mutex.Lock()
157 defer tok.mutex.Unlock()
158 if tok.ctx != nil {
159 tok.ctx.CloseSession(tok.sh)
160 tok.ctx = nil
161 runtime.SetFinalizer(tok, nil)
162 }
163 return nil
164 }
165
166 func (tok *Token) Config() *config.TokenConfig {
167 return tok.tokenConf
168 }
169
170 func (tok *Token) findSlot() (uint, error) {
171 tokenConf := tok.tokenConf
172 slots, err := tok.ctx.GetSlotList(false)
173 if err != nil {
174 return 0, nil
175 }
176 candidates := make([]uint, 0, len(slots))
177 for _, slot := range slots {
178 info, err := tok.ctx.GetTokenInfo(slot)
179 if err != nil {
180 if rv, ok := err.(pkcs11.Error); ok && rv == pkcs11.CKR_TOKEN_NOT_PRESENT {
181 continue
182 }
183 return 0, err
184 }
185 if tokenConf.Label != "" && tokenConf.Label != info.Label {
186 continue
187 } else if tokenConf.Serial != "" && tokenConf.Serial != info.SerialNumber {
188 continue
189 }
190 candidates = append(candidates, slot)
191 }
192 if len(candidates) == 0 {
193 return 0, errors.New("No token found with the specified attributes")
194 } else if len(candidates) != 1 {
195 return 0, errors.New("Multiple tokens matched the specified attributes")
196 } else {
197 return candidates[0], nil
198 }
199 }
200
201
202 func (tok *Token) isLoggedIn() (bool, error) {
203 tok.mutex.Lock()
204 defer tok.mutex.Unlock()
205 info, err := tok.ctx.GetSessionInfo(tok.sh)
206 if err != nil {
207 return false, err
208 }
209 return (info.State == CKS_RO_USER_FUNCTIONS || info.State == CKS_RW_USER_FUNCTIONS || info.State == CKS_RW_SO_FUNCTIONS), nil
210 }
211
212 func (tok *Token) Ping() error {
213 loggedIn, err := tok.isLoggedIn()
214 if err != nil {
215 return err
216 } else if !loggedIn {
217 return errors.New("token not logged in")
218 }
219 return nil
220 }
221
222 func (tok *Token) login(user uint, pin string) error {
223 tok.mutex.Lock()
224 defer tok.mutex.Unlock()
225 err := tok.ctx.Login(tok.sh, user, pin)
226 if err != nil {
227 if rv, ok := err.(pkcs11.Error); ok && rv == pkcs11.CKR_PIN_INCORRECT {
228 return sigerrors.PinIncorrectError{}
229 }
230 }
231 return err
232 }
233
234 func (tok *Token) autoLogIn(pinProvider passprompt.PasswordGetter) error {
235 tokenConf := tok.tokenConf
236 loggedIn, err := tok.isLoggedIn()
237 if err != nil {
238 return err
239 }
240 if loggedIn {
241 return nil
242 }
243 user := pkcs11.CKU_USER
244 if tokenConf.User != nil {
245 user = *tokenConf.User
246 }
247 loginFunc := func(pin string) (bool, error) {
248 if err := tok.login(user, pin); err == nil {
249 return true, nil
250 } else if _, ok := err.(sigerrors.PinIncorrectError); ok {
251 return false, nil
252 } else {
253 return false, err
254 }
255 }
256 initialPrompt := fmt.Sprintf("PIN for token %s user %08x: ", tokenConf.Name(), user)
257 keyringUser := fmt.Sprintf("%s.%08x", tokenConf.Name(), user)
258 return token.Login(tokenConf, pinProvider, loginFunc, keyringUser, initialPrompt)
259 }
260
261 func (tok *Token) getAttribute(handle pkcs11.ObjectHandle, attr uint) []byte {
262 attrs, err := tok.ctx.GetAttributeValue(tok.sh, handle, []*pkcs11.Attribute{pkcs11.NewAttribute(attr, nil)})
263 if err != nil {
264 return nil
265 }
266 return attrs[0].Value
267 }
268
269 func (tok *Token) findObject(attrs []*pkcs11.Attribute) ([]pkcs11.ObjectHandle, error) {
270 if err := tok.ctx.FindObjectsInit(tok.sh, attrs); err != nil {
271 return nil, err
272 }
273 objects, _, err := tok.ctx.FindObjects(tok.sh, 10)
274 if err != nil {
275 tok.ctx.FindObjectsFinal(tok.sh)
276 return nil, err
277 }
278 if err := tok.ctx.FindObjectsFinal(tok.sh); err != nil {
279 return nil, err
280 }
281 return objects, nil
282 }
283
284 func attrToInt(value []byte) uint {
285 var n uint
286 for i := len(value) - 1; i >= 0; i-- {
287 n = n<<8 | uint(value[i])
288 }
289 return n
290 }
291
View as plain text