2023-10-24 08:19:55 +00:00
|
|
|
import asyncio
|
2024-01-03 15:53:06 +00:00
|
|
|
from typing import Callable, Any
|
2023-10-24 08:19:55 +00:00
|
|
|
import didkit
|
|
|
|
import json
|
|
|
|
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
2023-11-10 05:45:12 +00:00
|
|
|
import idhub_ssikit
|
2024-01-03 15:53:06 +00:00
|
|
|
from ast import literal_eval
|
|
|
|
import copy
|
2023-10-24 08:19:55 +00:00
|
|
|
|
2023-11-22 13:09:08 +00:00
|
|
|
|
2024-01-03 15:53:06 +00:00
|
|
|
def deep_merge_dict_inplace(d1: dict, d2: dict):
|
|
|
|
"""
|
|
|
|
Implements d1 |= d2, but recursively.
|
|
|
|
Merges d1 and d2, giving preference to keys in d2.
|
|
|
|
Keys in d1 but not in d2 are left as-is.
|
|
|
|
"""
|
|
|
|
for key, val in d2.items():
|
|
|
|
if isinstance(d1.get(key, None), dict) and isinstance(val, dict):
|
|
|
|
deep_merge_dict_inplace(d1[key], val)
|
|
|
|
continue
|
|
|
|
d1[key] = val
|
2023-11-22 13:09:08 +00:00
|
|
|
|
|
|
|
|
2024-01-03 15:53:06 +00:00
|
|
|
def deep_merge_dict(d1: dict, d2: dict) -> dict:
|
|
|
|
"""
|
|
|
|
Implements d1 | d2, but recursively.
|
|
|
|
Merges d1 and d2, giving preference to keys in d2.
|
|
|
|
Keys in d1 but not in d2 are left as-is.
|
|
|
|
"""
|
|
|
|
d1 = copy.deepcopy(d1)
|
|
|
|
deep_merge_dict_inplace(d1, d2)
|
|
|
|
return d1
|
|
|
|
|
2023-11-28 08:45:06 +00:00
|
|
|
|
2024-01-03 15:53:06 +00:00
|
|
|
def deep_filter_dict(f: Callable[[Any], bool], d: dict):
|
|
|
|
"""
|
|
|
|
Implements builtin filter(), but recursively.
|
|
|
|
Applies f to all k,v pairs in d. If some v is a dict, recurse into v instead of applying f(v) directly.
|
|
|
|
"""
|
|
|
|
for key, val in d.items():
|
|
|
|
if isinstance(val, dict):
|
|
|
|
yield key, dict(deep_filter_dict(f, val))
|
|
|
|
elif f(val):
|
|
|
|
yield key, val
|
2023-11-28 08:45:06 +00:00
|
|
|
|
2024-01-03 15:53:06 +00:00
|
|
|
|
2024-01-25 10:02:41 +00:00
|
|
|
def test_all_vcs(use_webdid=False):
|
2024-01-03 15:53:06 +00:00
|
|
|
vcs = [
|
|
|
|
'membership-card',
|
|
|
|
'financial-vulnerability',
|
|
|
|
'course-credential',
|
|
|
|
'federation-membership',
|
2024-01-25 10:02:41 +00:00
|
|
|
'e-operator-claim'
|
2024-01-03 15:53:06 +00:00
|
|
|
]
|
|
|
|
for vc in vcs:
|
|
|
|
print(f"trying {vc}... ", end="")
|
|
|
|
try:
|
2024-01-25 10:02:41 +00:00
|
|
|
if use_webdid:
|
|
|
|
signed_cred = did_web_issue_vc_test_newstyle(vc)
|
|
|
|
else:
|
|
|
|
signed_cred = issue_vc_test_newstyle(vc)
|
2024-01-03 15:53:06 +00:00
|
|
|
ok, err = idhub_ssikit.verify_credential(signed_cred)
|
|
|
|
if ok:
|
|
|
|
print("OK")
|
|
|
|
else:
|
|
|
|
print("FAILED!", err)
|
|
|
|
open(f'/tmp/{vc}', mode='w').write(signed_cred)
|
|
|
|
except Exception as e:
|
|
|
|
print("FAILED! With exception:")
|
|
|
|
print(e)
|
|
|
|
|
|
|
|
|
2024-02-01 20:35:31 +00:00
|
|
|
def did_web_issue_vc_and_check_revocation(vc_name, revoked=True):
|
|
|
|
jwk_issuer = '{"kty":"OKP","crv":"Ed25519","x":"piojLFIHQ4Z6heRuPI87nrfMJKdet1dJIPG15iGjmDE","d":"zpOBTDrp_iNQTY5nZlIxLA34Sl7FXWXNGehFktznxTM"}'
|
|
|
|
jwk_subject = '{"kty":"OKP","crv":"Ed25519","x":"BuKyt44QKYSX6kmAt771ai37lIFNwYlhugWXPiqcyYU","d":"qbvMhSCPKvQ-vSkqNr3q8gWY5zPUj7ry0t2YnmT7agc"}'
|
|
|
|
|
|
|
|
did_issuer = "did:web:idhub.pangea.org:did-registry:allRevoked" if revoked else "did:web:idhub.pangea.org:did-registry:noneRevoked"
|
|
|
|
did_subject = didkit.key_to_did("key", jwk_subject)
|
|
|
|
|
|
|
|
vc_template = json.load(open(f'../../schemas/vc_templates/{vc_name}.json'))
|
|
|
|
data_base = json.load(open(f'../../schemas/vc_examples/base--data.json'))
|
|
|
|
data_base["issuer"]["id"] = did_issuer
|
|
|
|
data_base["credentialSubject"]["id"] = did_subject
|
|
|
|
data_specific = json.load(open(f'../../schemas/vc_examples/{vc_name}--data.json'))
|
|
|
|
data = deep_merge_dict(data_base, data_specific)
|
|
|
|
vc_rendered_unsigned = deep_merge_dict(vc_template, data)
|
|
|
|
|
|
|
|
signed_credential = idhub_ssikit.render_and_sign_credential(
|
|
|
|
vc_rendered_unsigned,
|
|
|
|
jwk_issuer,
|
|
|
|
)
|
|
|
|
|
|
|
|
ok, reason = idhub_ssikit.verify_credential(signed_credential)
|
|
|
|
print(ok)
|
|
|
|
print(reason)
|
|
|
|
|
|
|
|
|
2024-01-25 10:02:41 +00:00
|
|
|
def did_web_issue_vc_test_newstyle(vc_name):
|
|
|
|
jwk_issuer = '{"kty":"OKP","crv":"Ed25519","x":"piojLFIHQ4Z6heRuPI87nrfMJKdet1dJIPG15iGjmDE","d":"zpOBTDrp_iNQTY5nZlIxLA34Sl7FXWXNGehFktznxTM"}'
|
|
|
|
jwk_subject = '{"kty":"OKP","crv":"Ed25519","x":"BuKyt44QKYSX6kmAt771ai37lIFNwYlhugWXPiqcyYU","d":"qbvMhSCPKvQ-vSkqNr3q8gWY5zPUj7ry0t2YnmT7agc"}'
|
|
|
|
|
|
|
|
did_issuer = "did:web:idhub.pangea.org"
|
|
|
|
did_subject = didkit.key_to_did("key", jwk_subject)
|
|
|
|
|
|
|
|
vc_template = json.load(open(f'../../schemas/vc_templates/{vc_name}.json'))
|
|
|
|
data_base = json.load(open(f'../../schemas/vc_examples/base--data.json'))
|
|
|
|
data_base["issuer"]["id"] = did_issuer
|
|
|
|
data_base["credentialSubject"]["id"] = did_subject
|
|
|
|
data_specific = json.load(open(f'../../schemas/vc_examples/{vc_name}--data.json'))
|
|
|
|
data = deep_merge_dict(data_base, data_specific)
|
|
|
|
vc_rendered_unsigned = deep_merge_dict(vc_template, data)
|
|
|
|
|
|
|
|
signed_credential = idhub_ssikit.render_and_sign_credential(
|
|
|
|
vc_rendered_unsigned,
|
|
|
|
jwk_issuer,
|
|
|
|
)
|
|
|
|
return signed_credential
|
|
|
|
|
|
|
|
|
2024-01-03 15:53:06 +00:00
|
|
|
def issue_vc_test_newstyle(vc_name):
|
2023-11-28 08:45:06 +00:00
|
|
|
jwk_issuer = didkit.generate_ed25519_key()
|
|
|
|
jwk_subject = didkit.generate_ed25519_key()
|
|
|
|
|
|
|
|
did_issuer = didkit.key_to_did("key", jwk_issuer)
|
|
|
|
did_subject = didkit.key_to_did("key", jwk_subject)
|
2023-11-22 13:09:08 +00:00
|
|
|
|
2024-01-25 10:02:41 +00:00
|
|
|
vc_template = json.load(open(f'../../schemas/vc_templates/{vc_name}.json'))
|
|
|
|
data_base = json.load(open(f'../../schemas/vc_examples/base--data.json'))
|
2024-01-03 15:53:06 +00:00
|
|
|
data_base["issuer"]["id"] = did_issuer
|
|
|
|
data_base["credentialSubject"]["id"] = did_subject
|
2024-01-25 10:02:41 +00:00
|
|
|
data_specific = json.load(open(f'../../schemas/vc_examples/{vc_name}--data.json'))
|
2024-01-03 15:53:06 +00:00
|
|
|
data = deep_merge_dict(data_base, data_specific)
|
|
|
|
vc_rendered_unsigned = deep_merge_dict(vc_template, data)
|
|
|
|
|
2023-11-28 08:45:06 +00:00
|
|
|
signed_credential = idhub_ssikit.render_and_sign_credential(
|
2024-01-03 15:53:06 +00:00
|
|
|
vc_rendered_unsigned,
|
2023-11-28 08:45:06 +00:00
|
|
|
jwk_issuer,
|
|
|
|
)
|
2024-01-03 15:53:06 +00:00
|
|
|
return signed_credential
|
|
|
|
|
|
|
|
|
|
|
|
def issue_vc_test_and_fail_verification(vc_name):
|
|
|
|
signed_credential = issue_vc_test_newstyle(vc_name)
|
2023-11-28 08:45:06 +00:00
|
|
|
verification_result = idhub_ssikit.verify_credential(signed_credential)
|
|
|
|
print(verification_result)
|
2024-01-03 15:53:06 +00:00
|
|
|
|
2023-11-28 08:45:06 +00:00
|
|
|
def replace(s, position, character):
|
|
|
|
return s[:position] + character + s[position+1:]
|
2024-01-03 15:53:06 +00:00
|
|
|
signed_credential = replace(signed_credential, (len(signed_credential)//4)*3, ".")
|
2023-11-28 08:45:06 +00:00
|
|
|
verification_result = idhub_ssikit.verify_credential(signed_credential)
|
|
|
|
print(verification_result)
|
|
|
|
|
2023-11-22 13:09:08 +00:00
|
|
|
|
2023-11-28 08:45:06 +00:00
|
|
|
def issue_and_sign_vp_test():
|
2023-11-27 06:26:02 +00:00
|
|
|
"""
|
|
|
|
In this example execution two Verifiable Credentials associated with a single Holder are issued and then
|
|
|
|
combined into a single Verifiable Presentation.
|
|
|
|
The Verifiable Credentials are of two different models. The use-case is meant to mimic
|
|
|
|
- Holder being a physical person,
|
|
|
|
- Issuer A being "Pare Manel" foundation,
|
|
|
|
- Issuer B being "EXO" foundation,
|
|
|
|
- Verifier (not pictured) being "Som Connexio", which wants verifiable data of the Holder from both Issuers.
|
|
|
|
"""
|
2023-11-22 13:09:08 +00:00
|
|
|
jwk_issuer = didkit.generate_ed25519_key()
|
|
|
|
jwk_issuer2 = didkit.generate_ed25519_key()
|
|
|
|
jwk_subject = didkit.generate_ed25519_key()
|
|
|
|
|
|
|
|
did_issuer = didkit.key_to_did("key", jwk_issuer)
|
|
|
|
did_issuer2 = didkit.key_to_did("key", jwk_issuer2)
|
|
|
|
did_subject = didkit.key_to_did("key", jwk_subject)
|
|
|
|
print(did_issuer)
|
|
|
|
print(did_issuer2)
|
|
|
|
print(did_subject)
|
|
|
|
|
2024-01-25 10:02:41 +00:00
|
|
|
# TODO: WE'RE NO LONGER USING JINJA2
|
2023-11-22 13:09:08 +00:00
|
|
|
env = Environment(
|
|
|
|
loader=FileSystemLoader("vc_templates"),
|
|
|
|
autoescape=select_autoescape()
|
|
|
|
)
|
|
|
|
unsigned_vc_template = env.get_template("member.json")
|
|
|
|
data = {
|
|
|
|
"vc_id": "http://example.org/credentials/3731",
|
|
|
|
"issuer_did": did_issuer,
|
|
|
|
"subject_did": did_subject,
|
|
|
|
"issuance_date": "2020-08-19T21:41:50Z",
|
|
|
|
"subject_is_member_of": "Pangea"
|
|
|
|
}
|
|
|
|
signed_credential = idhub_ssikit.render_and_sign_credential(
|
|
|
|
unsigned_vc_template,
|
|
|
|
jwk_issuer,
|
|
|
|
data
|
|
|
|
)
|
|
|
|
data2 = data
|
|
|
|
data2["issuer_did"] = did_issuer2
|
|
|
|
signed_credential2 = idhub_ssikit.render_and_sign_credential(
|
|
|
|
unsigned_vc_template,
|
|
|
|
jwk_issuer2,
|
|
|
|
data2
|
|
|
|
)
|
|
|
|
signed_presentation = idhub_ssikit.issue_verifiable_presentation([signed_credential, signed_credential2], jwk_subject, did_subject)
|
2023-11-28 08:45:06 +00:00
|
|
|
print("##############--- SIGNED PRESENTATION ---##############")
|
2023-11-22 13:09:08 +00:00
|
|
|
print(signed_presentation)
|
2023-11-28 08:45:06 +00:00
|
|
|
print("##############--- ------------------- ---##############")
|
|
|
|
|
|
|
|
res = idhub_ssikit.verify_presentation(signed_presentation)
|
|
|
|
print(res)
|
|
|
|
|
2024-01-25 10:02:41 +00:00
|
|
|
|
|
|
|
def scratch():
|
|
|
|
jwk_issuer = didkit.generate_ed25519_key()
|
|
|
|
did_issuer = didkit.key_to_did("key", jwk_issuer)
|