Merge pull request #41 from imsglc/vc-ob-clr

add alpha implementations of ob3 and clr2 validators
This commit is contained in:
Markus Gylling 2022-10-20 13:33:41 +02:00 committed by GitHub
commit c48bd72b2d
61 changed files with 6651 additions and 0 deletions

85
inspector-vc/pom.xml Normal file
View File

@ -0,0 +1,85 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.1edtech</groupId>
<artifactId>inspector</artifactId>
<version>0.9.2</version>
</parent>
<artifactId>inspector-vc</artifactId>
<dependencies>
<dependency>
<groupId>org.1edtech</groupId>
<artifactId>inspector-core</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>auth0</artifactId>
<version>1.42.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.21.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.2</version>
</dependency>
<!-- https://repo.danubetech.com/#browse/browse:maven-public:com%2Fdanubetech%2Fverifiable-credentials-java%2F1.1-SNAPSHOT%2F1.1-20220818.090353-4 -->
<dependency>
<groupId>com.danubetech</groupId>
<artifactId>verifiable-credentials-java</artifactId>
<!-- <version>1.1-20220818.090353-4</version> -->
<version>1.1-SNAPSHOT</version>
</dependency>
<!-- https://repo.danubetech.com/#browse/browse:maven-public:com%2Fdanubetech%2Fkey-formats-java%2F1.6-SNAPSHOT -->
<dependency>
<groupId>com.danubetech</groupId>
<artifactId>key-formats-java</artifactId>
<version>1.6-SNAPSHOT</version>
</dependency>
<!-- https://github.com/filip26/iron-verifiable-credentials -->
<dependency>
<groupId>com.apicatalog</groupId>
<artifactId>iron-verifiable-credentials-jre8</artifactId>
<version>0.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.apicatalog/titanium-json-ld -->
<!-- https://github.com/filip26/titanium-json-ld -->
<dependency>
<groupId>com.apicatalog</groupId>
<artifactId>titanium-json-ld</artifactId>
<version>1.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.setl/rdf-urdna -->
<!-- https://github.com/setl/rdf-urdna <dependency> <groupId>io.setl</groupId>
<artifactId>rdf-urdna</artifactId> <version>1.1</version> <exclusions> <exclusion>
<groupId>junit</groupId> <artifactId>junit</artifactId> </exclusion> <exclusion>
<groupId>com.apicatalog</groupId> <artifactId>titanium-json-ld</artifactId>
</exclusion> </exclusions> </dependency> -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.json</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>danubetech-maven-public</id>
<url>https://repo.danubetech.com/repository/maven-public/</url>
</repository>
</repositories>
</project>

View File

@ -0,0 +1,133 @@
package org.oneedtech.inspect.vc;
import static org.oneedtech.inspect.util.code.Defensives.*;
import static org.oneedtech.inspect.util.resource.ResourceType.*;
import static org.oneedtech.inspect.vc.Credential.Type.*;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.oneedtech.inspect.core.probe.GeneratedObject;
import org.oneedtech.inspect.schema.Catalog;
import org.oneedtech.inspect.schema.SchemaKey;
import org.oneedtech.inspect.util.resource.Resource;
import org.oneedtech.inspect.util.resource.ResourceType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
/**
* A wrapper object for a verifiable credential. This contains e.g. the origin resource
* and the extracted JSON data plus any other stuff Probes need.
* @author mgylling
*/
public class Credential extends GeneratedObject {
final Resource resource;
final JsonNode jsonData;
final Credential.Type credentialType;
final String jwt;
public Credential(Resource resource, JsonNode data, String jwt) {
super(ID, GeneratedObject.Type.INTERNAL);
this.resource = checkNotNull(resource);
this.jsonData = checkNotNull(data);
this.jwt = jwt; //may be null
checkTrue(RECOGNIZED_PAYLOAD_TYPES.contains(resource.getType()));
ArrayNode typeNode = (ArrayNode)jsonData.get("type");
this.credentialType = Credential.Type.valueOf(typeNode);
}
public Credential(Resource resource, JsonNode data) {
this(resource, data, null);
}
public Resource getResource() {
return resource;
}
public JsonNode getJson() {
return jsonData;
}
public Credential.Type getCredentialType() {
return credentialType;
}
public Optional<String> getJwt() {
return Optional.ofNullable(jwt);
}
public ProofType getProofType() {
return jwt == null ? ProofType.EMBEDDED : ProofType.EXTERNAL;
}
private static final Map<Credential.Type, SchemaKey> schemas = new ImmutableMap.Builder<Credential.Type, SchemaKey>()
.put(AchievementCredential, Catalog.OB_30_ACHIEVEMENTCREDENTIAL_JSON)
.put(ClrCredential, Catalog.CLR_20_CLRCREDENTIAL_JSON)
.put(VerifiablePresentation, Catalog.CLR_20_CLRCREDENTIAL_JSON)
.put(EndorsementCredential, Catalog.OB_30_ENDORSEMENTCREDENTIAL_JSON)
.build();
/**
* Get the canonical schema for this credential if such exists.
*/
public Optional<SchemaKey> getSchemaKey() {
return Optional.ofNullable(schemas.get(credentialType));
}
public enum Type {
AchievementCredential,
OpenBadgeCredential, //treated as an alias of AchievementCredential
ClrCredential,
EndorsementCredential,
VerifiablePresentation,
VerifiableCredential, //this is an underspecifier in our context
Unknown;
public static Credential.Type valueOf (ArrayNode typeArray) {
if(typeArray != null) {
Iterator<JsonNode> iter = typeArray.iterator();
while(iter.hasNext()) {
String value = iter.next().asText();
if(value.equals("AchievementCredential") || value.equals("OpenBadgeCredential")) {
return AchievementCredential;
} else if(value.equals("ClrCredential")) {
return ClrCredential;
} else if(value.equals("VerifiablePresentation")) {
return VerifiablePresentation;
} else if(value.equals("EndorsementCredential")) {
return EndorsementCredential;
}
}
}
return Unknown;
}
}
public enum ProofType {
EXTERNAL,
EMBEDDED
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("resource", resource.getID())
.add("resourceType", resource.getType())
.add("credentialType", credentialType)
.add("json", jsonData)
.toString();
}
public static final String ID = Credential.class.getCanonicalName();
public static final List<ResourceType> RECOGNIZED_PAYLOAD_TYPES = List.of(SVG, PNG, JSON, JWT);
public static final String CREDENTIAL_KEY = "CREDENTIAL_KEY";
}

View File

@ -0,0 +1,141 @@
package org.oneedtech.inspect.vc;
import static java.lang.Boolean.TRUE;
import static org.oneedtech.inspect.core.probe.RunContext.Key.*;
import static org.oneedtech.inspect.core.report.ReportUtil.onProbeException;
import static org.oneedtech.inspect.util.code.Defensives.checkNotNull;
import static org.oneedtech.inspect.util.json.ObjectMapperCache.Config.DEFAULT;
import static org.oneedtech.inspect.vc.Credential.CREDENTIAL_KEY;
import static org.oneedtech.inspect.vc.Credential.ProofType.EXTERNAL;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.oneedtech.inspect.core.SubInspector;
import org.oneedtech.inspect.core.probe.GeneratedObject;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.probe.json.JsonPathEvaluator;
import org.oneedtech.inspect.core.report.Report;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.util.json.ObjectMapperCache;
import org.oneedtech.inspect.util.resource.Resource;
import org.oneedtech.inspect.util.resource.UriResource;
import org.oneedtech.inspect.util.resource.context.ResourceContext;
import org.oneedtech.inspect.vc.Credential.Type;
import org.oneedtech.inspect.vc.probe.ContextPropertyProbe;
import org.oneedtech.inspect.vc.probe.EmbeddedProofProbe;
import org.oneedtech.inspect.vc.probe.ExpirationProbe;
import org.oneedtech.inspect.vc.probe.ExternalProofProbe;
import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe;
import org.oneedtech.inspect.vc.probe.IssuanceProbe;
import org.oneedtech.inspect.vc.probe.RevocationListProbe;
import org.oneedtech.inspect.vc.probe.TypePropertyProbe;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* An inspector for EndorsementCredential objects.
* @author mgylling
*/
public class EndorsementInspector extends VCInspector implements SubInspector {
protected <B extends VCInspector.Builder<?>> EndorsementInspector(B builder) {
super(builder);
}
@Override
public Report run(Resource resource, Map<String, GeneratedObject> parentObjects) {
/*
* The resource param is the top-level credential that embeds the endorsement, we
* expect parentObjects to provide a pointer to the JsonNode we should check.
*
* The parent inspector is responsible to decode away possible jwt-ness, so that
* what we get here is a verbatim json node.
*
*/
Credential endorsement = (Credential) checkNotNull(parentObjects.get(CREDENTIAL_KEY));
ObjectMapper mapper = ObjectMapperCache.get(DEFAULT);
JsonPathEvaluator jsonPath = new JsonPathEvaluator(mapper);
RunContext ctx = new RunContext.Builder()
.put(this)
.put(JACKSON_OBJECTMAPPER, mapper)
.put(JSONPATH_EVALUATOR, jsonPath)
.build();
List<ReportItems> accumulator = new ArrayList<>();
int probeCount = 0;
try {
//context and type properties
Credential.Type type = Type.EndorsementCredential;
for(Probe<JsonNode> probe : List.of(new ContextPropertyProbe(type), new TypePropertyProbe(type))) {
probeCount++;
accumulator.add(probe.run(endorsement.getJson(), ctx));
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
}
//inline schema (parent inspector has already validated against canonical)
accumulator.add(new InlineJsonSchemaProbe().run(endorsement.getJson(), ctx));
//signatures, proofs
probeCount++;
if(endorsement.getProofType() == EXTERNAL){
//The credential originally contained in a JWT, validate the jwt and external proof.
accumulator.add(new ExternalProofProbe().run(endorsement, ctx));
} else {
//The credential not contained in a jwt, must have an internal proof.
accumulator.add(new EmbeddedProofProbe().run(endorsement, ctx));
}
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
//check refresh service if we are not already refreshed (check just like in external CLR)
probeCount++;
if(resource.getContext().get(REFRESHED) != TRUE) {
Optional<String> newID = checkRefreshService(endorsement, ctx);
if(newID.isPresent()) {
//TODO resource.type
return this.run(
new UriResource(new URI(newID.get()))
.setContext(new ResourceContext(REFRESHED, TRUE)));
}
}
//revocation, expiration and issuance
for(Probe<Credential> probe : List.of(new RevocationListProbe(),
new ExpirationProbe(), new IssuanceProbe())) {
probeCount++;
accumulator.add(probe.run(endorsement, ctx));
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
}
} catch (Exception e) {
accumulator.add(onProbeException(Probe.ID.NO_UNCAUGHT_EXCEPTIONS, resource, e));
}
return new Report(ctx, new ReportItems(accumulator), probeCount);
}
@Override
public <R extends Resource> Report run(R resource) {
throw new IllegalStateException("must use #run(resource, map)");
}
public static class Builder extends VCInspector.Builder<EndorsementInspector.Builder> {
@SuppressWarnings("unchecked")
@Override
public EndorsementInspector build() {
return new EndorsementInspector(this);
}
}
}

View File

@ -0,0 +1,232 @@
package org.oneedtech.inspect.vc;
import static java.lang.Boolean.TRUE;
import static org.oneedtech.inspect.core.Inspector.Behavior.RESET_CACHES_ON_RUN;
import static org.oneedtech.inspect.core.report.ReportUtil.onProbeException;
import static org.oneedtech.inspect.util.code.Defensives.*;
import static org.oneedtech.inspect.util.json.ObjectMapperCache.Config.DEFAULT;
import static org.oneedtech.inspect.vc.Credential.CREDENTIAL_KEY;
import static org.oneedtech.inspect.vc.Credential.ProofType.EXTERNAL;
import static org.oneedtech.inspect.vc.payload.PayloadParser.fromJwt;
import static org.oneedtech.inspect.vc.util.JsonNodeUtil.asNodeList;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.oneedtech.inspect.core.SubInspector;
import org.oneedtech.inspect.core.probe.GeneratedObject;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.probe.RunContext.Key;
import org.oneedtech.inspect.core.probe.json.JsonPathEvaluator;
import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe;
import org.oneedtech.inspect.core.report.Report;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.schema.JsonSchemaCache;
import org.oneedtech.inspect.schema.SchemaKey;
import org.oneedtech.inspect.util.code.Defensives;
import org.oneedtech.inspect.util.json.ObjectMapperCache;
import org.oneedtech.inspect.util.resource.Resource;
import org.oneedtech.inspect.util.resource.ResourceType;
import org.oneedtech.inspect.util.resource.UriResource;
import org.oneedtech.inspect.util.resource.context.ResourceContext;
import org.oneedtech.inspect.util.spec.Specification;
import org.oneedtech.inspect.vc.Credential.Type;
import org.oneedtech.inspect.vc.probe.ContextPropertyProbe;
import org.oneedtech.inspect.vc.probe.CredentialParseProbe;
import org.oneedtech.inspect.vc.probe.CredentialSubjectProbe;
import org.oneedtech.inspect.vc.probe.ExpirationProbe;
import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe;
import org.oneedtech.inspect.vc.probe.IssuanceProbe;
import org.oneedtech.inspect.vc.probe.EmbeddedProofProbe;
import org.oneedtech.inspect.vc.probe.RevocationListProbe;
import org.oneedtech.inspect.vc.probe.ExternalProofProbe;
import org.oneedtech.inspect.vc.probe.TypePropertyProbe;
import org.oneedtech.inspect.vc.util.CachingDocumentLoader;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
/**
* A verifier for Open Badges 3.0.
* @author mgylling
*/
public class OB30Inspector extends VCInspector implements SubInspector {
protected final List<Probe<Credential>> userProbes;
protected OB30Inspector(OB30Inspector.Builder builder) {
super(builder);
this.userProbes = ImmutableList.copyOf(builder.probes);
}
//https://docs.google.com/document/d/1_imUl2K-5tMib0AUxwA9CWb0Ap1b3qif0sXydih68J0/edit#
//https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#verificaton-and-validation
/*
* This inspector supports both standalone openbadge verification, as well as verification of
* AchievementCredentials embedded in e.g. CLR.
*
* When verifying a standalone AchievementCredential, call the run(Resource) method. When verifying
* an embedded AchievementCredential, call the run(Resource, Map) method.
*/
@Override
public Report run(Resource resource) {
super.check(resource); //TODO because URIs, this should be a fetch and cache
if(getBehavior(RESET_CACHES_ON_RUN) == TRUE) {
JsonSchemaCache.reset();
CachingDocumentLoader.reset();
}
ObjectMapper mapper = ObjectMapperCache.get(DEFAULT);
JsonPathEvaluator jsonPath = new JsonPathEvaluator(mapper);
RunContext ctx = new RunContext.Builder()
.put(this)
.put(resource)
.put(Key.JACKSON_OBJECTMAPPER, mapper)
.put(Key.JSONPATH_EVALUATOR, jsonPath)
.build();
List<ReportItems> accumulator = new ArrayList<>();
int probeCount = 0;
try {
//detect type (png, svg, json, jwt) and extract json data
probeCount++;
accumulator.add(new CredentialParseProbe().run(resource, ctx));
if(broken(accumulator, true)) return abort(ctx, accumulator, probeCount);
//we expect the above to place a generated object in the context
Credential ob = ctx.getGeneratedObject(Credential.ID);
//call the subinspector method of this
Report subReport = this.run(resource, Map.of(Credential.CREDENTIAL_KEY, ob));
probeCount += subReport.getSummary().getTotalRun();
accumulator.add(subReport);
//finally, run any user-added probes
for(Probe<Credential> probe : userProbes) {
probeCount++;
accumulator.add(probe.run(ob, ctx));
}
} catch (Exception e) {
accumulator.add(onProbeException(Probe.ID.NO_UNCAUGHT_EXCEPTIONS, resource, e));
}
return new Report(ctx, new ReportItems(accumulator), probeCount);
}
@Override
public Report run(Resource resource, Map<String, GeneratedObject> parentObjects) {
Credential ob = checkNotNull((Credential)parentObjects.get(CREDENTIAL_KEY));
ObjectMapper mapper = ObjectMapperCache.get(DEFAULT);
JsonPathEvaluator jsonPath = new JsonPathEvaluator(mapper);
RunContext ctx = new RunContext.Builder()
.put(this)
.put(resource)
.put(Key.JACKSON_OBJECTMAPPER, mapper)
.put(Key.JSONPATH_EVALUATOR, jsonPath)
.build();
List<ReportItems> accumulator = new ArrayList<>();
int probeCount = 0;
try {
//context and type properties
Credential.Type type = Type.OpenBadgeCredential;
for(Probe<JsonNode> probe : List.of(new ContextPropertyProbe(type), new TypePropertyProbe(type))) {
probeCount++;
accumulator.add(probe.run(ob.getJson(), ctx));
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
}
//canonical schema and inline schemata
SchemaKey schema = ob.getSchemaKey().orElseThrow();
for(Probe<JsonNode> probe : List.of(new JsonSchemaProbe(schema), new InlineJsonSchemaProbe(schema))) {
probeCount++;
accumulator.add(probe.run(ob.getJson(), ctx));
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
}
//credentialSubject
probeCount++;
accumulator.add(new CredentialSubjectProbe().run(ob.getJson(), ctx));
//signatures, proofs
probeCount++;
if(ob.getProofType() == EXTERNAL){
//The credential originally contained in a JWT, validate the jwt and external proof.
accumulator.add(new ExternalProofProbe().run(ob, ctx));
} else {
//The credential not contained in a jwt, must have an internal proof.
accumulator.add(new EmbeddedProofProbe().run(ob, ctx));
}
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
//check refresh service if we are not already refreshed
probeCount++;
if(resource.getContext().get(REFRESHED) != TRUE) {
Optional<String> newID = checkRefreshService(ob, ctx);
if(newID.isPresent()) {
return this.run(
new UriResource(new URI(newID.get()))
.setContext(new ResourceContext(REFRESHED, TRUE)));
}
}
//revocation, expiration and issuance
for(Probe<Credential> probe : List.of(new RevocationListProbe(),
new ExpirationProbe(), new IssuanceProbe())) {
probeCount++;
accumulator.add(probe.run(ob, ctx));
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
}
//embedded endorsements
EndorsementInspector endorsementInspector = new EndorsementInspector.Builder().build();
List<JsonNode> endorsements = asNodeList(ob.getJson(), "$..endorsement", jsonPath);
for(JsonNode node : endorsements) {
probeCount++;
Credential endorsement = new Credential(resource, node);
accumulator.add(endorsementInspector.run(resource, Map.of(CREDENTIAL_KEY, endorsement)));
}
//embedded jwt endorsements
endorsements = asNodeList(ob.getJson(), "$..endorsementJwt", jsonPath);
for(JsonNode node : endorsements) {
probeCount++;
String jwt = node.asText();
JsonNode vcNode = fromJwt(jwt, ctx);
Credential endorsement = new Credential(resource, vcNode, jwt);
accumulator.add(endorsementInspector.run(resource, Map.of(CREDENTIAL_KEY, endorsement)));
}
} catch (Exception e) {
accumulator.add(onProbeException(Probe.ID.NO_UNCAUGHT_EXCEPTIONS, resource, e));
}
return new Report(ctx, new ReportItems(accumulator), probeCount);
}
public static class Builder extends VCInspector.Builder<OB30Inspector.Builder> {
@SuppressWarnings("unchecked")
@Override
public OB30Inspector build() {
set(Specification.OB30);
set(ResourceType.OPENBADGE);
return new OB30Inspector(this);
}
}
}

View File

