Compare commits

...

9 Commits
v0.0.2 ... main

Author SHA1 Message Date
Alexey Proshutinskiy
2f6d124de1 add missing line 2021-09-27 16:36:04 +03:00
Alexey Proshutinskiy
a217274674 add aqua api for revocations 2021-09-27 16:35:34 +03:00
Alexey Proshutinskiy
201f65571c service: expose revoke api 2021-09-27 16:04:59 +03:00
Alexey Proshutinskiy
0978b2520d add_trust: check trust issued_at timestamp 2021-09-24 16:32:19 +03:00
Alexey Proshutinskiy
fc635c0ef5 return only valid trusts, couple renamings 2021-09-23 19:00:24 +03:00
Alexey Proshutinskiy
e50b247417 : 2021-09-22 02:59:50 +03:00
Alexey Proshutinskiy
c999fbc0ef add example README 2021-09-20 19:38:19 +03:00
Alexey Proshutinskiy
3e064e5570 add missing files 2021-09-20 19:35:10 +03:00
Alexey Proshutinskiy
83055549df add demo example with sign, issue and verify trust 2021-09-20 19:34:46 +03:00
15 changed files with 4578 additions and 89 deletions

63
aqua/trust-graph-api.aqua Normal file
View File

@ -0,0 +1,63 @@
import "trust-graph.aqua"
import "@fluencelabs/aqua-lib/builtin.aqua"
func get_trust_bytes(node: string, issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64) -> GetTrustBytesResult:
on node:
result <- TrustGraph.get_trust_bytes(issued_for_peer_id, expires_at_sec, issued_at_sec)
<- result
func issue_trust(node: string, issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64, trust_bytes: []u8) -> IssueTrustResult:
on node:
result <- TrustGraph.issue_trust(issued_for_peer_id, expires_at_sec, issued_at_sec, trust_bytes)
<- result
func verify_trust(node: string, trust: Trust, issuer_peer_id: string) -> VerifyTrustResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.verify_trust(trust, issuer_peer_id, timestamp_sec)
<- result
func add_trust(node: string, trust: Trust, issuer_peer_id: string) -> AddTrustResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.add_trust(trust, issuer_peer_id, timestamp_sec)
<- result
func add_root(node: string, peer_id: string, weight_factor: u32) -> AddRootResult:
on node:
result <- TrustGraph.add_root(peer_id, weight_factor)
<- result
func get_weight(node: string, peer_id: string) -> WeightResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_weight(peer_id, timestamp_sec)
<- result
func get_all_certs(node: string, issued_for: string) -> AllCertsResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_all_certs(issued_for, timestamp_sec)
<- result
func insert_cert(node: string, certificate: Certificate) -> InsertResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.insert_cert(certificate, timestamp_sec)
<- result
func get_revoke_bytes(node: string, revoked_peer_id: string, revoked_at: u64) -> GetRevokeBytesResult:
on node:
result <- TrustGraph.get_revoke_bytes(revoked_peer_id, revoked_at)
<- result
func issue_revocation(node: string, revoked_peer_id: string, revoked_by_peer_id: string, revoked_at_sec: u64, signature_bytes: []u8) -> IssueRevocationResult:
on node:
result <- TrustGraph.issue_revocation(revoked_peer_id, revoked_by_peer_id, revoked_at_sec, signature_bytes)
<- result
func revoke(node: string, revoke: Revoke) -> RevokeResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.revoke(revoke, timestamp_sec)
<- result

View File

@ -24,7 +24,12 @@ data AllCertsResult:
certificates: []Certificate
error: string
data GetTrustMetadataResult:
data GetRevokeBytesResult:
success: bool
error: string
result: []u8
data GetTrustBytesResult:
success: bool
error: string
result: []u8
@ -33,11 +38,27 @@ data InsertResult:
success: bool
error: string
data Revoke:
revoked_peer_id: string
revoked_at: u64
signature: string
sig_type: string
revoked_by: string
data IssueRevocationResult:
success: bool
error: string
revoke: Revoke
data IssueTrustResult:
success: bool
error: string
trust: Trust
data RevokeResult:
success: bool
error: string
data VerifyTrustResult:
success: bool
error: string
@ -49,12 +70,15 @@ data WeightResult:
error: string
service TrustGraph("trust-graph"):
add_root(peer_id: string, weight: u32) -> AddRootResult
add_root(peer_id: string, weight_factor: u32) -> AddRootResult
add_trust(trust: Trust, issuer_peer_id: string, timestamp_sec: u64) -> AddTrustResult
get_all_certs(issued_for: string, timestamp_sec: u64) -> AllCertsResult
get_trust_metadata(issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64) -> GetTrustMetadataResult
get_revoke_bytes(revoked_peer_id: string, revoked_at: u64) -> GetRevokeBytesResult
get_trust_bytes(issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64) -> GetTrustBytesResult
get_weight(peer_id: string, timestamp_sec: u64) -> WeightResult
insert_cert(certificate: Certificate, timestamp_sec: u64) -> InsertResult
insert_cert_raw(certificate: string, timestamp_sec: u64) -> InsertResult
issue_trust(issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64, signed_metadata: []u8) -> IssueTrustResult
issue_revocation(revoked_peer_id: string, revoked_by_peer_id: string, revoked_at_sec: u64, signature_bytes: []u8) -> IssueRevocationResult
issue_trust(issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64, trust_bytes: []u8) -> IssueTrustResult
revoke(revoke: Revoke, timestamp_sec: u64) -> RevokeResult
verify_trust(trust: Trust, issuer_peer_id: string, timestamp_sec: u64) -> VerifyTrustResult

