EndGame v3

This commit is contained in:
Aksh 2024-10-23 20:50:14 +05:30
commit 9e36ba54ee
646 changed files with 271674 additions and 0 deletions

View file

@ -0,0 +1,262 @@
package descriptor
import (
"crypto/ed25519"
"encoding/base64"
"encoding/binary"
"fmt"
"github.com/sirupsen/logrus"
"gobalance/pkg/btime"
"strings"
"time"
)
type Ed25519Certificate struct {
Version uint8
}
// Ed25519CertificateV1 version 1 Ed25519 certificate, which sign tor server and hidden service v3
// descriptors.
type Ed25519CertificateV1 struct {
Ed25519Certificate
Typ uint8
typInt int64
Expiration time.Time
KeyType uint8
Key ed25519.PublicKey
Extensions []Ed25519Extension
Signature []byte
}
func (c Ed25519CertificateV1) pack() (out []byte) {
out = append(out, c.Version)
out = append(out, c.Typ)
expiration := c.Expiration.Unix() / 3600
expirationBytes := make([]byte, 4)
binary.BigEndian.PutUint32(expirationBytes, uint32(expiration))
out = append(out, expirationBytes...)
out = append(out, c.KeyType)
out = append(out, c.Key...)
out = append(out, uint8(len(c.Extensions)))
for _, ext := range c.Extensions {
out = append(out, ext.Pack()...)
}
if c.Signature != nil {
out = append(out, c.Signature...)
}
return
}
// ToBase64 Base64 encoded certificate data.
func (c Ed25519CertificateV1) ToBase64() (out string) {
b64 := strings.Join(splitByLength(base64.StdEncoding.EncodeToString(c.pack()), 64), "\n")
out = fmt.Sprintf("-----BEGIN ED25519 CERT-----\n%s\n-----END ED25519 CERT-----", b64)
return out
}
func splitByLength(msg string, size int) (out []string) {
for i := 0; i < len(msg); i += size {
upper := i + size
if i+size > len(msg) {
upper = len(msg)
}
out = append(out, msg[i:upper])
}
return
}
const DefaultExpirationHours = 54 // HSv3 certificate expiration of tor
func NewEd25519CertificateV1(certType uint8, expiration *time.Time, keyType uint8, key ed25519.PublicKey,
extensions []Ed25519Extension, signingKey ed25519.PrivateKey, signature []byte) Ed25519CertificateV1 {
c := Ed25519CertificateV1{}
c.Version = 1
if certType == 0 {
panic("Certificate type is required")
} else if key == nil {
panic("Certificate key is required")
}
if certType == 8 {
c.Typ, c.typInt = HsV3DescSigning, 8
} else if certType == 9 {
c.Typ, c.typInt = HsV3IntroAuth, 9
} else if certType == 11 {
c.Typ, c.typInt = HsV3NtorEnc, 11
} else {
panic("unknown cert type")
}
if expiration == nil {
c.Expiration = btime.Clock.Now().UTC().Add(DefaultExpirationHours * time.Hour)
} else {
c.Expiration = expiration.UTC()
}
c.KeyType = keyType
c.Key = key
c.Extensions = extensions
c.Signature = signature
if signingKey != nil {
calculatedSig := ed25519.Sign(signingKey, c.pack())
/*
// if caller provides both signing key *and* signature then ensure they match
if self.signature and self.signature != calculated_sig:
raise ValueError("Signature calculated from its key (%s) mismatches '%s'" % (calculated_sig, self.signature))
*/
c.Signature = calculatedSig
}
if c.Typ == LINK || c.Typ == IDENTITY || c.Typ == AUTHENTICATE {
logrus.Panicf("Ed25519 certificate cannot have a type of %d. This is reserved for CERTS cells.", c.typInt)
} else if c.Typ == ED25519_IDENTITY {
panic("Ed25519 certificate cannot have a type of 7. This is reserved for RSA identity cross-certification.")
} else if c.Typ == 0 {
logrus.Panicf("Ed25519 certificate type %d is unrecognized", c.typInt)
}
return c
}
func (c *Ed25519CertificateV1) SigningKey() ed25519.PublicKey {
for _, ext := range c.Extensions {
if ext.Typ == HasSigningKey {
return ext.Data
}
}
return nil
}
const (
LINK = iota + 1
IDENTITY
AUTHENTICATE
ED25519_SIGNING
LINK_CERT
ED25519_AUTHENTICATE
ED25519_IDENTITY
HsV3DescSigning
HsV3IntroAuth
NTOR_ONION_KEY
HsV3NtorEnc
)
// Ed25519CertificateV1Unpack parses a byte encoded ED25519 certificate.
func Ed25519CertificateV1Unpack(content []byte) Ed25519CertificateV1 {
if len(content) == 0 {
logrus.Panicf("Failed to unpack ed25519 certificate")
}
version := content[0]
if version != 1 {
logrus.Panicf("Ed25519 certificate is version %c. Parser presently only supports version 1.", version)
}
return ed25519CertificateV1Unpack(content)
}
// Ed25519CertificateFromBase64 parses a base64 encoded ED25519 certificate.
func Ed25519CertificateFromBase64(content string) Ed25519CertificateV1 {
if strings.HasPrefix(content, "-----BEGIN ED25519 CERT-----\n") &&
strings.HasSuffix(content, "\n-----END ED25519 CERT-----") {
content = strings.TrimPrefix(content, "-----BEGIN ED25519 CERT-----\n")
content = strings.TrimSuffix(content, "\n-----END ED25519 CERT-----")
}
by, err := base64.StdEncoding.DecodeString(content)
if err != nil {
panic(err)
} else if len(by) == 0 {
return Ed25519CertificateV1{}
}
return Ed25519CertificateV1Unpack(by)
}
const (
Ed25519KeyLength = 32
Ed25519HeaderLength = 40
Ed25519SignatureLength = 64
)
func ed25519CertificateV1Unpack(content []byte) Ed25519CertificateV1 {
if len(content) < Ed25519HeaderLength+Ed25519SignatureLength {
logrus.Panicf("Ed25519 certificate was %d bytes, but should be at least %d", len(content), Ed25519HeaderLength+Ed25519SignatureLength)
}
header, signature := content[:len(content)-Ed25519SignatureLength], content[len(content)-Ed25519SignatureLength:]
version, header := header[0], header[1:]
certType, header := header[0], header[1:]
expirationHoursRaw, header := header[:4], header[4:]
var expirationHours int64
expirationHours |= int64(expirationHoursRaw[0]) << 24
expirationHours |= int64(expirationHoursRaw[1]) << 16
expirationHours |= int64(expirationHoursRaw[2]) << 8
expirationHours |= int64(expirationHoursRaw[3])
keyType, header := header[0], header[1:]
key, header := header[:Ed25519KeyLength], header[Ed25519KeyLength:]
extensionCount, extensionData := header[0], header[1:]
if version != 1 {
logrus.Panicf("Ed25519 v1 parser cannot read version %c certificates", version)
}
extensions := make([]Ed25519Extension, 0)
for i := 0; i < int(extensionCount); i++ {
var extension Ed25519Extension
extension, extensionData = Ed25519ExtensionPop(extensionData)
extensions = append(extensions, extension)
}
if len(extensionData) > 0 {
logrus.Panicf("Ed25519 certificate had %d bytes of unused extension data", len(extensionData))
}
expiration := time.Unix(int64(expirationHours)*3600, 0)
return NewEd25519CertificateV1(certType,
&expiration,
keyType, key, extensions, nil, signature)
}
type Ed25519Extension struct {
Typ uint8
Flags []string
FlagInt uint8
Data []byte
}
func NewEd25519Extension(extType, flagVal uint8, data []byte) Ed25519Extension {
e := Ed25519Extension{}
e.Typ = extType
e.Flags = make([]string, 0)
e.FlagInt = flagVal
e.Data = data
if flagVal > 0 && flagVal%2 == 1 {
e.Flags = append(e.Flags, "AFFECTS_VALIDATION")
flagVal -= 1
}
if flagVal > 0 {
e.Flags = append(e.Flags, "UNKNOWN")
}
if extType == HasSigningKey && len(data) != 32 {
logrus.Panicf("Ed25519 HAS_SIGNING_KEY extension must be 32 bytes, but was %d.", len(data))
}
return e
}
func Ed25519ExtensionPop(content []byte) (Ed25519Extension, []byte) {
if len(content) < 4 {
panic("Ed25519 extension is missing header fields")
}
dataSizeRaw, content := content[:2], content[2:]
var dataSize int64
dataSize |= int64(dataSizeRaw[0]) << 8
dataSize |= int64(dataSizeRaw[1])
extType, content := content[0], content[1:]
flags, content := content[0], content[1:]
data, content := content[:dataSize], content[dataSize:]
if int64(len(data)) != dataSize {
logrus.Panicf("Ed25519 extension is truncated. It should have %d bytes of data but there's only %d.", dataSize, len(data))
}
return NewEd25519Extension(extType, flags, data), content
}
func (e Ed25519Extension) Pack() (out []byte) {
dataSizeBytes := make([]byte, 2)
binary.BigEndian.PutUint16(dataSizeBytes, uint16(len(e.Data)))
out = append(out, dataSizeBytes...)
out = append(out, e.Typ)
out = append(out, e.FlagInt)
out = append(out, e.Data...)
return
}

View file

@ -0,0 +1,34 @@
package descriptor
import (
"encoding/base64"
"github.com/stretchr/testify/assert"
"testing"
)
func TestEd25519CertificateToBase64(t *testing.T) {
certRaw := `-----BEGIN ED25519 CERT-----
AQkABvnvASpbRl8c5Iwx+KYXIGHMA+66ZN88TppVrRqrwyZkv45UAQAgBABcfN7F
QCPKVVMMIsn/OMg/XEQjOhfiqBB7DDU36l7dR+vl8qUr8ApIEPse2nAPmz8EscmY
25grvptE/1o0mS1ynpEPmeFrGbUCVyWsntwLyn77bscvNdG8Mozov3bGFQU=
-----END ED25519 CERT-----`
cert := Ed25519CertificateFromBase64(certRaw)
newCert := cert.ToBase64()
assert.Equal(t, certRaw, newCert)
}
func TestEd25519CertificateV1Pack(t *testing.T) {
raw := "AQgABvnxAVx83sVAI8pVUwwiyf84yD9cRCM6F+KoEHsMNTfqXt1HAQAgBAB0tYzO/dvRZRujduw/KKmyulEhsEvjhVbhZ4ALCYkMgBpLO+hsNQqVdbTWvm5FrMZcyuCP4451WdpYlgOlsG8Mu3goFEM8B2KWQdzVpI69oq61geN5yzwnhO7zH/o1qwo="
by1, _ := base64.StdEncoding.DecodeString(raw)
cert := ed25519CertificateV1Unpack(by1)
by2 := cert.pack()
assert.Equal(t, by1, by2)
}
func TestEd25519ExtensionPack(t *testing.T) {
raw := "ACAEAHS1jM7929FlG6N27D8oqbK6USGwS+OFVuFngAsJiQyA"
by1, _ := base64.StdEncoding.DecodeString(raw)
ext, _ := Ed25519ExtensionPop(by1)
by2 := ext.Pack()
assert.Equal(t, by1, by2)
}

