mirror of
https://github.com/versity/versitygw.git
synced 2026-01-05 03:24:04 +00:00
The `openssl`/`curl` command generator script in `rest_scripts` supports both unsigned streaming payload trailers and signed streaming requests. This update adds support for signed streaming requests with trailers (`STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER`). **Usage** The script generates an OpenSSL command file, which is then used to send the request. Example: ```bash go run tests/rest_scripts/generateCommand.go \ --awsAccessKeyId access \ --awsSecretAccessKey secret \ --client openssl \ --commandType putObject \ --bucketName test \ --payload "hello" \ --payloadType STREAMING-UNSIGNED-PAYLOAD-TRAILER \ --chunkSize 8192 \ --objectKey obj \ --filePath req.txt \ --checksumType crc64nvme ``` You can then send the request with: ```bash openssl s_client -connect 127.0.0.1:7070 -ign_eof < req.txt > response.raw ```
157 lines
4.2 KiB
Go
157 lines
4.2 KiB
Go
package command
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
const (
|
|
streamPayloadTrailerAlgo = "AWS4-HMAC-SHA256-TRAILER"
|
|
)
|
|
|
|
type PayloadStreamingAWS4HMACSHA256 struct {
|
|
*PayloadChunkedAWS
|
|
hasher hash.Hash
|
|
}
|
|
|
|
func NewPayloadStreamingAWS4HMACSHA256(source DataSource, chunkSize int64, payloadType PayloadType, serviceString string, currentDateTime, yyyymmdd, checksumType string) *PayloadStreamingAWS4HMACSHA256 {
|
|
return &PayloadStreamingAWS4HMACSHA256{
|
|
PayloadChunkedAWS: &PayloadChunkedAWS{
|
|
PayloadChunked: &PayloadChunked{
|
|
Payload: &Payload{
|
|
dataSource: source,
|
|
payloadType: payloadType,
|
|
checksumType: checksumType,
|
|
dataSizeCalculated: false,
|
|
dataSize: 0,
|
|
},
|
|
chunkSize: chunkSize,
|
|
},
|
|
serviceString: serviceString,
|
|
currentDateTime: currentDateTime,
|
|
yyyymmdd: yyyymmdd,
|
|
lastSignature: "",
|
|
emptyByteSignature: SHA256HashZeroBytes,
|
|
signingKey: nil,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (s *PayloadStreamingAWS4HMACSHA256) AddInitialSignatureAndSigningKey(initialSignature string, signingKey []byte) {
|
|
s.lastSignature = initialSignature
|
|
s.signingKey = signingKey
|
|
}
|
|
|
|
func (s *PayloadStreamingAWS4HMACSHA256) GetContentLength() (int64, error) {
|
|
var trailerSize int64 = 85
|
|
if s.payloadType == StreamingAWS4HMACSHA256PayloadTrailer {
|
|
chLength, err := GetBase64ChecksumLength(s.checksumType)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
trailerSize += chLength + 92
|
|
|
|
// sum the checksum length
|
|
trailerSize += 16 + int64(len(s.checksumType))
|
|
}
|
|
return s.getChunkedPayloadContentLength(83, trailerSize)
|
|
}
|
|
|
|
func (s *PayloadStreamingAWS4HMACSHA256) addSignature(chunk []byte, outFile *os.File) error {
|
|
sha256sum := sha256.Sum256(chunk)
|
|
sha256sumString := hex.EncodeToString(sha256sum[:])
|
|
signature := s.getChunkedSTSSignature(sha256sumString)
|
|
if _, err := outFile.Write([]byte(";chunk-signature=" + signature)); err != nil {
|
|
return fmt.Errorf("error writing chunked signature: %w", err)
|
|
}
|
|
s.lastSignature = signature
|
|
return nil
|
|
}
|
|
|
|
func (s *PayloadStreamingAWS4HMACSHA256) addTrailer(outFile *os.File) error {
|
|
checksum, err := s.getBase64Checksum(s.hasher)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to calculated the trailing checksum: %w", err)
|
|
}
|
|
tr := fmt.Sprintf("x-amz-checksum-%s", s.checksumType)
|
|
trailer := fmt.Sprintf("%s:%s", tr, checksum)
|
|
|
|
finalSig := s.calculateTrailerSignature(trailer)
|
|
|
|
trailerStr := fmt.Sprintf(
|
|
"\r\n%s\r\nx-amz-trailer-signature:%s",
|
|
trailer,
|
|
finalSig,
|
|
)
|
|
|
|
if _, err := outFile.Write([]byte(trailerStr)); err != nil {
|
|
return fmt.Errorf("error writing final chunk trailer: %w", err)
|
|
}
|
|
|
|
s.lastSignature = finalSig
|
|
return nil
|
|
}
|
|
|
|
func (s *PayloadStreamingAWS4HMACSHA256) calculateTrailerSignature(trailer string) string {
|
|
trailer += "\n"
|
|
strToSign := s.getTrailerChunkStringToSign(trailer)
|
|
return hex.EncodeToString(hmacSHA256(s.signingKey, strToSign))
|
|
}
|
|
|
|
func (s *PayloadStreamingAWS4HMACSHA256) getTrailerChunkStringToSign(trailer string) string {
|
|
hsh := sha256.Sum256([]byte(trailer))
|
|
sig := hex.EncodeToString(hsh[:])
|
|
|
|
prefix := s.getStringToSignPrefix(streamPayloadTrailerAlgo)
|
|
|
|
strToSign := fmt.Sprintf("%s\n%s\n%s",
|
|
prefix,
|
|
s.lastSignature,
|
|
sig,
|
|
)
|
|
|
|
return strToSign
|
|
}
|
|
|
|
func (s PayloadStreamingAWS4HMACSHA256) getStringToSignPrefix(algo string) string {
|
|
return fmt.Sprintf("%s\n%s\n%s",
|
|
algo,
|
|
s.currentDateTime,
|
|
s.serviceString,
|
|
)
|
|
}
|
|
|
|
func (s *PayloadStreamingAWS4HMACSHA256) getReader() (io.Reader, error) {
|
|
sourceFile, err := s.dataSource.GetReader()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating reader: %w", err)
|
|
}
|
|
if s.payloadType == StreamingAWS4HMACSHA256PayloadTrailer && s.checksumType != "" {
|
|
s.hasher = s.getChecksumHasher()
|
|
sourceFile, err = s.dataSource.GetTeeReader(s.hasher)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating tee reader: %w", err)
|
|
}
|
|
}
|
|
return bufio.NewReader(sourceFile), nil
|
|
}
|
|
|
|
func (s *PayloadStreamingAWS4HMACSHA256) WritePayload(filePath string) error {
|
|
s.addSignatureFunc = s.addSignature
|
|
s.getReaderFunc = s.getReader
|
|
if s.payloadType == StreamingAWS4HMACSHA256PayloadTrailer {
|
|
s.addTrailerFunc = s.addTrailer
|
|
} else {
|
|
s.addTrailerFunc = func(outFile *os.File) error {
|
|
return nil
|
|
}
|
|
}
|
|
return s.writeChunkedPayload(filePath)
|
|
}
|