missing serialization

This commit is contained in:
Jędrzej Stuczyński
2024-02-08 16:10:10 +00:00
parent f687ebb0f5
commit ad9aee0ec0
14 changed files with 215 additions and 44 deletions
Generated
+1
View File
@@ -5388,6 +5388,7 @@ dependencies = [
"bls12_381",
"nym-coconut",
"serde",
"thiserror",
]
[[package]]
+3
View File
@@ -45,4 +45,7 @@ pub enum BandwidthControllerError {
#[error("Threshold not set yet")]
NoThreshold,
#[error("can't handle recovering storage with revision {stored}. {expected} was expected")]
UnsupportedCredentialStorageRevision { stored: u8, expected: u8 },
}
+11 -30
View File
@@ -2,40 +2,21 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::BandwidthControllerError;
use nym_credential_storage::models::{StorableIssuedCredential, StoredIssuedCredential};
use nym_credential_storage::models::StoredIssuedCredential;
use nym_credentials::coconut::bandwidth::issued::CURRENT_SERIALIZATION_REVISION;
use nym_credentials::coconut::bandwidth::IssuedBandwidthCredential;
use nym_validator_client::nym_api::EpochId;
pub fn stored_credential_to_issued_bandwidth(
cred: StoredIssuedCredential,
) -> Result<IssuedBandwidthCredential, BandwidthControllerError> {
/*
let bandwidth_credential = self
.storage
.get_next_coconut_credential()
.await
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?;
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
.map_err(|_| StorageError::InconsistentData)?;
let voucher_info = bandwidth_credential.voucher_info.clone();
let serial_number = Zeroizing::new(nym_coconut_interface::Attribute::try_from_bs58(
bandwidth_credential.serial_number,
)?);
let binding_number = Zeroizing::new(nym_coconut_interface::Attribute::try_from_bs58(
bandwidth_credential.binding_number,
)?);
let signature =
nym_coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?;
let epoch_id = u64::from_str(&bandwidth_credential.epoch_id)
.map_err(|_| StorageError::InconsistentData)?;
if cred.serialization_revision != CURRENT_SERIALIZATION_REVISION {
return Err(
BandwidthControllerError::UnsupportedCredentialStorageRevision {
stored: cred.serialization_revision,
expected: CURRENT_SERIALIZATION_REVISION,
},
);
}
*/
todo!()
}
pub fn issued_bandwidth_to_stored_credential(
issued: IssuedBandwidthCredential,
epoch_id: EpochId,
) -> StoredIssuedCredential {
todo!()
Ok(IssuedBandwidthCredential::unpack_v1(&cred.credential_data)?)
}
+1
View File
@@ -13,5 +13,6 @@ license.workspace = true
[dependencies]
bls12_381 = { workspace = true, default-features = false }
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
nym-coconut = { path = "../nymcoconut" }
+23 -3
View File
@@ -4,9 +4,11 @@
use bls12_381::Scalar;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use thiserror::Error;
pub use nym_coconut::{
aggregate_signature_shares, aggregate_verification_keys, blind_sign, hash_to_scalar,
aggregate_signature_shares, aggregate_verification_keys, blind_sign, hash_to_scalar, keygen,
prepare_blind_sign, prove_bandwidth_credential, verify_credential, Attribute, Base58,
BlindSignRequest, BlindedSignature, Bytable, CoconutError, KeyPair, Parameters,
PrivateAttribute, PublicAttribute, SecretKey, Signature, SignatureShare, VerificationKey,
@@ -20,12 +22,30 @@ pub const FREE_PASS_INFO_TYPE: &str = "FreeBandwidthPass";
// fn prove_credential(&self) -> Result<(), ()>;
// }
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[derive(Debug, Error)]
#[error("{0} is not a valid credential type")]
pub struct UnknownCredentialType(String);
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum CredentialType {
Voucher,
FreePass,
}
impl FromStr for CredentialType {
type Err = UnknownCredentialType;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == VOUCHER_INFO_TYPE {
Ok(CredentialType::Voucher)
} else if s == FREE_PASS_INFO_TYPE {
Ok(CredentialType::FreePass)
} else {
Err(UnknownCredentialType(s.to_string()))
}
}
}
impl CredentialType {
pub fn validate(&self, type_plain: &str) -> bool {
match self {
@@ -63,7 +83,7 @@ pub struct CredentialSigningData {
pub typ: CredentialType,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct CredentialSpendingData {
pub embedded_private_attributes: usize,
@@ -127,6 +127,10 @@ impl IssuanceBandwidthCredential {
))
}
pub fn new_freepass(expiry_date: Option<OffsetDateTime>) -> Self {
Self::new(FreePassIssuanceData::new(expiry_date))
}
pub fn blind_serial_number_in_g1subgroup(&self) -> G1Projective {
bandwidth_credential_params().gen1() * self.serial_number
}
@@ -137,10 +141,6 @@ impl IssuanceBandwidthCredential {
self.blind_serial_number_in_g1subgroup().to_bs58()
}
pub fn new_freepass(expiry_date: Option<OffsetDateTime>) -> Self {
Self::new(FreePassIssuanceData::new(expiry_date))
}
pub fn typ(&self) -> CredentialType {
self.variant_data.info()
}
+1
View File
@@ -14,6 +14,7 @@ pub use scheme::issuance::blind_sign;
pub use scheme::issuance::prepare_blind_sign;
pub use scheme::issuance::verify_partial_blind_signature;
pub use scheme::issuance::BlindSignRequest;
pub use scheme::keygen::keygen;
pub use scheme::keygen::ttp_keygen;
pub use scheme::keygen::KeyPair;
pub use scheme::keygen::SecretKey;
-1
View File
@@ -565,7 +565,6 @@ impl TryFrom<&[u8]> for KeyPair {
/// Generate a single Coconut keypair ((x, y0, y1...), (g2^x, g2^y0, ...)).
/// It is not suitable for threshold credentials as all subsequent calls to `keygen` generate keys
/// that are independent of each other.
#[cfg(test)]
pub fn keygen(params: &Parameters) -> KeyPair {
let attributes = params.gen_hs().len();
+1
View File
@@ -32,3 +32,4 @@ nym-credentials-interface = { path = "../../common/credentials-interface" }
workspace = true
default-features = false
+4 -1
View File
@@ -15,7 +15,10 @@ pub mod types;
/// Defines the current version of the communication protocol between gateway and clients.
/// It has to be incremented for any breaking change.
pub const PROTOCOL_VERSION: u8 = 1;
// history:
// 1 - initial release
// 2 - changes to client credentials structure
pub const PROTOCOL_VERSION: u8 = 2;
pub type GatewayMac = HmacOutput<GatewayIntegrityHmacAlgorithm>;
+149 -4
View File
@@ -3,10 +3,10 @@
use crate::GatewayRequestsError;
use nym_credentials::coconut::bandwidth::CredentialSpendingData;
use nym_credentials_interface::CoconutError;
use nym_credentials_interface::{CoconutError, VerifyCredentialRequest};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct CredentialSpendingWithEpoch {
/// The cryptographic material required for spending the underlying credential.
pub data: CredentialSpendingData,
@@ -16,6 +16,23 @@ pub struct CredentialSpendingWithEpoch {
pub epoch_id: u64,
}
// just a helper macro for checking required length and advancing the buffer
macro_rules! ensure_len_and_advance {
($b:expr, $n:expr) => {{
if $b.len() < $n {
return Err(GatewayRequestsError::CredentialDeserializationFailureEOF);
}
// create binding to the desired range
let bytes = &$b[..$n];
// update the initial binding
$b = &$b[$n..];
bytes
}};
}
impl CredentialSpendingWithEpoch {
pub fn new(data: CredentialSpendingData, epoch_id: u64) -> Self {
CredentialSpendingWithEpoch { data, epoch_id }
@@ -39,10 +56,138 @@ impl CredentialSpendingWithEpoch {
}
pub fn to_bytes(&self) -> Vec<u8> {
todo!()
// simple length prefixed serialization
// TODO: change it to a standard format instead
let mut bytes = Vec::new();
let embedded_private = (self.data.embedded_private_attributes as u32).to_be_bytes();
let theta = self.data.verify_credential_request.to_bytes();
let theta_len = (theta.len() as u32).to_be_bytes();
let public = (self.data.public_attributes_plain.len() as u32).to_be_bytes();
let typ = self.data.typ.to_string();
let typ_bytes = typ.as_bytes();
let typ_len = (typ_bytes.len() as u32).to_be_bytes();
bytes.extend_from_slice(&embedded_private);
bytes.extend_from_slice(&theta_len);
bytes.extend_from_slice(&theta);
bytes.extend_from_slice(&public);
for pub_element in &self.data.public_attributes_plain {
let bytes_el = pub_element.as_bytes();
let len = (bytes_el.len() as u32).to_be_bytes();
bytes.extend_from_slice(&len);
bytes.extend_from_slice(bytes_el);
}
bytes.extend_from_slice(&typ_len);
bytes.extend_from_slice(typ_bytes);
bytes.extend_from_slice(&self.epoch_id.to_be_bytes());
bytes
}
pub fn try_from_bytes(raw: &[u8]) -> Result<Self, GatewayRequestsError> {
todo!()
// initial binding
let mut b = raw;
let embedded_private_bytes = ensure_len_and_advance!(b, 4);
let embedded_private_attributes =
u32::from_be_bytes(embedded_private_bytes.try_into().unwrap()) as usize;
let theta_len_bytes = ensure_len_and_advance!(b, 4);
let theta_len = u32::from_be_bytes(theta_len_bytes.try_into().unwrap()) as usize;
let theta_bytes = ensure_len_and_advance!(b, theta_len);
let theta = VerifyCredentialRequest::from_bytes(theta_bytes)
.map_err(GatewayRequestsError::CredentialDeserializationFailureMalformedTheta)?;
let public_bytes = ensure_len_and_advance!(b, 4);
let public = u32::from_be_bytes(public_bytes.try_into().unwrap()) as usize;
let mut public_attributes_plain = Vec::with_capacity(public);
for _ in 0..public {
let element_len_bytes = ensure_len_and_advance!(b, 4);
let element_len = u32::from_be_bytes(element_len_bytes.try_into().unwrap()) as usize;
let element_bytes = ensure_len_and_advance!(b, element_len);
let element = String::from_utf8(element_bytes.to_vec())?;
public_attributes_plain.push(element);
}
let typ_len_bytes = ensure_len_and_advance!(b, 4);
let typ_len = u32::from_be_bytes(typ_len_bytes.try_into().unwrap()) as usize;
let typ_bytes = ensure_len_and_advance!(b, typ_len);
let raw_typ = String::from_utf8(typ_bytes.to_vec())?;
let typ = raw_typ.parse()?;
// tell the linter to chill out in for this last iteration
#[allow(unused_assignments)]
let epoch_id_bytes = ensure_len_and_advance!(b, 8);
let epoch_id = u64::from_be_bytes(epoch_id_bytes.try_into().unwrap());
Ok(CredentialSpendingWithEpoch {
data: CredentialSpendingData {
embedded_private_attributes,
verify_credential_request: theta,
public_attributes_plain,
typ,
},
epoch_id,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use nym_credentials::coconut::bandwidth::bandwidth_credential_params;
use nym_credentials::IssuanceBandwidthCredential;
use nym_credentials_interface::{blind_sign, hash_to_scalar};
#[test]
fn credential_roundtrip() {
// make valid request
let params = bandwidth_credential_params();
let keypair = nym_credentials_interface::keygen(params);
let issuance = IssuanceBandwidthCredential::new_freepass(None);
let sig_req = issuance.prepare_for_signing();
let pub_attrs_hashed = sig_req
.public_attributes_plain
.iter()
.map(hash_to_scalar)
.collect::<Vec<_>>();
let pub_attrs = pub_attrs_hashed.iter().collect::<Vec<_>>();
let blind_sig = blind_sign(
params,
keypair.secret_key(),
&sig_req.blind_sign_request,
&pub_attrs,
)
.unwrap();
let sig = blind_sig
.unblind(
keypair.verification_key(),
&sig_req.pedersen_commitments_openings,
)
.unwrap();
let issued = issuance.into_issued_credential(sig);
let spending = issued
.prepare_for_spending(keypair.verification_key())
.unwrap();
let with_epoch = CredentialSpendingWithEpoch {
data: spending,
epoch_id: 42,
};
let bytes = with_epoch.to_bytes();
let recovered = CredentialSpendingWithEpoch::try_from_bytes(&bytes).unwrap();
assert_eq!(with_epoch, recovered);
}
}
+15 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2020-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::authentication::encrypted_address::EncryptedAddressBytes;
@@ -8,6 +8,7 @@ use crate::registration::handshake::SharedKeys;
use crate::{GatewayMacSize, PROTOCOL_VERSION};
use log::error;
use nym_credentials::coconut::bandwidth::CredentialSpendingData;
use nym_credentials_interface::{CoconutError, UnknownCredentialType};
use nym_crypto::generic_array::typenum::Unsigned;
use nym_crypto::hmac::recompute_keyed_hmac_and_verify_tag;
use nym_crypto::symmetric::stream_cipher;
@@ -18,6 +19,7 @@ use nym_sphinx::params::{GatewayEncryptionAlgorithm, GatewayIntegrityHmacAlgorit
use nym_sphinx::DestinationAddressBytes;
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
use std::string::FromUtf8Error;
use thiserror::Error;
use tungstenite::protocol::Message;
@@ -102,6 +104,18 @@ pub enum GatewayRequestsError {
#[from]
source: MixPacketFormattingError,
},
#[error("failed to deserialize provided credential: EOF")]
CredentialDeserializationFailureEOF,
#[error("failed to deserialize provided credential: malformed string: {0}")]
CredentialDeserializationFailureMalformedString(#[from] FromUtf8Error),
#[error("failed to deserialize provided credential: {0}")]
CredentialDeserializationFailureUnknownType(#[from] UnknownCredentialType),
#[error("failed to deserialize provided credential: malformed verify request: {0}")]
CredentialDeserializationFailureMalformedTheta(CoconutError),
}
#[derive(Serialize, Deserialize, Debug)]
+1
View File
@@ -3943,6 +3943,7 @@ dependencies = [
"bls12_381",
"nym-coconut",
"serde",
"thiserror",
]
[[package]]
+1
View File
@@ -3198,6 +3198,7 @@ dependencies = [
"bls12_381",
"nym-coconut",
"serde",
"thiserror",
]
[[package]]