package authn import ( "crypto/ed25519" "encoding/binary" "errors" ) const ( // EventDomainMarkerV1 binds the v1 server event signature to the Galaxy // gateway transport contract. EventDomainMarkerV1 = "galaxy-event-v1" ) var ( // ErrInvalidEventSignature reports that a gateway stream event signature is // not a raw Ed25519 signature for the canonical event signing input. ErrInvalidEventSignature = errors.New("invalid event signature") ) // EventSigningFields contains the canonical v1 stream-event fields that are // bound into the server signing input. type EventSigningFields struct { // EventType identifies the stable client-facing event category. EventType string // EventID is the stable event correlation identifier. EventID string // TimestampMS carries the server event timestamp in milliseconds. TimestampMS int64 // RequestID optionally correlates the event to the opening client request. RequestID string // TraceID optionally carries the client-supplied tracing correlation value. TraceID string // PayloadHash is the raw SHA-256 digest of event payload bytes. PayloadHash []byte } // BuildEventSigningInput returns the canonical byte sequence the v1 gateway // stream-event signature covers. String and byte fields are length-prefixed // with uvarint(len(field)) followed by raw bytes, while TimestampMS is // appended as an 8-byte big-endian uint64. func BuildEventSigningInput(fields EventSigningFields) []byte { size := len(EventDomainMarkerV1) + len(fields.EventType) + len(fields.EventID) + len(fields.RequestID) + len(fields.TraceID) + len(fields.PayloadHash) + (6 * binary.MaxVarintLen64) + 8 buf := make([]byte, 0, size) buf = appendLengthPrefixedString(buf, EventDomainMarkerV1) buf = appendLengthPrefixedString(buf, fields.EventType) buf = appendLengthPrefixedString(buf, fields.EventID) buf = binary.BigEndian.AppendUint64(buf, uint64(fields.TimestampMS)) buf = appendLengthPrefixedString(buf, fields.RequestID) buf = appendLengthPrefixedString(buf, fields.TraceID) buf = appendLengthPrefixedBytes(buf, fields.PayloadHash) return buf } // VerifyEventSignature verifies that signature authenticates fields under // publicKey using the canonical v1 event signing input. func VerifyEventSignature(publicKey ed25519.PublicKey, signature []byte, fields EventSigningFields) error { if len(publicKey) != ed25519.PublicKeySize || len(signature) != ed25519.SignatureSize { return ErrInvalidEventSignature } if !ed25519.Verify(publicKey, BuildEventSigningInput(fields), signature) { return ErrInvalidEventSignature } return nil }