Skip to content

Commit

Permalink
Merge pull request #826 from walt-id/core-wallet
Browse files Browse the repository at this point in the history
Core wallet
  • Loading branch information
waltkb authored Nov 18, 2024
2 parents c053ac8 + 1a623bc commit e0c8ff3
Show file tree
Hide file tree
Showing 65 changed files with 4,376 additions and 5 deletions.
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ val modules = listOf(
"waltid-idpkit"
),

"$libraries:waltid-core-wallet",

/*
* "$libraries:util".group(
"waltid-reporting"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ data class AuthorizationRequest(
* If response_uri and redirect_uri are empty and client_id_scheme is "redirect_uri", the response_uri or redirect_uri (depending on the response_mode) are taken from the client_id parameter
*/
fun getRedirectOrResponseUri(): String? {
return when(responseMode) {
return when (responseMode) {
ResponseMode.direct_post -> responseUri
else -> redirectUri
} ?: when(clientIdScheme) {
} ?: when (clientIdScheme) {
ClientIdScheme.RedirectUri -> clientId
else -> null
}
Expand Down Expand Up @@ -147,7 +147,7 @@ data class AuthorizationRequest(
codeChallengeMethod?.let { put("code_challenge_method", JsonPrimitive(it)) }
idTokenHint?.let { put("id_token_hint", JsonPrimitive(it)) }
customParameters.forEach { (key, value) ->
when(value.size) {
when (value.size) {
1 -> put(key, JsonPrimitive(value.first()))
else -> put(key, JsonArray(value.map { JsonPrimitive(it) }))
}
Expand Down Expand Up @@ -223,7 +223,10 @@ data class AuthorizationRequest(

suspend fun fromRequestObjectByReference(requestUri: String): AuthorizationRequest {
println("Request object by reference: $requestUri")
return fromRequestObject(id.walt.oid4vc.util.http.get(requestUri).bodyAsText())
val body = id.walt.oid4vc.util.http.get(requestUri).bodyAsText()
println("Reference resolves to: $body")

return fromRequestObject(body)
}

fun fromRequestObject(request: String): AuthorizationRequest {
Expand Down
101 changes: 101 additions & 0 deletions waltid-libraries/waltid-core-wallet/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
id("maven-publish")
}

group = "id.walt.wallet"

repositories {
mavenCentral()
}

kotlin {
jvm {
withJava()
}

jvmToolchain(21)

sourceSets {
val ktor_version = "2.3.12"

val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")

implementation("io.github.oshai:kotlin-logging-jvm:7.0.0")

implementation(project(":waltid-libraries:crypto:waltid-crypto"))
implementation(project(":waltid-libraries:waltid-did"))

implementation(project(":waltid-libraries:protocols:waltid-openid4vc"))
implementation(project(":waltid-libraries:sdjwt:waltid-sdjwt"))
implementation(project(":waltid-libraries:credentials:waltid-mdoc-credentials"))
implementation(project(":waltid-libraries:credentials:waltid-verifiable-credentials"))

// Ktor client
implementation("io.ktor:ktor-client-core:$ktor_version")
implementation("io.ktor:ktor-client-serialization:$ktor_version")
implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
implementation("io.ktor:ktor-client-json:$ktor_version")
implementation("io.ktor:ktor-client-logging:$ktor_version")

// Bouncy Castle
implementation("org.bouncycastle:bcprov-lts8on:2.73.6")
implementation("org.bouncycastle:bcpkix-lts8on:2.73.6")

// Problematic libraries:
implementation("com.nimbusds:nimbus-jose-jwt:9.41.1")
implementation("com.augustcellars.cose:cose-java:1.1.0")
}
}
val jvmMain by getting {
dependencies {
// Ktor client
implementation("io.ktor:ktor-client-okhttp-jvm:$ktor_version")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0")
}
}
val jvmTest by getting {
dependencies {
implementation("org.junit.jupiter:junit-jupiter-api:5.11.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
implementation("org.slf4j:slf4j-simple:2.0.16")
}
}
}
}

publishing {
repositories {
maven {
url = uri("https://maven.waltid.dev/releases")
val envUsername = System.getenv("MAVEN_USERNAME")
val envPassword = System.getenv("MAVEN_PASSWORD")

val usernameFile = File("$rootDir/secret_maven_username.txt")
val passwordFile = File("$rootDir/secret_maven_password.txt")

val secretMavenUsername = envUsername ?: usernameFile.let { if (it.isFile) it.readLines().first() else "" }
//println("Deploy username length: ${secretMavenUsername.length}")
val secretMavenPassword = envPassword ?: passwordFile.let { if (it.isFile) it.readLines().first() else "" }

//if (secretMavenPassword.isBlank()) {
// println("WARNING: Password is blank!")
//}

credentials {
username = secretMavenUsername
password = secretMavenPassword
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package id.walt.wallet.core

import id.walt.crypto.keys.Key
import id.walt.oid4vc.data.CredentialOffer
import id.walt.oid4vc.data.dif.PresentationDefinition
import id.walt.sdjwt.SDJWTVCTypeMetadata
import id.walt.wallet.core.service.SSIKit2WalletService
import id.walt.wallet.core.service.WalletServiceManager
import id.walt.wallet.core.service.WalletServiceManager.getWalletService
import id.walt.wallet.core.service.WalletServiceManager.httpClient
import id.walt.wallet.core.service.exchange.*
import id.walt.wallet.core.utils.WalletCredential
import id.walt.webwallet.usecase.exchange.FilterData
import io.ktor.http.*
import io.ktor.util.*

object CoreWallet {

val wallet = SSIKit2WalletService(
http = httpClient
)

/**
* Claim credential(s) from an issuer
* @param offer The offer request to use
* @param did The DID to issue the credential(s) to
*
* @return List of credentials
*/
suspend fun useOfferRequest(offer: String, did: String, key: Key): List<CredentialDataResult> {
return IssuanceService.useOfferRequest(
offer = offer,
credentialWallet = SSIKit2WalletService.getCredentialWallet(did = did),
clientId = SSIKit2WalletService.testCIClientConfig.clientID,
key = key,
did = did
)
}

/**
* Present credential(s) to a Relying Party
*
*/
suspend fun usePresentationRequest(
did: String,
presentationRequest: String,
selectedCredentials: List<CredentialDataResult>,
disclosures: Map<CredentialDataResult, List<String>>? = null,
key: Key,
): UsePresentationResponse {
val wallet = getWalletService()

val result = wallet.usePresentationRequest(
PresentationRequestParameter(
key = key,
did = did,
request = presentationRequest,
selectedCredentials = selectedCredentials,
disclosures = disclosures,
)
) // TODO add disclosures here

if (result.isSuccess) {
return UsePresentationResponse(ok = true, redirectUri = result.getOrThrow())
} else {
val err = result.exceptionOrNull()
println("Presentation failed: $err")

return when (err) {
is SSIKit2WalletService.PresentationError ->
UsePresentationResponse(ok = false, redirectUri = err.redirectUri, errorMessage = err.message)

else -> UsePresentationResponse(ok = false, redirectUri = null, errorMessage = err?.message)
}
}
}

/**
* Returns the credentials stored in the wallet that match the passed presentation definition
*
* @param presentationDefinition Presentation definition to match credentials against
*
* @return Credentials that match the presentation definition
*/
fun matchCredentialsForPresentationDefinition(
credentials: List<WalletCredential>,
presentationDefinition: PresentationDefinition
): List<WalletCredential> {
val matchedCredentials =
WalletServiceManager.matchPresentationDefinitionCredentialsUseCase.match(credentials, presentationDefinition)
return matchedCredentials
}

/**
* Returns the credentials that are required by the presentation definition but not found in the wallet
*
* @param presentationDefinition Presentation definition to match credentials against
*
* @return Filters that failed to fulfill the presentation definition
*/
fun unmatchedCredentialsForPresentationDefinition(
credentials: List<WalletCredential>,
presentationDefinition: PresentationDefinition
): List<FilterData> {
val unmatchedCredentialTypes = WalletServiceManager.unmatchedPresentationDefinitionCredentialsUseCase.find(
credentials, presentationDefinition
)
return unmatchedCredentialTypes
}

/**
* Return resolved / parsed presentation request
* @param request PresentationRequest to resolve/parse
*/
suspend fun resolvePresentationRequest(request: String): String {
val wallet = getWalletService()
val parsedRequest = wallet.resolvePresentationRequest(request)
return parsedRequest
}

/**
* Return resolved / parsed credential offer
* @param offer Credential offer request to resolve/parse
* @return Resolved credential offer
*/
suspend fun resolveCredentialOffer(offer: String): CredentialOffer {
val wallet = getWalletService()
val reqParams = Url(offer).parameters.toMap()
val parsedOffer = wallet.resolveCredentialOffer(id.walt.oid4vc.requests.CredentialOfferRequest.fromHttpParameters(reqParams))
return parsedOffer
}

/**
* Receive an verifiable credential type (VCT) URL and return resolved vct object as described in IETF SD-JWT VC
* @param vct The value of the vct in URL format, e.g. https://example.com/mycustomvct
* @return Resolved VCT
*/
suspend fun resolveVctUrl(vct: String): SDJWTVCTypeMetadata {
val wallet = getWalletService()
return wallet.resolveVct(vct)
}


}
Loading

0 comments on commit e0c8ff3

Please sign in to comment.