mirror of
https://github.com/netbirdio/netbird.git
synced 2026-03-31 06:24:18 -04:00
Auto-update logic moved out of the UI into a dedicated updatemanager.Manager service that runs in the connection layer. The UI no longer polls or checks for updates independently. The update manager supports three modes driven by the management server's auto-update policy: No policy set by mgm: checks GitHub for the latest version and notifies the user (previous behavior, now centralized) mgm enforces update: the "About" menu triggers installation directly instead of just downloading the file — user still initiates the action mgm forces update: installation proceeds automatically without user interaction updateManager lifecycle is now owned by daemon, giving the daemon server direct control via a new TriggerUpdate RPC Introduces EngineServices struct to group external service dependencies passed to NewEngine, reducing its argument count from 11 to 4
172 lines
4.0 KiB
Go
172 lines
4.0 KiB
Go
package reposign
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
maxClockSkew = 5 * time.Minute
|
|
)
|
|
|
|
// KeyID is a unique identifier for a Key (first 8 bytes of SHA-256 of public Key)
|
|
type KeyID [8]byte
|
|
|
|
// computeKeyID generates a unique ID from a public Key
|
|
func computeKeyID(pub ed25519.PublicKey) KeyID {
|
|
h := sha256.Sum256(pub)
|
|
var id KeyID
|
|
copy(id[:], h[:8])
|
|
return id
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler for KeyID
|
|
func (k KeyID) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(k.String())
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler for KeyID
|
|
func (k *KeyID) UnmarshalJSON(data []byte) error {
|
|
var s string
|
|
if err := json.Unmarshal(data, &s); err != nil {
|
|
return err
|
|
}
|
|
|
|
parsed, err := ParseKeyID(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
*k = parsed
|
|
return nil
|
|
}
|
|
|
|
// ParseKeyID parses a hex string (16 hex chars = 8 bytes) into a KeyID.
|
|
func ParseKeyID(s string) (KeyID, error) {
|
|
var id KeyID
|
|
if len(s) != 16 {
|
|
return id, fmt.Errorf("invalid KeyID length: got %d, want 16 hex chars (8 bytes)", len(s))
|
|
}
|
|
|
|
b, err := hex.DecodeString(s)
|
|
if err != nil {
|
|
return id, fmt.Errorf("failed to decode KeyID: %w", err)
|
|
}
|
|
|
|
copy(id[:], b)
|
|
return id, nil
|
|
}
|
|
|
|
func (k KeyID) String() string {
|
|
return fmt.Sprintf("%x", k[:])
|
|
}
|
|
|
|
// KeyMetadata contains versioning and lifecycle information for a Key
|
|
type KeyMetadata struct {
|
|
ID KeyID `json:"id"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
ExpiresAt time.Time `json:"expires_at,omitempty"` // Optional expiration
|
|
}
|
|
|
|
// PublicKey wraps a public Key with its Metadata
|
|
type PublicKey struct {
|
|
Key ed25519.PublicKey
|
|
Metadata KeyMetadata
|
|
}
|
|
|
|
func parsePublicKeyBundle(bundle []byte, typeTag string) ([]PublicKey, error) {
|
|
var keys []PublicKey
|
|
for len(bundle) > 0 {
|
|
keyInfo, rest, err := parsePublicKey(bundle, typeTag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
keys = append(keys, keyInfo)
|
|
bundle = rest
|
|
}
|
|
if len(keys) == 0 {
|
|
return nil, errors.New("no keys found in bundle")
|
|
}
|
|
return keys, nil
|
|
}
|
|
|
|
func parsePublicKey(data []byte, typeTag string) (PublicKey, []byte, error) {
|
|
b, rest := pem.Decode(data)
|
|
if b == nil {
|
|
return PublicKey{}, nil, errors.New("failed to decode PEM data")
|
|
}
|
|
if b.Type != typeTag {
|
|
return PublicKey{}, nil, fmt.Errorf("PEM type is %q, want %q", b.Type, typeTag)
|
|
}
|
|
|
|
// Unmarshal JSON-embedded format
|
|
var pub PublicKey
|
|
if err := json.Unmarshal(b.Bytes, &pub); err != nil {
|
|
return PublicKey{}, nil, fmt.Errorf("failed to unmarshal public key: %w", err)
|
|
}
|
|
|
|
// Validate key length
|
|
if len(pub.Key) != ed25519.PublicKeySize {
|
|
return PublicKey{}, nil, fmt.Errorf("incorrect Ed25519 public key size: expected %d, got %d",
|
|
ed25519.PublicKeySize, len(pub.Key))
|
|
}
|
|
|
|
// Always recompute ID to ensure integrity
|
|
pub.Metadata.ID = computeKeyID(pub.Key)
|
|
|
|
return pub, rest, nil
|
|
}
|
|
|
|
type PrivateKey struct {
|
|
Key ed25519.PrivateKey
|
|
Metadata KeyMetadata
|
|
}
|
|
|
|
func parsePrivateKey(data []byte, typeTag string) (PrivateKey, error) {
|
|
b, rest := pem.Decode(data)
|
|
if b == nil {
|
|
return PrivateKey{}, errors.New("failed to decode PEM data")
|
|
}
|
|
if len(rest) > 0 {
|
|
return PrivateKey{}, errors.New("trailing PEM data")
|
|
}
|
|
if b.Type != typeTag {
|
|
return PrivateKey{}, fmt.Errorf("PEM type is %q, want %q", b.Type, typeTag)
|
|
}
|
|
|
|
// Unmarshal JSON-embedded format
|
|
var pk PrivateKey
|
|
if err := json.Unmarshal(b.Bytes, &pk); err != nil {
|
|
return PrivateKey{}, fmt.Errorf("failed to unmarshal private key: %w", err)
|
|
}
|
|
|
|
// Validate key length
|
|
if len(pk.Key) != ed25519.PrivateKeySize {
|
|
return PrivateKey{}, fmt.Errorf("incorrect Ed25519 private key size: expected %d, got %d",
|
|
ed25519.PrivateKeySize, len(pk.Key))
|
|
}
|
|
|
|
return pk, nil
|
|
}
|
|
|
|
func verifyAny(publicRootKeys []PublicKey, msg, sig []byte) bool {
|
|
// Verify with root keys
|
|
var rootKeys []ed25519.PublicKey
|
|
for _, r := range publicRootKeys {
|
|
rootKeys = append(rootKeys, r.Key)
|
|
}
|
|
|
|
for _, k := range rootKeys {
|
|
if ed25519.Verify(k, msg, sig) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|