crypto.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright 2020 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. package v5wire
  17. import (
  18. "crypto/aes"
  19. "crypto/cipher"
  20. "crypto/ecdsa"
  21. "crypto/elliptic"
  22. "errors"
  23. "fmt"
  24. "hash"
  25. "github.com/ethereum/go-ethereum/common/math"
  26. "github.com/ethereum/go-ethereum/crypto"
  27. "github.com/ethereum/go-ethereum/p2p/enode"
  28. "golang.org/x/crypto/hkdf"
  29. )
  30. const (
  31. // Encryption/authentication parameters.
  32. aesKeySize = 16
  33. gcmNonceSize = 12
  34. )
  35. // Nonce represents a nonce used for AES/GCM.
  36. type Nonce [gcmNonceSize]byte
  37. // EncodePubkey encodes a public key.
  38. func EncodePubkey(key *ecdsa.PublicKey) []byte {
  39. switch key.Curve {
  40. case crypto.S256():
  41. return crypto.CompressPubkey(key)
  42. default:
  43. panic("unsupported curve " + key.Curve.Params().Name + " in EncodePubkey")
  44. }
  45. }
  46. // DecodePubkey decodes a public key in compressed format.
  47. func DecodePubkey(curve elliptic.Curve, e []byte) (*ecdsa.PublicKey, error) {
  48. switch curve {
  49. case crypto.S256():
  50. if len(e) != 33 {
  51. return nil, errors.New("wrong size public key data")
  52. }
  53. return crypto.DecompressPubkey(e)
  54. default:
  55. return nil, fmt.Errorf("unsupported curve %s in DecodePubkey", curve.Params().Name)
  56. }
  57. }
  58. // idNonceHash computes the ID signature hash used in the handshake.
  59. func idNonceHash(h hash.Hash, challenge, ephkey []byte, destID enode.ID) []byte {
  60. h.Reset()
  61. h.Write([]byte("discovery v5 identity proof"))
  62. h.Write(challenge)
  63. h.Write(ephkey)
  64. h.Write(destID[:])
  65. return h.Sum(nil)
  66. }
  67. // makeIDSignature creates the ID nonce signature.
  68. func makeIDSignature(hash hash.Hash, key *ecdsa.PrivateKey, challenge, ephkey []byte, destID enode.ID) ([]byte, error) {
  69. input := idNonceHash(hash, challenge, ephkey, destID)
  70. switch key.Curve {
  71. case crypto.S256():
  72. idsig, err := crypto.Sign(input, key)
  73. if err != nil {
  74. return nil, err
  75. }
  76. return idsig[:len(idsig)-1], nil // remove recovery ID
  77. default:
  78. return nil, fmt.Errorf("unsupported curve %s", key.Curve.Params().Name)
  79. }
  80. }
  81. // s256raw is an unparsed secp256k1 public key ENR entry.
  82. type s256raw []byte
  83. func (s256raw) ENRKey() string { return "secp256k1" }
  84. // verifyIDSignature checks that signature over idnonce was made by the given node.
  85. func verifyIDSignature(hash hash.Hash, sig []byte, n *enode.Node, challenge, ephkey []byte, destID enode.ID) error {
  86. switch idscheme := n.Record().IdentityScheme(); idscheme {
  87. case "v4":
  88. var pubkey s256raw
  89. if n.Load(&pubkey) != nil {
  90. return errors.New("no secp256k1 public key in record")
  91. }
  92. input := idNonceHash(hash, challenge, ephkey, destID)
  93. if !crypto.VerifySignature(pubkey, input, sig) {
  94. return errInvalidNonceSig
  95. }
  96. return nil
  97. default:
  98. return fmt.Errorf("can't verify ID nonce signature against scheme %q", idscheme)
  99. }
  100. }
  101. type hashFn func() hash.Hash
  102. // deriveKeys creates the session keys.
  103. func deriveKeys(hash hashFn, priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, n1, n2 enode.ID, challenge []byte) *session {
  104. const text = "discovery v5 key agreement"
  105. var info = make([]byte, 0, len(text)+len(n1)+len(n2))
  106. info = append(info, text...)
  107. info = append(info, n1[:]...)
  108. info = append(info, n2[:]...)
  109. eph := ecdh(priv, pub)
  110. if eph == nil {
  111. return nil
  112. }
  113. kdf := hkdf.New(hash, eph, challenge, info)
  114. sec := session{writeKey: make([]byte, aesKeySize), readKey: make([]byte, aesKeySize)}
  115. kdf.Read(sec.writeKey)
  116. kdf.Read(sec.readKey)
  117. for i := range eph {
  118. eph[i] = 0
  119. }
  120. return &sec
  121. }
  122. // ecdh creates a shared secret.
  123. func ecdh(privkey *ecdsa.PrivateKey, pubkey *ecdsa.PublicKey) []byte {
  124. secX, secY := pubkey.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes())
  125. if secX == nil {
  126. return nil
  127. }
  128. sec := make([]byte, 33)
  129. sec[0] = 0x02 | byte(secY.Bit(0))
  130. math.ReadBits(secX, sec[1:])
  131. return sec
  132. }
  133. // encryptGCM encrypts pt using AES-GCM with the given key and nonce. The ciphertext is
  134. // appended to dest, which must not overlap with plaintext. The resulting ciphertext is 16
  135. // bytes longer than plaintext because it contains an authentication tag.
  136. func encryptGCM(dest, key, nonce, plaintext, authData []byte) ([]byte, error) {
  137. block, err := aes.NewCipher(key)
  138. if err != nil {
  139. panic(fmt.Errorf("can't create block cipher: %v", err))
  140. }
  141. aesgcm, err := cipher.NewGCMWithNonceSize(block, gcmNonceSize)
  142. if err != nil {
  143. panic(fmt.Errorf("can't create GCM: %v", err))
  144. }
  145. return aesgcm.Seal(dest, nonce, plaintext, authData), nil
  146. }
  147. // decryptGCM decrypts ct using AES-GCM with the given key and nonce.
  148. func decryptGCM(key, nonce, ct, authData []byte) ([]byte, error) {
  149. block, err := aes.NewCipher(key)
  150. if err != nil {
  151. return nil, fmt.Errorf("can't create block cipher: %v", err)
  152. }
  153. if len(nonce) != gcmNonceSize {
  154. return nil, fmt.Errorf("invalid GCM nonce size: %d", len(nonce))
  155. }
  156. aesgcm, err := cipher.NewGCMWithNonceSize(block, gcmNonceSize)
  157. if err != nil {
  158. return nil, fmt.Errorf("can't create GCM: %v", err)
  159. }
  160. pt := make([]byte, 0, len(ct))
  161. return aesgcm.Open(pt, nonce, ct, authData)
  162. }