1package main 2 3import ( 4 "bufio" 5 "encoding/hex" 6 "errors" 7 "flag" 8 "fmt" 9 stdlog "log" 10 "os" 11 "strconv" 12 "strings" 13 14 "github.com/ebfe/scard" 15 "github.com/ethereum/go-ethereum/log" 16) 17 18type commandFunc func(*scard.Card) error 19 20var ( 21 logger = log.New("package", "status-go/cmd/keycard") 22 23 commands map[string]commandFunc 24 command string 25 26 flagCapFile = flag.String("a", "", "applet cap file path") 27 flagOverwrite = flag.Bool("f", false, "force applet installation if already installed") 28 flagLogLevel = flag.String("l", "", `Log level, one of: "error", "warn", "info", "debug", and "trace"`) 29) 30 31func initLogger() { 32 if *flagLogLevel == "" { 33 *flagLogLevel = "info" 34 } 35 36 level, err := log.LvlFromString(strings.ToLower(*flagLogLevel)) 37 if err != nil { 38 stdlog.Fatal(err) 39 } 40 41 handler := log.StreamHandler(os.Stderr, log.TerminalFormat(true)) 42 filteredHandler := log.LvlFilterHandler(level, handler) 43 log.Root().SetHandler(filteredHandler) 44} 45 46func init() { 47 commands = map[string]commandFunc{ 48 "install": commandInstall, 49 "info": commandInfo, 50 "delete": commandDelete, 51 "init": commandInit, 52 "pair": commandPair, 53 "status": commandStatus, 54 "shell": commandShell, 55 } 56 57 if len(os.Args) < 2 { 58 usage() 59 } 60 61 command = os.Args[1] 62 if len(os.Args) > 2 { 63 flag.CommandLine.Parse(os.Args[2:]) 64 } 65 66 initLogger() 67} 68 69func usage() { 70 fmt.Printf("\nUsage:\n keycard COMMAND [FLAGS]\n\nAvailable commands:\n") 71 for name := range commands { 72 fmt.Printf(" %s\n", name) 73 } 74 fmt.Print("\nFlags:\n\n") 75 flag.PrintDefaults() 76 os.Exit(1) 77} 78 79func fail(msg string, ctx ...interface{}) { 80 logger.Error(msg, ctx...) 81 os.Exit(1) 82} 83 84func main() { 85 ctx, err := scard.EstablishContext() 86 if err != nil { 87 fail("error establishing card context", "error", err) 88 } 89 defer func() { 90 if err := ctx.Release(); err != nil { 91 logger.Error("error releasing context", "error", err) 92 } 93 }() 94 95 readers, err := ctx.ListReaders() 96 if err != nil { 97 fail("error getting readers", "error", err) 98 } 99 100 if len(readers) == 0 { 101 fail("couldn't find any reader") 102 } 103 104 if len(readers) > 1 { 105 fail("too many readers found") 106 } 107 108 reader := readers[0] 109 logger.Debug("using reader", "name", reader) 110 logger.Debug("connecting to card", "reader", reader) 111 card, err := ctx.Connect(reader, scard.ShareShared, scard.ProtocolAny) 112 if err != nil { 113 fail("error connecting to card", "error", err) 114 } 115 defer func() { 116 if err := card.Disconnect(scard.ResetCard); err != nil { 117 logger.Error("error disconnecting card", "error", err) 118 } 119 }() 120 121 status, err := card.Status() 122 if err != nil { 123 fail("error getting card status", "error", err) 124 } 125 126 switch status.ActiveProtocol { 127 case scard.ProtocolT0: 128 logger.Debug("card protocol", "T", "0") 129 case scard.ProtocolT1: 130 logger.Debug("card protocol", "T", "1") 131 default: 132 logger.Debug("card protocol", "T", "unknown") 133 } 134 135 if f, ok := commands[command]; ok { 136 err = f(card) 137 if err != nil { 138 logger.Error("error executing command", "command", command, "error", err) 139 os.Exit(1) 140 } 141 os.Exit(0) 142 } 143 144 fail("unknown command", "command", command) 145 usage() 146} 147 148func ask(description string) string { 149 r := bufio.NewReader(os.Stdin) 150 fmt.Printf("%s: ", description) 151 text, err := r.ReadString('\n') 152 if err != nil { 153 stdlog.Fatal(err) 154 } 155 156 return strings.TrimSpace(text) 157} 158 159func askHex(description string) []byte { 160 s := ask(description) 161 if s[:2] == "0x" { 162 s = s[2:] 163 } 164 165 data, err := hex.DecodeString(s) 166 if err != nil { 167 stdlog.Fatal(err) 168 } 169 170 return data 171} 172 173func askInt(description string) int { 174 s := ask(description) 175 i, err := strconv.ParseInt(s, 10, 8) 176 if err != nil { 177 stdlog.Fatal(err) 178 } 179 180 return int(i) 181} 182 183func commandInstall(card *scard.Card) error { 184 if *flagCapFile == "" { 185 logger.Error("you must specify a cap file path with the -f flag\n") 186 usage() 187 } 188 189 f, err := os.Open(*flagCapFile) 190 if err != nil { 191 fail("error opening cap file", "error", err) 192 } 193 defer f.Close() 194 195 i := NewInstaller(card) 196 197 return i.Install(f, *flagOverwrite) 198} 199 200func commandInfo(card *scard.Card) error { 201 i := NewInitializer(card) 202 info, err := i.Info() 203 if err != nil { 204 return err 205 } 206 207 fmt.Printf("Installed: %+v\n", info.Installed) 208 fmt.Printf("Initialized: %+v\n", info.Initialized) 209 fmt.Printf("InstanceUID: 0x%x\n", info.InstanceUID) 210 fmt.Printf("SecureChannelPublicKey: 0x%x\n", info.SecureChannelPublicKey) 211 fmt.Printf("Version: 0x%x\n", info.Version) 212 fmt.Printf("AvailableSlots: 0x%x\n", info.AvailableSlots) 213 fmt.Printf("KeyUID: 0x%x\n", info.KeyUID) 214 fmt.Printf("Capabilities:\n") 215 fmt.Printf(" Secure channel:%v\n", info.HasSecureChannelCapability()) 216 fmt.Printf(" Key management:%v\n", info.HasKeyManagementCapability()) 217 fmt.Printf(" Credentials Management:%v\n", info.HasCredentialsManagementCapability()) 218 fmt.Printf(" NDEF:%v\n", info.HasNDEFCapability()) 219 220 return nil 221} 222 223func commandDelete(card *scard.Card) error { 224 i := NewInstaller(card) 225 err := i.Delete() 226 if err != nil { 227 return err 228 } 229 230 fmt.Printf("applet deleted\n") 231 232 return nil 233} 234 235func commandInit(card *scard.Card) error { 236 i := NewInitializer(card) 237 secrets, err := i.Init() 238 if err != nil { 239 return err 240 } 241 242 fmt.Printf("PIN %s\n", secrets.Pin()) 243 fmt.Printf("PUK %s\n", secrets.Puk()) 244 fmt.Printf("Pairing password: %s\n", secrets.PairingPass()) 245 246 return nil 247} 248 249func commandPair(card *scard.Card) error { 250 i := NewInitializer(card) 251 pairingPass := ask("Pairing password") 252 info, err := i.Pair(pairingPass) 253 if err != nil { 254 return err 255 } 256 257 fmt.Printf("Pairing key 0x%x\n", info.Key) 258 fmt.Printf("Pairing Index %d\n", info.Index) 259 260 return nil 261} 262 263func commandStatus(card *scard.Card) error { 264 i := NewInitializer(card) 265 key := askHex("Pairing key") 266 index := askInt("Pairing index") 267 268 appStatus, err := i.Status(key, index) 269 if err != nil { 270 return err 271 } 272 273 fmt.Printf("Pin retry count: %d\n", appStatus.PinRetryCount) 274 fmt.Printf("PUK retry count: %d\n", appStatus.PUKRetryCount) 275 fmt.Printf("Key initialized: %v\n", appStatus.KeyInitialized) 276 277 return nil 278} 279 280func commandShell(card *scard.Card) error { 281 fi, _ := os.Stdin.Stat() 282 if (fi.Mode() & os.ModeCharDevice) == 0 { 283 s := NewShell(card) 284 return s.Run() 285 } else { 286 return errors.New("non interactive shell. you must pipe commands") 287 } 288} 289