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

How is it possible to persist V3 .onion address? #38

Open
qwertyqop opened this issue Mar 26, 2022 · 3 comments
Open

How is it possible to persist V3 .onion address? #38

qwertyqop opened this issue Mar 26, 2022 · 3 comments

Comments

@qwertyqop
Copy link

I would like to keep my onion address static after I close the program and reopen it.
this is done with V2 addressed as described in issue #19, however it is not applicable with V3 addresses.
how do I make V3 addresses persistent after reopening the program?

@Danukeru
Copy link

Danukeru commented Apr 16, 2022

Working off of the libtor example, observe these comments and changes.

import  (
...
        "github.com/cretz/bine/torutil/ed25519"
         b64 "encoding/base64"
)

...

    // $ dd if=/dev/urandom bs=64 count=1 status=none| base64 -w100
    // OR if you generated a vanity address, use this to dump the last 64 bytes from a stored onion-V3 :
    // $ dd if=hs_ed25519_secret_key skip=32 bs=1 status=none | base64 -w 100
    _b64Ed25519 := "OUyeOZtOT8JgE2JrjDsoFTCGWYdz+rc1CTEbgGtD6swEQcD1jmWkWUrkmFiRLkcBVvio5/yUyRJ24fqNyei57Q=="
    _key, _ := b64.StdEncoding.DecodeString(_b64Ed25519)
    
    // Create an onion service to listen on any port but show as 80
    onion, err := t.Listen(ctx, &tor.ListenConf{RemotePorts: []int{80}, Version3: true, Key: ed25519.PrivateKey(_key)})
    if err != nil {
        log.Panicf("Failed to create onion service: %v", err)
    }
    defer onion.Close()

@ciehanski
Copy link

As @Danukeru's example shows, you can utilize a vanity address as well. There is a nice little Go package here that can assist with generating that if you'd like to specify a reusable prefix for your V3 address.

@mh-cbon
Copy link

mh-cbon commented Aug 15, 2022

I use this scheme based on ciehanski post.

To create a key,

func createKey(path string) (crypto.PrivateKey, error) {
	path = strings.TrimSuffix(path, "hs_ed25519_secret_key")
	err := os.MkdirAll(path, 0700)
	if err != nil {
		return nil, err
	}

	publicKey, secretKey, err := ed25519.GenerateKey(nil)
	if err != nil {
		return nil, err
	}
	onionAddress := encodePublicKey(publicKey)

	expandedSecretKey := expandSecretKey(secretKey)
	secretKeyFile := append([]byte("== ed25519v1-secret: type0 ==\x00\x00\x00"), expandedSecretKey[:]...)
	secretKeyPath := filepath.Join(path, "/hs_ed25519_secret_key")
	err = os.WriteFile(secretKeyPath, secretKeyFile, 0600)
	if err != nil {
		return nil, err
	}
	publicKeyFile := append([]byte("== ed25519v1-public: type0 ==\x00\x00\x00"), publicKey...)
	publicKeyPath := filepath.Join(path, "/hs_ed25519_public_key")
	err = os.WriteFile(publicKeyPath, publicKeyFile, 0600)
	if err != nil {
		os.Remove(secretKeyPath)
		return nil, err
	}
	hostnameFile := []byte(onionAddress + ".onion\n")
	hostnamePath := filepath.Join(path, "/hostname")
	err = os.WriteFile(hostnamePath, hostnameFile, 0600)
	if err != nil {
		os.Remove(secretKeyPath)
		os.Remove(publicKeyPath)
		return nil, err
	}
	return bed25519.PrivateKey(expandedSecretKey[:]), nil
}
func expandSecretKey(secretKey ed25519.PrivateKey) [64]byte {
	hash := sha512.Sum512(secretKey[:32])
	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([]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([]byte{0x03})
	onionAddress := base32.StdEncoding.EncodeToString(onionAddressBytes.Bytes())

	return strings.ToLower(onionAddress)
}

to load a key

func loadKey(path string) (crypto.PrivateKey, error) {
	var key crypto.PrivateKey
	if path != "" {
		if !strings.HasSuffix(path, "hs_ed25519_secret_key") {
			path = filepath.Join(path, "hs_ed25519_secret_key")
		}
		d, err := os.ReadFile(path)
		if err != nil {
			return nil, fmt.Errorf("failed to load the private key: %v", err)
		}
		d = bytes.TrimPrefix(d, []byte("== ed25519v1-secret: type0 ==\x00\x00\x00"))
		key = bed25519.PrivateKey(d)
	}
	return key, nil
}

The bed25519 import is github.com/cretz/bine/torutil/ed25519

Then I give the key to the Tor process creator and it works upon restart. smooth.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants