Skip to content

Commit

Permalink
Merge pull request #89 from velocitycareerlabs/VL-7093-support-both-v…
Browse files Browse the repository at this point in the history
…nf-protocols-using-didJwk

support both vnf protocols using didJwk
  • Loading branch information
michaelavoyan authored Feb 6, 2024
2 parents 95b7f10 + 5316bfe commit 190095f
Show file tree
Hide file tree
Showing 23 changed files with 119 additions and 64 deletions.
4 changes: 2 additions & 2 deletions VCL/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ android {
defaultConfig {
minSdk 24
targetSdk 33
versionName "1.23.4"
versionCode 124
versionName "2.0.0"
versionCode 125
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
Expand Down
7 changes: 4 additions & 3 deletions VCL/src/main/java/io/velocitycareerlabs/api/VCL.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ interface VCL {

fun submitPresentation(
presentationSubmission: VCLPresentationSubmission,
didJwk: VCLDidJwk? = null,
didJwk: VCLDidJwk,
remoteCryptoServicesToken: VCLToken? = null,
successHandler: (VCLSubmissionResult) -> Unit,
errorHandler: (VCLError) -> Unit
Expand All @@ -60,7 +60,7 @@ interface VCL {

fun generateOffers(
generateOffersDescriptor: VCLGenerateOffersDescriptor,
didJwk: VCLDidJwk? = null,
didJwk: VCLDidJwk,
remoteCryptoServicesToken: VCLToken? = null,
successHandler: (VCLOffers) -> Unit,
errorHandler: (VCLError) -> Unit
Expand All @@ -75,7 +75,7 @@ interface VCL {

fun finalizeOffers(
finalizeOffersDescriptor: VCLFinalizeOffersDescriptor,
didJwk: VCLDidJwk? = null,
didJwk: VCLDidJwk,
sessionToken: VCLToken,
remoteCryptoServicesToken: VCLToken? = null,
successHandler: (VCLJwtVerifiableCredentials) -> Unit,
Expand Down Expand Up @@ -103,6 +103,7 @@ interface VCL {
)

fun generateSignedJwt(
didJwk: VCLDidJwk,
jwtDescriptor: VCLJwtDescriptor,
remoteCryptoServicesToken: VCLToken? = null,
successHandler: (VCLJwt) -> Unit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ import org.json.JSONObject
import java.util.UUID

data class VCLJwtDescriptor(
/**
* The Id of the existing private key to sign with
*/
val keyId: String? = null,
/**
* Json formatted payload
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@

package io.velocitycareerlabs.api.jwt

import io.velocitycareerlabs.api.entities.VCLDidJwk
import io.velocitycareerlabs.api.entities.VCLJwt
import io.velocitycareerlabs.api.entities.VCLJwtDescriptor
import io.velocitycareerlabs.api.entities.VCLPublicJwk
import io.velocitycareerlabs.api.entities.VCLResult
import io.velocitycareerlabs.api.entities.VCLToken

interface VCLJwtSignService {
fun sign(
kid: String? = null,
didJwk: VCLDidJwk,
nonce: String? = null,
jwtDescriptor: VCLJwtDescriptor,
remoteCryptoServicesToken: VCLToken? = null,
Expand Down
8 changes: 5 additions & 3 deletions VCL/src/main/java/io/velocitycareerlabs/impl/VCLImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ internal class VCLImpl: VCL {

override fun submitPresentation(
presentationSubmission: VCLPresentationSubmission,
didJwk: VCLDidJwk?,
didJwk: VCLDidJwk,
remoteCryptoServicesToken: VCLToken?,
successHandler: (VCLSubmissionResult) -> Unit,
errorHandler: (VCLError) -> Unit
Expand Down Expand Up @@ -363,7 +363,7 @@ internal class VCLImpl: VCL {

override fun generateOffers(
generateOffersDescriptor: VCLGenerateOffersDescriptor,
didJwk: VCLDidJwk?,
didJwk: VCLDidJwk,
remoteCryptoServicesToken: VCLToken?,
successHandler: (VCLOffers) -> Unit,
errorHandler: (VCLError) -> Unit
Expand Down Expand Up @@ -432,7 +432,7 @@ internal class VCLImpl: VCL {

override fun finalizeOffers(
finalizeOffersDescriptor: VCLFinalizeOffersDescriptor,
didJwk: VCLDidJwk?,
didJwk: VCLDidJwk,
sessionToken: VCLToken,
remoteCryptoServicesToken: VCLToken?,
successHandler: (VCLJwtVerifiableCredentials) -> Unit,
Expand Down Expand Up @@ -526,12 +526,14 @@ internal class VCLImpl: VCL {
}

override fun generateSignedJwt(
didJwk: VCLDidJwk,
jwtDescriptor: VCLJwtDescriptor,
remoteCryptoServicesToken: VCLToken?,
successHandler: (VCLJwt) -> Unit,
errorHandler: (VCLError) -> Unit
) {
jwtServiceUseCase.generateSignedJwt(
didJwk = didJwk,
jwtDescriptor = jwtDescriptor,
remoteCryptoServicesToken = remoteCryptoServicesToken
) { jwtResult ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ internal class JwtServiceRepositoryImpl(
}

override fun generateSignedJwt(
kid: String?, // did:jwk in case of person binding
didJwk: VCLDidJwk,
nonce: String?, // nonce == challenge
jwtDescriptor: VCLJwtDescriptor,
remoteCryptoServicesToken: VCLToken?,
completionBlock: (VCLResult<VCLJwt>) -> Unit
) {
jwtSignService.sign(
kid = kid,
didJwk = didJwk,
nonce = nonce,
jwtDescriptor = jwtDescriptor,
remoteCryptoServicesToken = remoteCryptoServicesToken,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,17 @@ internal class FinalizeOffersUseCaseImpl(

override fun finalizeOffers(
finalizeOffersDescriptor: VCLFinalizeOffersDescriptor,
didJwk: VCLDidJwk?,
didJwk: VCLDidJwk,
sessionToken: VCLToken,
remoteCryptoServicesToken: VCLToken?,
completionBlock: (VCLResult<VCLJwtVerifiableCredentials>) -> Unit
) {
executor.runOnBackground {
this.jwtServiceRepository.generateSignedJwt(
kid = didJwk?.kid,
didJwk = didJwk,
nonce = finalizeOffersDescriptor.offers.challenge,
jwtDescriptor = VCLJwtDescriptor(
keyId = didJwk?.keyId,
iss = didJwk?.did ?: UUID.randomUUID().toString(),
iss = didJwk.did,
aud = finalizeOffersDescriptor.aud
),
remoteCryptoServicesToken = remoteCryptoServicesToken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ internal class JwtServiceUseCaseImpl(
}

override fun generateSignedJwt(
kid: String?,
didJwk: VCLDidJwk,
nonce: String?,
jwtDescriptor: VCLJwtDescriptor,
remoteCryptoServicesToken: VCLToken?,
completionBlock: (VCLResult<VCLJwt>) -> Unit
) {
executor.runOnBackground {
jwtServiceRepository.generateSignedJwt(
kid = kid,
didJwk = didJwk,
nonce = nonce,
jwtDescriptor = jwtDescriptor,
remoteCryptoServicesToken = remoteCryptoServicesToken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,17 @@ internal class SubmissionUseCaseImpl(
): SubmissionUseCase {
override fun submit(
submission: VCLSubmission,
didJwk: VCLDidJwk?,
didJwk: VCLDidJwk,
remoteCryptoServicesToken: VCLToken?,
completionBlock: (VCLResult<VCLSubmissionResult>) -> Unit
) {
executor.runOnBackground {
jwtServiceRepository.generateSignedJwt(
kid = didJwk?.kid,
didJwk = didJwk,
jwtDescriptor = VCLJwtDescriptor(
keyId = didJwk?.keyId,
payload = submission.generatePayload(didJwk?.did),
payload = submission.generatePayload(didJwk.did),
jti = submission.jti,
iss = didJwk?.did ?: ""
iss = didJwk.did
),
remoteCryptoServicesToken = remoteCryptoServicesToken,
completionBlock = { signedJwtResult ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal interface JwtServiceRepository {
completionBlock: (VCLResult<Boolean>) -> Unit
)
fun generateSignedJwt(
kid: String? = null,
didJwk: VCLDidJwk,
nonce: String? = null,
jwtDescriptor: VCLJwtDescriptor,
remoteCryptoServicesToken: VCLToken? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import io.velocitycareerlabs.api.entities.VCLFinalizeOffersDescriptor
internal interface FinalizeOffersUseCase {
fun finalizeOffers(
finalizeOffersDescriptor: VCLFinalizeOffersDescriptor,
didJwk: VCLDidJwk? = null,
didJwk: VCLDidJwk,
sessionToken: VCLToken,
remoteCryptoServicesToken: VCLToken?,
completionBlock: (VCLResult<VCLJwtVerifiableCredentials>) -> Unit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal interface JwtServiceUseCase {
completionBlock: (VCLResult<Boolean>) -> Unit
)
fun generateSignedJwt(
kid: String? = null,
didJwk: VCLDidJwk,
nonce: String? = null,
jwtDescriptor: VCLJwtDescriptor,
remoteCryptoServicesToken: VCLToken?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import io.velocitycareerlabs.api.entities.VCLToken
internal interface SubmissionUseCase {
fun submit(
submission: VCLSubmission,
didJwk: VCLDidJwk? = null,
didJwk: VCLDidJwk,
remoteCryptoServicesToken: VCLToken?,
completionBlock: (VCLResult<VCLSubmissionResult>) -> Unit
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import com.nimbusds.jose.crypto.ECDSASigner
import com.nimbusds.jose.jwk.ECKey
import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.jwt.SignedJWT
import io.velocitycareerlabs.api.entities.VCLDidJwk
import io.velocitycareerlabs.api.entities.VCLJwt
import io.velocitycareerlabs.api.entities.VCLJwtDescriptor
import io.velocitycareerlabs.api.entities.VCLPublicJwk
import io.velocitycareerlabs.api.entities.VCLResult
import io.velocitycareerlabs.api.entities.VCLToken
import io.velocitycareerlabs.api.entities.error.VCLError
Expand All @@ -33,19 +35,20 @@ class VCLJwtSignServiceLocalImpl(
private val keyService: VCLKeyService
): VCLJwtSignService {
override fun sign(
kid: String?,
didJwk: VCLDidJwk,
nonce: String?,
jwtDescriptor: VCLJwtDescriptor,
remoteCryptoServicesToken: VCLToken?,
completionBlock: (VCLResult<VCLJwt>) -> Unit
) {
getSecretReference(jwtDescriptor.keyId) { ecKeyResult ->
getSecretReference(didJwk.keyId) { ecKeyResult ->
ecKeyResult.handleResult(
successHandler = { ecKey ->
try {
val header = JWSHeader.Builder(JWSAlgorithm.ES256K)
.type(JOSEObjectType(GlobalConfig.TypeJwt))
kid?.let { header.keyID(it) } ?: run { header.jwk(ecKey.toPublicJWK()) }
.jwk(ecKey.toPublicJWK()) // always provide
.keyID(didJwk.kid) // always provide
val jwtHeader = header.build()

val signedJWT = SignedJWT(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

package io.velocitycareerlabs.impl.jwt.remote

import io.velocitycareerlabs.api.entities.VCLDidJwk
import io.velocitycareerlabs.api.entities.VCLJwt
import io.velocitycareerlabs.api.entities.VCLJwtDescriptor
import io.velocitycareerlabs.api.entities.VCLPublicJwk
import io.velocitycareerlabs.api.entities.VCLResult
import io.velocitycareerlabs.api.entities.VCLToken
import io.velocitycareerlabs.api.entities.error.VCLError
Expand All @@ -27,15 +29,15 @@ internal class VCLJwtSignServiceRemoteImpl(
private val jwtSignServiceUrl: String
): VCLJwtSignService {
override fun sign(
kid: String?,
didJwk: VCLDidJwk,
nonce: String?,
jwtDescriptor: VCLJwtDescriptor,
remoteCryptoServicesToken: VCLToken?,
completionBlock: (VCLResult<VCLJwt>) -> Unit
) {
networkService.sendRequest(
endpoint = jwtSignServiceUrl,
body = generateJwtPayloadToSign(kid, nonce, jwtDescriptor).toString(),
body = generateJwtPayloadToSign(didJwk, nonce, jwtDescriptor).toString(),
contentType = Request.ContentTypeApplicationJson,
method = Request.HttpMethod.POST,
headers = listOf(
Expand Down Expand Up @@ -64,7 +66,7 @@ internal class VCLJwtSignServiceRemoteImpl(
}

internal fun generateJwtPayloadToSign(
kid: String?,
didJwk: VCLDidJwk,
nonce: String?,
jwtDescriptor: VCLJwtDescriptor
): JSONObject {
Expand All @@ -73,11 +75,12 @@ internal class VCLJwtSignServiceRemoteImpl(
val options = JSONObject()
val payload = jwtDescriptor.payload?.copy() ?: JSONObject()

// Base assumption:
// HeaderValues.XVnfProtocolVersion == VCLXVnfProtocolVersion.XVnfProtocolVersion1
header.putOpt(KeyJwk, didJwk.publicJwk.valueJson)
// HeaderValues.XVnfProtocolVersion == VCLXVnfProtocolVersion.XVnfProtocolVersion2
header.putOpt(KeyKid, kid)
header.putOpt(KeyKid, didJwk.kid)

options.putOpt(KeyKeyId, jwtDescriptor.keyId)
options.putOpt(KeyKeyId, didJwk.keyId)

payload.putOpt(KeyNonce, nonce)
payload.putOpt(KeyAud, jwtDescriptor.aud)
Expand All @@ -94,6 +97,7 @@ internal class VCLJwtSignServiceRemoteImpl(
companion object CodingKeys {
const val KeyKeyId = "keyId"
const val KeyKid = "kid"
const val KeyJwk = "jwk"
const val KeyIss = "iss"
const val KeyAud = "aud"
const val KeyJti = "jti"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package io.velocitycareerlabs.entities

import android.os.Build
import io.velocitycareerlabs.api.entities.VCLCredentialManifest
import io.velocitycareerlabs.api.entities.VCLDidJwk
import io.velocitycareerlabs.api.entities.VCLFinalizeOffersDescriptor
import io.velocitycareerlabs.api.entities.VCLJwt
import io.velocitycareerlabs.api.entities.VCLJwtDescriptor
Expand Down Expand Up @@ -36,6 +37,9 @@ import org.robolectric.annotation.Config
class VCLFinalizeOffersDescriptorTest {
lateinit var subject: VCLFinalizeOffersDescriptor

private lateinit var didJwk: VCLDidJwk
private val keyService = VCLKeyServiceLocalImpl(SecretStoreServiceMock.Instance)

private val offers = VCLOffers(
payload = JSONObject(),
all = listOf(),
Expand All @@ -54,6 +58,14 @@ class VCLFinalizeOffersDescriptorTest {

@Before
fun setUp() {
keyService.generateDidJwk(null) { didJwkResult ->
didJwkResult.handleResult({
didJwk = it
}, {
assert(false) { "Failed to generate did:jwk $it" }
})
}

val credentialManifest =
VCLCredentialManifest(
jwt = VCLJwt(encodedJwt = CredentialManifestMocks.JwtCredentialManifest1),
Expand All @@ -72,6 +84,7 @@ class VCLFinalizeOffersDescriptorTest {
fun testGenerateRequestBody() {
val payload = "{\"key1\": \"value1\"}".toJsonObject()
VCLJwtSignServiceLocalImpl(VCLKeyServiceLocalImpl(SecretStoreServiceMock.Instance)).sign(
didJwk = didJwk,
nonce = nonceMock,
jwtDescriptor = VCLJwtDescriptor(
payload = payload,
Expand Down
Loading

0 comments on commit 190095f

Please sign in to comment.