mirror of
https://github.com/fluencelabs/trust-graph
synced 2024-12-04 15:20:19 +00:00
Trust Graph: implement WASM built-in (#18)
This commit is contained in:
parent
18f010c710
commit
53562bc8a7
@ -18,14 +18,16 @@ jobs:
|
|||||||
keys:
|
keys:
|
||||||
- trust-graph00-{{ checksum "./service/Cargo.lock" }}-{{ checksum "./Cargo.lock" }}-{{ checksum "./keypair/Cargo.lock" }}
|
- trust-graph00-{{ checksum "./service/Cargo.lock" }}-{{ checksum "./Cargo.lock" }}-{{ checksum "./keypair/Cargo.lock" }}
|
||||||
- run: |
|
- run: |
|
||||||
rustup toolchain install nightly-2021-04-24-x86_64-unknown-linux-gnu
|
rustup target add wasm32-wasi
|
||||||
rustup default 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 --
|
cargo test --no-fail-fast --release --all-features --
|
||||||
- save_cache:
|
- save_cache:
|
||||||
paths:
|
paths:
|
||||||
- ~/.cargo
|
- ~/.cargo
|
||||||
- ~/.rustup
|
- ~/.rustup
|
||||||
key: trust-graph00-{{ checksum "./Cargo.lock" }}-{{ checksum "./keypair/Cargo.lock" }}
|
key: trust-graph00-{{ checksum "./Cargo.lock" }-{{ checksum "./service/Cargo.lock" }}}-{{ checksum "./keypair/Cargo.lock" }}
|
||||||
|
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
|
14
.github/download_marine.sh
vendored
Executable file
14
.github/download_marine.sh
vendored
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -o pipefail -o errexit -o nounset
|
||||||
|
set -x
|
||||||
|
|
||||||
|
MARINE_RELEASE="https://api.github.com/repos/fluencelabs/marine/releases/latest"
|
||||||
|
OUT_DIR=/usr/local/bin
|
||||||
|
|
||||||
|
# get metadata about release
|
||||||
|
curl -s -H "Accept: application/vnd.github.v3+json" $MARINE_RELEASE |
|
||||||
|
# extract url and name for asset with name "marine"
|
||||||
|
# also append $OUT_DIR to each name so file is saved to $OUT_DIR
|
||||||
|
jq -r ".assets | .[] | select(.name == \"marine\") | \"\(.browser_download_url) $OUT_DIR/\(.name)\"" |
|
||||||
|
# download assets
|
||||||
|
xargs -n2 bash -c 'curl -L $0 -o $1 && chmod +x $1'
|
5
.github/workflows/changelog_config.json
vendored
Normal file
5
.github/workflows/changelog_config.json
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"template": "${{CHANGELOG}}\n\n${{UNCATEGORIZED}}",
|
||||||
|
"pr_template": "- #${{NUMBER}} ${{TITLE}}",
|
||||||
|
"empty_template": "- no changes"
|
||||||
|
}
|
158
.github/workflows/release.yml
vendored
Normal file
158
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
name: "publish-release"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
npm-publish:
|
||||||
|
name: "Publish"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: rust
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
steps:
|
||||||
|
### Setup
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set env
|
||||||
|
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Download jq
|
||||||
|
run: |
|
||||||
|
curl -L https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 -o /usr/local/bin/jq
|
||||||
|
chmod +x /usr/local/bin/jq
|
||||||
|
|
||||||
|
- name: Download marine
|
||||||
|
run: bash $GITHUB_WORKSPACE/.github/download_marine.sh
|
||||||
|
|
||||||
|
- name: Cache npm
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.npm
|
||||||
|
key: ${{ runner.os }}-node-v01-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-node-v01-
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: "15"
|
||||||
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Install toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: nightly-2021-09-01
|
||||||
|
override: true
|
||||||
|
|
||||||
|
### Build
|
||||||
|
- name: trust-graph.wasm
|
||||||
|
working-directory: ./service
|
||||||
|
run: ./build.sh
|
||||||
|
|
||||||
|
- name: Check Aqua compiles
|
||||||
|
working-directory: ./aqua
|
||||||
|
run: |
|
||||||
|
npm i
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
- name: Create builtin distribution package
|
||||||
|
run: |
|
||||||
|
./builtin-package/package.sh
|
||||||
|
|
||||||
|
- name: Build Changelog
|
||||||
|
id: changelog
|
||||||
|
uses: mikepenz/release-changelog-builder-action@v1
|
||||||
|
with:
|
||||||
|
configuration: ".github/workflows/changelog_config.json"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
## Publish
|
||||||
|
- name: Release
|
||||||
|
id: release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
name: trust-graph ${{ env.RELEASE_VERSION }}
|
||||||
|
tag_name: ${{ env.RELEASE_VERSION }}
|
||||||
|
files: |
|
||||||
|
trust-graph.tar.gz
|
||||||
|
body: ${{steps.changelog.outputs.changelog}}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
fail_on_unmatched_files: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
### Publish Aqua API
|
||||||
|
- name: Publish Aqua API
|
||||||
|
run: |
|
||||||
|
npm version ${{ env.RELEASE_VERSION }} --allow-same-version
|
||||||
|
npm publish --access public
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
working-directory: ./aqua
|
||||||
|
|
||||||
|
## Update node-distro repo
|
||||||
|
- name: Calculate SHA256
|
||||||
|
run: |
|
||||||
|
du -hs trust-graph.tar.gz
|
||||||
|
echo $(sha256sum trust-graph.tar.gz)
|
||||||
|
echo "SHA256=$(sha256sum trust-graph.tar.gz | awk '{ print $1 }')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Get tar.gz URL
|
||||||
|
id: package-url
|
||||||
|
uses: actions/github-script@v4
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
result-encoding: string
|
||||||
|
script: |
|
||||||
|
try {
|
||||||
|
let assets = await github.repos.listReleaseAssets({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
release_id: "${{ steps.release.outputs.id }}",
|
||||||
|
});
|
||||||
|
console.dir(assets);
|
||||||
|
let package = assets.data.find((a) => a.name === 'trust-graph.tar.gz');
|
||||||
|
let url = package.browser_download_url;
|
||||||
|
console.log("URL: " + url);
|
||||||
|
return url;
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Err: " + e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Update version in node-distro repo
|
||||||
|
uses: benc-uk/workflow-dispatch@v1
|
||||||
|
with:
|
||||||
|
workflow: update_service
|
||||||
|
repo: fluencelabs/node-distro
|
||||||
|
ref: 'main'
|
||||||
|
token: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
|
inputs: '{
|
||||||
|
"name": "trust-graph",
|
||||||
|
"version": "${{ env.RELEASE_VERSION }}",
|
||||||
|
"url": "${{ steps.package-url.outputs.result }}",
|
||||||
|
"sha256": "${{ env.SHA256 }}"
|
||||||
|
}'
|
||||||
|
|
||||||
|
- name: Log notice
|
||||||
|
uses: actions/github-script@v4
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
console.dir(core);
|
||||||
|
core.info("trust-graph was updated to ${{ env.RELEASE_VERSION }} in node-distro repo");
|
17
.github/workflows/tag.yml
vendored
Normal file
17
.github/workflows/tag.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
name: "tag"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tag:
|
||||||
|
name: "Tag"
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Bump version and push tag
|
||||||
|
id: tag_version
|
||||||
|
uses: mathieudutour/github-tag-action@v5.5
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.PERSONAL_TOKEN }}
|
20
.gitignore
vendored
20
.gitignore
vendored
@ -1,2 +1,18 @@
|
|||||||
.idea
|
service/target
|
||||||
target/
|
service/artifacts
|
||||||
|
builtin-package/*.wasm
|
||||||
|
trust-graph.tar.gz
|
||||||
|
|
||||||
|
**/*.rs.bk
|
||||||
|
**/.idea
|
||||||
|
**/artifacts
|
||||||
|
**/.DS_Store
|
||||||
|
**/node_modules
|
||||||
|
**/dist
|
||||||
|
|
||||||
|
# Remove after https://github.com/fluencelabs/aqua/issues/287
|
||||||
|
aqua/target/typescript/**
|
||||||
|
example/src/generated/**
|
||||||
|
example/generated/**
|
||||||
|
admin/src/generated/**
|
||||||
|
admin/generated/**
|
||||||
|
1543
Cargo.lock
generated
1543
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
15
Cargo.toml
15
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "trust-graph"
|
name = "trust-graph"
|
||||||
version = "0.2.8"
|
version = "0.3.0"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "trust graph"
|
description = "trust graph"
|
||||||
@ -8,25 +8,24 @@ license = "Apache-2.0"
|
|||||||
repository = "https://github.com/fluencelabs/trust-graph"
|
repository = "https://github.com/fluencelabs/trust-graph"
|
||||||
|
|
||||||
[dependencies]
|
[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"] }
|
serde = { version = "=1.0.118", features = ["derive"] }
|
||||||
|
|
||||||
fluence-keypair = { path = "./keypair", version = "0.4.0" }
|
fluence-keypair = { path = "./keypair", version = "0.5.0" }
|
||||||
serde_json = "1.0.58"
|
serde_json = "1.0.58"
|
||||||
bs58 = "0.3.1"
|
bs58 = "0.3.1"
|
||||||
failure = "0.1.6"
|
failure = "0.1.6"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
ref-cast = "1.0.2"
|
ref-cast = "1.0.2"
|
||||||
derivative = "2.1.1"
|
derivative = "2.1.1"
|
||||||
ed25519-dalek = { version = "1.0.1", features = ["serde"] }
|
|
||||||
rand = "0.7.0"
|
|
||||||
signature = "1.3.0"
|
signature = "1.3.0"
|
||||||
serde_with = "1.6.0"
|
serde_with = "1.6.0"
|
||||||
thiserror = "1.0.23"
|
thiserror = "1.0.23"
|
||||||
libsecp256k1 = "0.3.5"
|
sha2 = "0.9.5"
|
||||||
ring = "0.16.20"
|
rand = "0.7.0"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"keypair"
|
"keypair",
|
||||||
|
"service"
|
||||||
]
|
]
|
||||||
|
59
README.md
59
README.md
@ -6,61 +6,8 @@ The network-wide peer relationship layer is used to manage connectivity and perm
|
|||||||
|
|
||||||
`/.` is the main project with all trust graph logic and in-memory storage as a default
|
`/.` is the main project with all trust graph logic and in-memory storage as a default
|
||||||
|
|
||||||
`identity` directory is an abstracted cryptographical layer (key pairs, signature, etc.)
|
`keypair` directory is an abstracted cryptographical layer (key pairs, public keys, signatures, etc.)
|
||||||
|
|
||||||
`wasm` is a package that provides `fce` API and could be compiled to a Wasm file. It is used `SQLite` as storage and could be used only with `SQLite` Wasm file near.
|
`service` is a package that provides `marine` API and could be compiled to a Wasm file. It is uses `SQLite` as storage.
|
||||||
|
|
||||||
`js` is a `npm` package that allows you to create and serialize certificates
|
|
||||||
|
|
||||||
|
|
||||||
### Use trust-graph as a library
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
// Generate a new key pair
|
|
||||||
let root_kp = KeyPair::generate();
|
|
||||||
|
|
||||||
// Generate a key for which a certificate will be issued
|
|
||||||
let issued_for = KeyPair::generate();
|
|
||||||
|
|
||||||
// A time when the certificate will be issued and whet it will be expired
|
|
||||||
let now = Duration::from_secs(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as u64)
|
|
||||||
let expires_at = Duration::from_secs(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as u64 + 10000)
|
|
||||||
|
|
||||||
// Create a certificate
|
|
||||||
let mut cert = Certificate::issue_root(&root_kp, issued_for.public_key(), expires_at, now);
|
|
||||||
|
|
||||||
// We can add more keys to extend created certificate
|
|
||||||
// The method requires current_time to check if the old certificate is valid
|
|
||||||
let new_key = KeyPair::generate();
|
|
||||||
let new_cert = Certificate::issue(
|
|
||||||
&issued_for,
|
|
||||||
new_key.public_key(),
|
|
||||||
&cert,
|
|
||||||
expires_at,
|
|
||||||
now,
|
|
||||||
current_time(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Create new trust graph instance
|
|
||||||
let st = Box::new(InMemoryStorage::new());
|
|
||||||
let mut graph = TrustGraph::new(st);
|
|
||||||
|
|
||||||
// Add root weights. Basic keys that certificates should start with
|
|
||||||
graph.add_root_weight(root_kp.public_key().into(), 1);
|
|
||||||
|
|
||||||
// Add the certificate to a trust graph
|
|
||||||
// current_time is to check if certificate is still valid
|
|
||||||
// Could throw an error if the certificate is expired or malformed
|
|
||||||
graph.add(new_cert, current_time()).unwrap();
|
|
||||||
|
|
||||||
// We can check a weight of a key based on certificates we added and root weights
|
|
||||||
// If one public key have multiple trusts, we will get the maximum
|
|
||||||
let w = graph.weight(new_key.public_key()).unwrap().unwrap();
|
|
||||||
|
|
||||||
// Every trust or chain of trusts could be revoked by owners of keys in certificates
|
|
||||||
|
|
||||||
let revoke = Revoke::create(&issued_for, new_key.public_key(), current_time());
|
|
||||||
graph.revoke(revoke).unwrap();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
`example` is a `js` script that shows how to issue, sign trusts/revokes, get certificates
|
||||||
|
6
admin/README.md
Normal file
6
admin/README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# How to generate export certificates
|
||||||
|
1. Go to `local-network`
|
||||||
|
2. Run `docker compose up -d` to start Fluence node
|
||||||
|
3. Go back to `../admin`
|
||||||
|
4. Put `root_secret_key.ed25519` and `issuer_secret_key.ed25519` to folder
|
||||||
|
5. Run `npm run start`
|
9
admin/aqua/export.aqua
Normal file
9
admin/aqua/export.aqua
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import get_trust_bytes, issue_trust from "../../aqua/trust-graph-api.aqua"
|
||||||
|
export get_trust_bytes, issue_trust
|
||||||
|
|
||||||
|
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||||
|
|
||||||
|
func timestamp_sec(node: string) -> u64:
|
||||||
|
on node:
|
||||||
|
result <- Peer.timestamp_sec()
|
||||||
|
<- result
|
105
admin/index.ts
Normal file
105
admin/index.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
get_trust_bytes,
|
||||||
|
issue_trust,
|
||||||
|
timestamp_sec,
|
||||||
|
|
||||||
|
} from "./generated/export";
|
||||||
|
import { Fluence, KeyPair } from "@fluencelabs/fluence";
|
||||||
|
import { Node, krasnodar, stage, testNet } from "@fluencelabs/fluence-network-environment";
|
||||||
|
import * as fs from "fs";
|
||||||
|
const bs58 = require('bs58');
|
||||||
|
|
||||||
|
let local: Node[] = [
|
||||||
|
{
|
||||||
|
peerId: "12D3KooWHBG9oaVx4i3vi6c1rSBUm7MLBmyGmmbHoZ23pmjDCnvK",
|
||||||
|
multiaddr:
|
||||||
|
"/ip4/127.0.0.1/tcp/9990/ws/p2p/12D3KooWHBG9oaVx4i3vi6c1rSBUm7MLBmyGmmbHoZ23pmjDCnvK",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
async function issue_trust_helper(node: string, issuer_kp: KeyPair, issuer_peer_id: string, issued_for_peer_id: string, expires_at_sec: number, issued_at_sec: number) {
|
||||||
|
let trust_metadata = await get_trust_bytes(node, issued_for_peer_id, expires_at_sec, issued_at_sec);
|
||||||
|
const signed_metadata = await issuer_kp.Libp2pPeerId.privKey.sign(Uint8Array.from(trust_metadata.result));
|
||||||
|
|
||||||
|
let trust = await issue_trust(node, issued_for_peer_id, expires_at_sec, issued_at_sec, Array.from(signed_metadata));
|
||||||
|
return trust.trust
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main(environment: Node[]) {
|
||||||
|
let node = environment[0].peerId;
|
||||||
|
await Fluence.start({ connectTo: environment[0]});
|
||||||
|
console.log(
|
||||||
|
"📗 created a fluence peer %s with relay %s",
|
||||||
|
Fluence.getStatus().peerId,
|
||||||
|
Fluence.getStatus().relayPeerId
|
||||||
|
);
|
||||||
|
|
||||||
|
let root_sk_b58 = fs.readFileSync("./root_secret_key.ed25519").toString();
|
||||||
|
let issuer_sk_b58 = fs.readFileSync("./issuer_secret_key.ed25519").toString();
|
||||||
|
let root_kp = await KeyPair.fromEd25519SK(bs58.decode(root_sk_b58));
|
||||||
|
let issuer_kp = await KeyPair.fromEd25519SK(bs58.decode(issuer_sk_b58));
|
||||||
|
console.log("Root private key: %s", root_sk_b58);
|
||||||
|
console.log("Root peer id: %s", root_kp.Libp2pPeerId.toB58String());
|
||||||
|
console.log("Issuer private key: %s", issuer_sk_b58);
|
||||||
|
|
||||||
|
let cur_time = await timestamp_sec(node);
|
||||||
|
let expires_at = cur_time + 60 * 60 * 24 * 365;
|
||||||
|
let common_chain = [] as any;
|
||||||
|
// self-signed root trust
|
||||||
|
common_chain.push(await issue_trust_helper(node, root_kp, root_kp.Libp2pPeerId.toB58String(), root_kp.Libp2pPeerId.toB58String(), expires_at, cur_time));
|
||||||
|
// from root to issuer
|
||||||
|
common_chain.push(await issue_trust_helper(node, root_kp, root_kp.Libp2pPeerId.toB58String(), issuer_kp.Libp2pPeerId.toB58String(), expires_at, cur_time));
|
||||||
|
|
||||||
|
let certificates = [];
|
||||||
|
for (let i = 0; i < krasnodar.length; i++) {
|
||||||
|
// from issuer to node
|
||||||
|
let trust = await issue_trust_helper(node, issuer_kp, issuer_kp.Libp2pPeerId.toB58String(), krasnodar[i].peerId, expires_at, cur_time);
|
||||||
|
let cert = {chain: [...common_chain, trust]};
|
||||||
|
certificates.push(cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync("../builtin-package/on_start.json", JSON.stringify({certs: certificates}));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = process.argv.slice(2);
|
||||||
|
let environment: Node[];
|
||||||
|
if (args[0] == "testnet") {
|
||||||
|
environment = testNet;
|
||||||
|
console.log("📘 Will connect to testNet");
|
||||||
|
} else if (args[0] == "stage") {
|
||||||
|
environment = stage;
|
||||||
|
console.log("📘 Will connect to stage");
|
||||||
|
} else if (args[0] == "krasnodar") {
|
||||||
|
environment = krasnodar;
|
||||||
|
console.log("📘 Will connect to krasnodar");
|
||||||
|
} else if (args[0] == "local") {
|
||||||
|
environment = local;
|
||||||
|
console.log("📘 Will connect to local");
|
||||||
|
} else {
|
||||||
|
throw "Specify environment: krasnodar, testnet, stage or local";
|
||||||
|
}
|
||||||
|
|
||||||
|
main(environment)
|
||||||
|
.then(() => process.exit(0))
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
4129
admin/package-lock.json
generated
Normal file
4129
admin/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
admin/package.json
Normal file
26
admin/package.json
Normal 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.4.1-240",
|
||||||
|
"@fluencelabs/aqua-lib": "0.2.0",
|
||||||
|
"@fluencelabs/fluence": "0.14.3",
|
||||||
|
"@fluencelabs/fluence-network-environment": "^1.0.10",
|
||||||
|
"@fluencelabs/trust-graph": "file:../aqua",
|
||||||
|
"bs58": "^4.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^4.4.3"
|
||||||
|
}
|
||||||
|
}
|
69
admin/tsconfig.json
Normal file
69
admin/tsconfig.json
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||||
|
|
||||||
|
/* Basic Options */
|
||||||
|
// "incremental": true, /* Enable incremental compilation */
|
||||||
|
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
||||||
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||||
|
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||||
|
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
|
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||||
|
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||||
|
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||||
|
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||||
|
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||||
|
"outDir": "./dist", /* Redirect output structure to the directory. */
|
||||||
|
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
|
// "composite": true, /* Enable project compilation */
|
||||||
|
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||||
|
// "removeComments": true, /* Do not emit comments to output. */
|
||||||
|
// "noEmit": true, /* Do not emit outputs. */
|
||||||
|
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||||
|
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||||
|
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||||
|
|
||||||
|
/* Strict Type-Checking Options */
|
||||||
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
|
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
|
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||||
|
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||||
|
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||||
|
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||||
|
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||||
|
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||||
|
|
||||||
|
/* Additional Checks */
|
||||||
|
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||||
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
|
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
|
||||||
|
/* Module Resolution Options */
|
||||||
|
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
|
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||||
|
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||||
|
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
|
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
|
||||||
|
/* Source Map Options */
|
||||||
|
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||||
|
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||||
|
|
||||||
|
/* Experimental Options */
|
||||||
|
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
|
|
||||||
|
/* Advanced Options */
|
||||||
|
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||||
|
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||||
|
}
|
||||||
|
}
|
5544
aqua/package-lock.json
generated
Normal file
5544
aqua/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
aqua/package.json
Normal file
36
aqua/package.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "@fluencelabs/trust-graph",
|
||||||
|
"version": "0.1.12",
|
||||||
|
"description": "Aqua Trust Graph API library",
|
||||||
|
"files": [
|
||||||
|
"*.aqua"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@fluencelabs/aqua-lib": "0.2.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"generate-aqua": "../service/build.sh",
|
||||||
|
"compile-aqua": "aqua -i . -o ./target/typescript",
|
||||||
|
"build": "npm run compile-aqua"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/fluencelabs/trust-graph",
|
||||||
|
"directory": "aqua"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"aqua",
|
||||||
|
"fluence",
|
||||||
|
"trust-graph",
|
||||||
|
"p2p"
|
||||||
|
],
|
||||||
|
"author": "Fluence Labs",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/fluencelabs/trust-graph/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/fluencelabs/trust-graph#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"@fluencelabs/aqua": "0.4.1-240"
|
||||||
|
}
|
||||||
|
}
|
101
aqua/trust-graph-api.aqua
Normal file
101
aqua/trust-graph-api.aqua
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
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 get_host_certs(node: string, issued_for: string) -> AllCertsResult:
|
||||||
|
on node:
|
||||||
|
timestamp_sec <- Peer.timestamp_sec()
|
||||||
|
result <- TrustGraph.get_host_certs(timestamp_sec)
|
||||||
|
<- result
|
||||||
|
|
||||||
|
func get_host_certs_from(node: string, issuer: string) -> AllCertsResult:
|
||||||
|
on node:
|
||||||
|
timestamp_sec <- Peer.timestamp_sec()
|
||||||
|
result <- TrustGraph.get_host_certs_from(issuer, 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
|
||||||
|
|
||||||
|
service TrustOp("op"):
|
||||||
|
array_length(a: []Trust) -> u64
|
||||||
|
|
||||||
|
service BoolOp("op"):
|
||||||
|
array_length(a: []bool) -> u64
|
||||||
|
|
||||||
|
-- https://github.com/fluencelabs/trust-graph/issues/26
|
||||||
|
func isFluencePeer() -> bool:
|
||||||
|
certs_result <- get_host_certs_from(HOST_PEER_ID, "12D3KooWM45u7AQxsb4MuQJNYT3NWHHMLU7JTbBV66RTfF3KSzdR")
|
||||||
|
resultBox: *bool
|
||||||
|
if certs_result.success:
|
||||||
|
for cert <- certs_result.certificates:
|
||||||
|
len <- TrustOp.array_length(cert.chain)
|
||||||
|
if len == 3:
|
||||||
|
if cert.chain!0.issued_for == "12D3KooWNbZKaPWRZ8wgjGvrxdJFz9Fq5uVwkR6ERV1f74HhPdyB":
|
||||||
|
if cert.chain!1.issued_for == "12D3KooWM45u7AQxsb4MuQJNYT3NWHHMLU7JTbBV66RTfF3KSzdR":
|
||||||
|
resultBox <<- true
|
||||||
|
|
||||||
|
result_len <- BoolOp.array_length(resultBox)
|
||||||
|
result: *bool
|
||||||
|
if result_len == 0:
|
||||||
|
result <<- false
|
||||||
|
else:
|
||||||
|
result <<- true
|
||||||
|
<- result!
|
87
aqua/trust-graph.aqua
Normal file
87
aqua/trust-graph.aqua
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
module TrustGraph declares *
|
||||||
|
|
||||||
|
data AddRootResult:
|
||||||
|
success: bool
|
||||||
|
error: string
|
||||||
|
|
||||||
|
data AddTrustResult:
|
||||||
|
success: bool
|
||||||
|
error: string
|
||||||
|
weight: u32
|
||||||
|
|
||||||
|
data Trust:
|
||||||
|
issued_for: string
|
||||||
|
expires_at: u64
|
||||||
|
signature: string
|
||||||
|
sig_type: string
|
||||||
|
issued_at: u64
|
||||||
|
|
||||||
|
data Certificate:
|
||||||
|
chain: []Trust
|
||||||
|
|
||||||
|
data AllCertsResult:
|
||||||
|
success: bool
|
||||||
|
certificates: []Certificate
|
||||||
|
error: string
|
||||||
|
|
||||||
|
data GetRevokeBytesResult:
|
||||||
|
success: bool
|
||||||
|
error: string
|
||||||
|
result: []u8
|
||||||
|
|
||||||
|
data GetTrustBytesResult:
|
||||||
|
success: bool
|
||||||
|
error: string
|
||||||
|
result: []u8
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
data WeightResult:
|
||||||
|
success: bool
|
||||||
|
weight: u32
|
||||||
|
peer_id: string
|
||||||
|
error: string
|
||||||
|
|
||||||
|
service TrustGraph("trust-graph"):
|
||||||
|
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_host_certs(timestamp_sec: u64) -> AllCertsResult
|
||||||
|
get_host_certs_from(issuer: string, timestamp_sec: u64) -> AllCertsResult
|
||||||
|
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
|
||||||
|
get_weight_factor(max_chain_len: u32) -> u32
|
||||||
|
insert_cert(certificate: Certificate, timestamp_sec: u64) -> InsertResult
|
||||||
|
insert_cert_raw(certificate: string, timestamp_sec: u64) -> InsertResult
|
||||||
|
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
|
7
builtin-package/blueprint.json
Normal file
7
builtin-package/blueprint.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "trust-graph",
|
||||||
|
"dependencies": [
|
||||||
|
"name:sqlite3",
|
||||||
|
"name:trust-graph"
|
||||||
|
]
|
||||||
|
}
|
31
builtin-package/on_start.air
Normal file
31
builtin-package/on_start.air
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call relay ("trust-graph" "add_root") ["12D3KooWNbZKaPWRZ8wgjGvrxdJFz9Fq5uVwkR6ERV1f74HhPdyB" 2] add_root_res)
|
||||||
|
(xor
|
||||||
|
(match add_root_res.$.success! true
|
||||||
|
(null)
|
||||||
|
)
|
||||||
|
(call relay ("op" "return") [add_root_res.$.error!])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(seq
|
||||||
|
(fold certs i
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call relay ("peer" "timestamp_sec") [] cur_time)
|
||||||
|
(call relay ("trust-graph" "insert_cert") [i cur_time] insert_result)
|
||||||
|
)
|
||||||
|
(xor
|
||||||
|
(match insert_result.$.success! true
|
||||||
|
(null)
|
||||||
|
)
|
||||||
|
(call relay ("op" "return") [insert_result.$.error!])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(next i)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(call relay ("op" "return") [true])
|
||||||
|
)
|
||||||
|
)
|
1
builtin-package/on_start.json
Normal file
1
builtin-package/on_start.json
Normal file
File diff suppressed because one or more lines are too long
22
builtin-package/package.sh
Executable file
22
builtin-package/package.sh
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -o pipefail -o nounset -o errexit
|
||||||
|
|
||||||
|
# set current working directory to script directory to run script from everywhere
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
SCRIPT_DIR="$(pwd)"
|
||||||
|
|
||||||
|
(
|
||||||
|
echo "*** copy wasm files ***"
|
||||||
|
cd ../service
|
||||||
|
cp artifacts/*.wasm "$SCRIPT_DIR"
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
echo "*** create builtin distribution package ***"
|
||||||
|
cd ..
|
||||||
|
mv builtin-package trust-graph
|
||||||
|
tar --exclude="package.sh" -f trust-graph.tar.gz -zcv ./trust-graph
|
||||||
|
mv trust-graph builtin-package
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "*** done ***"
|
3
builtin-package/sqlite3_config.json
Normal file
3
builtin-package/sqlite3_config.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"name": "sqlite3"
|
||||||
|
}
|
9
builtin-package/trust-graph_config.json
Normal file
9
builtin-package/trust-graph_config.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "trust-graph",
|
||||||
|
"preopened_files": [
|
||||||
|
"/tmp"
|
||||||
|
],
|
||||||
|
"mapped_dirs": {
|
||||||
|
"tmp": "./tmp"
|
||||||
|
}
|
||||||
|
}
|
5
example/README.md
Normal file
5
example/README.md
Normal 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`
|
10
example/aqua/export.aqua
Normal file
10
example/aqua/export.aqua
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
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, isFluencePeer from "../../aqua/trust-graph-api.aqua"
|
||||||
|
|
||||||
|
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, isFluencePeer
|
||||||
|
|
||||||
|
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||||
|
|
||||||
|
func timestamp_sec(node: string) -> u64:
|
||||||
|
on node:
|
||||||
|
result <- Peer.timestamp_sec()
|
||||||
|
<- result
|
@ -1,811 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* This file is auto-generated. Do not edit manually: changes may be erased.
|
|
||||||
* Generated by Aqua compiler: https://github.com/fluencelabs/aqua/.
|
|
||||||
* If you find any bugs, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
|
||||||
* Aqua version: 0.3.1-228
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
import { Fluence, FluencePeer } from '@fluencelabs/fluence';
|
|
||||||
import {
|
|
||||||
ResultCodes,
|
|
||||||
RequestFlow,
|
|
||||||
RequestFlowBuilder,
|
|
||||||
CallParams,
|
|
||||||
} from '@fluencelabs/fluence/dist/internal/compilerSupport/v1';
|
|
||||||
|
|
||||||
|
|
||||||
// Services
|
|
||||||
|
|
||||||
|
|
||||||
// Functions
|
|
||||||
|
|
||||||
export function verify_trust(node: string, trust: {expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}, issuer_peer_id: string, config?: {ttl?: number}) : Promise<{error:string;success:boolean}>;
|
|
||||||
export function verify_trust(peer: FluencePeer, node: string, trust: {expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}, issuer_peer_id: string, config?: {ttl?: number}) : Promise<{error:string;success:boolean}>;
|
|
||||||
export function verify_trust(...args: any) {
|
|
||||||
let peer: FluencePeer;
|
|
||||||
let node: any;
|
|
||||||
let trust: any;
|
|
||||||
let issuer_peer_id: any;
|
|
||||||
let config: any;
|
|
||||||
if (FluencePeer.isInstance(args[0])) {
|
|
||||||
peer = args[0];
|
|
||||||
node = args[1];
|
|
||||||
trust = args[2];
|
|
||||||
issuer_peer_id = args[3];
|
|
||||||
config = args[4];
|
|
||||||
} else {
|
|
||||||
peer = Fluence.getPeer();
|
|
||||||
node = args[0];
|
|
||||||
trust = args[1];
|
|
||||||
issuer_peer_id = args[2];
|
|
||||||
config = args[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
let request: RequestFlow;
|
|
||||||
const promise = new Promise<{error:string;success:boolean}>((resolve, reject) => {
|
|
||||||
const r = new RequestFlowBuilder()
|
|
||||||
.disableInjections()
|
|
||||||
.withRawScript(
|
|
||||||
`
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "node") [] node)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "trust") [] trust)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "issuer_peer_id") [] issuer_peer_id)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(call node ("peer" "timestamp_sec") [] timestamp_sec)
|
|
||||||
(call node ("trust-graph" "verify_trust") [trust issuer_peer_id timestamp_sec] result)
|
|
||||||
)
|
|
||||||
(seq
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(call %init_peer_id% ("callbackSrv" "response") [result])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3])
|
|
||||||
)
|
|
||||||
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.configHandler((h) => {
|
|
||||||
h.on('getDataSrv', '-relay-', () => {
|
|
||||||
return peer.getStatus().relayPeerId;
|
|
||||||
});
|
|
||||||
h.on('getDataSrv', 'node', () => {return node;});
|
|
||||||
h.on('getDataSrv', 'trust', () => {return trust;});
|
|
||||||
h.on('getDataSrv', 'issuer_peer_id', () => {return issuer_peer_id;});
|
|
||||||
h.onEvent('callbackSrv', 'response', (args) => {
|
|
||||||
const [res] = args;
|
|
||||||
resolve(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
|
||||||
const [err] = args;
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.handleScriptError(reject)
|
|
||||||
.handleTimeout(() => {
|
|
||||||
reject('Request timed out for verify_trust');
|
|
||||||
})
|
|
||||||
if(config && config.ttl) {
|
|
||||||
r.withTTL(config.ttl)
|
|
||||||
}
|
|
||||||
request = r.build();
|
|
||||||
});
|
|
||||||
peer.internals.initiateFlow(request!);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function issue_trust(node: string, issued_for_peer_id: string, expires_at_sec: number, issued_at_sec: number, trust_bytes: number[], config?: {ttl?: number}) : Promise<{error:string;success:boolean;trust:{expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}}>;
|
|
||||||
export function issue_trust(peer: FluencePeer, node: string, issued_for_peer_id: string, expires_at_sec: number, issued_at_sec: number, trust_bytes: number[], config?: {ttl?: number}) : Promise<{error:string;success:boolean;trust:{expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}}>;
|
|
||||||
export function issue_trust(...args: any) {
|
|
||||||
let peer: FluencePeer;
|
|
||||||
let node: any;
|
|
||||||
let issued_for_peer_id: any;
|
|
||||||
let expires_at_sec: any;
|
|
||||||
let issued_at_sec: any;
|
|
||||||
let trust_bytes: any;
|
|
||||||
let config: any;
|
|
||||||
if (FluencePeer.isInstance(args[0])) {
|
|
||||||
peer = args[0];
|
|
||||||
node = args[1];
|
|
||||||
issued_for_peer_id = args[2];
|
|
||||||
expires_at_sec = args[3];
|
|
||||||
issued_at_sec = args[4];
|
|
||||||
trust_bytes = args[5];
|
|
||||||
config = args[6];
|
|
||||||
} else {
|
|
||||||
peer = Fluence.getPeer();
|
|
||||||
node = args[0];
|
|
||||||
issued_for_peer_id = args[1];
|
|
||||||
expires_at_sec = args[2];
|
|
||||||
issued_at_sec = args[3];
|
|
||||||
trust_bytes = args[4];
|
|
||||||
config = args[5];
|
|
||||||
}
|
|
||||||
|
|
||||||
let request: RequestFlow;
|
|
||||||
const promise = new Promise<{error:string;success:boolean;trust:{expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}}>((resolve, reject) => {
|
|
||||||
const r = new RequestFlowBuilder()
|
|
||||||
.disableInjections()
|
|
||||||
.withRawScript(
|
|
||||||
`
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "node") [] node)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "issued_for_peer_id") [] issued_for_peer_id)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "expires_at_sec") [] expires_at_sec)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "issued_at_sec") [] issued_at_sec)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "trust_bytes") [] trust_bytes)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(call node ("trust-graph" "issue_trust") [issued_for_peer_id expires_at_sec issued_at_sec trust_bytes] result)
|
|
||||||
(seq
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(call %init_peer_id% ("callbackSrv" "response") [result])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3])
|
|
||||||
)
|
|
||||||
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.configHandler((h) => {
|
|
||||||
h.on('getDataSrv', '-relay-', () => {
|
|
||||||
return peer.getStatus().relayPeerId;
|
|
||||||
});
|
|
||||||
h.on('getDataSrv', 'node', () => {return node;});
|
|
||||||
h.on('getDataSrv', 'issued_for_peer_id', () => {return issued_for_peer_id;});
|
|
||||||
h.on('getDataSrv', 'expires_at_sec', () => {return expires_at_sec;});
|
|
||||||
h.on('getDataSrv', 'issued_at_sec', () => {return issued_at_sec;});
|
|
||||||
h.on('getDataSrv', 'trust_bytes', () => {return trust_bytes;});
|
|
||||||
h.onEvent('callbackSrv', 'response', (args) => {
|
|
||||||
const [res] = args;
|
|
||||||
resolve(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
|
||||||
const [err] = args;
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.handleScriptError(reject)
|
|
||||||
.handleTimeout(() => {
|
|
||||||
reject('Request timed out for issue_trust');
|
|
||||||
})
|
|
||||||
if(config && config.ttl) {
|
|
||||||
r.withTTL(config.ttl)
|
|
||||||
}
|
|
||||||
request = r.build();
|
|
||||||
});
|
|
||||||
peer.internals.initiateFlow(request!);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function insert_cert(node: string, certificate: {chain:{expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}[]}, config?: {ttl?: number}) : Promise<{error:string;success:boolean}>;
|
|
||||||
export function insert_cert(peer: FluencePeer, node: string, certificate: {chain:{expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}[]}, config?: {ttl?: number}) : Promise<{error:string;success:boolean}>;
|
|
||||||
export function insert_cert(...args: any) {
|
|
||||||
let peer: FluencePeer;
|
|
||||||
let node: any;
|
|
||||||
let certificate: any;
|
|
||||||
let config: any;
|
|
||||||
if (FluencePeer.isInstance(args[0])) {
|
|
||||||
peer = args[0];
|
|
||||||
node = args[1];
|
|
||||||
certificate = args[2];
|
|
||||||
config = args[3];
|
|
||||||
} else {
|
|
||||||
peer = Fluence.getPeer();
|
|
||||||
node = args[0];
|
|
||||||
certificate = args[1];
|
|
||||||
config = args[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
let request: RequestFlow;
|
|
||||||
const promise = new Promise<{error:string;success:boolean}>((resolve, reject) => {
|
|
||||||
const r = new RequestFlowBuilder()
|
|
||||||
.disableInjections()
|
|
||||||
.withRawScript(
|
|
||||||
`
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "node") [] node)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "certificate") [] certificate)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(call node ("peer" "timestamp_sec") [] timestamp_sec)
|
|
||||||
(call node ("trust-graph" "insert_cert") [certificate timestamp_sec] result)
|
|
||||||
)
|
|
||||||
(seq
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(call %init_peer_id% ("callbackSrv" "response") [result])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3])
|
|
||||||
)
|
|
||||||
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.configHandler((h) => {
|
|
||||||
h.on('getDataSrv', '-relay-', () => {
|
|
||||||
return peer.getStatus().relayPeerId;
|
|
||||||
});
|
|
||||||
h.on('getDataSrv', 'node', () => {return node;});
|
|
||||||
h.on('getDataSrv', 'certificate', () => {return certificate;});
|
|
||||||
h.onEvent('callbackSrv', 'response', (args) => {
|
|
||||||
const [res] = args;
|
|
||||||
resolve(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
|
||||||
const [err] = args;
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.handleScriptError(reject)
|
|
||||||
.handleTimeout(() => {
|
|
||||||
reject('Request timed out for insert_cert');
|
|
||||||
})
|
|
||||||
if(config && config.ttl) {
|
|
||||||
r.withTTL(config.ttl)
|
|
||||||
}
|
|
||||||
request = r.build();
|
|
||||||
});
|
|
||||||
peer.internals.initiateFlow(request!);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function get_all_certs(node: string, issued_for: string, config?: {ttl?: number}) : Promise<{certificates:{chain:{expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}[]}[];error:string;success:boolean}>;
|
|
||||||
export function get_all_certs(peer: FluencePeer, node: string, issued_for: string, config?: {ttl?: number}) : Promise<{certificates:{chain:{expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}[]}[];error:string;success:boolean}>;
|
|
||||||
export function get_all_certs(...args: any) {
|
|
||||||
let peer: FluencePeer;
|
|
||||||
let node: any;
|
|
||||||
let issued_for: any;
|
|
||||||
let config: any;
|
|
||||||
if (FluencePeer.isInstance(args[0])) {
|
|
||||||
peer = args[0];
|
|
||||||
node = args[1];
|
|
||||||
issued_for = args[2];
|
|
||||||
config = args[3];
|
|
||||||
} else {
|
|
||||||
peer = Fluence.getPeer();
|
|
||||||
node = args[0];
|
|
||||||
issued_for = args[1];
|
|
||||||
config = args[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
let request: RequestFlow;
|
|
||||||
const promise = new Promise<{certificates:{chain:{expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}[]}[];error:string;success:boolean}>((resolve, reject) => {
|
|
||||||
const r = new RequestFlowBuilder()
|
|
||||||
.disableInjections()
|
|
||||||
.withRawScript(
|
|
||||||
`
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "node") [] node)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "issued_for") [] issued_for)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(call node ("peer" "timestamp_sec") [] timestamp_sec)
|
|
||||||
(call node ("trust-graph" "get_all_certs") [issued_for timestamp_sec] result)
|
|
||||||
)
|
|
||||||
(seq
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(call %init_peer_id% ("callbackSrv" "response") [result])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3])
|
|
||||||
)
|
|
||||||
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.configHandler((h) => {
|
|
||||||
h.on('getDataSrv', '-relay-', () => {
|
|
||||||
return peer.getStatus().relayPeerId;
|
|
||||||
});
|
|
||||||
h.on('getDataSrv', 'node', () => {return node;});
|
|
||||||
h.on('getDataSrv', 'issued_for', () => {return issued_for;});
|
|
||||||
h.onEvent('callbackSrv', 'response', (args) => {
|
|
||||||
const [res] = args;
|
|
||||||
resolve(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
|
||||||
const [err] = args;
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.handleScriptError(reject)
|
|
||||||
.handleTimeout(() => {
|
|
||||||
reject('Request timed out for get_all_certs');
|
|
||||||
})
|
|
||||||
if(config && config.ttl) {
|
|
||||||
r.withTTL(config.ttl)
|
|
||||||
}
|
|
||||||
request = r.build();
|
|
||||||
});
|
|
||||||
peer.internals.initiateFlow(request!);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function add_trust(node: string, trust: {expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}, issuer_peer_id: string, config?: {ttl?: number}) : Promise<{error:string;success:boolean;weight:number}>;
|
|
||||||
export function add_trust(peer: FluencePeer, node: string, trust: {expires_at:number;issued_at:number;issued_for:string;sig_type:string;signature:string}, issuer_peer_id: string, config?: {ttl?: number}) : Promise<{error:string;success:boolean;weight:number}>;
|
|
||||||
export function add_trust(...args: any) {
|
|
||||||
let peer: FluencePeer;
|
|
||||||
let node: any;
|
|
||||||
let trust: any;
|
|
||||||
let issuer_peer_id: any;
|
|
||||||
let config: any;
|
|
||||||
if (FluencePeer.isInstance(args[0])) {
|
|
||||||
peer = args[0];
|
|
||||||
node = args[1];
|
|
||||||
trust = args[2];
|
|
||||||
issuer_peer_id = args[3];
|
|
||||||
config = args[4];
|
|
||||||
} else {
|
|
||||||
peer = Fluence.getPeer();
|
|
||||||
node = args[0];
|
|
||||||
trust = args[1];
|
|
||||||
issuer_peer_id = args[2];
|
|
||||||
config = args[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
let request: RequestFlow;
|
|
||||||
const promise = new Promise<{error:string;success:boolean;weight:number}>((resolve, reject) => {
|
|
||||||
const r = new RequestFlowBuilder()
|
|
||||||
.disableInjections()
|
|
||||||
.withRawScript(
|
|
||||||
`
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "node") [] node)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "trust") [] trust)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "issuer_peer_id") [] issuer_peer_id)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(call node ("peer" "timestamp_sec") [] timestamp_sec)
|
|
||||||
(call node ("trust-graph" "add_trust") [trust issuer_peer_id timestamp_sec] result)
|
|
||||||
)
|
|
||||||
(seq
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(call %init_peer_id% ("callbackSrv" "response") [result])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3])
|
|
||||||
)
|
|
||||||
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.configHandler((h) => {
|
|
||||||
h.on('getDataSrv', '-relay-', () => {
|
|
||||||
return peer.getStatus().relayPeerId;
|
|
||||||
});
|
|
||||||
h.on('getDataSrv', 'node', () => {return node;});
|
|
||||||
h.on('getDataSrv', 'trust', () => {return trust;});
|
|
||||||
h.on('getDataSrv', 'issuer_peer_id', () => {return issuer_peer_id;});
|
|
||||||
h.onEvent('callbackSrv', 'response', (args) => {
|
|
||||||
const [res] = args;
|
|
||||||
resolve(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
|
||||||
const [err] = args;
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.handleScriptError(reject)
|
|
||||||
.handleTimeout(() => {
|
|
||||||
reject('Request timed out for add_trust');
|
|
||||||
})
|
|
||||||
if(config && config.ttl) {
|
|
||||||
r.withTTL(config.ttl)
|
|
||||||
}
|
|
||||||
request = r.build();
|
|
||||||
});
|
|
||||||
peer.internals.initiateFlow(request!);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function add_root(node: string, peer_id: string, weight_factor: number, config?: {ttl?: number}) : Promise<{error:string;success:boolean}>;
|
|
||||||
export function add_root(peer: FluencePeer, node: string, peer_id: string, weight_factor: number, config?: {ttl?: number}) : Promise<{error:string;success:boolean}>;
|
|
||||||
export function add_root(...args: any) {
|
|
||||||
let peer: FluencePeer;
|
|
||||||
let node: any;
|
|
||||||
let peer_id: any;
|
|
||||||
let weight_factor: any;
|
|
||||||
let config: any;
|
|
||||||
if (FluencePeer.isInstance(args[0])) {
|
|
||||||
peer = args[0];
|
|
||||||
node = args[1];
|
|
||||||
peer_id = args[2];
|
|
||||||
weight_factor = args[3];
|
|
||||||
config = args[4];
|
|
||||||
} else {
|
|
||||||
peer = Fluence.getPeer();
|
|
||||||
node = args[0];
|
|
||||||
peer_id = args[1];
|
|
||||||
weight_factor = args[2];
|
|
||||||
config = args[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
let request: RequestFlow;
|
|
||||||
const promise = new Promise<{error:string;success:boolean}>((resolve, reject) => {
|
|
||||||
const r = new RequestFlowBuilder()
|
|
||||||
.disableInjections()
|
|
||||||
.withRawScript(
|
|
||||||
`
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "node") [] node)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "peer_id") [] peer_id)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "weight_factor") [] weight_factor)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(call node ("trust-graph" "add_root") [peer_id weight_factor] result)
|
|
||||||
(seq
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(call %init_peer_id% ("callbackSrv" "response") [result])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3])
|
|
||||||
)
|
|
||||||
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.configHandler((h) => {
|
|
||||||
h.on('getDataSrv', '-relay-', () => {
|
|
||||||
return peer.getStatus().relayPeerId;
|
|
||||||
});
|
|
||||||
h.on('getDataSrv', 'node', () => {return node;});
|
|
||||||
h.on('getDataSrv', 'peer_id', () => {return peer_id;});
|
|
||||||
h.on('getDataSrv', 'weight_factor', () => {return weight_factor;});
|
|
||||||
h.onEvent('callbackSrv', 'response', (args) => {
|
|
||||||
const [res] = args;
|
|
||||||
resolve(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
|
||||||
const [err] = args;
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.handleScriptError(reject)
|
|
||||||
.handleTimeout(() => {
|
|
||||||
reject('Request timed out for add_root');
|
|
||||||
})
|
|
||||||
if(config && config.ttl) {
|
|
||||||
r.withTTL(config.ttl)
|
|
||||||
}
|
|
||||||
request = r.build();
|
|
||||||
});
|
|
||||||
peer.internals.initiateFlow(request!);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function get_weight(node: string, peer_id: string, config?: {ttl?: number}) : Promise<{error:string;peer_id:string;success:boolean;weight:number}>;
|
|
||||||
export function get_weight(peer: FluencePeer, node: string, peer_id: string, config?: {ttl?: number}) : Promise<{error:string;peer_id:string;success:boolean;weight:number}>;
|
|
||||||
export function get_weight(...args: any) {
|
|
||||||
let peer: FluencePeer;
|
|
||||||
let node: any;
|
|
||||||
let peer_id: any;
|
|
||||||
let config: any;
|
|
||||||
if (FluencePeer.isInstance(args[0])) {
|
|
||||||
peer = args[0];
|
|
||||||
node = args[1];
|
|
||||||
peer_id = args[2];
|
|
||||||
config = args[3];
|
|
||||||
} else {
|
|
||||||
peer = Fluence.getPeer();
|
|
||||||
node = args[0];
|
|
||||||
peer_id = args[1];
|
|
||||||
config = args[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
let request: RequestFlow;
|
|
||||||
const promise = new Promise<{error:string;peer_id:string;success:boolean;weight:number}>((resolve, reject) => {
|
|
||||||
const r = new RequestFlowBuilder()
|
|
||||||
.disableInjections()
|
|
||||||
.withRawScript(
|
|
||||||
`
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "node") [] node)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "peer_id") [] peer_id)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(call node ("peer" "timestamp_sec") [] timestamp_sec)
|
|
||||||
(call node ("trust-graph" "get_weight") [peer_id timestamp_sec] result)
|
|
||||||
)
|
|
||||||
(seq
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(call %init_peer_id% ("callbackSrv" "response") [result])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3])
|
|
||||||
)
|
|
||||||
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.configHandler((h) => {
|
|
||||||
h.on('getDataSrv', '-relay-', () => {
|
|
||||||
return peer.getStatus().relayPeerId;
|
|
||||||
});
|
|
||||||
h.on('getDataSrv', 'node', () => {return node;});
|
|
||||||
h.on('getDataSrv', 'peer_id', () => {return peer_id;});
|
|
||||||
h.onEvent('callbackSrv', 'response', (args) => {
|
|
||||||
const [res] = args;
|
|
||||||
resolve(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
|
||||||
const [err] = args;
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.handleScriptError(reject)
|
|
||||||
.handleTimeout(() => {
|
|
||||||
reject('Request timed out for get_weight');
|
|
||||||
})
|
|
||||||
if(config && config.ttl) {
|
|
||||||
r.withTTL(config.ttl)
|
|
||||||
}
|
|
||||||
request = r.build();
|
|
||||||
});
|
|
||||||
peer.internals.initiateFlow(request!);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function get_trust_bytes(node: string, issued_for_peer_id: string, expires_at_sec: number, issued_at_sec: number, config?: {ttl?: number}) : Promise<{error:string;result:number[];success:boolean}>;
|
|
||||||
export function get_trust_bytes(peer: FluencePeer, node: string, issued_for_peer_id: string, expires_at_sec: number, issued_at_sec: number, config?: {ttl?: number}) : Promise<{error:string;result:number[];success:boolean}>;
|
|
||||||
export function get_trust_bytes(...args: any) {
|
|
||||||
let peer: FluencePeer;
|
|
||||||
let node: any;
|
|
||||||
let issued_for_peer_id: any;
|
|
||||||
let expires_at_sec: any;
|
|
||||||
let issued_at_sec: any;
|
|
||||||
let config: any;
|
|
||||||
if (FluencePeer.isInstance(args[0])) {
|
|
||||||
peer = args[0];
|
|
||||||
node = args[1];
|
|
||||||
issued_for_peer_id = args[2];
|
|
||||||
expires_at_sec = args[3];
|
|
||||||
issued_at_sec = args[4];
|
|
||||||
config = args[5];
|
|
||||||
} else {
|
|
||||||
peer = Fluence.getPeer();
|
|
||||||
node = args[0];
|
|
||||||
issued_for_peer_id = args[1];
|
|
||||||
expires_at_sec = args[2];
|
|
||||||
issued_at_sec = args[3];
|
|
||||||
config = args[4];
|
|
||||||
}
|
|
||||||
|
|
||||||
let request: RequestFlow;
|
|
||||||
const promise = new Promise<{error:string;result:number[];success:boolean}>((resolve, reject) => {
|
|
||||||
const r = new RequestFlowBuilder()
|
|
||||||
.disableInjections()
|
|
||||||
.withRawScript(
|
|
||||||
`
|
|
||||||
(xor
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(seq
|
|
||||||
(call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "node") [] node)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "issued_for_peer_id") [] issued_for_peer_id)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "expires_at_sec") [] expires_at_sec)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("getDataSrv" "issued_at_sec") [] issued_at_sec)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(call node ("trust-graph" "get_trust_bytes") [issued_for_peer_id expires_at_sec issued_at_sec] result)
|
|
||||||
(seq
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call -relay- ("op" "noop") [])
|
|
||||||
)
|
|
||||||
(xor
|
|
||||||
(call %init_peer_id% ("callbackSrv" "response") [result])
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3])
|
|
||||||
)
|
|
||||||
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.configHandler((h) => {
|
|
||||||
h.on('getDataSrv', '-relay-', () => {
|
|
||||||
return peer.getStatus().relayPeerId;
|
|
||||||
});
|
|
||||||
h.on('getDataSrv', 'node', () => {return node;});
|
|
||||||
h.on('getDataSrv', 'issued_for_peer_id', () => {return issued_for_peer_id;});
|
|
||||||
h.on('getDataSrv', 'expires_at_sec', () => {return expires_at_sec;});
|
|
||||||
h.on('getDataSrv', 'issued_at_sec', () => {return issued_at_sec;});
|
|
||||||
h.onEvent('callbackSrv', 'response', (args) => {
|
|
||||||
const [res] = args;
|
|
||||||
resolve(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
h.onEvent('errorHandlingSrv', 'error', (args) => {
|
|
||||||
const [err] = args;
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.handleScriptError(reject)
|
|
||||||
.handleTimeout(() => {
|
|
||||||
reject('Request timed out for get_trust_bytes');
|
|
||||||
})
|
|
||||||
if(config && config.ttl) {
|
|
||||||
r.withTTL(config.ttl)
|
|
||||||
}
|
|
||||||
request = r.build();
|
|
||||||
});
|
|
||||||
peer.internals.initiateFlow(request!);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
126
example/index.ts
Normal file
126
example/index.ts
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* 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,
|
||||||
|
timestamp_sec,
|
||||||
|
get_all_certs,
|
||||||
|
get_revoke_bytes,
|
||||||
|
issue_revocation,
|
||||||
|
revoke
|
||||||
|
} from "./generated/export";
|
||||||
|
import { Fluence, KeyPair } from "@fluencelabs/fluence";
|
||||||
|
import { Node } from "@fluencelabs/fluence-network-environment";
|
||||||
|
import assert from "assert";
|
||||||
|
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 add_trust_helper(node: string, issuer_kp: KeyPair, issuer_peer_id: string, issued_for_peer_id: string, expires_at_sec: number, issued_at_sec: number) {
|
||||||
|
let trust_metadata = await get_trust_bytes(node, issued_for_peer_id, expires_at_sec, issued_at_sec);
|
||||||
|
const signed_metadata = await issuer_kp.Libp2pPeerId.privKey.sign(Uint8Array.from(trust_metadata.result));
|
||||||
|
|
||||||
|
let trust = await issue_trust(node, issued_for_peer_id, expires_at_sec, issued_at_sec, Array.from(signed_metadata));
|
||||||
|
console.log("Issued trust %s", trust.trust);
|
||||||
|
|
||||||
|
let result = await verify_trust(node, trust.trust, issuer_peer_id);
|
||||||
|
console.log("Verify trust result: %s", result);
|
||||||
|
|
||||||
|
let result_add = await add_trust(node, trust.trust, issuer_peer_id);
|
||||||
|
console.log("Add trust result: %s", result_add);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function revoke_helper(node: string, issuer_kp: KeyPair, revoked_by_peer_id: string, revoked_peer_id: string, revoked_at_sec: number) {
|
||||||
|
let trust_metadata = await get_revoke_bytes(node, revoked_peer_id, revoked_at_sec);
|
||||||
|
const signed_metadata = await issuer_kp.Libp2pPeerId.privKey.sign(Uint8Array.from(trust_metadata.result));
|
||||||
|
|
||||||
|
let revocation = await issue_revocation(node, revoked_peer_id, revoked_by_peer_id, revoked_at_sec, Array.from(signed_metadata));
|
||||||
|
console.log("Issued revocation %s", revocation.revoke);
|
||||||
|
|
||||||
|
let result_add = await revoke(node, revocation.revoke);
|
||||||
|
console.log("Revoke result: %s", result_add);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log("📘 Will connect to local nodes");
|
||||||
|
// key from local-network/builtins_secret_key.ed25519 to connect as builtins owner
|
||||||
|
let sk = bs58.decode("5FwE32bDcphFzuMca7Y2qW1gdR64fTBYoRNvD4MLE1hecDGhCMQGKn8aseMr5wRo4Xo2CRFdrEAawUNLYkgQD78K").slice(0, 32); // first 32 bytes - secret key, second - public key
|
||||||
|
let builtins_keypair = await KeyPair.fromEd25519SK(sk);
|
||||||
|
await Fluence.start({ connectTo: local[0], KeyPair: builtins_keypair});
|
||||||
|
console.log(
|
||||||
|
"📗 created a fluence peer %s with relay %s",
|
||||||
|
Fluence.getStatus().peerId,
|
||||||
|
Fluence.getStatus().relayPeerId
|
||||||
|
);
|
||||||
|
const issued_timestamp_sec = await timestamp_sec(local[0].peerId);
|
||||||
|
const expires_at_sec = issued_timestamp_sec + 999999999;
|
||||||
|
const issuer_kp = await KeyPair.fromEd25519SK(bs58.decode("29Apzfedhw2Jxh94Jj4rNSmavQ1TkNe8ALYRA7bMegobwp423aLrURxLk32WtXgXHDqoSz7GAT9fQfoMhVd1e5Ww"));
|
||||||
|
|
||||||
|
let add_root_result = await add_root(local[0].peerId, local[0].peerId, 2);
|
||||||
|
console.log("Add root weight result: %s", add_root_result);
|
||||||
|
|
||||||
|
// add root trust
|
||||||
|
await add_trust_helper(local[0].peerId, issuer_kp, local[0].peerId, local[0].peerId, expires_at_sec, issued_timestamp_sec);
|
||||||
|
|
||||||
|
let root_weight_result = await get_weight(local[0].peerId, local[0].peerId);
|
||||||
|
console.log("Root weight: %s", root_weight_result);
|
||||||
|
|
||||||
|
// issue trust by local[0].peerId for local[1].peerId and add to tg
|
||||||
|
await add_trust_helper(local[0].peerId, issuer_kp, local[0].peerId, local[1].peerId, expires_at_sec, issued_timestamp_sec);
|
||||||
|
let weight_result = await get_weight(local[0].peerId, local[1].peerId);
|
||||||
|
console.log("Trust weight: %s", weight_result);
|
||||||
|
|
||||||
|
assert(root_weight_result.weight / 2 === weight_result.weight);
|
||||||
|
|
||||||
|
let certs = await get_all_certs(local[0].peerId, local[1].peerId);
|
||||||
|
console.log("Certs: %s", JSON.stringify(certs.certificates));
|
||||||
|
assert(certs.certificates.length === 1);
|
||||||
|
|
||||||
|
// wait to create revoke after trust (because timestamp in secs)
|
||||||
|
await new Promise(f => setTimeout(f, 1000));
|
||||||
|
|
||||||
|
// revoke local[1].peerId trust
|
||||||
|
await revoke_helper(local[0].peerId, issuer_kp, local[0].peerId, local[1].peerId, await timestamp_sec(local[0].peerId));
|
||||||
|
|
||||||
|
let empty_certs = await get_all_certs(local[0].peerId, local[1].peerId);
|
||||||
|
assert(empty_certs.certificates.length === 0);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
|
.then(() => process.exit(0))
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
4129
example/package-lock.json
generated
Normal file
4129
example/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
example/package.json
Normal file
26
example/package.json
Normal 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.4.1-240",
|
||||||
|
"@fluencelabs/aqua-lib": "0.2.0",
|
||||||
|
"@fluencelabs/fluence": "0.14.3",
|
||||||
|
"@fluencelabs/fluence-network-environment": "^1.0.10",
|
||||||
|
"@fluencelabs/trust-graph": "file:../aqua",
|
||||||
|
"bs58": "^4.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^4.4.3"
|
||||||
|
}
|
||||||
|
}
|
69
example/tsconfig.json
Normal file
69
example/tsconfig.json
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||||
|
|
||||||
|
/* Basic Options */
|
||||||
|
// "incremental": true, /* Enable incremental compilation */
|
||||||
|
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
||||||
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||||
|
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||||
|
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
|
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||||
|
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||||
|
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||||
|
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||||
|
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||||
|
"outDir": "./dist", /* Redirect output structure to the directory. */
|
||||||
|
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
|
// "composite": true, /* Enable project compilation */
|
||||||
|
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||||
|
// "removeComments": true, /* Do not emit comments to output. */
|
||||||
|
// "noEmit": true, /* Do not emit outputs. */
|
||||||
|
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||||
|
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||||
|
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||||
|
|
||||||
|
/* Strict Type-Checking Options */
|
||||||
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
|
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
|
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||||
|
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||||
|
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||||
|
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||||
|
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||||
|
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||||
|
|
||||||
|
/* Additional Checks */
|
||||||
|
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||||
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
|
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
|
||||||
|
/* Module Resolution Options */
|
||||||
|
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
|
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||||
|
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||||
|
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
|
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
|
||||||
|
/* Source Map Options */
|
||||||
|
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||||
|
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||||
|
|
||||||
|
/* Experimental Options */
|
||||||
|
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
|
|
||||||
|
/* Advanced Options */
|
||||||
|
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||||
|
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 12,
|
|
||||||
sourceType: 'module', // Allows for the use of imports
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es2021: true,
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'airbnb-base',
|
|
||||||
'plugin:@typescript-eslint/eslint-recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
// Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
|
|
||||||
'plugin:prettier/recommended',
|
|
||||||
],
|
|
||||||
plugins: ['@typescript-eslint', 'prettier'],
|
|
||||||
rules: {},
|
|
||||||
settings: {
|
|
||||||
'import/extensions': ['.js', '.ts'],
|
|
||||||
},
|
|
||||||
};
|
|
17
js/.gitignore
vendored
17
js/.gitignore
vendored
@ -1,17 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
bundle/
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
/dist/
|
|
@ -1,12 +0,0 @@
|
|||||||
.idea
|
|
||||||
.gitignore
|
|
||||||
node_modules
|
|
||||||
types
|
|
||||||
|
|
||||||
src/
|
|
||||||
|
|
||||||
tsconfig.json
|
|
||||||
webpack.config.js
|
|
||||||
|
|
||||||
bundle
|
|
||||||
pkg
|
|
@ -1,8 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
semi: true,
|
|
||||||
trailingComma: "all",
|
|
||||||
singleQuote: true,
|
|
||||||
printWidth: 120,
|
|
||||||
tabWidth: 4,
|
|
||||||
useTabs: false
|
|
||||||
};
|
|
@ -1,4 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
preset: 'ts-jest',
|
|
||||||
testEnvironment: 'node',
|
|
||||||
};
|
|
7295
js/package-lock.json
generated
7295
js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@fluencelabs/trust-graph",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"description": "Trust graph implementation",
|
|
||||||
"main": "./dist/index.js",
|
|
||||||
"typings": "./dist/index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"test": "jest --watch",
|
|
||||||
"test:all": "jest",
|
|
||||||
"build": "tsc"
|
|
||||||
},
|
|
||||||
"repository": "https://github.com/fluencelabs/fluence-js",
|
|
||||||
"author": "Fluence Labs",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"bs58": "4.0.1",
|
|
||||||
"libp2p-crypto": "0.19.0",
|
|
||||||
"loglevel": "1.7.0",
|
|
||||||
"peer-id": "0.13.12"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/base64-js": "1.2.5",
|
|
||||||
"@types/bs58": "4.0.1",
|
|
||||||
"assert": "2.0.0",
|
|
||||||
"libp2p-ts": "https://github.com/ChainSafe/libp2p-ts.git#fca072c9764436ef71f974a211bb1befae432575",
|
|
||||||
"@types/node": "14.14.28",
|
|
||||||
"mocha-loader": "^5.1.5",
|
|
||||||
"ts-loader": "7.0.5",
|
|
||||||
"ts-mocha": "8.0.0",
|
|
||||||
"typescript": "^3.9.5",
|
|
||||||
"jest": "^26.6.3",
|
|
||||||
"@types/jest": "^26.0.20",
|
|
||||||
"ts-jest": "^26.5.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
|
|
||||||
import {certificateFromString, certificateToString} from "../certificate";
|
|
||||||
|
|
||||||
describe('Typescript usage suite', () => {
|
|
||||||
|
|
||||||
it('should serialize and deserialize certificate correctly', async function () {
|
|
||||||
let cert = `11
|
|
||||||
1111
|
|
||||||
5566Dn4ZXXbBK5LJdUsE7L3pG9qdAzdPY47adjzkhEx9
|
|
||||||
3HNXpW2cLdqXzf4jz5EhsGEBFkWzuVdBCyxzJUZu2WPVU7kpzPjatcqvdJMjTtcycVAdaV5qh2fCGphSmw8UMBkr
|
|
||||||
158981172690500
|
|
||||||
1589974723504
|
|
||||||
2EvoZAZaGjKWFVdr36F1jphQ5cW7eK3yM16mqEHwQyr7
|
|
||||||
4UAJQWzB3nTchBtwARHAhsn7wjdYtqUHojps9xV6JkuLENV8KRiWM3BhQByx5KijumkaNjr7MhHjouLawmiN1A4d
|
|
||||||
1590061123504
|
|
||||||
1589974723504`;
|
|
||||||
|
|
||||||
let deser = await certificateFromString(cert);
|
|
||||||
let ser = certificateToString(deser);
|
|
||||||
|
|
||||||
expect(ser).toEqual(cert);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO implement this test when `trust-graph-js - fluence-js - fluence node` chain will be available
|
|
||||||
// export async function testCerts() {
|
|
||||||
// const key1 = await generatePeerId();
|
|
||||||
// const key2 = await generatePeerId();
|
|
||||||
//
|
|
||||||
// // connect to two different nodes
|
|
||||||
// const cl1 = new FluenceClientImpl(key1);
|
|
||||||
// const cl2 = new FluenceClientImpl(key2);
|
|
||||||
//
|
|
||||||
// await cl1.connect('/dns4/134.209.186.43/tcp/9003/ws/p2p/12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb');
|
|
||||||
// await cl2.connect('/ip4/134.209.186.43/tcp/9002/ws/p2p/12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er');
|
|
||||||
//
|
|
||||||
// let trustGraph1 = new TrustGraph(/* cl1 */);
|
|
||||||
// let trustGraph2 = new TrustGraph(/* cl2 */);
|
|
||||||
//
|
|
||||||
// let issuedAt = new Date();
|
|
||||||
// let expiresAt = new Date();
|
|
||||||
// // certificate expires after one day
|
|
||||||
// expiresAt.setDate(new Date().getDate() + 1);
|
|
||||||
//
|
|
||||||
// // create root certificate for key1 and extend it with key2
|
|
||||||
// let rootCert = await nodeRootCert(key1);
|
|
||||||
// let extended = await issue(key1, key2, rootCert, expiresAt.getTime(), issuedAt.getTime());
|
|
||||||
//
|
|
||||||
// // publish certificates to Fluence network
|
|
||||||
// await trustGraph1.publishCertificates(key2.toB58String(), [extended]);
|
|
||||||
//
|
|
||||||
// // get certificates from network
|
|
||||||
// let certs = await trustGraph2.getCertificates(key2.toB58String());
|
|
||||||
//
|
|
||||||
// // root certificate could be different because nodes save trusts with bigger `expiresAt` date and less `issuedAt` date
|
|
||||||
// expect(certs[0].chain[1].issuedFor.toB58String()).to.be.equal(extended.chain[1].issuedFor.toB58String());
|
|
||||||
// expect(certs[0].chain[1].signature).to.be.equal(extended.chain[1].signature);
|
|
||||||
// expect(certs[0].chain[1].expiresAt).to.be.equal(extended.chain[1].expiresAt);
|
|
||||||
// expect(certs[0].chain[1].issuedAt).to.be.equal(extended.chain[1].issuedAt);
|
|
||||||
//
|
|
||||||
// await cl1.disconnect();
|
|
||||||
// await cl2.disconnect();
|
|
||||||
// }
|
|
@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Fluence Labs Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { createTrust, Trust, trustFromString, trustToString } from './trust';
|
|
||||||
import * as PeerId from 'peer-id';
|
|
||||||
|
|
||||||
const FORMAT = '11';
|
|
||||||
const VERSION = '1111';
|
|
||||||
|
|
||||||
// TODO verify certificate
|
|
||||||
// Chain of trusts started from self-signed root trust.
|
|
||||||
export interface Certificate {
|
|
||||||
chain: Trust[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function certificateToString(cert: Certificate): string {
|
|
||||||
let certStr = cert.chain.map((t) => trustToString(t)).join('\n');
|
|
||||||
return `${FORMAT}\n${VERSION}\n${certStr}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function certificateFromString(str: string): Promise<Certificate> {
|
|
||||||
let lines = str.split('\n');
|
|
||||||
// last line could be empty
|
|
||||||
if (!lines[lines.length - 1]) {
|
|
||||||
lines.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO do match different formats and versions
|
|
||||||
let _format = lines[0];
|
|
||||||
let _version = lines[1];
|
|
||||||
|
|
||||||
// every trust is 4 lines, certificate lines number without format and version should be divided by 4
|
|
||||||
if ((lines.length - 2) % 4 !== 0) {
|
|
||||||
throw Error('Incorrect format of the certificate:\n' + str);
|
|
||||||
}
|
|
||||||
|
|
||||||
let chain: Trust[] = [];
|
|
||||||
|
|
||||||
let i;
|
|
||||||
for (i = 2; i < lines.length; i = i + 4) {
|
|
||||||
chain.push(await trustFromString(lines[i], lines[i + 1], lines[i + 2], lines[i + 3]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return { chain };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates new certificate with root trust (self-signed public key) from a key pair.
|
|
||||||
export async function issueRoot(
|
|
||||||
issuedBy: PeerId,
|
|
||||||
forPk: PeerId,
|
|
||||||
expiresAt: number,
|
|
||||||
issuedAt: number,
|
|
||||||
): Promise<Certificate> {
|
|
||||||
if (expiresAt < issuedAt) {
|
|
||||||
throw Error('Expiration time should be greater then issued time.');
|
|
||||||
}
|
|
||||||
|
|
||||||
let maxDate = new Date(158981172690500).getTime();
|
|
||||||
|
|
||||||
let rootTrust = await createTrust(issuedBy, issuedBy, maxDate, issuedAt);
|
|
||||||
let trust = await createTrust(forPk, issuedBy, expiresAt, issuedAt);
|
|
||||||
let chain = [rootTrust, trust];
|
|
||||||
|
|
||||||
return {
|
|
||||||
chain: chain,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a new trust into chain of trust in certificate.
|
|
||||||
export async function issue(
|
|
||||||
issuedBy: PeerId,
|
|
||||||
forPk: PeerId,
|
|
||||||
extendCert: Certificate,
|
|
||||||
expiresAt: number,
|
|
||||||
issuedAt: number,
|
|
||||||
): Promise<Certificate> {
|
|
||||||
if (expiresAt < issuedAt) {
|
|
||||||
throw Error('Expiration time should be greater then issued time.');
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastTrust = extendCert.chain[extendCert.chain.length - 1];
|
|
||||||
|
|
||||||
if (lastTrust.issuedFor !== issuedBy) {
|
|
||||||
throw Error('`issuedFor` should be equal to `issuedBy` in the last trust of the chain.');
|
|
||||||
}
|
|
||||||
|
|
||||||
let trust = await createTrust(forPk, issuedBy, expiresAt, issuedAt);
|
|
||||||
let chain = [...extendCert.chain];
|
|
||||||
chain.push(trust);
|
|
||||||
|
|
||||||
return {
|
|
||||||
chain: chain,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Fluence Labs Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from './certificate'
|
|
||||||
export * from './trust'
|
|
||||||
export * from './trust_graph'
|
|
||||||
export * from './misc'
|
|
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Fluence Labs Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as PeerId from 'peer-id';
|
|
||||||
import { keys } from 'libp2p-crypto';
|
|
||||||
import { Certificate, issueRoot } from './certificate';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate root certificate with one of the Fluence trusted key for one day.
|
|
||||||
*/
|
|
||||||
export async function nodeRootCert(issuedFor: PeerId): Promise<Certificate> {
|
|
||||||
// prettier-ignore
|
|
||||||
let seed = [46, 188, 245, 171, 145, 73, 40, 24, 52, 233, 215, 163, 54, 26, 31, 221, 159, 179, 126, 106, 27, 199, 189, 194, 80, 133, 235, 42, 42, 247, 80, 201];
|
|
||||||
|
|
||||||
let privateK = await keys.generateKeyPairFromSeed('Ed25519', Uint8Array.from(seed), 256);
|
|
||||||
let peerId = await PeerId.createFromPrivKey(Buffer.from(privateK.bytes));
|
|
||||||
|
|
||||||
let issuedAt = new Date();
|
|
||||||
let expiresAt = new Date();
|
|
||||||
expiresAt.setDate(new Date().getDate() + 1);
|
|
||||||
|
|
||||||
return await issueRoot(peerId, issuedFor, expiresAt.getTime(), issuedAt.getTime());
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Fluence Labs Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as PeerId from 'peer-id';
|
|
||||||
import { decode, encode } from 'bs58';
|
|
||||||
import crypto from 'libp2p-crypto';
|
|
||||||
const ed25519 = crypto.keys.supportedKeys.ed25519;
|
|
||||||
|
|
||||||
// One element in chain of trust in a certificate.
|
|
||||||
export interface Trust {
|
|
||||||
issuedFor: PeerId;
|
|
||||||
expiresAt: number;
|
|
||||||
signature: string;
|
|
||||||
issuedAt: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trustToString(trust: Trust): string {
|
|
||||||
return `${encode(trust.issuedFor.pubKey.marshal())}\n${trust.signature}\n${trust.expiresAt}\n${trust.issuedAt}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function trustFromString(
|
|
||||||
issuedFor: string,
|
|
||||||
signature: string,
|
|
||||||
expiresAt: string,
|
|
||||||
issuedAt: string,
|
|
||||||
): Promise<Trust> {
|
|
||||||
let pubKey = ed25519.unmarshalEd25519PublicKey(decode(issuedFor));
|
|
||||||
let peerId = await PeerId.createFromPubKey(Buffer.from(pubKey.bytes));
|
|
||||||
|
|
||||||
return {
|
|
||||||
issuedFor: peerId,
|
|
||||||
signature: signature,
|
|
||||||
expiresAt: parseInt(expiresAt),
|
|
||||||
issuedAt: parseInt(issuedAt),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createTrust(
|
|
||||||
forPk: PeerId,
|
|
||||||
issuedBy: PeerId,
|
|
||||||
expiresAt: number,
|
|
||||||
issuedAt: number,
|
|
||||||
): Promise<Trust> {
|
|
||||||
let bytes = toSignMessage(forPk, expiresAt, issuedAt);
|
|
||||||
let signature = await issuedBy.privKey.sign(Buffer.from(bytes));
|
|
||||||
let signatureStr = encode(signature);
|
|
||||||
|
|
||||||
return {
|
|
||||||
issuedFor: forPk,
|
|
||||||
expiresAt: expiresAt,
|
|
||||||
signature: signatureStr,
|
|
||||||
issuedAt: issuedAt,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function toSignMessage(pk: PeerId, expiresAt: number, issuedAt: number): Uint8Array {
|
|
||||||
let bytes = new Uint8Array(48);
|
|
||||||
let pkEncoded = pk.pubKey.marshal();
|
|
||||||
|
|
||||||
bytes.set(pkEncoded, 0);
|
|
||||||
bytes.set(numToArray(expiresAt), 32);
|
|
||||||
bytes.set(numToArray(issuedAt), 40);
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
function numToArray(n: number): number[] {
|
|
||||||
let byteArray = [0, 0, 0, 0, 0, 0, 0, 0];
|
|
||||||
|
|
||||||
for (let index = 0; index < byteArray.length; index++) {
|
|
||||||
let byte = n & 0xff;
|
|
||||||
byteArray[index] = byte;
|
|
||||||
n = (n - byte) / 256;
|
|
||||||
}
|
|
||||||
|
|
||||||
return byteArray;
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Fluence Labs Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Certificate, certificateFromString, certificateToString } from './certificate';
|
|
||||||
import * as log from 'loglevel';
|
|
||||||
|
|
||||||
// TODO inherit this with FluenceClient in fluence-js
|
|
||||||
// The client to interact with the Fluence trust graph API
|
|
||||||
export class TrustGraph {
|
|
||||||
//client: FluenceClient;
|
|
||||||
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
// Publish certificate to Fluence network. It will be published in Kademlia neighbourhood by `peerId` key.
|
|
||||||
async publishCertificates(peerId: string, certs: Certificate[]) {
|
|
||||||
let certsStr = [];
|
|
||||||
for (let cert of certs) {
|
|
||||||
certsStr.push(await certificateToString(cert));
|
|
||||||
}
|
|
||||||
// TODO inherit this with FluenceClient in fluence-js
|
|
||||||
throw new Error("unimplemented")
|
|
||||||
/*let response = await this.client.callPeer("add_certificates", {
|
|
||||||
certificates: certsStr,
|
|
||||||
peer_id: peerId
|
|
||||||
});*/
|
|
||||||
let response: any = {};
|
|
||||||
|
|
||||||
if (response.reason) {
|
|
||||||
throw Error(response.reason);
|
|
||||||
} else if (response.status) {
|
|
||||||
return response.status;
|
|
||||||
} else {
|
|
||||||
throw Error(
|
|
||||||
`Unexpected response: ${response}. Should be 'status' field for a success response or 'reason' field for an error.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get certificates that stores in Kademlia neighbourhood by `peerId` key.
|
|
||||||
async getCertificates(peerId: string): Promise<Certificate[]> {
|
|
||||||
let resp: any = {};
|
|
||||||
// TODO inherit this with FluenceClient in fluence-js
|
|
||||||
throw new Error("unimplemented")
|
|
||||||
/*let resp = await this.client.callPeer("certificates", {
|
|
||||||
peer_id: peerId
|
|
||||||
});*/
|
|
||||||
|
|
||||||
let certificatesRaw = resp.certificates;
|
|
||||||
|
|
||||||
if (!(certificatesRaw && Array.isArray(certificatesRaw))) {
|
|
||||||
log.error(Array.isArray(certificatesRaw));
|
|
||||||
throw Error('Unexpected. Certificates should be presented in the response as an array.');
|
|
||||||
}
|
|
||||||
|
|
||||||
let certs = [];
|
|
||||||
for (let cert of certificatesRaw) {
|
|
||||||
certs.push(await certificateFromString(cert));
|
|
||||||
}
|
|
||||||
|
|
||||||
return certs;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"typeRoots": [
|
|
||||||
"./node_modules/@types",
|
|
||||||
"./node_modules/libp2p-ts/types",
|
|
||||||
"./types"
|
|
||||||
],
|
|
||||||
"outDir": "./dist/",
|
|
||||||
"baseUrl": ".",
|
|
||||||
"sourceMap": true,
|
|
||||||
"inlineSources": true,
|
|
||||||
"strictFunctionTypes": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"pretty": true,
|
|
||||||
"target": "ES5",
|
|
||||||
"module": "commonjs",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"declaration": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"declarationMap": true,
|
|
||||||
"strict": true,
|
|
||||||
"noImplicitAny": false,
|
|
||||||
"alwaysStrict": true,
|
|
||||||
"noImplicitThis": true,
|
|
||||||
"strictNullChecks": false
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"node_modules",
|
|
||||||
"dist",
|
|
||||||
"bundle",
|
|
||||||
"src/__test__"
|
|
||||||
],
|
|
||||||
"include": ["src/**/*"]
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fluence-keypair"
|
name = "fluence-keypair"
|
||||||
version = "0.4.0"
|
version = "0.5.1"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "identity"
|
description = "identity"
|
||||||
|
@ -19,13 +19,13 @@
|
|||||||
// DEALINGS IN THE SOFTWARE.
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
//! Ed25519 keys.
|
//! Ed25519 keys.
|
||||||
use crate::error::{DecodingError, SigningError};
|
use crate::error::{DecodingError, SigningError, VerificationError};
|
||||||
|
use core::fmt;
|
||||||
use ed25519_dalek::{self as ed25519, Signer as _, Verifier as _};
|
use ed25519_dalek::{self as ed25519, Signer as _, Verifier as _};
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
use core::fmt;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// An Ed25519 keypair.
|
/// An Ed25519 keypair.
|
||||||
pub struct Keypair(ed25519::Keypair);
|
pub struct Keypair(ed25519::Keypair);
|
||||||
@ -73,7 +73,9 @@ impl Keypair {
|
|||||||
|
|
||||||
impl fmt::Debug for Keypair {
|
impl fmt::Debug for Keypair {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Keypair").field("public", &self.0.public).finish()
|
f.debug_struct("Keypair")
|
||||||
|
.field("public", &self.0.public)
|
||||||
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +83,8 @@ impl Clone for Keypair {
|
|||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
let mut sk_bytes = self.0.secret.to_bytes();
|
let mut sk_bytes = self.0.secret.to_bytes();
|
||||||
let secret = SecretKey::from_bytes(&mut sk_bytes)
|
let secret = SecretKey::from_bytes(&mut sk_bytes)
|
||||||
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k").0;
|
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
|
||||||
|
.0;
|
||||||
let public = ed25519::PublicKey::from_bytes(&self.0.public.to_bytes())
|
let public = ed25519::PublicKey::from_bytes(&self.0.public.to_bytes())
|
||||||
.expect("ed25519::PublicKey::from_bytes(to_bytes(k)) != k");
|
.expect("ed25519::PublicKey::from_bytes(to_bytes(k)) != k");
|
||||||
Keypair(ed25519::Keypair { secret, public })
|
Keypair(ed25519::Keypair { secret, public })
|
||||||
@ -107,7 +110,10 @@ impl From<SecretKey> for Keypair {
|
|||||||
fn from(sk: SecretKey) -> Self {
|
fn from(sk: SecretKey) -> Self {
|
||||||
let secret: ed25519::ExpandedSecretKey = (&sk.0).into();
|
let secret: ed25519::ExpandedSecretKey = (&sk.0).into();
|
||||||
let public = ed25519::PublicKey::from(&secret);
|
let public = ed25519::PublicKey::from(&secret);
|
||||||
Keypair(ed25519::Keypair { secret: sk.0, public })
|
Keypair(ed25519::Keypair {
|
||||||
|
secret: sk.0,
|
||||||
|
public,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,8 +123,16 @@ pub struct PublicKey(ed25519::PublicKey);
|
|||||||
|
|
||||||
impl PublicKey {
|
impl PublicKey {
|
||||||
/// Verify the Ed25519 signature on a message using the public key.
|
/// Verify the Ed25519 signature on a message using the public key.
|
||||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<(), SigningError> {
|
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<(), VerificationError> {
|
||||||
ed25519::Signature::try_from(sig).and_then(|s| self.0.verify(msg, &s)).map_err(SigningError::Ed25519)
|
ed25519::Signature::try_from(sig)
|
||||||
|
.and_then(|s| self.0.verify(msg, &s))
|
||||||
|
.map_err(|e| {
|
||||||
|
VerificationError::Ed25519(
|
||||||
|
e,
|
||||||
|
bs58::encode(sig).into_string(),
|
||||||
|
bs58::encode(self.0.as_bytes()).into_string(),
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the public key into a byte array in compressed form, i.e.
|
/// Encode the public key into a byte array in compressed form, i.e.
|
||||||
@ -148,8 +162,7 @@ impl AsRef<[u8]> for SecretKey {
|
|||||||
impl Clone for SecretKey {
|
impl Clone for SecretKey {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
let mut sk_bytes = self.0.to_bytes();
|
let mut sk_bytes = self.0.to_bytes();
|
||||||
Self::from_bytes(&mut sk_bytes)
|
Self::from_bytes(&mut sk_bytes).expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
|
||||||
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,8 +177,11 @@ impl SecretKey {
|
|||||||
pub fn generate() -> Self {
|
pub fn generate() -> Self {
|
||||||
let mut bytes = [0u8; 32];
|
let mut bytes = [0u8; 32];
|
||||||
rand::thread_rng().fill_bytes(&mut bytes);
|
rand::thread_rng().fill_bytes(&mut bytes);
|
||||||
SecretKey(ed25519::SecretKey::from_bytes(&bytes)
|
SecretKey(
|
||||||
.expect("this returns `Err` only if the length is wrong; the length is correct; qed"))
|
ed25519::SecretKey::from_bytes(&bytes).expect(
|
||||||
|
"this returns `Err` only if the length is wrong; the length is correct; qed",
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an Ed25519 secret key from a byte slice, zeroing the input on success.
|
/// Create an Ed25519 secret key from a byte slice, zeroing the input on success.
|
||||||
@ -173,8 +189,7 @@ impl SecretKey {
|
|||||||
/// returned.
|
/// returned.
|
||||||
pub fn from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result<Self, DecodingError> {
|
pub fn from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result<Self, DecodingError> {
|
||||||
let sk_bytes = sk_bytes.as_mut();
|
let sk_bytes = sk_bytes.as_mut();
|
||||||
let secret = ed25519::SecretKey::from_bytes(&*sk_bytes)
|
let secret = ed25519::SecretKey::from_bytes(&*sk_bytes).map_err(DecodingError::Ed25519)?;
|
||||||
.map_err(DecodingError::Ed25519)?;
|
|
||||||
sk_bytes.zeroize();
|
sk_bytes.zeroize();
|
||||||
Ok(SecretKey(secret))
|
Ok(SecretKey(secret))
|
||||||
}
|
}
|
||||||
@ -189,9 +204,7 @@ mod tests {
|
|||||||
use quickcheck::*;
|
use quickcheck::*;
|
||||||
|
|
||||||
fn eq_keypairs(kp1: &Keypair, kp2: &Keypair) -> bool {
|
fn eq_keypairs(kp1: &Keypair, kp2: &Keypair) -> bool {
|
||||||
kp1.public() == kp2.public()
|
kp1.public() == kp2.public() && kp1.0.secret.as_bytes() == kp2.0.secret.as_bytes()
|
||||||
&&
|
|
||||||
kp1.0.secret.as_bytes() == kp2.0.secret.as_bytes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -200,9 +213,7 @@ mod tests {
|
|||||||
let kp1 = Keypair::generate();
|
let kp1 = Keypair::generate();
|
||||||
let mut kp1_enc = kp1.encode();
|
let mut kp1_enc = kp1.encode();
|
||||||
let kp2 = Keypair::decode(&mut kp1_enc).unwrap();
|
let kp2 = Keypair::decode(&mut kp1_enc).unwrap();
|
||||||
eq_keypairs(&kp1, &kp2)
|
eq_keypairs(&kp1, &kp2) && kp1_enc.iter().all(|b| *b == 0)
|
||||||
&&
|
|
||||||
kp1_enc.iter().all(|b| *b == 0)
|
|
||||||
}
|
}
|
||||||
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
|
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
|
||||||
}
|
}
|
||||||
@ -213,9 +224,7 @@ mod tests {
|
|||||||
let kp1 = Keypair::generate();
|
let kp1 = Keypair::generate();
|
||||||
let mut sk = kp1.0.secret.to_bytes();
|
let mut sk = kp1.0.secret.to_bytes();
|
||||||
let kp2 = Keypair::from(SecretKey::from_bytes(&mut sk).unwrap());
|
let kp2 = Keypair::from(SecretKey::from_bytes(&mut sk).unwrap());
|
||||||
eq_keypairs(&kp1, &kp2)
|
eq_keypairs(&kp1, &kp2) && sk == [0u8; 32]
|
||||||
&&
|
|
||||||
sk == [0u8; 32]
|
|
||||||
}
|
}
|
||||||
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
|
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ pub enum DecodingError {
|
|||||||
Ed25519(
|
Ed25519(
|
||||||
#[from]
|
#[from]
|
||||||
#[source]
|
#[source]
|
||||||
ed25519_dalek::ed25519::Error
|
ed25519_dalek::ed25519::Error,
|
||||||
),
|
),
|
||||||
#[error("Failed to decode with RSA")]
|
#[error("Failed to decode with RSA")]
|
||||||
Rsa,
|
Rsa,
|
||||||
@ -49,6 +49,8 @@ pub enum DecodingError {
|
|||||||
Base58DecodeError(#[source] bs58::decode::Error),
|
Base58DecodeError(#[source] bs58::decode::Error),
|
||||||
#[error("Raw signature decoding failed: type {0} not supported")]
|
#[error("Raw signature decoding failed: type {0} not supported")]
|
||||||
RawSignatureUnsupportedType(String),
|
RawSignatureUnsupportedType(String),
|
||||||
|
#[error("public key is not inlined in peer id: {0}")]
|
||||||
|
PublicKeyNotInlined(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error during signing of a message.
|
/// An error during signing of a message.
|
||||||
@ -58,7 +60,7 @@ pub enum SigningError {
|
|||||||
Ed25519(
|
Ed25519(
|
||||||
#[from]
|
#[from]
|
||||||
#[source]
|
#[source]
|
||||||
ed25519_dalek::ed25519::Error
|
ed25519_dalek::ed25519::Error,
|
||||||
),
|
),
|
||||||
#[error("Failed to sign with RSA")]
|
#[error("Failed to sign with RSA")]
|
||||||
Rsa,
|
Rsa,
|
||||||
@ -66,6 +68,20 @@ pub enum SigningError {
|
|||||||
Secp256k1(
|
Secp256k1(
|
||||||
#[from]
|
#[from]
|
||||||
#[source]
|
#[source]
|
||||||
secp256k1::Error
|
secp256k1::Error,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error during verification of a message.
|
||||||
|
#[derive(ThisError, Debug)]
|
||||||
|
pub enum VerificationError {
|
||||||
|
#[error("Failed to verify signature {1} with {2} ed25519 public key: {0}")]
|
||||||
|
Ed25519(#[source] ed25519_dalek::ed25519::Error, String, String),
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[error("Failed to verify signature {1} with {2} RSA public key: {0}")]
|
||||||
|
Rsa(#[source] ring::error::Unspecified, String, String),
|
||||||
|
|
||||||
|
#[error("Failed to verify signature {1} with {2} secp256k1 public key: {0}")]
|
||||||
|
Secp256k1(#[source] secp256k1::Error, String, String),
|
||||||
|
}
|
||||||
|
@ -20,15 +20,15 @@
|
|||||||
|
|
||||||
//! A node's network identity keys.
|
//! A node's network identity keys.
|
||||||
use crate::ed25519;
|
use crate::ed25519;
|
||||||
|
use crate::error::{DecodingError, Error, SigningError, VerificationError};
|
||||||
|
use crate::public_key::PublicKey;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use crate::rsa;
|
use crate::rsa;
|
||||||
use crate::secp256k1;
|
use crate::secp256k1;
|
||||||
use crate::public_key::PublicKey;
|
|
||||||
use crate::signature::Signature;
|
use crate::signature::Signature;
|
||||||
use crate::error::{Error, DecodingError, SigningError};
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
use libp2p_core::PeerId;
|
use libp2p_core::PeerId;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// Identity keypair of a node.
|
/// Identity keypair of a node.
|
||||||
///
|
///
|
||||||
@ -48,7 +48,6 @@ use libp2p_core::PeerId;
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
|
||||||
|
|
||||||
pub enum KeyFormat {
|
pub enum KeyFormat {
|
||||||
Ed25519,
|
Ed25519,
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@ -66,7 +65,7 @@ impl FromStr for KeyFormat {
|
|||||||
"secp256k1" => Ok(KeyFormat::Secp256k1),
|
"secp256k1" => Ok(KeyFormat::Secp256k1),
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
"rsa" => Ok(KeyFormat::Rsa),
|
"rsa" => Ok(KeyFormat::Rsa),
|
||||||
_ => Err(Error::InvalidKeyFormat(s.to_string()))
|
_ => Err(Error::InvalidKeyFormat(s.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,7 +79,7 @@ impl TryFrom<u8> for KeyFormat {
|
|||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
1 => Ok(KeyFormat::Rsa),
|
1 => Ok(KeyFormat::Rsa),
|
||||||
2 => Ok(KeyFormat::Secp256k1),
|
2 => Ok(KeyFormat::Secp256k1),
|
||||||
_ => Err(DecodingError::InvalidTypeByte)
|
_ => Err(DecodingError::InvalidTypeByte),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,6 +95,16 @@ impl From<KeyFormat> for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<KeyFormat> for String {
|
||||||
|
fn from(kf: KeyFormat) -> Self {
|
||||||
|
match kf {
|
||||||
|
KeyFormat::Ed25519 => "ed25519".to_string(),
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
KeyFormat::Rsa => "rsa".to_string(),
|
||||||
|
KeyFormat::Secp256k1 => "secp256k1".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum KeyPair {
|
pub enum KeyPair {
|
||||||
/// An Ed25519 keypair.
|
/// An Ed25519 keypair.
|
||||||
@ -153,7 +162,9 @@ impl KeyPair {
|
|||||||
Ed25519(ref pair) => Ok(Signature::Ed25519(ed25519::Signature(pair.sign(msg)?))),
|
Ed25519(ref pair) => Ok(Signature::Ed25519(ed25519::Signature(pair.sign(msg)?))),
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
Rsa(ref pair) => Ok(Signature::Rsa(rsa::Signature(pair.sign(msg)?))),
|
Rsa(ref pair) => Ok(Signature::Rsa(rsa::Signature(pair.sign(msg)?))),
|
||||||
Secp256k1(ref pair) => Ok(Signature::Secp256k1(secp256k1::Signature(pair.secret().sign(msg)?)))
|
Secp256k1(ref pair) => Ok(Signature::Secp256k1(secp256k1::Signature(
|
||||||
|
pair.secret().sign(msg)?,
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +190,11 @@ impl KeyPair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the signature on a message using the public key.
|
/// Verify the signature on a message using the public key.
|
||||||
pub fn verify(pk: &PublicKey, msg: &[u8], signature: &Signature) -> Result<(), SigningError> {
|
pub fn verify(
|
||||||
|
pk: &PublicKey,
|
||||||
|
msg: &[u8],
|
||||||
|
signature: &Signature,
|
||||||
|
) -> Result<(), VerificationError> {
|
||||||
pk.verify(msg, signature)
|
pk.verify(msg, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,12 +215,12 @@ impl KeyPair {
|
|||||||
KeyFormat::Ed25519 => Ok(Ed25519(ed25519::Keypair::decode(&mut bytes)?)),
|
KeyFormat::Ed25519 => Ok(Ed25519(ed25519::Keypair::decode(&mut bytes)?)),
|
||||||
KeyFormat::Secp256k1 => Ok(Secp256k1(secp256k1::SecretKey::from_bytes(bytes)?.into())),
|
KeyFormat::Secp256k1 => Ok(Secp256k1(secp256k1::SecretKey::from_bytes(bytes)?.into())),
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
KeyFormat::Rsa => Err(DecodingError::KeypairDecodingIsNotSupported)
|
KeyFormat::Rsa => Err(DecodingError::KeypairDecodingIsNotSupported),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_peer_id(&self) -> PeerId {
|
pub fn get_peer_id(&self) -> PeerId {
|
||||||
self.public().to_peer_id()
|
self.public().to_peer_id()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,23 +232,34 @@ impl From<libp2p_core::identity::Keypair> for KeyPair {
|
|||||||
Ed25519(kp) => KeyPair::Ed25519(ed25519::Keypair::decode(&mut kp.encode()).unwrap()),
|
Ed25519(kp) => KeyPair::Ed25519(ed25519::Keypair::decode(&mut kp.encode()).unwrap()),
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
// safety: these Keypair structures are identical
|
// safety: these Keypair structures are identical
|
||||||
Rsa(kp) => KeyPair::Rsa(unsafe { std::mem::transmute::<libp2p_core::identity::rsa::Keypair, rsa::Keypair>(kp) }),
|
Rsa(kp) => KeyPair::Rsa(unsafe {
|
||||||
Secp256k1(kp) => KeyPair::Secp256k1(secp256k1::Keypair::from(secp256k1::SecretKey::from_bytes(kp.secret().to_bytes()).unwrap())),
|
std::mem::transmute::<libp2p_core::identity::rsa::Keypair, rsa::Keypair>(kp)
|
||||||
|
}),
|
||||||
|
Secp256k1(kp) => KeyPair::Secp256k1(secp256k1::Keypair::from(
|
||||||
|
secp256k1::SecretKey::from_bytes(kp.secret().to_bytes()).unwrap(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<KeyPair> for libp2p_core::identity::Keypair {
|
impl From<KeyPair> for libp2p_core::identity::Keypair {
|
||||||
fn from(key: KeyPair) -> Self {
|
fn from(key: KeyPair) -> Self {
|
||||||
use KeyPair::*;
|
|
||||||
use libp2p_core::identity::Keypair;
|
|
||||||
use libp2p_core::identity;
|
use libp2p_core::identity;
|
||||||
|
use libp2p_core::identity::Keypair;
|
||||||
|
use KeyPair::*;
|
||||||
|
|
||||||
match key {
|
match key {
|
||||||
Ed25519(kp) => Keypair::Ed25519(identity::ed25519::Keypair::decode(kp.encode().to_vec().as_mut_slice()).unwrap()),
|
Ed25519(kp) => Keypair::Ed25519(
|
||||||
|
identity::ed25519::Keypair::decode(kp.encode().to_vec().as_mut_slice()).unwrap(),
|
||||||
|
),
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
Rsa(kp) => Keypair::Rsa(unsafe { std::mem::transmute::<rsa::Keypair, libp2p_core::identity::rsa::Keypair>(kp) }),
|
// safety: these Keypair structures are identical
|
||||||
Secp256k1(kp) => Keypair::Secp256k1(identity::secp256k1::Keypair::from(identity::secp256k1::SecretKey::from_bytes(kp.secret().to_bytes()).unwrap())),
|
Rsa(kp) => Keypair::Rsa(unsafe {
|
||||||
|
std::mem::transmute::<rsa::Keypair, libp2p_core::identity::rsa::Keypair>(kp)
|
||||||
|
}),
|
||||||
|
Secp256k1(kp) => Keypair::Secp256k1(identity::secp256k1::Keypair::from(
|
||||||
|
identity::secp256k1::SecretKey::from_bytes(kp.secret().to_bytes()).unwrap(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
use crate::ed25519;
|
use crate::ed25519;
|
||||||
use crate::error::{DecodingError, SigningError};
|
use crate::error::{DecodingError, VerificationError};
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use crate::rsa;
|
use crate::rsa;
|
||||||
use crate::secp256k1;
|
use crate::secp256k1;
|
||||||
@ -43,7 +43,7 @@ impl PublicKey {
|
|||||||
/// private key (authenticity), and that the message has not been
|
/// private key (authenticity), and that the message has not been
|
||||||
/// tampered with (integrity).
|
/// tampered with (integrity).
|
||||||
// TODO: add VerificationError
|
// TODO: add VerificationError
|
||||||
pub fn verify(&self, msg: &[u8], sig: &Signature) -> Result<(), SigningError> {
|
pub fn verify(&self, msg: &[u8], sig: &Signature) -> Result<(), VerificationError> {
|
||||||
use PublicKey::*;
|
use PublicKey::*;
|
||||||
match self {
|
match self {
|
||||||
Ed25519(pk) => pk.verify(msg, sig.to_vec()),
|
Ed25519(pk) => pk.verify(msg, sig.to_vec()),
|
||||||
@ -111,6 +111,17 @@ impl PublicKey {
|
|||||||
pub fn to_peer_id(&self) -> PeerId {
|
pub fn to_peer_id(&self) -> PeerId {
|
||||||
PeerId::from_public_key(self.clone().into())
|
PeerId::from_public_key(self.clone().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_key_format(&self) -> KeyFormat {
|
||||||
|
use PublicKey::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Ed25519(_) => KeyFormat::Ed25519,
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
Rsa(_) => KeyFormat::Rsa,
|
||||||
|
Secp256k1(_) => KeyFormat::Secp256k1,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<libp2p_core::identity::PublicKey> for PublicKey {
|
impl From<libp2p_core::identity::PublicKey> for PublicKey {
|
||||||
@ -150,15 +161,12 @@ impl From<PublicKey> for libp2p_core::identity::PublicKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<libp2p_core::PeerId> for PublicKey {
|
impl TryFrom<libp2p_core::PeerId> for PublicKey {
|
||||||
type Error = eyre::Error;
|
type Error = DecodingError;
|
||||||
|
|
||||||
fn try_from(peer_id: libp2p_core::PeerId) -> eyre::Result<PublicKey> {
|
fn try_from(peer_id: libp2p_core::PeerId) -> Result<Self, Self::Error> {
|
||||||
Ok(peer_id
|
Ok(peer_id
|
||||||
.as_public_key()
|
.as_public_key()
|
||||||
.ok_or(eyre::eyre!(
|
.ok_or(DecodingError::PublicKeyNotInlined(peer_id.to_base58()))?
|
||||||
"public key is not inlined in peer id: {}",
|
|
||||||
peer_id
|
|
||||||
))?
|
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,16 +19,19 @@
|
|||||||
// DEALINGS IN THE SOFTWARE.
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
//! RSA keys.
|
//! RSA keys.
|
||||||
use crate::error::{DecodingError, SigningError};
|
use crate::error::{DecodingError, SigningError, VerificationError};
|
||||||
|
|
||||||
use asn1_der::{Asn1Der, FromDerObject, IntoDerObject, DerObject, DerTag, DerValue, Asn1DerError};
|
use asn1_der::{Asn1Der, Asn1DerError, DerObject, DerTag, DerValue, FromDerObject, IntoDerObject};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use ring::rand::SystemRandom;
|
use ring::rand::SystemRandom;
|
||||||
use ring::signature::{self, RsaKeyPair, RSA_PKCS1_SHA256, RSA_PKCS1_2048_8192_SHA256};
|
|
||||||
use ring::signature::KeyPair;
|
use ring::signature::KeyPair;
|
||||||
use std::{fmt::{self, Write}, sync::Arc};
|
use ring::signature::{self, RsaKeyPair, RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_SHA256};
|
||||||
use zeroize::Zeroize;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
fmt::{self, Write},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
/// An RSA keypair.
|
/// An RSA keypair.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -40,8 +43,7 @@ impl Keypair {
|
|||||||
///
|
///
|
||||||
/// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5
|
/// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5
|
||||||
pub fn from_pkcs8(der: &mut [u8]) -> Result<Self, DecodingError> {
|
pub fn from_pkcs8(der: &mut [u8]) -> Result<Self, DecodingError> {
|
||||||
let kp = RsaKeyPair::from_pkcs8(&der)
|
let kp = RsaKeyPair::from_pkcs8(&der).map_err(|_| DecodingError::Rsa)?;
|
||||||
.map_err(|_| DecodingError::Rsa)?;
|
|
||||||
der.zeroize();
|
der.zeroize();
|
||||||
Ok(Keypair(Arc::new(kp)))
|
Ok(Keypair(Arc::new(kp)))
|
||||||
}
|
}
|
||||||
@ -57,7 +59,7 @@ impl Keypair {
|
|||||||
let rng = SystemRandom::new();
|
let rng = SystemRandom::new();
|
||||||
match self.0.sign(&RSA_PKCS1_SHA256, &rng, &data, &mut signature) {
|
match self.0.sign(&RSA_PKCS1_SHA256, &rng, &data, &mut signature) {
|
||||||
Ok(()) => Ok(signature),
|
Ok(()) => Ok(signature),
|
||||||
Err(_) => Err(SigningError::Rsa)
|
Err(_) => Err(SigningError::Rsa),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,9 +70,15 @@ pub struct PublicKey(Vec<u8>);
|
|||||||
|
|
||||||
impl PublicKey {
|
impl PublicKey {
|
||||||
/// Verify an RSA signature on a message using the public key.
|
/// Verify an RSA signature on a message using the public key.
|
||||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<(), SigningError> {
|
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<(), VerificationError> {
|
||||||
let key = signature::UnparsedPublicKey::new(&RSA_PKCS1_2048_8192_SHA256, &self.0);
|
let key = signature::UnparsedPublicKey::new(&RSA_PKCS1_2048_8192_SHA256, &self.0);
|
||||||
key.verify(msg, sig).map_err(|_| SigningError::Rsa)
|
key.verify(msg, sig).map_err(|e| {
|
||||||
|
VerificationError::Rsa(
|
||||||
|
e,
|
||||||
|
bs58::encode(sig).into_string(),
|
||||||
|
bs58::encode(&self.0).into_string(),
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the RSA public key in DER as a PKCS#1 RSAPublicKey structure,
|
/// Encode the RSA public key in DER as a PKCS#1 RSAPublicKey structure,
|
||||||
@ -99,7 +107,8 @@ impl PublicKey {
|
|||||||
subjectPublicKey: Asn1SubjectPublicKey(self.clone()),
|
subjectPublicKey: Asn1SubjectPublicKey(self.clone()),
|
||||||
};
|
};
|
||||||
let mut buf = vec![0u8; spki.serialized_len()];
|
let mut buf = vec![0u8; spki.serialized_len()];
|
||||||
spki.serialize(buf.iter_mut()).map(|_| buf)
|
spki.serialize(buf.iter_mut())
|
||||||
|
.map(|_| buf)
|
||||||
.expect("RSA X.509 public key encoding failed.")
|
.expect("RSA X.509 public key encoding failed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,9 +130,7 @@ impl fmt::Debug for PublicKey {
|
|||||||
write!(hex, "{:02x}", byte).expect("Can't fail on writing to string");
|
write!(hex, "{:02x}", byte).expect("Can't fail on writing to string");
|
||||||
}
|
}
|
||||||
|
|
||||||
f.debug_struct("PublicKey")
|
f.debug_struct("PublicKey").field("pkcs1", &hex).finish()
|
||||||
.field("pkcs1", &hex)
|
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,6 +280,8 @@ mod tests {
|
|||||||
fn prop(SomeKeypair(kp): SomeKeypair, msg: Vec<u8>) -> Result<bool, SigningError> {
|
fn prop(SomeKeypair(kp): SomeKeypair, msg: Vec<u8>) -> Result<bool, SigningError> {
|
||||||
kp.sign(&msg).map(|s| kp.public().verify(&msg, &s).is_ok())
|
kp.sign(&msg).map(|s| kp.public().verify(&msg, &s).is_ok())
|
||||||
}
|
}
|
||||||
QuickCheck::new().tests(10).quickcheck(prop as fn(_, _) -> _);
|
QuickCheck::new()
|
||||||
|
.tests(10)
|
||||||
|
.quickcheck(prop as fn(_, _) -> _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,17 +19,17 @@
|
|||||||
// DEALINGS IN THE SOFTWARE.
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
//! Secp256k1 keys.
|
//! Secp256k1 keys.
|
||||||
use crate::error::{DecodingError, SigningError};
|
use crate::error::{DecodingError, SigningError, VerificationError};
|
||||||
|
|
||||||
use asn1_der::{FromDerObject, DerObject};
|
use asn1_der::{DerObject, FromDerObject};
|
||||||
use rand::RngCore;
|
|
||||||
use sha2::{Digest as ShaDigestTrait, Sha256};
|
|
||||||
use secp256k1::Message;
|
|
||||||
use zeroize::Zeroize;
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use serde::{Deserialize, Serialize, Serializer, Deserializer};
|
use rand::RngCore;
|
||||||
|
use secp256k1::Message;
|
||||||
use serde::de::Error as SerdeError;
|
use serde::de::Error as SerdeError;
|
||||||
use serde_bytes::{Bytes as SerdeBytes, ByteBuf as SerdeByteBuf};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
|
||||||
|
use sha2::{Digest as ShaDigestTrait, Sha256};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
/// A Secp256k1 keypair.
|
/// A Secp256k1 keypair.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -57,7 +57,9 @@ impl Keypair {
|
|||||||
|
|
||||||
impl fmt::Debug for Keypair {
|
impl fmt::Debug for Keypair {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Keypair").field("public", &self.public).finish()
|
f.debug_struct("Keypair")
|
||||||
|
.field("public", &self.public)
|
||||||
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,8 +108,8 @@ impl SecretKey {
|
|||||||
/// error is returned.
|
/// error is returned.
|
||||||
pub fn from_bytes(mut sk: impl AsMut<[u8]>) -> Result<Self, DecodingError> {
|
pub fn from_bytes(mut sk: impl AsMut<[u8]>) -> Result<Self, DecodingError> {
|
||||||
let sk_bytes = sk.as_mut();
|
let sk_bytes = sk.as_mut();
|
||||||
let secret = secp256k1::SecretKey::parse_slice(&*sk_bytes)
|
let secret =
|
||||||
.map_err(|_| DecodingError::Secp256k1)?;
|
secp256k1::SecretKey::parse_slice(&*sk_bytes).map_err(|_| DecodingError::Secp256k1)?;
|
||||||
sk_bytes.zeroize();
|
sk_bytes.zeroize();
|
||||||
Ok(SecretKey(secret))
|
Ok(SecretKey(secret))
|
||||||
}
|
}
|
||||||
@ -119,13 +121,12 @@ impl SecretKey {
|
|||||||
pub fn from_der(mut der: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
|
pub fn from_der(mut der: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
|
||||||
// TODO: Stricter parsing.
|
// TODO: Stricter parsing.
|
||||||
let der_obj = der.as_mut();
|
let der_obj = der.as_mut();
|
||||||
let obj: Vec<DerObject> = FromDerObject::deserialize((&*der_obj).iter())
|
let obj: Vec<DerObject> =
|
||||||
.map_err(|_| DecodingError::Secp256k1)?;
|
FromDerObject::deserialize((&*der_obj).iter()).map_err(|_| DecodingError::Secp256k1)?;
|
||||||
der_obj.zeroize();
|
der_obj.zeroize();
|
||||||
let sk_obj = obj.into_iter().nth(1)
|
let sk_obj = obj.into_iter().nth(1).ok_or(DecodingError::Secp256k1)?;
|
||||||
.ok_or(DecodingError::Secp256k1)?;
|
let mut sk_bytes: Vec<u8> =
|
||||||
let mut sk_bytes: Vec<u8> = FromDerObject::from_der_object(sk_obj)
|
FromDerObject::from_der_object(sk_obj).map_err(|_| DecodingError::Secp256k1)?;
|
||||||
.map_err(|_| DecodingError::Secp256k1)?;
|
|
||||||
let sk = SecretKey::from_bytes(&mut sk_bytes)?;
|
let sk = SecretKey::from_bytes(&mut sk_bytes)?;
|
||||||
sk_bytes.zeroize();
|
sk_bytes.zeroize();
|
||||||
Ok(sk)
|
Ok(sk)
|
||||||
@ -147,9 +148,12 @@ impl SecretKey {
|
|||||||
/// Sign a raw message of length 256 bits with this secret key, produces a DER-encoded
|
/// Sign a raw message of length 256 bits with this secret key, produces a DER-encoded
|
||||||
/// ECDSA signature.
|
/// ECDSA signature.
|
||||||
pub fn sign_hashed(&self, msg: &[u8]) -> Result<Vec<u8>, SigningError> {
|
pub fn sign_hashed(&self, msg: &[u8]) -> Result<Vec<u8>, SigningError> {
|
||||||
let m = Message::parse_slice(msg)
|
let m = Message::parse_slice(msg).map_err(SigningError::Secp256k1)?;
|
||||||
.map_err(SigningError::Secp256k1)?;
|
Ok(secp256k1::sign(&m, &self.0)
|
||||||
Ok(secp256k1::sign(&m, &self.0).0.serialize_der().as_ref().into())
|
.0
|
||||||
|
.serialize_der()
|
||||||
|
.as_ref()
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,15 +163,24 @@ pub struct PublicKey(secp256k1::PublicKey);
|
|||||||
|
|
||||||
impl PublicKey {
|
impl PublicKey {
|
||||||
/// Verify the Secp256k1 signature on a message using the public key.
|
/// Verify the Secp256k1 signature on a message using the public key.
|
||||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<(), SigningError> {
|
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<(), VerificationError> {
|
||||||
self.verify_hashed(Sha256::digest(msg).as_ref(), sig)
|
self.verify_hashed(Sha256::digest(msg).as_ref(), sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the Secp256k1 DER-encoded signature on a raw 256-bit message using the public key.
|
/// Verify the Secp256k1 DER-encoded signature on a raw 256-bit message using the public key.
|
||||||
pub fn verify_hashed(&self, msg: &[u8], sig: &[u8]) -> Result<(), SigningError> {
|
pub fn verify_hashed(&self, msg: &[u8], sig: &[u8]) -> Result<(), VerificationError> {
|
||||||
Message::parse_slice(msg)
|
Message::parse_slice(msg)
|
||||||
.and_then(|m| secp256k1::Signature::parse_der(sig).map(|s| secp256k1::verify(&m, &s, &self.0)))
|
.and_then(|m| {
|
||||||
.map_err(SigningError::Secp256k1).map(|_| ())
|
secp256k1::Signature::parse_der(sig).map(|s| secp256k1::verify(&m, &s, &self.0))
|
||||||
|
})
|
||||||
|
.map_err(|e| {
|
||||||
|
VerificationError::Secp256k1(
|
||||||
|
e,
|
||||||
|
bs58::encode(sig).into_string(),
|
||||||
|
bs58::encode(self.0.serialize_compressed()).into_string(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the public key in compressed form, i.e. with one coordinate
|
/// Encode the public key in compressed form, i.e. with one coordinate
|
||||||
@ -190,11 +203,10 @@ impl PublicKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Serialize for PublicKey {
|
impl Serialize for PublicKey {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
SerdeBytes::new(self.encode().to_vec().as_slice()).serialize(serializer)
|
SerdeBytes::new(self.encode().to_vec().as_slice()).serialize(serializer)
|
||||||
}
|
}
|
||||||
@ -202,15 +214,14 @@ impl Serialize for PublicKey {
|
|||||||
|
|
||||||
impl<'d> Deserialize<'d> for PublicKey {
|
impl<'d> Deserialize<'d> for PublicKey {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'d>,
|
D: Deserializer<'d>,
|
||||||
{
|
{
|
||||||
let bytes = <SerdeByteBuf>::deserialize(deserializer)?;
|
let bytes = <SerdeByteBuf>::deserialize(deserializer)?;
|
||||||
PublicKey::decode(bytes.as_slice()).map_err(SerdeError::custom)
|
PublicKey::decode(bytes.as_slice()).map_err(SerdeError::custom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Signature(pub Vec<u8>);
|
pub struct Signature(pub Vec<u8>);
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ impl Signature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes_with_public_key(key_format: KeyFormat, bytes: Vec<u8>) -> Self {
|
pub fn from_bytes(key_format: KeyFormat, bytes: Vec<u8>) -> Self {
|
||||||
match key_format {
|
match key_format {
|
||||||
KeyFormat::Ed25519 => Signature::Ed25519(ed25519::Signature(bytes)),
|
KeyFormat::Ed25519 => Signature::Ed25519(ed25519::Signature(bytes)),
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
1
local-network/builtins_secret_key.ed25519
Normal file
1
local-network/builtins_secret_key.ed25519
Normal file
@ -0,0 +1 @@
|
|||||||
|
5FwE32bDcphFzuMca7Y2qW1gdR64fTBYoRNvD4MLE1hecDGhCMQGKn8aseMr5wRo4Xo2CRFdrEAawUNLYkgQD78K
|
77
local-network/docker-compose.yml
Normal file
77
local-network/docker-compose.yml
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# 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
|
||||||
|
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
|
||||||
|
ports:
|
||||||
|
- 7770:7770 # tcp
|
||||||
|
- 9990:9990 # ws
|
||||||
|
- 5000:5001 # ipfs rpc
|
||||||
|
- 4000:4001 # ipfs swarm
|
||||||
|
- 18080:18080 # /metrics
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- fluence-0:/.fluence
|
||||||
|
- data-0:/config
|
||||||
|
- ./builtins_secret_key.ed25519:/.fluence/v1/builtins_secret_key.ed25519
|
||||||
|
networks:
|
||||||
|
- fluence
|
||||||
|
|
||||||
|
# fluence-1: # /ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWRABanQHUn28dxavN9ZS1zZghqoZVAYtFpoN7FdtoGTFv
|
||||||
|
# command: -f ed25519 -k 5fNENMwkUT4dW3hPs9ZwqV4qA5pdTtUChTazAx9Awe2Vpz1yaJu3VCmcEZow6YgdFBGoZoFAZUZBbF3c2Ebd2iL -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7771 -w 9991 --bootstraps /dns4/fluence-0/tcp/7770 /dns4/fluence-2/tcp/7772 #/dns4/kras-00.fluence.dev/tcp/7770
|
||||||
|
# container_name: fluence-1
|
||||||
|
# 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
|
||||||
|
# ports:
|
||||||
|
# - 7771:7771 # tcp
|
||||||
|
# - 9991:9991 # ws
|
||||||
|
# - 5001:5001 # ipfs rpc
|
||||||
|
# - 4001:4001 # ipfs swarm
|
||||||
|
# - 18081:18080 # /metrics
|
||||||
|
# restart: always
|
||||||
|
# volumes:
|
||||||
|
# - fluence-1:/.fluence
|
||||||
|
# - data-1:/config
|
||||||
|
# networks:
|
||||||
|
# - fluence
|
||||||
|
#
|
||||||
|
# fluence-2: # /ip4/127.0.0.1/tcp/9992/ws/p2p/12D3KooWFpQ7LHxcC9FEBUh3k4nSCC12jBhijJv3gJbi7wsNYzJ5
|
||||||
|
# command: -f ed25519 -k 5DTs9LQS8Ay2dM8xBcikDRwYLMcanhsC6tynSSgpLyBZEv5Ey34LVw1fYcCuUj9A9EfvQJB2bsaGhSRoHQ7D6UE5 -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7772 -w 9992 --bootstraps /dns4/fluence-0/tcp/7770 /dns4/fluence-1/tcp/7771 #/dns4/kras-00.fluence.dev/tcp/7770
|
||||||
|
# container_name: fluence-2
|
||||||
|
# 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
|
||||||
|
# ports:
|
||||||
|
# - 7772:7772 # tcp
|
||||||
|
# - 9992:9992 # ws
|
||||||
|
# - 5002:5001 # ipfs rpc
|
||||||
|
# - 4002:4001 # ipfs swarm
|
||||||
|
# - 18082:18080 # /metrics
|
||||||
|
# restart: always
|
||||||
|
# volumes:
|
||||||
|
# - fluence-2:/.fluence
|
||||||
|
# - data-2:/config
|
||||||
|
# networks:
|
||||||
|
# - fluence
|
||||||
|
|
||||||
|
version: "3.5"
|
||||||
|
volumes:
|
||||||
|
fluence-0:
|
||||||
|
# fluence-1:
|
||||||
|
# fluence-2:
|
||||||
|
data-0:
|
||||||
|
# data-1:
|
||||||
|
# data-2:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
fluence:
|
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly-2021-09-01"
|
||||||
|
targets = [ "x86_64-apple-darwin", "x86_64-unknown-linux-gnu" ]
|
38
service/Cargo.toml
Normal file
38
service/Cargo.toml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
[package]
|
||||||
|
name = "trust-graph-wasm"
|
||||||
|
version = "0.2.1"
|
||||||
|
authors = ["Fluence Labs"]
|
||||||
|
edition = "2018"
|
||||||
|
description = "trust graph wasm"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "trust-graph"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
trust-graph = { version = "0.3.0", path = "../." }
|
||||||
|
fluence-keypair = { version = "0.5.0", path = "../keypair" }
|
||||||
|
marine-rs-sdk = { version = "0.6.14", features = ["logger"] }
|
||||||
|
marine-sqlite-connector = "0.5.2"
|
||||||
|
|
||||||
|
libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2", features = ["secp256k1"]}
|
||||||
|
|
||||||
|
log = "0.4.8"
|
||||||
|
anyhow = "1.0.31"
|
||||||
|
boolinator = "2.4.0"
|
||||||
|
once_cell = "1.4.1"
|
||||||
|
parking_lot = "0.11.1"
|
||||||
|
serde_json = "1.0"
|
||||||
|
bs58 = "0.3.1"
|
||||||
|
rmp-serde = "0.15.0"
|
||||||
|
bincode = "1.3.1"
|
||||||
|
serde_bencode = "^0.2.3"
|
||||||
|
thiserror = "1.0.23"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
marine-rs-sdk-test = "0.4.0"
|
||||||
|
rusqlite = "0.26.1"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
marine-rs-sdk-test = "0.4.0"
|
16
service/Config.toml
Normal file
16
service/Config.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
modules_dir = "artifacts/"
|
||||||
|
|
||||||
|
[[module]]
|
||||||
|
name = "sqlite3"
|
||||||
|
logger_enabled = true
|
||||||
|
|
||||||
|
[module.wasi]
|
||||||
|
preopened_files = ["./data"]
|
||||||
|
mapped_dirs = { "data" = "data" }
|
||||||
|
[[module]]
|
||||||
|
name = "trust-graph"
|
||||||
|
logger_enabled = true
|
||||||
|
|
||||||
|
[module.wasi]
|
||||||
|
preopened_files = ["./data"]
|
||||||
|
mapped_dirs = { "data" = "data" }
|
33
service/build.rs
Normal file
33
service/build.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
use marine_rs_sdk_test::generate_marine_test_env;
|
||||||
|
use marine_rs_sdk_test::ServiceDescription;
|
||||||
|
fn main() {
|
||||||
|
let services = vec![(
|
||||||
|
"trust_graph".to_string(),
|
||||||
|
ServiceDescription {
|
||||||
|
config_path: "Config.toml".to_string(),
|
||||||
|
modules_dir: Some("artifacts".to_string()),
|
||||||
|
},
|
||||||
|
)];
|
||||||
|
|
||||||
|
let target = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||||
|
if target != "wasm32" {
|
||||||
|
generate_marine_test_env(services, "marine_test_env.rs", file!());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=src/main.rs");
|
||||||
|
}
|
19
service/build.sh
Executable file
19
service/build.sh
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -o errexit -o nounset -o pipefail
|
||||||
|
|
||||||
|
# set current working directory to script directory to run script from everywhere
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# build trust-graph.wasm
|
||||||
|
marine build --release
|
||||||
|
|
||||||
|
# copy .wasm to artifacts
|
||||||
|
rm -f artifacts/*
|
||||||
|
mkdir -p artifacts
|
||||||
|
cp ../target/wasm32-wasi/release/trust-graph.wasm artifacts/
|
||||||
|
|
||||||
|
# download SQLite 3 to use in tests
|
||||||
|
curl -L https://github.com/fluencelabs/sqlite/releases/download/v0.15.0_w/sqlite3.wasm -o artifacts/sqlite3.wasm
|
||||||
|
|
||||||
|
# generate Aqua bindings
|
||||||
|
marine aqua artifacts/trust-graph.wasm -s TrustGraph -i trust-graph > ../aqua/trust-graph.aqua
|
3
service/rust-toolchain.toml
Normal file
3
service/rust-toolchain.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly-2021-09-01"
|
||||||
|
targets = [ "x86_64-apple-darwin", "x86_64-unknown-linux-gnu" ]
|
169
service/src/dto.rs
Normal file
169
service/src/dto.rs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
use crate::dto::DtoConversionError::PeerIdDecodeError;
|
||||||
|
use fluence_keypair::error::DecodingError;
|
||||||
|
use fluence_keypair::{KeyFormat, PublicKey, 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;
|
||||||
|
|
||||||
|
#[derive(ThisError, Debug)]
|
||||||
|
pub enum DtoConversionError {
|
||||||
|
#[error("Cannot convert base58 string to bytes: {0}")]
|
||||||
|
Base58Error(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
bs58::decode::Error,
|
||||||
|
),
|
||||||
|
#[error("Cannot convert string to PublicKey: {0}")]
|
||||||
|
PublicKeyDecodeError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
DecodingError,
|
||||||
|
),
|
||||||
|
#[error("Cannot decode peer id from string: {0}")]
|
||||||
|
PeerIdDecodeError(String),
|
||||||
|
#[error("{0}")]
|
||||||
|
InvalidKeyFormat(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
fluence_keypair::error::Error,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub struct Certificate {
|
||||||
|
pub chain: Vec<Trust>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<trust_graph::Certificate> for Certificate {
|
||||||
|
fn from(c: trust_graph::Certificate) -> Self {
|
||||||
|
let chain: Vec<Trust> = c.chain.into_iter().map(|t| t.into()).collect();
|
||||||
|
return Certificate { chain };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Certificate> for trust_graph::Certificate {
|
||||||
|
type Error = DtoConversionError;
|
||||||
|
|
||||||
|
fn try_from(c: Certificate) -> Result<Self, Self::Error> {
|
||||||
|
let chain: Result<Vec<trust_graph::Trust>, DtoConversionError> = c
|
||||||
|
.chain
|
||||||
|
.into_iter()
|
||||||
|
.map(|t| trust_graph::Trust::try_from(t))
|
||||||
|
.collect();
|
||||||
|
let chain = chain?;
|
||||||
|
return Ok(trust_graph::Certificate { chain });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Trust {
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Trust> for trust_graph::Trust {
|
||||||
|
type Error = DtoConversionError;
|
||||||
|
|
||||||
|
fn try_from(t: Trust) -> Result<Self, Self::Error> {
|
||||||
|
let issued_for = PublicKey::try_from(
|
||||||
|
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_bytes(KeyFormat::from_str(&t.sig_type)?, signature);
|
||||||
|
let expires_at = Duration::from_secs(t.expires_at);
|
||||||
|
let issued_at = Duration::from_secs(t.issued_at);
|
||||||
|
return Ok(trust_graph::Trust {
|
||||||
|
issued_for,
|
||||||
|
expires_at,
|
||||||
|
signature,
|
||||||
|
issued_at,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<trust_graph::Trust> for Trust {
|
||||||
|
fn from(t: trust_graph::Trust) -> Self {
|
||||||
|
let issued_for = t.issued_for.to_peer_id().to_base58();
|
||||||
|
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.into(),
|
||||||
|
issued_at,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 = PublicKey::try_from(
|
||||||
|
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 = PublicKey::try_from(
|
||||||
|
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_bytes(KeyFormat::from_str(&r.sig_type)?, signature);
|
||||||
|
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.into(),
|
||||||
|
revoked_by,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
23
service/src/main.rs
Normal file
23
service/src/main.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use crate::storage_impl::create_tables;
|
||||||
|
use marine_rs_sdk::module_manifest;
|
||||||
|
use marine_rs_sdk::WasmLoggerBuilder;
|
||||||
|
|
||||||
|
module_manifest!();
|
||||||
|
|
||||||
|
mod dto;
|
||||||
|
mod results;
|
||||||
|
mod service_api;
|
||||||
|
mod service_impl;
|
||||||
|
mod storage_impl;
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
WasmLoggerBuilder::new()
|
||||||
|
.with_log_level(log::LevelFilter::Trace)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
create_tables();
|
||||||
|
}
|
258
service/src/results.rs
Normal file
258
service/src/results.rs
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
use crate::dto::{Certificate, Revoke, Trust};
|
||||||
|
use crate::service_impl::ServiceError;
|
||||||
|
use marine_rs_sdk::marine;
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub struct InsertResult {
|
||||||
|
pub success: bool,
|
||||||
|
pub error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Result<(), ServiceError>> for InsertResult {
|
||||||
|
fn from(result: Result<(), ServiceError>) -> Self {
|
||||||
|
match result {
|
||||||
|
Ok(()) => InsertResult {
|
||||||
|
success: true,
|
||||||
|
error: "".to_string(),
|
||||||
|
},
|
||||||
|
Err(e) => InsertResult {
|
||||||
|
success: false,
|
||||||
|
error: format!("{}", e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub struct WeightResult {
|
||||||
|
pub success: bool,
|
||||||
|
pub weight: u32,
|
||||||
|
pub peer_id: String,
|
||||||
|
pub error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Result<(u32, String), ServiceError>> for WeightResult {
|
||||||
|
fn from(result: Result<(u32, String), ServiceError>) -> Self {
|
||||||
|
match result {
|
||||||
|
Ok((weight, peer_id)) => WeightResult {
|
||||||
|
success: true,
|
||||||
|
weight,
|
||||||
|
peer_id,
|
||||||
|
error: "".to_string(),
|
||||||
|
},
|
||||||
|
Err(e) => WeightResult {
|
||||||
|
success: false,
|
||||||
|
weight: 0u32,
|
||||||
|
peer_id: "".to_string(),
|
||||||
|
error: format!("{}", e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub struct AllCertsResult {
|
||||||
|
pub success: bool,
|
||||||
|
pub certificates: Vec<Certificate>,
|
||||||
|
pub error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Result<Vec<Certificate>, ServiceError>> for AllCertsResult {
|
||||||
|
fn from(result: Result<Vec<Certificate>, ServiceError>) -> Self {
|
||||||
|
match result {
|
||||||
|
Ok(certs) => AllCertsResult {
|
||||||
|
success: true,
|
||||||
|
certificates: certs,
|
||||||
|
error: "".to_string(),
|
||||||
|
},
|
||||||
|
Err(e) => AllCertsResult {
|
||||||
|
success: false,
|
||||||
|
certificates: vec![],
|
||||||
|
error: format!("{}", e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub struct AddRootResult {
|
||||||
|
pub success: bool,
|
||||||
|
pub error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Result<(), ServiceError>> for AddRootResult {
|
||||||
|
fn from(result: Result<(), ServiceError>) -> Self {
|
||||||
|
match result {
|
||||||
|
Ok(()) => AddRootResult {
|
||||||
|
success: true,
|
||||||
|
error: "".to_string(),
|
||||||
|
},
|
||||||
|
Err(e) => AddRootResult {
|
||||||
|
success: false,
|
||||||
|
error: format!("{}", e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub struct GetTrustBytesResult {
|
||||||
|
pub success: bool,
|
||||||
|
pub error: String,
|
||||||
|
pub result: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Result<Vec<u8>, ServiceError>> for GetTrustBytesResult {
|
||||||
|
fn from(result: Result<Vec<u8>, ServiceError>) -> Self {
|
||||||
|
match result {
|
||||||
|
Ok(res) => GetTrustBytesResult {
|
||||||
|
success: true,
|
||||||
|
error: "".to_string(),
|
||||||
|
result: res,
|
||||||
|
},
|
||||||
|
Err(e) => GetTrustBytesResult {
|
||||||
|
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(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub struct VerifyTrustResult {
|
||||||
|
pub success: bool,
|
||||||
|
pub error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Result<(), ServiceError>> for VerifyTrustResult {
|
||||||
|
fn from(result: Result<(), ServiceError>) -> Self {
|
||||||
|
match result {
|
||||||
|
Ok(()) => VerifyTrustResult {
|
||||||
|
success: true,
|
||||||
|
error: "".to_string(),
|
||||||
|
},
|
||||||
|
Err(e) => VerifyTrustResult {
|
||||||
|
success: false,
|
||||||
|
error: format!("{}", e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub struct AddTrustResult {
|
||||||
|
pub success: bool,
|
||||||
|
pub error: String,
|
||||||
|
pub weight: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Result<u32, ServiceError>> for AddTrustResult {
|
||||||
|
fn from(result: Result<u32, ServiceError>) -> Self {
|
||||||
|
match result {
|
||||||
|
Ok(weight) => AddTrustResult {
|
||||||
|
success: true,
|
||||||
|
error: "".to_string(),
|
||||||
|
weight,
|
||||||
|
},
|
||||||
|
Err(e) => AddTrustResult {
|
||||||
|
success: false,
|
||||||
|
error: format!("{}", e),
|
||||||
|
weight: u32::default(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
138
service/src/service_api.rs
Normal file
138
service/src/service_api.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use crate::dto::{Certificate, Revoke, Trust};
|
||||||
|
use crate::results::{
|
||||||
|
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_host_certs_impl, get_revoke_bytes_impl,
|
||||||
|
get_trust_bytes_impl, get_weight_impl, insert_cert_impl, insert_cert_impl_raw,
|
||||||
|
issue_revocation_impl, issue_trust_impl, revoke_impl, verify_trust_impl, ServiceError,
|
||||||
|
};
|
||||||
|
use marine_rs_sdk::{get_call_parameters, marine, CallParameters};
|
||||||
|
use trust_graph::MAX_WEIGHT_FACTOR;
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
fn get_weight_factor(max_chain_len: u32) -> u32 {
|
||||||
|
MAX_WEIGHT_FACTOR.checked_sub(max_chain_len).unwrap_or(0u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
/// 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;
|
||||||
|
if call_parameters.service_creator_peer_id == init_peer_id {
|
||||||
|
add_root_impl(peer_id, weight_factor).into()
|
||||||
|
} else {
|
||||||
|
return AddRootResult {
|
||||||
|
success: false,
|
||||||
|
error: ServiceError::NotOwner.to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
/// add a certificate in string representation to trust graph if it is valid
|
||||||
|
/// see `trust_graph::Certificate` class for string encoding/decoding
|
||||||
|
fn insert_cert_raw(certificate: String, timestamp_sec: u64) -> InsertResult {
|
||||||
|
insert_cert_impl_raw(certificate, timestamp_sec).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
/// add a certificate in JSON representation to trust graph if it is valid
|
||||||
|
/// see `dto::Certificate` class for structure
|
||||||
|
fn insert_cert(certificate: Certificate, timestamp_sec: u64) -> InsertResult {
|
||||||
|
insert_cert_impl(certificate, timestamp_sec).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
fn get_all_certs(issued_for: String, timestamp_sec: u64) -> AllCertsResult {
|
||||||
|
get_all_certs_impl(issued_for, timestamp_sec).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
fn get_host_certs(timestamp_sec: u64) -> AllCertsResult {
|
||||||
|
get_host_certs_impl(timestamp_sec).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
fn get_host_certs_from(issuer: String, timestamp_sec: u64) -> AllCertsResult {
|
||||||
|
let host_id = get_call_parameters().host_id;
|
||||||
|
get_all_certs_impl(host_id, timestamp_sec)
|
||||||
|
.map(|certs| {
|
||||||
|
certs
|
||||||
|
.into_iter()
|
||||||
|
.filter(|cert| cert.chain.iter().any(|t| t.issued_for == issuer))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
fn get_weight(peer_id: String, timestamp_sec: u64) -> WeightResult {
|
||||||
|
get_weight_impl(peer_id.clone(), timestamp_sec)
|
||||||
|
.map(|w| (w, peer_id))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
fn get_trust_bytes(
|
||||||
|
issued_for_peer_id: String,
|
||||||
|
expires_at_sec: u64,
|
||||||
|
issued_at_sec: u64,
|
||||||
|
) -> GetTrustBytesResult {
|
||||||
|
get_trust_bytes_impl(issued_for_peer_id, expires_at_sec, issued_at_sec).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
fn issue_trust(
|
||||||
|
issued_for_peer_id: String,
|
||||||
|
expires_at_sec: u64,
|
||||||
|
issued_at_sec: u64,
|
||||||
|
trust_bytes: Vec<u8>,
|
||||||
|
) -> IssueTrustResult {
|
||||||
|
issue_trust_impl(
|
||||||
|
issued_for_peer_id,
|
||||||
|
expires_at_sec,
|
||||||
|
issued_at_sec,
|
||||||
|
trust_bytes,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
fn verify_trust(trust: Trust, issuer_peer_id: String, timestamp_sec: u64) -> VerifyTrustResult {
|
||||||
|
verify_trust_impl(trust, issuer_peer_id, timestamp_sec).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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()
|
||||||
|
}
|
255
service/src/service_impl.rs
Normal file
255
service/src/service_impl.rs
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
use crate::dto::{Certificate, DtoConversionError, Revoke, Trust};
|
||||||
|
use crate::service_impl::ServiceError::InvalidTimestampTetraplet;
|
||||||
|
use crate::storage_impl::get_data;
|
||||||
|
use fluence_keypair::error::DecodingError;
|
||||||
|
use fluence_keypair::{PublicKey, Signature};
|
||||||
|
use libp2p_core::PeerId;
|
||||||
|
use marine_rs_sdk::CallParameters;
|
||||||
|
use std::convert::{Into, TryFrom, TryInto};
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::time::Duration;
|
||||||
|
use thiserror::Error as ThisError;
|
||||||
|
use trust_graph::{CertificateError, TrustError, TrustGraphError};
|
||||||
|
|
||||||
|
pub static TRUSTED_TIMESTAMP_SERVICE_ID: &str = "peer";
|
||||||
|
pub static TRUSTED_TIMESTAMP_FUNCTION_NAME: &str = "timestamp_sec";
|
||||||
|
|
||||||
|
/// Check timestamps are generated on the current host with builtin ("peer" "timestamp_sec")
|
||||||
|
pub(crate) fn check_timestamp_tetraplets(
|
||||||
|
call_parameters: &CallParameters,
|
||||||
|
arg_number: usize,
|
||||||
|
) -> Result<(), ServiceError> {
|
||||||
|
let tetraplets = call_parameters
|
||||||
|
.tetraplets
|
||||||
|
.get(arg_number)
|
||||||
|
.ok_or(InvalidTimestampTetraplet)?;
|
||||||
|
let tetraplet = tetraplets.get(0).ok_or(InvalidTimestampTetraplet)?;
|
||||||
|
(tetraplet.service_id == TRUSTED_TIMESTAMP_SERVICE_ID
|
||||||
|
&& tetraplet.function_name == TRUSTED_TIMESTAMP_FUNCTION_NAME
|
||||||
|
&& tetraplet.peer_pk == call_parameters.host_id)
|
||||||
|
.then(|| ())
|
||||||
|
.ok_or(InvalidTimestampTetraplet)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ThisError, Debug)]
|
||||||
|
pub enum ServiceError {
|
||||||
|
#[error("peer id parse error: {0}")]
|
||||||
|
PeerIdParseError(String),
|
||||||
|
#[error("public key extraction from peer id failed: {0}")]
|
||||||
|
PublicKeyExtractionError(String),
|
||||||
|
#[error("{0}")]
|
||||||
|
PublicKeyDecodeError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
DecodingError,
|
||||||
|
),
|
||||||
|
#[error("{0}")]
|
||||||
|
TGError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
TrustGraphError,
|
||||||
|
),
|
||||||
|
#[error("{0}")]
|
||||||
|
CertError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
CertificateError,
|
||||||
|
),
|
||||||
|
#[error("{0}")]
|
||||||
|
DtoError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
DtoConversionError,
|
||||||
|
),
|
||||||
|
#[error("{0}")]
|
||||||
|
TrustError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
TrustError,
|
||||||
|
),
|
||||||
|
#[error("you should use host peer.timestamp_sec to pass timestamp")]
|
||||||
|
InvalidTimestampTetraplet,
|
||||||
|
#[error("{0} can't be issued later than the current timestamp")]
|
||||||
|
InvalidTimestamp(String),
|
||||||
|
#[error("Root could add only by trust graph service owner")]
|
||||||
|
NotOwner,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_peer_id(peer_id: String) -> Result<PeerId, ServiceError> {
|
||||||
|
libp2p_core::PeerId::from_str(&peer_id)
|
||||||
|
.map_err(|e| ServiceError::PeerIdParseError(format!("{:?}", e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_public_key(peer_id: String) -> Result<PublicKey, ServiceError> {
|
||||||
|
PublicKey::try_from(
|
||||||
|
parse_peer_id(peer_id)
|
||||||
|
.map_err(|e| ServiceError::PublicKeyExtractionError(e.to_string()))?,
|
||||||
|
)
|
||||||
|
.map_err(ServiceError::PublicKeyDecodeError)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 mut tg = get_data().lock();
|
||||||
|
let public_key = extract_public_key(peer_id)?;
|
||||||
|
let weight = tg.weight(public_key, Duration::from_secs(timestamp_sec))?;
|
||||||
|
Ok(weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_cert(certificate: trust_graph::Certificate, timestamp_sec: u64) -> Result<(), ServiceError> {
|
||||||
|
let timestamp_sec = Duration::from_secs(timestamp_sec);
|
||||||
|
let mut tg = get_data().lock();
|
||||||
|
tg.add(certificate, timestamp_sec)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_cert_impl_raw(certificate: String, timestamp_sec: u64) -> Result<(), ServiceError> {
|
||||||
|
let certificate = trust_graph::Certificate::from_str(&certificate)?;
|
||||||
|
|
||||||
|
add_cert(certificate, timestamp_sec)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_all_certs_impl(
|
||||||
|
issued_for: String,
|
||||||
|
timestamp_sec: u64,
|
||||||
|
) -> Result<Vec<Certificate>, ServiceError> {
|
||||||
|
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 1)?;
|
||||||
|
get_certs_helper(issued_for, timestamp_sec)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_host_certs_impl(timestamp_sec: u64) -> Result<Vec<Certificate>, ServiceError> {
|
||||||
|
let cp = marine_rs_sdk::get_call_parameters();
|
||||||
|
check_timestamp_tetraplets(&cp, 0)?;
|
||||||
|
get_certs_helper(cp.host_id, timestamp_sec)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_certs_helper(
|
||||||
|
issued_for: String,
|
||||||
|
timestamp_sec: u64,
|
||||||
|
) -> Result<Vec<Certificate>, ServiceError> {
|
||||||
|
let mut tg = get_data().lock();
|
||||||
|
|
||||||
|
let public_key = extract_public_key(issued_for)?;
|
||||||
|
let certs = tg.get_all_certs(public_key, Duration::from_secs(timestamp_sec))?;
|
||||||
|
Ok(certs.into_iter().map(|c| c.into()).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_cert_impl(certificate: Certificate, timestamp_sec: u64) -> Result<(), ServiceError> {
|
||||||
|
let certificate: trust_graph::Certificate = certificate.try_into()?;
|
||||||
|
|
||||||
|
add_cert(certificate, timestamp_sec)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_root_impl(peer_id: String, weight: u32) -> Result<(), ServiceError> {
|
||||||
|
let mut tg = get_data().lock();
|
||||||
|
let public_key = extract_public_key(peer_id)?;
|
||||||
|
tg.add_root_weight_factor(public_key, weight)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_trust_bytes_impl(
|
||||||
|
peer_id: String,
|
||||||
|
expires_at_sec: u64,
|
||||||
|
issued_at_sec: u64,
|
||||||
|
) -> Result<Vec<u8>, ServiceError> {
|
||||||
|
let public_key = extract_public_key(peer_id)?;
|
||||||
|
Ok(trust_graph::Trust::signature_bytes(
|
||||||
|
&public_key,
|
||||||
|
Duration::from_secs(expires_at_sec),
|
||||||
|
Duration::from_secs(issued_at_sec),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn issue_trust_impl(
|
||||||
|
peer_id: String,
|
||||||
|
expires_at_sec: u64,
|
||||||
|
issued_at_sec: u64,
|
||||||
|
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(public_key.get_key_format(), trust_bytes);
|
||||||
|
Ok(Trust::from(trust_graph::Trust::new(
|
||||||
|
public_key,
|
||||||
|
expires_at_sec,
|
||||||
|
issued_at_sec,
|
||||||
|
signature,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_trust_impl(
|
||||||
|
trust: Trust,
|
||||||
|
issuer_peer_id: String,
|
||||||
|
timestamp_sec: u64,
|
||||||
|
) -> Result<(), ServiceError> {
|
||||||
|
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 2)?;
|
||||||
|
let public_key = extract_public_key(issuer_peer_id)?;
|
||||||
|
trust_graph::Trust::verify(
|
||||||
|
&trust.try_into()?,
|
||||||
|
&public_key,
|
||||||
|
Duration::from_secs(timestamp_sec),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_trust_impl(
|
||||||
|
trust: Trust,
|
||||||
|
issuer_peer_id: String,
|
||||||
|
timestamp_sec: u64,
|
||||||
|
) -> 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::InvalidTimestamp("Trust".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tg = get_data().lock();
|
||||||
|
tg.add_trust(
|
||||||
|
&trust.try_into()?,
|
||||||
|
public_key,
|
||||||
|
Duration::from_secs(timestamp_sec),
|
||||||
|
)
|
||||||
|
.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(revoked_by_pk.get_key_format(), 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)?;
|
||||||
|
|
||||||
|
if revoke.revoked_at > timestamp_sec {
|
||||||
|
return Err(ServiceError::InvalidTimestamp("Revoke".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tg = get_data().lock();
|
||||||
|
|
||||||
|
tg.revoke(revoke.try_into()?).map_err(ServiceError::TGError)
|
||||||
|
}
|
332
service/src/storage_impl.rs
Normal file
332
service/src/storage_impl.rs
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
// store list of trusts
|
||||||
|
// check if trust is already in list before adding
|
||||||
|
// if there is an older trust - don't add received trust
|
||||||
|
|
||||||
|
use crate::storage_impl::SQLiteStorageError::{
|
||||||
|
FieldConversionDB, PublicKeyConversion, PublicKeyFromStr, WeightFactorConversionDB,
|
||||||
|
};
|
||||||
|
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
use fluence_keypair::error::DecodingError;
|
||||||
|
use fluence_keypair::Signature;
|
||||||
|
use marine_sqlite_connector::{Connection, Error as InternalSqliteError, Value};
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use rmp_serde::decode::Error as RmpDecodeError;
|
||||||
|
use rmp_serde::encode::Error as RmpEncodeError;
|
||||||
|
use std::convert::From;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::time::Duration;
|
||||||
|
use thiserror::Error as ThisError;
|
||||||
|
use trust_graph::{
|
||||||
|
Auth, PublicKeyHashable as PK, PublicKeyHashable, Revoke, Storage, StorageError, Trust,
|
||||||
|
TrustGraph, TrustRelation, WeightFactor,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
static INSTANCE: OnceCell<Mutex<TrustGraph<SQLiteStorage>>> = OnceCell::new();
|
||||||
|
|
||||||
|
static AUTH_TYPE: i64 = 0;
|
||||||
|
static REVOKE_TYPE: i64 = 1;
|
||||||
|
pub static DB_PATH: &str = "data/users12312233.sqlite";
|
||||||
|
|
||||||
|
pub fn create_tables() {
|
||||||
|
let connection = marine_sqlite_connector::open(DB_PATH).unwrap();
|
||||||
|
|
||||||
|
connection
|
||||||
|
.execute(
|
||||||
|
"CREATE TABLE IF NOT EXISTS trust_relations(
|
||||||
|
relation_type INTEGER,
|
||||||
|
issued_for TEXT,
|
||||||
|
issued_by TEXT,
|
||||||
|
issued_at INTEGER,
|
||||||
|
expires_at INTEGER,
|
||||||
|
signature TEXT,
|
||||||
|
PRIMARY KEY (issued_for, issued_by)
|
||||||
|
);",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
connection
|
||||||
|
.execute(
|
||||||
|
"CREATE TABLE IF NOT EXISTS roots(
|
||||||
|
public_key TEXT,
|
||||||
|
weight_factor INTEGER
|
||||||
|
);",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_data() -> &'static Mutex<TrustGraph<SQLiteStorage>> {
|
||||||
|
INSTANCE.get_or_init(|| {
|
||||||
|
let connection = marine_sqlite_connector::open(DB_PATH).unwrap();
|
||||||
|
Mutex::new(TrustGraph::new(SQLiteStorage::new(connection)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SQLiteStorage {
|
||||||
|
connection: Connection,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl SQLiteStorage {
|
||||||
|
pub fn new(connection: Connection) -> SQLiteStorage {
|
||||||
|
SQLiteStorage { connection }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_relation(&mut self, relation: TrustRelation) -> Result<(), SQLiteStorageError> {
|
||||||
|
match self.get_relation(
|
||||||
|
relation.issued_for().as_ref(),
|
||||||
|
relation.issued_by().as_ref(),
|
||||||
|
)? {
|
||||||
|
Some(TrustRelation::Auth(auth)) => {
|
||||||
|
if auth.trust.issued_at < relation.issued_at() {
|
||||||
|
self.insert(relation)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(TrustRelation::Revoke(revoke)) => {
|
||||||
|
if revoke.revoked_at < relation.issued_at() {
|
||||||
|
self.insert(relation)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None => {
|
||||||
|
self.insert(relation)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ThisError, Debug)]
|
||||||
|
pub enum SQLiteStorageError {
|
||||||
|
#[error("{0}")]
|
||||||
|
SQLiteError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
InternalSqliteError,
|
||||||
|
),
|
||||||
|
#[error("{0}")]
|
||||||
|
PublicKeyFromStr(String),
|
||||||
|
#[error("{0}")]
|
||||||
|
EncodeError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
RmpEncodeError,
|
||||||
|
),
|
||||||
|
#[error("{0}")]
|
||||||
|
DecodeError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
RmpDecodeError,
|
||||||
|
),
|
||||||
|
#[error("Cannot convert field from DB")]
|
||||||
|
FieldConversionDB,
|
||||||
|
#[error("Cannot convert weight factor as integer from DB")]
|
||||||
|
WeightFactorConversionDB,
|
||||||
|
#[error("Cannot convert public key as binary from DB")]
|
||||||
|
PublicKeyConversion,
|
||||||
|
#[error("Cannot revoke. There is no trust with such PublicKey")]
|
||||||
|
PublicKeyNotFound,
|
||||||
|
#[error("Cannot decode signature from DB: {0}")]
|
||||||
|
SignatureDecodeError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
DecodingError,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
fn parse_relation(row: &[Value]) -> Result<TrustRelation, SQLiteStorageError> {
|
||||||
|
let relation_type = row[0].as_integer().ok_or(FieldConversionDB)?;
|
||||||
|
let issued_for = PK::from_str(row[1].as_string().ok_or(FieldConversionDB)?)?;
|
||||||
|
let issued_by = PK::from_str(row[2].as_string().ok_or(FieldConversionDB)?)?;
|
||||||
|
let issued_at = Duration::from_secs(row[3].as_integer().ok_or(FieldConversionDB)? as u64);
|
||||||
|
let expires_at = Duration::from_secs(row[4].as_integer().ok_or(FieldConversionDB)? as u64);
|
||||||
|
let signature = Signature::decode(row[5].as_binary().ok_or(FieldConversionDB)?.to_vec())?;
|
||||||
|
|
||||||
|
if relation_type == AUTH_TYPE {
|
||||||
|
Ok(TrustRelation::Auth(Auth {
|
||||||
|
trust: Trust {
|
||||||
|
issued_for: issued_for.into(),
|
||||||
|
expires_at,
|
||||||
|
signature,
|
||||||
|
issued_at,
|
||||||
|
},
|
||||||
|
issued_by: issued_by.into(),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Ok(TrustRelation::Revoke(Revoke {
|
||||||
|
pk: issued_for.into(),
|
||||||
|
revoked_at: issued_at,
|
||||||
|
revoked_by: issued_by.into(),
|
||||||
|
signature,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SQLiteStorageError> for String {
|
||||||
|
fn from(err: SQLiteStorageError) -> Self {
|
||||||
|
err.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StorageError for SQLiteStorageError {}
|
||||||
|
|
||||||
|
impl Storage for SQLiteStorage {
|
||||||
|
type Error = SQLiteStorageError;
|
||||||
|
|
||||||
|
fn get_relation(
|
||||||
|
&self,
|
||||||
|
issued_for: &PK,
|
||||||
|
issued_by: &PK,
|
||||||
|
) -> Result<Option<TrustRelation>, Self::Error> {
|
||||||
|
let mut cursor = self
|
||||||
|
.connection
|
||||||
|
.prepare(
|
||||||
|
"SELECT relation_type, issued_for, issued_by, issued_at, expires_at, signature \
|
||||||
|
FROM trust_relations WHERE issued_by = ? AND issued_for = ?",
|
||||||
|
)?
|
||||||
|
.cursor();
|
||||||
|
|
||||||
|
cursor.bind(&[
|
||||||
|
Value::String(format!("{}", issued_by)),
|
||||||
|
Value::String(format!("{}", issued_for)),
|
||||||
|
])?;
|
||||||
|
|
||||||
|
if let Some(row) = cursor.next()? {
|
||||||
|
parse_relation(row).map(Some)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return all auths issued for pk
|
||||||
|
fn get_authorizations(&self, pk: &PublicKeyHashable) -> Result<Vec<Auth>, Self::Error> {
|
||||||
|
let mut cursor = self
|
||||||
|
.connection
|
||||||
|
.prepare(
|
||||||
|
"SELECT relation_type, issued_for, issued_by, issued_at, expires_at, signature \
|
||||||
|
FROM trust_relations WHERE issued_for = ? and relation_type = ?",
|
||||||
|
)?
|
||||||
|
.cursor();
|
||||||
|
|
||||||
|
cursor.bind(&[Value::String(format!("{}", pk)), Value::Integer(AUTH_TYPE)])?;
|
||||||
|
let mut auths: Vec<Auth> = vec![];
|
||||||
|
|
||||||
|
while let Some(row) = cursor.next()? {
|
||||||
|
if let TrustRelation::Auth(auth) = parse_relation(row)? {
|
||||||
|
auths.push(auth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(auths)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&mut self, relation: TrustRelation) -> Result<(), Self::Error> {
|
||||||
|
let mut statement = self
|
||||||
|
.connection
|
||||||
|
.prepare("INSERT OR REPLACE INTO trust_relations VALUES (?, ?, ?, ?, ?, ?)")?;
|
||||||
|
|
||||||
|
let relation_type = match relation {
|
||||||
|
TrustRelation::Auth(_) => AUTH_TYPE,
|
||||||
|
TrustRelation::Revoke(_) => REVOKE_TYPE,
|
||||||
|
};
|
||||||
|
|
||||||
|
statement.bind(1, &Value::Integer(relation_type))?;
|
||||||
|
statement.bind(
|
||||||
|
2,
|
||||||
|
&Value::String(format!("{}", relation.issued_for().as_ref())),
|
||||||
|
)?;
|
||||||
|
statement.bind(
|
||||||
|
3,
|
||||||
|
&Value::String(format!("{}", relation.issued_by().as_ref())),
|
||||||
|
)?;
|
||||||
|
statement.bind(4, &Value::Integer(relation.issued_at().as_secs() as i64))?;
|
||||||
|
statement.bind(5, &Value::Integer(relation.expires_at().as_secs() as i64))?;
|
||||||
|
statement.bind(6, &Value::Binary(relation.signature().encode()))?;
|
||||||
|
|
||||||
|
statement.next()?;
|
||||||
|
Ok({})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_root_weight_factor(&self, pk: &PK) -> Result<Option<WeightFactor>, Self::Error> {
|
||||||
|
let mut cursor = self
|
||||||
|
.connection
|
||||||
|
.prepare("SELECT public_key, weight_factor FROM roots WHERE public_key = ?")?
|
||||||
|
.cursor();
|
||||||
|
|
||||||
|
cursor.bind(&[Value::String(format!("{}", pk))])?;
|
||||||
|
|
||||||
|
if let Some(row) = cursor.next()? {
|
||||||
|
let w = u32::try_from(row[1].as_integer().ok_or(WeightFactorConversionDB)?)
|
||||||
|
.map_err(|_e| WeightFactorConversionDB)?;
|
||||||
|
|
||||||
|
Ok(Some(w))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_root_weight_factor(
|
||||||
|
&mut self,
|
||||||
|
pk: PK,
|
||||||
|
weight_factor: WeightFactor,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let mut cursor = self
|
||||||
|
.connection
|
||||||
|
.prepare("INSERT OR REPLACE INTO roots VALUES (?, ?)")?
|
||||||
|
.cursor();
|
||||||
|
|
||||||
|
cursor.bind(&[
|
||||||
|
Value::String(format!("{}", pk)),
|
||||||
|
Value::Integer(i64::from(weight_factor)),
|
||||||
|
])?;
|
||||||
|
|
||||||
|
cursor.next()?;
|
||||||
|
Ok({})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root_keys(&self) -> Result<Vec<PK>, Self::Error> {
|
||||||
|
let mut cursor = self
|
||||||
|
.connection
|
||||||
|
.prepare("SELECT public_key, weight_factor FROM roots")?
|
||||||
|
.cursor();
|
||||||
|
|
||||||
|
let mut roots = vec![];
|
||||||
|
|
||||||
|
while let Some(row) = cursor.next()? {
|
||||||
|
log::info!("row: {:?}", row);
|
||||||
|
let pk = row[0].as_string().ok_or(PublicKeyConversion)?;
|
||||||
|
let pk: PK = PK::from_str(pk).map_err(|e| PublicKeyFromStr(e.to_string()))?;
|
||||||
|
|
||||||
|
roots.push(pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(roots)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn revoke(&mut self, revoke: Revoke) -> Result<(), Self::Error> {
|
||||||
|
self.update_relation(TrustRelation::Revoke(revoke))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_auth(&mut self, auth: Auth, _cur_time: Duration) -> Result<(), Self::Error> {
|
||||||
|
self.update_relation(TrustRelation::Auth(auth))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_expired(&mut self, cur_time: Duration) -> Result<(), Self::Error> {
|
||||||
|
let mut cursor = self
|
||||||
|
.connection
|
||||||
|
.prepare("DELETE FROM trust_relations WHERE expires_at <= ? AND relation_type = ?")?
|
||||||
|
.cursor();
|
||||||
|
|
||||||
|
cursor.bind(&[
|
||||||
|
Value::Integer(cur_time.as_secs() as i64),
|
||||||
|
Value::Integer(AUTH_TYPE),
|
||||||
|
])?;
|
||||||
|
|
||||||
|
cursor.next()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
832
service/src/tests.rs
Normal file
832
service/src/tests.rs
Normal file
@ -0,0 +1,832 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod service_tests {
|
||||||
|
marine_rs_sdk_test::include_test_env!("/marine_test_env.rs");
|
||||||
|
use crate::service_impl::{
|
||||||
|
ServiceError, TRUSTED_TIMESTAMP_FUNCTION_NAME, TRUSTED_TIMESTAMP_SERVICE_ID,
|
||||||
|
};
|
||||||
|
use crate::storage_impl::DB_PATH;
|
||||||
|
use fluence_keypair::KeyPair;
|
||||||
|
use libp2p_core::PeerId;
|
||||||
|
use marine_rs_sdk::{CallParameters, SecurityTetraplet};
|
||||||
|
use marine_test_env::trust_graph::{Certificate, Revoke, ServiceInterface, Trust};
|
||||||
|
use rusqlite::Connection;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
static HOST_ID: &str = "some_host_id";
|
||||||
|
|
||||||
|
struct Auth {
|
||||||
|
issuer: PeerId,
|
||||||
|
trust: Trust,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Trust {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.expires_at == other.expires_at
|
||||||
|
&& self.issued_at == other.issued_at
|
||||||
|
&& self.issued_for == other.issued_for
|
||||||
|
&& self.signature == other.signature
|
||||||
|
&& self.sig_type == other.sig_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Trust {}
|
||||||
|
|
||||||
|
fn current_time() -> u64 {
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_env() {
|
||||||
|
let connection = Connection::open(DB_PATH).unwrap();
|
||||||
|
|
||||||
|
connection
|
||||||
|
.execute("DELETE FROM trust_relations", [])
|
||||||
|
.unwrap();
|
||||||
|
connection.execute("DELETE FROM roots", []).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_correct_timestamp_cp(arg_number: usize) -> CallParameters {
|
||||||
|
get_correct_timestamp_cp_with_host_id(arg_number, HOST_ID.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_correct_timestamp_cp_with_host_id(arg_number: usize, host_id: String) -> CallParameters {
|
||||||
|
let mut cp = CallParameters {
|
||||||
|
host_id: host_id.clone(),
|
||||||
|
..CallParameters::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
for _ in 0..arg_number {
|
||||||
|
cp.tetraplets.push(vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
cp.tetraplets.push(vec![SecurityTetraplet {
|
||||||
|
peer_pk: host_id,
|
||||||
|
service_id: TRUSTED_TIMESTAMP_SERVICE_ID.to_string(),
|
||||||
|
function_name: TRUSTED_TIMESTAMP_FUNCTION_NAME.to_string(),
|
||||||
|
json_path: "".to_string(),
|
||||||
|
}]);
|
||||||
|
|
||||||
|
cp
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_root_peer_id(trust_graph: &mut ServiceInterface, peer_id: PeerId, weight_factor: u32) {
|
||||||
|
let result = trust_graph.add_root(peer_id.to_base58(), weight_factor);
|
||||||
|
assert!(result.success, "{}", result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_root_with_trust(
|
||||||
|
trust_graph: &mut ServiceInterface,
|
||||||
|
issuer_kp: &KeyPair,
|
||||||
|
issued_at_sec: u64,
|
||||||
|
expires_at_sec: u64,
|
||||||
|
weight_factor: u32,
|
||||||
|
) -> Trust {
|
||||||
|
let result = trust_graph.add_root(issuer_kp.get_peer_id().to_base58(), weight_factor);
|
||||||
|
assert!(result.success, "{}", result.error);
|
||||||
|
add_trust(
|
||||||
|
trust_graph,
|
||||||
|
issuer_kp,
|
||||||
|
&issuer_kp.get_peer_id(),
|
||||||
|
issued_at_sec,
|
||||||
|
expires_at_sec,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue_trust(
|
||||||
|
trust_graph: &mut ServiceInterface,
|
||||||
|
issuer_kp: &KeyPair,
|
||||||
|
issued_for: &PeerId,
|
||||||
|
issued_at_sec: u64,
|
||||||
|
expires_at_sec: u64,
|
||||||
|
) -> Trust {
|
||||||
|
let result =
|
||||||
|
trust_graph.get_trust_bytes(issued_for.to_base58(), expires_at_sec, issued_at_sec);
|
||||||
|
assert!(result.success, "{}", result.error);
|
||||||
|
|
||||||
|
let trust_bytes = issuer_kp.sign(&result.result).unwrap().to_vec().to_vec();
|
||||||
|
let issue_result = trust_graph.issue_trust(
|
||||||
|
issued_for.to_base58(),
|
||||||
|
expires_at_sec,
|
||||||
|
issued_at_sec,
|
||||||
|
trust_bytes,
|
||||||
|
);
|
||||||
|
assert!(issue_result.success, "{}", issue_result.error);
|
||||||
|
|
||||||
|
issue_result.trust
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue_root_trust(
|
||||||
|
trust_graph: &mut ServiceInterface,
|
||||||
|
issuer_kp: &KeyPair,
|
||||||
|
issued_at_sec: u64,
|
||||||
|
expires_at_sec: u64,
|
||||||
|
) -> Trust {
|
||||||
|
issue_trust(
|
||||||
|
trust_graph,
|
||||||
|
issuer_kp,
|
||||||
|
&issuer_kp.get_peer_id(),
|
||||||
|
issued_at_sec,
|
||||||
|
expires_at_sec,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_trust(
|
||||||
|
trust_graph: &mut ServiceInterface,
|
||||||
|
issuer_kp: &KeyPair,
|
||||||
|
issued_for: &PeerId,
|
||||||
|
issued_at_sec: u64,
|
||||||
|
expires_at_sec: u64,
|
||||||
|
) -> Trust {
|
||||||
|
let trust = issue_trust(
|
||||||
|
trust_graph,
|
||||||
|
issuer_kp,
|
||||||
|
issued_for,
|
||||||
|
issued_at_sec,
|
||||||
|
expires_at_sec,
|
||||||
|
);
|
||||||
|
let add_trust_result = trust_graph.add_trust_cp(
|
||||||
|
trust.clone(),
|
||||||
|
issuer_kp.get_peer_id().to_base58(),
|
||||||
|
issued_at_sec,
|
||||||
|
get_correct_timestamp_cp(2),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(add_trust_result.success, "{}", add_trust_result.error);
|
||||||
|
|
||||||
|
trust
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_trust_checked(
|
||||||
|
trust_graph: &mut ServiceInterface,
|
||||||
|
trust: Trust,
|
||||||
|
issuer_peer_id: PeerId,
|
||||||
|
cur_time: u64,
|
||||||
|
) {
|
||||||
|
let result = trust_graph.add_trust_cp(
|
||||||
|
trust,
|
||||||
|
issuer_peer_id.to_base58(),
|
||||||
|
cur_time,
|
||||||
|
get_correct_timestamp_cp(2),
|
||||||
|
);
|
||||||
|
assert!(result.success, "{}", result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_trusts(trust_graph: &mut ServiceInterface, trusts: &[Auth], cur_time: u64) {
|
||||||
|
for auth in trusts.iter() {
|
||||||
|
add_trust_checked(trust_graph, auth.trust.clone(), auth.issuer, cur_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn revoke(
|
||||||
|
trust_graph: &mut ServiceInterface,
|
||||||
|
issuer_kp: &KeyPair,
|
||||||
|
revoked_peer_id: &PeerId,
|
||||||
|
revoked_at_sec: u64,
|
||||||
|
) -> Revoke {
|
||||||
|
let result = trust_graph.get_revoke_bytes(revoked_peer_id.to_base58(), revoked_at_sec);
|
||||||
|
assert!(result.success, "{}", result.error);
|
||||||
|
|
||||||
|
let revoke_bytes = issuer_kp.sign(&result.result).unwrap().to_vec().to_vec();
|
||||||
|
let issue_result = trust_graph.issue_revocation(
|
||||||
|
revoked_peer_id.to_base58(),
|
||||||
|
issuer_kp.get_peer_id().to_base58(),
|
||||||
|
revoked_at_sec,
|
||||||
|
revoke_bytes,
|
||||||
|
);
|
||||||
|
assert!(issue_result.success, "{}", issue_result.error);
|
||||||
|
|
||||||
|
let revoke_result = trust_graph.revoke_cp(
|
||||||
|
issue_result.revoke.clone(),
|
||||||
|
revoked_at_sec,
|
||||||
|
get_correct_timestamp_cp(1),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(revoke_result.success, "{}", revoke_result.error);
|
||||||
|
|
||||||
|
issue_result.revoke
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_trust_chain_with(
|
||||||
|
trust_graph: &mut ServiceInterface,
|
||||||
|
len: usize,
|
||||||
|
// Map of index to keypair. These key pairs will be used in trust chains at the given indexes
|
||||||
|
keys: HashMap<usize, KeyPair>,
|
||||||
|
expires_at: u64,
|
||||||
|
issued_at: u64,
|
||||||
|
) -> (Vec<KeyPair>, Vec<Auth>) {
|
||||||
|
assert!(len > 2);
|
||||||
|
|
||||||
|
let root_kp = KeyPair::generate_ed25519();
|
||||||
|
let second_kp = KeyPair::generate_ed25519();
|
||||||
|
|
||||||
|
let root_trust = issue_root_trust(trust_graph, &root_kp, issued_at, expires_at);
|
||||||
|
let second_trust = issue_trust(
|
||||||
|
trust_graph,
|
||||||
|
&root_kp,
|
||||||
|
&second_kp.get_peer_id(),
|
||||||
|
issued_at,
|
||||||
|
expires_at,
|
||||||
|
);
|
||||||
|
let mut chain = vec![
|
||||||
|
Auth {
|
||||||
|
issuer: root_kp.get_peer_id(),
|
||||||
|
trust: root_trust,
|
||||||
|
},
|
||||||
|
Auth {
|
||||||
|
issuer: root_kp.get_peer_id(),
|
||||||
|
trust: second_trust,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut key_pairs = vec![root_kp, second_kp];
|
||||||
|
|
||||||
|
for idx in 2..len {
|
||||||
|
let kp = keys
|
||||||
|
.get(&idx)
|
||||||
|
.unwrap_or(&KeyPair::generate_ed25519())
|
||||||
|
.clone();
|
||||||
|
let previous_kp = &key_pairs[idx - 1];
|
||||||
|
|
||||||
|
let trust = issue_trust(
|
||||||
|
trust_graph,
|
||||||
|
&previous_kp,
|
||||||
|
&kp.get_peer_id(),
|
||||||
|
issued_at,
|
||||||
|
expires_at,
|
||||||
|
);
|
||||||
|
chain.push(Auth {
|
||||||
|
issuer: previous_kp.get_peer_id(),
|
||||||
|
trust,
|
||||||
|
});
|
||||||
|
key_pairs.push(kp);
|
||||||
|
}
|
||||||
|
|
||||||
|
(key_pairs, chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_trust_chain_with_len(
|
||||||
|
trust_graph: &mut ServiceInterface,
|
||||||
|
len: usize,
|
||||||
|
keys: HashMap<usize, KeyPair>,
|
||||||
|
) -> (Vec<KeyPair>, Vec<Auth>) {
|
||||||
|
let cur_time = current_time();
|
||||||
|
let far_future = cur_time + 60;
|
||||||
|
|
||||||
|
generate_trust_chain_with(trust_graph, len, keys, far_future, cur_time)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_weight(trust_graph: &mut ServiceInterface, peer_id: PeerId, cur_time: u64) -> u32 {
|
||||||
|
let result =
|
||||||
|
trust_graph.get_weight_cp(peer_id.to_base58(), cur_time, get_correct_timestamp_cp(1));
|
||||||
|
assert!(result.success, "{}", result.error);
|
||||||
|
result.weight
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_certs(
|
||||||
|
trust_graph: &mut ServiceInterface,
|
||||||
|
issued_for: PeerId,
|
||||||
|
cur_time: u64,
|
||||||
|
) -> Vec<Certificate> {
|
||||||
|
let result = trust_graph.get_all_certs_cp(
|
||||||
|
issued_for.to_base58(),
|
||||||
|
cur_time,
|
||||||
|
get_correct_timestamp_cp(1),
|
||||||
|
);
|
||||||
|
assert!(result.success, "{}", result.error);
|
||||||
|
result.certificates
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_root_not_owner() {
|
||||||
|
let mut trust_graph = ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
let cp = CallParameters {
|
||||||
|
init_peer_id: "other_peer_id".to_string(),
|
||||||
|
service_creator_peer_id: "some_peer_id".to_string(),
|
||||||
|
..CallParameters::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let some_peer_id = KeyPair::generate_ed25519().get_peer_id();
|
||||||
|
let result = trust_graph.add_root_cp(some_peer_id.to_base58(), 0, cp);
|
||||||
|
assert!(!result.success);
|
||||||
|
assert_eq!(result.error, ServiceError::NotOwner.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_root_owner() {
|
||||||
|
let mut trust_graph = ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
let peer_id = "some_peer_id".to_string();
|
||||||
|
|
||||||
|
let cp = CallParameters {
|
||||||
|
init_peer_id: peer_id.clone(),
|
||||||
|
service_creator_peer_id: peer_id,
|
||||||
|
..CallParameters::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let some_peer_id = KeyPair::generate_ed25519().get_peer_id();
|
||||||
|
let result = trust_graph.add_root_cp(some_peer_id.to_base58(), 0, cp);
|
||||||
|
assert!(result.success, "{}", result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_root_trust() {
|
||||||
|
let mut trust_graph = ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
|
||||||
|
let root_kp = KeyPair::generate_ed25519();
|
||||||
|
let root_peer_id = root_kp.get_peer_id();
|
||||||
|
let expires_at_sec = 9999u64;
|
||||||
|
let issued_at_sec = 0u64;
|
||||||
|
|
||||||
|
add_root_peer_id(&mut trust_graph, root_kp.get_peer_id(), 4u32);
|
||||||
|
|
||||||
|
let result =
|
||||||
|
trust_graph.get_trust_bytes(root_peer_id.to_base58(), expires_at_sec, issued_at_sec);
|
||||||
|
assert!(result.success, "{}", result.error);
|
||||||
|
let trust_bytes = root_kp.sign(&result.result).unwrap().to_vec().to_vec();
|
||||||
|
|
||||||
|
let issue_result = trust_graph.issue_trust(
|
||||||
|
root_peer_id.to_base58(),
|
||||||
|
expires_at_sec,
|
||||||
|
issued_at_sec,
|
||||||
|
trust_bytes,
|
||||||
|
);
|
||||||
|
assert!(issue_result.success, "{}", issue_result.error);
|
||||||
|
|
||||||
|
let verify_result = trust_graph.verify_trust_cp(
|
||||||
|
issue_result.trust.clone(),
|
||||||
|
root_peer_id.to_base58(),
|
||||||
|
100u64,
|
||||||
|
get_correct_timestamp_cp(2),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(verify_result.success, "{}", verify_result.error);
|
||||||
|
|
||||||
|
let add_trust_result = trust_graph.add_trust_cp(
|
||||||
|
issue_result.trust,
|
||||||
|
root_peer_id.to_base58(),
|
||||||
|
100u64,
|
||||||
|
get_correct_timestamp_cp(2),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(add_trust_result.success, "{}", add_trust_result.error);
|
||||||
|
assert_eq!(
|
||||||
|
get_weight(&mut trust_graph, root_peer_id, 100u64),
|
||||||
|
add_trust_result.weight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expired_root_trust() {
|
||||||
|
let mut trust_graph = marine_test_env::trust_graph::ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
|
||||||
|
let root_kp = KeyPair::generate_ed25519();
|
||||||
|
let cur_time = 100u64;
|
||||||
|
let root_expired_time = cur_time + 10000;
|
||||||
|
add_root_with_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root_kp,
|
||||||
|
cur_time,
|
||||||
|
root_expired_time - 1,
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
|
||||||
|
let trust_kp = KeyPair::generate_ed25519();
|
||||||
|
add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root_kp,
|
||||||
|
&trust_kp.get_peer_id(),
|
||||||
|
cur_time,
|
||||||
|
root_expired_time + 99999,
|
||||||
|
);
|
||||||
|
|
||||||
|
let root_weight = get_weight(&mut trust_graph, root_kp.get_peer_id(), cur_time);
|
||||||
|
let trust_weight = get_weight(&mut trust_graph, trust_kp.get_peer_id(), cur_time);
|
||||||
|
assert_eq!(root_weight / 2, trust_weight);
|
||||||
|
|
||||||
|
let certs = get_all_certs(&mut trust_graph, trust_kp.get_peer_id(), cur_time);
|
||||||
|
assert_eq!(certs.len(), 1);
|
||||||
|
|
||||||
|
// get all certs after root expiration
|
||||||
|
let certs = get_all_certs(&mut trust_graph, trust_kp.get_peer_id(), root_expired_time);
|
||||||
|
assert_eq!(certs.len(), 0);
|
||||||
|
|
||||||
|
// check garbage collector
|
||||||
|
let certs = get_all_certs(&mut trust_graph, trust_kp.get_peer_id(), cur_time);
|
||||||
|
assert_eq!(certs.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn revoke_test() {
|
||||||
|
let mut trust_graph = marine_test_env::trust_graph::ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
|
||||||
|
let root_kp = KeyPair::generate_ed25519();
|
||||||
|
let mut cur_time = 100u64;
|
||||||
|
add_root_with_trust(&mut trust_graph, &root_kp, cur_time, cur_time + 9999, 4u32);
|
||||||
|
|
||||||
|
let trust_kp = KeyPair::generate_ed25519();
|
||||||
|
add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root_kp,
|
||||||
|
&trust_kp.get_peer_id(),
|
||||||
|
cur_time,
|
||||||
|
cur_time + 99999,
|
||||||
|
);
|
||||||
|
|
||||||
|
let weight = get_weight(&mut trust_graph, trust_kp.get_peer_id(), cur_time);
|
||||||
|
assert_ne!(weight, 0u32);
|
||||||
|
|
||||||
|
cur_time += 1;
|
||||||
|
revoke(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root_kp,
|
||||||
|
&trust_kp.get_peer_id(),
|
||||||
|
cur_time,
|
||||||
|
);
|
||||||
|
|
||||||
|
let weight = get_weight(&mut trust_graph, trust_kp.get_peer_id(), cur_time);
|
||||||
|
assert_eq!(weight, 0u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_one_trust_to_cert_last() {
|
||||||
|
let mut trust_graph = ServiceInterface::new();
|
||||||
|
let (key_pairs, mut trusts) =
|
||||||
|
generate_trust_chain_with_len(&mut trust_graph, 5, HashMap::new());
|
||||||
|
let cur_time = current_time();
|
||||||
|
|
||||||
|
let root_peer_id = key_pairs[0].get_peer_id();
|
||||||
|
add_root_peer_id(&mut trust_graph, root_peer_id, 2);
|
||||||
|
add_trusts(&mut trust_graph, &trusts, cur_time);
|
||||||
|
|
||||||
|
let issued_by = key_pairs.last().unwrap().get_peer_id();
|
||||||
|
let trust_kp = KeyPair::generate_ed25519();
|
||||||
|
let issued_for = trust_kp.get_peer_id();
|
||||||
|
let future = cur_time + 60;
|
||||||
|
let trust = add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&key_pairs.last().unwrap(),
|
||||||
|
&issued_for,
|
||||||
|
cur_time,
|
||||||
|
future,
|
||||||
|
);
|
||||||
|
trusts.push(Auth {
|
||||||
|
issuer: issued_by,
|
||||||
|
trust,
|
||||||
|
});
|
||||||
|
|
||||||
|
let previous_weight = get_weight(&mut trust_graph, issued_by, cur_time);
|
||||||
|
assert_ne!(previous_weight, 0u32);
|
||||||
|
|
||||||
|
let weight = get_weight(&mut trust_graph, issued_for, cur_time);
|
||||||
|
assert_eq!(weight * 2, previous_weight);
|
||||||
|
|
||||||
|
let certs = get_all_certs(&mut trust_graph, issued_for, cur_time);
|
||||||
|
assert_eq!(certs.len(), 1);
|
||||||
|
|
||||||
|
for (i, trust) in certs[0].chain.iter().enumerate() {
|
||||||
|
assert_eq!(*trust, trusts[i].trust);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expired_trust() {
|
||||||
|
let mut trust_graph = ServiceInterface::new();
|
||||||
|
let (key_pairs, mut trusts) =
|
||||||
|
generate_trust_chain_with_len(&mut trust_graph, 5, HashMap::new());
|
||||||
|
let cur_time = current_time();
|
||||||
|
|
||||||
|
let root1_peer_id = key_pairs[0].get_peer_id();
|
||||||
|
add_root_peer_id(&mut trust_graph, root1_peer_id, 2);
|
||||||
|
add_trusts(&mut trust_graph, &trusts, cur_time);
|
||||||
|
|
||||||
|
let issued_by = key_pairs.last().unwrap().get_peer_id();
|
||||||
|
let trust_kp = KeyPair::generate_ed25519();
|
||||||
|
let issued_for = trust_kp.get_peer_id();
|
||||||
|
let expired_time = cur_time + 60;
|
||||||
|
|
||||||
|
let trust = add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&key_pairs.last().unwrap(),
|
||||||
|
&issued_for,
|
||||||
|
cur_time,
|
||||||
|
expired_time,
|
||||||
|
);
|
||||||
|
trusts.push(Auth {
|
||||||
|
issuer: issued_by,
|
||||||
|
trust,
|
||||||
|
});
|
||||||
|
|
||||||
|
let certs = get_all_certs(&mut trust_graph, issued_for, cur_time);
|
||||||
|
assert_eq!(certs.len(), 1);
|
||||||
|
for (i, trust) in certs[0].chain.iter().enumerate() {
|
||||||
|
assert_eq!(*trust, trusts[i].trust);
|
||||||
|
}
|
||||||
|
|
||||||
|
let certs = get_all_certs(&mut trust_graph, issued_for, expired_time);
|
||||||
|
assert_eq!(certs.len(), 0);
|
||||||
|
|
||||||
|
// check garbage collector
|
||||||
|
let certs = get_all_certs(&mut trust_graph, issued_for, cur_time);
|
||||||
|
assert_eq!(certs.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_one_cert() {
|
||||||
|
let mut trust_graph = ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
let (key_pairs, trusts) =
|
||||||
|
generate_trust_chain_with_len(&mut trust_graph, 5, HashMap::new());
|
||||||
|
|
||||||
|
let cur_time = current_time();
|
||||||
|
let root_peer_id = key_pairs[0].get_peer_id();
|
||||||
|
add_root_peer_id(&mut trust_graph, root_peer_id, 1);
|
||||||
|
|
||||||
|
for auth in trusts.iter() {
|
||||||
|
add_trust_checked(&mut trust_graph, auth.trust.clone(), auth.issuer, cur_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
let certs = trust_graph.get_all_certs_cp(
|
||||||
|
key_pairs.last().unwrap().get_peer_id().to_base58(),
|
||||||
|
cur_time,
|
||||||
|
get_correct_timestamp_cp(1),
|
||||||
|
);
|
||||||
|
assert!(certs.success, "{}", certs.error);
|
||||||
|
let certs = certs.certificates;
|
||||||
|
assert_eq!(certs.len(), 1);
|
||||||
|
|
||||||
|
for (i, trust) in certs[0].chain.iter().enumerate() {
|
||||||
|
assert_eq!(*trust, trusts[i].trust);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chain_from_root_to_another_root() {
|
||||||
|
let mut trust_graph = ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
let (kps, trusts) = generate_trust_chain_with_len(&mut trust_graph, 6, HashMap::new());
|
||||||
|
let cur_time = current_time();
|
||||||
|
let far_future = cur_time + 9999;
|
||||||
|
|
||||||
|
// add first and last trusts as roots
|
||||||
|
add_root_peer_id(&mut trust_graph, kps[0].get_peer_id(), 0);
|
||||||
|
add_trusts(&mut trust_graph, &trusts, cur_time);
|
||||||
|
add_root_with_trust(&mut trust_graph, &kps[5], cur_time, far_future, 0);
|
||||||
|
|
||||||
|
let certs = get_all_certs(&mut trust_graph, kps[5].get_peer_id(), cur_time);
|
||||||
|
// first with self-signed last trust, second - without
|
||||||
|
assert_eq!(certs.len(), 2);
|
||||||
|
assert_eq!(certs[0].chain.len(), 6);
|
||||||
|
assert_eq!(certs[1].chain.len(), 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_revoke_gc() {
|
||||||
|
let mut trust_graph = marine_test_env::trust_graph::ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
|
||||||
|
let root_kp = KeyPair::generate_ed25519();
|
||||||
|
let cur_time = 100u64;
|
||||||
|
add_root_with_trust(&mut trust_graph, &root_kp, cur_time, cur_time + 999, 4u32);
|
||||||
|
|
||||||
|
let trust_kp = KeyPair::generate_ed25519();
|
||||||
|
add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root_kp,
|
||||||
|
&trust_kp.get_peer_id(),
|
||||||
|
cur_time,
|
||||||
|
cur_time + 99999,
|
||||||
|
);
|
||||||
|
|
||||||
|
let weight = get_weight(&mut trust_graph, trust_kp.get_peer_id(), cur_time);
|
||||||
|
assert_ne!(weight, 0u32);
|
||||||
|
|
||||||
|
let revoked_time = cur_time + 1;
|
||||||
|
revoke(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root_kp,
|
||||||
|
&trust_kp.get_peer_id(),
|
||||||
|
revoked_time,
|
||||||
|
);
|
||||||
|
|
||||||
|
let weight = get_weight(&mut trust_graph, trust_kp.get_peer_id(), revoked_time);
|
||||||
|
assert_eq!(weight, 0u32);
|
||||||
|
|
||||||
|
// add trust issued earlier than last revoke
|
||||||
|
add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root_kp,
|
||||||
|
&trust_kp.get_peer_id(),
|
||||||
|
revoked_time - 10,
|
||||||
|
cur_time + 99999,
|
||||||
|
);
|
||||||
|
|
||||||
|
let weight = get_weight(&mut trust_graph, trust_kp.get_peer_id(), revoked_time);
|
||||||
|
assert_eq!(weight, 0u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_trust() {
|
||||||
|
let mut trust_graph = marine_test_env::trust_graph::ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
|
||||||
|
let root_kp = KeyPair::generate_ed25519();
|
||||||
|
let mut cur_time = 100u64;
|
||||||
|
add_root_with_trust(&mut trust_graph, &root_kp, cur_time, cur_time + 999, 4u32);
|
||||||
|
|
||||||
|
let trust_kp = KeyPair::generate_ed25519();
|
||||||
|
let expires_at_sec = cur_time + 10;
|
||||||
|
add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root_kp,
|
||||||
|
&trust_kp.get_peer_id(),
|
||||||
|
cur_time,
|
||||||
|
expires_at_sec,
|
||||||
|
);
|
||||||
|
|
||||||
|
let weight = get_weight(&mut trust_graph, trust_kp.get_peer_id(), cur_time);
|
||||||
|
assert_ne!(weight, 0u32);
|
||||||
|
|
||||||
|
cur_time = expires_at_sec - 1;
|
||||||
|
let future_time = expires_at_sec + 10;
|
||||||
|
// add trust that expires lately
|
||||||
|
add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root_kp,
|
||||||
|
&trust_kp.get_peer_id(),
|
||||||
|
cur_time,
|
||||||
|
future_time + 99999,
|
||||||
|
);
|
||||||
|
|
||||||
|
// first trust should be replaced by second (and has already been expired)
|
||||||
|
let weight = get_weight(&mut trust_graph, trust_kp.get_peer_id(), future_time);
|
||||||
|
assert_ne!(weight, 0u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn path_from_root_to_root_weight() {
|
||||||
|
let mut trust_graph = marine_test_env::trust_graph::ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
|
||||||
|
let root1_kp = KeyPair::generate_ed25519();
|
||||||
|
let root2_kp = KeyPair::generate_ed25519();
|
||||||
|
let cur_time = 100;
|
||||||
|
let far_future = cur_time + 99999;
|
||||||
|
// root with bigger weight (smaller weight factor)
|
||||||
|
add_root_with_trust(&mut trust_graph, &root1_kp, cur_time, far_future, 0u32);
|
||||||
|
// opposite
|
||||||
|
add_root_with_trust(&mut trust_graph, &root2_kp, cur_time, far_future, 5u32);
|
||||||
|
|
||||||
|
// issue trust from root2 to any other peer_id
|
||||||
|
let issued_by_root2_peer_id = KeyPair::generate_ed25519().get_peer_id();
|
||||||
|
add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root2_kp,
|
||||||
|
&issued_by_root2_peer_id,
|
||||||
|
cur_time,
|
||||||
|
far_future,
|
||||||
|
);
|
||||||
|
|
||||||
|
let root2_weight_before = get_weight(&mut trust_graph, root2_kp.get_peer_id(), cur_time);
|
||||||
|
let issued_by_root2_peer_id_before =
|
||||||
|
get_weight(&mut trust_graph, issued_by_root2_peer_id, cur_time);
|
||||||
|
// issue trust from root1 to root2
|
||||||
|
add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root1_kp,
|
||||||
|
&root2_kp.get_peer_id(),
|
||||||
|
cur_time,
|
||||||
|
far_future,
|
||||||
|
);
|
||||||
|
|
||||||
|
let root2_weight_after = get_weight(&mut trust_graph, root2_kp.get_peer_id(), cur_time);
|
||||||
|
let issued_by_root2_peer_id_after =
|
||||||
|
get_weight(&mut trust_graph, issued_by_root2_peer_id, cur_time);
|
||||||
|
|
||||||
|
assert!(issued_by_root2_peer_id_before < issued_by_root2_peer_id_after);
|
||||||
|
assert!(root2_weight_before < root2_weight_after);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_self_signed_weight() {
|
||||||
|
let mut trust_graph = marine_test_env::trust_graph::ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
|
||||||
|
let root_kp = KeyPair::generate_ed25519();
|
||||||
|
let cur_time = 100;
|
||||||
|
let far_future = cur_time + 99999;
|
||||||
|
|
||||||
|
add_root_with_trust(&mut trust_graph, &root_kp, cur_time, far_future, 0u32);
|
||||||
|
|
||||||
|
// issue trust from root to any other peer
|
||||||
|
let other_peer_kp = KeyPair::generate_ed25519();
|
||||||
|
add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&root_kp,
|
||||||
|
&other_peer_kp.get_peer_id(),
|
||||||
|
cur_time,
|
||||||
|
far_future,
|
||||||
|
);
|
||||||
|
|
||||||
|
let weight_before = get_weight(&mut trust_graph, other_peer_kp.get_peer_id(), cur_time);
|
||||||
|
|
||||||
|
// issue self-signed trust
|
||||||
|
add_trust(
|
||||||
|
&mut trust_graph,
|
||||||
|
&other_peer_kp,
|
||||||
|
&other_peer_kp.get_peer_id(),
|
||||||
|
cur_time,
|
||||||
|
far_future,
|
||||||
|
);
|
||||||
|
|
||||||
|
let weight_after = get_weight(&mut trust_graph, other_peer_kp.get_peer_id(), cur_time);
|
||||||
|
assert_eq!(weight_after, weight_before);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_one_host_cert() {
|
||||||
|
let mut trust_graph = ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
let (key_pairs, trusts) =
|
||||||
|
generate_trust_chain_with_len(&mut trust_graph, 5, HashMap::new());
|
||||||
|
|
||||||
|
let cur_time = current_time();
|
||||||
|
let root_peer_id = key_pairs[0].get_peer_id();
|
||||||
|
add_root_peer_id(&mut trust_graph, root_peer_id, 1);
|
||||||
|
|
||||||
|
for auth in trusts.iter() {
|
||||||
|
add_trust_checked(&mut trust_graph, auth.trust.clone(), auth.issuer, cur_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cp = get_correct_timestamp_cp_with_host_id(
|
||||||
|
0,
|
||||||
|
key_pairs.last().unwrap().get_peer_id().to_base58(),
|
||||||
|
);
|
||||||
|
let certs = trust_graph.get_host_certs_cp(cur_time, cp);
|
||||||
|
|
||||||
|
assert!(certs.success, "{}", certs.error);
|
||||||
|
let certs = certs.certificates;
|
||||||
|
assert_eq!(certs.len(), 1);
|
||||||
|
|
||||||
|
for (i, trust) in certs[0].chain.iter().enumerate() {
|
||||||
|
assert_eq!(*trust, trusts[i].trust);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_one_host_cert_from() {
|
||||||
|
let mut trust_graph = ServiceInterface::new();
|
||||||
|
clear_env();
|
||||||
|
let (key_pairs, mut trusts) =
|
||||||
|
generate_trust_chain_with_len(&mut trust_graph, 5, HashMap::new());
|
||||||
|
|
||||||
|
let cur_time = current_time();
|
||||||
|
let root_peer_id = key_pairs[0].get_peer_id();
|
||||||
|
add_root_peer_id(&mut trust_graph, root_peer_id, 1);
|
||||||
|
|
||||||
|
for auth in trusts.iter() {
|
||||||
|
add_trust_checked(&mut trust_graph, auth.trust.clone(), auth.issuer, cur_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cp = get_correct_timestamp_cp_with_host_id(
|
||||||
|
1,
|
||||||
|
key_pairs.last().unwrap().get_peer_id().to_base58(),
|
||||||
|
);
|
||||||
|
let certs = trust_graph.get_host_certs_from_cp(
|
||||||
|
key_pairs[3].get_peer_id().to_base58(),
|
||||||
|
cur_time,
|
||||||
|
cp,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(certs.success, "{}", certs.error);
|
||||||
|
let certs = certs.certificates;
|
||||||
|
assert_eq!(certs.len(), 1);
|
||||||
|
assert_eq!(certs[0].chain.len(), 5);
|
||||||
|
|
||||||
|
for (i, trust) in certs[0].chain.iter().enumerate() {
|
||||||
|
assert_eq!(*trust, trusts[i].trust);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -74,6 +74,47 @@ impl Certificate {
|
|||||||
Self { chain }
|
Self { chain }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_from_root_trust(root_trust: Trust, issued_trust: Trust, cur_time: Duration) -> Result<Self, CertificateError> {
|
||||||
|
Trust::verify(&root_trust, &root_trust.issued_for, cur_time).map_err(MalformedRoot)?;
|
||||||
|
Trust::verify(&issued_trust, &root_trust.issued_for, cur_time).map_err(|e| VerificationError(1, e))?;
|
||||||
|
|
||||||
|
Ok(Self { chain: vec![root_trust, issued_trust] })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn issue_with_trust(issued_by: PublicKey, trust: Trust, extend_cert: &Certificate, cur_time: Duration) -> Result<Self, CertificateError> {
|
||||||
|
if trust.expires_at.lt(&trust.issued_at) {
|
||||||
|
return Err(ExpirationError {
|
||||||
|
expires_at: format!("{:?}", trust.expires_at),
|
||||||
|
issued_at: format!("{:?}", trust.issued_at),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Certificate::verify(extend_cert, &[extend_cert.chain[0].issued_for.clone()], cur_time)?;
|
||||||
|
// check if `issued_by` is allowed to issue a certificate (i.e., there’s a trust for it in a chain)
|
||||||
|
let mut previous_trust_num: i32 = -1;
|
||||||
|
for pk_id in 0..extend_cert.chain.len() {
|
||||||
|
if extend_cert.chain[pk_id].issued_for == issued_by {
|
||||||
|
previous_trust_num = pk_id as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if previous_trust_num == -1 {
|
||||||
|
return Err(KeyInCertificateError);
|
||||||
|
};
|
||||||
|
|
||||||
|
// splitting old chain to add new trust after given public key
|
||||||
|
let mut new_chain = extend_cert
|
||||||
|
.chain
|
||||||
|
.split_at((previous_trust_num + 1) as usize)
|
||||||
|
.0
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
new_chain.push(trust);
|
||||||
|
|
||||||
|
Ok(Self { chain: new_chain })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Creates new certificate with root trust (self-signed public key) from a key pair.
|
/// Creates new certificate with root trust (self-signed public key) from a key pair.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn issue_root(
|
pub fn issue_root(
|
||||||
|
11
src/lib.rs
11
src/lib.rs
@ -25,6 +25,7 @@
|
|||||||
unused_unsafe,
|
unused_unsafe,
|
||||||
unreachable_patterns
|
unreachable_patterns
|
||||||
)]
|
)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
mod certificate;
|
mod certificate;
|
||||||
pub mod certificate_serde;
|
pub mod certificate_serde;
|
||||||
@ -34,13 +35,13 @@ mod revoke;
|
|||||||
mod trust;
|
mod trust;
|
||||||
mod trust_graph;
|
mod trust_graph;
|
||||||
mod trust_graph_storage;
|
mod trust_graph_storage;
|
||||||
mod trust_node;
|
mod trust_relation;
|
||||||
|
|
||||||
pub use crate::certificate::{Certificate, CertificateError};
|
pub use crate::certificate::{Certificate, CertificateError};
|
||||||
pub use crate::misc::current_time;
|
pub use crate::misc::current_time;
|
||||||
pub use crate::public_key_hashable::PublicKeyHashable;
|
pub use crate::public_key_hashable::PublicKeyHashable;
|
||||||
pub use crate::revoke::Revoke;
|
pub use crate::revoke::Revoke;
|
||||||
pub use crate::trust::Trust;
|
pub use crate::trust::{Trust, TrustError};
|
||||||
pub use crate::trust_graph::{TrustGraph, TrustGraphError, Weight};
|
pub use crate::trust_graph::{TrustGraph, TrustGraphError, WeightFactor, MAX_WEIGHT_FACTOR};
|
||||||
pub use crate::trust_graph_storage::{Storage, StorageError, InMemoryStorage, InMemoryStorageError};
|
pub use crate::trust_graph_storage::{Storage, StorageError};
|
||||||
pub use crate::trust_node::{Auth, TrustNode};
|
pub use crate::trust_relation::{Auth, TrustRelation};
|
||||||
|
@ -19,6 +19,7 @@ use fluence_keypair::key_pair::KeyPair;
|
|||||||
use fluence_keypair::public_key::PublicKey;
|
use fluence_keypair::public_key::PublicKey;
|
||||||
use fluence_keypair::signature::Signature;
|
use fluence_keypair::signature::Signature;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha2::Digest;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use thiserror::Error as ThisError;
|
use thiserror::Error as ThisError;
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ pub enum RevokeError {
|
|||||||
IncorrectSignature(
|
IncorrectSignature(
|
||||||
#[from]
|
#[from]
|
||||||
#[source]
|
#[source]
|
||||||
fluence_keypair::error::SigningError
|
fluence_keypair::error::VerificationError,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,12 +44,12 @@ pub struct Revoke {
|
|||||||
/// the issuer of this revocation
|
/// the issuer of this revocation
|
||||||
pub revoked_by: PublicKey,
|
pub revoked_by: PublicKey,
|
||||||
/// proof of this revocation
|
/// proof of this revocation
|
||||||
signature: Signature,
|
pub signature: Signature,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Revoke {
|
impl Revoke {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn new(
|
pub fn new(
|
||||||
pk: PublicKey,
|
pk: PublicKey,
|
||||||
revoked_by: PublicKey,
|
revoked_by: PublicKey,
|
||||||
revoked_at: Duration,
|
revoked_at: Duration,
|
||||||
@ -71,14 +72,14 @@ impl Revoke {
|
|||||||
Revoke::new(to_revoke, revoker.public(), revoked_at, signature)
|
Revoke::new(to_revoke, revoker.public(), revoked_at, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature_bytes(pk: &PublicKey, revoked_at: Duration) -> Vec<u8> {
|
pub fn signature_bytes(pk: &PublicKey, revoked_at: Duration) -> Vec<u8> {
|
||||||
let mut msg = Vec::new();
|
let mut metadata = Vec::new();
|
||||||
let pk_bytes = &pk.encode();
|
let pk_bytes = &pk.encode();
|
||||||
msg.push(pk_bytes.len() as u8);
|
metadata.push(pk_bytes.len() as u8);
|
||||||
msg.extend(pk_bytes);
|
metadata.extend(pk_bytes);
|
||||||
msg.extend_from_slice(&(revoked_at.as_secs() as u64).to_le_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.
|
/// Verifies that revocation is cryptographically correct.
|
||||||
@ -87,7 +88,8 @@ impl Revoke {
|
|||||||
|
|
||||||
revoke
|
revoke
|
||||||
.revoked_by
|
.revoked_by
|
||||||
.verify(msg.as_slice(), &revoke.signature).map_err(IncorrectSignature)
|
.verify(msg.as_slice(), &revoke.signature)
|
||||||
|
.map_err(IncorrectSignature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
src/trust.rs
11
src/trust.rs
@ -22,6 +22,7 @@ use fluence_keypair::key_pair::KeyPair;
|
|||||||
use fluence_keypair::public_key::PublicKey;
|
use fluence_keypair::public_key::PublicKey;
|
||||||
use fluence_keypair::signature::Signature;
|
use fluence_keypair::signature::Signature;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha2::Digest;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -67,7 +68,7 @@ pub enum TrustError {
|
|||||||
SignatureError(
|
SignatureError(
|
||||||
#[from]
|
#[from]
|
||||||
#[source]
|
#[source]
|
||||||
fluence_keypair::error::SigningError,
|
fluence_keypair::error::VerificationError,
|
||||||
),
|
),
|
||||||
|
|
||||||
/// Errors occurred on trust decoding from different formats
|
/// Errors occurred on trust decoding from different formats
|
||||||
@ -113,7 +114,7 @@ impl Trust {
|
|||||||
expires_at: Duration,
|
expires_at: Duration,
|
||||||
issued_at: Duration,
|
issued_at: Duration,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let msg = Self::metadata_bytes(&issued_for, expires_at, issued_at);
|
let msg = Self::signature_bytes(&issued_for, expires_at, issued_at);
|
||||||
|
|
||||||
let signature = issued_by.sign(msg.as_slice()).unwrap();
|
let signature = issued_by.sign(msg.as_slice()).unwrap();
|
||||||
|
|
||||||
@ -136,12 +137,12 @@ impl Trust {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let msg: &[u8] =
|
let msg: &[u8] =
|
||||||
&Self::metadata_bytes(&trust.issued_for, trust.expires_at, trust.issued_at);
|
&Self::signature_bytes(&trust.issued_for, trust.expires_at, trust.issued_at);
|
||||||
|
|
||||||
KeyPair::verify(issued_by, msg, &trust.signature).map_err(SignatureError)
|
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 signature_bytes(pk: &PublicKey, expires_at: Duration, issued_at: Duration) -> Vec<u8> {
|
||||||
let pk_encoded = pk.encode();
|
let pk_encoded = pk.encode();
|
||||||
let expires_at_encoded: [u8; EXPIRATION_LEN] = (expires_at.as_secs() as u64).to_le_bytes();
|
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();
|
let issued_at_encoded: [u8; ISSUED_LEN] = (issued_at.as_secs() as u64).to_le_bytes();
|
||||||
@ -151,7 +152,7 @@ impl Trust {
|
|||||||
metadata.extend_from_slice(&expires_at_encoded[0..EXPIRATION_LEN]);
|
metadata.extend_from_slice(&expires_at_encoded[0..EXPIRATION_LEN]);
|
||||||
metadata.extend_from_slice(&issued_at_encoded[0..ISSUED_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
|
/// Encode the trust into a byte array
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::certificate::CertificateError::{CertificateLengthError, Unexpected};
|
use crate::certificate::CertificateError::CertificateLengthError;
|
||||||
use crate::certificate::{Certificate, CertificateError};
|
use crate::certificate::{Certificate, CertificateError};
|
||||||
use crate::public_key_hashable::PublicKeyHashable as PK;
|
use crate::public_key_hashable::PublicKeyHashable as PK;
|
||||||
use crate::revoke::Revoke;
|
use crate::revoke::Revoke;
|
||||||
@ -24,8 +24,8 @@ use crate::trust_graph::TrustGraphError::{
|
|||||||
CertificateCheckError, EmptyChain, InternalStorageError, NoRoot,
|
CertificateCheckError, EmptyChain, InternalStorageError, NoRoot,
|
||||||
};
|
};
|
||||||
use crate::trust_graph_storage::Storage;
|
use crate::trust_graph_storage::Storage;
|
||||||
use crate::trust_node::{Auth, TrustNode};
|
use crate::trust_relation::Auth;
|
||||||
use crate::StorageError;
|
use crate::{StorageError, TrustError};
|
||||||
use fluence_keypair::public_key::PublicKey;
|
use fluence_keypair::public_key::PublicKey;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::collections::{HashSet, VecDeque};
|
use std::collections::{HashSet, VecDeque};
|
||||||
@ -35,7 +35,9 @@ use std::time::Duration;
|
|||||||
use thiserror::Error as ThisError;
|
use thiserror::Error as ThisError;
|
||||||
|
|
||||||
/// for simplicity, we store `n` where Weight = 1/n^2
|
/// for simplicity, we store `n` where Weight = 1/n^2
|
||||||
pub type Weight = u32;
|
pub type WeightFactor = u32;
|
||||||
|
|
||||||
|
pub static MAX_WEIGHT_FACTOR: u32 = 16;
|
||||||
|
|
||||||
/// Graph to efficiently calculate weights of certificates and get chains of certificates.
|
/// Graph to efficiently calculate weights of certificates and get chains of certificates.
|
||||||
/// TODO serialization/deserialization
|
/// TODO serialization/deserialization
|
||||||
@ -68,6 +70,14 @@ pub enum TrustGraphError {
|
|||||||
#[source]
|
#[source]
|
||||||
RevokeError,
|
RevokeError,
|
||||||
),
|
),
|
||||||
|
#[error("Path to {0} not found")]
|
||||||
|
AddTrustError(String),
|
||||||
|
#[error("Trust verification error: {0}")]
|
||||||
|
TrustVerificationError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
TrustError,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: StorageError + 'static> From<T> for TrustGraphError {
|
impl<T: StorageError + 'static> From<T> for TrustGraphError {
|
||||||
@ -82,7 +92,10 @@ impl From<TrustGraphError> for String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub fn get_weight_from_factor(wf: WeightFactor) -> u32 {
|
||||||
|
2u32.pow(MAX_WEIGHT_FACTOR.checked_sub(wf).unwrap_or(0u32))
|
||||||
|
}
|
||||||
|
|
||||||
impl<S> TrustGraph<S>
|
impl<S> TrustGraph<S>
|
||||||
where
|
where
|
||||||
S: Storage,
|
S: Storage,
|
||||||
@ -92,95 +105,104 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert new root weight
|
/// Insert new root weight
|
||||||
pub fn add_root_weight(
|
pub fn add_root_weight_factor(
|
||||||
&mut self,
|
&mut self,
|
||||||
pk: PublicKey,
|
pk: PublicKey,
|
||||||
weight: Weight,
|
weight: WeightFactor,
|
||||||
) -> Result<(), TrustGraphError> {
|
) -> Result<(), TrustGraphError> {
|
||||||
Ok(self.storage.add_root_weight(pk.into(), weight)?)
|
Ok(self.storage.add_root_weight_factor(pk.into(), weight)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get trust by public key
|
pub fn add_trust<T, P>(
|
||||||
pub fn get(&self, pk: PublicKey) -> Result<Option<TrustNode>, TrustGraphError> {
|
&mut self,
|
||||||
Ok(self.storage.get(&pk.into())?)
|
trust: T,
|
||||||
|
issued_by: P,
|
||||||
|
cur_time: Duration,
|
||||||
|
) -> Result<u32, TrustGraphError>
|
||||||
|
where
|
||||||
|
T: Borrow<Trust>,
|
||||||
|
P: Borrow<PublicKey>,
|
||||||
|
{
|
||||||
|
Trust::verify(trust.borrow(), issued_by.borrow(), cur_time)?;
|
||||||
|
let next_weight = self.get_next_weight(
|
||||||
|
issued_by.borrow().as_ref(),
|
||||||
|
trust.borrow().issued_for.as_ref(),
|
||||||
|
cur_time,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if next_weight == 0u32 {
|
||||||
|
return Ok(0u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
let auth = Auth {
|
||||||
|
trust: trust.borrow().clone(),
|
||||||
|
issued_by: issued_by.borrow().clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.storage.update_auth(auth, cur_time)?;
|
||||||
|
|
||||||
|
Ok(next_weight)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove cur_time from api, leave it for tests only
|
|
||||||
/// Certificate is a chain of trusts, add this chain to graph
|
/// Certificate is a chain of trusts, add this chain to graph
|
||||||
pub fn add<C>(&mut self, cert: C, cur_time: Duration) -> Result<(), TrustGraphError>
|
pub fn add<C>(&mut self, cert: C, cur_time: Duration) -> Result<(), TrustGraphError>
|
||||||
where
|
where
|
||||||
C: Borrow<Certificate>,
|
C: Borrow<Certificate>,
|
||||||
{
|
{
|
||||||
let roots: Vec<PublicKey> = self
|
let chain = &cert.borrow().chain;
|
||||||
.storage
|
let mut issued_by = chain.get(0).ok_or(EmptyChain)?.issued_for.clone();
|
||||||
.root_keys()?
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(Into::into)
|
|
||||||
.collect();
|
|
||||||
// Check that certificate is valid and converges to one of the known roots
|
|
||||||
Certificate::verify(cert.borrow(), roots.as_slice(), cur_time)?;
|
|
||||||
|
|
||||||
let mut chain = cert.borrow().chain.iter();
|
// TODO: optimize to check only root weight
|
||||||
let root_trust = chain.next().ok_or(EmptyChain)?;
|
|
||||||
let root_pk: PK = root_trust.issued_for.clone().into();
|
|
||||||
|
|
||||||
// Insert new TrustNode for this root_pk if there wasn't one
|
|
||||||
if self.storage.get(&root_pk)?.is_none() {
|
|
||||||
let mut trust_node = TrustNode::new(root_trust.issued_for.clone(), cur_time);
|
|
||||||
let root_auth = Auth {
|
|
||||||
trust: root_trust.clone(),
|
|
||||||
issued_by: root_trust.issued_for.clone(),
|
|
||||||
};
|
|
||||||
trust_node.update_auth(root_auth);
|
|
||||||
self.storage.insert(root_pk, trust_node)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert remaining trusts to the graph
|
|
||||||
let mut previous_trust = root_trust;
|
|
||||||
for trust in chain {
|
for trust in chain {
|
||||||
let pk = trust.issued_for.clone().into();
|
self.add_trust(trust, issued_by, cur_time)?;
|
||||||
|
issued_by = trust.issued_for.clone();
|
||||||
let auth = Auth {
|
|
||||||
trust: trust.clone(),
|
|
||||||
issued_by: previous_trust.issued_for.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.storage
|
|
||||||
.update_auth(&pk, auth, &root_trust.issued_for, cur_time)?;
|
|
||||||
|
|
||||||
previous_trust = trust;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_next_weight(
|
||||||
|
&mut self,
|
||||||
|
issued_by: &PK,
|
||||||
|
issued_for: &PK,
|
||||||
|
cur_time: Duration,
|
||||||
|
) -> Result<u32, TrustGraphError> {
|
||||||
|
let issued_by_weight = self.weight(issued_by.as_ref(), cur_time)?;
|
||||||
|
|
||||||
|
// self-signed trust has same weight as max weight of issuer
|
||||||
|
if issued_by.eq(issued_for) {
|
||||||
|
Ok(issued_by_weight)
|
||||||
|
} else {
|
||||||
|
Ok(issued_by_weight / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the maximum weight of trust for one public key.
|
/// Get the maximum weight of trust for one public key.
|
||||||
pub fn weight<P>(&self, pk: P) -> Result<Option<Weight>, TrustGraphError>
|
pub fn weight<P>(&mut self, pk: P, cur_time: Duration) -> Result<u32, TrustGraphError>
|
||||||
where
|
where
|
||||||
P: Borrow<PublicKey>,
|
P: Borrow<PublicKey>,
|
||||||
{
|
{
|
||||||
if let Some(weight) = self.storage.get_root_weight(pk.borrow().as_ref())? {
|
let mut max_weight = 0;
|
||||||
return Ok(Some(weight));
|
if let Some(weight_factor) = self.storage.get_root_weight_factor(pk.borrow().as_ref())? {
|
||||||
|
max_weight = get_weight_from_factor(weight_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
let roots: Vec<PublicKey> = self
|
|
||||||
.storage
|
|
||||||
.root_keys()?
|
|
||||||
.iter()
|
|
||||||
.map(|pk| pk.clone().into())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// get all possible certificates from the given public key to all roots in the graph
|
// get all possible certificates from the given public key to all roots in the graph
|
||||||
let certs = self.get_all_certs(pk, roots.as_slice())?;
|
let certs = self.get_all_certs(pk, cur_time)?;
|
||||||
self.certificates_weight(certs)
|
if let Some(weight_factor) = self.certificates_weight_factor(certs)? {
|
||||||
|
max_weight = std::cmp::max(max_weight, get_weight_from_factor(weight_factor))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(max_weight)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate weight from given certificates
|
/// Calculate weight from given certificates
|
||||||
/// Returns None if there is no such public key
|
/// Returns None if there is no such public key
|
||||||
/// or some trust between this key and a root key is revoked.
|
/// or some trust between this key and a root key is revoked.
|
||||||
/// TODO handle non-direct revocations
|
pub fn certificates_weight_factor<C, I>(
|
||||||
pub fn certificates_weight<C, I>(&self, certs: I) -> Result<Option<Weight>, TrustGraphError>
|
&self,
|
||||||
|
certs: I,
|
||||||
|
) -> Result<Option<WeightFactor>, TrustGraphError>
|
||||||
where
|
where
|
||||||
C: Borrow<Certificate>,
|
C: Borrow<Certificate>,
|
||||||
I: IntoIterator<Item = C>,
|
I: IntoIterator<Item = C>,
|
||||||
@ -192,7 +214,7 @@ where
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut weight = std::u32::MAX;
|
let mut weight_factor = u32::MAX;
|
||||||
|
|
||||||
for cert in certs {
|
for cert in certs {
|
||||||
let c = cert.borrow();
|
let c = cert.borrow();
|
||||||
@ -202,31 +224,30 @@ where
|
|||||||
.first()
|
.first()
|
||||||
.ok_or(CertificateCheckError(CertificateLengthError))?;
|
.ok_or(CertificateCheckError(CertificateLengthError))?;
|
||||||
|
|
||||||
let root_weight = self
|
let root_weight_factor = self
|
||||||
.storage
|
.storage
|
||||||
.get_root_weight(first.issued_for.as_ref())?
|
.get_root_weight_factor(first.issued_for.as_ref())?
|
||||||
.ok_or(NoRoot)?;
|
.ok_or(NoRoot)?;
|
||||||
|
|
||||||
// certificate weight = root weight + 1 * every other element in the chain
|
// certificate weight_factor = root weight factor + 1 * every other element in the chain
|
||||||
// (except root, so the formula is `root weight + chain length - 1`)
|
// (except root, so the formula is `root weight factor + chain length - 1`)
|
||||||
weight = std::cmp::min(weight, root_weight + c.chain.len() as u32 - 1)
|
weight_factor =
|
||||||
|
std::cmp::min(weight_factor, root_weight_factor + c.chain.len() as u32 - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(weight))
|
Ok(Some(weight_factor))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// BF search for all converging paths (chains) in the graph
|
/// BF search for all converging paths (chains) in the graph
|
||||||
/// TODO could be optimized with closure, that will calculate the weight on the fly
|
|
||||||
/// TODO or store auths to build certificates
|
|
||||||
fn bf_search_paths(
|
fn bf_search_paths(
|
||||||
&self,
|
&self,
|
||||||
node: &TrustNode,
|
pk: &PK,
|
||||||
roots: HashSet<&PK>,
|
roots: HashSet<&PK>,
|
||||||
) -> Result<Vec<Vec<Auth>>, TrustGraphError> {
|
) -> Result<Vec<Vec<Auth>>, TrustGraphError> {
|
||||||
// queue to collect all chains in the trust graph (each chain is a path in the trust graph)
|
// 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 mut chains_queue: VecDeque<Vec<Auth>> = VecDeque::new();
|
||||||
|
|
||||||
let node_auths: Vec<Auth> = node.authorizations().cloned().collect();
|
let node_auths: Vec<Auth> = self.storage.get_authorizations(pk)?;
|
||||||
// put all auth in the queue as the first possible paths through the graph
|
// put all auth in the queue as the first possible paths through the graph
|
||||||
for auth in node_auths {
|
for auth in node_auths {
|
||||||
chains_queue.push_back(vec![auth]);
|
chains_queue.push_back(vec![auth]);
|
||||||
@ -244,13 +265,9 @@ where
|
|||||||
.last()
|
.last()
|
||||||
.expect("`cur_chain` always has at least one element");
|
.expect("`cur_chain` always has at least one element");
|
||||||
|
|
||||||
let auths: Vec<Auth> = self
|
let auths = self
|
||||||
.storage
|
.storage
|
||||||
.get(&last.issued_by.clone().into())?
|
.get_authorizations(&last.issued_by.clone().into())?;
|
||||||
.ok_or(CertificateCheckError(Unexpected))?
|
|
||||||
.authorizations()
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for auth in auths {
|
for auth in auths {
|
||||||
// if there is auth, that we not visited in the current chain, copy chain and append this auth
|
// if there is auth, that we not visited in the current chain, copy chain and append this auth
|
||||||
@ -266,7 +283,7 @@ where
|
|||||||
|
|
||||||
// to be considered a valid chain, the chain must:
|
// to be considered a valid chain, the chain must:
|
||||||
// - end with a self-signed trust
|
// - end with a self-signed trust
|
||||||
// - that trust must converge to one of the root weights
|
// - that trust must converge to one of the roots
|
||||||
// - there should be more than 1 trust in the chain
|
// - there should be more than 1 trust in the chain
|
||||||
let self_signed = last.issued_by == last.trust.issued_for;
|
let self_signed = last.issued_by == last.trust.issued_for;
|
||||||
let issued_by: &PK = last.issued_by.as_ref();
|
let issued_by: &PK = last.issued_by.as_ref();
|
||||||
@ -280,402 +297,46 @@ where
|
|||||||
Ok(terminated_chains)
|
Ok(terminated_chains)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove `roots` argument from api, leave it for tests and internal usage only
|
|
||||||
/// Get all possible certificates where `issued_for` will be the last element of the chain
|
/// 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.
|
/// and one of the destinations is the root of this chain.
|
||||||
pub fn get_all_certs<P>(
|
pub fn get_all_certs<P>(
|
||||||
&self,
|
&mut self,
|
||||||
issued_for: P,
|
issued_for: P,
|
||||||
roots: &[PublicKey],
|
cur_time: Duration,
|
||||||
) -> Result<Vec<Certificate>, TrustGraphError>
|
) -> Result<Vec<Certificate>, TrustGraphError>
|
||||||
where
|
where
|
||||||
P: Borrow<PublicKey>,
|
P: Borrow<PublicKey>,
|
||||||
{
|
{
|
||||||
// get all auths (edges) for issued public key
|
// garbage collect
|
||||||
let issued_for_node = self.storage.get(issued_for.borrow().as_ref())?;
|
self.storage.remove_expired(cur_time)?;
|
||||||
|
|
||||||
let roots = roots.iter().map(|pk| pk.as_ref());
|
// maybe later we should retrieve root keys lazily
|
||||||
let keys = self.storage.root_keys()?;
|
let keys = self.storage.root_keys()?;
|
||||||
let roots = keys.iter().chain(roots).collect();
|
let roots = keys.iter().collect();
|
||||||
|
|
||||||
match issued_for_node {
|
Ok(self
|
||||||
Some(node) => Ok(self
|
.bf_search_paths(issued_for.borrow().as_ref(), roots)?
|
||||||
.bf_search_paths(&node, roots)?
|
.into_iter()
|
||||||
.iter()
|
.map(|auths| {
|
||||||
.map(|auths| {
|
let trusts: Vec<Trust> = auths.into_iter().map(|auth| auth.trust).rev().collect();
|
||||||
// TODO: can avoid cloning here by returning &Certificate
|
Certificate::new_unverified(trusts)
|
||||||
let trusts: Vec<Trust> =
|
})
|
||||||
auths.iter().map(|auth| auth.trust.clone()).rev().collect();
|
.filter(|c| {
|
||||||
Certificate::new_unverified(trusts)
|
// Certificate with one trust means nothing, gotta be a bug. Checking for it here.
|
||||||
})
|
debug_assert!(
|
||||||
.filter(|c| {
|
c.chain.len() > 1,
|
||||||
// Certificate with one trust means nothing, gotta be a bug. Checking for it here.
|
"certificate with chain of len 1 arose: {:#?}",
|
||||||
debug_assert!(
|
c
|
||||||
c.chain.len() > 1,
|
);
|
||||||
"certificate with chain of len 1 arose: {:#?}",
|
c.chain.len() > 1
|
||||||
c
|
})
|
||||||
);
|
.collect())
|
||||||
c.chain.len() > 1
|
|
||||||
})
|
|
||||||
.collect()),
|
|
||||||
None => Ok(Vec::new()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark public key as revoked.
|
/// Mark public key as revoked.
|
||||||
pub fn revoke(&mut self, revoke: Revoke) -> Result<(), TrustGraphError> {
|
pub fn revoke(&mut self, revoke: Revoke) -> Result<(), TrustGraphError> {
|
||||||
Revoke::verify(&revoke)?;
|
Revoke::verify(&revoke)?;
|
||||||
|
|
||||||
let pk: PK = revoke.pk.clone().into();
|
Ok(self.storage.revoke(revoke)?)
|
||||||
|
|
||||||
Ok(self.storage.revoke(&pk, revoke)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check information about new certificates and about revoked certificates.
|
|
||||||
/// Do it once per some time
|
|
||||||
// TODO
|
|
||||||
fn maintain() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::misc::current_time;
|
|
||||||
use crate::trust_graph_storage::InMemoryStorage;
|
|
||||||
use failure::_core::time::Duration;
|
|
||||||
use fluence_keypair::key_pair::KeyPair;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
pub fn one_minute() -> Duration {
|
|
||||||
Duration::new(60, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_root_cert() -> (KeyPair, KeyPair, Certificate) {
|
|
||||||
let root_kp = KeyPair::generate_ed25519();
|
|
||||||
let second_kp = KeyPair::generate_ed25519();
|
|
||||||
|
|
||||||
let cur_time = current_time();
|
|
||||||
|
|
||||||
(
|
|
||||||
root_kp.clone(),
|
|
||||||
second_kp.clone(),
|
|
||||||
Certificate::issue_root(
|
|
||||||
&root_kp,
|
|
||||||
second_kp.public(),
|
|
||||||
cur_time.checked_add(one_minute()).unwrap(),
|
|
||||||
cur_time,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_cert_with(
|
|
||||||
len: usize,
|
|
||||||
// Map of index to keypair. These key pairs will be used in trust chains at the given indexes
|
|
||||||
keys: HashMap<usize, KeyPair>,
|
|
||||||
expires_at: Duration,
|
|
||||||
issued_at: Duration,
|
|
||||||
) -> Result<(Vec<KeyPair>, Certificate), TrustGraphError> {
|
|
||||||
assert!(len > 2);
|
|
||||||
|
|
||||||
let root_kp = KeyPair::generate_ed25519();
|
|
||||||
let second_kp = KeyPair::generate_ed25519();
|
|
||||||
|
|
||||||
let mut cert = Certificate::issue_root(&root_kp, second_kp.public(), expires_at, issued_at);
|
|
||||||
|
|
||||||
let mut key_pairs = vec![root_kp, second_kp];
|
|
||||||
|
|
||||||
for idx in 2..len {
|
|
||||||
let kp = keys
|
|
||||||
.get(&idx)
|
|
||||||
.unwrap_or(&KeyPair::generate_ed25519())
|
|
||||||
.clone();
|
|
||||||
let previous_kp = &key_pairs[idx - 1];
|
|
||||||
cert = Certificate::issue(
|
|
||||||
&previous_kp,
|
|
||||||
kp.public(),
|
|
||||||
&cert,
|
|
||||||
expires_at,
|
|
||||||
// TODO: why `issued_at = issued_at - 60 seconds`?
|
|
||||||
issued_at.checked_sub(Duration::from_secs(60)).unwrap(),
|
|
||||||
current_time(),
|
|
||||||
)?;
|
|
||||||
key_pairs.push(kp);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((key_pairs, cert))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_cert_with_len(
|
|
||||||
len: usize,
|
|
||||||
keys: HashMap<usize, KeyPair>,
|
|
||||||
) -> Result<(Vec<KeyPair>, Certificate), TrustGraphError> {
|
|
||||||
let cur_time = current_time();
|
|
||||||
let far_future = cur_time.checked_add(one_minute()).unwrap();
|
|
||||||
|
|
||||||
generate_cert_with(len, keys, far_future, cur_time)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_add_cert_without_trusted_root() {
|
|
||||||
let (_, _, cert) = generate_root_cert();
|
|
||||||
|
|
||||||
let cur_time = current_time();
|
|
||||||
|
|
||||||
let st = InMemoryStorage::new();
|
|
||||||
let mut graph = TrustGraph::new(st);
|
|
||||||
let addition = graph.add(cert, cur_time);
|
|
||||||
assert_eq!(addition.is_ok(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_add_cert() {
|
|
||||||
let (root, _, cert) = generate_root_cert();
|
|
||||||
|
|
||||||
let st = InMemoryStorage::new();
|
|
||||||
let mut graph = TrustGraph::new(st);
|
|
||||||
graph.add_root_weight(root.public().into(), 0).unwrap();
|
|
||||||
|
|
||||||
let addition = graph.add(cert, current_time());
|
|
||||||
assert_eq!(addition.is_ok(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_add_certs_with_same_trusts_and_different_expirations_ed25519() {
|
|
||||||
let cur_time = current_time();
|
|
||||||
let far_future = cur_time + Duration::from_secs(10);
|
|
||||||
let far_far_future = cur_time + Duration::from_secs(900);
|
|
||||||
let key_pair1 = KeyPair::generate_ed25519();
|
|
||||||
let key_pair2 = KeyPair::generate_ed25519();
|
|
||||||
|
|
||||||
// Use key_pair1 and key_pair2 for 5th and 6th trust in the cert chain
|
|
||||||
let mut chain_keys = HashMap::new();
|
|
||||||
chain_keys.insert(5, key_pair1.clone());
|
|
||||||
chain_keys.insert(6, key_pair2.clone());
|
|
||||||
|
|
||||||
let (key_pairs1, cert1) =
|
|
||||||
generate_cert_with(10, chain_keys, far_future * 2, far_future).expect("");
|
|
||||||
|
|
||||||
// Use key_pair1 and key_pair2 for 7th and 8th trust in the cert chain
|
|
||||||
let mut chain_keys = HashMap::new();
|
|
||||||
chain_keys.insert(7, key_pair1.clone());
|
|
||||||
chain_keys.insert(8, key_pair2.clone());
|
|
||||||
|
|
||||||
let (key_pairs2, cert2) =
|
|
||||||
generate_cert_with(10, chain_keys, far_far_future * 2, far_far_future).unwrap();
|
|
||||||
|
|
||||||
let st = InMemoryStorage::new();
|
|
||||||
let mut graph = TrustGraph::new(st);
|
|
||||||
let root1_pk = key_pairs1[0].public();
|
|
||||||
let root2_pk = key_pairs2[0].public();
|
|
||||||
graph.add_root_weight(root1_pk.into(), 1).unwrap();
|
|
||||||
graph.add_root_weight(root2_pk.into(), 0).unwrap();
|
|
||||||
graph.add(cert1, cur_time).unwrap();
|
|
||||||
|
|
||||||
let node2 = graph.get(key_pair2.public()).unwrap().unwrap();
|
|
||||||
let auth_by_kp1 = node2
|
|
||||||
.authorizations()
|
|
||||||
.find(|a| a.issued_by == key_pair1.public())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(auth_by_kp1.trust.expires_at, far_future * 2);
|
|
||||||
|
|
||||||
graph.add(cert2, cur_time).unwrap();
|
|
||||||
|
|
||||||
let node2 = graph.get(key_pair2.public()).unwrap().unwrap();
|
|
||||||
let auth_by_kp1 = node2
|
|
||||||
.authorizations()
|
|
||||||
.find(|a| a.issued_by == key_pair1.public())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(auth_by_kp1.trust.expires_at, far_far_future * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_one_cert_in_graph() {
|
|
||||||
let (key_pairs, cert1) = generate_cert_with_len(10, HashMap::new()).unwrap();
|
|
||||||
let last_trust = cert1.chain[9].clone();
|
|
||||||
|
|
||||||
let st = InMemoryStorage::new();
|
|
||||||
let mut graph = TrustGraph::new(st);
|
|
||||||
|
|
||||||
let root_pk = key_pairs[0].public();
|
|
||||||
graph.add_root_weight(root_pk.into(), 1).unwrap();
|
|
||||||
|
|
||||||
graph.add(cert1, current_time()).unwrap();
|
|
||||||
|
|
||||||
let w1 = graph.weight(key_pairs[0].public()).unwrap().unwrap();
|
|
||||||
assert_eq!(w1, 1);
|
|
||||||
|
|
||||||
let w2 = graph.weight(key_pairs[1].public()).unwrap().unwrap();
|
|
||||||
assert_eq!(w2, 2);
|
|
||||||
|
|
||||||
let w3 = graph.weight(key_pairs[9].public()).unwrap().unwrap();
|
|
||||||
assert_eq!(w3, 10);
|
|
||||||
|
|
||||||
let node = graph.get(key_pairs[9].public()).unwrap().unwrap();
|
|
||||||
let auths: Vec<&Auth> = node.authorizations().collect();
|
|
||||||
|
|
||||||
assert_eq!(auths.len(), 1);
|
|
||||||
assert_eq!(auths[0].trust, last_trust);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cycles_in_graph() {
|
|
||||||
let key_pair1 = KeyPair::generate_ed25519();
|
|
||||||
let key_pair2 = KeyPair::generate_ed25519();
|
|
||||||
let key_pair3 = KeyPair::generate_ed25519();
|
|
||||||
|
|
||||||
let mut chain_keys = HashMap::new();
|
|
||||||
chain_keys.insert(3, key_pair1.clone());
|
|
||||||
chain_keys.insert(5, key_pair2.clone());
|
|
||||||
chain_keys.insert(7, key_pair3.clone());
|
|
||||||
|
|
||||||
let (key_pairs1, cert1) = generate_cert_with_len(10, chain_keys).unwrap();
|
|
||||||
|
|
||||||
let mut chain_keys = HashMap::new();
|
|
||||||
chain_keys.insert(7, key_pair1.clone());
|
|
||||||
chain_keys.insert(6, key_pair2.clone());
|
|
||||||
chain_keys.insert(5, key_pair3.clone());
|
|
||||||
|
|
||||||
let (key_pairs2, cert2) = generate_cert_with_len(10, chain_keys).unwrap();
|
|
||||||
|
|
||||||
let st = InMemoryStorage::new();
|
|
||||||
let mut graph = TrustGraph::new(st);
|
|
||||||
let root1_pk = key_pairs1[0].public();
|
|
||||||
let root2_pk = key_pairs2[0].public();
|
|
||||||
graph.add_root_weight(root1_pk.into(), 1).unwrap();
|
|
||||||
graph.add_root_weight(root2_pk.into(), 0).unwrap();
|
|
||||||
|
|
||||||
let last_pk1 = cert1.chain[9].issued_for.clone();
|
|
||||||
let last_pk2 = cert2.chain[9].issued_for.clone();
|
|
||||||
|
|
||||||
graph.add(cert1, current_time()).unwrap();
|
|
||||||
graph.add(cert2, current_time()).unwrap();
|
|
||||||
|
|
||||||
let revoke1 = Revoke::create(&key_pairs1[3], key_pairs1[4].public(), current_time());
|
|
||||||
graph.revoke(revoke1).unwrap();
|
|
||||||
let revoke2 = Revoke::create(&key_pairs2[5], key_pairs2[6].public(), current_time());
|
|
||||||
graph.revoke(revoke2).unwrap();
|
|
||||||
|
|
||||||
let w1 = graph.weight(key_pair1.public()).unwrap().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().unwrap();
|
|
||||||
let w_last1 = graph.weight(last_pk1).unwrap().unwrap();
|
|
||||||
let w_last2 = graph.weight(last_pk2).unwrap().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(w1, 4);
|
|
||||||
assert_eq!(w2.is_none(), true);
|
|
||||||
assert_eq!(w3, 5);
|
|
||||||
assert_eq!(w_last1, 7);
|
|
||||||
assert_eq!(w_last2, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_one_cert() {
|
|
||||||
let (key_pairs, cert) = generate_cert_with_len(5, HashMap::new()).unwrap();
|
|
||||||
|
|
||||||
let st = InMemoryStorage::new();
|
|
||||||
let mut graph = TrustGraph::new(st);
|
|
||||||
let root1_pk = key_pairs[0].public();
|
|
||||||
graph.add_root_weight(root1_pk.clone().into(), 1).unwrap();
|
|
||||||
|
|
||||||
graph.add(cert.clone(), current_time()).unwrap();
|
|
||||||
|
|
||||||
let certs = graph
|
|
||||||
.get_all_certs(key_pairs.last().unwrap().public(), &[root1_pk])
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(certs.len(), 1);
|
|
||||||
assert_eq!(certs[0], cert);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_chain_from_root_to_another_root() {
|
|
||||||
let (_, cert) = generate_cert_with_len(6, HashMap::new()).unwrap();
|
|
||||||
|
|
||||||
let st = InMemoryStorage::new();
|
|
||||||
let mut graph = TrustGraph::new(st);
|
|
||||||
// add first and last trusts as roots
|
|
||||||
graph
|
|
||||||
.add_root_weight(cert.chain[0].clone().issued_for.into(), 1)
|
|
||||||
.unwrap();
|
|
||||||
graph
|
|
||||||
.add_root_weight(cert.chain[3].clone().issued_for.into(), 1)
|
|
||||||
.unwrap();
|
|
||||||
graph
|
|
||||||
.add_root_weight(cert.chain[5].clone().issued_for.into(), 1)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
graph.add(cert.clone(), current_time()).unwrap();
|
|
||||||
|
|
||||||
let t = cert.chain[5].clone();
|
|
||||||
let certs = graph.get_all_certs(t.issued_for, &[]).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(certs.len(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_find_certs() {
|
|
||||||
let key_pair1 = KeyPair::generate_ed25519();
|
|
||||||
let key_pair2 = KeyPair::generate_ed25519();
|
|
||||||
let key_pair3 = KeyPair::generate_ed25519();
|
|
||||||
|
|
||||||
let mut chain_keys = HashMap::new();
|
|
||||||
chain_keys.insert(2, key_pair1.clone());
|
|
||||||
chain_keys.insert(3, key_pair2.clone());
|
|
||||||
chain_keys.insert(4, key_pair3.clone());
|
|
||||||
|
|
||||||
let (key_pairs1, cert1) = generate_cert_with_len(5, chain_keys).unwrap();
|
|
||||||
|
|
||||||
let mut chain_keys = HashMap::new();
|
|
||||||
chain_keys.insert(4, key_pair1.clone());
|
|
||||||
chain_keys.insert(3, key_pair2.clone());
|
|
||||||
chain_keys.insert(2, key_pair3.clone());
|
|
||||||
|
|
||||||
let (key_pairs2, cert2) = generate_cert_with_len(5, chain_keys).unwrap();
|
|
||||||
|
|
||||||
let mut chain_keys = HashMap::new();
|
|
||||||
chain_keys.insert(3, key_pair1.clone());
|
|
||||||
chain_keys.insert(4, key_pair2.clone());
|
|
||||||
chain_keys.insert(2, key_pair3.clone());
|
|
||||||
|
|
||||||
let (key_pairs3, cert3) = generate_cert_with_len(5, chain_keys).unwrap();
|
|
||||||
|
|
||||||
let st = InMemoryStorage::new();
|
|
||||||
let mut graph = TrustGraph::new(st);
|
|
||||||
let root1_pk = key_pairs1[0].public();
|
|
||||||
let root2_pk = key_pairs2[0].public();
|
|
||||||
let root3_pk = key_pairs3[0].public();
|
|
||||||
graph.add_root_weight(root1_pk.clone().into(), 1).unwrap();
|
|
||||||
graph.add_root_weight(root2_pk.clone().into(), 0).unwrap();
|
|
||||||
graph.add_root_weight(root3_pk.clone().into(), 0).unwrap();
|
|
||||||
|
|
||||||
graph.add(cert1, current_time()).unwrap();
|
|
||||||
graph.add(cert2, current_time()).unwrap();
|
|
||||||
graph.add(cert3, current_time()).unwrap();
|
|
||||||
|
|
||||||
let roots_values = [root1_pk, root2_pk, root3_pk];
|
|
||||||
|
|
||||||
let certs1 = graph
|
|
||||||
.get_all_certs(key_pair1.public(), &roots_values)
|
|
||||||
.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(), &roots_values)
|
|
||||||
.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(), &roots_values)
|
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,126 +1,28 @@
|
|||||||
use crate::public_key_hashable::PublicKeyHashable as PK;
|
use crate::public_key_hashable::PublicKeyHashable as PK;
|
||||||
use crate::revoke::Revoke;
|
use crate::revoke::Revoke;
|
||||||
use crate::trust_graph::Weight;
|
use crate::trust_graph::WeightFactor;
|
||||||
use crate::trust_graph_storage::InMemoryStorageError::RevokeError;
|
use crate::trust_relation::{Auth, TrustRelation};
|
||||||
use crate::trust_node::{Auth, TrustNode};
|
|
||||||
use fluence_keypair::public_key::PublicKey;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use thiserror::Error as ThisError;
|
|
||||||
|
|
||||||
pub trait StorageError: std::error::Error + Display {}
|
pub trait StorageError: std::error::Error + Display {}
|
||||||
|
|
||||||
pub trait Storage {
|
pub trait Storage {
|
||||||
type Error: StorageError + 'static;
|
type Error: StorageError + 'static;
|
||||||
|
|
||||||
fn get(&self, pk: &PK) -> Result<Option<TrustNode>, Self::Error>;
|
fn get_relation(
|
||||||
fn insert(&mut self, pk: PK, node: TrustNode) -> Result<(), Self::Error>;
|
&self,
|
||||||
|
issued_for: &PK,
|
||||||
|
issued_by: &PK,
|
||||||
|
) -> Result<Option<TrustRelation>, Self::Error>;
|
||||||
|
|
||||||
fn get_root_weight(&self, pk: &PK) -> Result<Option<Weight>, Self::Error>;
|
fn get_authorizations(&self, pk: &PK) -> Result<Vec<Auth>, Self::Error>;
|
||||||
fn add_root_weight(&mut self, pk: PK, weight: Weight) -> Result<(), Self::Error>;
|
fn insert(&mut self, node: TrustRelation) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
|
fn get_root_weight_factor(&self, pk: &PK) -> Result<Option<WeightFactor>, Self::Error>;
|
||||||
|
fn add_root_weight_factor(&mut self, pk: PK, weight: WeightFactor) -> Result<(), Self::Error>;
|
||||||
fn root_keys(&self) -> Result<Vec<PK>, Self::Error>;
|
fn root_keys(&self) -> Result<Vec<PK>, Self::Error>;
|
||||||
fn revoke(&mut self, pk: &PK, revoke: Revoke) -> Result<(), Self::Error>;
|
fn revoke(&mut self, revoke: Revoke) -> Result<(), Self::Error>;
|
||||||
fn update_auth(
|
fn update_auth(&mut self, auth: Auth, cur_time: Duration) -> Result<(), Self::Error>;
|
||||||
&mut self,
|
fn remove_expired(&mut self, current_time: Duration) -> Result<(), Self::Error>;
|
||||||
pk: &PK,
|
|
||||||
auth: Auth,
|
|
||||||
issued_for: &PublicKey,
|
|
||||||
cur_time: Duration,
|
|
||||||
) -> Result<(), Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct InMemoryStorage {
|
|
||||||
nodes: HashMap<PK, TrustNode>,
|
|
||||||
root_weights: HashMap<PK, Weight>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InMemoryStorage {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn new_in_memory(root_weights: Vec<(PublicKey, Weight)>) -> Self {
|
|
||||||
let root_weights = root_weights
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, w)| (k.into(), w))
|
|
||||||
.collect();
|
|
||||||
Self {
|
|
||||||
nodes: HashMap::new(),
|
|
||||||
root_weights,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
InMemoryStorage {
|
|
||||||
nodes: HashMap::new(),
|
|
||||||
root_weights: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ThisError, Debug)]
|
|
||||||
pub enum InMemoryStorageError {
|
|
||||||
#[error("InMemoryStorageError::RevokeError {0:?}")]
|
|
||||||
RevokeError(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StorageError for InMemoryStorageError {}
|
|
||||||
|
|
||||||
impl Storage for InMemoryStorage {
|
|
||||||
type Error = InMemoryStorageError;
|
|
||||||
|
|
||||||
fn get(&self, pk: &PK) -> Result<Option<TrustNode>, Self::Error> {
|
|
||||||
Ok(self.nodes.get(pk).cloned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert(&mut self, pk: PK, node: TrustNode) -> Result<(), Self::Error> {
|
|
||||||
self.nodes.insert(pk, node);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_root_weight(&self, pk: &PK) -> Result<Option<Weight>, Self::Error> {
|
|
||||||
Ok(self.root_weights.get(pk).cloned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_root_weight(&mut self, pk: PK, weight: Weight) -> Result<(), Self::Error> {
|
|
||||||
self.root_weights.insert(pk, weight);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn root_keys(&self) -> Result<Vec<PK>, Self::Error> {
|
|
||||||
Ok(self.root_weights.keys().cloned().map(Into::into).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn revoke(&mut self, pk: &PK, revoke: Revoke) -> Result<(), Self::Error> {
|
|
||||||
match self.nodes.get_mut(&pk) {
|
|
||||||
Some(trust_node) => {
|
|
||||||
trust_node.update_revoke(revoke);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
None => Err(RevokeError(
|
|
||||||
"There is no trust with such PublicKey".to_string(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_auth(
|
|
||||||
&mut self,
|
|
||||||
pk: &PK,
|
|
||||||
auth: Auth,
|
|
||||||
issued_for: &PublicKey,
|
|
||||||
cur_time: Duration,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
match self.nodes.get_mut(&pk) {
|
|
||||||
Some(trust_node) => {
|
|
||||||
trust_node.update_auth(auth);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let mut trust_node = TrustNode::new(issued_for.clone(), cur_time);
|
|
||||||
trust_node.update_auth(auth);
|
|
||||||
self.nodes.insert(pk.clone(), trust_node);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
76
src/trust_relation.rs
Normal file
76
src/trust_relation.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::revoke::Revoke;
|
||||||
|
use crate::trust::Trust;
|
||||||
|
use failure::_core::time::Duration;
|
||||||
|
use fluence_keypair::public_key::PublicKey;
|
||||||
|
use fluence_keypair::Signature;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Represents who give a trust
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Auth {
|
||||||
|
/// proof of this authorization
|
||||||
|
pub trust: Trust,
|
||||||
|
/// the issuer of this authorization
|
||||||
|
pub issued_by: PublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum TrustRelation {
|
||||||
|
Auth(Auth),
|
||||||
|
Revoke(Revoke),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrustRelation {
|
||||||
|
/// Returns timestamp of when this relation was created
|
||||||
|
pub fn issued_at(&self) -> Duration {
|
||||||
|
match self {
|
||||||
|
TrustRelation::Auth(auth) => auth.trust.issued_at,
|
||||||
|
TrustRelation::Revoke(revoke) => revoke.revoked_at,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns public key of the creator of this relation
|
||||||
|
pub fn issued_by(&self) -> &PublicKey {
|
||||||
|
match self {
|
||||||
|
TrustRelation::Auth(auth) => &auth.issued_by,
|
||||||
|
TrustRelation::Revoke(revoke) => &revoke.revoked_by,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn issued_for(&self) -> &PublicKey {
|
||||||
|
match self {
|
||||||
|
TrustRelation::Auth(auth) => &auth.trust.issued_for,
|
||||||
|
TrustRelation::Revoke(revoke) => &revoke.pk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expires_at(&self) -> Duration {
|
||||||
|
match self {
|
||||||
|
TrustRelation::Auth(auth) => auth.trust.expires_at,
|
||||||
|
TrustRelation::Revoke(_) => Duration::from_secs(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signature(&self) -> &Signature {
|
||||||
|
match self {
|
||||||
|
TrustRelation::Auth(auth) => &auth.trust.signature,
|
||||||
|
TrustRelation::Revoke(revoke) => &revoke.signature,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1781
wasm/Cargo.lock
generated
1781
wasm/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,28 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "trust-graph-wasm"
|
|
||||||
version = "0.2.0"
|
|
||||||
authors = ["Fluence Labs"]
|
|
||||||
edition = "2018"
|
|
||||||
description = "trust graph wasm"
|
|
||||||
license = "Apache-2.0"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "trust-graph"
|
|
||||||
path = "src/main.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
trust-graph = { version = "0.2.1", path = "../" }
|
|
||||||
fluence-identity = { version = "0.2.1", path = "../identity" }
|
|
||||||
log = "0.4.8"
|
|
||||||
fluence = { version = "0.2.18", features = ["logger"] }
|
|
||||||
anyhow = "1.0.31"
|
|
||||||
boolinator = "2.4.0"
|
|
||||||
once_cell = "1.4.1"
|
|
||||||
parking_lot = "0.11.1"
|
|
||||||
fce-sqlite-connector = "0.1.3"
|
|
||||||
serde_json = "1.0"
|
|
||||||
bs58 = "0.3.1"
|
|
||||||
rmp-serde = "0.15.0"
|
|
||||||
bincode = "1.3.1"
|
|
||||||
serde_bencode = "^0.2.3"
|
|
||||||
thiserror = "1.0.23"
|
|
@ -1,15 +0,0 @@
|
|||||||
modules_dir = "artifacts/"
|
|
||||||
|
|
||||||
[[module]]
|
|
||||||
name = "sqlite3"
|
|
||||||
mem_pages_count = 100
|
|
||||||
logger_enabled = false
|
|
||||||
|
|
||||||
[[module]]
|
|
||||||
name = "trust-graph"
|
|
||||||
mem_pages_count = 1
|
|
||||||
logger_enabled = true
|
|
||||||
|
|
||||||
[module.wasi]
|
|
||||||
preopened_files = ["/tmp"]
|
|
||||||
mapped_dirs = { "tmp" = "/tmp" }
|
|
@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
fce build
|
|
||||||
|
|
||||||
rm artifacts/trust-graph.wasm
|
|
||||||
mv -f target/wasm32-wasi/debug/trust-graph.wasm artifacts/
|
|
||||||
RUST_LOG="info" fce-repl Config.toml
|
|
101
wasm/src/dto.rs
101
wasm/src/dto.rs
@ -1,101 +0,0 @@
|
|||||||
use fluence::fce;
|
|
||||||
use fluence_identity::public_key::PKError;
|
|
||||||
use fluence_identity::signature::SignatureError;
|
|
||||||
use fluence_identity::{PublicKey, Signature};
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::time::Duration;
|
|
||||||
use thiserror::Error as ThisError;
|
|
||||||
|
|
||||||
#[derive(ThisError, Debug)]
|
|
||||||
pub enum DtoConversionError {
|
|
||||||
#[error("Cannot convert base58 string to bytes: {0}")]
|
|
||||||
Base58Error(
|
|
||||||
#[from]
|
|
||||||
#[source]
|
|
||||||
bs58::decode::Error,
|
|
||||||
),
|
|
||||||
#[error("Cannot convert string to PublicKey: {0}")]
|
|
||||||
PublicKeyDecodeError(
|
|
||||||
#[from]
|
|
||||||
#[source]
|
|
||||||
PKError,
|
|
||||||
),
|
|
||||||
#[error("Cannot convert string to PublicKey: {0}")]
|
|
||||||
SignatureDecodeError(
|
|
||||||
#[from]
|
|
||||||
#[source]
|
|
||||||
SignatureError,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fce]
|
|
||||||
pub struct Certificate {
|
|
||||||
pub chain: Vec<Trust>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<trust_graph::Certificate> for Certificate {
|
|
||||||
fn from(c: trust_graph::Certificate) -> Self {
|
|
||||||
let chain: Vec<Trust> = c.chain.into_iter().map(|t| t.into()).collect();
|
|
||||||
return Certificate { chain };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Certificate> for trust_graph::Certificate {
|
|
||||||
type Error = DtoConversionError;
|
|
||||||
|
|
||||||
fn try_from(c: Certificate) -> Result<Self, Self::Error> {
|
|
||||||
let chain: Result<Vec<trust_graph::Trust>, DtoConversionError> = c
|
|
||||||
.chain
|
|
||||||
.into_iter()
|
|
||||||
.map(|t| trust_graph::Trust::try_from(t))
|
|
||||||
.collect();
|
|
||||||
let chain = chain?;
|
|
||||||
return Ok(trust_graph::Certificate { chain });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fce]
|
|
||||||
pub struct Trust {
|
|
||||||
/// For whom this certificate is issued, base58
|
|
||||||
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,
|
|
||||||
/// Creation time of a trust, in secs
|
|
||||||
pub issued_at: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 signature = bs58::decode(&t.signature).into_vec()?;
|
|
||||||
let signature = Signature::from_bytes(&signature)?;
|
|
||||||
let expires_at = Duration::from_secs(t.expires_at);
|
|
||||||
let issued_at = Duration::from_secs(t.issued_at);
|
|
||||||
return Ok(trust_graph::Trust {
|
|
||||||
issued_for,
|
|
||||||
expires_at,
|
|
||||||
signature,
|
|
||||||
issued_at,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<trust_graph::Trust> for Trust {
|
|
||||||
fn from(t: trust_graph::Trust) -> Self {
|
|
||||||
let issued_for = bs58::encode(t.issued_for.to_bytes()).into_string();
|
|
||||||
let signature = bs58::encode(t.signature.to_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,
|
|
||||||
issued_at,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
use fluence::WasmLoggerBuilder;
|
|
||||||
|
|
||||||
mod dto;
|
|
||||||
mod results;
|
|
||||||
mod service_api;
|
|
||||||
mod service_impl;
|
|
||||||
mod storage_impl;
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
WasmLoggerBuilder::new()
|
|
||||||
.with_log_level(log::Level::Info)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// only option for now is to copy tests from trust graph,
|
|
||||||
// change connector to sqlite and fix compilation -_-
|
|
||||||
// TODO: fix it
|
|
||||||
/*#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
}*/
|
|
@ -1,93 +0,0 @@
|
|||||||
use crate::dto::Certificate;
|
|
||||||
use crate::service_impl::ServiceError;
|
|
||||||
use fluence::fce;
|
|
||||||
|
|
||||||
#[fce]
|
|
||||||
pub struct InsertResult {
|
|
||||||
pub ret_code: u32,
|
|
||||||
pub error: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Result<(), ServiceError>> for InsertResult {
|
|
||||||
fn from(result: Result<(), ServiceError>) -> Self {
|
|
||||||
match result {
|
|
||||||
Ok(()) => InsertResult {
|
|
||||||
ret_code: 0,
|
|
||||||
error: "".to_string(),
|
|
||||||
},
|
|
||||||
Err(e) => InsertResult {
|
|
||||||
ret_code: 1,
|
|
||||||
error: format!("{}", e),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fce]
|
|
||||||
pub struct WeightResult {
|
|
||||||
pub ret_code: u32,
|
|
||||||
pub weight: Vec<u32>,
|
|
||||||
pub error: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Result<Option<u32>, ServiceError>> for WeightResult {
|
|
||||||
fn from(result: Result<Option<u32>, ServiceError>) -> Self {
|
|
||||||
match result {
|
|
||||||
Ok(wo) => WeightResult {
|
|
||||||
ret_code: 0,
|
|
||||||
weight: wo.map(|w| vec![w]).unwrap_or(vec![]),
|
|
||||||
error: "".to_string(),
|
|
||||||
},
|
|
||||||
Err(e) => WeightResult {
|
|
||||||
ret_code: 1,
|
|
||||||
weight: vec![],
|
|
||||||
error: format!("{}", e),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fce]
|
|
||||||
pub struct AllCertsResult {
|
|
||||||
pub ret_code: u32,
|
|
||||||
pub certificates: Vec<Certificate>,
|
|
||||||
pub error: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Result<Vec<Certificate>, ServiceError>> for AllCertsResult {
|
|
||||||
fn from(result: Result<Vec<Certificate>, ServiceError>) -> Self {
|
|
||||||
match result {
|
|
||||||
Ok(certs) => AllCertsResult {
|
|
||||||
ret_code: 0,
|
|
||||||
certificates: certs,
|
|
||||||
error: "".to_string(),
|
|
||||||
},
|
|
||||||
Err(e) => AllCertsResult {
|
|
||||||
ret_code: 1,
|
|
||||||
certificates: vec![],
|
|
||||||
error: format!("{}", e),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fce]
|
|
||||||
pub struct AddRootResult {
|
|
||||||
pub ret_code: u32,
|
|
||||||
pub error: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Result<(), ServiceError>> for AddRootResult {
|
|
||||||
fn from(result: Result<(), ServiceError>) -> Self {
|
|
||||||
match result {
|
|
||||||
Ok(()) => AddRootResult {
|
|
||||||
ret_code: 0,
|
|
||||||
error: "".to_string(),
|
|
||||||
},
|
|
||||||
Err(e) => AddRootResult {
|
|
||||||
ret_code: 1,
|
|
||||||
error: format!("{}", e),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
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 fluence::{fce, CallParameters};
|
|
||||||
|
|
||||||
#[fce]
|
|
||||||
/// add a certificate in string representation to trust graph if it is valid
|
|
||||||
/// see `trust_graph::Certificate` class for string encoding/decoding
|
|
||||||
// TODO change `current_time` to time service
|
|
||||||
fn insert_cert_raw(certificate: String, current_time: u64) -> InsertResult {
|
|
||||||
insert_cert_impl_raw(certificate, current_time).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fce]
|
|
||||||
/// add a certificate in JSON representation to trust graph if it is valid
|
|
||||||
/// see `dto::Certificate` class for structure
|
|
||||||
fn insert_cert(certificate: Certificate, current_time: u64) -> InsertResult {
|
|
||||||
insert_cert_impl(certificate, current_time).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fce]
|
|
||||||
fn get_weight(public_key: String) -> WeightResult {
|
|
||||||
get_weight_impl(public_key).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fce]
|
|
||||||
fn get_all_certs(issued_for: String) -> AllCertsResult {
|
|
||||||
get_all_certs_impl(issued_for).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fce]
|
|
||||||
/// could add only a host of a trust graph service
|
|
||||||
fn add_root(pk: String, weight: u32) -> AddRootResult {
|
|
||||||
let call_parameters: CallParameters = fluence::get_call_parameters();
|
|
||||||
let init_peer_id = call_parameters.init_peer_id.clone();
|
|
||||||
if call_parameters.host_id == init_peer_id {
|
|
||||||
add_root_impl(pk, weight).into()
|
|
||||||
} else {
|
|
||||||
return AddRootResult {
|
|
||||||
ret_code: 1,
|
|
||||||
error: "Root could add only a host of trust graph service".to_string(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO rewrite test after #[fce_test] will be implemented
|
|
||||||
// #[fce]
|
|
||||||
// fn test() -> String {
|
|
||||||
// let mut tg = get_data().lock();
|
|
||||||
//
|
|
||||||
// let root_kp = KeyPair::generate();
|
|
||||||
// let root_kp2 = KeyPair::generate();
|
|
||||||
// let second_kp = KeyPair::generate();
|
|
||||||
//
|
|
||||||
// let expires_at = Duration::new(15, 15);
|
|
||||||
// let issued_at = Duration::new(5, 5);
|
|
||||||
//
|
|
||||||
// let cert = trust_graph::Certificate::issue_root(
|
|
||||||
// &root_kp,
|
|
||||||
// second_kp.public_key(),
|
|
||||||
// expires_at,
|
|
||||||
// issued_at,
|
|
||||||
// );
|
|
||||||
// tg.add_root_weight(root_kp.public().into(), 0).unwrap();
|
|
||||||
// tg.add_root_weight(root_kp2.public().into(), 1).unwrap();
|
|
||||||
// tg.add(cert, Duration::new(10, 10)).unwrap();
|
|
||||||
//
|
|
||||||
// let a = tg.get(second_kp.public_key()).unwrap();
|
|
||||||
// let str = format!("{:?}", a);
|
|
||||||
// log::info!("{}", &str);
|
|
||||||
//
|
|
||||||
// str
|
|
||||||
// }
|
|
@ -1,86 +0,0 @@
|
|||||||
use crate::dto::{Certificate, DtoConversionError};
|
|
||||||
use crate::storage_impl::get_data;
|
|
||||||
use fluence_identity::public_key::PKError;
|
|
||||||
use fluence_identity::PublicKey;
|
|
||||||
use std::convert::{Into, TryInto};
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::time::Duration;
|
|
||||||
use thiserror::Error as ThisError;
|
|
||||||
use trust_graph::{CertificateError, TrustGraphError};
|
|
||||||
|
|
||||||
#[derive(ThisError, Debug)]
|
|
||||||
pub enum ServiceError {
|
|
||||||
#[error("{0}")]
|
|
||||||
PublicKeyDecodeError(
|
|
||||||
#[from]
|
|
||||||
#[source]
|
|
||||||
PKError,
|
|
||||||
),
|
|
||||||
#[error("{0}")]
|
|
||||||
TGError(
|
|
||||||
#[from]
|
|
||||||
#[source]
|
|
||||||
TrustGraphError,
|
|
||||||
),
|
|
||||||
#[error("{0}")]
|
|
||||||
CertError(
|
|
||||||
#[from]
|
|
||||||
#[source]
|
|
||||||
CertificateError,
|
|
||||||
),
|
|
||||||
#[error("{0}")]
|
|
||||||
DtoError(
|
|
||||||
#[from]
|
|
||||||
#[source]
|
|
||||||
DtoConversionError,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_weight_impl(public_key: String) -> Result<Option<u32>, ServiceError> {
|
|
||||||
let tg = get_data().lock();
|
|
||||||
let public_key = string_to_public_key(public_key)?;
|
|
||||||
let weight = tg.weight(public_key)?;
|
|
||||||
Ok(weight)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_cert(certificate: trust_graph::Certificate, duration: u64) -> Result<(), ServiceError> {
|
|
||||||
let duration = Duration::from_millis(duration);
|
|
||||||
let mut tg = get_data().lock();
|
|
||||||
tg.add(certificate, duration)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_cert_impl_raw(certificate: String, duration: u64) -> Result<(), ServiceError> {
|
|
||||||
let certificate = trust_graph::Certificate::from_str(&certificate)?;
|
|
||||||
|
|
||||||
add_cert(certificate, duration)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn string_to_public_key(public_key: String) -> Result<PublicKey, ServiceError> {
|
|
||||||
let public_key = PublicKey::from_base58(&public_key)?;
|
|
||||||
|
|
||||||
Ok(public_key)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_all_certs_impl(issued_for: String) -> Result<Vec<Certificate>, ServiceError> {
|
|
||||||
let tg = get_data().lock();
|
|
||||||
|
|
||||||
let public_key = string_to_public_key(issued_for)?;
|
|
||||||
let certs = tg.get_all_certs(public_key, &[])?;
|
|
||||||
Ok(certs.into_iter().map(|c| c.into()).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_cert_impl(certificate: Certificate, duration: u64) -> Result<(), ServiceError> {
|
|
||||||
let certificate: trust_graph::Certificate = certificate.try_into()?;
|
|
||||||
|
|
||||||
add_cert(certificate, duration)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_root_impl(pk: String, weight: u32) -> Result<(), ServiceError> {
|
|
||||||
let mut tg = get_data().lock();
|
|
||||||
let pk = PublicKey::from_base58(&pk)?.into();
|
|
||||||
tg.add_root_weight(pk, weight)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,229 +0,0 @@
|
|||||||
// store list of trusts
|
|
||||||
// check if trust is already in list before adding
|
|
||||||
// if there is an older trust - don't add received trust
|
|
||||||
|
|
||||||
use crate::storage_impl::SQLiteStorageError::{
|
|
||||||
PublcKeyNotFound, PublicKeyConversion, PublicKeyFromStr, TrustNodeConversion,
|
|
||||||
WeightConversionDB,
|
|
||||||
};
|
|
||||||
use core::convert::TryFrom;
|
|
||||||
use fce_sqlite_connector;
|
|
||||||
use fce_sqlite_connector::Connection;
|
|
||||||
use fce_sqlite_connector::Error as InternalSqliteError;
|
|
||||||
use fce_sqlite_connector::Value;
|
|
||||||
use fluence_identity::public_key::PublicKey;
|
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use rmp_serde::decode::Error as RmpDecodeError;
|
|
||||||
use rmp_serde::encode::Error as RmpEncodeError;
|
|
||||||
use std::convert::From;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::time::Duration;
|
|
||||||
use thiserror::Error as ThisError;
|
|
||||||
use trust_graph::{
|
|
||||||
Auth, PublicKeyHashable as PK, Revoke, Storage, StorageError, TrustGraph, TrustNode, Weight,
|
|
||||||
};
|
|
||||||
|
|
||||||
static INSTANCE: OnceCell<Mutex<TrustGraph<SQLiteStorage>>> = OnceCell::new();
|
|
||||||
|
|
||||||
pub fn get_data() -> &'static Mutex<TrustGraph<SQLiteStorage>> {
|
|
||||||
INSTANCE.get_or_init(|| {
|
|
||||||
let db_path = "/tmp/users123123.sqlite";
|
|
||||||
let connection = fce_sqlite_connector::open(db_path).unwrap();
|
|
||||||
|
|
||||||
let init_sql = "CREATE TABLE IF NOT EXISTS trustnodes(
|
|
||||||
public_key TEXT PRIMARY KEY,
|
|
||||||
trustnode BLOB NOT NULL
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS roots(
|
|
||||||
public_key TEXT,
|
|
||||||
weight INTEGER
|
|
||||||
);";
|
|
||||||
|
|
||||||
connection.execute(init_sql).expect("cannot connect to db");
|
|
||||||
|
|
||||||
Mutex::new(TrustGraph::new(SQLiteStorage::new(connection)))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SQLiteStorage {
|
|
||||||
connection: Connection,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SQLiteStorage {
|
|
||||||
pub fn new(connection: Connection) -> SQLiteStorage {
|
|
||||||
SQLiteStorage { connection }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ThisError, Debug)]
|
|
||||||
pub enum SQLiteStorageError {
|
|
||||||
#[error("{0}")]
|
|
||||||
SQLiteError(
|
|
||||||
#[from]
|
|
||||||
#[source]
|
|
||||||
InternalSqliteError,
|
|
||||||
),
|
|
||||||
#[error("{0}")]
|
|
||||||
PublicKeyFromStr(String),
|
|
||||||
#[error("{0}")]
|
|
||||||
EncodeError(
|
|
||||||
#[from]
|
|
||||||
#[source]
|
|
||||||
RmpEncodeError,
|
|
||||||
),
|
|
||||||
#[error("{0}")]
|
|
||||||
DecodeError(
|
|
||||||
#[from]
|
|
||||||
#[source]
|
|
||||||
RmpDecodeError,
|
|
||||||
),
|
|
||||||
#[error("Cannot convert weight as integer from DB")]
|
|
||||||
WeightConversionDB,
|
|
||||||
#[error("Cannot convert public key as binary from DB")]
|
|
||||||
PublicKeyConversion,
|
|
||||||
#[error("Cannot convert trust node as binary from DB")]
|
|
||||||
TrustNodeConversion,
|
|
||||||
#[error("Cannot revoke. There is no trust with such PublicKey")]
|
|
||||||
PublcKeyNotFound,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SQLiteStorageError> for String {
|
|
||||||
fn from(err: SQLiteStorageError) -> Self {
|
|
||||||
err.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StorageError for SQLiteStorageError {}
|
|
||||||
|
|
||||||
impl Storage for SQLiteStorage {
|
|
||||||
type Error = SQLiteStorageError;
|
|
||||||
|
|
||||||
fn get(&self, pk: &PK) -> Result<Option<TrustNode>, Self::Error> {
|
|
||||||
let mut cursor = self
|
|
||||||
.connection
|
|
||||||
.prepare("SELECT trustnode FROM trustnodes WHERE public_key = ?")?
|
|
||||||
.cursor();
|
|
||||||
|
|
||||||
cursor.bind(&[Value::String(format!("{}", pk))])?;
|
|
||||||
|
|
||||||
match cursor.next().unwrap() {
|
|
||||||
Some(r) => {
|
|
||||||
log::info!("row: {:?}", r);
|
|
||||||
let tn_bin: &[u8] = r[0].as_binary().ok_or(TrustNodeConversion)?;
|
|
||||||
|
|
||||||
log::info!("binary: {:?}", tn_bin);
|
|
||||||
|
|
||||||
let trust_node: TrustNode = rmp_serde::from_read_ref(tn_bin)?;
|
|
||||||
|
|
||||||
log::info!("trustnode: {:?}", trust_node);
|
|
||||||
|
|
||||||
Ok(Some(trust_node))
|
|
||||||
}
|
|
||||||
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert(&mut self, pk: PK, node: TrustNode) -> Result<(), Self::Error> {
|
|
||||||
let mut cursor = self
|
|
||||||
.connection
|
|
||||||
.prepare("INSERT OR REPLACE INTO trustnodes VALUES (?, ?)")?
|
|
||||||
.cursor();
|
|
||||||
|
|
||||||
let tn_vec = rmp_serde::to_vec(&node)?;
|
|
||||||
|
|
||||||
log::info!("insert: {:?}", tn_vec);
|
|
||||||
|
|
||||||
cursor.bind(&[Value::String(format!("{}", pk)), Value::Binary(tn_vec)])?;
|
|
||||||
|
|
||||||
cursor.next()?;
|
|
||||||
Ok({})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_root_weight(&self, pk: &PK) -> Result<Option<Weight>, Self::Error> {
|
|
||||||
let mut cursor = self
|
|
||||||
.connection
|
|
||||||
.prepare("SELECT public_key,weight FROM roots WHERE public_key = ?")?
|
|
||||||
.cursor();
|
|
||||||
|
|
||||||
cursor.bind(&[Value::String(format!("{}", pk))])?;
|
|
||||||
|
|
||||||
if let Some(row) = cursor.next()? {
|
|
||||||
log::info!("row: {:?}", row);
|
|
||||||
|
|
||||||
let w = u32::try_from(row[1].as_integer().ok_or(WeightConversionDB)?)
|
|
||||||
.map_err(|_e| WeightConversionDB)?;
|
|
||||||
|
|
||||||
Ok(Some(w))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_root_weight(&mut self, pk: PK, weight: Weight) -> Result<(), Self::Error> {
|
|
||||||
log::info!("add root: {} weight: {}", pk, weight);
|
|
||||||
let mut cursor = self
|
|
||||||
.connection
|
|
||||||
.prepare("INSERT OR REPLACE INTO roots VALUES (?, ?)")?
|
|
||||||
.cursor();
|
|
||||||
|
|
||||||
cursor.bind(&[
|
|
||||||
Value::String(format!("{}", pk)),
|
|
||||||
Value::Integer(i64::from(weight)),
|
|
||||||
])?;
|
|
||||||
|
|
||||||
cursor.next()?;
|
|
||||||
Ok({})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn root_keys(&self) -> Result<Vec<PK>, Self::Error> {
|
|
||||||
let mut cursor = self
|
|
||||||
.connection
|
|
||||||
.prepare("SELECT public_key,weight FROM roots")?
|
|
||||||
.cursor();
|
|
||||||
|
|
||||||
let mut roots = vec![];
|
|
||||||
|
|
||||||
while let Some(row) = cursor.next()? {
|
|
||||||
log::info!("row: {:?}", row);
|
|
||||||
let pk = row[0].as_string().ok_or(PublicKeyConversion)?;
|
|
||||||
let pk: PK = PK::from_str(pk).map_err(|e| PublicKeyFromStr(e.to_string()))?;
|
|
||||||
|
|
||||||
roots.push(pk)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(roots)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn revoke(&mut self, pk: &PK, revoke: Revoke) -> Result<(), Self::Error> {
|
|
||||||
match self.get(&pk)? {
|
|
||||||
Some(mut trust_node) => {
|
|
||||||
trust_node.update_revoke(revoke);
|
|
||||||
self.insert(pk.clone(), trust_node)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
None => Err(PublcKeyNotFound),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_auth(
|
|
||||||
&mut self,
|
|
||||||
pk: &PK,
|
|
||||||
auth: Auth,
|
|
||||||
issued_for: &PublicKey,
|
|
||||||
cur_time: Duration,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
match self.get(&pk)? {
|
|
||||||
Some(mut trust_node) => {
|
|
||||||
trust_node.update_auth(auth);
|
|
||||||
self.insert(pk.clone(), trust_node)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let mut trust_node = TrustNode::new(issued_for.clone(), cur_time);
|
|
||||||
trust_node.update_auth(auth);
|
|
||||||
self.insert(pk.clone(), trust_node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user