[![Apache 2.0 license][license-shield]][license-url] [![Discord][discord-shield]][discord-url] [![StackExchange][stackexchange-shield]][stackexchange-url] # SD-JWT Reference implementation Rust implementation of the [Selective Disclosure for JWTs (SD-JWT) **version 07**](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html) ## Overview This library supports * **Encoding**: - creating disclosers and replacing values in objects and arrays with the digest of their disclosure. - Adding decoys to objects and arrays. * **Decoding** - Recursively replace digests in objects and arrays with their corresponding disclosure value. `Sha-256` hash function is shipped by default, encoding/decoding with other hash functions is possible. ## Getting started Include the library in your `cargo.toml`. ```bash [dependencies] sd-jwt-payload = { version = "0.2.1" } ``` ## Examples See [sd_jwt.rs](./examples/sd_jwt.rs) for a runnable example. ## Usage This library consists of the major structs: 1. [`SdObjectEncoder`](./src/encoder.rs): creates SD objects. 2. [`SdObjectDecoder`](./src/decoder.rs): decodes SD objects. 3. [`Disclosure`](./src/disclosure.rs): used by the `SdObjectEncoder` and `SdObjectDecoder` to represent a disclosure. 3. [`SdJwt`](./src/sd_jwt.rs): creates/parses SD-JWTs. 4. [`Hasher`](./src/hasher.rs): a trait to provide hash functions to the encoder/decoder. 5. [`Sha256Hasher`](./src/hasher.rs): implements `Hasher` for the `Sha-256` hash function. ### Encoding Any JSON object can be encoded ```rust let object = json!({ "given_name": "John", "family_name": "Doe", "address": { "street_address": "123 Main St", "region": "Anystate", }, "phone": [ "+49 123456", "+49 234567" ] }); ``` ```rust let mut encoder: SdObjectEncoder = object.try_into()?; ``` This creates a stateful encoder with `Sha-256` hash function by default to create disclosure digests. *Note: `SdObjectEncoder` is generic over `Hasher` which allows custom encoding with other hash functions.* The encoder can encode any of the object's values or array elements, using the `conceal` method. Suppose the value of `street_address` should be selectively disclosed as well as the value of `address` and the first `phone` value. ```rust let disclosure1 = encoder.conceal("/address/street_address"], None)?; let disclosure2 = encoder.conceal("/address", None)?; let disclosure3 = encoder.conceal("/phone/0", None)?; ``` ``` "WyJHaGpUZVYwV2xlUHE1bUNrVUtPVTkzcXV4WURjTzIiLCAic3RyZWV0X2FkZHJlc3MiLCAiMTIzIE1haW4gU3QiXQ" "WyJVVXVBelg5RDdFV1g0c0FRVVM5aURLYVp3cU13blUiLCAiYWRkcmVzcyIsIHsicmVnaW9uIjoiQW55c3RhdGUiLCJfc2QiOlsiaHdiX2d0eG01SnhVbzJmTTQySzc3Q194QTUxcmkwTXF0TVVLZmI0ZVByMCJdfV0" "WyJHRDYzSTYwUFJjb3dvdXJUUmg4OG5aM1JNbW14YVMiLCAiKzQ5IDEyMzQ1NiJd" ``` *Note: the `conceal` method takes a [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) to determine the element to conceal inside the JSON object.* The encoder also supports adding decoys. For instance, the amount of phone numbers and the amount of claims need to be hidden. ```rust encoder.add_decoys("/phone", 3).unwrap(); //Adds 3 decoys to the array `phone`. encoder.add_decoys("", 6).unwrap(); // Adds 6 decoys to the top level object. ``` Add the hash function claim. ```rust encoder.add_sd_alg_property(); // This adds "_sd_alg": "sha-256" ``` Now `encoder.object()?` will return the encoded object. ```json { "given_name": "John", "family_name": "Doe", "phone": [ { "...": "eZVn0KkQm_T8x-x57VxYt-_MmNG91Sh34E-bZEnNfWY" }, "+49 234567", { "...": "KAiJIx0tktQRXBxZSBVVld9298bZIp2WkpkDYDa3CWQ" }, { "...": "CBKARPh6sdTCJyliZ7pBOYzix7Z4Bb4yRh0EykHX2Uw" }, { "...": "oi1KgsYXgqBFXUXvbVaHSGYYaWhkB5RL55T90Gl_5s0" } ], "_sd": [ "Jj5jBeGEawY6vRvmHDg55EjeAIP8FVhWEV2FczhUXrY", "8eqphBPJyCBgUJhNWNP7ci-Y79N615wpZQrxi5D4ju8", "_hOU5puJjNzSBhK0bwh3h8_b6H6nN7vd_7I0uTp80Mo", "G_tH70MrfCkVM0HhsH9REObIt1Ei19477y6CEsS0Zlo", "zP56MeH0ryjzqh9Kadrb5C9Z2BE2FWg8nb3g0rR3LSA", "dgfVW11ip9OOyVi8M4h1RjXK8akw7ICeMQkjUwSI6iU", "Bx33mOyTF5-w8gRS5yL4YQ4dig44V3lmHxk1WRss_7U" ], "_sd_alg": "sha-256" } ``` *Note: no JWT claims like `exp` or `iat` are added. If necessary, these need to be added and validated manually.* ### Creating SD-JWT Since creating JWTs is outside the scope of this library, see [sd_jwt.rs example](./examples/sd_jwt.rs) where `josekit` is used to create `jwt` with the object above as the claim set. Create SD-JWT ```rust let sd_jwt: SdJwt = SdJwt::new(jwt, disclosures.clone(), None); let sd_jwt: String = sd_jwt.presentation(); ``` ``` eyJ0eXAiOiJTRC1KV1QiLCJhbGciOiJIUzI1NiJ9.eyJnaXZlbl9uYW1lIjoiSm9obiIsImZhbWlseV9uYW1lIjoiRG9lIiwicGhvbmUiOlt7Ii4uLiI6ImVaVm4wS2tRbV9UOHgteDU3VnhZdC1fTW1ORzkxU2gzNEUtYlpFbk5mV1kifSwiKzQ5IDIzNDU2NyIseyIuLi4iOiJLQWlKSXgwdGt0UVJYQnhaU0JWVmxkOTI5OGJaSXAyV2twa0RZRGEzQ1dRIn0seyIuLi4iOiJDQktBUlBoNnNkVENKeWxpWjdwQk9Zeml4N1o0QmI0eVJoMEV5a0hYMlV3In0seyIuLi4iOiJvaTFLZ3NZWGdxQkZYVVh2YlZhSFNHWVlhV2hrQjVSTDU1VDkwR2xfNXMwIn1dLCJfc2QiOlsiSmo1akJlR0Vhd1k2dlJ2bUhEZzU1RWplQUlQOEZWaFdFVjJGY3poVVhyWSIsIjhlcXBoQlBKeUNCZ1VKaE5XTlA3Y2ktWTc5TjYxNXdwWlFyeGk1RDRqdTgiLCJfaE9VNXB1SmpOelNCaEswYndoM2g4X2I2SDZuTjd2ZF83STB1VHA4ME1vIiwiR190SDcwTXJmQ2tWTTBIaHNIOVJFT2JJdDFFaTE5NDc3eTZDRXNTMFpsbyIsInpQNTZNZUgwcnlqenFoOUthZHJiNUM5WjJCRTJGV2c4bmIzZzByUjNMU0EiLCJkZ2ZWVzExaXA5T095Vmk4TTRoMVJqWEs4YWt3N0lDZU1Ra2pVd1NJNmlVIiwiQngzM21PeVRGNS13OGdSUzV5TDRZUTRkaWc0NFYzbG1IeGsxV1Jzc183VSJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.knTqw4FMCplHoMu7mfiix7dv4lIjYgRIn-tmuemAhbY~WyJHaGpUZVYwV2xlUHE1bUNrVUtPVTkzcXV4WURjTzIiLCAic3RyZWV0X2FkZHJlc3MiLCAiMTIzIE1haW4gU3QiXQ~WyJVVXVBelg5RDdFV1g0c0FRVVM5aURLYVp3cU13blUiLCAiYWRkcmVzcyIsIHsicmVnaW9uIjoiQW55c3RhdGUiLCJfc2QiOlsiaHdiX2d0eG01SnhVbzJmTTQySzc3Q194QTUxcmkwTXF0TVVLZmI0ZVByMCJdfV0~WyJHRDYzSTYwUFJjb3dvdXJUUmg4OG5aM1JNbW14YVMiLCAiKzQ5IDEyMzQ1NiJd~ ``` ### Decoding Parse the SD-JWT string to extract the JWT and the disclosures in order to decode the claims and construct the disclosed values. *Note: Validating the signature of the JWT and extracting the claim set is outside the scope of this library. ```rust let sd_jwt: SdJwt = SdJwt::parse(sd_jwt_string)?; let claims_set: // extract claims from `sd_jwt.jwt`. let decoder = SdObjectDecoder::new(); let decoded_object = decoder.decode(claims_set, &sd_jwt.disclosures)?; ``` `decoded_object`: ```json { "given_name": "John", "family_name": "Doe", "phone": [ "+49 123456", "+49 234567" ], "address": { "region": "Anystate", "street_address": "123 Main St" } } ``` Note: * `street_address` and `address` are recursively decoded. * `_sd_alg` property was removed. ## Contributing Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. If 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". Don't forget to give the project a star! Thanks again! 1. Fork the Project 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 4. Push to the Branch (`git push origin feature/AmazingFeature`) 5. Open a Pull Request

