dup_crypto/keys/
text_signable.rs

1//  Copyright (C) 2020 Éloïs SANCHEZ.
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU Affero General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU Affero General Public License for more details.
12//
13// You should have received a copy of the GNU Affero General Public License
14// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16//! Generic code for signing data in text format
17
18use super::*;
19
20/// Signatureable in text format
21pub trait TextSignable: Debug + Clone {
22    /// Return signable text
23    fn as_signable_text(&self) -> String;
24    /// Return entity issuer pubkey
25    fn issuer_pubkey(&self) -> PubKeyEnum;
26    /// Return entity signature
27    fn signature(&self) -> Option<Sig>;
28    /// Change signature
29    fn set_signature(&mut self, _signature: Sig);
30    /// Sign entity
31    fn sign(&mut self, signator: &SignatorEnum) -> Result<String, SignError> {
32        if self.signature().is_some() {
33            return Err(SignError::AlreadySign);
34        }
35        match self.issuer_pubkey() {
36            PubKeyEnum::Ed25519(_) => match signator {
37                SignatorEnum::Ed25519(ed25519_signator) => {
38                    let text = self.as_signable_text();
39                    let sig = ed25519_signator.sign(&text.as_bytes());
40                    self.set_signature(Sig::Ed25519(sig));
41                    let str_sig = sig.to_base64();
42                    Ok(format!("{}{}", text, str_sig))
43                }
44                _ => Err(SignError::WrongAlgo),
45            },
46        }
47    }
48    /// Check signature of entity
49    fn verify(&self) -> Result<(), SigError> {
50        if let Some(signature) = self.signature() {
51            match self.issuer_pubkey() {
52                PubKeyEnum::Ed25519(pubkey) => match signature {
53                    Sig::Ed25519(sig) => pubkey.verify(&self.as_signable_text().as_bytes(), &sig),
54                    _ => Err(SigError::NotSameAlgo),
55                },
56            }
57        } else {
58            Err(SigError::NotSig)
59        }
60    }
61}
62
63#[cfg(test)]
64mod tests {
65
66    use super::*;
67
68    #[derive(Debug, Clone)]
69    struct TextSignableTestImpl {
70        issuer: PubKeyEnum,
71        text: String,
72        sig: Option<Sig>,
73    }
74
75    impl TextSignable for TextSignableTestImpl {
76        fn as_signable_text(&self) -> String {
77            format!("{}:{}", self.issuer, self.text)
78        }
79        fn issuer_pubkey(&self) -> PubKeyEnum {
80            self.issuer
81        }
82        fn signature(&self) -> Option<Sig> {
83            self.sig
84        }
85        fn set_signature(&mut self, new_signature: Sig) {
86            self.sig = Some(new_signature);
87        }
88    }
89
90    #[test]
91    fn test_text_signable() {
92        let key_pair = super::super::tests::valid_key_pair_1();
93
94        let signator = key_pair.generate_signator();
95
96        let mut text_signable = TextSignableTestImpl {
97            issuer: key_pair.public_key(),
98            text: "toto".to_owned(),
99            sig: None,
100        };
101
102        assert_eq!(Err(SigError::NotSig), text_signable.verify());
103        assert_eq!(
104            Err(SignError::WrongAlgo),
105            text_signable.sign(&SignatorEnum::Schnorr())
106        );
107        text_signable.issuer = key_pair.public_key();
108        assert_eq!(
109            Ok("VYgskcKKh525MzFRzpCiT5KXCQrnFLTnzMLffbvm9uw:toto+IC1fFkkYo5ox2loc1IMLCtrir1i6oyljfshNXIyXVcz6sJMFqn+6o8Zip4XdTzoBEORkbcnEnqQEr4TgaHpCw==".to_owned()),
110            text_signable.sign(&signator)
111        );
112        assert_eq!(Err(SignError::AlreadySign), text_signable.sign(&signator));
113        assert_eq!(Ok(()), text_signable.verify());
114        text_signable.sig.replace(Sig::Schnorr());
115        assert_eq!(Err(SigError::NotSameAlgo), text_signable.verify());
116    }
117}