Skip to content

Commit 27af57e

Browse files
committed
Make DefaultAwsSigner expect/actual, point to CRT signer on Native
1 parent 1eebd5e commit 27af57e

File tree

8 files changed

+160
-139
lines changed

8 files changed

+160
-139
lines changed

runtime/auth/aws-signing-default/api/aws-signing-default.api

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ public final class aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerBui
55
public final fun setTelemetryProvider (Laws/smithy/kotlin/runtime/telemetry/TelemetryProvider;)V
66
}
77

8-
public final class aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerKt {
8+
public final class aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerJVMKt {
99
public static final fun DefaultAwsSigner (Lkotlin/jvm/functions/Function1;)Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigner;
1010
public static final fun getDefaultAwsSigner ()Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigner;
1111
}

runtime/auth/aws-signing-default/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,11 @@ kotlin {
2525
all {
2626
languageSettings.optIn("aws.smithy.kotlin.runtime.InternalApi")
2727
}
28+
29+
nativeMain {
30+
dependencies {
31+
implementation(project(":runtime:auth:aws-signing-crt"))
32+
}
33+
}
2834
}
2935
}

runtime/auth/aws-signing-default/common/src/aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSigner.kt

Lines changed: 1 addition & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -4,136 +4,5 @@
44
*/
55
package aws.smithy.kotlin.runtime.auth.awssigning
66

