WIP: peer_id gen, secret key, tg service with peer_id api

TODO:
- issue certificates (two step with signing outside) in wasm service
- more tests
This commit is contained in:
Alexey Proshutinskiy 2021-08-26 17:27:05 +03:00
parent c67579c0bd
commit 05db49c757
19 changed files with 258 additions and 48 deletions

39
.circleci/config.yml Normal file
View File

@ -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

4
Cargo.lock generated
View File

@ -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",
]

View File

@ -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]

View File

@ -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]

View File

@ -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 {

View File

@ -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.

View File

@ -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<Vec<u8>> {
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<libp2p_core::identity::Keypair> for KeyPair {

View File

@ -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<S>(value: &PeerId, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
value.to_base58().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<PeerId, D::Error>
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))
})
}
}

View File

View File

@ -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<libp2p_core::identity::PublicKey> for PublicKey {
@ -114,6 +119,19 @@ impl From<libp2p_core::identity::PublicKey> for PublicKey {
}
}
impl From<PublicKey> 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<PublicKey> {
Ok(peer_id.as_public_key().ok_or(eyre::eyre!("public key is not inlined in peer id: {}", peer_id))?.into())
}

View File

@ -30,6 +30,11 @@ pub enum Signature {
Secp256k1(secp256k1::Signature),
}
pub struct RawSignature {
pub bytes: Vec<u8>,
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<u8> = 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<Self, DecodingError> {
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)]

View File

@ -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"

View File

@ -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

View File

@ -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<Certificate> 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<Trust> for trust_graph::Trust {
type Error = DtoConversionError;
fn try_from(t: Trust) -> Result<Self, Self::Error> {
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<Trust> for trust_graph::Trust {
impl From<trust_graph::Trust> 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,
};
}

View File

@ -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<Result<(), ServiceError>> 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<Result<(), ServiceError>> for InsertResult {
#[marine]
pub struct WeightResult {
pub ret_code: u32,
pub success: bool,
pub weight: Vec<u32>,
pub error: String,
}
@ -34,12 +34,12 @@ impl From<Result<Option<u32>, ServiceError>> for WeightResult {
fn from(result: Result<Option<u32>, 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<Result<Option<u32>, ServiceError>> for WeightResult {
#[marine]
pub struct AllCertsResult {
pub ret_code: u32,
pub success: bool,
pub certificates: Vec<Certificate>,
pub error: String,
}
@ -58,12 +58,12 @@ impl From<Result<Vec<Certificate>, ServiceError>> for AllCertsResult {
fn from(result: Result<Vec<Certificate>, 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<Result<Vec<Certificate>, ServiceError>> for AllCertsResult {
#[marine]
pub struct AddRootResult {
pub ret_code: u32,
pub success: bool,
pub error: String,
}
@ -81,13 +81,61 @@ impl From<Result<(), ServiceError>> 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<u8>,
}
impl From<Result<Vec<u8>, ServiceError>> for GetTrustMetadataResult {
fn from(result: Result<Vec<u8>, 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<Result<Trust, ServiceError>> for IssueTrustResult {
fn from(result: Result<Trust, ServiceError>) -> Self {
match result {
Ok(trust) => IssueTrustResult {
success: true,
error: "".to_string(),
trust,
},
Err(e) => IssueTrustResult {
success: false,
error: format!("{}", e),
trust: Trust::default(),
},
}
}
}

View File

@ -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<u8>, sig_type: String) -> IssueTrustResult {
// issue_trust_impl(peer_id, expires_at, issued_at, signed_metadata).into()
// }

View File

@ -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<Vec<u8>, 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<u8>) -> Result<Trust, ServiceError> {
// let public_key = extract_public_key(peer_id)?;
// trust_graph::Trust::new(public_key, Duration::from_secs(expires_at), Duration::from_secs(issued_at),
// )
// }

View File

@ -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

View File

@ -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<u8> {
pub fn metadata_bytes(pk: &PublicKey, expires_at: Duration, issued_at: Duration) -> Vec<u8> {
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