Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapt JSON serialization #19

Merged
merged 3 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions examples/json_serialization_general/specification.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
user_claims:
sub: john_doe_42
!sd given_name: John
!sd family_name: Doe
!sd birthdate: "1940-01-01"

holder_disclosed_claims:
sub: null
given_name: true
family_name: true
birthdate: false

key_binding: True

serialization_format: json

settings_override:
key_settings:
key_size: 256
kty: EC
issuer_keys:
- kty: EC
d: Ur2bNKuBPOrAaxsRnbSH6hIhmNTxSGXshDSUD1a1y7g
crv: P-256
x: b28d4MwZMjw8-00CG4xfnn9SLMVMM19SlqZpVb_uNtQ
y: Xv5zWwuoaTgdS6hV43yI6gBwTnjukmFQQnJ_kCxzqk8
kid: issuer-key-1
- kty: EC
crv: P-256
d: WsGosxrp0XK7VEviPL9xBm3fBb7Xys2vLhPGhESNoXY
x: bN-hp3IN0GZB3OlaQnHDPhY4nZsZbQyo4wY-y1NWCvA
y: vaSsH5jt9zt3aQvTvrSaFYLyjPG9Ug-2vntoNXlCbVU
kid: issuer-key-2

holder_key:
kty: EC
d: 5K5SCos8zf9zRemGGUl6yfok-_NiiryNZsvANWMhF-I
crv: P-256
x: TCAER19Zvu3OHF4j4W4vfSVoHIP1ILilDls7vCeGemc
y: ZxjiWWbZMQGHVWKVQ4hbSIirsVfuecCE6t4jT9F2HZQ
18 changes: 9 additions & 9 deletions examples/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ key_settings:

kty: EC

issuer_key:
kty: EC
d: Ur2bNKuBPOrAaxsRnbSH6hIhmNTxSGXshDSUD1a1y7g
crv: P-256
x: b28d4MwZMjw8-00CG4xfnn9SLMVMM19SlqZpVb_uNtQ
y: Xv5zWwuoaTgdS6hV43yI6gBwTnjukmFQQnJ_kCxzqk8
issuer_keys:
danielfett marked this conversation as resolved.
Show resolved Hide resolved
- kty: EC
d: Ur2bNKuBPOrAaxsRnbSH6hIhmNTxSGXshDSUD1a1y7g
crv: P-256
x: b28d4MwZMjw8-00CG4xfnn9SLMVMM19SlqZpVb_uNtQ
y: Xv5zWwuoaTgdS6hV43yI6gBwTnjukmFQQnJ_kCxzqk8

holder_key:
kty: EC
Expand All @@ -23,9 +23,9 @@ key_settings:

key_binding_nonce: "1234567890"

expiry_seconds: 86400000 # 1000 days
expiry_seconds: 86400000 # 1000 days

random_seed: 0

iat: 1683000000 # Tue May 02 2023 04:00:00 GMT+0000
exp: 1883000000 # Sat Sep 01 2029 23:33:20 GMT+0000
iat: 1683000000 # Tue May 02 2023 04:00:00 GMT+0000
exp: 1883000000 # Sat Sep 01 2029 23:33:20 GMT+0000
104 changes: 63 additions & 41 deletions src/sd_jwt/bin/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@
textwrap_json,
textwrap_text,
multiline_code,
markdown_disclosures,
EXAMPLE_SHORT_WIDTH,
)
from sd_jwt.utils.yaml_specification import load_yaml_specification
from sd_jwt.utils.yaml_specification import (
load_yaml_specification,
remove_sdobj_wrappers,
)

logger = logging.getLogger("sd_jwt")

Expand All @@ -35,6 +39,7 @@ def generate_nonce():

DEFAULT_EXP_MINS = 15


