README
¶
Using the smartcard axiawallet
Requirements
- A USB smartcard reader
- A keycard that supports the status app
- PCSCD version 4.3 running on your system Only version 4.3 is currently supported
Preparing the smartcard
WARNING: FOILLOWING THESE INSTRUCTIONS WILL DESTROY THE MASTER KEY ON YOUR CARD. ONLY PROCEED IF NO FUNDS ARE ASSOCIATED WITH THESE ACCOUNTS
You can use status' keycard-cli and you should get at least version 2.1.1 of their smartcard application
You also need to make sure that the PCSC daemon is running on your system.
Then, you can install the application to the card by typing:
keycard install -a keycard_v2.2.1.cap && keycard init
At the end of this process, you will be provided with a PIN, a PUK and a pairing password. Write them down, you'll need them shortly.
Start geth
with the console
command. You will notice the following warning:
WARN [04-09|16:58:38.898] Failed to open axiawallet url=keycard://044def09 err="smartcard: pairing password needed"
Write down the URL (keycard://044def09
in this example). Then ask geth
to open the axiawallet:
> personal.openAXIAwallet("keycard://044def09", "pairing password")
The pairing password has been generated during the card initialization process.
The process needs to be repeated once more with the PIN:
> personal.openAXIAwallet("keycard://044def09", "PIN number")
If everything goes well, you should see your new account when typing personal
on the console:
> personal
WARN [04-09|17:02:07.330] Smartcard axiawallet account derivation failed url=keycard://044def09 err="Unexpected response status Cla=0x80, Ins=0xd1, Sw=0x6985"
{
listAccounts: [],
listAXIAwallets: [{
status: "Empty, waiting for initialization",
url: "keycard://044def09"
}],
...
}
So the communication with the card is working, but there is no key associated with this axiawallet. Let's create it:
> personal.initializeAXIAwallet("keycard://044def09")
"tilt ... impact"
You should get a list of words, this is your seed so write them down. Your axiawallet should now be initialized:
> personal.listAXIAwallets
[{
accounts: [{
address: "0x678b7cd55c61917defb23546a41803c5bfefbc7a",
url: "keycard://044d/m/44'/60'/0'/0/0"
}],
status: "Online",
url: "keycard://044def09"
}]
You're all set!
Usage
- Start
geth
with theconsole
command - Check the card's URL by checking
personal.listAXIAwallets
:
listAXIAwallets: [{
status: "Online, can derive public keys",
url: "keycard://a4d73015"
}]
- Open the axiawallet, you will be prompted for your pairing password, then PIN:
personal.openAXIAwallet("keycard://a4d73015")
- Check that creation was successful by typing e.g.
personal
. Then use it like a regular axiawallet.
Known issues
- Starting geth with a valid card seems to make firefox crash.
- PCSC version 4.4 should work, but is currently untested
Documentation
¶
Index ¶
- Constants
- Variables
- type AXIAwallet
- func (w *AXIAwallet) Accounts() []accounts.Account
- func (w *AXIAwallet) Close() error
- func (w *AXIAwallet) Contains(account accounts.Account) bool
- func (w *AXIAwallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error)
- func (w *AXIAwallet) Initialize(seed []byte) error
- func (w *AXIAwallet) Open(passphrase string) error
- func (w *AXIAwallet) SelfDerive(bases []accounts.DerivationPath, chain interfaces.ChainStateReader)
- func (w *AXIAwallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error)
- func (w *AXIAwallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error)
- func (w *AXIAwallet) SignText(account accounts.Account, text []byte) ([]byte, error)
- func (w *AXIAwallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error)
- func (w *AXIAwallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
- func (w *AXIAwallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, ...) (*types.Transaction, error)
- func (w *AXIAwallet) Status() (string, error)
- func (w *AXIAwallet) URL() accounts.URL
- func (w *AXIAwallet) Unpair(pin []byte) error
- type Hub
- type SecureChannelSession
- type Session
Constants ¶
const ( P1DeriveKeyFromMaster = uint8(0x00) P1DeriveKeyFromParent = uint8(0x01) P1DeriveKeyFromCurrent = uint8(0x10) )
List of ADPU command parameters
const Scheme = "keycard"
Scheme is the URI prefix for smartcard axiawallets.
Variables ¶
var ( // DerivationSignatureHash is used to derive the public key from the signature of this hash DerivationSignatureHash = sha256.Sum256(common.Hash{}.Bytes()) )
var ErrAlreadyOpen = errors.New("smartcard: already open")
ErrAlreadyOpen is returned if the smart card is attempted to be opened, but there is already a paired and unlocked session.
var ErrPINNeeded = errors.New("smartcard: pin needed")
ErrPINNeeded is returned if opening the smart card requires a PIN code. In this case, the calling application should request user input to enter the PIN and send it back.
var ErrPINUnblockNeeded = errors.New("smartcard: pin unblock needed")
ErrPINUnblockNeeded is returned if opening the smart card requires a PIN code, but all PIN attempts have already been exhausted. In this case the calling application should request user input for the PUK and a new PIN code to set fo the card.
var ErrPairingPasswordNeeded = errors.New("smartcard: pairing password needed")
ErrPairingPasswordNeeded is returned if opening the smart card requires pairing with a pairing password. In this case, the calling application should request user input to enter the pairing password and send it back.
var ErrPubkeyMismatch = errors.New("smartcard: recovered public key mismatch")
ErrPubkeyMismatch is returned if the public key recovered from a signature does not match the one expected by the user.
Functions ¶
This section is empty.
Types ¶
type AXIAwallet ¶
type AXIAwallet struct { Hub *Hub // A handle to the Hub that instantiated this axiawallet. PublicKey []byte // The axiawallet's public key (used for communication and identification, not signing!) // contains filtered or unexported fields }
AXIAwallet represents a smartcard axiawallet instance.
func NewAXIAwallet ¶
func NewAXIAwallet(hub *Hub, card *pcsc.Card) *AXIAwallet
NewAXIAwallet constructs and returns a new AXIAwallet instance.
func (*AXIAwallet) Accounts ¶
func (w *AXIAwallet) Accounts() []accounts.Account
Accounts retrieves the list of signing accounts the axiawallet is currently aware of. For hierarchical deterministic axiawallets, the list will not be exhaustive, rather only contain the accounts explicitly pinned during account derivation.
func (*AXIAwallet) Close ¶
func (w *AXIAwallet) Close() error
Close stops and closes the axiawallet, freeing any resources.
func (*AXIAwallet) Contains ¶
func (w *AXIAwallet) Contains(account accounts.Account) bool
Contains returns whether an account is part of this particular axiawallet or not.
func (*AXIAwallet) Derive ¶
func (w *AXIAwallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error)
Derive attempts to explicitly derive a hierarchical deterministic account at the specified derivation path. If requested, the derived account will be added to the axiawallet's tracked account list.
func (*AXIAwallet) Initialize ¶
func (w *AXIAwallet) Initialize(seed []byte) error
Initialize installs a keypair generated from the provided key into the axiawallet.
func (*AXIAwallet) Open ¶
func (w *AXIAwallet) Open(passphrase string) error
Open initializes access to a axiawallet instance. It is not meant to unlock or decrypt account keys, rather simply to establish a connection to hardware axiawallets and/or to access derivation seeds.
The passphrase parameter may or may not be used by the implementation of a particular axiawallet instance. The reason there is no passwordless open method is to strive towards a uniform axiawallet handling, oblivious to the different backend providers.
Please note, if you open a axiawallet, you must close it to release any allocated resources (especially important when working with hardware axiawallets).
func (*AXIAwallet) SelfDerive ¶
func (w *AXIAwallet) SelfDerive(bases []accounts.DerivationPath, chain interfaces.ChainStateReader)
SelfDerive sets a base account derivation path from which the axiawallet attempts to discover non zero accounts and automatically add them to list of tracked accounts.
Note, self derivation will increment the last component of the specified path opposed to descending into a child path to allow discovering accounts starting from non zero components.
Some hardware axiawallets switched derivation paths through their evolution, so this method supports providing multiple bases to discover old user accounts too. Only the last base will be used to derive the next empty account.
You can disable automatic account discovery by calling SelfDerive with a nil chain state reader.
func (*AXIAwallet) SignData ¶
func (w *AXIAwallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error)
SignData requests the axiawallet to sign the hash of the given data.
It looks up the account specified either solely via its address contained within, or optionally with the aid of any location metadata from the embedded URL field.
If the axiawallet requires additional authentication to sign the request (e.g. a password to decrypt the account, or a PIN code to verify the transaction), an AuthNeededError instance will be returned, containing infos for the user about which fields or actions are needed. The user may retry by providing the needed details via SignDataWithPassphrase, or by other means (e.g. unlock the account in a keystore).
func (*AXIAwallet) SignDataWithPassphrase ¶
func (w *AXIAwallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error)
SignDataWithPassphrase requests the axiawallet to sign the given hash with the given passphrase as extra authentication information.
It looks up the account specified either solely via its address contained within, or optionally with the aid of any location metadata from the embedded URL field.
func (*AXIAwallet) SignText ¶
SignText requests the axiawallet to sign the hash of a given piece of data, prefixed by the Ethereum prefix scheme It looks up the account specified either solely via its address contained within, or optionally with the aid of any location metadata from the embedded URL field.
If the axiawallet requires additional authentication to sign the request (e.g. a password to decrypt the account, or a PIN code to verify the transaction), an AuthNeededError instance will be returned, containing infos for the user about which fields or actions are needed. The user may retry by providing the needed details via SignHashWithPassphrase, or by other means (e.g. unlock the account in a keystore).
func (*AXIAwallet) SignTextWithPassphrase ¶
func (w *AXIAwallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error)
SignTextWithPassphrase implements accounts.AXIAwallet, attempting to sign the given hash with the given account using passphrase as extra authentication
func (*AXIAwallet) SignTx ¶
func (w *AXIAwallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
SignTx requests the axiawallet to sign the given transaction.
It looks up the account specified either solely via its address contained within, or optionally with the aid of any location metadata from the embedded URL field.
If the axiawallet requires additional authentication to sign the request (e.g. a password to decrypt the account, or a PIN code to verify the transaction), an AuthNeededError instance will be returned, containing infos for the user about which fields or actions are needed. The user may retry by providing the needed details via SignTxWithPassphrase, or by other means (e.g. unlock the account in a keystore).
func (*AXIAwallet) SignTxWithPassphrase ¶
func (w *AXIAwallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
SignTxWithPassphrase requests the axiawallet to sign the given transaction, with the given passphrase as extra authentication information.
It looks up the account specified either solely via its address contained within, or optionally with the aid of any location metadata from the embedded URL field.
func (*AXIAwallet) Status ¶
func (w *AXIAwallet) Status() (string, error)
Status returns a textual status to aid the user in the current state of the axiawallet. It also returns an error indicating any failure the axiawallet might have encountered.
func (*AXIAwallet) URL ¶
func (w *AXIAwallet) URL() accounts.URL
URL retrieves the canonical path under which this axiawallet is reachable. It is user by upper layers to define a sorting order over all axiawallets from multiple backends.
func (*AXIAwallet) Unpair ¶
func (w *AXIAwallet) Unpair(pin []byte) error
Unpair deletes an existing axiawallet pairing.
type Hub ¶
type Hub struct {
// contains filtered or unexported fields
}
Hub is a accounts.Backend that can find and handle generic PC/SC hardware axiawallets.
func (*Hub) AXIAwallets ¶
func (hub *Hub) AXIAwallets() []accounts.AXIAwallet
AXIAwallets implements accounts.Backend, returning all the currently tracked smart cards that appear to be hardware axiawallets.
func (*Hub) Subscribe ¶
func (hub *Hub) Subscribe(sink chan<- accounts.AXIAwalletEvent) event.Subscription
Subscribe implements accounts.Backend, creating an async subscription to receive notifications on the addition or removal of smart card axiawallets.
type SecureChannelSession ¶
type SecureChannelSession struct { PairingKey []byte // A permanent shared secret for a pairing, if present PairingIndex uint8 // The pairing index // contains filtered or unexported fields }
SecureChannelSession enables secure communication with a hardware axiawallet.
func NewSecureChannelSession ¶
func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSession, error)
NewSecureChannelSession creates a new secure channel for the given card and public key.
func (*SecureChannelSession) Open ¶
func (s *SecureChannelSession) Open() error
Open initializes the secure channel.
func (*SecureChannelSession) Pair ¶
func (s *SecureChannelSession) Pair(pairingPassword []byte) error
Pair establishes a new pairing with the smartcard.
func (*SecureChannelSession) Unpair ¶
func (s *SecureChannelSession) Unpair() error
Unpair disestablishes an existing pairing.
type Session ¶
type Session struct { AXIAwallet *AXIAwallet // A handle to the axiawallet that opened the session Channel *SecureChannelSession // A secure channel for encrypted messages // contains filtered or unexported fields }
Session represents a secured communication session with the axiawallet.