1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package assuan
18
19
20
21 import (
22 "bytes"
23 "crypto"
24 "crypto/rsa"
25 "errors"
26 "fmt"
27 "math/big"
28 "strings"
29
30 "github.com/sassoftware/relic/lib/dlog"
31 "github.com/sassoftware/relic/lib/x509tools"
32 "github.com/sassoftware/relic/signers/sigerrors"
33 )
34
35 type ScdConn struct {
36 *Conn
37 Serial string
38 }
39
40 type ScdKey struct {
41 Serial string
42 Fingerprint string
43 KeyGrip string
44 KeyId string
45
46 conn *ScdConn
47 }
48
49 func DialScd(path string) (*ScdConn, error) {
50 conn, err := Dial(path)
51 if err != nil {
52 return nil, err
53 }
54 return &ScdConn{Conn: conn}, nil
55 }
56
57
58 func (s *ScdConn) Learn() ([]*ScdKey, error) {
59 res, err := s.Conn.Transact("LEARN", func(inquiry string, lines []string) (string, error) {
60 return "", nil
61 })
62 if err != nil {
63 return nil, fmt.Errorf("failed to enumerate token: %s", err)
64 }
65 var keyids, keygrips []string
66 fingerprints := make(map[string]string)
67 for _, line := range res.Lines {
68 parts := strings.Split(line, " ")
69 switch parts[0] {
70 case "SERIALNO":
71 if len(parts) < 2 {
72 continue
73 }
74 s.Serial = parts[1]
75 case "KEYPAIRINFO":
76 if len(parts) < 3 || parts[1] == "X" {
77 continue
78 }
79 keygrips = append(keygrips, parts[1])
80 keyids = append(keyids, parts[2])
81 case "KEY-FPR":
82 if len(parts) < 3 {
83 continue
84 }
85 keyid := "OPENPGP." + parts[1]
86 fingerprints[keyid] = parts[2]
87 }
88 }
89 if len(keyids) == 0 {
90 return nil, errors.New("failed to enumerate token: no valid key found")
91 }
92 infos := make([]*ScdKey, 0, len(keyids))
93 dlog.Printf(3, "scdaemon token with serial %s has keys:", s.Serial)
94 for i, keyid := range keyids {
95 info := &ScdKey{
96 conn: s,
97 Serial: s.Serial,
98 KeyId: keyid,
99 KeyGrip: keygrips[i],
100 Fingerprint: fingerprints[keyid],
101 }
102 dlog.Printf(3, " keyid=%s keygrip=%s fingerprint=%s", info.KeyId, info.KeyGrip, info.Fingerprint)
103 infos = append(infos, info)
104 }
105 return infos, nil
106 }
107
108
109 func (s *ScdConn) CheckPin(pin string) error {
110 if s.Serial == "" {
111 infos, err := s.Learn()
112 if err != nil {
113 return err
114 }
115 s.Serial = infos[0].Serial
116 }
117 _, err := s.Conn.Transact("CHECKPIN "+s.Serial, func(inquiry string, lines []string) (string, error) {
118 if strings.HasPrefix(inquiry, "NEEDPIN") {
119 return pin + "\x00", nil
120 } else {
121 return "", fmt.Errorf("unexpected INQUIRE: %s", inquiry)
122 }
123 })
124 if eres, ok := err.(Response); ok && strings.Contains(eres.StatusMessage, "Bad PIN") {
125 return sigerrors.PinIncorrectError{}
126 } else if err != nil {
127 return fmt.Errorf("failed to validate PIN: %s", err)
128 }
129 return nil
130 }
131
132
133 func (k *ScdKey) Public() (crypto.PublicKey, error) {
134 res, err := k.conn.Transact("READKEY "+k.KeyId, nil)
135 if err != nil {
136 return nil, err
137 }
138 exp, err := parseCsExp(res.Blob)
139 if err != nil {
140 return nil, err
141 }
142 if len(exp.Items) != 1 {
143 return nil, errors.New("invalid public key in token")
144 }
145 exp = exp.Items[0]
146 if len(exp.Items) != 2 || !bytes.Equal(exp.Items[0].Value, []byte("public-key")) {
147 return nil, errors.New("invalid public key in token")
148 }
149 exp = exp.Items[1]
150 if len(exp.Items) == 0 {
151 return nil, errors.New("invalid public key in token")
152 }
153 keyType := string(exp.Items[0].Value)
154 values := make(map[string][]byte)
155 for _, item := range exp.Items[1:] {
156 if len(item.Items) != 2 {
157 return nil, errors.New("invalid public key in token")
158 }
159 name := string(item.Items[0].Value)
160 value := item.Items[1].Value
161 values[name] = value
162 }
163 switch keyType {
164 case "rsa":
165 n := values["n"]
166 e := values["e"]
167 if n == nil || e == nil {
168 return nil, errors.New("invalid RSA public key in token")
169 }
170 return &rsa.PublicKey{
171 N: new(big.Int).SetBytes(n),
172 E: int(new(big.Int).SetBytes(e).Int64()),
173 }, nil
174 default:
175 return nil, fmt.Errorf("unsupported public key of type %s in token", keyType)
176 }
177 }
178
179
180 func (k *ScdKey) Sign(hashValue []byte, opts crypto.SignerOpts, pin string) ([]byte, error) {
181 if opts == nil || opts.HashFunc() == 0 {
182 return nil, errors.New("Signer options are required")
183 } else if _, ok := opts.(*rsa.PSSOptions); ok {
184 return nil, errors.New("RSA-PSS not implemented")
185 }
186 hashName := x509tools.HashNames[opts.HashFunc()]
187 if hashName == "" {
188 return nil, errors.New("unsupported hash algorithm")
189 }
190 hashName = strings.ToLower(strings.Replace(hashName, "-", "", -1))
191 res, err := k.conn.Transact(fmt.Sprintf("SETDATA %X", hashValue), nil)
192 if err != nil {
193 return nil, err
194 }
195 res, err = k.conn.Transact(fmt.Sprintf("PKSIGN --hash=%s %s\n", hashName, k.KeyId),
196 func(inquiry string, lines []string) (string, error) {
197 if strings.HasPrefix(inquiry, "NEEDPIN") {
198 return pin + "\x00", nil
199 } else {
200 return "", fmt.Errorf("unexpected INQUIRE: %s", inquiry)
201 }
202 })
203 if eres, ok := err.(Response); ok && strings.Contains(eres.StatusMessage, "Bad PIN") {
204 return nil, sigerrors.PinIncorrectError{}
205 } else if err != nil {
206 return nil, fmt.Errorf("failed to sign: %s", err)
207 }
208 return res.Blob, nil
209 }
210
View as plain text