diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..08adce5 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,39 @@ +version: 2.1 + +orbs: + docker: circleci/docker@1.5.0 + +jobs: + Build: + docker: + - image: circleci/rust:latest + resource_class: xlarge + environment: + RUST_BACKTRACE: 1 + steps: + - checkout + - run: | + sudo bash .github/download_marine.sh + - restore_cache: + keys: + - trust-graph00-{{ checksum "./service/Cargo.lock" }}-{{ checksum "./Cargo.lock" }}-{{ checksum "./keypair/Cargo.lock" }} + - run: | + rustup toolchain install nightly-2021-04-24-x86_64-unknown-linux-gnu + rustup default nightly-2021-04-24-x86_64-unknown-linux-gnu + rustup target add wasm32-wasi --toolchain nightly-2021-04-24-x86_64-unknown-linux-gnu + cargo test --no-fail-fast --release --all-features -- + cd ./service + ./build.sh + cargo test --no-fail-fast --release --all-features -- + - save_cache: + paths: + - ~/.cargo + - ~/.rustup + key: trust-graph00-{{ checksum "./service/Cargo.lock" }}-{{ checksum "./Cargo.lock" }}-{{ checksum "./keypair/Cargo.lock" }} + + +workflows: + version: 2 + CircleCI: + jobs: + - Build diff --git a/Cargo.lock b/Cargo.lock index 91bd417..fbda802 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2481,18 +2481,16 @@ version = "0.2.7" dependencies = [ "bs58 0.3.1", "derivative", - "ed25519-dalek", "failure", "fluence-fork-libp2p-core", "fluence-keypair", - "libsecp256k1", "log", "rand 0.7.3", "ref-cast", - "ring", "serde", "serde_json", "serde_with", + "sha2 0.9.5", "signature", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index f5276dd..e15fcf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" repository = "https://github.com/fluencelabs/trust-graph" [dependencies] -libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2" } +libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2", features = ["secp256k1"] } serde = { version = "=1.0.118", features = ["derive"] } fluence-keypair = { path = "./keypair", version = "0.3.0" } @@ -18,14 +18,10 @@ failure = "0.1.6" log = "0.4.11" ref-cast = "1.0.2" derivative = "2.1.1" -ed25519-dalek = { version = "1.0.1", features = ["serde"] } signature = "1.3.0" serde_with = "1.6.0" thiserror = "1.0.23" -libsecp256k1 = "0.3.5" - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -ring = "0.16.20" +sha2 = "0.9.5" rand = "0.7.0" [workspace] diff --git a/keypair/Cargo.toml b/keypair/Cargo.toml index 1be1e9a..9deee80 100644 --- a/keypair/Cargo.toml +++ b/keypair/Cargo.toml @@ -18,12 +18,12 @@ ed25519 = "1.0.3" serde_with = "1.6.0" thiserror = "1.0.23" lazy_static = "1.2" -libsecp256k1 = "0.3.5" +libsecp256k1 = "0.3.1" asn1_der = "0.6.1" sha2 = "0.9.1" zeroize = "1" serde_bytes = "0.11" -libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2" } +libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2", features = ["secp256k1"]} eyre = "0.6.5" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/keypair/src/ed25519.rs b/keypair/src/ed25519.rs index c8ee2a2..3e9aa0c 100644 --- a/keypair/src/ed25519.rs +++ b/keypair/src/ed25519.rs @@ -136,7 +136,7 @@ impl PublicKey { } /// An Ed25519 secret key. -pub struct SecretKey(ed25519::SecretKey); +pub struct SecretKey(pub ed25519::SecretKey); /// View the bytes of the secret key. impl AsRef<[u8]> for SecretKey { diff --git a/keypair/src/error.rs b/keypair/src/error.rs index fa0b06c..37e02a5 100644 --- a/keypair/src/error.rs +++ b/keypair/src/error.rs @@ -47,6 +47,8 @@ pub enum DecodingError { InvalidTypeByte, #[error("Cannot decode public key from base58 :{0}")] Base58DecodeError(#[source] bs58::decode::Error), + #[error("Raw signature decoding failed: type {0} not supported")] + RawSignatureUnsupportedType(String), } /// An error during signing of a message. diff --git a/keypair/src/key_pair.rs b/keypair/src/key_pair.rs index 9a3df83..a6fb43b 100644 --- a/keypair/src/key_pair.rs +++ b/keypair/src/key_pair.rs @@ -28,6 +28,7 @@ use crate::signature::Signature; use crate::error::{Error, DecodingError, SigningError}; use std::str::FromStr; use std::convert::TryFrom; +use libp2p_core::PeerId; /// Identity keypair of a node. /// @@ -167,6 +168,16 @@ impl KeyPair { } } + pub fn secret(&self) -> eyre::Result> { + use KeyPair::*; + match self { + Ed25519(pair) => Ok(pair.secret().0.to_bytes().to_vec()), + #[cfg(not(target_arch = "wasm32"))] + Rsa(_) => Err(eyre::eyre!("secret key is not available for RSA")), + Secp256k1(pair) => Ok(pair.secret().to_bytes().to_vec()), + } + } + /// Verify the signature on a message using the public key. pub fn verify(pk: &PublicKey, msg: &[u8], signature: &Signature) -> Result<(), SigningError> { pk.verify(msg, signature) @@ -192,6 +203,10 @@ impl KeyPair { KeyFormat::Rsa => Err(DecodingError::KeypairDecodingIsNotSupported) } } + + pub fn get_peer_id(&self) -> PeerId { + self.public().to_peer_id() + } } impl From for KeyPair { diff --git a/keypair/src/lib.rs b/keypair/src/lib.rs index db7b509..5498856 100644 --- a/keypair/src/lib.rs +++ b/keypair/src/lib.rs @@ -39,3 +39,26 @@ pub use key_pair::KeyPair; pub use key_pair::KeyFormat; pub use crate::public_key::PublicKey; pub use crate::signature::Signature; + +pub mod peerid_serializer { + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use std::str::FromStr; + use libp2p_core::PeerId; + + pub fn serialize(value: &PeerId, serializer: S) -> Result + where + S: Serializer, + { + value.to_base58().serialize(serializer) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let str = String::deserialize(deserializer)?; + PeerId::from_str(&str).map_err(|e| { + serde::de::Error::custom(format!("peer id deserialization failed for {:?}", e)) + }) + } +} \ No newline at end of file diff --git a/keypair/src/peer_id.rs b/keypair/src/peer_id.rs deleted file mode 100644 index e69de29..0000000 diff --git a/keypair/src/public_key.rs b/keypair/src/public_key.rs index 82aea91..1e0c761 100644 --- a/keypair/src/public_key.rs +++ b/keypair/src/public_key.rs @@ -23,6 +23,7 @@ use crate::signature::Signature; use serde::{Deserialize, Serialize}; use crate::key_pair::KeyFormat; use std::convert::TryFrom; +use libp2p_core::PeerId; /// The public key of a node's identity keypair. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -99,6 +100,10 @@ impl PublicKey { Secp256k1(pk) => pk.encode().to_vec(), } } + + pub fn to_peer_id(&self) -> PeerId { + PeerId::from_public_key(self.clone().into()) + } } impl From for PublicKey { @@ -114,6 +119,19 @@ impl From for PublicKey { } } +impl From for libp2p_core::identity::PublicKey { + fn from(key: PublicKey) -> Self { + use libp2p_core::identity as libp2p_identity; + + match key { + PublicKey::Ed25519(key) => libp2p_identity::PublicKey::Ed25519(libp2p_identity::ed25519::PublicKey::decode(&key.encode()[..]).unwrap()), + #[cfg(not(target_arch = "wasm32"))] + PublicKey::Rsa(key) => libp2p_identity::PublicKey::Rsa(libp2p_identity::rsa::PublicKey::decode_x509(&key.encode_x509()).unwrap()), + PublicKey::Secp256k1(key) => libp2p_identity::PublicKey::Secp256k1(libp2p_identity::secp256k1::PublicKey::decode(&key.encode()[..]).unwrap()), + } + } +} + pub fn peer_id_to_fluence_pk(peer_id: libp2p_core::PeerId) -> eyre::Result { Ok(peer_id.as_public_key().ok_or(eyre::eyre!("public key is not inlined in peer id: {}", peer_id))?.into()) } diff --git a/keypair/src/signature.rs b/keypair/src/signature.rs index f8f1b09..2383473 100644 --- a/keypair/src/signature.rs +++ b/keypair/src/signature.rs @@ -30,6 +30,11 @@ pub enum Signature { Secp256k1(secp256k1::Signature), } +pub struct RawSignature { + pub bytes: Vec, + pub sig_type: String, +} + impl Signature { fn get_prefix(&self) -> u8 { use Signature::*; @@ -46,7 +51,7 @@ impl Signature { use Signature::*; let mut result: Vec = vec![self.get_prefix()]; - + match self { Ed25519(sig) => result.extend(sig.0.clone()), #[cfg(not(target_arch = "wasm32"))] @@ -64,7 +69,6 @@ impl Signature { #[cfg(not(target_arch = "wasm32"))] KeyFormat::Rsa => Ok(Signature::Rsa(rsa::Signature(bytes[1..].to_vec()))), KeyFormat::Secp256k1 => Ok(Signature::Secp256k1(secp256k1::Signature(bytes[1..].to_vec()))), - } } @@ -78,6 +82,31 @@ impl Signature { Secp256k1(sig) => &sig.0, } } + + pub fn get_signature_type(&self) -> String { + use Signature::*; + + match self { + Ed25519(_) => "Ed25519".to_string(), + #[cfg(not(target_arch = "wasm32"))] + Rsa(_) => "RSA".to_string(), + Secp256k1(_) => "Secp256k1".to_string(), + } + } + + pub fn get_raw_signature(&self) -> RawSignature { + RawSignature { bytes: self.to_vec().clone().to_vec(), sig_type: self.get_signature_type() } + } + + pub fn from_raw_signature(raw_signature: RawSignature) -> Result { + match raw_signature.sig_type.as_str() { + "Ed25519" => Ok(Signature::Ed25519(crate::ed25519::Signature(raw_signature.bytes))), + #[cfg(not(target_arch = "wasm32"))] + "RSA" => Ok(Signature::Rsa(crate::rsa::Signature(raw_signature.bytes))), + "Secp256k1" => Ok(Signature::Secp256k1(crate::secp256k1::Signature(raw_signature.bytes))), + _ => Err(DecodingError::RawSignatureUnsupportedType(raw_signature.sig_type)), + } + } } #[cfg(test)] diff --git a/service/Cargo.toml b/service/Cargo.toml index f80411b..01dfe1b 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -16,7 +16,7 @@ fluence-keypair = { version = "0.3.0", path = "../keypair" } marine-rs-sdk = { version = "0.6.11", features = ["logger"] } marine-sqlite-connector = "0.5.0" -libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2" } +libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2", features = ["secp256k1"]} log = "0.4.8" anyhow = "1.0.31" diff --git a/service/build.sh b/service/build.sh index a8c4e05..f1ef3f1 100755 --- a/service/build.sh +++ b/service/build.sh @@ -5,7 +5,7 @@ set -o errexit -o nounset -o pipefail cd "$(dirname "$0")" # build trust-graph.wasm -cargo update +#cargo update marine build --release # copy .wasm to artifacts diff --git a/service/src/dto.rs b/service/src/dto.rs index 4111767..427fbfd 100644 --- a/service/src/dto.rs +++ b/service/src/dto.rs @@ -1,9 +1,14 @@ use marine_rs_sdk::marine; use fluence_keypair::error::DecodingError; -use fluence_keypair::{PublicKey, Signature}; +use fluence_keypair::{Signature}; use std::convert::TryFrom; use std::time::Duration; use thiserror::Error as ThisError; +use libp2p_core::PeerId; +use fluence_keypair::public_key::peer_id_to_fluence_pk; +use std::str::FromStr; +use fluence_keypair::signature::RawSignature; +use crate::dto::DtoConversionError::PeerIdDecodeError; #[derive(ThisError, Debug)] pub enum DtoConversionError { @@ -19,6 +24,8 @@ pub enum DtoConversionError { #[source] DecodingError, ), + #[error("Cannot decode peer id from string: {0}")] + PeerIdDecodeError(String), } #[marine] @@ -48,14 +55,16 @@ impl TryFrom for trust_graph::Certificate { } #[marine] +#[derive(Default)] pub struct Trust { - /// For whom this certificate is issued, base58 + /// For whom this certificate is issued, base58 peer_id pub issued_for: String, /// Expiration date of a trust, in secs pub expires_at: u64, /// Signature of a previous trust in a chain. /// Signature is self-signed if it is a root trust, base58 pub signature: String, + pub sig_type: String, /// Creation time of a trust, in secs pub issued_at: u64, } @@ -64,9 +73,11 @@ impl TryFrom for trust_graph::Trust { type Error = DtoConversionError; fn try_from(t: Trust) -> Result { - let issued_for = PublicKey::from_base58(&t.issued_for)?; + let issued_for = peer_id_to_fluence_pk(PeerId::from_str(&t.issued_for) + .map_err(|e| PeerIdDecodeError(format!("{:?}", e)))?) + .map_err(|e| DtoConversionError::PeerIdDecodeError(e.to_string()))?; let signature = bs58::decode(&t.signature).into_vec()?; - let signature = Signature::decode(signature)?; + let signature = Signature::from_raw_signature(RawSignature { bytes: signature, sig_type: t.sig_type })?; let expires_at = Duration::from_secs(t.expires_at); let issued_at = Duration::from_secs(t.issued_at); return Ok(trust_graph::Trust { @@ -81,13 +92,15 @@ impl TryFrom for trust_graph::Trust { impl From for Trust { fn from(t: trust_graph::Trust) -> Self { let issued_for = bs58::encode(t.issued_for.encode()).into_string(); - let signature = bs58::encode(t.signature.encode()).into_string(); + let raw_signature = t.signature.get_raw_signature(); + let signature = bs58::encode(raw_signature.bytes).into_string(); let expires_at = t.expires_at.as_secs(); let issued_at = t.issued_at.as_secs(); return Trust { issued_for, expires_at, signature, + sig_type: raw_signature.sig_type, issued_at, }; } diff --git a/service/src/results.rs b/service/src/results.rs index b59512f..8c98f48 100644 --- a/service/src/results.rs +++ b/service/src/results.rs @@ -1,10 +1,10 @@ -use crate::dto::Certificate; +use crate::dto::{Certificate, Trust}; use crate::service_impl::ServiceError; use marine_rs_sdk::marine; #[marine] pub struct InsertResult { - pub ret_code: u32, + pub success: bool, pub error: String, } @@ -12,11 +12,11 @@ impl From> for InsertResult { fn from(result: Result<(), ServiceError>) -> Self { match result { Ok(()) => InsertResult { - ret_code: 0, + success: true, error: "".to_string(), }, Err(e) => InsertResult { - ret_code: 1, + success: false, error: format!("{}", e), }, } @@ -25,7 +25,7 @@ impl From> for InsertResult { #[marine] pub struct WeightResult { - pub ret_code: u32, + pub success: bool, pub weight: Vec, pub error: String, } @@ -34,12 +34,12 @@ impl From, ServiceError>> for WeightResult { fn from(result: Result, ServiceError>) -> Self { match result { Ok(wo) => WeightResult { - ret_code: 0, + success: true, weight: wo.map(|w| vec![w]).unwrap_or(vec![]), error: "".to_string(), }, Err(e) => WeightResult { - ret_code: 1, + success: false, weight: vec![], error: format!("{}", e), }, @@ -49,7 +49,7 @@ impl From, ServiceError>> for WeightResult { #[marine] pub struct AllCertsResult { - pub ret_code: u32, + pub success: bool, pub certificates: Vec, pub error: String, } @@ -58,12 +58,12 @@ impl From, ServiceError>> for AllCertsResult { fn from(result: Result, ServiceError>) -> Self { match result { Ok(certs) => AllCertsResult { - ret_code: 0, + success: true, certificates: certs, error: "".to_string(), }, Err(e) => AllCertsResult { - ret_code: 1, + success: false, certificates: vec![], error: format!("{}", e), }, @@ -73,7 +73,7 @@ impl From, ServiceError>> for AllCertsResult { #[marine] pub struct AddRootResult { - pub ret_code: u32, + pub success: bool, pub error: String, } @@ -81,13 +81,61 @@ impl From> for AddRootResult { fn from(result: Result<(), ServiceError>) -> Self { match result { Ok(()) => AddRootResult { - ret_code: 0, + success: true, error: "".to_string(), }, Err(e) => AddRootResult { - ret_code: 1, + success: false, error: format!("{}", e), }, } } } + +#[marine] +pub struct GetTrustMetadataResult { + pub success: bool, + pub error: String, + pub result: Vec, +} + +impl From, ServiceError>> for GetTrustMetadataResult { + fn from(result: Result, ServiceError>) -> Self { + match result { + Ok(res) => GetTrustMetadataResult { + success: true, + error: "".to_string(), + result: res + }, + Err(e) => GetTrustMetadataResult { + success: false, + error: format!("{}", e), + result: vec![] + }, + } + } +} + +#[marine] +pub struct IssueTrustResult { + pub success: bool, + pub error: String, + pub trust: Trust, +} + +impl From> for IssueTrustResult { + fn from(result: Result) -> Self { + match result { + Ok(trust) => IssueTrustResult { + success: true, + error: "".to_string(), + trust, + }, + Err(e) => IssueTrustResult { + success: false, + error: format!("{}", e), + trust: Trust::default(), + }, + } + } +} diff --git a/service/src/service_api.rs b/service/src/service_api.rs index 2d6946f..0a69e54 100644 --- a/service/src/service_api.rs +++ b/service/src/service_api.rs @@ -1,8 +1,6 @@ use crate::dto::Certificate; -use crate::results::{AddRootResult, AllCertsResult, InsertResult, WeightResult}; -use crate::service_impl::{ - add_root_impl, get_all_certs_impl, get_weight_impl, insert_cert_impl, insert_cert_impl_raw, -}; +use crate::results::{AddRootResult, AllCertsResult, InsertResult, WeightResult, GetTrustMetadataResult}; +use crate::service_impl::{add_root_impl, get_all_certs_impl, get_weight_impl, insert_cert_impl, insert_cert_impl_raw, get_trust_metadata_imp}; use marine_rs_sdk::{CallParameters, marine}; #[marine] @@ -39,8 +37,18 @@ fn add_root(peer_id: String, weight: u32) -> AddRootResult { add_root_impl(peer_id, weight).into() } else { return AddRootResult { - ret_code: 1, + success: true, error: "Root could add only a host of trust graph service".to_string(), }; } } + +#[marine] +fn get_trust_metadata(peer_id: String, expires_at: u64, issued_at: u64) -> GetTrustMetadataResult { + get_trust_metadata_imp(peer_id, expires_at, issued_at).into() +} + +// #[marine] +// fn issue_trust(peer_id: String, expires_at: u64, issued_at: u64, signed_metadata: Vec, sig_type: String) -> IssueTrustResult { +// issue_trust_impl(peer_id, expires_at, issued_at, signed_metadata).into() +// } \ No newline at end of file diff --git a/service/src/service_impl.rs b/service/src/service_impl.rs index c794b47..657d31e 100644 --- a/service/src/service_impl.rs +++ b/service/src/service_impl.rs @@ -1,4 +1,4 @@ -use crate::dto::{Certificate, DtoConversionError}; +use crate::dto::{Certificate, DtoConversionError, Trust}; use crate::storage_impl::get_data; use fluence_keypair::error::DecodingError; use fluence_keypair::PublicKey; @@ -98,3 +98,16 @@ pub fn add_root_impl(peer_id: String, weight: u32) -> Result<(), ServiceError> { tg.add_root_weight(public_key, weight)?; Ok(()) } + +pub fn get_trust_metadata_imp(peer_id: String, expires_at: u64, issued_at: u64) -> Result, ServiceError> { + let public_key = extract_public_key(peer_id)?; + Ok(trust_graph::Trust::metadata_bytes(&public_key, + Duration::from_secs(expires_at), + Duration::from_secs(issued_at))) +} + +// pub fn issue_trust_impl(peer_id: String, expires_at: u64, issued_at: u64, signed_metadata: Vec) -> Result { +// let public_key = extract_public_key(peer_id)?; +// trust_graph::Trust::new(public_key, Duration::from_secs(expires_at), Duration::from_secs(issued_at), +// ) +// } \ No newline at end of file diff --git a/service/trust-graph.aqua b/service/trust-graph.aqua index 4895508..98ff6fb 100644 --- a/service/trust-graph.aqua +++ b/service/trust-graph.aqua @@ -1,33 +1,40 @@ data AddRootResult: - ret_code: u32 + success: bool error: string data Trust: issued_for: string expires_at: u64 signature: string + sig_type: string issued_at: u64 data Certificate: chain: []Trust data AllCertsResult: - ret_code: u32 + success: bool certificates: []Certificate error: string +data GetTrustMetadataResult: + success: bool + error: string + result: []u8 + data InsertResult: - ret_code: u32 + success: bool error: string data WeightResult: - ret_code: u32 + success: bool weight: []u32 error: string service TrustGraph("trust-graph"): - add_root(pk: string, weight: u32) -> AddRootResult + add_root(peer_id: string, weight: u32) -> AddRootResult get_all_certs(issued_for: string) -> AllCertsResult - get_weight(public_key: string) -> WeightResult + get_trust_metadata(peer_id: string, expires_at: u64, issued_at: u64) -> GetTrustMetadataResult + get_weight(peer_id: string) -> WeightResult insert_cert(certificate: Certificate, current_time: u64) -> InsertResult insert_cert_raw(certificate: string, current_time: u64) -> InsertResult diff --git a/src/trust.rs b/src/trust.rs index 5138446..cbe91b7 100644 --- a/src/trust.rs +++ b/src/trust.rs @@ -24,6 +24,7 @@ use std::num::ParseIntError; use std::time::Duration; use thiserror::Error as ThisError; use serde::{Deserialize, Serialize}; +use sha2::Digest; pub const EXPIRATION_LEN: usize = 8; pub const ISSUED_LEN: usize = 8; @@ -139,7 +140,7 @@ impl Trust { KeyPair::verify(issued_by, msg, &trust.signature).map_err(SignatureError) } - fn metadata_bytes(pk: &PublicKey, expires_at: Duration, issued_at: Duration) -> Vec { + pub fn metadata_bytes(pk: &PublicKey, expires_at: Duration, issued_at: Duration) -> Vec { let pk_encoded = pk.encode(); let expires_at_encoded: [u8; EXPIRATION_LEN] = (expires_at.as_secs() as u64).to_le_bytes(); let issued_at_encoded: [u8; ISSUED_LEN] = (issued_at.as_secs() as u64).to_le_bytes(); @@ -149,7 +150,7 @@ impl Trust { metadata.extend_from_slice(&expires_at_encoded[0..EXPIRATION_LEN]); metadata.extend_from_slice(&issued_at_encoded[0..ISSUED_LEN]); - metadata + sha2::Sha256::digest(&metadata).to_vec() } /// Encode the trust into a byte array