97 lines
2.6 KiB
Go
97 lines
2.6 KiB
Go
|
package v4a
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"crypto/sha256"
|
||
|
"encoding/hex"
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
signerCrypto "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth/signer/v4asdk2/internal/crypto"
|
||
|
v4Internal "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth/signer/v4asdk2/internal/v4"
|
||
|
)
|
||
|
|
||
|
// EventStreamSigner is an AWS EventStream protocol signer.
|
||
|
type EventStreamSigner interface {
|
||
|
GetSignature(ctx context.Context, headers, payload []byte, signingTime time.Time, optFns ...func(*StreamSignerOptions)) ([]byte, error)
|
||
|
}
|
||
|
|
||
|
// StreamSignerOptions is the configuration options for StreamSigner.
|
||
|
type StreamSignerOptions struct{}
|
||
|
|
||
|
// StreamSigner implements Signature Version 4 (SigV4) signing of event stream encoded payloads.
|
||
|
type StreamSigner struct {
|
||
|
options StreamSignerOptions
|
||
|
|
||
|
credentials Credentials
|
||
|
service string
|
||
|
|
||
|
prevSignature []byte
|
||
|
}
|
||
|
|
||
|
// NewStreamSigner returns a new AWS EventStream protocol signer.
|
||
|
func NewStreamSigner(credentials Credentials, service string, seedSignature []byte, optFns ...func(*StreamSignerOptions)) *StreamSigner {
|
||
|
o := StreamSignerOptions{}
|
||
|
|
||
|
for _, fn := range optFns {
|
||
|
fn(&o)
|
||
|
}
|
||
|
|
||
|
return &StreamSigner{
|
||
|
options: o,
|
||
|
credentials: credentials,
|
||
|
service: service,
|
||
|
prevSignature: seedSignature,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *StreamSigner) VerifySignature(headers, payload []byte, signingTime time.Time, signature []byte, optFns ...func(*StreamSignerOptions)) error {
|
||
|
options := s.options
|
||
|
|
||
|
for _, fn := range optFns {
|
||
|
fn(&options)
|
||
|
}
|
||
|
|
||
|
prevSignature := s.prevSignature
|
||
|
|
||
|
st := v4Internal.NewSigningTime(signingTime)
|
||
|
|
||
|
scope := buildCredentialScope(st, s.service)
|
||
|
|
||
|
stringToSign := s.buildEventStreamStringToSign(headers, payload, prevSignature, scope, &st)
|
||
|
|
||
|
ok, err := signerCrypto.VerifySignature(&s.credentials.PrivateKey.PublicKey, makeHash(sha256.New(), []byte(stringToSign)), signature)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if !ok {
|
||
|
return fmt.Errorf("v4a: invalid signature")
|
||
|
}
|
||
|
|
||
|
s.prevSignature = signature
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *StreamSigner) buildEventStreamStringToSign(headers, payload, previousSignature []byte, credentialScope string, signingTime *v4Internal.SigningTime) string {
|
||
|
hash := sha256.New()
|
||
|
return strings.Join([]string{
|
||
|
"AWS4-ECDSA-P256-SHA256-PAYLOAD",
|
||
|
signingTime.TimeFormat(),
|
||
|
credentialScope,
|
||
|
hex.EncodeToString(previousSignature),
|
||
|
hex.EncodeToString(makeHash(hash, headers)),
|
||
|
hex.EncodeToString(makeHash(hash, payload)),
|
||
|
}, "\n")
|
||
|
}
|
||
|
|
||
|
func buildCredentialScope(st v4Internal.SigningTime, service string) string {
|
||
|
return strings.Join([]string{
|
||
|
st.Format(shortTimeFormat),
|
||
|
service,
|
||
|
"aws4_request",
|
||
|
}, "/")
|
||
|
|
||
|
}
|