use std::{error::Error, fmt::Debug}; use rsa::{ pkcs8::{DecodePrivateKey, DecodePublicKey}, pss::{Signature, SigningKey, VerifyingKey}, sha2::Sha256, signature::{RandomizedSigner, SignatureEncoding, Verifier}, RsaPrivateKey, RsaPublicKey, }; use serde::{Deserialize, Serialize}; use crate::handshake::HandshakeMessage; use self::{authentication::AuthenticationMessage, repository::RepositoryMessage}; pub mod authentication; pub mod issues; pub mod repository; pub mod user; #[derive(Clone, Serialize, Deserialize)] pub enum MessageKind { Handshake(HandshakeMessage), Repository(RepositoryMessage), Authentication(AuthenticationMessage), } /// An authenticated message. /// /// Includes the message, with a digest generated with /// our private key. #[derive(Serialize, Deserialize)] pub struct Authenticated { #[serde(flatten)] message: T, token: String, digest: Vec, } impl Clone for Authenticated where T: Clone + Serialize, { fn clone(&self) -> Self { Self { message: self.message.clone(), token: self.token.clone(), digest: self.digest.clone(), } } } impl Debug for Authenticated where T: Debug + Serialize, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Authenticated") .field("message", &self.message) .field("token", &self.token) .field("digest", &self.digest) .finish() } } impl Authenticated { pub fn new(message: T, token: String, private_key: String) -> Result> { let mut rng = rand::thread_rng(); let private_key = RsaPrivateKey::from_pkcs8_pem(&private_key)?; let signing_key = SigningKey::::new(private_key); let message_json = serde_json::to_vec(&message)?; let signature = signing_key.sign_with_rng(&mut rng, &message_json); Ok(Self { message, token, digest: signature.to_vec(), }) } pub async fn inner(&self) -> &T { &self.message } pub async fn validate(&self, key: String) -> Result<(), Box> { let public_key = RsaPublicKey::from_public_key_pem(&key)?; let verifying_key: VerifyingKey = VerifyingKey::new(public_key); let message_json = serde_json::to_vec(&self.message)?; verifying_key.verify(&message_json, &Signature::try_from(self.digest.as_ref())?)?; Ok(()) } }