Skip to content

Commit

Permalink
Update CredentialPack to use ParsedCredential (#39)
Browse files Browse the repository at this point in the history
* Update CredentialPack to use ParsedCredential

* Update SpruceKit Demo

* Small adjustments to make demo app compatible

* Parse any JSON type

---------

Co-authored-by: Juliano Cezar Chagas Tavares <[email protected]>
  • Loading branch information
cobward and Juliano1612 authored Oct 15, 2024
1 parent 99ad02e commit 6b039d4
Show file tree
Hide file tree
Showing 12 changed files with 309 additions and 236 deletions.
8 changes: 0 additions & 8 deletions .idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 0 additions & 33 deletions MobileSdk/src/main/java/com/spruceid/mobile/sdk/BaseCredential.kt

This file was deleted.

112 changes: 112 additions & 0 deletions MobileSdk/src/main/java/com/spruceid/mobile/sdk/Credential.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package com.spruceid.mobile.sdk

import com.spruceid.mobile.sdk.rs.JsonVc
import com.spruceid.mobile.sdk.rs.JwtVc
import com.spruceid.mobile.sdk.rs.Mdoc
import org.json.JSONException
import org.json.JSONObject
import org.json.JSONTokener

/**
* Access all of the elements in the mdoc, ignoring namespaces and missing elements that cannot be encoded as JSON.
*/
fun Mdoc.jsonEncodedDetailsAll(): JSONObject = this.jsonEncodedDetailsInternal(null)

/**
* Access the specified elements in the mdoc, ignoring namespaces and missing elements that cannot be encoded as JSON.
*/
fun Mdoc.jsonEncodedDetailsFiltered(elementIdentifiers: List<String>): JSONObject = this.jsonEncodedDetailsInternal(elementIdentifiers)


private fun Mdoc.jsonEncodedDetailsInternal(elementIdentifiers: List<String>?): JSONObject =
JSONObject(
// Ignore the namespaces.
this.details().values.flatMap { elements ->
elements.map { element ->
val id = element.identifier
val jsonString = element.value

// If a filter is provided, filter out non-specified ids.
if (elementIdentifiers != null) {
if (!elementIdentifiers.contains(id)) {
return@map null
}
}

if (jsonString != null) {
try {
val jsonElement = JSONTokener(jsonString).nextValue()
return@map Pair(id, jsonElement)
} catch (e: JSONException) {
print("failed to decode '$id' as JSON: $e")
}
}

return@map null
}
}.filterNotNull().toMap()
)

/**
* Access the W3C VCDM credential (not including the JWT envelope).
*/
fun JwtVc.credentialClaims(): JSONObject {
try {
return JSONObject(this.credentialAsJsonEncodedUtf8String())
} catch (e: Error) {
print("failed to decode VCDM data from UTF-8-encoded JSON")
return JSONObject()
}
}

/**
* Access the specified claims from the W3C VCDM credential (not including the JWT envelope).
*/
fun JwtVc.credentialClaimsFiltered(claimNames: List<String>): JSONObject {
val old = this.credentialClaims()
val new = JSONObject()
for (name in claimNames) {
if (old.has(name)) {
new.put(name, keyPathFinder(old, name.split(".").toMutableList()))
}
}
return new
}

/**
* Access the W3C VCDM credential.
*/
fun JsonVc.credentialClaims(): JSONObject {
try {
return JSONObject(this.credentialAsJsonEncodedUtf8String())
} catch (e: Error) {
print("failed to decode VCDM data from UTF-8-encoded JSON")
return JSONObject()
}
}

/**
* Access the specified claims from the W3C VCDM credential.
*/
fun JsonVc.credentialClaimsFiltered(claimNames: List<String>): JSONObject {
val old = this.credentialClaims()
val new = JSONObject()
for (name in claimNames) {
new.put(name, keyPathFinder(old, name.split(".").toMutableList()))
}
return new
}

private fun keyPathFinder(json: Any, path: MutableList<String>): Any {
try {
val firstKey = path.first()
val element = (json as JSONObject)[firstKey]
path.removeAt(0)
if (path.isNotEmpty()) {
return keyPathFinder(element, path)
}
return element
} catch (e: Exception) {
return ""
}
}
156 changes: 73 additions & 83 deletions MobileSdk/src/main/java/com/spruceid/mobile/sdk/CredentialPack.kt
Original file line number Diff line number Diff line change
@@ -1,108 +1,98 @@
package com.spruceid.mobile.sdk

import java.security.KeyFactory
import java.security.KeyStore
import java.security.cert.Certificate
import java.security.cert.CertificateFactory
import java.security.spec.PKCS8EncodedKeySpec
import java.util.Base64
import com.spruceid.mobile.sdk.rs.JsonVc
import com.spruceid.mobile.sdk.rs.JwtVc
import com.spruceid.mobile.sdk.rs.Mdoc
import com.spruceid.mobile.sdk.rs.ParsedCredential
import org.json.JSONObject

/**
* Collection of BaseCredentials with methods to interact with all instances
*/
class CredentialPack {
private val credentials: MutableList<BaseCredential>
private val credentials: MutableList<ParsedCredential>

constructor() {
credentials = mutableListOf()
}

constructor(credentialsArray: MutableList<BaseCredential>) {
constructor(credentialsArray: MutableList<ParsedCredential>) {
this.credentials = credentialsArray
}

fun addW3CVC(credentialString: String): List<BaseCredential> {
val vc = W3CVC(credentialString = credentialString)
credentials.add(vc)
/**
* Add a JwtVc to the CredentialPack.
*/
fun addJwtVc(jwtVc: JwtVc): List<ParsedCredential> {
credentials.add(ParsedCredential.newJwtVcJson(jwtVc))
return credentials
}

fun addMDoc(
id: String,
mdocBase64: String,
keyPEM: String,
keyBase64: String
): List<BaseCredential> {
try {
val decodedKey = Base64.getDecoder().decode(
keyBase64
)

val privateKey = KeyFactory.getInstance(
"EC"
).generatePrivate(
PKCS8EncodedKeySpec(
decodedKey
)
)

val cert: Array<Certificate> = arrayOf(
CertificateFactory.getInstance(
"X.509"
).generateCertificate(
keyPEM.byteInputStream()
)
)

val ks: KeyStore = KeyStore.getInstance(
"AndroidKeyStore"
)

ks.load(
null
)

ks.setKeyEntry(
"someAlias",
privateKey,
null,
cert
)

credentials.add(
MDoc(
id,
Base64.getDecoder().decode(mdocBase64),
"someAlias"
)
)
} catch (e: Throwable) {
print(
e
)
throw e
}
/**
* Add a JsonVc to the CredentialPack.
*/
fun addJsonVc(jsonVc: JsonVc): List<ParsedCredential> {
credentials.add(ParsedCredential.newLdpVc(jsonVc))
return credentials
}

fun get(keys: List<String>): Map<String, Map<String, Any>> {
val values = emptyMap<String, Map<String, Any>>().toMutableMap()

for (credential in credentials) {
values[credential.getId()!!] = credential.get(keys)
}
return values
}

fun getCredentialsByIds(credentialsIds: List<String>): List<BaseCredential> {
return credentials.filter { credential -> credentialsIds.contains(credential.getId()) }
}

fun getCredentials(): List<BaseCredential> {
/**
* Add an Mdoc to the CredentialPack.
*/
fun addMdoc(mdoc: Mdoc): List<ParsedCredential> {
credentials.add(ParsedCredential.newMsoMdoc(mdoc))
return credentials
}

fun getCredentialById(credentialId: String): BaseCredential? {
return credentials.find { credential -> credential.getId().equals(credentialId) }
}
/**
* Find claims from all credentials in this CredentialPack.
*/
fun findCredentialClaims(claimNames: List<String>): Map<String, JSONObject> =
this.list()
.map { credential ->
var claims: JSONObject
val mdoc = credential.asMsoMdoc()
val jwtVc = credential.asJwtVc()
val jsonVc = credential.asJsonVc()

if (mdoc != null) {
claims = mdoc.jsonEncodedDetailsFiltered(claimNames)
} else if (jwtVc != null) {
claims = jwtVc.credentialClaimsFiltered(claimNames)
} else if (jsonVc != null) {
claims = jsonVc.credentialClaimsFiltered(claimNames)
} else {
var type: String
try {
type = credential.intoGenericForm().type
} catch (e: Error) {
type = "unknown"
}
print("unsupported credential type: $type")
claims = JSONObject()
}

return@map Pair(credential.id(), claims)
}
.toMap()


/**
* Get credentials by id.
*/
fun getCredentialsByIds(credentialsIds: List<String>): List<ParsedCredential> =
this.list().filter { credential -> credentialsIds.contains(credential.id()) }


/**
* Get a credential by id.
*/
fun getCredentialById(credentialId: String): ParsedCredential? =
this.list().find { credential -> credential.id() == credentialId }


/**
* List all of the credentials in the CredentialPack.
*/
fun list(): List<ParsedCredential> = this.credentials
}
Loading

0 comments on commit 6b039d4

Please sign in to comment.