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

Support Selective Disclosure SD-JWT #1268

Merged
merged 35 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7586ed8
first encoder/decoder impl
abdulmth Oct 15, 2023
e1a3f3e
implement poc validator
abdulmth Oct 17, 2023
b1ecf88
kb-jwt validator poc
abdulmth Nov 27, 2023
0c24017
fix issuance date validation
abdulmth Nov 27, 2023
a6c0938
improve validator
abdulmth Nov 30, 2023
ba1be31
improvements
abdulmth Dec 6, 2023
95f4b60
improve example
abdulmth Dec 7, 2023
033792f
tst
abdulmth Dec 7, 2023
348484b
add tests
abdulmth Dec 11, 2023
37ea17f
kb tests
abdulmth Dec 11, 2023
67b0881
fmt
abdulmth Dec 11, 2023
a6b0951
clippy
abdulmth Dec 11, 2023
67d3dde
struct for key binding options
abdulmth Dec 11, 2023
7dd781a
clippy
abdulmth Dec 11, 2023
344a0c3
Merge branch 'main' into feat/poc-sd-jwt
abdulmth Dec 11, 2023
14349c5
fix example
abdulmth Dec 12, 2023
aca2fff
dprint fmt
abdulmth Dec 12, 2023
6a5cc5a
fix license
abdulmth Dec 12, 2023
6459d98
add wasm bindings
abdulmth Jan 10, 2024
7800866
decouple JwtValidator and SdJwtValidator
abdulmth Jan 11, 2024
68622e7
binding for validator
abdulmth Jan 11, 2024
1106490
wasm bindings for validator
abdulmth Jan 15, 2024
d365a92
Merge branch 'main' into feat/poc-sd-jwt
abdulmth Jan 15, 2024
3bbb4f0
improvements
abdulmth Jan 15, 2024
56e6bb7
clippy
abdulmth Jan 15, 2024
4bf9990
fmt
abdulmth Jan 15, 2024
8a215bc
skip serialization of none
abdulmth Jan 15, 2024
6c30f93
improvements
abdulmth Jan 15, 2024
7573000
api reference
abdulmth Jan 15, 2024
b2fe9d7
fix wasm clippy issues
abdulmth Jan 15, 2024
5b29f16
dprint
abdulmth Jan 15, 2024
979c224
make `sd-jwt-payload` optional
abdulmth Jan 15, 2024
7cf7585
add example to CI, improvements
abdulmth Jan 17, 2024
d28a27d
fmt
abdulmth Jan 17, 2024
ecddea3
fix cypress test
abdulmth Jan 17, 2024
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
2 changes: 1 addition & 1 deletion bindings/wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ wasm-bindgen-futures = { version = "0.4", default-features = false }
version = "1.0.0"
path = "../../identity_iota"
default-features = false
features = ["client", "revocation-bitmap", "resolver", "domain-linkage"]
features = ["client", "revocation-bitmap", "resolver", "domain-linkage", "sd-jwt"]

[dev-dependencies]
rand = "0.8.5"
Expand Down
173 changes: 161 additions & 12 deletions bindings/wasm/docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ It does not imply anything about a potentially present proof property on the cre
<p>Note that having an instance of this type only means the JWS it was constructed from was verified.
It does not imply anything about a potentially present proof property on the presentation itself.</p>
</dd>
<dt><a href="#Disclosure">Disclosure</a></dt>
<dd></dd>
<dt><a href="#DomainLinkageConfiguration">DomainLinkageConfiguration</a></dt>
<dd><p>DID Configuration Resource which contains Domain Linkage Credentials.
It can be placed in an origin&#39;s <code>.well-known</code> directory to prove linkage between the origin and a DID.
Expand Down Expand Up @@ -130,6 +132,10 @@ verifiable <a href="#Credential">Credential</a>s and <a href="#Presentation">Pre
<dt><a href="#RevocationBitmap">RevocationBitmap</a></dt>
<dd><p>A compressed bitmap for managing credential revocation.</p>
</dd>
<dt><a href="#SdObjectDecoder">SdObjectDecoder</a></dt>
<dd></dd>
<dt><a href="#SdObjectEncoder">SdObjectEncoder</a></dt>
<dd></dd>
<dt><a href="#Service">Service</a></dt>
<dd><p>A DID Document Service used to enable trusted interactions associated with a DID subject.</p>
</dd>
Expand All @@ -149,10 +155,6 @@ working with storage backed DID documents.</p>
## Members

