accounts/scwallet: flag to specify path to smartcard daemon (#19439)

* accounts/scwallet: Add a switch to enable smartcard support

* accounts: change the meaning of the switch

* disable card support in windows until tested
* only activate account if pcscd socket file is present
* the switch is now the path to the socket file

* accounts/scwallet: holiman's review feedback

* accounts/scwallet: send the path to go-pcsclite

* accounts/scwallet: add default, per platform path

* accounts/scwallet: fix error log warning

* accounts/scwallet: update pcsc lib to latest

* accounts/scwallet: use default path from pcsclite

* scwallet: forgot to change switch name

* cmd: minor style cleanups (error handling first, then happy path)
This commit is contained in:
Guillaume Ballet 2019-05-31 11:30:28 +02:00 committed by Péter Szilágyi
parent 30263ad37d
commit 7a22da98b9
12 changed files with 158 additions and 17 deletions

View File

@ -152,8 +152,8 @@ func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error {
} }
// NewHub creates a new hardware wallet manager for smartcards. // NewHub creates a new hardware wallet manager for smartcards.
func NewHub(scheme string, datadir string) (*Hub, error) { func NewHub(daemonPath string, scheme string, datadir string) (*Hub, error) {
context, err := pcsc.EstablishContext(pcsc.ScopeSystem) context, err := pcsc.EstablishContext(daemonPath, pcsc.ScopeSystem)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -66,6 +66,7 @@ var (
utils.KeyStoreDirFlag, utils.KeyStoreDirFlag,
utils.ExternalSignerFlag, utils.ExternalSignerFlag,
utils.NoUSBFlag, utils.NoUSBFlag,
utils.SmartCardDaemonPathFlag,
utils.DashboardEnabledFlag, utils.DashboardEnabledFlag,
utils.DashboardAddrFlag, utils.DashboardAddrFlag,
utils.DashboardPortFlag, utils.DashboardPortFlag,

View File

@ -72,6 +72,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.AncientFlag, utils.AncientFlag,
utils.KeyStoreDirFlag, utils.KeyStoreDirFlag,
utils.NoUSBFlag, utils.NoUSBFlag,
utils.SmartCardDaemonPathFlag,
utils.NetworkIdFlag, utils.NetworkIdFlag,
utils.TestnetFlag, utils.TestnetFlag,
utils.RinkebyFlag, utils.RinkebyFlag,

View File

@ -58,6 +58,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/p2p/netutil"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
pcsclite "github.com/gballet/go-libpcsclite"
cli "gopkg.in/urfave/cli.v1" cli "gopkg.in/urfave/cli.v1"
) )
@ -129,6 +130,11 @@ var (
Name: "nousb", Name: "nousb",
Usage: "Disables monitoring for and managing USB hardware wallets", Usage: "Disables monitoring for and managing USB hardware wallets",
} }
SmartCardDaemonPathFlag = cli.StringFlag{
Name: "pcscdpath",
Usage: "Path to the smartcard daemon (pcscd) socket file",
Value: pcsclite.PCSCDSockName,
}
NetworkIdFlag = cli.Uint64Flag{ NetworkIdFlag = cli.Uint64Flag{
Name: "networkid", Name: "networkid",
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)", Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)",
@ -1126,6 +1132,7 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
setWS(ctx, cfg) setWS(ctx, cfg)
setNodeUserIdent(ctx, cfg) setNodeUserIdent(ctx, cfg)
setDataDir(ctx, cfg) setDataDir(ctx, cfg)
setSmartCard(ctx, cfg)
if ctx.GlobalIsSet(ExternalSignerFlag.Name) { if ctx.GlobalIsSet(ExternalSignerFlag.Name) {
cfg.ExternalSigner = ctx.GlobalString(ExternalSignerFlag.Name) cfg.ExternalSigner = ctx.GlobalString(ExternalSignerFlag.Name)
@ -1145,6 +1152,26 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
} }
} }
func setSmartCard(ctx *cli.Context, cfg *node.Config) {
// Skip enabling smartcards if no path is set
path := ctx.GlobalString(SmartCardDaemonPathFlag.Name)
if path == "" {
return
}
// Sanity check that the smartcard path is valid
fi, err := os.Stat(path)
if err != nil {
log.Error("Failed to verify smartcard daemon path", "path", path, "err", err)
return
}
if fi.Mode()&os.ModeType != os.ModeSocket {
log.Error("Invalid smartcard daemon path", "path", path, "type", fi.Mode().String())
return
}
// Smartcard daemon path exists and is a socket, enable it
cfg.SmartCardDaemonPath = path
}
func setDataDir(ctx *cli.Context, cfg *node.Config) { func setDataDir(ctx *cli.Context, cfg *node.Config) {
switch { switch {
case ctx.GlobalIsSet(DataDirFlag.Name): case ctx.GlobalIsSet(DataDirFlag.Name):

View File

@ -95,6 +95,9 @@ type Config struct {
// NoUSB disables hardware wallet monitoring and connectivity. // NoUSB disables hardware wallet monitoring and connectivity.
NoUSB bool `toml:",omitempty"` NoUSB bool `toml:",omitempty"`
// SmartCardDaemonPath is the path to the smartcard daemon's socket
SmartCardDaemonPath string `toml:",omitempty"`
// IPCPath is the requested location to place the IPC endpoint. If the path is // IPCPath is the requested location to place the IPC endpoint. If the path is
// a simple file name, it is placed inside the data directory (or on the root // a simple file name, it is placed inside the data directory (or on the root
// pipe path on Windows), whereas if it's a resolvable path name (absolute or // pipe path on Windows), whereas if it's a resolvable path name (absolute or
@ -505,11 +508,13 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
backends = append(backends, trezorhub) backends = append(backends, trezorhub)
} }
} }
// Start a smart card hub if len(conf.SmartCardDaemonPath) > 0 {
if schub, err := scwallet.NewHub(scwallet.Scheme, keydir); err != nil { // Start a smart card hub
log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err)) if schub, err := scwallet.NewHub(conf.SmartCardDaemonPath, scwallet.Scheme, keydir); err != nil {
} else { log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err))
backends = append(backends, schub) } else {
backends = append(backends, schub)
}
} }
} }

View File

@ -61,8 +61,6 @@ const (
SCardPowever = 0x0010 /* Card is powered */ SCardPowever = 0x0010 /* Card is powered */
SCardNegotiable = 0x0020 /* Ready for PTS */ SCardNegotiable = 0x0020 /* Ready for PTS */
SCardSpecific = 0x0040 /* PTS has been set */ SCardSpecific = 0x0040 /* PTS has been set */
PCSCDSockName = "/run/pcscd/pcscd.comm"
) )
// List of commands to send to the daemon // List of commands to send to the daemon

35
vendor/github.com/gballet/go-libpcsclite/doc_bsd.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
// BSD 3-Clause License
//
// Copyright (c) 2019, Guillaume Ballet
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// +build dragonfly darwin freebsd netbsd openbsd solaris
package pcsc
const PCSCDSockName string = "/var/run/pcscd/pcscd.comm"

35
vendor/github.com/gballet/go-libpcsclite/doc_linux.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
// BSD 3-Clause License
//
// Copyright (c) 2019, Guillaume Ballet
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// +build linux
package pcsc
const PCSCDSockName string = "/run/pcscd/pcscd.comm"

View File

@ -0,0 +1,35 @@
// BSD 3-Clause License
//
// Copyright (c) 2019, Guillaume Ballet
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// +build windows
package pcsc
const PCSCDSockName string = ""

View File

@ -62,7 +62,7 @@ func messageSendWithHeader(command uint32, conn net.Conn, data []byte) error {
return err return err
} }
// ClientSetupSession prepares a communication channel for the client to talk to the server. // clientSetupSession prepares a communication channel for the client to talk to the server.
// This is called by the application to create a socket for local IPC with the // This is called by the application to create a socket for local IPC with the
// server. The socket is associated to the file \c PCSCLITE_CSOCK_NAME. // server. The socket is associated to the file \c PCSCLITE_CSOCK_NAME.
/* /*
@ -73,6 +73,10 @@ func messageSendWithHeader(command uint32, conn net.Conn, data []byte) error {
* @retval -1 The socket can not open a connection. * @retval -1 The socket can not open a connection.
* @retval -1 Can not set the socket to non-blocking. * @retval -1 Can not set the socket to non-blocking.
*/ */
func clientSetupSession() (net.Conn, error) { func clientSetupSession(daemonPath string) (net.Conn, error) {
return net.Dial("unix", PCSCDSockName) path := PCSCDSockName
if len(daemonPath) > 0 {
path = daemonPath
}
return net.Dial("unix", path)
} }

View File

@ -56,10 +56,10 @@ type Client struct {
// EstablishContext asks the PCSC daemon to create a context // EstablishContext asks the PCSC daemon to create a context
// handle for further communication with connected cards and // handle for further communication with connected cards and
// readers. // readers.
func EstablishContext(scope uint32) (*Client, error) { func EstablishContext(path string, scope uint32) (*Client, error) {
client := &Client{} client := &Client{}
conn, err := clientSetupSession() conn, err := clientSetupSession(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }

6
vendor/vendor.json vendored
View File

@ -135,10 +135,10 @@
"revisionTime": "2018-04-18T12:24:29Z" "revisionTime": "2018-04-18T12:24:29Z"
}, },
{ {
"checksumSHA1": "GXqHzd0XkPLX/iulpOncaxbxzZo=", "checksumSHA1": "+fiJGimxPPRSfi9sED4Lp6ytBeo=",
"path": "github.com/gballet/go-libpcsclite", "path": "github.com/gballet/go-libpcsclite",
"revision": "312b5175032f98274685a4dd81935a92ad2412a5", "revision": "2fd9b619dd3c5d74acbd975f997a6441984d74a7",
"revisionTime": "2019-04-03T18:15:18Z" "revisionTime": "2019-05-28T10:50:17Z"
}, },
{ {
"checksumSHA1": "gxV/cPPLkByTdY8y172t7v4qcZA=", "checksumSHA1": "gxV/cPPLkByTdY8y172t7v4qcZA=",