@ -0,0 +1,79 @@
package org.oneedtech.inspect.vc;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.oneedtech.inspect.core.Inspector;
import org.oneedtech.inspect.core.probe.Outcome;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.Report;
import org.oneedtech.inspect.core.report.ReportItems;
import com.fasterxml.jackson.databind.JsonNode;
/**
* Abstract base for verifiable credentials inspectors/verifiers.
* @author mgylling
*/
public abstract class VCInspector extends Inspector {
protected <B extends VCInspector.Builder<?>> VCInspector(B builder) {
super(builder);
}
protected Report abort(RunContext ctx, List<ReportItems> accumulator, int probeCount) {
return new Report(ctx, new ReportItems(accumulator), probeCount);
}
protected boolean broken(List<ReportItems> accumulator) {
return broken(accumulator, false);
}
protected boolean broken(List<ReportItems> accumulator, boolean force) {
if(!force && getBehavior(Inspector.Behavior.VALIDATOR_FAIL_FAST) == Boolean.FALSE) {
return false;
}
for(ReportItems items : accumulator) {
if(items.contains(Outcome.FATAL, Outcome.EXCEPTION)) return true;
}
return false;
}
/**
* If the AchievementCredential or EndorsementCredential has a refreshService property and the type of the
* RefreshService object is 1EdTechCredentialRefresh, you should fetch the refreshed credential from the URL
* provided, then start the verification process over using the response as input. If the request fails,
* the credential is invalid.
*/
protected Optional<String> checkRefreshService(Credential crd, RunContext ctx) {
JsonNode refreshServiceNode = crd.getJson().get("refreshService");
if(refreshServiceNode != null) {
JsonNode serviceTypeNode = refreshServiceNode.get("type");
if(serviceTypeNode != null && serviceTypeNode.asText().equals("1EdTechCredentialRefresh")) {
JsonNode serviceURINode = refreshServiceNode.get("id");
if(serviceURINode != null) {
return Optional.of(serviceURINode.asText());
}
}
}
return Optional.empty();
}
protected static final String REFRESHED = "is.refreshed.credential";
public abstract static class Builder<B extends VCInspector.Builder<B>> extends Inspector.Builder<B> {
final List<Probe<Credential>> probes;
public Builder() {
super();
this.probes = new ArrayList<>();
}
public VCInspector.Builder<B> add(Probe<Credential> probe) {
probes.add(probe);
return this;
}
}
}

View File

@ -0,0 +1,32 @@
package org.oneedtech.inspect.vc.payload;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.oneedtech.inspect.util.code.Defensives.checkTrue;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.util.resource.Resource;
import org.oneedtech.inspect.util.resource.ResourceType;
import org.oneedtech.inspect.vc.Credential;
import com.fasterxml.jackson.databind.JsonNode;
/**
* A credential extractor for JSON files.
* @author mgylling
*/
public final class JsonParser extends PayloadParser {
@Override
public boolean supports(ResourceType type) {
return type == ResourceType.JSON;
}
@Override
public Credential parse(Resource resource, RunContext ctx) throws Exception {
checkTrue(resource.getType() == ResourceType.JSON);
String json = resource.asByteSource().asCharSource(UTF_8).read();
JsonNode node = fromString(json, ctx);
return new Credential(resource, node);
}
}

View File

@ -0,0 +1,32 @@
package org.oneedtech.inspect.vc.payload;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.oneedtech.inspect.util.code.Defensives.checkTrue;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.util.resource.Resource;
import org.oneedtech.inspect.util.resource.ResourceType;
import org.oneedtech.inspect.vc.Credential;
import com.fasterxml.jackson.databind.JsonNode;
/**
* A credential extractor for JWT files.
* @author mgylling
*/
public final class JwtParser extends PayloadParser {
@Override
public boolean supports(ResourceType type) {
return type == ResourceType.JWT;
}
@Override
public Credential parse(Resource resource, RunContext ctx) throws Exception {
checkTrue(resource.getType() == ResourceType.JWT);
String jwt = resource.asByteSource().asCharSource(UTF_8).read();
JsonNode node = fromJwt(jwt, ctx);
return new Credential(resource, node, jwt);
}
}

View File

@ -0,0 +1,52 @@
package org.oneedtech.inspect.vc.payload;
import java.util.Base64;
import java.util.List;
import java.util.Base64.Decoder;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.util.resource.Resource;
import org.oneedtech.inspect.util.resource.ResourceType;
import org.oneedtech.inspect.vc.Credential;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Splitter;
/**
* Abstract base for extracting Credential instances from payloads.
* @author mgylling
*/
public abstract class PayloadParser {
public abstract boolean supports(ResourceType type);
public abstract Credential parse(Resource source, RunContext ctx) throws Exception;
protected static JsonNode fromString(String json, RunContext context) throws Exception {
return ((ObjectMapper)context.get(RunContext.Key.JACKSON_OBJECTMAPPER)).readTree(json);
}
/**
* Decode as per https://www.imsglobal.org/spec/ob/v3p0/#jwt-proof
* @return The decoded JSON String
*/
public static JsonNode fromJwt(String jwt, RunContext context) throws Exception {
List<String> parts = Splitter.on('.').splitToList(jwt);
if(parts.size() != 3) throw new IllegalArgumentException("invalid jwt");
final Decoder decoder = Base64.getUrlDecoder();
/*
* For this step we are only deserializing the stored badge out of the payload.
* The entire jwt is stored separately for signature verification later.
*/
String jwtPayload = new String(decoder.decode(parts.get(1)));
//Deserialize and fetch the 'vc' node from the object
JsonNode outerPayload = fromString(jwtPayload, context);
JsonNode vcNode = outerPayload.get("vc");
return vcNode;
}
}

View File

@ -0,0 +1,25 @@
package org.oneedtech.inspect.vc.payload;
import static org.oneedtech.inspect.util.code.Defensives.checkNotNull;
import java.util.List;
import org.oneedtech.inspect.util.resource.Resource;
/**
* A factory to create PayloadParser instances for various resource types.
* @author mgylling
*/
public class PayloadParserFactory {
private static final Iterable<PayloadParser> parsers = List.of(
new PngParser(), new SvgParser(),
new JsonParser(), new JwtParser());
public static PayloadParser of(Resource resource) {
checkNotNull(resource.getType());
for(PayloadParser cex : parsers) {
if(cex.supports(resource.getType())) return cex;
}
throw new IllegalArgumentException();
}
}

View File

@ -0,0 +1,102 @@
package org.oneedtech.inspect.vc.payload;
import static org.oneedtech.inspect.util.code.Defensives.checkTrue;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.util.resource.Resource;
import org.oneedtech.inspect.util.resource.ResourceType;
import org.oneedtech.inspect.vc.Credential;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.fasterxml.jackson.databind.JsonNode;
/**
* A credential extractor for PNG images.
* @author mgylling
*/
public final class PngParser extends PayloadParser {
@Override
public boolean supports(ResourceType type) {
return type == ResourceType.PNG;
}
@Override
public Credential parse(Resource resource, RunContext ctx) throws Exception {
checkTrue(resource.getType() == ResourceType.PNG);
try(InputStream is = resource.asByteSource().openStream()) {
ImageReader imageReader = ImageIO.getImageReadersByFormatName("png").next();
imageReader.setInput(ImageIO.createImageInputStream(is), true);
IIOMetadata metadata = imageReader.getImageMetadata(0);
String vcString = null;
String jwtString = null;
String formatSearch = null;
JsonNode vcNode = null;
String[] names = metadata.getMetadataFormatNames();
int length = names.length;
for (int i = 0; i < length; i++) {
//Check all names rather than limiting to PNG format to remain malleable through any library changes. (Could limit to "javax_imageio_png_1.0")
formatSearch = getOpenBadgeCredentialNodeText(metadata.getAsTree(names[i]));
if(formatSearch != null) {
vcString = formatSearch;
break;
}
}
if(vcString == null) {
throw new IllegalArgumentException("No credential inside PNG");
}
vcString = vcString.trim();
if(vcString.charAt(0) != '{'){
//This is a jwt. Fetch either the 'vc' out of the payload and save the string for signature verification.
jwtString = vcString;
vcNode = fromJwt(vcString, ctx);
}
else {
vcNode = fromString(vcString, ctx);
}
return new Credential(resource, vcNode, jwtString);
}
}
private String getOpenBadgeCredentialNodeText(Node node){
NamedNodeMap attributes = node.getAttributes();
//If this node is labeled with the attribute keyword: 'openbadgecredential' it is the right one.
Node keyword = attributes.getNamedItem("keyword");
if(keyword != null && keyword.getNodeValue().equals("openbadgecredential")){
Node textAttribute = attributes.getNamedItem("text");
if(textAttribute != null) {
return textAttribute.getNodeValue();
}
}
//iterate over all children depth first and search for the credential node.
Node child = node.getFirstChild();
while (child != null) {
String nodeValue = getOpenBadgeCredentialNodeText(child);
if(nodeValue != null) {
return nodeValue;
}
child = child.getNextSibling();
}
//Return null if we haven't found anything at this recursive depth.
return null;
}
}

View File

@ -0,0 +1,79 @@
package org.oneedtech.inspect.vc.payload;
import static org.oneedtech.inspect.util.code.Defensives.checkTrue;
import java.io.InputStream;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.XMLEvent;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.util.code.Defensives;
import org.oneedtech.inspect.util.resource.Resource;
import org.oneedtech.inspect.util.resource.ResourceType;
import org.oneedtech.inspect.util.xml.XMLInputFactoryCache;
import org.oneedtech.inspect.vc.Credential;
import com.fasterxml.jackson.databind.JsonNode;
/**
* A credential extractor for SVG documents.
* @author mgylling
*/
public final class SvgParser extends PayloadParser {
@Override
public boolean supports(ResourceType type) {
return type == ResourceType.SVG;
}
@Override
public Credential parse(Resource resource, RunContext ctx) throws Exception {
checkTrue(resource.getType() == ResourceType.SVG);
try(InputStream is = resource.asByteSource().openStream()) {
XMLEventReader reader = XMLInputFactoryCache.getInstance().createXMLEventReader(is);
while(reader.hasNext()) {
XMLEvent ev = reader.nextEvent();
if(isEndElem(ev, OB_CRED_ELEM)) break;
if(isStartElem(ev, OB_CRED_ELEM)) {
Attribute verifyAttr = ev.asStartElement().getAttributeByName(OB_CRED_VERIFY_ATTR);
if(verifyAttr != null) {
String jwt = verifyAttr.getValue();
JsonNode node = fromJwt(jwt, ctx);
return new Credential(resource, node, jwt);
} else {
while(reader.hasNext()) {
ev = reader.nextEvent();
if(isEndElem(ev, OB_CRED_ELEM)) break;
if(ev.getEventType() == XMLEvent.CHARACTERS) {
Characters chars = ev.asCharacters();
if(!chars.isWhiteSpace()) {
JsonNode node = fromString(chars.getData(), ctx);
return new Credential(resource, node);
}
}
}
}
}
} //while(reader.hasNext()) {
}
throw new IllegalArgumentException("No credential inside SVG");
}
private boolean isEndElem(XMLEvent ev, QName name) {
return ev.isEndElement() && ev.asEndElement().getName().equals(name);
}
private boolean isStartElem(XMLEvent ev, QName name) {
return ev.isStartElement() && ev.asStartElement().getName().equals(name);
}
private static final QName OB_CRED_ELEM = new QName("https://purl.imsglobal.org/ob/v3p0", "credential");
private static final QName OB_CRED_VERIFY_ATTR = new QName("verify");
}

View File

@ -0,0 +1,76 @@
package org.oneedtech.inspect.vc.probe;
import static org.oneedtech.inspect.vc.Credential.Type.*;
import static org.oneedtech.inspect.util.code.Defensives.checkNotNull;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.vc.Credential;
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.ImmutableMap;
/**
* A Probe that verifies a credential's context property.
*
* @author mgylling
*/
public class ContextPropertyProbe extends Probe<JsonNode> {
private final Credential.Type type;
public ContextPropertyProbe(Credential.Type type) {
super(ID);
this.type = checkNotNull(type);
}
@Override
public ReportItems run(JsonNode root, RunContext ctx) throws Exception {
ArrayNode contextNode = (ArrayNode) root.get("@context");
if (contextNode == null) {
return notRun("No @context property", ctx);
}
List<String> expected = values.get(values.keySet()
.stream()
.filter(s->s.contains(type))
.findFirst()
.orElseThrow(()-> new IllegalArgumentException(type.name() + " not recognized")));
List<String> given = JsonNodeUtil.asStringList(contextNode);
int pos = 0;
for (String uri : expected) {
if ((given.size() < pos + 1) || !given.get(pos).equals(uri)) {
return error("missing required @context uri " + uri + " at position " + (pos + 1), ctx);
}
pos++;
}
return success(ctx);
}
private final static Map<Set<Credential.Type>, List<String>> values = new ImmutableMap.Builder<Set<Credential.Type>, List<String>>()
.put(Set.of(OpenBadgeCredential, AchievementCredential, EndorsementCredential),
List.of("https://www.w3.org/2018/credentials/v1",
//"https://imsglobal.github.io/openbadges-specification/context.json")) //dev legacy
"https://purl.imsglobal.org/spec/ob/v3p0/context.json"))
.put(Set.of(ClrCredential),
List.of("https://www.w3.org/2018/credentials/v1",
// "https://dc.imsglobal.org/draft/clr/v2p0/context", //dev legacy
// "https://imsglobal.github.io/openbadges-specification/context.json")) //dev legacy
"https://purl.imsglobal.org/spec/clr/v2p0/context.json",
"https://purl.imsglobal.org/spec/ob/v3p0/context.json"))
.build();
public static final String ID = ContextPropertyProbe.class.getSimpleName();
}

View File

@ -0,0 +1,54 @@
package org.oneedtech.inspect.vc.probe;
import java.util.Optional;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.util.resource.Resource;
import org.oneedtech.inspect.util.resource.ResourceType;
import org.oneedtech.inspect.util.resource.detect.TypeDetector;
import org.oneedtech.inspect.vc.Credential;
import org.oneedtech.inspect.vc.payload.PayloadParserFactory;
/**
* A probe that verifies that the incoming credential resource is of a recognized payload type
* and if so extracts and stores the VC json data (a 'Credential' instance)
* in the RunContext.
* @author mgylling
*/
public class CredentialParseProbe extends Probe<Resource> {
@Override
public ReportItems run(Resource resource, RunContext context) throws Exception {
try {
//TODO if .detect reads from a URIResource twice. Cache the resource on first call.
Optional<ResourceType> type = Optional.ofNullable(resource.getType());
if(type.isEmpty() || type.get() == ResourceType.UNKNOWN) {
type = TypeDetector.detect(resource, true);
if(type.isEmpty()) {
//TODO if URI fetch, TypeDetector likely to fail
System.err.println("typedetector fail: extend behavior here");
return fatal("Could not detect credential payload type", context);
} else {
resource.setType(type.get());
}
}
if(!Credential.RECOGNIZED_PAYLOAD_TYPES.contains(type.get())) {
return fatal("Payload type not supported: " + type.get().getName(), context);
}
Credential crd = PayloadParserFactory.of(resource).parse(resource, context);
context.addGeneratedObject(crd);
return success(this, context);
} catch (Exception e) {
return fatal("Error while parsing credential: " + e.getMessage(), context);
}
}
}

View File

@ -0,0 +1,42 @@
package org.oneedtech.inspect.vc.probe;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.ReportItems;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
/**
* A Probe that checks credential subject specifics not capturable by schemata.
*
* @author mgylling
*/
public class CredentialSubjectProbe extends Probe<JsonNode> {
public CredentialSubjectProbe() {
super(ID);
}
@Override
public ReportItems run(JsonNode root, RunContext ctx) throws Exception {
JsonNode subject = root.get("credentialSubject");
if(subject == null) return notRun("no credentialSubject node found", ctx); //error reported by schema
/*
* Check that we have either .id or .identifier populated
*/
JsonNode id = root.get("id");
if (id != null && id.textValue().strip().length() > 0) return success(ctx);
JsonNode identifier = root.get("identifier");
if(identifier != null && identifier instanceof ArrayNode
&& ((ArrayNode)identifier).size() > 0) return success(ctx);
return error("no id in credentialSubject", ctx);
}
public static final String ID = CredentialSubjectProbe.class.getSimpleName();
}

View File

@ -0,0 +1,159 @@
package org.oneedtech.inspect.vc.probe;
import java.io.StringReader;
import java.net.URI;
import org.bouncycastle.util.Arrays;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.vc.Credential;
import org.oneedtech.inspect.vc.util.CachingDocumentLoader;
import com.apicatalog.ld.DocumentError;
import com.apicatalog.multibase.Multibase;
import com.apicatalog.vc.processor.StatusVerifier;
import com.danubetech.verifiablecredentials.VerifiableCredential;
import info.weboftrust.ldsignatures.verifier.Ed25519Signature2020LdVerifier;
/**
* A Probe that verifies a credential's embedded proof.
* @author mgylling
*/
public class EmbeddedProofProbe extends Probe<Credential> {
public EmbeddedProofProbe() {
super(ID);
}
/*
* Using verifiable-credentials-java (https://github.com/danubetech/verifiable-credentials-java)
*/
@Override
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
//TODO check that proof is Ed25519 - issue error if not ("type": "Ed25519Signature2020",
//TODO check value "proofPurpose": "assertionMethod", if not error
VerifiableCredential vc = VerifiableCredential.fromJson(new StringReader(crd.getJson().toString()));
vc.setDocumentLoader(new CachingDocumentLoader());
URI method = vc.getLdProof().getVerificationMethod();
// The verification method must dereference to an Ed25519VerificationKey2020.
// Danubetech's Ed25519Signature2020LdVerifier expects the decoded public key
// from the Ed25519VerificationKey2020 (32 bytes).
String publicKeyMultibase = "";
// Formats accepted:
//
// [controller]#[publicKeyMultibase]
// did:key:[publicKeyMultibase]
// [publicKeyMultibase]
// TODO fourth format that we don't support yet: a URL that returns a Ed25519VerificationKey2020
// if starts with http and does not have hashcode, try fetch and see if returns Ed25519VerificationKey2020
// property is publicKeyMultibase
if (method.toString().contains("#")) {
publicKeyMultibase = method.getFragment();
} else {
if (method.toString().startsWith("did")) {
String didScheme = method.getSchemeSpecificPart();
if (didScheme.startsWith("key:")) {
publicKeyMultibase = didScheme.substring(4);
} else {
return error("Unknown verification method: " + method.toString(), ctx);
}
} else {
publicKeyMultibase = method.toString();
}
}
// Decode the Multibase to Multicodec and check that it is an Ed25519 public key
byte[] publicKeyMulticodec;
try {
publicKeyMulticodec = Multibase.decode(publicKeyMultibase);
if (publicKeyMulticodec[0] != -19 || publicKeyMulticodec[1] != 1) {
return error("Verification method does not contain an Ed25519 public key", ctx);
}
} catch (Exception e) {
return error("Verification method is invalid: " + e.getMessage(), ctx);
}
// Extract the publicKey bytes from the Multicodec
byte[] publicKey = Arrays.copyOfRange(publicKeyMulticodec, 2, publicKeyMulticodec.length);
Ed25519Signature2020LdVerifier verifier = new Ed25519Signature2020LdVerifier(publicKey);
//TODO find out whether we also should check that controller matches issuer ID:
// if [controller]#[publicKeyMultibase] format - check [controller] segment
// 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.
try {
boolean verify = verifier.verify(vc);
if (!verify) {
return error("Embedded proof verification failed.", ctx);
}
} catch (Exception e) {
return fatal("Embedded proof verification failed:" + e.getMessage(), ctx);
}
return success(ctx);
}
/*
* 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
* Iron internally retests something that we're already testing out in the
* Inspector class (e.g. expiration). But use this for now -- and remember
* that this probe is only run if the given credential has internal proof
* (aka is not a jwt).
*/
// /*
// * Using iron-verifiable-credentials (https://github.com/filip26/iron-verifiable-credentials)
// */
// @Override
// public ReportItems run(Credential crd, RunContext ctx) throws Exception {
// JsonDocument jsonDoc = JsonDocument.of(new StringReader(crd.getJson().toString()));
// JsonObject json = jsonDoc.getJsonContent().get().asJsonObject();
// try {
// Vc.verify(json)
// .loader(new CachingDocumentLoader())
// .useBundledContexts(false) //we control the cache in the loader
// .statusVerifier(new IronNoopStatusVerifier())
// //.domain(...)
// //.didResolver(...)
// .isValid();
// } catch (DocumentError e) {
// return error(e.getType() + " " + e.getSubject(), ctx);
// } catch (VerificationError e) {
// //System.err.println(e.getCode() + " (ProofVerifierProbe)");
// if(e.getCode() == Code.Internal) {
// return exception(e.getMessage(), ctx.getResource());
// } else if(e.getCode().equals(Code.Expired)) {
// //handled by other probe
// } else {
// return fatal(e.getCode().name() + " " + e.getMessage(), ctx);
// }
//
// }
// return success(ctx);
// }
private static final class IronNoopStatusVerifier implements StatusVerifier {
@Override
public void verify(Status status) throws DocumentError, VerifyError {
// noop
}
}
public static final String ID = EmbeddedProofProbe.class.getSimpleName();
}

