Skip to content

Commit

Permalink
Merge pull request #265 from streetycat/Issue_#71
Browse files Browse the repository at this point in the history
Issue #71: Group
  • Loading branch information
lurenpluto committed May 18, 2023
2 parents 4d93a52 + c2bfcaf commit e5e8b3a
Show file tree
Hide file tree
Showing 151 changed files with 20,496 additions and 1,031 deletions.
3 changes: 3 additions & 0 deletions src/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ members = [
"./component/cyfs-chunk-lib",
"./component/cyfs-mobile-stack",
"./component/cyfs-bdt-ext",
"./component/cyfs-group-lib",
"./component/cyfs-group",

"./service/ood-control",
"./service/ood-daemon",
Expand Down Expand Up @@ -74,6 +76,7 @@ members = [
"./tests/cyfs-stack-test",
"./tests/cyfs-bench-mark",
"./tests/cyfs-stack-bench",
"./tests/group-example",
]

[profile.release]
Expand Down
2 changes: 1 addition & 1 deletion src/component/cyfs-backup/src/state_backup/roots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl RootObjectCategoryManager {
list.push(item);

let item = RootObjectCategory {
object_type: ObjectTypeCode::SimpleGroup.to_u16(),
object_type: ObjectTypeCode::Group.to_u16(),
ref_depth: 0,
};
list.push(item);
Expand Down
1 change: 1 addition & 0 deletions src/component/cyfs-base-meta/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ serde_json = '1.0'
hex = '0.4.2'
async-trait = '0.1.53'
async-std = '1.11'
futures = '0.3.25'
primitive-types = { version = '0.9' }
codec = { package = 'parity-scale-codec', version = '2.0', default-features = false, features = ['derive', 'full'], optional = true }
238 changes: 238 additions & 0 deletions src/component/cyfs-base-meta/src/group.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
use std::collections::HashSet;

use cyfs_base::{
BuckyError, BuckyErrorCode, BuckyResult, Group, NamedObject, ObjectDesc, ObjectId,
ObjectTypeCode, People, PeopleId, RawEncode, RsaCPUObjectVerifier, Signature,
SingleKeyObjectDesc, Verifier,
};
use cyfs_core::ToGroupShell;

async fn verify_signature(
signs: Option<&Vec<Signature>>,
data_buf: &[u8],
verifier: &RsaCPUObjectVerifier,
signer_id: &PeopleId,
) -> BuckyResult<()> {
let signs = signs.map_or([].as_slice(), |s| s.as_slice());
let sign = signs.iter().find(|s| match s.sign_source() {
cyfs_base::SignatureSource::Object(signer) => &signer.obj_id == signer_id.object_id(),
_ => false,
});

match sign {
Some(sign) => {
if verifier.verify(data_buf, sign).await {
Ok(())
} else {
let msg = format!("Invalid signature from {}", signer_id);
log::warn!("{}", msg);
Err(BuckyError::new(BuckyErrorCode::InvalidSignature, msg))
}
}
None => {
let msg = format!("Not found signature from {}", signer_id);
log::warn!("{}", msg);
Err(BuckyError::new(BuckyErrorCode::NotFound, msg))
}
}
}

async fn verify_group_signature(
group: &Group,
people_id: &PeopleId,
member_querier: &impl MemberQuerier,
) -> BuckyResult<()> {
let people = member_querier.get_people(people_id).await?;
let verifier = RsaCPUObjectVerifier::new(people.desc().public_key().clone());
let desc_buf = group.desc().raw_hash_value()?;
verify_signature(
group.signs().desc_signs(),
desc_buf.as_slice(),
&verifier,
people_id,
)
.await?;
let body_buf = group.body().as_ref().unwrap().raw_hash_value()?;
verify_signature(
group.signs().body_signs(),
body_buf.as_slice(),
&verifier,
people_id,
)
.await
}

#[async_trait::async_trait]
pub trait MemberQuerier: Send + Sync {
async fn get_people(&self, people_id: &PeopleId) -> BuckyResult<People>;
}

#[async_trait::async_trait]
pub trait GroupVerifier {
// Check the update is allowed
async fn is_update_valid(
&self,
latest_group: Option<&Group>,
member_querier: &impl MemberQuerier,
) -> BuckyResult<()>;
}

#[async_trait::async_trait]
impl GroupVerifier for Group {
async fn is_update_valid(
&self,
latest_group: Option<&Group>,
member_querier: &impl MemberQuerier,
) -> BuckyResult<()> {
let group_id = self.desc().object_id();

if self.admins().len() == 0 {
let msg = format!("Update group({}) with no admins.", group_id);
log::warn!("{}", msg);
return Err(BuckyError::new(BuckyErrorCode::Failed, msg));
}

let (last_admins, last_members) = match latest_group {
Some(latest_group) => {
let latest_group_id = latest_group.desc().object_id();
if group_id != latest_group_id {
let msg = format!(
"The new group({}) is unmatch with the latest group({}).",
group_id, latest_group_id
);
log::warn!("{}", msg);
return Err(BuckyError::new(BuckyErrorCode::Unmatch, msg));
}

let latest_group_shell = latest_group.to_shell();
let latest_group_shell_id = latest_group_shell.shell_id();
if self.version() != latest_group.version() + 1
|| self.prev_shell_id() != &Some(latest_group_shell_id)
{
let msg = format!("Attempt to update group({}) from unknown version({}-1/{:?}), latest version: {}/{}.", group_id, self.version(), self.prev_shell_id(), latest_group.version(), latest_group_shell_id);
log::warn!("{}", msg);
return Err(BuckyError::new(BuckyErrorCode::Unmatch, msg));
}

(
HashSet::<ObjectId>::from_iter(
latest_group
.admins()
.keys()
.filter(|m| m.obj_type_code() == ObjectTypeCode::People)
.map(|m| *m),
),
HashSet::<ObjectId>::from_iter(
latest_group
.members()
.keys()
.filter(|m| m.obj_type_code() == ObjectTypeCode::People)
.map(|m| *m),
),
)
}
None => match self.prev_shell_id() {
Some(prev_shell_id) => {
let msg = format!(
"The latest group({}) is necessary for update. prev_shell_id: {}",
group_id, prev_shell_id
);
log::warn!("{}", msg);
return Err(BuckyError::new(BuckyErrorCode::Unmatch, msg));
}
None => {
if let Some(founder) = self.founder_id() {
if self.admins().values().find(|m| &m.id == founder).is_none() {
let msg = format!(
"Update group({}) the founder({}) must be an administrator.",
group_id, founder
);
log::warn!("{}", msg);
return Err(BuckyError::new(BuckyErrorCode::Failed, msg));
}
}
(HashSet::new(), HashSet::new())
}
},
};

// admins: > 1/2
// new members: all

let add_admins = HashSet::<ObjectId>::from_iter(
self.admins()
.keys()
.filter(|m| {
m.obj_type_code() == ObjectTypeCode::People && !last_admins.contains(*m)
})
.map(|m| *m),
);
let add_members = HashSet::<ObjectId>::from_iter(
self.members()
.keys()
.filter(|m| {
m.obj_type_code() == ObjectTypeCode::People && !last_members.contains(*m)
})
.map(|m| *m),
);

let additionals = add_admins
.union(&add_members)
.map(|id| *id)
.collect::<Vec<_>>();
if additionals.len() != add_admins.len() + add_members.len() {
let msg = format!(
"Update group({}) with admins is not necessary in members.",
group_id
);
log::warn!("{}", msg);
return Err(BuckyError::new(BuckyErrorCode::Failed, msg));
}

let check_peoples =
futures::future::join_all(last_admins.iter().chain(additionals.iter()).map(
|people_id| async move {
let people_id = PeopleId::try_from(people_id).unwrap();
verify_group_signature(self, &people_id, member_querier).await
},
))
.await;

let (last_admin_signs, add_member_signs) = check_peoples.split_at(last_admins.len());
let last_admin_sign_count = last_admin_signs.iter().filter(|s| s.is_ok()).count();
if last_admins.len() > 0 && last_admin_sign_count <= last_admins.len() / 2 {
let msg = format!(
"Update group({}) failed for signatures from admins in latest version is not enough: expected {}, got {}.",
group_id,
last_admins.len() / 2 + 1,
last_admin_sign_count
);
log::warn!("{}", msg);
return Err(BuckyError::new(BuckyErrorCode::InvalidSignature, msg));
}

let failed_add_member_sign_pos = add_member_signs.iter().position(|s| s.is_err());
match failed_add_member_sign_pos {
Some(pos) => {
let msg = format!(
"Update group({}) failed for signatures from additional member({:?}) is invalid.",
group_id,
additionals.get(pos)
);
log::warn!("{}", msg);
return Err(BuckyError::new(BuckyErrorCode::InvalidSignature, msg));
}
None => {
log::info!(
"Update group({}) verify ok, from {:?}/{:?}, to {}/{}",
group_id,
latest_group.map(|group| group.version()),
self.prev_shell_id(),
self.version(),
self.to_shell().shell_id()
);
Ok(())
}
}
}
}
26 changes: 14 additions & 12 deletions src/component/cyfs-base-meta/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
extern crate alloc;

pub use block::*;
pub use code::*;
pub use config::*;
pub use contract::*;
pub use event::*;
pub use extension::*;
pub use group::*;
pub use nft::*;
pub use sn_service::*;
pub use spv::*;
pub use tx::*;
pub use types::*;
pub use view::*;
pub use code::*;
pub use contract::*;
pub use nft::*;

mod types;
mod config;
mod block;
mod view;
mod event;
mod tx;
mod sn_service;
mod spv;
mod extension;
mod code;
mod config;
mod contract;
mod nft;
mod event;
pub mod evm_def;
mod extension;
mod group;
mod nft;
mod sn_service;
mod spv;
mod tx;
mod types;
mod view;
Loading

0 comments on commit e5e8b3a

Please sign in to comment.