1use core::ops::Deref;
6
7use bitcoin::secp256k1::hashes::{hmac, sha256, Hash, HashEngine, HmacEngine};
8use bitcoin::secp256k1::{self, Scalar};
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12use super::nut00::{BlindSignature, Proof};
13use super::nut01::{PublicKey, SecretKey};
14use super::nut02::Id;
15use crate::dhke::{hash_e, hash_to_curve};
16use crate::{Amount, SECP256K1};
17
18#[derive(Debug, Error)]
20pub enum Error {
21 #[error("No DLEQ proof provided")]
23 MissingDleqProof,
24 #[error("Incomplete DLEQ proof")]
26 IncompleteDleqProof,
27 #[error("Invalid DLEQ proof")]
29 InvalidDleqProof,
30 #[error("Could not derive deterministic DLEQ nonce")]
32 CouldNotDeriveDleqNonce,
33 #[error(transparent)]
35 DHKE(#[from] crate::dhke::Error),
36 #[error(transparent)]
38 NUT01(#[from] crate::nuts::nut01::Error),
39 #[error(transparent)]
41 Secp256k1(#[from] secp256k1::Error),
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
48pub struct BlindSignatureDleq {
49 pub e: SecretKey,
51 pub s: SecretKey,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59pub struct ProofDleq {
60 pub e: SecretKey,
62 pub s: SecretKey,
64 pub r: SecretKey,
66}
67
68impl ProofDleq {
69 pub fn new(e: SecretKey, s: SecretKey, r: SecretKey) -> Self {
71 Self { e, s, r }
72 }
73}
74
75fn verify_dleq(
77 blinded_message: PublicKey, blinded_signature: PublicKey, e: &SecretKey,
80 s: &SecretKey,
81 mint_pubkey: PublicKey, ) -> Result<(), Error> {
83 let e_bytes: [u8; 32] = e.to_secret_bytes();
84 let e: Scalar = e.as_scalar();
85
86 let a: PublicKey = mint_pubkey.mul_tweak(&SECP256K1, &e)?.into();
88
89 let a: PublicKey = a.negate(&SECP256K1).into();
91 let r1: PublicKey = s.public_key().combine(&a)?.into(); let s: Scalar = Scalar::from(s.deref().to_owned());
95 let b: PublicKey = blinded_message.mul_tweak(&SECP256K1, &s)?.into();
96
97 let c: PublicKey = blinded_signature.mul_tweak(&SECP256K1, &e)?.into();
99
100 let c: PublicKey = c.negate(&SECP256K1).into();
102 let r2: PublicKey = b.combine(&c)?.into();
103
104 let hash_e: [u8; 32] = hash_e([r1, r2, mint_pubkey, blinded_signature]);
106
107 if e_bytes != hash_e {
108 tracing::warn!("DLEQ on signature failed");
109 tracing::debug!("e_bytes: {:?}, hash_e: {:?}", e_bytes, hash_e);
110 return Err(Error::InvalidDleqProof);
111 }
112
113 Ok(())
114}
115
116fn derive_deterministic_nonce(
117 blinded_signature: PublicKey, blinded_message: &PublicKey, mint_secret_key: &SecretKey, ) -> Result<SecretKey, Error> {
121 for counter in u8::MIN..=u8::MAX {
122 let mut message = Vec::with_capacity(16 + (3 * 65) + 1);
123 message.extend_from_slice(b"Cashu_DLEQ_R_v1");
124 message.extend_from_slice(&mint_secret_key.public_key().to_uncompressed_bytes());
125 message.extend_from_slice(&blinded_message.to_uncompressed_bytes());
126 message.extend_from_slice(&blinded_signature.to_uncompressed_bytes());
127 message.push(counter);
128
129 let mut engine = HmacEngine::<sha256::Hash>::new(mint_secret_key.as_secret_bytes());
130 engine.input(&message);
131 let hmac_result = hmac::Hmac::<sha256::Hash>::from_engine(engine);
132 let result_bytes = hmac_result.to_byte_array();
133
134 match SecretKey::from_slice(&result_bytes) {
135 Ok(nonce) => return Ok(nonce),
136 Err(_) => continue,
137 }
138 }
139
140 Err(Error::CouldNotDeriveDleqNonce)
141}
142
143fn calculate_dleq(
144 blinded_signature: PublicKey, blinded_message: &PublicKey, mint_secret_key: &SecretKey, ) -> Result<BlindSignatureDleq, Error> {
148 let r: SecretKey =
149 derive_deterministic_nonce(blinded_signature, blinded_message, mint_secret_key)?;
150
151 let r1 = r.public_key();
153
154 let r_scal: Scalar = r.as_scalar();
156 let r2: PublicKey = blinded_message.mul_tweak(&SECP256K1, &r_scal)?.into();
157
158 let e: [u8; 32] = hash_e([r1, r2, mint_secret_key.public_key(), blinded_signature]);
160 let e_sk: SecretKey = SecretKey::from_slice(&e)?;
161
162 let s1: SecretKey = e_sk.mul_tweak(&mint_secret_key.as_scalar())?.into();
164
165 let s: SecretKey = r.add_tweak(&s1.to_scalar())?.into();
167
168 Ok(BlindSignatureDleq { e: e_sk, s })
169}
170
171impl Proof {
172 pub fn verify_dleq(&self, mint_pubkey: PublicKey) -> Result<(), Error> {
174 match &self.dleq {
175 Some(dleq) => {
176 let y = hash_to_curve(self.secret.as_bytes())?;
177
178 let r: Scalar = dleq.r.as_scalar();
179 let bs1: PublicKey = mint_pubkey.mul_tweak(&SECP256K1, &r)?.into();
180
181 let blinded_signature: PublicKey = self.c.combine(&bs1)?.into();
182 let blinded_message: PublicKey = y.combine(&dleq.r.public_key())?.into();
183
184 verify_dleq(
185 blinded_message,
186 blinded_signature,
187 &dleq.e,
188 &dleq.s,
189 mint_pubkey,
190 )
191 }
192 None => Err(Error::MissingDleqProof),
193 }
194 }
195}
196
197impl BlindSignature {
198 #[inline]
200 pub fn new(
201 amount: Amount,
202 blinded_signature: PublicKey,
203 keyset_id: Id,
204 blinded_message: &PublicKey,
205 mint_secretkey: &SecretKey,
206 ) -> Result<Self, Error> {
207 Ok(Self {
208 amount,
209 keyset_id,
210 c: blinded_signature,
211 dleq: Some(calculate_dleq(
212 blinded_signature,
213 blinded_message,
214 mint_secretkey,
215 )?),
216 })
217 }
218
219 #[inline]
221 pub fn verify_dleq(
222 &self,
223 mint_pubkey: PublicKey,
224 blinded_message: PublicKey,
225 ) -> Result<(), Error> {
226 match &self.dleq {
227 Some(dleq) => verify_dleq(blinded_message, self.c, &dleq.e, &dleq.s, mint_pubkey),
228 None => Err(Error::MissingDleqProof),
229 }
230 }
231
232 pub fn add_dleq_proof(
241 &mut self,
242 blinded_message: &PublicKey,
243 mint_secretkey: &SecretKey,
244 ) -> Result<(), Error> {
245 let dleq: BlindSignatureDleq = calculate_dleq(self.c, blinded_message, mint_secretkey)?;
246 self.dleq = Some(dleq);
247 Ok(())
248 }
249}
250
251#[cfg(test)]
252mod tests {
253
254 use std::str::FromStr;
255
256 use super::*;
257
258 #[test]
259 fn test_blind_signature_dleq() {
260 let blinded_sig = r#"{"amount":8,"id":"00882760bfa2eb41","C_":"02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2","dleq":{"e":"9818e061ee51d5c8edc3342369a554998ff7b4381c8652d724cdf46429be73d9","s":"9818e061ee51d5c8edc3342369a554998ff7b4381c8652d724cdf46429be73da"}}"#;
261
262 let blinded: BlindSignature = serde_json::from_str(blinded_sig).unwrap();
263
264 let secret_key =
265 SecretKey::from_hex("0000000000000000000000000000000000000000000000000000000000000001")
266 .unwrap();
267
268 let mint_key = secret_key.public_key();
269
270 let blinded_secret = PublicKey::from_str(
271 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
272 )
273 .unwrap();
274
275 blinded.verify_dleq(mint_key, blinded_secret).unwrap()
276 }
277
278 #[test]
279 fn test_blind_signature_dleq_deterministic_nonce_vector() {
280 let mint_secret_key =
281 SecretKey::from_hex("0000000000000000000000000000000000000000000000000000000000000002")
282 .expect("valid mint secret key");
283 let blinded_message = PublicKey::from_str(
284 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
285 )
286 .expect("valid blinded message");
287 let blinded_signature = PublicKey::from_str(
288 "0244eccfc7a348274458bb38044c7f3c389b3c2086c7ec18b5812d2877ab937787",
289 )
290 .expect("valid blinded signature");
291
292 let blind_signature = BlindSignature::new(
293 Amount::from(1),
294 blinded_signature,
295 Id::from_str("00882760bfa2eb41").expect("valid keyset id"),
296 &blinded_message,
297 &mint_secret_key,
298 )
299 .expect("deterministic DLEQ proof");
300 let dleq = blind_signature.dleq.expect("DLEQ proof");
301
302 assert_eq!(
303 dleq.e.to_secret_hex(),
304 "2a16ffee280aff3c429045607f9b8e0bf8b35910c44c1b20b9dfaf01b263d7b3"
305 );
306 assert_eq!(
307 dleq.s.to_secret_hex(),
308 "9df27731238334718d120d4f74611a7c668233f988e687ac3fb188f0a34a2dab"
309 );
310 assert!(verify_dleq(
311 blinded_message,
312 blinded_signature,
313 &dleq.e,
314 &dleq.s,
315 mint_secret_key.public_key(),
316 )
317 .is_ok());
318 }
319
320 #[test]
321 fn test_proof_dleq() {
322 let proof = r#"{"amount": 1,"id": "00882760bfa2eb41","secret": "daf4dd00a2b68a0858a80450f52c8a7d2ccf87d375e43e216e0c571f089f63e9","C": "024369d2d22a80ecf78f3937da9d5f30c1b9f74f0c32684d583cca0fa6a61cdcfc","dleq": {"e": "b31e58ac6527f34975ffab13e70a48b6d2b0d35abc4b03f0151f09ee1a9763d4","s": "8fbae004c59e754d71df67e392b6ae4e29293113ddc2ec86592a0431d16306d8","r": "a6d13fcd7a18442e6076f5e1e7c887ad5de40a019824bdfa9fe740d302e8d861"}}"#;
323
324 let proof: Proof = serde_json::from_str(proof).unwrap();
325
326 let a: PublicKey = PublicKey::from_str(
328 "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
329 )
330 .unwrap();
331
332 assert!(proof.verify_dleq(a).is_ok());
333 }
334
335 #[test]
343 fn test_proof_dleq_wrong_mint_key() {
344 let proof = r#"{"amount": 1,"id": "00882760bfa2eb41","secret": "daf4dd00a2b68a0858a80450f52c8a7d2ccf87d375e43e216e0c571f089f63e9","C": "024369d2d22a80ecf78f3937da9d5f30c1b9f74f0c32684d583cca0fa6a61cdcfc","dleq": {"e": "b31e58ac6527f34975ffab13e70a48b6d2b0d35abc4b03f0151f09ee1a9763d4","s": "8fbae004c59e754d71df67e392b6ae4e29293113ddc2ec86592a0431d16306d8","r": "a6d13fcd7a18442e6076f5e1e7c887ad5de40a019824bdfa9fe740d302e8d861"}}"#;
345
346 let proof: Proof = serde_json::from_str(proof).unwrap();
347
348 let wrong_key: PublicKey = PublicKey::from_str(
350 "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
351 )
352 .unwrap();
353
354 assert!(proof.verify_dleq(wrong_key).is_err());
356 }
357
358 #[test]
366 fn test_proof_dleq_missing() {
367 let proof = r#"{"amount": 1,"id": "00882760bfa2eb41","secret": "daf4dd00a2b68a0858a80450f52c8a7d2ccf87d375e43e216e0c571f089f63e9","C": "024369d2d22a80ecf78f3937da9d5f30c1b9f74f0c32684d583cca0fa6a61cdcfc"}"#;
368
369 let proof: Proof = serde_json::from_str(proof).unwrap();
370
371 let a: PublicKey = PublicKey::from_str(
372 "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
373 )
374 .unwrap();
375
376 let result = proof.verify_dleq(a);
378 assert!(result.is_err());
379 assert!(matches!(result.unwrap_err(), Error::MissingDleqProof));
380 }
381
382 #[test]
389 fn test_blind_signature_dleq_wrong_key() {
390 let blinded_sig = r#"{"amount":8,"id":"00882760bfa2eb41","C_":"02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2","dleq":{"e":"9818e061ee51d5c8edc3342369a554998ff7b4381c8652d724cdf46429be73d9","s":"9818e061ee51d5c8edc3342369a554998ff7b4381c8652d724cdf46429be73da"}}"#;
391
392 let blinded: BlindSignature = serde_json::from_str(blinded_sig).unwrap();
393
394 let wrong_key =
396 SecretKey::from_hex("0000000000000000000000000000000000000000000000000000000000000002")
397 .unwrap();
398
399 let blinded_secret = PublicKey::from_str(
400 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
401 )
402 .unwrap();
403
404 assert!(blinded
406 .verify_dleq(wrong_key.public_key(), blinded_secret)
407 .is_err());
408 }
409
410 #[test]
418 fn test_blind_signature_dleq_tampered() {
419 let tampered_sig = r#"{"amount":8,"id":"00882760bfa2eb41","C_":"02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2","dleq":{"e":"0000000000000000000000000000000000000000000000000000000000000001","s":"0000000000000000000000000000000000000000000000000000000000000002"}}"#;
421
422 let blinded: BlindSignature = serde_json::from_str(tampered_sig).unwrap();
423
424 let secret_key =
425 SecretKey::from_hex("0000000000000000000000000000000000000000000000000000000000000001")
426 .unwrap();
427
428 let blinded_secret = PublicKey::from_str(
429 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
430 )
431 .unwrap();
432
433 assert!(blinded
435 .verify_dleq(secret_key.public_key(), blinded_secret)
436 .is_err());
437 }
438
439 #[test]
447 fn test_add_dleq_proof() {
448 use crate::nuts::nut02::Id;
449
450 let secret_key =
451 SecretKey::from_hex("0000000000000000000000000000000000000000000000000000000000000001")
452 .unwrap();
453
454 let blinded_message = PublicKey::from_str(
455 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
456 )
457 .unwrap();
458
459 let blinded_signature = PublicKey::from_str(
460 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
461 )
462 .unwrap();
463
464 let mut blind_sig = BlindSignature {
465 amount: Amount::from(1),
466 keyset_id: Id::from_str("00882760bfa2eb41").unwrap(),
467 c: blinded_signature,
468 dleq: None,
469 };
470
471 assert!(blind_sig.dleq.is_none());
473
474 blind_sig
476 .add_dleq_proof(&blinded_message, &secret_key)
477 .unwrap();
478
479 assert!(blind_sig.dleq.is_some());
481
482 assert!(blind_sig
484 .verify_dleq(secret_key.public_key(), blinded_message)
485 .is_ok());
486 }
487}