7-
import aws.smithy.kotlin.runtime.ExperimentalApi
8-
import aws.smithy.kotlin.runtime.http.Headers
9-
import aws.smithy.kotlin.runtime.http.request.HttpRequest
10-
import aws.smithy.kotlin.runtime.telemetry.TelemetryProvider
11-
import aws.smithy.kotlin.runtime.telemetry.logging.logger
12-
import aws.smithy.kotlin.runtime.time.TimestampFormat
13-
import kotlin.coroutines.coroutineContext
14-
157
/** The default implementation of [AwsSigner] */
16-
public val DefaultAwsSigner: AwsSigner = DefaultAwsSignerImpl()
17-
18-
/** Creates a customized instance of [AwsSigner] */
19-
@Suppress("ktlint:standard:function-naming")
20-
public fun DefaultAwsSigner(block: DefaultAwsSignerBuilder.() -> Unit): AwsSigner =
21-
DefaultAwsSignerBuilder().apply(block).build()
22-
23-
/** A builder class for creating instances of [AwsSigner] using the default implementation */
24-
public class DefaultAwsSignerBuilder {
25-
public var telemetryProvider: TelemetryProvider? = null
26-
27-
public fun build(): AwsSigner = DefaultAwsSignerImpl(
28-
telemetryProvider = telemetryProvider,
29-
)
30-
}
31-
32-
private val AwsSigningAlgorithm.signatureCalculator
33-
get() = when (this) {
34-
AwsSigningAlgorithm.SIGV4 -> SignatureCalculator.SigV4
35-
AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> SignatureCalculator.SigV4a
36-
}
37-
38-
@OptIn(ExperimentalApi::class)
39-
internal class DefaultAwsSignerImpl(
40-
private val canonicalizer: Canonicalizer = Canonicalizer.Default,
41-
private val requestMutator: RequestMutator = RequestMutator.Default,
42-
private val telemetryProvider: TelemetryProvider? = null,
43-
) : AwsSigner {
44-
override suspend fun sign(request: HttpRequest, config: AwsSigningConfig): AwsSigningResult<HttpRequest> {
45-
val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner")
46-
?: coroutineContext.logger<DefaultAwsSignerImpl>()
47-
48-
val canonical = canonicalizer.canonicalRequest(request, config)
49-
if (config.logRequest) {
50-
logger.trace { "Canonical request:\n${canonical.requestString}" }
51-
}
52-
53-
val signatureCalculator = config.algorithm.signatureCalculator
54-
55-
val stringToSign = signatureCalculator.stringToSign(canonical.requestString, config)
56-
logger.trace { "String to sign:\n$stringToSign" }
57-
58-
val signingKey = signatureCalculator.signingKey(config)
59-
60-
val signature = signatureCalculator.calculate(signingKey, stringToSign)
61-
logger.debug { "Calculated signature: $signature" }
62-
63-
val signedRequest = requestMutator.appendAuth(config, canonical, signature)
64-
65-
return AwsSigningResult(signedRequest, signature.encodeToByteArray())
66-
}
67-
68-
override suspend fun signChunk(
69-
chunkBody: ByteArray,
70-
prevSignature: ByteArray,
71-
config: AwsSigningConfig,
72-
): AwsSigningResult<Unit> {
73-
val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner")
74-
?: coroutineContext.logger<DefaultAwsSignerImpl>()
75-
76-
val signatureCalculator = config.algorithm.signatureCalculator
77-
78-
val stringToSign = signatureCalculator.chunkStringToSign(chunkBody, prevSignature, config)
79-
logger.trace { "Chunk string to sign:\n$stringToSign" }
80-
81-
val signingKey = signatureCalculator.signingKey(config)
82-
83-
val signature = signatureCalculator.calculate(signingKey, stringToSign)
84-
logger.debug { "Calculated chunk signature: $signature" }
85-
86-
return AwsSigningResult(Unit, signature.encodeToByteArray())
87-
}
88-
89-
override suspend fun signChunkTrailer(
90-
trailingHeaders: Headers,
91-
prevSignature: ByteArray,
92-
config: AwsSigningConfig,
93-
): AwsSigningResult<Unit> {
94-
val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner")
95-
?: coroutineContext.logger<DefaultAwsSignerImpl>()
96-
97-
val signatureCalculator = config.algorithm.signatureCalculator
98-
99-
// FIXME - can we share canonicalization code more than we are..., also this reduce is inefficient.
100-
// canonicalize the headers
101-
val trailingHeadersBytes = trailingHeaders.entries().sortedBy { e -> e.key.lowercase() }
102-
.map { e ->
103-
buildString {
104-
append(e.key.lowercase())
105-
append(":")
106-
append(e.value.joinToString(",") { v -> v.trim() })
107-
append("\n")
108-
}.encodeToByteArray()
109-
}.reduce { acc, bytes -> acc + bytes }
110-
111-
val stringToSign = signatureCalculator.chunkTrailerStringToSign(trailingHeadersBytes, prevSignature, config)
112-
logger.trace { "Chunk trailer string to sign:\n$stringToSign" }
113-
114-
val signingKey = signatureCalculator.signingKey(config)
115-
116-
val signature = signatureCalculator.calculate(signingKey, stringToSign)
117-
logger.debug { "Calculated chunk signature: $signature" }
118-
119-
return AwsSigningResult(Unit, signature.encodeToByteArray())
120-
}
121-
}
122-
123-
/**
124-
* Formats a credential scope consisting of a signing date, region (SigV4 only), service, and a signature type
125-
*/
126-
internal val AwsSigningConfig.credentialScope: String
127-
get() = run {
128-
val signingDate = signingDate.format(TimestampFormat.ISO_8601_CONDENSED_DATE)
129-
return when (algorithm) {
130-
AwsSigningAlgorithm.SIGV4 -> "$signingDate/$region/$service/aws4_request"
131-
AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> "$signingDate/$service/aws4_request"
132-
}
133-
}
134-
135-
/**
136-
* Formats the value for a credential header/parameter
137-
*/
138-
internal fun credentialValue(config: AwsSigningConfig): String =
139-
"${config.credentials.accessKeyId}/${config.credentialScope}"
8+
public expect val DefaultAwsSigner: AwsSigner

runtime/auth/aws-signing-default/common/src/aws/smithy/kotlin/runtime/auth/awssigning/RequestMutator.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package aws.smithy.kotlin.runtime.auth.awssigning
66

77
import aws.smithy.kotlin.runtime.http.request.HttpRequest
8+
import aws.smithy.kotlin.runtime.time.TimestampFormat
89

