-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add manual-revocation-check script (#177)
* 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
Showing
4 changed files
with
299 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
[](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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"}`); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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== |