mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-05 05:06:14 +01:00
feat(nut12): verify receive proofs
This commit is contained in:
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user