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:
|
||||
- trust-graph00-{{ checksum "./service/Cargo.lock" }}-{{ checksum "./Cargo.lock" }}-{{ checksum "./keypair/Cargo.lock" }}
|
||||
- run: |
|
||||
rustup toolchain install nightly-2021-04-24-x86_64-unknown-linux-gnu
|
||||
rustup default nightly-2021-04-24-x86_64-unknown-linux-gnu
|
||||
rustup target add wasm32-wasi
|
||||
cargo test --no-fail-fast --release --all-features --
|
||||
cd ./service
|
||||
./build.sh
|
||||
cargo test --no-fail-fast --release --all-features --
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.cargo
|
||||
- ~/.rustup
|
||||
key: trust-graph00-{{ checksum "./Cargo.lock" }}-{{ checksum "./keypair/Cargo.lock" }}
|
||||
key: trust-graph00-{{ checksum "./Cargo.lock" }-{{ checksum "./service/Cargo.lock" }}}-{{ checksum "./keypair/Cargo.lock" }}
|
||||
|
||||
|
||||
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
|
||||
target/
|
||||
service/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]
|
||||
name = "trust-graph"
|
||||
version = "0.2.8"
|
||||
version = "0.3.0"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
description = "trust graph"
|
||||
@ -8,25 +8,24 @@ license = "Apache-2.0"
|
||||
repository = "https://github.com/fluencelabs/trust-graph"
|
||||
|
||||
[dependencies]
|
||||
libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2" }
|
||||
libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2", features = ["secp256k1"] }
|
||||
serde = { version = "=1.0.118", features = ["derive"] }
|
||||
|
||||
fluence-keypair = { path = "./keypair", version = "0.4.0" }
|
||||
fluence-keypair = { path = "./keypair", version = "0.5.0" }
|
||||
serde_json = "1.0.58"
|
||||
bs58 = "0.3.1"
|
||||
failure = "0.1.6"
|
||||
log = "0.4.11"
|
||||
ref-cast = "1.0.2"
|
||||
derivative = "2.1.1"
|
||||
ed25519-dalek = { version = "1.0.1", features = ["serde"] }
|
||||
rand = "0.7.0"
|
||||
signature = "1.3.0"
|
||||
serde_with = "1.6.0"
|
||||
thiserror = "1.0.23"
|
||||
libsecp256k1 = "0.3.5"
|
||||
ring = "0.16.20"
|
||||
sha2 = "0.9.5"
|
||||
rand = "0.7.0"
|
||||
|
||||
[workspace]
|
||||
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
|
||||
|
||||
`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.
|
||||
|
||||
`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();
|
||||
```
|
||||
`service` is a package that provides `marine` API and could be compiled to a Wasm file. It is uses `SQLite` as storage.
|
||||
|
||||
`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]
|
||||
name = "fluence-keypair"
|
||||
version = "0.4.0"
|
||||
version = "0.5.1"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
description = "identity"
|
||||
|
@ -19,13 +19,13 @@
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! 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 rand::RngCore;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
use zeroize::Zeroize;
|
||||
use core::fmt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// An Ed25519 keypair.
|
||||
pub struct Keypair(ed25519::Keypair);
|
||||
@ -73,7 +73,9 @@ impl Keypair {
|
||||
|
||||
impl fmt::Debug for Keypair {
|
||||
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 {
|
||||
let mut sk_bytes = self.0.secret.to_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())
|
||||
.expect("ed25519::PublicKey::from_bytes(to_bytes(k)) != k");
|
||||
Keypair(ed25519::Keypair { secret, public })
|
||||
@ -107,7 +110,10 @@ impl From<SecretKey> for Keypair {
|
||||
fn from(sk: SecretKey) -> Self {
|
||||
let secret: ed25519::ExpandedSecretKey = (&sk.0).into();
|
||||
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 {
|
||||
/// Verify the Ed25519 signature on a message using the public key.
|
||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<(), SigningError> {
|
||||
ed25519::Signature::try_from(sig).and_then(|s| self.0.verify(msg, &s)).map_err(SigningError::Ed25519)
|
||||
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(|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.
|
||||
@ -148,8 +162,7 @@ impl AsRef<[u8]> for SecretKey {
|
||||
impl Clone for SecretKey {
|
||||
fn clone(&self) -> Self {
|
||||
let mut sk_bytes = self.0.to_bytes();
|
||||
Self::from_bytes(&mut sk_bytes)
|
||||
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
|
||||
Self::from_bytes(&mut sk_bytes).expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,8 +177,11 @@ impl SecretKey {
|
||||
pub fn generate() -> Self {
|
||||
let mut bytes = [0u8; 32];
|
||||
rand::thread_rng().fill_bytes(&mut bytes);
|
||||
SecretKey(ed25519::SecretKey::from_bytes(&bytes)
|
||||
.expect("this returns `Err` only if the length is wrong; the length is correct; qed"))
|
||||
SecretKey(
|
||||
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.
|
||||
@ -173,8 +189,7 @@ impl SecretKey {
|
||||
/// returned.
|
||||
pub fn from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result<Self, DecodingError> {
|
||||
let sk_bytes = sk_bytes.as_mut();
|
||||
let secret = ed25519::SecretKey::from_bytes(&*sk_bytes)
|
||||
.map_err(DecodingError::Ed25519)?;
|
||||
let secret = ed25519::SecretKey::from_bytes(&*sk_bytes).map_err(DecodingError::Ed25519)?;
|
||||
sk_bytes.zeroize();
|
||||
Ok(SecretKey(secret))
|
||||
}
|
||||
@ -189,9 +204,7 @@ mod tests {
|
||||
use quickcheck::*;
|
||||
|
||||
fn eq_keypairs(kp1: &Keypair, kp2: &Keypair) -> bool {
|
||||
kp1.public() == kp2.public()
|
||||
&&
|
||||
kp1.0.secret.as_bytes() == kp2.0.secret.as_bytes()
|
||||
kp1.public() == kp2.public() && kp1.0.secret.as_bytes() == kp2.0.secret.as_bytes()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -200,9 +213,7 @@ mod tests {
|
||||
let kp1 = Keypair::generate();
|
||||
let mut kp1_enc = kp1.encode();
|
||||
let kp2 = Keypair::decode(&mut kp1_enc).unwrap();
|
||||
eq_keypairs(&kp1, &kp2)
|
||||
&&
|
||||
kp1_enc.iter().all(|b| *b == 0)
|
||||
eq_keypairs(&kp1, &kp2) && kp1_enc.iter().all(|b| *b == 0)
|
||||
}
|
||||
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
|
||||
}
|
||||
@ -213,9 +224,7 @@ mod tests {
|
||||
let kp1 = Keypair::generate();
|
||||
let mut sk = kp1.0.secret.to_bytes();
|
||||
let kp2 = Keypair::from(SecretKey::from_bytes(&mut sk).unwrap());
|
||||
eq_keypairs(&kp1, &kp2)
|
||||
&&
|
||||
sk == [0u8; 32]
|
||||
eq_keypairs(&kp1, &kp2) && sk == [0u8; 32]
|
||||
}
|
||||
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ pub enum DecodingError {
|
||||
Ed25519(
|
||||
#[from]
|
||||
#[source]
|
||||
ed25519_dalek::ed25519::Error
|
||||
ed25519_dalek::ed25519::Error,
|
||||
),
|
||||
#[error("Failed to decode with RSA")]
|
||||
Rsa,
|
||||
@ -49,6 +49,8 @@ pub enum DecodingError {
|
||||
Base58DecodeError(#[source] bs58::decode::Error),
|
||||
#[error("Raw signature decoding failed: type {0} not supported")]
|
||||
RawSignatureUnsupportedType(String),
|
||||
#[error("public key is not inlined in peer id: {0}")]
|
||||
PublicKeyNotInlined(String),
|
||||
}
|
||||
|
||||
/// An error during signing of a message.
|
||||
@ -58,7 +60,7 @@ pub enum SigningError {
|
||||
Ed25519(
|
||||
#[from]
|
||||
#[source]
|
||||
ed25519_dalek::ed25519::Error
|
||||
ed25519_dalek::ed25519::Error,
|
||||
),
|
||||
#[error("Failed to sign with RSA")]
|
||||
Rsa,
|
||||
@ -66,6 +68,20 @@ pub enum SigningError {
|
||||
Secp256k1(
|
||||
#[from]
|
||||
#[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.
|
||||
use crate::ed25519;
|
||||
use crate::error::{DecodingError, Error, SigningError, VerificationError};
|
||||
use crate::public_key::PublicKey;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::rsa;
|
||||
use crate::secp256k1;
|
||||
use crate::public_key::PublicKey;
|
||||
use crate::signature::Signature;
|
||||
use crate::error::{Error, DecodingError, SigningError};
|
||||
use std::str::FromStr;
|
||||
use std::convert::TryFrom;
|
||||
use libp2p_core::PeerId;
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Identity keypair of a node.
|
||||
///
|
||||
@ -48,7 +48,6 @@ use libp2p_core::PeerId;
|
||||
/// ```
|
||||
///
|
||||
|
||||
|
||||
pub enum KeyFormat {
|
||||
Ed25519,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@ -66,7 +65,7 @@ impl FromStr for KeyFormat {
|
||||
"secp256k1" => Ok(KeyFormat::Secp256k1),
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
"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"))]
|
||||
1 => Ok(KeyFormat::Rsa),
|
||||
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)]
|
||||
pub enum KeyPair {
|
||||
/// An Ed25519 keypair.
|
||||
@ -153,7 +162,9 @@ impl KeyPair {
|
||||
Ed25519(ref pair) => Ok(Signature::Ed25519(ed25519::Signature(pair.sign(msg)?))),
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
||||
@ -200,7 +215,7 @@ impl KeyPair {
|
||||
KeyFormat::Ed25519 => Ok(Ed25519(ed25519::Keypair::decode(&mut bytes)?)),
|
||||
KeyFormat::Secp256k1 => Ok(Secp256k1(secp256k1::SecretKey::from_bytes(bytes)?.into())),
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
KeyFormat::Rsa => Err(DecodingError::KeypairDecodingIsNotSupported)
|
||||
KeyFormat::Rsa => Err(DecodingError::KeypairDecodingIsNotSupported),
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,23 +232,34 @@ impl From<libp2p_core::identity::Keypair> for KeyPair {
|
||||
Ed25519(kp) => KeyPair::Ed25519(ed25519::Keypair::decode(&mut kp.encode()).unwrap()),
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
// safety: these Keypair structures are identical
|
||||
Rsa(kp) => KeyPair::Rsa(unsafe { 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())),
|
||||
Rsa(kp) => KeyPair::Rsa(unsafe {
|
||||
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 {
|
||||
fn from(key: KeyPair) -> Self {
|
||||
use KeyPair::*;
|
||||
use libp2p_core::identity::Keypair;
|
||||
use libp2p_core::identity;
|
||||
use libp2p_core::identity::Keypair;
|
||||
use KeyPair::*;
|
||||
|
||||
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"))]
|
||||
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())),
|
||||
// safety: these Keypair structures are identical
|
||||
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.
|
||||
*/
|
||||
use crate::ed25519;
|
||||
use crate::error::{DecodingError, SigningError};
|
||||
use crate::error::{DecodingError, VerificationError};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::rsa;
|
||||
use crate::secp256k1;
|
||||
@ -43,7 +43,7 @@ impl PublicKey {
|
||||
/// private key (authenticity), and that the message has not been
|
||||
/// tampered with (integrity).
|
||||
// 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::*;
|
||||
match self {
|
||||
Ed25519(pk) => pk.verify(msg, sig.to_vec()),
|
||||
@ -111,6 +111,17 @@ impl PublicKey {
|
||||
pub fn to_peer_id(&self) -> PeerId {
|
||||
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 {
|
||||
@ -150,15 +161,12 @@ impl From<PublicKey> for libp2p_core::identity::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
|
||||
.as_public_key()
|
||||
.ok_or(eyre::eyre!(
|
||||
"public key is not inlined in peer id: {}",
|
||||
peer_id
|
||||
))?
|
||||
.ok_or(DecodingError::PublicKeyNotInlined(peer_id.to_base58()))?
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
@ -19,16 +19,19 @@
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! 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 ring::rand::SystemRandom;
|
||||
use ring::signature::{self, RsaKeyPair, RSA_PKCS1_SHA256, RSA_PKCS1_2048_8192_SHA256};
|
||||
use ring::signature::KeyPair;
|
||||
use std::{fmt::{self, Write}, sync::Arc};
|
||||
use zeroize::Zeroize;
|
||||
use ring::signature::{self, RsaKeyPair, RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_SHA256};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt::{self, Write},
|
||||
sync::Arc,
|
||||
};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// An RSA keypair.
|
||||
#[derive(Clone)]
|
||||
@ -40,8 +43,7 @@ impl Keypair {
|
||||
///
|
||||
/// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5
|
||||
pub fn from_pkcs8(der: &mut [u8]) -> Result<Self, DecodingError> {
|
||||
let kp = RsaKeyPair::from_pkcs8(&der)
|
||||
.map_err(|_| DecodingError::Rsa)?;
|
||||
let kp = RsaKeyPair::from_pkcs8(&der).map_err(|_| DecodingError::Rsa)?;
|
||||
der.zeroize();
|
||||
Ok(Keypair(Arc::new(kp)))
|
||||
}
|
||||
@ -57,7 +59,7 @@ impl Keypair {
|
||||
let rng = SystemRandom::new();
|
||||
match self.0.sign(&RSA_PKCS1_SHA256, &rng, &data, &mut signature) {
|
||||
Ok(()) => Ok(signature),
|
||||
Err(_) => Err(SigningError::Rsa)
|
||||
Err(_) => Err(SigningError::Rsa),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,9 +70,15 @@ pub struct PublicKey(Vec<u8>);
|
||||
|
||||
impl PublicKey {
|
||||
/// 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);
|
||||
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,
|
||||
@ -99,7 +107,8 @@ impl PublicKey {
|
||||
subjectPublicKey: Asn1SubjectPublicKey(self.clone()),
|
||||
};
|
||||
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.")
|
||||
}
|
||||
|
||||
@ -121,9 +130,7 @@ impl fmt::Debug for PublicKey {
|
||||
write!(hex, "{:02x}", byte).expect("Can't fail on writing to string");
|
||||
}
|
||||
|
||||
f.debug_struct("PublicKey")
|
||||
.field("pkcs1", &hex)
|
||||
.finish()
|
||||
f.debug_struct("PublicKey").field("pkcs1", &hex).finish()
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,6 +280,8 @@ mod tests {
|
||||
fn prop(SomeKeypair(kp): SomeKeypair, msg: Vec<u8>) -> Result<bool, SigningError> {
|
||||
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.
|
||||
|
||||
//! Secp256k1 keys.
|
||||
use crate::error::{DecodingError, SigningError};
|
||||
use crate::error::{DecodingError, SigningError, VerificationError};
|
||||
|
||||
use asn1_der::{FromDerObject, DerObject};
|
||||
use rand::RngCore;
|
||||
use sha2::{Digest as ShaDigestTrait, Sha256};
|
||||
use secp256k1::Message;
|
||||
use zeroize::Zeroize;
|
||||
use asn1_der::{DerObject, FromDerObject};
|
||||
use core::fmt;
|
||||
use serde::{Deserialize, Serialize, Serializer, Deserializer};
|
||||
use rand::RngCore;
|
||||
use secp256k1::Message;
|
||||
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.
|
||||
#[derive(Clone)]
|
||||
@ -57,7 +57,9 @@ impl Keypair {
|
||||
|
||||
impl fmt::Debug for Keypair {
|
||||
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.
|
||||
pub fn from_bytes(mut sk: impl AsMut<[u8]>) -> Result<Self, DecodingError> {
|
||||
let sk_bytes = sk.as_mut();
|
||||
let secret = secp256k1::SecretKey::parse_slice(&*sk_bytes)
|
||||
.map_err(|_| DecodingError::Secp256k1)?;
|
||||
let secret =
|
||||
secp256k1::SecretKey::parse_slice(&*sk_bytes).map_err(|_| DecodingError::Secp256k1)?;
|
||||
sk_bytes.zeroize();
|
||||
Ok(SecretKey(secret))
|
||||
}
|
||||
@ -119,13 +121,12 @@ impl SecretKey {
|
||||
pub fn from_der(mut der: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
|
||||
// TODO: Stricter parsing.
|
||||
let der_obj = der.as_mut();
|
||||
let obj: Vec<DerObject> = FromDerObject::deserialize((&*der_obj).iter())
|
||||
.map_err(|_| DecodingError::Secp256k1)?;
|
||||
let obj: Vec<DerObject> =
|
||||
FromDerObject::deserialize((&*der_obj).iter()).map_err(|_| DecodingError::Secp256k1)?;
|
||||
der_obj.zeroize();
|
||||
let sk_obj = obj.into_iter().nth(1)
|
||||
.ok_or(DecodingError::Secp256k1)?;
|
||||
let mut sk_bytes: Vec<u8> = FromDerObject::from_der_object(sk_obj)
|
||||
.map_err(|_| DecodingError::Secp256k1)?;
|
||||
let sk_obj = obj.into_iter().nth(1).ok_or(DecodingError::Secp256k1)?;
|
||||
let mut sk_bytes: Vec<u8> =
|
||||
FromDerObject::from_der_object(sk_obj).map_err(|_| DecodingError::Secp256k1)?;
|
||||
let sk = SecretKey::from_bytes(&mut sk_bytes)?;
|
||||
sk_bytes.zeroize();
|
||||
Ok(sk)
|
||||
@ -147,9 +148,12 @@ impl SecretKey {
|
||||
/// Sign a raw message of length 256 bits with this secret key, produces a DER-encoded
|
||||
/// ECDSA signature.
|
||||
pub fn sign_hashed(&self, msg: &[u8]) -> Result<Vec<u8>, SigningError> {
|
||||
let m = Message::parse_slice(msg)
|
||||
.map_err(SigningError::Secp256k1)?;
|
||||
Ok(secp256k1::sign(&m, &self.0).0.serialize_der().as_ref().into())
|
||||
let m = Message::parse_slice(msg).map_err(SigningError::Secp256k1)?;
|
||||
Ok(secp256k1::sign(&m, &self.0)
|
||||
.0
|
||||
.serialize_der()
|
||||
.as_ref()
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,15 +163,24 @@ pub struct PublicKey(secp256k1::PublicKey);
|
||||
|
||||
impl PublicKey {
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
.and_then(|m| secp256k1::Signature::parse_der(sig).map(|s| secp256k1::verify(&m, &s, &self.0)))
|
||||
.map_err(SigningError::Secp256k1).map(|_| ())
|
||||
.and_then(|m| {
|
||||
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
|
||||
@ -190,7 +203,6 @@ impl PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Serialize for PublicKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
@ -210,7 +222,6 @@ impl<'d> Deserialize<'d> for PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
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 {
|
||||
KeyFormat::Ed25519 => Signature::Ed25519(ed25519::Signature(bytes)),
|
||||
#[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 }
|
||||
}
|
||||
|
||||
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.
|
||||
#[allow(dead_code)]
|
||||
pub fn issue_root(
|
||||
|
11
src/lib.rs
11
src/lib.rs
@ -25,6 +25,7 @@
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod certificate;
|
||||
pub mod certificate_serde;
|
||||
@ -34,13 +35,13 @@ mod revoke;
|
||||
mod trust;
|
||||
mod trust_graph;
|
||||
mod trust_graph_storage;
|
||||
mod trust_node;
|
||||
mod trust_relation;
|
||||
|
||||
pub use crate::certificate::{Certificate, CertificateError};
|
||||
pub use crate::misc::current_time;
|
||||
pub use crate::public_key_hashable::PublicKeyHashable;
|
||||
pub use crate::revoke::Revoke;
|
||||
pub use crate::trust::Trust;
|
||||
pub use crate::trust_graph::{TrustGraph, TrustGraphError, Weight};
|
||||
pub use crate::trust_graph_storage::{Storage, StorageError, InMemoryStorage, InMemoryStorageError};
|
||||
pub use crate::trust_node::{Auth, TrustNode};
|
||||
pub use crate::trust::{Trust, TrustError};
|
||||
pub use crate::trust_graph::{TrustGraph, TrustGraphError, WeightFactor, MAX_WEIGHT_FACTOR};
|
||||
pub use crate::trust_graph_storage::{Storage, StorageError};
|
||||
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::signature::Signature;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Digest;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
@ -28,7 +29,7 @@ pub enum RevokeError {
|
||||
IncorrectSignature(
|
||||
#[from]
|
||||
#[source]
|
||||
fluence_keypair::error::SigningError
|
||||
fluence_keypair::error::VerificationError,
|
||||
),
|
||||
}
|
||||
|
||||
@ -43,12 +44,12 @@ pub struct Revoke {
|
||||
/// the issuer of this revocation
|
||||
pub revoked_by: PublicKey,
|
||||
/// proof of this revocation
|
||||
signature: Signature,
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl Revoke {
|
||||
#[allow(dead_code)]
|
||||
fn new(
|
||||
pub fn new(
|
||||
pk: PublicKey,
|
||||
revoked_by: PublicKey,
|
||||
revoked_at: Duration,
|
||||
@ -71,14 +72,14 @@ impl Revoke {
|
||||
Revoke::new(to_revoke, revoker.public(), revoked_at, signature)
|
||||
}
|
||||
|
||||
fn signature_bytes(pk: &PublicKey, revoked_at: Duration) -> Vec<u8> {
|
||||
let mut msg = Vec::new();
|
||||
pub fn signature_bytes(pk: &PublicKey, revoked_at: Duration) -> Vec<u8> {
|
||||
let mut metadata = Vec::new();
|
||||
let pk_bytes = &pk.encode();
|
||||
msg.push(pk_bytes.len() as u8);
|
||||
msg.extend(pk_bytes);
|
||||
msg.extend_from_slice(&(revoked_at.as_secs() as u64).to_le_bytes());
|
||||
metadata.push(pk_bytes.len() as u8);
|
||||
metadata.extend(pk_bytes);
|
||||
metadata.extend_from_slice(&(revoked_at.as_secs() as u64).to_le_bytes());
|
||||
|
||||
msg
|
||||
sha2::Sha256::digest(&metadata).to_vec()
|
||||
}
|
||||
|
||||
/// Verifies that revocation is cryptographically correct.
|
||||
@ -87,7 +88,8 @@ impl Revoke {
|
||||
|
||||
revoke
|
||||
.revoked_by
|
||||
.verify(msg.as_slice(), &revoke.signature).map_err(IncorrectSignature)
|
||||
.verify(msg.as_slice(), &revoke.signature)
|
||||
.map_err(IncorrectSignature)
|
||||
}
|
||||
}
|
||||
|
||||
|
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::signature::Signature;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Digest;
|
||||
use std::convert::TryInto;
|
||||
use std::num::ParseIntError;
|
||||
use std::time::Duration;
|
||||
@ -67,7 +68,7 @@ pub enum TrustError {
|
||||
SignatureError(
|
||||
#[from]
|
||||
#[source]
|
||||
fluence_keypair::error::SigningError,
|
||||
fluence_keypair::error::VerificationError,
|
||||
),
|
||||
|
||||
/// Errors occurred on trust decoding from different formats
|
||||
@ -113,7 +114,7 @@ impl Trust {
|
||||
expires_at: Duration,
|
||||
issued_at: Duration,
|
||||
) -> 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();
|
||||
|
||||
@ -136,12 +137,12 @@ impl Trust {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 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();
|
||||
@ -151,7 +152,7 @@ impl Trust {
|
||||
metadata.extend_from_slice(&expires_at_encoded[0..EXPIRATION_LEN]);
|
||||
metadata.extend_from_slice(&issued_at_encoded[0..ISSUED_LEN]);
|
||||
|
||||
metadata
|
||||
sha2::Sha256::digest(&metadata).to_vec()
|
||||
}
|
||||
|
||||
/// Encode the trust into a byte array
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::certificate::CertificateError::{CertificateLengthError, Unexpected};
|
||||
use crate::certificate::CertificateError::CertificateLengthError;
|
||||
use crate::certificate::{Certificate, CertificateError};
|
||||
use crate::public_key_hashable::PublicKeyHashable as PK;
|
||||
use crate::revoke::Revoke;
|
||||
@ -24,8 +24,8 @@ use crate::trust_graph::TrustGraphError::{
|
||||
CertificateCheckError, EmptyChain, InternalStorageError, NoRoot,
|
||||
};
|
||||
use crate::trust_graph_storage::Storage;
|
||||
use crate::trust_node::{Auth, TrustNode};
|
||||
use crate::StorageError;
|
||||
use crate::trust_relation::Auth;
|
||||
use crate::{StorageError, TrustError};
|
||||
use fluence_keypair::public_key::PublicKey;
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
@ -35,7 +35,9 @@ use std::time::Duration;
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
/// 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.
|
||||
/// TODO serialization/deserialization
|
||||
@ -68,6 +70,14 @@ pub enum TrustGraphError {
|
||||
#[source]
|
||||
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 {
|
||||
@ -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>
|
||||
where
|
||||
S: Storage,
|
||||
@ -92,95 +105,104 @@ where
|
||||
}
|
||||
|
||||
/// Insert new root weight
|
||||
pub fn add_root_weight(
|
||||
pub fn add_root_weight_factor(
|
||||
&mut self,
|
||||
pk: PublicKey,
|
||||
weight: Weight,
|
||||
weight: WeightFactor,
|
||||
) -> 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 get(&self, pk: PublicKey) -> Result<Option<TrustNode>, TrustGraphError> {
|
||||
Ok(self.storage.get(&pk.into())?)
|
||||
pub fn add_trust<T, P>(
|
||||
&mut self,
|
||||
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
|
||||
pub fn add<C>(&mut self, cert: C, cur_time: Duration) -> Result<(), TrustGraphError>
|
||||
where
|
||||
C: Borrow<Certificate>,
|
||||
{
|
||||
let roots: Vec<PublicKey> = self
|
||||
.storage
|
||||
.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 chain = &cert.borrow().chain;
|
||||
let mut issued_by = chain.get(0).ok_or(EmptyChain)?.issued_for.clone();
|
||||
|
||||
let mut chain = cert.borrow().chain.iter();
|
||||
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;
|
||||
// TODO: optimize to check only root weight
|
||||
for trust in chain {
|
||||
let pk = trust.issued_for.clone().into();
|
||||
|
||||
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;
|
||||
self.add_trust(trust, issued_by, cur_time)?;
|
||||
issued_by = trust.issued_for.clone();
|
||||
}
|
||||
|
||||
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.
|
||||
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
|
||||
P: Borrow<PublicKey>,
|
||||
{
|
||||
if let Some(weight) = self.storage.get_root_weight(pk.borrow().as_ref())? {
|
||||
return Ok(Some(weight));
|
||||
let mut max_weight = 0;
|
||||
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
|
||||
let certs = self.get_all_certs(pk, roots.as_slice())?;
|
||||
self.certificates_weight(certs)
|
||||
let certs = self.get_all_certs(pk, cur_time)?;
|
||||
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
|
||||
/// Returns None if there is no such public key
|
||||
/// or some trust between this key and a root key is revoked.
|
||||
/// TODO handle non-direct revocations
|
||||
pub fn certificates_weight<C, I>(&self, certs: I) -> Result<Option<Weight>, TrustGraphError>
|
||||
pub fn certificates_weight_factor<C, I>(
|
||||
&self,
|
||||
certs: I,
|
||||
) -> Result<Option<WeightFactor>, TrustGraphError>
|
||||
where
|
||||
C: Borrow<Certificate>,
|
||||
I: IntoIterator<Item = C>,
|
||||
@ -192,7 +214,7 @@ where
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut weight = std::u32::MAX;
|
||||
let mut weight_factor = u32::MAX;
|
||||
|
||||
for cert in certs {
|
||||
let c = cert.borrow();
|
||||
@ -202,31 +224,30 @@ where
|
||||
.first()
|
||||
.ok_or(CertificateCheckError(CertificateLengthError))?;
|
||||
|
||||
let root_weight = self
|
||||
let root_weight_factor = self
|
||||
.storage
|
||||
.get_root_weight(first.issued_for.as_ref())?
|
||||
.get_root_weight_factor(first.issued_for.as_ref())?
|
||||
.ok_or(NoRoot)?;
|
||||
|
||||
// certificate weight = root weight + 1 * every other element in the chain
|
||||
// (except root, so the formula is `root weight + chain length - 1`)
|
||||
weight = std::cmp::min(weight, root_weight + c.chain.len() as u32 - 1)
|
||||
// certificate weight_factor = root weight factor + 1 * every other element in the chain
|
||||
// (except root, so the formula is `root weight factor + chain length - 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
|
||||
/// 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(
|
||||
&self,
|
||||
node: &TrustNode,
|
||||
pk: &PK,
|
||||
roots: HashSet<&PK>,
|
||||
) -> Result<Vec<Vec<Auth>>, TrustGraphError> {
|
||||
// queue to collect all chains in the trust graph (each chain is a path in the trust graph)
|
||||
let mut chains_queue: VecDeque<Vec<Auth>> = VecDeque::new();
|
||||
|
||||
let node_auths: Vec<Auth> = node.authorizations().cloned().collect();
|
||||
let node_auths: Vec<Auth> = self.storage.get_authorizations(pk)?;
|
||||
// put all auth in the queue as the first possible paths through the graph
|
||||
for auth in node_auths {
|
||||
chains_queue.push_back(vec![auth]);
|
||||
@ -244,13 +265,9 @@ where
|
||||
.last()
|
||||
.expect("`cur_chain` always has at least one element");
|
||||
|
||||
let auths: Vec<Auth> = self
|
||||
let auths = self
|
||||
.storage
|
||||
.get(&last.issued_by.clone().into())?
|
||||
.ok_or(CertificateCheckError(Unexpected))?
|
||||
.authorizations()
|
||||
.cloned()
|
||||
.collect();
|
||||
.get_authorizations(&last.issued_by.clone().into())?;
|
||||
|
||||
for auth in auths {
|
||||
// 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:
|
||||
// - 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
|
||||
let self_signed = last.issued_by == last.trust.issued_for;
|
||||
let issued_by: &PK = last.issued_by.as_ref();
|
||||
@ -280,32 +297,28 @@ where
|
||||
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
|
||||
/// and one of the destinations is the root of this chain.
|
||||
pub fn get_all_certs<P>(
|
||||
&self,
|
||||
&mut self,
|
||||
issued_for: P,
|
||||
roots: &[PublicKey],
|
||||
cur_time: Duration,
|
||||
) -> Result<Vec<Certificate>, TrustGraphError>
|
||||
where
|
||||
P: Borrow<PublicKey>,
|
||||
{
|
||||
// get all auths (edges) for issued public key
|
||||
let issued_for_node = self.storage.get(issued_for.borrow().as_ref())?;
|
||||
// garbage collect
|
||||
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 roots = keys.iter().chain(roots).collect();
|
||||
let roots = keys.iter().collect();
|
||||
|
||||
match issued_for_node {
|
||||
Some(node) => Ok(self
|
||||
.bf_search_paths(&node, roots)?
|
||||
.iter()
|
||||
Ok(self
|
||||
.bf_search_paths(issued_for.borrow().as_ref(), roots)?
|
||||
.into_iter()
|
||||
.map(|auths| {
|
||||
// TODO: can avoid cloning here by returning &Certificate
|
||||
let trusts: Vec<Trust> =
|
||||
auths.iter().map(|auth| auth.trust.clone()).rev().collect();
|
||||
let trusts: Vec<Trust> = auths.into_iter().map(|auth| auth.trust).rev().collect();
|
||||
Certificate::new_unverified(trusts)
|
||||
})
|
||||
.filter(|c| {
|
||||
@ -317,365 +330,13 @@ where
|
||||
);
|
||||
c.chain.len() > 1
|
||||
})
|
||||
.collect()),
|
||||
None => Ok(Vec::new()),
|
||||
}
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Mark public key as revoked.
|
||||
pub fn revoke(&mut self, revoke: Revoke) -> Result<(), TrustGraphError> {
|
||||
Revoke::verify(&revoke)?;
|
||||
|
||||
let pk: PK = revoke.pk.clone().into();
|
||||
|
||||
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);
|
||||
Ok(self.storage.revoke(revoke)?)
|
||||
}
|
||||
}
|
||||
|
@ -1,126 +1,28 @@
|
||||
use crate::public_key_hashable::PublicKeyHashable as PK;
|
||||
use crate::revoke::Revoke;
|
||||
use crate::trust_graph::Weight;
|
||||
use crate::trust_graph_storage::InMemoryStorageError::RevokeError;
|
||||
use crate::trust_node::{Auth, TrustNode};
|
||||
use fluence_keypair::public_key::PublicKey;
|
||||
use std::collections::HashMap;
|
||||
use crate::trust_graph::WeightFactor;
|
||||
use crate::trust_relation::{Auth, TrustRelation};
|
||||
use std::fmt::Display;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
pub trait StorageError: std::error::Error + Display {}
|
||||
|
||||
pub trait Storage {
|
||||
type Error: StorageError + 'static;
|
||||
|
||||
fn get(&self, pk: &PK) -> Result<Option<TrustNode>, Self::Error>;
|
||||
fn insert(&mut self, pk: PK, node: TrustNode) -> Result<(), Self::Error>;
|
||||
fn get_relation(
|
||||
&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 add_root_weight(&mut self, pk: PK, weight: Weight) -> Result<(), Self::Error>;
|
||||
fn get_authorizations(&self, pk: &PK) -> Result<Vec<Auth>, 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 revoke(&mut self, pk: &PK, revoke: Revoke) -> Result<(), Self::Error>;
|
||||
fn update_auth(
|
||||
&mut self,
|
||||
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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn revoke(&mut self, revoke: Revoke) -> Result<(), Self::Error>;
|
||||
fn update_auth(&mut self, auth: Auth, cur_time: Duration) -> Result<(), Self::Error>;
|
||||
fn remove_expired(&mut self, current_time: Duration) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
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