Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 39877e9

Browse files
committedAug 10, 2021
tmp
1 parent fadc40b commit 39877e9

File tree

5 files changed

+469
-44
lines changed

5 files changed

+469
-44
lines changed
 

‎piv/internal/pcsc/pcsc.go

+280-38
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,33 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
// Package pcsc implements the libpcsclite protocol for communicating with pcscd.
16+
//
17+
// This package is a pure Go implementation of libpcsclite, allowing piv-go to
18+
// communicate directly with pcscd without cgo. This still relies on pcscd to
19+
// communicate with the OS, and will be less reliable than linking against the
20+
// shared libraries provided by the pcscd packages.
21+
//
22+
// This package will NOT work with the native Mac and Windows libraries, which
23+
// are provided directly by the OS. Though pcsclite can be installed on Mac.
1524
package pcsc
1625

1726
import (
1827
"bytes"
1928
"context"
2029
"encoding/binary"
30+
"encoding/hex"
2131
"fmt"
2232
"io"
2333
"net"
2434
"os"
2535
)
2636

37+
// pcscd messages directly encode C structs over a unix domain socket. Determine
38+
// the host's byte order with build tags so encoding/binary can replicate this
39+
// behavior.
40+
//
41+
// https://groups.google.com/g/golang-nuts/c/3GEzwKfRRQw/m/ppkJKrT4cfAJ
2742
var nativeByteOrder binary.ByteOrder
2843

2944
const (
@@ -32,40 +47,82 @@ const (
3247
)
3348

3449
const (
35-
scardSSuccess = 0
50+
// RVSuccess is a return value indicating the operation succeeded.
51+
RVSuccess = 0
52+
53+
majorVersion = 4
54+
minorVersion = 4
3655

3756
// https://github.com/LudovicRousseau/PCSC/blob/1.9.0/src/winscard_msg.h#L76
38-
commandVersion = 0x11
39-
commandGetReadersState = 0x12
57+
commandEstablishContext = 0x01
58+
commandReleaseContext = 0x02
59+
commandConnect = 0x04
60+
commandDisconnect = 0x05
61+
commandBeginTransaction = 0x07
62+
commandEndTransaction = 0x08
63+
commandTransmit = 0x09
64+
commandVersion = 0x11
65+
commandGetReadersState = 0x12
66+
67+
// Context modes to be passed to NewContext.
68+
//
69+
// https://github.com/LudovicRousseau/PCSC/blob/1.9.0/src/PCSC/pcsclite.h.in#L248
70+
Exclusive = 0x0001
71+
Shared = 0x0002
72+
Direct = 0x0003
73+
74+
// Different protocols that can be used when connecting to a card.
75+
ProtocolT0 = 0x0001
76+
ProtocolT1 = 0x0002
77+
ProtocolRaw = 0x0004
78+
ProtocolT15 = 0x0005
79+
80+
// Disconnect option.
81+
LeaveCard = 0x0000
82+
ResetCard = 0x0001
83+
UnpowerCard = 0x0002
84+
EjectCard = 0x0003
4085
)
4186

87+
// RVError wraps an underlying PCSC error code.
88+
type RVError struct {
89+
RV uint32
90+
}
91+
92+
// Error returns a string encoding of the error code. Note that human readable
93+
// messages are not provided by this package, and are handled by the piv
94+
// package instead.
95+
func (r *RVError) Error() string {
96+
return fmt.Sprintf("rv 0x%x", r.RV)
97+
}
98+
99+
// Client represents a connection with the pcscd process.
42100
type Client struct {
43101
conn net.Conn
44102
}
45103

104+
// Close releases the underlying connection. It does not release any contexts
105+
// which must be closed separately.
46106
func (c *Client) Close() error {
47107
return c.conn.Close()
48108
}
49109

50-
func (c *Client) version() (major, minor int32, err error) {
110+
func (c *Client) checkVersion() error {
51111
req := struct {
52112
Major int32
53113
Minor int32
54114
RV uint32
55-
}{4, 4, scardSSuccess}
56-
57-
var resp struct {
58-
Major int32
59-
Minor int32
60-
RV uint32
115+
}{majorVersion, minorVersion, RVSuccess}
116+
if err := c.sendMessage(commandVersion, req, &req); err != nil {
117+
return fmt.Errorf("send message: %v", err)
61118
}
62-
if err := c.sendMessage(commandVersion, req, &resp); err != nil {
63-
return 0, 0, fmt.Errorf("send message: %v", err)
119+
if req.RV != RVSuccess {
120+
return &RVError{RV: req.RV}
64121
}
65-
if resp.RV != scardSSuccess {
66-
return 0, 0, fmt.Errorf("invalid response value: %x", resp.RV)
122+
if req.Major != majorVersion {
123+
return fmt.Errorf("unsupported major version of pcscd protocol: %d", req.Major)
67124
}
68-
return resp.Major, resp.Minor, nil
125+
return nil
69126
}
70127

71128
const (
@@ -77,25 +134,14 @@ const (
77134
maxReaders = 16
78135
)
79136

80-
// https://github.com/LudovicRousseau/PCSC/blob/1.9.0/src/eventhandler.h#L52
81-
// typedef struct pubReaderStatesList
82-
// {
83-
// char readerName[MAX_READERNAME]; /**< reader name */
84-
// uint32_t eventCounter; /**< number of card events */
85-
// uint32_t readerState; /**< SCARD_* bit field */
86-
// int32_t readerSharing; /**< PCSCLITE_SHARING_* sharing status */
137+
// readerState holds metadata about a PCSC card.
87138
//
88-
// UCHAR cardAtr[MAX_ATR_SIZE]; /**< ATR */
89-
// uint32_t cardAtrLength; /**< ATR length */
90-
// uint32_t cardProtocol; /**< SCARD_PROTOCOL_* value */
91-
// }
92-
// READER_STATE;
93-
139+
// https://github.com/LudovicRousseau/PCSC/blob/1.9.0/src/eventhandler.h#L52
94140
type readerState struct {
95141
Name [maxReaderNameSize]byte
96142
EventCounter uint32
97143
State uint32
98-
Sharing uint32
144+
Sharing int32
99145

100146
Attr [maxAttributeSize]byte
101147
AttrSize uint32
@@ -115,11 +161,11 @@ func (r readerState) name() string {
115161
return string(r.Name[:i])
116162
}
117163

118-
func (c *Client) readers() ([]string, error) {
119-
var resp [maxReaders]readerState
120-
121-
if err := c.sendMessage(commandGetReadersState, nil, &resp); err != nil {
122-
return nil, fmt.Errorf("send message: %v", err)
164+
// Readers returns the names of all readers that are connected to the device.
165+
func (c *Client) Readers() ([]string, error) {
166+
resp, err := c.readers()
167+
if err != nil {
168+
return nil, err
123169
}
124170

125171
var names []string
@@ -132,6 +178,182 @@ func (c *Client) readers() ([]string, error) {
132178
return names, nil
133179
}
134180

181+
func (c *Client) readers() (states [maxReaders]readerState, err error) {
182+
if err := c.sendMessage(commandGetReadersState, nil, &states); err != nil {
183+
return states, fmt.Errorf("send message: %v", err)
184+
}
185+
return states, nil
186+
}
187+
188+
// https://github.com/LudovicRousseau/PCSC/blob/1.9.0/src/winscard_msg.h#L118
189+
type establishRequest struct {
190+
Scope uint32
191+
Context uint32
192+
RV uint32
193+
}
194+
195+
// Context holds an open PCSC context, which is required to perform actions
196+
// such as starting transactions or transmitting data to a card.
197+
type Context struct {
198+
client *Client
199+
context uint32
200+
}
201+
202+
// NewContext attempts to establish a context with the PCSC daemon. The returned
203+
// context is only valid while the client is open.
204+
func (c *Client) NewContext() (*Context, error) {
205+
const scopeSystem = 0x0002
206+
req := establishRequest{
207+
Scope: scopeSystem,
208+
RV: RVSuccess,
209+
}
210+
if err := c.sendMessage(commandEstablishContext, req, &req); err != nil {
211+
return nil, fmt.Errorf("establish context: %v", err)
212+
}
213+
if req.RV != RVSuccess {
214+
return nil, &RVError{RV: req.RV}
215+
}
216+
return &Context{client: c, context: req.Context}, nil
217+
}
218+
219+
// https://github.com/LudovicRousseau/PCSC/blob/1.9.0/src/winscard_msg.h#L118
220+
type releaseRequest struct {
221+
Context uint32
222+
RV uint32
223+
}
224+
225+
// Close releases the context with the PCSC daemon.
226+
func (c *Context) Close() error {
227+
req := releaseRequest{
228+
Context: c.context,
229+
RV: RVSuccess,
230+
}
231+
if err := c.client.sendMessage(commandReleaseContext, req, &req); err != nil {
232+
return fmt.Errorf("release context: %v", err)
233+
}
234+
if req.RV != RVSuccess {
235+
return &RVError{RV: req.RV}
236+
}
237+
return nil
238+
}
239+
240+
// Connection represents a connection to a specific smartcard.
241+
type Connection struct {
242+
client *Client
243+
context uint32
244+
card int32
245+
}
246+
247+
// https://github.com/LudovicRousseau/PCSC/blob/1.9.0/src/winscard_msg.h#L141
248+
type connectRequest struct {
249+
Context uint32
250+
Reader [maxReaderNameSize]byte
251+
ShareMode uint32
252+
PreferredProtocols uint32
253+
Card int32
254+
ActiveProtocols uint32
255+
RV uint32
256+
}
257+
258+
func (c *Context) Connect(reader string, mode uint32) (*Connection, error) {
259+
req := connectRequest{
260+
Context: c.context,
261+
ShareMode: Shared,
262+
PreferredProtocols: ProtocolT1,
263+
RV: RVSuccess,
264+
}
265+
266+
if len(reader)+1 > maxReaderNameSize {
267+
return nil, fmt.Errorf("reader name too long")
268+
}
269+
copy(req.Reader[:], []byte(reader))
270+
271+
if err := c.client.sendMessage(commandConnect, req, &req); err != nil {
272+
return nil, fmt.Errorf("send message: %v", err)
273+
}
274+
if req.RV != RVSuccess {
275+
return nil, &RVError{RV: req.RV}
276+
}
277+
if req.Card == 0 {
278+
return nil, fmt.Errorf("card returned no value")
279+
}
280+
return &Connection{
281+
client: c.client, context: c.context, card: req.Card,
282+
}, nil
283+
}
284+
285+
//https://github.com/LudovicRousseau/PCSC/blob/1.9.0/src/winscard_msg.h#L172
286+
type disconnectRequest struct {
287+
Card int32
288+
Disposition uint32
289+
RV uint32
290+
}
291+
292+
func (c *Connection) Close() error {
293+
req := disconnectRequest{
294+
Card: c.card,
295+
Disposition: LeaveCard,
296+
RV: RVSuccess,
297+
}
298+
299+
if err := c.client.sendMessage(commandDisconnect, req, &req); err != nil {
300+
return fmt.Errorf("send message: %v", err)
301+
}
302+
if req.RV != RVSuccess {
303+
return &RVError{RV: req.RV}
304+
}
305+
return nil
306+
}
307+
308+
// https://github.com/LudovicRousseau/PCSC/blob/1.9.0/src/winscard_msg.h#L184
309+
type beginRequest struct {
310+
Card int32
311+
RV uint32
312+
}
313+
314+
// BeginTransaction is called before transmitting data to the card.
315+
func (c *Connection) BeginTransaction() error {
316+
req := beginRequest{
317+
Card: c.card,
318+
RV: RVSuccess,
319+
}
320+
321+
if err := c.client.sendMessage(commandBeginTransaction, req, &req); err != nil {
322+
return fmt.Errorf("send message: %v", err)
323+
}
324+
if req.RV != RVSuccess {
325+
return &RVError{RV: req.RV}
326+
}
327+
return nil
328+
}
329+
330+
// https://github.com/LudovicRousseau/PCSC/blob/1.9.0/src/winscard_msg.h#L195
331+
type endRequest struct {
332+
Card int32
333+
Disposition uint32
334+
RV uint32
335+
}
336+
337+
func (c *Connection) EndTransaction() error {
338+
req := endRequest{
339+
Card: c.card,
340+
Disposition: LeaveCard,
341+
RV: RVSuccess,
342+
}
343+
344+
if err := c.client.sendMessage(commandEndTransaction, req, &req); err != nil {
345+
return fmt.Errorf("send message: %v", err)
346+
}
347+
if req.RV != RVSuccess {
348+
return &RVError{RV: req.RV}
349+
}
350+
return nil
351+
}
352+
353+
func (c *Connection) Transmit(b []byte) ([]byte, error) {
354+
return nil, nil
355+
}
356+
135357
func (c *Client) sendMessage(command uint32, req, resp interface{}) error {
136358
var data []byte
137359
if req != nil {
@@ -156,16 +378,33 @@ func (c *Client) sendMessage(command uint32, req, resp interface{}) error {
156378
return fmt.Errorf("write request bytes: %v", err)
157379
}
158380

159-
if err := binary.Read(c.conn, nativeByteOrder, resp); err != nil {
381+
respData := &bytes.Buffer{}
382+
if err := binary.Read(io.TeeReader(c.conn, respData), nativeByteOrder, resp); err != nil {
160383
return fmt.Errorf("read response: %v", err)
161384
}
385+
386+
fmt.Fprintln(os.Stderr, "Request:")
387+
fmt.Fprintln(os.Stderr, hex.Dump(data[8:]))
388+
389+
fmt.Fprintln(os.Stderr, "Response:")
390+
fmt.Fprintln(os.Stderr, hex.Dump(respData.Bytes()))
162391
return nil
163392
}
164393

394+
// Config is used to modify client behavior.
165395
type Config struct {
396+
// SocketPath can be used to override a path to the pcscd socket. This field
397+
// is generally not required unless pcscd has been compiled with modified
398+
// options.
399+
//
400+
// This value defaults to the pcsclite behavior, preferring the value of the
401+
// PCSCLITE_CSOCK_NAME environment variable then defaulting to
402+
// "/run/pcscd/pcscd.comm".
166403
SocketPath string
167404
}
168405

406+
// NewClient attempts to initialize a connection with pcscd. The context is used
407+
// for dialing the unix domain socket.
169408
func NewClient(ctx context.Context, c *Config) (*Client, error) {
170409
p := c.SocketPath
171410
if p == "" {
@@ -180,7 +419,10 @@ func NewClient(ctx context.Context, c *Config) (*Client, error) {
180419
if err != nil {
181420
return nil, fmt.Errorf("dial unix socket: %v", err)
182421
}
183-
return &Client{
184-
conn: conn,
185-
}, nil
422+
client := &Client{conn: conn}
423+
if err := client.checkVersion(); err != nil {
424+
client.Close()
425+
return nil, err
426+
}
427+
return client, nil
186428
}

‎piv/internal/pcsc/pcsc_test.go

+95-6
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@ func TestVersion(t *testing.T) {
2626
}
2727
defer c.Close()
2828

29-
major, minor, err := c.version()
30-
if err != nil {
31-
t.Fatalf("getting client version: %v", err)
29+
if err := c.checkVersion(); err != nil {
30+
t.Fatalf("checking client client version: %v", err)
3231
}
33-
t.Log(major, minor)
3432
}
3533

3634
func TestListReaders(t *testing.T) {
@@ -40,9 +38,100 @@ func TestListReaders(t *testing.T) {
4038
}
4139
defer c.Close()
4240

43-
readers, err := c.readers()
41+
if _, err := c.Readers(); err != nil {
42+
t.Fatalf("listing readers: %v", err)
43+
}
44+
}
45+
46+
func TestNewContext(t *testing.T) {
47+
c, err := NewClient(context.Background(), &Config{})
48+
if err != nil {
49+
t.Fatalf("create client: %v", err)
50+
}
51+
defer c.Close()
52+
53+
ctx, err := c.NewContext()
54+
if err != nil {
55+
t.Fatalf("create context: %v", err)
56+
}
57+
if err := ctx.Close(); err != nil {
58+
t.Fatalf("close context: %v", err)
59+
}
60+
}
61+
62+
func TestConnect(t *testing.T) {
63+
c, err := NewClient(context.Background(), &Config{})
64+
if err != nil {
65+
t.Fatalf("create client: %v", err)
66+
}
67+
defer c.Close()
68+
69+
ctx, err := c.NewContext()
70+
if err != nil {
71+
t.Fatalf("create context: %v", err)
72+
}
73+
defer func() {
74+
if err := ctx.Close(); err != nil {
75+
t.Fatalf("close context: %v", err)
76+
}
77+
}()
78+
79+
readers, err := c.Readers()
4480
if err != nil {
4581
t.Fatalf("listing readers: %v", err)
4682
}
47-
t.Log(readers)
83+
if len(readers) == 0 {
84+
t.Skipf("no readers available for test")
85+
}
86+
reader := readers[0]
87+
88+
card, err := ctx.Connect(reader, Exclusive)
89+
if err != nil {
90+
t.Fatalf("new card: %v", err)
91+
}
92+
if err := card.Close(); err != nil {
93+
t.Fatalf("close card: %v", err)
94+
}
95+
}
96+
97+
func TestBeginTransaction(t *testing.T) {
98+
c, err := NewClient(context.Background(), &Config{})
99+
if err != nil {
100+
t.Fatalf("create client: %v", err)
101+
}
102+
defer c.Close()
103+
104+
ctx, err := c.NewContext()
105+
if err != nil {
106+
t.Fatalf("create context: %v", err)
107+
}
108+
defer func() {
109+
if err := ctx.Close(); err != nil {
110+
t.Fatalf("close context: %v", err)
111+
}
112+
}()
113+
114+
readers, err := c.Readers()
115+
if err != nil {
116+
t.Fatalf("listing readers: %v", err)
117+
}
118+
if len(readers) == 0 {
119+
t.Skipf("no readers available for test")
120+
}
121+
reader := readers[0]
122+
card, err := ctx.Connect(reader, Exclusive)
123+
if err != nil {
124+
t.Fatalf("new card: %v", err)
125+
}
126+
defer func() {
127+
if err := card.Close(); err != nil {
128+
t.Fatalf("close card: %v", err)
129+
}
130+
}()
131+
if err := card.BeginTransaction(); err != nil {
132+
t.Fatalf("begin transaction: %v", err)
133+
}
134+
if err := card.EndTransaction(); err != nil {
135+
t.Fatalf("end transaction: %v", err)
136+
}
48137
}

‎piv/pcsc_linux.go

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
// +build cgo,!pcscgo
16+
1517
package piv
1618

1719
import "C"

‎piv/pcsc_unix.go

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
// +build cgo,!pcscgo
1516
// +build darwin linux freebsd
1617

1718
package piv

‎piv/pcsc_unix_no_cgo.go

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// +build !cgo pcscgo
16+
// +build linux freebsd
17+
18+
package piv
19+
20+
import (
21+
"context"
22+
"errors"
23+
24+
"github.com/go-piv/piv-go/piv/internal/pcsc"
25+
)
26+
27+
func scCheck(err error) error {
28+
var e *pcsc.RVError
29+
if errors.As(err, &e) {
30+
return &scErr{int64(e.RV)}
31+
}
32+
return err
33+
}
34+
35+
const rcSuccess = pcsc.RVSuccess
36+
37+
type scContext struct {
38+
client *pcsc.Client
39+
ctx *pcsc.Context
40+
}
41+
42+
func newSCContext() (*scContext, error) {
43+
c, err := pcsc.NewClient(context.Background(), &pcsc.Config{})
44+
if err != nil {
45+
return nil, err
46+
}
47+
ctx, err := c.NewContext(pcsc.Exclusive)
48+
if err != nil {
49+
c.Close()
50+
return nil, err
51+
}
52+
return &scContext{c, ctx}, nil
53+
}
54+
55+
func (c *scContext) Close() error {
56+
err1 := c.ctx.Close()
57+
if err := c.client.Close(); err != nil {
58+
return err
59+
}
60+
return err1
61+
}
62+
63+
func (c *scContext) ListReaders() ([]string, error) {
64+
return c.client.Readers()
65+
}
66+
67+
type scHandle struct {
68+
}
69+
70+
func (c *scContext) Connect(reader string) (*scHandle, error) {
71+
return nil, nil
72+
}
73+
74+
func (h *scHandle) Close() error {
75+
return nil
76+
}
77+
78+
type scTx struct {
79+
}
80+
81+
func (h *scHandle) Begin() (*scTx, error) {
82+
return nil, nil
83+
}
84+
85+
func (t *scTx) Close() error {
86+
return nil
87+
}
88+
89+
func (t *scTx) transmit(req []byte) (more bool, b []byte, err error) {
90+
return false, nil, nil
91+
}

0 commit comments

Comments
 (0)
Please sign in to comment.