(back to top)

## License Distributed under the Apache License. See `LICENSE` for more information.

(back to top)

[contributors-shield]: https://img.shields.io/github/contributors/iotaledger/template.svg?style=for-the-badge [contributors-url]: https://github.com/iotaledger/template/graphs/contributors [forks-shield]: https://img.shields.io/github/forks/iotaledger/template.svg?style=for-the-badge [forks-url]: https://github.com/iotaledger/template/network/members [stars-shield]: https://img.shields.io/github/stars/iotaledger/template.svg?style=for-the-badge [stars-url]: https://github.com/iotaledger/template/stargazers [issues-shield]: https://img.shields.io/github/issues/iotaledger/template.svg?style=for-the-badge [issues-url]: https://github.com/iotaledger/template/issues [license-shield]: https://img.shields.io/github/license/iotaledger/template.svg?style=for-the-badge [license-url]: https://github.com/iotaledger/sd-jwt/blob/main/LICENSE [discord-shield]: https://img.shields.io/badge/Discord-9cf.svg?style=for-the-badge&logo=discord [discord-url]: https://discord.iota.org [stackexchange-shield]: https://img.shields.io/badge/StackExchange-9cf.svg?style=for-the-badge&logo=stackexchange [stackexchange-url]: https://iota.stackexchange.com