package lafsession import ( "crypto/ed25519" "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" ) // Identity is a long-term Ed25519 key pair used for mutual authentication. type Identity struct { Private ed25519.PrivateKey Public ed25519.PublicKey } // GenerateIdentity creates a new random identity key pair. func GenerateIdentity() (*Identity, error) { pub, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { return nil, err } return &Identity{Private: priv, Public: pub}, nil } // IdentityFromSeed derives an identity from a 32-byte seed (deterministic). func IdentityFromSeed(seed []byte) (*Identity, error) { if len(seed) != ed25519.SeedSize { return nil, fmt.Errorf("lafsession: seed must be %d bytes", ed25519.SeedSize) } priv := ed25519.NewKeyFromSeed(seed) return &Identity{Private: priv, Public: priv.Public().(ed25519.PublicKey)}, nil } // PublicBytes returns the 32-byte public key. func (id *Identity) PublicBytes() []byte { return append([]byte(nil), id.Public...) } // Fingerprint returns a SHA-256 hex fingerprint of the public key (full 32 bytes hex). func (id *Identity) Fingerprint() string { sum := sha256.Sum256(id.Public) return hex.EncodeToString(sum[:]) } // ShortFingerprint returns the first 20 bytes of SHA-256 as spaced hex groups (display). func (id *Identity) ShortFingerprint() string { sum := sha256.Sum256(id.Public) h := hex.EncodeToString(sum[:20]) out := make([]byte, 0, 24+5) for i := 0; i < len(h); i += 4 { if i > 0 { out = append(out, ' ') } end := i + 4 if end > len(h) { end = len(h) } out = append(out, h[i:end]...) } return string(out) } // Sign signs a transcript with the identity private key. func (id *Identity) Sign(msg []byte) []byte { return ed25519.Sign(id.Private, msg) } // Verify checks a signature under this identity public key. func (id *Identity) Verify(msg, sig []byte) bool { return ed25519.Verify(id.Public, msg, sig) } // VerifyPeer checks a signature under an arbitrary peer public key. func VerifyPeer(peerPub, msg, sig []byte) bool { if len(peerPub) != ed25519.PublicKeySize { return false } return ed25519.Verify(ed25519.PublicKey(peerPub), msg, sig) } // FingerprintOf returns SHA-256 hex of a raw public key. func FingerprintOf(pub []byte) string { sum := sha256.Sum256(pub) return hex.EncodeToString(sum[:]) }