View File

@ -0,0 +1,43 @@
package org.oneedtech.inspect.vc.probe;
import java.time.ZonedDateTime;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.vc.Credential;
import com.fasterxml.jackson.databind.JsonNode;
/**
* A Probe that verifies a credential's expiration status
* @author mgylling
*/
public class ExpirationProbe extends Probe<Credential> {
public ExpirationProbe() {
super(ID);
}
@Override
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
/*
* If the AchievementCredential or EndorsementCredential has an expirationDate property
* and the expiration date is prior to the current date, the credential has expired.
*/
JsonNode node = crd.getJson().get("expirationDate");
if(node != null) {
try {
ZonedDateTime expirationDate = ZonedDateTime.parse(node.textValue());
if (ZonedDateTime.now().isAfter(expirationDate)) {
return fatal("The credential has expired (expiration date was " + node.asText() + ").", ctx);
}
} catch (Exception e) {
return exception("Error while checking expirationDate: " + e.getMessage(), ctx.getResource());
}
}
return success(ctx);
}
public static final String ID = ExpirationProbe.class.getSimpleName();
}

View File

@ -0,0 +1,151 @@
package org.oneedtech.inspect.vc.probe;
import static org.oneedtech.inspect.util.code.Defensives.checkTrue;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.vc.Credential;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Splitter;
/**
* A Probe that verifies credential external proof (jwt)
* @author mlyon
*/
public class ExternalProofProbe extends Probe<Credential> {
public ExternalProofProbe() {
super(ID);
}
@Override
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
try {
verifySignature(crd, ctx);
} catch (Exception e) {
return fatal("Error verifying jwt signature: " + e.getMessage(), ctx);
}
return success(ctx);
}
private void verifySignature(Credential crd, RunContext ctx) throws Exception {
checkTrue(crd.getJwt().isPresent(), "no jwt supplied");
checkTrue(crd.getJwt().get().length() > 0, "no jwt supplied");
DecodedJWT decodedJwt = null;
String jwt = crd.getJwt().get();
List<String> parts = Splitter.on('.').splitToList(jwt);
if(parts.size() != 3) throw new IllegalArgumentException("invalid jwt");
final Decoder decoder = Base64.getUrlDecoder();
String joseHeader = new String(decoder.decode(parts.get(0)));
ObjectMapper mapper = ((ObjectMapper)ctx.get(RunContext.Key.JACKSON_OBJECTMAPPER));
JsonNode headerObj = mapper.readTree(joseHeader);
//MUST be "RS256"
JsonNode alg = headerObj.get("alg");
if(alg == null || !alg.textValue().equals("RS256")) { throw new Exception("alg must be present and must be 'RS256'"); }
//TODO: decoded jwt will check timestamps, but shall we explicitly break these out?
//Option 1, fetch directly from header
JsonNode jwk = headerObj.get("jwk");
//Option 2, fetch from a hosting url
JsonNode kid = headerObj.get("kid");
if(jwk == null && kid == null) { throw new Exception("Key must present in either jwk or kid value."); }
if(kid != null){
//Load jwk JsonNode from url and do the rest the same below.
//TODO Consider additional testing.
String kidUrl = kid.textValue();
String jwkResponse = fetchJwk(kidUrl);
if(jwkResponse == null) { throw new Exception("Unable to retrieve jwk value from url specified in kid."); }
jwk = mapper.readTree(jwkResponse);
}
//Clean up may be required. Currently need to cleanse extra double quoting.
String modulusString = jwk.get("n").textValue();
String exponentString = jwk.get("e").textValue();
BigInteger modulus = new BigInteger(1, decoder.decode(modulusString));
BigInteger exponent = new BigInteger(1, decoder.decode(exponentString));
PublicKey pub = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, exponent));
Algorithm algorithm = Algorithm.RSA256((RSAPublicKey)pub, null);
JWTVerifier verifier = JWT.require(algorithm)
.build(); //Reusable verifier instance
try {
decodedJwt = verifier.verify(jwt);
}
catch(SignatureVerificationException ex){
throw new Exception("JWT Invalid signature", ex);
}
catch(AlgorithmMismatchException ex){
throw new Exception("JWT Algorithm mismatch", ex);
}
catch(TokenExpiredException ex){
throw new Exception("JWT Token expired", ex);
}
catch(InvalidClaimException ex){
throw new Exception("JWT, one or more claims are invalid", ex);
}
}
private String fetchJwk(String fetchUrl){
String responseString = null;
try {
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(fetchUrl);
CloseableHttpResponse response = client.execute(httpGet);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
responseString = EntityUtils.toString(entity, "UTF-8");
}
client.close();
}
catch(Exception ex){
responseString = null;
}
return responseString;
}
public static final String ID = ExternalProofProbe.class.getSimpleName();
}

View File

@ -0,0 +1,74 @@
package org.oneedtech.inspect.vc.probe;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.schema.SchemaKey;
import org.oneedtech.inspect.vc.Credential;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
/**
* Detect inline schemas in a credential and run them.
* @author mgylling
*/
public class InlineJsonSchemaProbe extends Probe<JsonNode> {
private static final Set<String> types = Set.of("1EdTechJsonSchemaValidator2019");
private SchemaKey skip;
public InlineJsonSchemaProbe() {
super(ID);
}
public InlineJsonSchemaProbe(SchemaKey skip) {
super(ID);
this.skip = skip;
}
@Override
public ReportItems run(JsonNode root, RunContext ctx) throws Exception {
List<ReportItems> accumulator = new ArrayList<>();
Set<String> ioErrors = new HashSet<>();
//note - we don't get deep nested ones in e.g. EndorsementCredential
JsonNode credentialSchemaNode = root.get("credentialSchema");
if(credentialSchemaNode == null) return success(ctx);
ArrayNode schemas = (ArrayNode) credentialSchemaNode; //TODO guard this cast
for(JsonNode schemaNode : schemas) {
JsonNode typeNode = schemaNode.get("type");
if(typeNode == null || !types.contains(typeNode.asText())) continue;
JsonNode idNode = schemaNode.get("id");
if(idNode == null) continue;
String id = idNode.asText().strip();
if(ioErrors.contains(id)) continue;
if(equals(skip, id)) continue;
try {
accumulator.add(new JsonSchemaProbe(id).run(root, ctx));
} catch (Exception e) {
if(!ioErrors.contains(id)) {
ioErrors.add(id);
accumulator.add(error("Could not read schema resource " + id, ctx));
}
}
}
return new ReportItems(accumulator);
}
private boolean equals(SchemaKey key, String id) {
if(key == null) return false;
return key.getCanonicalURI().equals(id);
}
public static final String ID = InlineJsonSchemaProbe.class.getSimpleName();
}

View File

@ -0,0 +1,43 @@
package org.oneedtech.inspect.vc.probe;
import java.time.ZonedDateTime;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.vc.Credential;
import com.fasterxml.jackson.databind.JsonNode;
/**
* A Probe that verifies a credential's issuance status
* @author mgylling
*/
public class IssuanceProbe extends Probe<Credential> {
public IssuanceProbe() {
super(ID);
}
@Override
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
/*
* If the AchievementCredential or EndorsementCredential issuanceDate
* property after the current date, the credential is not yet valid.
*/
JsonNode node = crd.getJson().get("issuanceDate");
if(node != null) {
try {
ZonedDateTime issuanceDate = ZonedDateTime.parse(node.textValue());
if (issuanceDate.isAfter(ZonedDateTime.now())) {
return fatal("The credential is not yet issued (issuance date is " + node.asText() + ").", ctx);
}
} catch (Exception e) {
return exception("Error while checking issuanceDate: " + e.getMessage(), ctx.getResource());
}
}
return success(ctx);
}
public static final String ID = IssuanceProbe.class.getSimpleName();
}

View File

@ -0,0 +1,79 @@
package org.oneedtech.inspect.vc.probe;
import static org.oneedtech.inspect.core.probe.RunContext.Key.JACKSON_OBJECTMAPPER;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.List;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.vc.Credential;
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* A Probe that verifies a credential's revocation status.
* @author mgylling
*/
public class RevocationListProbe extends Probe<Credential> {
public RevocationListProbe() {
super(ID);
}
@Override
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
/*
* If the AchievementCredential or EndorsementCredential has a credentialStatus property
* and the type of the CredentialStatus object is 1EdTechRevocationList, fetch the
* credential status from the URL provided. If the request is unsuccessful,
* report a warning, not an error.
*/
JsonNode credentialStatus = crd.getJson().get("credentialStatus");
if(credentialStatus != null) {
JsonNode type = credentialStatus.get("type");
if(type != null && type.asText().strip().equals("1EdTechRevocationList")) {
JsonNode listID = credentialStatus.get("id");
if(listID != null) {
try {
URL url = new URI(listID.asText().strip()).toURL();
try (InputStream is = url.openStream()) {
JsonNode revocList = ((ObjectMapper)ctx.get(JACKSON_OBJECTMAPPER)).readTree(is.readAllBytes());
/* To check if a credential has been revoked, the verifier issues a GET request
* to the URL of the issuer's 1EdTech Revocation List Status Method. If the
* credential's id is in the list of revokedCredentials and the value of
* revoked is true or ommitted, the issuer has revoked the credential. */
JsonNode crdID = crd.getJson().get("id"); //TODO these != checks sb removed (trigger warning)
if(crdID != null) {
List<JsonNode> list = JsonNodeUtil.asNodeList(revocList.get("revokedCredentials"));
if(list != null) {
for(JsonNode item : list) {
JsonNode revID = item.get("id");
JsonNode revoked = item.get("revoked");
if(revID != null && revID.equals(crdID) && (revoked == null || revoked.asBoolean())) {
return fatal("Credential has been revoked", ctx);
}
}
}
}
}
} catch (Exception e) {
return warning("Error when fetching credentialStatus resource " + e.getMessage(), ctx);
}
}
}
}
return success(ctx);
}
public static final String ID = RevocationListProbe.class.getSimpleName();
}

View File

@ -0,0 +1,64 @@
package org.oneedtech.inspect.vc.probe;
import static org.oneedtech.inspect.util.code.Defensives.checkNotNull;
import java.util.List;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.vc.Credential;
import org.oneedtech.inspect.vc.Credential.Type;
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
/**
* A Probe that verifies a credential's type property.
*
* @author mgylling
*/
public class TypePropertyProbe extends Probe<JsonNode> {
private final Credential.Type expected;
public TypePropertyProbe(Credential.Type expected) {
super(ID);
this.expected = checkNotNull(expected);
}
@Override
public ReportItems run(JsonNode root, RunContext ctx) throws Exception {
ArrayNode typeNode = (ArrayNode) root.get("type");
if (typeNode == null)
return fatal("No type property", ctx);
List<String> values = JsonNodeUtil.asStringList(typeNode);
if (!values.contains("VerifiableCredential")) {
return fatal("The type property does not contain the entry 'VerifiableCredential'", ctx);
}
if (expected == Credential.Type.OpenBadgeCredential) {
if (!values.contains("OpenBadgeCredential") && !values.contains("AchievementCredential")) {
return fatal("The type property does not contain one of 'OpenBadgeCredential' or 'AchievementCredential'", ctx);
}
} else if (expected == Credential.Type.ClrCredential) {
if (!values.contains("ClrCredential")) {
return fatal("The type property does not contain the entry 'ClrCredential'", ctx);
}
} else if (expected == Credential.Type.EndorsementCredential) {
if (!values.contains("EndorsementCredential")) {
return fatal("The type property does not contain the entry 'EndorsementCredential'", ctx);
}
} else {
// TODO implement
throw new IllegalStateException();
}
return success(ctx);
}
public static final String ID = TypePropertyProbe.class.getSimpleName();
}

View File

@ -0,0 +1,78 @@
package org.oneedtech.inspect.vc.util;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.time.Duration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.oneedtech.inspect.util.code.Tuple;
import com.apicatalog.jsonld.JsonLdError;
import com.apicatalog.jsonld.JsonLdErrorCode;
import com.apicatalog.jsonld.document.Document;
import com.apicatalog.jsonld.document.JsonDocument;
import com.apicatalog.jsonld.loader.DocumentLoader;
import com.apicatalog.jsonld.loader.DocumentLoaderOptions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Resources;
/**
* A com.apicatalog DocumentLoader with a threadsafe static cache.
*
* @author mgylling
*/
public class CachingDocumentLoader implements DocumentLoader {
@Override
public Document loadDocument(URI url, DocumentLoaderOptions options) throws JsonLdError {
Tuple<String, DocumentLoaderOptions> tpl = new Tuple<>(url.toASCIIString(), options);
try {
return documentCache.get(tpl);
} catch (Exception e) {
logger.error("documentCache not able to load {}", url);
throw new JsonLdError(JsonLdErrorCode.INVALID_REMOTE_CONTEXT, e.getMessage());
}
}
static final ImmutableMap<String, URL> bundled = ImmutableMap.<String, URL>builder()
.put("https://purl.imsglobal.org/spec/clr/v2p0/context.json",Resources.getResource("contexts/clr-v2p0.json"))
.put("https://purl.imsglobal.org/spec/ob/v3p0/context.json",Resources.getResource("contexts/ob-v3p0.json"))
.put("https://imsglobal.github.io/openbadges-specification/context.json",Resources.getResource("contexts/obv3x.jsonld"))
.put("https://www.w3.org/ns/did/v1", Resources.getResource("contexts/did-v1.jsonld"))
.put("https://www.w3.org/ns/odrl.jsonld", Resources.getResource("contexts/odrl.jsonld"))
.put("https://w3id.org/security/suites/ed25519-2020/v1",Resources.getResource("contexts/security-suites-ed25519-2020-v1.jsonld"))
.put("https://www.w3.org/2018/credentials/v1", Resources.getResource("contexts/2018-credentials-v1.jsonld"))
.put("https://w3id.org/security/v1", Resources.getResource("contexts/security-v1.jsonld"))
.put("https://w3id.org/security/v2", Resources.getResource("contexts/security-v2.jsonld"))
.put("https://w3id.org/security/v3", Resources.getResource("contexts/security-v3-unstable.jsonld"))
.put("https://w3id.org/security/bbs/v1", Resources.getResource("contexts/security-bbs-v1.jsonld"))
.put("https://w3id.org/security/suites/secp256k1-2019/v1", Resources.getResource("contexts/suites-secp256k1-2019.jsonld"))
.put("https://w3id.org/security/suites/ed25519-2018/v1", Resources.getResource("contexts/suites-ed25519-2018.jsonld"))
.put("https://w3id.org/security/suites/x25519-2019/v1", Resources.getResource("contexts/suites-x25519-2019.jsonld"))
.put("https://w3id.org/security/suites/jws-2020/v1", Resources.getResource("contexts/suites-jws-2020.jsonld"))
.build();
static final LoadingCache<Tuple<String, DocumentLoaderOptions>, Document> documentCache = CacheBuilder.newBuilder()
.initialCapacity(32).maximumSize(64).expireAfterAccess(Duration.ofHours(24))
.build(new CacheLoader<Tuple<String, DocumentLoaderOptions>, Document>() {
public Document load(final Tuple<String, DocumentLoaderOptions> id) throws Exception {
try (InputStream is = bundled.keySet().contains(id.t1)
? bundled.get(id.t1).openStream()
: new URI(id.t1).toURL().openStream();) {
return JsonDocument.of(is);
}
}
});
public static void reset() {
documentCache.invalidateAll();
}
private static final Logger logger = LogManager.getLogger();
}

View File

@ -0,0 +1,58 @@
package org.oneedtech.inspect.vc.util;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.oneedtech.inspect.core.probe.json.JsonPathEvaluator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
/**
* Json node utilities.
* @author mgylling
*/
public class JsonNodeUtil {
public static List<JsonNode> asNodeList(JsonNode root, String jsonPath, JsonPathEvaluator evaluator) {
List<JsonNode> list = new ArrayList<>();
ArrayNode array = evaluator.eval(jsonPath, root);
for(JsonNode node : array) {
if(!(node instanceof ArrayNode)) {
list.add(node);
} else {
ArrayNode values = (ArrayNode) node;
for(JsonNode value : values) {
list.add(value);
}
}
}
return list;
}
public static List<String> asStringList(JsonNode node) {
if(!(node instanceof ArrayNode)) {
return List.of(node.asText());
} else {
ArrayNode arrayNode = (ArrayNode)node;
return StreamSupport
.stream(arrayNode.spliterator(), false)
.map(n->n.asText().strip())
.collect(Collectors.toList());
}
}
public static List<JsonNode> asNodeList(JsonNode node) {
if(node == null) return null;
if(!(node instanceof ArrayNode)) {
return List.of(node);
} else {
ArrayNode arrayNode = (ArrayNode)node;
return StreamSupport
.stream(arrayNode.spliterator(), false)
.collect(Collectors.toList());
}
}
}

View File

@ -0,0 +1,69 @@
{
"@context": [
{
"@version": 1.1
},
"https://www.w3.org/ns/odrl.jsonld",
{
"ex": "https://example.org/examples#",
"schema": "http://schema.org/",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"3rdPartyCorrelation": "ex:3rdPartyCorrelation",
"AllVerifiers": "ex:AllVerifiers",
"Archival": "ex:Archival",
"BachelorDegree": "ex:BachelorDegree",
"Child": "ex:Child",
"CLCredentialDefinition2019": "ex:CLCredentialDefinition2019",
"CLSignature2019": "ex:CLSignature2019",
"IssuerPolicy": "ex:IssuerPolicy",
"HolderPolicy": "ex:HolderPolicy",
"Mother": "ex:Mother",
"RelationshipCredential": "ex:RelationshipCredential",
"UniversityDegreeCredential": "ex:UniversityDegreeCredential",
"AlumniCredential": "ex:AlumniCredential",
"DisputeCredential": "ex:DisputeCredential",
"PrescriptionCredential": "ex:PrescriptionCredential",
"ZkpExampleSchema2018": "ex:ZkpExampleSchema2018",
"issuerData": "ex:issuerData",
"attributes": "ex:attributes",
"signature": "ex:signature",
"signatureCorrectnessProof": "ex:signatureCorrectnessProof",
"primaryProof": "ex:primaryProof",
"nonRevocationProof": "ex:nonRevocationProof",
"alumniOf": {
"@id": "schema:alumniOf",
"@type": "rdf:HTML"
},
"child": {
"@id": "ex:child",
"@type": "@id"
},
"degree": "ex:degree",
"degreeType": "ex:degreeType",
"degreeSchool": "ex:degreeSchool",
"college": "ex:college",
"name": {
"@id": "schema:name",
"@type": "rdf:HTML"
},
"givenName": "schema:givenName",
"familyName": "schema:familyName",
"parent": {
"@id": "ex:parent",
"@type": "@id"
},
"referenceId": "ex:referenceId",
"documentPresence": "ex:documentPresence",
"evidenceDocument": "ex:evidenceDocument",
"spouse": "schema:spouse",
"subjectPresence": "ex:subjectPresence",
"verifier": {
"@id": "ex:verifier",
"@type": "@id"
},
"currentStatus": "ex:currentStatus",
"statusReason": "ex:statusReason",
"prescription": "ex:prescription"
}
]
}

View File

