Skip to content

Commit 68406fb

Browse files
Merge branch 'main' into test/make-podman-work
2 parents 7890209 + b55dc52 commit 68406fb

14 files changed

+294
-99
lines changed

.mise.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ PIP_INDEX_URL = "{{ get_env(name='PIP_INDEX_URL', default='') }}"
1919
java = "temurin-21"
2020
maven = "3.9"
2121
"aqua:casey/just" = "1.43.0"
22-
"aqua:rhysd/actionlint" = "v1.7.8"
22+
"aqua:rhysd/actionlint" = "v1.7.11"
2323
"aqua:siderolabs/conform" = "v0.1.0-alpha.30"
2424
"aqua:zricethezav/gitleaks" = "v8.29.1"
2525
"ubi:rvben/rumdl" = "v0.0.173"

DEVELOPMENT.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,62 @@ mvn test -Dtest.excludes='TraefikTest,WalletAccountTest,WalletAttributeAttestati
237237
The command above will run all test cases except
238238
TraefikTest, WalletAccountTest and WalletAttributeAttestationTest.
239239

240+
#### Checking that the verifier rejects untrusted issuers
241+
242+
In `VerifierBackendTest` we have a way to check that
243+
the local verifier rejects credentials issued by an untrusted party.
244+
Specifically, that the verifier reject credentials issued by
245+
a party not in the configured list of trust sources. There are two tests related
246+
to this:
247+
248+
1. `rejectsUntrustedPidIssuer`
249+
2. `rejectsCredentialFromUntrustedSource`
250+
251+
The first one requires a live, but untrusted, environment
252+
and the second one uses pre-generated credentials known to be untrusted.
253+
Since the first test requires a live environment it is disabled by default.
254+
255+
You can run it like so:
256+
257+
```shell
258+
env \
259+
DIGG_WALLET_ECOSYSTEM_INCLUDE_TESTS_WITH_UNTRUSTED_ISSUER=true \
260+
DIGG_WALLET_ECOSYSTEM_UNTRUSTED_WALLET_PROVIDER_BASE_URI=https://example.com/untrusted-wallet-provider \
261+
DIGG_WALLET_ECOSYSTEM_UNTRUSTED_PID_ISSUER_BASE_URI=https://example.com/untrusted-pid-issuer \
262+
DIGG_WALLET_ECOSYSTEM_UNTRUSTED_KEYCLOAK_BASE_URI=https://example.com/untrusted-idp \
263+
mvn test -Dtest=VerifierBackendTest#rejectsUntrustedPidIssuer
264+
```
265+
266+
The live test can be used to generate data for the second test.
267+
In order to do so, we can manipulate the certificates
268+
so that they are not trusted by the verifier.
269+
270+
```shell
271+
# Remove the root CA so that a new one is generated
272+
rm config/certificates/rootca/*
273+
274+
# Generate new keystores for all services
275+
config/certificates/generate_keystores.sh
276+
277+
# Use the existing trusted issuers instead of the newly generated ones
278+
git checkout config/certificates/verifier/trusted_issuers.p12
279+
280+
# Restart environment
281+
docker compose restart
282+
283+
# Run test
284+
env \
285+
DIGG_WALLET_ECOSYSTEM_INCLUDE_TESTS_WITH_UNTRUSTED_ISSUER=true \
286+
DIGG_WALLET_ECOSYSTEM_UNTRUSTED_WALLET_PROVIDER_BASE_URI=https://localhost/wallet-provider \
287+
DIGG_WALLET_ECOSYSTEM_UNTRUSTED_PID_ISSUER_BASE_URI=https://localhost/pid-issuer \
288+
DIGG_WALLET_ECOSYSTEM_UNTRUSTED_KEYCLOAK_BASE_URI=https://localhost/idp \
289+
mvn test -Dtest=VerifierBackendTest#rejectsUntrustedPidIssuer
290+
```
291+
292+
The test will print the credentials and signing key to standard out
293+
and this data can be copied into the corresponding variable values
294+
of the `rejectsCredentialFromUntrustedSource` test method.
295+
240296
### Quality Checks
241297

