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