@ -0,0 +1,315 @@
{
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"VerifiableCredential": {
"@id": "https://www.w3.org/2018/credentials#VerifiableCredential",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"cred": "https://www.w3.org/2018/credentials#",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"credentialSchema": {
"@id": "cred:credentialSchema",
"@type": "@id",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"cred": "https://www.w3.org/2018/credentials#",
"JsonSchemaValidator2018": "cred:JsonSchemaValidator2018"
}
},
"credentialStatus": {
"@id": "cred:credentialStatus",
"@type": "@id"
},
"credentialSubject": {
"@id": "cred:credentialSubject",
"@type": "@id"
},
"evidence": {
"@id": "cred:evidence",
"@type": "@id"
},
"expirationDate": {
"@id": "cred:expirationDate",
"@type": "xsd:dateTime"
},
"holder": {
"@id": "cred:holder",
"@type": "@id"
},
"issued": {
"@id": "cred:issued",
"@type": "xsd:dateTime"
},
"issuer": {
"@id": "cred:issuer",
"@type": "@id"
},
"issuanceDate": {
"@id": "cred:issuanceDate",
"@type": "xsd:dateTime"
},
"proof": {
"@id": "sec:proof",
"@type": "@id",
"@container": "@graph"
},
"refreshService": {
"@id": "cred:refreshService",
"@type": "@id",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"cred": "https://www.w3.org/2018/credentials#",
"ManualRefreshService2018": "cred:ManualRefreshService2018"
}
},
"termsOfUse": {
"@id": "cred:termsOfUse",
"@type": "@id"
},
"validFrom": {
"@id": "cred:validFrom",
"@type": "xsd:dateTime"
},
"validUntil": {
"@id": "cred:validUntil",
"@type": "xsd:dateTime"
}
}
},
"VerifiablePresentation": {
"@id": "https://www.w3.org/2018/credentials#VerifiablePresentation",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"cred": "https://www.w3.org/2018/credentials#",
"sec": "https://w3id.org/security#",
"holder": {
"@id": "cred:holder",
"@type": "@id"
},
"proof": {
"@id": "sec:proof",
"@type": "@id",
"@container": "@graph"
},
"verifiableCredential": {
"@id": "cred:verifiableCredential",
"@type": "@id",
"@container": "@graph"
}
}
},
"EcdsaSecp256k1Signature2019": {
"@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"challenge": "sec:challenge",
"created": {
"@id": "http://purl.org/dc/terms/created",
"@type": "xsd:dateTime"
},
"domain": "sec:domain",
"expires": {
"@id": "sec:expiration",
"@type": "xsd:dateTime"
},
"jws": "sec:jws",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"assertionMethod": {
"@id": "sec:assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "sec:authenticationMethod",
"@type": "@id",
"@container": "@set"
}
}
},
"proofValue": "sec:proofValue",
"verificationMethod": {
"@id": "sec:verificationMethod",
"@type": "@id"
}
}
},
"EcdsaSecp256r1Signature2019": {
"@id": "https://w3id.org/security#EcdsaSecp256r1Signature2019",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"challenge": "sec:challenge",
"created": {
"@id": "http://purl.org/dc/terms/created",
"@type": "xsd:dateTime"
},
"domain": "sec:domain",
"expires": {
"@id": "sec:expiration",
"@type": "xsd:dateTime"
},
"jws": "sec:jws",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"assertionMethod": {
"@id": "sec:assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "sec:authenticationMethod",
"@type": "@id",
"@container": "@set"
}
}
},
"proofValue": "sec:proofValue",
"verificationMethod": {
"@id": "sec:verificationMethod",
"@type": "@id"
}
}
},
"Ed25519Signature2018": {
"@id": "https://w3id.org/security#Ed25519Signature2018",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"challenge": "sec:challenge",
"created": {
"@id": "http://purl.org/dc/terms/created",
"@type": "xsd:dateTime"
},
"domain": "sec:domain",
"expires": {
"@id": "sec:expiration",
"@type": "xsd:dateTime"
},
"jws": "sec:jws",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"assertionMethod": {
"@id": "sec:assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "sec:authenticationMethod",
"@type": "@id",
"@container": "@set"
}
}
},
"proofValue": "sec:proofValue",
"verificationMethod": {
"@id": "sec:verificationMethod",
"@type": "@id"
}
}
},
"RsaSignature2018": {
"@id": "https://w3id.org/security#RsaSignature2018",
"@context": {
"@version": 1.1,
"@protected": true,
"challenge": "sec:challenge",
"created": {
"@id": "http://purl.org/dc/terms/created",
"@type": "xsd:dateTime"
},
"domain": "sec:domain",
"expires": {
"@id": "sec:expiration",
"@type": "xsd:dateTime"
},
"jws": "sec:jws",
"nonce": "sec:nonce",
"proofPurpose": {
"@id": "sec:proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"assertionMethod": {
"@id": "sec:assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "sec:authenticationMethod",
"@type": "@id",
"@container": "@set"
}
}
},
"proofValue": "sec:proofValue",
"verificationMethod": {
"@id": "sec:verificationMethod",
"@type": "@id"
}
}
},
"proof": {
"@id": "https://w3id.org/security#proof",
"@type": "@id",
"@container": "@graph"
}
}
}

View File

@ -0,0 +1,53 @@
{
"@context": {
"id": "@id",
"type": "@type",
"ClrCredential": {
"@id": "https://purl.imsglobal.org/spec/clr/v2p0/vocab.html#ClrCredential",
"@context": {
"id": "@id",
"type": "@type"
}
},
"ClrSubject": {
"@id": "https://purl.imsglobal.org/spec/clr/v2p0/vocab.html#ClrSubject",
"@context": {
"id": "@id",
"type": "@type",
"cred": "https://www.w3.org/2018/credentials#",
"obi": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#",
"achievement": {
"@id": "https://purl.imsglobal.org/spec/clr/v2p0/vocab.html#achievement",
"@type": "obi:Achievement",
"@container": "@set"
},
"association": {
"@id": "https://purl.imsglobal.org/spec/clr/v2p0/vocab.html#association",
"@type": "https://purl.imsglobal.org/spec/clr/v2p0/vocab.html#Association",
"@container": "@set"
},
"verifiableCredential": {
"@id": "https://purl.imsglobal.org/spec/clr/v2p0/vocab.html#verifiableCredential",
"@type": "cred:verifiableCredential",
"@container": "@set"
}
}
},
"Association": {
"@id": "https://purl.imsglobal.org/spec/clr/v2p0/vocab.html#Association",
"@context": {
"associationType": {
"@id": "https://purl.imsglobal.org/spec/clr/v2p0/vocab.html#AssociationType"
},
"sourceId": {
"@id": "https://purl.imsglobal.org/spec/clr/v2p0/vocab.html#sourceId",
"@type": "xsd:anyURI"
},
"targetId": {
"@id": "https://purl.imsglobal.org/spec/clr/v2p0/vocab.html#targetId",
"@type": "xsd:anyURI"
}
}
}
}
}

View File

@ -0,0 +1,58 @@
{
"@context": {
"@protected": true,
"id": "@id",
"type": "@type",
"alsoKnownAs": {
"@id": "https://www.w3.org/ns/activitystreams#alsoKnownAs",
"@type": "@id"
},
"assertionMethod": {
"@id": "https://w3id.org/security#assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "https://w3id.org/security#authenticationMethod",
"@type": "@id",
"@container": "@set"
},
"capabilityDelegation": {
"@id": "https://w3id.org/security#capabilityDelegationMethod",
"@type": "@id",
"@container": "@set"
},
"capabilityInvocation": {
"@id": "https://w3id.org/security#capabilityInvocationMethod",
"@type": "@id",
"@container": "@set"
},
"controller": {
"@id": "https://w3id.org/security#controller",
"@type": "@id"
},
"keyAgreement": {
"@id": "https://w3id.org/security#keyAgreementMethod",
"@type": "@id",
"@container": "@set"
},
"service": {
"@id": "https://www.w3.org/ns/did#service",
"@type": "@id",
"@context": {
"@protected": true,
"id": "@id",
"type": "@type",
"serviceEndpoint": {
"@id": "https://www.w3.org/ns/did#serviceEndpoint",
"@type": "@id"
}
}
},
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
}

View File

@ -0,0 +1,440 @@
{
"@context": {
"id": "@id",
"type": "@type",
"xsd": "https://www.w3.org/2001/XMLSchema#",
"OpenBadgeCredential": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#OpenBadgeCredential"
},
"Achievement": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Achievement",
"@context": {
"achievementType": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#achievementType",
"@type": "xsd:string"
},
"alignment": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#alignment",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Alignment",
"@container": "@set"
},
"creator": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Profile"
},
"creditsAvailable": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#creditsAvailable",
"@type": "xsd:float"
},
"criteria": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Criteria",
"@type": "@id"
},
"fieldOfStudy": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#fieldOfStudy",
"@type": "xsd:string"
},
"humanCode": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#humanCode",
"@type": "xsd:string"
},
"otherIdentifier": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#otherIdentifier",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#IdentifierEntry",
"@container": "@set"
},
"related": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#related",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Related",
"@container": "@set"
},
"resultDescription": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#resultDescription",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#ResultDescription",
"@container": "@set"
},
"specialization": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#specialization",
"@type": "xsd:string"
},
"tag": {
"@id": "https://schema.org/keywords",
"@type": "xsd:string",
"@container": "@set"
},
"version": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#version",
"@type": "xsd:string"
}
}
},
"AchievementCredential": {
"@id": "OpenBadgeCredential"
},
"AchievementSubject": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#AchievementSubject",
"@context": {
"achievement": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Achievement"
},
"activityEndDate": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#activityEndDate",
"@type": "xsd:date"
},
"activityStartDate": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#activityStartDate",
"@type": "xsd:date"
},
"creditsEarned": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#creditsEarned",
"@type": "xsd:float"
},
"identifier": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#identifier",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#IdentityObject",
"@container": "@set"
},
"licenseNumber": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#licenseNumber",
"@type": "xsd:string"
},
"result": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#result",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Result",
"@container": "@set"
},
"role": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#role",
"@type": "xsd:string"
},
"source": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#source",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Profile"
},
"term": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#term",
"@type": "xsd:string"
}
}
},
"Address": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Address",
"@context": {
"addressCountry": {
"@id": "https://schema.org/addressCountry",
"@type": "xsd:string"
},
"addressCountryCode": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#CountryCode",
"@type": "xsd:string"
},
"addressLocality": {
"@id": "https://schema.org/addressLocality",
"@type": "xsd:string"
},
"addressRegion": {
"@id": "https://schema.org/addressRegion",
"@type": "xsd:string"
},
"geo": {
"@id" : "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#GeoCoordinates"
},
"postOfficeBoxNumber": {
"@id": "https://schema.org/postOfficeBoxNumber",
"@type": "xsd:string"
},
"postalCode": {
"@id": "https://schema.org/postalCode",
"@type": "xsd:string"
},
"streetAddress": {
"@id": "https://schema.org/streetAddress",
"@type": "xsd:string"
}
}
},
"Alignment": {
"@id": "https://schema.org/Alignment",
"@context": {
"targetCode": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#targetCode",
"@type": "xsd:string"
},
"targetDescription": {
"@id": "https://schema.org/targetDescription",
"@type": "xsd:string"
},
"targetFramework": {
"@id": "https://schema.org/targetFramework",
"@type": "xsd:string"
},
"targetName": {
"@id": "https://schema.org/targetName",
"@type": "xsd:string"
},
"targetType": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#targetType",
"@type": "xsd:string"
},
"targetUrl": {
"@id": "https://schema.org/targetUrl",
"@type": "xsd:anyURI"
}
}
},
"Criteria": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Criteria"
},
"EndorsementCredential": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#EndorsementCredential"
},
"EndorsementSubject": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#EndorsementSubject",
"@context": {
"endorsementComment": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#endorsementComment",
"@type": "xsd:string"
}
}
},
"Evidence": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Evidence",
"@context": {
"audience": {
"@id": "https://schema.org/audience",
"@type": "xsd:string"
},
"genre": {
"@id": "https://schema.org/genre",
"@type": "xsd:string"
}
}
},
"GeoCoordinates": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#GeoCoordinates",
"@context": {
"latitude": {
"@id": "https://schema.org/latitude",
"@type": "xsd:string"
},
"longitude": {
"@id": "https://schema.org/longitude",
"@type": "xsd:string"
}
}
},
"IdentifierEntry": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#IdentifierEntry",
"@context": {
"identifier": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#identifier",
"@type": "xsd:string"
},
"identifierType": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#identifierType",
"@type": "xsd:string"
}
}
},
"IdentityObject": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#IdentityObject",
"@context": {
"hashed": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#hashed",
"@type": "xsd:boolean"
},
"identityHash": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#identityHash",
"@type": "xsd:string"
},
"identityType": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#identityType",
"@type": "xsd:string"
},
"salt": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#salt",
"@type": "xsd:string"
}
}
},
"Image": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Image",
"@context": {
"caption": {
"@id": "https://schema.org/caption",
"@type": "xsd:string"
}
}
},
"Profile": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Profile",
"@context": {
"additionalName": {
"@id": "https://schema.org/additionalName",
"@type": "xsd:string"
},
"address": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Address"
},
"dateOfBirth": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#dateOfBirth",
"@type": "xsd:date"
},
"email": {
"@id": "https://schema.org/email",
"@type": "xsd:string"
},
"familyName": {
"@id": "https://schema.org/familyName",
"@type": "xsd:string"
},
"familyNamePrefix": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#familyNamePrefix",
"@type": "xsd:string"
},
"givenName": {
"@id": "https://schema.org/givenName",
"@type": "xsd:string"
},
"honorificPrefix": {
"@id": "https://schema.org/honorificPrefix",
"@type": "xsd:string"
},
"honorificSuffix": {
"@id": "https://schema.org/honorificSuffix",
"@type": "xsd:string"
},
"otherIdentifier": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#otherIdentifier",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#IdentifierEntry",
"@container": "@set"
},
"parentOrg": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#parentOrg",
"@type": "xsd:string"
},
"patronymicName": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#patronymicName",
"@type": "xsd:string"
},
"phone": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#PhoneNumber",
"@type": "xsd:string"
},
"official": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#official",
"@type": "xsd:string"
}
}
},
"Related": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Related",
"@context": {
"version": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#version",
"@type": "xsd:string"
}
}
},
"Result": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Result",
"@context": {
"achievedLevel": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#achievedLevel",
"@type": "xsd:anyURI"
},
"resultDescription": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#resultDescription",
"@type": "xsd:anyURI"
},
"status": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#status",
"@type": "xsd:string"
},
"value": {
"@id": "https://schema.org/value",
"@type": "xsd:string"
}
}
},
"ResultDescription": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#ResultDescription",
"@context": {
"allowedValue": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#allowedValue",
"@type": "xsd:string",
"@container": "@set"
},
"requiredLevel": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#requiredLevel",
"@type": "xsd:anyURI"
},
"requiredValue": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#requiredValue",
"@type": "xsd:string"
},
"resultType": {
"@id":"https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#resultType",
"@type": "xsd:string"
},
"rubricCriterionLevel": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#rubricCriterionLevel",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#RubricCriterionLevel",
"@container": "@set"
},
"valueMax": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#valueMax",
"@type": "xsd:string"
},
"valueMin": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#valueMin",
"@type": "xsd:string"
}
}
},
"RubricCriterionLevel": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#RubricCriterionLevel",
"@context": {
"level": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#level",
"@type": "xsd:string"
},
"points": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#points",
"@type": "xsd:string"
}
}
},
"alignment": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#alignment",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Alignment",
"@container": "@set"
},
"description": {
"@id": "https://schema.org/description",
"@type": "xsd:string"
},
"endorsement": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#endorsement",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#EndorsementCredential",
"@container": "@set"
},
"image": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#image",
"@type": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#Image"
},
"name": {
"@id": "https://schema.org/name",
"@type": "xsd:string"
},
"narrative": {
"@id": "https://purl.imsglobal.org/spec/ob/v3p0/vocab.html#narrative",
"@type": "xsd:string"
},
"url": {
"@id": "https://schema.org/url",
"@type": "xsd:anyURI"
}
}
}

View File

@ -0,0 +1,440 @@
{
"@context": {
"id": "@id",
"type": "@type",
"xsd": "https://www.w3.org/2001/XMLSchema#",
"OpenBadgeCredential": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#OpenBadgeCredential"
},
"Achievement": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Achievement",
"@context": {
"achievementType": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#achievementType",
"@type": "xsd:string"
},
"alignment": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#alignment",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Alignment",
"@container": "@set"
},
"creator": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Profile"
},
"creditsAvailable": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#creditsAvailable",
"@type": "xsd:float"
},
"criteria": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Criteria",
"@type": "@id"
},
"fieldOfStudy": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#fieldOfStudy",
"@type": "xsd:string"
},
"humanCode": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#humanCode",
"@type": "xsd:string"
},
"otherIdentifier": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#otherIdentifier",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#IdentifierEntry",
"@container": "@set"
},
"related": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#related",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Related",
"@container": "@set"
},
"resultDescription": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#resultDescription",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#ResultDescription",
"@container": "@set"
},
"specialization": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#specialization",
"@type": "xsd:string"
},
"tag": {
"@id": "https://schema.org/keywords",
"@type": "xsd:string",
"@container": "@set"
},
"version": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#version",
"@type": "xsd:string"
}
}
},
"AchievementCredential": {
"@id": "OpenBadgeCredential"
},
"AchievementSubject": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#AchievementSubject",
"@context": {
"achievement": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Achievement"
},
"activityEndDate": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#activityEndDate",
"@type": "xsd:date"
},
"activityStartDate": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#activityStartDate",
"@type": "xsd:date"
},
"creditsEarned": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#creditsEarned",
"@type": "xsd:float"
},
"identifier": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#identifier",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#IdentityObject",
"@container": "@set"
},
"licenseNumber": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#licenseNumber",
"@type": "xsd:string"
},
"result": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#result",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Result",
"@container": "@set"
},
"role": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#role",
"@type": "xsd:string"
},
"source": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#source",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Profile"
},
"term": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#term",
"@type": "xsd:string"
}
}
},
"Address": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Address",
"@context": {
"addressCountry": {
"@id": "https://schema.org/addressCountry",
"@type": "xsd:string"
},
"addressCountryCode": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#CountryCode",
"@type": "xsd:string"
},
"addressLocality": {
"@id": "https://schema.org/addresLocality",
"@type": "xsd:string"
},
"addressRegion": {
"@id": "https://schema.org/addressRegion",
"@type": "xsd:string"
},
"geo": {
"@id" : "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#GeoCoordinates"
},
"postOfficeBoxNumber": {
"@id": "https://schema.org/postOfficeBoxNumber",
"@type": "xsd:string"
},
"postalCode": {
"@id": "https://schema.org/postalCode",
"@type": "xsd:string"
},
"streetAddress": {
"@id": "https://schema.org/streetAddress",
"@type": "xsd:string"
}
}
},
"Alignment": {
"@id": "https://schema.org/Alignment",
"@context": {
"targetCode": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#targetCode",
"@type": "xsd:string"
},
"targetDescription": {
"@id": "https://schema.org/targetDescription",
"@type": "xsd:string"
},
"targetFramework": {
"@id": "https://schema.org/targetFramework",
"@type": "xsd:string"
},
"targetName": {
"@id": "https://schema.org/targetName",
"@type": "xsd:string"
},
"targetType": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#targetType",
"@type": "xsd:string"
},
"targetUrl": {
"@id": "https://schema.org/targetUrl",
"@type": "xsd:anyURI"
}
}
},
"Criteria": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Criteria"
},
"EndorsementCredential": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#EndorsementCredential"
},
"EndorsementSubject": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#EndorsementSubject",
"@context": {
"endorsementComment": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#endorsementComment",
"@type": "xsd:string"
}
}
},
"Evidence": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Evidence",
"@context": {
"audience": {
"@id": "https://schema.org/audience",
"@type": "xsd:string"
},
"genre": {
"@id": "https://schema.org/genre",
"@type": "xsd:string"
}
}
},
"GeoCoordinates": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#GeoCoordinates",
"@context": {
"latitude": {
"@id": "https://schema.org/latitude",
"@type": "xsd:string"
},
"longitude": {
"@id": "https://schema.org/longitude",
"@type": "xsd:string"
}
}
},
"IdentifierEntry": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#IdentifierEntry",
"@context": {
"identifier": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#identifier",
"@type": "xsd:string"
},
"identifierType": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#identifierType",
"@type": "xsd:string"
}
}
},
"IdentityObject": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#IdentityObject",
"@context": {
"hashed": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#hashed",
"@type": "xsd:boolean"
},
"identityHash": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#identityHash",
"@type": "xsd:string"
},
"identityType": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#identityType",
"@type": "xsd:string"
},
"salt": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#salt",
"@type": "xsd:string"
}
}
},
"Image": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Image",
"@context": {
"caption": {
"@id": "https://schema.org/caption",
"@type": "xsd:string"
}
}
},
"Profile": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Profile",
"@context": {
"additionalName": {
"@id": "https://schema.org/additionalName",
"@type": "xsd:string"
},
"address": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Address"
},
"dateOfBirth": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#dateOfBirth",
"@type": "xsd:date"
},
"email": {
"@id": "https://schema.org/email",
"@type": "xsd:string"
},
"familyName": {
"@id": "https://schema.org/familyName",
"@type": "xsd:string"
},
"familyNamePrefix": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#familyNamePrefix",
"@type": "xsd:string"
},
"givenName": {
"@id": "https://schema.org/givenName",
"@type": "xsd:string"
},
"honorificPrefix": {
"@id": "https://schema.org/honorificPrefix",
"@type": "xsd:string"
},
"honorificSuffix": {
"@id": "https://schema.org/honorificSuffix",
"@type": "xsd:string"
},
"otherIdentifier": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#otherIdentifier",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#IdentifierEntry",
"@container": "@set"
},
"parentOrg": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#parentOrg",
"@type": "xsd:string"
},
"patronymicName": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#patronymicName",
"@type": "xsd:string"
},
"phone": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#PhoneNumber",
"@type": "xsd:string"
},
"official": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#official",
"@type": "xsd:string"
}
}
},
"Related": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Related",
"@context": {
"version": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#version",
"@type": "xsd:string"
}
}
},
"Result": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Result",
"@context": {
"achievedLevel": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#achievedLevel",
"@type": "xsd:anyURI"
},
"resultDescription": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#resultDescription",
"@type": "xsd:anyURI"
},
"status": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#status",
"@type": "xsd:string"
},
"value": {
"@id": "https://schema.org/value",
"@type": "xsd:string"
}
}
},
"ResultDescription": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#ResultDescription",
"@context": {
"allowedValue": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#allowedValue",
"@type": "xsd:string",
"@container": "@set"
},
"requiredLevel": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#requiredLevel",
"@type": "xsd:anyURI"
},
"requiredValue": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#requiredValue",
"@type": "xsd:string"
},
"resultType": {
"@id":"https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#resultType",
"@type": "xsd:string"
},
"rubricCriterionLevel": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#rubricCriterionLevel",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#RubricCriterionLevel",
"@container": "@set"
},
"valueMax": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#valueMax",
"@type": "xsd:string"
},
"valueMin": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#valueMin",
"@type": "xsd:string"
}
}
},
"RubricCriterionLevel": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#RubricCriterionLevel",
"@context": {
"level": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#level",
"@type": "xsd:string"
},
"points": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#points",
"@type": "xsd:string"
}
}
},
"alignment": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#alignment",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Alignment",
"@container": "@set"
},
"description": {
"@id": "https://schema.org/description",
"@type": "xsd:string"
},
"endorsement": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#endorsement",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#EndorsementCredential",
"@container": "@set"
},
"image": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#image",
"@type": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#Image"
},
"name": {
"@id": "https://schema.org/name",
"@type": "xsd:string"
},
"narrative": {
"@id": "https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#narrative",
"@type": "xsd:string"
},
"url": {
"@id": "https://schema.org/url",
"@type": "xsd:anyURI"
}
}
}

