-
Notifications
You must be signed in to change notification settings - Fork 0
Add sdJwt presentation #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
b2774ed
f355664
500c785
8f8f73e
df11201
8676c49
c9d2e6f
143dea7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,9 +35,11 @@ import eu.europa.ec.eudi.wallet.document.sample.SampleDocumentManager | |
import eu.europa.ec.eudi.wallet.internal.getCertificate | ||
import eu.europa.ec.eudi.wallet.internal.mainExecutor | ||
import eu.europa.ec.eudi.wallet.issue.openid4vci.* | ||
import eu.europa.ec.eudi.wallet.keystore.KeyGenerator | ||
import eu.europa.ec.eudi.wallet.keystore.KeyGeneratorImpl | ||
import eu.europa.ec.eudi.wallet.transfer.openid4vp.OpenId4VpCBORResponse | ||
import eu.europa.ec.eudi.wallet.transfer.openid4vp.OpenId4VpCBORResponseGeneratorImpl | ||
import eu.europa.ec.eudi.wallet.transfer.openid4vp.OpenId4vpManager | ||
import eu.europa.ec.eudi.wallet.transfer.openid4vp.responseGenerator.OpenId4VpResponseGeneratorDelegator | ||
import eu.europa.ec.eudi.wallet.util.DefaultNfcEngagementService | ||
import java.security.cert.X509Certificate | ||
import java.util.concurrent.Executor | ||
|
@@ -68,7 +70,7 @@ import java.util.concurrent.Executor | |
* | ||
*/ | ||
@SuppressLint("StaticFieldLeak") | ||
object EudiWallet { | ||
object EudiWallet : KeyGenerator by KeyGeneratorImpl { | ||
|
||
@Volatile | ||
private lateinit var context: Context | ||
|
@@ -88,6 +90,7 @@ object EudiWallet { | |
fun init(context: Context, config: EudiWalletConfig) { | ||
this.context = context.applicationContext | ||
this._config = config | ||
DocumentManagerSdJwt.init(context, config.userAuthenticationRequired) | ||
} | ||
|
||
/** | ||
|
@@ -197,7 +200,9 @@ object EudiWallet { | |
* @throws IllegalStateException if [EudiWallet] is not firstly initialized via the [init] method | ||
*/ | ||
fun deleteDocumentById(documentId: DocumentId): DeleteDocumentResult = | ||
documentManager.deleteDocumentById(documentId) | ||
documentManager.deleteDocumentById(documentId).apply { | ||
if (this is DeleteDocumentResult.Success) DocumentManagerSdJwt.deleteDocument(documentId) | ||
} | ||
|
||
/** | ||
* Create an [UnsignedDocument] for the given [docType] | ||
|
@@ -224,7 +229,10 @@ object EudiWallet { | |
* @return [StoreDocumentResult] | ||
* @throws IllegalStateException if [EudiWallet] is not firstly initialized via the [init] method | ||
*/ | ||
fun storeIssuedDocument(unsignedDocument: UnsignedDocument, data: ByteArray): StoreDocumentResult = | ||
fun storeIssuedDocument( | ||
unsignedDocument: UnsignedDocument, | ||
data: ByteArray | ||
): StoreDocumentResult = | ||
documentManager.storeIssuedDocument(unsignedDocument, data) | ||
|
||
private var openId4VciManager: OpenId4VciManager? = null | ||
|
@@ -280,7 +288,15 @@ object EudiWallet { | |
config(config) | ||
logger = this@EudiWallet.logger | ||
ktorHttpClientFactory = _config.ktorHttpClientFactory | ||
}.also { it.issueDocumentByDocType(docType, txCode, executor, authorizationHandler, onEvent) } | ||
}.also { | ||
it.issueDocumentByDocType( | ||
docType, | ||
txCode, | ||
executor, | ||
authorizationHandler, | ||
onEvent | ||
) | ||
} | ||
} ?: run { | ||
(executor ?: context.mainExecutor()).execute { | ||
onEvent(IssueEvent.failure(IllegalStateException("OpenId4Vci config is not set in configuration"))) | ||
|
@@ -316,7 +332,15 @@ object EudiWallet { | |
config(config) | ||
logger = this@EudiWallet.logger | ||
ktorHttpClientFactory = _config.ktorHttpClientFactory | ||
}.also { it.issueDocumentByOffer(offer, txCode, executor, authorizationHandler, onEvent) } | ||
}.also { | ||
it.issueDocumentByOffer( | ||
offer, | ||
txCode, | ||
executor, | ||
authorizationHandler, | ||
onEvent | ||
) | ||
} | ||
} ?: run { | ||
(executor ?: context.mainExecutor()).execute { | ||
onEvent(IssueEvent.failure(IllegalStateException("OpenId4Vci config is not set in configuration"))) | ||
|
@@ -351,7 +375,15 @@ object EudiWallet { | |
config(config) | ||
logger = this@EudiWallet.logger | ||
ktorHttpClientFactory = _config.ktorHttpClientFactory | ||
}.also { it.issueDocumentByOfferUri(offerUri, txCode, executor, authorizationHandler, onEvent) } | ||
}.also { | ||
it.issueDocumentByOfferUri( | ||
offerUri, | ||
txCode, | ||
executor, | ||
authorizationHandler, | ||
onEvent | ||
) | ||
} | ||
} ?: run { | ||
(executor ?: context.mainExecutor()).execute { | ||
onEvent(IssueEvent.failure(IllegalStateException("OpenId4Vci config is not set in configuration"))) | ||
|
@@ -383,7 +415,12 @@ object EudiWallet { | |
ktorHttpClientFactory = _config.ktorHttpClientFactory | ||
}.also { | ||
when (val document = documentManager.getDocumentById(documentId)) { | ||
is DeferredDocument -> it.issueDeferredDocument(document, executor, onResult) | ||
is DeferredDocument -> it.issueDeferredDocument( | ||
document, | ||
executor, | ||
onResult | ||
) | ||
|
||
else -> (executor ?: context.mainExecutor()).execute { | ||
onResult( | ||
DeferredIssueResult.DocumentFailed( | ||
|
@@ -474,8 +511,16 @@ object EudiWallet { | |
* @return [EudiWallet] | ||
*/ | ||
fun setTrustedReaderCertificates(trustedReaderCertificates: List<X509Certificate>) = apply { | ||
deviceResponseGenerator.setReaderTrustStore(ReaderTrustStore.getDefault(trustedReaderCertificates)) | ||
openId4VpCBORResponseGenerator.setReaderTrustStore(ReaderTrustStore.getDefault(trustedReaderCertificates)) | ||
deviceResponseGenerator.setReaderTrustStore( | ||
ReaderTrustStore.getDefault( | ||
trustedReaderCertificates | ||
) | ||
) | ||
openId4VpCBORResponseGenerator.setReaderTrustStore( | ||
ReaderTrustStore.getDefault( | ||
trustedReaderCertificates | ||
) | ||
) | ||
} | ||
|
||
/** | ||
|
@@ -630,9 +675,10 @@ object EudiWallet { | |
// create response | ||
val responseResult = when (transferMode) { | ||
TransferMode.OPENID4VP -> | ||
openId4vpManager?.responseGenerator?.createResponse(disclosedDocuments) ?: ResponseResult.Failure( | ||
Throwable("Openid4vpManager has not been initialized properly") | ||
) | ||
openId4vpManager?.responseGenerator?.createResponse(disclosedDocuments) | ||
?: ResponseResult.Failure( | ||
Throwable("Openid4vpManager has not been initialized properly") | ||
) | ||
|
||
TransferMode.ISO_18013_5, TransferMode.REST_API -> | ||
transferManager.responseGenerator.createResponse(disclosedDocuments) | ||
|
@@ -645,7 +691,12 @@ object EudiWallet { | |
is ResponseResult.Success -> { | ||
when (transferMode) { | ||
TransferMode.OPENID4VP -> | ||
openId4vpManager?.sendResponse((responseResult.response as OpenId4VpCBORResponse).deviceResponseBytes) | ||
openId4vpManager?.sendResponse( | ||
when(val result = responseResult.response){ | ||
is OpenId4VpCBORResponse -> result.deviceResponseBytes | ||
is DeviceResponse -> result.deviceResponseBytes | ||
else -> throw Exception() | ||
}) | ||
|
||
TransferMode.ISO_18013_5, TransferMode.REST_API -> | ||
transferManager.sendResponse((responseResult.response as DeviceResponse).deviceResponseBytes) | ||
|
@@ -695,9 +746,23 @@ object EudiWallet { | |
|
||
private val transferManagerDocumentsResolver: DocumentsResolver | ||
get() = DocumentsResolver { req -> | ||
documentManager.getDocuments(Document.State.ISSUED) | ||
|
||
DocumentManagerSdJwt | ||
.getAllDocuments() | ||
// .filter { doc -> doc.vct == req.docType } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uncommented code, remove? |
||
.map { doc -> | ||
RequestDocument( | ||
documentId = doc.id, | ||
docType = doc.vct, | ||
docName = doc.docName, | ||
userAuthentication = doc.requiresUserAuth, | ||
docRequest = req | ||
) | ||
}.takeIf { it.isNotEmpty() }?.let { return@DocumentsResolver it } | ||
|
||
return@DocumentsResolver documentManager.getDocuments(Document.State.ISSUED) | ||
.filterIsInstance<IssuedDocument>() | ||
.filter { doc -> doc.docType == req.docType } | ||
// .filter { doc -> doc.docType == req.docType } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uncommented code, remove? |
||
.map { doc -> | ||
RequestDocument( | ||
documentId = doc.id, | ||
|
@@ -721,9 +786,9 @@ object EudiWallet { | |
} | ||
} | ||
|
||
private val openId4VpCBORResponseGenerator: OpenId4VpCBORResponseGeneratorImpl by lazy { | ||
private val openId4VpCBORResponseGenerator: OpenId4VpResponseGeneratorDelegator by lazy { | ||
requireInit { | ||
OpenId4VpCBORResponseGeneratorImpl.Builder(context) | ||
OpenId4VpResponseGeneratorDelegator.Builder(context) | ||
.apply { | ||
_config.trustedReaderCertificates?.let { | ||
readerTrustStore = ReaderTrustStore.getDefault(it) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,7 @@ import eu.europa.ec.eudi.openid4vci.* | |
*/ | ||
internal data class DefaultOffer( | ||
@JvmSynthetic val credentialOffer: CredentialOffer, | ||
@JvmSynthetic val credentialConfigurationFilter: CredentialConfigurationFilter = CredentialConfigurationFilter.MsoMdocFormatFilter, | ||
@JvmSynthetic val credentialConfigurationFilter: CredentialConfigurationFilter = CredentialConfigurationFilter.SdJwtOrMsoMdocFormatFilter, | ||
) : Offer { | ||
|
||
private val issuerMetadata: CredentialIssuerMetadata | ||
|
@@ -41,8 +41,8 @@ internal data class DefaultOffer( | |
|
||
override val offeredDocuments: List<Offer.OfferedDocument> | ||
get() = issuerMetadata.credentialConfigurationsSupported | ||
.filterKeys { it in credentialOffer.credentialConfigurationIdentifiers } | ||
.filterValues { credentialConfigurationFilter(it) } | ||
//.filterKeys { it in credentialOffer.credentialConfigurationIdentifiers } | ||
//.filterValues { credentialConfigurationFilter(it) } | ||
Comment on lines
+45
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uncommented code, remove? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still uncommented. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hab einen Kommentar drüber damit man weiß, dass das wieder rein muss |
||
.map { (id, conf) -> DefaultOfferedDocument(id, conf) } | ||
|
||
override val txCodeSpec: Offer.TxCodeSpec? | ||
|
@@ -92,7 +92,7 @@ internal val CredentialConfiguration.name: String | |
internal val CredentialConfiguration.docType: String | ||
@JvmSynthetic get() = when (this) { | ||
is MsoMdocCredential -> docType | ||
is SdJwtVcCredential -> "eu.europa.ec.eudi.pid_jwt_vc_json" | ||
is SdJwtVcCredential -> "eu.europa.ec.eudi.pid.1" | ||
else -> "unknown" | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package eu.europa.ec.eudi.wallet.issue.openid4vci | ||
|
||
import android.content.Context | ||
import eu.europa.ec.eudi.wallet.document.DocumentId | ||
import eu.europa.ec.eudi.wallet.util.getEncryptedSharedPreferences | ||
import org.json.JSONException | ||
import org.json.JSONObject | ||
import java.util.Base64 | ||
|
||
object DocumentManagerSdJwt { | ||
private lateinit var dataStore: SdJwtDocumentDataStore | ||
|
||
fun init(context: Context, requiresUserAuth: Boolean) { | ||
dataStore = SdJwtDocumentDataStore(context, requiresUserAuth) | ||
} | ||
|
||
fun storeDocument(id: String, credentials: String) { | ||
dataStore.add(id, credentials) | ||
} | ||
|
||
fun getDocumentById(id: String) = dataStore.get(id) | ||
|
||
fun getAllDocuments() = dataStore.getAll() | ||
|
||
fun deleteDocument(documentId: DocumentId) { | ||
// quick but very dirty solution (we decided to only have one document at all times) | ||
deleteAllDocuments() | ||
} | ||
|
||
fun deleteAllDocuments() { | ||
dataStore.deleteAll() | ||
} | ||
|
||
} | ||
|
||
data class SdJwtDocument( | ||
val id: String, | ||
val vct: String, | ||
val docName: String, | ||
val requiresUserAuth: Boolean, | ||
val data: String, | ||
) | ||
|
||
private class SdJwtDocumentDataStore( | ||
context: Context, | ||
val requiresUserAuth: Boolean, | ||
) { | ||
private var sharedPreferences = getEncryptedSharedPreferences(context, PREF_FILE_NAME) | ||
|
||
fun add(id: String, credentials: String) { | ||
sharedPreferences.edit().putString(id, credentials).apply() | ||
} | ||
|
||
fun get(id: String) = sharedPreferences.getString(id, null)?.toDocument(id, requiresUserAuth) | ||
|
||
fun getAll() = sharedPreferences.all.mapNotNull { | ||
(it.value as? String)?.toDocument(it.key, requiresUserAuth) | ||
} | ||
|
||
fun delete(id: String) { | ||
sharedPreferences.edit().remove(id).apply() | ||
} | ||
|
||
fun deleteAll() { | ||
sharedPreferences.edit().clear().apply() | ||
} | ||
|
||
private companion object { | ||
private const val PREF_FILE_NAME = "document_manager_sdjwt_prefs" | ||
} | ||
} | ||
|
||
private fun String.toDocument( | ||
id: String, | ||
requiresUserAuth: Boolean, | ||
) = try { | ||
val payloadString = split(".")[1] | ||
val payloadJson = JSONObject(String(Base64.getUrlDecoder().decode(payloadString))) | ||
|
||
val vct = payloadJson.getString("vct") | ||
val docName = "Personalausweis" | ||
val data = payloadJson.toString() | ||
|
||
SdJwtDocument( | ||
id = id, | ||
vct = vct, | ||
docName = docName, | ||
requiresUserAuth = requiresUserAuth, | ||
data = this, | ||
) | ||
} catch (_: JSONException) { | ||
null | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kurze Frage:
Waren unsere ID's jetzt gesynced, so dass das funktionieren kann?
(Oder nicht und wir müssen das noch einmal anpassen?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Die Id´s sind nicht synced, aber wir gehen davon aus, dass wir nur ein SdJwt haben können und löschen das wenn irgendein Mdoc gelöscht wird. Das ist good enough würde ich sagen. Wir sollten in die Lib noch einen Check einbauen, dass man nicht mehr als ein Pid haben kann, damit hier nichts kaputt gehen kann