mirror of
https://github.com/Egida/EndGame0.git
synced 2025-08-07 13:52:20 -04:00
EndGame v3
This commit is contained in:
commit
9e36ba54ee
646 changed files with 271674 additions and 0 deletions
262
sourcecode/gobalance/pkg/stem/descriptor/certificate.go
Normal file
262
sourcecode/gobalance/pkg/stem/descriptor/certificate.go
Normal 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
|
||||
}
|
34
sourcecode/gobalance/pkg/stem/descriptor/certificate_test.go
Normal file
34
sourcecode/gobalance/pkg/stem/descriptor/certificate_test.go
Normal 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)
|
||||
}
|
832
sourcecode/gobalance/pkg/stem/descriptor/hidden_service.go
Normal file
832
sourcecode/gobalance/pkg/stem/descriptor/hidden_service.go
Normal 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[:]
|
||||
}
|
1373
sourcecode/gobalance/pkg/stem/descriptor/hidden_service_test.go
Normal file
1373
sourcecode/gobalance/pkg/stem/descriptor/hidden_service_test.go
Normal file
File diff suppressed because one or more lines are too long
343
sourcecode/gobalance/pkg/stem/util/ed25519.go
Normal file
343
sourcecode/gobalance/pkg/stem/util/ed25519.go
Normal 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)
|
||||
}
|
25
sourcecode/gobalance/pkg/stem/util/ed25519_test.go
Normal file
25
sourcecode/gobalance/pkg/stem/util/ed25519_test.go
Normal 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))
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue