package utils import ( "crypto/sha1" "crypto/sha512" "encoding/base32" "encoding/base64" "golang.org/x/crypto/ed25519" "golang.org/x/crypto/sha3" "strings" ) // GetTorHostname takes a []byte contained a DER-encoded RSA public key // and returns the first 16 bytes of the base32 encoded sha1 hash of the key. // This is the onion hostname of the tor service represented by the public key. func GetTorHostname(publicKeyBytes []byte) string { h := sha1.New() h.Write(publicKeyBytes) sha1bytes := h.Sum(nil) data := base32.StdEncoding.EncodeToString(sha1bytes) return strings.ToLower(data[0:16]) } // Expand ed25519.PrivateKey to (a || RH) form, return base64 func expandKey(pri ed25519.PrivateKey) string { h := sha512.Sum512(pri[:32]) // Set bits so that h[:32] is private scalar "a" h[0] &= 248 h[31] &= 127 h[31] |= 64 // Since h[32:] is RH, h is now (a || RH) return base64.StdEncoding.EncodeToString(h[:]) } // V3HostnameLength is the length of a Tor V3 Onion Address (without the .onion suffix) const V3HostnameLength = 56 // Hidden service version const version = byte(0x03) // Salt used to create checkdigits const salt = ".onion checksum" func getCheckdigits(pub ed25519.PublicKey) []byte { // Calculate checksum sha3(".onion checksum" || publicKey || version) checkstr := []byte(salt) checkstr = append(checkstr, pub...) checkstr = append(checkstr, version) checksum := sha3.Sum256(checkstr) return checksum[:2] } // GetTorV3Hostname converts an ed25519 public key to a valid tor onion hostname func GetTorV3Hostname(pub ed25519.PublicKey) string { // Construct onion address base32(publicKey || checkdigits || version) checkdigits := getCheckdigits(pub) combined := pub[:] combined = append(combined, checkdigits...) combined = append(combined, version) serviceID := base32.StdEncoding.EncodeToString(combined) return strings.ToLower(serviceID) } // IsValidHostname returns true if the given address is a valid onion v3 address func IsValidHostname(address string) bool { if len(address) == V3HostnameLength { data, err := base32.StdEncoding.DecodeString(strings.ToUpper(address)) if err == nil { pubkey := data[0:ed25519.PublicKeySize] if GetTorV3Hostname(ed25519.PublicKey(pubkey)) == address { return true } } } return false }