Skip to content

Commit f25c593

Browse files
UMR1352AlbertoSvgwulfraem
authored
Add ZK BBS+-based selectively disclosable credentials (JPT) (#1355)
* Support BBS+ and JWP (#1285) * merge main * Wasm bindings for Jpt credentials * JPT presentation bindings * docs * jsonprooftoken payloads * Refactor `RevocationTimeframeStatus` to align with other setups (#1340) * refactor `RevocationTimeframeStatus` to other setups * fix smaller typos * binding coverage for jsonprooftoken * Use latest releases of zkryptium/json-proof-token and add new BLS key representation (#1339) * update zkryptium/json-proof-token deps and new BLS key representation * minor fix * Use zkryptium for cryptographic operations inside Memstore (#1351) * update zkryptium/json-proof-token deps and new BLS key representation * minor fix * use zkryptium for crypto operations and JPT for serialization * fix format * Feat/jpt bbs+ sd stronghold impl (#1354) * Implement JwkStorageExt for StrongholdStorage * reorganize code * persist changes to stronghold when creating bbs+ keypair, clippy, fmt * feature gate * zkp wasm example * zkp_revocation wasm example * wasm bindings * fix docs * rename JwkStorageExt to JwkStorageBbsPlusExt * JwkStorageBbsPlusExt impl refactor for Stronghold, MemStore, WasmStore * Squashed commit of the following: commit 30c9bf2 Author: Foorack / Max Faxälv <max@foorack.com> Date: Tue Apr 2 10:32:48 2024 +0200 inherit `repository` in identity_verification (#1348) commit 1e9c9a3 Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed Mar 27 15:35:29 2024 +0100 Release wasm-v1.2.0 (#1345) commit 84a630d Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed Mar 27 15:32:19 2024 +0100 Release v1.2.0 (#1347) commit 1aba4b5 Author: Eike Haß <eike-hass@web.de> Date: Wed Mar 27 13:13:27 2024 +0100 removed dev_dep version commit 0352b84 Author: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Wed Mar 27 10:44:43 2024 +0100 Support %-encoded characters in DID method id (#1303) commit e68538f Author: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Tue Mar 26 11:58:35 2024 +0100 gRPC bindings (#1264) commit e53561e Author: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Tue Mar 26 11:18:14 2024 +0100 allow large result err variants (#1342) commit 4a144a3 Author: Eike Haß <eike-hass@web.de> Date: Tue Mar 19 09:51:52 2024 +0100 fix readme links (#1336) commit 0af29fc Author: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Mon Mar 18 17:16:57 2024 +0100 Feat/custom verification method (#1334) * Add support for arbitrary (custom) verification method data * wasm bindings * custom method type + wasm * workaround serde's issue * Update bindings/wasm/src/verification/wasm_method_data.rs Co-authored-by: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> * review comments * fmt * review comment --------- Co-authored-by: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> commit edb9150 Author: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Tue Mar 12 14:45:04 2024 +0100 use latest release of sd-jwt-payload (#1333) * use latest release of sd-jwt-payload * make clippy happy commit 0794379 Author: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> Date: Wed Mar 6 14:16:00 2024 +0100 Wasm bindings for `BlockChainAccountId` verification method. (#1326) commit 59d38f7 Author: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> Date: Wed Mar 6 10:56:23 2024 +0100 Add constructor for VerificationMethod in TS (#1321) * clippy * fmt * add stronghold bbs+ tests * review comments * add license header * fix wasm bindings * Persist Stronghold's changes only when its handle is dropped * Fix StrongholdStorage::get_public_key * rename stronghold_jwk_storage_ext * Add inx-faucet profile in CI * change stronghold crate's structure, revert persist changes on drop * review comments * Update identity_credential/src/presentation/jwp_presentation_builder.rs Co-authored-by: wulfraem <wulfraem@users.noreply.github.com> * fix wasm bindings * expose stronghold's key types * revert last commit * Add "Fondazione Links" to license header * Squashed commit of the following: commit 9abdb38 Author: Sven <sven.feuchtmueller@gmx.de> Date: Tue May 14 09:16:09 2024 +0200 Add EcDSA verifier (#1353) * add ecdsa verifier * add identity_ecdsa_verifier to workspace, add license headers * Update identity_ecdsa_verifier/Cargo.toml Co-authored-by: wulfraem <wulfraem@users.noreply.github.com> * Update identity_ecdsa_verifier/src/secp256k1.rs Co-authored-by: wulfraem <wulfraem@users.noreply.github.com> * Update identity_ecdsa_verifier/Cargo.toml Co-authored-by: wulfraem <wulfraem@users.noreply.github.com> * Update identity_ecdsa_verifier/src/secp256k1.rs Co-authored-by: wulfraem <wulfraem@users.noreply.github.com> * Update identity_ecdsa_verifier/src/secp256r1.rs Co-authored-by: wulfraem <wulfraem@users.noreply.github.com> * add feedback * add OpenSSL installation to windows runner in CI * update license headers and authors for ecdsa verifier * update license template to allow multiple contributors --------- Co-authored-by: Sebastian Wolfram <wulfraem@users.noreply.github.com> commit 149bfac Author: wulfraem <wulfraem@users.noreply.github.com> Date: Mon May 13 10:44:09 2024 +0200 Fix findings after clippy update (#1365) * fix clippy findings * fix formatting * refactor .clone_into calls into .to_string * fix previous edit * disable empty_docs for wasm binding for now * fix missing newline * disable self update from rust setup in ci for now * update self update skip to skip only for windows build commit 51aedd5 Author: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Tue Apr 30 16:16:36 2024 +0200 Use STRONGHOLD_PWD_FILE env variable to pass stronghold's password (#1363) commit edec26c Author: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Tue Apr 30 15:40:55 2024 +0200 Arbitrary data signing service (#1350) commit f59e75a Author: Eike Haß <eike-hass@web.de> Date: Tue Apr 30 15:34:40 2024 +0200 Fix dockerhub workflow (#1343) commit 993cfec Author: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Fri Apr 26 13:39:29 2024 +0200 add inx-faucet profile (#1356) * update stronghold and sdk --------- Co-authored-by: Alberto Solavagione <albertosolavagione30@gmail.com> Co-authored-by: wulfraem <wulfraem@users.noreply.github.com>
1 parent b634ead commit f25c593

File tree

115 files changed

+8084
-413
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+8084
-413
lines changed

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ serde = { version = "1.0", default-features = false, features = ["alloc", "deriv
2424
thiserror = { version = "1.0", default-features = false }
2525
strum = { version = "0.25", default-features = false, features = ["std", "derive"] }
2626
serde_json = { version = "1.0", default-features = false }
27+
json-proof-token = { version = "0.3.5" }
28+
zkryptium = { version = "0.2.2", default-features = false, features = ["bbsplus"] }
2729

2830
[workspace.package]
2931
authors = ["IOTA Stiftung"]

bindings/grpc/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ futures = { version = "0.3" }
2222
identity_eddsa_verifier = { path = "../../identity_eddsa_verifier" }
2323
identity_iota = { path = "../../identity_iota", features = ["resolver", "sd-jwt", "domain-linkage", "domain-linkage-fetch", "status-list-2021"] }
2424
identity_stronghold = { path = "../../identity_stronghold", features = ["send-sync-storage"] }
25-
iota-sdk = { version = "1.1.2", features = ["stronghold"] }
25+
iota-sdk = { version = "1.1.5", features = ["stronghold"] }
2626
openssl = { version = "0.10", features = ["vendored"] }
2727
prost = "0.12"
2828
rand = "0.8.5"

bindings/wasm/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ console_error_panic_hook = { version = "0.1" }
2121
futures = { version = "0.3" }
2222
identity_eddsa_verifier = { path = "../../identity_eddsa_verifier", default-features = false, features = ["ed25519"] }
2323
js-sys = { version = "0.3.61" }
24+
json-proof-token = "0.3.4"
2425
proc_typescript = { version = "0.1.0", path = "./proc_typescript" }
2526
serde = { version = "1.0", features = ["derive"] }
2627
serde_json = { version = "1.0", default-features = false }
@@ -29,11 +30,12 @@ serde_repr = { version = "0.1", default-features = false }
2930
tokio = { version = "1.29", default-features = false, features = ["sync"] }
3031
wasm-bindgen = { version = "0.2.85", features = ["serde-serialize"] }
3132
wasm-bindgen-futures = { version = "0.4", default-features = false }
33+
zkryptium = "0.2.2"
3234

3335
[dependencies.identity_iota]
3436
path = "../../identity_iota"
3537
default-features = false
36-
features = ["client", "revocation-bitmap", "resolver", "domain-linkage", "sd-jwt", "status-list-2021"]
38+
features = ["client", "revocation-bitmap", "resolver", "domain-linkage", "sd-jwt", "status-list-2021", "jpt-bbs-plus"]
3739

3840
[dev-dependencies]
3941
rand = "0.8.5"

bindings/wasm/docs/api-reference.md

+1,501-163
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
import {
2+
Credential,
3+
FailFast,
4+
IotaDID,
5+
IotaDocument,
6+
IotaIdentityClient,
7+
JptCredentialValidationOptions,
8+
JptCredentialValidator,
9+
JptCredentialValidatorUtils,
10+
JptPresentationValidationOptions,
11+
JptPresentationValidator,
12+
JptPresentationValidatorUtils,
13+
JwkMemStore,
14+
JwpCredentialOptions,
15+
JwpPresentationOptions,
16+
KeyIdMemStore,
17+
MethodScope,
18+
ProofAlgorithm,
19+
SelectiveDisclosurePresentation,
20+
Storage,
21+
} from "@iota/identity-wasm/node";
22+
import {
23+
type Address,
24+
AliasOutput,
25+
Client,
26+
MnemonicSecretManager,
27+
SecretManager,
28+
SecretManagerType,
29+
Utils,
30+
} from "@iota/sdk-wasm/node";
31+
import { API_ENDPOINT, ensureAddressHasFunds } from "../util";
32+
33+
/** Creates a DID Document and publishes it in a new Alias Output.
34+
35+
Its functionality is equivalent to the "create DID" example
36+
and exists for convenient calling from the other examples. */
37+
export async function createDid(client: Client, secretManager: SecretManagerType, storage: Storage): Promise<{
38+
address: Address;
39+
document: IotaDocument;
40+
fragment: string;
41+
}> {
42+
const didClient = new IotaIdentityClient(client);
43+
const networkHrp: string = await didClient.getNetworkHrp();
44+
45+
const secretManagerInstance = new SecretManager(secretManager);
46+
const walletAddressBech32 = (await secretManagerInstance.generateEd25519Addresses({
47+
accountIndex: 0,
48+
range: {
49+
start: 0,
50+
end: 1,
51+
},
52+
bech32Hrp: networkHrp,
53+
}))[0];
54+
55+
console.log("Wallet address Bech32:", walletAddressBech32);
56+
57+
await ensureAddressHasFunds(client, walletAddressBech32);
58+
59+
const address: Address = Utils.parseBech32Address(walletAddressBech32);
60+
61+
// Create a new DID document with a placeholder DID.
62+
// The DID will be derived from the Alias Id of the Alias Output after publishing.
63+
const document = new IotaDocument(networkHrp);
64+
65+
const fragment = await document.generateMethodJwp(
66+
storage,
67+
ProofAlgorithm.BLS12381_SHA256,
68+
undefined,
69+
MethodScope.VerificationMethod(),
70+
);
71+
// Construct an Alias Output containing the DID document, with the wallet address
72+
// set as both the state controller and governor.
73+
const aliasOutput: AliasOutput = await didClient.newDidOutput(address, document);
74+
75+
// Publish the Alias Output and get the published DID document.
76+
const published = await didClient.publishDidOutput(secretManager, aliasOutput);
77+
78+
return { address, document: published, fragment };
79+
}
80+
export async function zkp() {
81+
// ===========================================================================
82+
// Step 1: Create identity for the issuer.
83+
// ===========================================================================
84+
85+
// Create a new client to interact with the IOTA ledger.
86+
const client = new Client({
87+
primaryNode: API_ENDPOINT,
88+
localPow: true,
89+
});
90+
91+
// Creates a new wallet and identity (see "0_create_did" example).
92+
const issuerSecretManager: MnemonicSecretManager = {
93+
mnemonic: Utils.generateMnemonic(),
94+
};
95+
const issuerStorage: Storage = new Storage(
96+
new JwkMemStore(),
97+
new KeyIdMemStore(),
98+
);
99+
let { document: issuerDocument, fragment: issuerFragment } = await createDid(
100+
client,
101+
issuerSecretManager,
102+
issuerStorage,
103+
);
104+
105+
// ===========================================================================
106+
// Step 2: Issuer creates and signs a Verifiable Credential with BBS algorithm.
107+
// ===========================================================================
108+
109+
// Create a credential subject indicating the degree earned by Alice.
110+
const subject = {
111+
name: "Alice",
112+
mainCourses: ["Object-oriented Programming", "Mathematics"],
113+
degree: {
114+
type: "BachelorDegree",
115+
name: "Bachelor of Science and Arts",
116+
},
117+
GPA: 4.0,
118+
};
119+
120+
// Build credential using the above subject and issuer.
121+
const credential = new Credential({
122+
id: "https:/example.edu/credentials/3732",
123+
issuer: issuerDocument.id(),
124+
type: "UniversityDegreeCredential",
125+
credentialSubject: subject,
126+
});
127+
const credentialJpt = await issuerDocument
128+
.createCredentialJpt(
129+
credential,
130+
issuerStorage,
131+
issuerFragment,
132+
new JwpCredentialOptions(),
133+
);
134+
// Validate the credential's proof using the issuer's DID Document, the credential's semantic structure,
135+
// that the issuance date is not in the future and that the expiration date is not in the past:
136+
const decodedJpt = JptCredentialValidator.validate(
137+
credentialJpt,
138+
issuerDocument,
139+
new JptCredentialValidationOptions(),
140+
FailFast.FirstError,
141+
);
142+
143+
// ===========================================================================
144+
// Step 3: Issuer sends the Verifiable Credential to the holder.
145+
// ===========================================================================
146+
console.log("Sending credential (as JPT) to the holder: " + credentialJpt.toString());
147+
148+
// ============================================================================================
149+
// Step 4: Holder resolve Issuer's DID, retrieve Issuer's document and validate the Credential
150+
// ============================================================================================
151+
const identityClient = new IotaIdentityClient(client);
152+
153+
// Holder resolves issuer's DID.
154+
let issuerDid = IotaDID.parse(JptCredentialValidatorUtils.extractIssuerFromIssuedJpt(credentialJpt).toString());
155+
let issuerDoc = await identityClient.resolveDid(issuerDid);
156+
157+
// Holder validates the credential and retrieve the JwpIssued, needed to construct the JwpPresented
158+
let decodedCredential = JptCredentialValidator.validate(
159+
credentialJpt,
160+
issuerDoc,
161+
new JptCredentialValidationOptions(),
162+
FailFast.FirstError,
163+
);
164+
165+
// ===========================================================================
166+
// Step 5: Verifier sends the holder a challenge and requests a Presentation.
167+
//
168+
// Please be aware that when we mention "Presentation," we are not alluding to the Verifiable Presentation standard as defined by W3C (https://www.w3.org/TR/vc-data-model/#presentations).
169+
// Instead, our reference is to a JWP Presentation (https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-proof#name-presented-form), which differs from the W3C standard.
170+
// ===========================================================================
171+
172+
// A unique random challenge generated by the requester per presentation can mitigate replay attacks.
173+
const challenge = "475a7984-1bb5-4c4c-a56f-822bccd46440";
174+
175+
// =========================================================================================================
176+
// Step 6: Holder engages in the Selective Disclosure of credential's attributes.
177+
// =========================================================================================================
178+
const methodId = decodedCredential
179+
.decodedJwp()
180+
.getIssuerProtectedHeader()
181+
.kid!;
182+
const selectiveDisclosurePresentation = new SelectiveDisclosurePresentation(decodedCredential.decodedJwp());
183+
selectiveDisclosurePresentation.concealInSubject("mainCourses[1]");
184+
selectiveDisclosurePresentation.concealInSubject("degree.name");
185+
186+
// =======================================================================================================================================
187+
// Step 7: Holder needs Issuer's Public Key to compute the Signature Proof of Knowledge and construct the Presentation
188+
// JPT.
189+
// =======================================================================================================================================
190+
191+
// Construct a JPT(JWP in the Presentation form) representing the Selectively Disclosed Verifiable Credential
192+
const presentationOptions = new JwpPresentationOptions();
193+
presentationOptions.nonce = challenge;
194+
const presentationJpt = await issuerDoc
195+
.createPresentationJpt(
196+
selectiveDisclosurePresentation,
197+
methodId,
198+
presentationOptions,
199+
);
200+
201+
// ===========================================================================
202+
// Step 8: Holder sends a Presentation JPT to the Verifier.
203+
// ===========================================================================
204+
205+
console.log("Sending presentation (as JPT) to the verifier: " + presentationJpt.toString());
206+
207+
// ===========================================================================
208+
// Step 9: Verifier receives the Presentation and verifies it.
209+
// ===========================================================================
210+
211+
// Verifier resolve Issuer DID
212+
const issuerDidV = IotaDID.parse(
213+
JptPresentationValidatorUtils.extractIssuerFromPresentedJpt(presentationJpt).toString(),
214+
);
215+
const issuerDocV = await identityClient.resolveDid(issuerDidV);
216+
217+
const presentationValidationOptions = new JptPresentationValidationOptions({ nonce: challenge });
218+
const decodedPresentedCredential = JptPresentationValidator.validate(
219+
presentationJpt,
220+
issuerDocV,
221+
presentationValidationOptions,
222+
FailFast.FirstError,
223+
);
224+
225+
console.log("Presented credential successfully validated: " + decodedPresentedCredential.credential());
226+
}

0 commit comments

Comments
 (0)