| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | // 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 scwallet | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"crypto/aes" | 
					
						
							|  |  |  | 	"crypto/cipher" | 
					
						
							|  |  |  | 	"crypto/rand" | 
					
						
							|  |  |  | 	"crypto/sha256" | 
					
						
							|  |  |  | 	"crypto/sha512" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2018-04-20 10:37:45 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | 	pcsc "github.com/gballet/go-libpcsclite" | 
					
						
							| 
									
										
										
										
											2018-04-20 10:37:45 +03:00
										 |  |  | 	"github.com/wsddn/go-ecdh" | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | 	"golang.org/x/crypto/pbkdf2" | 
					
						
							|  |  |  | 	"golang.org/x/text/unicode/norm" | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	maxPayloadSize  = 223 | 
					
						
							|  |  |  | 	pairP1FirstStep = 0 | 
					
						
							|  |  |  | 	pairP1LastStep  = 1 | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	scSecretLength = 32 | 
					
						
							|  |  |  | 	scBlockSize    = 16 | 
					
						
							| 
									
										
										
										
											2018-04-20 10:37:45 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	insOpenSecureChannel    = 0x10 | 
					
						
							|  |  |  | 	insMutuallyAuthenticate = 0x11 | 
					
						
							|  |  |  | 	insPair                 = 0x12 | 
					
						
							|  |  |  | 	insUnpair               = 0x13 | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pairingSalt = "Keycard Pairing Password Salt" | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // SecureChannelSession enables secure communication with a hardware wallet. | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | type SecureChannelSession struct { | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | 	card          *pcsc.Card // A handle to the smartcard for communication | 
					
						
							|  |  |  | 	secret        []byte     // A shared secret generated from our ECDSA keys | 
					
						
							|  |  |  | 	publicKey     []byte     // Our own ephemeral public key | 
					
						
							|  |  |  | 	PairingKey    []byte     // A permanent shared secret for a pairing, if present | 
					
						
							|  |  |  | 	sessionEncKey []byte     // The current session encryption key | 
					
						
							|  |  |  | 	sessionMacKey []byte     // The current session MAC key | 
					
						
							|  |  |  | 	iv            []byte     // The current IV | 
					
						
							|  |  |  | 	PairingIndex  uint8      // The pairing index | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // NewSecureChannelSession creates a new secure channel for the given card and public key. | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSession, error) { | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	// Generate an ECDSA keypair for ourselves | 
					
						
							|  |  |  | 	gen := ecdh.NewEllipticECDH(crypto.S256()) | 
					
						
							|  |  |  | 	private, public, err := gen.GenerateKey(rand.Reader) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cardPublic, ok := gen.Unmarshal(keyData) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("Could not unmarshal public key from card") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	secret, err := gen.GenerateSharedSecret(private, cardPublic) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &SecureChannelSession{ | 
					
						
							|  |  |  | 		card:      card, | 
					
						
							|  |  |  | 		secret:    secret, | 
					
						
							|  |  |  | 		publicKey: gen.Marshal(public), | 
					
						
							|  |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // Pair establishes a new pairing with the smartcard. | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | func (s *SecureChannelSession) Pair(pairingPassword []byte) error { | 
					
						
							| 
									
										
										
										
											2019-03-24 16:15:43 +01:00
										 |  |  | 	secretHash := pbkdf2.Key(norm.NFKD.Bytes(pairingPassword), norm.NFKD.Bytes([]byte(pairingSalt)), 50000, 32, sha256.New) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	challenge := make([]byte, 32) | 
					
						
							|  |  |  | 	if _, err := rand.Read(challenge); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	response, err := s.pair(pairP1FirstStep, challenge) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	md := sha256.New() | 
					
						
							|  |  |  | 	md.Write(secretHash[:]) | 
					
						
							|  |  |  | 	md.Write(challenge) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	expectedCryptogram := md.Sum(nil) | 
					
						
							|  |  |  | 	cardCryptogram := response.Data[:32] | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | 	cardChallenge := response.Data[32:64] | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if !bytes.Equal(expectedCryptogram, cardCryptogram) { | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | 		return fmt.Errorf("Invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	md.Reset() | 
					
						
							|  |  |  | 	md.Write(secretHash[:]) | 
					
						
							|  |  |  | 	md.Write(cardChallenge) | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	response, err = s.pair(pairP1LastStep, md.Sum(nil)) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	md.Reset() | 
					
						
							|  |  |  | 	md.Write(secretHash[:]) | 
					
						
							|  |  |  | 	md.Write(response.Data[1:]) | 
					
						
							|  |  |  | 	s.PairingKey = md.Sum(nil) | 
					
						
							|  |  |  | 	s.PairingIndex = response.Data[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // Unpair disestablishes an existing pairing. | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | func (s *SecureChannelSession) Unpair() error { | 
					
						
							|  |  |  | 	if s.PairingKey == nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("Cannot unpair: not paired") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-23 08:45:08 +01:00
										 |  |  | 	_, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{}) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	s.PairingKey = nil | 
					
						
							|  |  |  | 	// Close channel | 
					
						
							|  |  |  | 	s.iv = nil | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // Open initializes the secure channel. | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | func (s *SecureChannelSession) Open() error { | 
					
						
							|  |  |  | 	if s.iv != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("Session already opened") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	response, err := s.open() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Generate the encryption/mac key by hashing our shared secret, | 
					
						
							|  |  |  | 	// pairing key, and the first bytes returned from the Open APDU. | 
					
						
							|  |  |  | 	md := sha512.New() | 
					
						
							|  |  |  | 	md.Write(s.secret) | 
					
						
							|  |  |  | 	md.Write(s.PairingKey) | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	md.Write(response.Data[:scSecretLength]) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	keyData := md.Sum(nil) | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	s.sessionEncKey = keyData[:scSecretLength] | 
					
						
							|  |  |  | 	s.sessionMacKey = keyData[scSecretLength : scSecretLength*2] | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// The IV is the last bytes returned from the Open APDU. | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	s.iv = response.Data[scSecretLength:] | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-21 19:43:14 +01:00
										 |  |  | 	return s.mutuallyAuthenticate() | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // mutuallyAuthenticate is an internal method to authenticate both ends of the | 
					
						
							|  |  |  | // connection. | 
					
						
							|  |  |  | func (s *SecureChannelSession) mutuallyAuthenticate() error { | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	data := make([]byte, scSecretLength) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if _, err := rand.Read(data); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-23 08:45:08 +01:00
										 |  |  | 	response, err := s.transmitEncrypted(claSCWallet, insMutuallyAuthenticate, 0, 0, data) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if response.Sw1 != 0x90 || response.Sw2 != 0x00 { | 
					
						
							|  |  |  | 		return fmt.Errorf("Got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	if len(response.Data) != scSecretLength { | 
					
						
							|  |  |  | 		return fmt.Errorf("Response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // open is an internal method that sends an open APDU. | 
					
						
							| 
									
										
										
										
											2018-04-20 10:37:45 +03:00
										 |  |  | func (s *SecureChannelSession) open() (*responseAPDU, error) { | 
					
						
							|  |  |  | 	return transmit(s.card, &commandAPDU{ | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 		Cla:  claSCWallet, | 
					
						
							|  |  |  | 		Ins:  insOpenSecureChannel, | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		P1:   s.PairingIndex, | 
					
						
							|  |  |  | 		P2:   0, | 
					
						
							|  |  |  | 		Data: s.publicKey, | 
					
						
							|  |  |  | 		Le:   0, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // pair is an internal method that sends a pair APDU. | 
					
						
							| 
									
										
										
										
											2018-04-20 10:37:45 +03:00
										 |  |  | func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*responseAPDU, error) { | 
					
						
							|  |  |  | 	return transmit(s.card, &commandAPDU{ | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 		Cla:  claSCWallet, | 
					
						
							|  |  |  | 		Ins:  insPair, | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		P1:   p1, | 
					
						
							|  |  |  | 		P2:   0, | 
					
						
							|  |  |  | 		Data: data, | 
					
						
							|  |  |  | 		Le:   0, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-23 08:45:08 +01:00
										 |  |  | // transmitEncrypted sends an encrypted message, and decrypts and returns the response. | 
					
						
							|  |  |  | func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) { | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if s.iv == nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("Channel not open") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data, err := s.encryptAPDU(data) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-04 20:06:40 +02:00
										 |  |  | 	meta := [16]byte{cla, ins, p1, p2, byte(len(data) + scBlockSize)} | 
					
						
							|  |  |  | 	if err = s.updateIV(meta[:], data); err != nil { | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fulldata := make([]byte, len(s.iv)+len(data)) | 
					
						
							|  |  |  | 	copy(fulldata, s.iv) | 
					
						
							|  |  |  | 	copy(fulldata[len(s.iv):], data) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-20 10:37:45 +03:00
										 |  |  | 	response, err := transmit(s.card, &commandAPDU{ | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		Cla:  cla, | 
					
						
							|  |  |  | 		Ins:  ins, | 
					
						
							|  |  |  | 		P1:   p1, | 
					
						
							|  |  |  | 		P2:   p2, | 
					
						
							|  |  |  | 		Data: fulldata, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-04 20:06:40 +02:00
										 |  |  | 	rmeta := [16]byte{byte(len(response.Data))} | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	rmac := response.Data[:len(s.iv)] | 
					
						
							|  |  |  | 	rdata := response.Data[len(s.iv):] | 
					
						
							|  |  |  | 	plainData, err := s.decryptAPDU(rdata) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-04 20:06:40 +02:00
										 |  |  | 	if err = s.updateIV(rmeta[:], rdata); err != nil { | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !bytes.Equal(s.iv, rmac) { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("Invalid MAC in response") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-20 10:37:45 +03:00
										 |  |  | 	rapdu := &responseAPDU{} | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	rapdu.deserialize(plainData) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	if rapdu.Sw1 != sw1Ok { | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // encryptAPDU is an internal method that serializes and encrypts an APDU. | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	if len(data) > maxPayloadSize { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("Payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	data = pad(data, 0x80) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret := make([]byte, len(data)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	a, err := aes.NewCipher(s.sessionEncKey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	crypter := cipher.NewCBCEncrypter(a, s.iv) | 
					
						
							|  |  |  | 	crypter.CryptBlocks(ret, data) | 
					
						
							|  |  |  | 	return ret, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // pad applies message padding to a 16 byte boundary. | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | func pad(data []byte, terminator byte) []byte { | 
					
						
							|  |  |  | 	padded := make([]byte, (len(data)/16+1)*16) | 
					
						
							|  |  |  | 	copy(padded, data) | 
					
						
							|  |  |  | 	padded[len(data)] = terminator | 
					
						
							|  |  |  | 	return padded | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // decryptAPDU is an internal method that decrypts and deserializes an APDU. | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) { | 
					
						
							|  |  |  | 	a, err := aes.NewCipher(s.sessionEncKey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret := make([]byte, len(data)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	crypter := cipher.NewCBCDecrypter(a, s.iv) | 
					
						
							|  |  |  | 	crypter.CryptBlocks(ret, data) | 
					
						
							|  |  |  | 	return unpad(ret, 0x80) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // unpad strips padding from a message. | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | func unpad(data []byte, terminator byte) ([]byte, error) { | 
					
						
							|  |  |  | 	for i := 1; i <= 16; i++ { | 
					
						
							|  |  |  | 		switch data[len(data)-i] { | 
					
						
							|  |  |  | 		case 0: | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		case terminator: | 
					
						
							|  |  |  | 			return data[:len(data)-i], nil | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("Expected end of padding, got %d", data[len(data)-i]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil, fmt.Errorf("Expected end of padding, got 0") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // updateIV is an internal method that updates the initialization vector after | 
					
						
							|  |  |  | // each message exchanged. | 
					
						
							|  |  |  | func (s *SecureChannelSession) updateIV(meta, data []byte) error { | 
					
						
							|  |  |  | 	data = pad(data, 0) | 
					
						
							|  |  |  | 	a, err := aes.NewCipher(s.sessionMacKey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	crypter := cipher.NewCBCEncrypter(a, make([]byte, 16)) | 
					
						
							|  |  |  | 	crypter.CryptBlocks(meta, meta) | 
					
						
							|  |  |  | 	crypter.CryptBlocks(data, data) | 
					
						
							|  |  |  | 	// The first 16 bytes of the last block is the MAC | 
					
						
							|  |  |  | 	s.iv = data[len(data)-32 : len(data)-16] | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |