Implement EIP-191 integrity proofs
This commit is contained in:
parent
1ec8cb4ddd
commit
75fe4df328
4 changed files with 77 additions and 2 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
use serde_json::Value;
|
||||||
use tokio_postgres::GenericClient;
|
use tokio_postgres::GenericClient;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -69,3 +70,18 @@ pub async fn prepare_update_person(
|
||||||
recipients,
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use rsa::RsaPrivateKey;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::ethereum::identity::DidPkh;
|
||||||
use crate::utils::crypto::sign_message;
|
use crate::utils::crypto::sign_message;
|
||||||
use super::canonicalization::{canonicalize_object, CanonicalizationError};
|
use super::canonicalization::{canonicalize_object, CanonicalizationError};
|
||||||
|
|
||||||
|
@ -14,6 +15,9 @@ pub const PROOF_KEY: &str = "proof";
|
||||||
// - Signature algorithm: RSASSA-PKCS1-v1_5
|
// - Signature algorithm: RSASSA-PKCS1-v1_5
|
||||||
pub const PROOF_TYPE_JCS_RSA: &str = "JcsRsaSignature2022";
|
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";
|
pub const PROOF_PURPOSE: &str = "assertionMethod";
|
||||||
|
|
||||||
/// Data Integrity Proof
|
/// Data Integrity Proof
|
||||||
|
@ -42,6 +46,19 @@ impl IntegrityProof {
|
||||||
proof_value: signature.to_string(),
|
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)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
use rsa::RsaPublicKey;
|
use rsa::RsaPublicKey;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::ethereum::{
|
||||||
|
identity::DidPkh,
|
||||||
|
signatures::recover_address,
|
||||||
|
utils::address_to_string,
|
||||||
|
};
|
||||||
use crate::utils::crypto::verify_signature;
|
use crate::utils::crypto::verify_signature;
|
||||||
use super::canonicalization::{canonicalize_object, CanonicalizationError};
|
use super::canonicalization::{canonicalize_object, CanonicalizationError};
|
||||||
use super::create::{
|
use super::create::{
|
||||||
|
@ -78,6 +83,21 @@ pub fn verify_json_signature(
|
||||||
Ok(())
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::activitypub::builders::{
|
||||||
update_person::{
|
update_person::{
|
||||||
build_update_person,
|
build_update_person,
|
||||||
prepare_update_person,
|
prepare_update_person,
|
||||||
|
prepare_signed_update_person,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
@ -25,7 +26,11 @@ use crate::ethereum::identity::{
|
||||||
create_identity_claim,
|
create_identity_claim,
|
||||||
verify_identity_proof,
|
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::oauth::auth::get_current_user;
|
||||||
use crate::mastodon_api::pagination::get_paginated_response;
|
use crate::mastodon_api::pagination::get_paginated_response;
|
||||||
use crate::mastodon_api::search::helpers::search_profiles_only;
|
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) {
|
if !current_user.profile.identity_proofs.any(&signer) {
|
||||||
return Err(ValidationError("unknown signer").into());
|
return Err(ValidationError("unknown signer").into());
|
||||||
};
|
};
|
||||||
let _activity = build_update_person(
|
let activity = build_update_person(
|
||||||
&config.instance_url(),
|
&config.instance_url(),
|
||||||
¤t_user,
|
¤t_user,
|
||||||
Some(data.internal_activity_id),
|
Some(data.internal_activity_id),
|
||||||
).map_err(|_| HttpError::InternalError)?;
|
).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(),
|
||||||
|
¤t_user,
|
||||||
|
activity_value,
|
||||||
|
).await?.spawn_deliver();
|
||||||
|
|
||||||
let account = Account::from_user(current_user, &config.instance_url());
|
let account = Account::from_user(current_user, &config.instance_url());
|
||||||
Ok(HttpResponse::Ok().json(account))
|
Ok(HttpResponse::Ok().json(account))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue