forked from cerc-io/plugeth
accounts, internal: Changes in response to review
This commit is contained in:
parent
f7027dd68c
commit
78375608a4
@ -139,12 +139,12 @@ func (path DerivationPath) MarshalJSON() ([]byte, error) {
|
|||||||
return []byte(fmt.Sprintf("\"%s\"", path.String())), nil
|
return []byte(fmt.Sprintf("\"%s\"", path.String())), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dp *DerivationPath) UnmarshalJSON(b []byte) error {
|
func (path *DerivationPath) UnmarshalJSON(b []byte) error {
|
||||||
var path string
|
var dp string
|
||||||
var err error
|
var err error
|
||||||
if err = json.Unmarshal(b, &path); err != nil {
|
if err = json.Unmarshal(b, &dp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*dp, err = ParseDerivationPath(path)
|
*path, err = ParseDerivationPath(dp)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -22,20 +22,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CLA_ISO7816 = 0
|
claISO7816 = 0
|
||||||
|
|
||||||
INS_SELECT = 0xA4
|
insSelect = 0xA4
|
||||||
INS_GET_RESPONSE = 0xC0
|
insGetResponse = 0xC0
|
||||||
INS_PAIR = 0x12
|
insPair = 0x12
|
||||||
INS_UNPAIR = 0x13
|
insUnpair = 0x13
|
||||||
INS_OPEN_SECURE_CHANNEL = 0x10
|
insOpenSecureChannel = 0x10
|
||||||
INS_MUTUALLY_AUTHENTICATE = 0x11
|
insMutuallyAuthenticate = 0x11
|
||||||
|
|
||||||
SW1_GET_RESPONSE = 0x61
|
sw1GetResponse = 0x61
|
||||||
SW1_OK = 0x90
|
sw1Ok = 0x90
|
||||||
)
|
)
|
||||||
|
|
||||||
// CommandAPDU represents an application data unit sent to a smartcard
|
// CommandAPDU represents an application data unit sent to a smartcard.
|
||||||
type CommandAPDU struct {
|
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
|
||||||
@ -72,13 +72,13 @@ 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)
|
||||||
|
|
||||||
|
@ -14,6 +14,22 @@
|
|||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// This package implements support for smartcard-based hardware wallets such as
|
||||||
|
// the one written by Status: https://github.com/status-im/hardware-wallet
|
||||||
|
//
|
||||||
|
// This implementation of smartcard wallets have a different interaction process
|
||||||
|
// to other types of hardware wallet. The process works like this:
|
||||||
|
//
|
||||||
|
// 1. (First use with a given client) Establish a pairing between hardware
|
||||||
|
// wallet and client. This requires a secret value called a 'PUK'. You can
|
||||||
|
// pair with an unpaired wallet with `personal.openWallet(URI, PUK)`.
|
||||||
|
// 2. (First use only) Initialize the wallet, which generates a keypair, stores
|
||||||
|
// it on the wallet, and returns it so the user can back it up. You can
|
||||||
|
// initialize a wallet with `personal.initializeWallet(URI)`.
|
||||||
|
// 3. Connect to the wallet using the pairing information established in step 1.
|
||||||
|
// You can connect to a paired wallet with `personal.openWallet(URI, PIN)`.
|
||||||
|
// 4. Interact with the wallet as normal.
|
||||||
|
|
||||||
package scwallet
|
package scwallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -32,6 +48,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Scheme is the URI prefix for smartcard wallets.
|
||||||
const Scheme = "pcsc"
|
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
|
||||||
@ -41,9 +58,9 @@ const refreshCycle = 5 * 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
|
||||||
|
|
||||||
// SmartcardPairing contains information about a smart card we have paired with
|
// smartcardPairing contains information about a smart card we have paired with
|
||||||
// or might pair withub.
|
// or might pair with the hub.
|
||||||
type SmartcardPairing struct {
|
type smartcardPairing struct {
|
||||||
PublicKey []byte `json:"publicKey"`
|
PublicKey []byte `json:"publicKey"`
|
||||||
PairingIndex uint8 `json:"pairingIndex"`
|
PairingIndex uint8 `json:"pairingIndex"`
|
||||||
PairingKey []byte `json:"pairingKey"`
|
PairingKey []byte `json:"pairingKey"`
|
||||||
@ -56,7 +73,7 @@ type Hub struct {
|
|||||||
|
|
||||||
context *scard.Context
|
context *scard.Context
|
||||||
datadir string
|
datadir string
|
||||||
pairings map[string]SmartcardPairing
|
pairings map[string]smartcardPairing
|
||||||
refreshed time.Time // Time instance when the list of wallets was last refreshed
|
refreshed time.Time // Time instance when the list of wallets was last refreshed
|
||||||
wallets map[string]*Wallet // Mapping from reader names to wallet instances
|
wallets map[string]*Wallet // Mapping from reader names to wallet instances
|
||||||
updateFeed event.Feed // Event feed to notify wallet additions/removals
|
updateFeed event.Feed // Event feed to notify wallet additions/removals
|
||||||
@ -71,7 +88,7 @@ type Hub struct {
|
|||||||
var HubType = reflect.TypeOf(&Hub{})
|
var HubType = reflect.TypeOf(&Hub{})
|
||||||
|
|
||||||
func (hub *Hub) readPairings() error {
|
func (hub *Hub) readPairings() error {
|
||||||
hub.pairings = make(map[string]SmartcardPairing)
|
hub.pairings = make(map[string]smartcardPairing)
|
||||||
pairingFile, err := os.Open(hub.datadir + "/smartcards.json")
|
pairingFile, err := os.Open(hub.datadir + "/smartcards.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
@ -84,7 +101,7 @@ func (hub *Hub) readPairings() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var pairings []SmartcardPairing
|
var pairings []smartcardPairing
|
||||||
if err := json.Unmarshal(pairingData, &pairings); err != nil {
|
if err := json.Unmarshal(pairingData, &pairings); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -101,7 +118,7 @@ func (hub *Hub) writePairings() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pairings := make([]SmartcardPairing, 0, len(hub.pairings))
|
pairings := make([]smartcardPairing, 0, len(hub.pairings))
|
||||||
for _, pairing := range hub.pairings {
|
for _, pairing := range hub.pairings {
|
||||||
pairings = append(pairings, pairing)
|
pairings = append(pairings, pairing)
|
||||||
}
|
}
|
||||||
@ -118,7 +135,7 @@ func (hub *Hub) writePairings() error {
|
|||||||
return pairingFile.Close()
|
return pairingFile.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *Hub) getPairing(wallet *Wallet) *SmartcardPairing {
|
func (hub *Hub) getPairing(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
|
||||||
@ -126,7 +143,7 @@ func (hub *Hub) getPairing(wallet *Wallet) *SmartcardPairing {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *Hub) setPairing(wallet *Wallet, pairing *SmartcardPairing) error {
|
func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error {
|
||||||
if pairing == nil {
|
if pairing == nil {
|
||||||
delete(hub.pairings, string(wallet.PublicKey))
|
delete(hub.pairings, string(wallet.PublicKey))
|
||||||
} else {
|
} else {
|
||||||
@ -158,7 +175,7 @@ func NewHub(scheme string, datadir string) (*Hub, error) {
|
|||||||
return hub, nil
|
return hub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wallets implements accounts.Backend, returning all the currently tracked USB
|
// Wallets implements accounts.Backend, returning all the currently tracked
|
||||||
// devices that appear to be hardware wallets.
|
// devices that appear to be hardware wallets.
|
||||||
func (hub *Hub) Wallets() []accounts.Wallet {
|
func (hub *Hub) Wallets() []accounts.Wallet {
|
||||||
// Make sure the list of wallets is up to date
|
// Make sure the list of wallets is up to date
|
||||||
@ -176,7 +193,7 @@ func (hub *Hub) Wallets() []accounts.Wallet {
|
|||||||
return cpy
|
return cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
// refreshWallets scans the USB devices attached to the machine and updates the
|
// refreshWallets scans the devices attached to the machine and updates the
|
||||||
// list of wallets based on the found devices.
|
// list of wallets based on the found devices.
|
||||||
func (hub *Hub) refreshWallets() {
|
func (hub *Hub) refreshWallets() {
|
||||||
elapsed := time.Since(hub.refreshed)
|
elapsed := time.Since(hub.refreshed)
|
||||||
|
@ -32,15 +32,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MAX_PAYLOAD_SIZE = 223
|
maxPayloadSize = 223
|
||||||
PAIR_P1_FIRST_STEP = 0
|
pairP1FirstStep = 0
|
||||||
PAIR_P1_LAST_STEP = 1
|
pairP1LastStep = 1
|
||||||
|
|
||||||
SC_SECRET_LENGTH = 32
|
scSecretLength = 32
|
||||||
SC_BLOCK_SIZE = 16
|
scBlockSize = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
// SecureChannelSession enables secure communication with a hardware wallet
|
// SecureChannelSession enables secure communication with a hardware wallet.
|
||||||
type SecureChannelSession struct {
|
type SecureChannelSession struct {
|
||||||
card *scard.Card // A handle to the smartcard for communication
|
card *scard.Card // A handle to the smartcard for communication
|
||||||
secret []byte // A shared secret generated from our ECDSA keys
|
secret []byte // A shared secret generated from our ECDSA keys
|
||||||
@ -52,7 +52,7 @@ type SecureChannelSession struct {
|
|||||||
PairingIndex uint8 // The pairing index
|
PairingIndex uint8 // The pairing index
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSecureChannelSession creates a new secure channel for the given card and public key
|
// NewSecureChannelSession creates a new secure channel for the given card and public key.
|
||||||
func NewSecureChannelSession(card *scard.Card, keyData []byte) (*SecureChannelSession, error) {
|
func NewSecureChannelSession(card *scard.Card, keyData []byte) (*SecureChannelSession, error) {
|
||||||
// Generate an ECDSA keypair for ourselves
|
// Generate an ECDSA keypair for ourselves
|
||||||
gen := ecdh.NewEllipticECDH(crypto.S256())
|
gen := ecdh.NewEllipticECDH(crypto.S256())
|
||||||
@ -78,7 +78,7 @@ func NewSecureChannelSession(card *scard.Card, keyData []byte) (*SecureChannelSe
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pair establishes a new pairing with the smartcard
|
// Pair establishes a new pairing with the smartcard.
|
||||||
func (s *SecureChannelSession) Pair(sharedSecret []byte) error {
|
func (s *SecureChannelSession) Pair(sharedSecret []byte) error {
|
||||||
secretHash := sha256.Sum256(sharedSecret)
|
secretHash := sha256.Sum256(sharedSecret)
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ func (s *SecureChannelSession) Pair(sharedSecret []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := s.pair(PAIR_P1_FIRST_STEP, challenge)
|
response, err := s.pair(pairP1FirstStep, challenge)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -107,7 +107,7 @@ func (s *SecureChannelSession) Pair(sharedSecret []byte) error {
|
|||||||
md.Reset()
|
md.Reset()
|
||||||
md.Write(secretHash[:])
|
md.Write(secretHash[:])
|
||||||
md.Write(cardChallenge)
|
md.Write(cardChallenge)
|
||||||
response, err = s.pair(PAIR_P1_LAST_STEP, md.Sum(nil))
|
response, err = s.pair(pairP1LastStep, md.Sum(nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -121,13 +121,13 @@ func (s *SecureChannelSession) Pair(sharedSecret []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpair disestablishes an existing pairing
|
// Unpair disestablishes an existing pairing.
|
||||||
func (s *SecureChannelSession) Unpair() error {
|
func (s *SecureChannelSession) Unpair() error {
|
||||||
if s.PairingKey == nil {
|
if s.PairingKey == nil {
|
||||||
return fmt.Errorf("Cannot unpair: not paired")
|
return fmt.Errorf("Cannot unpair: not paired")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := s.TransmitEncrypted(CLA_SCWALLET, INS_UNPAIR, s.PairingIndex, 0, []byte{})
|
_, err := s.TransmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ func (s *SecureChannelSession) Unpair() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open initializes the secure channel
|
// Open initializes the secure channel.
|
||||||
func (s *SecureChannelSession) Open() error {
|
func (s *SecureChannelSession) Open() error {
|
||||||
if s.iv != nil {
|
if s.iv != nil {
|
||||||
return fmt.Errorf("Session already opened")
|
return fmt.Errorf("Session already opened")
|
||||||
@ -153,13 +153,13 @@ func (s *SecureChannelSession) Open() error {
|
|||||||
md := sha512.New()
|
md := sha512.New()
|
||||||
md.Write(s.secret)
|
md.Write(s.secret)
|
||||||
md.Write(s.PairingKey)
|
md.Write(s.PairingKey)
|
||||||
md.Write(response.Data[:SC_SECRET_LENGTH])
|
md.Write(response.Data[:scSecretLength])
|
||||||
keyData := md.Sum(nil)
|
keyData := md.Sum(nil)
|
||||||
s.sessionEncKey = keyData[:SC_SECRET_LENGTH]
|
s.sessionEncKey = keyData[:scSecretLength]
|
||||||
s.sessionMacKey = keyData[SC_SECRET_LENGTH : SC_SECRET_LENGTH*2]
|
s.sessionMacKey = keyData[scSecretLength : scSecretLength*2]
|
||||||
|
|
||||||
// The IV is the last bytes returned from the Open APDU.
|
// The IV is the last bytes returned from the Open APDU.
|
||||||
s.iv = response.Data[SC_SECRET_LENGTH:]
|
s.iv = response.Data[scSecretLength:]
|
||||||
|
|
||||||
if err := s.mutuallyAuthenticate(); err != nil {
|
if err := s.mutuallyAuthenticate(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -171,12 +171,12 @@ func (s *SecureChannelSession) Open() error {
|
|||||||
// mutuallyAuthenticate is an internal method to authenticate both ends of the
|
// mutuallyAuthenticate is an internal method to authenticate both ends of the
|
||||||
// connection.
|
// connection.
|
||||||
func (s *SecureChannelSession) mutuallyAuthenticate() error {
|
func (s *SecureChannelSession) mutuallyAuthenticate() error {
|
||||||
data := make([]byte, SC_SECRET_LENGTH)
|
data := make([]byte, scSecretLength)
|
||||||
if _, err := rand.Read(data); err != nil {
|
if _, err := rand.Read(data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := s.TransmitEncrypted(CLA_SCWALLET, INS_MUTUALLY_AUTHENTICATE, 0, 0, data)
|
response, err := s.TransmitEncrypted(claSCWallet, insMutuallyAuthenticate, 0, 0, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -184,18 +184,18 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error {
|
|||||||
return fmt.Errorf("Got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
|
return fmt.Errorf("Got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(response.Data) != SC_SECRET_LENGTH {
|
if len(response.Data) != scSecretLength {
|
||||||
return fmt.Errorf("Response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), SC_SECRET_LENGTH)
|
return fmt.Errorf("Response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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: CLA_SCWALLET,
|
Cla: claSCWallet,
|
||||||
Ins: INS_OPEN_SECURE_CHANNEL,
|
Ins: insOpenSecureChannel,
|
||||||
P1: s.PairingIndex,
|
P1: s.PairingIndex,
|
||||||
P2: 0,
|
P2: 0,
|
||||||
Data: s.publicKey,
|
Data: s.publicKey,
|
||||||
@ -203,11 +203,11 @@ 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: CLA_SCWALLET,
|
Cla: claSCWallet,
|
||||||
Ins: INS_PAIR,
|
Ins: insPair,
|
||||||
P1: p1,
|
P1: p1,
|
||||||
P2: 0,
|
P2: 0,
|
||||||
Data: data,
|
Data: data,
|
||||||
@ -215,7 +215,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")
|
||||||
@ -225,7 +225,7 @@ func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []b
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
meta := []byte{cla, ins, p1, p2, byte(len(data) + SC_BLOCK_SIZE), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
meta := []byte{cla, ins, p1, p2, byte(len(data) + scBlockSize), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
if err = s.updateIV(meta, data); err != nil {
|
if err = s.updateIV(meta, data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -263,17 +263,17 @@ func (s *SecureChannelSession) TransmitEncrypted(cla, ins, p1, p2 byte, data []b
|
|||||||
rapdu := &ResponseAPDU{}
|
rapdu := &ResponseAPDU{}
|
||||||
rapdu.deserialize(plainData)
|
rapdu.deserialize(plainData)
|
||||||
|
|
||||||
if rapdu.Sw1 != SW1_OK {
|
if rapdu.Sw1 != sw1Ok {
|
||||||
return nil, fmt.Errorf("Unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
return nil, fmt.Errorf("Unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return rapdu, nil
|
return rapdu, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// encryptAPDU is an internal method that serializes and encrypts an APDU
|
// encryptAPDU is an internal method that serializes and encrypts an APDU.
|
||||||
func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
|
func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
|
||||||
if len(data) > MAX_PAYLOAD_SIZE {
|
if len(data) > maxPayloadSize {
|
||||||
return nil, fmt.Errorf("Payload of %d bytes exceeds maximum of %d", len(data), MAX_PAYLOAD_SIZE)
|
return nil, fmt.Errorf("Payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
|
||||||
}
|
}
|
||||||
data = pad(data, 0x80)
|
data = pad(data, 0x80)
|
||||||
|
|
||||||
@ -288,7 +288,7 @@ func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// pad applies message padding to a 16 byte boundary
|
// pad applies message padding to a 16 byte boundary.
|
||||||
func pad(data []byte, terminator byte) []byte {
|
func pad(data []byte, terminator byte) []byte {
|
||||||
padded := make([]byte, (len(data)/16+1)*16)
|
padded := make([]byte, (len(data)/16+1)*16)
|
||||||
copy(padded, data)
|
copy(padded, data)
|
||||||
@ -296,7 +296,7 @@ func pad(data []byte, terminator byte) []byte {
|
|||||||
return padded
|
return padded
|
||||||
}
|
}
|
||||||
|
|
||||||
// decryptAPDU is an internal method that decrypts and deserializes an APDU
|
// decryptAPDU is an internal method that decrypts and deserializes an APDU.
|
||||||
func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) {
|
func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) {
|
||||||
a, err := aes.NewCipher(s.sessionEncKey)
|
a, err := aes.NewCipher(s.sessionEncKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -310,7 +310,7 @@ func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) {
|
|||||||
return unpad(ret, 0x80)
|
return unpad(ret, 0x80)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unpad strips padding from a message
|
// unpad strips padding from a message.
|
||||||
func unpad(data []byte, terminator byte) ([]byte, error) {
|
func unpad(data []byte, terminator byte) ([]byte, error) {
|
||||||
for i := 1; i <= 16; i++ {
|
for i := 1; i <= 16; i++ {
|
||||||
switch data[len(data)-i] {
|
switch data[len(data)-i] {
|
||||||
|
@ -42,7 +42,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
APPLET_AID = []byte{0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x57, 0x61, 0x6C, 0x6C, 0x65, 0x74, 0x41, 0x70, 0x70}
|
appletAID = []byte{0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x57, 0x61, 0x6C, 0x6C, 0x65, 0x74, 0x41, 0x70, 0x70}
|
||||||
AlreadyOpenError = errors.New("Wallet already open")
|
AlreadyOpenError = errors.New("Wallet already open")
|
||||||
PairingRequiredError = errors.New("Pairing required with personal.openWallet(puk)")
|
PairingRequiredError = errors.New("Pairing required with personal.openWallet(puk)")
|
||||||
PinRequiredError = errors.New("Must unlock with personal.openWallet(pin)")
|
PinRequiredError = errors.New("Must unlock with personal.openWallet(pin)")
|
||||||
@ -52,23 +52,23 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CLA_SCWALLET = 0x80
|
claSCWallet = 0x80
|
||||||
INS_VERIFY_PIN = 0x20
|
insVerifyPin = 0x20
|
||||||
INS_EXPORT_KEY = 0xC2
|
insExportKey = 0xC2
|
||||||
INS_SIGN = 0xC0
|
insSign = 0xC0
|
||||||
INS_LOAD_KEY = 0xD0
|
insLoadKey = 0xD0
|
||||||
INS_DERIVE_KEY = 0xD1
|
insDeriveKey = 0xD1
|
||||||
INS_STATUS = 0xF2
|
insStatus = 0xF2
|
||||||
DERIVE_P1_ASSISTED = uint8(0x01)
|
deriveP1Assisted = uint8(0x01)
|
||||||
DERIVE_P1_APPEND = uint8(0x80)
|
deriveP1Append = uint8(0x80)
|
||||||
DERIVE_P2_KEY_PATH = uint8(0x00)
|
deriveP2KeyPath = uint8(0x00)
|
||||||
DERIVE_P2_PUBLIC_KEY = uint8(0x01)
|
deriveP2PublicKey = uint8(0x01)
|
||||||
STATUS_P1_WALLET_STATUS = uint8(0x00)
|
statusP1WalletStatus = uint8(0x00)
|
||||||
STATUS_P1_PATH = uint8(0x01)
|
statusP1Path = uint8(0x01)
|
||||||
SIGN_P1_PRECOMPUTED_HASH = uint8(0x01)
|
signP1PrecomputedHash = uint8(0x01)
|
||||||
SIGN_P2_ONLY_BLOCK = uint8(0x81)
|
signP2OnlyBlock = uint8(0x81)
|
||||||
EXPORT_P1_ANY = uint8(0x00)
|
exportP1Any = uint8(0x00)
|
||||||
EXPORT_P2_PUBKEY = uint8(0x01)
|
exportP2Pubkey = uint8(0x01)
|
||||||
|
|
||||||
// Minimum time to wait between self derivation attempts, even it the user is
|
// Minimum time to wait between self derivation attempts, even it the user is
|
||||||
// requesting accounts like crazy.
|
// requesting accounts like crazy.
|
||||||
@ -120,10 +120,10 @@ func transmit(card *scard.Card, command *CommandAPDU) (*ResponseAPDU, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Are we being asked to fetch the response separately?
|
// Are we being asked to fetch the response separately?
|
||||||
if response.Sw1 == SW1_GET_RESPONSE && (command.Cla != CLA_ISO7816 || command.Ins != INS_GET_RESPONSE) {
|
if response.Sw1 == sw1GetResponse && (command.Cla != claISO7816 || command.Ins != insGetResponse) {
|
||||||
return transmit(card, &CommandAPDU{
|
return transmit(card, &CommandAPDU{
|
||||||
Cla: CLA_ISO7816,
|
Cla: claISO7816,
|
||||||
Ins: INS_GET_RESPONSE,
|
Ins: insGetResponse,
|
||||||
P1: 0,
|
P1: 0,
|
||||||
P2: 0,
|
P2: 0,
|
||||||
Data: nil,
|
Data: nil,
|
||||||
@ -131,7 +131,7 @@ func transmit(card *scard.Card, command *CommandAPDU) (*ResponseAPDU, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.Sw1 != SW1_OK {
|
if response.Sw1 != sw1Ok {
|
||||||
return nil, fmt.Errorf("Unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
|
return nil, fmt.Errorf("Unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,11 +173,11 @@ func (w *Wallet) connect() error {
|
|||||||
// doselect is an internal (unlocked) function to send a SELECT APDU to the card.
|
// doselect is an internal (unlocked) function to send a SELECT APDU to the card.
|
||||||
func (w *Wallet) doselect() (*applicationInfo, error) {
|
func (w *Wallet) doselect() (*applicationInfo, error) {
|
||||||
response, err := transmit(w.card, &CommandAPDU{
|
response, err := transmit(w.card, &CommandAPDU{
|
||||||
Cla: CLA_ISO7816,
|
Cla: claISO7816,
|
||||||
Ins: INS_SELECT,
|
Ins: insSelect,
|
||||||
P1: 4,
|
P1: 4,
|
||||||
P2: 0,
|
P2: 0,
|
||||||
Data: APPLET_AID,
|
Data: appletAID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -685,7 +685,7 @@ func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationP
|
|||||||
return accounts.ParseDerivationPath(parts[1])
|
return accounts.ParseDerivationPath(parts[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session represents a secured communication session with the wallet
|
// Session represents a secured communication session with the wallet.
|
||||||
type Session struct {
|
type Session struct {
|
||||||
Wallet *Wallet // A handle to the wallet that opened the session
|
Wallet *Wallet // A handle to the wallet that opened the session
|
||||||
Channel *SecureChannelSession // A secure channel for encrypted messages
|
Channel *SecureChannelSession // A secure channel for encrypted messages
|
||||||
@ -693,13 +693,13 @@ type Session struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pair establishes a new pairing over this channel, using the provided secret.
|
// pair establishes a new pairing over this channel, using the provided secret.
|
||||||
func (s *Session) pair(secret []byte) (SmartcardPairing, error) {
|
func (s *Session) pair(secret []byte) (smartcardPairing, error) {
|
||||||
err := s.Channel.Pair(secret)
|
err := s.Channel.Pair(secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SmartcardPairing{}, err
|
return smartcardPairing{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return SmartcardPairing{
|
return smartcardPairing{
|
||||||
PublicKey: s.Wallet.PublicKey,
|
PublicKey: s.Wallet.PublicKey,
|
||||||
PairingIndex: s.Channel.PairingIndex,
|
PairingIndex: s.Channel.PairingIndex,
|
||||||
PairingKey: s.Channel.PairingKey,
|
PairingKey: s.Channel.PairingKey,
|
||||||
@ -707,7 +707,7 @@ func (s *Session) pair(secret []byte) (SmartcardPairing, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// unpair deletes an existing pairing
|
// unpair deletes an existing pairing.
|
||||||
func (s *Session) unpair() error {
|
func (s *Session) unpair() error {
|
||||||
if !s.verified {
|
if !s.verified {
|
||||||
return fmt.Errorf("Unpair requires that the PIN be verified")
|
return fmt.Errorf("Unpair requires that the PIN be verified")
|
||||||
@ -715,27 +715,27 @@ func (s *Session) unpair() error {
|
|||||||
return s.Channel.Unpair()
|
return s.Channel.Unpair()
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifyPin unlocks a wallet with the provided pin
|
// verifyPin unlocks a wallet with the provided pin.
|
||||||
func (s *Session) verifyPin(pin []byte) error {
|
func (s *Session) verifyPin(pin []byte) error {
|
||||||
if _, err := s.Channel.TransmitEncrypted(CLA_SCWALLET, INS_VERIFY_PIN, 0, 0, pin); err != nil {
|
if _, err := s.Channel.TransmitEncrypted(claSCWallet, insVerifyPin, 0, 0, pin); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.verified = true
|
s.verified = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// release releases resources associated with the channel
|
// release releases resources associated with the channel.
|
||||||
func (s *Session) release() error {
|
func (s *Session) release() error {
|
||||||
return s.Wallet.card.Disconnect(scard.LeaveCard)
|
return s.Wallet.card.Disconnect(scard.LeaveCard)
|
||||||
}
|
}
|
||||||
|
|
||||||
// paired returns true if a valid pairing exists
|
// paired returns true if a valid pairing exists.
|
||||||
func (s *Session) paired() bool {
|
func (s *Session) paired() bool {
|
||||||
return s.Channel.PairingKey != nil
|
return s.Channel.PairingKey != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate uses an existing pairing to establish a secure channel
|
// authenticate uses an existing pairing to establish a secure channel.
|
||||||
func (s *Session) authenticate(pairing SmartcardPairing) error {
|
func (s *Session) authenticate(pairing smartcardPairing) error {
|
||||||
if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) {
|
if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) {
|
||||||
return fmt.Errorf("Cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
|
return fmt.Errorf("Cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
|
||||||
}
|
}
|
||||||
@ -744,7 +744,7 @@ func (s *Session) authenticate(pairing SmartcardPairing) error {
|
|||||||
return s.Channel.Open()
|
return s.Channel.Open()
|
||||||
}
|
}
|
||||||
|
|
||||||
// walletStatus describes a smartcard wallet's status information
|
// walletStatus describes a smartcard wallet's status information.
|
||||||
type walletStatus struct {
|
type walletStatus struct {
|
||||||
PinRetryCount int // Number of remaining PIN retries
|
PinRetryCount int // Number of remaining PIN retries
|
||||||
PukRetryCount int // Number of remaining PUK retries
|
PukRetryCount int // Number of remaining PUK retries
|
||||||
@ -756,9 +756,9 @@ func (w walletStatus) String() string {
|
|||||||
return fmt.Sprintf("pinRetryCount=%d, pukRetryCount=%d, initialized=%t, supportsPkDerivation=%t", w.PinRetryCount, w.PukRetryCount, w.Initialized, w.SupportsPKDerivation)
|
return fmt.Sprintf("pinRetryCount=%d, pukRetryCount=%d, initialized=%t, supportsPkDerivation=%t", w.PinRetryCount, w.PukRetryCount, w.Initialized, w.SupportsPKDerivation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getWalletStatus fetches the wallet's status from the card
|
// getWalletStatus fetches the wallet's status from the card.
|
||||||
func (s *Session) getWalletStatus() (*walletStatus, error) {
|
func (s *Session) getWalletStatus() (*walletStatus, error) {
|
||||||
response, err := s.Channel.TransmitEncrypted(CLA_SCWALLET, INS_STATUS, STATUS_P1_WALLET_STATUS, 0, nil)
|
response, err := s.Channel.TransmitEncrypted(claSCWallet, insStatus, statusP1WalletStatus, 0, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -771,9 +771,9 @@ func (s *Session) getWalletStatus() (*walletStatus, error) {
|
|||||||
return status, nil
|
return status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDerivationPath fetches the wallet's current derivation path from the card
|
// getDerivationPath fetches the wallet's current derivation path from the card.
|
||||||
func (s *Session) getDerivationPath() (accounts.DerivationPath, error) {
|
func (s *Session) getDerivationPath() (accounts.DerivationPath, error) {
|
||||||
response, err := s.Channel.TransmitEncrypted(CLA_SCWALLET, INS_STATUS, STATUS_P1_PATH, 0, nil)
|
response, err := s.Channel.TransmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -783,14 +783,14 @@ func (s *Session) getDerivationPath() (accounts.DerivationPath, error) {
|
|||||||
return path, binary.Read(buf, binary.BigEndian, &path)
|
return path, binary.Read(buf, binary.BigEndian, &path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// initializeData contains data needed to initialize the smartcard wallet
|
// initializeData contains data needed to initialize the smartcard wallet.
|
||||||
type initializeData struct {
|
type initializeData struct {
|
||||||
PublicKey []byte `asn1:"tag:0"`
|
PublicKey []byte `asn1:"tag:0"`
|
||||||
PrivateKey []byte `asn1:"tag:1"`
|
PrivateKey []byte `asn1:"tag:1"`
|
||||||
ChainCode []byte `asn1:"tag:2"`
|
ChainCode []byte `asn1:"tag:2"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize initializes the card with new key data
|
// initialize initializes the card with new key data.
|
||||||
func (s *Session) initialize(seed []byte) error {
|
func (s *Session) initialize(seed []byte) error {
|
||||||
// HMAC the seed to produce the private key and chain code
|
// HMAC the seed to produce the private key and chain code
|
||||||
mac := hmac.New(sha512.New, []byte("Bitcoin seed"))
|
mac := hmac.New(sha512.New, []byte("Bitcoin seed"))
|
||||||
@ -814,11 +814,11 @@ func (s *Session) initialize(seed []byte) error {
|
|||||||
// Nasty hack to force the top-level struct tag to be context-specific
|
// Nasty hack to force the top-level struct tag to be context-specific
|
||||||
data[0] = 0xA1
|
data[0] = 0xA1
|
||||||
|
|
||||||
_, err = s.Channel.TransmitEncrypted(CLA_SCWALLET, INS_LOAD_KEY, 0x02, 0, data)
|
_, err = s.Channel.TransmitEncrypted(claSCWallet, insLoadKey, 0x02, 0, data)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// derive derives a new HD key path on the card
|
// derive derives a new HD key path on the card.
|
||||||
func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error) {
|
func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error) {
|
||||||
// If the current path is a prefix of the desired path, we don't have to
|
// If the current path is a prefix of the desired path, we don't have to
|
||||||
// start again.
|
// start again.
|
||||||
@ -858,7 +858,7 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
|
|||||||
return s.Wallet.makeAccount(crypto.PubkeyToAddress(*crypto.ToECDSAPub(pubkey)), path), nil
|
return s.Wallet.makeAccount(crypto.PubkeyToAddress(*crypto.ToECDSAPub(pubkey)), path), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyDerivationInfo contains information on the current key derivation step
|
// keyDerivationInfo contains information on the current key derivation step.
|
||||||
type keyDerivationInfo struct {
|
type keyDerivationInfo struct {
|
||||||
PublicKeyX []byte `asn1:"tag:3"` // The X coordinate of the current public key
|
PublicKeyX []byte `asn1:"tag:3"` // The X coordinate of the current public key
|
||||||
Signature struct {
|
Signature struct {
|
||||||
@ -871,16 +871,16 @@ type keyDerivationInfo struct {
|
|||||||
// a specific path, and performing the necessary computations to finish the public key
|
// a specific path, and performing the necessary computations to finish the public key
|
||||||
// generation step.
|
// generation step.
|
||||||
func (s *Session) deriveKeyAssisted(reset bool, pathComponent uint32) ([]byte, error) {
|
func (s *Session) deriveKeyAssisted(reset bool, pathComponent uint32) ([]byte, error) {
|
||||||
p1 := DERIVE_P1_ASSISTED
|
p1 := deriveP1Assisted
|
||||||
if !reset {
|
if !reset {
|
||||||
p1 |= DERIVE_P1_APPEND
|
p1 |= deriveP1Append
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
if err := binary.Write(buf, binary.BigEndian, pathComponent); err != nil {
|
if err := binary.Write(buf, binary.BigEndian, pathComponent); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
response, err := s.Channel.TransmitEncrypted(CLA_SCWALLET, INS_DERIVE_KEY, p1, DERIVE_P2_KEY_PATH, buf.Bytes())
|
response, err := s.Channel.TransmitEncrypted(claSCWallet, insDeriveKey, p1, deriveP2KeyPath, buf.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -900,7 +900,7 @@ func (s *Session) deriveKeyAssisted(reset bool, pathComponent uint32) ([]byte, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = s.Channel.TransmitEncrypted(CLA_SCWALLET, INS_DERIVE_KEY, DERIVE_P1_ASSISTED|DERIVE_P1_APPEND, DERIVE_P2_PUBLIC_KEY, pubkey)
|
_, err = s.Channel.TransmitEncrypted(claSCWallet, insDeriveKey, deriveP1Assisted|deriveP1Append, deriveP2PublicKey, pubkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -908,15 +908,15 @@ func (s *Session) deriveKeyAssisted(reset bool, pathComponent uint32) ([]byte, e
|
|||||||
return pubkey, nil
|
return pubkey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyExport contains information on an exported keypair
|
// keyExport contains information on an exported keypair.
|
||||||
type keyExport struct {
|
type keyExport struct {
|
||||||
PublicKey []byte `asn1:"tag:0"`
|
PublicKey []byte `asn1:"tag:0"`
|
||||||
PrivateKey []byte `asn1:"tag:1,optional"`
|
PrivateKey []byte `asn1:"tag:1,optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPublicKey returns the public key for the current derivation path
|
// getPublicKey returns the public key for the current derivation path.
|
||||||
func (s *Session) getPublicKey() ([]byte, error) {
|
func (s *Session) getPublicKey() ([]byte, error) {
|
||||||
response, err := s.Channel.TransmitEncrypted(CLA_SCWALLET, INS_EXPORT_KEY, EXPORT_P1_ANY, EXPORT_P2_PUBKEY, nil)
|
response, err := s.Channel.TransmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -930,7 +930,7 @@ func (s *Session) getPublicKey() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// signatureData contains information on a signature - the signature itself and
|
// signatureData contains information on a signature - the signature itself and
|
||||||
// the corresponding public key
|
// the corresponding public key.
|
||||||
type signatureData struct {
|
type signatureData struct {
|
||||||
PublicKey []byte `asn1:"tag:0"`
|
PublicKey []byte `asn1:"tag:0"`
|
||||||
Signature struct {
|
Signature struct {
|
||||||
@ -949,7 +949,7 @@ func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error
|
|||||||
}
|
}
|
||||||
deriveTime := time.Now()
|
deriveTime := time.Now()
|
||||||
|
|
||||||
response, err := s.Channel.TransmitEncrypted(CLA_SCWALLET, INS_SIGN, SIGN_P1_PRECOMPUTED_HASH, SIGN_P2_ONLY_BLOCK, hash)
|
response, err := s.Channel.TransmitEncrypted(claSCWallet, insSign, signP1PrecomputedHash, signP2OnlyBlock, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -473,6 +473,7 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args Sen
|
|||||||
return s.SendTransaction(ctx, args, passwd)
|
return s.SendTransaction(ctx, args, passwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitializeWallet initializes a new wallet at the provided URL, by generating and returning a new private key.
|
||||||
func (s *PrivateAccountAPI) InitializeWallet(ctx context.Context, url string) (string, error) {
|
func (s *PrivateAccountAPI) InitializeWallet(ctx context.Context, url string) (string, error) {
|
||||||
wallet, err := s.am.Wallet(url)
|
wallet, err := s.am.Wallet(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -499,6 +500,7 @@ func (s *PrivateAccountAPI) InitializeWallet(ctx context.Context, url string) (s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unpair deletes a pairing between wallet and geth.
|
||||||
func (s *PrivateAccountAPI) Unpair(ctx context.Context, url string, pin string) error {
|
func (s *PrivateAccountAPI) Unpair(ctx context.Context, url string, pin string) error {
|
||||||
wallet, err := s.am.Wallet(url)
|
wallet, err := s.am.Wallet(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user