├── .gitignore ├── scripts └── install_lint.sh ├── Makefile ├── go.mod ├── cmd ├── address │ ├── address.go │ └── main.go └── main.go ├── README.md └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | *data-dir* 2 | *.key -------------------------------------------------------------------------------- /scripts/install_lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ -z "${GOPATH}" ]]; then 4 | export GOPATH=~/go 5 | fi 6 | 7 | if ! command -v golangci-lint &> /dev/null 8 | then 9 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.45.0 10 | fi 11 | 12 | export PATH=$PATH:$(go env GOPATH)/bin -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: lint test install build 2 | all: install 3 | 4 | lint: 5 | bash ./scripts/install_lint.sh 6 | ${GOPATH}/bin/golangci-lint run 7 | 8 | test: 9 | go test ./... 10 | 11 | install: 12 | cd cmd/ && go install && cd .. 13 | 14 | build: 15 | cd cmd/ && go build -o onioncli && mv onioncli .. && cd address && go build -o onionaddress && mv onionaddress ../.. && cd ../.. 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/noot/onion-service 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect 7 | github.com/cretz/bine v0.2.0 // indirect 8 | github.com/gogo/protobuf v1.3.2 // indirect 9 | github.com/ipfs/go-log v1.0.5 // indirect 10 | github.com/ipfs/go-log/v2 v2.1.3 // indirect 11 | github.com/opentracing/opentracing-go v1.2.0 // indirect 12 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 13 | github.com/urfave/cli/v2 v2.4.0 // indirect 14 | go.uber.org/atomic v1.7.0 // indirect 15 | go.uber.org/multierr v1.6.0 // indirect 16 | go.uber.org/zap v1.16.0 // indirect 17 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect 18 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect 19 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /cmd/address/address.go: -------------------------------------------------------------------------------- 1 | // Code based off gist: https://gist.github.com/wybiral/8f737644fc140c97b6b26c13b1409837 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/rand" 7 | "encoding/base32" 8 | "golang.org/x/crypto/ed25519" 9 | "golang.org/x/crypto/sha3" 10 | "strings" 11 | ) 12 | 13 | // Hidden service version 14 | const version = byte(0x03) 15 | 16 | // Salt used to create checkdigits 17 | const salt = ".onion checksum" 18 | 19 | // Generate returns an address corresponding to the given private key (without the .onion) 20 | func GenerateAddress() (string, ed25519.PrivateKey, error) { 21 | pub, priv, err := ed25519.GenerateKey(rand.Reader) 22 | if err != nil { 23 | return "", nil, err 24 | } 25 | 26 | return getServiceID(pub), priv, nil 27 | } 28 | 29 | func getCheckdigits(pub ed25519.PublicKey) []byte { 30 | // Calculate checksum sha3(".onion checksum" || publicKey || version) 31 | checkstr := []byte(salt) 32 | checkstr = append(checkstr, pub...) 33 | checkstr = append(checkstr, version) 34 | checksum := sha3.Sum256(checkstr) 35 | return checksum[:2] 36 | } 37 | 38 | func getServiceID(pub ed25519.PublicKey) string { 39 | // Construct onion address base32(publicKey || checkdigits || version) 40 | checkdigits := getCheckdigits(pub) 41 | combined := pub[:] 42 | combined = append(combined, checkdigits...) 43 | combined = append(combined, version) 44 | serviceID := base32.StdEncoding.EncodeToString(combined) 45 | return strings.ToLower(serviceID) 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hidden service server 2 | 3 | A CLI that will host a static website as a .onion hidden service. 4 | 5 | Comes with an additional binary that can be used to generate vanity .onion addresses. 6 | 7 | ## Requirements 8 | 9 | - go1.17 10 | - tor 0.4.x 11 | - download source here https://www.torproject.org/download/tor/ or `wget https://dist.torproject.org/tor-0.4.6.10.tar.gz` 12 | - download additional dependencies: `sudo apt install libevent-dev libssl-dev zlib1g zlib1g-dev -y` 13 | - extract files (`tar -xzf`) and navigate to directory 14 | - `./configure && make && sudo make install`. Check that tor is installed with `tor --version`. 15 | 16 | ## Usage 17 | 18 | ### Build 19 | 20 | ``` 21 | make build 22 | ``` 23 | 24 | This places the binaries `onioncli` and `onionaddress` in the project root. 25 | 26 | ### Run 27 | 28 | Instead of building the project, you can also run it: 29 | ``` 30 | go run ./cmd/... [flags] 31 | ``` 32 | 33 | ### Usage 34 | 35 | To serve a static website: 36 | ```bash 37 | ./onioncli --serve-dir ~/my-website 38 | $ 2022-04-13T10:44:44.217-0400 INFO cmd cmd/main.go:153 Open Tor browser and navigate to http://7ukuzklqxkwesfs3dla5zzj3bsjb6v2rx25bq3fr662qistclpixgxqd.onion 39 | ``` 40 | 41 | If you have run the CLI before and have a server private key already (by default stored in `service.key`), you can pass it to the CLI so that the .onion address used will be the same as before. 42 | 43 | ```bash 44 | ./onioncli --private-key=service.key --serve-dir ~/my-website 45 | ``` 46 | 47 | You can also turn on debug logs with `--log=debug`. 48 | 49 | #### Vanity addresses 50 | 51 | To find a vanity address and its private key: 52 | ```bash 53 | ./onionaddress --prefix --count=3 54 | ``` 55 | 56 | This will search for and print 3 .onion addresses with the given prefix and their corresponding private keys. The private keys can be used with `onioncli --private-key=`. 57 | 58 | Note: for 4-letter prefixes and less, this process is quite quick. For 5-letter prefixes, it took around ~30 minutes on my machine to find 1 address, for a 6-letter prefix, it took ~90 minutes. This grows exponentially the longer the prefix gets. -------------------------------------------------------------------------------- /cmd/address/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "sync" 9 | "time" 10 | 11 | "github.com/urfave/cli/v2" 12 | ) 13 | 14 | var app = &cli.App{ 15 | Name: "onionaddress", 16 | Usage: "vanity onion address generator. for example, to find 5 addresses with the prefix \"fun\":\n\t$ ./onionaddress --prefix fun --count=5", 17 | Flags: []cli.Flag{ 18 | &cli.StringFlag{ 19 | Name: "prefix", 20 | Usage: "designated prefix to search for", 21 | }, 22 | &cli.StringFlag{ 23 | Name: "regex", 24 | Usage: "regex to match for; if this is set, --prefix is ignored. eg: to find address that are prefixed with either \"pea\" or \"noo\":\n\t$ ./onionaddress --regex --regex=^\\(pea\\|noo\\)\\(.*\\) --count 5", 25 | }, 26 | &cli.UintFlag{ 27 | Name: "max", 28 | Usage: "maximum number of iterations per goroutine; if --count is set, this is ignored. default=65536", 29 | }, 30 | &cli.UintFlag{ 31 | Name: "grs", 32 | Usage: "number of goroutines to use for search. default=1", 33 | }, 34 | &cli.UintFlag{ 35 | Name: "count", 36 | Usage: "how many addresses with the matching prefix to find. if set, ignores --max and runs until that many addresses are found", 37 | }, 38 | &cli.BoolFlag{ 39 | Name: "no-prefix", 40 | Usage: "don't search for a specific prefix, but print all addresses and keys found", 41 | }, 42 | }, 43 | Action: run, 44 | } 45 | 46 | func main() { 47 | err := app.Run(os.Args) 48 | if err != nil { 49 | panic(err) 50 | } 51 | } 52 | 53 | func run(c *cli.Context) error { 54 | max := uint64(c.Uint("max")) 55 | if max == 0 { 56 | max = 65536 57 | } 58 | 59 | regex := c.String("regex") 60 | prefix := c.String("prefix") 61 | if len(prefix) == 0 && !c.Bool("no-prefix") && regex == "" { 62 | return fmt.Errorf("must provide --prefix or --regex; if no prefix is desired, use the --no-prefix option") 63 | } 64 | 65 | count := c.Uint("count") 66 | if count != 0 { 67 | max = ^uint64(0) 68 | } 69 | 70 | grs := int(c.Uint("grs")) 71 | if grs == 0 { 72 | grs = 1 73 | } 74 | 75 | start := time.Now() 76 | 77 | var found uint 78 | var wg sync.WaitGroup 79 | var mu sync.Mutex 80 | 81 | wg.Add(grs) 82 | 83 | for i := 0; i < grs; i++ { 84 | go func() { 85 | defer wg.Done() 86 | for j := uint64(0); j < max; j++ { 87 | if found >= count && count != 0 { 88 | break 89 | } 90 | 91 | addr, priv, err := GenerateAddress() 92 | if err != nil { 93 | continue 94 | } 95 | 96 | if regex != "" { 97 | matched, err := regexp.Match(regex, []byte(addr)) 98 | if err != nil { 99 | continue 100 | } 101 | 102 | if !matched { 103 | continue 104 | } 105 | } else if addr[:len(prefix)] != prefix { 106 | continue 107 | } 108 | 109 | mu.Lock() 110 | fmt.Fprintf(os.Stdout, "%d %s.onion\t%s\n", found, addr, hex.EncodeToString(priv)) 111 | found++ 112 | mu.Unlock() 113 | } 114 | }() 115 | } 116 | 117 | wg.Wait() 118 | 119 | duration := time.Since(start) 120 | fmt.Printf("duration: %dms\n", duration.Milliseconds()) 121 | return nil 122 | } 123 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/rand" 6 | "encoding/hex" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "os" 11 | "os/signal" 12 | "path/filepath" 13 | "syscall" 14 | "time" 15 | 16 | "golang.org/x/crypto/ed25519" 17 | 18 | "github.com/cretz/bine/tor" 19 | logging "github.com/ipfs/go-log" 20 | "github.com/urfave/cli/v2" 21 | ) 22 | 23 | const ( 24 | defaultPrivateKeyFile = "service.key" 25 | ) 26 | 27 | var log = logging.Logger("cmd") 28 | 29 | var app = &cli.App{ 30 | Name: "onioncli", 31 | Usage: "host a static website as a .onion.\n\tExample usage:\n\t$ ./onioncli --serve-dir ~/my-website", 32 | Flags: []cli.Flag{ 33 | &cli.StringFlag{ 34 | Name: "datadir", 35 | Usage: "data directory used by program. if empty, uses data-dir-*.", 36 | }, 37 | &cli.StringFlag{ 38 | Name: "private-key", 39 | Usage: "path to private key file. if not set, generates a new private key and writes it to service.key", 40 | }, 41 | &cli.StringFlag{ 42 | Name: "log", 43 | Usage: "logging level. one of crit|error|warn|info|debug", 44 | }, 45 | &cli.StringFlag{ 46 | Name: "serve-dir", 47 | Usage: "path to static website to serve", 48 | }, 49 | }, 50 | Action: run, 51 | } 52 | 53 | func getPrivateKey(c *cli.Context) (ed25519.PrivateKey, error) { 54 | pkFile := c.String("private-key") 55 | if pkFile != "" { 56 | log.Debugf("reading private key from file %s", pkFile) 57 | pkHexBytes, err := ioutil.ReadFile(filepath.Clean(pkFile)) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | pkStr := string(pkHexBytes) 63 | if len(pkStr) < ed25519.PrivateKeySize*2 { 64 | return nil, fmt.Errorf("invalid private key string size: got %d, expected %d", len(pkStr), ed25519.PrivateKeySize*2) 65 | } 66 | 67 | pkBytes, err := hex.DecodeString(pkStr[:ed25519.PrivateKeySize*2]) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | if len(pkBytes) != ed25519.PrivateKeySize { 73 | return nil, fmt.Errorf("invalid private key size: got %d, expected %d", len(pkBytes), ed25519.PrivateKeySize) 74 | } 75 | 76 | return ed25519.PrivateKey(pkBytes), nil 77 | } 78 | 79 | log.Debugf("generating new private key and writing to file %s", defaultPrivateKeyFile) 80 | _, pk, err := ed25519.GenerateKey(rand.Reader) 81 | if err != nil { 82 | return nil, err 83 | } 84 | 85 | pkStr := hex.EncodeToString(pk) 86 | err = ioutil.WriteFile(defaultPrivateKeyFile, []byte(pkStr), os.ModePerm) 87 | if err != nil { 88 | return nil, err 89 | } 90 | 91 | return pk, nil 92 | } 93 | 94 | func setLogLevel(c *cli.Context) error { 95 | const ( 96 | levelError = "error" 97 | levelWarn = "warn" 98 | levelInfo = "info" 99 | levelDebug = "debug" 100 | ) 101 | 102 | level := c.String("log") 103 | if level == "" { 104 | level = levelInfo 105 | } 106 | 107 | switch level { 108 | case levelError, levelWarn, levelInfo, levelDebug: 109 | default: 110 | return fmt.Errorf("invalid log level") 111 | } 112 | 113 | _ = logging.SetLogLevel("cmd", level) 114 | return nil 115 | } 116 | 117 | func run(c *cli.Context) error { 118 | err := setLogLevel(c) 119 | if err != nil { 120 | return err 121 | } 122 | 123 | serveDir := c.String("serve-dir") 124 | if serveDir == "" { 125 | return fmt.Errorf("must provide --serve-dir (path to static website to serve)") 126 | } 127 | 128 | log.Info("Starting and registering onion service, please wait...") 129 | 130 | startConf := &tor.StartConf{ 131 | NoAutoSocksPort: true, 132 | DataDir: c.String("datadir"), 133 | } 134 | if c.String("log") == "debug" { 135 | // if debug is enabled, write all logs to stdout 136 | startConf.DebugWriter = os.Stdout 137 | } 138 | 139 | t, err := tor.Start(context.Background(), startConf) 140 | if err != nil { 141 | return fmt.Errorf("failed to start tor: %w", err) 142 | } 143 | 144 | defer t.Close() 145 | 146 | // Wait at most a few minutes to publish the service 147 | listenCtx, listenCancel := context.WithTimeout(context.Background(), 3*time.Minute) 148 | defer listenCancel() 149 | 150 | pk, err := getPrivateKey(c) 151 | if err != nil { 152 | return fmt.Errorf("failed to get private key: %w", err) 153 | } 154 | 155 | // Create a v3 onion service to listen on any port but show as 80 156 | onion, err := t.Listen(listenCtx, &tor.ListenConf{ 157 | Version3: true, 158 | RemotePorts: []int{80}, 159 | Key: pk, 160 | }) 161 | if err != nil { 162 | return fmt.Errorf("unable to create onion service: %w", err) 163 | } 164 | defer onion.Close() 165 | 166 | log.Infof("Open Tor browser and navigate to http://%v.onion\n", onion.ID) 167 | log.Infof("Press ctrl+c to exit") 168 | 169 | // Serve the current folder from HTTP 170 | errCh := make(chan error, 1) 171 | go func() { 172 | errCh <- http.Serve(onion, NewHandler(onion, serveDir)) 173 | }() 174 | 175 | // End when enter is pressed 176 | go func() { 177 | sigc := make(chan os.Signal, 1) 178 | signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM) 179 | defer signal.Stop(sigc) 180 | <-sigc 181 | errCh <- nil 182 | }() 183 | 184 | if err = <-errCh; err != nil { 185 | return fmt.Errorf("failed serving: %w", err) 186 | } 187 | 188 | return nil 189 | } 190 | 191 | func main() { 192 | err := app.Run(os.Args) 193 | if err != nil { 194 | panic(err) 195 | } 196 | } 197 | 198 | var _ http.Handler = &Handler{} 199 | 200 | type Handler struct { 201 | onion *tor.OnionService 202 | serveDir string 203 | } 204 | 205 | func NewHandler(onion *tor.OnionService, serveDir string) *Handler { 206 | return &Handler{ 207 | onion: onion, 208 | serveDir: serveDir, 209 | } 210 | } 211 | 212 | func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 213 | if r.Method != "GET" { 214 | http.Error(w, "Method is not supported.", http.StatusNotFound) 215 | return 216 | } 217 | 218 | handler := http.FileServer(http.Dir(h.serveDir)) 219 | handler.ServeHTTP(w, r) 220 | } 221 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= 3 | github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 4 | github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo= 5 | github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 9 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 10 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 11 | github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= 12 | github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= 13 | github.com/ipfs/go-log/v2 v2.1.3 h1:1iS3IU7aXRlbgUpN8yTTpJ53NXYjAe37vcI5+5nYrzk= 14 | github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= 15 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 16 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 17 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 18 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 19 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 20 | github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= 21 | github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= 22 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 23 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 24 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 25 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 26 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 27 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 28 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 29 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 30 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 31 | github.com/urfave/cli/v2 v2.4.0 h1:m2pxjjDFgDxSPtO8WSdbndj17Wu2y8vOT86wE/tjr+I= 32 | github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg= 33 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 34 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 35 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 36 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 37 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 38 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 39 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 40 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 41 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 42 | go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= 43 | go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 44 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 45 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 46 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 47 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 48 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= 49 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 50 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 51 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 52 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 53 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 54 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 55 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 56 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 57 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 58 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 59 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 60 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= 61 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 62 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 63 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 64 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 65 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 66 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 67 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 68 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 69 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= 70 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 71 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 72 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 73 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 74 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 75 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 76 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 77 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 78 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 79 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 80 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 81 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 82 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 83 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 84 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 85 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 86 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 87 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 88 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 89 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 90 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 91 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 92 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 93 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 94 | --------------------------------------------------------------------------------