From 6c02fee1c23389490d01ba84cf3741b74a45d794 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 15:52:12 +0100 Subject: [PATCH 01/21] Add AWSKey and AWSKeyMetadataSDK for AWS KMS integration --- .../kotlin/id.walt.crypto.keys.aws/AWSKey.kt | 224 ++++++++++++++++++ .../AWSKeyMetadataSDK.kt | 9 + 2 files changed, 233 insertions(+) create mode 100644 waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/AWSKey.kt create mode 100644 waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/AWSKeyMetadataSDK.kt diff --git a/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/AWSKey.kt b/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/AWSKey.kt new file mode 100644 index 000000000..a458e4d1d --- /dev/null +++ b/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/AWSKey.kt @@ -0,0 +1,224 @@ +package id.walt.crypto.keys.aws + +import aws.sdk.kotlin.services.kms.KmsClient +import aws.sdk.kotlin.services.kms.model.* +import id.walt.crypto.exceptions.KeyTypeNotSupportedException +import id.walt.crypto.keys.EccUtils +import id.walt.crypto.keys.Key +import id.walt.crypto.keys.KeyMeta +import id.walt.crypto.keys.KeyType +import id.walt.crypto.keys.jwk.JWKKey +import id.walt.crypto.utils.Base64Utils.decodeFromBase64 +import id.walt.crypto.utils.Base64Utils.encodeToBase64 +import id.walt.crypto.utils.Base64Utils.encodeToBase64Url +import id.walt.crypto.utils.JsonUtils.toJsonElement +import id.walt.crypto.utils.jwsSigningAlgorithm +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.jsonObject + + +@Serializable +@SerialName("aws") +class AWSKey( + val config: AWSKeyMetadataSDK, + val id: String, + private var _publicKey: String? = null, + private var _keyType: KeyType? = null, + + ) : Key() { + + @Transient + override var keyType: KeyType + get() = _keyType!! + set(value) { + _keyType = value + } + + override val hasPrivateKey: Boolean + get() = false + + + override fun toString(): String = "[AWS ${keyType.name} key @AWS ${config.region} - $id]" + + override suspend fun getKeyId(): String = getPublicKey().getKeyId() + + override suspend fun getThumbprint(): String { + TODO("Not yet implemented") + } + + override suspend fun exportJWK(): String = throw NotImplementedError("JWK export is not available for remote keys.") + + override suspend fun exportJWKObject(): JsonObject = Json.parseToJsonElement(_publicKey!!).jsonObject + + override suspend fun exportPEM(): String = throw NotImplementedError("PEM export is not available for remote keys.") + + private val awsSigningAlgorithm by lazy { + when (keyType) { + KeyType.secp256r1, KeyType.secp256k1 -> "ECDSA_SHA_256" + KeyType.RSA -> "RSASSA_PKCS1_V1_5_SHA_256" + else -> throw KeyTypeNotSupportedException(keyType.name) + } + } + + override suspend fun signRaw(plaintext: ByteArray): ByteArray { + val signRequest = SignRequest { + this.keyId = id + signingAlgorithm = SigningAlgorithmSpec.fromValue(awsSigningAlgorithm) + message = plaintext + messageType = MessageType.Raw + } + + return KmsClient { region = config.region }.use { kmsClient -> + kmsClient.sign(signRequest).signature ?: throw IllegalStateException("Signature not returned") + } + } + + + override suspend fun signJws( + plaintext: ByteArray, + headers: Map + ): String { + val appendedHeader = HashMap(headers).apply { + put("alg", jwsSigningAlgorithm(keyType).toJsonElement()) + } + + val header = Json.encodeToString(appendedHeader).encodeToByteArray().encodeToBase64Url() + val payload = plaintext.encodeToBase64Url() + + var rawSignature = signRaw("$header.$payload".encodeToByteArray()) + + if (keyType in listOf(KeyType.secp256r1, KeyType.secp256k1)) { // TODO: Add RSA support + rawSignature = EccUtils.convertDERtoIEEEP1363(rawSignature) + } + + val encodedSignature = rawSignature.encodeToBase64Url() + val jws = "$header.$payload.$encodedSignature" + + return jws + } + + override suspend fun verifyRaw( + signed: ByteArray, + detachedPlaintext: ByteArray? + ): Result { + val verifyRequest = VerifyRequest { + this.keyId = id + signingAlgorithm = SigningAlgorithmSpec.fromValue(awsSigningAlgorithm) + message = detachedPlaintext + this.signature = signed + messageType = MessageType.Raw + } + + return KmsClient { region = config.region }.use { kmsClient -> + val response = kmsClient.verify(verifyRequest) + Result.success(response.signatureValid.toString().decodeFromBase64()) + } + } + + + override suspend fun verifyJws(signedJws: String): Result { + val publicKey = getPublicKey() + val verification = publicKey.verifyJws(signedJws) + return verification + } + + @Transient + private var backedKey: Key? = null + + override suspend fun getPublicKey(): Key = backedKey ?: when { + _publicKey != null -> _publicKey!!.let { JWKKey.importJWK(it).getOrThrow() } + else -> retrievePublicKey() + }.also { newBackedKey -> backedKey = newBackedKey } + + + private suspend fun retrievePublicKey(): Key { + val publicKey = getAwsPublicKey(config, id) + _publicKey = publicKey.exportJWK() + return publicKey + } + + override suspend fun getPublicKeyRepresentation(): ByteArray { + TODO("Not yet implemented") + } + + override suspend fun getMeta(): KeyMeta { + TODO("Not yet implemented") + } + + override suspend fun deleteKey(): Boolean { + val request = ScheduleKeyDeletionRequest { + this.keyId = id + pendingWindowInDays = 7 + } + + val delete = KmsClient { region = config.region }.use { kmsClient -> + kmsClient.scheduleKeyDeletion(request) + } + return delete.keyState?.value == "PendingDeletion" + } + + + companion object { + + + suspend fun generateKey(keyType: KeyType, config: AWSKeyMetadataSDK): AWSKey { + val request = CreateKeyRequest { + this.description = description + keySpec = KeySpec.fromValue(keyTypeToAwsKeyMapping(keyType)) + keyUsage = KeyUsageType.SignVerify + } + val response = KmsClient { region = config.region }.use { kmsClient -> + kmsClient.createKey(request) + } + + val keyid = response.keyMetadata?.keyId ?: throw IllegalStateException("Key ID not returned") + val publicKey = getAwsPublicKey(config, keyid) + val keyType = response.keyMetadata?.keySpec?.value + return AWSKey( + config = config, + id = keyid, + _publicKey = publicKey.exportJWK(), + _keyType = awsKeyToKeyTypeMapping(keyType.toString()) + ) + } + + + suspend fun getAwsPublicKey(config: AWSKeyMetadataSDK, keyId: String): Key { + KmsClient { region = config.region }.use { kmsClient -> + val pk = kmsClient.getPublicKey(GetPublicKeyRequest { + this.keyId = keyId + }).publicKey ?: throw IllegalStateException("Public key not returned") + val encodedPk = pk.encodeToBase64() + + val pemKey = """ +-----BEGIN PUBLIC KEY----- +$encodedPk +-----END PUBLIC KEY----- +""".trimIndent() + val keyJWK = JWKKey.importPEM(pemKey) + return keyJWK.getOrThrow() + } + } + + private fun keyTypeToAwsKeyMapping(type: KeyType) = when (type) { + KeyType.secp256r1 -> "ECC_NIST_P256" + KeyType.secp256k1 -> "ECC_SECG_P256K1" + KeyType.RSA -> "RSA_2048" + else -> throw KeyTypeNotSupportedException(type.name) + } + + private fun awsKeyToKeyTypeMapping(type: String) = when (type) { + "ECC_NIST_P256" -> KeyType.secp256r1 + "ECC_SECG_P256K1" -> KeyType.secp256k1 + "RSA_2048" -> KeyType.RSA + else -> throw KeyTypeNotSupportedException(type) + } + } + +} \ No newline at end of file diff --git a/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/AWSKeyMetadataSDK.kt b/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/AWSKeyMetadataSDK.kt new file mode 100644 index 000000000..94347f044 --- /dev/null +++ b/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/AWSKeyMetadataSDK.kt @@ -0,0 +1,9 @@ +package id.walt.crypto.keys.aws + +import kotlinx.serialization.Serializable + + +@Serializable +data class AWSKeyMetadataSDK ( + val region: String +) \ No newline at end of file From de2d2d806ce63b116461fdb82eb63461890ab1fd Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 15:53:09 +0100 Subject: [PATCH 02/21] Rename AWSKey to AWSKeyRestAPI for clarity and consistency. --- .../kotlin/id/walt/crypto/keys/KeySerialization.kt | 4 ++-- .../kotlin/id/walt/crypto/keys/aws/AWSKeyCreator.kt | 2 +- .../walt/crypto/keys/aws/{AWSKey.kt => AWSKeyRestAPI.kt} | 8 ++++---- .../waltid-crypto/src/commonTest/kotlin/AWSKeyTest.kt | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) rename waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/aws/{AWSKey.kt => AWSKeyRestAPI.kt} (99%) diff --git a/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeySerialization.kt b/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeySerialization.kt index a15119e7b..d974ba418 100644 --- a/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeySerialization.kt +++ b/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeySerialization.kt @@ -1,6 +1,6 @@ package id.walt.crypto.keys -import id.walt.crypto.keys.aws.AWSKey +import id.walt.crypto.keys.aws.AWSKeyRestAPI import id.walt.crypto.keys.azure.AzureKey import id.walt.crypto.keys.jwk.JWKKey import id.walt.crypto.keys.oci.OCIKeyRestApi @@ -28,7 +28,7 @@ object KeySerialization { subclass(JWKKey::class) subclass(TSEKey::class) subclass(OCIKeyRestApi::class) - subclass(AWSKey::class) + subclass(AWSKeyRestAPI::class) subclass(AzureKey::class) } } diff --git a/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/aws/AWSKeyCreator.kt b/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/aws/AWSKeyCreator.kt index 16be30f85..a0c0148e5 100644 --- a/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/aws/AWSKeyCreator.kt +++ b/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/aws/AWSKeyCreator.kt @@ -4,6 +4,6 @@ import id.walt.crypto.keys.KeyType interface AWSKeyCreator { - suspend fun generate(type: KeyType, metadata: AWSKeyMetadata): AWSKey + suspend fun generate(type: KeyType, metadata: AWSKeyMetadata): AWSKeyRestAPI } \ No newline at end of file diff --git a/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/aws/AWSKey.kt b/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/aws/AWSKeyRestAPI.kt similarity index 99% rename from waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/aws/AWSKey.kt rename to waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/aws/AWSKeyRestAPI.kt index fac1482c9..96d16be36 100644 --- a/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/aws/AWSKey.kt +++ b/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/aws/AWSKeyRestAPI.kt @@ -62,8 +62,8 @@ var timeoutAt: Instant? = null @JsExport @Suppress("TRANSIENT_IS_REDUNDANT") @Serializable -@SerialName("aws") -class AWSKey( +@SerialName("aws-rest-api") +class AWSKeyRestAPI( val config: AWSKeyMetadata, val id: String, private var _publicKey: String? = null, @@ -608,7 +608,7 @@ $public @JsExport.Ignore - override suspend fun generate(type: KeyType, config: AWSKeyMetadata): AWSKey { + override suspend fun generate(type: KeyType, config: AWSKeyMetadata): AWSKeyRestAPI { if (config.auth.accessKeyId.isNullOrBlank() && config.auth.secretAccessKey.isNullOrBlank()) { getAccess(config) @@ -648,7 +648,7 @@ $public val publicKey = getPublicKey(config, keyId.toString()) - return AWSKey( + return AWSKeyRestAPI( config = config, id = keyId.toString(), _publicKey = publicKey.exportJWK(), diff --git a/waltid-libraries/crypto/waltid-crypto/src/commonTest/kotlin/AWSKeyTest.kt b/waltid-libraries/crypto/waltid-crypto/src/commonTest/kotlin/AWSKeyTest.kt index 4b0607bc5..b3d9e0aec 100644 --- a/waltid-libraries/crypto/waltid-crypto/src/commonTest/kotlin/AWSKeyTest.kt +++ b/waltid-libraries/crypto/waltid-crypto/src/commonTest/kotlin/AWSKeyTest.kt @@ -1,7 +1,7 @@ import AWSKeyTest.Config.payloadJWS import id.walt.crypto.keys.KeyType import id.walt.crypto.keys.aws.AWSAuth -import id.walt.crypto.keys.aws.AWSKey +import id.walt.crypto.keys.aws.AWSKeyRestAPI import id.walt.crypto.keys.aws.AWSKeyMetadata import io.ktor.utils.io.core.* import kotlinx.coroutines.test.runTest @@ -43,7 +43,7 @@ class AWSKeyTest { }.fold(onSuccess = { it }, onFailure = { false }) - lateinit var keys: List + lateinit var keys: List @Test @@ -77,7 +77,7 @@ class AWSKeyTest { ) keys = Config.TESTABLE_KEY_TYPES.map { - AWSKey.generate(it, awsMetadata).also { key -> + AWSKeyRestAPI.generate(it, awsMetadata).also { key -> println("Generated key: ${key.keyType} - ${key.getKeyId()}") assertNotNull(key, "Key generation failed for $it") assertNotNull(key.getKeyId(), "Key ID should not be null") From 0d49ab8a3fa1f3e0e4c1b9c68072e7bd29aab988 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 15:53:39 +0100 Subject: [PATCH 03/21] Add dependency on waltid-crypto-aws in issuer API --- waltid-services/waltid-issuer-api/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/waltid-services/waltid-issuer-api/build.gradle.kts b/waltid-services/waltid-issuer-api/build.gradle.kts index bc00d7bff..dc9ae9420 100644 --- a/waltid-services/waltid-issuer-api/build.gradle.kts +++ b/waltid-services/waltid-issuer-api/build.gradle.kts @@ -92,6 +92,8 @@ dependencies { // walt.id api(project(":waltid-libraries:crypto:waltid-crypto")) + implementation(project(":waltid-libraries:crypto:waltid-crypto-aws")) + api(project(":waltid-libraries:waltid-did")) api(project(":waltid-libraries:credentials:waltid-verifiable-credentials")) From 127d68f6178c1b345c6e285aaa0f0831d865c2c9 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 15:53:55 +0100 Subject: [PATCH 04/21] Add AWS crypto library to wallet API dependencies --- waltid-services/waltid-wallet-api/build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/waltid-services/waltid-wallet-api/build.gradle.kts b/waltid-services/waltid-wallet-api/build.gradle.kts index 353c7ad8c..d3dc76f15 100644 --- a/waltid-services/waltid-wallet-api/build.gradle.kts +++ b/waltid-services/waltid-wallet-api/build.gradle.kts @@ -121,6 +121,9 @@ dependencies { implementation(project(":waltid-libraries:crypto:waltid-crypto")) implementation(project(":waltid-libraries:crypto:waltid-crypto-oci")) + implementation(project(":waltid-libraries:crypto:waltid-crypto-aws")) + + implementation(project(":waltid-libraries:waltid-did")) implementation(project(":waltid-libraries:credentials:waltid-verification-policies")) From e768153301a7beb3f3152b24eb3ab304973c5ddc Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 15:54:09 +0100 Subject: [PATCH 05/21] Add AWS crypto library to wallet API dependencies --- .../walt/issuer/issuance/IssuanceExamples.kt | 31 +++++++++++++++++-- .../id/walt/issuer/issuance/IssuerApi.kt | 4 +++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuanceExamples.kt b/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuanceExamples.kt index c1e90b641..cd28ee9a9 100644 --- a/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuanceExamples.kt +++ b/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuanceExamples.kt @@ -12,9 +12,10 @@ import kotlinx.serialization.json.buildJsonArray object IssuanceExamples { - private inline fun typedValueExampleDescriptorDsl(content: String): ValueExampleDescriptorDsl.() -> Unit = { - value = Json.decodeFromString(content) - } + private inline fun typedValueExampleDescriptorDsl(content: String): ValueExampleDescriptorDsl.() -> Unit = + { + value = Json.decodeFromString(content) + } // language=json val openBadgeCredentialData = """ @@ -889,6 +890,29 @@ object IssuanceExamples { """.trimIndent() ) + + //language=JSON + val issuerOnboardingRequestAwsSdkExample = typedValueExampleDescriptorDsl( + """ + { + "key": + { + "backend": "aws", + "keyType": "secp256r1", + "config": + { + "region": "eu-central-1" + + } + }, + "did": + { + "method": "jwk" + } + } + """.trimIndent() + ) + //language=JSON val issuerOnboardingRequestAwsRestApiExampleWithRole = typedValueExampleDescriptorDsl( """ @@ -941,6 +965,7 @@ object IssuanceExamples { """.trimIndent() ) + //language=JSON val issuerOnboardingResponseOciRestApiExample = typedValueExampleDescriptorDsl( """ diff --git a/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuerApi.kt b/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuerApi.kt index 22eabd892..2f5b318f9 100644 --- a/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuerApi.kt +++ b/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuerApi.kt @@ -128,6 +128,10 @@ fun Application.issuerApi() { "did:jwk + Azure REST API key (Azure - Secp256r1)", IssuanceExamples.issuerOnboardingRequestAzureRestApiExample ) + example( + "did:jwk + AWS SDK key (AWS - Secp256r1)", + IssuanceExamples.issuerOnboardingRequestAwsSdkExample + ) required = true } } From 6e0feab88444596cdcd54fb8df6b4f47198e85fe Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 15:54:34 +0100 Subject: [PATCH 06/21] Refactor AWS key generation to use AWSKeyRestAPI. --- .../commonMain/kotlin/id/walt/crypto/keys/KeyManager.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeyManager.kt b/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeyManager.kt index 792d8f58d..d0f0adb69 100644 --- a/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeyManager.kt +++ b/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeyManager.kt @@ -3,7 +3,7 @@ package id.walt.crypto.keys import id.walt.crypto.exceptions.KeyBackendNotSupportedException import id.walt.crypto.exceptions.KeyTypeMissingException import id.walt.crypto.exceptions.KeyTypeNotSupportedException -import id.walt.crypto.keys.aws.AWSKey +import id.walt.crypto.keys.aws.AWSKeyRestAPI import id.walt.crypto.keys.azure.AzureKey import id.walt.crypto.keys.jwk.JWKKey import id.walt.crypto.keys.oci.OCIKeyRestApi @@ -38,8 +38,8 @@ object KeyManager { Json.decodeFromJsonElement(generateRequest.config!!) ) } - register("aws") { generateRequest: KeyGenerationRequest -> - AWSKey.generate( + register("aws-rest-api") { generateRequest: KeyGenerationRequest -> + AWSKeyRestAPI.generate( generateRequest.keyType, Json.decodeFromJsonElement(generateRequest.config!!) ) @@ -51,6 +51,7 @@ object KeyManager { Json.decodeFromJsonElement(generateRequest.config!!) ) } + } fun registerByType(type: KType, typeId: String, createFunction: suspend (KeyGenerationRequest) -> Key) { From 1ff3fee44a4fe09d62a9db40d604a3782f78ca2f Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 15:55:47 +0100 Subject: [PATCH 07/21] Add AWS crypto module to settings.gradle.kts --- settings.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/settings.gradle.kts b/settings.gradle.kts index 4f87dc9bf..2357e4d21 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,6 +18,7 @@ val modules = listOf( * "$libraries:crypto".group( "waltid-crypto", "waltid-crypto-oci", + "crypto:waltid-crypto-aws", "waltid-crypto-android" whenEnabled enableAndroidBuild, "waltid-crypto-ios" whenEnabled enableIosBuild, "waltid-target-ios" whenEnabled enableIosBuild, From 2bd325a3295da2e58c4f9eb3779058ec3d288e03 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 15:56:08 +0100 Subject: [PATCH 08/21] Add initialization of WaltCryptoAws in issuer service --- .../waltid-issuer-api/src/main/kotlin/id/walt/issuer/Main.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/Main.kt b/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/Main.kt index 91c4d68ba..5abbcedde 100644 --- a/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/Main.kt +++ b/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/Main.kt @@ -6,6 +6,7 @@ import id.walt.commons.ServiceMain import id.walt.commons.featureflag.CommonsFeatureCatalog import id.walt.commons.featureflag.FeatureManager.whenFeature import id.walt.commons.web.WebService +import id.walt.crypto.keys.aws.WaltCryptoAws import id.walt.did.helpers.WaltidServices import id.walt.issuer.entra.entraIssuance import id.walt.issuer.issuance.OidcApi.oidcApi @@ -26,6 +27,7 @@ suspend fun main(args: Array) { ), init = { WaltidServices.minimalInit() + WaltCryptoAws.init() }, run = WebService(Application::issuerModule).run() ) From 1353935a72c9b23dcd0313ddc632ed468f7144d6 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 15:56:20 +0100 Subject: [PATCH 09/21] Add AWS key management support in WaltCryptoAws --- .../id.walt.crypto.keys.aws/WaltCryptoAws.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/WaltCryptoAws.kt diff --git a/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/WaltCryptoAws.kt b/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/WaltCryptoAws.kt new file mode 100644 index 000000000..ab6e4a460 --- /dev/null +++ b/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/WaltCryptoAws.kt @@ -0,0 +1,20 @@ +package id.walt.crypto.keys.aws + +import id.walt.crypto.keys.KeyGenerationRequest +import id.walt.crypto.keys.KeyManager +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromJsonElement + +object WaltCryptoAws { + fun init() { + KeyManager.register("aws") { generateRequest: KeyGenerationRequest -> + AWSKey.generateKey( + generateRequest.keyType, + Json.decodeFromJsonElement(generateRequest.config!!) + ) + } + + + } + +} \ No newline at end of file From 58095fe56a29e9f26040488eebb519eae662b1d5 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 16:00:27 +0100 Subject: [PATCH 10/21] Add module for AWS-based cryptographic functionality --- settings.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/settings.gradle.kts b/settings.gradle.kts index 2357e4d21..000beec5e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -95,3 +95,5 @@ plugins { } rootProject.name = "waltid-identity" +include("waltid-libraries:crypto:waltid-crypto-aws") +findProject(":waltid-libraries:crypto:waltid-crypto-aws")?.name = "waltid-crypto-aws" From 8ff2d214ebf1ff35f2c90896d31eb18795932d91 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 17:20:52 +0100 Subject: [PATCH 11/21] Add waltid-crypto-aws module to Dockerfile copies --- waltid-services/waltid-issuer-api/Dockerfile | 2 ++ waltid-services/waltid-wallet-api/Dockerfile | 2 ++ 2 files changed, 4 insertions(+) diff --git a/waltid-services/waltid-issuer-api/Dockerfile b/waltid-services/waltid-issuer-api/Dockerfile index 2fabca878..4d5232a99 100644 --- a/waltid-services/waltid-issuer-api/Dockerfile +++ b/waltid-services/waltid-issuer-api/Dockerfile @@ -7,6 +7,7 @@ COPY waltid-libraries/credentials/waltid-verifiable-credentials/build.gradle.kts COPY waltid-libraries/credentials/waltid-verification-policies/build.gradle.kts /work/waltid-libraries/credentials/waltid-verification-policies/ COPY waltid-libraries/credentials/waltid-dif-definitions-parser/build.gradle.kts /work/waltid-libraries/credentials/waltid-dif-definitions-parser/ COPY waltid-libraries/crypto/waltid-crypto/build.gradle.kts /work/waltid-libraries/crypto/waltid-crypto/ +COPY waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts /work/waltid-libraries/crypto/waltid-crypto-aws/ COPY waltid-libraries/waltid-did/build.gradle.kts /work/waltid-libraries/waltid-did/ COPY waltid-libraries/protocols/waltid-openid4vc/build.gradle.kts /work/waltid-libraries/protocols/waltid-openid4vc/ COPY waltid-libraries/sdjwt/waltid-sdjwt/build.gradle.kts /work/waltid-libraries/sdjwt/waltid-sdjwt/ @@ -23,6 +24,7 @@ COPY waltid-libraries/credentials/waltid-verifiable-credentials/. /work/waltid-l COPY waltid-libraries/credentials/waltid-verification-policies/. /work/waltid-libraries/credentials/waltid-verification-policies COPY waltid-libraries/credentials/waltid-dif-definitions-parser/. /work/waltid-libraries/credentials/waltid-dif-definitions-parser COPY waltid-libraries/crypto/waltid-crypto/. /work/waltid-libraries/crypto/waltid-crypto +COPY waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts /work/waltid-libraries/crypto/waltid-crypto-aws/ COPY waltid-libraries/waltid-did/. /work/waltid-libraries/waltid-did COPY waltid-libraries/protocols/waltid-openid4vc/. /work/waltid-libraries/protocols/waltid-openid4vc COPY waltid-libraries/sdjwt/waltid-sdjwt/. /work/waltid-libraries/sdjwt/waltid-sdjwt diff --git a/waltid-services/waltid-wallet-api/Dockerfile b/waltid-services/waltid-wallet-api/Dockerfile index adac3501f..6f7c192f1 100644 --- a/waltid-services/waltid-wallet-api/Dockerfile +++ b/waltid-services/waltid-wallet-api/Dockerfile @@ -7,6 +7,7 @@ COPY waltid-libraries/protocols/waltid-openid4vc/build.gradle.kts /work/waltid-l COPY waltid-libraries/sdjwt/waltid-sdjwt/build.gradle.kts /work/waltid-libraries/sdjwt/waltid-sdjwt/ COPY waltid-libraries/crypto/waltid-crypto/build.gradle.kts /work/waltid-libraries/crypto/waltid-crypto/ COPY waltid-libraries/crypto/waltid-crypto-oci/build.gradle.kts /work/waltid-libraries/crypto/waltid-crypto-oci/ +COPY waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts /work/waltid-libraries/crypto/waltid-crypto-aws/ COPY waltid-libraries/waltid-did/build.gradle.kts /work/waltid-libraries/waltid-did/ COPY waltid-libraries/credentials/waltid-mdoc-credentials/build.gradle.kts /work/waltid-libraries/credentials/waltid-mdoc-credentials/ COPY waltid-libraries/credentials/waltid-verifiable-credentials/build.gradle.kts /work/waltid-libraries/credentials/waltid-verifiable-credentials/ @@ -27,6 +28,7 @@ COPY waltid-libraries/protocols/waltid-openid4vc/. /work/waltid-libraries/protoc COPY waltid-libraries/sdjwt/waltid-sdjwt/. /work/waltid-libraries/sdjwt/waltid-sdjwt COPY waltid-libraries/crypto/waltid-crypto/. /work/waltid-libraries/crypto/waltid-crypto COPY waltid-libraries/crypto/waltid-crypto-oci/. /work/waltid-libraries/crypto/waltid-crypto-oci +COPY waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts /work/waltid-libraries/crypto/waltid-crypto-aws/ COPY waltid-libraries/waltid-did/. /work/waltid-libraries/waltid-did COPY waltid-libraries/credentials/waltid-mdoc-credentials/. /work/waltid-libraries/credentials/waltid-mdoc-credentials COPY waltid-libraries/credentials/waltid-verifiable-credentials/. /work/waltid-libraries/credentials/waltid-verifiable-credentials From a63156b84a7d285d597de840998db4a1ee5c0a36 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 17:23:35 +0100 Subject: [PATCH 12/21] Update Dockerfiles to copy entire crypto-aws directory --- waltid-services/waltid-issuer-api/Dockerfile | 2 +- waltid-services/waltid-wallet-api/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/waltid-services/waltid-issuer-api/Dockerfile b/waltid-services/waltid-issuer-api/Dockerfile index 4d5232a99..a78a300f7 100644 --- a/waltid-services/waltid-issuer-api/Dockerfile +++ b/waltid-services/waltid-issuer-api/Dockerfile @@ -24,7 +24,7 @@ COPY waltid-libraries/credentials/waltid-verifiable-credentials/. /work/waltid-l COPY waltid-libraries/credentials/waltid-verification-policies/. /work/waltid-libraries/credentials/waltid-verification-policies COPY waltid-libraries/credentials/waltid-dif-definitions-parser/. /work/waltid-libraries/credentials/waltid-dif-definitions-parser COPY waltid-libraries/crypto/waltid-crypto/. /work/waltid-libraries/crypto/waltid-crypto -COPY waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts /work/waltid-libraries/crypto/waltid-crypto-aws/ +COPY waltid-libraries/crypto/waltid-crypto-aws/. /work/waltid-libraries/crypto/waltid-crypto-aws COPY waltid-libraries/waltid-did/. /work/waltid-libraries/waltid-did COPY waltid-libraries/protocols/waltid-openid4vc/. /work/waltid-libraries/protocols/waltid-openid4vc COPY waltid-libraries/sdjwt/waltid-sdjwt/. /work/waltid-libraries/sdjwt/waltid-sdjwt diff --git a/waltid-services/waltid-wallet-api/Dockerfile b/waltid-services/waltid-wallet-api/Dockerfile index 6f7c192f1..6b7082dd4 100644 --- a/waltid-services/waltid-wallet-api/Dockerfile +++ b/waltid-services/waltid-wallet-api/Dockerfile @@ -28,7 +28,7 @@ COPY waltid-libraries/protocols/waltid-openid4vc/. /work/waltid-libraries/protoc COPY waltid-libraries/sdjwt/waltid-sdjwt/. /work/waltid-libraries/sdjwt/waltid-sdjwt COPY waltid-libraries/crypto/waltid-crypto/. /work/waltid-libraries/crypto/waltid-crypto COPY waltid-libraries/crypto/waltid-crypto-oci/. /work/waltid-libraries/crypto/waltid-crypto-oci -COPY waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts /work/waltid-libraries/crypto/waltid-crypto-aws/ +COPY waltid-libraries/crypto/waltid-crypto-aws/. /work/waltid-libraries/crypto/waltid-crypto-aws COPY waltid-libraries/waltid-did/. /work/waltid-libraries/waltid-did COPY waltid-libraries/credentials/waltid-mdoc-credentials/. /work/waltid-libraries/credentials/waltid-mdoc-credentials COPY waltid-libraries/credentials/waltid-verifiable-credentials/. /work/waltid-libraries/credentials/waltid-verifiable-credentials From efc8c689cec17ca1fe0a848ae78a848ae2a796ea Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 17:28:07 +0100 Subject: [PATCH 13/21] Add build script for AWS-backed crypto module --- .../crypto/waltid-crypto-aws/build.gradle.kts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts diff --git a/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts b/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts new file mode 100644 index 000000000..591249e32 --- /dev/null +++ b/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + kotlin("jvm") version "2.0.21" + kotlin("plugin.serialization") +} + +group = "id.walt.crypto" +version = "1.0.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(kotlin("test")) + // walt.id + api(project(":waltid-libraries:crypto:waltid-crypto")) + + // JSON + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") + + // Coroutines + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") + + //aws + implementation("aws.sdk.kotlin:kms:1.3.91") + implementation("aws.sdk.kotlin:dynamodb:1.3.91") + implementation("aws.sdk.kotlin:secretsmanager:1.3.91") + + + // JOSE + implementation("com.nimbusds:nimbus-jose-jwt:9.41.1") +} + +tasks.test { + useJUnitPlatform() +} +kotlin { + jvmToolchain(17) +} \ No newline at end of file From 365cb0084d44a4c4a33c719aede662ddfd0c507d Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 17:48:09 +0100 Subject: [PATCH 14/21] Add support for dynamic external key type registration --- .../id.walt.crypto.keys.aws/WaltCryptoAws.kt | 5 ++-- .../id/walt/crypto/keys/KeySerialization.kt | 26 +++++++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/WaltCryptoAws.kt b/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/WaltCryptoAws.kt index ab6e4a460..38a4df662 100644 --- a/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/WaltCryptoAws.kt +++ b/waltid-libraries/crypto/waltid-crypto-aws/src/main/kotlin/id.walt.crypto.keys.aws/WaltCryptoAws.kt @@ -2,6 +2,7 @@ package id.walt.crypto.keys.aws import id.walt.crypto.keys.KeyGenerationRequest import id.walt.crypto.keys.KeyManager +import id.walt.crypto.keys.KeySerialization import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromJsonElement @@ -13,8 +14,8 @@ object WaltCryptoAws { Json.decodeFromJsonElement(generateRequest.config!!) ) } - + KeySerialization.registerExternalKeyType(AWSKey::class) } +} -} \ No newline at end of file diff --git a/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeySerialization.kt b/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeySerialization.kt index d974ba418..61402686c 100644 --- a/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeySerialization.kt +++ b/waltid-libraries/crypto/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/KeySerialization.kt @@ -6,16 +6,19 @@ import id.walt.crypto.keys.jwk.JWKKey import id.walt.crypto.keys.oci.OCIKeyRestApi import id.walt.crypto.keys.tse.TSEKey import id.walt.crypto.utils.JsonUtils.toJsonElement +import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.encodeToString import kotlinx.serialization.json.* import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.subclass +import kotlinx.serialization.serializer import love.forte.plugin.suspendtrans.annotation.JsPromise import love.forte.plugin.suspendtrans.annotation.JvmAsync import love.forte.plugin.suspendtrans.annotation.JvmBlocking import kotlin.js.ExperimentalJsExport import kotlin.js.JsExport +import kotlin.reflect.KClass // TODO: Deprecate this in favour of KeyManager @@ -23,7 +26,7 @@ import kotlin.js.JsExport @JsExport object KeySerialization { - private val keySerializationModule = SerializersModule { + private var keySerializationModule = SerializersModule { polymorphic(Key::class) { subclass(JWKKey::class) subclass(TSEKey::class) @@ -31,13 +34,32 @@ object KeySerialization { subclass(AWSKeyRestAPI::class) subclass(AzureKey::class) } + } - private val keySerializationJson = Json { + private var keySerializationJson = Json { serializersModule = keySerializationModule } + @OptIn(InternalSerializationApi::class) + fun registerExternalKeyType(keyClass: KClass) { + keySerializationModule = SerializersModule { + polymorphic(Key::class) { + subclass(JWKKey::class) + subclass(TSEKey::class) + subclass(OCIKeyRestApi::class) + subclass(AWSKeyRestAPI::class) + subclass(AzureKey::class) + subclass(keyClass, keyClass.serializer()) + } + } + + keySerializationJson = Json { + serializersModule = keySerializationModule + } + } + fun serializeKey(key: Key): String = keySerializationJson.encodeToString(key) @Suppress("NON_EXPORTABLE_TYPE") From 658ee6f9ea133e16a8dbd6c9c374dbf580e1c337 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 18:26:20 +0100 Subject: [PATCH 15/21] Add SDK key generation support in wallet-api --- .../src/main/kotlin/id/walt/webwallet/Main.kt | 2 ++ .../walt/webwallet/web/controllers/KeyController.kt | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/Main.kt b/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/Main.kt index 49ed21b96..00ab680e3 100644 --- a/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/Main.kt +++ b/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/Main.kt @@ -6,6 +6,7 @@ import id.walt.commons.ServiceMain import id.walt.commons.featureflag.CommonsFeatureCatalog import id.walt.commons.featureflag.FeatureManager.whenFeature import id.walt.commons.web.WebService +import id.walt.crypto.keys.aws.WaltCryptoAws import id.walt.crypto.keys.oci.WaltCryptoOci import id.walt.did.helpers.WaltidServices import id.walt.webwallet.db.Db @@ -39,6 +40,7 @@ suspend fun main(args: Array) { webWalletSetup() WaltidServices.minimalInit() WaltCryptoOci.init() + WaltCryptoAws.init() Db.start() }, run = WebService(Application::webWalletModule).run() diff --git a/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/web/controllers/KeyController.kt b/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/web/controllers/KeyController.kt index cfc386bb0..50abff8f9 100644 --- a/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/web/controllers/KeyController.kt +++ b/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/web/controllers/KeyController.kt @@ -104,7 +104,7 @@ fun Application.keys() = walletRoute { } example("AWS key generation request") { value = KeyGenerationRequest( - backend = "aws", + backend = "aws-rest-api", keyType = KeyType.secp256r1, config = buildJsonObject { putJsonObject("auth") { @@ -129,6 +129,15 @@ fun Application.keys() = walletRoute { } ) } + example("AWS key generation request SDK") { + value = KeyGenerationRequest( + backend = "aws", + keyType = KeyType.secp256r1, + config = buildJsonObject { + put("region", JsonPrimitive("eu-central-1")) + } + ) + } } } response { From 8ff33ff486d02a225ae91a9dce01ec0cccfb0c59 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Thu, 12 Dec 2024 18:48:43 +0100 Subject: [PATCH 16/21] Update AWS backend configuration to use aws-rest-api for rest api key --- .../main/kotlin/id/walt/issuer/issuance/IssuanceExamples.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuanceExamples.kt b/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuanceExamples.kt index cd28ee9a9..4037b9e43 100644 --- a/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuanceExamples.kt +++ b/waltid-services/waltid-issuer-api/src/main/kotlin/id/walt/issuer/issuance/IssuanceExamples.kt @@ -870,7 +870,7 @@ object IssuanceExamples { { "key": { - "backend": "aws", + "backend": "aws-rest-api", "keyType": "secp256r1", "config": { @@ -919,7 +919,7 @@ object IssuanceExamples { { "key": { - "backend": "aws", + "backend": "aws-rest-api", "keyType": "secp256r1", "config": { From 8bbdf62e51f732a5ac7a505eaf0625e553b5437f Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Fri, 13 Dec 2024 11:28:59 +0100 Subject: [PATCH 17/21] Refactor AWS crypto module and add Maven publishing. --- settings.gradle.kts | 6 +- .../crypto/waltid-crypto-aws/build.gradle.kts | 83 +++++++++++++++++-- 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 000beec5e..2fb13aca7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,7 +18,7 @@ val modules = listOf( * "$libraries:crypto".group( "waltid-crypto", "waltid-crypto-oci", - "crypto:waltid-crypto-aws", + "waltid-crypto-aws", "waltid-crypto-android" whenEnabled enableAndroidBuild, "waltid-crypto-ios" whenEnabled enableIosBuild, "waltid-target-ios" whenEnabled enableIosBuild, @@ -95,5 +95,5 @@ plugins { } rootProject.name = "waltid-identity" -include("waltid-libraries:crypto:waltid-crypto-aws") -findProject(":waltid-libraries:crypto:waltid-crypto-aws")?.name = "waltid-crypto-aws" +//include("waltid-libraries:crypto:waltid-crypto-aws") +//findProject(":waltid-libraries:crypto:waltid-crypto-aws")?.name = "waltid-crypto-aws" diff --git a/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts b/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts index 591249e32..c05e0bbca 100644 --- a/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts +++ b/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts @@ -1,13 +1,15 @@ plugins { kotlin("jvm") version "2.0.21" - kotlin("plugin.serialization") + kotlin("plugin.serialization") version "2.0.21" + id("com.github.ben-manes.versions") + id("maven-publish") } group = "id.walt.crypto" -version = "1.0.0-SNAPSHOT" repositories { mavenCentral() + maven("https://jitpack.io") } dependencies { @@ -19,21 +21,90 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") // Coroutines - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") - //aws + // AWS implementation("aws.sdk.kotlin:kms:1.3.91") implementation("aws.sdk.kotlin:dynamodb:1.3.91") implementation("aws.sdk.kotlin:secretsmanager:1.3.91") - // JOSE implementation("com.nimbusds:nimbus-jose-jwt:9.41.1") } +java { + sourceCompatibility = JavaVersion.VERSION_15 + targetCompatibility = JavaVersion.VERSION_15 + withJavadocJar() + withSourcesJar() +} + tasks.test { useJUnitPlatform() } + kotlin { - jvmToolchain(17) + jvmToolchain(15) + sourceSets { + all { + languageSettings.enableLanguageFeature("InlineClasses") + } + } +} + +publishing { + publications { + create("maven") { + from(components["java"]) + + pom { + name.set("Walt.id Crypto AWS") + description.set("Walt.id Crypto AWS Integration") + url.set("https://walt.id") + + licenses { + license { + name.set("Apache License 2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0") + } + } + + developers { + developer { + id.set("walt.id") + name.set("walt.id") + email.set("office@walt.id") + } + } + } + } + } + + repositories { + maven { + val releasesRepoUrl = uri("https://maven.waltid.dev/releases") + val snapshotsRepoUrl = uri("https://maven.waltid.dev/snapshots") + url = uri( + if (version.toString().endsWith("SNAPSHOT") + ) snapshotsRepoUrl else releasesRepoUrl + ) + + val envUsername = System.getenv("MAVEN_USERNAME") + val envPassword = System.getenv("MAVEN_PASSWORD") + + val usernameFile = File("$rootDir/secret_maven_username.txt") + val passwordFile = File("$rootDir/secret_maven_password.txt") + + val secretMavenUsername = envUsername ?: usernameFile.let { + if (it.isFile) it.readLines().first() else "" + } + val secretMavenPassword = envPassword ?: passwordFile.let { + if (it.isFile) it.readLines().first() else "" + } + credentials { + username = secretMavenUsername + password = secretMavenPassword + } + } + } } \ No newline at end of file From 486b0251462245cd009bd6e3f9d699437c5e9410 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Sun, 15 Dec 2024 15:00:35 +0100 Subject: [PATCH 18/21] Add AWS key tests and update dependencies Include comprehensive tests for AWS key operations, such as key creation, signing, verification, and deletion. Also, add kotlinx-coroutines-test to dependencies and disable test execution by default. --- .../crypto/waltid-crypto-aws/build.gradle.kts | 7 ++ .../src/test/kotlin/AwsSDKKeyTest.kt | 96 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 waltid-libraries/crypto/waltid-crypto-aws/src/test/kotlin/AwsSDKKeyTest.kt diff --git a/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts b/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts index c05e0bbca..dfd525a9c 100644 --- a/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts +++ b/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts @@ -14,6 +14,8 @@ repositories { dependencies { testImplementation(kotlin("test")) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0") + // walt.id api(project(":waltid-libraries:crypto:waltid-crypto")) @@ -43,6 +45,11 @@ tasks.test { useJUnitPlatform() } +tasks.withType { + enabled = false +} + + kotlin { jvmToolchain(15) sourceSets { diff --git a/waltid-libraries/crypto/waltid-crypto-aws/src/test/kotlin/AwsSDKKeyTest.kt b/waltid-libraries/crypto/waltid-crypto-aws/src/test/kotlin/AwsSDKKeyTest.kt new file mode 100644 index 000000000..aa1131f8e --- /dev/null +++ b/waltid-libraries/crypto/waltid-crypto-aws/src/test/kotlin/AwsSDKKeyTest.kt @@ -0,0 +1,96 @@ +import id.walt.crypto.keys.KeyType +import id.walt.crypto.keys.aws.AWSKey +import id.walt.crypto.keys.aws.AWSKeyMetadataSDK +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +class AwsSDKKeyTest { + + private object Config { + val payloadJWS = JsonObject( + mapOf( + "sub" to JsonPrimitive("16bb17e0-e733-4622-9384-122bc2fc6290"), + "iss" to JsonPrimitive("http://localhost:3000"), + "aud" to JsonPrimitive("TOKEN") + ) + ) + const val payload = "Hello, World!" + val TESTABLE_KEY_TYPES = listOf(KeyType.RSA, KeyType.secp256r1, KeyType.secp256k1) + } + + private lateinit var keys: List + + @Test + fun testAws() = runTest { + awsTestKeyCreation() + awsTestPublicKeys() + awsTestSignRaw() + awsTestSignJws() + awsTestDeleteKey() + } + + private suspend fun awsTestKeyCreation() { + keys = Config.TESTABLE_KEY_TYPES.map { + AWSKey.generateKey( + it, + AWSKeyMetadataSDK( + region = "eu-central-1", + ) + ).also { + println("Generated key: ${it.keyType} - ${it.getKeyId()}") + assertNotNull(it, "Key generation failed for $it") + assertNotNull(it.getKeyId(), "Key ID should not be null") + assertTrue(it.getKeyId().isNotBlank(), "Key ID should not be blank") + } + } + println("Generated ${keys.size} keys") + assertTrue(keys.isNotEmpty(), "No keys were created") + } + + private suspend fun awsTestPublicKeys() { + keys.forEach { key -> + val publicKey = key.getPublicKey() + println("Public key: ${publicKey.exportJWK()}") + assertNotNull(publicKey, "Public key should not be null") + } + } + + private suspend fun awsTestSignRaw() { + keys.forEach { key -> + println("RAW sign/verification test with key type: ${key.keyType}") + val signature = key.signRaw(Config.payload.toByteArray()) + val verified = key.verifyRaw(signature, Config.payload.toByteArray()) + assertNotNull(signature, "Signature should not be null") + assertTrue(signature.isNotEmpty(), "Signature should not be empty") + assertTrue(verified.isSuccess, "Raw signature verification failed") + } + } + + private suspend fun awsTestSignJws() { + keys.forEach { key -> + println("JWS sign/verification test with key type: ${key.keyType}") + val signature = key.signJws(Config.payloadJWS.toString().toByteArray()) + val verified = key.verifyJws(signature) + assertNotNull(signature, "JWS signature should not be null") + assertTrue(signature.isNotEmpty(), "JWS signature should not be empty") + assertTrue(verified.isSuccess, "JWS signature verification failed") + assertEquals(Config.payloadJWS, verified.getOrThrow(), "JWS payload mismatch after verification") + } + } + + private suspend fun awsTestDeleteKey() { + keys.forEach { key -> + println("Deleting key: ${key.getKeyId()}") + val delete = key.deleteKey() + println("Delete result: $delete") + assertTrue(delete, "Key deletion failed") + + } + + } +} From 0a4639013d63420497e33ec76c688edcf67d9b34 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Tue, 17 Dec 2024 13:14:13 +0100 Subject: [PATCH 19/21] docs : created a README.md for the aws sdk module --- .../crypto/waltid-crypto-aws/README.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 waltid-libraries/crypto/waltid-crypto-aws/README.md diff --git a/waltid-libraries/crypto/waltid-crypto-aws/README.md b/waltid-libraries/crypto/waltid-crypto-aws/README.md new file mode 100644 index 000000000..32c32e901 --- /dev/null +++ b/waltid-libraries/crypto/waltid-crypto-aws/README.md @@ -0,0 +1,33 @@ +# AWS SDK Extension for walt.id Crypto + +A Kotlin-based extension that enhances walt.id crypto with native AWS key management capabilities. + +## Overview + +This extension introduces `AwsKey`, a robust implementation leveraging the AWS SDK for Kotlin to manage cryptographic keys. It serves as a more integrated alternative to the platform-agnostic `AWSKeyRestAPI` found in the base walt.id crypto library. + +## Key Features + +- Native AWS SDK integration for optimal performance +- Kotlin-specific implementation +- Seamless key management through AWS KMS +- Direct SDK access instead of REST API calls + +## Authentication + +The extension utilizes AWS SDK's default credential provider chain for authentication, automatically detecting credentials from multiple sources including: + +- Environment variables +- AWS credentials file +- IAM roles for EC2 +- Container credentials +- SSO credentials + +## Comparison to Base Implementation + +While the base `AWSKeyRestAPI` offers cross-platform compatibility through REST endpoints, this extension provides: + +- Improved performance through direct SDK calls +- Enhanced error handling +- Native integration with AWS services + From 8dbb90b53f7b2b0cb8129c08ea5cddea08cffb36 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Tue, 17 Dec 2024 13:14:31 +0100 Subject: [PATCH 20/21] docs : updated crypto README.md file with aws --- .../crypto/waltid-crypto/README.md | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/waltid-libraries/crypto/waltid-crypto/README.md b/waltid-libraries/crypto/waltid-crypto/README.md index 55ceda6fb..440f7d429 100644 --- a/waltid-libraries/crypto/waltid-crypto/README.md +++ b/waltid-libraries/crypto/waltid-crypto/README.md @@ -706,7 +706,7 @@ vault secrets enable transit ### Working with AWSKey -An AWS account is required in order to be able to use an `AWSKey` for signing and verification. This +An AWS account is required in order to be able to use an `AWSKeyRestAPI` for signing and verification. This implies covering the following steps: 1. [create an AWS account](https://aws.amazon.com/resources/create-account/) @@ -715,16 +715,24 @@ implies covering the following steps: #### AwsKeyMetadata -The `AWSKeyMetadata` class is used to specify the AWS access key ID, secret access key, and region. -It is used when creating an `AWSKey` instance. +The `AWSKeyMetadata` class is used to specify the AWS access key ID, secret access key, roleName and region. +It is used when creating an `AWSKeyRestAPI` instance. + + + ```kotlin @Serializable data class AWSKeyMetadata( - val accessKeyId: String, - val secretAccessKey: String, - val region: String -) + val auth: AWSAuth, +) { + constructor( + accessKeyId: String? = null, + secretAccessKey: String? = null, + region: String? = null, + roleName: String? = null, + ) : this(AWSAuth(accessKeyId, secretAccessKey, region, roleName)) +} ``` For usage examples on _create_, _sign_, _verify_, _import_ and _export_ functions see From 65722fc35e7909bbe6a1bae78d85c7752a142ac5 Mon Sep 17 00:00:00 2001 From: SuperBatata Date: Wed, 18 Dec 2024 13:58:31 +0100 Subject: [PATCH 21/21] cleaning code --- settings.gradle.kts | 2 -- waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts | 2 -- 2 files changed, 4 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 2fb13aca7..5d49411c4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -95,5 +95,3 @@ plugins { } rootProject.name = "waltid-identity" -//include("waltid-libraries:crypto:waltid-crypto-aws") -//findProject(":waltid-libraries:crypto:waltid-crypto-aws")?.name = "waltid-crypto-aws" diff --git a/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts b/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts index dfd525a9c..25c487dd9 100644 --- a/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts +++ b/waltid-libraries/crypto/waltid-crypto-aws/build.gradle.kts @@ -27,8 +27,6 @@ dependencies { // AWS implementation("aws.sdk.kotlin:kms:1.3.91") - implementation("aws.sdk.kotlin:dynamodb:1.3.91") - implementation("aws.sdk.kotlin:secretsmanager:1.3.91") // JOSE implementation("com.nimbusds:nimbus-jose-jwt:9.41.1")