910
/**
1011
* An object that can mutate requests to include signing attributes.
@@ -55,3 +56,21 @@ internal class DefaultRequestMutator : RequestMutator {
5556
return canonical.request.build()
5657
}
5758
}
59+
60+
/**
61+
* Formats a credential scope consisting of a signing date, region (SigV4 only), service, and a signature type
62+
*/
63+
internal val AwsSigningConfig.credentialScope: String
64+
get() = run {
65+
val signingDate = signingDate.format(TimestampFormat.ISO_8601_CONDENSED_DATE)
66+
return when (algorithm) {
67+
AwsSigningAlgorithm.SIGV4 -> "$signingDate/$region/$service/aws4_request"
68+
AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> "$signingDate/$service/aws4_request"
69+
}
70+
}
71+
72+
/**
73+
* Formats the value for a credential header/parameter
74+
*/
75+
internal fun credentialValue(config: AwsSigningConfig): String =
76+
"${config.credentials.accessKeyId}/${config.credentialScope}"
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package aws.smithy.kotlin.runtime.auth.awssigning
6+
7+
import aws.smithy.kotlin.runtime.ExperimentalApi
8+
import aws.smithy.kotlin.runtime.http.Headers
9+
import aws.smithy.kotlin.runtime.http.request.HttpRequest
10+
import aws.smithy.kotlin.runtime.telemetry.TelemetryProvider
11+
import aws.smithy.kotlin.runtime.telemetry.logging.logger
12+
import kotlin.coroutines.coroutineContext
13+
14+
/** The default implementation of [AwsSigner] */
15+
public actual val DefaultAwsSigner: AwsSigner
16+
get() = DefaultAwsSignerImpl()
17+
18+
/** Creates a customized instance of [AwsSigner] */
19+
@Suppress("ktlint:standard:function-naming")
20+
public fun DefaultAwsSigner(block: DefaultAwsSignerBuilder.() -> Unit): AwsSigner =
21+
DefaultAwsSignerBuilder().apply(block).build()
22+
23+
/** A builder class for creating instances of [AwsSigner] using the default implementation */
24+
public class DefaultAwsSignerBuilder {
25+
public var telemetryProvider: TelemetryProvider? = null
26+
27+
public fun build(): AwsSigner = DefaultAwsSignerImpl(
28+
telemetryProvider = telemetryProvider,
29+
)
30+
}
31+
32+
private val AwsSigningAlgorithm.signatureCalculator
33+
get() = when (this) {
34+
AwsSigningAlgorithm.SIGV4 -> SignatureCalculator.SigV4
35+
AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> SignatureCalculator.SigV4a
36+
}
37+
38+
@OptIn(ExperimentalApi::class)
39+
internal class DefaultAwsSignerImpl(
40+
private val canonicalizer: Canonicalizer = Canonicalizer.Default,
41+
private val requestMutator: RequestMutator = RequestMutator.Default,
42+
private val telemetryProvider: TelemetryProvider? = null,
43+
) : AwsSigner {
44+
override suspend fun sign(request: HttpRequest, config: AwsSigningConfig): AwsSigningResult<HttpRequest> {
45+
val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner")
46+
?: coroutineContext.logger<DefaultAwsSignerImpl>()
47+
48+
val canonical = canonicalizer.canonicalRequest(request, config)
49+
if (config.logRequest) {
50+
logger.trace { "Canonical request:\n${canonical.requestString}" }
51+
}
52+
53+
val signatureCalculator = config.algorithm.signatureCalculator
54+
55+
val stringToSign = signatureCalculator.stringToSign(canonical.requestString, config)
56+
logger.trace { "String to sign:\n$stringToSign" }
57+
58+
val signingKey = signatureCalculator.signingKey(config)
59+
60+
val signature = signatureCalculator.calculate(signingKey, stringToSign)
61+
logger.debug { "Calculated signature: $signature" }
62+
63+
val signedRequest = requestMutator.appendAuth(config, canonical, signature)
64+
65+
return AwsSigningResult(signedRequest, signature.encodeToByteArray())
66+
}
67+
68+
override suspend fun signChunk(
69+
chunkBody: ByteArray,
70+
prevSignature: ByteArray,
71+
config: AwsSigningConfig,
72+
): AwsSigningResult<Unit> {
73+
val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner")
74+
?: coroutineContext.logger<DefaultAwsSignerImpl>()
75+
76+
val signatureCalculator = config.algorithm.signatureCalculator
77+
78+
val stringToSign = signatureCalculator.chunkStringToSign(chunkBody, prevSignature, config)
79+
logger.trace { "Chunk string to sign:\n$stringToSign" }
80+
81+
val signingKey = signatureCalculator.signingKey(config)
82+
83+
val signature = signatureCalculator.calculate(signingKey, stringToSign)
84+
logger.debug { "Calculated chunk signature: $signature" }
85+
86+
return AwsSigningResult(Unit, signature.encodeToByteArray())
87+
}
88+
89+
override suspend fun signChunkTrailer(
90+
trailingHeaders: Headers,
91+
prevSignature: ByteArray,
92+
config: AwsSigningConfig,
93+
): AwsSigningResult<Unit> {
94+
val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner")
95+
?: coroutineContext.logger<DefaultAwsSignerImpl>()
96+
97+
val signatureCalculator = config.algorithm.signatureCalculator
98+
99+
// FIXME - can we share canonicalization code more than we are..., also this reduce is inefficient.
100+
// canonicalize the headers
101+
val trailingHeadersBytes = trailingHeaders.entries().sortedBy { e -> e.key.lowercase() }
102+
.map { e ->
103+
buildString {
104+
append(e.key.lowercase())
105+
append(":")
106+
append(e.value.joinToString(",") { v -> v.trim() })
107+
append("\n")
108+
}.encodeToByteArray()
109+
}.reduce { acc, bytes -> acc + bytes }
110+
111+
val stringToSign = signatureCalculator.chunkTrailerStringToSign(trailingHeadersBytes, prevSignature, config)
112+
logger.trace { "Chunk trailer string to sign:\n$stringToSign" }
113+
114+
val signingKey = signatureCalculator.signingKey(config)
115+
116+
val signature = signatureCalculator.calculate(signingKey, stringToSign)
117+
logger.debug { "Calculated chunk signature: $signature" }
118+
119+
return AwsSigningResult(Unit, signature.encodeToByteArray())
120+
}
121+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package aws.smithy.kotlin.runtime.auth.awssigning
6+
7+
import aws.smithy.kotlin.runtime.auth.awssigning.crt.CrtAwsSigner
8+
9+
/** The default implementation of [AwsSigner] */
10+
public actual val DefaultAwsSigner: AwsSigner
11+
get() = CrtAwsSigner

runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/BasicSigningTestBase.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,6 @@ public abstract class BasicSigningTestBase : HasSigner {
8484
assertEquals(expectedSig, result.signature.decodeToString())
8585
}
8686

87-
@IgnoreNative // ecdsaSecp256r1 not implemented on Native. Can we make the signer implementation expect/actual and
88-
// use CRT signer on Native?
8987
@Test
9088
public open fun testSignRequestSigV4Asymmetric(): TestResult = runTest {
9189
// sanity test

runtime/runtime-core/native/src/aws/smithy/kotlin/runtime/hashing/EcdsaNative.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
*/
55
package aws.smithy.kotlin.runtime.hashing
66

7-
// FIXME Implement using aws-c-cal: https://github.com/awslabs/aws-c-cal/blob/main/include/aws/cal/ecc.h
8-
// Will need to be implemented and exposed in aws-crt-kotlin. Or maybe we can _only_ offer the CRT signer on Native?
9-
// Will require updating DefaultAwsSigner to be expect/actual and set to CrtSigner on Native.
107
/**
118
* ECDSA on the SECP256R1 curve.
129
*/
13-
public actual fun ecdsaSecp256r1(key: ByteArray, message: ByteArray): ByteArray = TODO("Not yet implemented")
10+
public actual fun ecdsaSecp256r1(key: ByteArray, message: ByteArray): ByteArray = error("This function should not be invoked on Native, which uses the CrtAwsSigner.")

0 commit comments

Comments
 (0)