242298
This project uses `just` + `mise` for local quality checks.

docker-compose.yaml

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ services:
4848
- wallet-ecosystem-network
4949

5050
wallet-provider:
51-
image: ghcr.io/diggsweden/wallet-provider:v0.0.6
51+
image: ghcr.io/diggsweden/wallet-provider:v0.0.7
5252
container_name: wallet-provider
5353
hostname: wallet-provider
5454
volumes:
@@ -115,19 +115,16 @@ services:
115115
- ISSUER_CREDENTIALRESPONSEENCRYPTION_REQUIRED=true
116116
- ISSUER_CREDENTIALRESPONSEENCRYPTION_ALGORITHMSSUPPORTED=ECDH-ES
117117
- ISSUER_CREDENTIALRESPONSEENCRYPTION_ENCRYPTIONMETHODS=A128GCM
118-
- ISSUER_PID_MSO_MDOC_ENABLED=true
119-
- ISSUER_PID_MSO_MDOC_ENCODER_DURATION=P30D
120-
- ISSUER_PID_MSO_MDOC_NOTIFICATIONS_ENABLED=true
118+
- ISSUER_PID_MSO_MDOC_ENABLED=false
121119
- ISSUER_PID_SD_JWT_VC_ENABLED=true
122120
- ISSUER_PID_SD_JWT_VC_NOTUSEBEFORE=PT20S
123121
- ISSUER_PID_SD_JWT_VC_NOTIFICATIONS_ENABLED=true
124122
- ISSUER_PID_SD_JWT_VC_KEY_ATTESTATIONS_REQUIRED=true
125123
- ISSUER_PID_ISSUINGCOUNTRY=SE
126124
- ISSUER_PID_ISSUINGJURISDICTION=SE-Y # Västernorrland
127-
- ISSUER_EHIC_ISSUINGCOUNTRY=SE
128-
- ISSUER_MDL_ENABLED=true
129-
- ISSUER_MDL_MSO_MDOC_ENCODER_DURATION=P5D
130-
- ISSUER_MDL_NOTIFICATIONS_ENABLED=true
125+
- ISSUER_EHIC_ENABLED=false
126+
- ISSUER_MDL_ENABLED=false
127+
- ISSUER_LEARNINGCREDENTIAL_ENABLED=false
131128
- ISSUER_CREDENTIALOFFER_URI=openid-credential-offer://
132129
- ISSUER_SIGNING_KEY=LoadFromKeystore
133130
- ISSUER_SIGNING_KEY_KEYSTORE=file:///opt/common/pid_issuer.p12
@@ -287,7 +284,7 @@ services:
287284
- wallet-ecosystem-network
288285

289286
wallet-client-gateway:
290-
image: ghcr.io/diggsweden/wallet-client-gateway:v0.4.4
287+
image: ghcr.io/diggsweden/wallet-client-gateway:v0.4.5
291288
container_name: wallet-client-gateway
292289
hostname: wallet-client-gateway
293290
volumes:

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "wallet-ecosystem",
33
"devDependencies": {
4-
"@prettier/plugin-xml": "^3.4.2",
5-
"prettier": "^3.6.2"
4+
"@prettier/plugin-xml": "3.4.2",
5+
"prettier": "3.6.2"
66
}
77
}

