Commit 7d5886dc authored by Péter Szilágyi's avatar Péter Szilágyi Committed by Guillaume Ballet

accounts, console: frendly card errors, support pin unblock

parent 38694394
...@@ -21,29 +21,15 @@ import ( ...@@ -21,29 +21,15 @@ import (
"encoding/binary" "encoding/binary"
) )
const ( // commandAPDU represents an application data unit sent to a smartcard.
claISO7816 = 0 type commandAPDU struct {
insSelect = 0xA4
insGetResponse = 0xC0
insPair = 0x12
insUnpair = 0x13
insOpenSecureChannel = 0x10
insMutuallyAuthenticate = 0x11
sw1GetResponse = 0x61
sw1Ok = 0x90
)
// CommandAPDU represents an application data unit sent to a smartcard.
type CommandAPDU struct {
Cla, Ins, P1, P2 uint8 // Class, Instruction, Parameter 1, Parameter 2 Cla, Ins, P1, P2 uint8 // Class, Instruction, Parameter 1, Parameter 2
Data []byte // Command data Data []byte // Command data
Le uint8 // Command data length Le uint8 // Command data length
} }
// serialize serializes a command APDU. // serialize serializes a command APDU.
func (ca CommandAPDU) serialize() ([]byte, error) { func (ca commandAPDU) serialize() ([]byte, error) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
if err := binary.Write(buf, binary.BigEndian, ca.Cla); err != nil { if err := binary.Write(buf, binary.BigEndian, ca.Cla); err != nil {
...@@ -72,14 +58,14 @@ func (ca CommandAPDU) serialize() ([]byte, error) { ...@@ -72,14 +58,14 @@ func (ca CommandAPDU) serialize() ([]byte, error) {
return buf.Bytes(), nil return buf.Bytes(), nil
} }
// ResponseAPDU represents an application data unit received from a smart card. // responseAPDU represents an application data unit received from a smart card.
type ResponseAPDU struct { type responseAPDU struct {
Data []byte // response data Data []byte // response data
Sw1, Sw2 uint8 // status words 1 and 2 Sw1, Sw2 uint8 // status words 1 and 2
} }
// deserialize deserializes a response APDU. // deserialize deserializes a response APDU.
func (ra *ResponseAPDU) deserialize(data []byte) error { func (ra *responseAPDU) deserialize(data []byte) error {
ra.Data = make([]byte, len(data)-2) ra.Data = make([]byte, len(data)-2)
buf := bytes.NewReader(data) buf := bytes.NewReader(data)
......
...@@ -36,6 +36,7 @@ import ( ...@@ -36,6 +36,7 @@ import (
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"os" "os"
"sort"
"sync" "sync"
"time" "time"
...@@ -51,7 +52,7 @@ const Scheme = "pcsc" ...@@ -51,7 +52,7 @@ const Scheme = "pcsc"
// refreshCycle is the maximum time between wallet refreshes (if USB hotplug // refreshCycle is the maximum time between wallet refreshes (if USB hotplug
// notifications don't work). // notifications don't work).
const refreshCycle = 5 * time.Second const refreshCycle = time.Second
// refreshThrottling is the minimum time between wallet refreshes to avoid thrashing. // refreshThrottling is the minimum time between wallet refreshes to avoid thrashing.
const refreshThrottling = 500 * time.Millisecond const refreshThrottling = 500 * time.Millisecond
...@@ -132,7 +133,7 @@ func (hub *Hub) writePairings() error { ...@@ -132,7 +133,7 @@ func (hub *Hub) writePairings() error {
return pairingFile.Close() return pairingFile.Close()
} }
func (hub *Hub) getPairing(wallet *Wallet) *smartcardPairing { func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {
pairing, ok := hub.pairings[string(wallet.PublicKey)] pairing, ok := hub.pairings[string(wallet.PublicKey)]
if ok { if ok {
return &pairing return &pairing
...@@ -182,13 +183,7 @@ func (hub *Hub) Wallets() []accounts.Wallet { ...@@ -182,13 +183,7 @@ func (hub *Hub) Wallets() []accounts.Wallet {
for _, wallet := range hub.wallets { for _, wallet := range hub.wallets {
cpy = append(cpy, wallet) cpy = append(cpy, wallet)
} }
for i := 0; i < len(cpy); i++ { sort.Sort(accounts.WalletsByURL(cpy))
for j := i + 1; j < len(cpy); j++ {
if cpy[i].URL().Cmp(cpy[j].URL()) > 0 {
cpy[i], cpy[j] = cpy[j], cpy[i]
}
}
}
return cpy return cpy
} }
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
package scwallet package scwallet
import ( import (
//"crypto/ecdsa"
"bytes" "bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
...@@ -25,10 +24,10 @@ import ( ...@@ -25,10 +24,10 @@ import (
"crypto/sha256" "crypto/sha256"
"crypto/sha512" "crypto/sha512"
"fmt" "fmt"
//"math/big"
"github.com/ebfe/scard" "github.com/ebfe/scard"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
ecdh "github.com/wsddn/go-ecdh" "github.com/wsddn/go-ecdh"
) )
const ( const (
...@@ -38,6 +37,11 @@ const ( ...@@ -38,6 +37,11 @@ const (
scSecretLength = 32 scSecretLength = 32
scBlockSize = 16 scBlockSize = 16
insOpenSecureChannel = 0x10
insMutuallyAuthenticate = 0x11
insPair = 0x12
insUnpair = 0x13
) )
// SecureChannelSession enables secure communication with a hardware wallet. // SecureChannelSession enables secure communication with a hardware wallet.
...@@ -192,8 +196,8 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error { ...@@ -192,8 +196,8 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error {
} }
// open is an internal method that sends an open APDU. // open is an internal method that sends an open APDU.
func (s *SecureChannelSession) open() (*ResponseAPDU, error) { func (s *SecureChannelSession) open() (*responseAPDU, error) {
return transmit(s.card, &CommandAPDU{ return transmit(s.card, &commandAPDU{
Cla: claSCWallet, Cla: claSCWallet,
Ins: insOpenSecureChannel, Ins: insOpenSecureChannel,
P1: s.PairingIndex, P1: s.PairingIndex,
...@@ -204,8 +208,8 @@ func (s *SecureChannelSession) open() (*ResponseAPDU, error) { ...@@ -204,8 +208,8 @@ func (s *SecureChannelSession) open() (*ResponseAPDU, error) {
} }
// pair is an internal method that sends a pair APDU. // pair is an internal method that sends a pair APDU.
func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*ResponseAPDU, error) { func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*responseAPDU, error) {
return transmit(s.card, &CommandAPDU{ return transmit(s.card, &commandAPDU{
Cla: claSCWallet, Cla: claSCWallet,
Ins: insPair, Ins: insPair,
P1: p1, P1: p1,
...@@ -216,7 +220,7 @@ func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*ResponseAPDU, error ...@@ -216,7 +220,7 @@ func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*ResponseAPDU, error
} }
// TransmitEncrypted sends an encrypted message, and decrypts and returns the response. // TransmitEncrypted sends an encrypted message, and decrypts and returns the response.
func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*ResponseAPDU, error) { func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) {
if s.iv == nil { if s.iv == nil {
return nil, fmt.Errorf("Channel not open") return nil, fmt.Errorf("Channel not open")
} }
...@@ -234,7 +238,7 @@ func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []b ...@@ -234,7 +238,7 @@ func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []b
copy(fulldata, s.iv) copy(fulldata, s.iv)
copy(fulldata[len(s.iv):], data) copy(fulldata[len(s.iv):], data)
response, err := transmit(s.card, &CommandAPDU{ response, err := transmit(s.card, &commandAPDU{
Cla: cla, Cla: cla,
Ins: ins, Ins: ins,
P1: p1, P1: p1,
...@@ -260,7 +264,7 @@ func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []b ...@@ -260,7 +264,7 @@ func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []b
return nil, fmt.Errorf("Invalid MAC in response") return nil, fmt.Errorf("Invalid MAC in response")
} }
rapdu := &ResponseAPDU{} rapdu := &responseAPDU{}
rapdu.deserialize(plainData) rapdu.deserialize(plainData)
if rapdu.Sw1 != sw1Ok { if rapdu.Sw1 != sw1Ok {
......
This diff is collapsed.
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package accounts
// AccountsByURL implements sort.Interface for []Account based on the URL field.
type AccountsByURL []Account
func (a AccountsByURL) Len() int { return len(a) }
func (a AccountsByURL) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a AccountsByURL) Less(i, j int) bool { return a[i].URL.Cmp(a[j].URL) < 0 }
// WalletsByURL implements sort.Interface for []Wallet based on the URL field.
type WalletsByURL []Wallet
func (w WalletsByURL) Len() int { return len(w) }
func (w WalletsByURL) Swap(i, j int) { w[i], w[j] = w[j], w[i] }
func (w WalletsByURL) Less(i, j int) bool { return w[i].URL().Cmp(w[j].URL()) < 0 }
...@@ -141,6 +141,24 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) { ...@@ -141,6 +141,24 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
} }
} }
case strings.HasSuffix(err.Error(), scwallet.ErrPINUnblockNeeded.Error()):
// PIN unblock requested, fetch PUK and new PIN from the user
var pukpin string
if input, err := b.prompter.PromptPassword("Please enter current PUK: "); err != nil {
throwJSException(err.Error())
} else {
pukpin = input
}
if input, err := b.prompter.PromptPassword("Please enter new PIN: "); err != nil {
throwJSException(err.Error())
} else {
pukpin += input
}
passwd, _ = otto.ToValue(pukpin)
if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
throwJSException(err.Error())
}
case strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()): case strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()):
// PIN input requested, fetch from the user and call open again // PIN input requested, fetch from the user and call open again
if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil { if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment