JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Add Instance Authentication

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨5a042fd

⁨src/messages/mod.rs⁩ - ⁨5124⁩ bytes
Raw
1 use std::{error::Error, fmt::Debug};
2
3 use rsa::{
4 pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey},
5 pss::{Signature, SigningKey, VerifyingKey},
6 sha2::Sha256,
7 signature::{RandomizedSigner, SignatureEncoding, Verifier},
8 RsaPrivateKey, RsaPublicKey,
9 };
10 use serde::{Deserialize, Serialize};
11
12 use crate::{handshake::HandshakeMessage, model::instance::Instance};
13
14 use self::{authentication::AuthenticationMessage, repository::RepositoryMessage};
15
16 pub mod authentication;
17 pub mod issues;
18 pub mod repository;
19 pub mod user;
20
21 #[derive(Clone, Serialize, Deserialize)]
22 pub enum MessageKind {
23 Handshake(HandshakeMessage),
24 Repository(RepositoryMessage),
25 Authentication(AuthenticationMessage),
26 }
27
28 /// An authenticated message, where the instance is authenticating
29 /// a request it is making for itself.
30 #[derive(Serialize, Deserialize)]
31 pub struct InstanceAuthenticated<T: Serialize> {
32 message: T,
33 instance: Instance,
34 signature: Vec<u8>,
35 }
36
37 impl<T> Clone for InstanceAuthenticated<T>
38 where
39 T: Clone + Serialize,
40 {
41 fn clone(&self) -> Self {
42 Self {
43 message: self.message.clone(),
44 instance: self.instance.clone(),
45 signature: self.signature.clone(),
46 }
47 }
48 }
49
50 impl<T> Debug for InstanceAuthenticated<T>
51 where
52 T: Debug + Serialize,
53 {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 f.debug_struct("Authenticated")
56 .field("message", &self.message)
57 .field("instance", &self.instance)
58 .field("signature", &self.signature)
59 .finish()
60 }
61 }
62
63 impl<T: Serialize> InstanceAuthenticated<T> {
64 pub fn new(
65 message: T,
66 instance: Instance,
67 private_key: String,
68 ) -> Result<Self, Box<dyn Error>> {
69 let mut rng = rand::thread_rng();
70
71 let private_key = RsaPrivateKey::from_pkcs1_pem(&private_key)?;
72 let signing_key = SigningKey::<Sha256>::new(private_key);
73
74 let message_json = serde_json::to_vec(&message)?;
75
76 let signature = signing_key.sign_with_rng(&mut rng, &message_json);
77
78 Ok(Self {
79 message,
80 instance,
81 signature: signature.to_vec(),
82 })
83 }
84
85 pub async fn inner(&self) -> &T {
86 &self.message
87 }
88
89 pub async fn validate(&self, instance: &Instance) -> Result<(), Box<dyn Error>> {
90 let public_key = public_key(instance).await?;
91 let public_key = RsaPublicKey::from_pkcs1_pem(&public_key).unwrap();
92
93 let verifying_key: VerifyingKey<Sha256> = VerifyingKey::new(public_key);
94
95 let message_json = serde_json::to_vec(&self.message).unwrap();
96
97 verifying_key
98 .verify(
99 &message_json,
100 &Signature::try_from(self.signature.as_ref()).unwrap(),
101 )
102 .unwrap();
103
104 Ok(())
105 }
106 }
107
108 /// An authenticated message.
109 ///
110 /// Includes the message, with a digest generated with
111 /// our private key.
112 #[derive(Serialize, Deserialize)]
113 pub struct UserAuthenticated<T: Serialize> {
114 #[serde(flatten)]
115 message: T,
116 token: String,
117 digest: Vec<u8>,
118 }
119
120 impl<T> Clone for UserAuthenticated<T>
121 where
122 T: Clone + Serialize,
123 {
124 fn clone(&self) -> Self {
125 Self {
126 message: self.message.clone(),
127 token: self.token.clone(),
128 digest: self.digest.clone(),
129 }
130 }
131 }
132
133 impl<T> Debug for UserAuthenticated<T>
134 where
135 T: Debug + Serialize,
136 {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 f.debug_struct("Authenticated")
139 .field("message", &self.message)
140 .field("token", &self.token)
141 .field("digest", &self.digest)
142 .finish()
143 }
144 }
145
146 impl<T: Serialize> UserAuthenticated<T> {
147 pub fn new(message: T, token: String, private_key: String) -> Result<Self, Box<dyn Error>> {
148 let mut rng = rand::thread_rng();
149
150 let private_key = RsaPrivateKey::from_pkcs1_pem(&private_key)?;
151 let signing_key = SigningKey::<Sha256>::new(private_key);
152
153 let message_json = serde_json::to_vec(&message)?;
154
155 let signature = signing_key.sign_with_rng(&mut rng, &message_json);
156
157 Ok(Self {
158 message,
159 token,
160 digest: signature.to_vec(),
161 })
162 }
163
164 pub async fn inner(&self) -> &T {
165 &self.message
166 }
167
168 pub async fn validate(&self, key: String) -> Result<(), Box<dyn Error>> {
169 let public_key = RsaPublicKey::from_pkcs1_pem(&key).unwrap();
170
171 let verifying_key: VerifyingKey<Sha256> = VerifyingKey::new(public_key);
172
173 let message_json = serde_json::to_vec(&self.message).unwrap();
174
175 verifying_key
176 .verify(
177 &message_json,
178 &Signature::try_from(self.digest.as_ref()).unwrap(),
179 )
180 .unwrap();
181
182 Ok(())
183 }
184 }
185
186 async fn public_key(instance: &Instance) -> Result<String, Box<dyn Error>> {
187 let key = reqwest::get(format!("https://{}/.giterated/pubkey.pem", instance.url))
188 .await?
189 .text()
190 .await?;
191
192 Ok(key)
193 }
194