mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-18 16:26:38 +00:00
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
|
|
}
|