├── .gitkeep ├── go.mod ├── RandomAddresses └── README.md ├── Addresses └── README.md ├── .github └── FUNDING.yml ├── go.sum ├── LICENSE ├── README.md ├── main.go └── random.go /.gitkeep: -------------------------------------------------------------------------------- 1 | Addresses 2 | RandomAddresses 3 | 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module oniongen-go 2 | 3 | go 1.14 4 | 5 | require ( 6 | golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad 7 | golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect 8 | ) 9 | -------------------------------------------------------------------------------- /RandomAddresses/README.md: -------------------------------------------------------------------------------- 1 | ## Random Onion Address 2 | To generate v3 .onion random address. 3 | 4 | ```go 5 | go run random.go 6 | ``` 7 | 8 | This will generate a random onion address. 9 | 10 | > The onion addresses will be stored in RandomAddress dictionary. 11 | -------------------------------------------------------------------------------- /Addresses/README.md: -------------------------------------------------------------------------------- 1 | ## Address 2 | This folder contains onion address you have generated with below command. 3 | ```sh 4 | go run main.go 5 | 6 | regex regex pattern addresses should match, consisiting of: A-Z, 2-7 7 | number number of matching addresses to generate before exiting 8 | ``` 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | 2 | # These are supported funding model platforms 3 | 4 | github: # sumithemmadi 5 | patreon: # Replace with a single Patreon username 6 | open_collective: # Replace with a single Open Collective username 7 | ko_fi: # Replace with a single Ko-fi username 8 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | liberapay: sumithemmadi 11 | issuehunt: sumithemmadi 12 | otechie: # Replace with a single Otechie username 13 | custom: # ["https://paypal.me/sumithemmadi"] 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 2 | golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= 3 | golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 4 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 5 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 6 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 7 | golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo= 8 | golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 9 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 10 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Emmadi Sumith Kumar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # oniongen 2 | 3 | v3 .onion address vanity URL generator written in Go. 4 | 5 | This implementation generates random ed25519 keys across all CPU cores. The ed25519 public key is converted to a Tor v3 .onion address which is then compared to a user supplied regex to find a vanity URL. If the regex for the .onion address matches, the secret key is expanded for use by Tor and the public key, secret key, and hostname are written to file in a new directory named for the .onion address. The program terminates when the user supplied number of addresses have been generated. 6 | 7 | ## Usage 8 | 9 | ```go 10 | go run main.go 11 | 12 | regex regex pattern addresses should match, consisiting of: A-Z, 2-7 13 | number number of matching addresses to generate before exiting 14 | ``` 15 | 16 | ## Example 17 | 18 | ```go 19 | go run main.go "^test" 5 20 | 21 | generate 5 onion addresses starting with "test" 22 | ``` 23 | > The onion addresses will be stored in `Addresses` directory. 24 | ## Random Onion Address 25 | 26 | To generate v3 .onion random address. 27 | 28 | ```go 29 | go run random.go 30 | ``` 31 | This will generate a random onion addresses. 32 | 33 | > The onion addresses will be stored in `RandomAddress` directory. 34 | 42 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2021 Emmadi Sumith Kumar 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | package main 27 | 28 | import ( 29 | "bytes" 30 | "crypto/ed25519" 31 | "crypto/sha512" 32 | "encoding/base32" 33 | "fmt" 34 | "io/ioutil" 35 | "os" 36 | "regexp" 37 | "runtime" 38 | "strconv" 39 | "strings" 40 | "sync" 41 | 42 | "golang.org/x/crypto/sha3" 43 | ) 44 | 45 | func generate(wg *sync.WaitGroup, re *regexp.Regexp) { 46 | 47 | for { 48 | 49 | publicKey, secretKey, err := ed25519.GenerateKey(nil) 50 | checkErr(err) 51 | 52 | onionAddress := encodePublicKey(publicKey) 53 | 54 | // If a matching address is found, save key and notify wait group 55 | if re.MatchString(onionAddress) == true { 56 | fmt.Println(onionAddress+".onion") 57 | save(onionAddress, publicKey, expandSecretKey(secretKey)) 58 | wg.Done() 59 | } 60 | } 61 | } 62 | 63 | func expandSecretKey(secretKey ed25519.PrivateKey) [64]byte { 64 | 65 | hash := sha512.Sum512(secretKey[:32]) 66 | hash[0] &= 248 67 | hash[31] &= 127 68 | hash[31] |= 64 69 | return hash 70 | 71 | } 72 | 73 | func encodePublicKey(publicKey ed25519.PublicKey) string { 74 | 75 | // checksum = H(".onion checksum" || pubkey || version) 76 | var checksumBytes bytes.Buffer 77 | checksumBytes.Write([]byte(".onion checksum")) 78 | checksumBytes.Write([]byte(publicKey)) 79 | checksumBytes.Write([]byte{0x03}) 80 | checksum := sha3.Sum256(checksumBytes.Bytes()) 81 | 82 | // onion_address = base32(pubkey || checksum || version) 83 | var onionAddressBytes bytes.Buffer 84 | onionAddressBytes.Write([]byte(publicKey)) 85 | onionAddressBytes.Write([]byte(checksum[:2])) 86 | onionAddressBytes.Write([]byte{0x03}) 87 | onionAddress := base32.StdEncoding.EncodeToString(onionAddressBytes.Bytes()) 88 | 89 | return strings.ToLower(onionAddress) 90 | 91 | } 92 | 93 | func save(onionAddress string, publicKey ed25519.PublicKey, secretKey [64]byte) { 94 | os.MkdirAll("Addresses/"+onionAddress, 0700) 95 | 96 | secretKeyFile := append([]byte("== ed25519v1-secret: type0 ==\x00\x00\x00"), secretKey[:]...) 97 | checkErr(ioutil.WriteFile("Addresses/"+onionAddress+"/hs_ed25519_secret_key", secretKeyFile, 0600)) 98 | 99 | publicKeyFile := append([]byte("== ed25519v1-public: type0 ==\x00\x00\x00"), publicKey...) 100 | checkErr(ioutil.WriteFile("Addresses/"+onionAddress+"/hs_ed25519_public_key", publicKeyFile, 0600)) 101 | 102 | checkErr(ioutil.WriteFile("Addresses/"+onionAddress+"/hostname", []byte(onionAddress+".onion"), 0600)) 103 | } 104 | 105 | func checkErr(err error) { 106 | if err != nil { 107 | panic(err) 108 | } 109 | } 110 | 111 | func main() { 112 | 113 | // Set runtime to use all available CPUs. 114 | runtime.GOMAXPROCS(runtime.NumCPU()) 115 | 116 | // Compile regex from first argument. 117 | re, _ := regexp.Compile(os.Args[1]) 118 | 119 | // Get the number of desired addreses from second argument. 120 | numAddresses, _ := strconv.Atoi(os.Args[2]) 121 | 122 | // WaitGroup of size equal to desired number of addresses 123 | var wg sync.WaitGroup 124 | wg.Add(numAddresses) 125 | 126 | // For each CPU, run a generate goroutine 127 | for i := 0; i < runtime.NumCPU(); i++ { 128 | go generate(&wg, re) 129 | } 130 | 131 | // Exit after the desired number of addresses have been found. 132 | wg.Wait() 133 | 134 | } 135 | -------------------------------------------------------------------------------- /random.go: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Emmadi Sumith Kumar 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package main 25 | 26 | import ( 27 | "bytes" 28 | "crypto/ed25519" 29 | "crypto/sha512" 30 | "encoding/base32" 31 | "fmt" 32 | "io/ioutil" 33 | "os" 34 | "regexp" 35 | "runtime" 36 | "strconv" 37 | "strings" 38 | "sync" 39 | // "time" 40 | "math/rand" 41 | "golang.org/x/crypto/sha3" 42 | ) 43 | //var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 44 | var letters = []rune("abcdefghijklmnopqrstuvwxyz") 45 | 46 | 47 | func RandomVariable(n int) string { 48 | b := make([]rune, n) 49 | for i := range b { 50 | b[i] = letters[rand.Intn(len(letters))] 51 | } 52 | return string(b) 53 | } 54 | /* 55 | func main() { 56 | rand.Seed(time.Now().UnixNano()) 57 | 58 | fmt.Println(RandomVariable(1)) 59 | } 60 | */ 61 | func generate(wg *sync.WaitGroup, re *regexp.Regexp) { 62 | 63 | for { 64 | 65 | publicKey, secretKey, err := ed25519.GenerateKey(nil) 66 | checkErr(err) 67 | 68 | onionAddress := encodePublicKey(publicKey) 69 | 70 | // If a matching address is found, save key and notify wait group 71 | if re.MatchString(onionAddress) == true { 72 | fmt.Println(onionAddress+".onion") 73 | save(onionAddress, publicKey, expandSecretKey(secretKey)) 74 | wg.Done() 75 | } 76 | } 77 | } 78 | 79 | func expandSecretKey(secretKey ed25519.PrivateKey) [64]byte { 80 | 81 | hash := sha512.Sum512(secretKey[:32]) 82 | hash[0] &= 248 83 | hash[31] &= 127 84 | hash[31] |= 64 85 | return hash 86 | 87 | } 88 | 89 | func encodePublicKey(publicKey ed25519.PublicKey) string { 90 | 91 | // checksum = H(".onion checksum" || pubkey || version) 92 | var checksumBytes bytes.Buffer 93 | checksumBytes.Write([]byte(".onion checksum")) 94 | checksumBytes.Write([]byte(publicKey)) 95 | checksumBytes.Write([]byte{0x03}) 96 | checksum := sha3.Sum256(checksumBytes.Bytes()) 97 | 98 | // onion_address = base32(pubkey || checksum || version) 99 | var onionAddressBytes bytes.Buffer 100 | onionAddressBytes.Write([]byte(publicKey)) 101 | onionAddressBytes.Write([]byte(checksum[:2])) 102 | onionAddressBytes.Write([]byte{0x03}) 103 | onionAddress := base32.StdEncoding.EncodeToString(onionAddressBytes.Bytes()) 104 | 105 | return strings.ToLower(onionAddress) 106 | 107 | } 108 | 109 | func save(onionAddress string, publicKey ed25519.PublicKey, secretKey [64]byte) { 110 | os.MkdirAll("RandomAddresses/"+onionAddress, 0700) 111 | 112 | secretKeyFile := append([]byte("== ed25519v1-secret: type0 ==\x00\x00\x00"), secretKey[:]...) 113 | checkErr(ioutil.WriteFile("RandomAddresses/"+onionAddress+"/hs_ed25519_secret_key", secretKeyFile, 0600)) 114 | 115 | publicKeyFile := append([]byte("== ed25519v1-public: type0 ==\x00\x00\x00"), publicKey...) 116 | checkErr(ioutil.WriteFile("RandomAddresses/"+onionAddress+"/hs_ed25519_public_key", publicKeyFile, 0600)) 117 | 118 | checkErr(ioutil.WriteFile("RandomAddresses/"+onionAddress+"/hostname", []byte(onionAddress+".onion"), 0600)) 119 | } 120 | 121 | func checkErr(err error) { 122 | if err != nil { 123 | panic(err) 124 | } 125 | } 126 | 127 | func main() { 128 | 129 | // Set runtime to use all available CPUs. 130 | runtime.GOMAXPROCS(runtime.NumCPU()) 131 | 132 | // Compile regex from first argument. 133 | re, _ := regexp.Compile(RandomVariable(4)) 134 | 135 | // Get the number of desired addreses from second argument. 136 | numAddresses, _ := strconv.Atoi("1") 137 | 138 | // WaitGroup of size equal to desired number of addresses 139 | var wg sync.WaitGroup 140 | wg.Add(numAddresses) 141 | 142 | // For each CPU, run a generate goroutine 143 | for i := 0; i < runtime.NumCPU(); i++ { 144 | go generate(&wg, re) 145 | } 146 | 147 | // Exit after the desired number of addresses have been found. 148 | wg.Wait() 149 | 150 | } 151 | --------------------------------------------------------------------------------