diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/Ed25519Signature2022LdVerifier.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/Ed25519Signature2022LdVerifier.java new file mode 100644 index 0000000..2f2fce0 --- /dev/null +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/Ed25519Signature2022LdVerifier.java @@ -0,0 +1,52 @@ +package org.oneedtech.inspect.vc.verification; + +import com.danubetech.keyformats.crypto.ByteVerifier; +import com.danubetech.keyformats.crypto.impl.Ed25519_EdDSA_PublicKeyVerifier; +import com.danubetech.keyformats.jose.JWSAlgorithm; +import info.weboftrust.ldsignatures.LdProof; +import info.weboftrust.ldsignatures.verifier.LdVerifier; +import io.ipfs.multibase.Multibase; + +import java.security.GeneralSecurityException; + +public class Ed25519Signature2022LdVerifier extends LdVerifier { + + public Ed25519Signature2022LdVerifier(ByteVerifier verifier) { + + super(SignatureSuites.SIGNATURE_SUITE_ED25519SIGNATURE2022, verifier, new URDNA2015Canonicalizer()); + } + + public Ed25519Signature2022LdVerifier(byte[] publicKey) { + + this(new Ed25519_EdDSA_PublicKeyVerifier(publicKey)); + } + + public Ed25519Signature2022LdVerifier() { + + this((ByteVerifier) null); + } + + public static boolean verify(byte[] signingInput, LdProof ldProof, ByteVerifier verifier) throws GeneralSecurityException { + + // verify + + String proofValue = ldProof.getProofValue(); + if (proofValue == null) throw new GeneralSecurityException("No 'proofValue' in proof."); + + boolean verify; + + byte[] bytes = Multibase.decode(proofValue); + verify = verifier.verify(signingInput, bytes, JWSAlgorithm.EdDSA); + + // done + + return verify; + } + + @Override + public boolean verify(byte[] signingInput, LdProof ldProof) throws GeneralSecurityException { + + return verify(signingInput, ldProof, this.getVerifier()); + } + +} diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/Ed25519Signature2022SignatureSuite.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/Ed25519Signature2022SignatureSuite.java new file mode 100644 index 0000000..33790e9 --- /dev/null +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/Ed25519Signature2022SignatureSuite.java @@ -0,0 +1,28 @@ +package org.oneedtech.inspect.vc.verification; + +import com.danubetech.keyformats.jose.JWSAlgorithm; +import com.danubetech.keyformats.jose.KeyTypeName; +import info.weboftrust.ldsignatures.suites.SignatureSuite; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class Ed25519Signature2022SignatureSuite extends SignatureSuite { + + Ed25519Signature2022SignatureSuite() { + + super( + "DataIntegrityProof", + URI.create("https://www.w3.org/TR/vc-di-eddsa"), + URI.create("https://w3id.org/security#URDNA2015"), + URI.create("http://w3id.org/digests#sha256"), + URI.create("http://w3id.org/security#ed25519"), + List.of(KeyTypeName.Ed25519), + Map.of(KeyTypeName.Ed25519, List.of(JWSAlgorithm.EdDSA)), + Arrays.asList(LDSecurityContexts.JSONLD_CONTEXT_W3ID_SUITES_ED25519_2022_V1, + info.weboftrust.ldsignatures.jsonld.LDSecurityContexts.JSONLD_CONTEXT_W3ID_SECURITY_V3)); + } + +} diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/Eddsa2022LdProof.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/Eddsa2022LdProof.java new file mode 100644 index 0000000..b163d3e --- /dev/null +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/Eddsa2022LdProof.java @@ -0,0 +1,49 @@ +package org.oneedtech.inspect.vc.verification; + +import java.net.URI; + +import com.apicatalog.jsonld.loader.DocumentLoader; + +import foundation.identity.jsonld.JsonLDObject; +import foundation.identity.jsonld.JsonLDUtils; +import info.weboftrust.ldsignatures.LdProof; + +public class Eddsa2022LdProof extends LdProof { + public static final URI[] DEFAULT_JSONLD_CONTEXTS = { LDSecurityContexts.JSONLD_CONTEXT_W3ID_SUITES_ED25519_2022_V1 }; + public static final DocumentLoader DEFAULT_DOCUMENT_LOADER = LDSecurityContexts.DOCUMENT_LOADER; + + public static Builder> builder() { + return new Builder(new Eddsa2022LdProof()); + } + + /* + * Factory methods + */ + + public static class Builder> extends LdProof.Builder { + + private boolean addCryptosuite = true; + + public Builder(LdProof jsonLdObject) { + super(jsonLdObject); + } + + @Override + public B base(JsonLDObject base) { + addCryptosuite = false; + return super.base(base); + } + + @Override + public LdProof build() { + super.build(); + + if (addCryptosuite) { + JsonLDUtils.jsonLdAdd(this.jsonLdObject, "cryptosuite", "eddsa-2022"); + } + + return (LdProof) this.jsonLdObject; + + } + } +} \ No newline at end of file diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/LDSecurityContexts.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/LDSecurityContexts.java new file mode 100644 index 0000000..f8652e1 --- /dev/null +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/LDSecurityContexts.java @@ -0,0 +1,43 @@ +package org.oneedtech.inspect.vc.verification; + +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import com.apicatalog.jsonld.JsonLdError; +import com.apicatalog.jsonld.document.JsonDocument; +import com.apicatalog.jsonld.http.media.MediaType; +import com.apicatalog.jsonld.loader.DocumentLoader; +import com.google.common.io.Resources; + +import foundation.identity.jsonld.ConfigurableDocumentLoader; + +public class LDSecurityContexts { + public static final URI JSONLD_CONTEXT_W3ID_SUITES_ED25519_2022_V1 = URI.create("https://w3id.org/security/data-integrity/v1"); + + public static final Map CONTEXTS; + public static final DocumentLoader DOCUMENT_LOADER; + + static { + + try { + + CONTEXTS = new HashMap<>(); + + CONTEXTS.putAll(info.weboftrust.ldsignatures.jsonld.LDSecurityContexts.CONTEXTS); + + CONTEXTS.put(JSONLD_CONTEXT_W3ID_SUITES_ED25519_2022_V1, + JsonDocument.of(MediaType.JSON_LD, Resources.getResource("contexts/data-integrity-v1.jsonld").openStream())); + + for (Map.Entry context : CONTEXTS.entrySet()) { + context.getValue().setDocumentUrl(context.getKey()); + } + } catch (JsonLdError | IOException ex) { + + throw new ExceptionInInitializerError(ex); + } + + DOCUMENT_LOADER = new ConfigurableDocumentLoader(CONTEXTS); + } +} diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/SignatureSuites.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/SignatureSuites.java new file mode 100644 index 0000000..4301065 --- /dev/null +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/SignatureSuites.java @@ -0,0 +1,5 @@ +package org.oneedtech.inspect.vc.verification; + +public class SignatureSuites { + public static final Ed25519Signature2022SignatureSuite SIGNATURE_SUITE_ED25519SIGNATURE2022 = new Ed25519Signature2022SignatureSuite(); +} diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/URDNA2015Canonicalizer.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/URDNA2015Canonicalizer.java new file mode 100644 index 0000000..0ed3a9f --- /dev/null +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/verification/URDNA2015Canonicalizer.java @@ -0,0 +1,52 @@ +package org.oneedtech.inspect.vc.verification; + +import foundation.identity.jsonld.JsonLDException; +import foundation.identity.jsonld.JsonLDObject; +import info.weboftrust.ldsignatures.LdProof; +import info.weboftrust.ldsignatures.canonicalizer.Canonicalizer; +import info.weboftrust.ldsignatures.util.SHAUtil; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.List; + +public class URDNA2015Canonicalizer extends Canonicalizer { + + public URDNA2015Canonicalizer() { + + super(List.of("urdna2015")); + } + + @Override + public byte[] canonicalize(LdProof ldProof, JsonLDObject jsonLdObject) throws IOException, GeneralSecurityException, JsonLDException { + + // construct the LD proof without proof values + + LdProof ldProofWithoutProofValues = Eddsa2022LdProof.builder() + .base(ldProof) + .defaultContexts(true) + .build(); + LdProof.removeLdProofValues(ldProofWithoutProofValues); + + // construct the LD object without proof + + JsonLDObject jsonLdObjectWithoutProof = JsonLDObject.builder() + .base(jsonLdObject) + .build(); + jsonLdObjectWithoutProof.setDocumentLoader(jsonLdObject.getDocumentLoader()); + LdProof.removeFromJsonLdObject(jsonLdObjectWithoutProof); + + // canonicalize the LD proof and LD object + + String canonicalizedLdProofWithoutProofValues = ldProofWithoutProofValues.normalize("urdna2015"); + String canonicalizedJsonLdObjectWithoutProof = jsonLdObjectWithoutProof.normalize("urdna2015"); + + // construct the canonicalization result + + byte[] canonicalizationResult = new byte[64]; + System.arraycopy(SHAUtil.sha256(canonicalizedLdProofWithoutProofValues), 0, canonicalizationResult, 0, 32); + System.arraycopy(SHAUtil.sha256(canonicalizedJsonLdObjectWithoutProof), 0, canonicalizationResult, 32, 32); + + return canonicalizationResult; + } +} \ No newline at end of file