diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/ApolloImpl.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/ApolloImpl.kt index 89d6ea35b..5fe04dea8 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/ApolloImpl.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/ApolloImpl.kt @@ -38,6 +38,7 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.RawKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.SeedKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorableKey +import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorablePrivateKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.TypeKey import org.hyperledger.identus.walletsdk.logger.LogComponent import org.hyperledger.identus.walletsdk.logger.LogLevel diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/did/prismdid/PrismDIDPublicKey.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/did/prismdid/PrismDIDPublicKey.kt index 14a4f0eca..263430bbf 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/did/prismdid/PrismDIDPublicKey.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/did/prismdid/PrismDIDPublicKey.kt @@ -1,13 +1,20 @@ package org.hyperledger.identus.walletsdk.castor.did.prismdid -import org.hyperledger.identus.apollo.secp256k1.Secp256k1Lib import org.hyperledger.identus.protos.CompressedECKeyData +import org.hyperledger.identus.protos.ECKeyData import org.hyperledger.identus.protos.KeyUsage +import org.hyperledger.identus.walletsdk.apollo.utils.Ed25519PublicKey import org.hyperledger.identus.walletsdk.apollo.utils.Secp256k1PublicKey import org.hyperledger.identus.walletsdk.domain.buildingblocks.Apollo import org.hyperledger.identus.walletsdk.domain.models.CastorError 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 +import org.hyperledger.identus.walletsdk.domain.models.keyManagement.CurvePointYKey +import org.hyperledger.identus.walletsdk.domain.models.keyManagement.KeyTypes import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey +import org.hyperledger.identus.walletsdk.domain.models.keyManagement.RawKey +import org.hyperledger.identus.walletsdk.domain.models.keyManagement.TypeKey import pbandk.ByteArr import kotlin.jvm.Throws @@ -54,7 +61,26 @@ class PrismDIDPublicKey { this.usage = proto.usage.fromProto() this.keyData = when (proto.keyData) { is org.hyperledger.identus.protos.PublicKey.KeyData.CompressedEcKeyData -> { - Secp256k1PublicKey(proto.keyData.value.data.array) + val compressedEcKeyData = proto.compressedEcKeyData!! + apollo.createPublicKey( + properties = mapOf( + TypeKey().property to KeyTypes.EC, + CurveKey().property to compressedEcKeyData.curve, + RawKey().property to compressedEcKeyData.data.array + ) + ) + } + + is org.hyperledger.identus.protos.PublicKey.KeyData.EcKeyData -> { + val ecKeyData = proto.ecKeyData!! + apollo.createPublicKey( + properties = mapOf( + TypeKey().property to KeyTypes.EC, + CurveKey().property to ecKeyData.curve, + CurvePointXKey().property to ecKeyData.x, + CurvePointYKey().property to ecKeyData.y + ) + ) } else -> { @@ -69,14 +95,33 @@ class PrismDIDPublicKey { * @return the converted Protobuf PublicKey object */ fun toProto(): org.hyperledger.identus.protos.PublicKey { - val compressedPublicKey = Secp256k1PublicKey(Secp256k1Lib().compressPublicKey(keyData.getValue())) - return org.hyperledger.identus.protos.PublicKey( - id = id, - usage = usage.toProto(), - keyData = org.hyperledger.identus.protos.PublicKey.KeyData.CompressedEcKeyData( - compressedPublicKey.toProto() + if (keyData.getCurve() == Curve.SECP256K1.value) { + val x = keyData.getProperty(CurvePointXKey().property) + val y = keyData.getProperty(CurvePointYKey().property) + + return org.hyperledger.identus.protos.PublicKey( + id = id, + usage = usage.toProto(), + keyData = org.hyperledger.identus.protos.PublicKey.KeyData.EcKeyData( + ecKeyData = ECKeyData( + curve = keyData.getCurve(), + x = ByteArr(x.encodeToByteArray()), + y = ByteArr(y.encodeToByteArray()) + ) + ) + ) + } else { + return org.hyperledger.identus.protos.PublicKey( + id = id, + usage = usage.toProto(), + keyData = org.hyperledger.identus.protos.PublicKey.KeyData.CompressedEcKeyData( + compressedEcKeyData = CompressedECKeyData( + curve = Curve.ED25519.value, + data = ByteArr(keyData.getEncoded()) + ) + ) ) - ) + } } /** @@ -128,6 +173,18 @@ fun Secp256k1PublicKey.toProto(): CompressedECKeyData { ) } +/** + * Converts a Ed25519PublicKey object to a CompressedECKeyData object. + * + * @return the converted CompressedECKeyData object. + */ +fun Ed25519PublicKey.toProto(): CompressedECKeyData { + return CompressedECKeyData( + curve = Curve.ED25519.value, + data = ByteArr(raw) + ) +} + /** * Generates the identifier for a PrismDIDPublicKey.Usage based on the given index. * 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 9357cd43d..b3b553c8f 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 @@ -374,10 +374,15 @@ open class EdgeAgent { suspend fun createNewPrismDID( keyPathIndex: Int? = null, alias: String? = null, - services: Array = emptyArray() + services: Array = emptyArray(), + format: String? = null ): DID { val index = keyPathIndex ?: (pluto.getPrismLastKeyPathIndex().first() + 1) - val keyPair = Secp256k1KeyPair.generateKeyPair(seed, KeyCurve(Curve.SECP256K1, index)) + val keyPair = if (format == "vc+sd-jwt") { + Ed25519KeyPair.generateKeyPair() + } else { + Secp256k1KeyPair.generateKeyPair(seed, KeyCurve(Curve.SECP256K1, index)) + } val did = castor.createPrismDID(masterPublicKey = keyPair.publicKey, services = services) registerPrismDID(did, index, alias, keyPair.privateKey) return did @@ -659,16 +664,21 @@ open class EdgeAgent { CredentialType.JWT, CredentialType.SDJWT -> { val privateKeyKeyPath = pluto.getPrismDIDKeyPathIndex(did).first() - val keyPair = Secp256k1KeyPair.generateKeyPair( - seed, - KeyCurve(Curve.SECP256K1, privateKeyKeyPath) - ) val offerDataString = offer.attachments.firstNotNullOf { it.data.getDataAsJsonString() } val offerJsonObject = Json.parseToJsonElement(offerDataString).jsonObject - val jwtString = + + val jwtString = if (type == CredentialType.JWT) { + val keyPair = Secp256k1KeyPair.generateKeyPair( + seed, + KeyCurve(Curve.SECP256K1, privateKeyKeyPath) + ) pollux.processCredentialRequestJWT(did, keyPair.privateKey, offerJsonObject) + } else { + val keyPair = Ed25519KeyPair.generateKeyPair() + pollux.processCredentialRequestSDJWT(did, keyPair.privateKey, offerJsonObject) + } val attachmentDescriptor = AttachmentDescriptor( mediaType = ContentType.Application.Json.toString(), 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 8d54685fc..a3fbf3943 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 @@ -296,7 +296,7 @@ open class PolluxImpl( ): String { val domain = getDomain(offerJson) ?: throw PolluxError.NoDomainOrChallengeFound() val challenge = getChallenge(offerJson) ?: throw PolluxError.NoDomainOrChallengeFound() - return signClaimsRequestCredentialJWT(subjectDID, privateKey, domain, challenge) + return signClaims(subjectDID, privateKey, domain, challenge) } /** diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/castor/PrismDIDPublicKeyTests.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/castor/PrismDIDPublicKeyTests.kt index 22575cac4..102255734 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/castor/PrismDIDPublicKeyTests.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/castor/PrismDIDPublicKeyTests.kt @@ -3,31 +3,34 @@ package org.hyperledger.identus.walletsdk.castor import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.hyperledger.identus.protos.PublicKey +import org.hyperledger.identus.walletsdk.apollo.ApolloImpl import org.hyperledger.identus.walletsdk.apollo.utils.Ed25519KeyPair import org.hyperledger.identus.walletsdk.apollo.utils.Ed25519PrivateKey import org.hyperledger.identus.walletsdk.apollo.utils.Ed25519PublicKey import org.hyperledger.identus.walletsdk.castor.did.prismdid.PrismDIDPublicKey import org.hyperledger.identus.walletsdk.castor.did.prismdid.id +import org.hyperledger.identus.walletsdk.domain.buildingblocks.Apollo import org.junit.Ignore import org.junit.Test +import org.mockito.kotlin.mock import kotlin.test.assertContentEquals import kotlin.test.assertEquals class PrismDIDPublicKeyTests { @OptIn(ExperimentalCoroutinesApi::class) - @Ignore("PrismDIDPublicKey requires Secp256k1Lib to be an interface in order to mock its result. Once that is done this test can be added back.") +// @Ignore("PrismDIDPublicKey requires Secp256k1Lib to be an interface in order to mock its result. Once that is done this test can be added back.") @Test fun it_should_parse_proto_toPrismDIDPublicKey() = runTest { - val apollo = ApolloMock() - val seed = apollo.createRandomSeed(passphrase = "mnemonics").seed + val apollo = ApolloImpl() +// val seed = apollo.createRandomSeed(passphrase = "mnemonics").seed val keyPair = Ed25519KeyPair( privateKey = Ed25519PrivateKey(ByteArray(0)), publicKey = Ed25519PublicKey(ByteArray(0)) ) val publicKey = PrismDIDPublicKey( - apollo = ApolloMock(), + apollo = apollo, id = PrismDIDPublicKey.Usage.MASTER_KEY.id(0), usage = PrismDIDPublicKey.Usage.MASTER_KEY, keyData = keyPair.publicKey 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 55a54a7cc..deca17fd8 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 @@ -375,6 +375,26 @@ class EdgeAgentTests { assertTrue { plutoMockOld.wasStorePrismDIDAndPrivateKeysCalled } } + @Test + fun testCreateNewPrismDID_whenVcSdJwt_then() = runTest { + val apollo = ApolloImpl() + val castor = CastorImpl(apollo) + val agent = EdgeAgent( + apollo = apollo, + castor = castor, + pluto = plutoMockOld, + mercury = mercuryMockOld, + pollux = polluxMockOld, + connectionManager = connectionManagerOld, + seed = seed, + api = null, + logger = LoggerMock(), + agentOptions = AgentOptions() + ) + plutoMockOld.getPrismLastKeyPathIndexReturn = flow { emit(0) } + val newDID = agent.createNewPrismDID(format = "vc+sd-jwt") + } + @Test fun testCreateNewPeerDID_shouldCreateNewDID_whenCalled() = runTest { val agent = spy( diff --git a/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/dids/DIDsFragment.kt b/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/dids/DIDsFragment.kt index ab26c847b..10d160f36 100644 --- a/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/dids/DIDsFragment.kt +++ b/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/dids/DIDsFragment.kt @@ -32,6 +32,9 @@ class DIDsFragment : Fragment() { binding.createDid.setOnClickListener { viewModel.createPeerDID() } + binding.createPrismDid.setOnClickListener { + viewModel.createPrismDID() + } } override fun onDestroyView() { diff --git a/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/dids/DIDsViewModel.kt b/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/dids/DIDsViewModel.kt index 3fa6a671c..4eda6d535 100644 --- a/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/dids/DIDsViewModel.kt +++ b/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/ui/dids/DIDsViewModel.kt @@ -45,4 +45,11 @@ class DIDsViewModel(application: Application) : AndroidViewModel(application) { ) } } + + fun createPrismDID() { + viewModelScope.launch { + val sdk = Sdk.getInstance() + val did = sdk.agent.createNewPrismDID(format = "vc+sd-jwt") + } + } } 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 1bbf4671b..f1d2d8df5 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 @@ -119,29 +119,13 @@ class MessagesViewModel(application: Application) : AndroidViewModel(application toDID = DID(toDID), presentationClaims = SDJWTPresentationClaims( claims = mapOf( - "familyName" to InputFieldFilter( + "firstName" to InputFieldFilter( type = "string", pattern = "Wonderland" ), - "givenName" to InputFieldFilter( - type = "string", - pattern = "Alice" - ), - "drivingClass" to InputFieldFilter( - type = "integer", - pattern = "3" - ), - "dateOfIssuance" to InputFieldFilter( - type = "string", - pattern = "2020-11-13T20:20:39+00:00" - ), "emailAddress" to InputFieldFilter( type = "string", pattern = "alice@wonderland.com" - ), - "drivingLicenseID" to InputFieldFilter( - type = "string", - pattern = "12345" ) ) ) @@ -261,7 +245,8 @@ class MessagesViewModel(application: Application) : AndroidViewModel(application processedOffers.add(it) viewModelScope.launch { val offer = OfferCredential.fromMessage(message) - val subjectDID = agent.createNewPrismDID() + val format = message.attachments.first().format + val subjectDID = agent.createNewPrismDID(format = format) val request = agent.prepareRequestCredentialWithIssuer( subjectDID, diff --git a/sampleapp/src/main/res/layout/fragment_dids.xml b/sampleapp/src/main/res/layout/fragment_dids.xml index f6b24d899..30968ab6f 100644 --- a/sampleapp/src/main/res/layout/fragment_dids.xml +++ b/sampleapp/src/main/res/layout/fragment_dids.xml @@ -25,4 +25,15 @@ app:tint="@color/white" android:contentDescription="@string/todo"/> + + \ No newline at end of file