Skip to content

Commit

Permalink
Merge pull request #66 from velocitycareerlabs/VL-6438-verifications
Browse files Browse the repository at this point in the history
Check that the DID is consistent between the Deep Link and all messag…
  • Loading branch information
michaelavoyan authored Dec 14, 2023
2 parents 2da6086 + 9f32bc4 commit c4f2895
Show file tree
Hide file tree
Showing 47 changed files with 823 additions and 221 deletions.
5 changes: 2 additions & 3 deletions VCL/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ android {
defaultConfig {
minSdk 24
targetSdk 33
// Increase version for release
versionName "1.20.1"
versionCode 116
versionName "1.22.0"
versionCode 117
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,46 @@

package io.velocitycareerlabs.api.entities

import io.velocitycareerlabs.impl.extensions.getUrlSubPath
import kotlin.io.path.ExperimentalPathApi

data class VCLCredentialManifest(
val jwt: VCLJwt,
val vendorOriginContext: String? = null,
val verifiedProfile: VCLVerifiedProfile
val verifiedProfile: VCLVerifiedProfile,
val deepLink: VCLDeepLink? = null
) {
val iss: String get() = jwt.payload?.toJSONObject()?.get(KeyIss) as? String ?: ""
val did: String get() = iss
val issuerId: String get() = retrieveIssuerId()
val aud: String get() = retrieveAud()
val issuerId: String get() = jwt.payload?.toJSONObject()?.get(CodingKeys.KeyIssuer) as? String
?: (jwt.payload?.toJSONObject()?.get(CodingKeys.KeyIssuer) as? Map<*, *>)?.get(CodingKeys.KeyId) as? String
?: ""
val exchangeId: String get() = jwt.payload?.toJSONObject()?.get(KeyExchangeId) as? String ?: ""
val presentationDefinitionId: String get() = (jwt.payload?.toJSONObject()?.get(KeyPresentationDefinitionId) as? Map<*, *>)?.get(
KeyId) as? String ?: ""

val finalizeOffersUri: String get() =
(jwt.payload?.toJSONObject()?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get(
VCLCredentialManifest.KeyFinalizeOffersUri
)?.toString() ?: ""

val checkOffersUri: String get() =
(jwt.payload?.toJSONObject()?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get(
VCLCredentialManifest.KeyCheckOffersUri
)?.toString() ?: ""

val submitPresentationUri: String get() =
(jwt.payload?.toJSONObject()?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get(
VCLCredentialManifest.KeySubmitIdentificationUri
)?.toString() ?: ""

@OptIn(ExperimentalStdlibApi::class)
private fun retrieveIssuerId() =
((jwt.payload?.toJSONObject()?.getOrDefault(CodingKeys.KeyMetadata, HashMap<String, Any>()) as? Map<String, Any> )?.getOrDefault(CodingKeys.KeyFinalizeOffersUri, "") as? String ?: "").substringBefore("/issue/")
val presentationDefinitionId: String
get() = (jwt.payload?.toJSONObject()?.get(KeyPresentationDefinitionId) as? Map<*, *>)?.get(
KeyId
) as? String ?: ""

val finalizeOffersUri: String
get() =
(jwt.payload?.toJSONObject()
?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get(
VCLCredentialManifest.KeyFinalizeOffersUri
)?.toString() ?: ""

val checkOffersUri: String
get() =
(jwt.payload?.toJSONObject()
?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get(
VCLCredentialManifest.KeyCheckOffersUri
)?.toString() ?: ""

val submitPresentationUri: String
get() =
(jwt.payload?.toJSONObject()
?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get(
VCLCredentialManifest.KeySubmitIdentificationUri
)?.toString() ?: ""

private fun retrieveAud() =
((jwt.payload?.toJSONObject()?.getOrDefault(CodingKeys.KeyMetadata, HashMap<String, Any>()) as? Map<String, Any> )?.getOrDefault(CodingKeys.KeyFinalizeOffersUri, "") as? String ?: "").substringBefore("/issue/")
companion object CodingKeys {
const val KeyIssuingRequest = "issuing_request"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ open class VCLCredentialManifestDescriptor(
val issuingType: VCLIssuingType = VCLIssuingType.Career,
val credentialTypes: List<String>? = null,
val pushDelegate: VCLPushDelegate? = null,
val vendorOriginContext: String? = null
val vendorOriginContext: String? = null,
val deepLink: VCLDeepLink? = null
) {
val did = uri?.getUrlSubPath(KeyDidPrefix)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ class VCLCredentialManifestDescriptorByDeepLink(
): VCLCredentialManifestDescriptor(
uri = deepLink.requestUri,
issuingType = issuingType,
vendorOriginContext = deepLink.vendorOriginContext
vendorOriginContext = deepLink.vendorOriginContext,
deepLink = deepLink
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* Copyright 2022 Velocity Career Labs inc.
* SPDX-License-Identifier: Apache-2.0
*/

package io.velocitycareerlabs.api.entities

import io.velocitycareerlabs.impl.extensions.toJsonArray
Expand All @@ -22,8 +21,8 @@ data class VCLFinalizeOffersDescriptor(
.putOpt(KeyApprovedOfferIds, approvedOfferIds.toJsonArray())
.putOpt(KeyRejectedOfferIds, rejectedOfferIds.toJsonArray())

val did: String get() = credentialManifest.did
val issuerId: String get() = credentialManifest.issuerId
val aud: String get() = credentialManifest.aud
val exchangeId: String get() = credentialManifest.exchangeId
val finalizeOffersUri: String get() = credentialManifest.finalizeOffersUri
val serviceTypes: VCLServiceTypes get() = credentialManifest.verifiedProfile.serviceTypes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ data class VCLGenerateOffersDescriptor(
.putOpt(KeyTypes,types?.toJsonArray())
.putOpt(KeyOfferHashes,offerHashes?.toJsonArray())

val did: String get() = credentialManifest.did
val issuerId: String get() = credentialManifest.issuerId
val exchangeId: String get() = credentialManifest.exchangeId

val checkOffersUri: String get() = credentialManifest.checkOffersUri

companion object CodingKeys {
Expand Down
22 changes: 22 additions & 0 deletions VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLOffer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Created by Michael Avoyan on 10/12/2023.
*
* Copyright 2022 Velocity Career Labs inc.
* SPDX-License-Identifier: Apache-2.0
*/
package io.velocitycareerlabs.api.entities

import org.json.JSONObject

data class VCLOffer(val payload: JSONObject) {
val issuerId: String get() = payload.optJSONObject(CodingKeys.KeyIssuer)?.optString(KeyId)
?: payload.optString(CodingKeys.KeyIssuer)
?: ""
val id: String get() = payload.optString(CodingKeys.KeyId) ?: ""

companion object CodingKeys {
const val KeyId = "id"
const val KeyDid = "did"
const val KeyIssuer = "issuer"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import org.json.JSONObject

data class VCLOffers (
val payload: JSONObject,
val all: JSONArray,
val all: List<VCLOffer>,
val responseCode: Int,
val sessionToken: VCLToken,
val challenge: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,10 @@ internal enum class VCLErrorCode(val value: String) {
IssuerRequiresNotaryPermission("issuer_requires_notary_permission"),
InvalidCredentialSubjectType("invalid_credential_subject_type"),
InvalidCredentialSubjectContext("invalid_credential_subject_context"),
IssuerUnexpectedPermissionFailure("issuer_unexpected_permission_failure")
IssuerUnexpectedPermissionFailure("issuer_unexpected_permission_failure"),
// DID consistent with the Deep Link
MismatchedRequestIssuerDid("mismatched_request_issuer_did"),
MismatchedOfferIssuerDid("mismatched_offer_issuer_did"),
MismatchedCredentialIssuerDid("mismatched_credential_issuer_did"),
MismatchedPresentationRequestInspectorDid("mismatched_presentation_request_inspector_did")
}
17 changes: 13 additions & 4 deletions VCL/src/main/java/io/velocitycareerlabs/impl/VclBlocksProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import io.velocitycareerlabs.api.entities.VCLCredentialTypes
import io.velocitycareerlabs.api.entities.error.VCLError
import io.velocitycareerlabs.api.entities.error.VCLErrorCode
import io.velocitycareerlabs.api.entities.initialization.VCLCryptoServicesDescriptor
import io.velocitycareerlabs.impl.data.utils.CredentialDidVerifierImpl
import io.velocitycareerlabs.impl.data.verifiers.CredentialDidVerifierImpl
import io.velocitycareerlabs.impl.data.infrastructure.db.CacheServiceImpl
import io.velocitycareerlabs.impl.data.infrastructure.db.SecretStoreServiceImpl
import io.velocitycareerlabs.impl.data.infrastructure.network.NetworkServiceImpl
Expand All @@ -25,11 +25,16 @@ import io.velocitycareerlabs.impl.data.usecases.*
import io.velocitycareerlabs.api.jwt.VCLJwtSignService
import io.velocitycareerlabs.api.jwt.VCLJwtVerifyService
import io.velocitycareerlabs.api.keys.VCLKeyService
import io.velocitycareerlabs.impl.data.utils.CredentialIssuerVerifierEmptyImpl
import io.velocitycareerlabs.impl.data.utils.CredentialIssuerVerifierImpl
import io.velocitycareerlabs.impl.data.verifiers.CredentialIssuerVerifierEmptyImpl
import io.velocitycareerlabs.impl.data.verifiers.CredentialIssuerVerifierImpl
import io.velocitycareerlabs.impl.data.verifiers.CredentialManifestByDeepLinkVerifierImpl
import io.velocitycareerlabs.impl.data.verifiers.CredentialsByDeepLinkVerifierImpl
import io.velocitycareerlabs.impl.data.verifiers.OffersByDeepLinkVerifierImpl
import io.velocitycareerlabs.impl.data.verifiers.PresentationRequestByDeepLinkVerifierImpl
import io.velocitycareerlabs.impl.domain.models.*
import io.velocitycareerlabs.impl.domain.usecases.*
import io.velocitycareerlabs.impl.domain.utils.CredentialIssuerVerifier
import io.velocitycareerlabs.impl.domain.verifiers.CredentialIssuerVerifier
import io.velocitycareerlabs.impl.domain.verifiers.PresentationRequestByDeepLinkVerifier
import io.velocitycareerlabs.impl.jwt.local.VCLJwtSignServiceLocalImpl
import io.velocitycareerlabs.impl.jwt.local.VCLJwtVerifyServiceLocalImpl
import io.velocitycareerlabs.impl.jwt.remote.VCLJwtSignServiceRemoteImpl
Expand Down Expand Up @@ -169,6 +174,7 @@ internal object VclBlocksProvider {
chooseJwtSignService(context, cryptoServicesDescriptor),
chooseJwtVerifyService(cryptoServicesDescriptor)
),
PresentationRequestByDeepLinkVerifierImpl(),
ExecutorImpl()
)

Expand Down Expand Up @@ -212,6 +218,7 @@ internal object VclBlocksProvider {
chooseJwtSignService(context, cryptoServicesDescriptor),
chooseJwtVerifyService(cryptoServicesDescriptor)
),
CredentialManifestByDeepLinkVerifierImpl(),
ExecutorImpl()
)

Expand Down Expand Up @@ -244,6 +251,7 @@ internal object VclBlocksProvider {
GenerateOffersRepositoryImpl(
NetworkServiceImpl()
),
OffersByDeepLinkVerifierImpl(),
ExecutorImpl()
)

Expand Down Expand Up @@ -272,6 +280,7 @@ internal object VclBlocksProvider {
),
credentialIssuerVerifier,
CredentialDidVerifierImpl(),
CredentialsByDeepLinkVerifierImpl(),
ExecutorImpl()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ import io.velocitycareerlabs.api.entities.error.VCLError
import io.velocitycareerlabs.impl.data.infrastructure.network.Request
import io.velocitycareerlabs.impl.domain.infrastructure.network.NetworkService
import io.velocitycareerlabs.impl.domain.repositories.FinalizeOffersRepository
import io.velocitycareerlabs.impl.extensions.toJsonArray
import io.velocitycareerlabs.impl.extensions.toJwtList
import io.velocitycareerlabs.impl.extensions.toList
import org.json.JSONArray
import java.lang.Exception
import java.util.concurrent.CompletableFuture

internal class FinalizeOffersRepositoryImpl(
private val networkService: NetworkService
Expand All @@ -25,7 +28,7 @@ internal class FinalizeOffersRepositoryImpl(
finalizeOffersDescriptor: VCLFinalizeOffersDescriptor,
sessionToken: VCLToken,
proof: VCLJwt,
completionBlock: (VCLResult<List<String>>) -> Unit
completionBlock: (VCLResult<List<VCLJwt>>) -> Unit
) {
networkService.sendRequest(
endpoint = finalizeOffersDescriptor.finalizeOffersUri,
Expand All @@ -40,14 +43,12 @@ internal class FinalizeOffersRepositoryImpl(
result.handleResult(
{ finalizedOffersResponse ->
try {
val encodedJwts =
JSONArray(finalizedOffersResponse.payload).toList() as? List<String>
encodedJwts?.let {
finalizedOffersResponse.payload.toJwtList()?.let {
completionBlock(VCLResult.Success(it))
} ?: run {
completionBlock(
VCLResult.Failure(
VCLError("Failed to parse: $finalizedOffersResponse.payload")
VCLError("Failed to parse: ${finalizedOffersResponse.payload}")
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.velocitycareerlabs.api.entities.*
import io.velocitycareerlabs.api.entities.error.VCLError
import io.velocitycareerlabs.impl.data.infrastructure.network.Request
import io.velocitycareerlabs.impl.data.infrastructure.network.Response
import io.velocitycareerlabs.impl.data.utils.Utils
import io.velocitycareerlabs.impl.domain.infrastructure.network.NetworkService
import io.velocitycareerlabs.impl.domain.repositories.GenerateOffersRepository
import io.velocitycareerlabs.impl.extensions.toJsonArray
Expand Down Expand Up @@ -42,9 +43,11 @@ internal class GenerateOffersRepositoryImpl(
result.handleResult(
{ offersResponse ->
try {
completionBlock(VCLResult.Success(
parse(offersResponse, sessionToken)
))
completionBlock(
VCLResult.Success(
parse(offersResponse, sessionToken)
)
)
} catch (ex: Exception) {
completionBlock(VCLResult.Failure(VCLError(ex)))
}
Expand All @@ -62,17 +65,19 @@ internal class GenerateOffersRepositoryImpl(
offersResponse.payload.toJsonObject()?.let { payload ->
return VCLOffers(
payload = payload,
all = payload.optJSONArray(VCLOffers.CodingKeys.KeyOffers) ?: JSONArray(),
all = Utils.offersFromJsonArray(
payload.optJSONArray(VCLOffers.CodingKeys.KeyOffers) ?: JSONArray()
),
responseCode = offersResponse.code,
sessionToken = sessionToken,
challenge = payload.optString(VCLOffers.CodingKeys.KeyChallenge) ?: ""
)
} ?: run {
// VCLXVnfProtocolVersion.XVnfProtocolVersion1
offersResponse.payload.toJsonArray()?.let { allOffers ->
offersResponse.payload.toJsonArray()?.let { offersJsonArray ->
return VCLOffers(
payload = JSONObject(),
all = allOffers,
all = Utils.offersFromJsonArray(offersJsonArray),
responseCode = offersResponse.code,
sessionToken = sessionToken,
challenge = ""
Expand All @@ -81,7 +86,7 @@ internal class GenerateOffersRepositoryImpl(
// No offers
return VCLOffers(
payload = JSONObject(),
all = JSONArray(),
all = listOf(),
responseCode = offersResponse.code,
sessionToken = sessionToken,
challenge = ""
Expand Down
Loading

0 comments on commit c4f2895

Please sign in to comment.