1#![deny(missing_docs)]
4
5use rsa::{
6 hash::Hash,
7 pkcs8::{FromPrivateKey, FromPublicKey, ToPrivateKey},
8 PaddingScheme, PublicKey, RsaPrivateKey, RsaPublicKey,
9};
10use sha2::{Digest, Sha256};
11use std::fmt::Debug;
12
13#[derive(Debug, thiserror::Error)]
16pub enum RustcryptoError {
17 #[error(transparent)]
19 Pkcs8(#[from] rsa::pkcs8::Error),
20
21 #[error(transparent)]
23 Rsa(#[from] rsa::errors::Error),
24
25 #[error(transparent)]
27 Base64(#[from] base64::DecodeError),
28}
29
30#[derive(Debug, Clone)]
32pub struct Sha256Digest {
33 digest: Sha256,
34}
35
36pub struct RsaSigner {
38 private_key: RsaPrivateKey,
39}
40
41pub struct RsaVerifier {
43 public_key: RsaPublicKey,
44}
45
46#[derive(Clone, serde::Deserialize, serde::Serialize)]
50pub struct Rustcrypto {
51 key_id: String,
52 #[serde(with = "rsa_signer")]
53 private_key: RsaPrivateKey,
54}
55
56impl Rustcrypto {
57 pub fn new(key_id: String, private_key: RsaPrivateKey) -> Self {
59 Self {
60 key_id,
61 private_key,
62 }
63 }
64}
65
66impl apub_core::digest::Digest for Sha256Digest {
67 const NAME: &'static str = "SHA-256";
68
69 fn digest(&mut self, input: &[u8]) -> String {
70 self.digest.update(input);
71 let bytes = self.digest.finalize_reset();
72
73 base64::encode(&bytes)
74 }
75
76 fn update(&mut self, input: &[u8]) {
77 self.digest.update(input);
78 }
79
80 fn verify(&mut self, encoded: &str) -> bool {
81 let bytes = self.digest.finalize_reset();
82
83 base64::encode(&bytes) == encoded
84 }
85}
86
87impl apub_core::digest::DigestBuilder for Sha256Digest {
88 fn build() -> Self {
89 Sha256Digest {
90 digest: Sha256::new(),
91 }
92 }
93}
94
95impl apub_core::signature::Sign for RsaSigner {
96 type Error = RustcryptoError;
97
98 fn sign(&self, signing_string: &str) -> Result<String, Self::Error> {
99 let hashed = Sha256::digest(signing_string.as_bytes());
100 let bytes = self.private_key.sign(
101 PaddingScheme::PKCS1v15Sign {
102 hash: Some(Hash::SHA2_256),
103 },
104 &hashed,
105 )?;
106 Ok(base64::encode(bytes))
107 }
108}
109
110impl apub_core::signature::Verify for RsaVerifier {
111 type Error = RustcryptoError;
112
113 fn verify(&self, signing_string: &str, signature: &str) -> Result<bool, Self::Error> {
114 let decoded = base64::decode(signature)?;
115 let hashed = Sha256::digest(signing_string.as_bytes());
116 self.public_key.verify(
117 PaddingScheme::PKCS1v15Sign {
118 hash: Some(Hash::SHA2_256),
119 },
120 &hashed,
121 &decoded,
122 )?;
123 Ok(true)
124 }
125}
126
127impl apub_core::signature::VerifyBuilder for RsaVerifier {
128 fn build(public_key_pem: &str) -> Result<Self, Self::Error>
129 where
130 Self: Sized,
131 {
132 Ok(RsaVerifier {
133 public_key: RsaPublicKey::from_public_key_pem(public_key_pem)?,
134 })
135 }
136}
137
138impl apub_core::digest::DigestFactory for Rustcrypto {
139 type Digest = Sha256Digest;
140}
141
142impl apub_core::signature::PrivateKey for Rustcrypto {
143 type Signer = RsaSigner;
144
145 fn key_id(&self) -> String {
146 self.key_id.clone()
147 }
148
149 fn signer(&self) -> Self::Signer {
150 RsaSigner {
151 private_key: self.private_key.clone(),
152 }
153 }
154}
155
156impl apub_core::signature::PrivateKeyBuilder for Rustcrypto {
157 type Error = RustcryptoError;
158
159 fn build(key_id: String, private_key_pem: &str) -> Result<Self, Self::Error>
160 where
161 Self: Sized,
162 {
163 Ok(Rustcrypto {
164 key_id,
165 private_key: RsaPrivateKey::from_pkcs8_pem(private_key_pem)?,
166 })
167 }
168
169 fn private_key_pem(&self) -> Result<String, Self::Error> {
170 self.private_key
171 .to_pkcs8_pem()
172 .map(|s| (*s).clone())
173 .map_err(RustcryptoError::from)
174 }
175}
176
177impl Debug for RsaSigner {
178 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179 f.debug_struct("RsaSigner")
180 .field("private_key", &"hidden")
181 .finish()
182 }
183}
184
185impl Debug for Rustcrypto {
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 f.debug_struct("Rustcrypto")
188 .field("key_id", &self.key_id)
189 .field("private_key", &"hidden")
190 .finish()
191 }
192}
193
194mod rsa_signer {
195 use rsa::{
196 pkcs1::{FromRsaPrivateKey, ToRsaPrivateKey},
197 RsaPrivateKey,
198 };
199 use serde::{
200 de::{Deserialize, Deserializer},
201 ser::{Serialize, Serializer},
202 };
203
204 pub(super) fn serialize<S: Serializer>(
205 private_key: &RsaPrivateKey,
206 serializer: S,
207 ) -> Result<S::Ok, S::Error> {
208 use serde::ser::Error;
209
210 let der = private_key.to_pkcs1_der().map_err(S::Error::custom)?;
211 let der_string = base64::encode(der.as_der());
212
213 String::serialize(&der_string, serializer)
214 }
215
216 pub(super) fn deserialize<'de, D: Deserializer<'de>>(
217 deserializer: D,
218 ) -> Result<RsaPrivateKey, D::Error> {
219 use serde::de::Error;
220
221 let der_string = String::deserialize(deserializer)?;
222 let der = base64::decode(&der_string).map_err(D::Error::custom)?;
223
224 RsaPrivateKey::from_pkcs1_der(&der).map_err(D::Error::custom)
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::Rustcrypto;
231 use apub_core::signature::{PrivateKey, Sign};
232 use rsa::RsaPrivateKey;
233
234 #[test]
235 fn round_trip() {
236 let private_key = RsaPrivateKey::new(&mut rand::thread_rng(), 1024).unwrap();
237 let crypto = Rustcrypto::new("key-id".into(), private_key);
238 let signer = crypto.signer();
239
240 let first_sign = signer.sign("hello").unwrap();
241
242 let s = serde_json::to_string(&crypto).unwrap();
243 let crypto2: Rustcrypto = serde_json::from_str(&s).unwrap();
244 let signer2 = crypto2.signer();
245
246 let second_sign = signer2.sign("hello").unwrap();
247
248 let s2 = serde_json::to_string(&crypto2).unwrap();
249
250 assert_eq!(s, s2);
251 assert_eq!(first_sign, second_sign);
252 }
253}