EndGame0/sourcecode/gobalance/pkg/stem/descriptor/hidden_service.go
2024-10-23 20:50:14 +05:30

832 lines
26 KiB
Go

package descriptor
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/ed25519"
"crypto/x509"
"encoding/base32"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"github.com/sirupsen/logrus"
"gobalance/pkg/brand"
"gobalance/pkg/btime"
"gobalance/pkg/gobpk"
"gobalance/pkg/stem/util"
"golang.org/x/crypto/sha3"
"maze.io/x/crypto/x25519"
"strconv"
"strings"
)
// Descriptor common parent for all types of descriptors.
// https://github.com/torproject/torspec/blob/4da63977b86f4c17d0e8cf87ed492c72a4c9b2d9/rend-spec-v3.txt#L1057
type Descriptor struct {
HsDescriptorVersion int64
descriptorLifetime int64
DescriptorSigningKeyCert string
revisionCounter int64
superencrypted string
signature string
}
func (d *Descriptor) FromStr(content string) {
*d = *descFromStr(content)
}
func descFromStr(content string) *Descriptor {
d := &Descriptor{}
lines := strings.Split(content, "\n")
startCert := false
startSuperencrypted := false
for idx, line := range lines {
if idx == 0 {
d.HsDescriptorVersion, _ = strconv.ParseInt(strings.TrimPrefix(line, "hs-descriptor "), 10, 64)
continue
} else if idx == 1 {
d.descriptorLifetime, _ = strconv.ParseInt(strings.TrimPrefix(line, "descriptor-lifetime "), 10, 64)
continue
} else if line == "descriptor-signing-key-cert" {
startCert = true
continue
} else if line == "superencrypted" {
startSuperencrypted = true
continue
} else if strings.HasPrefix(line, "revision-counter ") {
d.revisionCounter, _ = strconv.ParseInt(strings.TrimPrefix(line, "revision-counter "), 10, 64)
continue
} else if strings.HasPrefix(line, "signature ") {
d.signature = strings.TrimPrefix(line, "signature ")
continue
}
if startCert {
d.DescriptorSigningKeyCert += line + "\n"
if line == "-----END ED25519 CERT-----" {
startCert = false
d.DescriptorSigningKeyCert = strings.TrimSpace(d.DescriptorSigningKeyCert)
}
} else if startSuperencrypted {
d.superencrypted += line + "\n"
if line == "-----END MESSAGE-----" {
startSuperencrypted = false
d.superencrypted = strings.TrimSpace(d.superencrypted)
}
}
}
return d
}
// BaseHiddenServiceDescriptor hidden service descriptor.
type BaseHiddenServiceDescriptor struct {
Descriptor
}
const (
// ExtensionType
HasSigningKey = 4
)
// HiddenServiceDescriptorV3 version 3 hidden service descriptor.
type HiddenServiceDescriptorV3 struct {
BaseHiddenServiceDescriptor
SigningCert Ed25519CertificateV1
InnerLayer *InnerLayer
rawContents string
}
func (d HiddenServiceDescriptorV3) String() string {
var sb strings.Builder
sb.WriteString("hs-descriptor 3\n")
sb.WriteString("descriptor-lifetime ")
sb.WriteString(strconv.FormatInt(d.descriptorLifetime, 10))
sb.WriteByte('\n')
sb.WriteString("descriptor-signing-key-cert\n")
sb.WriteString(d.DescriptorSigningKeyCert)
sb.WriteByte('\n')
sb.WriteString("revision-counter ")
sb.WriteString(strconv.FormatInt(d.revisionCounter, 10))
sb.WriteByte('\n')
sb.WriteString("superencrypted\n")
sb.WriteString(d.superencrypted)
sb.WriteByte('\n')
sb.WriteString("signature ")
sb.WriteString(d.signature)
return sb.String()
}
func blindedPubkey(identityKey gobpk.PrivateKey, blindingNonce []byte) ed25519.PublicKey {
return util.BlindedPubkey(identityKey.Public(), blindingNonce)
}
func blindedSign(msg []byte, identityKey gobpk.PrivateKey, blindedKey, blindingNonce []byte) []byte {
if identityKey.IsPrivKeyInTorFormat() {
return util.BlindedSignWithTorKey(msg, identityKey.Seed(), blindedKey, blindingNonce)
} else {
return util.BlindedSign(msg, identityKey.Seed(), blindedKey, blindingNonce)
}
}
func HiddenServiceDescriptorV3Content(blindingNonce []byte, identityKey gobpk.PrivateKey,
descSigningKey ed25519.PrivateKey, innerLayer *InnerLayer, revCounter *int64) string {
if innerLayer == nil {
tmp := InnerLayerCreate(nil)
innerLayer = &tmp
}
if descSigningKey == nil {
_, descSigningKey, _ = ed25519.GenerateKey(brand.Reader())
}
if revCounter == nil {
tmp := btime.Clock.Now().Unix()
revCounter = &tmp
}
blindedKey := blindedPubkey(identityKey, blindingNonce)
//if blinding_nonce != nil {
// blindedKey = onionbalance.BlindedPubkey(identityKey, blinding_nonce)
//}
pub := identityKey.Public()
subcredential := subcredential(pub, blindedKey)
//if outerLayer == nil {
outerLayer := OuterLayerCreate(innerLayer, revCounter, subcredential, blindedKey)
//}
// if {
signingCert := getSigningCert(blindedKey, descSigningKey, identityKey, blindingNonce)
// }
descContent := "hs-descriptor 3\n"
descContent += fmt.Sprintf("descriptor-lifetime %d\n", 180)
descContent += "descriptor-signing-key-cert\n"
descContent += signingCert.ToBase64() + "\n"
descContent += fmt.Sprintf("revision-counter %d\n", *revCounter)
descContent += "superencrypted\n"
descContent += outerLayer.encrypt(*revCounter, subcredential, blindedKey) + "\n"
sigContent := SigPrefixHsV3 + descContent
sig := ed25519.Sign(descSigningKey, []byte(sigContent))
descContent += fmt.Sprintf("signature %s", strings.TrimRight(base64.StdEncoding.EncodeToString(sig), "="))
return descContent
}
func priv2Pem(pk ed25519.PrivateKey) string {
var identityKeyPem bytes.Buffer
identityKeyBytes, _ := x509.MarshalPKCS8PrivateKey(pk)
block := &pem.Block{Type: "PRIVATE KEY", Bytes: identityKeyBytes}
_ = pem.Encode(&identityKeyPem, block)
return identityKeyPem.String()
}
func getSigningCert(blindedKey ed25519.PublicKey, descSigningKey ed25519.PrivateKey, identityKey gobpk.PrivateKey, blindingNonce []byte) Ed25519CertificateV1 {
extensions := []Ed25519Extension{NewEd25519Extension(HasSigningKey, 0, blindedKey)}
signingCert := NewEd25519CertificateV1(HsV3DescSigning, nil, 1, descSigningKey.Public().(ed25519.PublicKey), extensions, nil, nil)
signingCert.Signature = blindedSign(signingCert.pack(), identityKey, blindedKey, blindingNonce)
return signingCert
}
const SigPrefixHsV3 = "Tor onion service descriptor sig v3"
func HiddenServiceDescriptorV3Create(blindingNonce []byte, identityPrivKey gobpk.PrivateKey, descSigningKey ed25519.PrivateKey, v3DescInnerLayer InnerLayer, revCounter int64) *HiddenServiceDescriptorV3 {
return NewHiddenServiceDescriptorV3(HiddenServiceDescriptorV3Content(blindingNonce, identityPrivKey, descSigningKey, &v3DescInnerLayer, &revCounter))
}
func NewHiddenServiceDescriptorV3(rawContents string) *HiddenServiceDescriptorV3 {
d := &HiddenServiceDescriptorV3{}
d.rawContents = rawContents
d.Descriptor.FromStr(rawContents)
d.SigningCert = Ed25519CertificateFromBase64(d.DescriptorSigningKeyCert)
//lines := strings.Split(rawContents, "\n")
//startCert := false
//startSuperencrypted := false
//for idx, line := range lines {
// if idx == 0 {
// d.HsDescriptorVersion, _ = strconv.ParseInt(strings.TrimPrefix(line, "hs-descriptor "), 10, 64)
// continue
// } else if idx == 1 {
// d.descriptorLifetime, _ = strconv.ParseInt(strings.TrimPrefix(line, "descriptor-lifetime "), 10, 64)
// continue
// } else if line == "descriptor-signing-key-cert" {
// startCert = true
// continue
// } else if line == "superencrypted" {
// startSuperencrypted = true
// continue
// } else if strings.HasPrefix(line, "revision-counter ") {
// d.revisionCounter, _ = strconv.ParseInt(strings.TrimPrefix(line, "revision-counter "), 10, 64)
// continue
// } else if strings.HasPrefix(line, "signature ") {
// d.signature = strings.TrimPrefix(line, "signature ")
// continue
// }
// if startCert {
// d.DescriptorSigningKeyCert += line + "\n"
// if line == "-----END ED25519 CERT-----" {
// startCert = false
// d.DescriptorSigningKeyCert = strings.TrimSpace(d.DescriptorSigningKeyCert)
// }
// } else if startSuperencrypted {
// d.superencrypted += line + "\n"
// if line == "-----END MESSAGE-----" {
// startSuperencrypted = false
// d.superencrypted = strings.TrimSpace(d.superencrypted)
// }
// }
//}
// TODO - n0tr1v
return d
}
func (d *HiddenServiceDescriptorV3) Decrypt(onionAddress string) (i *InnerLayer, err error) {
if d.InnerLayer == nil {
descriptorSigningKeyCert := d.DescriptorSigningKeyCert
cert := Ed25519CertificateFromBase64(descriptorSigningKeyCert)
blindedKey := cert.SigningKey()
if blindedKey == nil {
return d.InnerLayer, errors.New("no signing key is present")
}
identityPublicKey := IdentityKeyFromAddress(onionAddress)
subcredential := subcredential(identityPublicKey, blindedKey)
outerLayer := outerLayerDecrypt(d.superencrypted, d.revisionCounter, subcredential, blindedKey)
tmp := innerLayerDecrypt(outerLayer, d.revisionCounter, subcredential, blindedKey)
d.InnerLayer = &tmp
}
return d.InnerLayer, nil
}
type InnerLayer struct {
outer OuterLayer
IntroductionPoints []IntroductionPointV3
unparsedIntroductionPoints string
rawContents string
}
func (l InnerLayer) encrypt(revisionCounter int64, subcredential, blindedKey []byte) string {
// encrypt back into an outer layer's 'encrypted' field
return encryptLayer(l.getBytes(), "hsdir-encrypted-data", revisionCounter, subcredential, blindedKey)
}
func (l InnerLayer) getBytes() []byte {
return []byte(l.rawContents)
}
func InnerLayerContent(introductionPoints []IntroductionPointV3) string {
var sb strings.Builder
sb.WriteString("create2-formats 2")
if introductionPoints != nil {
for _, ip := range introductionPoints {
sb.WriteByte('\n')
sb.WriteString(ip.encode())
}
}
return sb.String()
}
func InnerLayerCreate(introductionPoints []IntroductionPointV3) InnerLayer {
return NewInnerLayer(InnerLayerContent(introductionPoints), OuterLayer{})
}
func NewInnerLayer(content string, outerLayer OuterLayer) InnerLayer {
l := InnerLayer{}
l.rawContents = content
l.outer = outerLayer
div := strings.Index(content, "\nintroduction-point ")
if div != -1 {
l.unparsedIntroductionPoints = content[div+1:]
content = content[:div]
} else {
l.unparsedIntroductionPoints = ""
}
//entries := descriptor_components(content, validate)
l.parseV3IntroductionPoints()
return l
}
type IntroductionPointV3 struct {
LinkSpecifiers []LinkSpecifier
OnionKey string
EncKey string
AuthKeyCertRaw string
EncKeyCertRaw string
AuthKeyCert Ed25519CertificateV1
EncKeyCert Ed25519CertificateV1
LegacyKeyRaw any
}
func (i IntroductionPointV3) Equals(other IntroductionPointV3) bool {
return i.encode() == other.encode()
}
// Descriptor representation of this introduction point.
func (i IntroductionPointV3) encode() string {
var sb strings.Builder
linkCount := uint8(len(i.LinkSpecifiers))
linkSpecifiers := []byte{linkCount}
for _, ls := range i.LinkSpecifiers {
linkSpecifiers = append(linkSpecifiers, ls.pack()...)
}
sb.WriteString("introduction-point ")
sb.WriteString(base64.StdEncoding.EncodeToString(linkSpecifiers))
sb.WriteString("\n")
sb.WriteString("onion-key ntor ")
sb.WriteString(i.OnionKey)
sb.WriteString("\n")
sb.WriteString("auth-key\n")
sb.WriteString(i.AuthKeyCertRaw)
sb.WriteString("\n")
if i.EncKey != "" {
sb.WriteString("enc-key ntor ")
sb.WriteString(i.EncKey)
sb.WriteString("\n")
}
sb.WriteString("enc-key-cert\n")
sb.WriteString(i.EncKeyCertRaw)
return sb.String()
}
/**
// Descriptor representation of this introduction point.
func (i IntroductionPointV3) encode() string {
out := strings.Builder{}
linkCount := uint8(len(i.LinkSpecifiers))
linkSpecifiers := []byte{linkCount}
for _, ls := range i.LinkSpecifiers {
linkSpecifiers = append(linkSpecifiers, ls.pack()...)
}
out.WriteString(fmt.Sprintf("introduction-point %s\n", base64.StdEncoding.EncodeToString(linkSpecifiers)))
out.WriteString(fmt.Sprintf("onion-key ntor %s\n", i.OnionKey))
out.WriteString(fmt.Sprintf("auth-key\n%s\n", i.AuthKeyCertRaw))
if i.EncKey != "" {
out.WriteString(fmt.Sprintf("enc-key ntor %s\n", i.EncKey))
}
out.WriteString(fmt.Sprintf("enc-key-cert\n%s", i.EncKeyCertRaw))
return out.String()
}
*/
func parseLinkSpecifier(content string) []LinkSpecifier {
decoded, err := base64.StdEncoding.DecodeString(content)
if err != nil {
logrus.Panicf("Unable to base64 decode introduction point (%v): %s", err, content)
}
content = string(decoded)
linkSpecifiers := make([]LinkSpecifier, 0)
count, content := content[0], content[1:]
for i := 0; i < int(count); i++ {
var linkSpecifier LinkSpecifier
linkSpecifier, content = linkSpecifierPop(content)
linkSpecifiers = append(linkSpecifiers, linkSpecifier)
}
if len(content) > 0 {
logrus.Panicf("Introduction point had excessive data (%s)", content)
}
return linkSpecifiers
}
type LinkSpecifier struct {
Typ uint8
Value []byte
}
func (l LinkSpecifier) String() string {
return fmt.Sprintf("T:%d,V:%x", l.Typ, l.Value)
}
func (l LinkSpecifier) pack() (out []byte) {
out = append(out, l.Typ)
out = append(out, uint8(len(l.Value)))
out = append(out, l.Value...)
return
}
func linkSpecifierPop(packed string) (LinkSpecifier, string) {
linkType, packed := packed[0], packed[1:]
valueSize, packed := packed[0], packed[1:]
if int(valueSize) > len(packed) {
logrus.Panicf("Link specifier should have %d bytes, but only had %d remaining", valueSize, len(packed))
}
value, packed := packed[:valueSize], packed[valueSize:]
if linkType == 0 {
return LinkByIPv4Unpack(value).LinkSpecifier, packed
} else if linkType == 1 {
return LinkByIPv6Unpack(value).LinkSpecifier, packed
} else if linkType == 2 {
return NewLinkByFingerprint([]byte(value)).LinkSpecifier, packed
} else if linkType == 3 {
return NewLinkByEd25519([]byte(value)).LinkSpecifier, packed
}
return LinkSpecifier{Typ: linkType, Value: []byte(value)}, packed // unrecognized type
}
type LinkByIPv4 struct {
LinkSpecifier
Address string
Port uint16
}
func NewLinkByIPv4(address string, port uint16) LinkByIPv4 {
portBytes := make([]byte, 2)
binary.BigEndian.PutUint16(portBytes, port)
l := LinkByIPv4{}
l.Typ = 0
l.Value = append(packIPV4Address(address), portBytes...)
l.Address = address
l.Port = port
return l
}
func LinkByIPv4Unpack(value string) LinkByIPv4 {
if len(value) != 6 {
logrus.Panicf("IPv4 link specifiers should be six bytes, but was %d instead: %x", len(value), value)
}
addr, portRaw := value[:4], value[4:]
port := binary.BigEndian.Uint16([]byte(portRaw))
return NewLinkByIPv4(unpackIPV4Address([]byte(addr)), port)
}
func NewLinkByIPv6(address string, port uint16) LinkByIPv6 {
portBytes := make([]byte, 2)
binary.BigEndian.PutUint16(portBytes, port)
l := LinkByIPv6{}
l.Typ = 1
l.Value = append(packIPV6Address(address), portBytes...)
l.Address = address
l.Port = port
return l
}
func LinkByIPv6Unpack(value string) LinkByIPv6 {
if len(value) != 18 {
logrus.Panicf("IPv6 link specifiers should be eighteen bytes, but was %d instead: %x", len(value), value)
}
addr, portRaw := value[:16], value[16:]
port := binary.BigEndian.Uint16([]byte(portRaw))
return NewLinkByIPv6(unpackIPV6Address([]byte(addr)), port)
}
func packIPV4Address(address string) (out []byte) {
parts := strings.Split(address, ".")
for _, part := range parts {
tmp, _ := strconv.ParseUint(part, 10, 8)
out = append(out, uint8(tmp))
}
return
}
func unpackIPV4Address(value []byte) string {
strs := make([]string, 0)
for i := 0; i < 4; i++ {
strs = append(strs, fmt.Sprintf("%d", value[i]))
}
return strings.Join(strs, ".")
}
func packIPV6Address(address string) (out []byte) {
parts := strings.Split(address, ":")
for _, part := range parts {
tmp, _ := hex.DecodeString(part)
out = append(out, tmp...)
}
return
}
func unpackIPV6Address(value []byte) string {
strs := make([]string, 0)
for i := 0; i < 8; i++ {
strs = append(strs, fmt.Sprintf("%04x", value[i*2:(i+1)*2]))
}
return strings.Join(strs, ":")
}
type LinkByIPv6 struct {
LinkSpecifier
Address string
Port uint16
}
type LinkByFingerprint struct {
LinkSpecifier
Fingerprint []byte
}
type LinkByEd25519 struct {
LinkSpecifier
Fingerprint []byte
}
func NewLinkByFingerprint(value []byte) LinkByFingerprint {
if len(value) != 20 {
logrus.Panicf("Fingerprint link specifiers should be twenty bytes, but was %d instead: %x", len(value), value)
}
l := LinkByFingerprint{}
l.Typ = 2
l.Value = value
l.Fingerprint = value
return l
}
func NewLinkByEd25519(value []byte) LinkByEd25519 {
if len(value) != 32 {
logrus.Panicf("Fingerprint link specifiers should be thirty two bytes, but was %d instead: %x", len(value), value)
}
l := LinkByEd25519{}
l.Typ = 3
l.Value = value
l.Fingerprint = value
return l
}
func introductionPointV3Parse(content string) IntroductionPointV3 {
ip := IntroductionPointV3{}
authKeyCertContent := ""
encKeyCertContent := ""
lines := strings.Split(content, "\n")
startAuthKey := false
startEncKeyCert := false
for _, line := range lines {
if line == "auth-key" {
startAuthKey = true
continue
} else if strings.HasPrefix(line, "introduction-point ") {
ip.LinkSpecifiers = parseLinkSpecifier(strings.TrimPrefix(line, "introduction-point "))
continue
} else if strings.HasPrefix(line, "onion-key ntor ") {
ip.OnionKey = strings.TrimPrefix(line, "onion-key ntor ")
continue
} else if strings.HasPrefix(line, "enc-key ntor ") {
ip.EncKey = strings.TrimPrefix(line, "enc-key ntor ")
continue
} else if line == "enc-key-cert" {
startEncKeyCert = true
continue
}
if startAuthKey {
authKeyCertContent += line + "\n"
if line == "-----END ED25519 CERT-----" {
startAuthKey = false
authKeyCertContent = strings.TrimSpace(authKeyCertContent)
}
}
if startEncKeyCert {
encKeyCertContent += line + "\n"
if line == "-----END ED25519 CERT-----" {
startEncKeyCert = false
encKeyCertContent = strings.TrimSpace(encKeyCertContent)
}
}
}
ip.AuthKeyCertRaw = authKeyCertContent
ip.EncKeyCertRaw = encKeyCertContent
ip.AuthKeyCert = Ed25519CertificateFromBase64(authKeyCertContent)
ip.EncKeyCert = Ed25519CertificateFromBase64(encKeyCertContent)
return ip
}
func (l *InnerLayer) parseV3IntroductionPoints() {
introductionPoints := make([]IntroductionPointV3, 0)
remaining := l.unparsedIntroductionPoints
for remaining != "" {
div := strings.Index(remaining, "\nintroduction-point ")
var content string
if div != -1 {
content = remaining[:div]
remaining = remaining[div+1:]
} else {
content = remaining
remaining = ""
}
introductionPoints = append(introductionPoints, introductionPointV3Parse(content))
}
l.IntroductionPoints = introductionPoints
}
func innerLayerDecrypt(outerLayer OuterLayer, revisionCounter int64, subcredential, blindedKey ed25519.PublicKey) InnerLayer {
plaintext := decryptLayer(outerLayer.encrypted, "hsdir-encrypted-data", revisionCounter, subcredential, blindedKey)
return NewInnerLayer(plaintext, outerLayer)
}
type OuterLayer struct {
encrypted string
rawContent string
}
func (l OuterLayer) encrypt(revisionCounter int64, subcredential, blindedKey []byte) string {
// Spec mandated padding: "Before encryption the plaintext is padded with
// NUL bytes to the nearest multiple of 10k bytes."
content := append(l.getBytes(), bytes.Repeat([]byte("\x00"), len(l.getBytes())%10000)...)
// encrypt back into a hidden service descriptor's 'superencrypted' field
return encryptLayer(content, "hsdir-superencrypted-data", revisionCounter, subcredential, blindedKey)
}
func encryptLayer(plaintext []byte, constant string, revisionCounter int64, subcredential, blindedKey []byte) string {
salt := make([]byte, 16)
_, _ = brand.Read(salt)
return encryptLayerDet(plaintext, constant, revisionCounter, subcredential, blindedKey, salt)
}
// Deterministic code for tests
func encryptLayerDet(plaintext []byte, constant string, revisionCounter int64, subcredential, blindedKey, salt []byte) string {
ciphr, macFor := layerCipher(constant, revisionCounter, subcredential, blindedKey, salt)
ciphertext := make([]byte, len(plaintext))
ciphr.XORKeyStream(ciphertext, plaintext)
encoded := base64.StdEncoding.EncodeToString([]byte(string(salt) + string(ciphertext) + string(macFor(ciphertext))))
splits := splitByLength(encoded, 64)
joined := strings.Join(splits, "\n")
return fmt.Sprintf("-----BEGIN MESSAGE-----\n%s\n-----END MESSAGE-----", joined)
}
func (l OuterLayer) getBytes() []byte {
return []byte(l.rawContent)
}
func OuterLayerCreate(innerLayer *InnerLayer, revisionCounter *int64, subcredential, blindedKey []byte) OuterLayer {
return NewOuterLayer(OuterLayerContent(innerLayer, revisionCounter, subcredential, blindedKey))
}
// AuthorizedClient Client authorized to use a v3 hidden service.
// id: base64 encoded client id
// iv: base64 encoded randomized initialization vector
// cookie: base64 encoded authentication cookie
type AuthorizedClient struct {
id string
iv string
cookie string
}
func NewAuthorizedClient() AuthorizedClient {
a := AuthorizedClient{}
idBytes := make([]byte, 8)
_, _ = brand.Read(idBytes)
a.id = strings.TrimRight(base64.StdEncoding.EncodeToString(idBytes), "=")
ivBytes := make([]byte, 16)
_, _ = brand.Read(ivBytes)
a.iv = strings.TrimRight(base64.StdEncoding.EncodeToString(ivBytes), "=")
cookieBytes := make([]byte, 16)
_, _ = brand.Read(cookieBytes)
a.cookie = strings.TrimRight(base64.StdEncoding.EncodeToString(cookieBytes), "=")
return a
}
func OuterLayerContent(innerLayer *InnerLayer, revisionCounter *int64, subcredential, blindedKey []byte) string {
if innerLayer == nil {
tmp := InnerLayerCreate(nil)
innerLayer = &tmp
}
authorizedClients := make([]AuthorizedClient, 0)
for i := 0; i < 16; i++ {
authorizedClients = append(authorizedClients, NewAuthorizedClient())
}
pk, _ := x25519.GenerateKey(brand.Reader())
out := "desc-auth-type x25519\n"
out += "desc-auth-ephemeral-key " + base64.StdEncoding.EncodeToString(pk.PublicKey.Bytes()) + "\n"
for _, c := range authorizedClients {
out += fmt.Sprintf("auth-client %s %s %s\n", c.id, c.iv, c.cookie)
}
out += "encrypted\n"
out += innerLayer.encrypt(*revisionCounter, subcredential, blindedKey)
return out
}
func NewOuterLayer(content string) OuterLayer {
l := OuterLayer{}
l.rawContent = content
encrypted := parseOuterLayer(content)
l.encrypted = encrypted
return l
}
func parseOuterLayer(content string) string {
out := ""
lines := strings.Split(content, "\n")
startEncrypted := false
for _, line := range lines {
if line == "encrypted" {
startEncrypted = true
continue
}
if startEncrypted {
out += line + "\n"
if line == "-----END MESSAGE-----" {
startEncrypted = false
out = strings.TrimSpace(out)
}
}
}
out = strings.ReplaceAll(out, "\r", "")
out = strings.ReplaceAll(out, "\x00", "")
return strings.TrimSpace(out)
}
func outerLayerDecrypt(encrypted string, revisionCounter int64, subcredential, blindedKey ed25519.PublicKey) OuterLayer {
plaintext := decryptLayer(encrypted, "hsdir-superencrypted-data", revisionCounter, subcredential, blindedKey)
return NewOuterLayer(plaintext)
}
func decryptLayer(encryptedBlock, constant string, revisionCounter int64, subcredential, blindedKey ed25519.PublicKey) string {
if strings.HasPrefix(encryptedBlock, "-----BEGIN MESSAGE-----\n") &&
strings.HasSuffix(encryptedBlock, "\n-----END MESSAGE-----") {
encryptedBlock = strings.TrimPrefix(encryptedBlock, "-----BEGIN MESSAGE-----\n")
encryptedBlock = strings.TrimSuffix(encryptedBlock, "\n-----END MESSAGE-----")
}
encrypted, err := base64.StdEncoding.DecodeString(encryptedBlock)
if err != nil {
panic("Unable to decode encrypted block as base64")
}
if len(encrypted) < SALT_LEN+MAC_LEN {
logrus.Panicf("Encrypted block malformed (only %d bytes)", len(encrypted))
}
salt := encrypted[:SALT_LEN]
ciphertext := encrypted[SALT_LEN : len(encrypted)-MAC_LEN]
expectedMac := encrypted[len(encrypted)-MAC_LEN:]
ciphr, macFor := layerCipher(constant, revisionCounter, subcredential, blindedKey, salt)
if !bytes.Equal(expectedMac, macFor(ciphertext)) {
logrus.Panicf("Malformed mac (expected %x, but was %x)", expectedMac, macFor(ciphertext))
}
plaintext := make([]byte, len(ciphertext))
ciphr.XORKeyStream(plaintext, ciphertext)
return string(plaintext)
}
func layerCipher(constant string, revisionCounter int64, subcredential []byte, blindedKey ed25519.PublicKey, salt []byte) (cipher.Stream, func([]byte) []byte) {
keys := make([]byte, S_KEY_LEN+S_IV_LEN+MAC_LEN)
data1 := make([]byte, 8)
binary.BigEndian.PutUint64(data1, uint64(revisionCounter))
data := []byte(string(blindedKey) + string(subcredential) + string(data1) + string(salt) + constant)
sha3.ShakeSum256(keys, data)
secretKey := keys[:S_KEY_LEN]
secretIv := keys[S_KEY_LEN : S_KEY_LEN+S_IV_LEN]
macKey := keys[S_KEY_LEN+S_IV_LEN:]
block, _ := aes.NewCipher(secretKey)
ciphr := cipher.NewCTR(block, secretIv)
//cipher = Cipher(algorithms.AES(secret_key), modes.CTR(secret_iv), default_backend())
data2 := make([]byte, 8)
binary.BigEndian.PutUint64(data2, uint64(len(macKey)))
data3 := make([]byte, 8)
binary.BigEndian.PutUint64(data3, uint64(len(salt)))
macPrefix := string(data2) + string(macKey) + string(data3) + string(salt)
fn := func(ciphertext []byte) []byte {
tmp := sha3.Sum256([]byte(macPrefix + string(ciphertext)))
return tmp[:]
}
return ciphr, fn
}
const S_KEY_LEN = 32
const S_IV_LEN = 16
const SALT_LEN = 16
const MAC_LEN = 32
// IdentityKeyFromAddress converts a hidden service address into its public identity key.
func IdentityKeyFromAddress(onionAddress string) ed25519.PublicKey {
if strings.HasSuffix(onionAddress, ".onion") {
onionAddress = strings.TrimSuffix(onionAddress, ".onion")
}
decodedAddress, _ := base32.StdEncoding.DecodeString(strings.ToUpper(onionAddress))
pubKey := decodedAddress[:32]
expectedChecksum := decodedAddress[32:34]
version := decodedAddress[34:35]
checksumTmp := sha3.Sum256([]byte(".onion checksum" + string(pubKey) + string(version)))
checksum := checksumTmp[:2]
if !bytes.Equal(expectedChecksum, checksum) {
logrus.Panicf("Bad checksum (expected %x but was %x)", expectedChecksum, checksum)
}
return pubKey
}
func AddressFromIdentityKey(pub ed25519.PublicKey) string {
var checksumBytes bytes.Buffer
checksumBytes.Write([]byte(".onion checksum"))
checksumBytes.Write(pub)
checksumBytes.Write([]byte{0x03})
checksum := sha3.Sum256(checksumBytes.Bytes())
var onionAddressBytes bytes.Buffer
onionAddressBytes.Write(pub)
onionAddressBytes.Write(checksum[:2])
onionAddressBytes.Write([]byte{0x03})
addr := strings.ToLower(base32.StdEncoding.EncodeToString(onionAddressBytes.Bytes()))
return addr + ".onion"
}
func subcredential(identityKey, blindedKey ed25519.PublicKey) []byte {
// credential = H('credential' | public - identity - key)
// subcredential = H('subcredential' | credential | blinded - public - key)
credential := sha3.Sum256([]byte("credential" + string(identityKey)))
sub := sha3.Sum256([]byte("subcredential" + string(credential[:]) + string(blindedKey)))
return sub[:]
}