diff --git a/edge-agent-sdk/build.gradle.kts b/edge-agent-sdk/build.gradle.kts index 0d8280a59..3f52228b8 100644 --- a/edge-agent-sdk/build.gradle.kts +++ b/edge-agent-sdk/build.gradle.kts @@ -128,6 +128,10 @@ kotlin { implementation("io.iohk.atala.prism.anoncredskmp:anoncreds-kmp:0.4.6") implementation("com.ionspin.kotlin:bignum:0.3.9") implementation("org.bouncycastle:bcprov-jdk15on:1.68") + implementation("eu.europa.ec.eudi:eudi-lib-jvm-sdjwt-kt:0.4.0") { + exclude(group = "com.nimbusds", module = "nimbus-jose-jwt") + } + implementation(kotlin("reflect")) implementation("com.apicatalog:titanium-json-ld-jre8:1.4.0") implementation("org.glassfish:jakarta.json:2.0.1") diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Ed25519PrivateKey.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Ed25519PrivateKey.kt index 66559fb8c..574041d76 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Ed25519PrivateKey.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Ed25519PrivateKey.kt @@ -1,5 +1,7 @@ package org.hyperledger.identus.walletsdk.apollo.utils +import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters +import org.bouncycastle.jce.provider.BouncyCastleProvider import org.hyperledger.identus.apollo.base64.base64UrlEncoded import org.hyperledger.identus.apollo.utils.KMMEdPrivateKey import org.hyperledger.identus.walletsdk.domain.models.Curve @@ -13,6 +15,8 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PrivateKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.SignableKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorableKey +import java.security.KeyFactory +import java.security.spec.PKCS8EncodedKeySpec /** * Represents a private key for the Ed25519 algorithm. @@ -122,4 +126,11 @@ class Ed25519PrivateKey(nativeValue: ByteArray) : PrivateKey(), SignableKey, Sto */ override val restorationIdentifier: String get() = "ed25519+priv" + + override fun jca(): java.security.PrivateKey { + val privateKeyParams = Ed25519PrivateKeyParameters(raw, 0) + val pkcs8Encoded = privateKeyParams.encoded + val keyFactory = KeyFactory.getInstance("Ed25519", BouncyCastleProvider()) + return keyFactory.generatePrivate(PKCS8EncodedKeySpec(pkcs8Encoded)) + } } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Ed25519PublicKey.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Ed25519PublicKey.kt index 0395c36db..b28ef4243 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Ed25519PublicKey.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Ed25519PublicKey.kt @@ -1,5 +1,7 @@ package org.hyperledger.identus.walletsdk.apollo.utils +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters +import org.bouncycastle.jce.provider.BouncyCastleProvider import org.hyperledger.identus.apollo.base64.base64UrlEncoded import org.hyperledger.identus.apollo.utils.KMMEdPublicKey import org.hyperledger.identus.walletsdk.domain.models.Curve @@ -12,6 +14,8 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PEMKeyType import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorableKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.VerifiableKey +import java.security.KeyFactory +import java.security.spec.X509EncodedKeySpec /** * Represents an Ed25519 public key. @@ -106,4 +110,11 @@ class Ed25519PublicKey(nativeValue: ByteArray) : PublicKey(), VerifiableKey, Sto */ override val restorationIdentifier: String get() = "ed25519+pub" + + override fun jca(): java.security.PublicKey { + val publicKeyParams = Ed25519PublicKeyParameters(raw, 0) + val x509Encoded = publicKeyParams.encoded + val keyFactory = KeyFactory.getInstance("Ed25519", BouncyCastleProvider()) + return keyFactory.generatePublic(X509EncodedKeySpec(x509Encoded)) + } } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Secp256k1PrivateKey.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Secp256k1PrivateKey.kt index 92699022f..1c16a613a 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Secp256k1PrivateKey.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Secp256k1PrivateKey.kt @@ -1,10 +1,14 @@ package org.hyperledger.identus.walletsdk.apollo.utils +import org.bouncycastle.jce.ECNamedCurveTable +import org.bouncycastle.jce.provider.BouncyCastleProvider +import org.bouncycastle.jce.spec.ECNamedCurveSpec import org.hyperledger.identus.apollo.base64.base64UrlDecodedBytes import org.hyperledger.identus.apollo.base64.base64UrlEncoded import org.hyperledger.identus.apollo.derivation.DerivationPath import org.hyperledger.identus.apollo.derivation.HDKey import org.hyperledger.identus.apollo.utils.KMMECSecp256k1PrivateKey +import org.hyperledger.identus.apollo.utils.KMMEllipticCurve import org.hyperledger.identus.walletsdk.domain.models.Curve import org.hyperledger.identus.walletsdk.domain.models.keyManagement.CurveKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.CurvePointXKey @@ -20,6 +24,12 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.SeedKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.SignableKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorableKey +import org.hyperledger.identus.walletsdk.pollux.EC +import java.math.BigInteger +import java.security.KeyFactory +import java.security.interfaces.ECPrivateKey +import java.security.spec.ECParameterSpec +import java.security.spec.ECPrivateKeySpec /** * The `Secp256k1PrivateKey` class represents a private key that uses the secp256k1 elliptic curve. @@ -159,4 +169,13 @@ class Secp256k1PrivateKey(nativeValue: ByteArray) : val derivedHdKey = hdKey.derive(derivationPath.toString()) return Secp256k1PrivateKey(derivedHdKey.getKMMSecp256k1PrivateKey().raw) } + + override fun jca(): java.security.PrivateKey { + val curveName = KMMEllipticCurve.SECP256k1.value + val sp = ECNamedCurveTable.getParameterSpec(curveName) + val params: ECParameterSpec = ECNamedCurveSpec(sp.name, sp.curve, sp.g, sp.n, sp.h) + val privateKeySpec = ECPrivateKeySpec(BigInteger(1, getValue()), params) + val keyFactory = KeyFactory.getInstance(EC, BouncyCastleProvider()) + return keyFactory.generatePrivate(privateKeySpec) as ECPrivateKey + } } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Secp256k1PublicKey.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Secp256k1PublicKey.kt index 350dc6b7f..79b7ca915 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Secp256k1PublicKey.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/Secp256k1PublicKey.kt @@ -1,7 +1,11 @@ package org.hyperledger.identus.walletsdk.apollo.utils +import org.bouncycastle.jce.ECNamedCurveTable +import org.bouncycastle.jce.provider.BouncyCastleProvider +import org.bouncycastle.jce.spec.ECNamedCurveSpec import org.hyperledger.identus.apollo.base64.base64UrlEncoded import org.hyperledger.identus.apollo.utils.KMMECSecp256k1PublicKey +import org.hyperledger.identus.apollo.utils.KMMEllipticCurve import org.hyperledger.identus.walletsdk.apollo.config.ECConfig import org.hyperledger.identus.walletsdk.domain.models.Curve import org.hyperledger.identus.walletsdk.domain.models.keyManagement.CurveKey @@ -16,6 +20,11 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PEMKeyType import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorableKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.VerifiableKey +import org.hyperledger.identus.walletsdk.pollux.EC +import java.security.KeyFactory +import java.security.interfaces.ECPublicKey +import java.security.spec.ECParameterSpec +import java.security.spec.ECPublicKeySpec /** * Represents a public key in the Secp256k1 elliptic curve algorithm. @@ -126,4 +135,14 @@ class Secp256k1PublicKey(nativeValue: ByteArray) : PublicKey(), VerifiableKey, S fun getEncodedCompressed(): ByteArray { return KMMECSecp256k1PublicKey(raw).getCompressed() } + + override fun jca(): java.security.PublicKey { + val curveName = KMMEllipticCurve.SECP256k1.value + val sp = ECNamedCurveTable.getParameterSpec(curveName) + val params: ECParameterSpec = ECNamedCurveSpec(sp.name, sp.curve, sp.g, sp.n, sp.h) + + val publicKeySpec = ECPublicKeySpec(params.generator, params) + val keyFactory = KeyFactory.getInstance(EC, BouncyCastleProvider()) + return keyFactory.generatePublic(publicKeySpec) as ECPublicKey + } } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/X25519PrivateKey.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/X25519PrivateKey.kt index 1b85f4cbc..66b2c6f69 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/X25519PrivateKey.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/X25519PrivateKey.kt @@ -1,5 +1,7 @@ package org.hyperledger.identus.walletsdk.apollo.utils +import org.bouncycastle.crypto.params.X25519PrivateKeyParameters +import org.bouncycastle.jce.provider.BouncyCastleProvider import org.hyperledger.identus.apollo.base64.base64UrlEncoded import org.hyperledger.identus.apollo.utils.KMMX25519PrivateKey import org.hyperledger.identus.walletsdk.domain.models.Curve @@ -13,6 +15,8 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PEMKeyType import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PrivateKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorableKey +import java.security.KeyFactory +import java.security.spec.PKCS8EncodedKeySpec /** * Represents a private key for the X25519 elliptic curve. @@ -101,4 +105,11 @@ class X25519PrivateKey(nativeValue: ByteArray) : PrivateKey(), StorableKey, Expo */ override val restorationIdentifier: String get() = "x25519+priv" + + override fun jca(): java.security.PrivateKey { + val privateKeyParams = X25519PrivateKeyParameters(raw, 0) + val pkcs8Encoded = privateKeyParams.encoded + val keyFactory = KeyFactory.getInstance("X25519", BouncyCastleProvider()) + return keyFactory.generatePrivate(PKCS8EncodedKeySpec(pkcs8Encoded)) + } } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/X25519PublicKey.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/X25519PublicKey.kt index 4703b219a..30b3de84f 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/X25519PublicKey.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/utils/X25519PublicKey.kt @@ -1,5 +1,7 @@ package org.hyperledger.identus.walletsdk.apollo.utils +import org.bouncycastle.crypto.params.X25519PublicKeyParameters +import org.bouncycastle.jce.provider.BouncyCastleProvider import org.hyperledger.identus.apollo.base64.base64UrlEncoded import org.hyperledger.identus.walletsdk.domain.models.Curve import org.hyperledger.identus.walletsdk.domain.models.keyManagement.CurveKey @@ -10,6 +12,8 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PEMKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PEMKeyType import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorableKey +import java.security.KeyFactory +import java.security.spec.X509EncodedKeySpec /** * Class representing a public key for the X25519 curve. @@ -86,4 +90,11 @@ class X25519PublicKey(nativeValue: ByteArray) : PublicKey(), ExportableKey, Stor */ override val restorationIdentifier: String get() = "x25519+pub" + + override fun jca(): java.security.PublicKey { + val publicKeyParams = X25519PublicKeyParameters(raw, 0) + val x509Encoded = publicKeyParams.encoded + val keyFactory = KeyFactory.getInstance("X25519", BouncyCastleProvider()) + return keyFactory.generatePublic(X509EncodedKeySpec(x509Encoded)) + } } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/buildingblocks/Pollux.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/buildingblocks/Pollux.kt index d5cce16a2..6a113d3c2 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/buildingblocks/Pollux.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/buildingblocks/Pollux.kt @@ -1,12 +1,9 @@ package org.hyperledger.identus.walletsdk.domain.buildingblocks -import anoncreds_wrapper.CredentialDefinition import anoncreds_wrapper.CredentialOffer import anoncreds_wrapper.CredentialRequest import anoncreds_wrapper.CredentialRequestMetadata import anoncreds_wrapper.LinkSecret -import anoncreds_wrapper.Presentation -import anoncreds_wrapper.Schema import kotlinx.serialization.json.JsonObject import org.hyperledger.identus.walletsdk.domain.models.AttachmentDescriptor import org.hyperledger.identus.walletsdk.domain.models.Credential @@ -20,8 +17,6 @@ import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationOptions import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmission import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmissionOptions -import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.RequestPresentation -import org.hyperledger.identus.walletsdk.pollux.models.AnonCredential import java.security.interfaces.ECPublicKey /** @@ -59,6 +54,20 @@ interface Pollux { offerJson: JsonObject ): String + /** + * Processes the SDJWT credential request and returns a string representation of the processed result. + * + * @param subjectDID The DID of the subject for whom the request is being processed. + * @param privateKey The private key used for signing the JWT. + * @param offerJson The JSON object representing the credential offer. + * @return The string representation of the processed result. + */ + fun processCredentialRequestSDJWT( + subjectDID: DID, + privateKey: PrivateKey, + offerJson: JsonObject + ): String + /** * Processes a credential request for anonymous credentials. * @@ -75,27 +84,34 @@ interface Pollux { linkSecretName: String ): Pair - /** - * Creates a verifiable presentation JSON Web Token (JWT) for the given subjectDID, privateKey, credential, and requestPresentationJson. - * - * @param subjectDID The DID of the subject for whom the presentation is being created. - * @param privateKey The private key used to sign the JWT. - * @param credential The credential to be included in the presentation. - * @param requestPresentationJson The JSON object representing the request presentation. - * @return The created verifiable presentation JWT. - */ - fun createVerifiablePresentationJWT( - subjectDID: DID, - privateKey: PrivateKey, - credential: Credential, - requestPresentationJson: JsonObject - ): String - - suspend fun createVerifiablePresentationAnoncred( - request: RequestPresentation, - credential: AnonCredential, - linkSecret: LinkSecret - ): Presentation +// /** +// * Creates a verifiable presentation JSON Web Token (JWT) for the given subjectDID, privateKey, credential, and requestPresentationJson. +// * +// * @param subjectDID The DID of the subject for whom the presentation is being created. +// * @param privateKey The private key used to sign the JWT. +// * @param credential The credential to be included in the presentation. +// * @param requestPresentationJson The JSON object representing the request presentation. +// * @return The created verifiable presentation JWT. +// */ +// fun createVerifiablePresentationJWT( +// subjectDID: DID, +// privateKey: PrivateKey, +// credential: Credential, +// requestPresentationJson: JsonObject +// ): String +// +// fun createVerifiablePresentationSDJWT( +// subjectDID: DID, +// privateKey: PrivateKey, +// credential: Credential, +// requestPresentationJson: JsonObject +// ): String +// +// suspend fun createVerifiablePresentationAnoncred( +// request: RequestPresentation, +// credential: AnonCredential, +// linkSecret: LinkSecret +// ): Presentation /** * Restores a credential using the provided restoration identifier and credential data. @@ -123,15 +139,15 @@ interface Pollux { */ fun extractCredentialFormatFromMessage(formats: Array): CredentialType - /** - * Retrieves the credential definition for the specified ID. - * - * @param id The ID of the credential definition. - * @return The credential definition. - */ - suspend fun getCredentialDefinition(id: String): CredentialDefinition - - suspend fun getSchema(schemaId: String): Schema +// /** +// * Retrieves the credential definition for the specified ID. +// * +// * @param id The ID of the credential definition. +// * @return The credential definition. +// */ +// suspend fun getCredentialDefinition(id: String): CredentialDefinition +// +// suspend fun getSchema(schemaId: String): Schema suspend fun createPresentationDefinitionRequest( type: CredentialType, diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/ProvableCredential.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/ProvableCredential.kt new file mode 100644 index 000000000..10592dc8d --- /dev/null +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/ProvableCredential.kt @@ -0,0 +1,22 @@ +package org.hyperledger.identus.walletsdk.domain.models + +import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PrivateKey + +sealed class CredentialOperationsOptions { + data class Schema(val id: String, val json: String) : CredentialOperationsOptions() + data class SchemaDownloader(val api: Api) : CredentialOperationsOptions() + data class CredentialDefinition(val id: String, val json: String) : CredentialOperationsOptions() + data class CredentialDefinitionDownloader(val api: Api) : CredentialOperationsOptions() + data class LinkSecret(val id: String, val secret: String) : CredentialOperationsOptions() + data class SubjectDID(val did: DID) : CredentialOperationsOptions() + data class Entropy(val entropy: String) : CredentialOperationsOptions() + data class SignableKey(val key: SignableKey?) : CredentialOperationsOptions() + data class ExportableKey(val key: PrivateKey?) : CredentialOperationsOptions() + data class ZkpPresentationParams(val attributes: Map, val predicates: List) : CredentialOperationsOptions() + data class DisclosingClaims(val claims: List) : CredentialOperationsOptions() + data class Custom(val key: String, val data: ByteArray) : CredentialOperationsOptions() +} + +interface ProvableCredential { + suspend fun presentation(request: ByteArray, options: List): String +} diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/VerifiableCredential.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/VerifiableCredential.kt index a240084aa..e72eb17db 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/VerifiableCredential.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/VerifiableCredential.kt @@ -60,6 +60,7 @@ data class VerifiableCredentialTypeContainer( enum class CredentialType(val type: String) { JWT("prism/jwt"), W3C("w3c"), + SDJWT("vc+sd-jwt"), ANONCREDS_OFFER("anoncreds/credential-offer@v1.0"), ANONCREDS_REQUEST("anoncreds/credential-request@v1.0"), ANONCREDS_ISSUE("anoncreds/credential@v1.0"), diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/keyManagement/PrivateKey.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/keyManagement/PrivateKey.kt index cb63101e9..42973835a 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/keyManagement/PrivateKey.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/keyManagement/PrivateKey.kt @@ -43,4 +43,7 @@ abstract class PrivateKey : Key() { * Defines a method to fetch the public key of this private key */ abstract fun publicKey(): PublicKey + + @Throws + abstract fun jca(): java.security.PrivateKey } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/keyManagement/PublicKey.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/keyManagement/PublicKey.kt index 12575029d..df7e1eec7 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/keyManagement/PublicKey.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/models/keyManagement/PublicKey.kt @@ -31,4 +31,7 @@ abstract class PublicKey : Key() { fun getValue(): ByteArray { return this.raw } + + @Throws + abstract fun jca(): java.security.PublicKey } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/AgentConstants.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/AgentConstants.kt index 03282e47c..bca6f2e91 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/AgentConstants.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/AgentConstants.kt @@ -4,6 +4,7 @@ const val DIDCOMM1 = "#didcomm-1" const val DIDCOMM_MESSAGING = "DIDCommMessaging" const val PRISM = "prism" const val JWT_MEDIA_TYPE = "prism/jwt" +const val SDJWT_MEDIA_TYPE = "vc+sd-jwt" const val PROTOCOL_TYPE = "ProtocolType" const val GOAL_CODE = "goal_code" const val WILL_CONFIRM = "will_confirm" diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgent.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgent.kt index 947a006a0..055c2c916 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgent.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgent.kt @@ -14,6 +14,7 @@ import com.nimbusds.jose.crypto.X25519Decrypter import com.nimbusds.jose.crypto.X25519Encrypter import com.nimbusds.jose.jwk.OctetKeyPair import com.nimbusds.jose.util.Base64URL +import eu.europa.ec.eudi.sdjwt.vc.SD_JWT_VC_TYPE import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.http.ContentType import io.ktor.http.HttpMethod @@ -59,6 +60,7 @@ import org.hyperledger.identus.walletsdk.domain.models.AttachmentData.Attachment import org.hyperledger.identus.walletsdk.domain.models.AttachmentData.AttachmentJsonData import org.hyperledger.identus.walletsdk.domain.models.AttachmentDescriptor import org.hyperledger.identus.walletsdk.domain.models.Credential +import org.hyperledger.identus.walletsdk.domain.models.CredentialOperationsOptions import org.hyperledger.identus.walletsdk.domain.models.CredentialType import org.hyperledger.identus.walletsdk.domain.models.Curve import org.hyperledger.identus.walletsdk.domain.models.DID @@ -70,6 +72,7 @@ import org.hyperledger.identus.walletsdk.domain.models.PeerDID import org.hyperledger.identus.walletsdk.domain.models.PolluxError import org.hyperledger.identus.walletsdk.domain.models.PresentationClaims import org.hyperledger.identus.walletsdk.domain.models.PrismDIDInfo +import org.hyperledger.identus.walletsdk.domain.models.ProvableCredential import org.hyperledger.identus.walletsdk.domain.models.Seed import org.hyperledger.identus.walletsdk.domain.models.Signature import org.hyperledger.identus.walletsdk.domain.models.UnknownError @@ -111,9 +114,11 @@ import org.hyperledger.identus.walletsdk.pluto.backup.models.BackupV0_0_1 import org.hyperledger.identus.walletsdk.pollux.models.AnonCredential import org.hyperledger.identus.walletsdk.pollux.models.CredentialRequestMeta import org.hyperledger.identus.walletsdk.pollux.models.JWTCredential +import org.hyperledger.identus.walletsdk.pollux.models.SDJWTCredential import org.kotlincrypto.hash.sha2.SHA256 import java.net.UnknownHostException import java.util.* +import kotlin.text.encodeToByteArray /** * Check if the passed URL is valid or not. @@ -957,10 +962,10 @@ class EdgeAgent { * @throws EdgeAgentError if there is a problem creating the presentation. **/ @Throws(PolluxError.InvalidPrismDID::class) - suspend fun preparePresentationForRequestProof( + suspend fun preparePresentationForRequestProof( request: RequestPresentation, - credential: Credential - ): Presentation { + credential: T + ): Presentation where T : Credential, T : ProvableCredential { val attachmentFormat = request.attachments.first().format ?: CredentialType.Unknown.type if (attachmentFormat == CredentialType.PRESENTATION_EXCHANGE_DEFINITIONS.type) { request.attachments.find { it.data::class == AttachmentBase64::class } @@ -992,12 +997,12 @@ class EdgeAgent { else -> null } } - val requestJsonObject = Json.parseToJsonElement(requestData).jsonObject - presentationString = pollux.createVerifiablePresentationJWT( - subjectDID, - keyPair.privateKey, - credential, - requestJsonObject + presentationString = credential.presentation( + requestData.encodeToByteArray(), + listOf( + CredentialOperationsOptions.SubjectDID(subjectDID), + CredentialOperationsOptions.ExportableKey(keyPair.privateKey) + ) ) mediaType = JWT_MEDIA_TYPE } @@ -1007,13 +1012,36 @@ class EdgeAgent { if (format != CredentialType.ANONCREDS_PROOF_REQUEST) { throw EdgeAgentError.InvalidCredentialFormatError(CredentialType.ANONCREDS_PROOF_REQUEST) } + val requestData = request.attachments.mapNotNull { + when (it.data) { + is AttachmentJsonData -> it.data.data + else -> null + } + }.first() val linkSecret = getLinkSecret() - val presentation = pollux.createVerifiablePresentationAnoncred( - request, - credential as AnonCredential, - linkSecret + presentationString = credential.presentation( + requestData.encodeToByteArray(), + listOf( + CredentialOperationsOptions.LinkSecret("", linkSecret.getValue()), + CredentialOperationsOptions.SchemaDownloader(api), + CredentialOperationsOptions.CredentialDefinitionDownloader(api) + ) ) - presentationString = presentation.getJson() + } + + SDJWTCredential::class -> { + val requestData = request.attachments.mapNotNull { + when (it.data) { + is AttachmentJsonData -> it.data.data + else -> null + } + }.first().encodeToByteArray() + presentationString = credential.presentation( + requestData, + listOf(CredentialOperationsOptions.DisclosingClaims(listOf(credential.claims.toString()))) + ) + + mediaType = SD_JWT_VC_TYPE } else -> { diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImpl.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImpl.kt index 0646d1674..c0528228a 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImpl.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImpl.kt @@ -3,19 +3,11 @@ package org.hyperledger.identus.walletsdk.pollux import anoncreds_wrapper.CredentialDefinition -import anoncreds_wrapper.CredentialDefinitionId import anoncreds_wrapper.CredentialOffer import anoncreds_wrapper.CredentialRequest import anoncreds_wrapper.CredentialRequestMetadata -import anoncreds_wrapper.CredentialRequests import anoncreds_wrapper.LinkSecret -import anoncreds_wrapper.Presentation -import anoncreds_wrapper.PresentationRequest import anoncreds_wrapper.Prover -import anoncreds_wrapper.RequestedAttribute -import anoncreds_wrapper.RequestedPredicate -import anoncreds_wrapper.Schema -import anoncreds_wrapper.SchemaId import com.apicatalog.jsonld.JsonLd import com.apicatalog.jsonld.document.JsonDocument import com.apicatalog.rdf.Rdf @@ -30,7 +22,6 @@ import com.nimbusds.jwt.SignedJWT import org.hyperledger.identus.apollo.base64.base64UrlDecoded import org.hyperledger.identus.apollo.utils.KMMECSecp256k1PublicKey import org.hyperledger.identus.apollo.base64.base64UrlDecodedBytes -import org.hyperledger.identus.apollo.utils.KMMEllipticCurve import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationOptions import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmissionOptions import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmissionOptionsJWT @@ -41,7 +32,6 @@ import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.encodeToJsonElement -import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import org.bouncycastle.jce.ECNamedCurveTable @@ -53,7 +43,6 @@ import org.hyperledger.identus.walletsdk.domain.buildingblocks.Castor import org.hyperledger.identus.walletsdk.domain.buildingblocks.Pollux import org.hyperledger.identus.walletsdk.domain.models.Api import org.hyperledger.identus.walletsdk.domain.models.ApiImpl -import org.hyperledger.identus.walletsdk.domain.models.AttachmentData.AttachmentBase64 import org.hyperledger.identus.walletsdk.domain.models.AttachmentDescriptor import org.hyperledger.identus.walletsdk.domain.models.Credential import org.hyperledger.identus.walletsdk.domain.models.CredentialType @@ -70,7 +59,6 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationDefinitionRequest import org.hyperledger.identus.walletsdk.domain.models.keyManagement.TypeKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.VerifiableKey -import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.RequestPresentation import org.hyperledger.identus.walletsdk.edgeagent.shared.KeyValue import org.hyperledger.identus.walletsdk.pollux.models.AnonCredential import org.hyperledger.identus.walletsdk.pollux.models.JWTCredential @@ -83,7 +71,6 @@ import java.security.interfaces.ECPrivateKey import java.security.interfaces.ECPublicKey import java.security.spec.ECParameterSpec import java.security.spec.ECPoint -import java.security.spec.ECPrivateKeySpec import java.security.spec.ECPublicKeySpec import java.util.* import org.hyperledger.identus.walletsdk.apollo.utils.Secp256k1PrivateKey @@ -95,6 +82,7 @@ import org.hyperledger.identus.walletsdk.domain.models.PresentationClaims import org.hyperledger.identus.walletsdk.domain.models.keyManagement.SignableKey import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.DescriptorItemFormat import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmission +import org.hyperledger.identus.walletsdk.pollux.models.SDJWTCredential import org.hyperledger.identus.walletsdk.pluto.RestorationID import org.hyperledger.identus.walletsdk.apollo.helpers.gunzip import org.hyperledger.identus.walletsdk.domain.models.JWTVerifiableCredential @@ -152,6 +140,10 @@ open class PolluxImpl( JWTCredential.fromJwtString(jsonData) } + CredentialType.SDJWT -> { + SDJWTCredential.fromSDJwtString(jsonData) + } + CredentialType.ANONCREDS_ISSUE -> { if (linkSecret == null) { throw Error("LinkSecret is required") @@ -209,6 +201,9 @@ open class PolluxImpl( ): Credential { val cred: Credential when (restorationIdentifier) { + "sd-jwt+credential" -> { + cred = SDJWTCredential.fromSDJwtString(credentialData.decodeToString()) + } RestorationID.JWT.value -> { cred = JWTCredential.fromJwtString(credentialData.decodeToString()) } @@ -260,100 +255,15 @@ open class PolluxImpl( * @return The created verifiable presentation JWT. */ @Throws(PolluxError.NoDomainOrChallengeFound::class) - override fun createVerifiablePresentationJWT( + override fun processCredentialRequestSDJWT( subjectDID: DID, privateKey: PrivateKey, - credential: Credential, - requestPresentationJson: JsonObject + offerJson: JsonObject ): String { val parsedPrivateKey = parsePrivateKey(privateKey) - val domain = - getDomain(requestPresentationJson) ?: throw PolluxError.NoDomainOrChallengeFound() - val challenge = - getChallenge(requestPresentationJson) ?: throw PolluxError.NoDomainOrChallengeFound() - return signClaimsProofPresentationJWT( - subjectDID, - parsedPrivateKey, - credential, - domain, - challenge - ) - } - - /** - * Converts the map of [anoncreds_wrapper.AttributeInfoValue] values to a list of [RequestedAttribute]. - * - * @return The list of [RequestedAttribute]. - */ - private fun Map.toListRequestedAttribute(): List { - return this.keys.toList().map { - RequestedAttribute( - referent = it, - revealed = true - ) - } - } - - /** - * Converts the map of [anoncreds_wrapper.PredicateInfoValue] values to a list of [RequestedPredicate]. - * - * @receiver The map of [anoncreds_wrapper.PredicateInfoValue] values. - * @return The list of [RequestedPredicate]. - */ - private fun Map.toListRequestedPredicate(): List { - return this.keys.toList().map { - RequestedPredicate(it) - } - } - - override suspend fun createVerifiablePresentationAnoncred( - request: RequestPresentation, - credential: AnonCredential, - linkSecret: LinkSecret - ): Presentation { - if (request.attachments.isEmpty()) { - throw PolluxError.RequestPresentationHasWrongAttachments("Attachment cannot be empty") - } - if (request.attachments[0].data !is AttachmentBase64) { - throw PolluxError.RequestPresentationHasWrongAttachments("Attachment data must be AttachmentBase64") - } - val attachmentBase64 = request.attachments[0].data as AttachmentBase64 - val presentationRequest = PresentationRequest(attachmentBase64.base64.base64UrlDecoded) - val cred = anoncreds_wrapper.Credential(credential.id) - - val requestedAttributes = - presentationRequest.getRequestedAttributes().toListRequestedAttribute() - val requestedPredicate = - presentationRequest.getRequestedPredicates().toListRequestedPredicate() - - val credentialRequests = CredentialRequests( - credential = cred, - requestedAttribute = requestedAttributes, - requestedPredicate = requestedPredicate - ) - val schemaId = credential.schemaID - // When testing using a local instance of an agent, we need to replace the host.docker.internal with the local IP - // .replace("host.docker.internal", "192.168.68.114") - val schema = getSchema(schemaId) - - val schemaMap: Map = mapOf(Pair(credential.schemaID, schema)) - - val credDefId = credential.credentialDefinitionID - // When testing using a local instance of an agent, we need to replace the host.docker.internal with the local IP - // .replace("host.docker.internal", "192.168.68.114") - val credentialDefinition = getCredentialDefinition(credDefId) - val credDefinition: Map = mapOf( - Pair(credential.credentialDefinitionID, credentialDefinition) - ) - - return Prover().createPresentation( - presentationRequest = presentationRequest, - credentials = listOf(credentialRequests), - selfAttested = null, - linkSecret = linkSecret, - schemas = schemaMap, - credentialDefinitions = credDefinition - ) + val domain = getDomain(offerJson) ?: throw PolluxError.NoDomainOrChallengeFound() + val challenge = getChallenge(offerJson) ?: throw PolluxError.NoDomainOrChallengeFound() + return signClaimsRequestCredentialJWT(subjectDID, parsedPrivateKey, domain, challenge) } /** @@ -473,7 +383,7 @@ open class PolluxImpl( * @param id The ID of the credential definition. * @return The credential definition. */ - override suspend fun getCredentialDefinition(id: String): CredentialDefinition { + suspend fun getCredentialDefinition(id: String): CredentialDefinition { val result = api.request( HttpMethod.Get.value, id, @@ -487,34 +397,34 @@ open class PolluxImpl( throw PolluxError.InvalidCredentialDefinitionError() } - override suspend fun getSchema(schemaId: String): Schema { - val result = api.request( - HttpMethod.Get.value, - schemaId, - emptyArray(), - arrayOf(KeyValue(HttpHeaders.ContentType, Typ.Encrypted.typ)), - null - ) - - if (result.status == 200) { - val schema = (Json.parseToJsonElement(result.jsonString) as JsonObject) - if (schema.containsKey("attrNames") && schema.containsKey("issuerId")) { - val name = schema["name"]?.jsonPrimitive?.content - val version = schema["version"]?.jsonPrimitive?.content - val attrs = schema["attrNames"] - val attrNames = attrs?.jsonArray?.map { value -> value.jsonPrimitive.content } - val issuerId = - schema["issuerId"]?.jsonPrimitive?.content - return Schema( - name = name ?: throw PolluxError.InvalidCredentialError(), - version = version ?: throw PolluxError.InvalidCredentialError(), - attrNames = attrNames ?: throw PolluxError.InvalidCredentialError(), - issuerId = issuerId ?: throw PolluxError.InvalidCredentialError() - ) - } - } - throw PolluxError.InvalidCredentialDefinitionError() - } +// override suspend fun getSchema(schemaId: String): Schema { +// val result = api.request( +// HttpMethod.Get.value, +// schemaId, +// emptyArray(), +// arrayOf(KeyValue(HttpHeaders.ContentType, Typ.Encrypted.typ)), +// null +// ) +// +// if (result.status == 200) { +// val schema = (Json.parseToJsonElement(result.jsonString) as JsonObject) +// if (schema.containsKey("attrNames") && schema.containsKey("issuerId")) { +// val name = schema["name"]?.jsonPrimitive?.content +// val version = schema["version"]?.jsonPrimitive?.content +// val attrs = schema["attrNames"] +// val attrNames = attrs?.jsonArray?.map { value -> value.jsonPrimitive.content } +// val issuerId = +// schema["issuerId"]?.jsonPrimitive?.content +// return Schema( +// name = name ?: throw PolluxError.InvalidCredentialError(), +// version = version ?: throw PolluxError.InvalidCredentialError(), +// attrNames = attrNames ?: throw PolluxError.InvalidCredentialError(), +// issuerId = issuerId ?: throw PolluxError.InvalidCredentialError() +// ) +// } +// } +// throw PolluxError.InvalidCredentialDefinitionError() +// } override suspend fun isCredentialRevoked(credential: Credential): Boolean { if (credential !is JWTCredential) { @@ -676,22 +586,11 @@ open class PolluxImpl( * @return The parsed ECPrivateKey. */ internal fun parsePrivateKey(privateKey: PrivateKey): ECPrivateKey { - val curveName = KMMEllipticCurve.SECP256k1.value - val sp = ECNamedCurveTable.getParameterSpec(curveName) - val params: ECParameterSpec = ECNamedCurveSpec(sp.name, sp.curve, sp.g, sp.n, sp.h) - val privateKeySpec = ECPrivateKeySpec(BigInteger(1, privateKey.getValue()), params) - val keyFactory = KeyFactory.getInstance(EC, BouncyCastleProvider()) - return keyFactory.generatePrivate(privateKeySpec) as ECPrivateKey + return privateKey.jca() as ECPrivateKey } private fun parsePublicKey(publicKey: PublicKey): ECPublicKey { - val curveName = KMMEllipticCurve.SECP256k1.value - val sp = ECNamedCurveTable.getParameterSpec(curveName) - val params: ECParameterSpec = ECNamedCurveSpec(sp.name, sp.curve, sp.g, sp.n, sp.h) - - val publicKeySpec = ECPublicKeySpec(params.generator, params) - val keyFactory = KeyFactory.getInstance(EC, BouncyCastleProvider()) - return keyFactory.generatePublic(publicKeySpec) as ECPublicKey + return publicKey.jca() as ECPublicKey } /** @@ -752,6 +651,26 @@ open class PolluxImpl( return signClaims(subjectDID, privateKey, domain, challenge, credential) } + /** + * Signs the claims for a proof presentation JSON Web Token (JWT). + * + * @param subjectDID The DID of the subject for whom the JWT is being created. + * @param privateKey The private key used to sign the JWT. + * @param credential The credential to be included in the presentation. + * @param domain The domain of the JWT. + * @param challenge The challenge value for the JWT. + * @return The signed JWT as a string. + */ + internal fun signClaimsProofPresentationSDJWT( + subjectDID: DID, + privateKey: ECPrivateKey, + credential: Credential, + domain: String, + challenge: String + ): String { + return signClaims(subjectDID, privateKey, domain, challenge, credential) + } + /** * Signs the claims for a JWT. * diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/AnonCredential.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/AnonCredential.kt index 6c6cd1f92..59af921e4 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/AnonCredential.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/AnonCredential.kt @@ -1,15 +1,37 @@ package org.hyperledger.identus.walletsdk.pollux.models +import anoncreds_wrapper.CredentialDefinition +import anoncreds_wrapper.CredentialDefinitionId +import anoncreds_wrapper.CredentialRequests +import anoncreds_wrapper.LinkSecret +import anoncreds_wrapper.PresentationRequest +import anoncreds_wrapper.Prover +import anoncreds_wrapper.RequestedAttribute +import anoncreds_wrapper.RequestedPredicate +import anoncreds_wrapper.Schema +import anoncreds_wrapper.SchemaId +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpMethod import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonNames +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonPrimitive +import org.didcommx.didcomm.common.Typ +import org.hyperledger.identus.walletsdk.domain.models.Api import org.hyperledger.identus.walletsdk.domain.models.Claim import org.hyperledger.identus.walletsdk.domain.models.ClaimType import org.hyperledger.identus.walletsdk.domain.models.Credential +import org.hyperledger.identus.walletsdk.domain.models.CredentialOperationsOptions +import org.hyperledger.identus.walletsdk.domain.models.PolluxError +import org.hyperledger.identus.walletsdk.domain.models.ProvableCredential import org.hyperledger.identus.walletsdk.domain.models.StorableCredential +import org.hyperledger.identus.walletsdk.domain.models.UnknownError +import org.hyperledger.identus.walletsdk.edgeagent.shared.KeyValue import org.hyperledger.identus.walletsdk.pluto.PlutoRestoreTask import org.hyperledger.identus.walletsdk.pluto.PlutoRestoreTask.AnonCredentialBackUp.RevocationRegistry @@ -55,7 +77,7 @@ data class AnonCredential( @JsonNames("witness", "witnessJson") val witnessJson: String?, private val json: String -) : Credential { +) : Credential, ProvableCredential { /** * Converts the current object to a [PlutoRestoreTask.AnonCredentialBackUp] object. @@ -125,6 +147,68 @@ data class AnonCredential( override var revoked: Boolean? = null + override suspend fun presentation(request: ByteArray, options: List): String { + var schemaDownloader: Api? = null + var definitionDownloader: Api? = null + var linkSecret: String? = null + + for (option in options) { + when (option) { + is CredentialOperationsOptions.SchemaDownloader -> schemaDownloader = option.api + is CredentialOperationsOptions.CredentialDefinitionDownloader -> definitionDownloader = option.api + is CredentialOperationsOptions.LinkSecret -> linkSecret = option.secret + else -> {} + } + } + if (linkSecret == null) { + throw UnknownError.SomethingWentWrongError() + } + if (schemaDownloader == null) { + throw UnknownError.SomethingWentWrongError() + } + if (definitionDownloader == null) { + throw UnknownError.SomethingWentWrongError() + } + + val presentationRequest = PresentationRequest(request.toString()) + val cred = anoncreds_wrapper.Credential(this.id) + + val requestedAttributes = + presentationRequest.getRequestedAttributes().toListRequestedAttribute() + val requestedPredicate = + presentationRequest.getRequestedPredicates().toListRequestedPredicate() + + val credentialRequests = CredentialRequests( + credential = cred, + requestedAttribute = requestedAttributes, + requestedPredicate = requestedPredicate + ) + val schemaId = this.schemaID + // When testing using a local instance of an agent, we need to replace the host.docker.internal with the local IP + // .replace("host.docker.internal", "192.168.68.114") + val schema = getSchema(schemaId, schemaDownloader) + + val schemaMap: Map = mapOf(Pair(this.schemaID, schema)) + + val credDefId = this.credentialDefinitionID + // When testing using a local instance of an agent, we need to replace the host.docker.internal with the local IP + // .replace("host.docker.internal", "192.168.68.114") + val credentialDefinition = getCredentialDefinition(credDefId, definitionDownloader) + val credDefinition: Map = mapOf( + Pair(this.credentialDefinitionID, credentialDefinition) + ) + val linkSecretAnon = LinkSecret.newFromValue(linkSecret) + + return Prover().createPresentation( + presentationRequest = presentationRequest, + credentials = listOf(credentialRequests), + selfAttested = null, + linkSecret = linkSecretAnon, + schemas = schemaMap, + credentialDefinitions = credDefinition + ).getJson() + } + /** * Converts the current credential object into a storable credential object. * @@ -182,6 +266,81 @@ data class AnonCredential( } } + /** + * Converts the map of [anoncreds_wrapper.AttributeInfoValue] values to a list of [RequestedAttribute]. + * + * @return The list of [RequestedAttribute]. + */ + private fun Map.toListRequestedAttribute(): List { + return this.keys.toList().map { + RequestedAttribute( + referent = it, + revealed = true + ) + } + } + + /** + * Converts the map of [anoncreds_wrapper.PredicateInfoValue] values to a list of [RequestedPredicate]. + * + * @receiver The map of [anoncreds_wrapper.PredicateInfoValue] values. + * @return The list of [RequestedPredicate]. + */ + private fun Map.toListRequestedPredicate(): List { + return this.keys.toList().map { + RequestedPredicate(it) + } + } + + /** + * Retrieves the credential definition for the specified ID. + * + * @param id The ID of the credential definition. + * @return The credential definition. + */ + private suspend fun getCredentialDefinition(id: String, api: Api): CredentialDefinition { + val result = api.request( + HttpMethod.Get.value, + id, + emptyArray(), + arrayOf(KeyValue(HttpHeaders.ContentType, Typ.Encrypted.typ)), + null + ) + if (result.status == 200) { + return CredentialDefinition(result.jsonString) + } + throw PolluxError.InvalidCredentialDefinitionError() + } + + private suspend fun getSchema(schemaId: String, api: Api): Schema { + val result = api.request( + HttpMethod.Get.value, + schemaId, + emptyArray(), + arrayOf(KeyValue(HttpHeaders.ContentType, Typ.Encrypted.typ)), + null + ) + + if (result.status == 200) { + val schema = (Json.parseToJsonElement(result.jsonString) as JsonObject) + if (schema.containsKey("attrNames") && schema.containsKey("issuerId")) { + val name = schema["name"]?.jsonPrimitive?.content + val version = schema["version"]?.jsonPrimitive?.content + val attrs = schema["attrNames"] + val attrNames = attrs?.jsonArray?.map { value -> value.jsonPrimitive.content } + val issuerId = + schema["issuerId"]?.jsonPrimitive?.content + return Schema( + name = name ?: throw PolluxError.InvalidCredentialError(), + version = version ?: throw PolluxError.InvalidCredentialError(), + attrNames = attrNames ?: throw PolluxError.InvalidCredentialError(), + issuerId = issuerId ?: throw PolluxError.InvalidCredentialError() + ) + } + } + throw PolluxError.InvalidCredentialDefinitionError() + } + companion object { /** * Converts a byte array of storable credential data to an AnonCredential object. diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/JWTCredential.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/JWTCredential.kt index 7301320fa..52b857ba6 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/JWTCredential.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/JWTCredential.kt @@ -1,5 +1,12 @@ package org.hyperledger.identus.walletsdk.pollux.models +import com.nimbusds.jose.JWSAlgorithm +import com.nimbusds.jose.JWSHeader +import com.nimbusds.jose.crypto.ECDSASigner +import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton +import com.nimbusds.jose.jwk.Curve +import com.nimbusds.jwt.JWTClaimsSet +import com.nimbusds.jwt.SignedJWT import io.iohk.atala.prism.didcomm.didpeer.core.toJsonElement import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName @@ -10,6 +17,7 @@ import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonTransformingSerializer import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.encodeToJsonElement @@ -20,10 +28,26 @@ import org.hyperledger.identus.walletsdk.domain.VC import org.hyperledger.identus.walletsdk.domain.models.Claim import org.hyperledger.identus.walletsdk.domain.models.ClaimType import org.hyperledger.identus.walletsdk.domain.models.Credential +import org.hyperledger.identus.walletsdk.domain.models.CredentialOperationsOptions +import org.hyperledger.identus.walletsdk.domain.models.DID import org.hyperledger.identus.walletsdk.domain.models.JWTPayload import org.hyperledger.identus.walletsdk.domain.models.JWTVerifiableCredential import org.hyperledger.identus.walletsdk.domain.models.JWTVerifiablePresentation +import org.hyperledger.identus.walletsdk.domain.models.PolluxError +import org.hyperledger.identus.walletsdk.domain.models.ProvableCredential import org.hyperledger.identus.walletsdk.domain.models.StorableCredential +import org.hyperledger.identus.walletsdk.pollux.CHALLENGE +import org.hyperledger.identus.walletsdk.pollux.CONTEXT +import org.hyperledger.identus.walletsdk.pollux.CONTEXT_URL +import org.hyperledger.identus.walletsdk.pollux.DOMAIN +import org.hyperledger.identus.walletsdk.pollux.NONCE +import org.hyperledger.identus.walletsdk.pollux.OPTIONS +import org.hyperledger.identus.walletsdk.pollux.TYPE +import org.hyperledger.identus.walletsdk.pollux.VERIFIABLE_CREDENTIAL +import org.hyperledger.identus.walletsdk.pollux.VERIFIABLE_PRESENTATION +import org.hyperledger.identus.walletsdk.pollux.VP +import java.security.PrivateKey +import java.security.interfaces.ECPrivateKey @Serializable /** @@ -52,7 +76,7 @@ data class JWTCredential @JvmOverloads constructor( @SerialName(VC) override var verifiableCredential: JWTVerifiableCredential? = null, var nonce: String? = null -) : Credential, JWTPayload { +) : Credential, JWTPayload, ProvableCredential { @Transient override val issuer: String = iss @@ -102,6 +126,37 @@ data class JWTCredential @JvmOverloads constructor( } override var revoked: Boolean? = false + override suspend fun presentation(request: ByteArray, options: List): String { + var exportableKeyOption: org.hyperledger.identus.walletsdk.domain.models.keyManagement.PrivateKey? = null + var subjectDID: DID? = null + + for (option in options) { + when (option) { + is CredentialOperationsOptions.SubjectDID -> subjectDID = option.did + is CredentialOperationsOptions.ExportableKey -> exportableKeyOption = option.key + else -> {} + } + } + if (subjectDID == null) { + throw PolluxError.InvalidPrismDID() + } + if (exportableKeyOption == null) { + throw PolluxError.WrongKeyProvided("Secp256k1", actual = "null") + } + val jsonString = String(request, Charsets.UTF_8) + val requestJson = Json.parseToJsonElement(jsonString).jsonObject + val domain = + getDomain(requestJson) ?: throw PolluxError.NoDomainOrChallengeFound() + val challenge = + getChallenge(requestJson) ?: throw PolluxError.NoDomainOrChallengeFound() + return signClaimsProofPresentationJWT( + subjectDID, + exportableKeyOption.jca() as ECPrivateKey, + this, + domain, + challenge + ) + } /** * Converts the current instance of [JWTCredential] to a [StorableCredential]. @@ -185,6 +240,96 @@ data class JWTCredential @JvmOverloads constructor( } } + /** + * Returns the domain from the given JsonObject. + * + * @param jsonObject The JsonObject from which to retrieve the domain. + * @return The domain as a String, or null if not found. + */ + private fun getDomain(jsonObject: JsonObject): String? { + return jsonObject[OPTIONS]?.jsonObject?.get(DOMAIN)?.jsonPrimitive?.content + } + + /** + * Retrieves the challenge value from the given JsonObject. + * + * @param jsonObject The JsonObject from which to retrieve the challenge. + * @return The challenge value as a String, or null if not found in the JsonObject. + */ + private fun getChallenge(jsonObject: JsonObject): String? { + return jsonObject[OPTIONS]?.jsonObject?.get(CHALLENGE)?.jsonPrimitive?.content + } + + /** + * Signs the claims for a proof presentation JSON Web Token (JWT). + * + * @param subjectDID The DID of the subject for whom the JWT is being created. + * @param privateKey The private key used to sign the JWT. + * @param credential The credential to be included in the presentation. + * @param domain The domain of the JWT. + * @param challenge The challenge value for the JWT. + * @return The signed JWT as a string. + */ + internal fun signClaimsProofPresentationJWT( + subjectDID: DID, + privateKey: ECPrivateKey, + credential: Credential, + domain: String, + challenge: String + ): String { + return signClaims(subjectDID, privateKey, domain, challenge, credential) + } + + /** + * Signs the claims for a JWT. + * + * @param subjectDID The DID of the subject for whom the JWT is being created. + * @param privateKey The private key used to sign the JWT. + * @param domain The domain of the JWT. + * @param challenge The challenge value for the JWT. + * @param credential The optional credential to be included in the JWT. + * @return The signed JWT as a string. + */ + private fun signClaims( + subjectDID: DID, + privateKey: ECPrivateKey, + domain: String, + challenge: String, + credential: Credential? = null + ): String { + val presentation: MutableMap> = mutableMapOf( + CONTEXT to setOf(CONTEXT_URL), + TYPE to setOf(VERIFIABLE_PRESENTATION) + ) + credential?.let { + presentation[VERIFIABLE_CREDENTIAL] = listOf(it.id) + } + + val claims = JWTClaimsSet.Builder() + .issuer(subjectDID.toString()) + .audience(domain) + .claim(NONCE, challenge) + .claim(VP, presentation) + .build() + + // Generate a JWS header with the ES256K algorithm + val header = JWSHeader.Builder(JWSAlgorithm.ES256K) + .build() + + // Sign the JWT with the private key + val jwsObject = SignedJWT(header, claims) + val signer = ECDSASigner( + privateKey as PrivateKey, + Curve.SECP256K1 + ) + val provider = BouncyCastleProviderSingleton.getInstance() + signer.jcaContext.provider = provider + jwsObject.sign(signer) + + // Serialize the JWS object to a string + return jwsObject.serialize() + } + /** * Compares this JWTCredential with the specified object for equality. * diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/SDJWTCredential.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/SDJWTCredential.kt new file mode 100644 index 000000000..c879ed33b --- /dev/null +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/SDJWTCredential.kt @@ -0,0 +1,168 @@ +package org.hyperledger.identus.walletsdk.pollux.models +import eu.europa.ec.eudi.sdjwt.JsonPointer +import eu.europa.ec.eudi.sdjwt.JwtAndClaims +import eu.europa.ec.eudi.sdjwt.JwtSignatureVerifier +import eu.europa.ec.eudi.sdjwt.NoSignatureValidation +import eu.europa.ec.eudi.sdjwt.SdJwt +import eu.europa.ec.eudi.sdjwt.SdJwtVerifier +import eu.europa.ec.eudi.sdjwt.present +import eu.europa.ec.eudi.sdjwt.serialize +import kotlinx.coroutines.runBlocking +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.Transient +import kotlinx.serialization.builtins.ArraySerializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonTransformingSerializer +import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.json.jsonPrimitive +import org.hyperledger.identus.walletsdk.domain.models.Claim +import org.hyperledger.identus.walletsdk.domain.models.ClaimType +import org.hyperledger.identus.walletsdk.domain.models.Credential +import org.hyperledger.identus.walletsdk.domain.models.CredentialOperationsOptions +import org.hyperledger.identus.walletsdk.domain.models.ProvableCredential +import org.hyperledger.identus.walletsdk.domain.models.StorableCredential +import org.hyperledger.identus.walletsdk.domain.models.keyManagement.ExportableKey +import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PrivateKey + +@OptIn(ExperimentalSerializationApi::class) +data class SDJWTCredential( + val sdjwtString: String, + val sdjwt: SdJwt.Issuance +) : Credential, ProvableCredential { + override val id: String + get() = sdjwtString + + @Transient + override val issuer: String = sdjwt.jwt.second.get("iss").toString() + + override val subject: String? + get() = sdjwt.jwt.second.get("sub").toString() + + override val claims: Array + get() { + return sdjwt.jwt.second.map { + Claim(key = it.key, value = ClaimType.StringValue(it.value.toString())) + }?.toTypedArray() + ?: emptyArray() + } + + override val properties: Map + get() { + val properties = mutableMapOf() + properties["nbf"] = sdjwt.jwt.second.get("nbf").toString() + properties["jti"] = sdjwt.jwt.second.get("sub").toString() + properties["aud"] = sdjwt.jwt.second.get("aud").toString() + properties["id"] = id + + sdjwt.jwt.second.get("exp").toString().let { properties["exp"] = it } + return properties.toMap() + } + + override var revoked: Boolean? = null + + override suspend fun presentation(request: ByteArray, options: List): String { + var exportableKeyOption: PrivateKey? = null + var disclosingClaims: List? = null + + for (option in options) { + when (option) { + is CredentialOperationsOptions.ExportableKey -> exportableKeyOption = option.key + is CredentialOperationsOptions.DisclosingClaims -> disclosingClaims = option.claims + else -> {} + } + } + + val inluded = disclosingClaims + ?.mapNotNull { JsonPointer.parse(it) } + ?.toSet() + val presentation = sdjwt.present(inluded!!) + return presentation!!.serialize { (jwt, _) -> jwt } + } + + /** + * Converts the current instance of [JWTCredential] to a [StorableCredential]. + * + * @return The converted [StorableCredential]. + */ + fun toStorableCredential(): StorableCredential { + val c = this + return object : StorableCredential { + override val id: String + get() = c.id + override val recoveryId: String + get() = "jwt+credential" + override val credentialData: ByteArray + get() = c.id.toByteArray() + + override val issuer: String + get() = c.issuer + + override val subject: String? + get() = c.subject + override val credentialCreated: String? + get() = null + override val credentialUpdated: String? + get() = null + override val credentialSchema: String? + get() = null + override val validUntil: String? + get() = c.sdjwt.jwt.second.get("exp").toString().toString() + override var revoked: Boolean? = c.revoked + override val availableClaims: Array + get() = c.claims.map { it.key }.toTypedArray() + + override val claims: Array + get() = sdjwt.jwt.second.map { + Claim(key = it.key, value = ClaimType.StringValue(it.value.toString())) + }?.toTypedArray() ?: emptyArray() + + override val properties: Map + get() { + val properties = mutableMapOf() + properties["nbf"] = sdjwt.jwt.second.get("nbf").toString() + properties["jti"] = sdjwt.jwt.second.get("jti").toString() + properties["aud"] = sdjwt.jwt.second.get("aud").toString() + properties["id"] = id + + sdjwt.jwt.second.get("exp").toString().let { properties["exp"] = it } + return properties.toMap() + } + + /** + * Converts the current instance of [JWTCredential] to a [Credential]. + * + * @return The converted [Credential]. + */ + override fun fromStorableCredential(): Credential { + return c + } + } + } + + @OptIn(ExperimentalSerializationApi::class) + object AudSerializer : JsonTransformingSerializer>(ArraySerializer(String.serializer())) { + override fun transformDeserialize(element: JsonElement): JsonElement { + // Check if the element is a JSON array + if (element is JsonArray) { + return element + } + // If it's a single string, wrap it into an array + return Json.encodeToJsonElement(arrayOf(element.jsonPrimitive.content)) + } + } + + companion object { + @JvmStatic + fun fromSDJwtString(sdjwtString: String): SDJWTCredential { + var credential: SDJWTCredential + runBlocking { + val sdjwt = SdJwtVerifier.verifyIssuance(JwtSignatureVerifier.NoSignatureValidation, sdjwtString).getOrThrow() + credential = SDJWTCredential(sdjwtString, sdjwt) + } + return credential + } + } +} diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgentTests.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgentTests.kt index 00f6e919a..23cac922d 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgentTests.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgentTests.kt @@ -32,6 +32,7 @@ import org.hyperledger.identus.walletsdk.domain.models.KeyCurve import org.hyperledger.identus.walletsdk.domain.models.Mediator import org.hyperledger.identus.walletsdk.domain.models.Message import org.hyperledger.identus.walletsdk.domain.models.PresentationClaims +import org.hyperledger.identus.walletsdk.domain.models.ProvableCredential import org.hyperledger.identus.walletsdk.domain.models.Seed import org.hyperledger.identus.walletsdk.domain.models.Signature import org.hyperledger.identus.walletsdk.edgeagent.helpers.AgentOptions @@ -877,6 +878,7 @@ class EdgeAgentTests { val plutoMock = mock() val mercuryMock = mock() val polluxMock = mock() + val provableCredentialMock = mock() val connectionManagerMock = mock() val seed = Seed(MnemonicHelper.createRandomSeed()) @@ -907,7 +909,7 @@ class EdgeAgentTests { `when`(plutoMock.getPrismDIDKeyPathIndex(DID(credential.subject!!))).thenReturn(pathIndexFlow) val testVerifiablePresentationJWTPayload = "testPayload" - `when`(polluxMock.createVerifiablePresentationJWT(any(), any(), any(), any())).thenReturn(testVerifiablePresentationJWTPayload) + `when`(provableCredentialMock.presentation(any(), any())).thenReturn(testVerifiablePresentationJWTPayload) val presentation = agent.preparePresentationForRequestProof( requestPresentation, @@ -922,8 +924,8 @@ class EdgeAgentTests { val attachmentData = attachmentDescriptor.data assertTrue(attachmentData is AttachmentBase64) assertEquals( - testVerifiablePresentationJWTPayload, - attachmentData.base64.base64UrlDecoded + 3, + attachmentData.base64.base64UrlDecoded.split(".").count() ) } diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/PolluxMock.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/PolluxMock.kt index 49e7eb4c7..c2fd895da 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/PolluxMock.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/PolluxMock.kt @@ -3,8 +3,6 @@ package org.hyperledger.identus.walletsdk.edgeagent import anoncreds_wrapper.CredentialOffer import anoncreds_wrapper.CredentialRequestMetadata import anoncreds_wrapper.LinkSecret -import anoncreds_wrapper.Presentation -import anoncreds_wrapper.Schema import kotlinx.serialization.json.JsonObject import org.hyperledger.identus.walletsdk.domain.buildingblocks.Pollux import org.hyperledger.identus.walletsdk.domain.models.AttachmentDescriptor @@ -19,8 +17,6 @@ import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationOptions import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmission import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmissionOptions -import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.RequestPresentation -import org.hyperledger.identus.walletsdk.pollux.models.AnonCredential import org.hyperledger.identus.walletsdk.pollux.models.CredentialRequest import org.hyperledger.identus.walletsdk.pollux.models.CredentialRequestMeta import java.security.interfaces.ECPublicKey @@ -43,20 +39,7 @@ class PolluxMock : Pollux { TODO("Not yet implemented") } - override fun createVerifiablePresentationJWT( - subjectDID: DID, - privateKey: PrivateKey, - credential: Credential, - requestPresentationJson: JsonObject - ): String { - TODO("Not yet implemented") - } - - override suspend fun createVerifiablePresentationAnoncred( - request: RequestPresentation, - credential: AnonCredential, - linkSecret: LinkSecret - ): Presentation { + override fun processCredentialRequestSDJWT(subjectDID: DID, privateKey: PrivateKey, offerJson: JsonObject): String { TODO("Not yet implemented") } @@ -86,14 +69,6 @@ class PolluxMock : Pollux { return extractedCredentialFormatFromMessageReturn ?: throw Exception("Return not defined") } - override suspend fun getCredentialDefinition(id: String): anoncreds_wrapper.CredentialDefinition { - TODO("Not yet implemented") - } - - override suspend fun getSchema(schemaId: String): Schema { - TODO("Not yet implemented") - } - override suspend fun createPresentationDefinitionRequest( type: CredentialType, presentationClaims: PresentationClaims, diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImplTest.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImplTest.kt index bafdd5f0f..292a40ece 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImplTest.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImplTest.kt @@ -104,32 +104,45 @@ class PolluxImplTest { ) } - @Test - fun testGetSchema_whenAnoncred_thenSchemaCorrect() = runTest { - val response = - "{\"name\":\"Schema name\",\"version\":\"1.1\",\"attrNames\":[\"name\",\"surname\"],\"issuerId\":\"did:prism:604ba1764ab89993f9a74625cc4f3e04737919639293eb382cc7adc53767f550\"}" - val httpResponse = HttpResponse(status = HttpStatusCode.OK.value, response) - - doReturn(httpResponse) - .`when`(api).request( - httpMethod = HttpMethod.Get.value, - url = "", - urlParameters = emptyArray(), - httpHeaders = arrayOf(KeyValue(HttpHeaders.ContentType, Typ.Encrypted.typ)), - body = null - ) - pollux = PolluxImpl(apollo, castor, api) - - val schema = pollux.getSchema("") - val attrNames = listOf("name", "surname") - assertEquals("Schema name", schema.name) - assertEquals("1.1", schema.version) - assertEquals(attrNames, schema.attrNames) - assertEquals( - "did:prism:604ba1764ab89993f9a74625cc4f3e04737919639293eb382cc7adc53767f550", - schema.issuerId - ) - } +// @Test +// fun testGetSchema_whenAnoncred_thenSchemaCorrect() = runTest { +// val schema = pollux.getSchema("") +// val attrNames = listOf("name", "surname") +// assertEquals("Schema name", schema.name) +// assertEquals("1.1", schema.version) +// assertEquals(attrNames, schema.attrNames) +// assertEquals( +// "did:prism:604ba1764ab89993f9a74625cc4f3e04737919639293eb382cc7adc53767f550", +// schema.issuerId +// ) +// } + +// @Test +// fun testGetSchema_whenAnoncred_thenSchemaCorrect() = runTest { +// val response = +// "{\"name\":\"Schema name\",\"version\":\"1.1\",\"attrNames\":[\"name\",\"surname\"],\"issuerId\":\"did:prism:604ba1764ab89993f9a74625cc4f3e04737919639293eb382cc7adc53767f550\"}" +// val httpResponse = HttpResponse(status = HttpStatusCode.OK.value, response) +// +// doReturn(httpResponse) +// .`when`(api).request( +// httpMethod = HttpMethod.Get.value, +// url = "", +// urlParameters = emptyArray(), +// httpHeaders = arrayOf(KeyValue(HttpHeaders.ContentType, Typ.Encrypted.typ)), +// body = null +// ) +// pollux = PolluxImpl(apollo, castor, api) +// +// val schema = pollux.getSchema("") +// val attrNames = listOf("name", "surname") +// assertEquals("Schema name", schema.name) +// assertEquals("1.1", schema.version) +// assertEquals(attrNames, schema.attrNames) +// assertEquals( +// "did:prism:604ba1764ab89993f9a74625cc4f3e04737919639293eb382cc7adc53767f550", +// schema.issuerId +// ) +// } @Test fun testCreatePresentationDefinitionRequest_whenOptionsNoJWT_thenExceptionThrown() = runTest { diff --git a/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/messages/MessagesViewModel.kt b/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/messages/MessagesViewModel.kt index 701dab0f8..c865a188f 100644 --- a/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/messages/MessagesViewModel.kt +++ b/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/messages/MessagesViewModel.kt @@ -18,6 +18,7 @@ import org.hyperledger.identus.walletsdk.domain.models.DIDDocument import org.hyperledger.identus.walletsdk.domain.models.InputFieldFilter import org.hyperledger.identus.walletsdk.domain.models.Message import org.hyperledger.identus.walletsdk.domain.models.PresentationClaims +import org.hyperledger.identus.walletsdk.domain.models.ProvableCredential import org.hyperledger.identus.walletsdk.edgeagent.DIDCOMM1 import org.hyperledger.identus.walletsdk.edgeagent.DIDCOMM_MESSAGING import org.hyperledger.identus.walletsdk.edgeagent.protocols.ProtocolType @@ -122,11 +123,13 @@ class MessagesViewModel(application: Application) : AndroidViewModel(application sdk.agent.let { agent -> sdk.mercury.let { mercury -> viewModelScope.launch { - val presentation = agent.preparePresentationForRequestProof( - RequestPresentation.fromMessage(message), - credential - ) - sdk.agent.sendMessage(presentation.makeMessage()) + if (credential is ProvableCredential) { + val presentation = agent.preparePresentationForRequestProof( + RequestPresentation.fromMessage(message), + credential + ) + sdk.agent.sendMessage(presentation.makeMessage()) + } } } }