Semantic Web and Web 3.0 · class 11 · demo
A verifiable credential is signed RDF
examples/08-credential/credential.jsonld: read with its two contexts (local excerpts — there is no network) it is a
graph of 13 triples. Signed here as sign-credential.mjs signs it, with a key made when this
server started and kept only in memory. Then the verifier recomputes who signed what it was given.
Signed by someone else: the signature does NOT verify
expected signer (the proof's verificationMethod) 0xE1c8643C81B21181E648B3eF4Ce75D0229C34921
recovered from the signature and this content 0x453Cb327defeAd3867137ED7640056B498a5c62d
Another key signed exactly the same content and the proof claims to be the university: the recovered address gives it away.
The verifier's other checks
warning it is not valid yet (validFrom 2027-07-20T00:00:00Z)
As in verify-credential.py: validFrom is in the future on purpose (the class README, note 4) — a credential has a validity period, and the verifier checks it. A valid signature does not make it valid today.
The triples the verifier reads — the changed ones highlighted
The document shown below, read as RDF (lib/rdf.mjs, with the contexts of data/contexts/). With the demo's
proof the graph has 14 triples; the key created is not in any context, so it gives no triple.
| Subject | Predicate | Object |
|---|---|---|
| _:c0 | a | cred:VerifiableCredential |
| _:c0 | a | ufp:DiplomaUFP |
| _:c0 | cred:issuer | <did:web:ufp.pt> |
| _:c0 | cred:validFrom | "2027-07-20T00:00:00Z"^^xsd:dateTime |
| <did:key:z6Mk...> | ufp:degree | "MSc in Computer Engineering" |
| <did:key:z6Mk...> | ufp:grade | 17 |
| _:c0 | cred:credentialSubject | <did:key:z6Mk...> |
| <https://ufp.pt/status/2027#42> | a | cred:StatusList2021 |
| _:c0 | cred:credentialStatus | <https://ufp.pt/status/2027#42> |
| _:c1 | a | sec:DataIntegrityProof |
| _:c1 | sec:cryptosuite | "ecdsa-secp256k1-jcs-2026-demo" |
| _:c1 | sec:verificationMethod | <did:pkh:eip155:1:0xE1c8643C81B21181E648B3eF4Ce75D0229C34921> |
| _:c1 | sec:proofValue | "0x58c8fd359164b78bb44a97aebe24899826a69d9e8ab096d50161aff27a9c49cf449807c9a89d173f7d8707a4c80d424c389fc6ea543b8e9d0d9d8517d54a26ad1b" |
| _:c0 | sec:proof | _:c1 |
What the verifier was given
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://ufp.pt/contexts/diploma/v1"
],
"type": [
"VerifiableCredential",
"DiplomaUFP"
],
"issuer": "did:web:ufp.pt",
"validFrom": "2027-07-20T00:00:00Z",
"credentialSubject": {
"id": "did:key:z6Mk...",
"degree": "MSc in Computer Engineering",
"grade": 17
},
"credentialStatus": {
"type": "StatusList2021",
"id": "https://ufp.pt/status/2027#42"
},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "ecdsa-secp256k1-jcs-2026-demo",
"created": "2026-11-01T13:17:13.132Z",
"verificationMethod": "did:pkh:eip155:1:0xE1c8643C81B21181E648B3eF4Ce75D0229C34921",
"proofValue": "0x58c8fd359164b78bb44a97aebe24899826a69d9e8ab096d50161aff27a9c49cf449807c9a89d173f7d8707a4c80d424c389fc6ea543b8e9d0d9d8517d54a26ad1b"
}
}The class file: 13 triples
@prefix cred: <https://www.w3.org/2018/credentials#> .
@prefix ufp: <https://ufp.pt/vocabulary/diploma#> .
@prefix sec: <https://w3id.org/security#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
_:c0 a cred:VerifiableCredential, ufp:DiplomaUFP ;
cred:issuer <did:web:ufp.pt> ;
cred:validFrom "2027-07-20T00:00:00Z"^^xsd:dateTime ;
cred:credentialSubject <did:key:z6Mk...> ;
cred:credentialStatus <https://ufp.pt/status/2027#42> ;
sec:proof _:c1 .
<did:key:z6Mk...> ufp:degree "MSc in Computer Engineering" ;
ufp:grade 17 .
<https://ufp.pt/status/2027#42> a cred:StatusList2021 .
_:c1 a sec:DataIntegrityProof ;
sec:cryptosuite "eddsa-rdfc-2022" ;
sec:proofValue "z3Mv..." .
An in-memory chain (the Ethereum virtual machine, @ethereumjs/evm), in this one process: no test network, no wallet, no keys, no money. Nothing a visitor does is kept.