Skip to content

Commit 071defd

Browse files
committed
SigV4a request working!
1 parent fa7f73e commit 071defd

File tree

5 files changed

+50
-84
lines changed

5 files changed

+50
-84
lines changed

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

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

126126
param("Host", builder.url.hostAndPort, !signViaQueryParams, overwrite = false)
127-
param("X-Amz-Algorithm", ALGORITHM_NAME, signViaQueryParams)
127+
param("X-Amz-Algorithm", config.algorithm.authorizationName , 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))
131131
param("X-Amz-Expires", config.expiresAfter?.inWholeSeconds?.toString(), signViaQueryParams)
132132
param("X-Amz-Security-Token", sessionToken, !config.omitSessionToken) // Add pre-sig if omitSessionToken=false
133+
param("X-Amz-Region-Set", config.region, config.algorithm == AwsSigningAlgorithm.SIGV4_ASYMMETRIC) // FIXME Route through sigV4aSigningRegionSet to this config?
133134

134135
val headers = builder
135136
.headers

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,17 @@ internal class DefaultAwsSignerImpl(
120120
}
121121
}
122122

123-
/** The name of the SigV4 algorithm. */
124-
internal const val ALGORITHM_NAME = "AWS4-HMAC-SHA256"
125123

126124
/**
127-
* Formats a credential scope consisting of a signing date, region, service, and a signature type
125+
* Formats a credential scope consisting of a signing date, region (SigV4 only), service, and a signature type
128126
*/
129127
internal val AwsSigningConfig.credentialScope: String
130-
get() {
128+
get() = run {
131129
val signingDate = signingDate.format(TimestampFormat.ISO_8601_CONDENSED_DATE)
132-
return "$signingDate/$region/$service/aws4_request"
130+
return when (algorithm) {
131+
AwsSigningAlgorithm.SIGV4 -> "$signingDate/$region/$service/aws4_request"
132+
AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> "$signingDate/$service/aws4_request"
133+
}
133134
}
134135

135136
/**

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

Lines changed: 7 additions & 1 deletion
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"] = "$ALGORITHM_NAME $credential, $signedHeaders, $signature"
46+
canonical.request.headers["Authorization"] = "${config.algorithm.authorizationName} $credential, $signedHeaders, $signature"
4747
}
4848

4949
AwsSignatureType.HTTP_REQUEST_VIA_QUERY_PARAMS ->
@@ -55,3 +55,9 @@ 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: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -137,33 +137,49 @@ internal class SigV4aSignatureCalculator(
137137

138138
override fun signingKey(config: AwsSigningConfig): ByteArray {
139139
// 1.1: Compute fixed input string
140-
val label = "AWS4-ECDSA-P256-SHA256".encodeToByteArray()
141-
var counter: Byte = 0x01
140+
var counter: UByte = 1u
142141
var privateKey: ByteArray
143142

143+
// N value from NIST P-256 curve
144+
// FIXME optimization: n is never used by itself, only n-2. Subtract two from the const.
145+
val nBytes = "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551".decodeHexBytes()
146+
val n = BigInteger(nBytes)
147+
148+
// FIXME Public docs say secret access key needs to be Base64 encoded, that's not right.
149+
val inputKey = ("AWS4A" + config.credentials.secretAccessKey).encodeToByteArray()
150+
144151
do {
145-
val context = config.credentials.accessKeyId.encodeToByteArray() + byteArrayOf(counter)
146-
val length = byteArrayOf(0x01, 0x00) // "256"
147-
val fixedInputString: ByteArray = label + byteArrayOf(0x00) + context + length
152+
// See https://github.com/awslabs/aws-c-auth/blob/e8360a65e0f3337d4ac827945e00c3b55a641a5f/source/key_derivation.c#L70 for
153+
// more details of derivation process
154+
155+
// FIXME CRT implementation (4 bytes) and internal docs (1 byte) conflict.
156+
val headerBytes = byteArrayOf(0x00, 0x00, 0x00, 0x01)
157+
158+
// 256 big endian
159+
// FIXME CRT implementation (4 bytes) and internal docs (2 bytes) conflict.
160+
val lengthBytes = byteArrayOf(0x00, 0x00, 0x01, 0x00)
161+
162+
val fixedInputString: ByteArray = headerBytes +
163+
"AWS4-ECDSA-P256-SHA256".encodeToByteArray() +
164+
byteArrayOf(0x00) +
165+
config.credentials.accessKeyId.encodeToByteArray() +
166+
counter.toByte() +
167+
lengthBytes
148168

149169
// 1.2: Compute K0
150-
val inputKey = ("AWS4A" + config.credentials.secretAccessKey).encodeToByteArray()
151-
val k0 = hmac(inputKey, byteArrayOf(0x01) + fixedInputString, sha256Provider)
170+
val k0 = hmac(inputKey, fixedInputString, sha256Provider)
152171

153172
// 2: Compute the ECC key pair
154173
val c = BigInteger(k0)
155174

156-
val nBytes = "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551".decodeHexBytes()
157-
val n = BigInteger(nBytes)
158-
159-
privateKey = (n + BigInteger("1")).toByteArray()
175+
privateKey = (c + BigInteger("1")).toByteArray()
160176

161-
if (counter == Byte.MAX_VALUE && c <= n - BigInteger("2")) {
177+
if (counter.toByte() == 254.toByte() && c > n - BigInteger("2")) {
162178
throw IllegalStateException("Counter exceeded maximum length")
179+
} else {
180+
counter++
163181
}
164-
165-
counter = (counter + 0x01).toByte()
166-
} while (c <= n - BigInteger("2"))
182+
} while (c > n - BigInteger("2"))
167183

168184
return privateKey
169185
}

runtime/runtime-core/jvm/src/aws/smithy/kotlin/runtime/hashing/EcdsaJVM.kt

Lines changed: 4 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,13 @@ import java.security.interfaces.*
1313
* ECDSA on the SECP256R1 curve.
1414
*/
1515
public actual fun ecdsasecp256r1(key: ByteArray, message: ByteArray): ByteArray {
16-
// Convert byte array to BigInteger for private key
16+
// Convert private key to BigInteger
1717
val d = BigInteger(key)
1818

19-
// Get EC parameters for SECP256R1
20-
val spec = ECGenParameterSpec("secp256r1")
21-
2219
// Create key pair generator to get curve parameters
23-
val keyGen = KeyPairGenerator.getInstance("EC")
24-
keyGen.initialize(spec)
20+
val keyGen = KeyPairGenerator.getInstance("EC").apply {
21+
initialize(ECGenParameterSpec("secp256r1"))
22+
}
2523
val params = (keyGen.generateKeyPair().private as ECPrivateKey).params
2624

2725
// Create private key directly from the provided key bytes
@@ -36,60 +34,4 @@ public actual fun ecdsasecp256r1(key: ByteArray, message: ByteArray): ByteArray
3634
}.sign()
3735
}
3836

