Skip to content

Commit

Permalink
Merge pull request #1085 from MutinyWallet/blind-auth-followup
Browse files Browse the repository at this point in the history
Fixes for blind tokens
  • Loading branch information
TonyGiorgio committed Mar 26, 2024
2 parents 5ca7123 + 6c376aa commit 1708d95
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 7 deletions.
88 changes: 83 additions & 5 deletions mutiny-core/src/blindauth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const SPEND_KEY_CHILD_ID: ChildId = ChildId(0);
/// Child ID used to derive the blinding key from a service plan's DerivableSecret
const BLINDING_KEY_CHILD_ID: ChildId = ChildId(1);

#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
pub struct TokenStorage {
// (service_id, plan_id): number of times used
pub map: HashMap<ServicePlanIndex, u32>,
Expand Down Expand Up @@ -64,21 +64,52 @@ impl TokenStorage {
}
}

#[derive(Debug, Serialize, Deserialize, Clone, Default, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
pub struct ServicePlanIndex {
pub service_id: u32,
pub plan_id: u32,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
impl Serialize for ServicePlanIndex {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let string = format!("{}-{}", self.service_id, self.plan_id);
serializer.serialize_str(&string)
}
}

impl<'a> Deserialize<'a> for ServicePlanIndex {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'a>,
{
let uri = String::deserialize(deserializer)?;

let parts: Vec<&str> = uri.split('-').collect();
if parts.len() != 2 {
return Err(serde::de::Error::custom("Invalid ServicePlanIndex"));
}

let service_id = parts[0].parse::<u32>().map_err(serde::de::Error::custom)?;
let plan_id = parts[1].parse::<u32>().map_err(serde::de::Error::custom)?;
Ok(ServicePlanIndex {
service_id,
plan_id,
})
}
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct UnsignedToken {
pub counter: u32,
pub service_id: u32,
pub plan_id: u32,
pub blinded_message: BlindedMessage,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct SignedToken {
pub counter: u32,
pub service_id: u32,
Expand Down Expand Up @@ -219,7 +250,11 @@ impl<S: MutinyStorage> BlindAuthClient<S> {
// Maybe have an "issued" tokens call so we can see if we're caught up with the server?
self.storage
.insert_token_storage(token_storage_guard.clone())
.await?;
.await
.map_err(|e| {
log_error!(self.logger, "could not save token storage: {e:?}");
e
})?;

Ok(signed_token)
}
Expand Down Expand Up @@ -370,3 +405,46 @@ fn create_blind_auth_secret(
BLINDAUTH_CLIENT_NONCE,
))
}

#[cfg(test)]
mod test {
use crate::blindauth::{ServicePlanIndex, SignedToken, TokenStorage};
use tbs::{BlindedMessage, BlindedSignature};

#[test]
fn test_token_storage_serialization() {
let mut map = std::collections::HashMap::new();
map.insert(
ServicePlanIndex {
service_id: 1,
plan_id: 1,
},
1,
);

let token = SignedToken {
counter: 1,
service_id: 1,
plan_id: 1,
blinded_message: BlindedMessage(Default::default()),
blind_sig: BlindedSignature(Default::default()),
spent: false,
};

let storage = TokenStorage {
map,
tokens: vec![token],
version: 0,
};

let serialized = serde_json::to_string(&storage).unwrap();
let deserialized: TokenStorage = serde_json::from_str(&serialized).unwrap();

assert_eq!(storage, deserialized);

// test backwards compatibility
let string = "{\"map\":{\"1-1\":1},\"tokens\":[{\"counter\":1,\"service_id\":1,\"plan_id\":1,\"blinded_message\":\"c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"blind_sig\":\"c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"spent\":false}],\"version\":0}";
let deserialized: TokenStorage = serde_json::from_str(string).unwrap();
assert_eq!(storage, deserialized);
}
}
9 changes: 8 additions & 1 deletion mutiny-core/src/hermes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,14 @@ impl<S: MutinyStorage> HermesClient<S> {
let available_paid_token =
match find_hermes_token(&available_tokens, HERMES_SERVICE_ID, HERMES_PAID_PLAN_ID) {
Some(t) => t,
None => return Err(MutinyError::NotFound),
None => {
log_error!(
self.logger,
"No available paid token for Hermes, current tokens: {}",
available_tokens.len()
);
return Err(MutinyError::NotFound);
}
};

// check that we have a federation added and get it's id/invite code
Expand Down
2 changes: 1 addition & 1 deletion mutiny-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
extern crate core;

pub mod auth;
mod blindauth;
pub mod blindauth;
mod cashu;
mod chain;
pub mod encrypt;
Expand Down
18 changes: 18 additions & 0 deletions mutiny-wasm/src/indexed_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use gloo_utils::format::JsValueSerdeExt;
use lightning::util::logger::Logger;
use lightning::{log_debug, log_error, log_trace};
use log::error;
use mutiny_core::blindauth::TokenStorage;
use mutiny_core::logging::LOGGING_KEY;
use mutiny_core::storage::*;
use mutiny_core::vss::*;
Expand Down Expand Up @@ -445,6 +446,23 @@ impl IndexedDbStorage {
}
}
}
SERVICE_TOKENS => {
// we can get version from TokenStorage, so we should compare
match current.get_data::<TokenStorage>(&kv.key)? {
Some(token_storage) => {
if token_storage.version < kv.version {
let obj = vss.get_object(&kv.key).await?;
if serde_json::from_value::<TokenStorage>(obj.value.clone()).is_ok() {
return Ok(Some((kv.key, obj.value)));
}
}
}
None => {
let obj = vss.get_object(&kv.key).await?;
return Ok(Some((kv.key, obj.value)));
}
}
}
key => {
if key.starts_with(MONITORS_PREFIX_KEY) {
// we can get versions from monitors, so we should compare
Expand Down

0 comments on commit 1708d95

Please sign in to comment.