FILE / ScuroNeko/mtg

mtglib/secret.go

Исходный файл и его история в репозитории.
FILE 1f862027af98d3f6dd669746fc9ab02afb05f01f
Files
mtg/mtglib/secret.go
T

115 lines
2.0 KiB
Go

package mtglib
import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
"fmt"
"strings"
)
const (
SecretKeyLength = 16
secretFakeTLSFirstByte byte = 238
)
var secretEmptyKey [SecretKeyLength]byte
type Secret struct {
Key [SecretKeyLength]byte
Host string
}
func (s Secret) MarshalText() ([]byte, error) {
if s.Valid() {
return []byte(s.String()), nil
}
return nil, nil
}
func (s *Secret) UnmarshalText(data []byte) error {
text := string(data)
if text == "" {
return ErrSecretEmpty
}
var (
decoded []byte
err error
)
if strings.HasPrefix(text, "ee") {
decoded, err = hex.DecodeString(strings.TrimPrefix(text, "ee"))
}
if err != nil || len(decoded) <= SecretKeyLength {
decoded, err = base64.RawURLEncoding.DecodeString(text)
if err != nil {
return fmt.Errorf("incorrect secret format: %w", err)
}
if len(decoded) <= SecretKeyLength {
return fmt.Errorf("secret has incorrect length %d", len(text))
}
if decoded[0] != secretFakeTLSFirstByte {
return fmt.Errorf("incorrect first byte: %v", decoded[0])
}
decoded = decoded[1:]
}
copy(s.Key[:], decoded[:SecretKeyLength])
s.Host = string(decoded[SecretKeyLength:])
if s.Host == "" {
return fmt.Errorf("hostname cannot be empty: %s", text)
}
return nil
}
func (s Secret) Valid() bool {
return s.Key != secretEmptyKey && s.Host != ""
}
func (s Secret) String() string {
return s.Base64()
}
func (s Secret) Base64() string {
return base64.RawURLEncoding.EncodeToString(s.makeBytes())
}
func (s Secret) Hex() string {
return hex.EncodeToString(s.makeBytes())
}
func (s *Secret) makeBytes() []byte {
data := append([]byte{secretFakeTLSFirstByte}, s.Key[:]...)
data = append(data, s.Host...)
return data
}
func GenerateSecret(hostname string) Secret {
s := Secret{
Host: hostname,
}
if _, err := rand.Read(s.Key[:]); err != nil {
panic(err)
}
return s
}
func ParseSecret(secret string) (Secret, error) {
s := Secret{}
return s, s.UnmarshalText([]byte(secret))
}