5
example/README.md Normal file
View File

@ -0,0 +1,5 @@
# Run example locally
1. Go to `local-network`
2. Run `docker compose up -d` to start Fluence node
3. Go back to `../example`
4. Run `npm run start`

View File

@ -1,3 +1,3 @@
import TrustGraph from "../../aqua/trust-graph.aqua"
import get_trust_bytes, issue_trust, verify_trust, add_trust, add_root, get_weight, get_all_certs, insert_cert, get_revoke_bytes, issue_revocation, revoke from "../../aqua/trust-graph-api.aqua"
export TrustGraph
export get_trust_bytes, issue_trust, verify_trust, add_trust, add_root, get_weight, get_all_certs, insert_cert, get_revoke_bytes, issue_revocation, revoke

View File

@ -13,3 +13,66 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { get_trust_bytes, issue_trust, verify_trust, add_trust, add_root, get_weight, } from "./generated/export";
import { Fluence, KeyPair } from "@fluencelabs/fluence";
import { Node } from "@fluencelabs/fluence-network-environment";
const bs58 = require('bs58');
let local: Node[] = [
{
peerId: "12D3KooWHBG9oaVx4i3vi6c1rSBUm7MLBmyGmmbHoZ23pmjDCnvK",
multiaddr:
"/ip4/127.0.0.1/tcp/9990/ws/p2p/12D3KooWHBG9oaVx4i3vi6c1rSBUm7MLBmyGmmbHoZ23pmjDCnvK",
},
{
peerId: "12D3KooWRABanQHUn28dxavN9ZS1zZghqoZVAYtFpoN7FdtoGTFv",
multiaddr:
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWRABanQHUn28dxavN9ZS1zZghqoZVAYtFpoN7FdtoGTFv",
},
];
async function main(environment: Node[]) {
let builtins_keypair = await KeyPair.fromBytes(bs58.decode("5CGiJio6m76GxJ2wLj46PzSu6V7SRa5agv6meR3SJBKtvTgethRCmgBJKXWDSpSEBpgNUPd7Re5cZjF8mWW4kBfs"));
await Fluence.start({ connectTo: environment[0], KeyPair: builtins_keypair});
console.log(
"📗 created a fluence peer %s with relay %s",
Fluence.getStatus().peerId,
Fluence.getStatus().relayPeerId
);
const issuer_kp = await KeyPair.fromBytes(bs58.decode("29Apzfedhw2Jxh94Jj4rNSmavQ1TkNe8ALYRA7bMegobwp423aLrURxLk32WtXgXHDqoSz7GAT9fQfoMhVd1e5Ww"));
console.log("Issuer peer id: %", issuer_kp.Libp2pPeerId.toB58String());
let add_root_result = await add_root(local[0].peerId, local[0].peerId, 2);
console.log("Add root weight result: %s", add_root_result);
let trust_metadata = await get_trust_bytes(local[0].peerId, local[0].peerId, 99999999999, 0);
const signed_metadata = await issuer_kp.Libp2pPeerId.privKey.sign(Uint8Array.from(trust_metadata.result));
let root_trust = await issue_trust(local[0].peerId, local[0].peerId, 99999999999, 0, Array.from(signed_metadata));
console.log("Root trust %s", root_trust.trust);
let result = await verify_trust(local[0].peerId, root_trust.trust, local[0].peerId);
console.log("Verify root trust result: %s", result);
let result_add = await add_trust(local[0].peerId, root_trust.trust, local[0].peerId);
console.log("Add root trust result: %s", result_add);
let root_weight_result = await get_weight(local[0].peerId, local[0].peerId);
console.log("Root weight: %s", root_weight_result);
// TODO: insert trust to local[1].peerId, get this certs, add local[1].peerId as root in local[1].peerId and insert_certificate
return;
}
let environment: Node[];
environment = local;
console.log("📘 Will connect to local nodes");
main(environment)
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

4040
example/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

26
example/package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "trust-graph-aqua-example",
"version": "1.0.0",
"description": "A simple example of how to use trust-graph in TS",
"main": "index.js",
"scripts": {
"compile-aqua": "aqua -i aqua -o generated",
"prebuild": "npm run compile-aqua",
"build": "tsc",
"start": "node dist/index.js",
"prestart": "npm run build"
},
"author": "Fluence Labs",
"license": "MIT",
"dependencies": {
"@fluencelabs/aqua": "0.3.1-228",
"@fluencelabs/aqua-lib": "0.1.14",
"@fluencelabs/fluence": "^0.12.1",
"@fluencelabs/fluence-network-environment": "^1.0.10",
"@fluencelabs/trust-graph": "file:../aqua",
"bs58": "^4.0.1"
},
"devDependencies": {
"typescript": "^4.4.3"
}
}

View File

@ -1,13 +1,13 @@
# management secret key is NAB5rGwT4qOEB+6nLQawkTfCOV2eiFSjgQK8bfEdZXY=
services:
fluence-0: # /ip4/127.0.0.1/tcp/9990/ws/p2p/12D3KooWHBG9oaVx4i3vi6c1rSBUm7MLBmyGmmbHoZ23pmjDCnvK
command: -f ed25519 -k 29Apzfedhw2Jxh94Jj4rNSmavQ1TkNe8ALYRA7bMegobwp423aLrURxLk32WtXgXHDqoSz7GAT9fQfoMhVd1e5Ww -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7770 -w 9990 --bootstraps /dns4/fluence-1/tcp/7771 /dns4/fluence-2/tcp/7772
command: -f ed25519 -k 29Apzfedhw2Jxh94Jj4rNSmavQ1TkNe8ALYRA7bMegobwp423aLrURxLk32WtXgXHDqoSz7GAT9fQfoMhVd1e5Ww -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7770 -w 9990 # --bootstraps /dns4/fluence-1/tcp/7771 /dns4/fluence-2/tcp/7772
container_name: fluence-0
environment:
RUST_BACKTRACE: full
RUST_LOG: info,network=trace,aquamarine=info,aquamarine::actor=info,tokio_threadpool=info,tokio_reactor=info,mio=info,tokio_io=info,soketto=info,yamux=info,multistream_select=info,libp2p_secio=info,libp2p_websocket::framed=info,libp2p_ping=info,libp2p_core::upgrade::apply=info,libp2p_kad::kbucket=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,particle_server::behaviour::identify=info,libp2p_mplex=info,libp2p_identify=info,walrus=info,particle_protocol::libp2p_protocol::upgrade=info,kademlia::behaviour=info
WASM_LOG: info
image: fluencelabs/node:latest
image: fluencelabs/node:tg_test
ports:
- 7770:7770 # tcp
- 9990:9990 # ws

View File