View File

@ -0,0 +1,301 @@
{
"@context": {
"odrl": "http://www.w3.org/ns/odrl/2/",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
"owl": "http://www.w3.org/2002/07/owl#",
"skos": "http://www.w3.org/2004/02/skos/core#",
"dct": "http://purl.org/dc/terms/",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"vcard": "http://www.w3.org/2006/vcard/ns#",
"foaf": "http://xmlns.com/foaf/0.1/",
"schema": "http://schema.org/",
"cc": "http://creativecommons.org/ns#",
"uid": "@id",
"type": "@type",
"Policy": "odrl:Policy",
"Rule": "odrl:Rule",
"profile": {
"@type": "@id",
"@id": "odrl:profile"
},
"inheritFrom": {
"@type": "@id",
"@id": "odrl:inheritFrom"
},
"ConflictTerm": "odrl:ConflictTerm",
"conflict": {
"@type": "@vocab",
"@id": "odrl:conflict"
},
"perm": "odrl:perm",
"prohibit": "odrl:prohibit",
"invalid": "odrl:invalid",
"Agreement": "odrl:Agreement",
"Assertion": "odrl:Assertion",
"Offer": "odrl:Offer",
"Privacy": "odrl:Privacy",
"Request": "odrl:Request",
"Set": "odrl:Set",
"Ticket": "odrl:Ticket",
"Asset": "odrl:Asset",
"AssetCollection": "odrl:AssetCollection",
"relation": {
"@type": "@id",
"@id": "odrl:relation"
},
"hasPolicy": {
"@type": "@id",
"@id": "odrl:hasPolicy"
},
"target": {
"@type": "@id",
"@id": "odrl:target"
},
"output": {
"@type": "@id",
"@id": "odrl:output"
},
"partOf": {
"@type": "@id",
"@id": "odrl:partOf"
},
"source": {
"@type": "@id",
"@id": "odrl:source"
},
"Party": "odrl:Party",
"PartyCollection": "odrl:PartyCollection",
"function": {
"@type": "@vocab",
"@id": "odrl:function"
},
"PartyScope": "odrl:PartyScope",
"assignee": {
"@type": "@id",
"@id": "odrl:assignee"
},
"assigner": {
"@type": "@id",
"@id": "odrl:assigner"
},
"assigneeOf": {
"@type": "@id",
"@id": "odrl:assigneeOf"
},
"assignerOf": {
"@type": "@id",
"@id": "odrl:assignerOf"
},
"attributedParty": {
"@type": "@id",
"@id": "odrl:attributedParty"
},
"attributingParty": {
"@type": "@id",
"@id": "odrl:attributingParty"
},
"compensatedParty": {
"@type": "@id",
"@id": "odrl:compensatedParty"
},
"compensatingParty": {
"@type": "@id",
"@id": "odrl:compensatingParty"
},
"consentingParty": {
"@type": "@id",
"@id": "odrl:consentingParty"
},
"consentedParty": {
"@type": "@id",
"@id": "odrl:consentedParty"
},
"informedParty": {
"@type": "@id",
"@id": "odrl:informedParty"
},
"informingParty": {
"@type": "@id",
"@id": "odrl:informingParty"
},
"trackingParty": {
"@type": "@id",
"@id": "odrl:trackingParty"
},
"trackedParty": {
"@type": "@id",
"@id": "odrl:trackedParty"
},
"contractingParty": {
"@type": "@id",
"@id": "odrl:contractingParty"
},
"contractedParty": {
"@type": "@id",
"@id": "odrl:contractedParty"
},
"Action": "odrl:Action",
"action": {
"@type": "@vocab",
"@id": "odrl:action"
},
"includedIn": {
"@type": "@id",
"@id": "odrl:includedIn"
},
"implies": {
"@type": "@id",
"@id": "odrl:implies"
},
"Permission": "odrl:Permission",
"permission": {
"@type": "@id",
"@id": "odrl:permission"
},
"Prohibition": "odrl:Prohibition",
"prohibition": {
"@type": "@id",
"@id": "odrl:prohibition"
},
"obligation": {
"@type": "@id",
"@id": "odrl:obligation"
},
"use": "odrl:use",
"grantUse": "odrl:grantUse",
"aggregate": "odrl:aggregate",
"annotate": "odrl:annotate",
"anonymize": "odrl:anonymize",
"archive": "odrl:archive",
"concurrentUse": "odrl:concurrentUse",
"derive": "odrl:derive",
"digitize": "odrl:digitize",
"display": "odrl:display",
"distribute": "odrl:distribute",
"execute": "odrl:execute",
"extract": "odrl:extract",
"give": "odrl:give",
"index": "odrl:index",
"install": "odrl:install",
"modify": "odrl:modify",
"move": "odrl:move",
"play": "odrl:play",
"present": "odrl:present",
"print": "odrl:print",
"read": "odrl:read",
"reproduce": "odrl:reproduce",
"sell": "odrl:sell",
"stream": "odrl:stream",
"textToSpeech": "odrl:textToSpeech",
"transfer": "odrl:transfer",
"transform": "odrl:transform",
"translate": "odrl:translate",
"Duty": "odrl:Duty",
"duty": {
"@type": "@id",
"@id": "odrl:duty"
},
"consequence": {
"@type": "@id",
"@id": "odrl:consequence"
},
"remedy": {
"@type": "@id",
"@id": "odrl:remedy"
},
"acceptTracking": "odrl:acceptTracking",
"attribute": "odrl:attribute",
"compensate": "odrl:compensate",
"delete": "odrl:delete",
"ensureExclusivity": "odrl:ensureExclusivity",
"include": "odrl:include",
"inform": "odrl:inform",
"nextPolicy": "odrl:nextPolicy",
"obtainConsent": "odrl:obtainConsent",
"reviewPolicy": "odrl:reviewPolicy",
"uninstall": "odrl:uninstall",
"watermark": "odrl:watermark",
"Constraint": "odrl:Constraint",
"LogicalConstraint": "odrl:LogicalConstraint",
"constraint": {
"@type": "@id",
"@id": "odrl:constraint"
},
"refinement": {
"@type": "@id",
"@id": "odrl:refinement"
},
"Operator": "odrl:Operator",
"operator": {
"@type": "@vocab",
"@id": "odrl:operator"
},
"RightOperand": "odrl:RightOperand",
"rightOperand": "odrl:rightOperand",
"rightOperandReference": {
"@type": "xsd:anyURI",
"@id": "odrl:rightOperandReference"
},
"LeftOperand": "odrl:LeftOperand",
"leftOperand": {
"@type": "@vocab",
"@id": "odrl:leftOperand"
},
"unit": "odrl:unit",
"dataType": {
"@type": "xsd:anyType",
"@id": "odrl:datatype"
},
"status": "odrl:status",
"absolutePosition": "odrl:absolutePosition",
"absoluteSpatialPosition": "odrl:absoluteSpatialPosition",
"absoluteTemporalPosition": "odrl:absoluteTemporalPosition",
"absoluteSize": "odrl:absoluteSize",
"count": "odrl:count",
"dateTime": "odrl:dateTime",
"delayPeriod": "odrl:delayPeriod",
"deliveryChannel": "odrl:deliveryChannel",
"elapsedTime": "odrl:elapsedTime",
"event": "odrl:event",
"fileFormat": "odrl:fileFormat",
"industry": "odrl:industry:",
"language": "odrl:language",
"media": "odrl:media",
"meteredTime": "odrl:meteredTime",
"payAmount": "odrl:payAmount",
"percentage": "odrl:percentage",
"product": "odrl:product",
"purpose": "odrl:purpose",
"recipient": "odrl:recipient",
"relativePosition": "odrl:relativePosition",
"relativeSpatialPosition": "odrl:relativeSpatialPosition",
"relativeTemporalPosition": "odrl:relativeTemporalPosition",
"relativeSize": "odrl:relativeSize",
"resolution": "odrl:resolution",
"spatial": "odrl:spatial",
"spatialCoordinates": "odrl:spatialCoordinates",
"systemDevice": "odrl:systemDevice",
"timeInterval": "odrl:timeInterval",
"unitOfCount": "odrl:unitOfCount",
"version": "odrl:version",
"virtualLocation": "odrl:virtualLocation",
"eq": "odrl:eq",
"gt": "odrl:gt",
"gteq": "odrl:gteq",
"lt": "odrl:lt",
"lteq": "odrl:lteq",
"neq": "odrl:neg",
"isA": "odrl:isA",
"hasPart": "odrl:hasPart",
"isPartOf": "odrl:isPartOf",
"isAllOf": "odrl:isAllOf",
"isAnyOf": "odrl:isAnyOf",
"isNoneOf": "odrl:isNoneOf",
"or": "odrl:or",
"xone": "odrl:xone",
"and": "odrl:and",
"andSequence": "odrl:andSequence",
"policyUsage": "odrl:policyUsage"
}
}

View File

@ -0,0 +1,92 @@
{
"@context": {
"@version": 1.1,
"id": "@id",
"type": "@type",
"BbsBlsSignature2020": {
"@id": "https://w3id.org/security#BbsBlsSignature2020",
"@context": {
"@version": 1.1,
"@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",
"proofValue": "https://w3id.org/security#proofValue",
"nonce": "https://w3id.org/security#nonce",
"proofPurpose": {
"@id": "https://w3id.org/security#proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@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"
}
}
},
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
},
"BbsBlsSignatureProof2020": {
"@id": "https://w3id.org/security#BbsBlsSignatureProof2020",
"@context": {
"@version": 1.1,
"@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",
"nonce": "https://w3id.org/security#nonce",
"proofPurpose": {
"@id": "https://w3id.org/security#proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"sec": "https://w3id.org/security#",
"assertionMethod": {
"@id": "https://w3id.org/security#assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "https://w3id.org/security#authenticationMethod",
"@type": "@id",
"@container": "@set"
}
}
},
"proofValue": "https://w3id.org/security#proofValue",
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
},
"Bls12381G2Key2020": "https://w3id.org/security#Bls12381G2Key2020"
}
}

View File

@ -0,0 +1,93 @@
{
"@context": {
"id": "@id",
"type": "@type",
"@protected": true,
"proof": {
"@id": "https://w3id.org/security#proof",
"@type": "@id",
"@container": "@graph"
},
"Ed25519VerificationKey2020": {
"@id": "https://w3id.org/security#Ed25519VerificationKey2020",
"@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"
}
}
},
"Ed25519Signature2020": {
"@id": "https://w3id.org/security#Ed25519Signature2020",
"@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"
},
"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"
}
}
},
"proofValue": {
"@id": "https://w3id.org/security#proofValue",
"@type": "https://w3id.org/security#multibase"
},
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
}
}
}

View File

@ -0,0 +1,50 @@
{
"@context": {
"id": "@id",
"type": "@type",
"dc": "http://purl.org/dc/terms/",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"EcdsaKoblitzSignature2016": "sec:EcdsaKoblitzSignature2016",
"Ed25519Signature2018": "sec:Ed25519Signature2018",
"EncryptedMessage": "sec:EncryptedMessage",
"GraphSignature2012": "sec:GraphSignature2012",
"LinkedDataSignature2015": "sec:LinkedDataSignature2015",
"LinkedDataSignature2016": "sec:LinkedDataSignature2016",
"CryptographicKey": "sec:Key",
"authenticationTag": "sec:authenticationTag",
"canonicalizationAlgorithm": "sec:canonicalizationAlgorithm",
"cipherAlgorithm": "sec:cipherAlgorithm",
"cipherData": "sec:cipherData",
"cipherKey": "sec:cipherKey",
"created": {"@id": "dc:created", "@type": "xsd:dateTime"},
"creator": {"@id": "dc:creator", "@type": "@id"},
"digestAlgorithm": "sec:digestAlgorithm",
"digestValue": "sec:digestValue",
"domain": "sec:domain",
"encryptionKey": "sec:encryptionKey",
"expiration": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
"expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
"initializationVector": "sec:initializationVector",
"iterationCount": "sec:iterationCount",
"nonce": "sec:nonce",
"normalizationAlgorithm": "sec:normalizationAlgorithm",
"owner": {"@id": "sec:owner", "@type": "@id"},
"password": "sec:password",
"privateKey": {"@id": "sec:privateKey", "@type": "@id"},
"privateKeyPem": "sec:privateKeyPem",
"publicKey": {"@id": "sec:publicKey", "@type": "@id"},
"publicKeyBase58": "sec:publicKeyBase58",
"publicKeyPem": "sec:publicKeyPem",
"publicKeyWif": "sec:publicKeyWif",
"publicKeyService": {"@id": "sec:publicKeyService", "@type": "@id"},
"revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"},
"salt": "sec:salt",
"signature": "sec:signature",
"signatureAlgorithm": "sec:signingAlgorithm",
"signatureValue": "sec:signatureValue"
}
}

View File

@ -0,0 +1,59 @@
{
"@context": [{
"@version": 1.1
}, "https://w3id.org/security/v1", {
"AesKeyWrappingKey2019": "sec:AesKeyWrappingKey2019",
"DeleteKeyOperation": "sec:DeleteKeyOperation",
"DeriveSecretOperation": "sec:DeriveSecretOperation",
"EcdsaSecp256k1Signature2019": "sec:EcdsaSecp256k1Signature2019",
"EcdsaSecp256r1Signature2019": "sec:EcdsaSecp256r1Signature2019",
"EcdsaSecp256k1VerificationKey2019": "sec:EcdsaSecp256k1VerificationKey2019",
"EcdsaSecp256r1VerificationKey2019": "sec:EcdsaSecp256r1VerificationKey2019",
"Ed25519Signature2018": "sec:Ed25519Signature2018",
"Ed25519VerificationKey2018": "sec:Ed25519VerificationKey2018",
"EquihashProof2018": "sec:EquihashProof2018",
"ExportKeyOperation": "sec:ExportKeyOperation",
"GenerateKeyOperation": "sec:GenerateKeyOperation",
"KmsOperation": "sec:KmsOperation",
"RevokeKeyOperation": "sec:RevokeKeyOperation",
"RsaSignature2018": "sec:RsaSignature2018",
"RsaVerificationKey2018": "sec:RsaVerificationKey2018",
"Sha256HmacKey2019": "sec:Sha256HmacKey2019",
"SignOperation": "sec:SignOperation",
"UnwrapKeyOperation": "sec:UnwrapKeyOperation",
"VerifyOperation": "sec:VerifyOperation",
"WrapKeyOperation": "sec:WrapKeyOperation",
"X25519KeyAgreementKey2019": "sec:X25519KeyAgreementKey2019",
"allowedAction": "sec:allowedAction",
"assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"},
"authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"},
"capability": {"@id": "sec:capability", "@type": "@id"},
"capabilityAction": "sec:capabilityAction",
"capabilityChain": {"@id": "sec:capabilityChain", "@type": "@id", "@container": "@list"},
"capabilityDelegation": {"@id": "sec:capabilityDelegationMethod", "@type": "@id", "@container": "@set"},
"capabilityInvocation": {"@id": "sec:capabilityInvocationMethod", "@type": "@id", "@container": "@set"},
"caveat": {"@id": "sec:caveat", "@type": "@id", "@container": "@set"},
"challenge": "sec:challenge",
"ciphertext": "sec:ciphertext",
"controller": {"@id": "sec:controller", "@type": "@id"},
"delegator": {"@id": "sec:delegator", "@type": "@id"},
"equihashParameterK": {"@id": "sec:equihashParameterK", "@type": "xsd:integer"},
"equihashParameterN": {"@id": "sec:equihashParameterN", "@type": "xsd:integer"},
"invocationTarget": {"@id": "sec:invocationTarget", "@type": "@id"},
"invoker": {"@id": "sec:invoker", "@type": "@id"},
"jws": "sec:jws",
"keyAgreement": {"@id": "sec:keyAgreementMethod", "@type": "@id", "@container": "@set"},
"kmsModule": {"@id": "sec:kmsModule"},
"parentCapability": {"@id": "sec:parentCapability", "@type": "@id"},
"plaintext": "sec:plaintext",
"proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"},
"proofPurpose": {"@id": "sec:proofPurpose", "@type": "@vocab"},
"proofValue": "sec:proofValue",
"referenceId": "sec:referenceId",
"unwrappedKey": "sec:unwrappedKey",
"verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"},
"verifyData": "sec:verifyData",
"wrappedKey": "sec:wrappedKey"
}]
}

View File