<dl>
<dt><a href="#StateMetadataEncoding">StateMetadataEncoding</a></dt>
<dd></dd>
<dt><a href="#MethodRelationship">MethodRelationship</a></dt>
<dd></dd>
<dt><a href="#StatusCheck">StatusCheck</a></dt>
<dd><p>Controls validation behaviour when checking whether or not a credential has been revoked by its
<a href="https://www.w3.org/TR/vc-data-model/#status"><code>credentialStatus</code></a>.</p>
Expand Down Expand Up @@ -193,6 +195,10 @@ This variant is the default.</p>
<dt><a href="#FirstError">FirstError</a></dt>
<dd><p>Return after the first error occurs.</p>
</dd>
<dt><a href="#StateMetadataEncoding">StateMetadataEncoding</a></dt>
<dd></dd>
<dt><a href="#MethodRelationship">MethodRelationship</a></dt>
<dd></dd>
</dl>

## Functions
Expand Down Expand Up @@ -1384,6 +1390,65 @@ The `aud` property parsed from JWT claims.
The custom claims parsed from the JWT.

**Kind**: instance method of [<code>DecodedJwtPresentation</code>](#DecodedJwtPresentation)
<a name="Disclosure"></a>

## Disclosure
**Kind**: global class

* [Disclosure](#Disclosure)
* [new Disclosure(salt, claim_name, claim_value)](#new_Disclosure_new)
* _instance_
* [.disclosure()](#Disclosure+disclosure) ⇒ <code>string</code>
* [.salt()](#Disclosure+salt) ⇒ <code>string</code>
* [.claimName()](#Disclosure+claimName) ⇒ <code>string</code> \| <code>undefined</code>
* [.claimValue()](#Disclosure+claimValue) ⇒ <code>any</code>
* [.toJSON()](#Disclosure+toJSON) ⇒ <code>any</code>
* _static_
* [.fromJSON(json)](#Disclosure.fromJSON) ⇒ [<code>Disclosure</code>](#Disclosure)

<a name="new_Disclosure_new"></a>

### new Disclosure(salt, claim_name, claim_value)

| Param | Type |
| --- | --- |
| salt | <code>string</code> |
| claim_name | <code>string</code> \| <code>undefined</code> |
| claim_value | <code>any</code> |

<a name="Disclosure+disclosure"></a>

### disclosure.disclosure() ⇒ <code>string</code>
**Kind**: instance method of [<code>Disclosure</code>](#Disclosure)
<a name="Disclosure+salt"></a>

### disclosure.salt() ⇒ <code>string</code>
**Kind**: instance method of [<code>Disclosure</code>](#Disclosure)
<a name="Disclosure+claimName"></a>

### disclosure.claimName() ⇒ <code>string</code> \| <code>undefined</code>
**Kind**: instance method of [<code>Disclosure</code>](#Disclosure)
<a name="Disclosure+claimValue"></a>

### disclosure.claimValue() ⇒ <code>any</code>
**Kind**: instance method of [<code>Disclosure</code>](#Disclosure)
<a name="Disclosure+toJSON"></a>

### disclosure.toJSON() ⇒ <code>any</code>
Serializes this to a JSON object.

**Kind**: instance method of [<code>Disclosure</code>](#Disclosure)
<a name="Disclosure.fromJSON"></a>

### Disclosure.fromJSON(json) ⇒ [<code>Disclosure</code>](#Disclosure)
Deserializes an instance from a JSON object.

**Kind**: static method of [<code>Disclosure</code>](#Disclosure)

| Param | Type |
| --- | --- |
| json | <code>any</code> |

<a name="DomainLinkageConfiguration"></a>

## DomainLinkageConfiguration
Expand Down Expand Up @@ -4627,6 +4692,90 @@ if it is a valid Revocation Bitmap Service.
| --- | --- |
| service | [<code>Service</code>](#Service) |

<a name="SdObjectDecoder"></a>

## SdObjectDecoder
**Kind**: global class
<a name="SdObjectDecoder+decode"></a>

### sdObjectDecoder.decode(object, disclosures) ⇒ <code>Record.&lt;string, any&gt;</code>
**Kind**: instance method of [<code>SdObjectDecoder</code>](#SdObjectDecoder)

| Param | Type |
| --- | --- |
| object | <code>Record.&lt;string, any&gt;</code> |
| disclosures | <code>Array.&lt;string&gt;</code> |

<a name="SdObjectEncoder"></a>

## SdObjectEncoder
**Kind**: global class

* [SdObjectEncoder](#SdObjectEncoder)
* [new SdObjectEncoder(object)](#new_SdObjectEncoder_new)
* [.conceal(path, salt)](#SdObjectEncoder+conceal) ⇒ [<code>Disclosure</code>](#Disclosure)
* [.concealArrayEntry(path, element_index, salt)](#SdObjectEncoder+concealArrayEntry) ⇒ [<code>Disclosure</code>](#Disclosure)
* [.addSdAlgProperty()](#SdObjectEncoder+addSdAlgProperty)
* [.toString()](#SdObjectEncoder+toString) ⇒ <code>string</code>
* [.encoded_object()](#SdObjectEncoder+encoded_object) ⇒ <code>Record.&lt;string, any&gt;</code>
* [.toJSON()](#SdObjectEncoder+toJSON) ⇒ <code>any</code>
* [.addDecoys(path, number_of_decoys)](#SdObjectEncoder+addDecoys)

<a name="new_SdObjectEncoder_new"></a>

### new SdObjectEncoder(object)

| Param | Type |
| --- | --- |
| object | <code>any</code> |

<a name="SdObjectEncoder+conceal"></a>

### sdObjectEncoder.conceal(path, salt) ⇒ [<code>Disclosure</code>](#Disclosure)
**Kind**: instance method of [<code>SdObjectEncoder</code>](#SdObjectEncoder)

| Param | Type |
| --- | --- |
| path | <code>Array.&lt;string&gt;</code> |
| salt | <code>string</code> \| <code>undefined</code> |

<a name="SdObjectEncoder+concealArrayEntry"></a>

### sdObjectEncoder.concealArrayEntry(path, element_index, salt) ⇒ [<code>Disclosure</code>](#Disclosure)
**Kind**: instance method of [<code>SdObjectEncoder</code>](#SdObjectEncoder)

| Param | Type |
| --- | --- |
| path | <code>Array.&lt;string&gt;</code> |
| element_index | <code>number</code> |
| salt | <code>string</code> \| <code>undefined</code> |

<a name="SdObjectEncoder+addSdAlgProperty"></a>

### sdObjectEncoder.addSdAlgProperty()
**Kind**: instance method of [<code>SdObjectEncoder</code>](#SdObjectEncoder)
<a name="SdObjectEncoder+toString"></a>

### sdObjectEncoder.toString() ⇒ <code>string</code>
**Kind**: instance method of [<code>SdObjectEncoder</code>](#SdObjectEncoder)
<a name="SdObjectEncoder+encoded_object"></a>

### sdObjectEncoder.encoded\_object() ⇒ <code>Record.&lt;string, any&gt;</code>
**Kind**: instance method of [<code>SdObjectEncoder</code>](#SdObjectEncoder)
<a name="SdObjectEncoder+toJSON"></a>

### sdObjectEncoder.toJSON() ⇒ <code>any</code>
**Kind**: instance method of [<code>SdObjectEncoder</code>](#SdObjectEncoder)
<a name="SdObjectEncoder+addDecoys"></a>

### sdObjectEncoder.addDecoys(path, number_of_decoys)
**Kind**: instance method of [<code>SdObjectEncoder</code>](#SdObjectEncoder)

| Param | Type |
| --- | --- |
| path | <code>Array.&lt;string&gt;</code> |
| number_of_decoys | <code>number</code> |

<a name="Service"></a>

## Service
Expand Down Expand Up @@ -5041,14 +5190,6 @@ Deserializes an instance from a JSON object.
| --- | --- |
| json | <code>any</code> |

<a name="StateMetadataEncoding"></a>

## StateMetadataEncoding
**Kind**: global variable
<a name="MethodRelationship"></a>

## MethodRelationship
**Kind**: global variable
<a name="StatusCheck"></a>

## StatusCheck
Expand Down Expand Up @@ -5124,6 +5265,14 @@ Return all errors that occur during validation.
## FirstError
Return after the first error occurs.

**Kind**: global variable
<a name="StateMetadataEncoding"></a>

## StateMetadataEncoding
**Kind**: global variable
<a name="MethodRelationship"></a>

## MethodRelationship
**Kind**: global variable
<a name="start"></a>

Expand Down
3 changes: 2 additions & 1 deletion bindings/wasm/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ impl_wasm_error_from!(
identity_iota::credential::JwtValidationError,
identity_iota::credential::RevocationError,
identity_iota::verification::Error,
identity_iota::credential::DomainLinkageValidationError
identity_iota::credential::DomainLinkageValidationError,
identity_iota::sd_jwt_payload::Error
);

// Similar to `impl_wasm_error_from`, but uses the types name instead of requiring/calling Into &'static str
Expand Down
1 change: 1 addition & 0 deletions bindings/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub mod iota;
pub mod jose;
pub mod resolver;
pub mod revocation;
pub mod sd_jwt;
pub mod storage;
pub mod verification;

Expand Down
41 changes: 41 additions & 0 deletions bindings/wasm/src/sd_jwt/decoder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2020-2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use crate::{
common::{ArrayString, RecordStringAny},
error::{Result, WasmResult},
};
use identity_iota::sd_jwt_payload::SdObjectDecoder;
use serde_json::{Map, Value};
use wasm_bindgen::prelude::*;

/// Substitutes digests in an SD-JWT object by their corresponding plaintext values provided by disclosures.
#[wasm_bindgen(js_name = SdObjectDecoder, inspectable)]
pub struct WasmSdObjectDecoder(pub(crate) SdObjectDecoder);

#[wasm_bindgen(js_class = SdObjectDecoder)]
impl WasmSdObjectDecoder {
/// Creates a new `SdObjectDecoder` with `sha-256` hasher.
#[wasm_bindgen(constructor)]
pub fn new() -> WasmSdObjectDecoder {
Self(SdObjectDecoder::new_with_sha256())
}

/// Decodes an SD-JWT `object` containing by Substituting the digests with their corresponding
/// plaintext values provided by `disclosures`.
///
/// ## Notes
/// * Claims like `exp` or `iat` are not validated in the process of decoding.
/// * `_sd_alg` property will be removed if present.
#[wasm_bindgen]
pub fn decode(&self, object: RecordStringAny, disclosures: ArrayString) -> Result<RecordStringAny> {
let object: Map<String, Value> = object.into_serde().wasm_result()?;
let disclosures: Vec<String> = disclosures.into_serde().wasm_result()?;
let decoded = self.0.decode(&object, &disclosures).wasm_result()?;
Ok(
JsValue::from_serde(&decoded)
.wasm_result()?
.unchecked_into::<RecordStringAny>(),
)
}
}
68 changes: 68 additions & 0 deletions bindings/wasm/src/sd_jwt/disclosure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2020-2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use crate::error::Result;
use crate::error::WasmResult;
use identity_iota::sd_jwt_payload::Disclosure;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;

/// Represents an elements constructing a disclosure.
/// Object properties and array elements disclosures are supported.
///
/// See: https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html#name-disclosures
#[wasm_bindgen(js_name = Disclosure, inspectable)]
pub struct WasmDisclosure(pub(crate) Disclosure);

#[wasm_bindgen(js_class = Disclosure)]
impl WasmDisclosure {
#[wasm_bindgen(constructor)]
pub fn new(salt: String, claim_name: Option<String>, claim_value: JsValue) -> Result<WasmDisclosure> {
Ok(Self(Disclosure::new(
salt,
claim_name,
claim_value.into_serde().wasm_result()?,
)))
}

/// Parses a Base64 encoded disclosure into a `Disclosure`.
///
/// ## Error
///
/// Returns an `InvalidDisclosure` if input is not a valid disclosure.
#[wasm_bindgen]
pub fn parse(disclosure: String) -> Result<WasmDisclosure> {
Ok(WasmDisclosure(Disclosure::parse(disclosure).wasm_result()?))
}

/// Returns a copy of the base64url-encoded string.
#[wasm_bindgen(js_name = disclosure)]
pub fn disclosure(&self) -> String {
self.0.disclosure.clone()
}

/// Returns a copy of the base64url-encoded string.
#[wasm_bindgen(js_name = toString)]
pub fn to_string(&self) -> String {
self.0.disclosure.clone()
}

/// Returns a copy of the salt value.
#[wasm_bindgen(js_name = salt)]
pub fn salt(&self) -> String {
self.0.salt.clone()
}

/// Returns a copy of the claim name, optional for array elements.
#[wasm_bindgen(js_name = claimName)]
pub fn claim_name(&self) -> Option<String> {
self.0.claim_name.clone()
}

/// Returns a copy of the claim Value which can be of any type.
#[wasm_bindgen(js_name = claimValue)]
pub fn claim_value(&self) -> Result<JsValue> {
JsValue::from_serde(&self.0.claim_value.clone()).wasm_result()
}
}
impl_wasm_json!(WasmDisclosure, Disclosure);
Loading
Loading