From f687ebb0f5505ade07cdc6193968d3fa54cd86e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Thu, 8 Feb 2024 15:01:58 +0000 Subject: [PATCH] persisting the issued credentials --- Cargo.lock | 2 + .../bandwidth-controller/src/acquire/mod.rs | 31 +++++---- common/bandwidth-controller/src/lib.rs | 7 +- common/bandwidth-controller/src/utils.rs | 2 +- common/credential-storage/Cargo.toml | 4 +- .../20240206120000_add_credential_types.sql | 12 ++++ .../credential-storage/src/backends/memory.rs | 65 ++++++++++--------- .../credential-storage/src/backends/sqlite.rs | 50 +++++--------- .../src/ephemeral_storage.rs | 42 +++++------- common/credential-storage/src/models.rs | 47 ++++++++------ .../src/persistent_storage.rs | 37 ++++------- common/credential-storage/src/storage.rs | 26 +------- common/credential-utils/src/utils.rs | 5 +- common/credentials/Cargo.toml | 2 +- .../src/coconut/bandwidth/freepass.rs | 2 +- .../src/coconut/bandwidth/issuance.rs | 8 +++ .../src/coconut/bandwidth/issued.rs | 48 ++++++++++++-- nym-connect/desktop/Cargo.lock | 1 + sdk/rust/nym-sdk/src/bandwidth/client.rs | 10 ++- sdk/rust/nym-sdk/src/mixnet.rs | 4 +- 20 files changed, 210 insertions(+), 195 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb1d3f96cc..6a90787a0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5343,6 +5343,7 @@ dependencies = [ "sqlx", "thiserror", "tokio", + "zeroize", ] [[package]] @@ -5543,6 +5544,7 @@ dependencies = [ "sqlx", "subtle-encoding", "thiserror", + "time", "tokio", "tokio-stream", "tokio-tungstenite", diff --git a/common/bandwidth-controller/src/acquire/mod.rs b/common/bandwidth-controller/src/acquire/mod.rs index 5b842fc775..b61bfc2ad4 100644 --- a/common/bandwidth-controller/src/acquire/mod.rs +++ b/common/bandwidth-controller/src/acquire/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::BandwidthControllerError; -use nym_coconut::Base58; +use nym_credential_storage::models::StorableIssuedCredential; use nym_credential_storage::storage::Storage; use nym_credentials::coconut::bandwidth::{CredentialType, IssuanceBandwidthCredential}; use nym_credentials::coconut::utils::obtain_aggregate_signature; @@ -13,6 +13,7 @@ use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::Coin; use rand::rngs::OsRng; use state::State; +use zeroize::Zeroizing; pub mod state; @@ -43,7 +44,7 @@ where Ok(state) } -pub async fn get_credential( +pub async fn get_bandwidth_voucher( state: &State, client: &C, storage: &St, @@ -54,7 +55,7 @@ where ::StorageError: Send + Sync + 'static, { // temporary - assert!(!state.voucher.typ().is_free_pass()); + assert!(state.voucher.typ().is_voucher()); let epoch_id = client.get_current_epoch().await?.epoch_id; let threshold = client @@ -66,19 +67,21 @@ where let signature = obtain_aggregate_signature(&state.voucher, &coconut_api_clients, threshold).await?; + let issued = state.voucher.to_issued_credential(signature); + + // make sure the data gets zeroized after persisting it + let credential_data = Zeroizing::new(issued.pack_v1()); + let storable = StorableIssuedCredential { + serialization_revision: issued.current_serialization_revision(), + credential_data: credential_data.as_ref(), + credential_type: issued.typ().to_string(), + epoch_id: epoch_id + .try_into() + .expect("our epoch is has run over u32::MAX!"), + }; - // we asserted the that the bandwidth credential we obtained is **NOT** the free pass - // so the first public attribute must be the value - let voucher_value = state.voucher.get_plain_public_attributes()[0].clone(); storage - .insert_coconut_credential( - voucher_value, - CredentialType::Voucher.to_string(), - state.voucher.get_private_attributes()[0].to_bs58(), - state.voucher.get_private_attributes()[1].to_bs58(), - signature.to_bs58(), - epoch_id.to_string(), - ) + .insert_issued_credential(storable) .await .map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err))) } diff --git a/common/bandwidth-controller/src/lib.rs b/common/bandwidth-controller/src/lib.rs index 2902d93451..3fa91802ee 100644 --- a/common/bandwidth-controller/src/lib.rs +++ b/common/bandwidth-controller/src/lib.rs @@ -1,10 +1,9 @@ -// Copyright 2021-2023 - Nym Technologies SA +// Copyright 2021-2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::error::BandwidthControllerError; use crate::utils::stored_credential_to_issued_bandwidth; use log::{error, warn}; -use nym_credential_storage::error::StorageError; use nym_credential_storage::storage::Storage; use nym_credentials::coconut::bandwidth::CredentialSpendingData; use nym_credentials::coconut::utils::obtain_aggregate_verification_key; @@ -12,7 +11,6 @@ use nym_credentials_interface::VerificationKey; use nym_validator_client::coconut::all_coconut_api_clients; use nym_validator_client::nym_api::EpochId; use nym_validator_client::nyxd::contract_traits::DkgQueryClient; -use std::str::FromStr; pub mod acquire; pub mod error; @@ -69,8 +67,7 @@ impl BandwidthController { .await .map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?; - let epoch_id = u64::from_str(&retrieved_credential.epoch_id) - .map_err(|_| StorageError::InconsistentData)?; + let epoch_id = retrieved_credential.epoch_id as EpochId; let credential_id = retrieved_credential.id; let issued_bandwidth = stored_credential_to_issued_bandwidth(retrieved_credential)?; diff --git a/common/bandwidth-controller/src/utils.rs b/common/bandwidth-controller/src/utils.rs index 7f24f8e141..690466daa5 100644 --- a/common/bandwidth-controller/src/utils.rs +++ b/common/bandwidth-controller/src/utils.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::BandwidthControllerError; -use nym_credential_storage::models::StoredIssuedCredential; +use nym_credential_storage::models::{StorableIssuedCredential, StoredIssuedCredential}; use nym_credentials::coconut::bandwidth::IssuedBandwidthCredential; use nym_validator_client::nym_api::EpochId; diff --git a/common/credential-storage/Cargo.toml b/common/credential-storage/Cargo.toml index 9beb030e22..d478759ee7 100644 --- a/common/credential-storage/Cargo.toml +++ b/common/credential-storage/Cargo.toml @@ -11,7 +11,9 @@ async-trait = { workspace = true } log = { workspace = true } thiserror = { workspace = true } -tokio = { version = "1.24.1", features = ["sync"]} +tokio = { workspace = true, features = ["sync"]} +zeroize = { workspace = true, features = ["zeroize_derive"] } + [target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx] workspace = true diff --git a/common/credential-storage/migrations/20240206120000_add_credential_types.sql b/common/credential-storage/migrations/20240206120000_add_credential_types.sql index a72048344f..a75acca881 100644 --- a/common/credential-storage/migrations/20240206120000_add_credential_types.sql +++ b/common/credential-storage/migrations/20240206120000_add_credential_types.sql @@ -3,3 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +DROP TABLE coconut_credentials; +CREATE TABLE coconut_credentials +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + +-- introduce a way for us to introduce breaking changes in serialization + serialization_revision INTEGER NOT NULL, + credential_type TEXT NOT NULL, + credential_data BLOB NOT NULL, + epoch_id TEXT NOT NULL, + consumed BOOLEAN NOT NULL +); \ No newline at end of file diff --git a/common/credential-storage/src/backends/memory.rs b/common/credential-storage/src/backends/memory.rs index 2dbdff0925..80538337c8 100644 --- a/common/credential-storage/src/backends/memory.rs +++ b/common/credential-storage/src/backends/memory.rs @@ -1,59 +1,60 @@ -// Copyright 2023 - Nym Technologies SA +// Copyright 2023-2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::models::CoconutCredential; +use crate::models::StoredIssuedCredential; use std::sync::Arc; use tokio::sync::RwLock; #[derive(Clone)] pub struct CoconutCredentialManager { - inner: Arc>>, + inner: Arc>, +} + +#[derive(Default)] +struct CoconutCredentialManagerInner { + data: Vec, + _next_id: i64, +} + +impl CoconutCredentialManagerInner { + fn next_id(&mut self) -> i64 { + let next = self._next_id; + self._next_id += 1; + next + } } impl CoconutCredentialManager { /// Creates new empty instance of the `CoconutCredentialManager`. pub fn new() -> Self { CoconutCredentialManager { - inner: Arc::new(RwLock::new(Vec::new())), + inner: Default::default(), } } - /// Inserts provided signature into the database. - /// - /// # Arguments - /// - /// * `voucher_value`: Plaintext bandwidth value of the credential. - /// * `voucher_info`: Plaintext information of the credential. - /// * `serial_number`: Base58 representation of the serial number attribute. - /// * `binding_number`: Base58 representation of the binding number attribute. - /// * `signature`: Coconut credential in the form of a signature. - pub async fn insert_coconut_credential( + pub async fn insert_issued_credential( &self, - voucher_value: String, - voucher_info: String, - serial_number: String, - binding_number: String, - signature: String, - epoch_id: String, + credential_type: String, + serialization_revision: u8, + credential_data: &[u8], + epoch_id: u32, ) { - let mut creds = self.inner.write().await; - let id = creds.len() as i64; - creds.push(CoconutCredential { + let mut inner = self.inner.write().await; + let id = inner.next_id(); + inner.data.push(StoredIssuedCredential { id, - voucher_value, - voucher_info, - serial_number, - binding_number, - signature, + serialization_revision, + credential_data: credential_data.to_vec(), + credential_type, epoch_id, consumed: false, - }); + }) } /// Tries to retrieve one of the stored, unused credentials. - pub async fn get_next_coconut_credential(&self) -> Option { + pub async fn get_next_unspent_credential(&self) -> Option { let creds = self.inner.read().await; - creds.iter().find(|c| !c.consumed).cloned() + creds.data.iter().find(|c| !c.consumed).cloned() } /// Consumes in the database the specified credential. @@ -63,7 +64,7 @@ impl CoconutCredentialManager { /// * `id`: Database id. pub async fn consume_coconut_credential(&self, id: i64) { let mut creds = self.inner.write().await; - if let Some(cred) = creds.get_mut(id as usize) { + if let Some(cred) = creds.data.get_mut(id as usize) { cred.consumed = true; } } diff --git a/common/credential-storage/src/backends/sqlite.rs b/common/credential-storage/src/backends/sqlite.rs index f64ef811b0..d007c03c16 100644 --- a/common/credential-storage/src/backends/sqlite.rs +++ b/common/credential-storage/src/backends/sqlite.rs @@ -1,7 +1,7 @@ -// Copyright 2022 - Nym Technologies SA +// Copyright 2022-2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::models::CoconutCredential; +use crate::models::StoredIssuedCredential; #[derive(Clone)] pub struct CoconutCredentialManager { @@ -18,43 +18,29 @@ impl CoconutCredentialManager { CoconutCredentialManager { connection_pool } } - /// Inserts provided signature into the database. - /// - /// # Arguments - /// - /// * `voucher_value`: Plaintext bandwidth value of the credential. - /// * `voucher_info`: Plaintext information of the credential. - /// * `serial_number`: Base58 representation of the serial number attribute. - /// * `binding_number`: Base58 representation of the binding number attribute. - /// * `signature`: Coconut credential in the form of a signature. - pub async fn insert_coconut_credential( + pub async fn insert_issued_credential( &self, - voucher_value: String, - voucher_info: String, - serial_number: String, - binding_number: String, - signature: String, - epoch_id: String, + credential_type: String, + serialization_revision: u8, + credential_data: &[u8], + epoch_id: u32, ) -> Result<(), sqlx::Error> { sqlx::query!( - "INSERT INTO coconut_credentials(voucher_value, voucher_info, serial_number, binding_number, signature, epoch_id, consumed) VALUES (?, ?, ?, ?, ?, ?, ?)", - voucher_value, voucher_info, serial_number, binding_number, signature, epoch_id, false - ) - .execute(&self.connection_pool) - .await?; + r#" + INSERT INTO coconut_credentials(serialization_revision, credential_type, credential_data, epoch_id, consumed) + VALUES (?, ?, ?, ?, false) + "#, + serialization_revision, credential_type, credential_data, epoch_id + ).execute(&self.connection_pool).await?; Ok(()) } - /// Tries to retrieve one of the stored, unused credentials. - pub async fn get_next_coconut_credential( + pub async fn get_next_unspent_credential( &self, - ) -> Result, sqlx::Error> { - sqlx::query_as!( - CoconutCredential, - "SELECT * FROM coconut_credentials WHERE NOT consumed" - ) - .fetch_optional(&self.connection_pool) - .await + ) -> Result, sqlx::Error> { + sqlx::query_as("SELECT * FROM coconut_credentials WHERE NOT consumed LIMIT 1") + .fetch_optional(&self.connection_pool) + .await } /// Consumes in the database the specified credential. diff --git a/common/credential-storage/src/ephemeral_storage.rs b/common/credential-storage/src/ephemeral_storage.rs index 9a4dd561ba..aa6756ac78 100644 --- a/common/credential-storage/src/ephemeral_storage.rs +++ b/common/credential-storage/src/ephemeral_storage.rs @@ -3,7 +3,7 @@ use crate::backends::memory::CoconutCredentialManager; use crate::error::StorageError; -use crate::models::{CoconutCredential, StoredIssuedCredential}; +use crate::models::{StorableIssuedCredential, StoredIssuedCredential}; use crate::storage::Storage; use async_trait::async_trait; @@ -27,43 +27,31 @@ impl Default for EphemeralStorage { impl Storage for EphemeralStorage { type StorageError = StorageError; - async fn insert_coconut_credential( + async fn insert_issued_credential<'a>( &self, - voucher_value: String, - voucher_info: String, - serial_number: String, - binding_number: String, - signature: String, - epoch_id: String, + bandwidth_credential: StorableIssuedCredential<'a>, ) -> Result<(), StorageError> { self.coconut_credential_manager - .insert_coconut_credential( - voucher_value, - voucher_info, - serial_number, - binding_number, - signature, - epoch_id, + .insert_issued_credential( + bandwidth_credential.credential_type, + bandwidth_credential.serialization_revision, + bandwidth_credential.credential_data, + bandwidth_credential.epoch_id, ) .await; - Ok(()) } - async fn get_next_coconut_credential(&self) -> Result { - let credential = self - .coconut_credential_manager - .get_next_coconut_credential() - .await - .ok_or(StorageError::NoCredential)?; - - Ok(credential) - } - async fn get_next_unspent_credential( &self, ) -> Result { - todo!() + let credential = self + .coconut_credential_manager + .get_next_unspent_credential() + .await + .ok_or(StorageError::NoCredential)?; + + Ok(credential) } async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> { diff --git a/common/credential-storage/src/models.rs b/common/credential-storage/src/models.rs index 47372dd2b6..2695a190ad 100644 --- a/common/credential-storage/src/models.rs +++ b/common/credential-storage/src/models.rs @@ -1,31 +1,38 @@ // Copyright 2022-2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -#[derive(Clone)] -pub struct CoconutCredential { - #[allow(dead_code)] - pub id: i64, - pub voucher_value: String, - pub voucher_info: String, - pub serial_number: String, - pub binding_number: String, - pub signature: String, - pub epoch_id: String, - pub consumed: bool, -} +use zeroize::{Zeroize, ZeroizeOnDrop}; + +// #[derive(Clone)] +// pub struct CoconutCredential { +// #[allow(dead_code)] +// pub id: i64, +// pub voucher_value: String, +// pub voucher_info: String, +// pub serial_number: String, +// pub binding_number: String, +// pub signature: String, +// pub epoch_id: String, +// pub consumed: bool, +// } #[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))] +#[derive(Zeroize, ZeroizeOnDrop, Clone)] pub struct StoredIssuedCredential { pub id: i64, - pub serial_number: String, - pub binding_number: String, + pub serialization_revision: u8, + pub credential_data: Vec, + pub credential_type: String, - pub signature: String, - - pub variant_type: String, - pub serialized_variant_data: String, - - pub epoch_id: String, + pub epoch_id: u32, pub consumed: bool, } + +pub struct StorableIssuedCredential<'a> { + pub serialization_revision: u8, + pub credential_data: &'a [u8], + pub credential_type: String, + + pub epoch_id: u32, +} diff --git a/common/credential-storage/src/persistent_storage.rs b/common/credential-storage/src/persistent_storage.rs index 4fffc60777..b6772fdf56 100644 --- a/common/credential-storage/src/persistent_storage.rs +++ b/common/credential-storage/src/persistent_storage.rs @@ -5,7 +5,7 @@ use crate::backends::sqlite::CoconutCredentialManager; use crate::error::StorageError; use crate::storage::Storage; -use crate::models::{CoconutCredential, StoredIssuedCredential}; +use crate::models::{StorableIssuedCredential, StoredIssuedCredential}; use async_trait::async_trait; use log::{debug, error}; use sqlx::ConnectOptions; @@ -58,45 +58,34 @@ impl PersistentStorage { impl Storage for PersistentStorage { type StorageError = StorageError; - async fn insert_coconut_credential( + async fn insert_issued_credential<'a>( &self, - voucher_value: String, - voucher_info: String, - serial_number: String, - binding_number: String, - signature: String, - epoch_id: String, - ) -> Result<(), StorageError> { + bandwidth_credential: StorableIssuedCredential<'a>, + ) -> Result<(), Self::StorageError> { self.coconut_credential_manager - .insert_coconut_credential( - voucher_value, - voucher_info, - serial_number, - binding_number, - signature, - epoch_id, + .insert_issued_credential( + bandwidth_credential.credential_type, + bandwidth_credential.serialization_revision, + bandwidth_credential.credential_data, + bandwidth_credential.epoch_id, ) .await?; Ok(()) } - async fn get_next_coconut_credential(&self) -> Result { + async fn get_next_unspent_credential( + &self, + ) -> Result { let credential = self .coconut_credential_manager - .get_next_coconut_credential() + .get_next_unspent_credential() .await? .ok_or(StorageError::NoCredential)?; Ok(credential) } - async fn get_next_unspent_credential( - &self, - ) -> Result { - todo!() - } - async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> { self.coconut_credential_manager .consume_coconut_credential(id) diff --git a/common/credential-storage/src/storage.rs b/common/credential-storage/src/storage.rs index af16a4a8de..64fe0d6e82 100644 --- a/common/credential-storage/src/storage.rs +++ b/common/credential-storage/src/storage.rs @@ -1,7 +1,7 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::models::{CoconutCredential, StoredIssuedCredential}; +use crate::models::{StorableIssuedCredential, StoredIssuedCredential}; use async_trait::async_trait; use std::error::Error; @@ -9,31 +9,11 @@ use std::error::Error; pub trait Storage: Send + Sync { type StorageError: Error; - /// Inserts provided signature into the database. - /// - /// # Arguments - /// - /// * `voucher_value`: How much bandwidth is in the credential. - /// * `voucher_info`: What type of credential it is. - /// * `serial_number`: Serial number of the credential. - /// * `binding_number`: Binding number of the credential. - /// * `signature`: Coconut credential in the form of a signature. - /// * `epoch_id`: The epoch when it was signed. - #[deprecated] - async fn insert_coconut_credential( + async fn insert_issued_credential<'a>( &self, - voucher_value: String, - voucher_info: String, - serial_number: String, - binding_number: String, - signature: String, - epoch_id: String, + bandwidth_credential: StorableIssuedCredential<'a>, ) -> Result<(), Self::StorageError>; - /// Tries to retrieve one of the stored, unused credentials. - #[deprecated] - async fn get_next_coconut_credential(&self) -> Result; - /// Tries to retrieve one of the stored, unused credentials. async fn get_next_unspent_credential( &self, diff --git a/common/credential-utils/src/utils.rs b/common/credential-utils/src/utils.rs index 8e773a3225..2b2aaba9f7 100644 --- a/common/credential-utils/src/utils.rs +++ b/common/credential-utils/src/utils.rs @@ -44,7 +44,7 @@ where let state = nym_bandwidth_controller::acquire::deposit(client, amount.clone()).await?; - if nym_bandwidth_controller::acquire::get_credential(&state, client, persistent_storage) + if nym_bandwidth_controller::acquire::get_bandwidth_voucher(&state, client, persistent_storage) .await .is_err() { @@ -142,7 +142,8 @@ where let state = State::new(voucher); if let Err(e) = - nym_bandwidth_controller::acquire::get_credential(&state, client, shared_storage).await + nym_bandwidth_controller::acquire::get_bandwidth_voucher(&state, client, shared_storage) + .await { error!("Could not recover deposit {voucher_name} due to {e}, try again later",) } else { diff --git a/common/credentials/Cargo.toml b/common/credentials/Cargo.toml index bc096fa5a9..053e0f80fc 100644 --- a/common/credentials/Cargo.toml +++ b/common/credentials/Cargo.toml @@ -13,6 +13,7 @@ cosmrs = { workspace = true } thiserror = { workspace = true } log = { workspace = true } time = { workspace = true, features = ["serde"] } +serde = { workspace = true, features = ["derive"] } zeroize = { workspace = true } # I guess temporarily until we get serde support in coconut up and running @@ -20,7 +21,6 @@ nym-credentials-interface = { path = "../credentials-interface" } nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "serde"] } nym-api-requests = { path = "../../nym-api/nym-api-requests" } nym-validator-client = { path = "../client-libs/validator-client", default-features = false } -serde = { workspace = true, features = ["derive"] } [dev-dependencies] rand = "0.7.3" diff --git a/common/credentials/src/coconut/bandwidth/freepass.rs b/common/credentials/src/coconut/bandwidth/freepass.rs index 67fab7197b..0a1707ba3b 100644 --- a/common/credentials/src/coconut/bandwidth/freepass.rs +++ b/common/credentials/src/coconut/bandwidth/freepass.rs @@ -30,7 +30,7 @@ impl FreePassIssuedData { } } -#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] +#[derive(Zeroize, Serialize, Deserialize)] pub struct FreePassIssuanceData { /// the plain validity value of this credential expressed as unix timestamp #[zeroize(skip)] diff --git a/common/credentials/src/coconut/bandwidth/issuance.rs b/common/credentials/src/coconut/bandwidth/issuance.rs index 3ef9352782..02eb1a3c6c 100644 --- a/common/credentials/src/coconut/bandwidth/issuance.rs +++ b/common/credentials/src/coconut/bandwidth/issuance.rs @@ -238,9 +238,17 @@ impl IssuanceBandwidthCredential { .map_err(Error::SignatureAggregationError) } + // also drops self after the conversion pub fn into_issued_credential( self, aggregate_signature: Signature, + ) -> IssuedBandwidthCredential { + self.to_issued_credential(aggregate_signature) + } + + pub fn to_issued_credential( + &self, + aggregate_signature: Signature, ) -> IssuedBandwidthCredential { IssuedBandwidthCredential::new( self.serial_number, diff --git a/common/credentials/src/coconut/bandwidth/issued.rs b/common/credentials/src/coconut/bandwidth/issued.rs index 8a66e36365..62d5a4ecc7 100644 --- a/common/credentials/src/coconut/bandwidth/issued.rs +++ b/common/credentials/src/coconut/bandwidth/issued.rs @@ -1,23 +1,25 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::coconut::bandwidth::bandwidth_credential_params; use crate::coconut::bandwidth::freepass::FreePassIssuedData; use crate::coconut::bandwidth::issuance::{ BandwidthCredentialIssuanceDataVariant, IssuanceBandwidthCredential, }; use crate::coconut::bandwidth::voucher::BandwidthVoucherIssuedData; -use crate::coconut::bandwidth::{ - bandwidth_credential_params, CredentialSpendingData, CredentialType, -}; +use crate::coconut::bandwidth::{CredentialSpendingData, CredentialType}; +use crate::coconut::utils::scalar_serde_helper; use crate::error::Error; +use nym_credentials_interface::prove_bandwidth_credential; use nym_credentials_interface::{ - prove_bandwidth_credential, Parameters, PrivateAttribute, PublicAttribute, Signature, - VerificationKey, + Parameters, PrivateAttribute, PublicAttribute, Signature, VerificationKey, }; use serde::{Deserialize, Serialize}; use zeroize::{Zeroize, ZeroizeOnDrop}; -#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] +pub const CURRENT_SERIALIZATION_REVISION: u8 = 1; + +#[derive(Zeroize, Serialize, Deserialize)] pub enum BandwidthCredentialIssuedDataVariant { Voucher(BandwidthVoucherIssuedData), FreePass(FreePassIssuedData), @@ -68,13 +70,15 @@ impl BandwidthCredentialIssuedDataVariant { } // the only important thing to zeroize here are the private attributes, the rest can be made fully public for what we're concerned -#[derive(Zeroize, ZeroizeOnDrop)] +#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] pub struct IssuedBandwidthCredential { // private attributes /// a random secret value generated by the client used for double-spending detection + #[serde(with = "scalar_serde_helper")] serial_number: PrivateAttribute, /// a random secret value generated by the client used to bind multiple credentials together + #[serde(with = "scalar_serde_helper")] binding_number: PrivateAttribute, /// the underlying aggregated signature on the attributes @@ -85,6 +89,7 @@ pub struct IssuedBandwidthCredential { variant_data: BandwidthCredentialIssuedDataVariant, /// type of the bandwdith credential hashed onto a scalar + #[serde(with = "scalar_serde_helper")] type_prehashed: PublicAttribute, } @@ -105,6 +110,28 @@ impl IssuedBandwidthCredential { } } + pub fn current_serialization_revision(&self) -> u8 { + CURRENT_SERIALIZATION_REVISION + } + + /// Pack (serialize) this credential data into a stream of bytes using v1 serializer. + pub fn pack_v1(&self) -> Vec { + use bincode::Options; + // safety: our data format is stable and thus the serialization should not fail + make_storable_bincode_serializer().serialize(self).unwrap() + } + + /// Unpack (deserialize) the credential data from the given bytes using v1 serializer. + pub fn unpack_v1(bytes: &[u8]) -> Result { + use bincode::Options; + Ok(make_storable_bincode_serializer().deserialize(bytes)?) + } + + pub fn randomise_signature(&mut self) { + let signature_prime = self.signature.randomise(bandwidth_credential_params()); + self.signature = signature_prime.0 + } + pub fn default_parameters() -> Parameters { IssuanceBandwidthCredential::default_parameters() } @@ -142,3 +169,10 @@ impl IssuedBandwidthCredential { }) } } + +fn make_storable_bincode_serializer() -> impl bincode::Options { + use bincode::Options; + bincode::DefaultOptions::new() + .with_big_endian() + .with_varint_encoding() +} diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index 2cfd6ab068..ca362dc721 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -3915,6 +3915,7 @@ dependencies = [ "sqlx", "thiserror", "tokio", + "zeroize", ] [[package]] diff --git a/sdk/rust/nym-sdk/src/bandwidth/client.rs b/sdk/rust/nym-sdk/src/bandwidth/client.rs index c5756e0cdc..5051e5a1d4 100644 --- a/sdk/rust/nym-sdk/src/bandwidth/client.rs +++ b/sdk/rust/nym-sdk/src/bandwidth/client.rs @@ -57,7 +57,7 @@ where pub async fn acquire(&self, amount: u128) -> Result<()> { let amount = Coin::new(amount, &self.network_details.chain_details.mix_denom.base); let state = nym_bandwidth_controller::acquire::deposit(&self.client, amount).await?; - nym_bandwidth_controller::acquire::get_credential(&state, &self.client, self.storage) + nym_bandwidth_controller::acquire::get_bandwidth_voucher(&state, &self.client, self.storage) .await .map_err(|reason| Error::UnconvertedDeposit { reason, @@ -71,8 +71,12 @@ where let voucher = IssuanceBandwidthCredential::try_from_recovered_bytes(voucher_blob) .map_err(|_| Error::InvalidVoucherBlob)?; let state = State::new(voucher); - nym_bandwidth_controller::acquire::get_credential(&state, &self.client, self.storage) - .await?; + nym_bandwidth_controller::acquire::get_bandwidth_voucher( + &state, + &self.client, + self.storage, + ) + .await?; Ok(()) } diff --git a/sdk/rust/nym-sdk/src/mixnet.rs b/sdk/rust/nym-sdk/src/mixnet.rs index 9dc4dd9681..b54ef462ee 100644 --- a/sdk/rust/nym-sdk/src/mixnet.rs +++ b/sdk/rust/nym-sdk/src/mixnet.rs @@ -59,8 +59,8 @@ pub use nym_client_core::{ config::{GatewayEndpointConfig, GroupBy}, }; pub use nym_credential_storage::{ - ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage, models::CoconutCredential, - storage::Storage as CredentialStorage, + ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage, + models::StoredIssuedCredential, storage::Storage as CredentialStorage, }; pub use nym_network_defaults::NymNetworkDetails; pub use nym_socks5_client_core::config::Socks5;