Skip to content

Conversation

@lauzadis
Copy link
Contributor

Issue #

Description of changes

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@lauzadis lauzadis marked this pull request as ready for review February 20, 2025 16:58
@lauzadis lauzadis requested a review from a team as a code owner February 20, 2025 16:58
@github-actions

This comment has been minimized.

1 similar comment
@github-actions

This comment has been minimized.

Comment on lines 87 to 92
// Convert [this] [ByteArray] to a positive [BigInteger]
private fun ByteArray.toPositiveBigInteger(): BigInteger = if (isNotEmpty() && (get(0).toInt() and 0x80) != 0) {
BigInteger(byteArrayOf(0x00) + this) // Prepend 0x00 to ensure positive value
} else {
BigInteger(this)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Won't the The BigInteger implementations trim unnecessary leading zeroes automatically? Could we just do:

private fun ByteArray.toPositiveBigInteger() = BigInteger(byteArrayOf(0x00) + this) // Prepend 0x00 to ensure positive value

Comment on lines 16 to 20
/**
* Common signature implementation used for SigV4 and SigV4a, primarily for forming the strings-to-sign which don't differ
* between the two signing algorithms (besides their names).
*/
internal abstract class SigV4xSignatureCalculator(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: Using x to signify commonality between SigV4 and SigV4a is not very clear. I suggest calling this class BaseSigV4SignatureCalculator.

Comment on lines 25 to 27
check(algorithm == AwsSigningAlgorithm.SIGV4 || algorithm == AwsSigningAlgorithm.SIGV4_ASYMMETRIC) {
"This class should only be used for the ${AwsSigningAlgorithm.SIGV4} or ${AwsSigningAlgorithm.SIGV4_ASYMMETRIC} algorithms, got $algorithm"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Extract duplicated signing algorithms to a val and reference it in both places:

// top-level
private val supportedAlgorithms = setOf(AwsSigningAlgorithm.SIGV4, AwsSigningAlgorithm.SIGV4_ASYMMETRIC)

// in `init`
check(algorithm in supportedAlgorithms) {
    "This class only supports algorithms $supportedAlgorithms, got $algorithm"
}

Comment on lines 62 to 66
internal val AwsSigningAlgorithm.signingName: String
get() = when (this) {
AwsSigningAlgorithm.SIGV4 -> "AWS4-HMAC-SHA256"
AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> "AWS4-ECDSA-P256-SHA256"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Should this just be part of the AwsSigningAlgorithm class itself?

* NOTE: This duplicates parts of the event stream encoding implementation here to avoid a direct dependency.
* Should this become more involved than encoding a single date header we should reconsider this choice.
*
* see [Event Stream implementation](https://github.com/awslabs/aws-sdk-kotlin/blob/v0.16.4-beta/aws-runtime/protocols/aws-event-stream/common/src/aws/sdk/kotlin/runtime/protocol/eventstream/Header.kt#L51)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This is a real stale link. Can we refresh it?

/**
* ECDSA on the SECP256R1 curve.
*/
public expect fun ecdsasecp256r1(key: ByteArray, message: ByteArray): ByteArray
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: ECSDA and Secp256r1 are distinct words so we should uppercase that s (e.g., ecdsaSecp256r1)

import java.security.spec.*
import kotlin.test.Test

class EcdsaJVMTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Can we move these tests into common instead of jvm?

Copy link
Contributor Author

@lauzadis lauzadis Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not easily, due to the use of java.security.*. Given the FIXME written in EcdsaNative.kt (which we discussed / agreed upon(?) in standup), we are unlikely to ever implement this on Native, so I think it's fine to leave as JVM-only.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifically, that we'll be using CrtAwsSigner on Native instead of DefaultAwsSigner. This ecdsaSecp256r1 will not be used on Native in that case

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks for the clarification.

Comment on lines -17 to 20

class DefaultSignatureCalculatorTest {
class SigV4SignatureCalculatorTest {
// Test adapted from https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
@Test
fun testCalculate() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: We should add tests for SigV4a signature calculation with explicit inputs/outputs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

java.security's ECDSA implementation is non-deterministic, it chooses a random k value with each call to sign(). We'd get different (yet still valid) signatures when providing a consistent input. This library doesn't allow overriding the random generator which would allow us to provide a fixed "random" input for tests. The top recommendation seems to be switching to BouncyCastle and overriding the implementation of SecureRandom used.

Would you be fine with the tests asserting on everything except the signature value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We spoke offline and agreed to assert on everything except the signature value. Added a test suite for "string to sign" with test cases copied from CRT.

*/
private fun fixedInputString(accessKeyId: String, counter: Byte): ByteArray =
byteArrayOf(0x00, 0x00, 0x00, 0x01) +
"AWS4-ECDSA-P256-SHA256".encodeToByteArray() +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Could reuse AwsSigningAlgorithm.SIGV4_ASYMMETRIC.signingName

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

1 similar comment
@github-actions

This comment has been minimized.

@lauzadis lauzadis requested a review from ianbotsf February 21, 2025 20:46
@github-actions

This comment has been minimized.

class SigV4aSignatureCalculatorTest {
@Test
fun testStringToSign() = TESTS.forEach { testId ->
runTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Logging which test is running or adding a message to the assertEquals in case the assertion fails

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Comment on lines 30 to 37
/**
* The name of this algorithm to use when signing requests.
*/
public val signingName: String
get() = when (this) {
SIGV4 -> "AWS4-HMAC-SHA256"
SIGV4_ASYMMETRIC -> "AWS4-ECDSA-P256-SHA256"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: Embed as a constructor parameter

public enum class AwsSigningAlgorithm(public val signingName: String) {
    SIGV4("AWS4-HMAC-SHA256"),
    SIGV4_ASYMMETRIC("AWS4-ECDSA-P256-SHA256"),
}

Comment on lines 7 to 16
/**
* ECDSA on the SECP256R1 curve.
*/
// FIXME Implement using aws-c-cal: https://github.com/awslabs/aws-c-cal/blob/main/include/aws/cal/ecc.h
/**
* Will need to be implemented and exposed in aws-crt-kotlin.
* Or maybe we can _only_ offer the CRT signer on Native?
* Will require updating DefaultAwsSigner to be expect/actual and set to CrtSigner on Native.
*/
public actual fun ecdsaSecp256r1(key: ByteArray, message: ByteArray): ByteArray = TODO("Not yet implemented")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The FIXME ends and a new KDocs block opens but it seems like the content of it is more related to the FIXME than documentation about what the function does. Restructure to just continue the FIXME:

/**
 * ECDSA on the SECP256R1 curve.
 */
// FIXME Implement using aws-c-cal: https://github.com/awslabs/aws-c-cal/blob/main/include/aws/cal/ecc.h
//  Will need to be implemented and exposed in aws-crt-kotlin.
//  Or maybe we can _only_ offer the CRT signer on Native?
//  Will require updating DefaultAwsSigner to be expect/actual and set to CrtSigner on Native.
public actual fun ecdsaSecp256r1(key: ByteArray, message: ByteArray): ByteArray = TODO("Not yet implemented")

import java.security.spec.*
import kotlin.test.Test

class EcdsaJVMTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks for the clarification.

Comment on lines 66 to 72
/**
* Tests for [SigV4aSignatureCalculator]. Currently only tests forming the string-to-sign.
*
* TODO Add tests against header-signature.txt when java.security implements RFC 6979 / deterministic ECDSA.
* https://bugs.openjdk.org/browse/JDK-8239382
*/
class SigV4aSignatureCalculatorTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: TODO should not be part of KDocs.

@github-actions
Copy link

Affected Artifacts

Significantly increased in size

Artifact Pull Request (bytes) Latest Release (bytes) Delta (bytes) Delta (percentage)
aws-signing-default-jvm.jar 66,865 53,497 13,368 24.99%
Changed in size
Artifact Pull Request (bytes) Latest Release (bytes) Delta (bytes) Delta (percentage)
aws-signing-common-jvm.jar 71,836 71,597 239 0.33%
runtime-core-jvm.jar 827,267 825,504 1,763 0.21%

@lauzadis lauzadis merged commit 82b9ffe into main Feb 24, 2025
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants