apub_openssl/
lib.rs

1//! OpenSSL-backed cryptography for apub
2
3#![deny(missing_docs)]
4
5use openssl::{
6    error::ErrorStack,
7    hash::MessageDigest,
8    pkey::{PKey, Private, Public},
9    sha::Sha256,
10    sign::{Signer, Verifier},
11};
12use std::fmt::Debug;
13
14/// A sha256 digest
15#[derive(Clone)]
16pub struct OpenSslDigest {
17    digest: Sha256,
18}
19
20/// An RSA private key message signer
21pub struct OpenSslSigner {
22    private_key: PKey<Private>,
23}
24
25/// An RSA public key message verifier
26pub struct OpenSslVerifier {
27    public_key: PKey<Public>,
28}
29
30/// A type representing an actor's cryptography information
31///
32/// It includes a private key, plus its ID
33#[derive(Clone, serde::Deserialize, serde::Serialize)]
34pub struct OpenSsl {
35    key_id: String,
36    #[serde(with = "openssl_private_key")]
37    private_key: PKey<Private>,
38}
39
40impl OpenSsl {
41    /// Create a new OpenSsl cryptography type from a given key and ID
42    pub fn new(key_id: String, private_key: PKey<Private>) -> Self {
43        Self {
44            key_id,
45            private_key,
46        }
47    }
48}
49
50impl apub_core::digest::Digest for OpenSslDigest {
51    const NAME: &'static str = "SHA-256";
52
53    fn digest(&mut self, input: &[u8]) -> String {
54        self.digest.update(input);
55        let bytes = self.digest.clone().finish();
56
57        openssl::base64::encode_block(&bytes)
58    }
59
60    fn update(&mut self, input: &[u8]) {
61        self.digest.update(input);
62    }
63
64    fn verify(&mut self, encoded: &str) -> bool {
65        let bytes = self.digest.clone().finish();
66
67        openssl::base64::encode_block(&bytes) == encoded
68    }
69}
70
71impl apub_core::digest::DigestBuilder for OpenSslDigest {
72    fn build() -> Self {
73        OpenSslDigest {
74            digest: Sha256::new(),
75        }
76    }
77}
78
79impl apub_core::signature::Sign for OpenSslSigner {
80    type Error = ErrorStack;
81
82    fn sign(&self, signing_string: &str) -> Result<String, Self::Error> {
83        let mut signer = Signer::new(MessageDigest::sha256(), &self.private_key)?;
84        signer.update(signing_string.as_bytes())?;
85
86        Ok(openssl::base64::encode_block(&signer.sign_to_vec()?))
87    }
88}
89
90impl apub_core::signature::Verify for OpenSslVerifier {
91    type Error = ErrorStack;
92
93    fn verify(&self, signing_string: &str, signature: &str) -> Result<bool, Self::Error> {
94        let mut verifier = Verifier::new(MessageDigest::sha256(), &self.public_key)?;
95        verifier.update(signing_string.as_bytes())?;
96        verifier.verify(&openssl::base64::decode_block(signature)?)
97    }
98}
99
100impl apub_core::signature::VerifyBuilder for OpenSslVerifier {
101    fn build(public_key_pem: &str) -> Result<Self, Self::Error>
102    where
103        Self: Sized,
104    {
105        Ok(OpenSslVerifier {
106            public_key: PKey::public_key_from_pem(public_key_pem.as_bytes())?,
107        })
108    }
109}
110
111impl apub_core::digest::DigestFactory for OpenSsl {
112    type Digest = OpenSslDigest;
113}
114
115impl apub_core::signature::PrivateKey for OpenSsl {
116    type Signer = OpenSslSigner;
117
118    fn key_id(&self) -> String {
119        self.key_id.clone()
120    }
121
122    fn signer(&self) -> Self::Signer {
123        OpenSslSigner {
124            private_key: self.private_key.clone(),
125        }
126    }
127}
128
129impl apub_core::signature::PrivateKeyBuilder for OpenSsl {
130    type Error = ErrorStack;
131
132    fn build(key_id: String, private_key_pem: &str) -> Result<Self, Self::Error>
133    where
134        Self: Sized,
135    {
136        Ok(Self {
137            key_id,
138            private_key: PKey::private_key_from_pem(private_key_pem.as_bytes())?,
139        })
140    }
141
142    fn private_key_pem(&self) -> Result<String, Self::Error> {
143        self.private_key
144            .private_key_to_pem_pkcs8()
145            .map(|v| String::from_utf8_lossy(&v).to_string())
146    }
147}
148
149impl Debug for OpenSslDigest {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        f.debug_struct("OpenSslDigest")
152            .field("digest", &"Sha256")
153            .finish()
154    }
155}
156
157impl Debug for OpenSslSigner {
158    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159        f.debug_struct("OpenSslSigner")
160            .field("private_key", &"hidden")
161            .finish()
162    }
163}
164
165impl Debug for OpenSsl {
166    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167        f.debug_struct("OpenSsl")
168            .field("key_id", &self.key_id)
169            .field("private_key", &"hidden")
170            .finish()
171    }
172}
173
174mod openssl_private_key {
175    use openssl::pkey::{PKey, Private};
176    use serde::{
177        de::{Deserialize, Deserializer},
178        ser::{Serialize, Serializer},
179    };
180
181    pub(super) fn serialize<S: Serializer>(
182        private_key: &PKey<Private>,
183        serializer: S,
184    ) -> Result<S::Ok, S::Error> {
185        use serde::ser::Error;
186
187        let der = private_key.private_key_to_der().map_err(S::Error::custom)?;
188        let der_string = openssl::base64::encode_block(&der);
189
190        String::serialize(&der_string, serializer)
191    }
192
193    pub(super) fn deserialize<'de, D: Deserializer<'de>>(
194        deserializer: D,
195    ) -> Result<PKey<Private>, D::Error> {
196        use serde::de::Error;
197
198        let der_string = String::deserialize(deserializer)?;
199        let der = openssl::base64::decode_block(&der_string).map_err(D::Error::custom)?;
200
201        PKey::<Private>::private_key_from_der(&der).map_err(D::Error::custom)
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::OpenSsl;
208    use apub_core::signature::{PrivateKey, Sign};
209    use openssl::{pkey::PKey, rsa::Rsa};
210
211    #[test]
212    fn round_trip() {
213        let private_key = PKey::from_rsa(Rsa::generate(1024).unwrap()).unwrap();
214        let crypto = OpenSsl::new("key-id".into(), private_key);
215        let signer = crypto.signer();
216
217        let first_sign = signer.sign("hello").unwrap();
218
219        let s = serde_json::to_string(&crypto).unwrap();
220        let crypto2: OpenSsl = serde_json::from_str(&s).unwrap();
221        let signer2 = crypto2.signer();
222
223        let second_sign = signer2.sign("hello").unwrap();
224
225        let s2 = serde_json::to_string(&crypto2).unwrap();
226
227        assert_eq!(s, s2);
228        assert_eq!(first_sign, second_sign);
229    }
230}