You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

294 lines
8.1 KiB

7 years ago
7 years ago
7 years ago
7 years ago
  1. package nano
  2. import (
  3. "bytes"
  4. "encoding/hex"
  5. "github.com/pkg/errors"
  6. ledger "github.com/ethanfrey/ledger"
  7. crypto "github.com/tendermint/go-crypto"
  8. amino "github.com/tendermint/go-amino"
  9. )
  10. //nolint
  11. const (
  12. NameLedgerEd25519 = "ledger-ed25519"
  13. TypeLedgerEd25519 = 0x10
  14. // Timeout is the number of seconds to wait for a response from the ledger
  15. // if eg. waiting for user confirmation on button push
  16. Timeout = 20
  17. )
  18. var device *ledger.Ledger
  19. // getLedger gets a copy of the device, and caches it
  20. func getLedger() (*ledger.Ledger, error) {
  21. var err error
  22. if device == nil {
  23. device, err = ledger.FindLedger()
  24. }
  25. return device, err
  26. }
  27. func signLedger(device *ledger.Ledger, msg []byte) (pub crypto.PubKey, sig crypto.Signature, err error) {
  28. var resp []byte
  29. packets := generateSignRequests(msg)
  30. for _, pack := range packets {
  31. resp, err = device.Exchange(pack, Timeout)
  32. if err != nil {
  33. return pub, sig, err
  34. }
  35. }
  36. // the last call is the result we want and needs to be parsed
  37. key, bsig, err := parseDigest(resp)
  38. if err != nil {
  39. return pub, sig, err
  40. }
  41. var b [32]byte
  42. copy(b[:], key)
  43. return PubKeyLedgerEd25519FromBytes(b), crypto.SignatureEd25519FromBytes(bsig), nil
  44. }
  45. // PrivKeyLedgerEd25519 implements PrivKey, calling the ledger nano
  46. // we cache the PubKey from the first call to use it later
  47. type PrivKeyLedgerEd25519 struct {
  48. // PubKey should be private, but we want to encode it via go-amino
  49. // so we can view the address later, even without having the ledger
  50. // attached
  51. CachedPubKey crypto.PubKey
  52. }
  53. // NewPrivKeyLedgerEd25519 will generate a new key and store the
  54. // public key for later use.
  55. func NewPrivKeyLedgerEd25519() (crypto.PrivKey, error) {
  56. var pk PrivKeyLedgerEd25519
  57. // getPubKey will cache the pubkey for later use,
  58. // this allows us to return an error early if the ledger
  59. // is not plugged in
  60. _, err := pk.getPubKey()
  61. return pk.Wrap(), err
  62. }
  63. // ValidateKey allows us to verify the sanity of a key
  64. // after loading it from disk
  65. func (pk *PrivKeyLedgerEd25519) ValidateKey() error {
  66. // getPubKey will return an error if the ledger is not
  67. // properly set up...
  68. pub, err := pk.forceGetPubKey()
  69. if err != nil {
  70. return err
  71. }
  72. // verify this matches cached address
  73. if !pub.Equals(pk.CachedPubKey) {
  74. return errors.New("ledger doesn't match cached key")
  75. }
  76. return nil
  77. }
  78. // AssertIsPrivKeyInner fulfils PrivKey Interface
  79. func (pk *PrivKeyLedgerEd25519) AssertIsPrivKeyInner() {}
  80. // Bytes fulfils PrivKey Interface - but it stores the cached pubkey so we can verify
  81. // the same key when we reconnect to a ledger
  82. func (pk *PrivKeyLedgerEd25519) Bytes() []byte {
  83. return amino.BinaryBytes(pk.Wrap())
  84. }
  85. // Sign calls the ledger and stores the PubKey for future use
  86. //
  87. // XXX/TODO: panics if there is an error communicating with the ledger.
  88. //
  89. // Communication is checked on NewPrivKeyLedger and PrivKeyFromBytes,
  90. // returning an error, so this should only trigger if the privkey is held
  91. // in memory for a while before use.
  92. func (pk *PrivKeyLedgerEd25519) Sign(msg []byte) crypto.Signature {
  93. // oh, I wish there was better error handling
  94. dev, err := getLedger()
  95. if err != nil {
  96. panic(err)
  97. }
  98. pub, sig, err := signLedger(dev, msg)
  99. if err != nil {
  100. panic(err)
  101. }
  102. // if we have no pubkey yet, store it for future queries
  103. if pk.CachedPubKey.Empty() {
  104. pk.CachedPubKey = pub
  105. } else if !pk.CachedPubKey.Equals(pub) {
  106. panic("signed with a different key than stored")
  107. }
  108. return sig
  109. }
  110. // PubKey returns the stored PubKey
  111. // TODO: query the ledger if not there, once it is not volatile
  112. func (pk *PrivKeyLedgerEd25519) PubKey() crypto.PubKey {
  113. key, err := pk.getPubKey()
  114. if err != nil {
  115. panic(err)
  116. }
  117. return key
  118. }
  119. // getPubKey reads the pubkey from cache or from the ledger itself
  120. // since this involves IO, it may return an error, which is not exposed
  121. // in the PubKey interface, so this function allows better error handling
  122. func (pk *PrivKeyLedgerEd25519) getPubKey() (key crypto.PubKey, err error) {
  123. // if we have no pubkey, set it
  124. if pk.CachedPubKey.Empty() {
  125. pk.CachedPubKey, err = pk.forceGetPubKey()
  126. }
  127. return pk.CachedPubKey, err
  128. }
  129. // forceGetPubKey is like getPubKey but ignores any cached key
  130. // and ensures we get it from the ledger itself.
  131. func (pk *PrivKeyLedgerEd25519) forceGetPubKey() (key crypto.PubKey, err error) {
  132. dev, err := getLedger()
  133. if err != nil {
  134. return key, errors.New("Can't connect to ledger device")
  135. }
  136. key, _, err = signLedger(dev, []byte{0})
  137. if err != nil {
  138. return key, errors.New("Please open cosmos app on the ledger")
  139. }
  140. return key, err
  141. }
  142. // Equals fulfils PrivKey Interface - makes sure both keys refer to the
  143. // same
  144. func (pk *PrivKeyLedgerEd25519) Equals(other crypto.PrivKey) bool {
  145. if ledger, ok := other.Unwrap().(*PrivKeyLedgerEd25519); ok {
  146. return pk.CachedPubKey.Equals(ledger.CachedPubKey)
  147. }
  148. return false
  149. }
  150. // MockPrivKeyLedgerEd25519 behaves as the ledger, but stores a pre-packaged call-response
  151. // for use in test cases
  152. type MockPrivKeyLedgerEd25519 struct {
  153. Msg []byte
  154. Pub [KeyLength]byte
  155. Sig [SigLength]byte
  156. }
  157. // NewMockKey returns
  158. func NewMockKey(msg, pubkey, sig string) (pk MockPrivKeyLedgerEd25519) {
  159. var err error
  160. pk.Msg, err = hex.DecodeString(msg)
  161. if err != nil {
  162. panic(err)
  163. }
  164. bpk, err := hex.DecodeString(pubkey)
  165. if err != nil {
  166. panic(err)
  167. }
  168. bsig, err := hex.DecodeString(sig)
  169. if err != nil {
  170. panic(err)
  171. }
  172. copy(pk.Pub[:], bpk)
  173. copy(pk.Sig[:], bsig)
  174. return pk
  175. }
  176. var _ crypto.PrivKeyInner = MockPrivKeyLedgerEd25519{}
  177. // AssertIsPrivKeyInner fulfils PrivKey Interface
  178. func (pk MockPrivKeyLedgerEd25519) AssertIsPrivKeyInner() {}
  179. // Bytes fulfils PrivKey Interface - not supported
  180. func (pk MockPrivKeyLedgerEd25519) Bytes() []byte {
  181. return nil
  182. }
  183. // Sign returns a real SignatureLedger, if the msg matches what we expect
  184. func (pk MockPrivKeyLedgerEd25519) Sign(msg []byte) crypto.Signature {
  185. if !bytes.Equal(pk.Msg, msg) {
  186. panic("Mock key is for different msg")
  187. }
  188. return crypto.SignatureEd25519(pk.Sig).Wrap()
  189. }
  190. // PubKey returns a real PubKeyLedgerEd25519, that will verify this signature
  191. func (pk MockPrivKeyLedgerEd25519) PubKey() crypto.PubKey {
  192. return PubKeyLedgerEd25519FromBytes(pk.Pub)
  193. }
  194. // Equals compares that two Mocks have the same data
  195. func (pk MockPrivKeyLedgerEd25519) Equals(other crypto.PrivKey) bool {
  196. if mock, ok := other.Unwrap().(MockPrivKeyLedgerEd25519); ok {
  197. return bytes.Equal(mock.Pub[:], pk.Pub[:]) &&
  198. bytes.Equal(mock.Sig[:], pk.Sig[:]) &&
  199. bytes.Equal(mock.Msg, pk.Msg)
  200. }
  201. return false
  202. }
  203. ////////////////////////////////////////////
  204. // pubkey
  205. // PubKeyLedgerEd25519 works like a normal Ed25519 except a hash before the verify bytes
  206. type PubKeyLedgerEd25519 struct {
  207. crypto.PubKeyEd25519
  208. }
  209. // PubKeyLedgerEd25519FromBytes creates a PubKey from the raw bytes
  210. func PubKeyLedgerEd25519FromBytes(key [32]byte) crypto.PubKey {
  211. return PubKeyLedgerEd25519{crypto.PubKeyEd25519(key)}.Wrap()
  212. }
  213. // Bytes fulfils pk Interface - no data, just type info
  214. func (pk PubKeyLedgerEd25519) Bytes() []byte {
  215. return amino.BinaryBytes(pk.Wrap())
  216. }
  217. // VerifyBytes uses the normal Ed25519 algorithm but a sha512 hash beforehand
  218. func (pk PubKeyLedgerEd25519) VerifyBytes(msg []byte, sig crypto.Signature) bool {
  219. hmsg := hashMsg(msg)
  220. return pk.PubKeyEd25519.VerifyBytes(hmsg, sig)
  221. }
  222. // Equals implements PubKey interface
  223. func (pk PubKeyLedgerEd25519) Equals(other crypto.PubKey) bool {
  224. if ledger, ok := other.Unwrap().(PubKeyLedgerEd25519); ok {
  225. return pk.PubKeyEd25519.Equals(ledger.PubKeyEd25519.Wrap())
  226. }
  227. return false
  228. }
  229. /*** registration with go-data ***/
  230. func init() {
  231. crypto.PrivKeyMapper.
  232. RegisterImplementation(&PrivKeyLedgerEd25519{}, NameLedgerEd25519, TypeLedgerEd25519).
  233. RegisterImplementation(MockPrivKeyLedgerEd25519{}, "mock-ledger", 0x11)
  234. crypto.PubKeyMapper.
  235. RegisterImplementation(PubKeyLedgerEd25519{}, NameLedgerEd25519, TypeLedgerEd25519)
  236. }
  237. // Wrap fulfils interface for PrivKey struct
  238. func (pk *PrivKeyLedgerEd25519) Wrap() crypto.PrivKey {
  239. return crypto.PrivKey{PrivKeyInner: pk}
  240. }
  241. // Wrap fulfils interface for PrivKey struct
  242. func (pk MockPrivKeyLedgerEd25519) Wrap() crypto.PrivKey {
  243. return crypto.PrivKey{PrivKeyInner: pk}
  244. }
  245. // Wrap fulfils interface for PubKey struct
  246. func (pk PubKeyLedgerEd25519) Wrap() crypto.PubKey {
  247. return crypto.PubKey{PubKeyInner: pk}
  248. }