Skip to content
This repository has been archived by the owner on Jun 25, 2021. It is now read-only.

Commit

Permalink
Merge pull request #1714 from fizyk20/smd
Browse files Browse the repository at this point in the history
Secure Message Delivery initial changes
  • Loading branch information
jeanphilippeD committed Jul 19, 2019
2 parents 2ed0b1d + a03765a commit 1622e11
Show file tree
Hide file tree
Showing 5 changed files with 285 additions and 5 deletions.
190 changes: 190 additions & 0 deletions src/chain/bls_emu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// Copyright 2019 MaidSafe.net limited.
//
// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

//! Types emulating the BLS functionality until proper BLS lands
use super::{delivery_group_size, NetworkEvent, ProofSet, SectionInfo};
use crate::id::{FullId, PublicId};
use parsec;
use std::{
collections::{BTreeMap, BTreeSet},
fmt,
};

#[derive(Clone, PartialEq, Eq)]
pub struct PublicKeySet {
sec_info: SectionInfo,
threshold: usize,
}

#[derive(Clone, PartialEq, Eq)]
pub struct PublicKey(PublicKeySet);

pub type SignatureShare = ::safe_crypto::Signature;

pub struct SecretKeyShare(FullId);

#[derive(Clone, Copy, PartialEq, Eq)]
pub struct PublicKeyShare(PublicId);

#[derive(Clone, PartialEq, Eq)]
pub struct Signature {
sigs: BTreeMap<PublicId, SignatureShare>,
}

impl Signature {
pub fn from_proof_set(proofs: ProofSet) -> Self {
Self { sigs: proofs.sigs }
}
}

impl SecretKeyShare {
#[allow(unused)]
pub fn public_key_share(&self) -> PublicKeyShare {
PublicKeyShare(*self.0.public_id())
}

#[allow(unused)]
pub fn sign<M: AsRef<[u8]>>(&self, message: M) -> SignatureShare {
self.0.signing_private_key().sign_detached(message.as_ref())
}
}

impl PublicKeyShare {
#[allow(unused)]
pub fn verify<M: AsRef<[u8]>>(&self, sig: &SignatureShare, msg: M) -> bool {
self.0
.signing_public_key()
.verify_detached(sig, msg.as_ref())
}
}

impl PublicKeySet {
#[allow(unused)]
pub fn new(threshold: usize, keys: BTreeSet<PublicId>) -> Self {
let sec_info = SectionInfo::new(keys, Default::default(), None).unwrap();
Self {
threshold,
sec_info,
}
}

pub fn from_section_info(sec_info: SectionInfo) -> Self {
let threshold = delivery_group_size(sec_info.members().len()) - 1;
Self {
threshold,
sec_info,
}
}

#[allow(unused)]
pub fn threshold(&self) -> usize {
self.threshold
}

#[allow(unused)]
pub fn combine_signatures<'a, I>(&self, shares: I) -> Option<Signature>
where
I: IntoIterator<Item = (PublicKeyShare, &'a SignatureShare)>,
{
let sigs: BTreeMap<_, _> = shares
.into_iter()
.filter(|(pk, _ss)| self.sec_info.members().contains(&pk.0))
.map(|(pk, ss)| (pk.0, *ss))
.collect();
// In the BLS scheme, more than `threshold` valid signatures are needed to obtain a
// combined signature - copy this behaviour
if sigs.len() <= self.threshold {
None
} else {
Some(Signature { sigs })
}
}

#[allow(unused)]
pub fn public_key(&self) -> PublicKey {
PublicKey(self.clone())
}
}

impl PublicKey {
pub fn from_section_info(sec_info: &SectionInfo) -> Self {
PublicKey(PublicKeySet::from_section_info(sec_info.clone()))
}

pub fn verify<M: AsRef<[u8]>>(&self, sig: &Signature, msg: M) -> bool {
sig.sigs
.iter()
.filter(|&(pk, ss)| {
self.0.sec_info.members().contains(pk)
&& PublicKeyShare(*pk).verify(ss, msg.as_ref())
})
.count()
> self.0.threshold
}

pub fn as_event(&self) -> parsec::Observation<NetworkEvent, PublicId> {
parsec::Observation::OpaquePayload(NetworkEvent::SectionInfo(self.0.sec_info.clone()))
}
}

impl fmt::Debug for PublicKey {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "BLS-PublicKey({:?})", self.0.sec_info)
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::{chain::delivery_group_size, id::FullId};
use safe_crypto;
use unwrap::unwrap;

fn gen_section(size: usize) -> (PublicKeySet, Vec<SecretKeyShare>) {
unwrap!(safe_crypto::init());

let threshold = delivery_group_size(size) - 1;

let ids: Vec<_> = (0..size).map(|_| FullId::new()).collect();
let pub_ids = ids.iter().map(|full_id| *full_id.public_id()).collect();
let pk_set = PublicKeySet::new(threshold, pub_ids);

(pk_set, ids.into_iter().map(SecretKeyShare).collect())
}

#[test]
fn test_signature() {
let section_size = 10;
let min_sigs = delivery_group_size(section_size);

let (pk_set, sk_shares) = gen_section(section_size);

let data = [1u8, 2, 3, 4, 5, 6];

let mut sigs: Vec<_> = sk_shares
.iter()
.take(min_sigs - 1)
.map(|sk| (sk.public_key_share(), sk.sign(&data)))
.collect();

assert!(sigs.iter().all(|(pks, sig)| pks.verify(sig, &data)));

assert!(pk_set
.combine_signatures(sigs.iter().map(|(pk, sig)| (*pk, sig)))
.is_none());

sigs.push((
sk_shares[min_sigs - 1].public_key_share(),
sk_shares[min_sigs - 1].sign(&data),
));

let sig = unwrap!(pk_set.combine_signatures(sigs.iter().map(|(pk, sig)| (*pk, sig))));

assert!(pk_set.public_key().verify(&sig, &data));
}
}
16 changes: 15 additions & 1 deletion src/chain/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

