feat: establish NekoNest Cloud control and relay

This commit is contained in:
2026-08-12 23:25:43 +08:00
commit f27606b709
222 changed files with 71456 additions and 0 deletions
+305
View File
@@ -0,0 +1,305 @@
package authsnapshot
import (
"bytes"
"crypto/ed25519"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"sort"
"strconv"
"strings"
"time"
)
const (
SnapshotVersion = 1
MaxTTL = 5 * time.Minute
clockSkew = 30 * time.Second
signatureDomain = "nekonest-cloud/relay-authorization-snapshot/v1\n"
)
type Device struct {
DeviceID string `json:"device_id"`
CredentialHash string `json:"credential_hash"`
IdentityFingerprint string `json:"identity_fingerprint"`
Ed25519Public string `json:"ed25519_public"`
X25519Public string `json:"x25519_public"`
Name string `json:"name"`
OS string `json:"os"`
}
type Phone struct {
PhoneID string `json:"phone_id"`
CredentialHash string `json:"credential_hash"`
IdentityFingerprint string `json:"identity_fingerprint"`
Ed25519Public string `json:"ed25519_public"`
X25519Public string `json:"x25519_public"`
Name string `json:"name"`
}
type Payload struct {
SnapshotVersion int `json:"snapshot_version"`
TenantID string `json:"tenant_id"`
TenantStatus string `json:"tenant_status"`
HomeRegion string `json:"home_region"`
RelayNodeID string `json:"relay_node_id"`
PlacementGeneration int64 `json:"placement_generation"`
AuthorizationRevision int64 `json:"authorization_revision"`
Devices []Device `json:"devices"`
Phones []Phone `json:"phones,omitempty"`
IssuedAt string `json:"issued_at"`
ExpiresAt string `json:"expires_at"`
}
type Signed struct {
Algorithm string `json:"algorithm"`
KID string `json:"kid"`
Payload Payload `json:"payload"`
Signature string `json:"signature"`
}
type Key struct {
PublicKey ed25519.PublicKey
NotBefore time.Time
RetainUntil time.Time
}
type Keyring map[string]Key
func parseTime(value string) (time.Time, error) {
parsed, err := time.Parse(time.RFC3339Nano, value)
if err != nil {
return time.Time{}, fmt.Errorf("invalid snapshot time: %w", err)
}
return parsed, nil
}
func validPrefixedHex(value, prefix string, hexLength int) bool {
if !strings.HasPrefix(value, prefix) || len(value) != len(prefix)+hexLength {
return false
}
for _, char := range strings.TrimPrefix(value, prefix) {
if !strings.ContainsRune("0123456789abcdef", char) {
return false
}
}
return true
}
func validDigest(value string) bool {
if len(value) != 64 {
return false
}
for _, char := range value {
if !strings.ContainsRune("0123456789abcdef", char) {
return false
}
}
return true
}
func validPrefixedID(value, prefix string, maxSuffix int) bool {
if !strings.HasPrefix(value, prefix) || len(value) <= len(prefix) || len(value) > len(prefix)+maxSuffix {
return false
}
for _, char := range strings.TrimPrefix(value, prefix) {
if !(char >= 'A' && char <= 'Z') && !(char >= 'a' && char <= 'z') &&
!(char >= '0' && char <= '9') && !strings.ContainsRune("._:-", char) {
return false
}
}
return true
}
func validBase64URLKey(value string) bool {
if len(value) != 43 {
return false
}
for _, char := range value {
if !(char >= 'A' && char <= 'Z') && !(char >= 'a' && char <= 'z') &&
!(char >= '0' && char <= '9') && char != '_' && char != '-' {
return false
}
}
return true
}
func validatePayload(payload Payload, now time.Time) (time.Time, time.Time, error) {
if payload.SnapshotVersion != SnapshotVersion || !validPrefixedHex(payload.TenantID, "tenant_", 32) {
return time.Time{}, time.Time{}, errors.New("invalid snapshot identity")
}
if !strings.HasPrefix(payload.RelayNodeID, "node_") || len(payload.RelayNodeID) > 101 {
return time.Time{}, time.Time{}, errors.New("invalid relay node")
}
if payload.TenantStatus != "active" || payload.PlacementGeneration < 1 || payload.AuthorizationRevision < 0 {
return time.Time{}, time.Time{}, errors.New("snapshot does not authorize an active placement")
}
issuedAt, err := parseTime(payload.IssuedAt)
if err != nil {
return time.Time{}, time.Time{}, err
}
expiresAt, err := parseTime(payload.ExpiresAt)
if err != nil {
return time.Time{}, time.Time{}, err
}
if !expiresAt.After(issuedAt) || expiresAt.Sub(issuedAt) > MaxTTL {
return time.Time{}, time.Time{}, errors.New("invalid snapshot lifetime")
}
if now.Before(issuedAt.Add(-clockSkew)) || !now.Before(expiresAt) {
return time.Time{}, time.Time{}, errors.New("snapshot is not current")
}
previous := ""
for _, device := range payload.Devices {
if !validPrefixedHex(device.DeviceID, "host_", 32) || !validDigest(device.CredentialHash) {
return time.Time{}, time.Time{}, errors.New("invalid snapshot device")
}
if strings.TrimSpace(device.Name) == "" || len(device.Name) > 48 ||
(device.OS != "windows" && device.OS != "linux") ||
!validBase64URLKey(device.Ed25519Public) || !validBase64URLKey(device.X25519Public) ||
!validDigest(device.IdentityFingerprint) {
return time.Time{}, time.Time{}, errors.New("incomplete snapshot device identity")
}
if previous != "" && device.DeviceID <= previous {
return time.Time{}, time.Time{}, errors.New("snapshot devices are not strictly ordered")
}
previous = device.DeviceID
}
previous = ""
for _, phone := range payload.Phones {
if !validPrefixedID(phone.PhoneID, "phone_", 120) || !validDigest(phone.CredentialHash) ||
strings.TrimSpace(phone.Name) == "" || len(phone.Name) > 48 ||
!validBase64URLKey(phone.Ed25519Public) || !validBase64URLKey(phone.X25519Public) ||
!validDigest(phone.IdentityFingerprint) {
return time.Time{}, time.Time{}, errors.New("invalid snapshot phone")
}
if previous != "" && phone.PhoneID <= previous {
return time.Time{}, time.Time{}, errors.New("snapshot phones are not strictly ordered")
}
previous = phone.PhoneID
}
return issuedAt, expiresAt, nil
}
func canonicalJSON(value any) ([]byte, error) {
encoded, err := json.Marshal(value)
if err != nil {
return nil, err
}
decoder := json.NewDecoder(bytes.NewReader(encoded))
decoder.UseNumber()
var decoded any
if err := decoder.Decode(&decoded); err != nil {
return nil, err
}
var output bytes.Buffer
if err := writeCanonical(&output, decoded); err != nil {
return nil, err
}
return output.Bytes(), nil
}
func writeCanonical(output *bytes.Buffer, value any) error {
switch typed := value.(type) {
case nil:
output.WriteString("null")
case bool:
output.WriteString(strconv.FormatBool(typed))
case string:
encoded, _ := json.Marshal(typed)
output.Write(encoded)
case json.Number:
if _, err := strconv.ParseInt(string(typed), 10, 64); err != nil {
return fmt.Errorf("snapshot contains unsupported number %q", typed)
}
output.WriteString(string(typed))
case []any:
output.WriteByte('[')
for index, item := range typed {
if index > 0 {
output.WriteByte(',')
}
if err := writeCanonical(output, item); err != nil {
return err
}
}
output.WriteByte(']')
case map[string]any:
keys := make([]string, 0, len(typed))
for key := range typed {
keys = append(keys, key)
}
sort.Strings(keys)
output.WriteByte('{')
for index, key := range keys {
if index > 0 {
output.WriteByte(',')
}
encoded, _ := json.Marshal(key)
output.Write(encoded)
output.WriteByte(':')
if err := writeCanonical(output, typed[key]); err != nil {
return err
}
}
output.WriteByte('}')
default:
return fmt.Errorf("unsupported snapshot JSON value %T", value)
}
return nil
}
func SigningBytes(payload Payload) ([]byte, error) {
canonical, err := canonicalJSON(payload)
if err != nil {
return nil, err
}
return append([]byte(signatureDomain), canonical...), nil
}
func Verify(snapshot Signed, keyring Keyring, now time.Time) (Payload, error) {
if snapshot.Algorithm != "Ed25519" || snapshot.KID == "" {
return Payload{}, errors.New("unsupported snapshot signature")
}
issuedAt, expiresAt, err := validatePayload(snapshot.Payload, now)
if err != nil {
return Payload{}, err
}
key, ok := keyring[snapshot.KID]
if !ok || len(key.PublicKey) != ed25519.PublicKeySize {
return Payload{}, errors.New("snapshot signing key is not pinned")
}
if issuedAt.Before(key.NotBefore) || key.RetainUntil.Before(expiresAt) {
return Payload{}, errors.New("snapshot signing key lifetime is insufficient")
}
signature, err := base64.RawURLEncoding.DecodeString(snapshot.Signature)
if err != nil || len(signature) != ed25519.SignatureSize {
return Payload{}, errors.New("invalid snapshot signature encoding")
}
message, err := SigningBytes(snapshot.Payload)
if err != nil {
return Payload{}, err
}
if !ed25519.Verify(key.PublicKey, message, signature) {
return Payload{}, errors.New("snapshot signature mismatch")
}
return snapshot.Payload, nil
}
func PublicKeyFromJWK(raw json.RawMessage) (ed25519.PublicKey, string, error) {
var jwk struct {
KTY string `json:"kty"`
CRV string `json:"crv"`
X string `json:"x"`
KID string `json:"kid"`
}
if err := json.Unmarshal(raw, &jwk); err != nil || jwk.KTY != "OKP" || jwk.CRV != "Ed25519" {
return nil, "", errors.New("invalid Ed25519 JWK")
}
key, err := base64.RawURLEncoding.DecodeString(jwk.X)
if err != nil || len(key) != ed25519.PublicKeySize {
return nil, "", errors.New("invalid Ed25519 JWK key")
}
return ed25519.PublicKey(key), jwk.KID, nil
}