Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Verification JWT Revocation registry check #165

Merged
merged 13 commits into from
Jun 26, 2024
5 changes: 4 additions & 1 deletion edge-agent-sdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +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(kotlin("reflect"))

implementation("com.apicatalog:titanium-json-ld-jre8:1.4.0")
implementation("org.glassfish:jakarta.json:2.0.1")
implementation("io.setl:rdf-urdna:1.3")
}
}
val commonTest by getting {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
@file:Suppress("ktlint:standard:import-ordering")

package org.hyperledger.identus.walletsdk.edgeagent

import androidx.test.ext.junit.runners.AndroidJUnit4
import anoncreds_wrapper.LinkSecret
import io.iohk.atala.prism.apollo.base64.base64UrlDecoded
import io.iohk.atala.prism.apollo.derivation.MnemonicHelper
import org.hyperledger.identus.apollo.base64.base64UrlDecoded
import org.hyperledger.identus.apollo.derivation.MnemonicHelper
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
Expand Down Expand Up @@ -96,7 +98,7 @@ class AnoncredsTests {
HttpStatusCode(200, "Ok"),
getCredentialDefinitionResponse
)
val pollux = PolluxImpl(castorMock, apiMock)
val pollux = PolluxImpl(apolloMock, castorMock, apiMock)
plutoMock.getLinkSecretReturn = flow { emit(LinkSecret().getValue()) }

val agent = EdgeAgent(
Expand Down Expand Up @@ -156,7 +158,7 @@ class AnoncredsTests {
HttpStatusCode(200, "Ok"),
getCredentialDefinitionResponse
)
val pollux = PolluxImpl(castorMock, apiMock)
val pollux = PolluxImpl(apolloMock, castorMock, apiMock)
plutoMock.getLinkSecretReturn = flow { emit(LinkSecret().getValue()) }
val meta = CredentialRequestMeta(
linkSecretName = "1",
Expand Down Expand Up @@ -237,7 +239,7 @@ class AnoncredsTests {
val castorMock = mock<Castor>()
val plutoMock = mock<Pluto>()
val mercuryMock = mock<Mercury>()
val polluxMock: Pollux = PolluxImpl(castorMock, apiMock)
val polluxMock: Pollux = PolluxImpl(apolloMock, castorMock, apiMock)
val connectionManagerMock = mock<ConnectionManager>()
val seed = Seed(MnemonicHelper.createRandomSeed())

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.hyperledger.identus.apollo.derivation.EdHDKey
import org.hyperledger.identus.apollo.derivation.HDKey
import org.hyperledger.identus.apollo.derivation.MnemonicHelper
import org.hyperledger.identus.apollo.derivation.MnemonicLengthException
import org.hyperledger.identus.apollo.utils.KMMECSecp256k1PublicKey
import org.hyperledger.identus.apollo.utils.KMMEdPrivateKey
import org.hyperledger.identus.walletsdk.apollo.utils.Ed25519KeyPair
import org.hyperledger.identus.walletsdk.apollo.utils.Ed25519PrivateKey
Expand All @@ -24,6 +25,8 @@ import org.hyperledger.identus.walletsdk.domain.models.Curve
import org.hyperledger.identus.walletsdk.domain.models.Seed
import org.hyperledger.identus.walletsdk.domain.models.SeedWords
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.DerivationPathKey
import org.hyperledger.identus.walletsdk.domain.models.keyManagement.IndexKey
import org.hyperledger.identus.walletsdk.domain.models.keyManagement.JWK
Expand Down Expand Up @@ -235,6 +238,85 @@ class ApolloImpl : Apollo {
throw ApolloError.InvalidKeyType(TypeKey().property)
}

/**
* Creates a private key based on the provided properties.
*
* @param properties A map of properties used to create the private key. The map should contain the following keys:
* - "type" (String): The type of the key ("EC" or "Curve25519").
* - "curve" (String): The curve of the key.
* - "curvePointX" (String): The x point of the compressed key.
* - "curvePointY" (String): The y point of the compressed key.
* - "rawKey" (ByteArray): The raw key data (optional).
* - "index" (Int): The index for the key (only applicable for EC keys with curve "secp256k1").
* - "derivationPath" (String): The derivation path for the key (only applicable for EC keys with curve "secp256k1").
*
* @return The created private key.
*
* @throws ApolloError.InvalidKeyType If the provided key type is invalid.
* @throws ApolloError.InvalidKeyCurve If the provided key curve is invalid.
* @throws ApolloError.InvalidRawData If the provided raw key data is invalid.
* @throws ApolloError.InvalidIndex If the provided index is invalid.
* @throws ApolloError.InvalidDerivationPath If the provided derivation path is invalid.
*/
override fun createPublicKey(properties: Map<String, Any>): PublicKey {
if (!properties.containsKey(TypeKey().property)) {
throw ApolloError.InvalidKeyType(TypeKey().property)
}
if (!properties.containsKey(CurveKey().property)) {
throw ApolloError.InvalidKeyCurve(CurveKey().property)
}

val keyType = properties[TypeKey().property]
val curve = properties[CurveKey().property]

val keyData = properties[RawKey().property]
val curvePointX = properties[CurvePointXKey().property]
val curvePointY = properties[CurvePointYKey().property]

when (keyType) {
KeyTypes.EC -> {
when (curve) {
Curve.ED25519.value -> {
keyData?.let {
if (it !is ByteArray) {
throw ApolloError.InvalidRawData("KeyData must be a ByteArray")
}
return Ed25519PublicKey(it)
}
}

Curve.SECP256K1.value -> {
if (curvePointX != null && curvePointY != null) {
// Compressed key
val nativePublicKey = KMMECSecp256k1PublicKey.secp256k1FromByteCoordinates(
x = (curvePointX as String).base64UrlDecodedBytes,
y = (curvePointY as String).base64UrlDecodedBytes
)
return Secp256k1PublicKey(nativePublicKey.raw)
} else {
keyData?.let { data ->
if (data !is ByteArray) {
throw Exception("KeyData must be a ByteArray")
}
return Secp256k1PublicKey(data)
}
}
}
}
}

KeyTypes.Curve25519 -> {
keyData?.let {
if (it !is ByteArray) {
throw ApolloError.InvalidRawData("KeyData must be a ByteArray")
}
return X25519PublicKey(it)
}
}
}
throw ApolloError.InvalidKeyType(keyType.toString())
}

/**
* Checks if the provided data is associated with a private key identified by the given identifier.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.hyperledger.identus.walletsdk.apollo.helpers

import java.io.ByteArrayInputStream
import java.util.zip.GZIPInputStream

/**
* Pads the current ByteArray with the specified padValue at the beginning,
* making it equal to or larger than the specified length.
Expand All @@ -17,3 +20,11 @@ fun ByteArray.padStart(length: Int, padValue: Byte): ByteArray {
result
}
}

fun ByteArray.gunzip(): ByteArray {
val byteArrayInputStream = ByteArrayInputStream(this)
val gzipInputStream = GZIPInputStream(byteArrayInputStream)
val decompressedBytes = gzipInputStream.readBytes()

return decompressedBytes
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("ktlint:standard:import-ordering")

package org.hyperledger.identus.walletsdk.castor

import io.ipfs.multibase.Multibase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.hyperledger.identus.walletsdk.domain.models.Seed
import org.hyperledger.identus.walletsdk.domain.models.SeedWords
import org.hyperledger.identus.walletsdk.domain.models.keyManagement.KeyRestoration
import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PrivateKey
import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey
import kotlin.jvm.Throws

/**
Expand Down Expand Up @@ -50,4 +51,16 @@ interface Apollo : KeyRestoration {
* @return A PrivateKey object representing the created private key.
*/
fun createPrivateKey(properties: Map<String, Any>): PrivateKey

/**
* Creates a public key using the provided properties.
*
* @param properties A map containing the properties of the public key.
* The supported properties are:
* - "type": The type of the private key. Use KeyTypes.EC for elliptic curve keys.
* - "raw": The raw data used.
* - "curve": The key curve. Use Curve.SECP256K1 for secp256k1 curve.
* @return A PrivateKey object representing the created private key.
*/
fun createPublicKey(properties: Map<String, Any>): PublicKey
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,6 @@ interface Pollux {
): Boolean

suspend fun extractEcPublicKeyFromVerificationMethod(coreProperty: DIDDocumentCoreProperty): Array<ECPublicKey>

suspend fun isCredentialRevoked(credential: Credential): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,8 @@ constructor(
*
* @see PolluxError
*/
class InvalidJWTCredential : PolluxError("To create a JWT presentation please provide a valid JWTCredential") {
class InvalidJWTCredential(msg: String? = null) :
PolluxError(msg ?: "To create a JWT presentation please provide a valid JWTCredential") {
override val code: Int
get() = 54
}
Expand Down Expand Up @@ -822,17 +823,18 @@ constructor(
*/
class VerificationUnsuccessful(reason: String) : PolluxError(reason) {
override val code: Int
get() = 56
get() = 60
}

/**
* A class representing an error when a provided key is the wrong type.
*
* @see PolluxError
*/
class WrongKeyProvided(expected: String?, actual: String?) : PolluxError("Provided key is: $actual but should be $expected") {
class WrongKeyProvided(expected: String?, actual: String?) :
PolluxError("Provided key is: $actual but should be $expected") {
override val code: Int
get() = 57
get() = 61
}

/**
Expand All @@ -842,7 +844,7 @@ constructor(
*/
class NullField(field: String) : PolluxError("Field $field must not be null") {
override val code: Int
get() = 58
get() = 62
}

/**
Expand All @@ -852,6 +854,46 @@ constructor(
*/
class RequestPresentationHasWrongAttachments(reason: String) : PolluxError(reason) {
override val code: Int
get() = 59
get() = 63
}

/*
* Represents an error that occurs when the status list index is out of bounds compared to the decoded and decompressed value of encodedList.
*/
class StatusListOutOfBoundIndex : PolluxError("Status list index is out of bound") {
override val code: Int
get() = 64
}

/**
* Represents an error that occurs when a revocation registry json is missing a field.
*/
class RevocationRegistryJsonMissingFieldError(val field: String) : PolluxError("Revocation registry json missing: $field") {
override val code: Int
get() = 65
}

/**
* Represents an error that occurs when a revocation registry json is missing a field.
*/
class UnsupportedTypeError(val type: String) : PolluxError("Unsupported type: $type") {
override val code: Int
get() = 66
}

/**
* Represents an error that occurs when a field is null but should not be.
*/
class NonNullableError(val field: String) : PolluxError("Field $field are non nullable.") {
override val code: Int
get() = 67
}

/**
* Represents an error that occurs when a proof cannot be verified.
*/
class VerifyProofError() : PolluxError("The verification failed.") {
override val code: Int
get() = 68
}
}
Loading
Loading