View file

@ -0,0 +1,832 @@
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[:]
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,343 @@
package util
import (
"bytes"
"crypto/ed25519"
"crypto/sha512"
"fmt"
"math/big"
)
var b = 256
var d = bi(0).Mul(bi(-121665), inv(bi(121666)))
var d1 = biMod(biMul(bi(-121665), inv(bi(121666))), q)
var I = expmod(bi(2), biDiv(biSub(q, bi(1)), bi(4)), q)
var q = biSub(biExp(bi(2), bi(255)), bi(19))
var by = biMul(bi(4), inv(bi(5)))
var bx = xrecover(by)
var bB = []*big.Int{biMod(bx, q), biMod(by, q)}
var bB1 = []*big.Int{biMod(bx, q), biMod(by, q), bi(1), biMod(biMul(bx, by), q)}
var l = biAdd(biExp(bi(2), bi(252)), biFromStr("27742317777372353535851937790883648493"))
func biFromStr(v string) (out *big.Int) {
out = new(big.Int)
_, _ = fmt.Sscan(v, out)
return
}
// BlindedSignWithTorKey this is identical to stem's hidden_service.py:_blinded_sign() but takes an
// extended private key (i.e. in tor format) as its argument, instead of the
// standard format that hazmat does. It basically omits the "extended the key"
// step and does everything else the same.
func BlindedSignWithTorKey(msg []byte, identityKey ed25519.PrivateKey, blindedKey, blindingNonce []byte) []byte {
esk := identityKey.Seed()
return blindedSignP2(esk, msg, blindedKey, blindingNonce)
}
func BlindedSign(msg, identityKey, blindedKey, blindingNonce []byte) []byte {
identityKeyBytes := identityKey
// pad private identity key into an ESK (encrypted secret key)
tmp := sha512.Sum512(identityKeyBytes)
h := tmp[:]
sum := bi(0)
for i := int64(3); i < int64(b)-2; i++ {
sum = biAdd(sum, biMul(biExp(bi(2), bi(i)), bi(int64(Bit(h, i)))))
}
a := biAdd(biExp(bi(2), bi(int64(b-2))), sum)
tmpS := make([][]byte, 0)
for i := b / 8; i < b/4; i++ {
tmpS = append(tmpS, h[i:i+1])
}
k := bytes.Join(tmpS, []byte(""))
esk := append(encodeint(a), k...)
return blindedSignP2(esk, msg, blindedKey, blindingNonce)
}
func blindedSignP2(esk, msg, blindedKey, blindingNonce []byte) []byte {
// blind the ESK with this nonce
sum := bi(0)
for i := int64(3); i < int64(b-2); i++ {
bitRes := bi(int64(Bit(blindingNonce, i)))
sum = biAdd(sum, biMul(biExp(bi(2), bi(i)), bitRes))
}
mult := biAdd(biExp(bi(2), bi(int64(b-2))), sum)
s := decodeInt(esk[:32])
sPrime := biMod(biMul(s, mult), l)
k := esk[32:]
tmp := sha512.Sum512([]byte("Derive temporary signing key hash input" + string(k)))
kPrime := tmp[:32]
blindedEsk := append(encodeint(sPrime), kPrime...)
// finally, sign the message
a := decodeInt(blindedEsk[:32])
lines := make([][]byte, 0)
for i := b / 8; i < b/4; i++ {
lines = append(lines, blindedEsk[i:i+1])
}
toHint := append(bytes.Join(lines, []byte("")), msg...)
r := hint(toHint)
R := Scalarmult1(bB1, r)
S := biMod(biAdd(r, biMul(hint([]byte(string(Encodepoint(R))+string(blindedKey)+string(msg))), a)), l)
return append(Encodepoint(R), encodeint(S)...)
}
func hint(m []byte) *big.Int {
tmp := sha512.Sum512(m)
h := tmp[:]
sum := bi(0)
for i := 0; i < 2*b; i++ {
sum = biAdd(sum, biMul(biExp(bi(2), bi(int64(i))), bi(int64(Bit(h, int64(i))))))
}
return sum
}
//def Hint(m):
//h = H(m)
//return sum(2 ** i * bit(h, i) for i in range(2 * b))
func BlindedPubkey(identityKey ed25519.PublicKey, blindingNonce []byte) ed25519.PublicKey {
ed25519b := int64(256)
sum := bi(0)
for i := int64(3); i < ed25519b-2; i++ {
sum = biAdd(sum, biMul(biExp(bi(2), bi(i)), bi(int64(Bit(blindingNonce, i)))))
}
mult := biAdd(biExp(bi(2), bi(ed25519b-2)), sum)
P := Decodepoint(identityKey)
return Encodepoint(Scalarmult1(P, mult))
}
func Decodepoint(s []byte) []*big.Int {
sum := bi(0)
for i := 0; i < b-1; i++ {
sum = biAdd(sum, biMul(biExp(bi(2), bi(int64(i))), bi(int64(Bit(s, int64(i))))))
}
y := sum
x := xrecover(y)
if biAnd(x, bi(1)).Cmp(bi(int64(Bit(s, int64(b-1))))) != 0 {
x = biSub(q, x)
}
P := []*big.Int{x, y, bi(1), biMod(biMul(x, y), q)}
if !isoncurve(P) {
panic("decoding point that is not on curve")
}
return P
}
func decodeInt(s []uint8) *big.Int {
sum := bi(0)
for i := 0; i < 256; i++ {
tmpI := bi(int64(i))
base := bi(2)
e := bi(0).Exp(base, tmpI, nil)
m := bi(int64(Bit(s, int64(i))))
tmp := bi(0).Mul(e, m)
sum = sum.Add(sum, tmp)
}
return sum
}
func encodeint(y *big.Int) []byte {
bits := make([]*big.Int, 0)
for i := 0; i < b; i++ {
bits = append(bits, biAnd(biRsh(y, uint(i)), bi(1)))
}
final := make([]byte, 0)
for i := 0; i < b/8; i++ {
sum := bi(0)
for j := 0; j < 8; j++ {
sum = biAdd(sum, biLsh(bits[i*8+j], uint(j)))
}
final = append(final, byte(sum.Uint64()))
}
return final
}
func xrecover(y *big.Int) *big.Int {
xx := biMul(biSub(biMul(y, y), bi(1)), inv(biAdd(biMul(biMul(d, y), y), bi(1))))
x := expmod(xx, biDiv(biAdd(q, bi(3)), bi(8)), q)
if biMod(biSub(biMul(x, x), xx), q).Int64() != 0 {
x = biMod(biMul(x, I), q)
}
if biMod(x, bi(2)).Int64() != 0 {
x = biSub(q, x)
}
return x
}
func expmod(b, e, m *big.Int) *big.Int {
if e.Cmp(bi(0)) == 0 {
return bi(1)
}
t := biMod(biExp(expmod(b, biDiv(e, bi(2)), m), bi(2)), m)
if biAnd(e, bi(1)).Int64() == 1 {
t = biMod(biMul(t, b), m)
}
return t
}
func Bit(h []uint8, i int64) uint8 {
return (h[i/8] >> (i % 8)) & 1
}
func inv(x *big.Int) *big.Int {
return expmod(x, biSub(q, bi(2)), q)
}
func isoncurve(P []*big.Int) bool {
var d = biMod(biMul(bi(-121665), inv(bi(121666))), q)
var q = biSub(biExp(bi(2), bi(255)), bi(19))
x := P[0]
y := P[1]
z := P[2]
t := P[3]
return biMod(z, q).Cmp(bi(0)) != 0 &&
biMod(biMul(x, y), q).Cmp(biMod(biMul(z, t), q)) == 0 &&
biMod(biSub(biSub(biSub(biMul(y, y), biMul(x, x)), biMul(z, z)), biMul(biMul(d, t), t)), q).Int64() == 0
}
func edwardsAdd(P, Q []*big.Int) []*big.Int {
// This is formula sequence 'addition-add-2008-hwcd-3' from
// http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
x1 := P[0]
y1 := P[1]
z1 := P[2]
t1 := P[3]
x2 := Q[0]
y2 := Q[1]
z2 := Q[2]
t2 := Q[3]
a := biMod(biMul(biSub(y1, x1), biSub(y2, x2)), q)
b := biMod(biMul(biAdd(y1, x1), biAdd(y2, x2)), q)
c := biMod(biMul(biMul(biMul(t1, bi(2)), d1), t2), q)
dd := biMod(biMul(biMul(z1, bi(2)), z2), q)
e := biSub(b, a)
f := biSub(dd, c)
g := biAdd(dd, c)
h := biAdd(b, a)
x3 := biMul(e, f)
y3 := biMul(g, h)
t3 := biMul(e, h)
z3 := biMul(f, g)
return []*big.Int{biMod(x3, q), biMod(y3, q), biMod(z3, q), biMod(t3, q)}
}
func edwardsDouble(P []*big.Int) []*big.Int {
// This is formula sequence 'dbl-2008-hwcd' from
// http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
x1 := P[0]
y1 := P[1]
z1 := P[2]
a := biMod(biMul(x1, x1), q)
b := biMod(biMul(y1, y1), q)
c := biMod(biMul(biMul(bi(2), z1), z1), q)
e := biMod(biSub(biSub(biMul(biAdd(x1, y1), biAdd(x1, y1)), a), b), q)
g := biAdd(biMul(a, bi(-1)), b)
f := biSub(g, c)
h := biSub(biMul(a, bi(-1)), b)
x3 := biMul(e, f)
y3 := biMul(g, h)
t3 := biMul(e, h)
z3 := biMul(f, g)
return []*big.Int{biMod(x3, q), biMod(y3, q), biMod(z3, q), biMod(t3, q)}
}
func Scalarmult1(P []*big.Int, e *big.Int) []*big.Int {
if e.Cmp(bi(0)) == 0 {
return []*big.Int{bi(0), bi(1), bi(1), bi(0)}
}
Q := Scalarmult1(P, biDiv(e, bi(2)))
Q = edwardsDouble(Q)
if biAnd(e, bi(1)).Int64() == 1 {
//if e.And(e, bi(1)).Int64() == 1 {
Q = edwardsAdd(Q, P)
}
return Q
}
func Encodepoint(P []*big.Int) []byte {
x := P[0]
y := P[1]
z := P[2]
//t := P[3]
zi := inv(z)
x = biMod(biMul(x, zi), q)
y = biMod(biMul(y, zi), q)
bits := make([]uint8, 0)
for i := 0; i < b-1; i++ {
bits = append(bits, uint8(biAnd(biRsh(y, uint(i)), bi(1)).Int64()))
}
bits = append(bits, uint8(biAnd(x, bi(1)).Int64()))
by := make([]uint8, 0)
for i := 0; i < b/8; i++ {
sum := uint8(0)
for j := 0; j < 8; j++ {
sum += bits[i*8+j] << j
}
by = append(by, sum)
}
return by
}
//func Encodepoint(P []*big.Int) []byte {
// x := P[0]
// y := P[1]
// bits := make([]uint8, 0)
// for i := 0; i < b; i++ {
// bits = append(bits, uint8(biAnd(biRsh(y, uint(i)), bi(1)).Int64()))
// }
// by := make([]uint8, 0)
// bits = append(bits, uint8(biAnd(x, bi(1)).Int64()))
// for i := 0; i < b/8; i++ {
// sum := uint8(0)
// for j := 0; j < 8; j++ {
// sum += bits[i*8+j] << j
// }
// by = append(by, sum)
// }
// return by
//}
func bi(v int64) *big.Int {
return big.NewInt(v)
}
func biExp(a, b *big.Int) *big.Int {
return bi(0).Exp(a, b, nil)
}
func biDiv(a, b *big.Int) *big.Int {
return bi(0).Div(a, b)
}
func biSub(a, b *big.Int) *big.Int {
return bi(0).Sub(a, b)
}
func biAdd(a, b *big.Int) *big.Int {
return bi(0).Add(a, b)
}
func biAnd(a, b *big.Int) *big.Int {
return bi(0).And(a, b)
}
func biRsh(a *big.Int, b uint) *big.Int {
return bi(0).Rsh(a, b)
}
func biLsh(a *big.Int, b uint) *big.Int {
return bi(0).Lsh(a, b)
}
func biMul(a, b *big.Int) *big.Int {
return bi(0).Mul(a, b)
}
func biMod(a, b *big.Int) *big.Int {
return bi(0).Mod(a, b)
}

