From 4ed1f9942074867841a5acf28cec9f65b5aee9bd Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Mon, 22 Apr 2024 16:27:48 +0200 Subject: [PATCH] feat: update Iron VC to 0.14.0 (#4125) * feat: update Iron VC to 0.14.0 * sourcedoc, checkstyle * DEPENDENCIES * removed unneeded var * fix test * DEPENDENCIES [skip ci] * trigger ci --- DEPENDENCIES | 9 +- .../build.gradle.kts | 5 +- .../linkeddata/DataIntegrityKeyPair.java | 97 +++++++ .../linkeddata/DidMethodResolver.java | 14 +- .../linkeddata/LdpIssuer.java | 73 ++--- .../linkeddata/LdpVerifier.java | 262 +++++++----------- .../linkeddata/LdpIssuerTest.java | 87 +++--- .../linkeddata/LdpVerifierTest.java | 123 ++++---- .../linkeddata/LdpCreationUtils.java | 27 +- .../linkeddata/TestData.java | 7 +- .../common/crypto/lib/jws2020-lib/README.md | 2 +- .../crypto/lib/jws2020-lib/build.gradle.kts | 3 +- .../signature/jws2020/ByteArrayAdapter.java | 40 --- .../jws2020/IssuerCompatibility.java | 79 ------ .../signature/jws2020/JsonAdapter.java | 22 +- .../{JwkMethod.java => JsonWebKeyPair.java} | 10 +- .../signature/jws2020/Jwk2020KeyAdapter.java | 112 ++++++++ .../signature/jws2020/JwkAdapter.java | 79 ------ .../signature/jws2020/Jws2020CryptoSuite.java | 5 +- .../signature/jws2020/Jws2020Proof.java | 174 ++++++++++++ .../signature/jws2020/Jws2020ProofDraft.java | 124 +++++++++ .../signature/jws2020/Jws2020Schema.java | 110 -------- ...ureProvider.java => Jws2020Signature.java} | 27 +- .../jws2020/Jws2020SignatureSuite.java | 83 ++++++ .../security/signature/jws2020/JwsIssuer.java | 99 +++++++ .../jws2020/JwsSignature2020Suite.java | 68 ----- .../jws2020/JwsSignatureProofOptions.java | 31 --- .../src/main/resources/jws2020.jsonld | 78 ++++++ .../signature/jws2020/IssuerTests.java | 155 +++++++---- .../signature/jws2020/JsonAdapterTest.java | 62 ----- .../signature/jws2020/TestFunctions.java | 52 ++++ .../signature/jws2020/VerifierTests.java | 35 ++- .../com/apicatalog/vc/context.jsonld | 34 +++ .../com/apicatalog/vc/issuer-manifest.jsonld | 117 ++++++++ .../apicatalog/vc/issuer/0001-context.jsonld | 6 + .../com/apicatalog/vc/issuer/0001-in.jsonld | 12 + .../com/apicatalog/vc/issuer/0001-keys.json | 8 + .../com/apicatalog/vc/issuer/0001-out.jsonld | 55 ++++ .../com/apicatalog/vc/issuer/0002-in.jsonld | 12 + .../com/apicatalog/vc/issuer/0002-out.jsonld | 18 ++ .../com/apicatalog/vc/issuer/0003-in.jsonld | 12 + .../com/apicatalog/vc/issuer/0003-out.jsonld | 23 ++ .../com/apicatalog/vc/issuer/0004-in.jsonld | 12 + .../com/apicatalog/vc/issuer/0004-out.jsonld | 18 ++ .../com/apicatalog/vc/issuer/0005-in.jsonld | 18 ++ .../com/apicatalog/vc/issuer/0005-out.jsonld | 31 +++ .../com/apicatalog/vc/issuer/0006-in.jsonld | 28 ++ .../com/apicatalog/vc/issuer/0006-out.jsonld | 29 ++ .../com/apicatalog/vc/manifest.jsonld | 15 + .../com/apicatalog/vc/security-context.jsonld | 31 +++ .../apicatalog/vc/verifier-manifest.jsonld | 88 ++++++ .../com/apicatalog/vc/verifier/0001-in.jsonld | 18 ++ .../com/apicatalog/vc/verifier/0002-in.jsonld | 18 ++ .../com/apicatalog/vc/verifier/0003-in.jsonld | 23 ++ .../com/apicatalog/vc/verifier/0004-in.jsonld | 31 +++ .../com/apicatalog/vc/verifier/0005-in.jsonld | 31 +++ .../vc/verifier/0005-verification-key.json | 7 + .../com/apicatalog/vc/verifier/0006-in.jsonld | 18 ++ .../com/apicatalog/vc/verifier/0007-in.jsonld | 29 ++ .../com/apicatalog/vc/verifier/0008-in.jsonld | 29 ++ .../jws2020/issuing/linkedCredentialData.json | 2 +- .../resources/jws2020/verifying/0001_vc.json | 2 +- .../jws2020/verifying/0002_vc_forged.json | 2 +- .../jws2020/verifying/0003_vc_embedded.json | 2 +- .../verifying/0004_vc_two_valid_proofs.json | 2 +- .../verifying/0005_vc_one_forged_proof.json | 2 +- .../jws2020/verifying/0006_vc_did_key.json | 2 +- .../jws2020/verifying/0007_vp_compacted.json | 10 +- .../verifying/0007_vp_compacted_forged.json | 4 +- .../signature/jws2020/TestFunctions.java | 6 +- .../core/IdentityAndTrustExtension.java | 6 +- .../InMemorySignatureSuiteRegistry.java | 3 +- .../MultiFormatPresentationVerifierTest.java | 19 +- gradle/libs.versions.toml | 2 +- .../verification/SignatureSuiteRegistry.java | 3 +- 75 files changed, 2048 insertions(+), 944 deletions(-) create mode 100644 extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/DataIntegrityKeyPair.java delete mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/ByteArrayAdapter.java delete mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/IssuerCompatibility.java rename extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/{JwkMethod.java => JsonWebKeyPair.java} (70%) create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jwk2020KeyAdapter.java delete mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwkAdapter.java create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020Proof.java create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020ProofDraft.java delete mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020Schema.java rename extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/{Jws2020SignatureProvider.java => Jws2020Signature.java} (74%) create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020SignatureSuite.java create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsIssuer.java delete mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsSignature2020Suite.java delete mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsSignatureProofOptions.java create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/main/resources/jws2020.jsonld delete mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/JsonAdapterTest.java create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/TestFunctions.java create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/context.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer-manifest.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-context.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-keys.json create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-out.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0002-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0002-out.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0003-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0003-out.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0004-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0004-out.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0005-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0005-out.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0006-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0006-out.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/manifest.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/security-context.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier-manifest.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0001-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0002-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0003-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0004-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0005-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0005-verification-key.json create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0006-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0007-in.jsonld create mode 100644 extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0008-in.jsonld diff --git a/DEPENDENCIES b/DEPENDENCIES index db2c108abbd..9821e83b397 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,8 +1,9 @@ -maven/mavencentral/com.apicatalog/carbon-did/0.0.2, Apache-2.0, approved, #9239 -maven/mavencentral/com.apicatalog/iron-ed25519-cryptosuite-2020/0.8.1, Apache-2.0, approved, #11157 -maven/mavencentral/com.apicatalog/iron-verifiable-credentials/0.8.1, Apache-2.0, approved, #9234 +maven/mavencentral/com.apicatalog/carbon-did/0.3.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.apicatalog/copper-multibase/0.5.0, , restricted, clearlydefined +maven/mavencentral/com.apicatalog/copper-multicodec/0.1.1, , restricted, clearlydefined +maven/mavencentral/com.apicatalog/iron-ed25519-cryptosuite-2020/0.14.0, , restricted, clearlydefined +maven/mavencentral/com.apicatalog/iron-verifiable-credentials/0.14.0, Apache-2.0, approved, clearlydefined maven/mavencentral/com.apicatalog/titanium-json-ld/1.0.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/com.apicatalog/titanium-json-ld/1.3.1, Apache-2.0, approved, #8912 maven/mavencentral/com.apicatalog/titanium-json-ld/1.4.0, Apache-2.0, approved, #13683 maven/mavencentral/com.atomikos/atomikos-util/6.0.0, Apache-2.0, approved, #9326 maven/mavencentral/com.atomikos/transactions-api/6.0.0, Apache-2.0, approved, #10351 diff --git a/extensions/common/crypto/ldp-verifiable-credentials/build.gradle.kts b/extensions/common/crypto/ldp-verifiable-credentials/build.gradle.kts index 855ae4f00aa..ef54a30ccbe 100644 --- a/extensions/common/crypto/ldp-verifiable-credentials/build.gradle.kts +++ b/extensions/common/crypto/ldp-verifiable-credentials/build.gradle.kts @@ -17,9 +17,11 @@ plugins { } dependencies { + api(project(":extensions:common:crypto:lib:jws2020-lib")) implementation(project(":spi:common:json-ld-spi")) implementation(project(":spi:common:identity-trust-spi")) - implementation(project(":core:common:lib:util-lib")) + implementation(project(":core:common:lib:util-lib")) //exposes SignatureSuite in its public API + api(project(":spi:common:identity-did-spi")) // used for the Ed25519 Verifier in conjunction with OctetKeyPairs (OKP) runtimeOnly(libs.tink) @@ -35,6 +37,7 @@ dependencies { // deps for test fixtures testFixturesImplementation(project(":spi:common:json-ld-spi")) testFixturesImplementation(project(":core:common:lib:json-ld-lib")) + testFixturesImplementation(project(":extensions:common:crypto:lib:jws2020-lib")) testFixturesApi(libs.nimbus.jwt) testFixturesApi(testFixtures(project(":extensions:common:crypto:lib:jws2020-lib"))) diff --git a/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/DataIntegrityKeyPair.java b/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/DataIntegrityKeyPair.java new file mode 100644 index 00000000000..8047f058402 --- /dev/null +++ b/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/DataIntegrityKeyPair.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.verifiablecredentials.linkeddata; + + +import com.apicatalog.ld.signature.VerificationMethod; + +import java.net.URI; +import java.util.Arrays; +import java.util.Objects; + +/** + * Generic adapter object for a {@link VerificationMethod} + */ +class DataIntegrityKeyPair implements VerificationMethod { + private final URI id; + private final URI type; + private final URI controller; + private final byte[] privateKey; + private final byte[] publicKey; + + DataIntegrityKeyPair(URI id, URI type, URI controller, byte[] privateKey, byte[] publicKey) { + super(); + this.id = id; + this.type = type; + this.controller = controller; + this.privateKey = privateKey; + this.publicKey = publicKey; + } + + DataIntegrityKeyPair(URI id, URI type, URI controller, byte[] privateKey) { + this(id, type, controller, privateKey, null); + } + + @Override + public URI id() { + return id; + } + + @Override + public URI type() { + return type; + } + + @Override + public URI controller() { + return controller; + } + + public byte[] privateKey() { + return privateKey; + } + + public byte[] publicKey() { + return publicKey; + } + + @Override + public int hashCode() { + return Objects.hash(id, type, controller, Arrays.hashCode(privateKey), Arrays.hashCode(publicKey)); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (DataIntegrityKeyPair) obj; + return Objects.equals(this.id, that.id) && + Objects.equals(this.type, that.type) && + Objects.equals(this.controller, that.controller) && + Arrays.equals(this.privateKey, that.privateKey) && + Arrays.equals(this.publicKey, that.publicKey); + } + + @Override + public String toString() { + return "DataIntegrityKeyPair[" + + "id=" + id + ", " + + "type=" + type + ", " + + "controller=" + controller + ", " + + "privateKey=" + Arrays.toString(privateKey) + ", " + + "publicKey=" + Arrays.toString(publicKey) + ']'; + } + +} diff --git a/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/DidMethodResolver.java b/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/DidMethodResolver.java index ee5e1b6f317..c3a323f1ca3 100644 --- a/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/DidMethodResolver.java +++ b/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/DidMethodResolver.java @@ -16,11 +16,9 @@ import com.apicatalog.jsonld.loader.DocumentLoader; import com.apicatalog.ld.DocumentError; -import com.apicatalog.ld.signature.SignatureSuite; -import com.apicatalog.ld.signature.method.MethodResolver; -import com.apicatalog.ld.signature.method.VerificationMethod; -import com.apicatalog.vc.VcTag; -import com.apicatalog.vc.integrity.DataIntegrityKeyPair; +import com.apicatalog.ld.signature.VerificationMethod; +import com.apicatalog.vc.method.resolver.MethodResolver; +import com.apicatalog.vc.proof.Proof; import org.eclipse.edc.iam.did.spi.resolution.DidResolverRegistry; import org.eclipse.edc.spi.EdcException; @@ -38,19 +36,19 @@ public DidMethodResolver(DidResolverRegistry resolverRegistry) { } @Override - public VerificationMethod resolve(URI id, DocumentLoader loader, SignatureSuite suite) throws DocumentError { + public VerificationMethod resolve(URI id, DocumentLoader documentLoader, Proof proof) throws DocumentError { var didDocument = resolverRegistry.resolve(id.toString()) .orElseThrow(failure -> new EdcException(failure.getFailureDetail())); return didDocument.getVerificationMethod().stream() - .map(verificationMethod -> DataIntegrityKeyPair.createVerificationKey( + .map(verificationMethod -> new DataIntegrityKeyPair( URI.create(verificationMethod.getId()), URI.create(verificationMethod.getType()), URI.create(verificationMethod.getController()), verificationMethod.serializePublicKey()) ) .findFirst() - .orElseThrow(() -> new DocumentError(DocumentError.ErrorType.Unknown, suite.getSchema().tagged(VcTag.VerificationMethod.name()).term())); + .orElseThrow(() -> new DocumentError(DocumentError.ErrorType.Unknown, proof.method().type().toString())); } /** diff --git a/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpIssuer.java b/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpIssuer.java index f4a2f7532fb..646a992fd3e 100644 --- a/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpIssuer.java +++ b/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpIssuer.java @@ -14,20 +14,15 @@ package org.eclipse.edc.verifiablecredentials.linkeddata; -import com.apicatalog.jsonld.JsonLdReader; import com.apicatalog.jsonld.loader.DocumentLoader; import com.apicatalog.jsonld.loader.SchemeRouter; import com.apicatalog.ld.DocumentError; -import com.apicatalog.ld.signature.LinkedDataSignature; import com.apicatalog.ld.signature.SigningError; import com.apicatalog.ld.signature.key.KeyPair; -import com.apicatalog.ld.signature.proof.EmbeddedProof; -import com.apicatalog.ld.signature.proof.ProofOptions; -import com.apicatalog.vc.VcTag; +import com.apicatalog.vc.issuer.ProofDraft; import com.apicatalog.vc.loader.StaticContextLoader; -import jakarta.json.Json; +import com.apicatalog.vc.suite.SignatureSuite; import jakarta.json.JsonObject; -import jakarta.json.JsonValue; import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.result.Result; @@ -54,13 +49,14 @@ private LdpIssuer() { * Sign a document using a given key pair and proof options. Effectively, this method adds a {@code proof}-object to the JSON document. * The document is expanded internally, using the {@link JsonLd} interface. * - * @param document The document to sign. - * @param keyPair The key pair used for signing. - * @param proofOptions The proof options. + * @param document The document to sign. + * @param keyPair The key pair used for signing. + * @param proofDraft The proof options. * @return A result containing the signed document as a JsonObject, a failure otherwise. * @throws NullPointerException If any of the parameters is null. */ - public Result signDocument(JsonObject document, KeyPair keyPair, ProofOptions proofOptions) { + public Result signDocument(SignatureSuite signatureSuite, JsonObject document, KeyPair keyPair, ProofDraft proofDraft) { + Objects.requireNonNull(signatureSuite, "SignatureSuite must not be null"); Objects.requireNonNull(document, "Document must not be null"); Objects.requireNonNull(document, "KeyPair must not be null"); Objects.requireNonNull(document, "ProofOptions must not be null"); @@ -74,57 +70,24 @@ public Result signDocument(JsonObject document, KeyPair keyPair, Pro } return jsonLdService.expand(document) - .compose(expanded -> signExpanded(expanded, keyPair, proofOptions)); + .compose(expanded -> signExpanded(signatureSuite, expanded, keyPair, proofDraft)); } - private Result signExpanded(JsonObject expanded, KeyPair keyPair, ProofOptions options) { + private Result signExpanded(SignatureSuite signatureSuite, JsonObject expanded, KeyPair keyPair, ProofDraft proofDraft) { - var optionalObject = JsonLdReader.findFirstObject(expanded); - if (optionalObject.isEmpty()) { - return Result.failure("Error reading document: %s".formatted(DocumentError.ErrorType.Invalid)); - } - var object = optionalObject.get(); - - if (options.getSuite() == null) { - return Result.failure("Unsupported Crypto Suite: %s".formatted(SigningError.Code.UnsupportedCryptoSuite)); - } - - var data = EmbeddedProof.removeProof(object); - - var ldSignature = new LinkedDataSignature(options.getSuite().getCryptoSuite()); - - JsonObject proof; - try { - proof = options.getSuite().getSchema().write(options.toUnsignedProof()); - } catch (DocumentError e) { - monitor.warning("Error writing proof", e); - return Result.failure("Error writing proof: %s".formatted(e.getMessage())); - } - - var proofValueProperty = options.getSuite().getSchema().tagged(VcTag.ProofValue.name()); - - byte[] signature; try { - signature = ldSignature.sign(data, keyPair, proof); - } catch (SigningError e) { - monitor.warning("Error signing data", e); - return Result.failure("Error signing data: %s".formatted(e.getMessage())); + var signed = signatureSuite.createIssuer(keyPair) + .loader(loader) + .base(base) + .useBundledContexts(bundledContexts) + .sign(expanded, proofDraft); + return Result.success(signed.expanded()); + } catch (SigningError | DocumentError e) { + monitor.warning("Error signing document", e); + return Result.failure("Error signing document: " + e.getMessage()); } - JsonValue proofValue; - try { - proofValue = proofValueProperty.write(signature); - } catch (DocumentError e) { - monitor.warning("Error writing signature value to document", e); - return Result.failure("Error writing signature to document: %s".formatted(e.getCode())); - } - - proof = Json.createObjectBuilder(proof) - .add(proofValueProperty.term().uri(), Json.createArrayBuilder().add(proofValue)) - .build(); - - return Result.success(EmbeddedProof.addProof(object, proof)); } diff --git a/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpVerifier.java b/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpVerifier.java index 09e9d81cb4e..a87b7f1b86f 100644 --- a/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpVerifier.java +++ b/extensions/common/crypto/ldp-verifiable-credentials/src/main/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpVerifier.java @@ -14,36 +14,33 @@ package org.eclipse.edc.verifiablecredentials.linkeddata; -import com.apicatalog.jsonld.InvalidJsonLdValue; -import com.apicatalog.jsonld.JsonLdReader; import com.apicatalog.jsonld.json.JsonUtils; +import com.apicatalog.jsonld.lang.Keywords; import com.apicatalog.jsonld.loader.DocumentLoader; import com.apicatalog.jsonld.loader.SchemeRouter; import com.apicatalog.ld.DocumentError; import com.apicatalog.ld.DocumentError.ErrorType; -import com.apicatalog.ld.schema.LdProperty; -import com.apicatalog.ld.schema.LdTerm; -import com.apicatalog.ld.signature.LinkedDataSignature; -import com.apicatalog.ld.signature.SignatureSuite; +import com.apicatalog.ld.Term; +import com.apicatalog.ld.node.LdNode; +import com.apicatalog.ld.node.LdType; import com.apicatalog.ld.signature.VerificationError; -import com.apicatalog.ld.signature.VerificationError.Code; +import com.apicatalog.ld.signature.VerificationMethod; import com.apicatalog.ld.signature.key.VerificationKey; -import com.apicatalog.ld.signature.method.HttpMethodResolver; -import com.apicatalog.ld.signature.method.MethodResolver; -import com.apicatalog.ld.signature.method.VerificationMethod; -import com.apicatalog.ld.signature.proof.EmbeddedProof; -import com.apicatalog.vc.VcTag; +import com.apicatalog.vc.Presentation; import com.apicatalog.vc.VcVocab; +import com.apicatalog.vc.method.resolver.HttpMethodResolver; +import com.apicatalog.vc.method.resolver.MethodResolver; +import com.apicatalog.vc.proof.EmbeddedProof; +import com.apicatalog.vc.proof.Proof; +import com.apicatalog.vc.suite.SignatureSuite; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.json.Json; import jakarta.json.JsonObject; -import jakarta.json.JsonValue; +import jakarta.json.JsonStructure; import org.eclipse.edc.iam.identitytrust.spi.verification.CredentialVerifier; import org.eclipse.edc.iam.identitytrust.spi.verification.SignatureSuiteRegistry; import org.eclipse.edc.iam.identitytrust.spi.verification.VerifierContext; import org.eclipse.edc.jsonld.spi.JsonLd; -import org.eclipse.edc.jsonld.spi.JsonLdKeywords; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.util.uri.UriUtils; @@ -97,6 +94,9 @@ public Result verify(String rawInput, VerifierContext verifierContext) { } catch (JsonProcessingException e) { return failure("Failed to parse JSON: %s".formatted(e.toString())); } + var context = jo.containsKey(Keywords.CONTEXT) + ? JsonUtils.toJsonArray(jo.get(Keywords.CONTEXT)) + : null; var expansion = jsonLd.expand(jo); if (loader == null) { @@ -105,7 +105,7 @@ public Result verify(String rawInput, VerifierContext verifierContext) { } return expansion.compose(expandedDocument -> { try { - return verifyExpanded(expandedDocument, verifierContext); + return verifyExpanded(expandedDocument, verifierContext, context); } catch (DocumentError e) { return failure("Could not verify VP-LDP: message: %s, code: %s".formatted(e.getMessage(), e.getCode())); } catch (VerificationError e) { @@ -118,6 +118,25 @@ public URI getBase() { return base; } + private VerificationMethod resolveMethod(URI id, Proof proof, DocumentLoader loader) throws DocumentError { + + if (id == null) { + throw new DocumentError(ErrorType.Missing, "ProofVerificationId"); + } + + // find the method id resolver + final Optional resolver = methodResolvers.stream() + .filter(r -> r.isAccepted(id)) + .findFirst(); + + // try to resolve the method + if (resolver.isPresent()) { + return resolver.get().resolve(id, loader, proof); + } + + throw new DocumentError(ErrorType.Unknown, "ProofVerificationId"); + } + /** * Validates the credential issuer by comparing it with the provided verification method. * @@ -127,51 +146,37 @@ public URI getBase() { */ private Result validateCredentialIssuer(JsonObject expanded, VerificationMethod verificationMethod) { try { - var issuerUri = JsonLdReader.getId(expanded, VcVocab.ISSUER.uri()); - if (issuerUri.isEmpty()) { + var issuerUri = LdNode.of(expanded).node(VcVocab.ISSUER); + if (!issuerUri.exists()) { return failure("Document must contain an 'issuer' property."); } - if (!UriUtils.equalsIgnoreFragment(issuerUri.get(), verificationMethod.id())) { - return failure("Issuer and proof.verificationMethod mismatch: %s <> %s".formatted(issuerUri.get(), verificationMethod.id())); + if (!UriUtils.equalsIgnoreFragment(issuerUri.id(), verificationMethod.id())) { + return failure("Issuer and proof.verificationMethod mismatch: %s <> %s".formatted(issuerUri, verificationMethod.id())); } - } catch (InvalidJsonLdValue e) { + } catch (DocumentError e) { return failure("Error getting issuer: %s".formatted(e.getMessage())); } return success(); } - /** - * Extracts the first graph from a JSON-LD document, if it exists. When multiple VCs are present in a VP, they are - * expanded to a {@code @graph} object. - * - * @param document The JSON-LD document to extract the graph from. - * @return The first graph from the JSON-LD document, or the original document if no graph exists. - */ - private JsonValue extractGraph(JsonValue document) { - if (document.getValueType() == JsonValue.ValueType.OBJECT) { - if (document.asJsonObject().get(JsonLdKeywords.GRAPH) != null) { - return document.asJsonObject().getJsonArray(JsonLdKeywords.GRAPH).get(0); - } - } - return document; - } - - private Result verifyExpanded(JsonObject expanded, VerifierContext context) throws VerificationError, DocumentError { - + private Result verifyExpanded(JsonObject expanded, VerifierContext context, JsonStructure ldContext) throws VerificationError, DocumentError { if (isCredential(expanded)) { // data integrity validation - return verifyProofs(expanded); + return verifyProofs(expanded, ldContext); } else if (isPresentation(expanded)) { // verify presentation proofs - verifyProofs(expanded); + var presentationValidation = verifyProofs(expanded, ldContext); + if (!presentationValidation.succeeded()) { + return presentationValidation.mapTo(); + } // verify embedded credentials // verifiableCredentials var credentials = new ArrayList(); - for (var credential : JsonLdReader.getObjects(expanded, VcVocab.VERIFIABLE_CREDENTIALS.uri())) { + for (var credential : Presentation.getCredentials(expanded)) { if (JsonUtils.isNotObject(credential)) { return failure("Presentation contained an invalid 'verifiableCredential' object!"); @@ -180,175 +185,116 @@ private Result verifyExpanded(JsonObject expanded, VerifierContext context } return credentials.stream() - .map(this::extractGraph) .map(expCred -> context.verify(expCred.toString())) .reduce(Result::merge) .orElse(success()); // "no credentials" is still valid according to https://www.w3.org/TR/vc-data-model/#presentations-0 } else { - return failure("%s: %s".formatted(ErrorType.Unknown, LdTerm.TYPE)); + return failure("%s: %s".formatted(ErrorType.Unknown, Term.TYPE)); } } - private Result verifyProofs(JsonObject expanded) throws VerificationError, DocumentError { + private Result verifyProofs(JsonObject expanded, JsonStructure context) throws VerificationError, DocumentError { // get proofs - throws an exception if there is no proof, never null nor an // empty collection - var proofs = EmbeddedProof.assertProof(expanded); + var expandedProofs = EmbeddedProof.assertProof(expanded); + + + var allProofs = new ArrayList(expandedProofs.size()); // a data before issuance - no proof attached - var data = EmbeddedProof.removeProof(expanded); + var data = EmbeddedProof.removeProofs(expanded); // verify attached proofs' signatures - for (var embeddedProof : proofs) { + for (var expandedProof : expandedProofs) { - if (JsonUtils.isNotObject(embeddedProof)) { + if (JsonUtils.isNotObject(expandedProof)) { return failure("%s: %s".formatted(ErrorType.Invalid, VcVocab.PROOF)); } - var proofObject = embeddedProof.asJsonObject(); + var proofTypes = LdType.strings(expandedProof); - var proofType = JsonLdReader.getType(proofObject); - - if (proofType == null || proofType.isEmpty()) { - return failure("%s: %s, %s".formatted(ErrorType.Missing, VcVocab.PROOF, LdTerm.TYPE)); + if (proofTypes == null || proofTypes.isEmpty()) { + return failure("%s: %s, %s".formatted(ErrorType.Missing, VcVocab.PROOF, Term.TYPE)); } - var signatureSuite = proofType.stream() - .map(suiteRegistry::getForId) - .filter(Objects::nonNull) - .findFirst() - .orElseThrow(() -> new VerificationError(Code.UnsupportedCryptoSuite)); + var signatureSuite = findSuite(proofTypes, expandedProof); - if (signatureSuite.getSchema() == null) { - return failure("The suite [" + signatureSuite.getId() + "] does not provide proof schema."); + if (signatureSuite == null) { + return failure("No SignatureSuite found for proof type(s) '%s'.".formatted(String.join(","))); } - LdProperty proofValueProperty = signatureSuite.getSchema().tagged(VcTag.ProofValue.name()); - - if (proofValueProperty == null) { - return failure("The proof schema does not define the proof value."); + var proof = signatureSuite.getProof(expandedProof, loader); + if (proof == null) { + return failure("The suite [" + signatureSuite + "] returns null as a proof."); } - var proof = signatureSuite.getSchema().read(proofObject); - - signatureSuite.getSchema().validate(proof, params); + allProofs.add(proof); + } - if (!proof.contains(proofValueProperty.term())) { - return failure("%s: %s".formatted(ErrorType.Missing, proofValueProperty.term())); - } + for (var proof : allProofs) { + try { + proof.validate(Map.of()); - byte[] proofValue = proof.value(proofValueProperty.term()); + var proofValue = proof.signature(); - if (proofValue == null || proofValue.length == 0) { - return failure("%s: %s".formatted(ErrorType.Missing, proofValueProperty.term())); - } - - LdProperty methodProperty = signatureSuite.getSchema().tagged(VcTag.VerificationMethod.name()); + if (proofValue == null) { + return failure("Proof did not contain a valid signature."); + } - if (methodProperty == null) { - return failure("The proof schema does not define a verification method."); - } + var verificationMethod = getMethod(proof, loader); + if (verificationMethod == null) { + return failure("Proof did not contain a VerificationMethod."); + } - var verificationMethod = getMethod(methodProperty, proofObject, signatureSuite) - .orElseThrow(() -> new DocumentError(ErrorType.Missing, methodProperty.term())); + if (!(verificationMethod instanceof VerificationKey)) { + return failure("Proof did not contain a valid VerificationMethod, expected VerificationKey, got: %s".formatted(verificationMethod.getClass())); + } - if (!(verificationMethod instanceof VerificationKey)) { - return failure("%s: %s".formatted(ErrorType.Unknown, methodProperty.term())); - } + if (isCredential(expanded)) { + var failure = validateCredentialIssuer(expanded, verificationMethod); + if (failure.failed()) return failure; + } - if (isCredential(expanded)) { - var failure = validateCredentialIssuer(expanded, verificationMethod); - if (failure.failed()) return failure; + proof.verify(context, data, (VerificationKey) verificationMethod); + } catch (DocumentError error) { + return failure("Could not verify VP-LDP: message: %s, code: %s".formatted(error.getMessage(), error.getCode())); + } catch (VerificationError error) { + return failure("Verification failed: %s".formatted(error.getMessage())); } - - // remote a proof value - var unsignedProof = Json.createObjectBuilder(proofObject) - .remove(proofValueProperty.term().uri()) - .build(); - - var signature = new LinkedDataSignature(signatureSuite.getCryptoSuite()); - - // verify signature - signature.verify(data, unsignedProof, (VerificationKey) verificationMethod, proofValue); } - // all good return success(); } - private Optional getMethod(LdProperty property, JsonObject proofObject, SignatureSuite suite) throws DocumentError { - - var expanded = proofObject.getJsonArray(property.term().uri()); + private VerificationMethod getMethod(Proof proof, DocumentLoader loader) throws DocumentError { + final VerificationMethod method = proof.method(); - if (JsonUtils.isNull(expanded) || expanded.isEmpty()) { - return Optional.empty(); + if (method == null) { + throw new DocumentError(ErrorType.Missing, "ProofVerificationMethod"); } - for (var methodValue : expanded) { - - if (JsonUtils.isNotObject(methodValue)) { - throw new IllegalStateException(); // should never happen - } - - var methodObject = methodValue.asJsonObject(); - - var types = JsonLdReader.getType(methodObject); - - if (types == null || types.isEmpty()) { - return resolve(methodObject, suite, property); - } - - var method = property.read(methodObject); + final URI methodType = method.type(); - if (method instanceof VerificationKey && (((VerificationKey) method).publicKey() != null)) { - return Optional.of(method); - } - - return resolve(method.id(), suite, property); + if (methodType != null && method instanceof VerificationKey && (((VerificationKey) method).publicKey() != null)) { + return method; } - return Optional.empty(); + return resolveMethod(method.id(), proof, loader); } - private Optional resolve(JsonObject method, SignatureSuite suite, LdProperty property) throws DocumentError { - try { - var id = JsonLdReader - .getId(method) - .orElseThrow(() -> new DocumentError(ErrorType.Missing, property.term())); - - return resolve(id, suite, property); - - } catch (InvalidJsonLdValue e) { - throw new DocumentError(e, ErrorType.Invalid, property.term()); - } - - } - - private Optional resolve(URI id, SignatureSuite suite, LdProperty property) throws DocumentError { - - if (id == null) { - throw new DocumentError(ErrorType.Invalid, property.term()); - } - - // find the method id resolver - var resolver = methodResolvers.stream() - .filter(r -> r.isAccepted(id)) - .findFirst(); - - // try to resolve the method - if (resolver.isPresent()) { - return Optional.ofNullable(resolver.get().resolve(id, loader, suite)); - } - - throw new DocumentError(ErrorType.Unknown, property.term()); + private SignatureSuite findSuite(Collection proofTypes, JsonObject expandedProof) { + return suiteRegistry.getAllSuites().stream() + .filter(s -> proofTypes.stream().anyMatch(type -> s.isSupported(type, expandedProof))) + .findFirst().orElse(null); } private boolean isCredential(JsonObject expanded) { - return JsonLdReader.isTypeOf(VcVocab.CREDENTIAL_TYPE.uri(), expanded); + return LdNode.isTypeOf(VcVocab.CREDENTIAL_TYPE.uri(), expanded); } private boolean isPresentation(JsonObject expanded) { - return JsonLdReader.isTypeOf(VcVocab.PRESENTATION_TYPE.uri(), expanded); + return LdNode.isTypeOf(VcVocab.PRESENTATION_TYPE.uri(), expanded); } public static class Builder { diff --git a/extensions/common/crypto/ldp-verifiable-credentials/src/test/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpIssuerTest.java b/extensions/common/crypto/ldp-verifiable-credentials/src/test/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpIssuerTest.java index b4bbea30112..16603ee5910 100644 --- a/extensions/common/crypto/ldp-verifiable-credentials/src/test/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpIssuerTest.java +++ b/extensions/common/crypto/ldp-verifiable-credentials/src/test/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpIssuerTest.java @@ -14,8 +14,6 @@ package org.eclipse.edc.verifiablecredentials.linkeddata; -import com.apicatalog.ld.DocumentError; -import com.apicatalog.ld.signature.SigningError; import com.fasterxml.jackson.databind.ObjectMapper; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.jwk.Curve; @@ -29,8 +27,9 @@ import jakarta.json.JsonValue; import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.jsonld.spi.JsonLdKeywords; -import org.eclipse.edc.security.signature.jws2020.JwkMethod; -import org.eclipse.edc.security.signature.jws2020.JwsSignature2020Suite; +import org.eclipse.edc.security.signature.jws2020.JsonWebKeyPair; +import org.eclipse.edc.security.signature.jws2020.Jws2020ProofDraft; +import org.eclipse.edc.security.signature.jws2020.Jws2020SignatureSuite; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -59,8 +58,8 @@ class LdpIssuerTest { private final ObjectMapper mapper = createObjectMapper(); @Nested - class JsonWegSignature2020 { - private final JwsSignature2020Suite jws2020suite = new JwsSignature2020Suite(mapper); + class JsonWebSignature2020 { + private final Jws2020SignatureSuite jws2020suite = new Jws2020SignatureSuite(mapper); private LdpIssuer issuer; @BeforeEach @@ -86,15 +85,17 @@ void signSimpleCredential_ecKey() throws JOSEException { var verificationMethodUrl = "https://org.eclipse.edc/verification-method"; - var proofOptions = jws2020suite.createOptions() + var proofOptions = Jws2020ProofDraft.Builder.newInstance() + .mapper(mapper) .created(Instant.parse("2022-12-31T23:00:00Z")) - .verificationMethod(new JwkMethod(URI.create(verificationMethodUrl), null, null, null)) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); + .verificationMethod(new JsonWebKeyPair(URI.create(verificationMethodUrl), null, null, null)) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - var result = issuer.signDocument(vc, keypair, proofOptions); + var result = issuer.signDocument(jws2020suite, vc, keypair, proofOptions); assertThat(result.succeeded()).withFailMessage(result::getFailureDetail).isTrue(); - var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); + var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().getJsonArray("@graph").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); assertThat(verificationMethod.getValueType()).describedAs("Expected a String!").isEqualTo(JsonValue.ValueType.ARRAY); assertThat(verificationMethod.asJsonArray().get(0).asJsonObject().toString()).contains(verificationMethodUrl); @@ -103,7 +104,7 @@ void signSimpleCredential_ecKey() throws JOSEException { @DisplayName("t0001: a simple credential to sign (RSA Key)") @ParameterizedTest(name = "keySize = {0} bits") @ValueSource(ints = { 2048, 3072, 4096 }) - void signSimpleCredential_rsaKey(int keysize) throws SigningError, DocumentError, NoSuchAlgorithmException { + void signSimpleCredential_rsaKey(int keysize) throws NoSuchAlgorithmException { var vc = readResourceAsJson("jws2020/issuing/0001_vc.json"); var gen = KeyPairGenerator.getInstance("RSA"); @@ -120,14 +121,16 @@ void signSimpleCredential_rsaKey(int keysize) throws SigningError, DocumentError var verificationMethodUrl = "https://org.eclipse.edc/verification-method"; - var proofOptions = jws2020suite.createOptions() + var proofOptions = Jws2020ProofDraft.Builder.newInstance() + .mapper(mapper) .created(Instant.parse("2022-12-31T23:00:00Z")) - .verificationMethod(new JwkMethod(URI.create(verificationMethodUrl), null, null, null)) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); + .verificationMethod(new JsonWebKeyPair(URI.create(verificationMethodUrl), null, null, null)) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - var result = issuer.signDocument(vc, keypair, proofOptions); + var result = issuer.signDocument(jws2020suite, vc, keypair, proofOptions); assertThat(result.succeeded()).withFailMessage(result::getFailureDetail).isTrue(); - var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); + var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().getJsonArray("@graph").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); assertThat(verificationMethod.getValueType()).describedAs("Expected a String!").isEqualTo(JsonValue.ValueType.ARRAY); assertThat(verificationMethod.asJsonArray().get(0).asJsonObject().toString()).contains(verificationMethodUrl); @@ -135,7 +138,7 @@ void signSimpleCredential_rsaKey(int keysize) throws SigningError, DocumentError @DisplayName("t0001: a simple credential to sign (OctetKeyPair)") @Test - void signSimpleCredential_octetKeyPair() throws SigningError, DocumentError, JOSEException { + void signSimpleCredential_octetKeyPair() throws JOSEException { var vc = readResourceAsJson("jws2020/issuing/0001_vc.json"); var jwk = new OctetKeyPairGenerator(Curve.Ed25519).generate(); @@ -143,15 +146,17 @@ void signSimpleCredential_octetKeyPair() throws SigningError, DocumentError, JOS var verificationMethodUrl = "https://org.eclipse.edc/verification-method"; - var proofOptions = jws2020suite.createOptions() + var proofOptions = Jws2020ProofDraft.Builder.newInstance() + .mapper(mapper) .created(Instant.parse("2022-12-31T23:00:00Z")) - .verificationMethod(new JwkMethod(URI.create(verificationMethodUrl), null, null, null)) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); + .verificationMethod(new JsonWebKeyPair(URI.create(verificationMethodUrl), null, null, null)) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - var result = issuer.signDocument(vc, keypair, proofOptions); + var result = issuer.signDocument(jws2020suite, vc, keypair, proofOptions); assertThat(result.succeeded()).withFailMessage(result::getFailureDetail).isTrue(); - var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); + var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().getJsonArray("@graph").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); assertThat(verificationMethod.getValueType()).describedAs("Expected a String!").isEqualTo(JsonValue.ValueType.ARRAY); assertThat(verificationMethod.asJsonArray().get(0).asJsonObject().toString()).contains(verificationMethodUrl); @@ -163,15 +168,17 @@ void signEmbeddedVerificationMethod() throws JOSEException { var vc = readResourceAsJson("jws2020/issuing/0001_vc.json"); var keypair = createKeyPair(new ECKeyGenerator(Curve.P_384).keyID("test-kid").generate()); - var proofOptions = jws2020suite.createOptions() + var proofOptions = Jws2020ProofDraft.Builder.newInstance() + .mapper(mapper) .created(Instant.parse("2022-12-31T23:00:00Z")) .verificationMethod(keypair) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - var result = issuer.signDocument(vc, keypair, proofOptions); + var result = issuer.signDocument(jws2020suite, vc, keypair, proofOptions); assertThat(result.succeeded()).withFailMessage(result::getFailureDetail).isTrue(); - var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); + var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().getJsonArray("@graph").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); assertThat(verificationMethod).withFailMessage("Expected an JsonArray!").isInstanceOf(JsonArray.class); assertThat(verificationMethod.asJsonArray().get(0).asJsonObject().get("https://w3id.org/security#publicKeyJwk")) @@ -186,7 +193,7 @@ void signEmbeddedVerificationMethod() throws JOSEException { @DisplayName("t0004: a credential with DID key as verification method") @Test - void signVerificationDidKey() throws SigningError, DocumentError, ParseException { + void signVerificationDidKey() throws ParseException { var vc = readResourceAsJson("jws2020/issuing/0001_vc.json"); var eckey = (ECKey) JWK.parse(""" { @@ -204,15 +211,17 @@ void signVerificationDidKey() throws SigningError, DocumentError, ParseException // check https://w3c-ccg.github.io/did-method-key/#create for details var didKey = "did:key:zC2zU1wUHhYYX4CDwNwky9f5jtSvp5aQy5aNRQMHEdpK5xkJMy6TcMbWBP3scHbR6hhidR3RRjfAA7cuLxjydXgEiZUzRzguozYFeR3G6SzjAwswJ6hXKBWhFEHm2L6Rd6GRAw8r3kyPovxvcabdMF2gBy5TAioY1mVYFeT6"; - var proofOptions = jws2020suite.createOptions() + var proofOptions = Jws2020ProofDraft.Builder.newInstance() + .mapper(mapper) .created(Instant.parse("2022-12-31T23:00:00Z")) - .verificationMethod(new JwkMethod(URI.create(didKey), null, null, null)) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); + .verificationMethod(new JsonWebKeyPair(URI.create(didKey), null, null, null)) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - var result = issuer.signDocument(vc, keypair, proofOptions); + var result = issuer.signDocument(jws2020suite, vc, keypair, proofOptions); assertThat(result.succeeded()).withFailMessage(result::getFailureDetail).isTrue(); - var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); + var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().getJsonArray("@graph").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); assertThat(verificationMethod.getValueType()).describedAs("Expected a String!").isEqualTo(JsonValue.ValueType.ARRAY); assertThat(verificationMethod.asJsonArray().get(0).asJsonObject().toString()).contains(didKey); @@ -229,15 +238,17 @@ void signCompactedPresentation() throws JOSEException { var verificationMethodUrl = "https://org.eclipse.edc/verification-method"; - var proofOptions = jws2020suite.createOptions() + var proofOptions = Jws2020ProofDraft.Builder.newInstance() + .mapper(mapper) .created(Instant.parse("2022-12-31T23:00:00Z")) - .verificationMethod(new JwkMethod(URI.create(verificationMethodUrl), null, null, null)) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); + .verificationMethod(new JsonWebKeyPair(URI.create(verificationMethodUrl), null, null, null)) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - var result = issuer.signDocument(vp, keypair, proofOptions); + var result = issuer.signDocument(jws2020suite, vp, keypair, proofOptions); assertThat(result.succeeded()).withFailMessage(result::getFailureDetail).isTrue(); - var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); + var verificationMethod = result.getContent().getJsonArray("https://w3id.org/security#proof").get(0).asJsonObject().getJsonArray("@graph").get(0).asJsonObject().get("https://w3id.org/security#verificationMethod"); assertThat(verificationMethod.getValueType()).describedAs("Expected a String!").isEqualTo(JsonValue.ValueType.ARRAY); assertThat(verificationMethod.asJsonArray().get(0).asJsonObject().toString()).contains(verificationMethodUrl); diff --git a/extensions/common/crypto/ldp-verifiable-credentials/src/test/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpVerifierTest.java b/extensions/common/crypto/ldp-verifiable-credentials/src/test/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpVerifierTest.java index 911f62036c7..a8ebcce825e 100644 --- a/extensions/common/crypto/ldp-verifiable-credentials/src/test/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpVerifierTest.java +++ b/extensions/common/crypto/ldp-verifiable-credentials/src/test/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpVerifierTest.java @@ -16,23 +16,21 @@ import com.apicatalog.jsonld.loader.SchemeRouter; import com.apicatalog.ld.DocumentError; -import com.apicatalog.ld.signature.method.MethodResolver; -import com.apicatalog.ld.signature.method.VerificationMethod; -import com.apicatalog.vc.integrity.DataIntegrityProofOptions; +import com.apicatalog.ld.signature.VerificationMethod; +import com.apicatalog.vc.method.resolver.MethodResolver; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.jwk.Curve; -import com.nimbusds.jose.jwk.ECKey; import com.nimbusds.jose.jwk.gen.ECKeyGenerator; import jakarta.json.JsonObject; import org.eclipse.edc.iam.identitytrust.spi.verification.SignatureSuiteRegistry; import org.eclipse.edc.iam.identitytrust.spi.verification.VerifierContext; import org.eclipse.edc.jsonld.TitaniumJsonLd; -import org.eclipse.edc.security.signature.jws2020.JwkMethod; -import org.eclipse.edc.security.signature.jws2020.JwsSignature2020Suite; +import org.eclipse.edc.security.signature.jws2020.JsonWebKeyPair; +import org.eclipse.edc.security.signature.jws2020.Jws2020ProofDraft; +import org.eclipse.edc.security.signature.jws2020.Jws2020SignatureSuite; import org.eclipse.edc.security.signature.jws2020.TestDocumentLoader; -import org.eclipse.edc.security.signature.jws2020.TestFunctions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -45,6 +43,7 @@ import static org.eclipse.edc.jsonld.util.JacksonJsonLd.createObjectMapper; import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.eclipse.edc.security.signature.jws2020.TestFunctions.createKeyPair; import static org.eclipse.edc.verifiablecredentials.linkeddata.TestData.MEMBERSHIP_CREDENTIAL_ISSUER; import static org.eclipse.edc.verifiablecredentials.linkeddata.TestData.NAME_CREDENTIAL_ISSUER; import static org.eclipse.edc.verifiablecredentials.linkeddata.TestData.VC_CONTENT_CERTIFICATE_EXAMPLE; @@ -70,7 +69,7 @@ class LdpVerifierTest { @Nested class JsonWebSignature2020 { - private final JwsSignature2020Suite jwsSignatureSuite = new JwsSignature2020Suite(mapper); + private final Jws2020SignatureSuite jwsSignatureSuite = new Jws2020SignatureSuite(mapper); @BeforeEach void setUp() throws URISyntaxException { @@ -89,15 +88,20 @@ void setUp() throws URISyntaxException { .build(); context = VerifierContext.Builder.newInstance().verifier(ldpVerifier).build(); - when(suiteRegistry.getForId(any())).thenReturn(jwsSignatureSuite); + when(suiteRegistry.getAllSuites()).thenReturn(List.of(jwsSignatureSuite)); } - private DataIntegrityProofOptions generateEmbeddedProofOptions(ECKey vcKey, String id) { - return jwsSignatureSuite - .createOptions() + private Jws2020ProofDraft generateEmbeddedProof(VerificationMethod verificationMethod) { + return proofBuilder(verificationMethod) + .build(); + } + + private Jws2020ProofDraft.Builder proofBuilder(VerificationMethod verificationMethod) { + return Jws2020ProofDraft.Builder.newInstance() + .mapper(mapper) .created(Instant.now()) - .verificationMethod(TestFunctions.createKeyPair(vcKey, id)) // embedded proof - .purpose(URI.create("https://w3id.org/security#assertionMethod")); + .verificationMethod(verificationMethod) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")); } @Nested @@ -110,7 +114,8 @@ void verify_noCredentials_success() throws JOSEException { .generate(); var input = TestData.VP_CONTENT_TEMPLATE.formatted(""); - var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProofOptions(vpKey, VP_HOLDER), testDocLoader); + var verificationMethod = createKeyPair(vpKey, VP_HOLDER); + var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProof(verificationMethod), testDocLoader); var res = ldpVerifier.verify(rawVp, context); assertThat(res).isSucceeded(); @@ -122,7 +127,8 @@ void verify_singleValidCredentials_success() throws JOSEException { var vcKey = new ECKeyGenerator(Curve.P_256) .keyID("vc-sign-key") .generate(); - var rawVc = LdpCreationUtils.signDocument(VC_CONTENT_CERTIFICATE_EXAMPLE, vcKey, generateEmbeddedProofOptions(vcKey, "did:web:test-issuer"), testDocLoader); + var vcVerificationMethod = createKeyPair(vcKey, "did:web:test-issuer"); + var rawVc = LdpCreationUtils.signDocument(VC_CONTENT_CERTIFICATE_EXAMPLE, vcKey, generateEmbeddedProof(vcVerificationMethod), testDocLoader); // create signed VP, that contains the VC var vpKey = new ECKeyGenerator(Curve.P_384) @@ -130,7 +136,8 @@ void verify_singleValidCredentials_success() throws JOSEException { .generate(); var input = TestData.VP_CONTENT_TEMPLATE.formatted(rawVc); - var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProofOptions(vpKey, VP_HOLDER), testDocLoader); + var vpVerificationMethod = createKeyPair(vpKey, VP_HOLDER); + var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProof(vpVerificationMethod), testDocLoader); var res = ldpVerifier.verify(rawVp, context); assertThat(res).isSucceeded(); @@ -145,14 +152,16 @@ void verify_multipleValidCredentials_success() throws JOSEException { .generate(); var nameCredential = createNameCredential(); - var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, generateEmbeddedProofOptions(nameKey, NAME_CREDENTIAL_ISSUER), testDocLoader); + var nameVm = createKeyPair(nameKey, NAME_CREDENTIAL_ISSUER); + var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, generateEmbeddedProof(nameVm), testDocLoader); // create Membership credential var membershipCredential = createMembershipCredential(); var membershipKey = new ECKeyGenerator(Curve.P_384) .keyID("dataspace-issuance-key1") .generate(); - var signedMembershipCred = LdpCreationUtils.signDocument(membershipCredential, membershipKey, generateEmbeddedProofOptions(membershipKey, MEMBERSHIP_CREDENTIAL_ISSUER), testDocLoader); + var memberVm = createKeyPair(membershipKey, MEMBERSHIP_CREDENTIAL_ISSUER); + var signedMembershipCred = LdpCreationUtils.signDocument(membershipCredential, membershipKey, generateEmbeddedProof(memberVm), testDocLoader); // create signed VP, that contains the VC var vpKey = new ECKeyGenerator(Curve.P_384) @@ -161,7 +170,8 @@ void verify_multipleValidCredentials_success() throws JOSEException { var content = "%s, %s".formatted(signedNameCredential, signedMembershipCred); var input = TestData.VP_CONTENT_TEMPLATE.formatted(content); - var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProofOptions(vpKey, VP_HOLDER), testDocLoader); + var vpVerificationMethod = createKeyPair(vpKey, VP_HOLDER); + var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProof(vpVerificationMethod), testDocLoader); var res = ldpVerifier.verify(rawVp, context); assertThat(res).isSucceeded(); } @@ -173,16 +183,17 @@ void verify_singleInvalidVc_shouldFail() throws JOSEException { var membershipKey = new ECKeyGenerator(Curve.P_384) .keyID("dataspace-issuance-key1") .generate(); - var signedMembershipCred = LdpCreationUtils.signDocument(membershipCredential, membershipKey, generateEmbeddedProofOptions(membershipKey, MEMBERSHIP_CREDENTIAL_ISSUER), testDocLoader); + var vm1 = createKeyPair(membershipKey, MEMBERSHIP_CREDENTIAL_ISSUER); + var signedMembershipCred = LdpCreationUtils.signDocument(membershipCredential, membershipKey, generateEmbeddedProof(vm1), testDocLoader); // create name credential, but altered after-the-fact - // create IsoCertificate VC var nameKey = new ECKeyGenerator(Curve.P_256) .keyID("vc-sign-key1") .generate(); var nameCredential = createNameCredential(); - var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, generateEmbeddedProofOptions(nameKey, NAME_CREDENTIAL_ISSUER), testDocLoader); + var vm2 = createKeyPair(nameKey, NAME_CREDENTIAL_ISSUER); + var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, generateEmbeddedProof(vm2), testDocLoader); // now change the name signedNameCredential = signedNameCredential.replace("Test Person III", "Test Person IV"); @@ -193,20 +204,22 @@ void verify_singleInvalidVc_shouldFail() throws JOSEException { var content = "%s, %s".formatted(signedNameCredential, signedMembershipCred); var input = TestData.VP_CONTENT_TEMPLATE.formatted(content); - var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProofOptions(vpKey, VP_HOLDER), testDocLoader); + var vpVm = createKeyPair(vpKey, VP_HOLDER); + var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProof(vpVm), testDocLoader); var res = ldpVerifier.verify(rawVp, context); assertThat(res).isFailed().detail().contains("InvalidSignature"); } @Test - void verify_multipleInvalidVc_shouldSucceed() throws JOSEException { + void verify_multipleInvalidVc_shouldFail() throws JOSEException { // create Membership credential var membershipCredential = createMembershipCredential(); var membershipKey = new ECKeyGenerator(Curve.P_384) .keyID("dataspace-issuance-key1") .generate(); - var signedMembershipCred = LdpCreationUtils.signDocument(membershipCredential, membershipKey, generateEmbeddedProofOptions(membershipKey, MEMBERSHIP_CREDENTIAL_ISSUER), testDocLoader); + var verificationMethod = createKeyPair(membershipKey, MEMBERSHIP_CREDENTIAL_ISSUER); + var signedMembershipCred = LdpCreationUtils.signDocument(membershipCredential, membershipKey, generateEmbeddedProof(verificationMethod), testDocLoader); // tamper with the status -> causes signature failure! signedMembershipCred = signedMembershipCred.replace("active", "super-active"); @@ -217,7 +230,8 @@ void verify_multipleInvalidVc_shouldSucceed() throws JOSEException { .generate(); var nameCredential = createNameCredential(); - var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, generateEmbeddedProofOptions(nameKey, NAME_CREDENTIAL_ISSUER), testDocLoader); + var nameVm = createKeyPair(nameKey, NAME_CREDENTIAL_ISSUER); + var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, generateEmbeddedProof(nameVm), testDocLoader); // tamper with the name -> causes signature failure! signedNameCredential = signedNameCredential.replace("Test Person III", "Test Person IV"); @@ -228,7 +242,8 @@ void verify_multipleInvalidVc_shouldSucceed() throws JOSEException { var content = "%s, %s".formatted(signedNameCredential, signedMembershipCred); var input = TestData.VP_CONTENT_TEMPLATE.formatted(content); - var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProofOptions(vpKey, VP_HOLDER), testDocLoader); + var vpVm = createKeyPair(vpKey, VP_HOLDER); + var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProof(vpVm), testDocLoader); var res = ldpVerifier.verify(rawVp, context); assertThat(res).isFailed().detail().contains("InvalidSignature"); } @@ -241,14 +256,16 @@ void verify_forgedPresentation_shouldFail() throws JOSEException { .generate(); var nameCredential = createNameCredential(); - var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, generateEmbeddedProofOptions(nameKey, NAME_CREDENTIAL_ISSUER), testDocLoader); + var nameVm = createKeyPair(nameKey, NAME_CREDENTIAL_ISSUER); + var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, generateEmbeddedProof(nameVm), testDocLoader); // create Membership credential var membershipCredential = createMembershipCredential(); var membershipKey = new ECKeyGenerator(Curve.P_384) .keyID("dataspace-issuance-key1") .generate(); - var signedMembershipCred = LdpCreationUtils.signDocument(membershipCredential, membershipKey, generateEmbeddedProofOptions(membershipKey, MEMBERSHIP_CREDENTIAL_ISSUER), testDocLoader); + var memberVm = createKeyPair(membershipKey, MEMBERSHIP_CREDENTIAL_ISSUER); + var signedMembershipCred = LdpCreationUtils.signDocument(membershipCredential, membershipKey, generateEmbeddedProof(memberVm), testDocLoader); // create signed VP, that contains the VC var vpKey = new ECKeyGenerator(Curve.P_384) @@ -258,7 +275,8 @@ void verify_forgedPresentation_shouldFail() throws JOSEException { var input = TestData.VP_CONTENT_TEMPLATE.formatted(content); - var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProofOptions(vpKey, VP_HOLDER), testDocLoader); + var vpVm = createKeyPair(vpKey, VP_HOLDER); + var rawVp = LdpCreationUtils.signDocument(input, vpKey, generateEmbeddedProof(vpVm), testDocLoader); // tamper with the presentation rawVp = rawVp.replace("\"https://holder.test.com\"", "\"https://another-holder.test.com\""); // modify @@ -274,9 +292,9 @@ void verify_proofPurposeNotAssertionMethod() throws JOSEException { .generate(); var nameCredential = createNameCredential(); - var proofOptions = generateEmbeddedProofOptions(nameKey, NAME_CREDENTIAL_ISSUER); + var proofOptions = proofBuilder(createKeyPair(nameKey, NAME_CREDENTIAL_ISSUER)); var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, - proofOptions.purpose(URI.create("https://test.org/notValid")), testDocLoader); + proofOptions.proofPurpose(URI.create("https://test.org/notValid")).build(), testDocLoader); assertThat(ldpVerifier.verify(signedNameCredential, context)).isFailed() .detail().contains("InvalidProofPurpose"); @@ -287,14 +305,17 @@ void verify_proofPurposeNotAssertionMethod() throws JOSEException { class Credentials { @Test void verify_success() throws JOSEException { - var nameKey = new ECKeyGenerator(Curve.P_256) + var signingKey = new ECKeyGenerator(Curve.P_256) .keyID("vc-sign-key1") .generate(); var nameCredential = createNameCredential(); - var proofKey = new JwkMethod(URI.create(TestData.NAME_CREDENTIAL_ISSUER), URI.create("https://w3id.org/security#JsonWebKey2020"), null, nameKey); - var proofOptions = generateEmbeddedProofOptions(nameKey, NAME_CREDENTIAL_ISSUER).verificationMethod(proofKey); - var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, proofKey, proofOptions, testDocLoader); + var verificationMethod = new JsonWebKeyPair(URI.create(TestData.NAME_CREDENTIAL_ISSUER), URI.create("https://w3id.org/security#JsonWebKey2020"), null, signingKey); + + var proofOptions = proofBuilder(verificationMethod) + .build(); + + var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, verificationMethod, proofOptions, testDocLoader); assertThat(ldpVerifier.verify(signedNameCredential, context)).isSucceeded(); } @@ -311,10 +332,10 @@ void verify_forgedProof_shouldFail() throws JOSEException { var nameCredential = createNameCredential(); - var proofKey = new JwkMethod(URI.create(TestData.NAME_CREDENTIAL_ISSUER), URI.create("https://w3id.org/security#JsonWebKey2020"), null, nameKey); - var proofOptions = generateEmbeddedProofOptions(nameKey, NAME_CREDENTIAL_ISSUER).verificationMethod(proofKey); + var proofKey = new JsonWebKeyPair(URI.create(TestData.NAME_CREDENTIAL_ISSUER), URI.create("https://w3id.org/security#JsonWebKey2020"), null, nameKey); + var proofDraft = proofBuilder(proofKey).build(); - var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, forgedKey, proofOptions, testDocLoader); + var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, forgedKey, proofDraft, testDocLoader); assertThat(ldpVerifier.verify(signedNameCredential, context)).isFailed().detail().contains("InvalidSignature"); } @@ -332,9 +353,8 @@ void verify_proofPurposeNotAssertionMethod() throws JOSEException { .generate(); var nameCredential = createNameCredential(); - var proofOptions = generateEmbeddedProofOptions(nameKey, NAME_CREDENTIAL_ISSUER); - var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, - proofOptions.purpose(URI.create("https://test.org/notValid")), testDocLoader); + var proofOptions = proofBuilder(createKeyPair(nameKey, NAME_CREDENTIAL_ISSUER)).proofPurpose(URI.create("https://test.org/notValid")).build(); + var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, proofOptions, testDocLoader); assertThat(ldpVerifier.verify(signedNameCredential, context)).isFailed() .detail().contains("InvalidProofPurpose"); @@ -346,15 +366,15 @@ void verify_verificationMethodIsDid() throws JOSEException, DocumentError { .keyID("vc-sign-key1") .generate(); - var identifier = URI.create("did:web-test-issuer"); + var identifier = URI.create("did:web:test-issuer"); var nameCredential = createNameCredential(identifier.toString()); - VerificationMethod did = new JwkMethod(identifier, null, null, null); + var did = new JsonWebKeyPair(identifier, null, null, null); ArgumentMatcher uriMatcher = argument -> argument.equals(identifier); when(mockDidResolver.isAccepted(argThat(uriMatcher))).thenReturn(true); - when(mockDidResolver.resolve(argThat(uriMatcher), any(), any())).thenReturn(new JwkMethod(identifier, null, null, nameKey)); + when(mockDidResolver.resolve(argThat(uriMatcher), any(), any())).thenReturn(new JsonWebKeyPair(identifier, null, null, nameKey)); - var proofOptions = generateEmbeddedProofOptions(nameKey, NAME_CREDENTIAL_ISSUER).verificationMethod(did); + var proofOptions = proofBuilder(createKeyPair(nameKey, NAME_CREDENTIAL_ISSUER)).verificationMethod(did).build(); var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, proofOptions, testDocLoader); assertThat(ldpVerifier.verify(signedNameCredential, context)).isSucceeded(); @@ -366,21 +386,22 @@ void verify_verificationMethodIsDid_doesNotMatchIssuer() throws JOSEException, D .keyID("vc-sign-key1") .generate(); - var identifier = URI.create("did:web-test-issuer"); + var identifier = URI.create("did:web:test-issuer"); var nameCredential = createNameCredential("did:web:some-other-issuer"); - VerificationMethod did = new JwkMethod(identifier, null, null, null); + var did = new JsonWebKeyPair(identifier, null, null, null); ArgumentMatcher uriMatcher = argument -> argument.equals(identifier); when(mockDidResolver.isAccepted(argThat(uriMatcher))).thenReturn(true); - when(mockDidResolver.resolve(argThat(uriMatcher), any(), any())).thenReturn(new JwkMethod(identifier, null, null, nameKey)); + when(mockDidResolver.resolve(argThat(uriMatcher), any(), any())).thenReturn(new JsonWebKeyPair(identifier, null, null, nameKey)); - var proofOptions = generateEmbeddedProofOptions(nameKey, NAME_CREDENTIAL_ISSUER).verificationMethod(did); + var proofOptions = proofBuilder(createKeyPair(nameKey, NAME_CREDENTIAL_ISSUER)).verificationMethod(did).build(); var signedNameCredential = LdpCreationUtils.signDocument(nameCredential, nameKey, proofOptions, testDocLoader); assertThat(ldpVerifier.verify(signedNameCredential, context)).isFailed() .detail().contains("Issuer and proof.verificationMethod mismatch"); } + } } diff --git a/extensions/common/crypto/ldp-verifiable-credentials/src/testFixtures/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpCreationUtils.java b/extensions/common/crypto/ldp-verifiable-credentials/src/testFixtures/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpCreationUtils.java index de45d60b00d..b0f794dab24 100644 --- a/extensions/common/crypto/ldp-verifiable-credentials/src/testFixtures/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpCreationUtils.java +++ b/extensions/common/crypto/ldp-verifiable-credentials/src/testFixtures/java/org/eclipse/edc/verifiablecredentials/linkeddata/LdpCreationUtils.java @@ -18,36 +18,45 @@ import com.apicatalog.ld.DocumentError; import com.apicatalog.ld.signature.SigningError; import com.apicatalog.ld.signature.key.KeyPair; -import com.apicatalog.ld.signature.proof.ProofOptions; -import com.apicatalog.vc.Vc; +import com.apicatalog.vc.issuer.ProofDraft; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.nimbusds.jose.jwk.JWK; import jakarta.json.JsonObject; import org.eclipse.edc.jsonld.util.JacksonJsonLd; -import org.eclipse.edc.security.signature.jws2020.IssuerCompatibility; +import org.eclipse.edc.security.signature.jws2020.Jws2020SignatureSuite; import org.eclipse.edc.security.signature.jws2020.TestFunctions; import org.jetbrains.annotations.Nullable; public class LdpCreationUtils { private static final ObjectMapper MAPPER = JacksonJsonLd.createObjectMapper(); + private static final Jws2020SignatureSuite SUITE = new Jws2020SignatureSuite(MAPPER); - public static String signDocument(String jsonLdContent, JWK proofKey, ProofOptions proofOptions, @Nullable DocumentLoader testDocLoader) { + public static String signDocument(String jsonLdContent, JWK proofKey, ProofDraft proofDraft, @Nullable DocumentLoader testDocLoader) { try { var jsonLd = MAPPER.readValue(jsonLdContent, JsonObject.class); var ldKeypair = TestFunctions.createKeyPair(proofKey); - var issuer = Vc.sign(jsonLd, ldKeypair, proofOptions).loader(testDocLoader); - return IssuerCompatibility.compact(issuer).toString(); + + return SUITE.createIssuer(ldKeypair) + .loader(testDocLoader) + .sign(jsonLd, proofDraft) + .compacted() + .toString(); + } catch (JsonProcessingException | DocumentError | SigningError e) { throw new RuntimeException(e); } } - public static String signDocument(String jsonLdContent, KeyPair proofKey, ProofOptions proofOptions, @Nullable DocumentLoader testDocLoader) { + public static String signDocument(String jsonLdContent, KeyPair proofKey, ProofDraft proofDraft, @Nullable DocumentLoader testDocLoader) { try { var jsonLd = MAPPER.readValue(jsonLdContent, JsonObject.class); - var issuer = Vc.sign(jsonLd, proofKey, proofOptions).loader(testDocLoader); - return IssuerCompatibility.compact(issuer).toString(); + + return SUITE.createIssuer(proofKey) + .loader(testDocLoader) + .sign(jsonLd, proofDraft) + .compacted() + .toString(); } catch (JsonProcessingException | DocumentError | SigningError e) { throw new RuntimeException(e); } diff --git a/extensions/common/crypto/ldp-verifiable-credentials/src/testFixtures/java/org/eclipse/edc/verifiablecredentials/linkeddata/TestData.java b/extensions/common/crypto/ldp-verifiable-credentials/src/testFixtures/java/org/eclipse/edc/verifiablecredentials/linkeddata/TestData.java index 63564e5b847..044f84b6c7b 100644 --- a/extensions/common/crypto/ldp-verifiable-credentials/src/testFixtures/java/org/eclipse/edc/verifiablecredentials/linkeddata/TestData.java +++ b/extensions/common/crypto/ldp-verifiable-credentials/src/testFixtures/java/org/eclipse/edc/verifiablecredentials/linkeddata/TestData.java @@ -106,7 +106,8 @@ static String generateVc(String issuer, Instant issuanceDate, JsonObject credent return createObjectBuilder() .add(JsonLdKeywords.CONTEXT, Json.createArrayBuilder() .add("https://www.w3.org/2018/credentials/v1") - .add("https://www.w3.org/2018/credentials/examples/v1")) + .add("https://www.w3.org/2018/credentials/examples/v1") + .add("https://w3id.org/security/suites/jws-2020/v1")) .add("type", typeArray) .add("issuer", issuer) .add("issuanceDate", issuanceDate.toString()) @@ -141,9 +142,9 @@ static String createNameCredential() { Instant.parse("2020-06-01T12:00:00Z"), createObjectBuilder() .add("id", "https://some.subject/name#1") - .add("name", createObjectBuilder() + .add("name", Json.createObjectBuilder() .add("name", "Test Person III.") - .add("shortName", "Testy")).build(), + .add("short-name", "Testy")).build(), "NameCredential"); } diff --git a/extensions/common/crypto/lib/jws2020-lib/README.md b/extensions/common/crypto/lib/jws2020-lib/README.md index 8fffc2b000e..89c1c5546a4 100644 --- a/extensions/common/crypto/lib/jws2020-lib/README.md +++ b/extensions/common/crypto/lib/jws2020-lib/README.md @@ -22,7 +22,7 @@ check the `IssuerTests` and the `VerifierTests` for more comprehensive explanati JwsSignature2020Suite suite=new JwsSignature2020Suite(JacksonJsonLd.createObjectMapper()); JsonObject vc=createVcAsJsonLd(); JWK keyPair=createKeyPairAsJwk(); -JwkMethod signKeys=new JwkMethod(id,type,controller,keyPair); +JsonWebKeyPair signKeys=new JsonWebKeyPair(id,type,controller,keyPair); var options=suite.createOptions() .created(Instant.now()) diff --git a/extensions/common/crypto/lib/jws2020-lib/build.gradle.kts b/extensions/common/crypto/lib/jws2020-lib/build.gradle.kts index 647ff85dd2f..cbc65821ea5 100644 --- a/extensions/common/crypto/lib/jws2020-lib/build.gradle.kts +++ b/extensions/common/crypto/lib/jws2020-lib/build.gradle.kts @@ -27,13 +27,12 @@ dependencies { runtimeOnly(libs.tink) implementation(libs.jakartaJson) - api(libs.iron.vc) { + api("com.apicatalog:iron-verifiable-credentials:0.14.0") { exclude("com.github.multiformats") } testImplementation(testFixtures(project(":core:common:junit"))) testImplementation(project(":core:common:lib:json-ld-lib")) - testFixturesImplementation(testFixtures(project(":core:common:junit"))) testFixturesImplementation(libs.nimbus.jwt) testFixturesImplementation(project(":core:common:lib:json-ld-lib")) diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/ByteArrayAdapter.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/ByteArrayAdapter.java deleted file mode 100644 index 8b397113cb3..00000000000 --- a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/ByteArrayAdapter.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation - * - */ - -package org.eclipse.edc.security.signature.jws2020; - -import com.apicatalog.jsonld.lang.Keywords; -import com.apicatalog.ld.schema.adapter.LdValueAdapter; -import jakarta.json.Json; -import jakarta.json.JsonValue; -import org.eclipse.edc.jsonld.spi.JsonLdKeywords; - -class ByteArrayAdapter implements LdValueAdapter { - @Override - public byte[] read(JsonValue value) { - if (value.getValueType().equals(JsonValue.ValueType.OBJECT)) { - var obj = value.asJsonObject(); - return obj.getString(JsonLdKeywords.VALUE).getBytes(); - } - return value.toString().getBytes(); - } - - @Override - public JsonValue write(byte[] value) { - return Json.createObjectBuilder() - .add(Keywords.VALUE, new String(value)) - .build(); - } - -} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/IssuerCompatibility.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/IssuerCompatibility.java deleted file mode 100644 index 0da4edaee11..00000000000 --- a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/IssuerCompatibility.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation - * - */ - -package org.eclipse.edc.security.signature.jws2020; - -import com.apicatalog.jsonld.JsonLd; -import com.apicatalog.jsonld.JsonLdError; -import com.apicatalog.jsonld.document.Document; -import com.apicatalog.jsonld.document.JsonDocument; -import com.apicatalog.jsonld.loader.DocumentLoader; -import com.apicatalog.ld.DocumentError; -import com.apicatalog.ld.signature.SigningError; -import com.apicatalog.vc.processor.Issuer; -import jakarta.json.Json; -import jakarta.json.JsonObject; -import org.eclipse.edc.util.reflection.ReflectionUtil; - -import java.net.URI; -import java.util.Arrays; - -/** - * The {@link Issuer} adds the context, but currently that adds hard-coded {@code "https://w3id.org/security/suites/ed25519-2020/v1"}. - * For the Jwk2020 suite we need that to be {@code "https://w3id.org/security/suites/jws-2020/v1"}, so as a temporary workaround we do not - * use {@link Issuer#getCompacted()}, but rather use {@link IssuerCompatibility#compact(Issuer, String...)}. - */ -public class IssuerCompatibility { - /** - * Compacts the JSON structure represented by the {@link Issuer} by delegating to {@link JsonLd#compact(Document, URI)}. Note that before compacting, the JSON-LD is expanded, signed, all additional contexts are added - * and then compacted. - *

- * By default, the following contexts are added automatically: - *

    - *
  • https://www.w3.org/2018/credentials/v1
  • - *
  • https://w3id.org/security/suites/jws-2020/v1
  • - *
  • https://www.w3.org/ns/did/v1
  • - *
- * - * @param issuer The {@link Issuer} - * @param additionalContexts Any additional context URIs that should be used for compaction. For Jws2020 it is highly likely that - * @return a JSON-LD structure in compacted format that contains the signed content (e.g. a VC). - */ - public static JsonObject compact(Issuer issuer, String... additionalContexts) { - try { - var expanded = issuer.getExpanded(); - var arrayBuilder = Json.createArrayBuilder(); - Arrays.stream(additionalContexts).forEach(arrayBuilder::add); - var context = arrayBuilder - .add("https://www.w3.org/2018/credentials/v1") - .add("https://w3id.org/security/suites/jws-2020/v1") - .add("https://www.w3.org/ns/did/v1") - .build(); - return JsonLd.compact(JsonDocument.of(expanded), JsonDocument.of(context)).loader(getLoader(issuer)) - .get(); - - } catch (JsonLdError | SigningError | DocumentError e) { - throw new RuntimeException(e); - } - } - - /** - * rather crude hack to obtain the {@link Issuer}'s loader. The EDC util we're using here basically fetches the declared field recursively. - * - * @see ReflectionUtil#getFieldValue(String, Object) - */ - private static DocumentLoader getLoader(Issuer issuer) { - return ReflectionUtil.getFieldValue("loader", issuer); - } -} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JsonAdapter.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JsonAdapter.java index 45de3e8528e..bc4d2f975ab 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JsonAdapter.java +++ b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JsonAdapter.java @@ -14,41 +14,35 @@ package org.eclipse.edc.security.signature.jws2020; -import com.apicatalog.ld.schema.adapter.LdValueAdapter; +import com.apicatalog.ld.node.adapter.LdAdapter; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.json.Json; import jakarta.json.JsonObject; -import jakarta.json.JsonValue; import org.eclipse.edc.jsonld.spi.JsonLdKeywords; import java.util.Map; -class JsonAdapter implements LdValueAdapter { +class JsonAdapter implements LdAdapter { private final ObjectMapper mapper; JsonAdapter(ObjectMapper mapper) { this.mapper = mapper; } + @Override - public Object read(JsonValue value) { - var input = value; - if (value instanceof JsonObject) { - var jo = value.asJsonObject(); - input = jo.get(JsonLdKeywords.VALUE); - } - return mapper.convertValue(input, Object.class); + public Object read(JsonObject value) { + return mapper.convertValue(value, Object.class); } @Override - public JsonValue write(Object value) { + public JsonObject write(Object value) { if (value instanceof Map) { var jo = Json.createObjectBuilder(); jo.add(JsonLdKeywords.VALUE, Json.createObjectBuilder((Map) value)); jo.add(JsonLdKeywords.TYPE, JsonLdKeywords.JSON); - return mapper.convertValue(jo.build(), JsonValue.class); + return mapper.convertValue(jo.build(), JsonObject.class); } - return mapper.convertValue(value, JsonValue.class); + return mapper.convertValue(value, JsonObject.class); } - } diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwkMethod.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JsonWebKeyPair.java similarity index 70% rename from extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwkMethod.java rename to extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JsonWebKeyPair.java index 1558ca71d24..2d1eab6c82f 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwkMethod.java +++ b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JsonWebKeyPair.java @@ -19,13 +19,21 @@ import java.net.URI; -public record JwkMethod(URI id, URI type, URI controller, JWK keyPair) implements KeyPair { +/** + * {@link com.apicatalog.ld.signature.VerificationMethod} that consists of a Json Web Key (JWK). + * Serialization to a byte array happens by converting to String first ({@link JWK#toJSONString()}), and getting the bytes. + */ +public record JsonWebKeyPair(URI id, URI type, URI controller, JWK keyPair) implements KeyPair { @Override public byte[] privateKey() { return keyPair != null ? serializeKeyPair(keyPair) : null; } + @Override + public String algorithm() { + return "JsonWebKey2020"; + } @Override public byte[] publicKey() { diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jwk2020KeyAdapter.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jwk2020KeyAdapter.java new file mode 100644 index 00000000000..9b2c025a1bd --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jwk2020KeyAdapter.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.security.signature.jws2020; + +import com.apicatalog.ld.DocumentError; +import com.apicatalog.ld.Term; +import com.apicatalog.ld.node.LdNode; +import com.apicatalog.ld.node.LdNodeBuilder; +import com.apicatalog.ld.signature.VerificationMethod; +import com.apicatalog.vc.VcVocab; +import com.apicatalog.vc.method.MethodAdapter; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.jwk.JWK; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; +import org.eclipse.edc.security.token.jwt.CryptoConverter; + +import java.util.Map; +import java.util.Objects; + +import static java.util.Optional.ofNullable; + +/** + * {@link MethodAdapter} that encapsulates a JsonWebKey2020 ({@link JWK}) + */ +public class Jwk2020KeyAdapter implements MethodAdapter { + public static final Term CONTROLLER = Term.create("controller", VcVocab.SECURITY_VOCAB); + public static final Term PUBLIC_KEY_JWK = Term.create("publicKeyJwk", VcVocab.SECURITY_VOCAB); + public static final Term PRIVATE_KEY_JWK = Term.create("privateKeyJwk", VcVocab.SECURITY_VOCAB); + + private final ObjectMapper mapper; + private final JsonAdapter jsonAdapter; + + public Jwk2020KeyAdapter(ObjectMapper mapper) { + this.mapper = mapper; + jsonAdapter = new JsonAdapter(mapper); + } + + @Override + public VerificationMethod read(JsonObject document) throws DocumentError { + if (document == null) { + throw new IllegalArgumentException("Verification method cannot be null."); + } + var node = LdNode.of(document); + + var id = node.id(); + var type = node.type().link(); + var controller = node.node(CONTROLLER).id(); + + var keyProperty = getKeyProperty(node); + + var jwk = CryptoConverter.create(keyProperty); + return new JsonWebKeyPair(id, type, controller, jwk); + } + + @Override + public JsonObject write(VerificationMethod value) { + Objects.requireNonNull(value, "VerificationMethod cannot be null!"); + + var nodebuilder = new LdNodeBuilder(); + if (value.id() != null) { + nodebuilder.id(value.id()); + } + var embedded = false; + if (value.controller() != null) { + nodebuilder.set(CONTROLLER).id(value.controller()); + embedded = true; + } + + if (value instanceof JsonWebKeyPair ecKeyPair) { + if (ecKeyPair.keyPair() != null) { + nodebuilder.set(PUBLIC_KEY_JWK).map(jsonAdapter, serialize(ecKeyPair.keyPair().toPublicJWK().toJSONObject())); + embedded = true; + } + } + if (embedded) { + nodebuilder.type(value.type().toASCIIString()); + } + return nodebuilder.build(); + } + + private String getKeyProperty(LdNode node) throws DocumentError { + var str = node.scalar(PRIVATE_KEY_JWK).exists() ? + node.scalar(PRIVATE_KEY_JWK).value() : + node.scalar(PUBLIC_KEY_JWK).value(); + + return ofNullable(str).map(jsonValue -> { + try { + return mapper.writeValueAsString(jsonValue); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }).orElse(null); + } + + private JsonValue serialize(Map jsonObject) { + return mapper.convertValue(jsonObject, JsonValue.class); + } +} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwkAdapter.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwkAdapter.java deleted file mode 100644 index 948ad0ef18d..00000000000 --- a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwkAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation - * - */ - -package org.eclipse.edc.security.signature.jws2020; - -import com.apicatalog.ld.schema.LdObject; -import com.apicatalog.ld.schema.LdTerm; -import com.apicatalog.ld.schema.adapter.LdValueAdapter; -import com.apicatalog.ld.signature.method.VerificationMethod; -import com.apicatalog.vc.integrity.DataIntegrity; -import org.eclipse.edc.security.token.jwt.CryptoConverter; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -import static org.eclipse.edc.security.signature.jws2020.Jws2020Schema.JWK_PRIVATE_KEY; -import static org.eclipse.edc.security.signature.jws2020.Jws2020Schema.JWK_PUBLIC_KEY; - -/** - * Adapter that converts between {@link LdObject} and {@link VerificationMethod} - */ -class JwkAdapter implements LdValueAdapter { - - @Override - public VerificationMethod read(LdObject value) { - URI id = value.value(LdTerm.ID); - URI type = value.value(LdTerm.TYPE); - URI controller = value.value(DataIntegrity.CONTROLLER); - var keyProperty = getKeyProperty(value); - var jwk = CryptoConverter.create(keyProperty); - return new JwkMethod(id, type, controller, jwk); - } - - - @Override - public LdObject write(VerificationMethod method) { - var result = new HashMap(); - Objects.requireNonNull(method, "VerificationMethod cannot be null!"); - - if (method.id() != null) { - result.put(LdTerm.ID.uri(), method.id()); - } - if (method.type() != null) { - result.put(LdTerm.TYPE.uri(), method.type()); - } - if (method.controller() != null) { - result.put(DataIntegrity.CONTROLLER.uri(), method.controller()); - } - - if (method instanceof JwkMethod ecKeyPair) { - if (ecKeyPair.keyPair() != null) { - result.put(JWK_PUBLIC_KEY.uri(), ecKeyPair.keyPair().toPublicJWK().toJSONObject()); - } - } - - return new LdObject(result); - } - - private Map getKeyProperty(LdObject value) { - if (value.contains(JWK_PRIVATE_KEY)) { - return value.value(JWK_PRIVATE_KEY); - } - return value.value(JWK_PUBLIC_KEY); - } - -} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020CryptoSuite.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020CryptoSuite.java index d2849f0062b..a732918d44c 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020CryptoSuite.java +++ b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020CryptoSuite.java @@ -14,13 +14,12 @@ package org.eclipse.edc.security.signature.jws2020; -import com.apicatalog.ld.schema.LdTerm; import com.apicatalog.ld.signature.CryptoSuite; import com.apicatalog.ld.signature.primitive.MessageDigest; import com.apicatalog.ld.signature.primitive.Urdna2015; class Jws2020CryptoSuite extends CryptoSuite { - Jws2020CryptoSuite(LdTerm id) { - super(id, new Urdna2015(), new MessageDigest("SHA-256"), new Jws2020SignatureProvider()); + Jws2020CryptoSuite() { + super(new Urdna2015(), new MessageDigest("SHA-256"), new Jws2020Signature()); } } diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020Proof.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020Proof.java new file mode 100644 index 00000000000..8c3e5f00438 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020Proof.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.security.signature.jws2020; + +import com.apicatalog.ld.DocumentError; +import com.apicatalog.ld.signature.CryptoSuite; +import com.apicatalog.ld.signature.VerificationError; +import com.apicatalog.ld.signature.VerificationMethod; +import com.apicatalog.ld.signature.key.VerificationKey; +import com.apicatalog.vc.method.MethodAdapter; +import com.apicatalog.vc.proof.Proof; +import com.apicatalog.vc.proof.ProofValue; +import com.apicatalog.vc.solid.SolidProofValue; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonStructure; + +import java.net.URI; +import java.time.Instant; +import java.util.Map; +import java.util.Objects; + +import static org.eclipse.edc.security.signature.jws2020.Jws2020SignatureSuite.PROOF_VALUE_TERM; + +/** + * Represents the {@code proof} object of a verifiable credential which is backed by a JsonWebKey2020, either embedded or linked. + */ +public class Jws2020Proof implements Proof, MethodAdapter { + private final CryptoSuite cryptoSuite; + private JsonObject expandedDocument; + private URI id; + private Instant created; + private URI purpose; + private ProofValue jws; + private VerificationMethod verificationMethod; + private Jwk2020KeyAdapter adapter; + + protected Jws2020Proof() { + cryptoSuite = new Jws2020CryptoSuite(); + } + + @Override + public VerificationMethod read(JsonObject jsonObject) throws DocumentError { + return adapter.read(jsonObject); + } + + @Override + public JsonObject write(VerificationMethod verificationMethod) { + return adapter.write(verificationMethod); + } + + @Override + public VerificationMethod method() { + return verificationMethod; + } + + @Override + public ProofValue signature() { + return jws; + } + + @Override + public URI id() { + return id; + } + + @Override + public URI previousProof() { + return null; + } + + @Override + public CryptoSuite cryptoSuite() { + return cryptoSuite; + } + + @Override + public void validate(Map map) throws DocumentError { + if (created == null) { + throw new DocumentError(DocumentError.ErrorType.Missing, "Created"); + } + if (verificationMethod == null) { + throw new DocumentError(DocumentError.ErrorType.Missing, "VerificationMethod"); + } + if (purpose == null) { + throw new DocumentError(DocumentError.ErrorType.Missing, "ProofPurpose"); + } + if (!purpose.equals(URI.create("https://w3id.org/security#assertionMethod"))) { + throw new DocumentError(DocumentError.ErrorType.Invalid, "ProofPurpose"); + } + if (jws == null || ((SolidProofValue) jws).toByteArray().length == 0) { + throw new DocumentError(DocumentError.ErrorType.Missing, "ProofValue"); + } + } + + @Override + public void verify(JsonStructure context, JsonObject data, VerificationKey method) throws VerificationError, DocumentError { + jws.verify(cryptoSuite, context, data, unsigned(), method.publicKey()); + } + + @Override + public MethodAdapter methodProcessor() { + return this; + } + + private JsonObject unsigned() { + return Json.createObjectBuilder(expandedDocument).remove(PROOF_VALUE_TERM.uri()).build(); + } + + public static final class Builder { + private final Jws2020Proof instance; + + private Builder() { + instance = new Jws2020Proof(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Jws2020Proof build() { + Objects.requireNonNull(instance.expandedDocument, "JsonDocument cannot be null"); + Objects.requireNonNull(instance.adapter, "Jwk2020KeyAdapter cannot be null"); + return instance; + } + + public Builder id(URI id) { + instance.id = id; + return this; + } + + public Builder document(JsonObject expandedDocument) { + instance.expandedDocument = expandedDocument; + return this; + } + + public Builder created(Instant instant) { + instance.created = instant; + return this; + } + + public Builder proofPurpose(URI purpose) { + instance.purpose = purpose; + return this; + } + + public Builder jws(ProofValue jws) { + instance.jws = jws; + return this; + } + + public Builder adapter(Jwk2020KeyAdapter adapter) { + instance.adapter = adapter; + return this; + } + + public Builder verificationMethod(VerificationMethod verificationMethod) { + instance.verificationMethod = verificationMethod; + return this; + } + } +} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020ProofDraft.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020ProofDraft.java new file mode 100644 index 00000000000..7ae9e410ee6 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020ProofDraft.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.security.signature.jws2020; + +import com.apicatalog.ld.node.LdNodeBuilder; +import com.apicatalog.ld.signature.CryptoSuite; +import com.apicatalog.ld.signature.VerificationMethod; +import com.apicatalog.vc.ModelVersion; +import com.apicatalog.vc.integrity.DataIntegrityVocab; +import com.apicatalog.vc.issuer.ProofDraft; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; + +import java.net.URI; +import java.time.Instant; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import static org.eclipse.edc.security.signature.jws2020.Jws2020SignatureSuite.PROOF_VALUE_TERM; + +/** + * {@link ProofDraft} objects are effectively proof objects without a {@code proofValue} property, or - as it is called here - a {@code jws} + */ +public class Jws2020ProofDraft extends ProofDraft { + + private final Instant created; + private final URI proofPurpose; + private ObjectMapper mapper; + + private Jws2020ProofDraft(CryptoSuite crypto, VerificationMethod method, Instant created, URI proofPurpose) { + super(crypto, method); + this.created = created; + this.proofPurpose = proofPurpose; + } + + public static JsonObject signed(JsonObject unsigned, JsonValue proofValue) { + return LdNodeBuilder.of(unsigned).set(PROOF_VALUE_TERM).value(proofValue).build(); + } + + @Override + public Collection context(ModelVersion model) { + return List.of(Jws2020SignatureSuite.CONTEXT); + + } + + @Override + public JsonObject unsigned() { + var builder = new LdNodeBuilder(); + super.unsigned(builder, new Jwk2020KeyAdapter(mapper)); + + builder.type(Jws2020SignatureSuite.ID); + builder.set(DataIntegrityVocab.PURPOSE).id(proofPurpose); + builder.set(DataIntegrityVocab.CREATED).xsdDateTime(created != null ? created : Instant.now()); + + return builder.build(); + } + + public static final class Builder { + private Instant created; + private URI proofPurpose; + private VerificationMethod method; + private URI id; + private ObjectMapper mapper; + + private Builder() { + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder created(Instant created) { + this.created = created; + return this; + } + + public Builder verificationMethod(VerificationMethod verificationMethod) { + this.method = verificationMethod; + return this; + } + + public Builder proofPurpose(URI proofPurpose) { + this.proofPurpose = proofPurpose; + return this; + } + + public Builder method(VerificationMethod method) { + this.method = method; + return this; + } + + public Builder id(URI id) { + this.id = id; + return this; + } + + public Builder mapper(ObjectMapper mapper) { + this.mapper = mapper; + return this; + } + + public Jws2020ProofDraft build() { + Objects.requireNonNull(mapper, "mapper is required"); + var draft = new Jws2020ProofDraft(new Jws2020CryptoSuite(), method, created, proofPurpose); + draft.id = this.id; + draft.mapper = mapper; + return draft; + } + } +} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020Schema.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020Schema.java deleted file mode 100644 index c1caaa3bda0..00000000000 --- a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020Schema.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation - * - */ - -package org.eclipse.edc.security.signature.jws2020; - -import com.apicatalog.ld.schema.LdProperty; -import com.apicatalog.ld.schema.LdSchema; -import com.apicatalog.ld.schema.LdTerm; -import com.apicatalog.vc.VcTag; -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.time.Instant; - -import static com.apicatalog.ld.schema.LdSchema.id; -import static com.apicatalog.ld.schema.LdSchema.link; -import static com.apicatalog.ld.schema.LdSchema.object; -import static com.apicatalog.ld.schema.LdSchema.property; -import static com.apicatalog.ld.schema.LdSchema.string; -import static com.apicatalog.ld.schema.LdSchema.type; -import static com.apicatalog.ld.schema.LdSchema.xsdDateTime; -import static com.apicatalog.vc.VcSchema.proof; -import static com.apicatalog.vc.VcSchema.verificationMethod; -import static com.apicatalog.vc.VcVocab.SECURITY_VOCAB; -import static com.apicatalog.vc.integrity.DataIntegrity.CHALLENGE; -import static com.apicatalog.vc.integrity.DataIntegrity.CREATED; -import static com.apicatalog.vc.integrity.DataIntegrity.DOMAIN; -import static com.apicatalog.vc.integrity.DataIntegrity.PURPOSE; -import static com.apicatalog.vc.integrity.DataIntegrity.VERIFICATION_METHOD; - -/** - * Internal class that encapsulates all JSON schemas that are relevant for JWS2020, such as the structure of the verification method - */ -class Jws2020Schema { - public static final LdTerm JSON_WEB_KEY_TYPE = LdTerm.create("JsonWebKey2020", SECURITY_VOCAB); - public static final LdTerm JSON_WEB_SIGNATURE_TYPE = LdTerm.create("JsonWebSignature2020", SECURITY_VOCAB); - public static final LdTerm JWS = LdTerm.create("jws", SECURITY_VOCAB); - public static final LdTerm CONTROLLER = LdTerm.create("controller", SECURITY_VOCAB); - public static final LdTerm JWK_PRIVATE_KEY = LdTerm.create("privateKeyJwk", SECURITY_VOCAB); - public static final LdTerm JWK_PUBLIC_KEY = LdTerm.create("publicKeyJwk", SECURITY_VOCAB); - - - /** - * Creates an {@link LdSchema} for the verification method object of JWS2020, which looks roughly like this: - *
-     *     "verificationMethod": [
-     *       {
-     *         "id": "#ovsDKYBjFemIy8DVhc-w2LSi8CvXMw2AYDzHj04yxkc",
-     *         "type": "JsonWebKey2020",
-     *         "controller": "https://example.com/issuer/123",
-     *         "publicKeyJwk": {
-     *           "kty": "OKP",
-     *           "crv": "Ed25519",
-     *           "x": "CV-aGlld3nVdgnhoZK0D36Wk-9aIMlZjZOK2XhPMnkQ"
-     *         }
-     *       }
-     *     ],
-     * 
- * - * @param mapper The object mapper that is used to deserialize the {@code publicKeyJwk} part. - * @return The {@link LdSchema} that represents the above structure. Never null. - */ - public static LdSchema create(ObjectMapper mapper) { - return proof( - type(JSON_WEB_SIGNATURE_TYPE).required(), - property(CREATED, xsdDateTime()) - .test(created -> Instant.now().isAfter(created)) - .optional(), - property(CONTROLLER, link()), - property(PURPOSE, link()).required().test(uri -> uri.toString().equals("https://w3id.org/security#assertionMethod")), - verificationMethod(VERIFICATION_METHOD, getVerificationMethod(mapper).map(new JwkAdapter())).required(), - property(DOMAIN, string()) - .test((domain, params) -> !params.containsKey(DOMAIN.name()) || params.get(DOMAIN.name()).equals(domain)), - property(CHALLENGE, string()), - property(JWS, new ByteArrayAdapter(), VcTag.ProofValue.name()) - ); - } - - private static LdSchema getVerificationMethod(ObjectMapper mapper) { - return jsonWebKeySchema(mapper); - } - - private static LdSchema jsonWebKeySchema(ObjectMapper mapper) { - return new LdSchema( - object( - id().required(), - type(JSON_WEB_KEY_TYPE), - property(CONTROLLER, link()), - jwkPublicKey(mapper) - )); - } - - private static LdProperty jwkPublicKey(ObjectMapper mapper) { - return property(JWK_PUBLIC_KEY, new JsonAdapter(mapper)); - } - - private static LdProperty jwkPrivateKey(ObjectMapper mapper) { - return property(JWK_PRIVATE_KEY, new JsonAdapter(mapper)); - } -} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020SignatureProvider.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020Signature.java similarity index 74% rename from extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020SignatureProvider.java rename to extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020Signature.java index bb0c2bb87bb..d7d5cb2271f 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020SignatureProvider.java +++ b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020Signature.java @@ -24,18 +24,16 @@ import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.JWSObject; import com.nimbusds.jose.Payload; -import com.nimbusds.jose.jwk.Curve; -import com.nimbusds.jose.jwk.ECKey; import com.nimbusds.jose.jwk.JWK; -import com.nimbusds.jose.jwk.OctetKeyPair; -import com.nimbusds.jose.jwk.RSAKey; import org.eclipse.edc.security.token.jwt.CryptoConverter; -import org.eclipse.edc.spi.EdcException; import java.text.ParseException; import java.util.Collections; -class Jws2020SignatureProvider implements SignatureAlgorithm { +/** + * Implements signing a VerifiableCredential with the JsonWebSignature2020 algorithm + */ +class Jws2020Signature implements SignatureAlgorithm { @Override public void verify(byte[] publicKey, byte[] signature, byte[] data) throws VerificationError { @@ -90,7 +88,7 @@ public byte[] sign(byte[] privateKey, byte[] data) throws SigningError { } @Override - public KeyPair keygen(int length) throws KeyGenError { + public KeyPair keygen() throws KeyGenError { return null; } @@ -98,20 +96,7 @@ public KeyPair keygen(int length) throws KeyGenError { * Attempt to determine the {@link JWSAlgorithm} from the curve that is being used in the ECKey pair */ private JWSAlgorithm from(JWK keyPair) { - if (keyPair instanceof ECKey eckey) { - var jwsAlgorithm = JWSAlgorithm.Family.EC.stream() - .filter(algo -> Curve.forJWSAlgorithm(algo).contains(eckey.getCurve())) - .findFirst(); - return jwsAlgorithm.orElseThrow(() -> new EdcException("Could not determine JWSAlgorithm for Curve " + eckey.getCurve())); - } else if (keyPair instanceof OctetKeyPair okp) { - var jwsAlgorithm = JWSAlgorithm.Family.ED.stream() - .filter(algo -> Curve.forJWSAlgorithm(algo).contains(okp.getCurve())) - .findFirst(); - return jwsAlgorithm.orElseThrow(() -> new EdcException("Could not determine JWSAlgorithm for Curve " + okp.getCurve())); - } else if (keyPair instanceof RSAKey) { - return JWSAlgorithm.RS512; - } - return null; + return CryptoConverter.getRecommendedAlgorithm(CryptoConverter.createSigner(keyPair)); } private JWK deserialize(byte[] privateKey) { diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020SignatureSuite.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020SignatureSuite.java new file mode 100644 index 00000000000..0946c0e7a61 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/Jws2020SignatureSuite.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.security.signature.jws2020; + +import com.apicatalog.jsonld.loader.DocumentLoader; +import com.apicatalog.ld.DocumentError; +import com.apicatalog.ld.Term; +import com.apicatalog.ld.node.LdNode; +import com.apicatalog.ld.signature.key.KeyPair; +import com.apicatalog.vc.integrity.DataIntegrityVocab; +import com.apicatalog.vc.issuer.Issuer; +import com.apicatalog.vc.proof.Proof; +import com.apicatalog.vc.proof.ProofValue; +import com.apicatalog.vc.solid.SolidProofValue; +import com.apicatalog.vc.suite.SignatureSuite; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.json.JsonObject; + +import static com.apicatalog.vc.VcVocab.SECURITY_VOCAB; + +/** + * {@link SignatureSuite} that provides cryptographic facilities and a key schema for Json Web Signature 2020. + */ +public final class Jws2020SignatureSuite implements SignatureSuite { + public static final String CONTEXT = "https://w3id.org/security/suites/jws-2020/v1"; + public static final String JWS2020_ID = "JsonWebSignature2020"; + public static final Term PROOF_VALUE_TERM = Term.create("jws", SECURITY_VOCAB); + public static final String ID = SECURITY_VOCAB + JWS2020_ID; + public final Jwk2020KeyAdapter methodAdapter; + + /** + * Creates a new {@link Jws2020SignatureSuite} using an object mapper. That mapper is needed because parts of the schema are plain JSON. + */ + public Jws2020SignatureSuite(ObjectMapper mapper) { + methodAdapter = new Jwk2020KeyAdapter(mapper); + } + + @Override + public boolean isSupported(String proofType, JsonObject jsonObject) { + return ID.equals(proofType) || // expanded form + JWS2020_ID.equals(proofType); //non-expanded form + } + + @Override + public Proof getProof(JsonObject document, DocumentLoader documentLoader) throws DocumentError { + if (document == null) { + throw new IllegalArgumentException("The 'document' parameter must not be null."); + } + + var node = LdNode.of(document); + + return Jws2020Proof.Builder.newInstance() + .id(node.id()) + .document(document) + .created(node.scalar(DataIntegrityVocab.CREATED).xsdDateTime()) + .proofPurpose(node.node(DataIntegrityVocab.PURPOSE).id()) + .jws(getProofValue(node.scalar(PROOF_VALUE_TERM).string())) + .adapter(methodAdapter) + .verificationMethod(node.node(DataIntegrityVocab.VERIFICATION_METHOD).map(methodAdapter)) + .build(); + } + + @Override + public Issuer createIssuer(KeyPair keyPair) { + return new JwsIssuer(this, keyPair); + } + + private ProofValue getProofValue(String proofValue) { + return proofValue != null ? new SolidProofValue(proofValue.getBytes()) : null; + } +} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsIssuer.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsIssuer.java new file mode 100644 index 00000000000..881e3b5f24b --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsIssuer.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.security.signature.jws2020; + +import com.apicatalog.jsonld.loader.DocumentLoader; +import com.apicatalog.ld.DocumentError; +import com.apicatalog.ld.signature.LinkedDataSignature; +import com.apicatalog.ld.signature.SigningError; +import com.apicatalog.ld.signature.key.KeyPair; +import com.apicatalog.multibase.Multibase; +import com.apicatalog.vc.ModelVersion; +import com.apicatalog.vc.VcVocab; +import com.apicatalog.vc.Verifiable; +import com.apicatalog.vc.issuer.AbstractIssuer; +import com.apicatalog.vc.issuer.ProofDraft; +import com.apicatalog.vc.processor.ExpandedVerifiable; +import com.apicatalog.vc.proof.EmbeddedProof; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +/** + * replacement for the {@link com.apicatalog.vc.solid.SolidIssuer}, which would add a hardcoded {@code proofValue}, but + * JsonWebSignature2020 needs a {@code jws} field. + */ +class JwsIssuer extends AbstractIssuer { + JwsIssuer(Jws2020SignatureSuite jws2020SignatureSuite, KeyPair keyPair) { + super(jws2020SignatureSuite, keyPair, Multibase.BASE_64_URL); + } + + @Override + protected ExpandedVerifiable sign(final ModelVersion version, final JsonArray context, final JsonObject expanded, + final ProofDraft draft, final DocumentLoader loader) throws SigningError, DocumentError { + + if (keyPair.privateKey() == null || keyPair.privateKey().length == 0) { + throw new IllegalArgumentException("The private key is not provided, is null or an empty array."); + } + + var object = expanded; + + var verifiable = Verifiable.of(version, object); + + // TODO do something with exceptions, unify + if (verifiable.isCredential() && verifiable.asCredential().isExpired()) { + throw new SigningError(SigningError.Code.Expired); + } + + verifiable.validate(); + + // add issuance date if missing + if (verifiable.isCredential() && + (verifiable.version() == null || ModelVersion.V11.equals(verifiable.version())) && + verifiable.asCredential().issuanceDate() == null) { + + var issuanceDate = Instant.now().truncatedTo(ChronoUnit.SECONDS); + + object = Json.createObjectBuilder(object) + .add(VcVocab.ISSUANCE_DATE.uri(), issuanceDate.toString()) + .build(); + } + + // remove proofs + var unsigned = EmbeddedProof.removeProofs(object); + + // signature + var signature = sign(context, unsigned, draft); + + var proofValue = Json.createValue(new String(signature)); + + // signed proof + var signedProof = Jws2020ProofDraft.signed(draft.unsigned(), proofValue); + + return new ExpandedVerifiable(EmbeddedProof.addProof(object, signedProof), context, loader); + } + + @Override + protected byte[] sign(JsonArray context, JsonObject document, ProofDraft draft) throws SigningError { + var unsignedDraft = draft.unsigned(); + + var ldSignature = new LinkedDataSignature(draft.cryptoSuite()); + + return ldSignature.sign(document, keyPair.privateKey(), unsignedDraft); + } +} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsSignature2020Suite.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsSignature2020Suite.java deleted file mode 100644 index cd2b2b9665b..00000000000 --- a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsSignature2020Suite.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation - * - */ - -package org.eclipse.edc.security.signature.jws2020; - -import com.apicatalog.ld.schema.LdSchema; -import com.apicatalog.ld.schema.LdTerm; -import com.apicatalog.ld.signature.CryptoSuite; -import com.apicatalog.ld.signature.SignatureSuite; -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.net.URI; - -/** - * {@link SignatureSuite} that provides cryptographic facilities and a key schema for Json Web Signature 2020. - */ -public final class JwsSignature2020Suite implements SignatureSuite { - static final URI CONTEXT = URI.create("https://w3id.org/security/suites/jws-2020/v1"); - private final Jws2020CryptoSuite cryptoSuite; - private final ObjectMapper mapper; - - /** - * Creates a new {@link JwsSignature2020Suite} using an object mapper. That mapper is needed because parts of the schema are plain JSON. - * - * @param mapper a JSON-aware {@link ObjectMapper} - * @see Jws2020Schema - */ - public JwsSignature2020Suite(ObjectMapper mapper) { - this.mapper = mapper; - cryptoSuite = new Jws2020CryptoSuite(LdTerm.ID); - } - - @Override - public LdTerm getId() { - return Jws2020Schema.JSON_WEB_SIGNATURE_TYPE; - } - - @Override - public URI getContext() { - return CONTEXT; - } - - @Override - public LdSchema getSchema() { - return Jws2020Schema.create(mapper); - } - - @Override - public CryptoSuite getCryptoSuite() { - return cryptoSuite; - } - - @Override - public JwsSignatureProofOptions createOptions() { - return new JwsSignatureProofOptions(this); - } -} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsSignatureProofOptions.java b/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsSignatureProofOptions.java deleted file mode 100644 index ed658b306ff..00000000000 --- a/extensions/common/crypto/lib/jws2020-lib/src/main/java/org/eclipse/edc/security/signature/jws2020/JwsSignatureProofOptions.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation - * - */ - -package org.eclipse.edc.security.signature.jws2020; - -import com.apicatalog.vc.integrity.DataIntegrityProofOptions; - -/** - * Proof options for Jws2020 - */ -public class JwsSignatureProofOptions extends DataIntegrityProofOptions { - /** - * Create a new proof options instance - * - * @param suite The {@link JwsSignature2020Suite} for which the options are created. - */ - public JwsSignatureProofOptions(JwsSignature2020Suite suite) { - super(suite); - } -} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/main/resources/jws2020.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/main/resources/jws2020.jsonld new file mode 100644 index 00000000000..30f74b118c5 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/main/resources/jws2020.jsonld @@ -0,0 +1,78 @@ +{ + "@context": { + "privateKeyJwk": { + "@id": "https://w3id.org/security#privateKeyJwk", + "@type": "@json" + }, + "JsonWebKey2020": { + "@id": "https://w3id.org/security#JsonWebKey2020", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "publicKeyJwk": { + "@id": "https://w3id.org/security#publicKeyJwk", + "@type": "@json" + } + } + }, + "JsonWebSignature2020": { + "@id": "https://w3id.org/security#JsonWebSignature2020", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "challenge": "https://w3id.org/security#challenge", + "created": { + "@id": "http://purl.org/dc/terms/created", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "domain": "https://w3id.org/security#domain", + "expires": { + "@id": "https://w3id.org/security#expiration", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "jws": "https://w3id.org/security#jws", + "nonce": "https://w3id.org/security#nonce", + "proofPurpose": { + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "assertionMethod": { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityInvocation": { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityDelegation": { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set" + }, + "keyAgreement": { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set" + } + } + }, + "verificationMethod": { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id" + } + } + } + } +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/IssuerTests.java b/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/IssuerTests.java index d57ace6046e..cd3593ad88e 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/IssuerTests.java +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/IssuerTests.java @@ -17,12 +17,15 @@ import com.apicatalog.jsonld.loader.SchemeRouter; import com.apicatalog.ld.DocumentError; import com.apicatalog.ld.signature.SigningError; -import com.apicatalog.vc.Vc; +import com.apicatalog.ld.signature.VerificationError; +import com.apicatalog.vc.verifier.Verifier; +import com.fasterxml.jackson.databind.ObjectMapper; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.jwk.Curve; -import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.KeyUse; import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; import com.nimbusds.jose.jwk.gen.OctetKeyPairGenerator; import jakarta.json.JsonObject; import jakarta.json.JsonString; @@ -43,15 +46,18 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; import static org.eclipse.edc.junit.testfixtures.TestUtils.getResourceFileContentAsString; import static org.eclipse.edc.security.signature.jws2020.TestFunctions.createKeyPair; import static org.eclipse.edc.security.signature.jws2020.TestFunctions.readResourceAsJson; class IssuerTests { - private final JwsSignature2020Suite jws2020suite = new JwsSignature2020Suite(JacksonJsonLd.createObjectMapper()); //used to load remote data from a local directory private final TestDocumentLoader loader = new TestDocumentLoader("https://org.eclipse.edc/", "jws2020/issuing/", SchemeRouter.defaultInstance()); + private final ObjectMapper objectMapper = JacksonJsonLd.createObjectMapper(); + + private final Jws2020SignatureSuite suite = new Jws2020SignatureSuite(objectMapper); @DisplayName("t0001: a simple credential to sign (EC Key)") @Test @@ -61,18 +67,21 @@ void signSimpleCredential_ecKey() throws SigningError, DocumentError { var verificationMethodUrl = "https://org.eclipse.edc/verification-method"; - var proofOptions = jws2020suite.createOptions() + var proofDraft = Jws2020ProofDraft.Builder.newInstance() + .mapper(objectMapper) .created(Instant.parse("2022-12-31T23:00:00Z")) - .verificationMethod(new JwkMethod(URI.create(verificationMethodUrl), null, null, null)) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); - - - var issuer = Vc.sign(vc, keypair, proofOptions).loader(loader); + .verificationMethod(new JsonWebKeyPair(URI.create(verificationMethodUrl), null, null, null)) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - // would throw an exception - var compacted = IssuerCompatibility.compact(issuer, "https://www.w3.org/ns/did/v1"); - var verificationMethod = compacted.getJsonObject("sec:proof").get("verificationMethod"); + var issuer = suite.createIssuer(keypair) + .loader(loader) + .sign(vc, proofDraft); + var compacted = issuer.compacted(); + var verificationMethod = compacted.getJsonObject("proof").get("verificationMethod"); + var proofValue = compacted.getJsonObject("proof").get("jws"); + assertThat(proofValue).describedAs("Expected a JWS String!").isInstanceOf(JsonString.class); assertThat(verificationMethod).describedAs("Expected a String!").isInstanceOf(JsonString.class); assertThat(((JsonString) verificationMethod).getString()).isEqualTo(verificationMethodUrl); } @@ -97,18 +106,21 @@ void signSimpleCredential_rsaKey(int keysize) throws SigningError, DocumentError var verificationMethodUrl = "https://org.eclipse.edc/verification-method"; - var proofOptions = jws2020suite.createOptions() + var proofDraft = Jws2020ProofDraft.Builder.newInstance() + .mapper(objectMapper) .created(Instant.parse("2022-12-31T23:00:00Z")) - .verificationMethod(new JwkMethod(URI.create(verificationMethodUrl), null, null, null)) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); - - - var issuer = Vc.sign(vc, keypair, proofOptions).loader(loader); + .verificationMethod(new JsonWebKeyPair(URI.create(verificationMethodUrl), null, null, null)) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - // would throw an exception - var compacted = IssuerCompatibility.compact(issuer, "https://www.w3.org/ns/did/v1"); - var verificationMethod = compacted.getJsonObject("sec:proof").get("verificationMethod"); + var issuer = suite.createIssuer(keypair) + .loader(loader) + .sign(vc, proofDraft); + var compacted = issuer.compacted(); + var verificationMethod = compacted.getJsonObject("proof").get("verificationMethod"); + var proofValue = compacted.getJsonObject("proof").get("jws"); + assertThat(proofValue).describedAs("Expected a JWS String!").isInstanceOf(JsonString.class); assertThat(verificationMethod).describedAs("Expected a String!").isInstanceOf(JsonString.class); assertThat(((JsonString) verificationMethod).getString()).isEqualTo(verificationMethodUrl); } @@ -123,18 +135,21 @@ void signSimpleCredential_octetKeyPair() throws SigningError, DocumentError, JOS var verificationMethodUrl = "https://org.eclipse.edc/verification-method"; - var proofOptions = jws2020suite.createOptions() + var proofDraft = Jws2020ProofDraft.Builder.newInstance() + .mapper(objectMapper) .created(Instant.parse("2022-12-31T23:00:00Z")) - .verificationMethod(new JwkMethod(URI.create(verificationMethodUrl), null, null, null)) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); - - - var issuer = Vc.sign(vc, keypair, proofOptions).loader(loader); + .verificationMethod(new JsonWebKeyPair(URI.create(verificationMethodUrl), null, null, null)) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - // would throw an exception - var compacted = IssuerCompatibility.compact(issuer, "https://www.w3.org/ns/did/v1"); - var verificationMethod = compacted.getJsonObject("sec:proof").get("verificationMethod"); + var issuer = suite.createIssuer(keypair) + .loader(loader) + .sign(vc, proofDraft); + var compacted = issuer.compacted(); + var verificationMethod = compacted.getJsonObject("proof").get("verificationMethod"); + var proofValue = compacted.getJsonObject("proof").get("jws"); + assertThat(proofValue).describedAs("Expected a JWS String!").isInstanceOf(JsonString.class); assertThat(verificationMethod).describedAs("Expected a String!").isInstanceOf(JsonString.class); assertThat(((JsonString) verificationMethod).getString()).isEqualTo(verificationMethodUrl); } @@ -151,20 +166,23 @@ void signEmbeddedVerificationMethod() throws SigningError, DocumentError { var vc = readResourceAsJson("jws2020/issuing/0001_vc.json"); var keypair = createKeyPair(CryptoConverter.create(getResourceFileContentAsString("jws2020/issuing/private-key.json"))); - var proofOptions = jws2020suite.createOptions() + var proofDraft = Jws2020ProofDraft.Builder.newInstance() + .mapper(objectMapper) .created(Instant.parse("2022-12-31T23:00:00Z")) .verificationMethod(keypair) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); - + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - var issuer = Vc.sign(vc, keypair, proofOptions).loader(loader); + var issuer = suite.createIssuer(keypair) + .loader(loader) + .sign(vc, proofDraft); - // would throw an exception - var compacted = IssuerCompatibility.compact(issuer, "https://www.w3.org/ns/did/v1"); - var verificationMethod = compacted.getJsonObject("sec:proof").get("verificationMethod"); + var compacted = issuer.compacted(); + var verificationMethod = compacted.getJsonObject("proof").get("verificationMethod"); assertThat(verificationMethod).describedAs("Expected an Object!").isInstanceOf(JsonObject.class); assertThat(verificationMethod.asJsonObject().get("publicKeyJwk")) + .describedAs("JWK cannot be empty!") .isInstanceOf(JsonObject.class) .satisfies(jv -> { assertThat(jv.asJsonObject().get("x")).isNotNull(); @@ -177,7 +195,7 @@ void signEmbeddedVerificationMethod() throws SigningError, DocumentError { @Test void signVerificationDidKey() throws SigningError, DocumentError { var vc = readResourceAsJson("jws2020/issuing/0001_vc.json"); - var eckey = (ECKey) CryptoConverter.create(""" + var eckey = (JWK) CryptoConverter.create(""" { "kty": "EC", "d": "UEUJVbKZC3vR-y65gXx8NZVnE0QD5xe6qOk4eiObj-qVOg5zqt9zc0d6fdu4mUuu", @@ -193,18 +211,20 @@ void signVerificationDidKey() throws SigningError, DocumentError { // check https://w3c-ccg.github.io/did-method-key/#create for details var didKey = "did:key:zC2zU1wUHhYYX4CDwNwky9f5jtSvp5aQy5aNRQMHEdpK5xkJMy6TcMbWBP3scHbR6hhidR3RRjfAA7cuLxjydXgEiZUzRzguozYFeR3G6SzjAwswJ6hXKBWhFEHm2L6Rd6GRAw8r3kyPovxvcabdMF2gBy5TAioY1mVYFeT6"; - var proofOptions = jws2020suite.createOptions() - .created(Instant.parse("2022-12-31T23:00:00Z")) - .verificationMethod(new JwkMethod(URI.create(didKey), null, null, null)) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); - - var issuer = Vc.sign(vc, keypair, proofOptions).loader(loader); + var proofDraft = Jws2020ProofDraft.Builder.newInstance() + .mapper(objectMapper) + .created(Instant.parse("2022-12-31T23:00:00Z")) + .verificationMethod(new JsonWebKeyPair(URI.create(didKey), null, null, null)) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - // would throw an exception - var compacted = IssuerCompatibility.compact(issuer, "https://www.w3.org/ns/did/v1"); - var verificationMethod = compacted.getJsonObject("sec:proof").get("verificationMethod"); + var issuer = suite.createIssuer(keypair) + .loader(loader) + .sign(vc, proofDraft); + var compacted = issuer.compacted(); + var verificationMethod = compacted.getJsonObject("proof").get("verificationMethod"); assertThat(verificationMethod).describedAs("Expected a String!").isInstanceOf(JsonString.class); assertThat(((JsonString) verificationMethod).getString()).isEqualTo(didKey); @@ -219,20 +239,43 @@ void signCompactedPresentation() throws SigningError, DocumentError { var verificationMethodUrl = "https://org.eclipse.edc/verification-method"; - var proofOptions = jws2020suite.createOptions() + var proofDraft = Jws2020ProofDraft.Builder.newInstance() + .mapper(objectMapper) .created(Instant.parse("2022-12-31T23:00:00Z")) - .verificationMethod(new JwkMethod(URI.create(verificationMethodUrl), null, null, null)) - .purpose(URI.create("https://w3id.org/security#assertionMethod")); - - - var issuer = Vc.sign(vp, keypair, proofOptions).loader(loader); + .verificationMethod(new JsonWebKeyPair(URI.create(verificationMethodUrl), null, null, null)) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); - // would throw an exception - var compacted = IssuerCompatibility.compact(issuer, "https://www.w3.org/ns/did/v1"); - var verificationMethod = compacted.getJsonObject("sec:proof").get("verificationMethod"); + var issuer = suite.createIssuer(keypair) + .loader(loader) + .sign(vp, proofDraft); + var compacted = issuer.compacted(); + var verificationMethod = compacted.getJsonObject("proof").get("verificationMethod"); assertThat(verificationMethod).describedAs("Expected a String!").isInstanceOf(JsonString.class); assertThat(((JsonString) verificationMethod).getString()).isEqualTo(verificationMethodUrl); } + @Test + void signAndVerify() throws JOSEException, SigningError, DocumentError, VerificationError { + var vc = readResourceAsJson("jws2020/issuing/0001_vc.json"); + + var ecKey = new ECKeyGenerator(Curve.P_256).keyID("test-foo").generate(); + var keypair = createKeyPair(ecKey); + + + var proofDraft = Jws2020ProofDraft.Builder.newInstance() + .mapper(objectMapper) + .created(Instant.now()) + .verificationMethod(keypair) + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); + var signedCredential = suite.createIssuer(keypair) + .loader(loader) + .sign(vc, proofDraft) + .compacted(); + + //verify + assertThatNoException().isThrownBy(() -> Verifier.with(suite).loader(loader).verify(signedCredential).validate()); + } } diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/JsonAdapterTest.java b/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/JsonAdapterTest.java deleted file mode 100644 index 3d99faf3c70..00000000000 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/JsonAdapterTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation - * - */ - -package org.eclipse.edc.security.signature.jws2020; - -import jakarta.json.Json; -import jakarta.json.JsonObject; -import jakarta.json.JsonString; -import jakarta.json.JsonValue; -import org.eclipse.edc.jsonld.util.JacksonJsonLd; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; - -class JsonAdapterTest { - - private final JsonAdapter adapter = new JsonAdapter(JacksonJsonLd.createObjectMapper()); - - @Test - void read() { - var jo = Json.createValue("foobar"); - var result = adapter.read(jo); - assertThat(result).isEqualTo("foobar"); - } - - @Test - void read_jsonObjectWithValue() { - var jo = Json.createObjectBuilder() - .add("@type", "test-type") - .add("@value", "test-value") - .build(); - var result = adapter.read(jo); - assertThat(result).isEqualTo("test-value"); - } - - @Test - void write() { - var obj = "test-string"; - var result = adapter.write(obj); - assertThat(result).isInstanceOf(JsonString.class); - } - - @Test - void write_map() { - var map = Map.of("key1", "value1", "key2", "value2"); - var result = adapter.write(map); - assertThat(result).isInstanceOf(JsonObject.class).extracting(JsonValue::asJsonObject).matches(jo -> jo.size() == 2); - } -} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/TestFunctions.java b/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/TestFunctions.java new file mode 100644 index 00000000000..ed42ebaa33e --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/TestFunctions.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.security.signature.jws2020; + +import com.apicatalog.ld.signature.VerificationMethod; +import com.apicatalog.ld.signature.key.KeyPair; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.jwk.JWK; +import jakarta.json.JsonObject; +import org.eclipse.edc.jsonld.util.JacksonJsonLd; + +import java.io.IOException; +import java.net.URI; +import java.util.UUID; + +import static org.eclipse.edc.junit.testfixtures.TestUtils.getResourceFileContentAsString; + +public class TestFunctions { + + private static final ObjectMapper MAPPER = JacksonJsonLd.createObjectMapper(); + + public static JsonObject readResourceAsJson(String name) { + try { + return MAPPER.readValue(getResourceFileContentAsString(name), JsonObject.class); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static VerificationMethod createKeyPair(JWK jwk, String id) { + var type = URI.create("https://w3id.org/security#JsonWebKey2020"); + return new JsonWebKeyPair(URI.create(id), type, null, jwk); + } + + public static KeyPair createKeyPair(JWK jwk) { + var id = URI.create("https://org.eclipse.edc/keys/" + UUID.randomUUID()); + var type = URI.create("https://w3id.org/security#JsonWebKey2020"); + return new JsonWebKeyPair(id, type, URI.create("did:key:zQ3shP2mWsZYWgvgM11nenXRTx9L1yiJKmkf9dfX7NaMKb1pX"), jwk); + } +} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/VerifierTests.java b/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/VerifierTests.java index 4cd49737896..b512bbecd91 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/VerifierTests.java +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/java/org/eclipse/edc/security/signature/jws2020/VerifierTests.java @@ -17,7 +17,7 @@ import com.apicatalog.jsonld.loader.SchemeRouter; import com.apicatalog.ld.DocumentError; import com.apicatalog.ld.signature.VerificationError; -import com.apicatalog.vc.Vc; +import com.apicatalog.vc.verifier.Verifier; import org.eclipse.edc.jsonld.util.JacksonJsonLd; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; @@ -29,48 +29,47 @@ class VerifierTests { - private final JwsSignature2020Suite jws2020suite = new JwsSignature2020Suite(JacksonJsonLd.createObjectMapper()); - //used to load remote data from a local directory + private final Jws2020SignatureSuite jws2020suite = new Jws2020SignatureSuite(JacksonJsonLd.createObjectMapper()); private final TestDocumentLoader loader = new TestDocumentLoader("https://org.eclipse.edc/", "jws2020/verifying/", SchemeRouter.defaultInstance()); + private final Verifier verifier = Verifier.with(jws2020suite).loader(loader); @DisplayName("t0001: valid signed VC") @Test void verifyValidVc() throws VerificationError, DocumentError { var vc = readResourceAsJson("jws2020/verifying/0001_vc.json"); - var result = Vc.verify(vc, jws2020suite).loader(loader); - assertThatNoException().isThrownBy(result::isValid); + + var result = verifier.verify(vc); + assertThatNoException().isThrownBy(result::validate); } @DisplayName("t0002: forged credentials subject") @Test void verify_forgedSubject() throws VerificationError, DocumentError { var vc = readResourceAsJson("jws2020/verifying/0002_vc_forged.json"); - var result = Vc.verify(vc, jws2020suite).loader(loader); - assertThatThrownBy(result::isValid).isInstanceOf(VerificationError.class); + assertThatThrownBy(() -> verifier.verify(vc)).isInstanceOf(VerificationError.class); } @DisplayName("t0003: valid VC with embedded verification method") @Test void verifyVc_withEmbeddedVerificationMethod() throws VerificationError, DocumentError { var vc = readResourceAsJson("jws2020/verifying/0003_vc_embedded.json"); - var result = Vc.verify(vc, jws2020suite).loader(loader); - assertThatNoException().isThrownBy(result::isValid); + var result = verifier.verify(vc); + assertThatNoException().isThrownBy(result::validate); } @DisplayName("t0004: proof set of two valid proofs") @Test void verify_multipleValidProofs() throws VerificationError, DocumentError { var vc = readResourceAsJson("jws2020/verifying/0004_vc_two_valid_proofs.json"); - var result = Vc.verify(vc, jws2020suite).loader(loader); - assertThatNoException().isThrownBy(result::isValid); + var result = verifier.verify(vc); + assertThatNoException().isThrownBy(result::validate); } @DisplayName("t0005: proof set having one forged proof") @Test void verify_oneForgedProof() throws VerificationError, DocumentError { var vc = readResourceAsJson("jws2020/verifying/0005_vc_one_forged_proof.json"); - var result = Vc.verify(vc, jws2020suite).loader(loader); - assertThatThrownBy(result::isValid).isInstanceOf(VerificationError.class); + assertThatThrownBy(() -> verifier.verify(vc)).isInstanceOf(VerificationError.class); } /** @@ -85,8 +84,7 @@ void verify_oneForgedProof() throws VerificationError, DocumentError { @Test void verify_didKeyAsVerificationMethod() throws VerificationError, DocumentError { var vc = readResourceAsJson("jws2020/verifying/0006_vc_did_key.json"); - var result = Vc.verify(vc, jws2020suite).loader(loader); - assertThatThrownBy(result::isValid).isInstanceOf(UnsupportedOperationException.class) + assertThatThrownBy(() -> verifier.verify(vc)).isInstanceOf(UnsupportedOperationException.class) .hasMessage("Cannot deserialize public key, expected JWK format"); } @@ -94,15 +92,14 @@ void verify_didKeyAsVerificationMethod() throws VerificationError, DocumentError @Test void verify_validSignedVp() throws VerificationError, DocumentError { var vc = readResourceAsJson("jws2020/verifying/0007_vp_compacted.json"); - var result = Vc.verify(vc, jws2020suite).loader(loader); - assertThatNoException().isThrownBy(result::isValid); + var result = verifier.verify(vc); + assertThatNoException().isThrownBy(result::validate); } @DisplayName("t0008: forged signed VP") @Test void verify_forgedSignedVp() throws VerificationError, DocumentError { var vc = readResourceAsJson("jws2020/verifying/0007_vp_compacted_forged.json"); - var result = Vc.verify(vc, jws2020suite).loader(loader); - assertThatThrownBy(result::isValid).isInstanceOf(VerificationError.class); + assertThatThrownBy(() -> verifier.verify(vc)).isInstanceOf(VerificationError.class); } } diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/context.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/context.jsonld new file mode 100644 index 00000000000..86162b36b5c --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/context.jsonld @@ -0,0 +1,34 @@ +{ + "@context": [ + "https://w3id.org/security/suites/ed25519-2020/v1", + { + "@vocab": "https://github.com/filip26/iron-verifiable-credentials/tests/vocab#", + "mf": "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#", + "name": "mf:name", + "sequence": { + "@id": "mf:entries", + "@type": "@id" + }, + "input": { + "@id": "mf:action", + "@type": "@id" + }, + "context": { + "@type": "@id" + }, + "expect": { + "@id": "mf:result", + "@type": "@id" + }, + "expectErrorCode": { + "@id": "mf:result" + }, + "keyPair": { + "@type": "@id" + }, + "verificationMethod": { + "@type": "@id" + } + } + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer-manifest.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer-manifest.jsonld new file mode 100644 index 00000000000..4a28980b35e --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer-manifest.jsonld @@ -0,0 +1,117 @@ +{ + "@context": [ + "context.jsonld", + { + "@base": "issuer-manifest" + } + ], + "@id": "", + "@type": "mf:Manifest", + "name": "Verifiable Credentials Issuer Test Suite", + "sequence": [ + { + "@id": "#t0001", + "@type": [ + "PositiveEvaluationTest", + "IssuerTest" + ], + "name": "a simple credential to sign", + "input": "issuer/0001-in.jsonld", + "expect": "issuer/0001-out.jsonld", + "options": { + "keyPair": "issuer/0001-keys.json", + "verificationMethod": "verifier/0005-verification-key.json", + "created": "2022-05-28T17:02:05Z" + } + }, + { + "@id": "#t0002", + "@type": [ + "PositiveEvaluationTest", + "IssuerTest" + ], + "name": "compacted signed credential", + "input": "issuer/0002-in.jsonld", + "expect": "issuer/0002-out.jsonld", + "context": "issuer/0001-context.jsonld", + "options": { + "keyPair": "issuer/0001-keys.json", + "verificationMethod": "verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z" + } + }, + { + "@id": "#t0003", + "@type": [ + "PositiveEvaluationTest", + "IssuerTest" + ], + "name": "signed embedded verificationMethod", + "input": "issuer/0003-in.jsonld", + "expect": "issuer/0003-out.jsonld", + "context": "issuer/0001-context.jsonld", + "options": { + "keyPair": "issuer/0001-keys.json", + "verificationMethod": { + "@id": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "@type": "Ed25519VerificationKey2020", + "controller": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "publicKeyMultibase": "z6Mkska8oQD7QQQWxqa7L5ai4mH98HfAdSwomPFYKuqNyE2y" + }, + "created": "2022-06-04T20:33:01Z" + } + }, + { + "@id": "#t0004", + "@type": [ + "PositiveEvaluationTest", + "IssuerTest" + ], + "name": "a credential with DID key as a verification method", + "input": "issuer/0004-in.jsonld", + "expect": "issuer/0004-out.jsonld", + "context": "issuer/0001-context.jsonld", + "options": { + "keyPair": "issuer/0001-keys.json", + "verificationMethod": "did:key:z6Mkska8oQD7QQQWxqa7L5ai4mH98HfAdSwomPFYKuqNyE2y#z6Mkska8oQD7QQQWxqa7L5ai4mH98HfAdSwomPFYKuqNyE2y", + "created": "2022-05-28T17:02:05Z" + } + }, + { + "@id": "#t0005", + "@type": [ + "PositiveEvaluationTest", + "IssuerTest" + ], + "name": "proof set, co-sign", + "input": "issuer/0005-in.jsonld", + "expect": "issuer/0005-out.jsonld", + "context": "issuer/0001-context.jsonld", + "options": { + "keyPair": "issuer/0001-keys.json", + "verificationMethod": { + "@id": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "@type": "Ed25519VerificationKey2020", + "controller": "https://github.com/filip26/iron-verifiable-credentials/issuer/2" + }, + "created": "2022-06-07T21:06:15Z" + } + }, + { + "@id": "#t0006", + "@type": [ + "PositiveEvaluationTest", + "IssuerTest" + ], + "name": "compacted signed presentation", + "input": "issuer/0006-in.jsonld", + "expect": "issuer/0006-out.jsonld", + "context": "issuer/0001-context.jsonld", + "options": { + "keyPair": "issuer/0001-keys.json", + "verificationMethod": "verifier/0005-verification-key.json", + "created": "2022-06-09T21:49:41Z" + } + } + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-context.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-context.jsonld new file mode 100644 index 00000000000..10c5951c537 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-context.jsonld @@ -0,0 +1,6 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-in.jsonld new file mode 100644 index 00000000000..b60bb94a719 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-in.jsonld @@ -0,0 +1,12 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" + } +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-keys.json b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-keys.json new file mode 100644 index 00000000000..9cbf355b51e --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-keys.json @@ -0,0 +1,8 @@ +{ + "@context": "https://github.com/filip26/iron-verifiable-credentials/security-context.jsonld", + "id": "https://github.com/filip26/iron-verifiable-credentials/issuer/0001-keys.json", + "type": "Ed25519KeyPair2020", + "controller": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "publicKeyMultibase": "z6Mkska8oQD7QQQWxqa7L5ai4mH98HfAdSwomPFYKuqNyE2y", + "privateKeyMultibase": "z3u2RRiEW8idMgvP3kthwjWqPo9W8X4pvEp52toGwp8EjJvg" +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-out.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-out.jsonld new file mode 100644 index 00000000000..8e4647735be --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0001-out.jsonld @@ -0,0 +1,55 @@ +{ + "@id": "https://apicatalog/com/vc/test-credentials#0001", + "@type": [ + "https://www.w3.org/2018/credentials#VerifiableCredential" + ], + "https://www.w3.org/2018/credentials#issuer": [ + { + "@id": "https://github.com/filip26/iron-verifiable-credentials/issuer/1" + } + ], + "https://www.w3.org/2018/credentials#issuanceDate": [ + { + "@value": "2022-06-03T21:18:40Z", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + } + ], + "https://www.w3.org/2018/credentials#credentialSubject": [ + { + "@id": "did:example:ebfeb1f712ebc6f1c276e12ec21" + } + ], + "https://w3id.org/security#proof": [ + { + "@graph": [ + { + "@type": [ + "https://w3id.org/security#Ed25519Signature2020" + ], + "https://w3id.org/security#verificationMethod": [ + { + "@id": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json" + } + ], + "http://purl.org/dc/terms/created": [ + { + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + "@value": "2022-05-28T17:02:05Z" + } + ], + "https://w3id.org/security#proofPurpose": [ + { + "@id": "https://w3id.org/security#assertionMethod" + } + ], + "https://w3id.org/security#proofValue": [ + { + "@value": "z45tvtua7AVYDWHEhHciKBbxFGgVZQ8PKsW4pCkCaqQrAFf5BGDquvchPu7LRTdFxXYvQZsyxufCq7D6QXjdhFJgP", + "@type": "https://w3id.org/security#multibase" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0002-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0002-in.jsonld new file mode 100644 index 00000000000..b60bb94a719 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0002-in.jsonld @@ -0,0 +1,12 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" + } +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0002-out.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0002-out.jsonld new file mode 100644 index 00000000000..916dfdae498 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0002-out.jsonld @@ -0,0 +1,18 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ], + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z4VE14VeQx63Dufg2X3bCsdi8B2ggY9nZ9vk3MKRe9pvS66EytEx2awMLtNoSijTwyngpYheUr61SrPs9Cj6xaKWr" + } +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0003-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0003-in.jsonld new file mode 100644 index 00000000000..b60bb94a719 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0003-in.jsonld @@ -0,0 +1,12 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" + } +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0003-out.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0003-out.jsonld new file mode 100644 index 00000000000..adede57afea --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0003-out.jsonld @@ -0,0 +1,23 @@ +{ + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": { + "id": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "type": "Ed25519VerificationKey2020", + "controller": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "publicKeyMultibase": "z6Mkska8oQD7QQQWxqa7L5ai4mH98HfAdSwomPFYKuqNyE2y" + }, + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z2Ks3pYkqVViZyGhNyZD4wsQXCP8JzgctUVQXDMSAnApQsxn6dzvumbt4XE6twEcQ9mHasZWBxGzAo8r9ZEDDCBeA" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0004-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0004-in.jsonld new file mode 100644 index 00000000000..128dc42b869 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0004-in.jsonld @@ -0,0 +1,12 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "id": "https://apicatalog/com/vc/test-credentials#0023", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/23", + "issuanceDate": "2022-06-13T19:54:42Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" + } +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0004-out.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0004-out.jsonld new file mode 100644 index 00000000000..3c83da5bb22 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0004-out.jsonld @@ -0,0 +1,18 @@ +{ + "id": "https://apicatalog/com/vc/test-credentials#0023", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/23", + "issuanceDate": "2022-06-13T19:54:42Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": "did:key:z6Mkska8oQD7QQQWxqa7L5ai4mH98HfAdSwomPFYKuqNyE2y#z6Mkska8oQD7QQQWxqa7L5ai4mH98HfAdSwomPFYKuqNyE2y", + "created": "2022-05-28T17:02:05Z", + "proofPurpose": "assertionMethod", + "proofValue": "z4Ci2QGpPdJBbJ9T3mdT26BKhxkT1en6MDfZDX5FFQQUiv8kZzbWgui2ZM7LrgLGM1TKyDKrVwBMuAS5kFtV8WbcP" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0005-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0005-in.jsonld new file mode 100644 index 00000000000..355334f33f4 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0005-in.jsonld @@ -0,0 +1,18 @@ +{ + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z4VE14VeQx63Dufg2X3bCsdi8B2ggY9nZ9vk3MKRe9pvS66EytEx2awMLtNoSijTwyngpYheUr61SrPs9Cj6xaKWr" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0005-out.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0005-out.jsonld new file mode 100644 index 00000000000..b5d5dafcc74 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0005-out.jsonld @@ -0,0 +1,31 @@ +{ + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": [ + { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z4VE14VeQx63Dufg2X3bCsdi8B2ggY9nZ9vk3MKRe9pvS66EytEx2awMLtNoSijTwyngpYheUr61SrPs9Cj6xaKWr" + }, + { + "type": "Ed25519Signature2020", + "verificationMethod": { + "id": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "type": "Ed25519VerificationKey2020", + "controller": "https://github.com/filip26/iron-verifiable-credentials/issuer/2" + }, + "created": "2022-06-07T21:06:15Z", + "proofPurpose": "assertionMethod", + "proofValue": "z2KntzJQsQbHQ2qjkYu6w1F8Yi4PNvQHTENZCvdPXELjquQuKFJB5qupGDa1ZFtTRH2psXk8AGUxQqnzdUyiTLMoJ" + } + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ] +} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0006-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0006-in.jsonld new file mode 100644 index 00000000000..0de7c5ed36d --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0006-in.jsonld @@ -0,0 +1,28 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", + "type": "VerifiablePresentation", + "verifiableCredential": [ + { + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z4VE14VeQx63Dufg2X3bCsdi8B2ggY9nZ9vk3MKRe9pvS66EytEx2awMLtNoSijTwyngpYheUr61SrPs9Cj6xaKWr" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ] + } + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0006-out.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0006-out.jsonld new file mode 100644 index 00000000000..22d9d379f6d --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/issuer/0006-out.jsonld @@ -0,0 +1,29 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ], + "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", + "type": "VerifiablePresentation", + "verifiableCredential": { + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z4VE14VeQx63Dufg2X3bCsdi8B2ggY9nZ9vk3MKRe9pvS66EytEx2awMLtNoSijTwyngpYheUr61SrPs9Cj6xaKWr" + } + }, + "proof": { + "type": "Ed25519Signature2020", + "created": "2022-06-09T21:49:41Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "proofValue": "z3CmoWqYbLwUdbRS8HMzGGYsYbdtr2HCSUMvhwXhk1YQExeDd8wUktuFpPkggPPAUiRgFTFRs6cowGFh9GwTEm9E2" + } +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/manifest.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/manifest.jsonld new file mode 100644 index 00000000000..b0d7985c63c --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/manifest.jsonld @@ -0,0 +1,15 @@ +{ + "@context": [ + "context.jsonld", + { + "@base": "manifest" + } + ], + "@id": "", + "@type": "mf:Manifest", + "name": "Verifiable Credentials Test Suite", + "sequence": [ + "verifier-manifest.jsonld", + "issuer-manifest.jsonld" + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/security-context.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/security-context.jsonld new file mode 100644 index 00000000000..ee441002548 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/security-context.jsonld @@ -0,0 +1,31 @@ +{ + "@context": { + "id": "@id", + "type": "@type", + "@protected": true, + "Ed25519KeyPair2020": { + "@id": "https://w3id.org/security#Ed25519KeyPair2020", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "controller": { + "@id": "https://w3id.org/security#controller", + "@type": "@id" + }, + "revoked": { + "@id": "https://w3id.org/security#revoked", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "publicKeyMultibase": { + "@id": "https://w3id.org/security#publicKeyMultibase", + "@type": "https://w3id.org/security#multibase" + }, + "privateKeyMultibase": { + "@id": "https://w3id.org/security#privateKeyMultibase", + "@type": "https://w3id.org/security#multibase" + } + } + } + } +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier-manifest.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier-manifest.jsonld new file mode 100644 index 00000000000..e18b0896162 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier-manifest.jsonld @@ -0,0 +1,88 @@ +{ + "@context": [ + "context.jsonld", + { + "@base": "verifier-manifest" + } + ], + "@id": "", + "@type": "mf:Manifest", + "name": "Verifiable Credentials Verifier Test Suite", + "sequence": [ + { + "@id": "#t0001", + "@type": [ + "PositiveEvaluationTest", + "VeriferTest" + ], + "name": "valid signed VC", + "input": "verifier/0001-in.jsonld" + }, + { + "@id": "#t0002", + "@type": [ + "NegativeEvaluationTest", + "VeriferTest" + ], + "name": "forged credentials subject", + "input": "verifier/0002-in.jsonld", + "expectErrorCode": "InvalidSignature" + }, + { + "@id": "#t0003", + "@type": [ + "PositiveEvaluationTest", + "VeriferTest" + ], + "name": "valid VC with embedded verification method", + "input": "verifier/0003-in.jsonld" + }, + { + "@id": "#t0004", + "@type": [ + "PositiveEvaluationTest", + "VeriferTest" + ], + "name": "proof set of two valid proofs", + "input": "verifier/0004-in.jsonld" + }, + { + "@id": "#t0005", + "@type": [ + "NegativeEvaluationTest", + "VeriferTest" + ], + "name": "proof set having one forged proof", + "input": "verifier/0005-in.jsonld", + "expectErrorCode": "InvalidSignature" + }, + { + "@id": "#t0006", + "@type": [ + "PositiveEvaluationTest", + "VeriferTest" + ], + "name": "DID key as a verification method", + "input": "verifier/0006-in.jsonld" + }, + { + "@id": "#t0007", + "@type": [ + "PositiveEvaluationTest", + "VeriferTest" + ], + "name": "valid signed VP", + "input": "verifier/0007-in.jsonld" + }, + { + "@id": "#t0008", + "@type": [ + "NegativeEvaluationTest", + "VeriferTest" + ], + "name": "forged signed VP", + "input": "verifier/0008-in.jsonld", + "expectErrorCode": "InvalidSignature" + } + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0001-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0001-in.jsonld new file mode 100644 index 00000000000..355334f33f4 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0001-in.jsonld @@ -0,0 +1,18 @@ +{ + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z4VE14VeQx63Dufg2X3bCsdi8B2ggY9nZ9vk3MKRe9pvS66EytEx2awMLtNoSijTwyngpYheUr61SrPs9Cj6xaKWr" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0002-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0002-in.jsonld new file mode 100644 index 00000000000..41fb68ee6b3 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0002-in.jsonld @@ -0,0 +1,18 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ], + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z2KAAzisJbZCg1A9WqbRp5RAL16djJeA52vExfoFxVhnqKRQ6TDdZeAxtdxckpGNh5Jvxyw7RT6gv7uLqUgsx5US9" + } +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0003-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0003-in.jsonld new file mode 100644 index 00000000000..adede57afea --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0003-in.jsonld @@ -0,0 +1,23 @@ +{ + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": { + "id": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "type": "Ed25519VerificationKey2020", + "controller": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "publicKeyMultibase": "z6Mkska8oQD7QQQWxqa7L5ai4mH98HfAdSwomPFYKuqNyE2y" + }, + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z2Ks3pYkqVViZyGhNyZD4wsQXCP8JzgctUVQXDMSAnApQsxn6dzvumbt4XE6twEcQ9mHasZWBxGzAo8r9ZEDDCBeA" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0004-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0004-in.jsonld new file mode 100644 index 00000000000..b5d5dafcc74 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0004-in.jsonld @@ -0,0 +1,31 @@ +{ + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": [ + { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z4VE14VeQx63Dufg2X3bCsdi8B2ggY9nZ9vk3MKRe9pvS66EytEx2awMLtNoSijTwyngpYheUr61SrPs9Cj6xaKWr" + }, + { + "type": "Ed25519Signature2020", + "verificationMethod": { + "id": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "type": "Ed25519VerificationKey2020", + "controller": "https://github.com/filip26/iron-verifiable-credentials/issuer/2" + }, + "created": "2022-06-07T21:06:15Z", + "proofPurpose": "assertionMethod", + "proofValue": "z2KntzJQsQbHQ2qjkYu6w1F8Yi4PNvQHTENZCvdPXELjquQuKFJB5qupGDa1ZFtTRH2psXk8AGUxQqnzdUyiTLMoJ" + } + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ] +} diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0005-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0005-in.jsonld new file mode 100644 index 00000000000..104912fadf1 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0005-in.jsonld @@ -0,0 +1,31 @@ +{ + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": [ + { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z4XRCSmN1S86TcVfLfNQYiGhmAuEymVCAs9Nztm7UoXuBqyzGqaJd5giEsJHEoddprFXkpGHuhbrhvTMGwV6qVi4U" + }, + { + "type": "Ed25519Signature2020", + "verificationMethod": { + "id": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "type": "Ed25519VerificationKey2020", + "controller": "https://github.com/filip26/iron-verifiable-credentials/issuer/1" + }, + "created": "2022-06-07T21:06:15Z", + "proofPurpose": "assertionMethod", + "proofValue": "z3NdyGyM2RK1SUY23V1mbemVAnQTqcWpLUFkdmbXF4sC2RhhqL5JpeKyzjeLDeDo5Jr4vaaA7eDiebogM4MYczqnb" + } + ], + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0005-verification-key.json b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0005-verification-key.json new file mode 100644 index 00000000000..e34a05d21dc --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0005-verification-key.json @@ -0,0 +1,7 @@ +{ + "@context": "https://w3id.org/security/suites/ed25519-2020/v1", + "id": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "type": "Ed25519VerificationKey2020", + "controller": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "publicKeyMultibase": "z6Mkska8oQD7QQQWxqa7L5ai4mH98HfAdSwomPFYKuqNyE2y" +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0006-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0006-in.jsonld new file mode 100644 index 00000000000..baf7cc21978 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0006-in.jsonld @@ -0,0 +1,18 @@ +{ + "id": "https://apicatalog/com/vc/test-credentials#0023", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/23", + "issuanceDate": "2022-06-13T19:54:42Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": "did:key:z6Mkska8oQD7QQQWxqa7L5ai4mH98HfAdSwomPFYKuqNyE2y", + "created": "2022-05-28T17:02:05Z", + "proofPurpose": "assertionMethod", + "proofValue": "z3aTVwakBRYkJScYKeCcVh1NTogaj7UTA83B1TrEqKsBJwstWAvv9qekPXGvCZeptq7vTEk8VHBtK4EQLEWc7uYMC" + }, + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ] +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0007-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0007-in.jsonld new file mode 100644 index 00000000000..22d9d379f6d --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0007-in.jsonld @@ -0,0 +1,29 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ], + "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", + "type": "VerifiablePresentation", + "verifiableCredential": { + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "assertionMethod", + "proofValue": "z4VE14VeQx63Dufg2X3bCsdi8B2ggY9nZ9vk3MKRe9pvS66EytEx2awMLtNoSijTwyngpYheUr61SrPs9Cj6xaKWr" + } + }, + "proof": { + "type": "Ed25519Signature2020", + "created": "2022-06-09T21:49:41Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "proofValue": "z3CmoWqYbLwUdbRS8HMzGGYsYbdtr2HCSUMvhwXhk1YQExeDd8wUktuFpPkggPPAUiRgFTFRs6cowGFh9GwTEm9E2" + } +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0008-in.jsonld b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0008-in.jsonld new file mode 100644 index 00000000000..94fa75a7150 --- /dev/null +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/com/apicatalog/vc/verifier/0008-in.jsonld @@ -0,0 +1,29 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/ed25519-2020/v1" + ], + "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", + "type": "VerifiablePresentation", + "verifiableCredential": { + "id": "https://apicatalog/com/vc/test-credentials#0001", + "type": "VerifiableCredential", + "issuer": "https://github.com/filip26/iron-verifiable-credentials/issuer/1", + "issuanceDate": "2022-06-03T21:18:40Z", + "credentialSubject": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-04T20:33:01Z", + "proofPurpose": "authentication", + "proofValue": "z4XRCSmN1S86TcVfLfNQYiGhmAuEymVCAs9Nztm7UoXuBqyzGqaJd5giEsJHEoddprFXkpGHuhbrhvTMGwV6qVi4U" + } + }, + "proof": { + "type": "Ed25519Signature2020", + "verificationMethod": "https://github.com/filip26/iron-verifiable-credentials/verifier/0005-verification-key.json", + "created": "2022-06-09T21:49:41Z", + "proofPurpose": "assertionMethod", + "proofValue": "zF1iNdXJbwYwEnJMUGn3svD2uGHKkrdsfKskPaRkb32bgNG9xLLe7MwZvEGGydCpvdna7b1aXhY4dwrMWDyGzBQD" + } +} \ No newline at end of file diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/issuing/linkedCredentialData.json b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/issuing/linkedCredentialData.json index 5338c96c03d..09a6ea2b4dd 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/issuing/linkedCredentialData.json +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/issuing/linkedCredentialData.json @@ -22,7 +22,7 @@ "@id": "ex:status", "@type": "https://schema.org/Text" }, - "number": "http://schema.org/identifier" + "number": "ex:number" } }, "NameCredential": { diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0001_vc.json b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0001_vc.json index 207814df42a..a824a5cf476 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0001_vc.json +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0001_vc.json @@ -11,7 +11,7 @@ ], "issuer": "did:web:localhost:member0123456789", "expirationDate": "2024-12-31T23:00:00Z", - "sec:proof": { + "proof": { "type": "JsonWebSignature2020", "created": "2022-12-31T23:00:00Z", "proofPurpose": "assertionMethod", diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0002_vc_forged.json b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0002_vc_forged.json index 498a1d05538..b1a4a1ca135 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0002_vc_forged.json +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0002_vc_forged.json @@ -12,7 +12,7 @@ ], "issuer": "did:web:localhost:member0123456789", "expirationDate": "2024-12-31T23:00:00Z", - "sec:proof": { + "proof": { "type": "JsonWebSignature2020", "created": "2022-12-31T23:00:00Z", "proofPurpose": "assertionMethod", diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0003_vc_embedded.json b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0003_vc_embedded.json index c4f245ac6e1..0920b8e205a 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0003_vc_embedded.json +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0003_vc_embedded.json @@ -12,7 +12,7 @@ ], "issuer": "did:web:localhost:member0123456789", "expirationDate": "2024-12-31T23:00:00Z", - "sec:proof": { + "proof": { "type": "JsonWebSignature2020", "created": "2022-12-31T23:00:00Z", "proofPurpose": "assertionMethod", diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0004_vc_two_valid_proofs.json b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0004_vc_two_valid_proofs.json index 7e6ea7fe83f..0292e80f22f 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0004_vc_two_valid_proofs.json +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0004_vc_two_valid_proofs.json @@ -12,7 +12,7 @@ ], "issuer": "did:web:localhost:member0123456789", "expirationDate": "2024-12-31T23:00:00Z", - "sec:proof": [ + "proof": [ { "type": "JsonWebSignature2020", "created": "2022-12-31T23:00:00Z", diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0005_vc_one_forged_proof.json b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0005_vc_one_forged_proof.json index 47190f12773..1bda143083b 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0005_vc_one_forged_proof.json +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0005_vc_one_forged_proof.json @@ -12,7 +12,7 @@ ], "issuer": "did:web:localhost:member0123456789", "expirationDate": "2024-12-31T23:00:00Z", - "sec:proof": [ + "proof": [ { "type": "JsonWebSignature2020", "created": "2022-12-31T23:00:00Z", diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0006_vc_did_key.json b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0006_vc_did_key.json index f8e303deec2..e1331a5f7f3 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0006_vc_did_key.json +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0006_vc_did_key.json @@ -12,7 +12,7 @@ ], "issuer": "did:web:localhost:member0123456789", "expirationDate": "2024-12-31T23:00:00Z", - "sec:proof": { + "proof": { "type": "JsonWebSignature2020", "created": "2022-12-31T23:00:00Z", "proofPurpose": "assertionMethod", diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0007_vp_compacted.json b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0007_vp_compacted.json index 81b71a8239c..302b8c2e528 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0007_vp_compacted.json +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0007_vp_compacted.json @@ -15,7 +15,7 @@ ], "issuer": "did:web:localhost:member0123456789", "expirationDate": "2024-12-31T23:00:00Z", - "sec:proof": { + "proof": { "type": "JsonWebSignature2020", "created": "2022-12-31T23:00:00Z", "proofPurpose": "assertionMethod", @@ -23,12 +23,12 @@ "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzM4NCJ9..SwEkR4duA97jHy_WSVKIHLJqd8i2IidedmlMpUKyeV0YlPNz0pjPEKM9p7PqBb7oRIKG3-5qCxpzNhbsIEZZMzEMjWE1adckJ9SMiNr_G1wiAh3Op0cZHDgZBevIPElG" } }, - "sec:proof": { + "proof": { + "verificationMethod": "https://org.eclipse.edc/verification-method", "type": "JsonWebSignature2020", - "created": "2022-12-31T23:00:00Z", "proofPurpose": "assertionMethod", - "verificationMethod": "https://org.eclipse.edc/verification-method", - "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzM4NCJ9..kfTedAzvjguntyvun_puf_I46-I8YT--JI8XIxoRkKodU-oVK15bh_KXUlX4A8znjwGEz6MVSCH2jx21q13lXOOiuloGgfqRA31RY6r4ZBpzSw4dc9-dgsRlICJjUdmK" + "created": "2022-12-31T23:00:00Z", + "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzM4NCJ9..vo8x80Jm5h_qubTcDvrF3VXy6RRXcX0OdWQ8XLg_cXP_3-1-xXqT--dLMpQUfaPsOV-Y5Gu27gsukI73yKiBs63ebQ3Llc8gQB_BrZ5Ianfy4IcLh7dviW1KqtJ3e2y-" }, "@context": [ "https://www.w3.org/ns/did/v1", diff --git a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0007_vp_compacted_forged.json b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0007_vp_compacted_forged.json index d2572b4e84d..568310c86ca 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0007_vp_compacted_forged.json +++ b/extensions/common/crypto/lib/jws2020-lib/src/test/resources/jws2020/verifying/0007_vp_compacted_forged.json @@ -15,7 +15,7 @@ ], "issuer": "did:web:localhost:member0123456789", "expirationDate": "2024-12-31T23:00:00Z", - "sec:proof": { + "proof": { "type": "JsonWebSignature2020", "created": "2022-12-31T23:00:00Z", "proofPurpose": "assertionMethod", @@ -23,7 +23,7 @@ "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzM4NCJ9..dbi6LFkdeBeCz3sHaxRRFVJC2_rF8Z_oYqaoNOpYtzQh61WP78pK7nKT53WsE-7uiBUMamLA8vEGJpFQ3h4MXDi2OKh1YDpphS_pwyDkqYbsguMs2KYqPxe8t1OC2G1o" } }, - "sec:proof": { + "proof": { "type": "JsonWebSignature2020", "created": "2022-12-31T23:00:00Z", "proofPurpose": "assertionMethod", diff --git a/extensions/common/crypto/lib/jws2020-lib/src/testFixtures/java/org/eclipse/edc/security/signature/jws2020/TestFunctions.java b/extensions/common/crypto/lib/jws2020-lib/src/testFixtures/java/org/eclipse/edc/security/signature/jws2020/TestFunctions.java index 4884680af58..bd3fbbbce90 100644 --- a/extensions/common/crypto/lib/jws2020-lib/src/testFixtures/java/org/eclipse/edc/security/signature/jws2020/TestFunctions.java +++ b/extensions/common/crypto/lib/jws2020-lib/src/testFixtures/java/org/eclipse/edc/security/signature/jws2020/TestFunctions.java @@ -14,8 +14,8 @@ package org.eclipse.edc.security.signature.jws2020; +import com.apicatalog.ld.signature.VerificationMethod; import com.apicatalog.ld.signature.key.KeyPair; -import com.apicatalog.ld.signature.method.VerificationMethod; import com.fasterxml.jackson.databind.ObjectMapper; import com.nimbusds.jose.jwk.ECKey; import com.nimbusds.jose.jwk.JWK; @@ -35,7 +35,7 @@ public class TestFunctions { public static KeyPair createKeyPair(JWK jwk) { var id = URI.create("https://org.eclipse.edc/keys/" + UUID.randomUUID()); var type = URI.create("https://w3id.org/security#JsonWebKey2020"); - return new JwkMethod(id, type, null, jwk); + return new JsonWebKeyPair(id, type, null, jwk); } public static JsonObject readResourceAsJson(String name) { @@ -48,6 +48,6 @@ public static JsonObject readResourceAsJson(String name) { public static VerificationMethod createKeyPair(ECKey jwk, String id) { var type = URI.create("https://w3id.org/security#JsonWebKey2020"); - return new JwkMethod(URI.create(id), type, null, jwk); + return new JsonWebKeyPair(URI.create(id), type, null, jwk); } } diff --git a/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/IdentityAndTrustExtension.java b/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/IdentityAndTrustExtension.java index 3ce06a02b55..8a17b7eff34 100644 --- a/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/IdentityAndTrustExtension.java +++ b/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/IdentityAndTrustExtension.java @@ -38,7 +38,7 @@ import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provider; import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.security.signature.jws2020.JwsSignature2020Suite; +import org.eclipse.edc.security.signature.jws2020.Jws2020SignatureSuite; import org.eclipse.edc.spi.agent.ParticipantAgentService; import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.system.ServiceExtension; @@ -143,7 +143,7 @@ public void initialize(ServiceExtensionContext context) { rulesRegistry.addRule(JWT_VC_TOKEN_CONTEXT, new HasSubjectRule()); // TODO move in a separated extension? - signatureSuiteRegistry.register(JSON_2020_SIGNATURE_SUITE, new JwsSignature2020Suite(typeManager.getMapper(JSON_LD))); + signatureSuiteRegistry.register(JSON_2020_SIGNATURE_SUITE, new Jws2020SignatureSuite(typeManager.getMapper(JSON_LD))); try { jsonLd.registerCachedDocument(STATUSLIST_2021_URL, getClass().getClassLoader().getResource("statuslist2021.json").toURI()); @@ -161,7 +161,7 @@ public IdentityService createIdentityService(ServiceExtensionContext context) { var credentialValidationService = new VerifiableCredentialValidationServiceImpl(createPresentationVerifier(context), trustedIssuerRegistry, createRevocationListService(context), clock); - + return new IdentityAndTrustService(secureTokenService, getOwnDid(context), getCredentialServiceClient(context), validationAction, credentialServiceUrlResolver, claimTokenFunction, credentialValidationService); diff --git a/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/defaults/InMemorySignatureSuiteRegistry.java b/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/defaults/InMemorySignatureSuiteRegistry.java index bc770ee0c01..a1863d42b55 100644 --- a/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/defaults/InMemorySignatureSuiteRegistry.java +++ b/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/defaults/InMemorySignatureSuiteRegistry.java @@ -14,7 +14,7 @@ package org.eclipse.edc.iam.identitytrust.core.defaults; -import com.apicatalog.ld.signature.SignatureSuite; +import com.apicatalog.vc.suite.SignatureSuite; import org.eclipse.edc.iam.identitytrust.spi.verification.SignatureSuiteRegistry; import java.util.Collection; @@ -27,7 +27,6 @@ public class InMemorySignatureSuiteRegistry implements SignatureSuiteRegistry { @Override public void register(String w3cIdentifier, SignatureSuite suite) { registry.put(w3cIdentifier, suite); - registry.put(suite.getId().uri(), suite); } @Override diff --git a/extensions/common/iam/identity-trust/identity-trust-service/src/test/java/org/eclipse/edc/iam/identitytrust/service/verification/MultiFormatPresentationVerifierTest.java b/extensions/common/iam/identity-trust/identity-trust-service/src/test/java/org/eclipse/edc/iam/identitytrust/service/verification/MultiFormatPresentationVerifierTest.java index 79e16771337..d95ff3bf1ef 100644 --- a/extensions/common/iam/identity-trust/identity-trust-service/src/test/java/org/eclipse/edc/iam/identitytrust/service/verification/MultiFormatPresentationVerifierTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-service/src/test/java/org/eclipse/edc/iam/identitytrust/service/verification/MultiFormatPresentationVerifierTest.java @@ -15,7 +15,7 @@ package org.eclipse.edc.iam.identitytrust.service.verification; import com.apicatalog.jsonld.loader.SchemeRouter; -import com.apicatalog.vc.integrity.DataIntegrityProofOptions; +import com.apicatalog.vc.issuer.ProofDraft; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -29,7 +29,8 @@ import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.jsonld.util.JacksonJsonLd; import org.eclipse.edc.keys.spi.PublicKeyResolver; -import org.eclipse.edc.security.signature.jws2020.JwsSignature2020Suite; +import org.eclipse.edc.security.signature.jws2020.Jws2020ProofDraft; +import org.eclipse.edc.security.signature.jws2020.Jws2020SignatureSuite; import org.eclipse.edc.security.signature.jws2020.TestDocumentLoader; import org.eclipse.edc.security.signature.jws2020.TestFunctions; import org.eclipse.edc.spi.result.Result; @@ -49,6 +50,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.time.Instant; +import java.util.List; import java.util.Map; import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; @@ -77,7 +79,7 @@ class MultiFormatPresentationVerifierTest { public static final String INVALID_SIGNATURE = "Invalid signature"; private static final SignatureSuiteRegistry SIGNATURE_SUITE_REGISTRY = mock(); private static final ObjectMapper MAPPER = JacksonJsonLd.createObjectMapper(); - private static final JwsSignature2020Suite JWS_SIGNATURE_SUITE = new JwsSignature2020Suite(MAPPER); + private static final Jws2020SignatureSuite JWS_SIGNATURE_SUITE = new Jws2020SignatureSuite(MAPPER); private static ECKey vpSigningKey; private static ECKey vcSigningKey; private static TitaniumJsonLd jsonLd; @@ -89,6 +91,7 @@ class MultiFormatPresentationVerifierTest { @BeforeAll static void prepare() throws URISyntaxException, JOSEException { when(SIGNATURE_SUITE_REGISTRY.getForId(any())).thenReturn(JWS_SIGNATURE_SUITE); + when(SIGNATURE_SUITE_REGISTRY.getAllSuites()).thenReturn(List.of(JWS_SIGNATURE_SUITE)); jsonLd = new TitaniumJsonLd(mock()); jsonLd.registerCachedDocument("https://www.w3.org/ns/odrl.jsonld", Thread.currentThread().getContextClassLoader().getResource("odrl.jsonld").toURI()); jsonLd.registerCachedDocument("https://www.w3.org/ns/did/v1", Thread.currentThread().getContextClassLoader().getResource("jws2020.json").toURI()); @@ -119,12 +122,14 @@ void setup() { multiFormatVerifier = new MultiFormatPresentationVerifier(MY_OWN_DID, jwtPresentationVerifier, ldpVerifier); } - private DataIntegrityProofOptions generateEmbeddedProofOptions(ECKey vcKey, String proofPurpose) { - return JWS_SIGNATURE_SUITE - .createOptions() + private ProofDraft generateEmbeddedProofOptions(ECKey vcKey, String proofPurpose) { + + return Jws2020ProofDraft.Builder.newInstance() + .mapper(MAPPER) .created(Instant.now()) .verificationMethod(TestFunctions.createKeyPair(vcKey, proofPurpose)) // embedded proof - .purpose(URI.create("https://w3id.org/security#assertionMethod")); + .proofPurpose(URI.create("https://w3id.org/security#assertionMethod")) + .build(); } private Map asMap(String rawContent) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3fa3b7dcc45..e7be25946a4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ format.version = "1.1" [versions] apacheCommonsPool2 = "2.12.0" -apicatalog = "0.8.1" +apicatalog = "0.14.0" assertj = "3.25.3" atomikos = "6.0.0" awaitility = "4.2.1" diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/iam/identitytrust/spi/verification/SignatureSuiteRegistry.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/iam/identitytrust/spi/verification/SignatureSuiteRegistry.java index a6b232f6ae7..0b842d10ea7 100644 --- a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/iam/identitytrust/spi/verification/SignatureSuiteRegistry.java +++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/iam/identitytrust/spi/verification/SignatureSuiteRegistry.java @@ -14,7 +14,8 @@ package org.eclipse.edc.iam.identitytrust.spi.verification; -import com.apicatalog.ld.signature.SignatureSuite; + +import com.apicatalog.vc.suite.SignatureSuite; import java.util.Collection;