tree.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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 := newLinkEntry(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. /*
  105. We want to keep the UDP size below 512 bytes. The UDP size is roughly:
  106. UDP length = 8 + UDP payload length ( 229 )
  107. UPD Payload length:
  108. - dns.id 2
  109. - dns.flags 2
  110. - dns.count.queries 2
  111. - dns.count.answers 2
  112. - dns.count.auth_rr 2
  113. - dns.count.add_rr 2
  114. - queries (query-size + 6)
  115. - answers :
  116. - dns.resp.name 2
  117. - dns.resp.type 2
  118. - dns.resp.class 2
  119. - dns.resp.ttl 4
  120. - dns.resp.len 2
  121. - dns.txt.length 1
  122. - dns.txt resp_data_size
  123. So the total size is roughly a fixed overhead of `39`, and the size of the
  124. query (domain name) and response.
  125. The query size is, for example, FVY6INQ6LZ33WLCHO3BPR3FH6Y.snap.mainnet.ethdisco.net (52)
  126. We also have some static data in the response, such as `enrtree-branch:`, and potentially
  127. splitting the response up with `" "`, leaving us with a size of roughly `400` that we need
  128. to stay below.
  129. The number `370` is used to have some margin for extra overhead (for example, the dns query
  130. may be larger - more subdomains).
  131. */
  132. const (
  133. hashAbbrevSize = 1 + 16*13/8 // Size of an encoded hash (plus comma)
  134. maxChildren = 370 / hashAbbrevSize // 13 children
  135. minHashLength = 12
  136. )
  137. // MakeTree creates a tree containing the given nodes and links.
  138. func MakeTree(seq uint, nodes []*enode.Node, links []string) (*Tree, error) {
  139. // Sort records by ID and ensure all nodes have a valid record.
  140. records := make([]*enode.Node, len(nodes))
  141. copy(records, nodes)
  142. sortByID(records)
  143. for _, n := range records {
  144. if len(n.Record().Signature()) == 0 {
  145. return nil, fmt.Errorf("can't add node %v: unsigned node record", n.ID())
  146. }
  147. }
  148. // Create the leaf list.
  149. enrEntries := make([]entry, len(records))
  150. for i, r := range records {
  151. enrEntries[i] = &enrEntry{r}
  152. }
  153. linkEntries := make([]entry, len(links))
  154. for i, l := range links {
  155. le, err := parseLink(l)
  156. if err != nil {
  157. return nil, err
  158. }
  159. linkEntries[i] = le
  160. }
  161. // Create intermediate nodes.
  162. t := &Tree{entries: make(map[string]entry)}
  163. eroot := t.build(enrEntries)
  164. t.entries[subdomain(eroot)] = eroot
  165. lroot := t.build(linkEntries)
  166. t.entries[subdomain(lroot)] = lroot
  167. t.root = &rootEntry{seq: seq, eroot: subdomain(eroot), lroot: subdomain(lroot)}
  168. return t, nil
  169. }
  170. func (t *Tree) build(entries []entry) entry {
  171. if len(entries) == 1 {
  172. return entries[0]
  173. }
  174. if len(entries) <= maxChildren {
  175. hashes := make([]string, len(entries))
  176. for i, e := range entries {
  177. hashes[i] = subdomain(e)
  178. t.entries[hashes[i]] = e
  179. }
  180. return &branchEntry{hashes}
  181. }
  182. var subtrees []entry
  183. for len(entries) > 0 {
  184. n := maxChildren
  185. if len(entries) < n {
  186. n = len(entries)
  187. }
  188. sub := t.build(entries[:n])
  189. entries = entries[n:]
  190. subtrees = append(subtrees, sub)
  191. t.entries[subdomain(sub)] = sub
  192. }
  193. return t.build(subtrees)
  194. }
  195. func sortByID(nodes []*enode.Node) []*enode.Node {
  196. sort.Slice(nodes, func(i, j int) bool {
  197. return bytes.Compare(nodes[i].ID().Bytes(), nodes[j].ID().Bytes()) < 0
  198. })
  199. return nodes
  200. }
  201. // Entry Types
  202. type entry interface {
  203. fmt.Stringer
  204. }
  205. type (
  206. rootEntry struct {
  207. eroot string
  208. lroot string
  209. seq uint
  210. sig []byte
  211. }
  212. branchEntry struct {
  213. children []string
  214. }
  215. enrEntry struct {
  216. node *enode.Node
  217. }
  218. linkEntry struct {
  219. str string
  220. domain string
  221. pubkey *ecdsa.PublicKey
  222. }
  223. )
  224. // Entry Encoding
  225. var (
  226. b32format = base32.StdEncoding.WithPadding(base32.NoPadding)
  227. b64format = base64.RawURLEncoding
  228. )
  229. const (
  230. rootPrefix = "enrtree-root:v1"
  231. linkPrefix = "enrtree://"
  232. branchPrefix = "enrtree-branch:"
  233. enrPrefix = "enr:"
  234. )
  235. func subdomain(e entry) string {
  236. h := sha3.NewLegacyKeccak256()
  237. io.WriteString(h, e.String())
  238. return b32format.EncodeToString(h.Sum(nil)[:16])
  239. }
  240. func (e *rootEntry) String() string {
  241. return fmt.Sprintf(rootPrefix+" e=%s l=%s seq=%d sig=%s", e.eroot, e.lroot, e.seq, b64format.EncodeToString(e.sig))
  242. }
  243. func (e *rootEntry) sigHash() []byte {
  244. h := sha3.NewLegacyKeccak256()
  245. fmt.Fprintf(h, rootPrefix+" e=%s l=%s seq=%d", e.eroot, e.lroot, e.seq)
  246. return h.Sum(nil)
  247. }
  248. func (e *rootEntry) verifySignature(pubkey *ecdsa.PublicKey) bool {
  249. sig := e.sig[:crypto.RecoveryIDOffset] // remove recovery id
  250. enckey := crypto.FromECDSAPub(pubkey)
  251. return crypto.VerifySignature(enckey, e.sigHash(), sig)
  252. }
  253. func (e *branchEntry) String() string {
  254. return branchPrefix + strings.Join(e.children, ",")
  255. }
  256. func (e *enrEntry) String() string {
  257. return e.node.String()
  258. }
  259. func (e *linkEntry) String() string {
  260. return linkPrefix + e.str
  261. }
  262. func newLinkEntry(domain string, pubkey *ecdsa.PublicKey) *linkEntry {
  263. key := b32format.EncodeToString(crypto.CompressPubkey(pubkey))
  264. str := key + "@" + domain
  265. return &linkEntry{str, domain, pubkey}
  266. }
  267. // Entry Parsing
  268. func parseEntry(e string, validSchemes enr.IdentityScheme) (entry, error) {
  269. switch {
  270. case strings.HasPrefix(e, linkPrefix):
  271. return parseLinkEntry(e)
  272. case strings.HasPrefix(e, branchPrefix):
  273. return parseBranch(e)
  274. case strings.HasPrefix(e, enrPrefix):
  275. return parseENR(e, validSchemes)
  276. default:
  277. return nil, errUnknownEntry
  278. }
  279. }
  280. func parseRoot(e string) (rootEntry, error) {
  281. var eroot, lroot, sig string
  282. var seq uint
  283. if _, err := fmt.Sscanf(e, rootPrefix+" e=%s l=%s seq=%d sig=%s", &eroot, &lroot, &seq, &sig); err != nil {
  284. return rootEntry{}, entryError{"root", errSyntax}
  285. }
  286. if !isValidHash(eroot) || !isValidHash(lroot) {
  287. return rootEntry{}, entryError{"root", errInvalidChild}
  288. }
  289. sigb, err := b64format.DecodeString(sig)
  290. if err != nil || len(sigb) != crypto.SignatureLength {
  291. return rootEntry{}, entryError{"root", errInvalidSig}
  292. }
  293. return rootEntry{eroot, lroot, seq, sigb}, nil
  294. }
  295. func parseLinkEntry(e string) (entry, error) {
  296. le, err := parseLink(e)
  297. if err != nil {
  298. return nil, err
  299. }
  300. return le, nil
  301. }
  302. func parseLink(e string) (*linkEntry, error) {
  303. if !strings.HasPrefix(e, linkPrefix) {
  304. return nil, fmt.Errorf("wrong/missing scheme 'enrtree' in URL")
  305. }
  306. e = e[len(linkPrefix):]
  307. pos := strings.IndexByte(e, '@')
  308. if pos == -1 {
  309. return nil, entryError{"link", errNoPubkey}
  310. }
  311. keystring, domain := e[:pos], e[pos+1:]
  312. keybytes, err := b32format.DecodeString(keystring)
  313. if err != nil {
  314. return nil, entryError{"link", errBadPubkey}
  315. }
  316. key, err := crypto.DecompressPubkey(keybytes)
  317. if err != nil {
  318. return nil, entryError{"link", errBadPubkey}
  319. }
  320. return &linkEntry{e, domain, key}, nil
  321. }
  322. func parseBranch(e string) (entry, error) {
  323. e = e[len(branchPrefix):]
  324. if e == "" {
  325. return &branchEntry{}, nil // empty entry is OK
  326. }
  327. hashes := make([]string, 0, strings.Count(e, ","))
  328. for _, c := range strings.Split(e, ",") {
  329. if !isValidHash(c) {
  330. return nil, entryError{"branch", errInvalidChild}
  331. }
  332. hashes = append(hashes, c)
  333. }
  334. return &branchEntry{hashes}, nil
  335. }
  336. func parseENR(e string, validSchemes enr.IdentityScheme) (entry, error) {
  337. e = e[len(enrPrefix):]
  338. enc, err := b64format.DecodeString(e)
  339. if err != nil {
  340. return nil, entryError{"enr", errInvalidENR}
  341. }
  342. var rec enr.Record
  343. if err := rlp.DecodeBytes(enc, &rec); err != nil {
  344. return nil, entryError{"enr", err}
  345. }
  346. n, err := enode.New(validSchemes, &rec)
  347. if err != nil {
  348. return nil, entryError{"enr", err}
  349. }
  350. return &enrEntry{n}, nil
  351. }
  352. func isValidHash(s string) bool {
  353. dlen := b32format.DecodedLen(len(s))
  354. if dlen < minHashLength || dlen > 32 || strings.ContainsAny(s, "\n\r") {
  355. return false
  356. }
  357. buf := make([]byte, 32)
  358. _, err := b32format.Decode(buf, []byte(s))
  359. return err == nil
  360. }
  361. // truncateHash truncates the given base32 hash string to the minimum acceptable length.
  362. func truncateHash(hash string) string {
  363. maxLen := b32format.EncodedLen(minHashLength)
  364. if len(hash) < maxLen {
  365. panic(fmt.Errorf("dnsdisc: hash %q is too short", hash))
  366. }
  367. return hash[:maxLen]
  368. }
  369. // URL encoding
  370. // ParseURL parses an enrtree:// URL and returns its components.
  371. func ParseURL(url string) (domain string, pubkey *ecdsa.PublicKey, err error) {
  372. le, err := parseLink(url)
  373. if err != nil {
  374. return "", nil, err
  375. }
  376. return le.domain, le.pubkey, nil
  377. }