use super::{
candidate::Candidate,
shared_state::{PrefixChange, SharedState},
shared_state::{PrefixChange, SectionProofBlock, SharedState},
GenesisPfxInfo, NeighbourSigs, NetworkEvent, OnlinePayload, Proof, ProofSet, ProvingSection,
SectionInfo,
};
Expand Down Expand Up @@ -775,6 +775,12 @@ impl Chain {
) -> Result<(), RoutingError> {
let pfx = *sec_info.prefix();
if pfx.matches(self.our_id.name()) {
self.state
.our_history
.push(SectionProofBlock::from_sec_info_with_proofs(
&sec_info,
proofs.clone(),
));
self.state.our_infos.push((sec_info.clone(), proofs));
if !self.is_member && sec_info.members().contains(&self.our_id) {
self.is_member = true;
Expand Down Expand Up @@ -1348,6 +1354,13 @@ impl Chain {
}
}

#[cfg(test)]
impl Chain {
pub fn validate_our_history(&self) -> bool {
self.state.our_history.validate()
}
}

#[cfg(test)]
mod tests {
use super::super::{GenesisPfxInfo, Proof, ProofSet, SectionInfo};
Expand Down Expand Up @@ -1511,6 +1524,7 @@ mod tests {
full_ids.extend(new_ids);
let proofs = gen_proofs(&full_ids, chain.our_info().members(), &new_info);
unwrap!(chain.add_section_info(new_info, proofs));
assert!(chain.validate_our_history());
check_infos_for_duplication(&chain);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// permissions and limitations relating to use of the SAFE Network Software.

// The `chain` submodule contains the `Chain` implementation, which we reexport here.
pub(crate) mod bls_emu;
mod candidate;
#[allow(clippy::module_inception)]
mod chain;
Expand Down
76 changes: 74 additions & 2 deletions src/chain/shared_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
// permissions and limitations relating to use of the SAFE Network Software.

use super::{ProofSet, ProvingSection, SectionInfo};
use crate::{error::RoutingError, sha3::Digest256, Prefix, XorName};
use crate::{error::RoutingError, sha3::Digest256, BlsPublicKey, BlsSignature, Prefix, XorName};
use itertools::Itertools;
use maidsafe_utilities::serialisation;
use std::{
collections::BTreeSet,
collections::{BTreeSet, HashMap},
fmt::{self, Debug, Formatter},
iter, mem,
};
Expand All @@ -31,16 +32,24 @@ pub struct SharedState {
pub split_cache: Option<(SectionInfo, ProofSet)>,
/// The set of section info hashes that are currently merging.
pub merging: BTreeSet<Digest256>,
/// Our section's key history for Secure Message Delivery
pub our_history: SectionProofChain,
/// BLS public keys of other sections
pub their_keys: HashMap<Prefix<XorName>, BlsPublicKey>,
}

impl SharedState {
pub fn new(section_info: SectionInfo) -> Self {
let pk = BlsPublicKey::from_section_info(&section_info);
let our_history = SectionProofChain::from_genesis(pk);
Self {
new_info: section_info.clone(),
our_infos: NonEmptyList::new((section_info, Default::default())),
change: PrefixChange::None,
split_cache: None,
merging: Default::default(),
our_history,
their_keys: Default::default(),
}
}

Expand Down Expand Up @@ -220,3 +229,66 @@ impl NonEmptyList<(SectionInfo, ProofSet)> {
}
}
}

#[derive(Clone, PartialEq, Eq)]
pub struct SectionProofBlock {
key: BlsPublicKey,
sig: BlsSignature,
}

impl SectionProofBlock {
pub fn from_sec_info_with_proofs(sec_info: &SectionInfo, proofs: ProofSet) -> Self {
let key = BlsPublicKey::from_section_info(sec_info);
let sig = BlsSignature::from_proof_set(proofs);
SectionProofBlock { key, sig }
}

pub fn verify_with_pk(&self, pk: &BlsPublicKey) -> bool {
let to_verify = self.key.as_event();
match serialisation::serialise(&to_verify) {
Ok(data) => pk.verify(&self.sig, data),
_ => false,
}
}
}

#[derive(Clone, PartialEq, Eq)]
pub struct SectionProofChain {
genesis_pk: BlsPublicKey,
blocks: Vec<SectionProofBlock>,
}

impl SectionProofChain {
pub fn from_genesis(pk: BlsPublicKey) -> Self {
Self {
genesis_pk: pk,
blocks: Vec::new(),
}
}

pub fn push(&mut self, block: SectionProofBlock) {
self.blocks.push(block);
}

#[allow(unused)]
pub fn validate(&self) -> bool {
let mut current_pk = &self.genesis_pk;
for block in &self.blocks {
if !block.verify_with_pk(current_pk) {
return false;
}
current_pk = &block.key;
}
true
}
}

impl Debug for SectionProofChain {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(
formatter,
"SectionProofChain(len = {})",
self.blocks.len() + 1
)
}
}
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,12 @@ pub(crate) type NetworkBytes = bytes::Bytes;
#[cfg(feature = "mock_serialise")]
pub(crate) type NetworkBytes = std::rc::Rc<crate::messages::Message>;

pub(crate) use self::network_service::NetworkService;
pub use self::quic_p2p::Config as NetworkConfig;
pub(crate) use self::quic_p2p::{Event as NetworkEvent, Peer as ConnectionInfo, QuicP2p};
pub(crate) use self::{
chain::bls_emu::{PublicKey as BlsPublicKey, Signature as BlsSignature},
network_service::NetworkService,
quic_p2p::{Event as NetworkEvent, Peer as ConnectionInfo, QuicP2p},
};

#[cfg(test)]
mod tests {
Expand Down

0 comments on commit 1622e11

Please sign in to comment.