2019-03-01 18:44:07 +01:00
package main
import (
"bufio"
"encoding/hex"
2019-03-15 16:08:15 +01:00
"errors"
2019-03-01 18:44:07 +01:00
"flag"
"fmt"
stdlog "log"
"os"
"strconv"
"strings"
"github.com/ebfe/scard"
"github.com/ethereum/go-ethereum/log"
)
2019-04-16 15:54:22 +02:00
var version string
2019-04-03 12:09:48 +02:00
2019-03-06 17:57:52 +01:00
type commandFunc func ( * scard . Card ) error
2019-03-01 18:44:07 +01:00
var (
logger = log . New ( "package" , "status-go/cmd/keycard" )
commands map [ string ] commandFunc
command string
2019-11-19 18:02:36 +01:00
flagCapFile = flag . String ( "a" , "" , "applet cap file path" )
flagOverwrite = flag . Bool ( "f" , false , "force applet installation if already installed" )
flagLogLevel = flag . String ( "l" , "" , ` Log level, one of: "error", "warn", "info", "debug", and "trace" ` )
flagNdefTemplate = flag . String ( "ndef" , "" , "Specify a URL to use in the NDEF record. Use the {{.cashAddress}} variable to get the cash address: http://example.com/{{.cashAddress}}." )
2019-03-01 18:44:07 +01:00
)
func initLogger ( ) {
if * flagLogLevel == "" {
* flagLogLevel = "info"
}
level , err := log . LvlFromString ( strings . ToLower ( * flagLogLevel ) )
if err != nil {
stdlog . Fatal ( err )
}
handler := log . StreamHandler ( os . Stderr , log . TerminalFormat ( true ) )
filteredHandler := log . LvlFilterHandler ( level , handler )
log . Root ( ) . SetHandler ( filteredHandler )
}
func init ( ) {
commands = map [ string ] commandFunc {
2019-04-03 12:09:48 +02:00
"version" : commandVersion ,
2019-03-01 18:44:07 +01:00
"install" : commandInstall ,
"info" : commandInfo ,
"delete" : commandDelete ,
"init" : commandInit ,
2019-03-13 13:49:26 +01:00
"pair" : commandPair ,
2019-03-13 16:20:51 +01:00
"status" : commandStatus ,
2019-03-15 10:39:17 +01:00
"shell" : commandShell ,
2019-03-01 18:44:07 +01:00
}
if len ( os . Args ) < 2 {
usage ( )
}
command = os . Args [ 1 ]
if len ( os . Args ) > 2 {
flag . CommandLine . Parse ( os . Args [ 2 : ] )
}
initLogger ( )
}
func usage ( ) {
fmt . Printf ( "\nUsage:\n keycard COMMAND [FLAGS]\n\nAvailable commands:\n" )
for name := range commands {
fmt . Printf ( " %s\n" , name )
}
fmt . Print ( "\nFlags:\n\n" )
flag . PrintDefaults ( )
os . Exit ( 1 )
}
func fail ( msg string , ctx ... interface { } ) {
logger . Error ( msg , ctx ... )
os . Exit ( 1 )
}
2019-03-28 12:45:16 +01:00
func waitForCard ( ctx * scard . Context , readers [ ] string ) ( int , error ) {
rs := make ( [ ] scard . ReaderState , len ( readers ) )
for i := range rs {
rs [ i ] . Reader = readers [ i ]
rs [ i ] . CurrentState = scard . StateUnaware
}
for {
for i := range rs {
if rs [ i ] . EventState & scard . StatePresent != 0 {
return i , nil
}
rs [ i ] . CurrentState = rs [ i ] . EventState
}
err := ctx . GetStatusChange ( rs , - 1 )
if err != nil {
return - 1 , err
}
}
}
2019-03-01 18:44:07 +01:00
func main ( ) {
2019-04-17 12:09:28 +02:00
if command == "version" {
commandVersion ( nil )
return
}
2019-03-01 18:44:07 +01:00
ctx , err := scard . EstablishContext ( )
if err != nil {
fail ( "error establishing card context" , "error" , err )
}
defer func ( ) {
if err := ctx . Release ( ) ; err != nil {
logger . Error ( "error releasing context" , "error" , err )
}
} ( )
readers , err := ctx . ListReaders ( )
if err != nil {
fail ( "error getting readers" , "error" , err )
}
2019-03-28 12:45:16 +01:00
logger . Info ( "waiting for a card" )
2019-04-16 17:22:40 +02:00
if len ( readers ) == 0 {
fail ( "no smartcard reader found" )
}
2019-03-28 12:45:16 +01:00
index , err := waitForCard ( ctx , readers )
if err != nil {
fail ( "error waiting for card" , "error" , err )
2019-03-01 18:44:07 +01:00
}
2019-03-28 12:45:16 +01:00
logger . Info ( "card found" , "index" , index )
reader := readers [ index ]
2019-03-01 18:44:07 +01:00
logger . Debug ( "using reader" , "name" , reader )
logger . Debug ( "connecting to card" , "reader" , reader )
card , err := ctx . Connect ( reader , scard . ShareShared , scard . ProtocolAny )
if err != nil {
fail ( "error connecting to card" , "error" , err )
}
defer func ( ) {
if err := card . Disconnect ( scard . ResetCard ) ; err != nil {
logger . Error ( "error disconnecting card" , "error" , err )
}
} ( )
status , err := card . Status ( )
if err != nil {
fail ( "error getting card status" , "error" , err )
}
switch status . ActiveProtocol {
case scard . ProtocolT0 :
logger . Debug ( "card protocol" , "T" , "0" )
case scard . ProtocolT1 :
logger . Debug ( "card protocol" , "T" , "1" )
default :
logger . Debug ( "card protocol" , "T" , "unknown" )
}
if f , ok := commands [ command ] ; ok {
2019-03-06 17:57:52 +01:00
err = f ( card )
2019-03-01 18:44:07 +01:00
if err != nil {
logger . Error ( "error executing command" , "command" , command , "error" , err )
os . Exit ( 1 )
}
os . Exit ( 0 )
}
fail ( "unknown command" , "command" , command )
usage ( )
}
func ask ( description string ) string {
r := bufio . NewReader ( os . Stdin )
fmt . Printf ( "%s: " , description )
text , err := r . ReadString ( '\n' )
if err != nil {
stdlog . Fatal ( err )
}
return strings . TrimSpace ( text )
}
func askHex ( description string ) [ ] byte {
s := ask ( description )
if s [ : 2 ] == "0x" {
s = s [ 2 : ]
}
data , err := hex . DecodeString ( s )
if err != nil {
stdlog . Fatal ( err )
}
return data
}
2019-03-13 16:20:51 +01:00
func askInt ( description string ) int {
2019-03-01 18:44:07 +01:00
s := ask ( description )
2019-03-13 16:20:51 +01:00
i , err := strconv . ParseInt ( s , 10 , 8 )
2019-03-01 18:44:07 +01:00
if err != nil {
stdlog . Fatal ( err )
}
2019-03-13 16:20:51 +01:00
return int ( i )
2019-03-01 18:44:07 +01:00
}
2019-04-03 12:09:48 +02:00
func commandVersion ( card * scard . Card ) error {
fmt . Printf ( "version %+v\n" , version )
return nil
}
2019-03-06 17:57:52 +01:00
func commandInstall ( card * scard . Card ) error {
2019-03-01 18:44:07 +01:00
if * flagCapFile == "" {
logger . Error ( "you must specify a cap file path with the -f flag\n" )
usage ( )
}
f , err := os . Open ( * flagCapFile )
if err != nil {
fail ( "error opening cap file" , "error" , err )
}
defer f . Close ( )
2019-03-06 17:57:52 +01:00
i := NewInstaller ( card )
2019-03-01 18:44:07 +01:00
2019-11-19 18:02:36 +01:00
return i . Install ( f , * flagOverwrite , * flagNdefTemplate )
2019-03-01 18:44:07 +01:00
}
2019-03-06 17:57:52 +01:00
func commandInfo ( card * scard . Card ) error {
i := NewInitializer ( card )
2019-03-01 18:44:07 +01:00
info , err := i . Info ( )
if err != nil {
return err
}
2019-05-17 16:52:59 +02:00
var keyInitialized bool
if len ( info . KeyUID ) > 0 {
keyInitialized = true
}
2019-03-01 18:44:07 +01:00
fmt . Printf ( "Installed: %+v\n" , info . Installed )
fmt . Printf ( "Initialized: %+v\n" , info . Initialized )
2019-05-17 16:52:59 +02:00
fmt . Printf ( "Key Initialized: %+v\n" , keyInitialized )
2019-03-01 18:44:07 +01:00
fmt . Printf ( "InstanceUID: 0x%x\n" , info . InstanceUID )
2019-03-15 10:37:54 +01:00
fmt . Printf ( "SecureChannelPublicKey: 0x%x\n" , info . SecureChannelPublicKey )
2019-03-01 18:44:07 +01:00
fmt . Printf ( "Version: 0x%x\n" , info . Version )
fmt . Printf ( "AvailableSlots: 0x%x\n" , info . AvailableSlots )
fmt . Printf ( "KeyUID: 0x%x\n" , info . KeyUID )
2019-03-11 16:12:19 +01:00
fmt . Printf ( "Capabilities:\n" )
fmt . Printf ( " Secure channel:%v\n" , info . HasSecureChannelCapability ( ) )
fmt . Printf ( " Key management:%v\n" , info . HasKeyManagementCapability ( ) )
fmt . Printf ( " Credentials Management:%v\n" , info . HasCredentialsManagementCapability ( ) )
fmt . Printf ( " NDEF:%v\n" , info . HasNDEFCapability ( ) )
2019-03-01 18:44:07 +01:00
return nil
}
2019-03-06 17:57:52 +01:00
func commandDelete ( card * scard . Card ) error {
2019-03-11 11:49:00 +01:00
i := NewInstaller ( card )
2019-03-01 18:44:07 +01:00
err := i . Delete ( )
if err != nil {
return err
}
fmt . Printf ( "applet deleted\n" )
return nil
}
2019-03-06 17:57:52 +01:00
func commandInit ( card * scard . Card ) error {
i := NewInitializer ( card )
2019-03-01 18:44:07 +01:00
secrets , err := i . Init ( )
if err != nil {
return err
}
fmt . Printf ( "PIN %s\n" , secrets . Pin ( ) )
fmt . Printf ( "PUK %s\n" , secrets . Puk ( ) )
fmt . Printf ( "Pairing password: %s\n" , secrets . PairingPass ( ) )
return nil
}
2019-03-13 13:49:26 +01:00
func commandPair ( card * scard . Card ) error {
i := NewInitializer ( card )
pairingPass := ask ( "Pairing password" )
info , err := i . Pair ( pairingPass )
if err != nil {
return err
}
2019-03-11 12:53:02 +01:00
2019-03-13 13:49:26 +01:00
fmt . Printf ( "Pairing key 0x%x\n" , info . Key )
fmt . Printf ( "Pairing Index %d\n" , info . Index )
2019-03-11 12:53:02 +01:00
2019-03-13 13:49:26 +01:00
return nil
}
2019-03-11 12:53:02 +01:00
2019-03-13 16:20:51 +01:00
func commandStatus ( card * scard . Card ) error {
i := NewInitializer ( card )
key := askHex ( "Pairing key" )
index := askInt ( "Pairing index" )
2019-03-11 12:53:02 +01:00
2019-03-13 16:20:51 +01:00
appStatus , err := i . Status ( key , index )
if err != nil {
return err
}
2019-03-11 12:53:02 +01:00
2019-03-13 16:20:51 +01:00
fmt . Printf ( "Pin retry count: %d\n" , appStatus . PinRetryCount )
fmt . Printf ( "PUK retry count: %d\n" , appStatus . PUKRetryCount )
fmt . Printf ( "Key initialized: %v\n" , appStatus . KeyInitialized )
2019-03-11 12:53:02 +01:00
2019-03-13 16:20:51 +01:00
return nil
}
2019-03-15 10:39:17 +01:00
func commandShell ( card * scard . Card ) error {
2019-03-15 16:08:15 +01:00
fi , _ := os . Stdin . Stat ( )
if ( fi . Mode ( ) & os . ModeCharDevice ) == 0 {
s := NewShell ( card )
return s . Run ( )
} else {
return errors . New ( "non interactive shell. you must pipe commands" )
}
2019-03-15 10:39:17 +01:00
}