1package keycard 2 3import ( 4 "bytes" 5 "crypto/ecdsa" 6 "errors" 7 8 ethcrypto "github.com/ethereum/go-ethereum/crypto" 9 "github.com/status-im/keycard-go/apdu" 10 "github.com/status-im/keycard-go/crypto" 11 "github.com/status-im/keycard-go/globalplatform" 12 "github.com/status-im/keycard-go/types" 13) 14 15var ErrInvalidResponseMAC = errors.New("invalid response MAC") 16 17type SecureChannel struct { 18 c types.Channel 19 open bool 20 secret []byte 21 publicKey *ecdsa.PublicKey 22 encKey []byte 23 macKey []byte 24 iv []byte 25} 26 27func NewSecureChannel(c types.Channel) *SecureChannel { 28 return &SecureChannel{ 29 c: c, 30 } 31} 32 33func (sc *SecureChannel) GenerateSecret(cardPubKeyData []byte) error { 34 key, err := ethcrypto.GenerateKey() 35 if err != nil { 36 return err 37 } 38 39 cardPubKey, err := ethcrypto.UnmarshalPubkey(cardPubKeyData) 40 if err != nil { 41 return err 42 } 43 44 sc.publicKey = &key.PublicKey 45 sc.secret = crypto.GenerateECDHSharedSecret(key, cardPubKey) 46 47 return nil 48} 49 50func (sc *SecureChannel) Reset() { 51 sc.open = false 52} 53 54func (sc *SecureChannel) Init(iv, encKey, macKey []byte) { 55 sc.iv = iv 56 sc.encKey = encKey 57 sc.macKey = macKey 58 sc.open = true 59} 60 61func (sc *SecureChannel) Secret() []byte { 62 return sc.secret 63} 64 65func (sc *SecureChannel) PublicKey() *ecdsa.PublicKey { 66 return sc.publicKey 67} 68 69func (sc *SecureChannel) RawPublicKey() []byte { 70 return ethcrypto.FromECDSAPub(sc.publicKey) 71} 72 73func (sc *SecureChannel) Send(cmd *apdu.Command) (*apdu.Response, error) { 74 if sc.open { 75 encData, err := crypto.EncryptData(cmd.Data, sc.encKey, sc.iv) 76 if err != nil { 77 return nil, err 78 } 79 80 meta := []byte{cmd.Cla, cmd.Ins, cmd.P1, cmd.P2, byte(len(encData) + 16), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 81 if err = sc.updateIV(meta, encData); err != nil { 82 return nil, err 83 } 84 85 newData := append(sc.iv, encData...) 86 cmd.Data = newData 87 } 88 89 resp, err := sc.c.Send(cmd) 90 if err != nil { 91 return nil, err 92 } 93 94 if resp.Sw != globalplatform.SwOK { 95 return nil, apdu.NewErrBadResponse(resp.Sw, "unexpected sw in secure channel") 96 } 97 98 rmeta := []byte{byte(len(resp.Data)), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 99 rmac := resp.Data[:len(sc.iv)] 100 rdata := resp.Data[len(sc.iv):] 101 plainData, err := crypto.DecryptData(rdata, sc.encKey, sc.iv) 102 if err = sc.updateIV(rmeta, rdata); err != nil { 103 return nil, err 104 } 105 106 if !bytes.Equal(sc.iv, rmac) { 107 return nil, ErrInvalidResponseMAC 108 } 109 110 return apdu.ParseResponse(plainData) 111} 112 113func (sc *SecureChannel) updateIV(meta, data []byte) error { 114 mac, err := crypto.CalculateMac(meta, data, sc.macKey) 115 if err != nil { 116 return err 117 } 118 119 sc.iv = mac 120 121 return nil 122} 123 124func (sc *SecureChannel) OneShotEncrypt(secrets *Secrets) ([]byte, error) { 125 pubKeyData := ethcrypto.FromECDSAPub(sc.publicKey) 126 data := append([]byte(secrets.Pin()), []byte(secrets.Puk())...) 127 data = append(data, secrets.PairingToken()...) 128 129 return crypto.OneShotEncrypt(pubKeyData, sc.secret, data) 130} 131