mithril_stm/signature_scheme/bls_multi_signature/
proof_of_possession.rs

1use blst::{blst_p1, min_sig::Signature as BlstSig};
2
3use crate::StmResult;
4
5use super::{
6    BlsSignatureError, BlsSigningKey, POP, blst_error_to_stm_error,
7    helper::unsafe_helpers::{compress_p1, scalar_to_pk_in_g1, uncompress_p1},
8};
9
10/// MultiSig proof of possession, which contains two elements from G1. However,
11/// the two elements have different types: `k1` is represented as a BlstSig
12/// as it has the same structure, and this facilitates its verification. On
13/// the other hand, `k2` is a G1 point, as it does not share structure with
14/// the BLS signature, and we need to have an ad-hoc verification mechanism.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct BlsProofOfPossession {
17    k1: BlstSig,
18    k2: blst_p1,
19}
20
21impl BlsProofOfPossession {
22    /// Convert to a 96 byte string.
23    ///
24    /// # Layout
25    /// The layout of a `MspPoP` encoding is
26    /// * K1 (G1 point)
27    /// * K2 (G1 point)
28    pub fn to_bytes(self) -> [u8; 96] {
29        let mut pop_bytes = [0u8; 96];
30        pop_bytes[..48].copy_from_slice(&self.k1.to_bytes());
31
32        pop_bytes[48..].copy_from_slice(&compress_p1(&self.k2)[..]);
33        pop_bytes
34    }
35
36    /// Deserialize a byte string to a `PublicKeyPoP`.
37    pub fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
38        let k1 = match BlstSig::from_bytes(
39            bytes.get(..48).ok_or(BlsSignatureError::SerializationError)?,
40        ) {
41            Ok(key) => key,
42            Err(e) => {
43                return Err(blst_error_to_stm_error(e, None, None)
44                    .expect_err("If it passed, blst returns and error different to SUCCESS."));
45            }
46        };
47
48        let k2 = uncompress_p1(bytes.get(48..96).ok_or(BlsSignatureError::SerializationError)?)?;
49
50        Ok(Self { k1, k2 })
51    }
52
53    pub(crate) fn get_k1(self) -> BlstSig {
54        self.k1
55    }
56
57    pub(crate) fn get_k2(self) -> blst_p1 {
58        self.k2
59    }
60}
61
62impl From<&BlsSigningKey> for BlsProofOfPossession {
63    /// Convert a secret key into an `MspPoP`. This is performed by computing
64    /// `k1 =  H_G1(b"PoP" || mvk)` and `k2 = g1 * sk` where `H_G1` hashes into
65    /// `G1` and `g1` is the generator in `G1`.
66    fn from(sk: &BlsSigningKey) -> Self {
67        let k1 = sk.to_blst_secret_key().sign(POP, &[], &[]);
68        let k2 = scalar_to_pk_in_g1(&sk.to_blst_secret_key());
69        Self { k1, k2 }
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    mod golden {
76
77        use rand_chacha::ChaCha20Rng;
78        use rand_core::SeedableRng;
79
80        use crate::signature_scheme::{BlsProofOfPossession, BlsSigningKey};
81
82        const GOLDEN_JSON: &str = r#"[168,50,233,193,15,136,65,72,123,148,129,176,38,198,209,47,28,204,176,144,57,251,42,28,66,76,89,97,158,63,54,198,194,176,135,221,14,185,197,225,202,98,243,74,233,225,143,151,147,177,170,117,66,165,66,62,33,216,232,75,68,114,195,22,100,65,44,198,4,166,102,233,253,240,59,175,60,117,142,114,140,122,17,87,110,187,1,17,10,195,154,13,249,86,54,226]"#;
83
84        fn golden_value() -> BlsProofOfPossession {
85            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
86            let sk = BlsSigningKey::generate(&mut rng);
87            BlsProofOfPossession::from(&sk)
88        }
89
90        #[test]
91        fn golden_conversions() {
92            let value = serde_json::from_str(GOLDEN_JSON)
93                .expect("This JSON deserialization should not fail");
94            assert_eq!(golden_value(), value);
95
96            let serialized =
97                serde_json::to_string(&value).expect("This JSON serialization should not fail");
98            let golden_serialized = serde_json::to_string(&golden_value())
99                .expect("This JSON serialization should not fail");
100            assert_eq!(golden_serialized, serialized);
101        }
102    }
103}