39-
//public actual fun ecdsasecp256r1(key: ByteArray, message: ByteArray): ByteArray {
40-
// val d = BigInteger(key)
41-
//
42-
// // Get EC curve parameters for SECP256R1
43-
// val spec = ECGenParameterSpec("secp256r1")
44-
// val keyFactory = KeyFactory.getInstance("EC")
45-
//
46-
// // Generate ECPrivateKeySpec
47-
// val keyPairGenerator = KeyPairGenerator.getInstance("EC")
48-
// keyPairGenerator.initialize(spec)
49-
// val keyPair = keyPairGenerator.generateKeyPair()
50-
// val ecParams = (keyPair.private as ECPrivateKey).params
51-
//
52-
// // Compute the public key point P = d * G
53-
// val G = ecParams.generator
54-
// val publicPoint = ecParams.curve.multiply(G, d)
55-
//
56-
// // Create EC public key
57-
// val pubKeySpec = ECPublicKeySpec(publicPoint, ecParams)
58-
// val publicKey = keyFactory.generatePublic(pubKeySpec) as ECPublicKey
59-
//
60-
// // Create EC private key
61-
// val privateKeySpec = ECPrivateKeySpec(d, ecParams)
62-
// val privateKey = keyFactory.generatePrivate(privateKeySpec) as ECPrivateKey
63-
//
64-
// // Sign the message
65-
// return Signature.getInstance("SHA256withECDSA").apply {
66-
// initSign(privateKey)
67-
// update(message)
68-
// }.sign()
69-
//}
70-
71-
///**
72-
// * ECDSA on the SECP256R1 curve.
73-
// */
74-
//public actual fun ecdsasecp256r1(key: ByteArray, message: ByteArray): ByteArray {
75-
// val d = BigInteger(key)
76-
//
77-
// // Get SECP256R1 parameters from Java's built-in provider
78-
// val params = AlgorithmParameters.getInstance("EC").apply {
79-
// init(ECGenParameterSpec("secp256r1"))
80-
// }
81-
// val ecSpec = params.getParameterSpec(ECParameterSpec::class.java)
82-
//
83-
// // Create private key spec and generate key
84-
// val keySpec = ECPrivateKeySpec(d.toJvm(), ecSpec)
85-
// val kf = KeyFactory.getInstance("EC")
86-
// val privateKey = kf.generatePrivate(keySpec)
87-
//
88-
// // Sign the message
89-
// return Signature.getInstance("SHA256withECDSA").apply {
90-
// initSign(privateKey)
91-
// update(message)
92-
// }.sign()
93-
//}
94-
9537
private fun BigInteger.toJvm(): java.math.BigInteger = java.math.BigInteger(1, toByteArray())

0 commit comments

Comments
 (0)