phase 3
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package canon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// RequestDomainMarkerV1 binds the v1 client request signature to the Galaxy
|
||||
// gateway transport contract.
|
||||
RequestDomainMarkerV1 = "galaxy-request-v1"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidPayloadHash reports that payloadHash is not a raw SHA-256 digest.
|
||||
ErrInvalidPayloadHash = errors.New("payload_hash must be a 32-byte SHA-256 digest")
|
||||
|
||||
// ErrPayloadHashMismatch reports that payloadHash does not match payloadBytes.
|
||||
ErrPayloadHashMismatch = errors.New("payload_hash does not match payload_bytes")
|
||||
)
|
||||
|
||||
// RequestSigningFields contains the canonical v1 request fields that are bound
|
||||
// into the client signing input. The client populates this struct from a
|
||||
// request envelope before signing; the server populates it from a received
|
||||
// envelope before verification.
|
||||
type RequestSigningFields struct {
|
||||
// ProtocolVersion identifies the transport envelope version.
|
||||
ProtocolVersion string
|
||||
|
||||
// DeviceSessionID identifies the authenticated device session bound to the
|
||||
// request.
|
||||
DeviceSessionID string
|
||||
|
||||
// MessageType is the stable downstream routing key.
|
||||
MessageType string
|
||||
|
||||
// TimestampMS carries the client request timestamp in milliseconds.
|
||||
TimestampMS int64
|
||||
|
||||
// RequestID is the transport correlation and anti-replay identifier.
|
||||
RequestID string
|
||||
|
||||
// PayloadHash is the raw SHA-256 digest of payload bytes.
|
||||
PayloadHash []byte
|
||||
}
|
||||
|
||||
// BuildRequestSigningInput returns the canonical byte sequence the v1 client
|
||||
// request 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 BuildRequestSigningInput(fields RequestSigningFields) []byte {
|
||||
size := len(RequestDomainMarkerV1) +
|
||||
len(fields.ProtocolVersion) +
|
||||
len(fields.DeviceSessionID) +
|
||||
len(fields.MessageType) +
|
||||
len(fields.RequestID) +
|
||||
len(fields.PayloadHash) +
|
||||
(6 * binary.MaxVarintLen64) +
|
||||
8
|
||||
|
||||
buf := make([]byte, 0, size)
|
||||
buf = appendLengthPrefixedString(buf, RequestDomainMarkerV1)
|
||||
buf = appendLengthPrefixedString(buf, fields.ProtocolVersion)
|
||||
buf = appendLengthPrefixedString(buf, fields.DeviceSessionID)
|
||||
buf = appendLengthPrefixedString(buf, fields.MessageType)
|
||||
buf = binary.BigEndian.AppendUint64(buf, uint64(fields.TimestampMS))
|
||||
buf = appendLengthPrefixedString(buf, fields.RequestID)
|
||||
buf = appendLengthPrefixedBytes(buf, fields.PayloadHash)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// VerifyPayloadHash checks that payloadHash is the raw SHA-256 digest of
|
||||
// payloadBytes. Empty payloadBytes are valid and must use sha256.Sum256(nil).
|
||||
func VerifyPayloadHash(payloadBytes, payloadHash []byte) error {
|
||||
if len(payloadHash) != sha256.Size {
|
||||
return ErrInvalidPayloadHash
|
||||
}
|
||||
|
||||
sum := sha256.Sum256(payloadBytes)
|
||||
if !bytes.Equal(sum[:], payloadHash) {
|
||||
return ErrPayloadHashMismatch
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user