Skip to content

Commit

Permalink
feat: add manual-revocation-check script (#177)
Browse files Browse the repository at this point in the history
* feat: add manual-revocation-check script

* Update manual-revocation-check/README.md

Co-authored-by: Avner Matan <137777701+avner-m@users.noreply.github.com>

* Update manual-revocation-check/README.md

Co-authored-by: Avner Matan <137777701+avner-m@users.noreply.github.com>

* Update manual-revocation-check/manual-revocation-check.js

Co-authored-by: Avner Matan <137777701+avner-m@users.noreply.github.com>

* Update manual-revocation-check/manual-revocation-check.js

Co-authored-by: Avner Matan <137777701+avner-m@users.noreply.github.com>

* Update manual-revocation-check/manual-revocation-check.js

Co-authored-by: Avner Matan <137777701+avner-m@users.noreply.github.com>

* Update manual-revocation-check/manual-revocation-check.js

Co-authored-by: Avner Matan <137777701+avner-m@users.noreply.github.com>

* Update manual-revocation-check/README.md

Co-authored-by: Avner Matan <137777701+avner-m@users.noreply.github.com>

* Update manual-revocation-check/README.md

Co-authored-by: Avner Matan <137777701+avner-m@users.noreply.github.com>

* Update manual-revocation-check/README.md

Co-authored-by: Avner Matan <137777701+avner-m@users.noreply.github.com>

* Update manual-revocation-check/README.md

* Apply suggestions from code review

Co-authored-by: Avner Matan <137777701+avner-m@users.noreply.github.com>

* feat: add CWT example and clean up output

* docs: Update manual-revocation-check/README.md

---------

Co-authored-by: Avner Matan <137777701+avner-m@users.noreply.github.com>
  • Loading branch information
raleng and avner-m authored Oct 29, 2024
1 parent f09b997 commit ebe8b35
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 0 deletions.
64 changes: 64 additions & 0 deletions manual-revocation-check/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
[![MATTR](../docs/assets/mattr-logo-square.svg)](https://github.com/mattrglobal)

# Manual revocation check

## Overview

Credential revocation is a critical feature that allows issuers to invalidate previously issued credentials, ensuring the integrity and security of the system.

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.

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.

This sample script demonstrates how to retrieve the revocation list data, how to decode the data, and how to check the revocation status.

## How to run the script

### JSON credentials

JSON credentials contain a `credentialStatus` field that holds the reference to a revocation list.

```json
"credentialStatus": {
"id": "https://labs-mattr-university.vii.au01.mattr.global/core/v2/credentials/web-semantic/revocation-lists/57698edc-0826-4628-a2eb-888cb840d4b5#0",
"type": "RevocationList2020Status",
"revocationListIndex": "0",
"revocationListCredential": "https://labs-mattr-university.vii.au01.mattr.global/core/v2/credentials/web-semantic/revocation-lists/57698edc-0826-4628-a2eb-888cb840d4b5"
},
```

You can check the revocation status of this credential by running the script with node, providing the `revocationListCredential` and `revocationListIndex` as arguments.


```bash
node manual-revocation-check.js https://learn.vii.au01.mattr.global/core/v2/credentials/web-semantic/revocation-lists/0ec79c8e-9859-46c0-a277-6e48f468b16e 1
```

### CWT and Semantic CWT credentials

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.

If you provide the encoded form, the script will decode the credential and retrieve the list URL and index.

```bash
node manual-revocation-check.js CSC:/1/2KCE3IQEJB5DCMSLNFYUYUQBE2QFSAIJU4AXQJLENFSDU53FMI5G2YLUORZC25LONF3GK4TTNF2HSLTNMF2HI4TMMFRHGLTDN4DBUZZBHGDAIGTLJHJAAZDOMFWWK22FNVWWCICUMFZW2YLONBQWY5LNNZUU6ZTVJVAVIVCSEBGGCYTTEBKW42LWMVZHG2LUPE5AAAIAACRAEAADPCCWQ5DUOBZTULZPNRQWE4ZNNVQXI5DSFV2W42LWMVZHG2LUPEXHM2LJFZQXKMBRFZWWC5DUOIXGO3DPMJQWYL3DN5ZGKL3WGIXWG4TFMRSW45DJMFWHGL3DN5WXAYLDOQXXEZLWN5RWC5DJN5XC23DJON2HGLZXGM3WGMBWHBTC2NDFMM2C2NBTMY4C2YTDHA4S2NRZMUZDMMJUMEZWKZDGA7MEAUBXE2MBKC2XJAKLSCN2AVW6OJMQLBAJQWGFUIV55FG2U7NK6B4DRSTNOKHVLRBEON47KCHLKZ42FAOMHCA24SHRWIOAAVCKTNQQAE7J2INROWI4DS3ZK3JEXVQGGZ4UGAJFFI
```

If you decode the credential yourself, you can pass the revocation list URL and index.

```bash
node manual-revocation-check.js https://learn.vii.au01.mattr.global/core/v2/credentials/compact/revocation-lists/0ec79c8e-9859-46c0-a277-6e48f468b16e 1
```

## Results

The results are displayed in your terminal, showing the inferred credential type and the revocation status.

```
[Credential Type] CWT
[Revocation status] Revoked
```

---

<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>
124 changes: 124 additions & 0 deletions manual-revocation-check/manual-revocation-check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import base32Decode from "base32-decode";
import { decode as cborDecode } from "cbor-x";
import pako from "pako";
import * as base64 from "@stablelib/base64";

const urlOrCompact = process.argv[2];
const index = process.argv[3];

if (!urlOrCompact) {
console.error("No URL or compact credential provided.");
process.exit();
}

if (
!urlOrCompact.startsWith("http") &&
!urlOrCompact.startsWith("CSC") &&
!urlOrCompact.startsWith("CSS")
) {
console.error("Invalid argument provided. Neither URL, nor encoded compact credential.");
process.exit();
}

if (urlOrCompact.startsWith("http") && !index) {
console.error("No index provided for revocation list URL.");
process.exit();
}

function toBitArray(input) {
const bitArray = [];

for (let byte of input) {
let bits = byte.toString(2);
for (let bit of bits) {
bitArray.push(Number(bit));
}
}

return bitArray;
}

// CWT credentials are base32 encoded CBOR web tokens
// Revocable CWT credentials contain a reference to the
// public revocation list and their index in that list.
function retrieveRevocationUrlFromCompact(compact) {
const decoded = base32Decode(compact.slice(7), "RFC4648");
const cwt = cborDecode(Buffer.from(decoded));
const payload = cborDecode(cwt.value[2]);

const status = payload["-65537"];
const index = status["2"];
const listUrl = status["3"];

return { listUrl, index };
}

// The public revocation list endpoint for CWT credentials
// returns a CBOR web token that includes the gzip compressed
// revocation list as a bitstring.
async function isCompactRevoked(url, index) {
let response;
try {
response = await fetch(url);
if (!response.ok) {
console.error("Can not fetch revocation list from provided URL.")
process.exit()
}
} catch (_) {
console.error("Fetching revocation list from URL failed.")
process.exit()
}
const bytes = await response.arrayBuffer();

const cwt = cborDecode(Buffer.from(bytes));
const payload = cborDecode(cwt.value[2]);

const compressedList = payload["-65538"];
const list = pako.ungzip(compressedList);
const revocationList = toBitArray(list);

return revocationList[index] === 1;
}

// The public revocation list endpoint for JSON credentials
// returns a revocation credential that includes the encoded revocation
// list. The list is a base64url encoded and gzip compressed bitstring.
async function isWebSemanticRevoked(url, index) {
let response;
try {
response = await fetch(url);
if (!response.ok) {
console.error("Can not fetch revocation list from provided URL.")
process.exit()
}
} catch (_) {
console.error("Fetching revocation list from URL failed.")
process.exit()
}

const revocationCredential = await response.json();
const { encodedList } = revocationCredential.credentialSubject;

const list = pako.ungzip(base64.decodeURLSafe(encodedList));
const listBitArray = toBitArray(list);

return listBitArray[index] === 1;
}

let isRevoked;
if (urlOrCompact.startsWith("CSC") || urlOrCompact.startsWith("CSS")) {
console.log(`\n[Credential Type] ${urlOrCompact.startsWith("CSS") ? "Semantic " : ""}CWT (encoded)`);
const { listUrl, index } = retrieveRevocationUrlFromCompact(urlOrCompact);
isRevoked = await isCompactRevoked(listUrl, index);
} else if (urlOrCompact.includes("compact")) {
console.log(`\n[Credential Type] ${urlOrCompact.includes("semantic") ? "Semantic " : ""}CWT`);
isRevoked = await isCompactRevoked(urlOrCompact, index);
} else if (urlOrCompact.includes("web-semantic")) {
console.log("\n[Credential Type] JSON");
isRevoked = await isWebSemanticRevoked(urlOrCompact, index);
} else {
console.error("Can not identify credential type.");
process.exit()
}

console.log(`[Revocation status] ${isRevoked ? "Revoked" : "Not revoked"}`);
30 changes: 30 additions & 0 deletions manual-revocation-check/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "manual-revocation-check",
"version": "1.0.0",
"description": "A sample script how to check the revocation status of a compact or web semantic credential manually via the public revocation list endpoint.",
"main": "manual-revocation-check.js",
"dependencies": {
"@stablelib/base64": "^2.0.0",
"base32-decode": "^1.0.0",
"cbor-x": "^1.6.0",
"pako": "^2.1.0"
},
"scripts": {
"start": "node manual-revocation-check.js",
},
"repository": {
"type": "git",
"url": "git+https://github.com/mattrglobal/sample-apps.git"
},
"keywords": [
"sampleapp",
"mattr"
],
"author": "ralf.engbers@mattr.global",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/mattrglobal/sample-apps/issues"
},
"homepage": "https://github.com/mattrglobal/sample-apps#readme",
"type": "module"
}
81 changes: 81 additions & 0 deletions manual-revocation-check/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@cbor-extract/cbor-extract-darwin-arm64@2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.0.tgz#8d65cb861a99622e1b4a268e2d522d2ec6137338"
integrity sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==