def run():
parser = argparse.ArgumentParser(
description=f"{__file__} demo.",
Expand Down Expand Up @@ -119,6 +124,12 @@ def run():

### Load settings
settings = load_yaml_settings(_args.settings_path)
### Load example file
example_identifer = _args.example.stem
example = load_yaml_specification(_args.example)
### "settings_override" key in example can override settings
settings.update(example.get("settings_override", {}))
print(f"Settings: {settings}")

# If "no randomness" is requested, we hash the file name of the example
# file to use it as the random seed. This ensures that the same example
Expand All @@ -134,18 +145,15 @@ def run():
seed = None

demo_keys = get_jwk(settings["key_settings"], _args.no_randomness, seed)

### Load example file

example_identifer = _args.example.stem
example = load_yaml_specification(_args.example)
print(f"Using keys: {demo_keys}")
use_decoys = example.get("add_decoy_claims", False)
serialization_format = example.get("serialization_format", "compact")

### Add default claims if necessary
iat = _args.iat or int(datetime.datetime.utcnow().timestamp())
exp = _args.exp or iat + (DEFAULT_EXP_MINS * 60)
claims = {
"iss": settings.ISSUER,
"iss": settings["identifiers"]["issuer"],
"iat": iat,
"exp": exp,
}
Expand All @@ -156,82 +164,92 @@ def run():
SDJWTIssuer.unsafe_randomness = _args.no_randomness
sdjwt_at_issuer = SDJWTIssuer(
claims,
demo_keys["issuer_key"],
demo_keys["issuer_keys"],
demo_keys["holder_key"] if example.get("key_binding", False) else None,
add_decoy_claims=use_decoys,
serialization_format=serialization_format,
)

### Produce SD-JWT-R for selected example

# Note: The only input from the issuer is the combined SD-JWT and SVC!

sdjwt_at_holder = SDJWTHolder(sdjwt_at_issuer.sd_jwt_issuance)
sdjwt_at_holder = SDJWTHolder(
sdjwt_at_issuer.sd_jwt_issuance,
serialization_format=serialization_format,
)
sdjwt_at_holder.create_presentation(
example["holder_disclosed_claims"],
_args.nonce if example.get("key_binding", False) else None,
settings.VERIFIER if example.get("key_binding", False) else None,
(
settings["identifiers"]["issuer"]
if example.get("key_binding", False)
else None
),
demo_keys["holder_key"] if example.get("key_binding", False) else None,
)

### Verify the SD-JWT using the SD-JWT-R


# Define a function to check the issuer and retrieve the
# matching public key
def cb_get_issuer_key(issuer):
def cb_get_issuer_key(issuer, header_parameters):
# Do not use in production - this allows to use any issuer name for demo purposes
if issuer == claims["iss"]:
return demo_keys["issuer_public_key"]
return demo_keys["issuer_public_keys"]
else:
raise Exception(f"Unknown issuer: {issuer}")


# Note: The only input from the holder is the combined presentation!
sdjwt_at_verifier = SDJWTVerifier(
sdjwt_at_holder.sd_jwt_presentation,
cb_get_issuer_key,
settings.VERIFIER if example.get("key_binding", False) else None,
(
settings["identifiers"]["issuer"]
if example.get("key_binding", False)
else None
),
_args.nonce if example.get("key_binding", False) else None,
serialization_format=serialization_format,
)
verified = sdjwt_at_verifier.get_verified_payload()

### Done - now output everything to CLI (unless --replace-examples-in was used)

iid_payload = ""
danielfett marked this conversation as resolved.
Show resolved Hide resolved
for hash in sdjwt_at_holder._hash_to_decoded_disclosure:
salt, claim_name, claim_value = sdjwt_at_holder._hash_to_decoded_disclosure[hash]
b64 = sdjwt_at_holder._hash_to_disclosure[hash]
encoded_json = sdjwt_at_holder._base64url_decode(b64).decode("utf-8")

iid_payload += (
f"__Claim `{claim_name}`:__\n\n"
f" * SHA-256 Hash: `{hash}`\n"
f" * Disclosure:\\\n"
f"{multiline_code(textwrap_text(b64, EXAMPLE_SHORT_WIDTH))}\n"
f" * Contents:\n"
f"{multiline_code(textwrap_text(encoded_json, EXAMPLE_SHORT_WIDTH))}\n\n\n"
)

iid_payload = iid_payload.strip()

_artifacts = {
"user_claims": (example["user_claims"], "User Claims", "json"),
"sd_jwt_payload": (sdjwt_at_issuer.sd_jwt_payload, "Payload of the SD-JWT", "json"),
"user_claims": (
remove_sdobj_wrappers(example["user_claims"]),
"User Claims",
"json",
),
"sd_jwt_payload": (
sdjwt_at_issuer.sd_jwt_payload,
"Payload of the SD-JWT",
"json",
),
"sd_jwt_jws_part": (
sdjwt_at_issuer.serialized_sd_jwt,
"Serialized SD-JWT",
"txt",
),
"disclosures": (iid_payload, "Payloads of the II-Disclosures", "md"),
"disclosures": (
markdown_disclosures(
sdjwt_at_issuer.ii_disclosures,
),
"Payloads of the II-Disclosures",
"md",
),
"sd_jwt_issuance": (
sdjwt_at_issuer.sd_jwt_issuance,
"Combined SD-JWT and Disclosures",
"txt",
),
"kb_jwt_payload": (
sdjwt_at_holder.key_binding_jwt_payload
if example.get("key_binding")
else None,
(
sdjwt_at_holder.key_binding_jwt_payload
if example.get("key_binding")
else None
),
"Payload of the Holder Binding JWT",
"json",
),
Expand All @@ -245,7 +263,11 @@ def cb_get_issuer_key(issuer):
"Combined representation of SD-JWT and HS-Disclosures",
"txt",
),
"verified_contents": (verified, "Verified released contents of the SD-JWT", "json"),
"verified_contents": (
verified,
"Verified released contents of the SD-JWT",
"json",
),
}

