81 lines
2.6 KiB
Go
81 lines
2.6 KiB
Go
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
|
|
}
|