Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
fe55115
generate schemas (#623)
lucix-aws Feb 11, 2026
d4c2935
reduce the code size of schemas substantially
lucix-aws Feb 11, 2026
d529e95
fixup for event stream updates
lucix-aws Feb 27, 2026
44d1bfb
serde2: implement awsJson10 (#633)
lucix-aws Mar 5, 2026
f23a348
serde2: implement aws-protocols/restjson1 (#636)
lucix-aws Mar 12, 2026
2219ef9
add awsjson11, fix some nil bugs in json serde (#640)
lucix-aws Mar 17, 2026
2e4d10c
implement schema-serde event streams
lucix-aws Apr 6, 2026
58d7f87
implement rpcv2cbor (#646)
lucix-aws Apr 14, 2026
5f4544d
implement awsquery/ec2query (#653)
lucix-aws Apr 27, 2026
7b86c13
fix compile err
lucix-aws Apr 27, 2026
154c6a1
switch to explicit WriteStruct (#655)
lucix-aws Apr 30, 2026
05dd5b8
add prelude schemas, fix various codegen issues (#657)
lucix-aws May 5, 2026
e86c946
implement restxml (#656)
lucix-aws May 11, 2026
60bad24
serde2 lightning round (#659)
lucix-aws May 12, 2026
194a4fd
make traits indexable (#658)
lucix-aws May 14, 2026
43fa739
fix synthetic namespace collide
lucix-aws May 15, 2026
a10992e
gate schemaserde doc thing behind useExperimentalSerde
lucix-aws May 15, 2026
c844738
a
lucix-aws May 15, 2026
a9c2c4d
generate endpoint and auth code separate from protocol generator (#662)
lucix-aws May 15, 2026
4a9408b
addressed TODO
lucix-aws May 16, 2026
4f1aa62
serde2: drop writeptr shenanigans and optimize allocs on loop deseria…
lucix-aws May 18, 2026
55f7632
serde2: json parser (#660)
lucix-aws May 18, 2026
46bf285
drop readptr shenanigans (#664)
lucix-aws May 19, 2026
30af9be
fix benchmark test
lucix-aws May 22, 2026
71e6656
useExperimentalSerde -> useLegacySerde
lucix-aws May 22, 2026
7cf6966
fixup protocol test
lucix-aws May 22, 2026
e0ef149
internalize the event stream exported member
lucix-aws May 22, 2026
07813b2
fixup event stream tests
lucix-aws May 22, 2026
fb5f09e
do not mutate trait state
lucix-aws May 22, 2026
6792656
serde2 runtime optimizations (#668)
adwsingh May 29, 2026
222d1cc
fuzz
lucix-aws May 29, 2026
e9fbcbb
consolidate protocols
lucix-aws Jun 1, 2026
0117a3d
fix service schema
lucix-aws Jun 1, 2026
3e49f1d
a
lucix-aws Jun 1, 2026
4258947
a
lucix-aws Jun 1, 2026
90098e3
a
lucix-aws Jun 1, 2026
11aec0f
a
lucix-aws Jun 1, 2026
819dc7e
a
lucix-aws Jun 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
82 changes: 82 additions & 0 deletions aws-http-auth/sigv4/stream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package sigv4

import (
"crypto/sha256"
"encoding/hex"
"hash"
"strings"
"time"

"github.com/aws/smithy-go/aws-http-auth/credentials"
v4internal "github.com/aws/smithy-go/aws-http-auth/internal/v4"
)

// EventStreamSigner implements SigV4 event stream message signing.
//
// Unlike request signing, event stream message signing is **stateful**. The
// signer must be seeded with an initial signature from the originating HTTP
// request, and each message is signed using the signature of the previous
// message (starting at that seed value). Therefore the caller **MUST NOT**
// reuse an instance of an EventStreamSigner across multiple streams.
type EventStreamSigner struct {
creds credentials.Credentials
service string
region string
prev []byte
}

// EventStreamSignerOptions is reserved for future use.
type EventStreamSignerOptions struct{}

// NewEventStreamSigner returns an EventStreamSigner for a single stream.
func NewEventStreamSigner(creds credentials.Credentials, service, region string, seed []byte,
opts ...func(*EventStreamSignerOptions)) *EventStreamSigner {
return &EventStreamSigner{
creds: creds,
service: service,
region: region,
prev: seed,
}
}

// SignMessage computes the signature for the next message in the event stream.
func (s *EventStreamSigner) SignMessage(headers, payload []byte, now time.Time) ([]byte, error) {
now = now.UTC()

scopeNow := now.Format(v4internal.ShortTimeFormat)
key := deriveKey(s.creds.SecretAccessKey, s.service, s.region, scopeNow)
scope := strings.Join([]string{
scopeNow,
s.region,
s.service,
"aws4_request",
}, "/")

h := sha256.New()
toSign := strings.Join([]string{
"AWS4-HMAC-SHA256-PAYLOAD",
now.Format(v4internal.TimeFormat),
scope,
hex.EncodeToString(s.prev),
hex.EncodeToString(mkhash(h, headers)),
hex.EncodeToString(mkhash(h, payload)),
}, "\n")

signature := hmacSHA256(key, []byte(toSign))
s.prev = signature

return signature, nil
}

func deriveKey(secret, service, region, shortDate string) []byte {
key := hmacSHA256([]byte("AWS4"+secret), []byte(shortDate))
key = hmacSHA256(key, []byte(region))
key = hmacSHA256(key, []byte(service))
return hmacSHA256(key, []byte("aws4_request"))
}

func mkhash(h hash.Hash, b []byte) []byte {
h.Reset()
h.Write(b)
return h.Sum(nil)
}
81 changes: 81 additions & 0 deletions aws-http-auth/sigv4/stream_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package sigv4

import (
"encoding/hex"
"testing"
"time"

"github.com/aws/smithy-go/aws-http-auth/credentials"
)

func mustHexDecode(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return b
}

func TestEventStreamSigner_SignMessage(t *testing.T) {
signer := NewEventStreamSigner(
credentials.Credentials{
AccessKeyID: "AKID",
SecretAccessKey: "SECRET",
},
"transcribestreaming",
"us-east-1",
[]byte("foobarbazqux"),
)

messages := []struct {
Name string
Headers []byte
Payload []byte
Time time.Time
Expect []byte
}{
{
Name: "first message",
Headers: []byte("foo"),
Payload: []byte("foo"),
Time: time.Unix(10, 0),
Expect: mustHexDecode("e3a136405ec1152f62136742efd41f8639cd647a1cf21fab12aedb22cccd8999"),
},
{
Name: "second message",
Headers: []byte("bar"),
Payload: []byte("bar"),
Time: time.Unix(20, 0),
Expect: mustHexDecode("92a6ad677e839d3003672cb2cefc71ef80d42e8cb300fefce0807e8398c74068"),
},
{
Name: "third message",
Headers: []byte("baz"),
Payload: []byte("baz"),
Time: time.Unix(30, 0),
Expect: mustHexDecode("46d6660bd40f2ff5d6246a347895cabe7f4e89d9e1f4c6dad6dabdc801923be1"),
},
{
Name: "end of stream",
Headers: []byte{},
Payload: []byte{},
Time: time.Unix(30, 0),
Expect: mustHexDecode("492bcb3e21de447c85f8a9bef6c0eb833e992e405b9b3584be84edaccc5aad18"),
},
}

for _, tt := range messages {
t.Run(tt.Name, func(t *testing.T) {
s, err := signer.SignMessage(tt.Headers, tt.Payload, tt.Time)
if err != nil {
t.Fatal(err)
}

expect := hex.EncodeToString(tt.Expect)
actual := hex.EncodeToString(s)
if expect != actual {
t.Errorf("%v != %v", expect, actual)
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,9 @@ public void generateStructure(GenerateStructureDirective<GoCodegenContext, GoSet
var delegator = directive.context().writerDelegator();
delegator.useShapeWriter(directive.shape(), writer ->
new StructureGenerator(
directive.model(),
directive.symbolProvider(),
directive.context(),
writer,
directive.service(),
directive.shape(),
directive.symbolProvider().toSymbol(directive.shape()),
null
).run()
);
Expand All @@ -105,12 +102,9 @@ public void generateError(GenerateErrorDirective<GoCodegenContext, GoSettings> d
var delegator = directive.context().writerDelegator();
delegator.useShapeWriter(directive.shape(), writer ->
new StructureGenerator(
directive.model(),
directive.symbolProvider(),
directive.context(),
writer,
directive.service(),
directive.shape(),
directive.symbolProvider().toSymbol(directive.shape()),
null
).run()
);
Expand All @@ -120,7 +114,7 @@ public void generateError(GenerateErrorDirective<GoCodegenContext, GoSettings> d
public void generateUnion(GenerateUnionDirective<GoCodegenContext, GoSettings> directive) {
var delegator = directive.context().writerDelegator();
delegator.useShapeWriter(directive.shape(), writer ->
new UnionGenerator(directive.model(), directive.symbolProvider(), directive.shape())
new UnionGenerator(directive.context(), directive.model(), directive.symbolProvider(), directive.shape())
.generateUnion(writer)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* Chains together multiple Writables that can be composed into one Writable.
*/
public final class ChainWritable {
// FUTURE: move statics to Writable, make this file- or package-private

private final List<Writable> writables;

public ChainWritable() {
Expand All @@ -29,6 +31,14 @@ public static ChainWritable of(Collection<Writable> writables) {
return chain;
}

public static <T> ChainWritable of(Collection<T> values, Function<T, Writable> mapper) {
var chain = new ChainWritable();
chain.writables.addAll(values.stream()
.map(mapper)
.toList());
return chain;
}

public boolean isEmpty() {
return writables.isEmpty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

package software.amazon.smithy.go.codegen;

import static software.amazon.smithy.go.codegen.GoWriter.emptyGoTemplate;
import static software.amazon.smithy.go.codegen.GoWriter.goDocTemplate;
import static software.amazon.smithy.go.codegen.GoWriter.goTemplate;

Expand All @@ -40,13 +41,15 @@
public class ClientOptions implements Writable {
public static final String NAME = "Options";

private final GoCodegenContext ctx;
private final ProtocolGenerator.GenerationContext context;
private final ApplicationProtocol protocol;

private final List<ConfigField> fields;
private final Map<ShapeId, AuthSchemeDefinition> authSchemes;

public ClientOptions(ProtocolGenerator.GenerationContext context, ApplicationProtocol protocol) {
public ClientOptions(GoCodegenContext ctx, ProtocolGenerator.GenerationContext context, ApplicationProtocol protocol) {
this.ctx = ctx;
this.context = context;
this.protocol = protocol;

Expand Down Expand Up @@ -82,6 +85,8 @@ private Writable generate() {

$fields:W

$schemaSerdeProtocolFields:W

$protocolFields:W
}

Expand All @@ -100,10 +105,21 @@ private Writable generate() {
"protocolFields", generateProtocolFields(),
"copy", generateCopy(),
"getIdentityResolver", generateGetIdentityResolver(),
"helpers", generateHelpers()
"helpers", generateHelpers(),
"schemaSerdeProtocolFields", !ctx.settings().useLegacySerde()
? generateSchemaSerdeProtocolFields()
: emptyGoTemplate()
));
}

private Writable generateSchemaSerdeProtocolFields() {
ensureSupportedProtocol();
return goTemplate("""
$D
Protocol smithyhttp.ClientProtocol
""", SmithyGoDependency.SMITHY_HTTP_TRANSPORT);
}

private Writable generateProtocolTypes() {
ensureSupportedProtocol();
return goTemplate("""
Expand Down
Loading
Loading