@ -0,0 +1,710 @@
{
"@context": [{
"@version": 1.1,
"id": "@id",
"type": "@type",
"@protected": true,
"JsonWebKey2020": {
"@id": "https://w3id.org/security#JsonWebKey2020"
},
"JsonWebSignature2020": {
"@id": "https://w3id.org/security#JsonWebSignature2020",
"@context": {
"@version": 1.1,
"id": "@id",
"type": "@type",
"@protected": true,
"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": {
"@version": 1.1,
"@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"
}
}
},
"Ed25519VerificationKey2020": {
"@id": "https://w3id.org/security#Ed25519VerificationKey2020"
},
"Ed25519Signature2020": {
"@id": "https://w3id.org/security#Ed25519Signature2020",
"@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"
},
"nonce": "https://w3id.org/security#nonce",
"proofPurpose": {
"@id": "https://w3id.org/security#proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@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"
}
}
},
"proofValue": {
"@id": "https://w3id.org/security#proofValue"
},
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
},
"publicKeyJwk": {
"@id": "https://w3id.org/security#publicKeyJwk",
"@type": "@json"
},
"ethereumAddress": {
"@id": "https://w3id.org/security#ethereumAddress"
},
"publicKeyHex": {
"@id": "https://w3id.org/security#publicKeyHex"
},
"blockchainAccountId": {
"@id": "https://w3id.org/security#blockchainAccountId"
},
"MerkleProof2019": {
"@id": "https://w3id.org/security#MerkleProof2019"
},
"Bls12381G1Key2020": {
"@id": "https://w3id.org/security#Bls12381G1Key2020"
},
"Bls12381G2Key2020": {
"@id": "https://w3id.org/security#Bls12381G2Key2020"
},
"BbsBlsSignature2020": {
"@id": "https://w3id.org/security#BbsBlsSignature2020",
"@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",
"nonce": "https://w3id.org/security#nonce",
"proofPurpose": {
"@id": "https://w3id.org/security#proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@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"
}
}
},
"proofValue": "https://w3id.org/security#proofValue",
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
},
"BbsBlsSignatureProof2020": {
"@id": "https://w3id.org/security#BbsBlsSignatureProof2020",
"@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",
"nonce": "https://w3id.org/security#nonce",
"proofPurpose": {
"@id": "https://w3id.org/security#proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@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"
}
}
},
"proofValue": "https://w3id.org/security#proofValue",
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
},
"EcdsaKoblitzSignature2016": "https://w3id.org/security#EcdsaKoblitzSignature2016",
"Ed25519Signature2018": {
"@id": "https://w3id.org/security#Ed25519Signature2018",
"@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": {
"@version": 1.1,
"@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"
}
}
},
"proofValue": "https://w3id.org/security#proofValue",
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
},
"EncryptedMessage": "https://w3id.org/security#EncryptedMessage",
"GraphSignature2012": "https://w3id.org/security#GraphSignature2012",
"LinkedDataSignature2015": "https://w3id.org/security#LinkedDataSignature2015",
"LinkedDataSignature2016": "https://w3id.org/security#LinkedDataSignature2016",
"CryptographicKey": "https://w3id.org/security#Key",
"authenticationTag": "https://w3id.org/security#authenticationTag",
"canonicalizationAlgorithm": "https://w3id.org/security#canonicalizationAlgorithm",
"cipherAlgorithm": "https://w3id.org/security#cipherAlgorithm",
"cipherData": "https://w3id.org/security#cipherData",
"cipherKey": "https://w3id.org/security#cipherKey",
"created": {
"@id": "http://purl.org/dc/terms/created",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
},
"creator": {
"@id": "http://purl.org/dc/terms/creator",
"@type": "@id"
},
"digestAlgorithm": "https://w3id.org/security#digestAlgorithm",
"digestValue": "https://w3id.org/security#digestValue",
"domain": "https://w3id.org/security#domain",
"encryptionKey": "https://w3id.org/security#encryptionKey",
"expiration": {
"@id": "https://w3id.org/security#expiration",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
},
"expires": {
"@id": "https://w3id.org/security#expiration",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
},
"initializationVector": "https://w3id.org/security#initializationVector",
"iterationCount": "https://w3id.org/security#iterationCount",
"nonce": "https://w3id.org/security#nonce",
"normalizationAlgorithm": "https://w3id.org/security#normalizationAlgorithm",
"owner": "https://w3id.org/security#owner",
"password": "https://w3id.org/security#password",
"privateKey": "https://w3id.org/security#privateKey",
"privateKeyPem": "https://w3id.org/security#privateKeyPem",
"publicKey": "https://w3id.org/security#publicKey",
"publicKeyBase58": "https://w3id.org/security#publicKeyBase58",
"publicKeyPem": "https://w3id.org/security#publicKeyPem",
"publicKeyWif": "https://w3id.org/security#publicKeyWif",
"publicKeyService": "https://w3id.org/security#publicKeyService",
"revoked": {
"@id": "https://w3id.org/security#revoked",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
},
"salt": "https://w3id.org/security#salt",
"signature": "https://w3id.org/security#signature",
"signatureAlgorithm": "https://w3id.org/security#signingAlgorithm",
"signatureValue": "https://w3id.org/security#signatureValue",
"proofValue": "https://w3id.org/security#proofValue",
"AesKeyWrappingKey2019": "https://w3id.org/security#AesKeyWrappingKey2019",
"DeleteKeyOperation": "https://w3id.org/security#DeleteKeyOperation",
"DeriveSecretOperation": "https://w3id.org/security#DeriveSecretOperation",
"EcdsaSecp256k1Signature2019": {
"@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019",
"@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": {
"@version": 1.1,
"@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"
}
}
},
"proofValue": "https://w3id.org/security#proofValue",
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
},
"EcdsaSecp256r1Signature2019": {
"@id": "https://w3id.org/security#EcdsaSecp256r1Signature2019",
"@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": {
"@version": 1.1,
"@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"
}
}
},
"proofValue": "https://w3id.org/security#proofValue",
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
},
"EcdsaSecp256k1VerificationKey2019": "https://w3id.org/security#EcdsaSecp256k1VerificationKey2019",
"EcdsaSecp256r1VerificationKey2019": "https://w3id.org/security#EcdsaSecp256r1VerificationKey2019",
"Ed25519VerificationKey2018": "https://w3id.org/security#Ed25519VerificationKey2018",
"EquihashProof2018": "https://w3id.org/security#EquihashProof2018",
"ExportKeyOperation": "https://w3id.org/security#ExportKeyOperation",
"GenerateKeyOperation": "https://w3id.org/security#GenerateKeyOperation",
"KmsOperation": "https://w3id.org/security#KmsOperation",
"RevokeKeyOperation": "https://w3id.org/security#RevokeKeyOperation",
"RsaSignature2018": {
"@id": "https://w3id.org/security#RsaSignature2018",
"@context": {
"@protected": true,
"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": {
"@version": 1.1,
"@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"
}
}
},
"proofValue": "https://w3id.org/security#proofValue",
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
},
"RsaVerificationKey2018": "https://w3id.org/security#RsaVerificationKey2018",
"Sha256HmacKey2019": "https://w3id.org/security#Sha256HmacKey2019",
"SignOperation": "https://w3id.org/security#SignOperation",
"UnwrapKeyOperation": "https://w3id.org/security#UnwrapKeyOperation",
"VerifyOperation": "https://w3id.org/security#VerifyOperation",
"WrapKeyOperation": "https://w3id.org/security#WrapKeyOperation",
"X25519KeyAgreementKey2019": "https://w3id.org/security#X25519KeyAgreementKey2019",
"allowedAction": "https://w3id.org/security#allowedAction",
"assertionMethod": {
"@id": "https://w3id.org/security#assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "https://w3id.org/security#authenticationMethod",
"@type": "@id",
"@container": "@set"
},
"capability": {
"@id": "https://w3id.org/security#capability",
"@type": "@id"
},
"capabilityAction": "https://w3id.org/security#capabilityAction",
"capabilityChain": {
"@id": "https://w3id.org/security#capabilityChain",
"@type": "@id",
"@container": "@list"
},
"capabilityDelegation": {
"@id": "https://w3id.org/security#capabilityDelegationMethod",
"@type": "@id",
"@container": "@set"
},
"capabilityInvocation": {
"@id": "https://w3id.org/security#capabilityInvocationMethod",
"@type": "@id",
"@container": "@set"
},
"caveat": {
"@id": "https://w3id.org/security#caveat",
"@type": "@id",
"@container": "@set"
},
"challenge": "https://w3id.org/security#challenge",
"ciphertext": "https://w3id.org/security#ciphertext",
"controller": {
"@id": "https://w3id.org/security#controller",
"@type": "@id"
},
"delegator": {
"@id": "https://w3id.org/security#delegator",
"@type": "@id"
},
"equihashParameterK": {
"@id": "https://w3id.org/security#equihashParameterK",
"@type": "http://www.w3.org/2001/XMLSchema#:integer"
},
"equihashParameterN": {
"@id": "https://w3id.org/security#equihashParameterN",
"@type": "http://www.w3.org/2001/XMLSchema#:integer"
},
"invocationTarget": {
"@id": "https://w3id.org/security#invocationTarget",
"@type": "@id"
},
"invoker": {
"@id": "https://w3id.org/security#invoker",
"@type": "@id"
},
"jws": "https://w3id.org/security#jws",
"keyAgreement": {
"@id": "https://w3id.org/security#keyAgreementMethod",
"@type": "@id",
"@container": "@set"
},
"kmsModule": {
"@id": "https://w3id.org/security#kmsModule"
},
"parentCapability": {
"@id": "https://w3id.org/security#parentCapability",
"@type": "@id"
},
"plaintext": "https://w3id.org/security#plaintext",
"proof": {
"@id": "https://w3id.org/security#proof",
"@type": "@id",
"@container": "@graph"
},
"proofPurpose": {
"@id": "https://w3id.org/security#proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@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"
}
}
},
"referenceId": "https://w3id.org/security#referenceId",
"unwrappedKey": "https://w3id.org/security#unwrappedKey",
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
},
"verifyData": "https://w3id.org/security#verifyData",
"wrappedKey": "https://w3id.org/security#wrappedKey"
}]
}

View File

@ -0,0 +1,91 @@
{
"@context": {
"id": "@id",
"type": "@type",
"@protected": true,
"proof": {
"@id": "https://w3id.org/security#proof",
"@type": "@id",
"@container": "@graph"
},
"Ed25519VerificationKey2018": {
"@id": "https://w3id.org/security#Ed25519VerificationKey2018",
"@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"
},
"publicKeyBase58": {
"@id": "https://w3id.org/security#publicKeyBase58"
}
}
},
"Ed25519Signature2018": {
"@id": "https://w3id.org/security#Ed25519Signature2018",
"@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"
},
"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"
}
}
},
"jws": {
"@id": "https://w3id.org/security#jws"
},
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
}
}
}

View File

@ -0,0 +1,93 @@
{
"@context": {
"id": "@id",
"type": "@type",
"@protected": true,
"proof": {
"@id": "https://w3id.org/security#proof",
"@type": "@id",
"@container": "@graph"
},
"Ed25519VerificationKey2020": {
"@id": "https://w3id.org/security#Ed25519VerificationKey2020",
"@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"
}
}
},
"Ed25519Signature2020": {
"@id": "https://w3id.org/security#Ed25519Signature2020",
"@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"
},
"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"
}
}
},
"proofValue": {
"@id": "https://w3id.org/security#proofValue",
"@type": "https://w3id.org/security#multibase"
},
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
}
}
}

View File

@ -0,0 +1,82 @@
{
"@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"
}
}
}
}
}

View File

@ -0,0 +1,102 @@
{
"@context": {
"id": "@id",
"type": "@type",
"@protected": true,
"proof": {
"@id": "https://w3id.org/security#proof",
"@type": "@id",
"@container": "@graph"
},
"EcdsaSecp256k1VerificationKey2019": {
"@id": "https://w3id.org/security#EcdsaSecp256k1VerificationKey2019",
"@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"
},
"blockchainAccountId": {
"@id": "https://w3id.org/security#blockchainAccountId"
},
"publicKeyJwk": {
"@id": "https://w3id.org/security#publicKeyJwk",
"@type": "@json"
},
"publicKeyBase58": {
"@id": "https://w3id.org/security#publicKeyBase58"
},
"publicKeyMultibase": {
"@id": "https://w3id.org/security#publicKeyMultibase",
"@type": "https://w3id.org/security#multibase"
}
}
},
"EcdsaSecp256k1Signature2019": {
"@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019",
"@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"
},
"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"
}
}
},
"jws": {
"@id": "https://w3id.org/security#jws"
},
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
}
}
}

View File

@ -0,0 +1,26 @@
{
"@context": {
"id": "@id",
"type": "@type",
"@protected": true,
"X25519KeyAgreementKey2019": {
"@id": "https://w3id.org/security#X25519KeyAgreementKey2019",
"@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"
},
"publicKeyBase58": {
"@id": "https://w3id.org/security#publicKeyBase58"
}
}
}
}
}

View File

@ -0,0 +1,88 @@
package org.oneedtech.inspect.vc;
import java.io.StringWriter;
import java.net.URI;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import com.apicatalog.did.key.DidKey;
import com.apicatalog.ld.signature.ed25519.Ed25519Proof2020Adapter;
import com.apicatalog.ld.signature.key.KeyPair;
import com.apicatalog.ld.signature.proof.ProofOptions;
import com.apicatalog.ld.signature.proof.VerificationMethod;
import com.apicatalog.vc.Vc;
import com.apicatalog.vc.processor.Issuer;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.json.JsonWriter;
import jakarta.json.JsonWriterFactory;
import jakarta.json.stream.JsonGenerator;
public class IronTests {
@Disabled
@Test
void testOb_01() {
Assertions.assertDoesNotThrow(()->{
final DidKey didKey = DidKey.from(URI.create("did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"));
//https://w3id.org/security#Ed25519KeyPair2020
//https://w3id.org/security#Ed25519Signature2020
URI unsigned = Samples.OB30.JSON.SIMPLE_JSON_NOPROOF.asURL().toURI();
KeyPair kp = Vc.generateKeys("https://w3id.org/security#Ed25519Signature2020").get(URI.create("urn:1"), 256);
ProofOptions options = ProofOptions.create(
Ed25519Proof2020Adapter.TYPE,
//new VerificationMethod(URI.create("did:key:z6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj")),
new VerificationMethod(didKey.toUri()),
URI.create("https://w3id.org/security#assertionMethod")).created(Instant.now().truncatedTo(ChronoUnit.SECONDS));
Issuer issuer = Vc.sign(unsigned, kp, options);
System.err.println(pretty(issuer.getCompacted()));
JsonObject signed = issuer.getCompacted();
JsonObject proof = signed.getJsonObject("sec:proof");
Assertions.assertNotNull(proof);
Vc.verify(issuer.getCompacted()).isValid();
});
}
@Disabled
@Test
void testClr_01() {
Assertions.assertDoesNotThrow(()->{
URI unsigned = Samples.CLR20.JSON.SIMPLE_JSON_NOPROOF.asURL().toURI();
KeyPair kp = Vc.generateKeys("https://w3id.org/security#Ed25519Signature2020").get(URI.create("urn:1"), 256);
ProofOptions options = ProofOptions.create(
Ed25519Proof2020Adapter.TYPE,
new VerificationMethod(URI.create("did:key:z6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj")),
URI.create("https://w3id.org/security#assertionMethod"));
Issuer issuer = Vc.sign(unsigned, kp, options);
JsonObject job = issuer.getCompacted().getJsonObject("sec:proof");
//System.err.println (pretty(issuer.getCompacted().toString()));
Assertions.assertNotNull(job);
Vc.verify(issuer.getCompacted()).isValid();
});
}
String pretty(JsonObject jobj) {
Map<String, Object> properties = new HashMap<>(1);
properties.put(JsonGenerator.PRETTY_PRINTING, true);
StringWriter sw = new StringWriter();
JsonWriterFactory writerFactory = Json.createWriterFactory(properties);
JsonWriter jsonWriter = writerFactory.createWriter(sw);
jsonWriter.writeObject(jobj);
jsonWriter.close();
return sw.toString();
}
}

View File

@ -0,0 +1,174 @@
package org.oneedtech.inspect.vc;
import static org.junit.jupiter.api.Assertions.*;
import static org.oneedtech.inspect.test.Assertions.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.oneedtech.inspect.core.Inspector.Behavior;
import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe;
import org.oneedtech.inspect.core.report.Report;
import org.oneedtech.inspect.test.PrintHelper;
import org.oneedtech.inspect.vc.probe.ContextPropertyProbe;
import org.oneedtech.inspect.vc.probe.ExpirationProbe;
import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe;
import org.oneedtech.inspect.vc.probe.IssuanceProbe;
import org.oneedtech.inspect.vc.probe.EmbeddedProofProbe;
import org.oneedtech.inspect.vc.probe.TypePropertyProbe;
import com.google.common.collect.Iterables;
public class OB30Tests {
private static OB30Inspector validator;
private static boolean verbose = true;
@BeforeAll
static void setup() {
validator = new OB30Inspector.Builder()
.set(Behavior.TEST_INCLUDE_SUCCESS, true)
.set(Behavior.VALIDATOR_FAIL_FAST, true)
.build();
}
@Test
void testSimpleJsonValid() {
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertValid(report);
});
}
@Test
void testSimplePNGPlainValid() {
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.PNG.SIMPLE_JSON_PNG.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertValid(report);
});
}
@Test
void testSimplePNGJWTValid() {
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.PNG.SIMPLE_JWT_PNG.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertValid(report);
});
}
@Test
void testSimpleJsonSVGPlainValid() {
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.SVG.SIMPLE_JSON_SVG.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertValid(report);
});
}
@Test
void testSimpleJsonSVGJWTValid() {
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.SVG.SIMPLE_JWT_SVG.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertValid(report);
});
}
@Test
void testSimpleJsonInvalidUnknownType() {
//add a dumb value to .type and remove the ob type
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_TYPE.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertInvalid(report);
assertFatalCount(report, 1);
assertHasProbeID(report, TypePropertyProbe.ID, true);
});
}
@Test
void testSimpleJsonInvalidProofMethod() {
//add some garbage chars to proofValue
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_PROOF_METHOD_ERROR.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertInvalid(report);
assertErrorCount(report, 1);
assertHasProbeID(report, EmbeddedProofProbe.ID, true);
});
}
@Test
void testSimpleJsonInvalidProofValue() {
//add some garbage chars to proofValue
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_PROOF_VALUE_ERROR.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertInvalid(report);
assertErrorCount(report, 1);
assertHasProbeID(report, EmbeddedProofProbe.ID, true);
});
}
@Test
void testSimpleJsonExpired() {
//"expirationDate": "2020-01-20T00:00:00Z",
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_EXPIRED.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertInvalid(report);
assertHasProbeID(report, ExpirationProbe.ID, true);
});
}
@Test
void testSimpleJsonContextError() {
//removed one of the reqd context uris
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_ERR_CONTEXT.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertInvalid(report);
assertHasProbeID(report, ContextPropertyProbe.ID, true);
});
}
@Test
void testSimpleJsonSchemaError() throws Exception {
//issuer removed
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_ISSUER.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertInvalid(report);
assertHasProbeID(report, JsonSchemaProbe.ID, true);
});
}
@Disabled //TODO IssuanceVerifierProbe is not run because FATAL: InvalidSignature terminates
@Test
void testSimpleJsonNotIssued() {
//"issuanceDate": "2040-01-01T00:00:00Z",
//this breaks the proof too
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_ISSUED.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertInvalid(report);
assertHasProbeID(report, IssuanceProbe.ID, true);
});
}
@Test
void testCompleteJsonInvalidInlineSchemaRef() throws Exception {
//404 inline schema ref, and 404 refresh uri
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.JSON.COMPLETE_JSON.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertFalse(report.asBoolean());
assertTrue(Iterables.size(report.getErrors()) > 0);
assertTrue(Iterables.size(report.getExceptions()) > 0);
assertHasProbeID(report, InlineJsonSchemaProbe.ID, true);
});
}
}

View File

