1package v4
2
3import (
4	"encoding/hex"
5	"strings"
6	"time"
7
8	"github.com/aws/aws-sdk-go/aws/credentials"
9)
10
11type credentialValueProvider interface {
12	Get() (credentials.Value, error)
13}
14
15// StreamSigner implements signing of event stream encoded payloads
16type StreamSigner struct {
17	region  string
18	service string
19
20	credentials credentialValueProvider
21
22	prevSig []byte
23}
24
25// NewStreamSigner creates a SigV4 signer used to sign Event Stream encoded messages
26func NewStreamSigner(region, service string, seedSignature []byte, credentials *credentials.Credentials) *StreamSigner {
27	return &StreamSigner{
28		region:      region,
29		service:     service,
30		credentials: credentials,
31		prevSig:     seedSignature,
32	}
33}
34
35// GetSignature takes an event stream encoded headers and payload and returns a signature
36func (s *StreamSigner) GetSignature(headers, payload []byte, date time.Time) ([]byte, error) {
37	credValue, err := s.credentials.Get()
38	if err != nil {
39		return nil, err
40	}
41
42	sigKey := deriveSigningKey(s.region, s.service, credValue.SecretAccessKey, date)
43
44	keyPath := buildSigningScope(s.region, s.service, date)
45
46	stringToSign := buildEventStreamStringToSign(headers, payload, s.prevSig, keyPath, date)
47
48	signature := hmacSHA256(sigKey, []byte(stringToSign))
49	s.prevSig = signature
50
51	return signature, nil
52}
53
54func buildEventStreamStringToSign(headers, payload, prevSig []byte, scope string, date time.Time) string {
55	return strings.Join([]string{
56		"AWS4-HMAC-SHA256-PAYLOAD",
57		formatTime(date),
58		scope,
59		hex.EncodeToString(prevSig),
60		hex.EncodeToString(hashSHA256(headers)),
61		hex.EncodeToString(hashSHA256(payload)),
62	}, "\n")
63}
64