{"id":30126504,"url":"https://github.com/iotaledger/sd-jwt-payload","last_synced_at":"2025-08-10T16:51:02.681Z","repository":{"id":209875918,"uuid":"709778507","full_name":"iotaledger/sd-jwt-payload","owner":"iotaledger","description":"Rust implementation of Selective Disclosure for JWTs (SD-JWT)","archived":false,"fork":false,"pushed_at":"2025-01-20T12:37:32.000Z","size":111,"stargazers_count":5,"open_issues_count":1,"forks_count":3,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-01-30T17:39:50.976Z","etag":null,"topics":["jwt","sd-jwt","selective-disclosure","self-sovereign-identity"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iotaledger.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-25T11:36:28.000Z","updated_at":"2025-01-20T12:37:31.000Z","dependencies_parsed_at":"2023-12-11T11:23:11.952Z","dependency_job_id":"9264ac5a-4354-402e-b958-27f36e6f7a9e","html_url":"https://github.com/iotaledger/sd-jwt-payload","commit_stats":null,"previous_names":["iotaledger/sd-jwt","iotaledger/sd-jwt-payload"],"tags_count":4,"template":false,"template_full_name":"iotaledger/template","purl":"pkg:github/iotaledger/sd-jwt-payload","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iotaledger%2Fsd-jwt-payload","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iotaledger%2Fsd-jwt-payload/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iotaledger%2Fsd-jwt-payload/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iotaledger%2Fsd-jwt-payload/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iotaledger","download_url":"https://codeload.github.com/iotaledger/sd-jwt-payload/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iotaledger%2Fsd-jwt-payload/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269756297,"owners_count":24470560,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jwt","sd-jwt","selective-disclosure","self-sovereign-identity"],"created_at":"2025-08-10T16:50:59.275Z","updated_at":"2025-08-10T16:51:02.629Z","avatar_url":"https://github.com/iotaledger.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- This READM is based on the BEST-README-Template (https://github.com/othneildrew/Best-README-Template) --\u003e\n\u003cdiv id=\"top\"\u003e\u003c/div\u003e\n\u003c!--\n*** Thanks for checking out the Best-README-Template. If you have a suggestion\n*** that would make this better, please fork the repo and create a pull request\n*** or simply open an issue with the tag \"enhancement\".\n*** Don't forget to give the project a star!\n*** Thanks again! Now go create something AMAZING! :D\n--\u003e\n\n\n\n\u003c!-- PROJECT SHIELDS --\u003e\n\u003c!--\n*** I'm using markdown \"reference style\" links for readability.\n*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).\n*** See the bottom of this document for the declaration of the reference variables\n*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.\n*** https://www.markdownguide.org/basic-syntax/#reference-style-links\n--\u003e\n\u003c!-- [![Contributors][contributors-shield]][contributors-url] --\u003e\n\u003c!-- [![Forks][forks-shield]][forks-url] --\u003e\n\u003c!-- [![Stargazers][stars-shield]][stars-url] --\u003e\n\u003c!-- [![Issues][issues-shield]][issues-url] --\u003e\n[![Apache 2.0 license][license-shield]][license-url]\n[![Discord][discord-shield]][discord-url]\n[![StackExchange][stackexchange-shield]][stackexchange-url]\n\u003c!-- Add additional Badges. Some examples \u003e\n![Format Badge](https://github.com/iotaledger/template/workflows/Format/badge.svg \"Format Badge\")\n![Audit Badge](https://github.com/iotaledger/template/workflows/Audit/badge.svg \"Audit Badge\")\n![BuildBadge](https://github.com/iotaledger/template/workflows/Build/badge.svg \"Build Badge\")\n![Test Badge](https://github.com/iotaledger/template/workflows/Test/badge.svg \"Test Badge\")\n![Coverage Badge](https://coveralls.io/repos/github/iotaledger/template/badge.svg \"Coverage Badge\")\n\n\u003c!-- ABOUT THE PROJECT --\u003e\n\n# SD-JWT Reference implementation\n\nRust implementation of the [Selective Disclosure for JWTs (SD-JWT) **version 12**](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-12.html)\n\n## Overview\n\nThis library supports \n* **Issuing SD-JWTs**:\n  - Create a selectively disclosable JWT by choosing which properties can be concealed from a verifier.\n    Concealable claims are replaced with their disclosure's digest.\n  - Adding decoys to both JSON objects and arrays.\n  - Requiring an holder's key-bind.\n* **Managing SD-JWTs**  \n  - Conceal with ease any concealable property.\n  - Insert a key-bind.\n* **Verifying SD-JWTs**\n  - Recursively replace digests in objects and arrays with their corresponding disclosure value.\n\n`Sha-256` hash function is shipped by default, encoding/decoding with other hash functions is possible. \n\n## Getting started\nInclude the library in your `cargo.toml`.\n\n```bash\n[dependencies]\nsd-jwt-payload = { version = \"0.3.0\" }\n```\n\n## Examples\n\nSee [sd_jwt.rs](./examples/sd_jwt.rs) for a runnable example.\n\n## Usage\n\nThis library consists of the major structs:\n1. [`SdJwtBuilder`](./src/builder.rs): creates SD-JWTs.\n2. [`SdJwt`](./src/sd_jwt.rs): handles SD-JWTs.\n3. [`Disclosure`](./src/disclosure.rs): used throughout the library to represent disclosure objects.\n4. [`Hasher`](./src/hasher.rs): a trait to provide hash functions create and replace disclosures.\n5. [`Sha256Hasher`](./src/hasher.rs): implements `Hasher` for the `Sha-256` hash function.\n6. [`JwsSigner`](./src/signer.rs): a trait used to create JWS signatures.\n\n\n### Creation\nAny JSON object can be used to create an SD-JWT:\n\n```rust\n  let object = json!({\n    \"sub\": \"user_42\",\n    \"given_name\": \"John\",\n    \"family_name\": \"Doe\",\n    \"email\": \"johndoe@example.com\",\n    \"phone_number\": \"+1-202-555-0101\",\n    \"phone_number_verified\": true,\n    \"address\": {\n      \"street_address\": \"123 Main St\",\n      \"locality\": \"Anytown\",\n      \"region\": \"Anystate\",\n      \"country\": \"US\"\n    },\n    \"birthdate\": \"1940-01-01\",\n    \"updated_at\": 1570000000,\n    \"nationalities\": [\n      \"US\",\n      \"DE\"\n    ]\n  });\n```\n\n\n```rust\n  let builder: SdJwtBuilder = SdJwtBuilder::new(object);\n```\nThis creates a stateful builder with `Sha-256` hash function by default to create disclosure digests. \n\n*Note: `SdJwtBuilder` is generic over `Hasher` which allows custom encoding with other hash functions.*\n\nThe builder can encode any of the object's values or array elements, using the `make_concealable` method. Suppose the value of `street_address` in 'address' should be selectively disclosed as well as the entire value of `address` and the first `phone` value.\n\n\n```rust\n  builder\n    .make_concealable(\"/email\")?\n    .make_concealable(\"/phone_number\")?\n    .make_concealable(\"/address/street_address\")?\n    .make_concealable(\"/address\")?\n    .make_concealable(\"/nationalities/0\")?\n```\n\n*Note: the `make_concealable` method takes a [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) to determine the element to conceal inside the JSON object.*\n\n\nThe builder also supports adding decoys. For instance, the amount of phone numbers and the amount of claims need to be hidden.\n\n```rust\n  builder\n    .add_decoys(\"/nationalities\", 1)? // Adds 1 decoys to the array `nationalities`.\n    .add_decoys(\"\", 2)? // Adds 2 decoys to the top level object.\n```\n\nThrough the builder an issuer can require a specific key-binding that will be verified upon validation:\n\n```rust\n  builder\n    .require_key_binding(RequiredKeyBinding::Kid(\"key1\".to_string()))\n```\n\nInternally, builder's object now looks like:\n\n```json\n{\n  \"_sd\": [\n    \"5P7JOl7w5kWrMDQ71U4ts1CHaPPNTKDqOt9OaOdGMOg\",\n    \"73rQnMSG1np-GjzaM-yHfcZAIqmeaIK9Dn9N0atxHms\",\n    \"s0UiQ41MTAPnjfKk4HEYet0ksuMo0VTArCwG5ALiC84\",\n    \"v-xRCoLxbDcL5NZGX9uRFI0hgH9gx3uX1Y1EMcWeC5k\",\n    \"z7SAFTHCOGF8vXbHyIPXH6TQvo750AdGXhvqgMTA8Mw\"\n  ],\n  \"_sd_alg\": \"sha-256\",\n  \"cnf\": {\n    \"kid\": \"key1\"\n  },\n  \"sub\": \"user_42\",\n  \"given_name\": \"John\",\n  \"family_name\": \"Doe\",\n  \"nationalities\": [\n    {\n      \"...\": \"xYpMTpfay0Rb77IWvbJU1C4JT3kvJUftZHxZuwfiS1M\"\n    },\n    \"DE\",\n    {\n      \"...\": \"GqcdlPi6GUDcj9VVpm8kj29jfXCdyBx2GfWP34339hI\"\n    }\n  ],\n  \"phone_number_verified\": true,\n  \"updated_at\": 1570000000,\n  \"birthdate\": \"1940-01-01\"\n}\n```\n\n*Note: no JWT claims like `exp` or `iat` are added. If necessary, these need to be added and validated manually.*\n\nTo create the actual SD-JWT the `finish` method must be called on the builder:\n\n```rust\n  let signer = MyHS256Signer::new(); \n  let sd_jwt = builder\n    // ...\n    .finish(\u0026signer, \"ES256\")\n    .await?;\n```\n\n```\neyJ0eXAiOiJTRC1KV1QiLCJhbGciOiJIUzI1NiJ9.eyJnaXZlbl9uYW1lIjoiSm9obiIsImZhbWlseV9uYW1lIjoiRG9lIiwicGhvbmUiOlt7Ii4uLiI6ImVaVm4wS2tRbV9UOHgteDU3VnhZdC1fTW1ORzkxU2gzNEUtYlpFbk5mV1kifSwiKzQ5IDIzNDU2NyIseyIuLi4iOiJLQWlKSXgwdGt0UVJYQnhaU0JWVmxkOTI5OGJaSXAyV2twa0RZRGEzQ1dRIn0seyIuLi4iOiJDQktBUlBoNnNkVENKeWxpWjdwQk9Zeml4N1o0QmI0eVJoMEV5a0hYMlV3In0seyIuLi4iOiJvaTFLZ3NZWGdxQkZYVVh2YlZhSFNHWVlhV2hrQjVSTDU1VDkwR2xfNXMwIn1dLCJfc2QiOlsiSmo1akJlR0Vhd1k2dlJ2bUhEZzU1RWplQUlQOEZWaFdFVjJGY3poVVhyWSIsIjhlcXBoQlBKeUNCZ1VKaE5XTlA3Y2ktWTc5TjYxNXdwWlFyeGk1RDRqdTgiLCJfaE9VNXB1SmpOelNCaEswYndoM2g4X2I2SDZuTjd2ZF83STB1VHA4ME1vIiwiR190SDcwTXJmQ2tWTTBIaHNIOVJFT2JJdDFFaTE5NDc3eTZDRXNTMFpsbyIsInpQNTZNZUgwcnlqenFoOUthZHJiNUM5WjJCRTJGV2c4bmIzZzByUjNMU0EiLCJkZ2ZWVzExaXA5T095Vmk4TTRoMVJqWEs4YWt3N0lDZU1Ra2pVd1NJNmlVIiwiQngzM21PeVRGNS13OGdSUzV5TDRZUTRkaWc0NFYzbG1IeGsxV1Jzc183VSJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.knTqw4FMCplHoMu7mfiix7dv4lIjYgRIn-tmuemAhbY~WyJHaGpUZVYwV2xlUHE1bUNrVUtPVTkzcXV4WURjTzIiLCAic3RyZWV0X2FkZHJlc3MiLCAiMTIzIE1haW4gU3QiXQ~WyJVVXVBelg5RDdFV1g0c0FRVVM5aURLYVp3cU13blUiLCAiYWRkcmVzcyIsIHsicmVnaW9uIjoiQW55c3RhdGUiLCJfc2QiOlsiaHdiX2d0eG01SnhVbzJmTTQySzc3Q194QTUxcmkwTXF0TVVLZmI0ZVByMCJdfV0~WyJHRDYzSTYwUFJjb3dvdXJUUmg4OG5aM1JNbW14YVMiLCAiKzQ5IDEyMzQ1NiJd~\n```\n\n### Handling\n\nOnce an SD-JWT is obtained, any concealable property can be omitted from it by creating a presentation and calling the\n`conceal` method:\n\n```rust\n  let mut sd_jwt = SdJwt::parse(\"...\")?;\n  let hasher = Sha256Hasher::new();\n  let (presented_sd_jwt, removed_disclosures) = sd_jwt\n    .into_presentation(\u0026hasher)?\n    .conceal(\"/email\")?\n    .conceal(\"/nationalities/0\")?\n    .finish()?;\n```\n\nTo attach a key-binding JWT (KB-JWT) the `KeyBindingJwtBuilder` struct can be used:\n\n```rust\n  let mut sd_jwt = SdJwt::parse(\"...\")?;\n  // Can be used to check which key is required - if any.\n  let requird_kb: Option\u003c\u0026RequiredKeyBinding\u003e = sd_jwt.required_key_binding();\n\n  let signer = MyJwkSigner::new();\n  let hasher = Sha256Hasher::new();\n  let kb_jwt = KeyBindingJwtBuilder::new()\n    .nonce(\"abcd-efgh-ijkl-mnop\")\n    .iat(time::now())\n    .finish(\u0026sd_jwt, \u0026hasher, \"ES256\", \u0026signer)\n    .await?;\n  \n  let (sd_jwt, _) = sd_jwt.into_presentation(\u0026hasher)?\n    .attach_key_binding_jwt(kb_jwt)\n    .finish()?;\n```\n\n### Verifying\n\nThe SD-JWT can be turned into a JSON object of its disclosed values by calling the `into_disclosed_object` method:\n\n```rust\n  let mut sd_jwt = SdJwt::parse(\"...\")?;\n  let disclosed_object = sd_jwt.into_disclosed_object(\u0026hasher)?;\n```\n`disclosed_object`:\n\n```json\n{\n  \"address\": {\n    \"country\": \"US\",\n    \"locality\": \"Anytown\",\n    \"region\": \"Anystate\",\n    \"street_address\": \"123 Main St\"\n  },\n  \"phone_number\": \"+1-202-555-0101\",\n  \"cnf\": {\n    \"kid\": \"key1\"\n  },\n  \"sub\": \"user_42\",\n  \"given_name\": \"John\",\n  \"family_name\": \"Doe\",\n  \"nationalities\": [\n    \"DE\"\n  ],\n  \"phone_number_verified\": true,\n  \"updated_at\": 1570000000,\n  \"birthdate\": \"1940-01-01\"\n}\n\n```\n\nNote:\n* `_sd_alg` property was removed.\n\n\n\u003c!-- CONTRIBUTING --\u003e\n## Contributing\n\nContributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.\n\nIf you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag \"enhancement\".\nDon't forget to give the project a star! Thanks again!\n\n1. Fork the Project\n2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)\n3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)\n4. Push to the Branch (`git push origin feature/AmazingFeature`)\n5. Open a Pull Request\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003c!-- LICENSE --\u003e\n## License\n\nDistributed under the Apache License. See `LICENSE` for more information.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\u003c!-- MARKDOWN LINKS \u0026 IMAGES --\u003e\n\u003c!-- https://www.markdownguide.org/basic-syntax/#reference-style-links --\u003e\n[contributors-shield]: https://img.shields.io/github/contributors/iotaledger/template.svg?style=for-the-badge\n[contributors-url]: https://github.com/iotaledger/template/graphs/contributors\n[forks-shield]: https://img.shields.io/github/forks/iotaledger/template.svg?style=for-the-badge\n[forks-url]: https://github.com/iotaledger/template/network/members\n[stars-shield]: https://img.shields.io/github/stars/iotaledger/template.svg?style=for-the-badge\n[stars-url]: https://github.com/iotaledger/template/stargazers\n[issues-shield]: https://img.shields.io/github/issues/iotaledger/template.svg?style=for-the-badge\n[issues-url]: https://github.com/iotaledger/template/issues\n[license-shield]: https://img.shields.io/github/license/iotaledger/template.svg?style=for-the-badge\n[license-url]: https://github.com/iotaledger/sd-jwt/blob/main/LICENSE\n[discord-shield]: https://img.shields.io/badge/Discord-9cf.svg?style=for-the-badge\u0026logo=discord\n[discord-url]: https://discord.iota.org\n[stackexchange-shield]: https://img.shields.io/badge/StackExchange-9cf.svg?style=for-the-badge\u0026logo=stackexchange\n[stackexchange-url]: https://iota.stackexchange.com\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiotaledger%2Fsd-jwt-payload","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiotaledger%2Fsd-jwt-payload","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiotaledger%2Fsd-jwt-payload/lists"}