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