Merge pull request #50 from imsglc/fine-tune-verification-key-url-support
Fine tune verification key url support
This commit is contained in:
commit
f1cc6242bb
@ -65,37 +65,34 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
// The verification method must dereference to an Ed25519VerificationKey2020.
|
// The verification method must dereference to an Ed25519VerificationKey2020.
|
||||||
// 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 controller = null;
|
|
||||||
|
|
||||||
// Formats accepted:
|
// Formats accepted:
|
||||||
//
|
//
|
||||||
// [controller]#[publicKeyMultibase]
|
// [controller]#[publicKeyMultibase]
|
||||||
// did:key:[publicKeyMultibase]
|
// did:key:[publicKeyMultibase]
|
||||||
// http/s://[location of a Ed25519VerificationKey2020 document]
|
// http/s://[location of a Ed25519VerificationKey2020 document]
|
||||||
// http/s://[location of a controller document with a 'verificationMethod' with a Ed25519VerificationKey2020]
|
// http/s://[location of a controller document with a 'verificationMethod' with a Ed25519VerificationKey2020]
|
||||||
// [publicKeyMultibase]
|
|
||||||
|
String publicKeyMultibase;
|
||||||
|
String controller = null;
|
||||||
|
|
||||||
publicKeyMultibase = method.toString();
|
publicKeyMultibase = method.toString();
|
||||||
|
|
||||||
if (method.getFragment() != null) {
|
if (method.getFragment() != null && IsValidPublicKeyMultibase(method.getFragment())) {
|
||||||
publicKeyMultibase = method.getFragment();
|
publicKeyMultibase = method.getFragment();
|
||||||
controller = method.toString().substring(0, method.toString().indexOf("#"));
|
controller = method.toString().substring(0, method.toString().indexOf("#"));
|
||||||
} else {
|
} else {
|
||||||
if (method.getScheme().equals("did")) {
|
if (StringUtils.isBlank(method.getScheme())) {
|
||||||
|
return error("The verification method must be a valid URI (missing scheme)", ctx);
|
||||||
|
} else if (method.getScheme().equals("did")) {
|
||||||
if (method.getSchemeSpecificPart().startsWith("key:")) {
|
if (method.getSchemeSpecificPart().startsWith("key:")) {
|
||||||
publicKeyMultibase = method.getSchemeSpecificPart().substring(4);
|
publicKeyMultibase = method.getSchemeSpecificPart().substring("key:".length());
|
||||||
} 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")) {
|
||||||
// TODO: Can we use proof.getDocumentLoader()?
|
try {
|
||||||
ConfigurableDocumentLoader keyDocumentLoader = new ConfigurableDocumentLoader();
|
Document keyDocument = vc.getDocumentLoader().loadDocument(method, new DocumentLoaderOptions());
|
||||||
keyDocumentLoader.setEnableHttp(true);
|
|
||||||
keyDocumentLoader.setEnableHttps(true);
|
|
||||||
|
|
||||||
Document keyDocument = keyDocumentLoader.loadDocument(method, new DocumentLoaderOptions());
|
|
||||||
Optional<JsonStructure> keyStructure = keyDocument.getJsonContent();
|
Optional<JsonStructure> keyStructure = keyDocument.getJsonContent();
|
||||||
if (keyStructure.isEmpty()) {
|
if (keyStructure.isEmpty()) {
|
||||||
return error("Key document not found at " + method, ctx);
|
return error("Key document not found at " + method, ctx);
|
||||||
@ -116,6 +113,12 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
} else {
|
} else {
|
||||||
publicKeyMultibase = keyStructure.get().asJsonObject().getString("publicKeyMultibase");
|
publicKeyMultibase = keyStructure.get().asJsonObject().getString("publicKeyMultibase");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
return error("Invalid verification key URL: " + e.getMessage(), ctx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error("Unknown verification method scheme: " + method.getScheme(), ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,5 +157,16 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
return success(ctx);
|
return success(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Boolean IsValidPublicKeyMultibase(String publicKeyMultibase) {
|
||||||
|
try {
|
||||||
|
byte[] publicKeyMulticodec = Multibase.decode(publicKeyMultibase);
|
||||||
|
byte[] publicKey = Multicodec.decode(Codec.Ed25519PublicKey, publicKeyMulticodec);
|
||||||
|
return publicKey.length == 32;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static final String ID = EmbeddedProofProbe.class.getSimpleName();
|
public static final String ID = EmbeddedProofProbe.class.getSimpleName();
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import com.google.common.collect.Iterables;
|
|||||||
|
|
||||||
public class OB30Tests {
|
public class OB30Tests {
|
||||||
private static OB30Inspector validator;
|
private static OB30Inspector validator;
|
||||||
private static boolean verbose = false;
|
private static boolean verbose = true;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
static void setup() {
|
static void setup() {
|
||||||
@ -40,6 +40,15 @@ public class OB30Tests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSimpleDidMethodJsonValid() {
|
||||||
|
assertDoesNotThrow(()->{
|
||||||
|
Report report = validator.run(Samples.OB30.JSON.SIMPLE_DID_METHOD_JSON.asFileResource());
|
||||||
|
if(verbose) PrintHelper.print(report, true);
|
||||||
|
assertValid(report);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSimplePNGPlainValid() {
|
void testSimplePNGPlainValid() {
|
||||||
assertDoesNotThrow(()->{
|
assertDoesNotThrow(()->{
|
||||||
@ -90,7 +99,8 @@ public class OB30Tests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSimpleJsonInvalidProofMethod() {
|
void testSimpleJsonInvalidProofMethod() {
|
||||||
//add some garbage chars to proofValue
|
// add some garbage chars to the verification method fragment
|
||||||
|
// it will be treated a URL to a verification key, but the URL will not be found
|
||||||
assertDoesNotThrow(()->{
|
assertDoesNotThrow(()->{
|
||||||
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_PROOF_METHOD_ERROR.asFileResource());
|
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_PROOF_METHOD_ERROR.asFileResource());
|
||||||
if(verbose) PrintHelper.print(report, true);
|
if(verbose) PrintHelper.print(report, true);
|
||||||
@ -100,6 +110,42 @@ public class OB30Tests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSimpleJsonInvalidProofMethodNoScheme() {
|
||||||
|
// The verificationMethod is not a URI (no scheme)
|
||||||
|
assertDoesNotThrow(()->{
|
||||||
|
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_PROOF_METHOD_NO_SCHEME_ERROR.asFileResource());
|
||||||
|
if(verbose) PrintHelper.print(report, true);
|
||||||
|
assertInvalid(report);
|
||||||
|
assertErrorCount(report, 1);
|
||||||
|
assertHasProbeID(report, EmbeddedProofProbe.ID, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSimpleJsonInvalidProofMethodUnknownScheme() {
|
||||||
|
// The verificationMethod is not a URI (no scheme)
|
||||||
|
assertDoesNotThrow(()->{
|
||||||
|
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_PROOF_METHOD_UNKNOWN_SCHEME_ERROR.asFileResource());
|
||||||
|
if(verbose) PrintHelper.print(report, true);
|
||||||
|
assertInvalid(report);
|
||||||
|
assertErrorCount(report, 1);
|
||||||
|
assertHasProbeID(report, EmbeddedProofProbe.ID, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSimpleJsonInvalidProofMethodUnknownDidMethod() {
|
||||||
|
// The verificationMethod is an unknown DID Method
|
||||||
|
assertDoesNotThrow(()->{
|
||||||
|
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_PROOF_METHOD_UNKNOWN_DID_METHOD_ERROR.asFileResource());
|
||||||
|
if(verbose) PrintHelper.print(report, true);
|
||||||
|
assertInvalid(report);
|
||||||
|
assertErrorCount(report, 1);
|
||||||
|
assertHasProbeID(report, EmbeddedProofProbe.ID, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSimpleJsonInvalidProofValue() {
|
void testSimpleJsonInvalidProofValue() {
|
||||||
//add some garbage chars to proofValue
|
//add some garbage chars to proofValue
|
||||||
|
@ -11,9 +11,13 @@ public class Samples {
|
|||||||
public static final class JSON {
|
public static final class JSON {
|
||||||
public final static Sample COMPLETE_JSON = new Sample("ob30/complete.json", false);
|
public final static Sample COMPLETE_JSON = new Sample("ob30/complete.json", false);
|
||||||
public final static Sample SIMPLE_JSON = new Sample("ob30/simple.json", true);
|
public final static Sample SIMPLE_JSON = new Sample("ob30/simple.json", true);
|
||||||
|
public final static Sample SIMPLE_DID_METHOD_JSON = new Sample("ob30/simple-did-method.json", true);
|
||||||
public final static Sample SIMPLE_JSON_NOPROOF = new Sample("ob30/simple-noproof.json", false);
|
public final static Sample SIMPLE_JSON_NOPROOF = new Sample("ob30/simple-noproof.json", false);
|
||||||
public final static Sample SIMPLE_JSON_UNKNOWN_TYPE = new Sample("ob30/simple-err-type.json", false);
|
public final static Sample SIMPLE_JSON_UNKNOWN_TYPE = new Sample("ob30/simple-err-type.json", false);
|
||||||
public final static Sample SIMPLE_JSON_PROOF_METHOD_ERROR = new Sample("ob30/simple-err-proof-method.json", false);
|
public final static Sample SIMPLE_JSON_PROOF_METHOD_ERROR = new Sample("ob30/simple-err-proof-method.json", false);
|
||||||
|
public final static Sample SIMPLE_JSON_PROOF_METHOD_NO_SCHEME_ERROR = new Sample("ob30/simple-err-proof-method-no-scheme.json", false);
|
||||||
|
public final static Sample SIMPLE_JSON_PROOF_METHOD_UNKNOWN_SCHEME_ERROR = new Sample("ob30/simple-err-proof-method-unknown-scheme.json", false);
|
||||||
|
public final static Sample SIMPLE_JSON_PROOF_METHOD_UNKNOWN_DID_METHOD_ERROR = new Sample("ob30/simple-err-proof-method-unknown-did-method.json", false);
|
||||||
public final static Sample SIMPLE_JSON_PROOF_VALUE_ERROR = new Sample("ob30/simple-err-proof-value.json", false);
|
public final static Sample SIMPLE_JSON_PROOF_VALUE_ERROR = new Sample("ob30/simple-err-proof-value.json", false);
|
||||||
public final static Sample SIMPLE_JSON_EXPIRED = new Sample("ob30/simple-err-expired.json", false);
|
public final static Sample SIMPLE_JSON_EXPIRED = new Sample("ob30/simple-err-expired.json", false);
|
||||||
public final static Sample SIMPLE_JSON_ISSUED = new Sample("ob30/simple-err-issued.json", false);
|
public final static Sample SIMPLE_JSON_ISSUED = new Sample("ob30/simple-err-issued.json", false);
|
||||||
|
33
inspector-vc/src/test/resources/ob30/simple-did-method.json
Normal file
33
inspector-vc/src/test/resources/ob30/simple-did-method.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"@context" : [ "https://www.w3.org/2018/credentials/v1", "https://purl.imsglobal.org/spec/ob/v3p0/context.json", "https://purl.imsglobal.org/spec/ob/v3p0/extensions.json", "https://w3id.org/security/suites/ed25519-2020/v1" ],
|
||||||
|
"id" : "urn:uuid:280c19b6-9680-4a37-ba84-e38b1a4e4584",
|
||||||
|
"type" : [ "VerifiableCredential", "AchievementCredential" ],
|
||||||
|
"issuer" : {
|
||||||
|
"type" : [ "Profile" ],
|
||||||
|
"name" : "Andy F. Miller",
|
||||||
|
"id" : "urn:uuid:6f2e33e5-7a29-4155-840a-59483ba10164"
|
||||||
|
},
|
||||||
|
"issuanceDate" : "2022-11-10T07:38:00-08:00",
|
||||||
|
"name" : "test 1",
|
||||||
|
"credentialSubject" : {
|
||||||
|
"id" : "urn:uuid:6f2e33e5-7a29-4155-840a-59483ba10164",
|
||||||
|
"type" : [ "AchievementSubject" ],
|
||||||
|
"achievement" : {
|
||||||
|
"id" : "urn:uuid:35258e6f-4c05-4215-8ada-38a5a5b80510",
|
||||||
|
"type" : [ "Achievement" ],
|
||||||
|
"achievementType" : "Achievement",
|
||||||
|
"name" : "test 1",
|
||||||
|
"description" : "This is a test achievement",
|
||||||
|
"criteria" : {
|
||||||
|
"narrative" : "There is no criteria"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"proof" : [ {
|
||||||
|
"type" : "Ed25519Signature2020",
|
||||||
|
"created" : "2022-11-16T20:39:53Z",
|
||||||
|
"proofPurpose" : "assertionMethod",
|
||||||
|
"verificationMethod" : "did:key:z6MkwAQmEfso8UjHJZTQajRtqR5hDxAD95iJD4z53XnKCFms",
|
||||||
|
"proofValue" : "z361ueyGzREPvsWdnWUfkzTKXEd6u2DPPu2kDw3pDERJmzDFCqsuaPneqcRgz2hk9ycaNDYmC4Fy9c6S6BDDt5fVB"
|
||||||
|
} ]
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/2018/credentials/v1",
|
||||||
|
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||||
|
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "http://example.com/credentials/3527",
|
||||||
|
"type": [
|
||||||
|
"VerifiableCredential",
|
||||||
|
"OpenBadgeCredential"
|
||||||
|
],
|
||||||
|
"issuer": {
|
||||||
|
"id": "https://example.com/issuers/876543",
|
||||||
|
"type": [
|
||||||
|
"Profile"
|
||||||
|
],
|
||||||
|
"name": "Example Corp"
|
||||||
|
},
|
||||||
|
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||||
|
"name": "Teamwork Badge",
|
||||||
|
"credentialSubject": {
|
||||||
|
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||||
|
"type": [
|
||||||
|
"AchievementSubject"
|
||||||
|
],
|
||||||
|
"achievement": {
|
||||||
|
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||||
|
"type": [
|
||||||
|
"Achievement"
|
||||||
|
],
|
||||||
|
"criteria": {
|
||||||
|
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||||
|
},
|
||||||
|
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||||
|
"name": "Teamwork"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"proof": [
|
||||||
|
{
|
||||||
|
"type": "Ed25519Signature2020",
|
||||||
|
"created": "2022-11-16T18:54:22Z",
|
||||||
|
"verificationMethod": "z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||||
|
"proofPurpose": "assertionMethod",
|
||||||
|
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/2018/credentials/v1",
|
||||||
|
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||||
|
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "http://example.com/credentials/3527",
|
||||||
|
"type": [
|
||||||
|
"VerifiableCredential",
|
||||||
|
"OpenBadgeCredential"
|
||||||
|
],
|
||||||
|
"issuer": {
|
||||||
|
"id": "https://example.com/issuers/876543",
|
||||||
|
"type": [
|
||||||
|
"Profile"
|
||||||
|
],
|
||||||
|
"name": "Example Corp"
|
||||||
|
},
|
||||||
|
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||||
|
"name": "Teamwork Badge",
|
||||||
|
"credentialSubject": {
|
||||||
|
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||||
|
"type": [
|
||||||
|
"AchievementSubject"
|
||||||
|
],
|
||||||
|
"achievement": {
|
||||||
|
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||||
|
"type": [
|
||||||
|
"Achievement"
|
||||||
|
],
|
||||||
|
"criteria": {
|
||||||
|
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||||
|
},
|
||||||
|
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||||
|
"name": "Teamwork"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"proof": [
|
||||||
|
{
|
||||||
|
"type": "Ed25519Signature2020",
|
||||||
|
"created": "2022-11-16T18:54:22Z",
|
||||||
|
"verificationMethod": "did:example:z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||||
|
"proofPurpose": "assertionMethod",
|
||||||
|
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/2018/credentials/v1",
|
||||||
|
"https://purl.imsglobal.org/spec/ob/v3p0/context.json",
|
||||||
|
"https://w3id.org/security/suites/ed25519-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "http://example.com/credentials/3527",
|
||||||
|
"type": [
|
||||||
|
"VerifiableCredential",
|
||||||
|
"OpenBadgeCredential"
|
||||||
|
],
|
||||||
|
"issuer": {
|
||||||
|
"id": "https://example.com/issuers/876543",
|
||||||
|
"type": [
|
||||||
|
"Profile"
|
||||||
|
],
|
||||||
|
"name": "Example Corp"
|
||||||
|
},
|
||||||
|
"issuanceDate": "2010-01-01T00:00:00Z",
|
||||||
|
"name": "Teamwork Badge",
|
||||||
|
"credentialSubject": {
|
||||||
|
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
|
||||||
|
"type": [
|
||||||
|
"AchievementSubject"
|
||||||
|
],
|
||||||
|
"achievement": {
|
||||||
|
"id": "https://example.com/achievements/21st-century-skills/teamwork",
|
||||||
|
"type": [
|
||||||
|
"Achievement"
|
||||||
|
],
|
||||||
|
"criteria": {
|
||||||
|
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
|
||||||
|
},
|
||||||
|
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
|
||||||
|
"name": "Teamwork"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"proof": [
|
||||||
|
{
|
||||||
|
"type": "Ed25519Signature2020",
|
||||||
|
"created": "2022-11-16T18:54:22Z",
|
||||||
|
"verificationMethod": "xxx:z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv",
|
||||||
|
"proofPurpose": "assertionMethod",
|
||||||
|
"proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user