1use core::ops::Deref;
6
7use bitcoin::secp256k1::{self, Scalar};
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11use super::nut00::{BlindSignature, Proof};
12use super::nut01::{PublicKey, SecretKey};
13use super::nut02::Id;
14use crate::dhke::{hash_e, hash_to_curve};
15use crate::{Amount, SECP256K1};
16
17#[derive(Debug, Error)]
19pub enum Error {
20 #[error("No DLEQ proof provided")]
22 MissingDleqProof,
23 #[error("Incomplete DLEQ proof")]
25 IncompleteDleqProof,
26 #[error("Invalid DLEQ proof")]
28 InvalidDleqProof,
29 #[error(transparent)]
31 DHKE(#[from] crate::dhke::Error),
32 #[error(transparent)]
34 NUT01(#[from] crate::nuts::nut01::Error),
35 #[error(transparent)]
37 Secp256k1(#[from] secp256k1::Error),
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44pub struct BlindSignatureDleq {
45 pub e: SecretKey,
47 pub s: SecretKey,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
55pub struct ProofDleq {
56 pub e: SecretKey,
58 pub s: SecretKey,
60 pub r: SecretKey,
62}
63
64impl ProofDleq {
65 pub fn new(e: SecretKey, s: SecretKey, r: SecretKey) -> Self {
67 Self { e, s, r }
68 }
69}
70
71fn verify_dleq(
73 blinded_message: PublicKey, blinded_signature: PublicKey, e: &SecretKey,
76 s: &SecretKey,
77 mint_pubkey: PublicKey, ) -> Result<(), Error> {
79 let e_bytes: [u8; 32] = e.to_secret_bytes();
80 let e: Scalar = e.as_scalar();
81
82 let a: PublicKey = mint_pubkey.mul_tweak(&SECP256K1, &e)?.into();
84
85 let a: PublicKey = a.negate(&SECP256K1).into();
87 let r1: PublicKey = s.public_key().combine(&a)?.into(); let s: Scalar = Scalar::from(s.deref().to_owned());
91 let b: PublicKey = blinded_message.mul_tweak(&SECP256K1, &s)?.into();
92
93 let c: PublicKey = blinded_signature.mul_tweak(&SECP256K1, &e)?.into();
95
96 let c: PublicKey = c.negate(&SECP256K1).into();
98 let r2: PublicKey = b.combine(&c)?.into();
99
100 let hash_e: [u8; 32] = hash_e([r1, r2, mint_pubkey, blinded_signature]);
102
103 if e_bytes != hash_e {
104 tracing::warn!("DLEQ on signature failed");
105 tracing::debug!("e_bytes: {:?}, hash_e: {:?}", e_bytes, hash_e);
106 return Err(Error::InvalidDleqProof);
107 }
108
109 Ok(())
110}
111
112fn calculate_dleq(
113 blinded_signature: PublicKey, blinded_message: &PublicKey, mint_secret_key: &SecretKey, ) -> Result<BlindSignatureDleq, Error> {
117 let r: SecretKey = SecretKey::generate();
119
120 let r1 = r.public_key();
122
123 let r_scal: Scalar = r.as_scalar();
125 let r2: PublicKey = blinded_message.mul_tweak(&SECP256K1, &r_scal)?.into();
126
127 let e: [u8; 32] = hash_e([r1, r2, mint_secret_key.public_key(), blinded_signature]);
129 let e_sk: SecretKey = SecretKey::from_slice(&e)?;
130
131 let s1: SecretKey = e_sk.mul_tweak(&mint_secret_key.as_scalar())?.into();
133
134 let s: SecretKey = r.add_tweak(&s1.to_scalar())?.into();
136
137 Ok(BlindSignatureDleq { e: e_sk, s })
138}
139
140impl Proof {
141 pub fn verify_dleq(&self, mint_pubkey: PublicKey) -> Result<(), Error> {
143 match &self.dleq {
144 Some(dleq) => {
145 let y = hash_to_curve(self.secret.as_bytes())?;
146
147 let r: Scalar = dleq.r.as_scalar();
148 let bs1: PublicKey = mint_pubkey.mul_tweak(&SECP256K1, &r)?.into();
149
150 let blinded_signature: PublicKey = self.c.combine(&bs1)?.into();
151 let blinded_message: PublicKey = y.combine(&dleq.r.public_key())?.into();
152
153 verify_dleq(
154 blinded_message,
155 blinded_signature,
156 &dleq.e,
157 &dleq.s,
158 mint_pubkey,
159 )
160 }
161 None => Err(Error::MissingDleqProof),
162 }
163 }
164}
165
166impl BlindSignature {
167 #[inline]
169 pub fn new(
170 amount: Amount,
171 blinded_signature: PublicKey,
172 keyset_id: Id,
173 blinded_message: &PublicKey,
174 mint_secretkey: SecretKey,
175 ) -> Result<Self, Error> {
176 Ok(Self {
177 amount,
178 keyset_id,
179 c: blinded_signature,
180 dleq: Some(calculate_dleq(
181 blinded_signature,
182 blinded_message,
183 &mint_secretkey,
184 )?),
185 })
186 }
187
188 #[inline]
190 pub fn verify_dleq(
191 &self,
192 mint_pubkey: PublicKey,
193 blinded_message: PublicKey,
194 ) -> Result<(), Error> {
195 match &self.dleq {
196 Some(dleq) => verify_dleq(blinded_message, self.c, &dleq.e, &dleq.s, mint_pubkey),
197 None => Err(Error::MissingDleqProof),
198 }
199 }
200
201 pub fn add_dleq_proof(
210 &mut self,
211 blinded_message: &PublicKey,
212 mint_secretkey: &SecretKey,
213 ) -> Result<(), Error> {
214 let dleq: BlindSignatureDleq = calculate_dleq(self.c, blinded_message, mint_secretkey)?;
215 self.dleq = Some(dleq);
216 Ok(())
217 }
218}
219
220#[cfg(test)]
221mod tests {
222
223 use std::str::FromStr;
224
225 use super::*;
226
227 #[test]
228 fn test_blind_signature_dleq() {
229 let blinded_sig = r#"{"amount":8,"id":"00882760bfa2eb41","C_":"02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2","dleq":{"e":"9818e061ee51d5c8edc3342369a554998ff7b4381c8652d724cdf46429be73d9","s":"9818e061ee51d5c8edc3342369a554998ff7b4381c8652d724cdf46429be73da"}}"#;
230
231 let blinded: BlindSignature = serde_json::from_str(blinded_sig).unwrap();
232
233 let secret_key =
234 SecretKey::from_hex("0000000000000000000000000000000000000000000000000000000000000001")
235 .unwrap();
236
237 let mint_key = secret_key.public_key();
238
239 let blinded_secret = PublicKey::from_str(
240 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
241 )
242 .unwrap();
243
244 blinded.verify_dleq(mint_key, blinded_secret).unwrap()
245 }
246
247 #[test]
248 fn test_proof_dleq() {
249 let proof = r#"{"amount": 1,"id": "00882760bfa2eb41","secret": "daf4dd00a2b68a0858a80450f52c8a7d2ccf87d375e43e216e0c571f089f63e9","C": "024369d2d22a80ecf78f3937da9d5f30c1b9f74f0c32684d583cca0fa6a61cdcfc","dleq": {"e": "b31e58ac6527f34975ffab13e70a48b6d2b0d35abc4b03f0151f09ee1a9763d4","s": "8fbae004c59e754d71df67e392b6ae4e29293113ddc2ec86592a0431d16306d8","r": "a6d13fcd7a18442e6076f5e1e7c887ad5de40a019824bdfa9fe740d302e8d861"}}"#;
250
251 let proof: Proof = serde_json::from_str(proof).unwrap();
252
253 let a: PublicKey = PublicKey::from_str(
255 "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
256 )
257 .unwrap();
258
259 assert!(proof.verify_dleq(a).is_ok());
260 }
261
262 #[test]
270 fn test_proof_dleq_wrong_mint_key() {
271 let proof = r#"{"amount": 1,"id": "00882760bfa2eb41","secret": "daf4dd00a2b68a0858a80450f52c8a7d2ccf87d375e43e216e0c571f089f63e9","C": "024369d2d22a80ecf78f3937da9d5f30c1b9f74f0c32684d583cca0fa6a61cdcfc","dleq": {"e": "b31e58ac6527f34975ffab13e70a48b6d2b0d35abc4b03f0151f09ee1a9763d4","s": "8fbae004c59e754d71df67e392b6ae4e29293113ddc2ec86592a0431d16306d8","r": "a6d13fcd7a18442e6076f5e1e7c887ad5de40a019824bdfa9fe740d302e8d861"}}"#;
272
273 let proof: Proof = serde_json::from_str(proof).unwrap();
274
275 let wrong_key: PublicKey = PublicKey::from_str(
277 "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
278 )
279 .unwrap();
280
281 assert!(proof.verify_dleq(wrong_key).is_err());
283 }
284
285 #[test]
293 fn test_proof_dleq_missing() {
294 let proof = r#"{"amount": 1,"id": "00882760bfa2eb41","secret": "daf4dd00a2b68a0858a80450f52c8a7d2ccf87d375e43e216e0c571f089f63e9","C": "024369d2d22a80ecf78f3937da9d5f30c1b9f74f0c32684d583cca0fa6a61cdcfc"}"#;
295
296 let proof: Proof = serde_json::from_str(proof).unwrap();
297
298 let a: PublicKey = PublicKey::from_str(
299 "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
300 )
301 .unwrap();
302
303 let result = proof.verify_dleq(a);
305 assert!(result.is_err());
306 assert!(matches!(result.unwrap_err(), Error::MissingDleqProof));
307 }
308
309 #[test]
316 fn test_blind_signature_dleq_wrong_key() {
317 let blinded_sig = r#"{"amount":8,"id":"00882760bfa2eb41","C_":"02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2","dleq":{"e":"9818e061ee51d5c8edc3342369a554998ff7b4381c8652d724cdf46429be73d9","s":"9818e061ee51d5c8edc3342369a554998ff7b4381c8652d724cdf46429be73da"}}"#;
318
319 let blinded: BlindSignature = serde_json::from_str(blinded_sig).unwrap();
320
321 let wrong_key =
323 SecretKey::from_hex("0000000000000000000000000000000000000000000000000000000000000002")
324 .unwrap();
325
326 let blinded_secret = PublicKey::from_str(
327 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
328 )
329 .unwrap();
330
331 assert!(blinded
333 .verify_dleq(wrong_key.public_key(), blinded_secret)
334 .is_err());
335 }
336
337 #[test]
345 fn test_blind_signature_dleq_tampered() {
346 let tampered_sig = r#"{"amount":8,"id":"00882760bfa2eb41","C_":"02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2","dleq":{"e":"0000000000000000000000000000000000000000000000000000000000000001","s":"0000000000000000000000000000000000000000000000000000000000000002"}}"#;
348
349 let blinded: BlindSignature = serde_json::from_str(tampered_sig).unwrap();
350
351 let secret_key =
352 SecretKey::from_hex("0000000000000000000000000000000000000000000000000000000000000001")
353 .unwrap();
354
355 let blinded_secret = PublicKey::from_str(
356 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
357 )
358 .unwrap();
359
360 assert!(blinded
362 .verify_dleq(secret_key.public_key(), blinded_secret)
363 .is_err());
364 }
365
366 #[test]
374 fn test_add_dleq_proof() {
375 use crate::nuts::nut02::Id;
376
377 let secret_key =
378 SecretKey::from_hex("0000000000000000000000000000000000000000000000000000000000000001")
379 .unwrap();
380
381 let blinded_message = PublicKey::from_str(
382 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
383 )
384 .unwrap();
385
386 let blinded_signature = PublicKey::from_str(
387 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
388 )
389 .unwrap();
390
391 let mut blind_sig = BlindSignature {
392 amount: Amount::from(1),
393 keyset_id: Id::from_str("00882760bfa2eb41").unwrap(),
394 c: blinded_signature,
395 dleq: None,
396 };
397
398 assert!(blind_sig.dleq.is_none());
400
401 blind_sig
403 .add_dleq_proof(&blinded_message, &secret_key)
404 .unwrap();
405
406 assert!(blind_sig.dleq.is_some());
408
409 assert!(blind_sig
411 .verify_dleq(secret_key.public_key(), blinded_message)
412 .is_ok());
413 }
414}