From ec022595cc995d994f76925d420c53f7190fb0ba Mon Sep 17 00:00:00 2001 From: Sergey Minaev Date: Tue, 26 Dec 2023 15:31:46 +0500 Subject: [PATCH] Release v0.0.4 (#14) * API Documentation and small cleanup. * Bump crate version to 0.0.4. Signed-off-by: Sergey Minaev --- CONTRIBUTING.md | 10 +++++----- Cargo.toml | 3 ++- README.md | 2 +- src/disclosure.rs | 25 +++++++++++++++++++++++++ src/holder.rs | 24 ++++++++++++++++++++++++ src/issuer.rs | 40 ++++++++++++++++++++++++++++++++++++---- src/verifier.rs | 11 +++++++++++ 7 files changed, 104 insertions(+), 11 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1edb616..14a4baf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,13 +8,13 @@ We love your input! We want to make contributing to this project as easy and tra - Proposing new features - Becoming a maintainer -## We Develop with Github +## We Develop with GitHub -We use github to host code, to track issues and feature requests, as well as accept pull requests. +We use GitHub to host code, to track issues and feature requests, as well as accept pull requests. -## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests +## We Use [GitHub Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests -Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests: +Pull requests are the best way to propose changes to the codebase (we use [GitHub Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests: 1. Fork the repo and create your branch from `main`. 2. If you've added code that should be tested, add tests. @@ -27,7 +27,7 @@ Pull requests are the best way to propose changes to the codebase (we use [Githu In short, when you submit code changes, your submissions are understood to be under the same [Apache 2.0 License](http://www.apache.org/licenses/) that covers the project. Feel free to contact the maintainers if that's a concern. -## Report bugs using Github's [issues](https://github.com/openwallet-foundation-labs/sd-jwt-rust/issues) +## Report bugs using GitHub's [issues](https://github.com/openwallet-foundation-labs/sd-jwt-rust/issues) We use GitHub issues to track public bugs. Report a bug by opening a new issue it's that easy! diff --git a/Cargo.toml b/Cargo.toml index 1ee853a..7350fd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sd-jwt-rs" -version = "0.0.3" +version = "0.0.4" edition = "2021" license = "Apache-2.0 OR MIT" description = "Rust reference implementation of the IETF SD-JWT specification (v6)." @@ -24,3 +24,4 @@ strum = { version = "0.25", default-features = false, features = ["std", "derive [dev-dependencies] rstest = "0.18.2" +regex = "1.10" diff --git a/README.md b/README.md index 110ef08..b4de5c9 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ cargo test ``` ### Interoperability testing tool -Coming soon (planned for v0.0.4) +Coming soon (planned for v0.0.5) ## External Dependencies diff --git a/src/disclosure.rs b/src/disclosure.rs index ae873ab..079fbfa 100644 --- a/src/disclosure.rs +++ b/src/disclosure.rs @@ -29,3 +29,28 @@ impl SDJWTDisclosure { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::utils::base64url_decode; + use regex::Regex; + + + #[test] + fn test_sdjwt_disclosure_when_key_is_none() { + let sdjwt_disclosure = SDJWTDisclosure::new(None, "test"); + let decoded_disclosure: String = String::from_utf8(base64url_decode(&sdjwt_disclosure.raw_b64).unwrap()).unwrap(); + + let re = Regex::new(r#"\[".*", test]"#).unwrap(); + assert!(re.is_match(&decoded_disclosure)); + } + + #[test] + fn test_sdjwt_disclosure_when_key_is_present() { + let sdjwt_disclosure = SDJWTDisclosure::new(Some("key".to_string()), "test"); + let decoded = String::from_utf8(base64url_decode(&sdjwt_disclosure.raw_b64).unwrap()).unwrap(); + + let re = Regex::new(r#"\[".*", "key", test]"#).unwrap(); + assert!(re.is_match(&decoded)); } +} diff --git a/src/holder.rs b/src/holder.rs index b4c9f0f..79639fd 100644 --- a/src/holder.rs +++ b/src/holder.rs @@ -25,6 +25,19 @@ pub struct SDJWTHolder { } impl SDJWTHolder { + /// Build an instance of holder to create one or more presentations based on SD JWT provided by issuer. + /// + /// # Arguments + /// * `sd_jwt_with_disclosures` - SD JWT with disclosures in the format specified by `serialization_format` + /// * `serialization_format` - Serialization format of the SD JWT. Supported values are `compact` and `json` + /// + /// # Returns + /// * `SDJWTHolder` - Instance of SDJWTHolder + /// + /// # Errors + /// * `InvalidInput` - If the serialization format is not supported + /// * `InvalidState` - If the SD JWT data is not valid + /// * `DeserializationError` - If the SD JWT serialization is not valid pub fn new(sd_jwt_with_disclosures: String, serialization_format: String) -> Result { let serialization_format = serialization_format.to_lowercase(); if serialization_format != "compact" && serialization_format != "json" { @@ -70,6 +83,17 @@ impl SDJWTHolder { Ok(holder) } + /// Create a presentation based on the SD JWT provided by issuer. + /// + /// # Arguments + /// * `claims_to_disclose` - Claims to disclose in the presentation + /// * `nonce` - Nonce to be used in the key-binding JWT + /// * `aud` - Audience to be used in the key-binding JWT + /// * `holder_key` - Key to sign the key-binding JWT + /// * `sign_alg` - Signing algorithm to be used in the key-binding JWT + /// + /// # Returns + /// * `String` - Presentation in the format specified by `serialization_format` in the constructor. It can be either compact or json. pub fn create_presentation( &mut self, claims_to_disclose: Map, diff --git a/src/issuer.rs b/src/issuer.rs index 4182da4..96597ea 100644 --- a/src/issuer.rs +++ b/src/issuer.rs @@ -47,9 +47,20 @@ pub enum SDJWTClaimsStrategy<'a> { Partial(Vec<&'a str>), } -// { -// let strategy = Partial(vec!["$.address", "$.address.street_address"]) -// } +/// SDJWTClaimsStrategy is used to determine which claims can be selectively disclosed later by the holder. +/// +/// The following strategies are supported: +/// * No: No claims can be selectively disclosed. +/// * Flat: Top-level claims can be selectively disclosed, nested objects are fully disclosed. +/// * Full: All claims can be selectively disclosed. +/// * Partial: Claims can be selectively disclosed based on the provided JSONPaths. +/// +/// # Examples +/// ``` +/// use sd_jwt_rs::issuer::SDJWTClaimsStrategy; +/// +/// let strategy = SDJWTClaimsStrategy::Partial(vec!["$.address", "$.address.street_address"]); +/// ``` impl<'a> SDJWTClaimsStrategy<'a> { fn finalize_input(&mut self) -> Result<()> { match self { @@ -108,7 +119,17 @@ impl SDJWTIssuer { const DECOY_MIN_ELEMENTS: u32 = 2; const DECOY_MAX_ELEMENTS: u32 = 5; - pub fn new(issuer_key: EncodingKey,sign_alg: Option) -> Self { + /// Creates a new SDJWTIssuer instance. + /// + /// The instance can be used mutliple times to issue SD-JWTs. + /// + /// # Arguments + /// * `issuer_key` - The key used to sign the SD-JWT. + /// * `sign_alg` - The signing algorithm used to sign the SD-JWT. If not provided, the default algorithm is used. + /// + /// # Returns + /// A new SDJWTIssuer instance. + pub fn new(issuer_key: EncodingKey, sign_alg: Option) -> Self { SDJWTIssuer { sign_alg: sign_alg.unwrap_or(DEFAULT_SIGNING_ALG.to_owned()), add_decoy_claims: false, @@ -131,6 +152,17 @@ impl SDJWTIssuer { self.serialized_sd_jwt = Default::default(); } + /// Issues a SD-JWT. + /// + /// # Arguments + /// * `user_claims` - The claims to be included in the SD-JWT. + /// * `sd_strategy` - The strategy to be used to determine which claims to be selectively disclosed. See SDJWTClaimsStrategy for more details. + /// * `holder_key` - The key used to sign the SD-JWT. If not provided, no key binding is added to the SD-JWT. + /// * `add_decoy_claims` - If true, decoy claims are added to the SD-JWT. + /// * `serialization_format` - The serialization format to be used for the SD-JWT. Only "compact" and "json" formats are supported. + /// + /// # Returns + /// The issued SD-JWT as a string in the requested serialization format. pub fn issue_sd_jwt( &mut self, user_claims: Value, diff --git a/src/verifier.rs b/src/verifier.rs index 389906d..72d4b39 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -29,6 +29,17 @@ pub struct SDJWTVerifier { } impl SDJWTVerifier { + /// Create a new SDJWTVerifier instance. + /// + /// # Arguments + /// * `sd_jwt_presentation` - The SD-JWT presentation to verify. + /// * `cb_get_issuer_key` - A callback function that takes the issuer and the header of the SD-JWT and returns the public key of the issuer. + /// * `expected_aud` - The expected audience of the SD-JWT. + /// * `expected_nonce` - The expected nonce of the SD-JWT. + /// * `serialization_format` - The serialization format of the SD-JWT. + /// + /// # Returns + /// * `SDJWTVerifier` - The SDJWTVerifier instance. The verified claims can be accessed via the `verified_claims` property. pub fn new( sd_jwt_presentation: String, cb_get_issuer_key: Box,