use anyhow::Error; use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation}; use rsa::{ pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey}, pss::{Signature, SigningKey, VerifyingKey}, sha2::Sha256, signature::{RandomizedSigner, SignatureEncoding, Verifier}, RsaPrivateKey, RsaPublicKey, }; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use crate::{ authentication::UserTokenMetadata, handshake::HandshakeMessage, model::{instance::Instance, user::User}, }; use self::{ authentication::AuthenticationMessage, discovery::DiscoveryMessage, repository::RepositoryMessage, }; pub mod authentication; pub mod discovery; pub mod issues; pub mod repository; pub mod user; #[derive(Clone, Serialize, Deserialize)] pub enum MessageKind { Handshake(HandshakeMessage), Repository(RepositoryMessage), Authentication(AuthenticationMessage), Discovery(DiscoveryMessage), } /// An authenticated message, where the instance is authenticating /// a request it is making for itself. #[derive(Serialize, Deserialize)] pub struct InstanceAuthenticated { message: T, pub instance: Instance, signature: Vec, } impl PartialEq for InstanceAuthenticated where T: PartialEq + Serialize, { fn eq(&self, other: &Self) -> bool { self.message == other.message && self.instance == other.instance && self.signature == other.signature } } impl Eq for InstanceAuthenticated where T: Eq + Serialize {} impl std::hash::Hash for InstanceAuthenticated where T: std::hash::Hash + Serialize, { fn hash(&self, state: &mut H) { self.message.hash(state); self.instance.hash(state); self.signature.hash(state); } } impl Clone for InstanceAuthenticated where T: Clone + Serialize, { fn clone(&self) -> Self { Self { message: self.message.clone(), instance: self.instance.clone(), signature: self.signature.clone(), } } } impl Debug for InstanceAuthenticated where T: Debug + Serialize, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Authenticated") .field("message", &self.message) .field("instance", &self.instance) .field("signature", &self.signature) .finish() } } impl InstanceAuthenticated { pub fn new(message: T, instance: Instance, private_key: String) -> Result { let mut rng = rand::thread_rng(); let private_key = RsaPrivateKey::from_pkcs1_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, instance, signature: signature.to_vec(), }) } pub async fn inner(&self) -> &T { &self.message } pub async fn validate(&self, instance: &Instance) -> Result<(), Error> { let public_key = public_key(instance).await?; let public_key = RsaPublicKey::from_pkcs1_pem(&public_key).unwrap(); let verifying_key: VerifyingKey = VerifyingKey::new(public_key); let message_json = serde_json::to_vec(&self.message).unwrap(); verifying_key .verify( &message_json, &Signature::try_from(self.signature.as_ref()).unwrap(), ) .unwrap(); Ok(()) } } /// An authenticated message. /// /// Includes the message, with a digest generated with /// our private key. #[derive(Serialize, Deserialize)] pub struct ValidatedUserAuthenticated { #[serde(flatten)] message: T, pub(crate) user: User, } impl Clone for ValidatedUserAuthenticated where T: Clone + Serialize, { fn clone(&self) -> Self { Self { message: self.message.clone(), user: self.user.clone(), } } } impl Debug for ValidatedUserAuthenticated where T: Debug + Serialize, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Authenticated") .field("message", &self.message) .field("user", &self.user) .finish() } } impl ValidatedUserAuthenticated { pub async fn inner(&self) -> &T { &self.message } pub async fn user(&self) -> &User { &self.user } } /// An unvalidated authenticated message. /// /// Includes the message, with a digest generated with /// our private key. #[derive(Serialize, Deserialize)] pub struct UnvalidatedUserAuthenticated { #[serde(flatten)] message: T, token: String, digest: Vec, } impl Clone for UnvalidatedUserAuthenticated where T: Clone + Serialize, { fn clone(&self) -> Self { Self { message: self.message.clone(), token: self.token.clone(), digest: self.digest.clone(), } } } impl Debug for UnvalidatedUserAuthenticated 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 UnvalidatedUserAuthenticated { pub fn new(message: T, token: String, private_key: String) -> Result { let mut rng = rand::thread_rng(); let private_key = RsaPrivateKey::from_pkcs1_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) -> Result, Error> { let instance = { let mut validation = Validation::new(Algorithm::RS256); validation.insecure_disable_signature_validation(); let value: TokenData = decode(&self.token, &DecodingKey::from_secret(b"test"), &validation).unwrap(); value.claims.generated_for.clone() }; let public_key_raw = public_key(&instance).await?; let public_key = RsaPublicKey::from_pkcs1_pem(&public_key_raw).unwrap(); let verifying_key: VerifyingKey = VerifyingKey::new(public_key); let message_json = serde_json::to_vec(&self.message).unwrap(); verifying_key .verify( &message_json, &Signature::try_from(self.digest.as_ref()).unwrap(), ) .unwrap(); let verification_key = DecodingKey::from_rsa_pem(public_key_raw.as_bytes()).unwrap(); let data: TokenData = decode( &self.token, &verification_key, &Validation::new(Algorithm::RS256), ) .unwrap(); assert_eq!(data.claims.generated_for, instance); Ok(ValidatedUserAuthenticated { message: self.message, user: data.claims.user, }) } } async fn public_key(instance: &Instance) -> Result { let key = reqwest::get(format!("https://{}/.giterated/pubkey.pem", instance.url)) .await? .text() .await?; Ok(key) }