Skip to content

Commit cfcea3d

Browse files
committed
piv: permit use of shared smartcard connections
1 parent 1902689 commit cfcea3d

File tree

8 files changed

+419
-271
lines changed

8 files changed

+419
-271
lines changed

.github/workflows/test.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ jobs:
2222
id: go
2323
- name: Check out code into the Go module directory
2424
uses: actions/checkout@v2
25-
- name: Install staticcheck
26-
run: go install honnef.co/go/tools/cmd/[email protected]
2725
- name: Install libpcsc
2826
run: sudo apt-get install -y libpcsclite-dev pcscd pcsc-tools
2927
- name: Test
@@ -42,8 +40,6 @@ jobs:
4240
id: go
4341
- name: Check out code into the Go module directory
4442
uses: actions/checkout@v2
45-
- name: Install staticcheck
46-
run: go install honnef.co/go/tools/cmd/[email protected]
4743
- name: Test
4844
run: "make build"
4945
env:

Makefile

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
.PHONY: test
2-
test: lint
2+
test:
33
go test -v ./...
44

5-
.PHONY: lint
6-
lint:
7-
staticcheck ./...
8-
95
.PHONY: build
10-
build: lint
6+
build:
117
go build ./...

piv/key.go

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,12 @@ func (yk *YubiKey) AttestationCertificate() (*x509.Certificate, error) {
507507
//
508508
// If the slot doesn't have a key, the returned error wraps ErrNotFound.
509509
func (yk *YubiKey) Attest(slot Slot) (*x509.Certificate, error) {
510-
cert, err := ykAttest(yk.tx, slot)
510+
var cert *x509.Certificate
511+
err := yk.tx(func(tx *scTx) error {
512+
var err error
513+
cert, err = ykAttest(tx, slot)
514+
return err
515+
})
511516
if err == nil {
512517
return cert, nil
513518
}
@@ -558,7 +563,12 @@ func (yk *YubiKey) Certificate(slot Slot) (*x509.Certificate, error) {
558563
byte(slot.Object),
559564
},
560565
}
561-
resp, err := yk.tx.Transmit(cmd)
566+
var resp []byte
567+
err := yk.tx(func(tx *scTx) error {
568+
var err error
569+
resp, err = tx.Transmit(cmd)
570+
return err
571+
})
562572
if err != nil {
563573
return nil, fmt.Errorf("command failed: %w", err)
564574
}
@@ -605,10 +615,12 @@ func marshalASN1(tag byte, data []byte) []byte {
605615
// certificate isn't required to use the associated key for signing or
606616
// decryption.
607617
func (yk *YubiKey) SetCertificate(key [24]byte, slot Slot, cert *x509.Certificate) error {
608-
if err := ykAuthenticate(yk.tx, key, yk.rand); err != nil {
609-
return fmt.Errorf("authenticating with management key: %w", err)
610-
}
611-
return ykStoreCertificate(yk.tx, slot, cert)
618+
return yk.tx(func(tx *scTx) error {
619+
if err := ykAuthenticate(tx, key, yk.rand); err != nil {
620+
return fmt.Errorf("authenticating with management key: %w", err)
621+
}
622+
return ykStoreCertificate(tx, slot, cert)
623+
})
612624
}
613625

614626
func ykStoreCertificate(tx *scTx, slot Slot, cert *x509.Certificate) error {
@@ -659,10 +671,17 @@ type Key struct {
659671
// GenerateKey generates an asymmetric key on the card, returning the key's
660672
// public key.
661673
func (yk *YubiKey) GenerateKey(key [24]byte, slot Slot, opts Key) (crypto.PublicKey, error) {
662-
if err := ykAuthenticate(yk.tx, key, yk.rand); err != nil {
663-
return nil, fmt.Errorf("authenticating with management key: %w", err)
664-
}
665-
return ykGenerateKey(yk.tx, slot, opts)
674+
var pub crypto.PublicKey
675+
err := yk.tx(func(tx *scTx) error {
676+
if err := ykAuthenticate(tx, key, yk.rand); err != nil {
677+
return fmt.Errorf("authenticating with management key: %w", err)
678+
}
679+
680+
var err error
681+
pub, err = ykGenerateKey(tx, slot, opts)
682+
return err
683+
})
684+
return pub, err
666685
}
667686

668687
func ykGenerateKey(tx *scTx, slot Slot, o Key) (crypto.PublicKey, error) {
@@ -731,8 +750,12 @@ type KeyAuth struct {
731750
// If provided, PINPrompt is ignored.
732751
PIN string
733752
// PINPrompt can be used to interactively request the PIN from the user. The
734-
// method is only called when needed. For example, if a key specifies
735-
// PINPolicyOnce, PINPrompt will only be called once per YubiKey struct.
753+
// method is only called when needed for exclusive connections. If a key
754+
// specifies PINPolicyOnce, PINPrompt will only be called once per YubiKey
755+
// struct.
756+
//
757+
// Clients using a shared connection will call the PIN prompt on every
758+
// operation, regardless of PIN policy.
736759
PINPrompt func() (pin string, err error)
737760

738761
// PINPolicy can be used to specify the PIN caching strategy for the slot. If
@@ -743,7 +766,7 @@ type KeyAuth struct {
743766
PINPolicy PINPolicy
744767
}
745768

746-
func (k KeyAuth) authTx(yk *YubiKey, pp PINPolicy) error {
769+
func (k KeyAuth) authTx(tx *scTx, pp PINPolicy) error {
747770
// PINPolicyNever shouldn't require a PIN.
748771
if pp == PINPolicyNever {
749772
return nil
@@ -752,7 +775,7 @@ func (k KeyAuth) authTx(yk *YubiKey, pp PINPolicy) error {
752775
// PINPolicyAlways should always prompt a PIN even if the key says that
753776
// login isn't needed.
754777
// https://github.com/go-piv/piv-go/issues/49
755-
if pp != PINPolicyAlways && !ykLoginNeeded(yk.tx) {
778+
if pp != PINPolicyAlways && !ykLoginNeeded(tx) {
756779
return nil
757780
}
758781

@@ -767,14 +790,20 @@ func (k KeyAuth) authTx(yk *YubiKey, pp PINPolicy) error {
767790
if pin == "" {
768791
return fmt.Errorf("pin required but wasn't provided")
769792
}
770-
return ykLogin(yk.tx, pin)
793+
return ykLogin(tx, pin)
771794
}
772795

773796
func (k KeyAuth) do(yk *YubiKey, pp PINPolicy, f func(tx *scTx) ([]byte, error)) ([]byte, error) {
774-
if err := k.authTx(yk, pp); err != nil {
775-
return nil, err
776-
}
777-
return f(yk.tx)
797+
var b []byte
798+
err := yk.tx(func(tx *scTx) error {
799+
var err error
800+
if err := k.authTx(tx, pp); err != nil {
801+
return err
802+
}
803+
b, err = f(tx)
804+
return err
805+
})
806+
return b, err
778807
}
779808

780809
func pinPolicy(yk *YubiKey, slot Slot) (PINPolicy, error) {
@@ -919,11 +948,12 @@ func (yk *YubiKey) SetPrivateKeyInsecure(key [24]byte, slot Slot, private crypto
919948
tags = append(tags, param...)
920949
}
921950

922-
if err := ykAuthenticate(yk.tx, key, yk.rand); err != nil {
923-
return fmt.Errorf("authenticating with management key: %w", err)
924-
}
925-
926-
return ykImportKey(yk.tx, tags, slot, policy)
951+
return yk.tx(func(tx *scTx) error {
952+
if err := ykAuthenticate(tx, key, yk.rand); err != nil {
953+
return fmt.Errorf("authenticating with management key: %w", err)
954+
}
955+
return ykImportKey(tx, tags, slot, policy)
956+
})
927957
}
928958

929959
func ykImportKey(tx *scTx, tags []byte, slot Slot, o Key) error {

piv/pcsc_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func runHandleTest(t *testing.T, f func(t *testing.T, h *scHandle)) {
6363
if reader == "" {
6464
t.Skip("could not find yubikey, skipping testing")
6565
}
66-
h, err := c.Connect(reader)
66+
h, err := c.Connect(reader, false)
6767
if err != nil {
6868
t.Fatalf("connecting to %s: %v", reader, err)
6969
}

piv/pcsc_unix.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,17 @@ type scHandle struct {
9292
h C.SCARDHANDLE
9393
}
9494

95-
func (c *scContext) Connect(reader string) (*scHandle, error) {
95+
func (c *scContext) Connect(reader string, shared bool) (*scHandle, error) {
9696
var (
9797
handle C.SCARDHANDLE
9898
activeProtocol C.DWORD
9999
)
100+
var mode C.DWORD = C.SCARD_SHARE_EXCLUSIVE
101+
if shared {
102+
mode = C.SCARD_SHARE_SHARED
103+
}
100104
rc := C.SCardConnect(c.ctx, C.CString(reader),
101-
C.SCARD_SHARE_EXCLUSIVE, C.SCARD_PROTOCOL_T1,
105+
mode, C.SCARD_PROTOCOL_T1,
102106
&handle, &activeProtocol)
103107
if err := scCheck(rc); err != nil {
104108
return nil, err

piv/pcsc_windows.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ var (
3535
const (
3636
scardScopeSystem = 2
3737
scardShareExclusive = 1
38+
scardShareShared = 2
3839
scardLeaveCard = 0
3940
scardProtocolT1 = 2
4041
scardPCIT1 = 0
@@ -122,7 +123,7 @@ func (c *scContext) ListReaders() ([]string, error) {
122123
return readers, nil
123124
}
124125

125-
func (c *scContext) Connect(reader string) (*scHandle, error) {
126+
func (c *scContext) Connect(reader string, shared bool) (*scHandle, error) {
126127
var (
127128
handle syscall.Handle
128129
activeProtocol uint16
@@ -131,10 +132,15 @@ func (c *scContext) Connect(reader string) (*scHandle, error) {
131132
if err != nil {
132133
return nil, fmt.Errorf("invalid reader string: %v", err)
133134
}
135+
136+
mode := uintptr(scardShareExclusive)
137+
if shared {
138+
mode = scardShareShared
139+
}
134140
r0, _, _ := procSCardConnectW.Call(
135141
uintptr(c.ctx),
136142
uintptr(unsafe.Pointer(readerPtr)),
137-
scardShareExclusive,
143+
mode,
138144
scardProtocolT1,
139145
uintptr(unsafe.Pointer(&handle)),
140146
uintptr(activeProtocol),

0 commit comments

Comments
 (0)