Implement EIP-191 integrity proofs

This commit is contained in:
silverpill 2022-10-29 00:24:17 +00:00
parent 1ec8cb4ddd
commit 75fe4df328
4 changed files with 77 additions and 2 deletions

View file

@ -1,3 +1,4 @@
use serde_json::Value;
use tokio_postgres::GenericClient;
use uuid::Uuid;
@ -69,3 +70,18 @@ pub async fn prepare_update_person(
recipients,
})
}
pub async fn prepare_signed_update_person(
db_client: &impl GenericClient,
instance: &Instance,
user: &User,
activity: Value,
) -> Result<OutgoingActivity<Value>, DatabaseError> {
let recipients = get_update_person_recipients(db_client, &user.id).await?;
Ok(OutgoingActivity {
instance: instance.clone(),
sender: user.clone(),
activity,
recipients,
})
}

View file

@ -3,6 +3,7 @@ use rsa::RsaPrivateKey;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::ethereum::identity::DidPkh;
use crate::utils::crypto::sign_message;
use super::canonicalization::{canonicalize_object, CanonicalizationError};
@ -14,6 +15,9 @@ pub const PROOF_KEY: &str = "proof";
// - Signature algorithm: RSASSA-PKCS1-v1_5
pub const PROOF_TYPE_JCS_RSA: &str = "JcsRsaSignature2022";
// Similar to EthereumPersonalSignature2021 but with JCS
pub const PROOF_TYPE_JCS_EIP191: &str ="JcsEip191Signature2022";
pub const PROOF_PURPOSE: &str = "assertionMethod";
/// Data Integrity Proof
@ -42,6 +46,19 @@ impl IntegrityProof {
proof_value: signature.to_string(),
}
}
pub fn jcs_eip191(
signer: &DidPkh,
signature: &str,
) -> Self {
Self {
proof_type: PROOF_TYPE_JCS_EIP191.to_string(),
proof_purpose: PROOF_PURPOSE.to_string(),
verification_method: signer.to_string(),
created: Utc::now(),
proof_value: signature.to_string(),
}
}
}
#[derive(thiserror::Error, Debug)]

View file

@ -1,6 +1,11 @@
use rsa::RsaPublicKey;
use serde_json::Value;
use crate::ethereum::{
identity::DidPkh,
signatures::recover_address,
utils::address_to_string,
};
use crate::utils::crypto::verify_signature;
use super::canonicalization::{canonicalize_object, CanonicalizationError};
use super::create::{
@ -78,6 +83,21 @@ pub fn verify_json_signature(
Ok(())
}
pub fn verify_jcs_eip191_signature(
signer: &DidPkh,
message: &str,
signature: &str,
) -> Result<(), VerificationError> {
let signature_data = signature.parse()
.map_err(|_| VerificationError::InvalidProof("invalid proof"))?;
let signer_address = recover_address(message.as_bytes(), &signature_data)
.map_err(|_| VerificationError::InvalidSignature)?;
if address_to_string(signer_address) != signer.address.to_lowercase() {
return Err(VerificationError::InvalidSignature);
};
Ok(())
}
#[cfg(test)]
mod tests {
use serde_json::json;

View file

@ -11,6 +11,7 @@ use crate::activitypub::builders::{
update_person::{
build_update_person,
prepare_update_person,
prepare_signed_update_person,
},
};
use crate::config::Config;
@ -25,7 +26,11 @@ use crate::ethereum::identity::{
create_identity_claim,
verify_identity_proof,
};
use crate::json_signatures::canonicalization::canonicalize_object;
use crate::json_signatures::{
canonicalization::canonicalize_object,
create::{add_integrity_proof, IntegrityProof},
verify::verify_jcs_eip191_signature,
};
use crate::mastodon_api::oauth::auth::get_current_user;
use crate::mastodon_api::pagination::get_paginated_response;
use crate::mastodon_api::search::helpers::search_profiles_only;
@ -249,11 +254,28 @@ async fn send_signed_update(
if !current_user.profile.identity_proofs.any(&signer) {
return Err(ValidationError("unknown signer").into());
};
let _activity = build_update_person(
let activity = build_update_person(
&config.instance_url(),
&current_user,
Some(data.internal_activity_id),
).map_err(|_| HttpError::InternalError)?;
let canonical_json = canonicalize_object(&activity)
.map_err(|_| HttpError::InternalError)?;
verify_jcs_eip191_signature(&signer, &canonical_json, &data.signature)
.map_err(|_| ValidationError("invalid signature"))?;
let proof = IntegrityProof::jcs_eip191(&signer, &data.signature);
let mut activity_value = serde_json::to_value(activity)
.map_err(|_| HttpError::InternalError)?;
add_integrity_proof(&mut activity_value, proof)
.map_err(|_| HttpError::InternalError)?;
prepare_signed_update_person(
db_client,
&config.instance(),
&current_user,
activity_value,
).await?.spawn_deliver();
let account = Account::from_user(current_user, &config.instance_url());
Ok(HttpResponse::Ok().json(account))
}