feat(nut12): verify receive proofs

This commit is contained in:
thesimplekid
2024-03-25 14:42:02 +00:00
parent a6e77c62af
commit 96a151bfaa
3 changed files with 124 additions and 32 deletions

View File

@@ -50,6 +50,10 @@ pub enum Error {
LocalStore(#[from] localstore::Error),
#[error("`{0}`")]
Cashu(#[from] cashu::error::Error),
#[error("Could not verify Dleq")]
CouldNotVerifyDleq,
#[error("Unknown Key")]
UnknownKey,
#[error("`{0}`")]
Custom(String),
}
@@ -338,12 +342,17 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
let keys = self.get_keyset_keys(&mint_url, active_keyset_id).await?;
// Verify the signature DLEQ is valid
#[cfg(feature = "nut12")]
{
for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
let keys = self.localstore.get_keys(&sig.keyset_id).await?.unwrap();
let key = keys.amount_key(sig.amount).unwrap();
sig.verify_dleq(&key, &premint.blinded_message.b).unwrap();
let keys = self.get_keyset_keys(&mint_url, sig.keyset_id).await?;
let key = keys.amount_key(sig.amount).ok_or(Error::UnknownKey)?;
match sig.verify_dleq(&key, &premint.blinded_message.b) {
Ok(_) => (),
Err(cashu::nuts::nut12::Error::MissingDleqProof) => (),
Err(_) => return Err(Error::CouldNotVerifyDleq),
}
}
}
@@ -378,6 +387,26 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
let unit = token_data.unit.unwrap_or_default();
// Verify the signature DLEQ is valid
// Verify that all proofs in the token have a vlid DLEQ proof if one is supplied
#[cfg(feature = "nut12")]
{
for mint_proof in &token_data.token {
let mint_url = &mint_proof.mint;
let proofs = &mint_proof.proofs;
for proof in proofs {
let keys = self.get_keyset_keys(mint_url, proof.keyset_id).await?;
let key = keys.amount_key(proof.amount).ok_or(Error::UnknownKey)?;
match proof.verify_dleq(&key) {
Ok(_) => continue,
Err(cashu::nuts::nut12::Error::MissingDleqProof) => continue,
Err(_) => return Err(Error::CouldNotVerifyDleq),
}
}
}
}
let mut proofs: HashMap<UncheckedUrl, Proofs> = HashMap::new();
for token in token_data.token {
if token.proofs.is_empty() {
@@ -510,6 +539,22 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
let mut proof_count: HashMap<Id, u64> = HashMap::new();
for (promise, premint) in promises.iter().zip(blinded_messages) {
// Verify the signature DLEQ is valid
#[cfg(feature = "nut12")]
{
let keys = self
.localstore
.get_keys(&promise.keyset_id)
.await?
.ok_or(Error::UnknownKey)?;
let key = keys.amount_key(promise.amount).ok_or(Error::UnknownKey)?;
match promise.verify_dleq(&key, &premint.blinded_message.b) {
Ok(_) => (),
Err(cashu::nuts::nut12::Error::MissingDleqProof) => (),
Err(_) => return Err(Error::CouldNotVerifyDleq),
}
}
let a = self
.localstore
.get_keys(&promise.keyset_id)
@@ -909,6 +954,14 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
let mut sig_flag = None;
for proof in &mut proofs {
// Verify that proof DLEQ is valid
#[cfg(feature = "nut12")]
{
let keys = self.localstore.get_keys(&proof.keyset_id).await?.unwrap();
let key = keys.amount_key(proof.amount).unwrap();
proof.verify_dleq(&key).unwrap();
}
if let Ok(secret) =
<cashu::secret::Secret as TryInto<cashu::nuts::nut10::Secret>>::try_into(
proof.secret.clone(),

View File

@@ -115,18 +115,44 @@ mod wallet {
))?
.to_owned();
let unblinded_signature = unblind_message(blinded_c, r.into(), a)?;
let unblinded_signature = unblind_message(blinded_c, r.clone().into(), a)?;
let proof = Proof {
amount: blinded_signature.amount,
keyset_id: blinded_signature.keyset_id,
secret,
c: unblinded_signature,
#[cfg(feature = "nut11")]
witness: None,
#[cfg(feature = "nut12")]
dleq: blinded_signature.dleq,
};
let proof;
#[cfg(not(feature = "nut12"))]
{
proof = Proof {
amount: blinded_signature.amount,
keyset_id: blinded_signature.keyset_id,
secret,
c: unblinded_signature,
#[cfg(feature = "nut11")]
witness: None,
};
}
#[cfg(feature = "nut12")]
{
let dleq = if let Some(dleq) = blinded_signature.dleq {
Some(DleqProof {
e: dleq.e,
s: dleq.s,
r: Some(r),
})
} else {
None
};
proof = Proof {
amount: blinded_signature.amount,
keyset_id: blinded_signature.keyset_id,
secret,
c: unblinded_signature,
#[cfg(feature = "nut11")]
witness: None,
dleq,
};
}
proofs.push(proof);
}

View File

@@ -5,17 +5,31 @@ use std::ops::Mul;
use k256::Scalar;
use log::{debug, warn};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use super::{BlindedSignature, Proof, PublicKey, SecretKey};
use crate::dhke::{hash_e, hash_to_curve};
use crate::error::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("No Dleq Proof provided")]
MissingDleqProof,
#[error("Incomplete DLEQ Proof")]
IncompleteDleqProof,
#[error("Invalid Dleq Prood")]
InvalidDleqProof,
#[error("`{0}`")]
EllipticCurve(#[from] k256::elliptic_curve::Error),
#[error("`{0}`")]
Cashu(#[from] crate::error::Error),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DleqProof {
e: SecretKey,
s: SecretKey,
pub e: SecretKey,
pub s: SecretKey,
#[serde(skip_serializing_if = "Option::is_none")]
r: Option<SecretKey>,
pub r: Option<SecretKey>,
}
fn verify_dleq(
@@ -50,28 +64,27 @@ fn verify_dleq(
warn!("DLEQ on signature failed");
debug!("e_bytes: {:?}, Hash e: {:?}", e_bytes, hash_e);
// TODO: fix error
return Err(Error::TokenSpent);
return Err(Error::InvalidDleqProof);
}
Ok(())
}
impl Proof {
pub fn verify_dleq(
&self,
mint_pubkey: PublicKey,
blinding_factor: SecretKey,
) -> Result<(), Error> {
let (e, s): (k256::SecretKey, k256::SecretKey) = if let Some(dleq) = &self.dleq {
(dleq.e.clone().into(), dleq.s.clone().into())
} else {
// TODO: fix error
return Err(Error::AmountKey);
};
pub fn verify_dleq(&self, mint_pubkey: &PublicKey) -> Result<(), Error> {
let (e, s, blinding_factor): (k256::SecretKey, k256::SecretKey, k256::SecretKey) =
if let Some(dleq) = self.dleq.clone() {
if let Some(r) = dleq.r {
(dleq.e.into(), dleq.s.into(), r.into())
} else {
return Err(Error::IncompleteDleqProof);
}
} else {
return Err(Error::MissingDleqProof);
};
let c: k256::PublicKey = (&self.c).into();
let mint_pubkey: k256::PublicKey = mint_pubkey.into();
let blinding_factor: k256::SecretKey = blinding_factor.into();
let y = hash_to_curve(self.secret.0.as_bytes())?;
let blinded_signature = c.to_projective()
@@ -97,7 +110,7 @@ impl BlindedSignature {
(dleq.e.clone().into(), dleq.s.clone().into())
} else {
// TODO: fix error
return Err(Error::AmountKey);
return Err(Error::MissingDleqProof);
};
let mint_pubkey: k256::PublicKey = mint_pubkey.into();