Open qwertyqop opened 2 years ago
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()
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.
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.
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?