Skip to content

Commit

Permalink
feat: create prism did with Ed25519
Browse files Browse the repository at this point in the history
Signed-off-by: Cristian G <cristian.castro@iohk.io>
  • Loading branch information
cristianIOHK committed Sep 26, 2024
1 parent 7165cb2 commit 0e1feaf
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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 -> {
Expand All @@ -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())
)
)
)
)
}
}

/**
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,10 +374,15 @@ open class EdgeAgent {
suspend fun createNewPrismDID(
keyPathIndex: Int? = null,
alias: String? = null,
services: Array<DIDDocument.Service> = emptyArray()
services: Array<DIDDocument.Service> = 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
Expand Down Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ class DIDsFragment : Fragment() {
binding.createDid.setOnClickListener {
viewModel.createPeerDID()
}
binding.createPrismDid.setOnClickListener {
viewModel.createPrismDID()
}
}

override fun onDestroyView() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
)
)
Expand Down Expand Up @@ -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,
Expand Down
11 changes: 11 additions & 0 deletions sampleapp/src/main/res/layout/fragment_dids.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,15 @@
app:tint="@color/white"
android:contentDescription="@string/todo"/>

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/create_prism_did"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@android:drawable/ic_media_next"
app:layout_constraintBottom_toTopOf="@id/create_did"
app:layout_constraintEnd_toEndOf="parent"
app:tint="@color/white"
android:contentDescription="@string/todo"/>

</androidx.constraintlayout.widget.ConstraintLayout>

0 comments on commit 0e1feaf

Please sign in to comment.