# When decoys were used, list those as well
Expand All @@ -262,7 +284,6 @@ def cb_get_issuer_key(issuer):
"md",
)


if _args.output_dir:
logger.info(
f"Writing all the examples into separate files in '{_args.output_dir}'."
Expand Down Expand Up @@ -306,5 +327,6 @@ def cb_get_issuer_key(issuer):

sys.exit(0)


if __name__ == "__main__":
run()
run()
61 changes: 36 additions & 25 deletions src/sd_jwt/bin/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,22 @@


def generate_test_case_data(settings: Dict, testcase_path: Path, type: str):
seed = settings["random_seed"]
demo_keys = get_jwk(settings["key_settings"], True, seed)

### Load test case data
testcase = load_yaml_specification(testcase_path)
settings = {
**settings,
**testcase.get("settings_override", {}),
} # override settings

seed = settings["random_seed"]

demo_keys = get_jwk(settings["key_settings"], True, seed)
use_decoys = testcase.get("add_decoy_claims", False)
serialization_format = testcase.get("serialization_format", "compact")
include_default_claims = testcase.get("include_default_claims", True)
extra_header_parameters = testcase.get("extra_header_parameters", {})
issuer_keys = demo_keys["issuer_keys"]
holder_key = demo_keys["holder_key"] if testcase.get("key_binding", False) else None

claims = {}
if include_default_claims:
Expand All @@ -55,8 +62,8 @@ def generate_test_case_data(settings: Dict, testcase_path: Path, type: str):
SDJWTIssuer.unsafe_randomness = True
sdjwt_at_issuer = SDJWTIssuer(
claims,
demo_keys["issuer_key"],
demo_keys["holder_key"] if testcase.get("key_binding", False) else None,
issuer_keys,
holder_key,
add_decoy_claims=use_decoys,
serialization_format=serialization_format,
extra_header_parameters=extra_header_parameters,
Expand All @@ -70,13 +77,13 @@ def generate_test_case_data(settings: Dict, testcase_path: Path, type: str):
)
sdjwt_at_holder.create_presentation(
testcase["holder_disclosed_claims"],
settings["key_binding_nonce"]
if testcase.get("key_binding", False)
else None,
settings["identifiers"]["verifier"]
if testcase.get("key_binding", False)
else None,
demo_keys["holder_key"] if testcase.get("key_binding", False) else None,
settings["key_binding_nonce"] if testcase.get("key_binding", False) else None,
(
settings["identifiers"]["verifier"]
if testcase.get("key_binding", False)
else None
),
holder_key,
)

### Verify the SD-JWT using the SD-JWT-R
Expand All @@ -86,19 +93,19 @@ def generate_test_case_data(settings: Dict, testcase_path: Path, type: str):
def cb_get_issuer_key(issuer, header_parameters):
# Do not use in production - this allows to use any issuer name for demo purposes
if issuer == claims.get("iss", None):
return demo_keys["issuer_public_key"]
return demo_keys["issuer_public_keys"]
else:
raise Exception(f"Unknown issuer: {issuer}")

sdjwt_at_verifier = SDJWTVerifier(
sdjwt_at_holder.sd_jwt_presentation,
cb_get_issuer_key,
settings["identifiers"]["verifier"]
if testcase.get("key_binding", False)
else None,
settings["key_binding_nonce"]
if testcase.get("key_binding", False)
else None,
(
settings["identifiers"]["verifier"]
if testcase.get("key_binding", False)
else None
),
settings["key_binding_nonce"] if testcase.get("key_binding", False) else None,
serialization_format=serialization_format,
)
verified = sdjwt_at_verifier.get_verified_payload()
Expand Down Expand Up @@ -142,16 +149,20 @@ def cb_get_issuer_key(issuer, header_parameters):
_artifacts.update(
{
"kb_jwt_header": (
sdjwt_at_holder.key_binding_jwt_header
if testcase.get("key_binding")
else None,
(
sdjwt_at_holder.key_binding_jwt_header
if testcase.get("key_binding")
else None
),
"Header of the Holder Binding JWT",
"json",
),
"kb_jwt_payload": (
sdjwt_at_holder.key_binding_jwt_payload
if testcase.get("key_binding")
else None,
(
sdjwt_at_holder.key_binding_jwt_payload
if testcase.get("key_binding")
else None
),
"Payload of the Holder Binding JWT",
"json",
),
Expand Down
Loading
Loading