Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS Key Manager #65

Merged
merged 16 commits into from
Oct 11, 2023
Merged

AWS Key Manager #65

merged 16 commits into from
Oct 11, 2023

Conversation

tomdaffurn
Copy link
Contributor

Overview

  • Implement a KeyManager that uses AWS KMS
  • Improve the KeyManager interface to allow mapping of DIDs to aliases

# Conflicts:
#	crypto/build.gradle.kts
#	crypto/src/test/kotlin/web5/sdk/crypto/Secp256k1Test.kt
@bradleydwyer bradleydwyer self-requested a review October 9, 2023 09:50
@tomdaffurn tomdaffurn marked this pull request as ready for review October 10, 2023 04:43
Copy link
Contributor

@mistermoe mistermoe left a comment

Choose a reason for hiding this comment

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

barring @phoebe-lew's comment, looks gud2me! 🏅

* - [JWSAlgorithm.ES256K]
*/
public class AwsKeyManager(
private val kmsClient: AWSKMS = AWSKMSClientBuilder.standard().build()

Choose a reason for hiding this comment

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

Consider allowing the kmsClient to be provided by the caller (e.g. to allow configurable behaviour of the client like timeouts)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's already possibly for the caller to supply, isn't it?

  fun `test a custom KMS client`() {
    val kmsClient = AWSKMSClient.builder()
      .withCredentials(AWSStaticCredentialsProvider(BasicAWSCredentials("foo", "bar")))
      .build()
    val customisedKeyManager = AwsKeyManager(kmsClient = kmsClient)

    assertThrows<AmazonServiceException> {
      customisedKeyManager.generatePrivateKey(JWSAlgorithm.ES256K)
    }
  }

)

private val algorithmDetails = mapOf(
JWSAlgorithm.ES256K to AlgorithmDetails(
Copy link

@bradleydwyer bradleydwyer Oct 10, 2023

Choose a reason for hiding this comment

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

As a note, it is possible to drop the enum type for the assignment, which might be useful given the use of named params. e.g.

    ES256K to AlgorithmDetails(
      algorithm = ES256K,
      curve = SECP256K1,
      keySpec = ECC_SECG_P256K1,
      signingAlgorithm = ECDSA_SHA_256,
      newDigest = { SHA256Digest() }
    )

crypto/build.gradle.kts Outdated Show resolved Hide resolved
* @return The alias belonging to [publicKey]
*/
override fun getDefaultAlias(publicKey: JWK): String {
val jwkThumbprint = publicKey.computeThumbprint()
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious why thumbprint instead of using the kid field?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This does go into kid up in generatePrivateKey

Copy link
Contributor

Choose a reason for hiding this comment

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

@andresuribe87 because kid isn't a required property of JWKs (reference).

IMO, Using JWK thumbprints makes it so there's a deterministic mapping between a DID's verification method (presuming that the verification method has publicKeyJwk, a requirement we intend to surface as part of an upcoming interop profile) and a key manager's key alias without having to rely on a Verification Method ID or JWK kid. Especially because a verification method id and a kid (even if both are present) don't have to match

*
* Implementations should ensure that the signing process is secured, utilizing secure cryptographic
* practices and safeguarding the private key during the operation. The specific signing algorithm
* used may depend on the type and parameters of the stored key.
*/
public fun sign(keyAlias: String, payload: Payload)
public fun sign(keyAlias: String, signingInput: 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.

I think this would make it easier to consume for everyone.

Suggested change
public fun sign(keyAlias: String, signingInput: ByteArray): ByteArray
public fun sign(keyAlias: String, signingInput: ByteArray): JWSObject

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There was an argument made that all crypto ops should be bytes-in/bytes-out. This makes KeyManager compatible with Crypto.kt

* @return The alias belonging to [publicKey]
* @throws IllegalArgumentException if the key is not known to the [KeyManager]
*/
public fun getDefaultAlias(publicKey: JWK): String
Copy link
Contributor

Choose a reason for hiding this comment

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

Given that JWK already have a kid field, is it possible to use that field to identify keys?

IMO, it's easier to put the alias within that field, rather than expanding the interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this a good point, and kicked off a big discussion. Both AwsKeyManager and InMemoryKeyManager are setting the alias into kid, so it would work with the code we have here.

It's not clear that the publicKeyJwk will always have a kid though. It's optional. I think it's up to the DID method how that publicKeyJwk is put together?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm gonna merge this now to unblock #63, but happy to keep the convo going in Slack

@codecov-commenter
Copy link

codecov-commenter commented Oct 11, 2023

Codecov Report

Merging #65 (bbef724) into main (6901ced) will decrease coverage by 13.10%.
The diff coverage is 0.00%.

@@            Coverage Diff             @@
##             main     #65       +/-   ##
==========================================
- Coverage   13.09%   0.00%   -13.10%     
==========================================
  Files          15      16        +1     
  Lines         443     519       +76     
  Branches       54      57        +3     
==========================================
- Hits           58       0       -58     
- Misses        380     519      +139     
+ Partials        5       0        -5     
Files Coverage Δ
...ypto/src/main/kotlin/web5/sdk/crypto/KeyManager.kt 0.00% <ø> (ø)
.../main/kotlin/web5/sdk/crypto/InMemoryKeyManager.kt 0.00% <0.00%> (ø)
...o/src/main/kotlin/web5/sdk/crypto/AwsKeyManager.kt 0.00% <0.00%> (ø)

... and 3 files with indirect coverage changes

@tomdaffurn tomdaffurn merged commit 63e8975 into main Oct 11, 2023
2 of 3 checks passed
@mistermoe mistermoe deleted the tom/aws3 branch November 1, 2023 00:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants