Skip to main content

nym_compact_ecash/scheme/
keygen.rs

1// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::error::{CompactEcashError, Result};
5use crate::scheme::aggregation::aggregate_verification_keys;
6use crate::scheme::SignerIndex;
7use crate::traits::Bytable;
8use crate::utils::{hash_to_scalar, Polynomial};
9use crate::utils::{
10    try_deserialize_g1_projective, try_deserialize_g2_projective, try_deserialize_scalar,
11    try_deserialize_scalar_vec,
12};
13use crate::{ecash_group_parameters, Base58};
14use core::borrow::Borrow;
15use core::iter::Sum;
16use core::ops::{Add, Mul};
17use group::{Curve, GroupEncoding};
18use nym_bls12_381_fork::{G1Projective, G2Projective, Scalar};
19use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
20use serde::{Deserialize, Serialize};
21use zeroize::{Zeroize, ZeroizeOnDrop};
22
23#[derive(Debug, PartialEq, Clone, Zeroize, ZeroizeOnDrop)]
24pub struct SecretKeyAuth {
25    pub(crate) x: Scalar,
26    pub(crate) ys: Vec<Scalar>,
27}
28
29impl PemStorableKey for SecretKeyAuth {
30    type Error = CompactEcashError;
31
32    fn pem_type() -> &'static str {
33        "ECASH SECRET KEY"
34    }
35
36    fn to_bytes(&self) -> Vec<u8> {
37        self.to_bytes()
38    }
39
40    fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
41        Self::from_bytes(bytes)
42    }
43}
44
45impl TryFrom<&[u8]> for SecretKeyAuth {
46    type Error = CompactEcashError;
47
48    fn try_from(bytes: &[u8]) -> Result<SecretKeyAuth> {
49        // There should be x and at least one y
50        if bytes.len() < 32 * 2 + 8 || !(bytes.len() - 8).is_multiple_of(32) {
51            return Err(CompactEcashError::DeserializationInvalidLength {
52                actual: bytes.len(),
53                modulus_target: bytes.len() - 8,
54                target: 32 * 2 + 8,
55                modulus: 32,
56                object: "secret key".to_string(),
57            });
58        }
59
60        //SAFETY : slice to array conversion after a length check
61        #[allow(clippy::unwrap_used)]
62        let x_bytes: [u8; 32] = bytes[..32].try_into().unwrap();
63
64        #[allow(clippy::unwrap_used)]
65        let ys_len = u64::from_le_bytes(bytes[32..40].try_into().unwrap());
66        let actual_ys_len = (bytes.len() - 40) / 32;
67
68        if ys_len as usize != actual_ys_len {
69            return Err(CompactEcashError::DeserializationLengthMismatch {
70                type_name: "Secret_key ys".into(),
71                expected: ys_len as usize,
72                actual: actual_ys_len,
73            });
74        }
75
76        let x = try_deserialize_scalar(&x_bytes)?;
77        let ys = try_deserialize_scalar_vec(ys_len, &bytes[40..])?;
78
79        Ok(SecretKeyAuth { x, ys })
80    }
81}
82
83impl SecretKeyAuth {
84    /// Following a (distributed) key generation process, scalar values can be obtained
85    /// outside of the normal key generation process.
86    pub fn create_from_raw(x: Scalar, ys: Vec<Scalar>) -> Self {
87        Self { x, ys }
88    }
89
90    /// Extract the Scalar copy of the underlying secrets.
91    /// The caller of this function must exercise extreme care to not misuse the data and ensuring it gets zeroized
92    pub fn hazmat_to_raw(&self) -> (Scalar, Vec<Scalar>) {
93        (self.x, self.ys.clone())
94    }
95
96    pub fn size(&self) -> usize {
97        self.ys.len()
98    }
99
100    pub fn verification_key(&self) -> VerificationKeyAuth {
101        let params = ecash_group_parameters();
102        let g1 = params.gen1();
103        let g2 = params.gen2();
104        VerificationKeyAuth {
105            alpha: g2 * self.x,
106            beta_g1: self.ys.iter().map(|y| g1 * y).collect(),
107            beta_g2: self.ys.iter().map(|y| g2 * y).collect(),
108        }
109    }
110
111    pub fn to_bytes(&self) -> Vec<u8> {
112        let ys_len = self.ys.len();
113        let mut bytes = Vec::with_capacity(8 + (ys_len + 1) * 32);
114        bytes.extend_from_slice(&self.x.to_bytes());
115        bytes.extend_from_slice(&ys_len.to_le_bytes());
116        for y in self.ys.iter() {
117            bytes.extend_from_slice(&y.to_bytes())
118        }
119        bytes
120    }
121
122    pub fn from_bytes(bytes: &[u8]) -> Result<SecretKeyAuth> {
123        SecretKeyAuth::try_from(bytes)
124    }
125}
126
127#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
128pub struct VerificationKeyAuth {
129    pub(crate) alpha: G2Projective,
130    pub(crate) beta_g1: Vec<G1Projective>,
131    pub(crate) beta_g2: Vec<G2Projective>,
132}
133
134impl PemStorableKey for VerificationKeyAuth {
135    type Error = CompactEcashError;
136
137    fn pem_type() -> &'static str {
138        "ECASH VERIFICATION KEY"
139    }
140
141    fn to_bytes(&self) -> Vec<u8> {
142        self.to_bytes()
143    }
144
145    fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
146        Self::from_bytes(bytes)
147    }
148}
149
150impl TryFrom<&[u8]> for VerificationKeyAuth {
151    type Error = CompactEcashError;
152
153    fn try_from(bytes: &[u8]) -> Result<VerificationKeyAuth> {
154        // There should be at least alpha, one betaG1 and one betaG2 and their length
155        if bytes.len() < 96 * 2 + 48 + 8 || !(bytes.len() - 8 - 96).is_multiple_of(96 + 48) {
156            return Err(CompactEcashError::DeserializationInvalidLength {
157                actual: bytes.len(),
158                modulus_target: bytes.len() - 8 - 96,
159                target: 96 * 2 + 48 + 8,
160                modulus: 96 + 48,
161                object: "verification key".to_string(),
162            });
163        }
164
165        //SAFETY : slice to array conversion after a length check
166        #[allow(clippy::unwrap_used)]
167        let alpha_bytes: [u8; 96] = bytes[..96].try_into().unwrap();
168        #[allow(clippy::unwrap_used)]
169        let betas_len = u64::from_le_bytes(bytes[96..104].try_into().unwrap());
170
171        let actual_betas_len = (bytes.len() - 104) / (96 + 48);
172
173        if betas_len as usize != actual_betas_len {
174            return Err(CompactEcashError::DeserializationLengthMismatch {
175                type_name: "Verification_key betas".into(),
176                expected: betas_len as usize,
177                actual: actual_betas_len,
178            });
179        }
180
181        let alpha = try_deserialize_g2_projective(&alpha_bytes)?;
182
183        let mut beta_g1 = Vec::with_capacity(betas_len as usize);
184        let mut beta_g1_end: u64 = 0;
185        for i in 0..betas_len {
186            let start = (104 + i * 48) as usize;
187            let end = start + 48;
188            //SAFETY : slice to array conversion after a length check
189            #[allow(clippy::unwrap_used)]
190            let beta_i_bytes = bytes[start..end].try_into().unwrap();
191            let beta_i = try_deserialize_g1_projective(&beta_i_bytes)?;
192
193            beta_g1_end = end as u64;
194            beta_g1.push(beta_i)
195        }
196
197        let mut beta_g2 = Vec::with_capacity(betas_len as usize);
198        for i in 0..betas_len {
199            let start = (beta_g1_end + i * 96) as usize;
200            let end = start + 96;
201            //SAFETY : slice to array conversion after a length check
202            #[allow(clippy::unwrap_used)]
203            let beta_i_bytes = bytes[start..end].try_into().unwrap();
204            let beta_i = try_deserialize_g2_projective(&beta_i_bytes)?;
205
206            beta_g2.push(beta_i)
207        }
208
209        Ok(VerificationKeyAuth {
210            alpha,
211            beta_g1,
212            beta_g2,
213        })
214    }
215}
216
217impl<'b> Add<&'b VerificationKeyAuth> for VerificationKeyAuth {
218    type Output = VerificationKeyAuth;
219
220    #[inline]
221    fn add(self, rhs: &'b VerificationKeyAuth) -> VerificationKeyAuth {
222        // If you're trying to add two keys together that were created
223        // for different number of attributes, just panic as it's a
224        // nonsense operation.
225        assert_eq!(
226            self.beta_g1.len(),
227            rhs.beta_g1.len(),
228            "trying to add verification keys generated for different number of attributes [G1]"
229        );
230
231        assert_eq!(
232            self.beta_g2.len(),
233            rhs.beta_g2.len(),
234            "trying to add verification keys generated for different number of attributes [G2]"
235        );
236
237        assert_eq!(
238            self.beta_g1.len(),
239            self.beta_g2.len(),
240            "this key is incorrect - the number of elements G1 and G2 does not match"
241        );
242
243        assert_eq!(
244            rhs.beta_g1.len(),
245            rhs.beta_g2.len(),
246            "they key you want to add is incorrect - the number of elements G1 and G2 does not match"
247        );
248
249        VerificationKeyAuth {
250            alpha: self.alpha + rhs.alpha,
251            beta_g1: self
252                .beta_g1
253                .iter()
254                .zip(rhs.beta_g1.iter())
255                .map(|(self_beta_g1, rhs_beta_g1)| self_beta_g1 + rhs_beta_g1)
256                .collect(),
257            beta_g2: self
258                .beta_g2
259                .iter()
260                .zip(rhs.beta_g2.iter())
261                .map(|(self_beta_g2, rhs_beta_g2)| self_beta_g2 + rhs_beta_g2)
262                .collect(),
263        }
264    }
265}
266
267impl Mul<Scalar> for &VerificationKeyAuth {
268    type Output = VerificationKeyAuth;
269
270    #[inline]
271    fn mul(self, rhs: Scalar) -> Self::Output {
272        VerificationKeyAuth {
273            alpha: self.alpha * rhs,
274            beta_g1: self.beta_g1.iter().map(|b_i| b_i * rhs).collect(),
275            beta_g2: self.beta_g2.iter().map(|b_i| b_i * rhs).collect(),
276        }
277    }
278}
279
280impl<T> Sum<T> for VerificationKeyAuth
281where
282    T: Borrow<VerificationKeyAuth>,
283{
284    #[inline]
285    fn sum<I>(iter: I) -> Self
286    where
287        I: Iterator<Item = T>,
288    {
289        let mut peekable = iter.peekable();
290        let head_attributes = match peekable.peek() {
291            Some(head) => head.borrow().beta_g2.len(),
292            None => {
293                // TODO: this is a really weird edge case. You're trying to sum an EMPTY iterator
294                // of VerificationKey. So should it panic here or just return some nonsense value?
295                return VerificationKeyAuth::identity(0);
296            }
297        };
298
299        peekable.fold(
300            VerificationKeyAuth::identity(head_attributes),
301            |acc, item| acc + item.borrow(),
302        )
303    }
304}
305
306impl VerificationKeyAuth {
307    /// Create a (kinda) identity verification key using specified
308    /// number of 'beta' elements
309    pub(crate) fn identity(beta_size: usize) -> Self {
310        VerificationKeyAuth {
311            alpha: G2Projective::identity(),
312            beta_g1: vec![G1Projective::identity(); beta_size],
313            beta_g2: vec![G2Projective::identity(); beta_size],
314        }
315    }
316
317    pub fn aggregate(sigs: &[Self], indices: Option<&[SignerIndex]>) -> Result<Self> {
318        aggregate_verification_keys(sigs, indices)
319    }
320
321    pub fn alpha(&self) -> &G2Projective {
322        &self.alpha
323    }
324
325    pub fn beta_g1(&self) -> &Vec<G1Projective> {
326        &self.beta_g1
327    }
328
329    pub fn beta_g2(&self) -> &Vec<G2Projective> {
330        &self.beta_g2
331    }
332
333    pub fn to_bytes(&self) -> Vec<u8> {
334        let beta_g1_len = self.beta_g1.len();
335        let beta_g2_len = self.beta_g2.len();
336        let mut bytes = Vec::with_capacity(96 + 8 + beta_g1_len * 48 + beta_g2_len * 96);
337
338        bytes.extend_from_slice(&self.alpha.to_affine().to_compressed());
339
340        bytes.extend_from_slice(&beta_g1_len.to_le_bytes());
341
342        for beta_g1 in self.beta_g1.iter() {
343            bytes.extend_from_slice(&beta_g1.to_affine().to_compressed())
344        }
345
346        for beta_g2 in self.beta_g2.iter() {
347            bytes.extend_from_slice(&beta_g2.to_affine().to_compressed())
348        }
349
350        bytes
351    }
352
353    pub fn from_bytes(bytes: &[u8]) -> Result<VerificationKeyAuth> {
354        VerificationKeyAuth::try_from(bytes)
355    }
356}
357
358impl Bytable for VerificationKeyAuth {
359    fn to_byte_vec(&self) -> Vec<u8> {
360        self.to_bytes().to_vec()
361    }
362
363    fn try_from_byte_slice(slice: &[u8]) -> std::result::Result<Self, CompactEcashError> {
364        Self::from_bytes(slice)
365    }
366}
367
368impl Base58 for VerificationKeyAuth {}
369
370#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
371pub struct SecretKeyUser {
372    pub(crate) sk: Scalar,
373}
374
375impl SecretKeyUser {
376    pub fn public_key(&self) -> PublicKeyUser {
377        PublicKeyUser {
378            pk: ecash_group_parameters().gen1() * self.sk,
379        }
380    }
381
382    pub fn to_bytes(&self) -> Vec<u8> {
383        self.sk.to_bytes().to_vec()
384    }
385
386    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
387        let sk = Scalar::try_from_byte_slice(bytes)?;
388        Ok(SecretKeyUser { sk })
389    }
390}
391
392impl Bytable for SecretKeyUser {
393    fn to_byte_vec(&self) -> Vec<u8> {
394        self.to_bytes().to_vec()
395    }
396
397    fn try_from_byte_slice(slice: &[u8]) -> std::result::Result<Self, CompactEcashError> {
398        Self::from_bytes(slice)
399    }
400}
401
402impl Base58 for SecretKeyUser {}
403
404#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
405pub struct PublicKeyUser {
406    pub(crate) pk: G1Projective,
407}
408
409impl PublicKeyUser {
410    pub fn to_base58_string(&self) -> String {
411        bs58::encode(&self.pk.to_bytes()).into_string()
412    }
413
414    pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self> {
415        let bytes = bs58::decode(val).into_vec()?;
416        Self::from_bytes(&bytes)
417    }
418
419    pub fn to_bytes(&self) -> Vec<u8> {
420        self.pk.to_affine().to_compressed().to_vec()
421    }
422
423    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
424        if bytes.len() != 48 {
425            return Err(CompactEcashError::DeserializationLengthMismatch {
426                type_name: "PublicKeyUser".into(),
427                expected: 48,
428                actual: bytes.len(),
429            });
430        }
431        //SAFETY : slice to array conversion after a length check
432        #[allow(clippy::unwrap_used)]
433        let pk_bytes: &[u8; 48] = bytes[..48].try_into().unwrap();
434        let pk = try_deserialize_g1_projective(pk_bytes)?;
435        Ok(PublicKeyUser { pk })
436    }
437}
438
439impl Bytable for PublicKeyUser {
440    fn to_byte_vec(&self) -> Vec<u8> {
441        self.to_bytes().to_vec()
442    }
443
444    fn try_from_byte_slice(slice: &[u8]) -> Result<Self> {
445        Self::from_bytes(slice)
446    }
447}
448
449impl Base58 for PublicKeyUser {}
450
451#[derive(Debug, Zeroize, ZeroizeOnDrop)]
452pub struct KeyPairAuth {
453    secret_key: SecretKeyAuth,
454    #[zeroize(skip)]
455    verification_key: VerificationKeyAuth,
456    /// Optional index value specifying polynomial point used during threshold key generation.
457    pub index: Option<SignerIndex>,
458}
459
460impl From<SecretKeyAuth> for KeyPairAuth {
461    fn from(secret_key: SecretKeyAuth) -> Self {
462        KeyPairAuth {
463            verification_key: secret_key.verification_key(),
464            secret_key,
465            index: None,
466        }
467    }
468}
469
470impl PemStorableKeyPair for KeyPairAuth {
471    type PrivatePemKey = SecretKeyAuth;
472    type PublicPemKey = VerificationKeyAuth;
473
474    fn private_key(&self) -> &Self::PrivatePemKey {
475        &self.secret_key
476    }
477
478    fn public_key(&self) -> &Self::PublicPemKey {
479        &self.verification_key
480    }
481
482    fn from_keys(secret_key: Self::PrivatePemKey, verification_key: Self::PublicPemKey) -> Self {
483        Self::from_keys(secret_key, verification_key)
484    }
485}
486
487impl KeyPairAuth {
488    pub fn new(
489        sk: SecretKeyAuth,
490        vk: VerificationKeyAuth,
491        index: Option<SignerIndex>,
492    ) -> KeyPairAuth {
493        KeyPairAuth {
494            secret_key: sk,
495            verification_key: vk,
496            index,
497        }
498    }
499
500    pub fn from_keys(secret_key: SecretKeyAuth, verification_key: VerificationKeyAuth) -> Self {
501        Self {
502            secret_key,
503            verification_key,
504            index: None,
505        }
506    }
507
508    pub fn secret_key(&self) -> &SecretKeyAuth {
509        &self.secret_key
510    }
511
512    pub fn verification_key(&self) -> VerificationKeyAuth {
513        self.verification_key.clone()
514    }
515
516    pub fn verification_key_ref(&self) -> &VerificationKeyAuth {
517        &self.verification_key
518    }
519}
520
521#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
522pub struct KeyPairUser {
523    secret_key: SecretKeyUser,
524    public_key: PublicKeyUser,
525}
526
527impl From<KeyPairUser> for SecretKeyUser {
528    fn from(value: KeyPairUser) -> Self {
529        value.secret_key
530    }
531}
532
533impl From<SecretKeyUser> for KeyPairUser {
534    fn from(value: SecretKeyUser) -> Self {
535        KeyPairUser {
536            public_key: value.public_key(),
537            secret_key: value,
538        }
539    }
540}
541
542impl KeyPairUser {
543    #[allow(clippy::new_without_default)]
544    pub fn new() -> Self {
545        generate_keypair_user()
546    }
547
548    pub fn new_seeded<M: AsRef<[u8]>>(seed: M) -> Self {
549        generate_keypair_user_from_seed(seed)
550    }
551
552    pub fn secret_key(&self) -> &SecretKeyUser {
553        &self.secret_key
554    }
555
556    pub fn public_key(&self) -> PublicKeyUser {
557        self.public_key
558    }
559
560    pub fn to_bytes(&self) -> Vec<u8> {
561        [self.secret_key.to_bytes(), self.public_key.to_bytes()].concat()
562    }
563
564    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
565        if bytes.len() != 32 + 48 {
566            return Err(CompactEcashError::DeserializationLengthMismatch {
567                type_name: "KeyPairUser".into(),
568                expected: 80,
569                actual: bytes.len(),
570            });
571        }
572        let sk = SecretKeyUser::from_bytes(&bytes[..32])?;
573        let pk = PublicKeyUser::from_bytes(&bytes[32..32 + 48])?;
574        Ok(KeyPairUser {
575            secret_key: sk,
576            public_key: pk,
577        })
578    }
579}
580
581pub fn generate_keypair_user() -> KeyPairUser {
582    let params = ecash_group_parameters();
583    let sk_user = SecretKeyUser {
584        sk: params.random_scalar(),
585    };
586    let pk_user = PublicKeyUser {
587        pk: params.gen1() * sk_user.sk,
588    };
589
590    KeyPairUser {
591        secret_key: sk_user,
592        public_key: pk_user,
593    }
594}
595
596pub fn generate_keypair_user_from_seed<M: AsRef<[u8]>>(seed: M) -> KeyPairUser {
597    let params = ecash_group_parameters();
598    let sk_user = SecretKeyUser {
599        sk: hash_to_scalar(seed),
600    };
601    let pk_user = PublicKeyUser {
602        pk: params.gen1() * sk_user.sk,
603    };
604
605    KeyPairUser {
606        secret_key: sk_user,
607        public_key: pk_user,
608    }
609}
610
611pub fn ttp_keygen(threshold: u64, num_authorities: u64) -> Result<Vec<KeyPairAuth>> {
612    let params = ecash_group_parameters();
613    if threshold == 0 {
614        return Err(CompactEcashError::KeygenParameters);
615    }
616
617    if threshold > num_authorities {
618        return Err(CompactEcashError::KeygenParameters);
619    }
620
621    let attributes = params.gammas().len();
622
623    // generate polynomials
624    let v = Polynomial::new_random(params, threshold - 1);
625    let ws = (0..attributes + 1)
626        .map(|_| Polynomial::new_random(params, threshold - 1))
627        .collect::<Vec<_>>();
628
629    // TODO: potentially if we had some known authority identifier we could use that instead
630    // of the increasing (1,2,3,...) sequence
631    let polynomial_indices = (1..=num_authorities).collect::<Vec<_>>();
632
633    // generate polynomial shares
634    let x = polynomial_indices
635        .iter()
636        .map(|&id| v.evaluate(&Scalar::from(id)));
637    let ys = polynomial_indices.iter().map(|&id| {
638        ws.iter()
639            .map(|w| w.evaluate(&Scalar::from(id)))
640            .collect::<Vec<_>>()
641    });
642
643    // finally set the keys
644    let secret_keys = x.zip(ys).map(|(x, ys)| SecretKeyAuth { x, ys });
645
646    let keypairs = secret_keys
647        .zip(polynomial_indices.iter())
648        .map(|(secret_key, index)| {
649            let verification_key = secret_key.verification_key();
650            KeyPairAuth {
651                secret_key,
652                verification_key,
653                index: Some(*index),
654            }
655        })
656        .collect();
657
658    Ok(keypairs)
659}
660
661#[cfg(test)]
662mod tests {
663    use super::*;
664
665    fn assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
666
667    fn assert_zeroize<T: Zeroize>() {}
668
669    #[test]
670    fn secret_key_is_zeroized() {
671        assert_zeroize::<SecretKeyAuth>();
672        assert_zeroize_on_drop::<SecretKeyAuth>();
673
674        assert_zeroize::<SecretKeyUser>();
675        assert_zeroize_on_drop::<SecretKeyUser>();
676    }
677}