Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimization #10

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
oniongen

# Created by https://www.gitignore.io/api/go
# Edit at https://www.gitignore.io/?templates=go
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build:
./make.sh build
update_libs:
./make.sh update_libs
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

v3 .onion address vanity URL generator written in Go.

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.
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.

## Usage

```
go run main.go <regex> <number>

regex regex pattern addresses should match, consisiting of: A-Z, 2-7
regex regex pattern addresses should match, consisiting of: a-z, 2-7
number number of matching addresses to generate before exiting
```

Expand All @@ -22,8 +25,10 @@ go run main.go "^test" 5
```

## References

- Onion Addresses are defined in [Tor Rendezvous Specification - Version 3](https://github.com/torproject/torspec/blob/main/rend-spec-v3.txt)
- public key -> onion: https://github.com/torproject/torspec/blob/12271f0e6db00dee9600425b2de063e02f19c1ee/rend-spec-v3.txt#L2136-L2158
- secret key expansion:
- implementation in mkp224o: https://github.com/cathugger/mkp224o/blob/af5a7cfe122ba62e819b92c8b5a662151a284c69/ed25519/ed25519.h#L153-L161
- possibly related: https://github.com/torproject/torspec/blob/12271f0e6db00dee9600425b2de063e02f19c1ee/rend-spec-v3.txt#L2268-L2327 ??
- [specification](https://github.com/torproject/torspec/blob/12271f0e6db00dee9600425b2de063e02f19c1ee/rend-spec-v3.txt#L2268-L2327)
- [should the secret key be expanded?](https://tor.stackexchange.com/questions/23849/tor-onion-domain-should-the-secret-key-be-expanded)
- [implementation in tor](https://gitlab.torproject.org/tpo/core/tor/-/blob/main/src/ext/ed25519/donna/ed25519_tor.c#L65)
- [implementation in mkp224o](https://github.com/cathugger/mkp224o/blob/af5a7cfe122ba62e819b92c8b5a662151a284c69/ed25519/ed25519.h#L153-L161)
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module github.com/rdkr/oniongen-go

go 1.14
go 1.18

require (
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 // indirect
)
14 changes: 4 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
38 changes: 17 additions & 21 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ import (
"crypto/sha512"
"encoding/base32"
"fmt"
"io/ioutil"
"os"
"regexp"
"runtime"
"strconv"
"strings"
"sync"

"golang.org/x/crypto/sha3"
)

func generate(wg *sync.WaitGroup, re *regexp.Regexp) {
const b32Lower = "abcdefghijklmnopqrstuvwxyz234567"

for {
var b32Enc = base32.NewEncoding(b32Lower).WithPadding(base32.NoPadding)

func generate(wg *sync.WaitGroup, re *regexp.Regexp) {
for {
publicKey, secretKey, err := ed25519.GenerateKey(nil)
checkErr(err)

Expand All @@ -36,45 +36,42 @@ func generate(wg *sync.WaitGroup, re *regexp.Regexp) {
}

func expandSecretKey(secretKey ed25519.PrivateKey) [64]byte {

hash := sha512.Sum512(secretKey[:32])
// clamp the blinding factor 'h' according to the ed25519 spec
hash[0] &= 248
hash[31] &= 127
hash[31] |= 64
return hash

}

func encodePublicKey(publicKey ed25519.PublicKey) string {

// checksum = H(".onion checksum" || pubkey || version)
var checksumBytes bytes.Buffer
checksumBytes.Write([]byte(".onion checksum"))
checksumBytes.Write([]byte(publicKey))
checksumBytes.Write(publicKey)
checksumBytes.Write([]byte{0x03})
checksum := sha3.Sum256(checksumBytes.Bytes())

// onion_address = base32(pubkey || checksum || version)
var onionAddressBytes bytes.Buffer
onionAddressBytes.Write([]byte(publicKey))
onionAddressBytes.Write([]byte(checksum[:2]))
onionAddressBytes.Write(publicKey)
onionAddressBytes.Write(checksum[:2])
onionAddressBytes.Write([]byte{0x03})
onionAddress := base32.StdEncoding.EncodeToString(onionAddressBytes.Bytes())

return strings.ToLower(onionAddress)
onionAddress := b32Enc.EncodeToString(onionAddressBytes.Bytes())

return onionAddress
}

func save(onionAddress string, publicKey ed25519.PublicKey, secretKey [64]byte) {
os.MkdirAll(onionAddress, 0700)
checkErr(os.MkdirAll(onionAddress, 0700))

secretKeyFile := append([]byte("== ed25519v1-secret: type0 ==\x00\x00\x00"), secretKey[:]...)
checkErr(ioutil.WriteFile(onionAddress+"/hs_ed25519_secret_key", secretKeyFile, 0600))
checkErr(os.WriteFile(onionAddress+"/hs_ed25519_secret_key", secretKeyFile, 0600))

publicKeyFile := append([]byte("== ed25519v1-public: type0 ==\x00\x00\x00"), publicKey...)
checkErr(ioutil.WriteFile(onionAddress+"/hs_ed25519_public_key", publicKeyFile, 0600))
checkErr(os.WriteFile(onionAddress+"/hs_ed25519_public_key", publicKeyFile, 0600))

checkErr(ioutil.WriteFile(onionAddress+"/hostname", []byte(onionAddress+".onion\n"), 0600))
checkErr(os.WriteFile(onionAddress+"/hostname", []byte(onionAddress+".onion\n"), 0600))
}

func checkErr(err error) {
Expand All @@ -84,14 +81,14 @@ func checkErr(err error) {
}

func main() {

// Set runtime to use all available CPUs.
runtime.GOMAXPROCS(runtime.NumCPU())

prefix := os.Args[1]
// Compile regex from first argument.
re, _ := regexp.Compile(os.Args[1])
re, _ := regexp.Compile(prefix)

// Get the number of desired addreses from second argument.
// Get the number of desired addresses from second argument.
numAddresses, _ := strconv.Atoi(os.Args[2])

// WaitGroup of size equal to desired number of addresses
Expand All @@ -105,5 +102,4 @@ func main() {

// Exit after the desired number of addresses have been found.
wg.Wait()

}
32 changes: 32 additions & 0 deletions make.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
build() {
go mod download
GOAMD64=v3 go build -tags release -o ./oniongen
}

update_libs() {
go get -u
go mod tidy
}

help() {
go get -u
echo "build update_libs"
}

progname=$(basename $0)
subcommand=$1
case $subcommand in
"" | "-h" | "--help")
help
;;
*)
shift
echo "Executing: $subcommand"
${subcommand} "$@"
if [ $? = 127 ]; then
echo "Error: '$subcommand' is not a known subcommand." >&2
help
exit 1
fi
;;
esac