src/test/java/se/digg/wallet/ecosystem/EndToEndTest.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public class EndToEndTest {
3737
"wallet-unit-attestation/v2"
3838
})
3939
void getCredential(String wuaPath) throws Exception {
40-
IssuanceHelper issuanceHelper = new IssuanceHelper(new WalletProviderClient(wuaPath));
40+
IssuanceAgent issuer = new IssuanceAgent(new WalletProviderClient(wuaPath));
4141
// 1. Initialize transaction
4242
String nonce = UUID.randomUUID().toString();
4343
String dcqlId = UUID.randomUUID().toString();
@@ -62,12 +62,11 @@ void getCredential(String wuaPath) throws Exception {
6262
.keyUse(KeyUse.SIGNATURE)
6363
.generate();
6464

65-
String sdJwtVc = issuanceHelper.issuePidCredential(bindingKey, "tneal", "password");
65+
String sdJwtVc = issuer.issuePidCredential(bindingKey, "tneal", "password");
6666

6767
// 4. Create vp_token
6868
String vpToken =
69-
issuanceHelper.createVpToken(
70-
sdJwtVc, bindingKey, nonce, VerifierBackendClient.VERIFIER_AUDIENCE);
69+
VerifiablePresentationToken.asString(sdJwtVc, bindingKey, nonce);
7170

7271
// 5. Post wallet response
7372
String vpTokenJson = String.format("{ \"%s\": [ \"%s\" ] }", dcqlId, vpToken);

src/test/java/se/digg/wallet/ecosystem/IssuanceHelper.java renamed to src/test/java/se/digg/wallet/ecosystem/IssuanceAgent.java

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,51 +16,45 @@
1616
import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
1717
import com.nimbusds.jwt.JWTClaimsSet;
1818
import com.nimbusds.jwt.SignedJWT;
19-
import java.nio.charset.StandardCharsets;
20-
import java.security.MessageDigest;
2119
import java.time.Instant;
22-
import java.util.Base64;
2320
import java.util.Date;
2421
import java.util.List;
2522
import java.util.Map;
2623

27-
public class IssuanceHelper {
24+
public class IssuanceAgent {
2825

29-
private final KeycloakClient keycloak = new KeycloakClient();
26+
static IssuanceAgent untrusted() {
27+
return new IssuanceAgent(
28+
new KeycloakClient(ServiceIdentifier.UNTRUSTED_KEYCLOAK.getResourceRoot()),
29+
new WalletProviderClient(ServiceIdentifier.UNTRUSTED_WALLET_PROVIDER.getResourceRoot()),
30+
new PidIssuerClient(ServiceIdentifier.UNTRUSTED_PID_ISSUER.getResourceRoot()),
31+
ServiceIdentifier.UNTRUSTED_PID_ISSUER.toString());
32+
}
33+
34+
private final KeycloakClient keycloak;
3035
private final WalletProviderClient walletProvider;
31-
private final PidIssuerClient pidIssuer = new PidIssuerClient();
36+
private final PidIssuerClient pidIssuer;
37+
private final String audience;
3238

33-
public IssuanceHelper() {
39+
public IssuanceAgent() {
3440
this(new WalletProviderClient());
3541
}
3642

37-
public IssuanceHelper(WalletProviderClient walletProvider) {
38-
this.walletProvider = walletProvider;
43+
public IssuanceAgent(WalletProviderClient walletProvider) {
44+
this(new KeycloakClient(), walletProvider, new PidIssuerClient(),
45+
ServiceIdentifier.PID_ISSUER.toString());
3946
}
4047

41-
public String createVpToken(String sdJwtVc, ECKey bindingKey, String nonce, String audience)
42-
throws Exception {
43-
MessageDigest digest = MessageDigest.getInstance("SHA-256");
44-
byte[] hash = digest.digest(sdJwtVc.getBytes(StandardCharsets.UTF_8));
45-
String sdHash = Base64.getUrlEncoder().withoutPadding().encodeToString(hash);
48+
public IssuanceAgent(
49+
KeycloakClient keycloak,
50+
WalletProviderClient walletProvider,
51+
PidIssuerClient pidIssuer,
52+
String audience) {
4653

47-
JWTClaimsSet kbJwtClaims =
48-
new JWTClaimsSet.Builder()
49-
.audience(audience)
50-
.issueTime(Date.from(Instant.now()))
51-
.claim("nonce", nonce)
52-
.claim("sd_hash", sdHash)
53-
.build();
54-
SignedJWT kbJwt =
55-
new SignedJWT(
56-
new JWSHeader.Builder(JWSAlgorithm.ES256)
57-
.jwk(bindingKey.toPublicJWK())
58-
.type(new JOSEObjectType("kb+jwt"))
59-
.build(),
60-
kbJwtClaims);
61-
kbJwt.sign(new ECDSASigner(bindingKey));
62-
String kbJwtSerialized = kbJwt.serialize();
63-
return sdJwtVc + kbJwtSerialized;
54+
this.keycloak = keycloak;
55+
this.walletProvider = walletProvider;
56+
this.pidIssuer = pidIssuer;
57+
this.audience = audience;
6458
}
6559

6660
public String issuePidCredential(ECKey bindingKey, String username, String password)
@@ -107,7 +101,7 @@ private String createProof(ECKey jwk, String wua, String nonce) throws JOSEExcep
107101
JWTClaimsSet claims =
108102
new JWTClaimsSet.Builder()
109103
.issuer(jwk.toPublicJWK().toString())
110-
.audience(ServiceIdentifier.PID_ISSUER.toString())
104+
.audience(audience)
111105
.issueTime(Date.from(Instant.now()))
112106
.claim("nonce", nonce)
113107
.build();

src/test/java/se/digg/wallet/ecosystem/PidIssuerClient.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ public class PidIssuerClient {
3838
private final URI base;
3939

4040
public PidIssuerClient() {
41-
base = ServiceIdentifier.PID_ISSUER.getResourceRoot();
41+
this(ServiceIdentifier.PID_ISSUER.getResourceRoot());
42+
}
43+
44+
public PidIssuerClient(URI base) {
45+
this.base = base;
4246
}
4347

4448
public Response tryGetOpenIdCredentialIssuerMetadata(MetadataLocationStrategy strategy) {

src/test/java/se/digg/wallet/ecosystem/ServiceIdentifier.java

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,31 @@
88
import java.util.Optional;
99

1010
public enum ServiceIdentifier {
11-
KEYCLOAK("https://localhost/idp",
12-
"DIGG_WALLET_ECOSYSTEM_KEYCLOAK_BASE_URI"),
13-
PID_ISSUER("https://localhost/pid-issuer",
14-
"DIGG_WALLET_ECOSYSTEM_PID_ISSUER_BASE_URI"),
15-
WALLET_PROVIDER("https://localhost/wallet-provider",
16-
"DIGG_WALLET_ECOSYSTEM_WALLET_PROVIDER_BASE_URI"),
17-
VERIFIER_BACKEND("https://localhost/refimpl-verifier-backend",
18-
"DIGG_WALLET_ECOSYSTEM_VERIFIER_BACKEND_BASE_URI"),
19-
VERIFIER_FRONTEND("https://localhost/demo-verifier",
20-
"DIGG_WALLET_ECOSYSTEM_VERIFIER_FRONTEND_BASE_URI"),
21-
WALLET_CLIENT_GATEWAY("https://localhost/wallet-client-gateway",
22-
"DIGG_WALLET_ECOSYSTEM_WALLET_CLIENT_GATEWAY_BASE_URI");
11+
KEYCLOAK("https://localhost/idp"),
12+
UNTRUSTED_KEYCLOAK("https://localhost/untrusted-idp"),
13+
PID_ISSUER("https://localhost/pid-issuer"),
14+
UNTRUSTED_PID_ISSUER("https://localhost/untrusted-pid-issuer"),
15+
WALLET_PROVIDER("https://localhost/wallet-provider"),
16+
UNTRUSTED_WALLET_PROVIDER("https://localhost/untrusted-wallet-provider"),
17+
VERIFIER_BACKEND("https://localhost/refimpl-verifier-backend"),
18+
VERIFIER_FRONTEND("https://localhost/demo-verifier"),
19+
WALLET_CLIENT_GATEWAY("https://localhost/wallet-client-gateway");
2320

2421
private final String defaultUri;
25-
private final String environmentVariable;
2622

27-
ServiceIdentifier(String defaultUri, String environmentVariable) {
23+
ServiceIdentifier(String defaultUri) {
2824
this.defaultUri = defaultUri;
29-
this.environmentVariable = environmentVariable;
3025
}
3126

3227
private String getValue() {
33-
return Optional.ofNullable(System.getenv(environmentVariable))
28+
return Optional.ofNullable(System.getenv(getEnvironmentVariableName()))
3429
.orElse(defaultUri);
3530
}
3631

32+
String getEnvironmentVariableName() {
33+
return String.format("DIGG_WALLET_ECOSYSTEM_%s_BASE_URI", name());
34+
}
35+
3736
@Override
3837
public String toString() {
3938
return getValue();
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-FileCopyrightText: 2025 The Wallet Ecosystem Authors
2+
//
3+
// SPDX-License-Identifier: EUPL-1.2
4+
5+
package se.digg.wallet.ecosystem;
6+
7+
import static org.hamcrest.MatcherAssert.assertThat;
8+
import static org.hamcrest.Matchers.equalTo;
9+
import static se.digg.wallet.ecosystem.ServiceIdentifier.KEYCLOAK;
10+
import static se.digg.wallet.ecosystem.ServiceIdentifier.PID_ISSUER;
11+
import static se.digg.wallet.ecosystem.ServiceIdentifier.UNTRUSTED_KEYCLOAK;
12+
import static se.digg.wallet.ecosystem.ServiceIdentifier.UNTRUSTED_PID_ISSUER;
13+
import static se.digg.wallet.ecosystem.ServiceIdentifier.UNTRUSTED_WALLET_PROVIDER;
14+
import static se.digg.wallet.ecosystem.ServiceIdentifier.VERIFIER_BACKEND;
15+
import static se.digg.wallet.ecosystem.ServiceIdentifier.VERIFIER_FRONTEND;
16+
import static se.digg.wallet.ecosystem.ServiceIdentifier.WALLET_CLIENT_GATEWAY;
17+
import static se.digg.wallet.ecosystem.ServiceIdentifier.WALLET_PROVIDER;
18+
import static se.digg.wallet.ecosystem.ServiceIdentifier.values;
19+
20+
import java.util.Map;
21+
import java.util.function.Function;
22+
import java.util.stream.Collectors;
23+
import java.util.stream.Stream;
24+
import org.junit.jupiter.api.Test;
25+
26+
class ServiceIdentifierTest {
27+
28+
@Test
29+
void hasSpecificEnvironmentVariableNames() {
30+
assertThat(Stream.of(values()).collect(
31+
Collectors.toMap(Function.identity(), ServiceIdentifier::getEnvironmentVariableName)),
32+
equalTo(Map.of(
33+
KEYCLOAK, "DIGG_WALLET_ECOSYSTEM_KEYCLOAK_BASE_URI",
34+
UNTRUSTED_KEYCLOAK, "DIGG_WALLET_ECOSYSTEM_UNTRUSTED_KEYCLOAK_BASE_URI",
35+
PID_ISSUER, "DIGG_WALLET_ECOSYSTEM_PID_ISSUER_BASE_URI",
36+
UNTRUSTED_PID_ISSUER, "DIGG_WALLET_ECOSYSTEM_UNTRUSTED_PID_ISSUER_BASE_URI",
37+
WALLET_PROVIDER, "DIGG_WALLET_ECOSYSTEM_WALLET_PROVIDER_BASE_URI",
38+
UNTRUSTED_WALLET_PROVIDER, "DIGG_WALLET_ECOSYSTEM_UNTRUSTED_WALLET_PROVIDER_BASE_URI",
39+
VERIFIER_BACKEND, "DIGG_WALLET_ECOSYSTEM_VERIFIER_BACKEND_BASE_URI",
40+
VERIFIER_FRONTEND, "DIGG_WALLET_ECOSYSTEM_VERIFIER_FRONTEND_BASE_URI",
41+
WALLET_CLIENT_GATEWAY, "DIGG_WALLET_ECOSYSTEM_WALLET_CLIENT_GATEWAY_BASE_URI")));
42+
}
43+
}

0 commit comments

Comments
 (0)