Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NFC and ISO7816 Support #111

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ if err != nil {
// Find a YubiKey and open the reader.
var yk *piv.YubiKey
for _, card := range cards {
if strings.Contains(strings.ToLower(card), "yubikey") {
if yk, err = piv.Open(card); err != nil {
// ...
if yk, err := piv.Open(card); err == nil {
status := yk.Status()
if !strings.Contains(strings.ToLower(string(status.Atr())), "ubike") {
continue
}
// ..
break
}
}
Expand Down
23 changes: 23 additions & 0 deletions piv/pcsc.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,29 @@ func (a *apduErr) Unwrap() error {
return nil
}

type scStatus struct {
reader string
state uint32
protocol uint32
atr []byte
}

func (s *scStatus) Reader() string {
return s.reader
}

func (s *scStatus) State() uint32 {
return s.state
}

func (s *scStatus) Protocol() uint32 {
return s.protocol
}

func (s *scStatus) Atr() []byte {
return s.atr
}

type apdu struct {
instruction byte
param1 byte
Expand Down
42 changes: 40 additions & 2 deletions piv/pcsc_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ func (c *scContext) ListReaders() ([]string, error) {
}

type scHandle struct {
h C.SCARDHANDLE
h C.SCARDHANDLE
status scStatus
}

func (c *scContext) Connect(reader string) (*scHandle, error) {
Expand All @@ -103,7 +104,44 @@ func (c *scContext) Connect(reader string) (*scHandle, error) {
if err := scCheck(rc); err != nil {
return nil, err
}
return &scHandle{handle}, nil

var readerNameLen C.DWORD
var atrLen C.DWORD

C.SCardStatus(
handle,
nil,
&readerNameLen,
nil,
nil,
nil,
&atrLen,
)

var state uint32
var protocol uint32

readerName := make([]byte, readerNameLen)
atr := make([]byte, atrLen)

C.SCardStatus(
handle,
(*C.char)(unsafe.Pointer(&readerName[0])),
(*C.DWORD)(unsafe.Pointer(&readerNameLen)),
(*C.DWORD)(unsafe.Pointer(&state)),
(*C.DWORD)(unsafe.Pointer(&protocol)),
(*C.uchar)(unsafe.Pointer(&atr[0])),
(*C.DWORD)(unsafe.Pointer(&atrLen)),
)

status := scStatus{
reader: string(readerName),
state: state,
protocol: protocol,
atr: atr,
}

return &scHandle{handle, status}, nil
}

func (h *scHandle) Close() error {
Expand Down
52 changes: 48 additions & 4 deletions piv/pcsc_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var (
winscard = syscall.NewLazyDLL("Winscard.dll")
procSCardEstablishContext = winscard.NewProc("SCardEstablishContext")
procSCardListReadersW = winscard.NewProc("SCardListReadersW")
procSCardStatusW = winscard.NewProc("SCardStatusW")
procSCardReleaseContext = winscard.NewProc("SCardReleaseContext")
procSCardConnectW = winscard.NewProc("SCardConnectW")
procSCardDisconnect = winscard.NewProc("SCardDisconnect")
Expand Down Expand Up @@ -122,6 +123,11 @@ func (c *scContext) ListReaders() ([]string, error) {
return readers, nil
}

type scHandle struct {
handle syscall.Handle
status scStatus
}

func (c *scContext) Connect(reader string) (*scHandle, error) {
var (
handle syscall.Handle
Expand All @@ -142,11 +148,49 @@ func (c *scContext) Connect(reader string) (*scHandle, error) {
if err := scCheck(r0); err != nil {
return nil, err
}
return &scHandle{handle}, nil
}

type scHandle struct {
handle syscall.Handle
var readerNameLen uint32
var atrLen uint32
r0, _, _ = procSCardStatusW.Call(
uintptr(handle),
uintptr(unsafe.Pointer(nil)),
uintptr(unsafe.Pointer(&readerNameLen)),
uintptr(unsafe.Pointer(nil)),
uintptr(unsafe.Pointer(nil)),
uintptr(unsafe.Pointer(nil)),
uintptr(unsafe.Pointer(&atrLen)),
)
if err := scCheck(r0); err != nil {
return nil, err
}

var state uint32
var protocol uint32

readerName := make([]uint16, readerNameLen)
atr := make([]byte, atrLen)

r0, _, _ = procSCardStatusW.Call(
uintptr(handle),
uintptr(unsafe.Pointer(&readerName[0])),
uintptr(unsafe.Pointer(&readerNameLen)),
uintptr(unsafe.Pointer(&state)),
uintptr(unsafe.Pointer(&protocol)),
uintptr(unsafe.Pointer(&atr[0])),
uintptr(unsafe.Pointer(&atrLen)),
)
if err := scCheck(r0); err != nil {
return nil, err
}

status := scStatus{
reader: syscall.UTF16ToString(readerName),
state: state,
protocol: protocol,
atr: atr,
}

return &scHandle{handle, status}, nil
}

func (h *scHandle) Close() error {
Expand Down
8 changes: 6 additions & 2 deletions piv/piv.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ func (yk *YubiKey) Version() Version {
}
}

func (yk *YubiKey) Status() scStatus {
return yk.h.status
}

// Serial returns the YubiKey's serial number.
func (yk *YubiKey) Serial() (uint32, error) {
return ykSerial(yk.tx, yk.version)
Expand All @@ -216,15 +220,15 @@ func encodePIN(pin string) ([]byte, error) {
return data, nil
}

// authPIN attempts to authenticate against the card with the provided PIN.
// AuthPIN attempts to authenticate against the card with the provided PIN.
// The PIN is required to use and modify certain slots.
//
// After a specific number of authentication attemps with an invalid PIN,
// usually 3, the PIN will become block and refuse further attempts. At that
// point the PUK must be used to unblock the PIN.
//
// Use DefaultPIN if the PIN hasn't been set.
func (yk *YubiKey) authPIN(pin string) error {
func (yk *YubiKey) AuthPIN(pin string) error {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: If an application wants to cache a pin, then it is helpful if it has a way to determine if it's even valid first before trying to use it later.

return ykLogin(yk.tx, pin)
}

Expand Down
4 changes: 2 additions & 2 deletions piv/piv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func TestYubiKeyReset(t *testing.T) {
if err := yk.Reset(); err != nil {
t.Fatalf("resetting yubikey: %v", err)
}
if err := yk.authPIN(DefaultPIN); err != nil {
if err := yk.AuthPIN(DefaultPIN); err != nil {
t.Fatalf("login: %v", err)
}
}
Expand All @@ -199,7 +199,7 @@ func TestYubiKeyLogin(t *testing.T) {
yk, close := newTestYubiKey(t)
defer close()

if err := yk.authPIN(DefaultPIN); err != nil {
if err := yk.AuthPIN(DefaultPIN); err != nil {
t.Fatalf("login: %v", err)
}
}
Expand Down