Add support for remote key material
This commit is contained in:
parent
4eb82c819b
commit
84a7f82c5f
@ -2,6 +2,7 @@ package org.oneedtech.inspect.vc.probe;
|
|||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.oneedtech.inspect.core.probe.Probe;
|
import org.oneedtech.inspect.core.probe.Probe;
|
||||||
import org.oneedtech.inspect.core.probe.RunContext;
|
import org.oneedtech.inspect.core.probe.RunContext;
|
||||||
@ -9,6 +10,9 @@ import org.oneedtech.inspect.core.report.ReportItems;
|
|||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.Credential;
|
||||||
import org.oneedtech.inspect.vc.util.CachingDocumentLoader;
|
import org.oneedtech.inspect.vc.util.CachingDocumentLoader;
|
||||||
|
|
||||||
|
import com.apicatalog.jsonld.StringUtils;
|
||||||
|
import com.apicatalog.jsonld.document.Document;
|
||||||
|
import com.apicatalog.jsonld.loader.DocumentLoaderOptions;
|
||||||
import com.apicatalog.ld.DocumentError;
|
import com.apicatalog.ld.DocumentError;
|
||||||
import com.apicatalog.multibase.Multibase;
|
import com.apicatalog.multibase.Multibase;
|
||||||
import com.apicatalog.multicodec.Multicodec;
|
import com.apicatalog.multicodec.Multicodec;
|
||||||
@ -16,10 +20,14 @@ import com.apicatalog.multicodec.Multicodec.Codec;
|
|||||||
import com.apicatalog.vc.processor.StatusVerifier;
|
import com.apicatalog.vc.processor.StatusVerifier;
|
||||||
import com.danubetech.verifiablecredentials.VerifiableCredential;
|
import com.danubetech.verifiablecredentials.VerifiableCredential;
|
||||||
|
|
||||||
|
import foundation.identity.jsonld.ConfigurableDocumentLoader;
|
||||||
import info.weboftrust.ldsignatures.verifier.Ed25519Signature2020LdVerifier;
|
import info.weboftrust.ldsignatures.verifier.Ed25519Signature2020LdVerifier;
|
||||||
|
import jakarta.json.JsonObject;
|
||||||
|
import jakarta.json.JsonStructure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Probe that verifies a credential's embedded proof.
|
* A Probe that verifies a credential's embedded proof.
|
||||||
|
*
|
||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public class EmbeddedProofProbe extends Probe<Credential> {
|
public class EmbeddedProofProbe extends Probe<Credential> {
|
||||||
@ -28,15 +36,16 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
super(ID);
|
super(ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Using verifiable-credentials-java (https://github.com/danubetech/verifiable-credentials-java)
|
* Using verifiable-credentials-java
|
||||||
|
* (https://github.com/danubetech/verifiable-credentials-java)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
||||||
|
|
||||||
//TODO check that proof is Ed25519 - issue error if not ("type": "Ed25519Signature2020",
|
// TODO check that proof is Ed25519 - issue error if not ("type":
|
||||||
//TODO check value "proofPurpose": "assertionMethod", if not error
|
// "Ed25519Signature2020",
|
||||||
|
// TODO check value "proofPurpose": "assertionMethod", if not error
|
||||||
|
|
||||||
VerifiableCredential vc = VerifiableCredential.fromJson(new StringReader(crd.getJson().toString()));
|
VerifiableCredential vc = VerifiableCredential.fromJson(new StringReader(crd.getJson().toString()));
|
||||||
vc.setDocumentLoader(new CachingDocumentLoader());
|
vc.setDocumentLoader(new CachingDocumentLoader());
|
||||||
@ -47,7 +56,8 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
// Danubetech's Ed25519Signature2020LdVerifier expects the decoded public key
|
// Danubetech's Ed25519Signature2020LdVerifier expects the decoded public key
|
||||||
// from the Ed25519VerificationKey2020 (32 bytes).
|
// from the Ed25519VerificationKey2020 (32 bytes).
|
||||||
|
|
||||||
String publicKeyMultibase = "";
|
String publicKeyMultibase;
|
||||||
|
String controller;
|
||||||
|
|
||||||
// Formats accepted:
|
// Formats accepted:
|
||||||
//
|
//
|
||||||
@ -55,25 +65,51 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
// did:key:[publicKeyMultibase]
|
// did:key:[publicKeyMultibase]
|
||||||
// [publicKeyMultibase]
|
// [publicKeyMultibase]
|
||||||
|
|
||||||
// TODO fourth format that we don't support yet: a URL that returns a Ed25519VerificationKey2020
|
// TODO fourth format that we don't support yet: a URL that returns a
|
||||||
// if starts with http and does not have hashcode, try fetch and see if returns Ed25519VerificationKey2020
|
// Ed25519VerificationKey2020
|
||||||
|
// if starts with http and does not have hashcode, try fetch and see if returns
|
||||||
|
// Ed25519VerificationKey2020
|
||||||
// property is publicKeyMultibase
|
// property is publicKeyMultibase
|
||||||
|
|
||||||
publicKeyMultibase = method.toString();
|
publicKeyMultibase = method.toString();
|
||||||
|
|
||||||
if (method.getFragment() != null) {
|
if (method.getFragment() != null) {
|
||||||
publicKeyMultibase = method.getFragment();
|
publicKeyMultibase = method.getFragment();
|
||||||
} else {
|
} else {
|
||||||
if (method.getScheme().equals("did")) {
|
if (method.getScheme().equals("did")) {
|
||||||
if (method.getSchemeSpecificPart().startsWith("key:")) {
|
if (method.getSchemeSpecificPart().startsWith("key:")) {
|
||||||
publicKeyMultibase = method.getSchemeSpecificPart().substring(4);
|
publicKeyMultibase = method.getSchemeSpecificPart().substring(4);
|
||||||
} else {
|
} else {
|
||||||
return error("Unknown verification method: " + method, ctx);
|
return error("Unknown verification method: " + method, ctx);
|
||||||
}
|
}
|
||||||
} else if (method.getScheme().equals("http") || method.getScheme().equals("https")) {
|
} else if (method.getScheme().equals("http") || method.getScheme().equals("https")) {
|
||||||
return error("Cannot parse http verification key yet", ctx);
|
ConfigurableDocumentLoader documentLoader = new ConfigurableDocumentLoader();
|
||||||
}
|
documentLoader.setEnableHttp(true);
|
||||||
}
|
documentLoader.setEnableHttps(true);
|
||||||
|
|
||||||
|
// The verificationMethod URI must resolve to a Ed25519VerificationKey2020 as described here:
|
||||||
|
// https://w3c-ccg.github.io/di-eddsa-2020/#ed25519verificationkey2020
|
||||||
|
Document keyDocument = documentLoader.loadDocument(method, new DocumentLoaderOptions());
|
||||||
|
Optional<JsonStructure> keyStructure = keyDocument.getJsonContent();
|
||||||
|
if (keyStructure.isEmpty()) {
|
||||||
|
return error("Key document not found at " + method, ctx);
|
||||||
|
}
|
||||||
|
// First look for a plain key object
|
||||||
|
controller = keyStructure.get().asJsonObject().getString("controller");
|
||||||
|
if (StringUtils.isBlank(controller)) {
|
||||||
|
// Then look for a controller document (e.g. DID Document)
|
||||||
|
JsonObject keyVerificationMethod = keyStructure.get().asJsonObject()
|
||||||
|
.getJsonObject("verificationMethod");
|
||||||
|
if (keyVerificationMethod.isEmpty()) {
|
||||||
|
return error("Cannot parse key document from " + method, ctx);
|
||||||
|
}
|
||||||
|
controller = keyVerificationMethod.getString("controller");
|
||||||
|
publicKeyMultibase = keyVerificationMethod.getString("publicKeyMultibase");
|
||||||
|
} else {
|
||||||
|
publicKeyMultibase = keyStructure.get().asJsonObject().getString("publicKeyMultibase");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Decode the Multibase to Multicodec and check that it is an Ed25519 public key
|
// Decode the Multibase to Multicodec and check that it is an Ed25519 public key
|
||||||
// https://w3c-ccg.github.io/di-eddsa-2020/#ed25519verificationkey2020
|
// https://w3c-ccg.github.io/di-eddsa-2020/#ed25519verificationkey2020
|
||||||
@ -92,10 +128,11 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
|
|
||||||
Ed25519Signature2020LdVerifier verifier = new Ed25519Signature2020LdVerifier(publicKey);
|
Ed25519Signature2020LdVerifier verifier = new Ed25519Signature2020LdVerifier(publicKey);
|
||||||
|
|
||||||
//TODO find out whether we also should check that controller matches issuer ID:
|
// TODO find out whether we also should check that controller matches issuer ID:
|
||||||
// if [controller]#[publicKeyMultibase] format - check [controller] segment
|
// if [controller]#[publicKeyMultibase] format - check [controller] segment
|
||||||
// if did:key:[publicKeyMultibase] format: issuer ID must match the entire URI
|
// if did:key:[publicKeyMultibase] format: issuer ID must match the entire URI
|
||||||
// if [publicKeyMultibase] -- don't check issuer ID. Maybe we should warn about this syntax.
|
// if [publicKeyMultibase] -- don't check issuer ID. Maybe we should warn about
|
||||||
|
// this syntax.
|
||||||
|
|
||||||
try {
|
try {
|
||||||
boolean verify = verifier.verify(vc);
|
boolean verify = verifier.verify(vc);
|
||||||
@ -109,8 +146,6 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
return success(ctx);
|
return success(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: if using com.apicatalog Iron, we get a generic VC verifier that
|
* Note: if using com.apicatalog Iron, we get a generic VC verifier that
|
||||||
* will test other stuff than the Proof. So sometimes it may be that
|
* will test other stuff than the Proof. So sometimes it may be that
|
||||||
@ -120,36 +155,38 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
* (aka is not a jwt).
|
* (aka is not a jwt).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// /*
|
// /*
|
||||||
// * Using iron-verifiable-credentials (https://github.com/filip26/iron-verifiable-credentials)
|
// * Using iron-verifiable-credentials
|
||||||
// */
|
// (https://github.com/filip26/iron-verifiable-credentials)
|
||||||
// @Override
|
// */
|
||||||
// public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
// @Override
|
||||||
// JsonDocument jsonDoc = JsonDocument.of(new StringReader(crd.getJson().toString()));
|
// public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
||||||
// JsonObject json = jsonDoc.getJsonContent().get().asJsonObject();
|
// JsonDocument jsonDoc = JsonDocument.of(new
|
||||||
// try {
|
// StringReader(crd.getJson().toString()));
|
||||||
// Vc.verify(json)
|
// JsonObject json = jsonDoc.getJsonContent().get().asJsonObject();
|
||||||
// .loader(new CachingDocumentLoader())
|
// try {
|
||||||
// .useBundledContexts(false) //we control the cache in the loader
|
// Vc.verify(json)
|
||||||
// .statusVerifier(new IronNoopStatusVerifier())
|
// .loader(new CachingDocumentLoader())
|
||||||
// //.domain(...)
|
// .useBundledContexts(false) //we control the cache in the loader
|
||||||
// //.didResolver(...)
|
// .statusVerifier(new IronNoopStatusVerifier())
|
||||||
// .isValid();
|
// //.domain(...)
|
||||||
// } catch (DocumentError e) {
|
// //.didResolver(...)
|
||||||
// return error(e.getType() + " " + e.getSubject(), ctx);
|
// .isValid();
|
||||||
// } catch (VerificationError e) {
|
// } catch (DocumentError e) {
|
||||||
// //System.err.println(e.getCode() + " (ProofVerifierProbe)");
|
// return error(e.getType() + " " + e.getSubject(), ctx);
|
||||||
// if(e.getCode() == Code.Internal) {
|
// } catch (VerificationError e) {
|
||||||
// return exception(e.getMessage(), ctx.getResource());
|
// //System.err.println(e.getCode() + " (ProofVerifierProbe)");
|
||||||
// } else if(e.getCode().equals(Code.Expired)) {
|
// if(e.getCode() == Code.Internal) {
|
||||||
// //handled by other probe
|
// return exception(e.getMessage(), ctx.getResource());
|
||||||
// } else {
|
// } else if(e.getCode().equals(Code.Expired)) {
|
||||||
// return fatal(e.getCode().name() + " " + e.getMessage(), ctx);
|
// //handled by other probe
|
||||||
// }
|
// } else {
|
||||||
//
|
// return fatal(e.getCode().name() + " " + e.getMessage(), ctx);
|
||||||
// }
|
// }
|
||||||
// return success(ctx);
|
//
|
||||||
// }
|
// }
|
||||||
|
// return success(ctx);
|
||||||
|
// }
|
||||||
|
|
||||||
private static final class IronNoopStatusVerifier implements StatusVerifier {
|
private static final class IronNoopStatusVerifier implements StatusVerifier {
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
Reference in New Issue
Block a user