@ -1,14 +1,14 @@
use marine_rs_sdk::marine;
use crate::dto::DtoConversionError::PeerIdDecodeError;
use fluence_keypair::error::DecodingError;
use fluence_keypair::{Signature};
use fluence_keypair::public_key::peer_id_to_fluence_pk;
use fluence_keypair::signature::RawSignature;
use fluence_keypair::Signature;
use libp2p_core::PeerId;
use marine_rs_sdk::marine;
use std::convert::TryFrom;
use std::str::FromStr;
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 {
@ -73,11 +73,15 @@ impl TryFrom<Trust> for trust_graph::Trust {
type Error = DtoConversionError;
fn try_from(t: Trust) -> Result<Self, Self::Error> {
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 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::from_raw_signature(RawSignature { bytes: signature, sig_type: t.sig_type })?;
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 {
@ -105,3 +109,63 @@ impl From<trust_graph::Trust> for Trust {
};
}
}
#[marine]
#[derive(Default)]
pub struct Revoke {
/// who is revoked
pub revoked_peer_id: String,
/// date when revocation was created
pub revoked_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,
/// the issuer of this revocation, base58 peer id
pub revoked_by: String,
}
impl TryFrom<Revoke> for trust_graph::Revoke {
type Error = DtoConversionError;
fn try_from(r: Revoke) -> Result<Self, Self::Error> {
let revoked_pk = peer_id_to_fluence_pk(
PeerId::from_str(&r.revoked_peer_id)
.map_err(|e| PeerIdDecodeError(format!("{:?}", e)))?,
)
.map_err(|e| DtoConversionError::PeerIdDecodeError(e.to_string()))?;
let revoked_by_pk = peer_id_to_fluence_pk(
PeerId::from_str(&r.revoked_by).map_err(|e| PeerIdDecodeError(format!("{:?}", e)))?,
)
.map_err(|e| DtoConversionError::PeerIdDecodeError(e.to_string()))?;
let signature = bs58::decode(&r.signature).into_vec()?;
let signature = Signature::from_raw_signature(RawSignature {
bytes: signature,
sig_type: r.sig_type,
})?;
let revoked_at = Duration::from_secs(r.revoked_at);
return Ok(trust_graph::Revoke {
pk: revoked_pk,
revoked_at,
revoked_by: revoked_by_pk,
signature,
});
}
}
impl From<trust_graph::Revoke> for Revoke {
fn from(r: trust_graph::Revoke) -> Self {
let revoked_by = r.revoked_by.to_peer_id().to_base58();
let revoked_peer_id = r.pk.to_peer_id().to_base58();
let raw_signature = r.signature.get_raw_signature();
let signature = bs58::encode(raw_signature.bytes).into_string();
let revoked_at = r.revoked_at.as_secs();
return Revoke {
revoked_peer_id,
revoked_at,
signature,
sig_type: raw_signature.sig_type,
revoked_by,
};
}
}

View File

@ -1,4 +1,4 @@
use crate::dto::{Certificate, Trust};
use crate::dto::{Certificate, Revoke, Trust};
use crate::service_impl::ServiceError;
use marine_rs_sdk::marine;
@ -96,21 +96,21 @@ impl From<Result<(), ServiceError>> for AddRootResult {
}
#[marine]
pub struct GetTrustMetadataResult {
pub struct GetTrustBytesResult {
pub success: bool,
pub error: String,
pub result: Vec<u8>,
}
impl From<Result<Vec<u8>, ServiceError>> for GetTrustMetadataResult {
impl From<Result<Vec<u8>, ServiceError>> for GetTrustBytesResult {
fn from(result: Result<Vec<u8>, ServiceError>) -> Self {
match result {
Ok(res) => GetTrustMetadataResult {
Ok(res) => GetTrustBytesResult {
success: true,
error: "".to_string(),
result: res,
},
Err(e) => GetTrustMetadataResult {
Err(e) => GetTrustBytesResult {
success: false,
error: format!("{}", e),
result: vec![],
@ -187,3 +187,72 @@ impl From<Result<u32, ServiceError>> for AddTrustResult {
}
}
}
#[marine]
pub struct GetRevokeBytesResult {
pub success: bool,
pub error: String,
pub result: Vec<u8>,
}
impl From<Result<Vec<u8>, ServiceError>> for GetRevokeBytesResult {
fn from(result: Result<Vec<u8>, ServiceError>) -> Self {
match result {
Ok(res) => GetRevokeBytesResult {
success: true,
error: "".to_string(),
result: res,
},
Err(e) => GetRevokeBytesResult {
success: false,
error: format!("{}", e),
result: vec![],
},
}
}
}
#[marine]
pub struct IssueRevocationResult {
pub success: bool,
pub error: String,
pub revoke: Revoke,
}
impl From<Result<Revoke, ServiceError>> for IssueRevocationResult {
fn from(result: Result<Revoke, ServiceError>) -> Self {
match result {
Ok(revoke) => IssueRevocationResult {
success: true,
error: "".to_string(),
revoke,
},
Err(e) => IssueRevocationResult {
success: false,
error: format!("{}", e),
revoke: Revoke::default(),
},
}
}
}
#[marine]
pub struct RevokeResult {
pub success: bool,
pub error: String,
}
impl From<Result<(), ServiceError>> for RevokeResult {
fn from(result: Result<(), ServiceError>) -> Self {
match result {
Ok(()) => RevokeResult {
success: true,
error: "".to_string(),
},
Err(e) => RevokeResult {
success: false,
error: format!("{}", e),
},
}
}
}

View File

@ -1,11 +1,13 @@
use crate::dto::{Certificate, Trust};
use crate::dto::{Certificate, Revoke, Trust};
use crate::results::{
AddRootResult, AddTrustResult, AllCertsResult, GetTrustMetadataResult, InsertResult,
IssueTrustResult, VerifyTrustResult, WeightResult,
AddRootResult, AddTrustResult, AllCertsResult, GetRevokeBytesResult, GetTrustBytesResult,
InsertResult, IssueRevocationResult, IssueTrustResult, RevokeResult, VerifyTrustResult,
WeightResult,
};
use crate::service_impl::{
add_root_impl, add_trust_impl, get_all_certs_impl, get_trust_metadata_imp, get_weight_impl,
insert_cert_impl, insert_cert_impl_raw, issue_trust_impl, verify_trust_impl,
add_root_impl, add_trust_impl, get_all_certs_impl, get_revoke_bytes_impl, get_trust_bytes_imp,
get_weight_impl, insert_cert_impl, insert_cert_impl_raw, issue_revocation_impl,
issue_trust_impl, revoke_impl, verify_trust_impl,
};
use marine_rs_sdk::{marine, CallParameters};
@ -23,7 +25,6 @@ fn insert_cert(certificate: Certificate, timestamp_sec: u64) -> InsertResult {
insert_cert_impl(certificate, timestamp_sec).into()
}
// TODO: return only valid, delete expired, return max weight
#[marine]
fn get_weight(peer_id: String, timestamp_sec: u64) -> WeightResult {
get_weight_impl(peer_id.clone(), timestamp_sec)
@ -31,34 +32,34 @@ fn get_weight(peer_id: String, timestamp_sec: u64) -> WeightResult {
.into()
}
// TODO: return only valid, delete expired
// TODO: delete expired
#[marine]
fn get_all_certs(issued_for: String, timestamp_sec: u64) -> AllCertsResult {
get_all_certs_impl(issued_for, timestamp_sec).into()
}
#[marine]
/// could add only a host of a trust graph service
fn add_root(peer_id: String, weight: u32) -> AddRootResult {
/// could add only a owner of a trust graph service
fn add_root(peer_id: String, weight_factor: u32) -> AddRootResult {
let call_parameters: CallParameters = marine_rs_sdk::get_call_parameters();
let init_peer_id = call_parameters.init_peer_id.clone();
if call_parameters.host_id == init_peer_id {
add_root_impl(peer_id, weight).into()
if call_parameters.service_creator_peer_id == init_peer_id {
add_root_impl(peer_id, weight_factor).into()
} else {
return AddRootResult {
success: false,
error: "Root could add only a host of trust graph service".to_string(),
error: "Root could add only by trust graph service owner".to_string(),
};
}
}
#[marine]
fn get_trust_metadata(
fn get_trust_bytes(
issued_for_peer_id: String,
expires_at_sec: u64,
issued_at_sec: u64,
) -> GetTrustMetadataResult {
get_trust_metadata_imp(issued_for_peer_id, expires_at_sec, issued_at_sec).into()
) -> GetTrustBytesResult {
get_trust_bytes_imp(issued_for_peer_id, expires_at_sec, issued_at_sec).into()
}
#[marine]
@ -66,13 +67,13 @@ fn issue_trust(
issued_for_peer_id: String,
expires_at_sec: u64,
issued_at_sec: u64,
signed_metadata: Vec<u8>,
trust_bytes: Vec<u8>,
) -> IssueTrustResult {
issue_trust_impl(
issued_for_peer_id,
expires_at_sec,
issued_at_sec,
signed_metadata,
trust_bytes,
)
.into()
}
@ -82,8 +83,33 @@ fn verify_trust(trust: Trust, issuer_peer_id: String, timestamp_sec: u64) -> Ver
verify_trust_impl(trust, issuer_peer_id, timestamp_sec).into()
}
// TODO: check issued_at earlier than timestamp_sec
#[marine]
fn add_trust(trust: Trust, issuer_peer_id: String, timestamp_sec: u64) -> AddTrustResult {
add_trust_impl(trust, issuer_peer_id, timestamp_sec).into()
}
#[marine]
fn get_revoke_bytes(revoked_peer_id: String, revoked_at: u64) -> GetRevokeBytesResult {
get_revoke_bytes_impl(revoked_peer_id, revoked_at).into()
}
#[marine]
fn issue_revocation(
revoked_peer_id: String,
revoked_by_peer_id: String,
revoked_at_sec: u64,
signature_bytes: Vec<u8>,
) -> IssueRevocationResult {
issue_revocation_impl(
revoked_peer_id,
revoked_by_peer_id,
revoked_at_sec,
signature_bytes,
)
.into()
}
#[marine]
fn revoke(revoke: Revoke, timestamp_sec: u64) -> RevokeResult {
revoke_impl(revoke, timestamp_sec).into()
}

View File

@ -1,4 +1,4 @@
use crate::dto::{Certificate, DtoConversionError, Trust};
use crate::dto::{Certificate, DtoConversionError, Revoke, Trust};
use crate::service_impl::ServiceError::InvalidTimestampTetraplet;
use crate::storage_impl::get_data;
use fluence_keypair::error::DecodingError;
@ -7,6 +7,7 @@ use fluence_keypair::{PublicKey, Signature};
use libp2p_core::PeerId;
use marine_rs_sdk::CallParameters;
use std::convert::{Into, TryInto};
use std::iter::Rev;
use std::str::FromStr;
use std::time::Duration;
use thiserror::Error as ThisError;
@ -70,6 +71,8 @@ pub enum ServiceError {
),
#[error("you should use host peer.timestamp_sec to pass timestamp")]
InvalidTimestampTetraplet,
#[error("Trust can't be issued later than the current timestamp")]
InvalidTrustTimestamp,
}
fn parse_peer_id(peer_id: String) -> Result<PeerId, ServiceError> {
@ -82,11 +85,11 @@ fn extract_public_key(peer_id: String) -> Result<PublicKey, ServiceError> {
.map_err(|e| ServiceError::PublicKeyExtractionError(e.to_string()))
}
pub fn get_weight_impl(peer_id: String, _timestamp_sec: u64) -> Result<u32, ServiceError> {
pub fn get_weight_impl(peer_id: String, timestamp_sec: u64) -> Result<u32, ServiceError> {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 1)?;
let tg = get_data().lock();
let public_key = extract_public_key(peer_id)?;
let weight = tg.weight(public_key)?;
let weight = tg.weight(public_key, Duration::from_secs(timestamp_sec))?;
Ok(weight)
}
@ -106,13 +109,13 @@ pub fn insert_cert_impl_raw(certificate: String, timestamp_sec: u64) -> Result<(
pub fn get_all_certs_impl(
issued_for: String,
_timestamp_sec: u64,
timestamp_sec: u64,
) -> Result<Vec<Certificate>, ServiceError> {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 1)?;
let tg = get_data().lock();
let public_key = extract_public_key(issued_for)?;
let certs = tg.get_all_certs(public_key)?;
let certs = tg.get_all_certs(public_key, Duration::from_secs(timestamp_sec))?;
Ok(certs.into_iter().map(|c| c.into()).collect())
}
@ -130,7 +133,7 @@ pub fn add_root_impl(peer_id: String, weight: u32) -> Result<(), ServiceError> {
Ok(())
}
pub fn get_trust_metadata_imp(
pub fn get_trust_bytes_imp(
peer_id: String,
expires_at_sec: u64,
issued_at_sec: u64,
@ -147,12 +150,12 @@ pub fn issue_trust_impl(
peer_id: String,
expires_at_sec: u64,
issued_at_sec: u64,
signed_metadata: Vec<u8>,
trust_bytes: Vec<u8>,
) -> Result<Trust, ServiceError> {
let public_key = extract_public_key(peer_id)?;
let expires_at_sec = Duration::from_secs(expires_at_sec);
let issued_at_sec = Duration::from_secs(issued_at_sec);
let signature = Signature::from_bytes_with_public_key(&public_key, signed_metadata);
let signature = Signature::from_bytes_with_public_key(&public_key, trust_bytes);
Ok(Trust::from(trust_graph::Trust::new(
public_key,
expires_at_sec,
@ -184,6 +187,11 @@ pub fn add_trust_impl(
) -> Result<u32, ServiceError> {
let public_key = extract_public_key(issuer_peer_id)?;
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 2)?;
if trust.issued_at > timestamp_sec {
return Err(ServiceError::InvalidTrustTimestamp);
}
let mut tg = get_data().lock();
tg.add_trust(
&trust.try_into()?,
@ -192,3 +200,41 @@ pub fn add_trust_impl(
)
.map_err(ServiceError::TGError)
}
pub fn get_revoke_bytes_impl(
revoked_peer_id: String,
revoked_at: u64,
) -> Result<Vec<u8>, ServiceError> {
let public_key = extract_public_key(revoked_peer_id)?;
Ok(trust_graph::Revoke::signature_bytes(
&public_key,
Duration::from_secs(revoked_at),
))
}
pub fn issue_revocation_impl(
revoked_peer_id: String,
revoked_by_peer_id: String,
revoked_at_sec: u64,
signature_bytes: Vec<u8>,
) -> Result<Revoke, ServiceError> {
let revoked_pk = extract_public_key(revoked_peer_id)?;
let revoked_by_pk = extract_public_key(revoked_by_peer_id)?;
let revoked_at = Duration::from_secs(revoked_at_sec);
let signature = Signature::from_bytes_with_public_key(&revoked_by_pk, signature_bytes);
Ok(trust_graph::Revoke::new(revoked_pk, revoked_by_pk, revoked_at, signature).into())
}
pub fn revoke_impl(revoke: Revoke, timestamp_sec: u64) -> Result<(), ServiceError> {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 1)?;
// TODO: use error for revoke, not trust
if revoke.revoked_at > timestamp_sec {
return Err(ServiceError::InvalidTrustTimestamp);
}
let mut tg = get_data().lock();
tg.revoke(revoke.try_into()?).map_err(ServiceError::TGError)
}

View File

@ -44,11 +44,8 @@ mod tests {
macro_rules! issue_trust {
($trust_graph:expr, $issuer_kp:expr, $issued_peer_id: expr, $expires_at:expr, $issued_at: expr) => {{
let trust_metadata_result = $trust_graph.get_trust_metadata(
$issued_peer_id.to_base58(),
$expires_at,
$issued_at,
);
let trust_metadata_result =
$trust_graph.get_trust_bytes($issued_peer_id.to_base58(), $expires_at, $issued_at);
assert_result!(trust_metadata_result);
let metadata = trust_metadata_result.result;

View File

@ -19,6 +19,7 @@ use fluence_keypair::key_pair::KeyPair;
use fluence_keypair::public_key::PublicKey;
use fluence_keypair::signature::Signature;
use serde::{Deserialize, Serialize};
use sha2::Digest;
use std::time::Duration;
use thiserror::Error as ThisError;
@ -28,7 +29,7 @@ pub enum RevokeError {
IncorrectSignature(
#[from]
#[source]
fluence_keypair::error::SigningError
fluence_keypair::error::SigningError,
),
}
@ -43,12 +44,12 @@ pub struct Revoke {
/// the issuer of this revocation
pub revoked_by: PublicKey,
/// proof of this revocation
signature: Signature,
pub signature: Signature,
}
impl Revoke {
#[allow(dead_code)]
fn new(
pub fn new(
pk: PublicKey,
revoked_by: PublicKey,
revoked_at: Duration,
@ -71,14 +72,14 @@ impl Revoke {
Revoke::new(to_revoke, revoker.public(), revoked_at, signature)
}
fn signature_bytes(pk: &PublicKey, revoked_at: Duration) -> Vec<u8> {
let mut msg = Vec::new();
pub fn signature_bytes(pk: &PublicKey, revoked_at: Duration) -> Vec<u8> {
let mut metadata = Vec::new();
let pk_bytes = &pk.encode();
msg.push(pk_bytes.len() as u8);
msg.extend(pk_bytes);
msg.extend_from_slice(&(revoked_at.as_secs() as u64).to_le_bytes());
metadata.push(pk_bytes.len() as u8);
metadata.extend(pk_bytes);
metadata.extend_from_slice(&(revoked_at.as_secs() as u64).to_le_bytes());
msg
sha2::Sha256::digest(&metadata).to_vec()
}
/// Verifies that revocation is cryptographically correct.
@ -87,7 +88,8 @@ impl Revoke {
revoke
.revoked_by
.verify(msg.as_slice(), &revoke.signature).map_err(IncorrectSignature)
.verify(msg.as_slice(), &revoke.signature)
.map_err(IncorrectSignature)
}
}

View File

@ -131,7 +131,7 @@ where
{
Trust::verify(trust.borrow(), issued_by.borrow(), cur_time)?;
let issued_by_weight = self.weight(issued_by.borrow().clone().borrow())?;
let issued_by_weight = self.weight(issued_by.borrow().clone().borrow(), cur_time)?;
if issued_by_weight == 0u32 {
return Ok(0u32);
@ -188,7 +188,7 @@ where
}
/// Get the maximum weight of trust for one public key.
pub fn weight<P>(&self, pk: P) -> Result<u32, TrustGraphError>
pub fn weight<P>(&self, pk: P, cur_time: Duration) -> Result<u32, TrustGraphError>
where
P: Borrow<PublicKey>,
{
@ -197,7 +197,7 @@ where
}
// get all possible certificates from the given public key to all roots in the graph
let certs = self.get_all_certs(pk)?;
let certs = self.get_all_certs(pk, cur_time)?;
self.certificates_weight_factor(certs)
.map(|wf| wf.map(get_weight_from_factor).unwrap_or(0u32))
}
@ -251,11 +251,16 @@ where
&self,
node: &TrustNode,
roots: HashSet<&PK>,
cur_time: Duration,
) -> Result<Vec<Vec<Auth>>, TrustGraphError> {
// queue to collect all chains in the trust graph (each chain is a path in the trust graph)
let mut chains_queue: VecDeque<Vec<Auth>> = VecDeque::new();
let node_auths: Vec<Auth> = node.authorizations().cloned().collect();
let node_auths: Vec<Auth> = node
.authorizations()
.cloned()
.filter(|auth| auth.trust.expires_at > cur_time)
.collect();
// put all auth in the queue as the first possible paths through the graph
for auth in node_auths {
chains_queue.push_back(vec![auth]);
@ -279,6 +284,7 @@ where
.ok_or(CertificateCheckError(Unexpected))?
.authorizations()
.cloned()
.filter(|auth| auth.trust.expires_at > cur_time)
.collect();
for auth in auths {
@ -311,7 +317,11 @@ where
/// Get all possible certificates where `issued_for` will be the last element of the chain
/// and one of the destinations is the root of this chain.
pub fn get_all_certs<P>(&self, issued_for: P) -> Result<Vec<Certificate>, TrustGraphError>
pub fn get_all_certs<P>(
&self,
issued_for: P,
cur_time: Duration,
) -> Result<Vec<Certificate>, TrustGraphError>
where
P: Borrow<PublicKey>,
{
@ -323,7 +333,7 @@ where
match issued_for_node {
Some(node) => Ok(self
.bf_search_paths(&node, roots)?
.bf_search_paths(&node, roots, cur_time)?
.iter()
.map(|auths| {
// TODO: can avoid cloning here by returning &Certificate
@ -528,13 +538,13 @@ mod tests {
graph.add(cert1, current_time()).unwrap();
let root_weight = get_weight_from_factor(1);
let w1 = graph.weight(key_pairs[0].public()).unwrap();
let w1 = graph.weight(key_pairs[0].public(), current_time()).unwrap();
assert_eq!(w1, root_weight * 2u32.pow(0));
let w2 = graph.weight(key_pairs[1].public()).unwrap();
let w2 = graph.weight(key_pairs[1].public(), current_time()).unwrap();
assert_eq!(w2, root_weight / 2u32.pow(1));
let w3 = graph.weight(key_pairs[9].public()).unwrap();
let w3 = graph.weight(key_pairs[9].public(), current_time()).unwrap();
assert_eq!(w3, root_weight / 2u32.pow(9));
let node = graph.get(key_pairs[9].public()).unwrap().unwrap();
@ -590,12 +600,12 @@ mod tests {
);
graph.revoke(revoke2).unwrap();
let w1 = graph.weight(key_pair1.public()).unwrap();
let w1 = graph.weight(key_pair1.public(), current_time()).unwrap();
// all upper trusts are revoked for this public key
let w2 = graph.weight(key_pair2.public()).unwrap();
let w3 = graph.weight(key_pair3.public()).unwrap();
let w_last1 = graph.weight(last_pk1).unwrap();
let w_last2 = graph.weight(last_pk2).unwrap();
let w2 = graph.weight(key_pair2.public(), current_time()).unwrap();
let w3 = graph.weight(key_pair3.public(), current_time()).unwrap();
let w_last1 = graph.weight(last_pk1, current_time()).unwrap();
let w_last2 = graph.weight(last_pk2, current_time()).unwrap();
assert_eq!(w1, get_weight_from_factor(4));
assert_eq!(w2, 0); // revoked
@ -618,7 +628,7 @@ mod tests {
graph.add(cert.clone(), current_time()).unwrap();
let certs = graph
.get_all_certs(key_pairs.last().unwrap().public())
.get_all_certs(key_pairs.last().unwrap().public(), current_time())
.unwrap();
assert_eq!(certs.len(), 1);
@ -645,7 +655,7 @@ mod tests {
graph.add(cert.clone(), current_time()).unwrap();
let t = cert.chain[5].clone();
let certs = graph.get_all_certs(t.issued_for).unwrap();
let certs = graph.get_all_certs(t.issued_for, current_time()).unwrap();
assert_eq!(certs.len(), 1);
}
@ -696,17 +706,23 @@ mod tests {
graph.add(cert2, current_time()).unwrap();
graph.add(cert3, current_time()).unwrap();
let certs1 = graph.get_all_certs(key_pair1.public()).unwrap();
let certs1 = graph
.get_all_certs(key_pair1.public(), current_time())
.unwrap();
let lenghts1: Vec<usize> = certs1.iter().map(|c| c.chain.len()).collect();
let check_lenghts1: Vec<usize> = vec![3, 4, 4, 5, 5];
assert_eq!(lenghts1, check_lenghts1);
let certs2 = graph.get_all_certs(key_pair2.public()).unwrap();
let certs2 = graph
.get_all_certs(key_pair2.public(), current_time())
.unwrap();
let lenghts2: Vec<usize> = certs2.iter().map(|c| c.chain.len()).collect();
let check_lenghts2: Vec<usize> = vec![4, 4, 4, 5, 5];
assert_eq!(lenghts2, check_lenghts2);
let certs3 = graph.get_all_certs(key_pair3.public()).unwrap();
let certs3 = graph
.get_all_certs(key_pair3.public(), current_time())
.unwrap();
let lenghts3: Vec<usize> = certs3.iter().map(|c| c.chain.len()).collect();
let check_lenghts3: Vec<usize> = vec![3, 3, 5];
assert_eq!(lenghts3, check_lenghts3);
@ -737,11 +753,16 @@ mod tests {
let weight = graph
.add_trust(trust.clone(), issued_by.public(), cur_time)
.unwrap();
assert_eq!(weight, graph.weight(issued_by.public()).unwrap() / 2);
assert_eq!(
weight,
graph.weight(issued_by.public(), current_time()).unwrap() / 2
);
cert.chain.push(trust);
let certs = graph.get_all_certs(trust_kp.public()).unwrap();
let certs = graph
.get_all_certs(trust_kp.public(), current_time())
.unwrap();
assert_eq!(certs.len(), 1);
assert_eq!(certs[0], cert);
}
@ -771,13 +792,18 @@ mod tests {
let weight = graph
.add_trust(trust.clone(), issued_by.public(), cur_time)
.unwrap();
assert_eq!(weight, graph.weight(issued_by.public()).unwrap() / 2);
assert_eq!(
weight,
graph.weight(issued_by.public(), current_time()).unwrap() / 2
);
let target_cert = Certificate {
chain: vec![cert.chain[0].clone(), trust],
};
let certs = graph.get_all_certs(trust_kp.public()).unwrap();
let certs = graph
.get_all_certs(trust_kp.public(), current_time())
.unwrap();
assert_eq!(certs.len(), 1);
assert_eq!(certs[0], target_cert);
}
@ -804,6 +830,44 @@ mod tests {
);
graph.revoke(revoke.clone()).unwrap();
assert_eq!(0, graph.weight(revoked.public()).unwrap());
assert_eq!(0, graph.weight(revoked.public(), current_time()).unwrap());
}
#[test]
fn test_expired_trust() {
let (key_pairs, mut cert) = generate_cert_with_len(5, HashMap::new()).unwrap();
let cur_time = current_time();
let st = InMemoryStorage::new();
let mut graph = TrustGraph::new(st);
let root1_pk = key_pairs[0].public();
graph
.add_root_weight_factor(root1_pk.clone().into(), 2)
.unwrap();
graph.add(cert.clone(), cur_time).unwrap();
let issued_by = key_pairs.last().unwrap();
let trust_kp = KeyPair::generate_ed25519();
let expired_time = cur_time.checked_add(one_minute()).unwrap();
let trust = Trust::create(issued_by, trust_kp.public(), expired_time, cur_time);
let weight = graph
.add_trust(trust.clone(), issued_by.public(), cur_time)
.unwrap();
assert_eq!(
weight,
graph.weight(issued_by.public(), cur_time).unwrap() / 2
);
cert.chain.push(trust);
let certs = graph.get_all_certs(trust_kp.public(), cur_time).unwrap();
assert_eq!(certs.len(), 1);
assert_eq!(certs[0], cert);
let certs = graph
.get_all_certs(trust_kp.public(), expired_time)
.unwrap();
assert_eq!(certs.len(), 0);
}
}