Skip to content

Commit ebe8b35

Browse files
ralengavner-m
andauthored
feat: add manual-revocation-check script (#177)
* feat: add manual-revocation-check script * Update manual-revocation-check/README.md Co-authored-by: Avner Matan <[email protected]> * Update manual-revocation-check/README.md Co-authored-by: Avner Matan <[email protected]> * Update manual-revocation-check/manual-revocation-check.js Co-authored-by: Avner Matan <[email protected]> * Update manual-revocation-check/manual-revocation-check.js Co-authored-by: Avner Matan <[email protected]> * Update manual-revocation-check/manual-revocation-check.js Co-authored-by: Avner Matan <[email protected]> * Update manual-revocation-check/manual-revocation-check.js Co-authored-by: Avner Matan <[email protected]> * Update manual-revocation-check/README.md Co-authored-by: Avner Matan <[email protected]> * Update manual-revocation-check/README.md Co-authored-by: Avner Matan <[email protected]> * Update manual-revocation-check/README.md Co-authored-by: Avner Matan <[email protected]> * Update manual-revocation-check/README.md * Apply suggestions from code review Co-authored-by: Avner Matan <[email protected]> * feat: add CWT example and clean up output * docs: Update manual-revocation-check/README.md --------- Co-authored-by: Avner Matan <[email protected]>
1 parent f09b997 commit ebe8b35

File tree

4 files changed

+299
-0
lines changed

4 files changed

+299
-0
lines changed

manual-revocation-check/README.md

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
[![MATTR](../docs/assets/mattr-logo-square.svg)](https://github.com/mattrglobal)
2+
3+
# Manual revocation check
4+
5+
## Overview
6+
7+
Credential revocation is a critical feature that allows issuers to invalidate previously issued credentials, ensuring the integrity and security of the system.
8+
9+
MATTR provides capabilities to issue revokable credentials, to manage the revocation status of credentials, and to check the revocation status of a credential. Check out our [Credential management](https://learn.mattr.global/docs/capabilities/management) capabilities and our [Revocation tutorial](https://learn.mattr.global/tutorials/management/revocation) to learn more.
10+
11+
While we highly recommend to leverage your MATTR tenant for credential management, credentials also enable unauthenticated relying parties to check their revocation status to enable interoperability.
12+
13+
This sample script demonstrates how to retrieve the revocation list data, how to decode the data, and how to check the revocation status.
14+
15+
## How to run the script
16+
17+
### JSON credentials
18+
19+
JSON credentials contain a `credentialStatus` field that holds the reference to a revocation list.
20+
21+
```json
22+
"credentialStatus": {
23+
"id": "https://labs-mattr-university.vii.au01.mattr.global/core/v2/credentials/web-semantic/revocation-lists/57698edc-0826-4628-a2eb-888cb840d4b5#0",
24+
"type": "RevocationList2020Status",
25+
"revocationListIndex": "0",
26+
"revocationListCredential": "https://labs-mattr-university.vii.au01.mattr.global/core/v2/credentials/web-semantic/revocation-lists/57698edc-0826-4628-a2eb-888cb840d4b5"
27+
},
28+
```
29+
30+
You can check the revocation status of this credential by running the script with node, providing the `revocationListCredential` and `revocationListIndex` as arguments.
31+
32+
33+
```bash
34+
node manual-revocation-check.js https://learn.vii.au01.mattr.global/core/v2/credentials/web-semantic/revocation-lists/0ec79c8e-9859-46c0-a277-6e48f468b16e 1
35+
```
36+
37+
### CWT and Semantic CWT credentials
38+
39+
CWT and Semantic CWT credentials are usually shared and verified in their encoded forms. For convenience, you can provide the encoded credential or the revocation list URL and index to the script.
40+
41+
If you provide the encoded form, the script will decode the credential and retrieve the list URL and index.
42+
43+
```bash
44+
node manual-revocation-check.js CSC:/1/2KCE3IQEJB5DCMSLNFYUYUQBE2QFSAIJU4AXQJLENFSDU53FMI5G2YLUORZC25LONF3GK4TTNF2HSLTNMF2HI4TMMFRHGLTDN4DBUZZBHGDAIGTLJHJAAZDOMFWWK22FNVWWCICUMFZW2YLONBQWY5LNNZUU6ZTVJVAVIVCSEBGGCYTTEBKW42LWMVZHG2LUPE5AAAIAACRAEAADPCCWQ5DUOBZTULZPNRQWE4ZNNVQXI5DSFV2W42LWMVZHG2LUPEXHM2LJFZQXKMBRFZWWC5DUOIXGO3DPMJQWYL3DN5ZGKL3WGIXWG4TFMRSW45DJMFWHGL3DN5WXAYLDOQXXEZLWN5RWC5DJN5XC23DJON2HGLZXGM3WGMBWHBTC2NDFMM2C2NBTMY4C2YTDHA4S2NRZMUZDMMJUMEZWKZDGA7MEAUBXE2MBKC2XJAKLSCN2AVW6OJMQLBAJQWGFUIV55FG2U7NK6B4DRSTNOKHVLRBEON47KCHLKZ42FAOMHCA24SHRWIOAAVCKTNQQAE7J2INROWI4DS3ZK3JEXVQGGZ4UGAJFFI
45+
```
46+
47+
If you decode the credential yourself, you can pass the revocation list URL and index.
48+
49+
```bash
50+
node manual-revocation-check.js https://learn.vii.au01.mattr.global/core/v2/credentials/compact/revocation-lists/0ec79c8e-9859-46c0-a277-6e48f468b16e 1
51+
```
52+
53+
## Results
54+
55+
The results are displayed in your terminal, showing the inferred credential type and the revocation status.
56+
57+
```
58+
[Credential Type] CWT
59+
[Revocation status] Revoked
60+
```
61+
62+
---
63+
64+
<p align="center"><a href="https://mattr.global" target="_blank"><img height="40px" src ="../docs/assets/mattr-logo-tm.svg"></a></p><p align="center">Copyright © MATTR Limited. <a href="../LICENSE">Some rights reserved.</a><br/>“MATTR” is a trademark of MATTR Limited, registered in New Zealand and other countries.</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import base32Decode from "base32-decode";
2+
import { decode as cborDecode } from "cbor-x";
3+
import pako from "pako";
4+
import * as base64 from "@stablelib/base64";
5+
6+
const urlOrCompact = process.argv[2];
7+
const index = process.argv[3];
8+
9+
if (!urlOrCompact) {
10+
console.error("No URL or compact credential provided.");
11+
process.exit();
12+
}
13+
14+
if (
15+
!urlOrCompact.startsWith("http") &&
16+
!urlOrCompact.startsWith("CSC") &&
17+
!urlOrCompact.startsWith("CSS")
18+
) {
19+
console.error("Invalid argument provided. Neither URL, nor encoded compact credential.");
20+
process.exit();
21+
}
22+
23+
if (urlOrCompact.startsWith("http") && !index) {
24+
console.error("No index provided for revocation list URL.");
25+
process.exit();
26+
}
27+
28+
function toBitArray(input) {
29+
const bitArray = [];
30+
31+
for (let byte of input) {
32+
let bits = byte.toString(2);
33+
for (let bit of bits) {
34+
bitArray.push(Number(bit));
35+
}
36+
}
37+
38+
return bitArray;
39+
}
40+
41+
// CWT credentials are base32 encoded CBOR web tokens
42+
// Revocable CWT credentials contain a reference to the
43+
// public revocation list and their index in that list.
44+
function retrieveRevocationUrlFromCompact(compact) {
45+
const decoded = base32Decode(compact.slice(7), "RFC4648");
46+
const cwt = cborDecode(Buffer.from(decoded));
47+
const payload = cborDecode(cwt.value[2]);
48+
49+
const status = payload["-65537"];
50+
const index = status["2"];
51+
const listUrl = status["3"];
52+
53+
return { listUrl, index };
54+
}
55+
56+
// The public revocation list endpoint for CWT credentials
57+
// returns a CBOR web token that includes the gzip compressed
58+
// revocation list as a bitstring.
59+
async function isCompactRevoked(url, index) {
60+
let response;
61+
try {
62+
response = await fetch(url);
63+
if (!response.ok) {
64+
console.error("Can not fetch revocation list from provided URL.")
65+
process.exit()
66+
}
67+
} catch (_) {
68+
console.error("Fetching revocation list from URL failed.")
69+
process.exit()
70+
}
71+
const bytes = await response.arrayBuffer();
72+
73+
const cwt = cborDecode(Buffer.from(bytes));
74+
const payload = cborDecode(cwt.value[2]);
75+
76+
const compressedList = payload["-65538"];
77+
const list = pako.ungzip(compressedList);
78+
const revocationList = toBitArray(list);
79+
80+
return revocationList[index] === 1;
81+
}
82+
83+
// The public revocation list endpoint for JSON credentials
84+
// returns a revocation credential that includes the encoded revocation
85+
// list. The list is a base64url encoded and gzip compressed bitstring.
86+
async function isWebSemanticRevoked(url, index) {
87+
let response;
88+
try {
89+
response = await fetch(url);
90+
if (!response.ok) {
91+
console.error("Can not fetch revocation list from provided URL.")
92+
process.exit()
93+
}
94+
} catch (_) {
95+
console.error("Fetching revocation list from URL failed.")
96+
process.exit()
97+
}
98+
99+
const revocationCredential = await response.json();
100+
const { encodedList } = revocationCredential.credentialSubject;
101+
102+
const list = pako.ungzip(base64.decodeURLSafe(encodedList));
103+
const listBitArray = toBitArray(list);
104+
105+
return listBitArray[index] === 1;
106+
}
107+
108+
let isRevoked;
109+
if (urlOrCompact.startsWith("CSC") || urlOrCompact.startsWith("CSS")) {
110+
console.log(`\n[Credential Type] ${urlOrCompact.startsWith("CSS") ? "Semantic " : ""}CWT (encoded)`);
111+
const { listUrl, index } = retrieveRevocationUrlFromCompact(urlOrCompact);
112+
isRevoked = await isCompactRevoked(listUrl, index);
113+
} else if (urlOrCompact.includes("compact")) {
114+
console.log(`\n[Credential Type] ${urlOrCompact.includes("semantic") ? "Semantic " : ""}CWT`);
115+
isRevoked = await isCompactRevoked(urlOrCompact, index);
116+
} else if (urlOrCompact.includes("web-semantic")) {
117+
console.log("\n[Credential Type] JSON");
118+
isRevoked = await isWebSemanticRevoked(urlOrCompact, index);
119+
} else {
120+
console.error("Can not identify credential type.");
121+
process.exit()
122+
}
123+
124+
console.log(`[Revocation status] ${isRevoked ? "Revoked" : "Not revoked"}`);

manual-revocation-check/package.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "manual-revocation-check",
3+
"version": "1.0.0",
4+
"description": "A sample script how to check the revocation status of a compact or web semantic credential manually via the public revocation list endpoint.",
5+
"main": "manual-revocation-check.js",
6+
"dependencies": {
7+
"@stablelib/base64": "^2.0.0",
8+
"base32-decode": "^1.0.0",
9+
"cbor-x": "^1.6.0",
10+
"pako": "^2.1.0"
11+
},
12+
"scripts": {
13+
"start": "node manual-revocation-check.js",
14+
},
15+
"repository": {
16+
"type": "git",
17+
"url": "git+https://github.com/mattrglobal/sample-apps.git"
18+
},
19+
"keywords": [
20+
"sampleapp",
21+
"mattr"
22+
],
23+
"author": "[email protected]",
24+
"license": "Apache-2.0",
25+
"bugs": {
26+
"url": "https://github.com/mattrglobal/sample-apps/issues"
27+
},
28+
"homepage": "https://github.com/mattrglobal/sample-apps#readme",
29+
"type": "module"
30+
}

manual-revocation-check/yarn.lock

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+
# yarn lockfile v1
3+
4+
5+
"@cbor-extract/[email protected]":
6+
version "2.2.0"
7+
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.0.tgz#8d65cb861a99622e1b4a268e2d522d2ec6137338"
8+
integrity sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==
9+
10+
"@cbor-extract/[email protected]":
11+
version "2.2.0"
12+
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.0.tgz#9fbec199c888c5ec485a1839f4fad0485ab6c40a"
13+
integrity sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==
14+
15+
"@cbor-extract/[email protected]":
16+
version "2.2.0"
17+
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.0.tgz#bf77e0db4a1d2200a5aa072e02210d5043e953ae"
18+
integrity sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==
19+
20+
"@cbor-extract/[email protected]":
21+
version "2.2.0"
22+
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.0.tgz#491335037eb8533ed8e21b139c59f6df04e39709"
23+
integrity sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==
24+
25+
"@cbor-extract/[email protected]":
26+
version "2.2.0"
27+
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.0.tgz#672574485ccd24759bf8fb8eab9dbca517d35b97"
28+
integrity sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==
29+
30+
"@cbor-extract/[email protected]":
31+
version "2.2.0"
32+
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz#4b3f07af047f984c082de34b116e765cb9af975f"
33+
integrity sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==
34+
35+
"@stablelib/base64@^2.0.0":
36+
version "2.0.0"
37+
resolved "https://registry.yarnpkg.com/@stablelib/base64/-/base64-2.0.0.tgz#f13a98549cd5ca0750cd177bbd08b599d24e5f8e"
38+
integrity sha512-ffSfySa1ZpZYzM5FQ2xILQ2jifQ+GlgbDJzRTCtaB0sqta88KYghB/tlSV2VS2iHRCvMdUvJlLOW1rmSkziWnw==
39+
40+
base32-decode@^1.0.0:
41+
version "1.0.0"
42+
resolved "https://registry.yarnpkg.com/base32-decode/-/base32-decode-1.0.0.tgz#2a821d6a664890c872f20aa9aca95a4b4b80e2a7"
43+
integrity sha512-KNWUX/R7wKenwE/G/qFMzGScOgVntOmbE27vvc6GrniDGYb6a5+qWcuoXl8WIOQL7q0TpK7nZDm1Y04Yi3Yn5g==
44+
45+
cbor-extract@^2.2.0:
46+
version "2.2.0"
47+
resolved "https://registry.yarnpkg.com/cbor-extract/-/cbor-extract-2.2.0.tgz#cee78e630cbeae3918d1e2e58e0cebaf3a3be840"
48+
integrity sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==
49+
dependencies:
50+
node-gyp-build-optional-packages "5.1.1"
51+
optionalDependencies:
52+
"@cbor-extract/cbor-extract-darwin-arm64" "2.2.0"
53+
"@cbor-extract/cbor-extract-darwin-x64" "2.2.0"
54+
"@cbor-extract/cbor-extract-linux-arm" "2.2.0"
55+
"@cbor-extract/cbor-extract-linux-arm64" "2.2.0"
56+
"@cbor-extract/cbor-extract-linux-x64" "2.2.0"
57+
"@cbor-extract/cbor-extract-win32-x64" "2.2.0"
58+
59+
cbor-x@^1.6.0:
60+
version "1.6.0"
61+
resolved "https://registry.yarnpkg.com/cbor-x/-/cbor-x-1.6.0.tgz#89c35d2d805efc30e09a28349425cc05d57aacd7"
62+
integrity sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg==
63+
optionalDependencies:
64+
cbor-extract "^2.2.0"
65+
66+
detect-libc@^2.0.1:
67+
version "2.0.3"
68+
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
69+
integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
70+
71+
72+
version "5.1.1"
73+
resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz#52b143b9dd77b7669073cbfe39e3f4118bfc603c"
74+
integrity sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==
75+
dependencies:
76+
detect-libc "^2.0.1"
77+
78+
pako@^2.1.0:
79+
version "2.1.0"
80+
resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
81+
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==

0 commit comments

Comments
 (0)