Skip to content

Commit 7425e43

Browse files
authored
ConsentScreen and SD-JWT fixes. (#1439)
Looks like we're not including the `x5c` claim when creating an SD-JWT and the issuer signing key has a X.509 certificate chain. Fix this. Also bunch of fixes for the ConsentScreen in testapp to make it work with the latest update to the Compose libraries which fixes some bugs the existing code was relying on. Also temporarily disable running unit tests in the Android emulator since it current breaks our GitHub runner due to insufficient disk space. Test: Manually tested. Signed-off-by: David Zeuthen <[email protected]>
1 parent 9d5567e commit 7425e43

File tree

4 files changed

+175
-135
lines changed

4 files changed

+175
-135
lines changed

.github/workflows/unit-test.yml

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,23 @@ jobs:
3030
run: ./gradlew build -x test
3131
- name: Run unit tests
3232
run: ./gradlew test
33-
- name: Enable KVM
34-
run: |
35-
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
36-
sudo udevadm control --reload-rules
37-
sudo udevadm trigger --name-match=kvm
38-
- name: Run unit tests on Android Emulator API 34
39-
uses: reactivecircus/android-emulator-runner@v2
40-
with:
41-
api-level: 34
42-
arch: x86_64
43-
script: |
44-
./gradlew connectedCheck
45-
mkdir -p build/reports/logcat
46-
adb logcat -d > build/reports/logcat/logcat.txt || touch build/reports/logcat/logcat.txt
47-
- name: Upload Logcat Output
48-
if: always()
49-
uses: actions/upload-artifact@v4
50-
with:
51-
name: android-logcat
52-
path: build/reports/logcat/logcat.txt
33+
# - name: Enable KVM
34+
# run: |
35+
# echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
36+
# sudo udevadm control --reload-rules
37+
# sudo udevadm trigger --name-match=kvm
38+
# - name: Run unit tests on Android Emulator API 34
39+
# uses: reactivecircus/android-emulator-runner@v2
40+
# with:
41+
# api-level: 34
42+
# arch: x86_64
43+
# script: |
44+
# ./gradlew connectedCheck
45+
# mkdir -p build/reports/logcat
46+
# adb logcat -d > build/reports/logcat/logcat.txt || touch build/reports/logcat/logcat.txt
47+
# - name: Upload Logcat Output
48+
# if: always()
49+
# uses: actions/upload-artifact@v4
50+
# with:
51+
# name: android-logcat
52+
# path: build/reports/logcat/logcat.txt

multipaz/src/commonMain/kotlin/org/multipaz/sdjwt/SdJwt.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,8 @@ class SdJwt(
331331
*
332332
* This implementation uses recursive disclosures for all claims in the [claims] parameter.
333333
*
334-
* @param issuerKey the key to sign the issuerSigned JWT with.
334+
* @param issuerKey the key to sign the issuerSigned JWT with. If this is a [AsymmetricKey.X509Certified]
335+
* the certificate chain will be included in the `x5c` claim and always be disclosed.
335336
* @param kbKey if set, a `cnf` claim with this public key will be included in the Issuer-signed JWT.
336337
* @param claims the object with claims that can be selectively disclosed.
337338
* @param nonSdClaims claims to include in the Issuer-signed JWT which are always disclosed. This must at least
@@ -363,7 +364,15 @@ class SdJwt(
363364
creationTime = creationTime,
364365
expiresIn = expiresIn
365366
) {
367+
if (issuerKey is AsymmetricKey.X509Certified) {
368+
put("x5c", issuerKey.certChain.toX5c())
369+
}
366370
for (claim in nonSdClaims) {
371+
if (claim.key == "x5c" && issuerKey is AsymmetricKey.X509Certified) {
372+
throw IllegalArgumentException(
373+
"Claim x5c is already included because `issuerKey` is X509Certified"
374+
)
375+
}
367376
put(claim.key, claim.value)
368377
}
369378

multipaz/src/commonTest/kotlin/org/multipaz/presentment/model/digitalCredentialsPresentmentTest.kt

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.multipaz.presentment.model
22

33
import kotlinx.coroutines.flow.MutableStateFlow
4+
import kotlinx.coroutines.runBlocking
45
import kotlinx.coroutines.test.runTest
56
import kotlinx.serialization.ExperimentalSerializationApi
67
import kotlinx.serialization.json.Json
@@ -15,11 +16,11 @@ import org.multipaz.cbor.Simple
1516
import org.multipaz.cbor.addCborArray
1617
import org.multipaz.cbor.buildCborArray
1718
import org.multipaz.crypto.Algorithm
19+
import org.multipaz.crypto.AsymmetricKey
1820
import org.multipaz.crypto.Crypto
1921
import org.multipaz.crypto.EcCurve
2022
import org.multipaz.crypto.EcPrivateKey
2123
import org.multipaz.crypto.JsonWebEncryption
22-
import org.multipaz.crypto.AsymmetricKey
2324
import org.multipaz.crypto.X500Name
2425
import org.multipaz.crypto.X509CertChain
2526
import org.multipaz.document.Document
@@ -32,14 +33,14 @@ import org.multipaz.sdjwt.SdJwtKb
3233
import org.multipaz.storage.ephemeral.EphemeralStorage
3334
import org.multipaz.trustmanagement.TrustManagerLocal
3435
import org.multipaz.trustmanagement.TrustPoint
35-
import org.multipaz.util.Constants
3636
import org.multipaz.util.Logger
3737
import org.multipaz.util.fromBase64Url
3838
import org.multipaz.util.toBase64Url
39+
import kotlin.io.encoding.Base64
3940
import kotlin.random.Random
41+
import kotlin.test.BeforeTest
4042
import kotlin.test.Test
4143
import kotlin.test.assertEquals
42-
import kotlin.test.assertTrue
4344

4445
class DigitalCredentialsPresentmentTest {
4546
companion object {
@@ -52,6 +53,12 @@ class DigitalCredentialsPresentmentTest {
5253

5354
val documentStoreTestHarness = DocumentStoreTestHarness()
5455

56+
@BeforeTest
57+
fun setup() = runBlocking {
58+
documentStoreTestHarness.initialize()
59+
documentStoreTestHarness.provisionStandardDocuments()
60+
}
61+
5562
class TestPresentmentMechanism(
5663
protocol: String,
5764
data: JsonObject,
@@ -95,9 +102,6 @@ class DigitalCredentialsPresentmentTest {
95102
encryptionKey: EcPrivateKey?,
96103
dcql: JsonObject
97104
): TestOpenID4VPResponse {
98-
documentStoreTestHarness.initialize()
99-
documentStoreTestHarness.provisionStandardDocuments()
100-
101105
val readerTrustManager = TrustManagerLocal(EphemeralStorage())
102106
val presentmentSource = SimplePresentmentSource(
103107
documentStore = documentStoreTestHarness.documentStore,
@@ -416,6 +420,9 @@ class DigitalCredentialsPresentmentTest {
416420
expectedSdJwtResponse =
417421
"""
418422
{
423+
"x5c": [
424+
"${Base64.encode(documentStoreTestHarness.dsKey.certChain.certificates[0].encoded.toByteArray())}"
425+
],
419426
"iss": "https://example-issuer.com",
420427
"vct": "urn:eudi:pid:1",
421428
"family_name": "Mustermann",

0 commit comments

Comments
 (0)