This repository has been archived on 2020-04-20. You can view files and clone it, but cannot push or open issues or pull requests.
libricochet-go/utils/tor.go

78 lines
2.2 KiB
Go

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
}