From d279b147d9c8963c7f67aaeaf679b036f6439d0d Mon Sep 17 00:00:00 2001 From: xevisalle Date: Tue, 10 Dec 2024 13:50:21 +0100 Subject: [PATCH 1/3] core: Add crate documentation --- core/CHANGELOG.md | 3 +- core/benches/license_circuit.rs | 4 +-- core/src/circuit.rs | 11 +++++-- core/src/error.rs | 1 - core/src/gadgets.rs | 44 ++++++++++++++-------------- core/src/lib.rs | 15 +++++++++- core/src/license.rs | 20 +++++++++---- core/src/request.rs | 10 +++++-- core/src/session.rs | 51 ++++++++++++++++++++++++--------- core/tests/citadel.rs | 6 ++-- 10 files changed, 113 insertions(+), 52 deletions(-) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index c9966f3..5bb7845 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -10,7 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add `circuit` module -- Add `LicenseCreator` +- Add `LicenseOrigin` +- Add crate documentation ### Changed diff --git a/core/benches/license_circuit.rs b/core/benches/license_circuit.rs index 9b5fb32..d0c2dd4 100644 --- a/core/benches/license_circuit.rs +++ b/core/benches/license_circuit.rs @@ -10,7 +10,7 @@ use dusk_poseidon::{Domain, Hash}; use phoenix_core::{PublicKey, SecretKey}; use poseidon_merkle::{Item, Tree}; -use zk_citadel::{circuit, gadgets, License, LicenseCreator, SessionCookie}; +use zk_citadel::{circuit, gadgets, License, LicenseOrigin, SessionCookie}; use criterion::{criterion_group, criterion_main, Criterion}; use rand_core::OsRng; @@ -58,7 +58,7 @@ fn license_circuit_benchmark(crit: &mut Criterion) { let lic = License::new( &attr_data, &sk_lp, - &LicenseCreator::FromPublicKey(pk), + &LicenseOrigin::FromPublicKey(pk), &mut OsRng, ) .expect("License correctly computed."); diff --git a/core/src/circuit.rs b/core/src/circuit.rs index 7bda089..43de021 100644 --- a/core/src/circuit.rs +++ b/core/src/circuit.rs @@ -8,9 +8,14 @@ use crate::{gadgets, SessionCookie}; use dusk_plonk::prelude::*; #[allow(dead_code)] -pub const CAPACITY: usize = 15; // capacity required for the setup -pub const DEPTH: usize = 16; // depth of the n-ary Merkle tree +/// The capacity required for the setup +pub const CAPACITY: usize = 15; +/// The depth of the n-ary Merkle tree +pub const DEPTH: usize = 16; + +/// A standard license circuit that simply uses the [`use_license`] +/// gadget with no additional operations #[derive(Default, Debug)] pub struct LicenseCircuit { gp: gadgets::GadgetParameters, @@ -18,6 +23,8 @@ pub struct LicenseCircuit { } impl LicenseCircuit { + /// A method to create a new [`LicenseCircuit`] from some [`GadgetParameters`] + /// and a [`SessionCookie`] pub fn new(gp: &gadgets::GadgetParameters, sc: &SessionCookie) -> Self { Self { gp: *gp, sc: *sc } } diff --git a/core/src/error.rs b/core/src/error.rs index 1148368..11c8158 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -7,7 +7,6 @@ use core::fmt; /// All possible errors for Citadel -#[allow(missing_docs)] #[allow(clippy::enum_variant_names)] #[derive(Debug, Clone)] pub enum Error { diff --git a/core/src/gadgets.rs b/core/src/gadgets.rs index e2bdd4b..b5f76b3 100644 --- a/core/src/gadgets.rs +++ b/core/src/gadgets.rs @@ -23,18 +23,18 @@ use crate::{license::LIC_PLAINTEXT_SIZE, License, SessionCookie}; use poseidon_merkle::zk::opening_gadget; -// out of this circuit, the generated public inputs vector collects -// these values in that particular order: -// -// public_inputs[0]: session_id -// public_inputs[1]: session_hash -// public_inputs[2]: com_0 -// public_inputs[3]: com_1.x -// public_inputs[4]: com_1.y -// public_inputs[5]: com_2.x -// public_inputs[6]: com_2.y -// public_inputs[7]: root - +/// The [`use_license`] gadget for the [`LicenseCircuit`]. It is meant +/// to use Citadel licenses onchain. Out of this circuit, the generated +/// public inputs vector collects these values in that particular order: +/// +/// session_id +/// session_hash +/// com_0 +/// com_1.x +/// com_1.y +/// com_2.x +/// com_2.y +/// root pub fn use_license( composer: &mut Composer, gp: &GadgetParameters, @@ -110,6 +110,7 @@ pub fn use_license( Ok(()) } +/// The parameters required by the [`use_license`] gadget #[cfg_attr( feature = "rkyv-impl", derive(Archive, Serialize, Deserialize), @@ -117,17 +118,17 @@ pub fn use_license( )] #[derive(Debug, Clone, Copy)] pub struct GadgetParameters { - pub lpk: JubJubAffine, // license public key - pub lpk_p: JubJubAffine, // license public key prime - pub sig_lic: Signature, // signature of the license + lpk: JubJubAffine, // license public key + lpk_p: JubJubAffine, // license public key prime + sig_lic: Signature, // signature of the license - pub com_0: BlsScalar, // Hash commitment 0 - pub com_1: JubJubExtended, // Pedersen Commitment 1 - pub com_2: JubJubExtended, // Pedersen Commitment 2 + com_0: BlsScalar, // Hash commitment 0 + com_1: JubJubExtended, // Pedersen Commitment 1 + com_2: JubJubExtended, // Pedersen Commitment 2 - pub session_hash: BlsScalar, // hash of the session - pub sig_session_hash: SignatureDouble, // signature of the session_hash - pub merkle_proof: Opening<(), DEPTH>, // Merkle proof for the Proof of Validity + session_hash: BlsScalar, // hash of the session + sig_session_hash: SignatureDouble, // signature of the session_hash + merkle_proof: Opening<(), DEPTH>, // Merkle proof for the Proof of Validity } impl Default for GadgetParameters { @@ -156,6 +157,7 @@ impl Default for GadgetParameters { } impl GadgetParameters { + /// Method to generate the [`GadgetParameters`] struct #[allow(clippy::too_many_arguments)] pub fn compute_parameters( sk: &SecretKey, diff --git a/core/src/lib.rs b/core/src/lib.rs index 5812ecc..1c94948 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,14 +4,27 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +//! This package contains the core implementation of Citadel, a self-sovereign identity protocol for decentralized networks. +//! +//! **DISCLAIMER**: this package **has not gone through an exhaustive security analysis**, +//! so it is not intended to be used in a production environment, only for academic purposes. + +#![deny(missing_docs)] + mod error; mod license; mod request; mod session; +/// The arithmetic circuit module to use licenses pub mod circuit; + +/// The gadget module required by the license circuit and / or +/// in third party circuits, to use licenses pub mod gadgets; -pub use license::{License, LicenseCreator}; +pub use license::{License, LicenseOrigin}; pub use request::Request; pub use session::{Session, SessionCookie}; + +pub use error::Error; diff --git a/core/src/license.rs b/core/src/license.rs index ae00ebd..d97332b 100644 --- a/core/src/license.rs +++ b/core/src/license.rs @@ -26,11 +26,16 @@ use crate::request::{Request, REQ_PLAINTEXT_SIZE}; pub(crate) const LIC_PLAINTEXT_SIZE: usize = Signature::SIZE + JubJubScalar::SIZE; const LIC_ENCRYPTION_SIZE: usize = LIC_PLAINTEXT_SIZE + ENCRYPTION_EXTRA_SIZE; -pub enum LicenseCreator { +/// Enumaration used to create new licenses +pub enum LicenseOrigin { + /// From a [`Request`] sent onchain FromRequest(Request), + /// From a [`PublicKey`] of a given user FromPublicKey(PublicKey), } +/// The struct defining a Citadel license, an asset that represents +/// the right of a user to use a specific service #[cfg_attr( feature = "rkyv-impl", derive(Archive, Serialize, Deserialize), @@ -38,19 +43,22 @@ pub enum LicenseCreator { )] #[derive(Debug, Clone)] pub struct License { - pub lsa: StealthAddress, // license stealth address - pub enc: [u8; LIC_ENCRYPTION_SIZE], // encryption of the license signature and attribute data + /// The stealth address of the license + pub lsa: StealthAddress, + /// The encryption of the license signature and the attribute data + pub enc: [u8; LIC_ENCRYPTION_SIZE], } impl License { + /// Method to generate a new [`License`] pub fn new( attr_data: &JubJubScalar, sk_lp: &SecretKey, - lc: &LicenseCreator, + lc: &LicenseOrigin, rng: &mut R, ) -> Result { let (lsa, k_lic) = match lc { - LicenseCreator::FromRequest(req) => { + LicenseOrigin::FromRequest(req) => { let k_dh = dhke(sk_lp.a(), req.rsa.R()); let dec: [u8; REQ_PLAINTEXT_SIZE] = decrypt(&k_dh, &req.enc)?; @@ -66,7 +74,7 @@ impl License { (lsa, k_lic) } - LicenseCreator::FromPublicKey(pk_user) => { + LicenseOrigin::FromPublicKey(pk_user) => { let r_dh = JubJubScalar::random(&mut *rng); let lsa = pk_user.gen_stealth_address(&r_dh); let k_lic = dhke(&r_dh, pk_user.A()); diff --git a/core/src/request.rs b/core/src/request.rs index 3c88490..ed64518 100644 --- a/core/src/request.rs +++ b/core/src/request.rs @@ -23,6 +23,9 @@ use dusk_plonk::prelude::*; pub(crate) const REQ_PLAINTEXT_SIZE: usize = StealthAddress::SIZE + JubJubAffine::SIZE; const REQ_ENCRYPTION_SIZE: usize = REQ_PLAINTEXT_SIZE + ENCRYPTION_EXTRA_SIZE; +/// The struct defining a Citadel request, a set of information that +/// a user sends to the network to inform a LP that the user is +/// requesting a license from them #[cfg_attr( feature = "rkyv-impl", derive(Archive, Serialize, Deserialize), @@ -30,11 +33,14 @@ const REQ_ENCRYPTION_SIZE: usize = REQ_PLAINTEXT_SIZE + ENCRYPTION_EXTRA_SIZE; )] #[derive(Debug)] pub struct Request { - pub rsa: StealthAddress, // request stealth address - pub enc: [u8; REQ_ENCRYPTION_SIZE], // encryption of the license stealth address and k_lic + /// The stealth address for the request + pub rsa: StealthAddress, + /// The encryption of the license stealth address and the k_lic symmetric key + pub enc: [u8; REQ_ENCRYPTION_SIZE], } impl Request { + /// Method to create a new [`Request`] given ther user keys and the public key of LP pub fn new( sk_user: &SecretKey, pk_user: &PublicKey, diff --git a/core/src/session.rs b/core/src/session.rs index 82431ca..1f42449 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -14,6 +14,8 @@ use rkyv::{Archive, Deserialize, Serialize}; use dusk_plonk::prelude::*; +/// The struct defining a Citadel session, a set of public values shared +/// onchain that represent the use of a service by an unknown user #[cfg_attr( feature = "rkyv-impl", derive(Archive, Serialize, Deserialize), @@ -21,15 +23,24 @@ use dusk_plonk::prelude::*; )] #[derive(Debug)] pub struct Session { + /// The hash of the public key of the SP plus a nonce pub session_hash: BlsScalar, + /// The ID of the [`Session`], computed as the hash of + /// the lpk' and the challenge pub session_id: BlsScalar, - - pub com_0: BlsScalar, // Hash commitment 0 - pub com_1: JubJubExtended, // Pedersen Commitment 1 - pub com_2: JubJubExtended, // Pedersen Commitment 2 + /// The hash commitment 0 from the license circuit, + /// that commits to the public key of the LP + pub com_0: BlsScalar, + /// The Pedersen commitment 1 from the license circuit, + /// that commits to the attribute data + pub com_1: JubJubExtended, + /// The Pedersen commitment 2 from the license circuit, + /// that commits to the challenge + pub com_2: JubJubExtended, } impl Session { + /// Method that generates a [`Session`] from a public inputs vector pub fn from(public_inputs: &[BlsScalar]) -> Self { let session_id = public_inputs[0]; let session_hash = public_inputs[1]; @@ -54,6 +65,8 @@ impl Session { } } + /// Method that verifies a [`SessionCookie`], by checking if all the + /// openings match the commitments of the given [`Session`] pub fn verify(&self, sc: SessionCookie) -> Result<(), Error> { let session_hash = Hash::digest(Domain::Other, &[sc.pk_sp.get_u(), sc.pk_sp.get_v(), sc.r])[0]; @@ -80,6 +93,9 @@ impl Session { } } +/// The struct defining a session cookie, a secret value +/// known only by the user and the SP, used to verify that +/// a given session is correct #[cfg_attr( feature = "rkyv-impl", derive(Archive, Serialize, Deserialize), @@ -87,15 +103,24 @@ impl Session { )] #[derive(Default, Debug, Clone, Copy)] pub struct SessionCookie { - pub pk_sp: JubJubAffine, // public key of the SP - pub r: BlsScalar, // randomness for session_hash + /// Public key of the SP + pub pk_sp: JubJubAffine, + /// Randomness for session_hash + pub r: BlsScalar, + /// The ID of the session pub session_id: BlsScalar, - pub pk_lp: JubJubAffine, // public key of the LP - pub attr_data: JubJubScalar, // attribute data of the license - pub c: JubJubScalar, // challenge value - - pub s_0: BlsScalar, // randomness for com_0 - pub s_1: JubJubScalar, // randomness for com_1 - pub s_2: JubJubScalar, // randomness for com_2 + /// Public key of the LP + pub pk_lp: JubJubAffine, + /// Attribute data of the license + pub attr_data: JubJubScalar, + /// Challenge value + pub c: JubJubScalar, + + /// Randomness for com_0 + pub s_0: BlsScalar, + /// Randomness for com_1 + pub s_1: JubJubScalar, + /// Randomness for com_2 + pub s_2: JubJubScalar, } diff --git a/core/tests/citadel.rs b/core/tests/citadel.rs index 533c68e..b1f10c7 100644 --- a/core/tests/citadel.rs +++ b/core/tests/citadel.rs @@ -12,7 +12,7 @@ use phoenix_core::{PublicKey, SecretKey}; use poseidon_merkle::{Item, Tree}; use rand_core::OsRng; -use zk_citadel::{circuit, gadgets, License, LicenseCreator, Request, Session, SessionCookie}; +use zk_citadel::{circuit, gadgets, License, LicenseOrigin, Request, Session, SessionCookie}; static LABEL: &[u8; 12] = b"dusk-network"; @@ -43,7 +43,7 @@ fn test_full_citadel() { let lic = License::new( &attr_data, &sk_lp, - &LicenseCreator::FromRequest(req), + &LicenseOrigin::FromRequest(req), &mut OsRng, ) .expect("License correctly computed from request."); @@ -122,7 +122,7 @@ fn test_full_citadel() { let lic_from_pk = License::new( &attr_data, &sk_lp, - &LicenseCreator::FromPublicKey(pk), + &LicenseOrigin::FromPublicKey(pk), &mut OsRng, ) .expect("License correctly computed from public key."); From 46af66bc4e0d79a0c3251a6c4a9de8456a943035 Mon Sep 17 00:00:00 2001 From: xevisalle Date: Tue, 10 Dec 2024 13:53:49 +0100 Subject: [PATCH 2/3] contract: Rename LicenseCreator to LicenseOrigin --- contract/tests/license_contract.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contract/tests/license_contract.rs b/contract/tests/license_contract.rs index 0cf050f..543e819 100644 --- a/contract/tests/license_contract.rs +++ b/contract/tests/license_contract.rs @@ -15,7 +15,7 @@ use execution_core::plonk::{Prover, Verifier}; use rand::rngs::StdRng; use rand::{CryptoRng, RngCore, SeedableRng}; use rkyv::{check_archived_root, Deserialize, Infallible}; -use zk_citadel::{circuit, gadgets, License, LicenseCreator, Request, SessionCookie}; +use zk_citadel::{circuit, gadgets, License, LicenseOrigin, Request, SessionCookie}; const PROVER_BYTES: &[u8] = include_bytes!("../../target/prover"); @@ -56,7 +56,7 @@ fn create_test_license( rng: &mut R, ) -> License { let request = Request::new(sk_user, pk_user, pk_lp, rng).unwrap(); - License::new(attr, sk_lp, &LicenseCreator::FromRequest(request), rng).unwrap() + License::new(attr, sk_lp, &LicenseOrigin::FromRequest(request), rng).unwrap() } fn initialize() -> Session { @@ -301,7 +301,7 @@ fn use_license_get_session() { let request = Request::new(&sk_user, &pk_user, &pk_lp, rng).expect("Request correctly created."); let attr = JubJubScalar::from(USER_ATTRIBUTES); - let license = License::new(&attr, &sk_lp, &LicenseCreator::FromRequest(request), rng).unwrap(); + let license = License::new(&attr, &sk_lp, &LicenseOrigin::FromRequest(request), rng).unwrap(); let license_blob = rkyv::to_bytes::<_, 4096>(&license) .expect("Request should serialize correctly") From 9dceb49988e8fed2656ca73fed5fc672cdf9a76a Mon Sep 17 00:00:00 2001 From: xevisalle Date: Wed, 11 Dec 2024 15:06:03 +0100 Subject: [PATCH 3/3] core: Minor improvements --- core/src/gadgets.rs | 2 +- core/src/lib.rs | 6 +++++- core/src/license.rs | 12 ++++++------ core/src/session.rs | 4 ++-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/core/src/gadgets.rs b/core/src/gadgets.rs index b5f76b3..fef2390 100644 --- a/core/src/gadgets.rs +++ b/core/src/gadgets.rs @@ -24,7 +24,7 @@ use crate::{license::LIC_PLAINTEXT_SIZE, License, SessionCookie}; use poseidon_merkle::zk::opening_gadget; /// The [`use_license`] gadget for the [`LicenseCircuit`]. It is meant -/// to use Citadel licenses onchain. Out of this circuit, the generated +/// to use Citadel licenses on-chain. Out of this circuit, the generated /// public inputs vector collects these values in that particular order: /// /// session_id diff --git a/core/src/lib.rs b/core/src/lib.rs index 1c94948..12f9451 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,7 +4,11 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -//! This package contains the core implementation of Citadel, a self-sovereign identity protocol for decentralized networks. +//! This package contains the core implementation of Citadel, a self-sovereign identity protocol for +//! decentralized networks, allowing users to receive licenses issued by license providers (LP), and +//! to use them to prove sensitive information in zero-knowledge to service providers (SP). +//! +//! Reference: https://arxiv.org/pdf/2301.09378 //! //! **DISCLAIMER**: this package **has not gone through an exhaustive security analysis**, //! so it is not intended to be used in a production environment, only for academic purposes. diff --git a/core/src/license.rs b/core/src/license.rs index d97332b..5abfef2 100644 --- a/core/src/license.rs +++ b/core/src/license.rs @@ -8,7 +8,7 @@ use dusk_bytes::Serializable; use dusk_jubjub::dhke; use dusk_poseidon::{Domain, Hash}; use ff::Field; -use jubjub_schnorr::{SecretKey as NoteSecretKey, Signature}; +use jubjub_schnorr::{SecretKey as LicenseSecretKey, Signature}; use phoenix_core::{ aes::{decrypt, encrypt, ENCRYPTION_EXTRA_SIZE}, Error, PublicKey, SecretKey, StealthAddress, @@ -26,9 +26,9 @@ use crate::request::{Request, REQ_PLAINTEXT_SIZE}; pub(crate) const LIC_PLAINTEXT_SIZE: usize = Signature::SIZE + JubJubScalar::SIZE; const LIC_ENCRYPTION_SIZE: usize = LIC_PLAINTEXT_SIZE + ENCRYPTION_EXTRA_SIZE; -/// Enumaration used to create new licenses +/// Enumeration used to create new licenses pub enum LicenseOrigin { - /// From a [`Request`] sent onchain + /// From a [`Request`] sent on-chain FromRequest(Request), /// From a [`PublicKey`] of a given user FromPublicKey(PublicKey), @@ -54,10 +54,10 @@ impl License { pub fn new( attr_data: &JubJubScalar, sk_lp: &SecretKey, - lc: &LicenseOrigin, + lo: &LicenseOrigin, rng: &mut R, ) -> Result { - let (lsa, k_lic) = match lc { + let (lsa, k_lic) = match lo { LicenseOrigin::FromRequest(req) => { let k_dh = dhke(sk_lp.a(), req.rsa.R()); let dec: [u8; REQ_PLAINTEXT_SIZE] = decrypt(&k_dh, &req.enc)?; @@ -89,7 +89,7 @@ impl License { Domain::Other, &[lpk.get_u(), lpk.get_v(), BlsScalar::from(*attr_data)], )[0]; - let sig_lic = NoteSecretKey::from(sk_lp.a()).sign(rng, message); + let sig_lic = LicenseSecretKey::from(sk_lp.a()).sign(rng, message); let mut plaintext = sig_lic.to_bytes().to_vec(); plaintext.append(&mut attr_data.to_bytes().to_vec()); diff --git a/core/src/session.rs b/core/src/session.rs index 1f42449..f003f3f 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -15,7 +15,7 @@ use rkyv::{Archive, Deserialize, Serialize}; use dusk_plonk::prelude::*; /// The struct defining a Citadel session, a set of public values shared -/// onchain that represent the use of a service by an unknown user +/// on-chain that represent the use of a service by an unknown user #[cfg_attr( feature = "rkyv-impl", derive(Archive, Serialize, Deserialize), @@ -23,7 +23,7 @@ use dusk_plonk::prelude::*; )] #[derive(Debug)] pub struct Session { - /// The hash of the public key of the SP plus a nonce + /// The hash of the public key of the SP and a nonce pub session_hash: BlsScalar, /// The ID of the [`Session`], computed as the hash of /// the lpk' and the challenge