// Package oaddr provides onion address formatting package oaddr import ( "crypto" "crypto/ed25519" "encoding/base32" "fmt" "strings" "golang.org/x/crypto/sha3" ) // OnionAddress is an Ed25519 public key that represents a v3 onion address type OnionAddress [ed25519.PublicKeySize]byte // New outputs an onion address from a public key as defined in RFC 8032 func New(pub []byte) (addr OnionAddress, err error) { if got, want := len(pub), ed25519.PublicKeySize; got != want { return addr, fmt.Errorf("invalid public key size: %d", got) } copy(addr[:], pub) return addr, nil } // NewFromSigner outputs an onion address for a given signer func NewFromSigner(s crypto.Signer) (addr OnionAddress, err error) { switch t := s.Public().(type) { case ed25519.PublicKey: addr, err = New(s.Public().(ed25519.PublicKey)) default: err = fmt.Errorf("unknown key type: %v", t) } return } // String formats addr as defined in rend-spec-v3, see: // https://gitweb.torproject.org/torspec.git/tree/rend-spec-v3.txt#n2160 func (addr OnionAddress) String() string { b := addr[:] b = append(b, addr.checksum()...) b = append(b, addr.version()...) return strings.ToLower(base32.StdEncoding.EncodeToString(b)) + ".onion" } func (addr OnionAddress) checksum() []byte { h := sha3.New256() h.Write([]byte(".onion checksum")) h.Write(addr[:]) h.Write(addr.version()) sum := h.Sum(nil) return sum[:2] } func (addr OnionAddress) version() []byte { return []byte{0x03} }