@ -0,0 +1,39 @@
package org.oneedtech.inspect.vc;
import org.oneedtech.inspect.test.Sample;
public class Samples {
public static final class OB30 {
public static final class SVG {
public final static Sample SIMPLE_JSON_SVG = new Sample("ob30/simple-json.svg", true);
public final static Sample SIMPLE_JWT_SVG = new Sample("ob30/simple-jwt.svg", true);
}
public static final class JSON {
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_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_PROOF_METHOD_ERROR = new Sample("ob30/simple-err-proof-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_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_ISSUER = new Sample("ob30/simple-err-issuer.json", false);
public final static Sample SIMPLE_JSON_ERR_CONTEXT = new Sample("ob30/simple-err-context.json", false);
}
public static final class PNG {
public final static Sample SIMPLE_JWT_PNG = new Sample("ob30/simple-jwt.png", true);
public final static Sample SIMPLE_JSON_PNG = new Sample("ob30/simple-json.png", true);
}
public static final class JWT {
public final static Sample SIMPLE_JWT = new Sample("ob30/simple.jwt", true);
}
}
public static final class CLR20 {
public static final class JSON {
public final static Sample SIMPLE_JSON = new Sample("clr20/simple.json", true);
public final static Sample SIMPLE_JSON_NOPROOF = new Sample("clr20/simple-noproof.json", true);
public final static Sample SIMPLE_JWT = new Sample("clr20/simple.jwt", true);
}
}
}

View File

@ -0,0 +1,119 @@
package org.oneedtech.inspect.vc.credential;
import static org.junit.jupiter.api.Assertions.*;
import static org.oneedtech.inspect.util.json.ObjectMapperCache.Config.DEFAULT;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.probe.RunContext.Key;
import org.oneedtech.inspect.core.probe.json.JsonPathEvaluator;
import org.oneedtech.inspect.util.json.ObjectMapperCache;
import org.oneedtech.inspect.util.resource.Resource;
import org.oneedtech.inspect.util.resource.ResourceType;
import org.oneedtech.inspect.vc.Credential;
import org.oneedtech.inspect.vc.OB30Inspector;
import org.oneedtech.inspect.vc.Samples;
import org.oneedtech.inspect.vc.payload.PayloadParser;
import org.oneedtech.inspect.vc.payload.PayloadParserFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
public class PayloadParserTests {
@Test
void testSvgStringExtract() {
assertDoesNotThrow(()->{
Resource res = Samples.OB30.SVG.SIMPLE_JSON_SVG.asFileResource(ResourceType.SVG);
PayloadParser ext = PayloadParserFactory.of(res);
assertNotNull(ext);
Credential crd = ext.parse(res, mockOB30Context(res));
//System.out.println(crd.getJson().toPrettyString());
assertNotNull(crd);
assertNotNull(crd.getJson());
assertNotNull(crd.getJson().get("@context"));
});
}
@Test
void testSvgJwtExtract() {
assertDoesNotThrow(()->{
Resource res = Samples.OB30.SVG.SIMPLE_JWT_SVG.asFileResource(ResourceType.SVG);
PayloadParser ext = PayloadParserFactory.of(res);
assertNotNull(ext);
Credential crd = ext.parse(res, mockOB30Context(res));
//System.out.println(crd.getJson().toPrettyString());
assertNotNull(crd);
assertNotNull(crd.getJson());
assertNotNull(crd.getJson().get("@context"));
});
}
@Test
void testPngStringExtract() {
assertDoesNotThrow(()->{
Resource res = Samples.OB30.PNG.SIMPLE_JSON_PNG.asFileResource(ResourceType.PNG);
PayloadParser ext = PayloadParserFactory.of(res);
assertNotNull(ext);
Credential crd = ext.parse(res, mockOB30Context(res));
//System.out.println(crd.getJson().toPrettyString());
assertNotNull(crd);
assertNotNull(crd.getJson());
assertNotNull(crd.getJson().get("@context"));
});
}
@Test
void testPngJwtExtract() {
assertDoesNotThrow(()->{
Resource res = Samples.OB30.PNG.SIMPLE_JWT_PNG.asFileResource(ResourceType.PNG);
PayloadParser ext = PayloadParserFactory.of(res);
assertNotNull(ext);
Credential crd = ext.parse(res, mockOB30Context(res));
//System.out.println(crd.getJson().toPrettyString());
assertNotNull(crd);
assertNotNull(crd.getJson());
assertNotNull(crd.getJson().get("@context"));
});
}
@Test
void testJwtExtract() {
assertDoesNotThrow(()->{
Resource res = Samples.OB30.JWT.SIMPLE_JWT.asFileResource(ResourceType.JWT);
PayloadParser ext = PayloadParserFactory.of(res);
assertNotNull(ext);
Credential crd = ext.parse(res, mockOB30Context(res));
//System.out.println(crd.getJson().toPrettyString());
assertNotNull(crd);
assertNotNull(crd.getJson());
assertNotNull(crd.getJson().get("@context"));
});
}
@Test
void testJsonExtract() {
assertDoesNotThrow(()->{
Resource res = Samples.OB30.JSON.SIMPLE_JSON.asFileResource(ResourceType.JSON);
PayloadParser ext = PayloadParserFactory.of(res);
assertNotNull(ext);
Credential crd = ext.parse(res, mockOB30Context(res));
//System.out.println(crd.getJson().toPrettyString());
assertNotNull(crd);
assertNotNull(crd.getJson());
assertNotNull(crd.getJson().get("@context"));
});
}
private RunContext mockOB30Context(Resource res) {
ObjectMapper mapper = ObjectMapperCache.get(DEFAULT);
JsonPathEvaluator jsonPath = new JsonPathEvaluator(mapper);
return new RunContext.Builder()
.put(new OB30Inspector.Builder().build())
.put(res)
.put(Key.JACKSON_OBJECTMAPPER, mapper)
.put(Key.JSONPATH_EVALUATOR, jsonPath)
.build();
}
}

View File

@ -0,0 +1,34 @@
package org.oneedtech.inspect.vc.util;
import java.net.URI;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import com.apicatalog.jsonld.document.Document;
import com.apicatalog.jsonld.loader.DocumentLoader;
import com.apicatalog.jsonld.loader.DocumentLoaderOptions;
public class CachingDocumentLoaderTests {
@Test
void testStaticCachedDocumentBundled() {
Assertions.assertDoesNotThrow(()->{
DocumentLoader loader = new CachingDocumentLoader();
for(String id : CachingDocumentLoader.bundled.keySet()) {
Document doc = loader.loadDocument(new URI(id), new DocumentLoaderOptions());
Assertions.assertNotNull(doc);
}
});
}
@Test
void testStaticCachedDocumentKey() {
Assertions.assertDoesNotThrow(()->{
DocumentLoader loader = new CachingDocumentLoader();
URI uri = new URI("https://www.w3.org/ns/did/v1");
Document doc = loader.loadDocument(uri, new DocumentLoaderOptions());
Assertions.assertNotNull(doc);
});
}
}

View File

@ -0,0 +1,43 @@
package org.oneedtech.inspect.vc.util;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.oneedtech.inspect.util.json.ObjectMapperCache.Config.DEFAULT;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.oneedtech.inspect.core.probe.json.JsonPathEvaluator;
import org.oneedtech.inspect.util.json.ObjectMapperCache;
import org.oneedtech.inspect.vc.Samples;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
public class JsonNodeUtilTests {
static final ObjectMapper mapper = ObjectMapperCache.get(DEFAULT);
static final JsonPathEvaluator jsonPath = new JsonPathEvaluator(mapper);
@Test
void testFlattenNodeList() {
Assertions.assertDoesNotThrow(()->{
String json = Samples.OB30.JSON.COMPLETE_JSON.asString();
JsonNode root = mapper.readTree(json);
List<JsonNode> list = JsonNodeUtil.asNodeList(root, "$..endorsement", jsonPath);
Assertions.assertEquals(5, list.size());
for(JsonNode node : list) {
ArrayNode types = (ArrayNode) node.get("type");
boolean found = false;
for(JsonNode val : types) {
if(val.asText().equals("EndorsementCredential")) {
found = true;
}
}
assertTrue(found);
}
});
}
}

View File

@ -0,0 +1,712 @@
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "http://1edtech.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"name": "1EdTech University Degree for Example Student",
"description": "1EdTech University Degree Description",
"image": {
"id": "https://1edtech.edu/credentials/3732/image",
"type": "Image",
"caption": "1EdTech University Degree for Example Student"
},
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"type": [
"AchievementSubject"
],
"activityEndDate": "2010-01-02T00:00:00Z",
"activityStartDate": "2010-01-01T00:00:00Z",
"creditsEarned": 42,
"licenseNumber": "A-9320041",
"role": "Major Domo",
"source": {
"id": "https://school.edu/issuers/201234",
"type": [
"Profile"
],
"name": "1EdTech College of Arts"
},
"term": "Fall",
"identifier": [
{
"type": "IdentityObject",
"identityHash": "student@1edtech.edu",
"identityType": "email",
"hashed": false,
"salt": "not-used"
},
{
"type": "IdentityObject",
"identityHash": "somebody@gmail.com",
"identityType": "email",
"hashed": false,
"salt": "not-used"
}
],
"achievement": {
"id": "https://1edtech.edu/achievements/degree",
"type": [
"Achievement"
],
"alignment": [
{
"type": [
"Alignment"
],
"targetCode": "degree",
"targetDescription": "1EdTech University Degree programs.",
"targetName": "1EdTech University Degree",
"targetFramework": "1EdTech University Program and Course Catalog",
"targetType": "CFItem",
"targetUrl": "https://1edtech.edu/catalog/degree"
},
{
"type": [
"Alignment"
],
"targetCode": "degree",
"targetDescription": "1EdTech University Degree programs.",
"targetName": "1EdTech University Degree",
"targetFramework": "1EdTech University Program and Course Catalog",
"targetType": "CTDL",
"targetUrl": "https://credentialengineregistry.org/resources/ce-98cb027b-95ef-4494-908d-6f7790ec6b6b"
}
],
"achievementType": "Degree",
"creator": {
"id": "https://1edtech.edu/issuers/565049",
"type": [
"Profile"
],
"name": "1EdTech University",
"url": "https://1edtech.edu",
"phone": "1-222-333-4444",
"description": "1EdTech University provides online degree programs.",
"endorsement": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"type": [
"VerifiableCredential",
"EndorsementCredential"
],
"issuer": {
"id": "https://accrediter.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example Accrediting Agency"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"expirationDate": "2020-01-01T00:00:00Z",
"credentialSubject": {
"id": "https://1edtech.edu/issuers/565049",
"type": [
"EndorsementSubject"
],
"endorsementComment": "1EdTech University is in good standing"
},
"credentialSchema": [
{
"id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/endorsementcredential.json",
"type": "1EdTechJsonSchemaValidator2019"
},
{
"id": "https://accrediter.edu/schema/endorsementcredential.json",
"type": "JsonSchemaValidator2018"
}
],
"credentialStatus": {
"id": "https://1edtech.edu/credentials/3732/revocations",
"type": "1EdTechRevocationList"
},
"refreshService": {
"id": "http://1edtech.edu/credentials/3732",
"type": "1EdTechCredentialRefresh"
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-05-26T18:17:08Z",
"verificationMethod": "https://accrediter.edu/issuers/565049#key-1",
"proofPurpose": "assertionMethod",
"proofValue": "zvPkQiUFfJrgnCRhyPkTSkgrGXbnLR15pHH5HZVYNdM4TCAwQHqG7fMeMPLtYNRnEgoV1aJdR5E61eWu5sWRYgtA"
}
]
},
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"type": [
"VerifiableCredential",
"EndorsementCredential"
],
"issuer": {
"id": "https://state.gov/issuers/565049",
"type": [
"Profile"
],
"name": "State Department of Education"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"expirationDate": "2020-01-01T00:00:00Z",
"credentialSubject": {
"id": "https://1edtech.edu/issuers/565049",
"type": [
"EndorsementSubject"
],
"endorsementComment": "1EdTech University is in good standing"
},
"credentialSchema": [
{
"id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/endorsementcredential.json",
"type": "1EdTechJsonSchemaValidator2019"
},
{
"id": "https://state.gov/schema/endorsementcredential.json",
"type": "JsonSchemaValidator2018"
}
],
"credentialStatus": {
"id": "https://state.gov/credentials/3732/revocations",
"type": "1EdTechRevocationList"
},
"refreshService": {
"id": "http://state.gov/credentials/3732",
"type": "1EdTechCredentialRefresh"
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-05-26T18:25:59Z",
"verificationMethod": "https://accrediter.edu/issuers/565049#key-1",
"proofPurpose": "assertionMethod",
"proofValue": "z5bDnmSgDczXwZGya6ZjxKaxkdKxzsCMiVSsgEVWxnaWK7ZqbKnzcCd7mUKE9DQaAL2QMXP5AquPeW6W2CWrZ7jNC"
}
]
}
],
"image": {
"id": "https://1edtech.edu/logo.png",
"type": "Image",
"caption": "1EdTech University logo"
},
"email": "registrar@1edtech.edu",
"address": {
"type": [
"Address"
],
"addressCountry": "USA",
"addressCountryCode": "US",
"addressRegion": "TX",
"addressLocality": "Austin",
"streetAddress": "123 First St",
"postOfficeBoxNumber": "1",
"postalCode": "12345",
"geo": {
"type": "GeoCoordinates",
"latitude": 1,
"longitude": 1
}
},
"otherIdentifier": [
{
"type": "IdentifierEntry",
"identifier": "12345",
"identifierType": "sourcedId"
},
{
"type": "IdentifierEntry",
"identifier": "67890",
"identifierType": "nationalIdentityNumber"
}
],
"official": "Horace Mann",
"parentOrg": {
"id": "did:example:123456789",
"type": [
"Profile"
],
"name": "Universal Universities"
}
},
"creditsAvailable": 36,
"criteria": {
"id": "https://1edtech.edu/achievements/degree",
"narrative": "# Degree Requirements\nStudents must complete..."
},
"description": "1EdTech University Degree Description",
"endorsement": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"type": [
"VerifiableCredential",
"EndorsementCredential"
],
"issuer": {
"id": "https://accrediter.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example Accrediting Agency"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"expirationDate": "2020-01-01T00:00:00Z",
"credentialSubject": {
"id": "https://1edtech.edu/issuers/565049",
"type": [
"EndorsementSubject"
],
"endorsementComment": "1EdTech University is in good standing"
},
"credentialSchema": [
{
"id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/endorsementcredential.json",
"type": "1EdTechJsonSchemaValidator2019"
},
{
"id": "https://accrediter.edu/schema/endorsementcredential.json",
"type": "JsonSchemaValidator2018"
}
],
"credentialStatus": {
"id": "https://1edtech.edu/credentials/3732/revocations",
"type": "1EdTechRevocationList"
},
"refreshService": {
"id": "http://1edtech.edu/credentials/3732",
"type": "1EdTechCredentialRefresh"
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-05-26T18:17:08Z",
"verificationMethod": "https://accrediter.edu/issuers/565049#key-1",
"proofPurpose": "assertionMethod",
"proofValue": "zvPkQiUFfJrgnCRhyPkTSkgrGXbnLR15pHH5HZVYNdM4TCAwQHqG7fMeMPLtYNRnEgoV1aJdR5E61eWu5sWRYgtA"
}
]
}
],
"fieldOfStudy": "Research",
"humanCode": "R1",
"image": {
"id": "https://1edtech.edu/achievements/degree/image",
"type": "Image",
"caption": "1EdTech University Degree"
},
"name": "1EdTech University Degree",
"otherIdentifier": [
{
"type": "IdentifierEntry",
"identifier": "abde",
"identifierType": "identifier"
}
],
"resultDescription": [
{
"id": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c",
"type": [
"ResultDescription"
],
"alignment": [
{
"type": [
"Alignment"
],
"targetCode": "project",
"targetDescription": "Project description",
"targetName": "Final Project",
"targetFramework": "1EdTech University Program and Course Catalog",
"targetType": "CFItem",
"targetUrl": "https://1edtech.edu/catalog/degree/project"
}
],
"allowedValue": [
"D",
"C",
"B",
"A"
],
"name": "Final Project Grade",
"requiredValue": "C",
"resultType": "LetterGrade"
},
{
"id": "urn:uuid:a70ddc6a-4c4a-4bd8-8277-cb97c79f40c5",
"type": [
"ResultDescription"
],
"alignment": [
{
"type": [
"Alignment"
],
"targetCode": "project",
"targetDescription": "Project description",
"targetName": "Final Project",
"targetFramework": "1EdTech University Program and Course Catalog",
"targetType": "CFItem",
"targetUrl": "https://1edtech.edu/catalog/degree/project"
}
],
"allowedValue": [
"D",
"C",
"B",
"A"
],
"name": "Final Project Grade",
"requiredLevel": "urn:uuid:d05a0867-d0ad-4b03-bdb5-28fb5d2aab7a",
"resultType": "RubricCriterionLevel",
"rubricCriterionLevel": [
{
"id": "urn:uuid:d05a0867-d0ad-4b03-bdb5-28fb5d2aab7a",
"type": [
"RubricCriterionLevel"
],
"alignment": [
{
"type": [
"Alignment"
],
"targetCode": "project",
"targetDescription": "Project description",
"targetName": "Final Project",
"targetFramework": "1EdTech University Program and Course Catalog",
"targetType": "CFRubricCriterionLevel",
"targetUrl": "https://1edtech.edu/catalog/degree/project/rubric/levels/mastered"
}
],
"description": "The author demonstrated...",
"level": "Mastered",
"name": "Mastery",
"points": "4"
},
{
"id": "urn:uuid:6b84b429-31ee-4dac-9d20-e5c55881f80e",
"type": [
"RubricCriterionLevel"
],
"alignment": [
{
"type": [
"Alignment"
],
"targetCode": "project",
"targetDescription": "Project description",
"targetName": "Final Project",
"targetFramework": "1EdTech University Program and Course Catalog",
"targetType": "CFRubricCriterionLevel",
"targetUrl": "https://1edtech.edu/catalog/degree/project/rubric/levels/basic"
}
],
"description": "The author demonstrated...",
"level": "Basic",
"name": "Basic",
"points": "4"
}
]
},
{
"id": "urn:uuid:b07c0387-f2d6-4b65-a3f4-f4e4302ea8f7",
"type": [
"ResultDescription"
],
"name": "Project Status",
"resultType": "Status"
}
],
"specialization": "Computer Science Research",
"tag": [
"research",
"computer science"
]
},
"image": {
"id": "https://1edtech.edu/credentials/3732/image",
"type": "Image",
"caption": "1EdTech University Degree for Example Student"
},
"narrative": "There is a final project report and source code evidence.",
"result": [
{
"type": [
"Result"
],
"alignment": [
{
"type": [
"Alignment"
],
"targetCode": "project",
"targetDescription": "Project description",
"targetName": "Final Project",
"targetFramework": "1EdTech University Program and Course Catalog",
"targetType": "CFItem",
"targetUrl": "https://1edtech.edu/catalog/degree/project/result/1"
}
],
"resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c",
"value": "A"
},
{
"type": [
"Result"
],
"achievedLevel": "urn:uuid:d05a0867-d0ad-4b03-bdb5-28fb5d2aab7a",
"alignment": [
{
"type": [
"Alignment"
],
"targetCode": "project",
"targetDescription": "Project description",
"targetName": "Final Project",
"targetFramework": "1EdTech University Program and Course Catalog",
"targetType": "CFItem",
"targetUrl": "https://1edtech.edu/catalog/degree/project/result/1"
}
],
"resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c"
},
{
"type": [
"Result"
],
"resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c",
"status": "Completed"
}
]
},
"endorsement": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"type": [
"VerifiableCredential",
"EndorsementCredential"
],
"issuer": {
"id": "https://accrediter.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example Accrediting Agency"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"expirationDate": "2020-01-01T00:00:00Z",
"credentialSubject": {
"id": "https://1edtech.edu/issuers/565049",
"type": [
"EndorsementSubject"
],
"endorsementComment": "1EdTech University is in good standing"
},
"credentialSchema": [
{
"id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/endorsementcredential.json",
"type": "1EdTechJsonSchemaValidator2019"
},
{
"id": "https://accrediter.edu/schema/endorsementcredential.json",
"type": "JsonSchemaValidator2018"
}
],
"credentialStatus": {
"id": "https://1edtech.edu/credentials/3732/revocations",
"type": "1EdTechRevocationList"
},
"refreshService": {
"id": "http://1edtech.edu/credentials/3732",
"type": "1EdTechCredentialRefresh"
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-05-26T18:17:08Z",
"verificationMethod": "https://accrediter.edu/issuers/565049#key-1",
"proofPurpose": "assertionMethod",
"proofValue": "zvPkQiUFfJrgnCRhyPkTSkgrGXbnLR15pHH5HZVYNdM4TCAwQHqG7fMeMPLtYNRnEgoV1aJdR5E61eWu5sWRYgtA"
}
]
}
],
"evidence": [
{
"id": "https://1edtech.edu/credentials/3732/evidence/1",
"type": [
"Evidence"
],
"narrative": "# Final Project Report \n This project was ...",
"name": "Final Project Report",
"description": "This is the final project report.",
"genre": "Research",
"audience": "Department"
},
{
"id": "https://github.com/somebody/project",
"type": [
"Evidence"
],
"name": "Final Project Code",
"description": "This is the source code for the final project app.",
"genre": "Research",
"audience": "Department"
}
],
"issuer": {
"id": "https://1edtech.edu/issuers/565049",
"type": [
"Profile"
],
"name": "1EdTech University",
"url": "https://1edtech.edu",
"phone": "1-222-333-4444",
"description": "1EdTech University provides online degree programs.",
"endorsement": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"type": [
"VerifiableCredential",
"EndorsementCredential"
],
"issuer": {
"id": "https://accrediter.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example Accrediting Agency"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"expirationDate": "2020-01-01T00:00:00Z",
"credentialSubject": {
"id": "https://1edtech.edu/issuers/565049",
"type": [
"EndorsementSubject"
],
"endorsementComment": "1EdTech University is in good standing"
},
"credentialSchema": [
{
"id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/endorsementcredential.json",
"type": "1EdTechJsonSchemaValidator2019"
},
{
"id": "https://accrediter.edu/schema/endorsementcredential.json",
"type": "JsonSchemaValidator2018"
}
],
"credentialStatus": {
"id": "https://1edtech.edu/credentials/3732/revocations",
"type": "1EdTechRevocationList"
},
"refreshService": {
"id": "http://1edtech.edu/credentials/3732",
"type": "1EdTechCredentialRefresh"
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-05-26T18:17:08Z",
"verificationMethod": "https://accrediter.edu/issuers/565049#key-1",
"proofPurpose": "assertionMethod",
"proofValue": "zvPkQiUFfJrgnCRhyPkTSkgrGXbnLR15pHH5HZVYNdM4TCAwQHqG7fMeMPLtYNRnEgoV1aJdR5E61eWu5sWRYgtA"
}
]
}
],
"image": {
"id": "https://1edtech.edu/logo.png",
"type": "Image",
"caption": "1EdTech University logo"
},
"email": "registrar@1edtech.edu",
"address": {
"type": [
"Address"
],
"addressCountry": "USA",
"addressCountryCode": "US",
"addressRegion": "TX",
"addressLocality": "Austin",
"streetAddress": "123 First St",
"postOfficeBoxNumber": "1",
"postalCode": "12345",
"geo": {
"type": "GeoCoordinates",
"latitude": 1,
"longitude": 1
}
},
"otherIdentifier": [
{
"type": "IdentifierEntry",
"identifier": "12345",
"identifierType": "sourcedId"
},
{
"type": "IdentifierEntry",
"identifier": "67890",
"identifierType": "nationalIdentityNumber"
}
],
"official": "Horace Mann",
"parentOrg": {
"id": "did:example:123456789",
"type": [
"Profile"
],
"name": "Universal Universities"
}
},
"issuanceDate": "2010-01-01T00:00:00Z",
"expirationDate": "2020-01-01T00:00:00Z",
"credentialSchema": [
{
"id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/achievementcredential.json",
"type": "1EdTechJsonSchemaValidator2019"
}
],
"credentialStatus": {
"id": "https://1edtech.edu/credentials/3732/revocations",
"type": "1EdTechRevocationList"
},
"refreshService": {
"id": "http://1edtech.edu/credentials/3732",
"type": "1EdTechCredentialRefresh"
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-06-09T22:56:28Z",
"verificationMethod": "https://1edtech.edu/issuers/565049#key-1",
"proofPurpose": "assertionMethod",
"proofValue": "zPpg92pBBEqRMxAqHMFQJ6Kmwf1thF9GdzqCofyWTLE6AhuahQixBNuG9BLgk6vb8K3NoqzanajYYYJbEcEhvQtM"
}
]
}

