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

262 lines
7.8 KiB
Go

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
}