From 84a41385d065630b4d31d5577dc6b466535219ec Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 6 Jun 2024 10:36:06 +0200 Subject: [PATCH] verify revocation credential --- pyvckit/sign_vc.py | 2 +- pyvckit/templates.py | 4 +++- pyvckit/verify.py | 42 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/pyvckit/sign_vc.py b/pyvckit/sign_vc.py index 2420e88..e203ae3 100644 --- a/pyvckit/sign_vc.py +++ b/pyvckit/sign_vc.py @@ -32,7 +32,7 @@ def main(): signing_key = get_signing_key(key) credential = json.loads(credential_tmpl) - credential["issuer"] = did + credential["issuer"]["id"] = did credential["issuanceDate"] = now() cred = json.dumps(credential) diff --git a/pyvckit/templates.py b/pyvckit/templates.py index f4af929..3e85803 100644 --- a/pyvckit/templates.py +++ b/pyvckit/templates.py @@ -10,7 +10,9 @@ credential_tmpl = """{ "credentialSubject": { "id": "did:key:z6MkgGXSJoacuuNdwU1rGfPpFH72GACnzykKTxzCCTZs6Z2M" }, - "issuer": "", + "issuer": { + "id": "" + }, "issuanceDate": "" }""" diff --git a/pyvckit/verify.py b/pyvckit/verify.py index eee4c54..99232b7 100644 --- a/pyvckit/verify.py +++ b/pyvckit/verify.py @@ -1,12 +1,16 @@ import json +import zlib +import base64 import nacl.encoding import nacl.signing import multicodec import multiformats from nacl.signing import VerifyKey +from pyroaring import BitMap from pyvckit.sign import to_jws_payload +from pyvckit.did import resolve_did def get_signing_input(payload): @@ -48,6 +52,14 @@ def verify_vc(credential): if not message: return False + did_issuer = vc.get("issuer", {}) or vc.get("holder", {}) + if isinstance(did_issuer, dict): + did_issuer = did_issuer.get("id") + + did_issuer_method = vc["proof"]["verificationMethod"].split("#")[0] + if did_issuer != did_issuer_method: + return False + header_b64, signature = get_signing_input(message) header_jws, signature_jws = jws_split(jws) @@ -57,14 +69,42 @@ def verify_vc(credential): header_jws_json = json.loads( nacl.encoding.URLSafeBase64Encoder.decode(header_jws) ) + for k, v in header.items(): if header_jws_json.get(k) != v: return False verify_key = get_verify_key(vc) + try: data_verified = verify_key.verify(signature_jws+signature) except nacl.exceptions.BadSignatureError: return False - return data_verified == signature + + if data_verified != signature: + return False + + + if "credentialStatus" in vc: + # NOTE: THIS FIELD SHOULD BE SERIALIZED AS AN INTEGER, + # BUT IOTA DOCUMENTAITON SERIALIZES IT AS A STRING. + # DEFENSIVE CAST ADDED JUST IN CASE. + revocation_index = int(vc["credentialStatus"]["revocationBitmapIndex"]) + + if did_issuer[:7] == "did:web": # Only DID:WEB can revoke + issuer_did_document = resolve_did(did_issuer) + issuer_revocation_list = issuer_did_document["service"][0] + assert issuer_revocation_list["type"] == "RevocationBitmap2022" + revocation_bitmap = BitMap.deserialize( + zlib.decompress( + base64.b64decode( + issuer_revocation_list["serviceEndpoint"].rsplit(",")[1].encode('utf-8') + ) + ) + ) + if revocation_index in revocation_bitmap: + # Credential has been revoked by the issuer + return False + + return True