View File

@ -0,0 +1,35 @@
{
"@context": [
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "http://example.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"issuer": {
"id": "https://example.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example University"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"name": "Example University Degree",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"type": [
"AchievementSubject"
]
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-06-28T16:28:36Z",
"verificationMethod": "did:key:z6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj",
"proofPurpose": "assertionMethod",
"proofValue": "z3MUt2ZuU8Byqivxh6GphEM65AFYyNaGYibm97xLTafM7uGufZQLKvJR8itZwxKskvtFM3CUty46v26DZidMNoQnM"
}
]
}

View File

@ -0,0 +1,37 @@
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "http://example.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"issuer": {
"id": "https://example.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example University"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"expirationDate": "2020-01-20T00:00:00Z",
"name": "Example University Degree",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"type": [
"AchievementSubject"
]
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-06-28T16:28:36Z",
"verificationMethod": "did:key:z6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj",
"proofPurpose": "assertionMethod",
"proofValue": "z3MUt2ZuU8Byqivxh6GphEM65AFYyNaGYibm97xLTafM7uGufZQLKvJR8itZwxKskvtFM3CUty46v26DZidMNoQnM"
}
]
}

View File

@ -0,0 +1,37 @@
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "http://example.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"issuer": {
"id": "https://example.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example University"
},
"issuanceDate": "2040-01-01T00:00:00Z",
"expirationDate": "2050-01-20T00:00:00Z",
"name": "Example University Degree",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"type": [
"AchievementSubject"
]
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-06-28T16:28:36Z",
"verificationMethod": "did:key:z6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj",
"proofPurpose": "assertionMethod",
"proofValue": "z3MUt2ZuU8Byqivxh6GphEM65AFYyNaGYibm97xLTafM7uGufZQLKvJR8itZwxKskvtFM3CUty46v26DZidMNoQnM"
}
]
}

View File

@ -0,0 +1,29 @@
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "http://example.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"issuanceDate": "2010-01-01T00:00:00Z",
"name": "Example University Degree",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"type": [
"AchievementSubject"
]
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-06-28T16:28:36Z",
"verificationMethod": "did:key:z6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj",
"proofPurpose": "assertionMethod",
"proofValue": "z3MUt2ZuU8Byqivxh6GphEM65AFYyNaGYibm97xLTafM7uGufZQLKvJR8itZwxKskvtFM3CUty46v26DZidMNoQnM"
}
]
}

View File

@ -0,0 +1,54 @@
{
"@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": "http://example.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"issuer": {
"id": "https://example.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example University"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"name": "Example University Degree",
"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"
}
},
"credentialSchema": [
{
"id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/json/ob_v3p0_achievementcredential_schema.json",
"type": "1EdTechJsonSchemaValidator2019"
}
],
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-09-15T15:48:32Z",
"verificationMethod": "https://example.edu/issuers/565049#xxMkmY1R6tG2NEdRHzphdRT6JqxeYpHwLAHwbrDfQULpkMAj",
"proofPurpose": "assertionMethod",
"proofValue": "z3yUuWbFsLUp2CUrSZRaRbTk1UnkhpoJgJYu1SdMqd3AEMotpY41sKky7VzavnSfjApggtWJg1tcREvs5H4ZNnBRH"
}
]
}

View File

@ -0,0 +1,54 @@
{
"@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": "http://example.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"issuer": {
"id": "https://example.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example University"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"name": "Example University Degree",
"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"
}
},
"credentialSchema": [
{
"id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/json/ob_v3p0_achievementcredential_schema.json",
"type": "1EdTechJsonSchemaValidator2019"
}
],
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-09-15T15:48:32Z",
"verificationMethod": "https://example.edu/issuers/565049#z6MkmY1R6tG2NEdRHzphdRT6JqxeYpHwLAHwbrDfQULpkMAj",
"proofPurpose": "assertionMethod",
"proofValue": "z3fQCWGpz7b1HSH6DTwYiH5vutqtpJb5SHiP1VFK22xeBEW2D61tC9j3SktwPLNxPnTNZnPt4GeAZJPdVYserRqs4"
}
]
}

View File

@ -0,0 +1,36 @@
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "http://example.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OtherCredential"
],
"issuer": {
"id": "https://example.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example University"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"name": "Example University Degree",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"type": [
"AchievementSubject"
]
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-06-09T22:56:28Z",
"verificationMethod": "https://example.edu/issuers/565049#key-1",
"proofPurpose": "assertionMethod",
"proofValue": "z58ieJCh4kN6eE2Vq4TyYURKSC4hWWEK7b75NNUL2taZMhKqwTteuByG1wRoGDdCqqNLW5Gq1diUi4qyZ63tQRtyN"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:openbadges="https://purl.imsglobal.org/ob/v3p0" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<openbadges:credential>
<![CDATA[
{
"@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": "http://example.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"issuer": {
"id": "https://example.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example University"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"name": "Example University Degree",
"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"
}
},
"credentialSchema": [
{
"id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/json/ob_v3p0_achievementcredential_schema.json",
"type": "1EdTechJsonSchemaValidator2019"
}
],
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-09-15T15:48:32Z",
"verificationMethod": "https://example.edu/issuers/565049#z6MkmY1R6tG2NEdRHzphdRT6JqxeYpHwLAHwbrDfQULpkMAj",
"proofPurpose": "assertionMethod",
"proofValue": "z3yUuWbFsLUp2CUrSZRaRbTk1UnkhpoJgJYu1SdMqd3AEMotpY41sKky7VzavnSfjApggtWJg1tcREvs5H4ZNnBRH"
}
]
}
]]>
</openbadges:credential>
<g>
<path fill="none" stroke="#040000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M500,928.8c0,0,0,40.8,245,40.8" />
<path fill="none" stroke="#040000" stroke-width="1.9215" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M500,928.8c0,0,40.8,0,285.8,0" />
<path fill="none" stroke="#040000" stroke-width="1.9215" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M214.2,928.8c0,0,40.8,0,285.8,0" />
<path fill="none" stroke="#040000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M500,928.8c0,0,0,40.8-245,40.8" />
<path d="M581.7,316.2c22.6,0,40.8,18.3,40.8,40.8s-18.3,40.8-40.8,40.8c-22.6,0-40.8-18.3-40.8-40.8S559.1,316.2,581.7,316.2z" />
<circle cx="418.3" cy="357.1" r="40.8" />
<path d="M500,403.6c0,0-34.8,25.3-81.7,38.9c24.2,63.1,81.7,77.9,81.7,77.9s57.5-14.7,81.7-77.9C534.8,429,500,403.6,500,403.6z" />
<path d="M856.6,689.6c0,0-3.3-89,16.6-196.2c39.4,104.7,119.7,164.3,116.8,203.3C930.9,739.3,856.6,689.6,856.6,689.6z" />
<path d="M143.4,689.6c0,0,3.3-89-16.6-196.2C87.5,598.1,7.1,657.7,10.1,696.7C69.1,739.3,143.4,689.6,143.4,689.6z" />
<path d="M835.4,367.4c0-232.1-184.3-337-335.4-337s-335.4,104.8-335.4,337c-3,24.9-42.3,125.5-42.3,151.4c0,269.7,109.3,450.8,377.8,450.8c268.5,0,377.8-181.1,377.8-450.8C877.8,492.9,838.4,392.2,835.4,367.4z M500,856.9c-119.1,0-264.4-56.5-264.4-265c0-100.2,37.8-148.3,37.8-148.3s-72.5-271.8,132.2-271.8c50.9,0,77.3,48.4,94.4,48.4s50.8-48.4,94.4-48.4c204.7,0,132.2,271.8,132.2,271.8s37.8,48.1,37.8,148.3C764.5,800.4,619.1,856.9,500,856.9z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:openbadges="https://purl.imsglobal.org/ob/v3p0" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<openbadges:credential verify="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImp3ayI6eyJlIjoiQVFBQiIsImt0eSI6IlJTQSIsIm4iOiI2dGM1dnJnQ2JzbGNrRzdPTTJoWkxJMnNxSHZabTdLS2FPTTItck1WczdyVjBRMEFiZk1ia1pXRzlfTENfM3RaelRld0tiblY1ZXZycFlVVGI4V09TVzk3dXNwZFFFMG5wR1pKdFdFWWgxaFJoM1hEVFBPVFRLa1ZwR2lJQWVocTdIRHJsYmxUZE41TUlqRWpPNnFhWTRhRzlIYXBMeXZHVUY2a2xGb2ctODZOWktOUXR0dVJvZWtlb3ZBdWhxZFhMd0RVZnJuOWlZTEowNmtWQkNKa1ZHdS1RYVlBbUFBcElfTFJGUWV4SF9BNXJQZnhZWEhReHctWmpBaVZNdkhwYmVtYkExWm9HUU5XOW92ZS1ZNWRQSm5CcWlDNEtSTzViMUctQU1KaVdHZDEwSFVkTEFDZ3FHX2FkWGktcmdHUFJINmpGRG9ZQzc2eDROQzBvVG9NS3cifX0.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvb2IvdjNwMC9jb250ZXh0Lmpzb24iLCJodHRwczovL3B1cmwuaW1zZ2xvYmFsLm9yZy9zcGVjL29iL3YzcDAvZXh0ZW5zaW9ucy5qc29uIl0sImlkIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzM3MzIiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJpc3N1ZXIiOnsiaWQiOiJodHRwczovL2V4YW1wbGUuZWR1L2lzc3VlcnMvNTY1MDQ5IiwidHlwZSI6WyJQcm9maWxlIl0sIm5hbWUiOiJFeGFtcGxlIFVuaXZlcnNpdHkifSwiaXNzdWFuY2VEYXRlIjoiMjAxMC0wMS0wMVQwMDowMDowMFoiLCJuYW1lIjoiRXhhbXBsZSBVbml2ZXJzaXR5IERlZ3JlZSIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIiwidHlwZSI6WyJBY2hpZXZlbWVudFN1YmplY3QiXSwiYWNoaWV2ZW1lbnQiOnsiaWQiOiJodHRwczovL2V4YW1wbGUuY29tL2FjaGlldmVtZW50cy8yMXN0LWNlbnR1cnktc2tpbGxzL3RlYW13b3JrIiwidHlwZSI6WyJBY2hpZXZlbWVudCJdLCJjcml0ZXJpYSI6eyJuYXJyYXRpdmUiOiJUZWFtIG1lbWJlcnMgYXJlIG5vbWluYXRlZCBmb3IgdGhpcyBiYWRnZSBieSB0aGVpciBwZWVycyBhbmQgcmVjb2duaXplZCB1cG9uIHJldmlldyBieSBFeGFtcGxlIENvcnAgbWFuYWdlbWVudC4ifSwiZGVzY3JpcHRpb24iOiJUaGlzIGJhZGdlIHJlY29nbml6ZXMgdGhlIGRldmVsb3BtZW50IG9mIHRoZSBjYXBhY2l0eSB0byBjb2xsYWJvcmF0ZSB3aXRoaW4gYSBncm91cCBlbnZpcm9ubWVudC4iLCJuYW1lIjoiVGVhbXdvcmsifX0sImNyZWRlbnRpYWxTY2hlbWEiOlt7ImlkIjoiaHR0cHM6Ly9wdXJsLmltc2dsb2JhbC5vcmcvc3BlYy9vYi92M3AwL3NjaGVtYS9qc29uL29iX3YzcDBfYWNoaWV2ZW1lbnRjcmVkZW50aWFsX3NjaGVtYS5qc29uIiwidHlwZSI6IjFFZFRlY2hKc29uU2NoZW1hVmFsaWRhdG9yMjAxOSJ9XX0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5lZHUvaXNzdWVycy81NjUwNDkiLCJuYmYiOjEyNjIzMDQwMDAsImp0aSI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8zNzMyIiwic3ViIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIn0.ryVxvKHEQFu2b0Cu1L64A90ogiq4Ggkqv5i2ZN7phUt9AOlJfqKQIGrzl46CfmcaFqOBxwEJR_xtpaFRUrarwBnl81CSyHwVfSE1z9gQVLXgSRLNrh0UBMM53O4aVoJO-nYbr1f5YcS_d762o6TDlj9gAoCa3Y5j0vhwZPeRJD9cEONeYgpRhndYAD1SrFNvOopSQrY8l48p02oyYBdn1wSi0JOYAyn5MkvsfN6gL3P7GdOqoPYS-CoP3mtYrngUn9IwjpZUK7ZRnEzBgW_cvYwojZv5iYAtG56VN51JryxHeFL65rEchrCOnPNsQncx6M1UCx3RqOFgt_KE2pSKyw"></openbadges:credential>
<g>
<path fill="none" stroke="#040000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M500,928.8c0,0,0,40.8,245,40.8" />
<path fill="none" stroke="#040000" stroke-width="1.9215" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M500,928.8c0,0,40.8,0,285.8,0" />
<path fill="none" stroke="#040000" stroke-width="1.9215" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M214.2,928.8c0,0,40.8,0,285.8,0" />
<path fill="none" stroke="#040000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M500,928.8c0,0,0,40.8-245,40.8" />
<path d="M581.7,316.2c22.6,0,40.8,18.3,40.8,40.8s-18.3,40.8-40.8,40.8c-22.6,0-40.8-18.3-40.8-40.8S559.1,316.2,581.7,316.2z" />
<circle cx="418.3" cy="357.1" r="40.8" />
<path d="M500,403.6c0,0-34.8,25.3-81.7,38.9c24.2,63.1,81.7,77.9,81.7,77.9s57.5-14.7,81.7-77.9C534.8,429,500,403.6,500,403.6z" />
<path d="M856.6,689.6c0,0-3.3-89,16.6-196.2c39.4,104.7,119.7,164.3,116.8,203.3C930.9,739.3,856.6,689.6,856.6,689.6z" />
<path d="M143.4,689.6c0,0,3.3-89-16.6-196.2C87.5,598.1,7.1,657.7,10.1,696.7C69.1,739.3,143.4,689.6,143.4,689.6z" />
<path d="M835.4,367.4c0-232.1-184.3-337-335.4-337s-335.4,104.8-335.4,337c-3,24.9-42.3,125.5-42.3,151.4c0,269.7,109.3,450.8,377.8,450.8c268.5,0,377.8-181.1,377.8-450.8C877.8,492.9,838.4,392.2,835.4,367.4z M500,856.9c-119.1,0-264.4-56.5-264.4-265c0-100.2,37.8-148.3,37.8-148.3s-72.5-271.8,132.2-271.8c50.9,0,77.3,48.4,94.4,48.4s50.8-48.4,94.4-48.4c204.7,0,132.2,271.8,132.2,271.8s37.8,48.1,37.8,148.3C764.5,800.4,619.1,856.9,500,856.9z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,27 @@
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "http://example.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"issuer": {
"id": "https://example.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example University"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"name": "Example University Degree",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"type": [
"AchievementSubject"
]
}
}

View File

@ -0,0 +1,54 @@
{
"@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": "http://example.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"issuer": {
"id": "https://example.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example University"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"name": "Example University Degree",
"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"
}
},
"credentialSchema": [
{
"id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/json/ob_v3p0_achievementcredential_schema.json",
"type": "1EdTechJsonSchemaValidator2019"
}
],
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-09-15T15:48:32Z",
"verificationMethod": "https://example.edu/issuers/565049#z6MkmY1R6tG2NEdRHzphdRT6JqxeYpHwLAHwbrDfQULpkMAj",
"proofPurpose": "assertionMethod",
"proofValue": "z3yUuWbFsLUp2CUrSZRaRbTk1UnkhpoJgJYu1SdMqd3AEMotpY41sKky7VzavnSfjApggtWJg1tcREvs5H4ZNnBRH"
}
]
}

View File

@ -0,0 +1 @@
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vaW1zZ2xvYmFsLmdpdGh1Yi5pby9vcGVuYmFkZ2VzLXNwZWNpZmljYXRpb24vY29udGV4dC5qc29uIl0sImlkIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzM3MzIiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJpc3N1ZXIiOnsiaWQiOiJodHRwczovL2V4YW1wbGUuZWR1L2lzc3VlcnMvNTY1MDQ5IiwidHlwZSI6WyJQcm9maWxlIl0sIm5hbWUiOiJFeGFtcGxlIFVuaXZlcnNpdHkifSwiaXNzdWFuY2VEYXRlIjoiMjAxMC0wMS0wMVQwMDowMDowMFoiLCJuYW1lIjoiRXhhbXBsZSBVbml2ZXJzaXR5IERlZ3JlZSIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIiwidHlwZSI6WyJBY2hpZXZlbWVudFN1YmplY3QiXX19LCJpc3MiOiJodHRwczovL2V4YW1wbGUuZWR1L2lzc3VlcnMvNTY1MDQ5IiwibmJmIjoxMjYyMzA0MDAwLCJqdGkiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMzczMiIsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSJ9.G7W8od9rSZRsVyk26rXjg_fH2CyUihwNpepd6tWgLt_UHC1vUU0Clox8IicnOSkMyYEqAuNZAdCC9_35i1oUcyj1c076Aa0dsVQ2fFVuQPqXBlyZWcBmo5jqOK6R9NHzRAYXwLRXgrB8gz3lSK55cnHTnMtkpXXcUcHkS5ylWbXCLeOWKoygOCuxRN3N6kP-0HOyuk15PWlnkJ2zEKz2pBtVPaNEydcT0kEtoHFMEWVwqo6rnGV-Ea3M7ssDt3145mcl-DVYLXmBVdT8KoO47QAOBaVMR6k-hgrHNBcdhpI-o6IvLIFsGLgrNvWN67i8Z7Baum1mP-HBpsAigdmIpA