Skip to content

Commit

Permalink
Fix #3: SDJWT Issuance and Verification
Browse files Browse the repository at this point in the history
  • Loading branch information
josmilan authored and georgepadayatti committed Mar 29, 2024
1 parent b62aa88 commit 54c1ce0
Show file tree
Hide file tree
Showing 16 changed files with 674 additions and 169 deletions.
5 changes: 5 additions & 0 deletions eudi-wallet-oidc-android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies {
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
testImplementation("junit:junit:4.13.2")
testImplementation("org.json:json:20220924")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

Expand All @@ -53,4 +54,8 @@ dependencies {
implementation("com.squareup.okhttp3:logging-interceptor:4.3.1")
// Coroutine adapter for Retrofit
implementation("com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2")

implementation("com.github.decentralised-dataexchange:presentation-exchange-sdk-android:2024.3.1")

implementation("com.google.crypto.tink:tink-android:1.7.0")
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@ package com.ewc.eudi_wallet_oidc_android.models

import com.google.gson.annotations.SerializedName

data class ErrorResponse(
@SerializedName("error") var error: Int? = null,
@SerializedName("error_description") var errorDescription: String? = null
)

data class CredentialResponse(
@SerializedName("format") var format: String? = null,
@SerializedName("credential") var credential: String? = null,
@SerializedName("acceptance_token") var acceptanceToken: String? = null,
@SerializedName("error") var error: Int? = null,
@SerializedName("error_description") var errorDescription: String? = null,
@SerializedName("isDeferred") var isDeferred: Boolean? = null,
@SerializedName("isPinRequired") var isPinRequired: Boolean? = null,
@SerializedName("issuerConfig") var issuerConfig: IssuerWellKnownConfiguration? = null,
@SerializedName("authorizationConfig") var authorizationConfig: AuthorisationServerWellKnownConfiguration? = null,
@SerializedName("credentialOffer") var credentialOffer: CredentialOffer? = null
)


data class WrappedCredentialResponse(
var credentialResponse: CredentialResponse? = null,
var errorResponse: ErrorResponse? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.google.gson.annotations.SerializedName
data class PresentationDefinition(

@SerializedName("id") var id: String? = null,
@SerializedName("format") var format: VpFormatsSupported? = VpFormatsSupported(),
@SerializedName("format") var format: Map<String, Jwt>? = mapOf(),
@SerializedName("input_descriptors") var inputDescriptors: ArrayList<InputDescriptors>? = arrayListOf()

)
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ data class PresentationRequest(
@SerializedName("scope") var scope: String? = null,
@SerializedName("nonce") var nonce: String? = null,
@SerializedName("request_uri") var requestUri: String? = null,
@SerializedName("response_uri") var responseUri: String? = null,
@SerializedName("presentation_definition") var presentationDefinition: Any? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@ data class TokenResponse(
@SerializedName("c_nonce_expires_in") var cNonceExpiresIn: Int? = null,
@SerializedName("error") var error: String? = null,
@SerializedName("error_description") var errorDescription: String? = null
)

data class WrappedTokenResponse(
var tokenResponse: TokenResponse? = null,
var errorResponse: ErrorResponse? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.ewc.eudi_wallet_oidc_android.services

import java.net.URL

class UriValidationFailed(s: String) : Exception()

object UrlUtils {

/**
* Validate uri
*
* @param uri
*/
fun validateUri(uri: String?) {
if (uri.isNullOrBlank() || !UrlUtils.isValidUrl(uri?:"")) {
throw UriValidationFailed("URI validation failed")
}
}

fun isValidUrl(url: String): Boolean {
return try {
URL(url)
true
} catch (e: Exception) {
false
}
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
package com.ewc.eudi_wallet_oidc_android.services.did

import com.mediaparkpk.base58android.Base58
import com.nimbusds.jose.jwk.Curve
import com.nimbusds.jose.jwk.ECKey
import com.nimbusds.jose.jwk.OctetKeyPair
import com.nimbusds.jose.jwk.gen.OctetKeyPairGenerator
import com.nimbusds.jose.util.Base64URL
import java.nio.charset.StandardCharsets
import java.security.KeyFactory
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.PrivateKey
import java.security.PublicKey
import java.security.SecureRandom
import java.security.interfaces.ECPrivateKey
import java.security.interfaces.ECPublicKey
import java.security.spec.ECPublicKeySpec
import java.security.spec.PKCS8EncodedKeySpec
import com.mediaparkpk.base58android.Base58
import java.security.SecureRandom
import java.util.UUID


class DIDService : DIDServiceInterface{
class DIDService : DIDServiceInterface {

/**
* Generate a did:key:jcs-pub decentralised identifier.
Expand All @@ -25,7 +30,6 @@ class DIDService : DIDServiceInterface{
override fun createDID(jwk: ECKey): String {
val publicKey = jwk.toPublicJWK()

// Remove whitespaces from JSON string
val compactJson =
"{\"crv\":\"P-256\",\"kty\":\"EC\",\"x\":\"${publicKey?.x}\",\"y\":\"${publicKey?.y}\"}"

Expand All @@ -39,9 +43,7 @@ class DIDService : DIDServiceInterface{
val multiBaseEncoded = multiBaseEncode(multiCodecBytes!!)

// Prefix the string with "did:key"
val didKeyString = "did:key:z$multiBaseEncoded"

return didKeyString
return "did:key:z$multiBaseEncoded"
}

/**
Expand Down Expand Up @@ -70,8 +72,40 @@ class DIDService : DIDServiceInterface{
return ecKey
}

/**
* Generate JWK of curve Ed25519
*
* @return JWK
*/
override fun createED25519JWK(): OctetKeyPair? {
val jwk = OctetKeyPairGenerator(Curve.Ed25519)
.keyID(UUID.randomUUID().toString())
.generate()

return jwk
}

/**
* Generate DID for the ED25519
* @param privateKeyX - X value of the ED25519 jwk
*
* @return DID
*/
override fun createDidED25519(privateKeyX: Base64URL): String {
val startArray = byteArrayOf(0xed.toByte(), 0x01)
val newArray = startArray + Base64URL(privateKeyX.toString()).decode()
// 3. base58 encode the prefixed public key bytes.
var encoded = Base58.encode(newArray)
// 4. prefix the output with ‘z’
encoded = "did:key:z$encoded"
return encoded
}

/**
* Convert the PrivateKey to ECPrivateKey
* @param privateKey
*
* @return ECPrivateKey
*/
private fun convertToECPrivateKey(privateKey: PrivateKey): ECPrivateKey? {
return if (privateKey is ECPrivateKey) {
Expand All @@ -98,6 +132,9 @@ class DIDService : DIDServiceInterface{

/**
* Convert the PublicKey to ECPublicKey
* @param publicKey
*
* @return ECPublicKey
*/
private fun convertToECPublicKey(publicKey: PublicKey): ECPublicKey? {
return if (publicKey is ECPublicKey) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.ewc.eudi_wallet_oidc_android.services.did

import com.nimbusds.jose.jwk.ECKey
import com.nimbusds.jose.jwk.OctetKeyPair
import com.nimbusds.jose.util.Base64URL

interface DIDServiceInterface {

Expand All @@ -19,4 +21,8 @@ interface DIDServiceInterface {
* @return JWK
*/
fun createJWK(seed: String? = null): ECKey

fun createED25519JWK(): OctetKeyPair?

fun createDidED25519(privateKeyX: Base64URL): String
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,54 @@ package com.ewc.eudi_wallet_oidc_android.services.discovery

import com.ewc.eudi_wallet_oidc_android.models.AuthorisationServerWellKnownConfiguration
import com.ewc.eudi_wallet_oidc_android.models.IssuerWellKnownConfiguration
import com.ewc.eudi_wallet_oidc_android.services.UriValidationFailed
import com.ewc.eudi_wallet_oidc_android.services.UrlUtils
import com.ewc.eudi_wallet_oidc_android.services.network.ApiManager

class DiscoveryService : DiscoveryServiceInterface {

/**
* To fetch the Issue configuration
*
* @param credentialIssuerWellKnownURI
*
* @return IssuerWellKnownConfiguration
*/
override suspend fun getIssuerConfig(credentialIssuerWellKnownURI: String?): IssuerWellKnownConfiguration? {
if (credentialIssuerWellKnownURI.isNullOrBlank())
try {
UrlUtils.validateUri(credentialIssuerWellKnownURI)
val response =
ApiManager.api.getService()
?.fetchIssuerConfig("$credentialIssuerWellKnownURI")
return if (response?.isSuccessful == true) {
response.body()
} else {
null
}
} catch (exc: UriValidationFailed) {
return null

val response =
ApiManager.api.getService()?.fetchIssuerConfig("$credentialIssuerWellKnownURI/.well-known/openid-credential-issuer")
return if (response?.isSuccessful == true) {
response.body()
} else {
null
}
}

/**
* To fetch the authorisation server configuration
* @param authorisationServerWellKnownURI
*
* @param authorisationServerWellKnownURI
* @return AuthorisationServerWellKnownConfiguration
*/
override suspend fun getAuthConfig(authorisationServerWellKnownURI: String?): AuthorisationServerWellKnownConfiguration? {
if (authorisationServerWellKnownURI.isNullOrBlank())
return null
try {
UrlUtils.validateUri(authorisationServerWellKnownURI)

val response =
ApiManager.api.getService()?.fetchAuthConfig("$authorisationServerWellKnownURI/.well-known/openid-configuration")
return if (response?.isSuccessful == true) {
response.body()
} else {
null
val response =
ApiManager.api.getService()
?.fetchAuthConfig("$authorisationServerWellKnownURI")
return if (response?.isSuccessful == true) {
response.body()
} else {
null
}
} catch (exc: UriValidationFailed) {
return null
}
}
}
Loading

0 comments on commit 54c1ce0

Please sign in to comment.