1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package scdtoken
18
19 import (
20 "crypto"
21 "crypto/ecdsa"
22 "crypto/rsa"
23 "crypto/x509"
24 "errors"
25 "fmt"
26 "io"
27 "os"
28 "strings"
29 "sync"
30
31 "github.com/sassoftware/relic/config"
32 "github.com/sassoftware/relic/lib/assuan"
33 "github.com/sassoftware/relic/lib/passprompt"
34 "github.com/sassoftware/relic/lib/x509tools"
35 "github.com/sassoftware/relic/signers/sigerrors"
36 "github.com/sassoftware/relic/token"
37 )
38
39 func init() {
40 token.Openers["scdtoken"] = Open
41 token.Listers["scdtoken"] = List
42 }
43
44 var defaultScdSockets = []string{
45 "/run/user/$UID/gnupg/S.scdaemon",
46 "/var/run/user/$UID/gnupg/S.scdaemon",
47 "$HOME/.gnupg/S.scdaemon",
48 }
49
50 type scdToken struct {
51 config *config.Config
52 tokenConf *config.TokenConfig
53 sock *assuan.ScdConn
54 serial, pin string
55 keyInfos []*assuan.ScdKey
56 mu sync.Mutex
57 }
58
59 type scdKey struct {
60 token *scdToken
61 keyConf *config.KeyConfig
62 key *assuan.ScdKey
63 publicKey crypto.PublicKey
64 }
65
66 func findSock() string {
67 uid := fmt.Sprintf("%d", os.Getuid())
68 for _, fp := range defaultScdSockets {
69 fp = strings.Replace(fp, "$UID", uid, -1)
70 fp = os.ExpandEnv(fp)
71 _, err := os.Stat(fp)
72 if err == nil {
73 return fp
74 }
75 }
76 return ""
77 }
78
79 func List(sockPath string, output io.Writer) error {
80 if sockPath == "" {
81 sockPath = findSock()
82 }
83 if sockPath == "" {
84 return errors.New("scdaemon not found; provide an explicit path to the scdaemon socket")
85 }
86 sock, err := assuan.DialScd(sockPath)
87 if err != nil {
88 return err
89 }
90 defer sock.Close()
91 keyInfos, err := sock.Learn()
92 if err != nil {
93 return err
94 }
95 if len(keyInfos) == 0 {
96 fmt.Fprintln(output, "token is empty or missing")
97 } else {
98 fmt.Fprintf(output, "serial: %s\n", keyInfos[0].Serial)
99 }
100 return nil
101 }
102
103 func Open(conf *config.Config, tokenName string, prompt passprompt.PasswordGetter) (token.Token, error) {
104 tconf, err := conf.GetToken(tokenName)
105 if err != nil {
106 return nil, err
107 }
108 sockPath := tconf.Provider
109 if sockPath == "" {
110 sockPath = findSock()
111 }
112 if sockPath == "" {
113 return nil, fmt.Errorf("scdaemon not found; set tokens.%s.provider to the path to the scdaemon socket", tokenName)
114 }
115 sock, err := assuan.DialScd(sockPath)
116 if err != nil {
117 return nil, err
118 }
119 tok := &scdToken{
120 config: conf,
121 tokenConf: tconf,
122 sock: sock,
123 }
124 if err := tok.login(prompt); err != nil {
125 sock.Close()
126 return nil, err
127 }
128 return tok, nil
129 }
130
131 func (tok *scdToken) login(prompt passprompt.PasswordGetter) error {
132 tconf := tok.tokenConf
133 keyInfos, err := tok.sock.Learn()
134 if err != nil {
135 return err
136 }
137 tok.keyInfos = keyInfos
138 tok.serial = keyInfos[0].Serial
139 if tconf.Serial != "" && tconf.Serial != tok.serial {
140 return fmt.Errorf("scdaemon token %s has serial %s but configuration specifies %s", tconf.Name(), tok.serial, tconf.Serial)
141 }
142 loginFunc := func(pin string) (bool, error) {
143 if err := tok.sock.CheckPin(pin); err == nil {
144 tok.pin = pin
145 return true, nil
146 } else if _, ok := err.(sigerrors.PinIncorrectError); ok {
147 return false, nil
148 } else {
149 return false, err
150 }
151 }
152 initialPrompt := fmt.Sprintf("PIN for token %s (serial %s): ", tconf.Name(), tok.serial)
153 keyringUser := tok.serial
154 return token.Login(tconf, prompt, loginFunc, keyringUser, initialPrompt)
155 }
156
157 func (tok *scdToken) Ping() error {
158 tok.mu.Lock()
159 defer tok.mu.Unlock()
160
161 return nil
162 }
163
164 func (tok *scdToken) Close() error {
165 tok.mu.Lock()
166 defer tok.mu.Unlock()
167 if tok.sock != nil {
168 tok.sock.Close()
169 tok.sock = nil
170 }
171 return nil
172 }
173
174 func (tok *scdToken) Config() *config.TokenConfig {
175 return tok.tokenConf
176 }
177
178 func (tok *scdToken) ListKeys(opts token.ListOptions) error {
179 tok.mu.Lock()
180 defer tok.mu.Unlock()
181 fmt.Fprintf(opts.Output, "serial: %#v\n", tok.serial)
182 for i, key := range tok.keyInfos {
183 if opts.ID != "" && opts.ID != key.KeyId {
184 continue
185 }
186 fmt.Fprintf(opts.Output, "key %d:\n id: %s\n fingerprint: %s\n keygrip: %s\n", i+1, key.KeyId, key.Fingerprint, key.KeyGrip)
187 if opts.Values {
188 pub, err := key.Public()
189 if err != nil {
190 fmt.Fprintln(opts.Output, " error reading key:", err)
191 continue
192 }
193 switch k := pub.(type) {
194 case *rsa.PublicKey:
195 fmt.Fprintf(opts.Output, " n: 0x%x\n e: %d\n", k.N, k.E)
196 case *ecdsa.PublicKey:
197 curve, err := x509tools.CurveByCurve(k.Curve)
198 if err == nil {
199 fmt.Fprintf(opts.Output, " bits: %d\n", curve.Bits)
200 }
201 fmt.Fprintf(opts.Output, " x: %x\n y: %x\n", k.X, k.Y)
202 }
203 }
204 }
205 return nil
206 }
207
208 func (tok *scdToken) GetKey(keyName string) (token.Key, error) {
209 tok.mu.Lock()
210 defer tok.mu.Unlock()
211 keyConf, err := tok.config.GetKey(keyName)
212 if err != nil {
213 return nil, err
214 }
215 var key *assuan.ScdKey
216 for _, kc := range tok.keyInfos {
217 if keyConf.ID != "" && keyConf.ID != kc.KeyId {
218 continue
219 }
220 key = kc
221 break
222 }
223 if key.KeyId == "" {
224 return nil, fmt.Errorf("key %s not found in token %s", keyName, tok.tokenConf.Name())
225 }
226 pubkey, err := key.Public()
227 if err != nil {
228 return nil, err
229 }
230 return &scdKey{
231 token: tok,
232 keyConf: keyConf,
233 key: key,
234 publicKey: pubkey,
235 }, nil
236 }
237
238 func (key *scdKey) Public() crypto.PublicKey {
239 return key.publicKey
240 }
241
242 func (key *scdKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
243 key.token.mu.Lock()
244 defer key.token.mu.Unlock()
245 return key.key.Sign(digest, opts, key.token.pin)
246 }
247
248 func (key *scdKey) Config() *config.KeyConfig {
249 return key.keyConf
250 }
251
252 func (key *scdKey) GetID() []byte {
253 return []byte(key.token.serial)
254 }
255
256 func (tok *scdToken) Import(keyName string, privKey crypto.PrivateKey) (token.Key, error) {
257 return nil, errors.New("function not implemented for tokens of type \"scdaemon\"")
258 }
259
260 func (tok *scdToken) ImportCertificate(cert *x509.Certificate, labelBase string) error {
261 return errors.New("function not implemented for tokens of type \"scdaemon\"")
262 }
263
264 func (tok *scdToken) Generate(keyName string, keyType token.KeyType, bits uint) (token.Key, error) {
265
266 return nil, errors.New("function not implemented for tokens of type \"scdaemon\"")
267 }
268
269 func (key *scdKey) ImportCertificate(cert *x509.Certificate) error {
270 return errors.New("function not implemented for tokens of type \"scdaemon\"")
271 }
272
View as plain text