2022-10-23 19:13:05 +00:00
|
|
|
use actix_web::HttpRequest;
|
2022-10-23 23:46:51 +00:00
|
|
|
use serde_json::Value;
|
2022-10-23 19:13:05 +00:00
|
|
|
|
2023-04-25 13:49:35 +00:00
|
|
|
use fedimovies_config::Config;
|
|
|
|
use fedimovies_models::{
|
2023-03-30 20:27:17 +00:00
|
|
|
database::{DatabaseClient, DatabaseError},
|
|
|
|
profiles::queries::get_profile_by_remote_actor_id,
|
|
|
|
profiles::types::DbActorProfile,
|
|
|
|
};
|
2023-04-25 13:49:35 +00:00
|
|
|
use fedimovies_utils::{crypto_rsa::deserialize_public_key};
|
2023-02-18 22:25:49 +00:00
|
|
|
|
2022-10-23 19:13:05 +00:00
|
|
|
use crate::http_signatures::verify::{
|
2023-04-24 15:35:32 +00:00
|
|
|
parse_http_signature, verify_http_signature,
|
2022-10-27 18:23:48 +00:00
|
|
|
HttpSignatureVerificationError as HttpSignatureError,
|
2022-10-23 19:13:05 +00:00
|
|
|
};
|
2023-03-14 20:47:27 +00:00
|
|
|
use crate::json_signatures::{
|
|
|
|
proofs::ProofType,
|
|
|
|
verify::{
|
2023-04-25 13:49:35 +00:00
|
|
|
get_json_signature,
|
2023-04-24 15:35:32 +00:00
|
|
|
verify_rsa_json_signature, JsonSignatureVerificationError as JsonSignatureError,
|
2023-03-14 20:47:27 +00:00
|
|
|
JsonSigner,
|
|
|
|
},
|
2022-10-23 23:46:51 +00:00
|
|
|
};
|
2023-03-25 21:50:10 +00:00
|
|
|
use crate::media::MediaStorage;
|
2023-03-30 20:27:17 +00:00
|
|
|
|
2022-10-23 19:13:05 +00:00
|
|
|
use super::fetcher::helpers::get_or_import_profile_by_actor_id;
|
|
|
|
use super::receiver::HandlerError;
|
|
|
|
|
2022-10-27 18:23:48 +00:00
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
pub enum AuthenticationError {
|
|
|
|
#[error(transparent)]
|
|
|
|
HttpSignatureError(#[from] HttpSignatureError),
|
|
|
|
|
2022-12-07 22:10:58 +00:00
|
|
|
#[error("no HTTP signature")]
|
|
|
|
NoHttpSignature,
|
|
|
|
|
2022-10-23 23:46:51 +00:00
|
|
|
#[error(transparent)]
|
|
|
|
JsonSignatureError(#[from] JsonSignatureError),
|
|
|
|
|
|
|
|
#[error("no JSON signature")]
|
|
|
|
NoJsonSignature,
|
|
|
|
|
2022-11-23 11:40:36 +00:00
|
|
|
#[error("invalid JSON signature type")]
|
|
|
|
InvalidJsonSignatureType,
|
|
|
|
|
2022-10-27 18:23:48 +00:00
|
|
|
#[error("invalid key ID")]
|
|
|
|
InvalidKeyId(#[from] url::ParseError),
|
|
|
|
|
|
|
|
#[error("database error")]
|
|
|
|
DatabaseError(#[from] DatabaseError),
|
|
|
|
|
|
|
|
#[error("{0}")]
|
2022-11-27 10:53:51 +00:00
|
|
|
ImportError(String),
|
|
|
|
|
|
|
|
#[error("{0}")]
|
|
|
|
ActorError(&'static str),
|
2022-10-27 18:23:48 +00:00
|
|
|
|
|
|
|
#[error("invalid public key")]
|
|
|
|
InvalidPublicKey(#[from] rsa::pkcs8::Error),
|
|
|
|
|
|
|
|
#[error("actor and request signer do not match")]
|
2022-11-23 11:40:36 +00:00
|
|
|
UnexpectedSigner,
|
2022-10-27 18:23:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn key_id_to_actor_id(key_id: &str) -> Result<String, AuthenticationError> {
|
2022-10-23 19:13:05 +00:00
|
|
|
let key_url = url::Url::parse(key_id)?;
|
|
|
|
// Strip #main-key (works with most AP servers)
|
|
|
|
let actor_id = &key_url[..url::Position::BeforeQuery];
|
|
|
|
// GoToSocial compat
|
|
|
|
let actor_id = actor_id.trim_end_matches("/main-key");
|
|
|
|
Ok(actor_id.to_string())
|
|
|
|
}
|
|
|
|
|
2022-12-08 16:05:14 +00:00
|
|
|
async fn get_signer(
|
|
|
|
config: &Config,
|
2023-01-21 00:23:15 +00:00
|
|
|
db_client: &mut impl DatabaseClient,
|
2022-12-08 16:05:14 +00:00
|
|
|
signer_id: &str,
|
|
|
|
no_fetch: bool,
|
|
|
|
) -> Result<DbActorProfile, AuthenticationError> {
|
|
|
|
let signer = if no_fetch {
|
|
|
|
// Avoid fetching (e.g. if signer was deleted)
|
|
|
|
get_profile_by_remote_actor_id(db_client, signer_id).await?
|
|
|
|
} else {
|
|
|
|
match get_or_import_profile_by_actor_id(
|
|
|
|
db_client,
|
|
|
|
&config.instance(),
|
2023-03-25 21:50:10 +00:00
|
|
|
&MediaStorage::from(config),
|
2022-12-08 16:05:14 +00:00
|
|
|
signer_id,
|
2023-04-24 15:35:32 +00:00
|
|
|
)
|
|
|
|
.await
|
|
|
|
{
|
2022-12-08 16:05:14 +00:00
|
|
|
Ok(profile) => profile,
|
|
|
|
Err(HandlerError::DatabaseError(error)) => return Err(error.into()),
|
|
|
|
Err(other_error) => {
|
|
|
|
return Err(AuthenticationError::ImportError(other_error.to_string()));
|
2023-04-24 15:35:32 +00:00
|
|
|
}
|
2022-12-08 16:05:14 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(signer)
|
|
|
|
}
|
|
|
|
|
2022-10-23 19:13:05 +00:00
|
|
|
/// Verifies HTTP signature and returns signer
|
|
|
|
pub async fn verify_signed_request(
|
|
|
|
config: &Config,
|
2023-01-21 00:23:15 +00:00
|
|
|
db_client: &mut impl DatabaseClient,
|
2022-10-23 19:13:05 +00:00
|
|
|
request: &HttpRequest,
|
|
|
|
no_fetch: bool,
|
2022-10-27 18:23:48 +00:00
|
|
|
) -> Result<DbActorProfile, AuthenticationError> {
|
2023-04-24 15:35:32 +00:00
|
|
|
let signature_data =
|
|
|
|
match parse_http_signature(request.method(), request.uri(), request.headers()) {
|
|
|
|
Ok(signature_data) => signature_data,
|
|
|
|
Err(HttpSignatureError::NoSignature) => {
|
|
|
|
return Err(AuthenticationError::NoHttpSignature);
|
|
|
|
}
|
|
|
|
Err(other_error) => return Err(other_error.into()),
|
|
|
|
};
|
2022-10-23 19:13:05 +00:00
|
|
|
|
2022-12-07 23:27:05 +00:00
|
|
|
let signer_id = key_id_to_actor_id(&signature_data.key_id)?;
|
2022-12-08 16:05:14 +00:00
|
|
|
let signer = get_signer(config, db_client, &signer_id, no_fetch).await?;
|
2023-04-24 15:35:32 +00:00
|
|
|
let signer_actor = signer
|
|
|
|
.actor_json
|
|
|
|
.as_ref()
|
2022-12-07 23:27:05 +00:00
|
|
|
.expect("request should be signed by remote actor");
|
2023-04-24 15:35:32 +00:00
|
|
|
let signer_key = deserialize_public_key(&signer_actor.public_key.public_key_pem)?;
|
2022-10-23 19:13:05 +00:00
|
|
|
|
2022-12-07 23:27:05 +00:00
|
|
|
verify_http_signature(&signature_data, &signer_key)?;
|
2022-10-23 19:13:05 +00:00
|
|
|
|
2022-12-07 23:27:05 +00:00
|
|
|
Ok(signer)
|
2022-10-23 19:13:05 +00:00
|
|
|
}
|
|
|
|
|
2022-12-08 00:11:27 +00:00
|
|
|
/// Verifies JSON signature and returns signer
|
2022-10-23 23:46:51 +00:00
|
|
|
pub async fn verify_signed_activity(
|
|
|
|
config: &Config,
|
2023-01-21 00:23:15 +00:00
|
|
|
db_client: &mut impl DatabaseClient,
|
2022-10-23 23:46:51 +00:00
|
|
|
activity: &Value,
|
2022-12-08 16:05:14 +00:00
|
|
|
no_fetch: bool,
|
2022-10-23 23:46:51 +00:00
|
|
|
) -> Result<DbActorProfile, AuthenticationError> {
|
2022-12-07 22:10:58 +00:00
|
|
|
let signature_data = match get_json_signature(activity) {
|
|
|
|
Ok(signature_data) => signature_data,
|
|
|
|
Err(JsonSignatureError::NoProof) => {
|
|
|
|
return Err(AuthenticationError::NoJsonSignature);
|
2023-04-24 15:35:32 +00:00
|
|
|
}
|
2022-12-07 22:10:58 +00:00
|
|
|
Err(other_error) => return Err(other_error.into()),
|
|
|
|
};
|
2022-12-12 22:49:52 +00:00
|
|
|
// Signed activities must have `actor` property, to avoid situations
|
|
|
|
// where signer is identified by DID but there is no matching
|
|
|
|
// identity proof in the local database.
|
2023-04-24 15:35:32 +00:00
|
|
|
let actor_id = activity["actor"]
|
|
|
|
.as_str()
|
2022-12-12 22:49:52 +00:00
|
|
|
.ok_or(AuthenticationError::ActorError("unknown actor"))?;
|
|
|
|
let actor_profile = get_signer(config, db_client, actor_id, no_fetch).await?;
|
2022-11-02 17:15:15 +00:00
|
|
|
|
2022-12-08 00:11:27 +00:00
|
|
|
match signature_data.signer {
|
2022-11-02 12:16:10 +00:00
|
|
|
JsonSigner::ActorKeyId(ref key_id) => {
|
2023-03-12 23:37:40 +00:00
|
|
|
if signature_data.signature_type != ProofType::JcsRsaSignature {
|
2022-11-23 11:40:36 +00:00
|
|
|
return Err(AuthenticationError::InvalidJsonSignatureType);
|
|
|
|
};
|
2022-12-07 23:27:05 +00:00
|
|
|
let signer_id = key_id_to_actor_id(key_id)?;
|
2022-12-08 00:11:27 +00:00
|
|
|
if signer_id != actor_id {
|
|
|
|
return Err(AuthenticationError::UnexpectedSigner);
|
2022-11-02 12:16:10 +00:00
|
|
|
};
|
2023-04-24 15:35:32 +00:00
|
|
|
let signer_actor = actor_profile
|
|
|
|
.actor_json
|
|
|
|
.as_ref()
|
2022-12-07 23:27:05 +00:00
|
|
|
.expect("activity should be signed by remote actor");
|
2023-04-24 15:35:32 +00:00
|
|
|
let signer_key = deserialize_public_key(&signer_actor.public_key.public_key_pem)?;
|
2022-12-07 23:27:05 +00:00
|
|
|
verify_rsa_json_signature(&signature_data, &signer_key)?;
|
2023-04-24 15:35:32 +00:00
|
|
|
}
|
2022-11-10 18:47:22 +00:00
|
|
|
JsonSigner::Did(did) => {
|
2023-04-25 13:49:35 +00:00
|
|
|
return Err(AuthenticationError::InvalidJsonSignatureType);
|
2023-04-24 15:35:32 +00:00
|
|
|
}
|
2022-10-23 23:46:51 +00:00
|
|
|
};
|
2022-12-08 00:11:27 +00:00
|
|
|
// Signer is actor
|
|
|
|
Ok(actor_profile)
|
2022-10-23 23:46:51 +00:00
|
|
|
}
|
|
|
|
|
2022-10-23 19:13:05 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_key_id_to_actor_id() {
|
|
|
|
let key_id = "https://myserver.org/actor#main-key";
|
|
|
|
let actor_id = key_id_to_actor_id(key_id).unwrap();
|
|
|
|
assert_eq!(actor_id, "https://myserver.org/actor");
|
|
|
|
|
|
|
|
let key_id = "https://myserver.org/actor/main-key";
|
|
|
|
let actor_id = key_id_to_actor_id(key_id).unwrap();
|
|
|
|
assert_eq!(actor_id, "https://myserver.org/actor");
|
|
|
|
}
|
|
|
|
}
|