View file

@ -0,0 +1,25 @@
package util
import (
"crypto/ed25519"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"github.com/stretchr/testify/assert"
"testing"
)
func TestBlindedSign(t *testing.T) {
msg, _ := base64.StdEncoding.DecodeString(`AQgABvn+AUmtuF1+Nb/kJ67y1U0lI7HiDjRJwHHY+sQrHlBKomR3AQAgBAAtL5DBE1Moh7A+AGrzgWhcHOBo/W3lyhcLeip0LuI8Xw==`)
identityKeyPem := `-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIMjdAAyeb8pU3CzRK2z+yKSgWi0R33mfeAPpVnktRrwA
-----END PRIVATE KEY-----`
block, _ := pem.Decode([]byte(identityKeyPem))
key, _ := x509.ParsePKCS8PrivateKey(block.Bytes)
identityKey := key.(ed25519.PrivateKey)
blindedKey, _ := base64.StdEncoding.DecodeString(`LS+QwRNTKIewPgBq84FoXBzgaP1t5coXC3oqdC7iPF8=`)
blindingNonce, _ := base64.StdEncoding.DecodeString(`ljbKEFzZGbd3ZI29J67XTs6JV3Glp+uieQ5yORMhmdg=`)
expected := `xIrhGFs3VZKbV36zqCcudaWN0+K8s6zRRr5qki1uz/HjBL80SQ0HEirDp4DnNBAeYDIjNJwmrgQe6IU8ESHzDg==`
res := BlindedSign(msg, identityKey.Seed(), blindedKey, blindingNonce)
assert.Equal(t, expected, base64.StdEncoding.EncodeToString(res))
}