tree.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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. package dnsdisc
  17. import (
  18. "bytes"
  19. "crypto/ecdsa"
  20. "encoding/base32"
  21. "encoding/base64"
  22. "fmt"
  23. "io"
  24. "sort"
  25. "strings"
  26. "github.com/ethereum/go-ethereum/crypto"
  27. "github.com/ethereum/go-ethereum/p2p/enode"
  28. "github.com/ethereum/go-ethereum/p2p/enr"
  29. "github.com/ethereum/go-ethereum/rlp"
  30. "golang.org/x/crypto/sha3"
  31. )
  32. // Tree is a merkle tree of node records.
  33. type Tree struct {
  34. root *rootEntry
  35. entries map[string]entry
  36. }
  37. // Sign signs the tree with the given private key and sets the sequence number.
  38. func (t *Tree) Sign(key *ecdsa.PrivateKey, domain string) (url string, err error) {
  39. root := *t.root
  40. sig, err := crypto.Sign(root.sigHash(), key)
  41. if err != nil {
  42. return "", err
  43. }
  44. root.sig = sig
  45. t.root = &root
  46. link := &linkEntry{domain, &key.PublicKey}
  47. return link.String(), nil
  48. }
  49. // SetSignature verifies the given signature and assigns it as the tree's current
  50. // signature if valid.
  51. func (t *Tree) SetSignature(pubkey *ecdsa.PublicKey, signature string) error {
  52. sig, err := b64format.DecodeString(signature)
  53. if err != nil || len(sig) != crypto.SignatureLength {
  54. return errInvalidSig
  55. }
  56. root := *t.root
  57. root.sig = sig
  58. if !root.verifySignature(pubkey) {
  59. return errInvalidSig
  60. }
  61. t.root = &root
  62. return nil
  63. }
  64. // Seq returns the sequence number of the tree.
  65. func (t *Tree) Seq() uint {
  66. return t.root.seq
  67. }
  68. // Signature returns the signature of the tree.
  69. func (t *Tree) Signature() string {
  70. return b64format.EncodeToString(t.root.sig)
  71. }
  72. // ToTXT returns all DNS TXT records required for the tree.
  73. func (t *Tree) ToTXT(domain string) map[string]string {
  74. records := map[string]string{domain: t.root.String()}
  75. for _, e := range t.entries {
  76. sd := subdomain(e)
  77. if domain != "" {
  78. sd = sd + "." + domain
  79. }
  80. records[sd] = e.String()
  81. }
  82. return records
  83. }
  84. // Links returns all links contained in the tree.
  85. func (t *Tree) Links() []string {
  86. var links []string
  87. for _, e := range t.entries {
  88. if le, ok := e.(*linkEntry); ok {
  89. links = append(links, le.String())
  90. }
  91. }
  92. return links
  93. }
  94. // Nodes returns all nodes contained in the tree.
  95. func (t *Tree) Nodes() []*enode.Node {
  96. var nodes []*enode.Node
  97. for _, e := range t.entries {
  98. if ee, ok := e.(*enrEntry); ok {
  99. nodes = append(nodes, ee.node)
  100. }
  101. }
  102. return nodes
  103. }
  104. const (
  105. hashAbbrev = 16
  106. maxChildren = 300 / hashAbbrev * (13 / 8)
  107. minHashLength = 12
  108. )
  109. // MakeTree creates a tree containing the given nodes and links.
  110. func MakeTree(seq uint, nodes []*enode.Node, links []string) (*Tree, error) {
  111. // Sort records by ID and ensure all nodes have a valid record.
  112. records := make([]*enode.Node, len(nodes))
  113. copy(records, nodes)
  114. sortByID(records)
  115. for _, n := range records {
  116. if len(n.Record().Signature()) == 0 {
  117. return nil, fmt.Errorf("can't add node %v: unsigned node record", n.ID())
  118. }
  119. }
  120. // Create the leaf list.
  121. enrEntries := make([]entry, len(records))
  122. for i, r := range records {
  123. enrEntries[i] = &enrEntry{r}
  124. }
  125. linkEntries := make([]entry, len(links))
  126. for i, l := range links {
  127. le, err := parseLink(l)
  128. if err != nil {
  129. return nil, err
  130. }
  131. linkEntries[i] = le
  132. }
  133. // Create intermediate nodes.
  134. t := &Tree{entries: make(map[string]entry)}
  135. eroot := t.build(enrEntries)
  136. t.entries[subdomain(eroot)] = eroot
  137. lroot := t.build(linkEntries)
  138. t.entries[subdomain(lroot)] = lroot
  139. t.root = &rootEntry{seq: seq, eroot: subdomain(eroot), lroot: subdomain(lroot)}
  140. return t, nil
  141. }
  142. func (t *Tree) build(entries []entry) entry {
  143. if len(entries) == 1 {
  144. return entries[0]
  145. }
  146. if len(entries) <= maxChildren {
  147. hashes := make([]string, len(entries))
  148. for i, e := range entries {
  149. hashes[i] = subdomain(e)
  150. t.entries[hashes[i]] = e
  151. }
  152. return &branchEntry{hashes}
  153. }
  154. var subtrees []entry
  155. for len(entries) > 0 {
  156. n := maxChildren
  157. if len(entries) < n {
  158. n = len(entries)
  159. }
  160. sub := t.build(entries[:n])
  161. entries = entries[n:]
  162. subtrees = append(subtrees, sub)
  163. t.entries[subdomain(sub)] = sub
  164. }
  165. return t.build(subtrees)
  166. }
  167. func sortByID(nodes []*enode.Node) []*enode.Node {
  168. sort.Slice(nodes, func(i, j int) bool {
  169. return bytes.Compare(nodes[i].ID().Bytes(), nodes[j].ID().Bytes()) < 0
  170. })
  171. return nodes
  172. }
  173. // Entry Types
  174. type entry interface {
  175. fmt.Stringer
  176. }
  177. type (
  178. rootEntry struct {
  179. eroot string
  180. lroot string
  181. seq uint
  182. sig []byte
  183. }
  184. branchEntry struct {
  185. children []string
  186. }
  187. enrEntry struct {
  188. node *enode.Node
  189. }
  190. linkEntry struct {
  191. domain string
  192. pubkey *ecdsa.PublicKey
  193. }
  194. )
  195. // Entry Encoding
  196. var (
  197. b32format = base32.StdEncoding.WithPadding(base32.NoPadding)
  198. b64format = base64.RawURLEncoding
  199. )
  200. const (
  201. rootPrefix = "enrtree-root:v1"
  202. linkPrefix = "enrtree://"
  203. branchPrefix = "enrtree-branch:"
  204. enrPrefix = "enr:"
  205. )
  206. func subdomain(e entry) string {
  207. h := sha3.NewLegacyKeccak256()
  208. io.WriteString(h, e.String())
  209. return b32format.EncodeToString(h.Sum(nil)[:16])
  210. }
  211. func (e *rootEntry) String() string {
  212. return fmt.Sprintf(rootPrefix+" e=%s l=%s seq=%d sig=%s", e.eroot, e.lroot, e.seq, b64format.EncodeToString(e.sig))
  213. }
  214. func (e *rootEntry) sigHash() []byte {
  215. h := sha3.NewLegacyKeccak256()
  216. fmt.Fprintf(h, rootPrefix+" e=%s l=%s seq=%d", e.eroot, e.lroot, e.seq)
  217. return h.Sum(nil)
  218. }
  219. func (e *rootEntry) verifySignature(pubkey *ecdsa.PublicKey) bool {
  220. sig := e.sig[:crypto.RecoveryIDOffset] // remove recovery id
  221. return crypto.VerifySignature(crypto.FromECDSAPub(pubkey), e.sigHash(), sig)
  222. }
  223. func (e *branchEntry) String() string {
  224. return branchPrefix + strings.Join(e.children, ",")
  225. }
  226. func (e *enrEntry) String() string {
  227. return e.node.String()
  228. }
  229. func (e *linkEntry) String() string {
  230. pubkey := b32format.EncodeToString(crypto.CompressPubkey(e.pubkey))
  231. return fmt.Sprintf("%s%s@%s", linkPrefix, pubkey, e.domain)
  232. }
  233. // Entry Parsing
  234. func parseEntry(e string, validSchemes enr.IdentityScheme) (entry, error) {
  235. switch {
  236. case strings.HasPrefix(e, linkPrefix):
  237. return parseLinkEntry(e)
  238. case strings.HasPrefix(e, branchPrefix):
  239. return parseBranch(e)
  240. case strings.HasPrefix(e, enrPrefix):
  241. return parseENR(e, validSchemes)
  242. default:
  243. return nil, errUnknownEntry
  244. }
  245. }
  246. func parseRoot(e string) (rootEntry, error) {
  247. var eroot, lroot, sig string
  248. var seq uint
  249. if _, err := fmt.Sscanf(e, rootPrefix+" e=%s l=%s seq=%d sig=%s", &eroot, &lroot, &seq, &sig); err != nil {
  250. return rootEntry{}, entryError{"root", errSyntax}
  251. }
  252. if !isValidHash(eroot) || !isValidHash(lroot) {
  253. return rootEntry{}, entryError{"root", errInvalidChild}
  254. }
  255. sigb, err := b64format.DecodeString(sig)
  256. if err != nil || len(sigb) != crypto.SignatureLength {
  257. return rootEntry{}, entryError{"root", errInvalidSig}
  258. }
  259. return rootEntry{eroot, lroot, seq, sigb}, nil
  260. }
  261. func parseLinkEntry(e string) (entry, error) {
  262. le, err := parseLink(e)
  263. if err != nil {
  264. return nil, err
  265. }
  266. return le, nil
  267. }
  268. func parseLink(e string) (*linkEntry, error) {
  269. if !strings.HasPrefix(e, linkPrefix) {
  270. return nil, fmt.Errorf("wrong/missing scheme 'enrtree' in URL")
  271. }
  272. e = e[len(linkPrefix):]
  273. pos := strings.IndexByte(e, '@')
  274. if pos == -1 {
  275. return nil, entryError{"link", errNoPubkey}
  276. }
  277. keystring, domain := e[:pos], e[pos+1:]
  278. keybytes, err := b32format.DecodeString(keystring)
  279. if err != nil {
  280. return nil, entryError{"link", errBadPubkey}
  281. }
  282. key, err := crypto.DecompressPubkey(keybytes)
  283. if err != nil {
  284. return nil, entryError{"link", errBadPubkey}
  285. }
  286. return &linkEntry{domain, key}, nil
  287. }
  288. func parseBranch(e string) (entry, error) {
  289. e = e[len(branchPrefix):]
  290. if e == "" {
  291. return &branchEntry{}, nil // empty entry is OK
  292. }
  293. hashes := make([]string, 0, strings.Count(e, ","))
  294. for _, c := range strings.Split(e, ",") {
  295. if !isValidHash(c) {
  296. return nil, entryError{"branch", errInvalidChild}
  297. }
  298. hashes = append(hashes, c)
  299. }
  300. return &branchEntry{hashes}, nil
  301. }
  302. func parseENR(e string, validSchemes enr.IdentityScheme) (entry, error) {
  303. e = e[len(enrPrefix):]
  304. enc, err := b64format.DecodeString(e)
  305. if err != nil {
  306. return nil, entryError{"enr", errInvalidENR}
  307. }
  308. var rec enr.Record
  309. if err := rlp.DecodeBytes(enc, &rec); err != nil {
  310. return nil, entryError{"enr", err}
  311. }
  312. n, err := enode.New(validSchemes, &rec)
  313. if err != nil {
  314. return nil, entryError{"enr", err}
  315. }
  316. return &enrEntry{n}, nil
  317. }
  318. func isValidHash(s string) bool {
  319. dlen := b32format.DecodedLen(len(s))
  320. if dlen < minHashLength || dlen > 32 || strings.ContainsAny(s, "\n\r") {
  321. return false
  322. }
  323. buf := make([]byte, 32)
  324. _, err := b32format.Decode(buf, []byte(s))
  325. return err == nil
  326. }
  327. // truncateHash truncates the given base32 hash string to the minimum acceptable length.
  328. func truncateHash(hash string) string {
  329. maxLen := b32format.EncodedLen(minHashLength)
  330. if len(hash) < maxLen {
  331. panic(fmt.Errorf("dnsdisc: hash %q is too short", hash))
  332. }
  333. return hash[:maxLen]
  334. }
  335. // URL encoding
  336. // ParseURL parses an enrtree:// URL and returns its components.
  337. func ParseURL(url string) (domain string, pubkey *ecdsa.PublicKey, err error) {
  338. le, err := parseLink(url)
  339. if err != nil {
  340. return "", nil, err
  341. }
  342. return le.domain, le.pubkey, nil
  343. }