proxy_signatures/
lib.rs

1#![doc = include_str!("../README.md")]
2
3// # Ristretto255 Proxy Signatures
4//
5// Implementation of proxy signatures using the Ristretto255 prime-order group.
6// Based on MUO '96 proxy notion with KPW '97 "partial delegation with warrant" instantiation.
7//
8// ## Protocol Flow
9//
10// 1. Delegation: Original signer A delegates signing authority to proxy B with warrant w
11//    - A generates: `Rw = a*G`, `s_w = a + e_w*x_A` where `e_w = H(w || Rw)`
12// 2. Verification: B verifies: `s_w*G == Rw + e_w*Y_A`
13// 3. Key Derivation: B derives proxy key: `x_P = s_w + x_B (mod L)`, `Y_P = x_P*G`
14// 4. Signing: B signs message m using Schnorr signature under `x_P`
15// 5. Verification: Verifier reconstructs `Y_P` from `(w, Rw, Y_A, Y_B)` and verifies signature
16
17pub mod reexports {
18    pub use ct_codecs;
19    pub use libsodium_rs;
20}
21
22use ct_codecs::{Encoder, Hex};
23use libsodium_rs::crypto_core::ristretto255;
24use libsodium_rs::crypto_generichash;
25use libsodium_rs::crypto_scalarmult::ristretto255 as scalarmult_ristretto;
26use libsodium_rs::utils;
27use std::error::Error;
28use std::fmt;
29use std::hash::{Hash, Hasher};
30
31/// Type alias for Ristretto255 scalar values (32 bytes)
32pub type Scalar = [u8; ristretto255::SCALARBYTES];
33
34/// Type alias for Ristretto255 point values (32 bytes)
35pub type Point = [u8; ristretto255::BYTES];
36
37/// Custom error type for proxy signature operations
38#[derive(Debug)]
39pub enum ProxySignatureError {
40    /// Error from libsodium operations
41    Libsodium(libsodium_rs::SodiumError),
42    /// Invalid delegation token that failed verification
43    InvalidDelegation,
44    /// Proxy key derivation resulted in mismatched keys
45    ProxyKeyMismatch,
46    /// UTF-8 encoding error
47    Utf8Error(std::str::Utf8Error),
48    /// Invalid length for byte conversion
49    InvalidLength { expected: usize, actual: usize },
50}
51
52impl fmt::Display for ProxySignatureError {
53    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54        match self {
55            ProxySignatureError::Libsodium(e) => write!(f, "Libsodium error: {}", e),
56            ProxySignatureError::InvalidDelegation => write!(f, "Invalid delegation token"),
57            ProxySignatureError::ProxyKeyMismatch => {
58                write!(f, "Proxy key derivation mismatch")
59            }
60            ProxySignatureError::Utf8Error(e) => write!(f, "UTF-8 error: {}", e),
61            ProxySignatureError::InvalidLength { expected, actual } => {
62                write!(
63                    f,
64                    "Invalid length: expected {} bytes, got {}",
65                    expected, actual
66                )
67            }
68        }
69    }
70}
71
72impl Error for ProxySignatureError {}
73
74impl From<libsodium_rs::SodiumError> for ProxySignatureError {
75    fn from(error: libsodium_rs::SodiumError) -> Self {
76        ProxySignatureError::Libsodium(error)
77    }
78}
79
80impl From<std::str::Utf8Error> for ProxySignatureError {
81    fn from(error: std::str::Utf8Error) -> Self {
82        ProxySignatureError::Utf8Error(error)
83    }
84}
85
86/// Result type alias for proxy signature operations
87pub type Result<T> = std::result::Result<T, ProxySignatureError>;
88
89/// Cryptographic key pair containing a secret key and corresponding public key
90/// This represents original (non-proxy) keys that can create delegations
91#[derive(Clone, PartialEq, Eq)]
92pub struct KeyPair {
93    /// Secret scalar value
94    pub sk: Scalar,
95    /// Public point value
96    pub pk: Point,
97}
98
99impl KeyPair {
100    /// Generate a new random key pair
101    ///
102    /// # Returns
103    /// A new `KeyPair` with randomly generated secret and public keys
104    ///
105    /// # Errors
106    /// Returns an error if scalar multiplication fails
107    pub fn new() -> Result<Self> {
108        let sk = ristretto255::scalar_random();
109        let pk = scalarmult_ristretto::scalarmult_base(&sk)?;
110        Ok(KeyPair { sk, pk })
111    }
112
113    /// Get a reference to the public key
114    pub fn public_key(&self) -> &Point {
115        &self.pk
116    }
117
118    /// Get a reference to the secret key
119    pub fn secret_key(&self) -> &Scalar {
120        &self.sk
121    }
122
123    /// Create a KeyPair from existing secret key
124    ///
125    /// # Arguments
126    /// * `sk` - Secret key scalar
127    ///
128    /// # Returns
129    /// A new KeyPair with the given secret key and derived public key
130    ///
131    /// # Errors
132    /// Returns an error if scalar multiplication fails
133    pub fn from_secret_key(sk: Scalar) -> Result<Self> {
134        let pk = scalarmult_ristretto::scalarmult_base(&sk)?;
135        Ok(KeyPair { sk, pk })
136    }
137}
138
139impl fmt::Debug for KeyPair {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        f.debug_struct("KeyPair")
142            .field("sk", &"<redacted>")
143            .field("pk", &Hex::encode_to_string(self.pk).unwrap())
144            .finish()
145    }
146}
147
148impl fmt::Display for KeyPair {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        write!(
151            f,
152            "KeyPair(pk: {})",
153            &Hex::encode_to_string(self.pk).unwrap()[..8]
154        )
155    }
156}
157
158impl AsRef<Point> for KeyPair {
159    fn as_ref(&self) -> &Point {
160        &self.pk
161    }
162}
163
164impl From<&KeyPair> for Point {
165    fn from(keypair: &KeyPair) -> Self {
166        keypair.pk
167    }
168}
169
170impl Hash for KeyPair {
171    fn hash<H: Hasher>(&self, state: &mut H) {
172        // Only hash the public key, not the secret key
173        self.pk.hash(state);
174    }
175}
176
177/// Hash multiple byte arrays to produce a Ristretto255 scalar
178///
179/// # Arguments
180/// * `parts` - Slice of byte array references to hash together
181///
182/// # Returns
183/// A scalar value derived from the hash of concatenated inputs
184///
185/// # Errors
186/// Returns an error if hashing or scalar reduction fails
187fn hash_to_scalar(parts: &[&[u8]]) -> Result<Scalar> {
188    let mut state = crypto_generichash::State::new(None, 64)?;
189    for part in parts {
190        state.update(part);
191    }
192    let hash = state.finalize();
193    Ok(ristretto255::scalar_reduce(&hash)?)
194}
195
196/// Schnorr signature containing the signature scalar and commitment point
197#[derive(Clone, PartialEq, Eq)]
198pub struct SchnorrSignature {
199    /// Signature scalar s = k + e*sk
200    pub s: Scalar,
201    /// Commitment point R = k*G
202    pub r: Point,
203}
204
205impl SchnorrSignature {
206    /// Create a Schnorr signature for a message using a secret key
207    ///
208    /// # Arguments
209    /// * `sk` - Secret key scalar
210    /// * `msg` - Message bytes to sign
211    ///
212    /// # Returns
213    /// A new Schnorr signature
214    ///
215    /// # Errors
216    /// Returns an error if any cryptographic operation fails
217    pub fn sign(sk: &Scalar, msg: &[u8]) -> Result<Self> {
218        let k = ristretto255::scalar_random();
219        let r = scalarmult_ristretto::scalarmult_base(&k)?;
220
221        let e = hash_to_scalar(&[msg, &r])?;
222        let e_times_sk = ristretto255::scalar_mul(&e, sk)?;
223        let s = ristretto255::scalar_add(&k, &e_times_sk)?;
224
225        Ok(SchnorrSignature { s, r })
226    }
227
228    /// Verify a Schnorr signature against a public key and message
229    ///
230    /// # Arguments
231    /// * `pk` - Public key point
232    /// * `msg` - Message bytes that were signed
233    ///
234    /// # Returns
235    /// `true` if the signature is valid, `false` otherwise
236    ///
237    /// # Errors
238    /// Returns an error if any cryptographic operation fails
239    pub fn verify(&self, pk: &Point, msg: &[u8]) -> Result<bool> {
240        let e = hash_to_scalar(&[msg, &self.r])?;
241        let left = scalarmult_ristretto::scalarmult_base(&self.s)?;
242        let e_times_pk = scalarmult_ristretto::scalarmult(&e, pk)?;
243        let right = ristretto255::add(&self.r, &e_times_pk)?;
244
245        // Use constant-time comparison
246        Ok(utils::memcmp(&left, &right))
247    }
248}
249
250impl fmt::Debug for SchnorrSignature {
251    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252        f.debug_struct("SchnorrSignature")
253            .field("s", &Hex::encode_to_string(self.s).unwrap())
254            .field("r", &Hex::encode_to_string(self.r).unwrap())
255            .finish()
256    }
257}
258
259impl fmt::Display for SchnorrSignature {
260    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261        write!(
262            f,
263            "Sig({}...)",
264            &Hex::encode_to_string(self.s).unwrap()[..8]
265        )
266    }
267}
268
269impl Hash for SchnorrSignature {
270    fn hash<H: Hasher>(&self, state: &mut H) {
271        self.s.hash(state);
272        self.r.hash(state);
273    }
274}
275
276/// Delegation token that authorizes a proxy to sign on behalf of the original signer
277#[derive(Clone, PartialEq, Eq)]
278pub struct DelegationToken {
279    /// Warrant describing the delegation terms
280    pub warrant: Vec<u8>,
281    /// Commitment point Rw = a*G
282    pub rw: Point,
283    /// Signature scalar sw = a + e_w*x_A
284    pub sw: Scalar,
285    /// Proxy's public key (bound to prevent delegation transfer)
286    pub b_pub: Point,
287}
288
289impl DelegationToken {
290    /// Get the warrant as a string slice if it's valid UTF-8
291    pub fn warrant_str(&self) -> Result<&str> {
292        std::str::from_utf8(&self.warrant).map_err(ProxySignatureError::Utf8Error)
293    }
294
295    /// Get the warrant as a string, replacing invalid UTF-8 sequences
296    pub fn warrant_string_lossy(&self) -> std::borrow::Cow<'_, str> {
297        String::from_utf8_lossy(&self.warrant)
298    }
299
300    /// Get the proxy's public key
301    pub fn proxy_public_key(&self) -> &Point {
302        &self.b_pub
303    }
304
305    /// Create a delegation token authorizing a specific proxy
306    ///
307    /// # Arguments
308    /// * `a_keys` - Original signer's key pair (only accepts non-proxy KeyPair type)
309    /// * `b_pub` - Proxy's public key (cryptographically bound to prevent transfer)
310    /// * `warrant` - Bytes describing delegation terms
311    ///
312    /// # Returns
313    /// A new delegation token bound to the specific proxy
314    ///
315    /// # Errors
316    /// Returns an error if any cryptographic operation fails
317    ///
318    /// # Type Safety
319    /// This function only accepts `KeyPair`, not `ProxyKeyPair`, preventing
320    /// proxy-to-proxy redelegation at compile time.
321    pub fn create(a_keys: &KeyPair, b_pub: &Point, warrant: &[u8]) -> Result<Self> {
322        let a_nonce = ristretto255::scalar_random();
323        let rw = scalarmult_ristretto::scalarmult_base(&a_nonce)?;
324
325        // Include proxy's public key in the hash to bind the delegation
326        let e_w = hash_to_scalar(&[warrant, &rw, b_pub])?;
327        let e_times_sk = ristretto255::scalar_mul(&e_w, &a_keys.sk)?;
328        let sw = ristretto255::scalar_add(&a_nonce, &e_times_sk)?;
329
330        Ok(DelegationToken {
331            warrant: warrant.to_vec(),
332            rw,
333            sw,
334            b_pub: *b_pub,
335        })
336    }
337
338    /// Verify that a delegation token is valid for the given public key and proxy
339    ///
340    /// # Arguments
341    /// * `a_pub` - Original signer's public key
342    ///
343    /// # Returns
344    /// `true` if the delegation is valid for this specific proxy, `false` otherwise
345    ///
346    /// # Errors
347    /// Returns an error if any cryptographic operation fails
348    pub fn verify(&self, a_pub: &Point) -> Result<bool> {
349        // Include proxy's public key in the hash to verify binding
350        let e_w = hash_to_scalar(&[&self.warrant, &self.rw, &self.b_pub])?;
351        let left = scalarmult_ristretto::scalarmult_base(&self.sw)?;
352        let e_times_a = scalarmult_ristretto::scalarmult(&e_w, a_pub)?;
353        let right = ristretto255::add(&self.rw, &e_times_a)?;
354
355        // Use constant-time comparison
356        Ok(utils::memcmp(&left, &right))
357    }
358}
359
360impl fmt::Debug for DelegationToken {
361    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362        f.debug_struct("DelegationToken")
363            .field("warrant", &String::from_utf8_lossy(&self.warrant))
364            .field("rw", &Hex::encode_to_string(self.rw).unwrap())
365            .field("sw", &"<redacted>")
366            .field("b_pub", &Hex::encode_to_string(self.b_pub).unwrap())
367            .finish()
368    }
369}
370
371impl fmt::Display for DelegationToken {
372    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373        write!(
374            f,
375            "DelegationToken(warrant: {:?}, proxy: {}...)",
376            String::from_utf8_lossy(&self.warrant),
377            &Hex::encode_to_string(self.b_pub).unwrap()[..8]
378        )
379    }
380}
381
382/// Public context needed to verify proxy signatures
383#[derive(Clone, PartialEq, Eq)]
384pub struct ProxyPublicContext {
385    /// Warrant describing the delegation
386    pub warrant: Vec<u8>,
387    /// Commitment point from delegation token
388    pub rw: Point,
389    /// Proxy's combined public key
390    pub yp: Point,
391}
392
393impl ProxyPublicContext {
394    /// Create a new ProxyPublicContext
395    pub fn new(warrant: Vec<u8>, rw: Point, yp: Point) -> Self {
396        ProxyPublicContext { warrant, rw, yp }
397    }
398
399    /// Create context from a delegation token and proxy public key
400    pub fn from_token(token: &DelegationToken, yp: Point) -> Self {
401        ProxyPublicContext {
402            warrant: token.warrant.clone(),
403            rw: token.rw,
404            yp,
405        }
406    }
407
408    /// Get the warrant as a string if it's valid UTF-8
409    pub fn warrant_str(&self) -> Result<&str> {
410        std::str::from_utf8(&self.warrant).map_err(ProxySignatureError::Utf8Error)
411    }
412}
413
414impl fmt::Debug for ProxyPublicContext {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        f.debug_struct("ProxyPublicContext")
417            .field("warrant", &String::from_utf8_lossy(&self.warrant))
418            .field("rw", &Hex::encode_to_string(self.rw).unwrap())
419            .field("yp", &Hex::encode_to_string(self.yp).unwrap())
420            .finish()
421    }
422}
423
424impl fmt::Display for ProxyPublicContext {
425    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426        write!(
427            f,
428            "ProxyContext(warrant: {:?})",
429            String::from_utf8_lossy(&self.warrant)
430        )
431    }
432}
433
434/// Proxy key pair derived from delegation
435/// This type cannot be used to create new delegations (prevents redelegation chains)
436#[derive(Clone, PartialEq, Eq)]
437pub struct ProxyKeyPair {
438    /// Proxy's derived secret key
439    pub sk: Scalar,
440    /// Proxy's derived public key
441    pub pk: Point,
442}
443
444impl ProxyKeyPair {
445    /// Sign a message using the proxy key
446    ///
447    /// # Arguments
448    /// * `message` - Message to sign
449    ///
450    /// # Returns
451    /// A Schnorr signature created with the proxy key
452    ///
453    /// # Errors
454    /// Returns an error if signature creation fails
455    pub fn sign(&self, message: &[u8]) -> Result<SchnorrSignature> {
456        SchnorrSignature::sign(&self.sk, message)
457    }
458
459    /// Get a reference to the public key
460    pub fn public_key(&self) -> &Point {
461        &self.pk
462    }
463
464    /// Get a reference to the secret key
465    pub fn secret_key(&self) -> &Scalar {
466        &self.sk
467    }
468
469    /// Verify a signature with this proxy key
470    ///
471    /// # Arguments
472    /// * `message` - Message that was signed
473    /// * `signature` - Signature to verify
474    ///
475    /// # Returns
476    /// `true` if the signature is valid, `false` otherwise
477    ///
478    /// # Errors
479    /// Returns an error if verification fails
480    pub fn verify(&self, message: &[u8], signature: &SchnorrSignature) -> Result<bool> {
481        signature.verify(&self.pk, message)
482    }
483}
484
485impl fmt::Debug for ProxyKeyPair {
486    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487        f.debug_struct("ProxyKeyPair")
488            .field("sk", &"<redacted>")
489            .field("pk", &Hex::encode_to_string(self.pk).unwrap())
490            .finish()
491    }
492}
493
494impl fmt::Display for ProxyKeyPair {
495    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
496        write!(
497            f,
498            "ProxyKeyPair(pk: {}...)",
499            &Hex::encode_to_string(self.pk).unwrap()[..8]
500        )
501    }
502}
503
504impl AsRef<Point> for ProxyKeyPair {
505    fn as_ref(&self) -> &Point {
506        &self.pk
507    }
508}
509
510impl From<&ProxyKeyPair> for Point {
511    fn from(proxy: &ProxyKeyPair) -> Self {
512        proxy.pk
513    }
514}
515
516impl Hash for ProxyKeyPair {
517    fn hash<H: Hasher>(&self, state: &mut H) {
518        // Only hash the public key, not the secret key
519        self.pk.hash(state);
520    }
521}
522
523/// Derived proxy keys with named fields for better type safety
524#[derive(Clone, PartialEq, Eq, Debug)]
525pub struct ProxyKeys {
526    /// Proxy's derived secret key
527    pub sk: Scalar,
528    /// Proxy's derived public key
529    pub pk: Point,
530}
531
532impl ProxyKeys {
533    /// Create a new ProxyKeys instance
534    pub fn new(sk: Scalar, pk: Point) -> Self {
535        ProxyKeys { sk, pk }
536    }
537}
538
539/// Derive proxy signing keys from a delegation token
540///
541/// # Arguments
542/// * `b_keys` - Proxy's key pair
543/// * `a_pub` - Original signer's public key
544/// * `token` - Valid delegation token bound to this proxy
545///
546/// # Returns
547/// A `ProxyKeys` struct with named `sk` and `pk` fields for better type safety
548///
549/// # Errors
550/// Returns `InvalidDelegation` if token verification fails, `ProxyKeyMismatch`
551/// if the proxy's public key doesn't match the bound key, or if derived keys
552/// don't match expected values
553pub fn derive_proxy_keys(
554    b_keys: &KeyPair,
555    a_pub: &Point,
556    token: &DelegationToken,
557) -> Result<ProxyKeys> {
558    // Verify that this proxy is the one bound to the delegation token
559    if !utils::memcmp(&b_keys.pk, &token.b_pub) {
560        return Err(ProxySignatureError::ProxyKeyMismatch);
561    }
562
563    if !token.verify(a_pub)? {
564        return Err(ProxySignatureError::InvalidDelegation);
565    }
566
567    // Use the bound proxy key in the hash
568    let e_w = hash_to_scalar(&[&token.warrant, &token.rw, &token.b_pub])?;
569    let xp = ristretto255::scalar_add(&token.sw, &b_keys.sk)?;
570
571    // Compute YP both ways for verification
572    let yp_from_secret = scalarmult_ristretto::scalarmult_base(&xp)?;
573    let e_times_a = scalarmult_ristretto::scalarmult(&e_w, a_pub)?;
574    let temp = ristretto255::add(&token.rw, &e_times_a)?;
575    let yp_from_relation = ristretto255::add(&temp, &b_keys.pk)?;
576
577    // Use constant-time comparison
578    if !utils::memcmp(&yp_from_secret, &yp_from_relation) {
579        return Err(ProxySignatureError::ProxyKeyMismatch);
580    }
581
582    Ok(ProxyKeys::new(xp, yp_from_secret))
583}
584
585/// Derive proxy signing key pair from a delegation token
586///
587/// # Arguments
588/// * `b_keys` - Proxy's original key pair
589/// * `a_pub` - Original signer's public key
590/// * `token` - Valid delegation token bound to this proxy
591///
592/// # Returns
593/// A `ProxyKeyPair` that cannot be used to create new delegations (type-safe)
594///
595/// # Errors
596/// Returns `InvalidDelegation` if token verification fails, `ProxyKeyMismatch`
597/// if the proxy's public key doesn't match the bound key
598pub fn derive_proxy_key_pair(
599    b_keys: &KeyPair,
600    a_pub: &Point,
601    token: &DelegationToken,
602) -> Result<ProxyKeyPair> {
603    let keys = derive_proxy_keys(b_keys, a_pub, token)?;
604    Ok(ProxyKeyPair {
605        sk: keys.sk,
606        pk: keys.pk,
607    })
608}
609
610/// Create a proxy signature for a message
611///
612/// # Arguments
613/// * `xp` - Proxy's derived secret key
614/// * `message` - Message to sign
615///
616/// # Returns
617/// A Schnorr signature created with the proxy key
618///
619/// # Errors
620/// Returns an error if signature creation fails
621pub fn proxy_sign(xp: &Scalar, message: &[u8]) -> Result<SchnorrSignature> {
622    SchnorrSignature::sign(xp, message)
623}
624
625/// Verify a proxy signature
626///
627/// # Arguments
628/// * `a_pub` - Original signer's public key
629/// * `b_pub` - Proxy's public key
630/// * `message` - Message that was signed
631/// * `sig` - Signature to verify
632/// * `ctx` - Public context containing warrant and derived keys
633///
634/// # Returns
635/// `true` if the signature is valid, `false` otherwise
636///
637/// # Errors
638/// Returns an error if any cryptographic operation fails
639pub fn proxy_verify(
640    a_pub: &Point,
641    b_pub: &Point,
642    message: &[u8],
643    sig: &SchnorrSignature,
644    ctx: &ProxyPublicContext,
645) -> Result<bool> {
646    // Include proxy's public key in the hash for bound delegation verification
647    let e_w = hash_to_scalar(&[&ctx.warrant, &ctx.rw, b_pub])?;
648    let e_times_a = scalarmult_ristretto::scalarmult(&e_w, a_pub)?;
649    let temp = ristretto255::add(&ctx.rw, &e_times_a)?;
650    let yp = ristretto255::add(&temp, b_pub)?;
651
652    // Verify that the provided YP matches the computed one using constant-time comparison
653    if !utils::memcmp(&ctx.yp, &yp) {
654        return Ok(false);
655    }
656
657    sig.verify(&yp, message)
658}
659
660#[cfg(test)]
661mod tests {
662    use super::*;
663
664    #[test]
665    fn test_key_generation() -> Result<()> {
666        let keypair = KeyPair::new()?;
667        assert_eq!(keypair.sk.len(), ristretto255::SCALARBYTES);
668        assert_eq!(keypair.pk.len(), ristretto255::BYTES);
669
670        // Verify that multiple key generations produce different keys
671        let keypair2 = KeyPair::new()?;
672        assert!(!utils::memcmp(&keypair.sk, &keypair2.sk));
673        assert!(!utils::memcmp(&keypair.pk, &keypair2.pk));
674
675        Ok(())
676    }
677
678    #[test]
679    fn test_schnorr_signature() -> Result<()> {
680        let keypair = KeyPair::new()?;
681        let message = b"Test message for signing";
682
683        // Sign and verify
684        let sig = SchnorrSignature::sign(&keypair.sk, message)?;
685        assert!(sig.verify(&keypair.pk, message)?);
686
687        // Wrong message should fail
688        let wrong_message = b"Different message";
689        assert!(!sig.verify(&keypair.pk, wrong_message)?);
690
691        // Wrong public key should fail
692        let other_keypair = KeyPair::new()?;
693        assert!(!sig.verify(&other_keypair.pk, message)?);
694
695        Ok(())
696    }
697
698    #[test]
699    fn test_delegation_token() -> Result<()> {
700        let a = KeyPair::new()?;
701        let b = KeyPair::new()?;
702        let warrant = b"Proxy B may sign for A until 2024-12-31";
703
704        // Create and verify delegation
705        let token = DelegationToken::create(&a, &b.pk, warrant)?;
706        assert!(token.verify(&a.pk)?);
707
708        // Verification with wrong public key should fail
709        assert!(!token.verify(&b.pk)?);
710
711        // Modified warrant should fail
712        let mut bad_token = token.clone();
713        bad_token.warrant = b"Modified warrant".to_vec();
714        assert!(!bad_token.verify(&a.pk)?);
715
716        Ok(())
717    }
718
719    #[test]
720    fn test_proxy_signature_flow() -> Result<()> {
721        // Setup: Create key pairs
722        let a = KeyPair::new()?; // Original signer
723        let b = KeyPair::new()?; // Proxy signer
724
725        // Step 1: Create delegation
726        let warrant = b"Proxy B may sign on behalf of A for project X";
727        let token = DelegationToken::create(&a, &b.pk, warrant)?;
728        assert!(token.verify(&a.pk)?);
729
730        // Step 2: Derive proxy keys
731        let keys = derive_proxy_keys(&b, &a.pk, &token)?;
732
733        // Step 3: Create proxy signature
734        let message = b"Authorize payment of 100 units";
735        let sig = proxy_sign(&keys.sk, message)?;
736
737        // Step 4: Verify proxy signature
738        let ctx = ProxyPublicContext {
739            warrant: warrant.to_vec(),
740            rw: token.rw,
741            yp: keys.pk,
742        };
743
744        assert!(proxy_verify(&a.pk, &b.pk, message, &sig, &ctx)?);
745
746        // Wrong message should fail
747        let wrong_message = b"Authorize payment of 200 units";
748        assert!(!proxy_verify(&a.pk, &b.pk, wrong_message, &sig, &ctx)?);
749
750        // Wrong warrant in context should fail
751        let bad_ctx = ProxyPublicContext {
752            warrant: b"Different warrant".to_vec(),
753            rw: token.rw,
754            yp: keys.pk,
755        };
756        assert!(!proxy_verify(&a.pk, &b.pk, message, &sig, &bad_ctx)?);
757
758        // Wrong proxy public key should fail
759        let c = KeyPair::new()?;
760        assert!(!proxy_verify(&a.pk, &c.pk, message, &sig, &ctx)?);
761
762        Ok(())
763    }
764
765    #[test]
766    fn test_invalid_delegation() -> Result<()> {
767        let a = KeyPair::new()?;
768        let b = KeyPair::new()?;
769        let warrant = b"Test warrant";
770
771        // Create valid token but tamper with it
772        let mut token = DelegationToken::create(&a, &b.pk, warrant)?;
773
774        // Tamper with sw
775        token.sw[0] ^= 0xFF;
776
777        // Deriving proxy keys should fail
778        match derive_proxy_keys(&b, &a.pk, &token) {
779            Err(ProxySignatureError::InvalidDelegation) => (),
780            _ => panic!("Expected InvalidDelegation error"),
781        }
782
783        Ok(())
784    }
785
786    #[test]
787    fn test_proxy_binding_prevents_transfer() -> Result<()> {
788        let a = KeyPair::new()?;
789        let b = KeyPair::new()?;
790        let c = KeyPair::new()?; // Different proxy
791        let warrant = b"Proxy B may sign for A";
792
793        // Create delegation for proxy B
794        let token = DelegationToken::create(&a, &b.pk, warrant)?;
795        assert!(token.verify(&a.pk)?);
796
797        // B can derive proxy keys
798        let keys_b = derive_proxy_keys(&b, &a.pk, &token)?;
799        assert_eq!(keys_b.sk.len(), 32);
800        assert_eq!(keys_b.pk.len(), 32);
801
802        // C cannot use B's delegation token (proxy binding prevents this)
803        match derive_proxy_keys(&c, &a.pk, &token) {
804            Err(ProxySignatureError::ProxyKeyMismatch) => (),
805            _ => panic!("Expected ProxyKeyMismatch error for wrong proxy"),
806        }
807
808        // Create proxy signature with B's keys
809        let message = b"Test message";
810        let sig = proxy_sign(&keys_b.sk, message)?;
811
812        // Verify works with correct proxy
813        let ctx = ProxyPublicContext {
814            warrant: warrant.to_vec(),
815            rw: token.rw,
816            yp: keys_b.pk,
817        };
818        assert!(proxy_verify(&a.pk, &b.pk, message, &sig, &ctx)?);
819
820        // Verify fails with wrong proxy public key (C instead of B)
821        assert!(!proxy_verify(&a.pk, &c.pk, message, &sig, &ctx)?);
822
823        Ok(())
824    }
825
826    #[test]
827    fn test_proxy_cannot_redelegate() -> Result<()> {
828        let a = KeyPair::new()?;
829        let b = KeyPair::new()?;
830        let warrant_ab = b"Proxy B may sign for A";
831
832        // A creates delegation for B
833        let token_ab = DelegationToken::create(&a, &b.pk, warrant_ab)?;
834        assert!(token_ab.verify(&a.pk)?);
835
836        // B derives proxy keys - returns ProxyKeyPair type
837        let proxy_keys = derive_proxy_key_pair(&b, &a.pk, &token_ab)?;
838
839        // ProxyKeyPair can sign messages
840        let message = b"Test message";
841        let sig = proxy_keys.sign(message)?;
842        assert!(sig.verify(&proxy_keys.pk, message)?);
843
844        // Type safety: Cannot pass ProxyKeyPair to DelegationToken::create
845        // The following would not compile:
846        // let token_bc = DelegationToken::create(&proxy_keys, &c.pk, warrant_bc);
847        //
848        // This prevents proxy-to-proxy redelegation at compile time!
849        // No runtime check needed - the type system enforces this constraint.
850
851        // Verify that proxy keys work for their intended purpose (signing)
852        let sig2 = proxy_sign(&proxy_keys.sk, b"Another message")?;
853        assert_eq!(sig2.s.len(), 32);
854
855        Ok(())
856    }
857
858    #[test]
859    fn test_trait_implementations() -> Result<()> {
860        // Test Debug trait - doesn't expose secrets
861        let keypair = KeyPair::new()?;
862        let debug_str = format!("{:?}", keypair);
863        assert!(debug_str.contains("<redacted>"));
864        assert!(!debug_str.contains(&Hex::encode_to_string(keypair.sk).unwrap()));
865
866        // Test Display trait
867        let display_str = format!("{}", keypair);
868        assert!(display_str.starts_with("KeyPair(pk: "));
869
870        // Test PartialEq and Eq
871        let keypair2 = KeyPair::from_secret_key(keypair.sk)?;
872        assert_eq!(keypair, keypair2);
873
874        // Test Hash trait - can use in HashMap
875        use std::collections::HashMap;
876        let mut map = HashMap::new();
877        map.insert(keypair.clone(), "test value");
878        assert_eq!(map.get(&keypair2), Some(&"test value"));
879
880        // Test convenience methods
881        assert_eq!(keypair.public_key(), &keypair.pk);
882        assert_eq!(keypair.secret_key(), &keypair.sk);
883
884        // Test AsRef trait
885        let pk_ref: &Point = keypair.as_ref();
886        assert_eq!(pk_ref, &keypair.pk);
887
888        // Test From trait
889        let pk: Point = Point::from(&keypair);
890        assert_eq!(pk, keypair.pk);
891
892        // Test DelegationToken traits
893        let b = KeyPair::new()?;
894        let warrant = b"Test warrant";
895        let token = DelegationToken::create(&keypair, &b.pk, warrant)?;
896
897        // Test Debug doesn't expose sw
898        let token_debug = format!("{:?}", token);
899        assert!(token_debug.contains("<redacted>"));
900        assert!(!token_debug.contains(&Hex::encode_to_string(token.sw).unwrap()));
901
902        // Test Display
903        let token_display = format!("{}", token);
904        assert!(token_display.contains("Test warrant"));
905
906        // Test convenience methods
907        assert_eq!(token.warrant_str()?, "Test warrant");
908        assert_eq!(token.proxy_public_key(), &b.pk);
909
910        // Test ProxyPublicContext builder
911        let ctx = ProxyPublicContext::from_token(&token, keypair.pk);
912        assert_eq!(ctx.warrant, token.warrant);
913        assert_eq!(ctx.rw, token.rw);
914
915        // Test SchnorrSignature traits
916        let msg = b"Test message";
917        let sig = SchnorrSignature::sign(&keypair.sk, msg)?;
918        let sig2 = SchnorrSignature::sign(&keypair.sk, msg)?;
919        assert_ne!(sig, sig2); // Different nonce, different signature
920
921        // Test Hash for SchnorrSignature
922        let mut sig_map = HashMap::new();
923        sig_map.insert(sig.clone(), "signature 1");
924        assert_eq!(sig_map.get(&sig), Some(&"signature 1"));
925
926        Ok(())
927    }
928}