github.com/MetalBlockchain/subnet-evm@v0.4.9/accounts/scwallet/securechannel.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2018 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package scwallet
    28  
    29  import (
    30  	"bytes"
    31  	"crypto/aes"
    32  	"crypto/cipher"
    33  	"crypto/elliptic"
    34  	"crypto/rand"
    35  	"crypto/sha256"
    36  	"crypto/sha512"
    37  	"fmt"
    38  
    39  	"github.com/ethereum/go-ethereum/crypto"
    40  	pcsc "github.com/gballet/go-libpcsclite"
    41  	"golang.org/x/crypto/pbkdf2"
    42  	"golang.org/x/text/unicode/norm"
    43  )
    44  
    45  const (
    46  	maxPayloadSize  = 223
    47  	pairP1FirstStep = 0
    48  	pairP1LastStep  = 1
    49  
    50  	scSecretLength = 32
    51  	scBlockSize    = 16
    52  
    53  	insOpenSecureChannel    = 0x10
    54  	insMutuallyAuthenticate = 0x11
    55  	insPair                 = 0x12
    56  	insUnpair               = 0x13
    57  
    58  	pairingSalt = "Keycard Pairing Password Salt"
    59  )
    60  
    61  // SecureChannelSession enables secure communication with a hardware wallet.
    62  type SecureChannelSession struct {
    63  	card          *pcsc.Card // A handle to the smartcard for communication
    64  	secret        []byte     // A shared secret generated from our ECDSA keys
    65  	publicKey     []byte     // Our own ephemeral public key
    66  	PairingKey    []byte     // A permanent shared secret for a pairing, if present
    67  	sessionEncKey []byte     // The current session encryption key
    68  	sessionMacKey []byte     // The current session MAC key
    69  	iv            []byte     // The current IV
    70  	PairingIndex  uint8      // The pairing index
    71  }
    72  
    73  // NewSecureChannelSession creates a new secure channel for the given card and public key.
    74  func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSession, error) {
    75  	// Generate an ECDSA keypair for ourselves
    76  	key, err := crypto.GenerateKey()
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	cardPublic, err := crypto.UnmarshalPubkey(keyData)
    81  	if err != nil {
    82  		return nil, fmt.Errorf("could not unmarshal public key from card: %v", err)
    83  	}
    84  	secret, _ := key.Curve.ScalarMult(cardPublic.X, cardPublic.Y, key.D.Bytes())
    85  	return &SecureChannelSession{
    86  		card:      card,
    87  		secret:    secret.Bytes(),
    88  		publicKey: elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y),
    89  	}, nil
    90  }
    91  
    92  // Pair establishes a new pairing with the smartcard.
    93  func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
    94  	secretHash := pbkdf2.Key(norm.NFKD.Bytes(pairingPassword), norm.NFKD.Bytes([]byte(pairingSalt)), 50000, 32, sha256.New)
    95  
    96  	challenge := make([]byte, 32)
    97  	if _, err := rand.Read(challenge); err != nil {
    98  		return err
    99  	}
   100  
   101  	response, err := s.pair(pairP1FirstStep, challenge)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	md := sha256.New()
   107  	md.Write(secretHash[:])
   108  	md.Write(challenge)
   109  
   110  	expectedCryptogram := md.Sum(nil)
   111  	cardCryptogram := response.Data[:32]
   112  	cardChallenge := response.Data[32:64]
   113  
   114  	if !bytes.Equal(expectedCryptogram, cardCryptogram) {
   115  		return fmt.Errorf("invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram)
   116  	}
   117  
   118  	md.Reset()
   119  	md.Write(secretHash[:])
   120  	md.Write(cardChallenge)
   121  	response, err = s.pair(pairP1LastStep, md.Sum(nil))
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	md.Reset()
   127  	md.Write(secretHash[:])
   128  	md.Write(response.Data[1:])
   129  	s.PairingKey = md.Sum(nil)
   130  	s.PairingIndex = response.Data[0]
   131  
   132  	return nil
   133  }
   134  
   135  // Unpair disestablishes an existing pairing.
   136  func (s *SecureChannelSession) Unpair() error {
   137  	if s.PairingKey == nil {
   138  		return fmt.Errorf("cannot unpair: not paired")
   139  	}
   140  
   141  	_, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
   142  	if err != nil {
   143  		return err
   144  	}
   145  	s.PairingKey = nil
   146  	// Close channel
   147  	s.iv = nil
   148  	return nil
   149  }
   150  
   151  // Open initializes the secure channel.
   152  func (s *SecureChannelSession) Open() error {
   153  	if s.iv != nil {
   154  		return fmt.Errorf("session already opened")
   155  	}
   156  
   157  	response, err := s.open()
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	// Generate the encryption/mac key by hashing our shared secret,
   163  	// pairing key, and the first bytes returned from the Open APDU.
   164  	md := sha512.New()
   165  	md.Write(s.secret)
   166  	md.Write(s.PairingKey)
   167  	md.Write(response.Data[:scSecretLength])
   168  	keyData := md.Sum(nil)
   169  	s.sessionEncKey = keyData[:scSecretLength]
   170  	s.sessionMacKey = keyData[scSecretLength : scSecretLength*2]
   171  
   172  	// The IV is the last bytes returned from the Open APDU.
   173  	s.iv = response.Data[scSecretLength:]
   174  
   175  	return s.mutuallyAuthenticate()
   176  }
   177  
   178  // mutuallyAuthenticate is an internal method to authenticate both ends of the
   179  // connection.
   180  func (s *SecureChannelSession) mutuallyAuthenticate() error {
   181  	data := make([]byte, scSecretLength)
   182  	if _, err := rand.Read(data); err != nil {
   183  		return err
   184  	}
   185  
   186  	response, err := s.transmitEncrypted(claSCWallet, insMutuallyAuthenticate, 0, 0, data)
   187  	if err != nil {
   188  		return err
   189  	}
   190  	if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
   191  		return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: %#x%x", response.Sw1, response.Sw2)
   192  	}
   193  
   194  	if len(response.Data) != scSecretLength {
   195  		return fmt.Errorf("response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength)
   196  	}
   197  
   198  	return nil
   199  }
   200  
   201  // open is an internal method that sends an open APDU.
   202  func (s *SecureChannelSession) open() (*responseAPDU, error) {
   203  	return transmit(s.card, &commandAPDU{
   204  		Cla:  claSCWallet,
   205  		Ins:  insOpenSecureChannel,
   206  		P1:   s.PairingIndex,
   207  		P2:   0,
   208  		Data: s.publicKey,
   209  		Le:   0,
   210  	})
   211  }
   212  
   213  // pair is an internal method that sends a pair APDU.
   214  func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*responseAPDU, error) {
   215  	return transmit(s.card, &commandAPDU{
   216  		Cla:  claSCWallet,
   217  		Ins:  insPair,
   218  		P1:   p1,
   219  		P2:   0,
   220  		Data: data,
   221  		Le:   0,
   222  	})
   223  }
   224  
   225  // transmitEncrypted sends an encrypted message, and decrypts and returns the response.
   226  func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) {
   227  	if s.iv == nil {
   228  		return nil, fmt.Errorf("channel not open")
   229  	}
   230  
   231  	data, err := s.encryptAPDU(data)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  	meta := [16]byte{cla, ins, p1, p2, byte(len(data) + scBlockSize)}
   236  	if err = s.updateIV(meta[:], data); err != nil {
   237  		return nil, err
   238  	}
   239  
   240  	fulldata := make([]byte, len(s.iv)+len(data))
   241  	copy(fulldata, s.iv)
   242  	copy(fulldata[len(s.iv):], data)
   243  
   244  	response, err := transmit(s.card, &commandAPDU{
   245  		Cla:  cla,
   246  		Ins:  ins,
   247  		P1:   p1,
   248  		P2:   p2,
   249  		Data: fulldata,
   250  	})
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	rmeta := [16]byte{byte(len(response.Data))}
   256  	rmac := response.Data[:len(s.iv)]
   257  	rdata := response.Data[len(s.iv):]
   258  	plainData, err := s.decryptAPDU(rdata)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  
   263  	if err = s.updateIV(rmeta[:], rdata); err != nil {
   264  		return nil, err
   265  	}
   266  	if !bytes.Equal(s.iv, rmac) {
   267  		return nil, fmt.Errorf("invalid MAC in response")
   268  	}
   269  
   270  	rapdu := &responseAPDU{}
   271  	rapdu.deserialize(plainData)
   272  
   273  	if rapdu.Sw1 != sw1Ok {
   274  		return nil, fmt.Errorf("unexpected response status Cla=%#x, Ins=%#x, Sw=%#x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
   275  	}
   276  
   277  	return rapdu, nil
   278  }
   279  
   280  // encryptAPDU is an internal method that serializes and encrypts an APDU.
   281  func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
   282  	if len(data) > maxPayloadSize {
   283  		return nil, fmt.Errorf("payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
   284  	}
   285  	data = pad(data, 0x80)
   286  
   287  	ret := make([]byte, len(data))
   288  
   289  	a, err := aes.NewCipher(s.sessionEncKey)
   290  	if err != nil {
   291  		return nil, err
   292  	}
   293  	crypter := cipher.NewCBCEncrypter(a, s.iv)
   294  	crypter.CryptBlocks(ret, data)
   295  	return ret, nil
   296  }
   297  
   298  // pad applies message padding to a 16 byte boundary.
   299  func pad(data []byte, terminator byte) []byte {
   300  	padded := make([]byte, (len(data)/16+1)*16)
   301  	copy(padded, data)
   302  	padded[len(data)] = terminator
   303  	return padded
   304  }
   305  
   306  // decryptAPDU is an internal method that decrypts and deserializes an APDU.
   307  func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) {
   308  	a, err := aes.NewCipher(s.sessionEncKey)
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  
   313  	ret := make([]byte, len(data))
   314  
   315  	crypter := cipher.NewCBCDecrypter(a, s.iv)
   316  	crypter.CryptBlocks(ret, data)
   317  	return unpad(ret, 0x80)
   318  }
   319  
   320  // unpad strips padding from a message.
   321  func unpad(data []byte, terminator byte) ([]byte, error) {
   322  	for i := 1; i <= 16; i++ {
   323  		switch data[len(data)-i] {
   324  		case 0:
   325  			continue
   326  		case terminator:
   327  			return data[:len(data)-i], nil
   328  		default:
   329  			return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i])
   330  		}
   331  	}
   332  	return nil, fmt.Errorf("expected end of padding, got 0")
   333  }
   334  
   335  // updateIV is an internal method that updates the initialization vector after
   336  // each message exchanged.
   337  func (s *SecureChannelSession) updateIV(meta, data []byte) error {
   338  	data = pad(data, 0)
   339  	a, err := aes.NewCipher(s.sessionMacKey)
   340  	if err != nil {
   341  		return err
   342  	}
   343  	crypter := cipher.NewCBCEncrypter(a, make([]byte, 16))
   344  	crypter.CryptBlocks(meta, meta)
   345  	crypter.CryptBlocks(data, data)
   346  	// The first 16 bytes of the last block is the MAC
   347  	s.iv = data[len(data)-32 : len(data)-16]
   348  	return nil
   349  }