Skip to content

Commit 52df61d

Browse files
committed
Commonize into Sigv4xSignatureCalculator
1 parent 5f9786c commit 52df61d

File tree

3 files changed

+69
-97
lines changed

3 files changed

+69
-97
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ internal class DefaultCanonicalizer(private val sha256Supplier: HashSupplier = :
124124
}
125125

126126
param("Host", builder.url.hostAndPort, !signViaQueryParams, overwrite = false)
127-
param("X-Amz-Algorithm", config.algorithm.authorizationName, signViaQueryParams)
127+
param("X-Amz-Algorithm", config.algorithm.signingName, signViaQueryParams)
128128
param("X-Amz-Credential", credentialValue(config), signViaQueryParams)
129129
param("X-Amz-Content-Sha256", hash, addHashHeader)
130130
param("X-Amz-Date", config.signingDate.format(TimestampFormat.ISO_8601_CONDENSED))

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ internal class DefaultRequestMutator : RequestMutator {
4343
val credential = "Credential=${credentialValue(config)}"
4444
val signedHeaders = "SignedHeaders=${canonical.signedHeaders}"
4545
val signature = "Signature=$signatureHex"
46-
canonical.request.headers["Authorization"] = "${config.algorithm.authorizationName} $credential, $signedHeaders, $signature"
46+
canonical.request.headers["Authorization"] = "${config.algorithm.signingName} $credential, $signedHeaders, $signature"
4747
}
4848

4949
AwsSignatureType.HTTP_REQUEST_VIA_QUERY_PARAMS ->
@@ -55,9 +55,3 @@ internal class DefaultRequestMutator : RequestMutator {
5555
return canonical.request.build()
5656
}
5757
}
58-
59-
internal val AwsSigningAlgorithm.authorizationName: String
60-
get() = when (this) {
61-
AwsSigningAlgorithm.SIGV4 -> "AWS4-HMAC-SHA256"
62-
AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> "AWS4-ECDSA-P256-SHA256"
63-
}

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

Lines changed: 67 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -70,34 +70,62 @@ internal interface SignatureCalculator {
7070
fun chunkTrailerStringToSign(trailingHeaders: ByteArray, prevSignature: ByteArray, config: AwsSigningConfig): String
7171
}
7272

73-
internal class SigV4SignatureCalculator(private val sha256Provider: HashSupplier = ::Sha256) : SignatureCalculator {
74-
override fun calculate(signingKey: ByteArray, stringToSign: String): String =
75-
hmac(signingKey, stringToSign.encodeToByteArray(), sha256Provider).encodeToHex()
73+
/**
74+
* Common signature implementation used for SigV4 and SigV4a, primarily for forming the strings-to-sign which don't differ
75+
* between the two signing algorithms (besides their names).
76+
*/
77+
internal abstract class SigV4xSignatureCalculator(
78+
val algorithm: AwsSigningAlgorithm,
79+
open val sha256Provider: HashSupplier = ::Sha256,
80+
) : SignatureCalculator {
81+
init {
82+
check (algorithm == AwsSigningAlgorithm.SIGV4 || algorithm == AwsSigningAlgorithm.SIGV4_ASYMMETRIC) {
83+
"This class should only be used for the ${AwsSigningAlgorithm.SIGV4} or ${AwsSigningAlgorithm.SIGV4_ASYMMETRIC} algorithms, got $algorithm"
84+
}
85+
}
7686

77-
override fun chunkStringToSign(chunkBody: ByteArray, prevSignature: ByteArray, config: AwsSigningConfig): String =
78-
buildString {
79-
appendLine("AWS4-HMAC-SHA256-PAYLOAD")
80-
appendLine(config.signingDate.format(TimestampFormat.ISO_8601_CONDENSED))
81-
appendLine(config.credentialScope)
82-
appendLine(prevSignature.decodeToString()) // Should already be a byte array of ASCII hex chars
87+
override fun stringToSign(canonicalRequest: String, config: AwsSigningConfig): String = buildString {
88+
appendLine(algorithm.signingName)
89+
appendLine(config.signingDate.format(TimestampFormat.ISO_8601_CONDENSED))
90+
appendLine(config.credentialScope)
91+
append(canonicalRequest.encodeToByteArray().hash(sha256Provider).encodeToHex())
92+
}
8393

84-
val nonSignatureHeadersHash = when (config.signatureType) {
85-
AwsSignatureType.HTTP_REQUEST_EVENT -> eventStreamNonSignatureHeaders(config.signingDate)
86-
else -> HashSpecification.EmptyBody.hash
87-
}
94+
override fun chunkStringToSign(chunkBody: ByteArray, prevSignature: ByteArray, config: AwsSigningConfig): String = buildString {
95+
appendLine("${algorithm.signingName}-PAYLOAD")
96+
appendLine(config.signingDate.format(TimestampFormat.ISO_8601_CONDENSED))
97+
appendLine(config.credentialScope)
98+
appendLine(prevSignature.decodeToString()) // Should already be a byte array of ASCII hex chars
8899

89-
appendLine(nonSignatureHeadersHash)
90-
append(chunkBody.hash(sha256Provider).encodeToHex())
100+
val nonSignatureHeadersHash = when (config.signatureType) {
101+
AwsSignatureType.HTTP_REQUEST_EVENT -> eventStreamNonSignatureHeaders(config.signingDate)
102+
else -> HashSpecification.EmptyBody.hash
91103
}
92104

105+
appendLine(nonSignatureHeadersHash)
106+
append(chunkBody.hash(sha256Provider).encodeToHex())
107+
}
108+
93109
override fun chunkTrailerStringToSign(trailingHeaders: ByteArray, prevSignature: ByteArray, config: AwsSigningConfig): String =
94110
buildString {
95-
appendLine("AWS4-HMAC-SHA256-TRAILER")
111+
appendLine("${algorithm.signingName}-TRAILER")
96112
appendLine(config.signingDate.format(TimestampFormat.ISO_8601_CONDENSED))
97113
appendLine(config.credentialScope)
98114
appendLine(prevSignature.decodeToString())
99115
append(trailingHeaders.hash(sha256Provider).encodeToHex())
100116
}
117+
}
118+
119+
internal val AwsSigningAlgorithm.signingName: String
120+
get() = when (this) {
121+
AwsSigningAlgorithm.SIGV4 -> "AWS4-HMAC-SHA256"
122+
AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> "AWS4-ECDSA-P256-SHA256"
123+
}
124+
125+
126+
internal class SigV4SignatureCalculator(override val sha256Provider: HashSupplier = ::Sha256) : SigV4xSignatureCalculator(AwsSigningAlgorithm.SIGV4, sha256Provider) {
127+
override fun calculate(signingKey: ByteArray, stringToSign: String): String =
128+
hmac(signingKey, stringToSign.encodeToByteArray(), sha256Provider).encodeToHex()
101129

102130
override fun signingKey(config: AwsSigningConfig): ByteArray {
103131
fun hmac(key: ByteArray, message: String) = hmac(key, message.encodeToByteArray(), sha256Provider)
@@ -108,36 +136,20 @@ internal class SigV4SignatureCalculator(private val sha256Provider: HashSupplier
108136
val kService = hmac(kRegion, config.service)
109137
return hmac(kService, "aws4_request")
110138
}
111-
112-
override fun stringToSign(canonicalRequest: String, config: AwsSigningConfig): String =
113-
buildString {
114-
appendLine("AWS4-HMAC-SHA256")
115-
appendLine(config.signingDate.format(TimestampFormat.ISO_8601_CONDENSED))
116-
appendLine(config.credentialScope)
117-
append(canonicalRequest.encodeToByteArray().hash(sha256Provider).encodeToHex())
118-
}
119139
}
120140

121-
// FIXME Copied a few functions from SigV4SignatureCalculator, refactor to share
122-
internal class SigV4aSignatureCalculator(
123-
val sha256Provider: HashSupplier = ::Sha256,
124-
) : SignatureCalculator {
125-
override fun calculate(signingKey: ByteArray, stringToSign: String): String =
126-
ecdsasecp256r1(signingKey, stringToSign.encodeToByteArray()).encodeToHex()
141+
/**
142+
* The maximum number of iterations to attempt private key derivation using KDF in counter mode
143+
* Taken from CRT: https://github.com/awslabs/aws-c-auth/blob/e8360a65e0f3337d4ac827945e00c3b55a641a5f/source/key_derivation.c#L22
144+
*/
145+
internal const val MAX_KDF_COUNTER_ITERATIONS = 254.toByte()
127146

128-
override fun stringToSign(
129-
canonicalRequest: String,
130-
config: AwsSigningConfig,
131-
): String = buildString {
132-
appendLine("AWS4-ECDSA-P256-SHA256")
133-
appendLine(config.signingDate.format(TimestampFormat.ISO_8601_CONDENSED))
134-
appendLine(config.credentialScope)
135-
append(canonicalRequest.encodeToByteArray().hash(sha256Provider).encodeToHex())
136-
}
147+
internal class SigV4aSignatureCalculator(override val sha256Provider: HashSupplier = ::Sha256) : SigV4xSignatureCalculator(AwsSigningAlgorithm.SIGV4_ASYMMETRIC, sha256Provider) {
148+
override fun calculate(signingKey: ByteArray, stringToSign: String): String = ecdsasecp256r1(signingKey, stringToSign.encodeToByteArray()).encodeToHex()
137149

150+
// See https://github.com/awslabs/aws-c-auth/blob/e8360a65e0f3337d4ac827945e00c3b55a641a5f/source/key_derivation.c#L70 for more details of derivation process
138151
override fun signingKey(config: AwsSigningConfig): ByteArray {
139-
// 1.1: Compute fixed input string
140-
var counter: UByte = 1u
152+
var counter: Byte = 1
141153
var privateKey: ByteArray
142154

143155
// N value from NIST P-256 curve
@@ -150,32 +162,15 @@ internal class SigV4aSignatureCalculator(
150162
val inputKey = ("AWS4A" + config.credentials.secretAccessKey).encodeToByteArray()
151163

152164
do {
153-
// See https://github.com/awslabs/aws-c-auth/blob/e8360a65e0f3337d4ac827945e00c3b55a641a5f/source/key_derivation.c#L70 for
154-
// more details of derivation process
155-
156-
// FIXME CRT implementation (4 bytes) and internal docs (1 byte) conflict.
157-
val headerBytes = byteArrayOf(0x00, 0x00, 0x00, 0x01)
158-
159-
// 256 big endian
160-
// FIXME CRT implementation (4 bytes) and internal docs (2 bytes) conflict.
161-
val lengthBytes = byteArrayOf(0x00, 0x00, 0x01, 0x00)
162-
163-
val fixedInputString: ByteArray = headerBytes +
164-
"AWS4-ECDSA-P256-SHA256".encodeToByteArray() +
165-
byteArrayOf(0x00) +
166-
config.credentials.accessKeyId.encodeToByteArray() +
167-
counter.toByte() +
168-
lengthBytes
169-
170165
// 1.2: Compute K0
171-
val k0 = hmac(inputKey, fixedInputString, sha256Provider)
166+
val k0 = hmac(inputKey, fixedInputString(config.credentials.accessKeyId, counter), sha256Provider)
172167

173168
// 2: Compute the ECC key pair
174169
val c = BigInteger(k0)
175170

176171
privateKey = (c + BigInteger("1")).toByteArray()
177172

178-
if (counter.toByte() == 254.toByte() && c > n - BigInteger("2")) {
173+
if (counter == MAX_KDF_COUNTER_ITERATIONS && c > n - BigInteger("2")) {
179174
throw IllegalStateException("Counter exceeded maximum length")
180175
} else {
181176
counter++
@@ -185,36 +180,19 @@ internal class SigV4aSignatureCalculator(
185180
return privateKey
186181
}
187182

188-
override fun chunkStringToSign(
189-
chunkBody: ByteArray,
190-
prevSignature: ByteArray,
191-
config: AwsSigningConfig,
192-
): String = buildString {
193-
appendLine("AWS4-ECDSA-P256-SHA256-PAYLOAD")
194-
appendLine(config.signingDate.format(TimestampFormat.ISO_8601_CONDENSED))
195-
appendLine(config.credentialScope)
196-
appendLine(prevSignature.decodeToString()) // Should already be a byte array of ASCII hex chars
197-
198-
val nonSignatureHeadersHash = when (config.signatureType) {
199-
AwsSignatureType.HTTP_REQUEST_EVENT -> eventStreamNonSignatureHeaders(config.signingDate)
200-
else -> HashSpecification.EmptyBody.hash
201-
}
202-
203-
appendLine(nonSignatureHeadersHash)
204-
append(chunkBody.hash(sha256Provider).encodeToHex())
205-
}
183+
/**
184+
* Computes the fixed input string used for KDF
185+
* The final output looks like:
186+
* 0x00000001 || "AWS4-ECDSA-P256-SHA256" || 0x00 || AccessKeyId || counter || 0x00000100
187+
*/
188+
private fun fixedInputString(accessKeyId: String, counter: Byte): ByteArray =
189+
byteArrayOf(0x00, 0x00, 0x00, 0x01) + // FIXME CRT implementation (4 bytes) and internal docs (1 byte) conflict.
190+
"AWS4-ECDSA-P256-SHA256".encodeToByteArray() +
191+
byteArrayOf(0x00) +
192+
accessKeyId.encodeToByteArray() +
193+
counter +
194+
byteArrayOf(0x00, 0x00, 0x01, 0x00) // FIXME CRT implementation (4 bytes) and internal docs (2 bytes) conflict.
206195

207-
override fun chunkTrailerStringToSign(
208-
trailingHeaders: ByteArray,
209-
prevSignature: ByteArray,
210-
config: AwsSigningConfig,
211-
): String = buildString {
212-
appendLine("AWS4-ECDSA-P256-SHA256-TRAILER")
213-
appendLine(config.signingDate.format(TimestampFormat.ISO_8601_CONDENSED))
214-
appendLine(config.credentialScope)
215-
appendLine(prevSignature.decodeToString())
216-
append(trailingHeaders.hash(sha256Provider).encodeToHex())
217-
}
218196
}
219197

220198
private const val HEADER_TIMESTAMP_TYPE: Byte = 8

0 commit comments

Comments
 (0)