"@cbor-extract/cbor-extract-darwin-x64@2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.0.tgz#9fbec199c888c5ec485a1839f4fad0485ab6c40a"
integrity sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==

"@cbor-extract/cbor-extract-linux-arm64@2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.0.tgz#bf77e0db4a1d2200a5aa072e02210d5043e953ae"
integrity sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==

"@cbor-extract/cbor-extract-linux-arm@2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.0.tgz#491335037eb8533ed8e21b139c59f6df04e39709"
integrity sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==

"@cbor-extract/cbor-extract-linux-x64@2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.0.tgz#672574485ccd24759bf8fb8eab9dbca517d35b97"
integrity sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==

"@cbor-extract/cbor-extract-win32-x64@2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz#4b3f07af047f984c082de34b116e765cb9af975f"
integrity sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==

"@stablelib/base64@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@stablelib/base64/-/base64-2.0.0.tgz#f13a98549cd5ca0750cd177bbd08b599d24e5f8e"
integrity sha512-ffSfySa1ZpZYzM5FQ2xILQ2jifQ+GlgbDJzRTCtaB0sqta88KYghB/tlSV2VS2iHRCvMdUvJlLOW1rmSkziWnw==

base32-decode@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/base32-decode/-/base32-decode-1.0.0.tgz#2a821d6a664890c872f20aa9aca95a4b4b80e2a7"
integrity sha512-KNWUX/R7wKenwE/G/qFMzGScOgVntOmbE27vvc6GrniDGYb6a5+qWcuoXl8WIOQL7q0TpK7nZDm1Y04Yi3Yn5g==

cbor-extract@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/cbor-extract/-/cbor-extract-2.2.0.tgz#cee78e630cbeae3918d1e2e58e0cebaf3a3be840"
integrity sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==
dependencies:
node-gyp-build-optional-packages "5.1.1"
optionalDependencies:
"@cbor-extract/cbor-extract-darwin-arm64" "2.2.0"
"@cbor-extract/cbor-extract-darwin-x64" "2.2.0"
"@cbor-extract/cbor-extract-linux-arm" "2.2.0"
"@cbor-extract/cbor-extract-linux-arm64" "2.2.0"
"@cbor-extract/cbor-extract-linux-x64" "2.2.0"
"@cbor-extract/cbor-extract-win32-x64" "2.2.0"

cbor-x@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/cbor-x/-/cbor-x-1.6.0.tgz#89c35d2d805efc30e09a28349425cc05d57aacd7"
integrity sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg==
optionalDependencies:
cbor-extract "^2.2.0"

detect-libc@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==

node-gyp-build-optional-packages@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz#52b143b9dd77b7669073cbfe39e3f4118bfc603c"
integrity sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==
dependencies:
detect-libc "^2.0.1"

pako@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==

0 comments on commit ebe8b35

Please sign in to comment.