Skip to content
Snippets Groups Projects
Commit dabb5275 authored by Cédric Moreau's avatar Cédric Moreau
Browse files

feat: add `certify` command

parent a5edc5c5
No related branches found
No related tags found
No related merge requests found
......@@ -133,6 +133,7 @@ dependencies = [
"cryptoxide",
"reqwest",
"scrypt",
"serde",
"serde_json",
]
......@@ -774,6 +775,20 @@ name = "serde"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
......
......@@ -11,4 +11,5 @@ base64 = "0.13.0"
bs58 = "0.4.0"
cryptoxide = "0.3.3"
reqwest = { version = "0.11.4", features = ["json", "blocking"] }
serde = { version = "1.0.130", features = ["derive"] }
serde_json = "1.0.67"
\ No newline at end of file
use serde_json::Value;
use crate::bma::BmaNode;
pub struct Block<> {
pub version: u64,
pub currency: String,
pub number: u64,
pub hash: String
}
impl Block {
pub fn from_json(json: &Value) -> Block {
Block {
currency: json["currency"].as_str().unwrap().into(),
version: json["version"].as_u64().unwrap(),
number: json["number"].as_u64().unwrap(),
hash: json["hash"].as_str().unwrap().into()
}
}
pub fn blockstamp(&self) -> String {
format!("{}-{}", self.number, self.hash)
}
}
pub fn current(node: &BmaNode) -> Block {
let address = node.get_address();
let resp = reqwest::blocking::get(format!("{}/blockchain/current", address)).expect("Could not fetch /blockchain/current from distant node");
let json = &resp.json().expect("Could not parse /blockchain/current JSON response");
Block::from_json(&json)
}
\ No newline at end of file
mod current;
pub use current::current;
\ No newline at end of file
use crate::bma::{BmaNode};
use serde_json::Value;
pub struct LookupResult<'a>(pub(crate) &'a serde_json::Value);
pub struct LookupIdentity {
pub public: String,
pub uid: String,
pub blockstamp: String,
pub signature: String,
pub revoked: bool
}
impl LookupIdentity {
pub fn new(public: &str, uid: &str, blockstamp: &str, signature: &str, revoked: bool) -> LookupIdentity {
LookupIdentity {
public: String::from(public),
uid: String::from(uid),
blockstamp: String::from(blockstamp),
signature: String::from(signature),
revoked
}
}
}
pub fn lookup(node: BmaNode, uid_or_pub: String) -> () {
let address = node.get_address();
let resp = reqwest::blocking::get(format!("{}/wot/lookup/{}", address, uid_or_pub)).expect("Could not fetch lookup data from distant node");
lookup_print(&LookupResult(&resp.json().expect("Could not get JSON result from distant node")));
}
pub fn lookup_print(lookup_result: &LookupResult) -> () {
lookup2identities(lookup_result).iter().for_each(|i| println!("{} {} {}", i.public, i.uid, if i.revoked { "revoked" } else { "valid" }))
}
pub fn lookup2identities(lookup_result: &LookupResult) -> Vec<LookupIdentity> {
let mut identities: Vec<LookupIdentity> = vec![];
for result in lookup_result.0["results"].as_array().unwrap() {
let pubkey = result["pubkey"].as_str().unwrap();
let empty: &Vec<Value> = &Vec::new();
let uids: &Vec<Value> = result["uids"].as_array().unwrap_or(empty);
for anUid in uids.iter() {
let uid = anUid["uid"].as_str().unwrap();
let blockstamp = anUid["meta"]["timestamp"].as_str().unwrap();
let signature = anUid["self"].as_str().unwrap();
let revoked = anUid["revoked"].as_bool().unwrap();
identities.push(LookupIdentity::new(pubkey, uid, blockstamp, signature,revoked));
}
}
identities
}
\ No newline at end of file
......@@ -3,21 +3,11 @@ use reqwest;
use std::fmt::Display;
pub use crate::bma::node::BmaNode;
use serde_json::Value;
use reqwest::blocking::Response;
use crate::bma::lookup_identity::{LookupIdentity, lookup2identities, lookup_print, LookupResult};
use std::process::id;
mod node;
pub fn lookup(node: BmaNode, uid_or_pub: String) -> () {
let address = node.get_address();
let resp = reqwest::blocking::get(format!("{}/wot/lookup/{}", address, uid_or_pub)).expect("Could not fetch lookup data from distant node");
let res_json: serde_json::Value = resp.json().expect("Could not get JSON result from distant node");
for result in res_json["results"].as_array().unwrap() {
let pubkey = result["pubkey"].as_str().unwrap();
let empty: &Vec<Value> = &Vec::new();
let uids: &Vec<Value> = result["uids"].as_array().unwrap_or(empty);
for anUid in uids.iter() {
let uid = anUid["uid"].as_str().unwrap();
let revoked = anUid["revoked"].as_bool().unwrap();
println!("{} {} {}", pubkey, uid, if revoked { "revoked" } else { "valid" })
}
}
}
\ No newline at end of file
pub mod lookup_identity;
pub mod blockchain;
pub mod wot;
\ No newline at end of file
use std::fmt::{Display, Formatter};
use reqwest::{blocking, StatusCode};
use serde::{Deserialize, Serialize};
use crate::bma::BmaNode;
use crate::bma::lookup_identity::LookupIdentity;
#[derive(Serialize, Deserialize)]
struct BmaCertification {
cert: String
}
impl Display for BmaCertification {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(self.cert.as_str())
}
}
pub fn certify(node: &BmaNode, idty: &String, sig: &String) -> Result<(), String> {
let mut cert = BmaCertification { cert: format!("{}{}\n", idty, sig) };
let address = node.get_address();
let client = reqwest::blocking::Client::new();
let resp = client.post(format!("{}/wot/certify", address))
.json(&cert)
.send()
.expect("Error during POST /wot/certify");
if resp.status() != 200 {
return Err(format!("KO status : {}, message: {}", resp.status(), resp.text().unwrap()));
}
Ok(())
}
\ No newline at end of file
use crate::bma::BmaNode;
use crate::bma::lookup_identity::{LookupResult, lookup2identities, lookup_print};
use duniter_mini_client::crypto::scrypt_duniter_key::ScryptDuniterKey;
use duniter_mini_client::crypto::duniter_key::ToDuniterKey;
use std::process::id;
pub fn certify(node: BmaNode, uid_or_pub: String) -> Result<(), String> {
let address = node.get_address();
println!("Fetching identity using \"{}\" pattern...", uid_or_pub);
let resp = reqwest::blocking::get(format!("{}/wot/lookup/{}", address, uid_or_pub)).expect("Could not fetch lookup data from distant node");
let res_json = &resp.json().expect("Could not get JSON result from distant node");
let res_json: LookupResult = LookupResult(res_json);
let results = lookup2identities(&res_json);
let idty = match results.len() {
0 => return Err(String::from("No matching identity found")),
1 => &results[0],
_ => {
lookup_print(&res_json);
return Err(format!("Too much identities found ({})", results.len()))
},
};
if idty.revoked {
return Err(format!("Identity {} {} is revoked and cannot be certified anymore.", idty.public, idty.uid))
}
println!("Deriving key for signature...");
let keyring = ScryptDuniterKey::new(String::from("fakesalt"), String::from("fakepasswd")).derive(); // TODO: get salt/passwd from file/cli
println!("Get current block for blockstamp...");
let current = crate::bma::blockchain::current(&node);
let currency = &current.currency;
let issuer = keyring.get_public_base58();
let current_blockstamp = &current.blockstamp();
let certification = format!("Version: 10
Type: Certification
Currency: {}
Issuer: {}
IdtyIssuer: {}
IdtyUniqueID: {}
IdtyTimestamp: {}
IdtySignature: {}
CertTimestamp: {}
", currency, issuer, idty.public, idty.uid, idty.blockstamp, idty.signature, current_blockstamp);
println!("{}", certification);
println!("Sign and submit?");
let signature = keyring.sign(&certification);
println!("Signature: {}", signature);
println!("Submitting...");
// crate::bma::wot::certify(&node, &idty, &issuer, &current_blockstamp, &signature.to_string());
crate::bma::wot::certify(&node, &certification, &signature.to_string());
Ok(())
}
\ No newline at end of file
......@@ -4,6 +4,8 @@ use crate::cli::Command::*;
use crate::bma::BmaNode;
use std::env;
pub(crate) mod certify;
const DEFAULT_NODE: &str = "https://g1-test.duniter.org";
pub enum Command {
......@@ -15,6 +17,8 @@ pub enum Command {
KEYRING(String, String),
/// Search an identity on a Duniter node (BMA API)
LOOKUP(BmaNode, String),
/// Search an identity on a Duniter node (BMA API) and certify it
CERTIFY(BmaNode, String),
/// Unknown command
UNKNOWN(String)
}
......@@ -48,6 +52,12 @@ impl Command {
let bma_node = BmaNode::new(env::var("DUNITER_NODE").unwrap_or(default).as_str());
LOOKUP(bma_node, uid_or_pub)
},
"cert" => {
let uid_or_pub = args.next().expect("UID or pubkey must be provided");
let default = String::from(DEFAULT_NODE);
let bma_node = BmaNode::new(env::var("DUNITER_NODE").unwrap_or(default).as_str());
CERTIFY(bma_node, uid_or_pub)
},
_ => UNKNOWN(String::from(command)),
}
}
......
......@@ -4,7 +4,7 @@ use duniter_mini_client::{compute_pub, compute_sec, compute_key};
use crate::cli::Command;
pub use crate::cli::Command::{PUB, UNKNOWN};
use crate::cli::Command::{SEC, KEYRING, LOOKUP};
use crate::cli::Command::{SEC, KEYRING, LOOKUP, CERTIFY};
use duniter_mini_client::crypto::duniter_key::ScryptDuniterKey;
mod cli;
......@@ -14,11 +14,14 @@ mod bma;
fn main() {
let command = Command::from(env::args());
// TODO: Result<(), String>
match command {
PUB(salt, passwd) => println!("{}", compute_pub(ScryptDuniterKey::new(salt, passwd))),
SEC(salt, passwd) => println!("{}", compute_sec(ScryptDuniterKey::new(salt, passwd))),
KEYRING(salt, passwd) => println!("{}", compute_key(ScryptDuniterKey::new(salt, passwd))),
LOOKUP(bma_node, uid_or_pub) => bma::lookup(bma_node, uid_or_pub),
LOOKUP(bma_node, uid_or_pub) => bma::lookup_identity::lookup(bma_node, uid_or_pub),
CERTIFY(bma_node, uid_or_pub) => cli::certify::certify(bma_node, uid_or_pub).unwrap_or_else(|e| eprintln!("{}", e)),
UNKNOWN(cmd) => eprintln!("Unknown command {}", cmd),
};
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment