diff --git a/crates/particle-node-tests/tests/builtin.rs b/crates/particle-node-tests/tests/builtin.rs index bad405c90b..ee69f20b40 100644 --- a/crates/particle-node-tests/tests/builtin.rs +++ b/crates/particle-node-tests/tests/builtin.rs @@ -22,7 +22,7 @@ use std::str::FromStr; use std::time::Duration; use eyre::{Report, WrapErr}; -use fluence_keypair::{KeyPair, Signature}; +use fluence_keypair::{KeyFormat, KeyPair, Signature}; use itertools::Itertools; use libp2p::core::Multiaddr; use libp2p::kad::kbucket::Key; @@ -1629,6 +1629,62 @@ fn json_builtins() { } } +#[test] +fn insecure_sign_verify() { + let kp = KeyPair::from_secret_key((0..32).collect(), KeyFormat::Ed25519).unwrap(); + let swarms = make_swarms_with_builtins( + 1, + "tests/builtins/services".as_ref(), + Some(kp.clone()), + None, + ); + + let mut client = ConnectedClient::connect_to(swarms[0].multiaddr.clone()) + .wrap_err("connect client") + .unwrap(); + + client.send_particle( + r#" + (seq + (seq + (call relay ("registry" "get_record_bytes") ["key_id" "" [] [] 1 []] data) + (seq + (call relay ("insecure_sig" "sign") [data] sig_result) + (call relay ("insecure_sig" "verify") [sig_result.$.signature.[0]! data] result) + ) + ) + (call %init_peer_id% ("op" "return") [data sig_result result]) + ) + "#, + hashmap! { + "relay" => json!(client.node.to_string()), + }, + ); + + use serde_json::Value::Array; + use serde_json::Value::Bool; + use serde_json::Value::Object; + + if let [Array(data), Object(sig_result), Bool(result)] = + client.receive_args().unwrap().as_slice() + { + let data: Vec<_> = data.iter().map(|n| n.as_u64().unwrap() as u8).collect(); + + assert!(sig_result["success"].as_bool().unwrap()); + let signature = sig_result["signature"].as_array().unwrap()[0] + .as_array() + .unwrap() + .iter() + .map(|n| n.as_u64().unwrap() as u8) + .collect(); + let signature = Signature::from_bytes(kp.public().get_key_format(), signature); + assert!(result); + assert!(kp.public().verify(&data, &signature).is_ok()); + } else { + panic!("incorrect args: expected three arguments") + } +} + fn binary( service: &str, func: &str, diff --git a/particle-builtins/src/builtins.rs b/particle-builtins/src/builtins.rs index 9e89c62e86..aed8d0f0a9 100644 --- a/particle-builtins/src/builtins.rs +++ b/particle-builtins/src/builtins.rs @@ -23,7 +23,7 @@ use std::str::FromStr; use std::time::{Duration, Instant}; use derivative::Derivative; -use fluence_keypair::{KeyPair, Signature}; +use fluence_keypair::{KeyFormat, KeyPair, Signature}; use futures::stream::FuturesUnordered; use futures::StreamExt; use humantime_serde::re::humantime::format_duration as pretty; @@ -83,6 +83,9 @@ pub struct Builtins { pub custom_services: RwLock>, particles_vault_dir: path::PathBuf, + + #[derivative(Debug = "ignore")] + insecure_keypair: KeyPair, } impl Builtins @@ -125,6 +128,8 @@ where node_info, particles_vault_dir, custom_services: <_>::default(), + insecure_keypair: KeyPair::from_secret_key((0..32).collect(), KeyFormat::Ed25519) + .expect("error creating insecure keypair"), } } @@ -220,45 +225,49 @@ where ("debug", "stringify") => self.stringify(args.function_args), - ("stat", "service_memory") => wrap(self.service_mem_stats(args, particle)), - ("stat", "service_stat") => wrap(self.service_stat(args, particle)), - - ("math", "add") => binary(args, |x: i64, y: i64| -> R { math::add(x, y) }), - ("math", "sub") => binary(args, |x: i64, y: i64| -> R { math::sub(x, y) }), - ("math", "mul") => binary(args, |x: i64, y: i64| -> R { math::mul(x, y) }), - ("math", "fmul") => binary(args, |x: f64, y: f64| -> R { math::fmul_floor(x, y) }), - ("math", "div") => binary(args, |x: i64, y: i64| -> R { math::div(x, y) }), - ("math", "rem") => binary(args, |x: i64, y: i64| -> R { math::rem(x, y) }), - ("math", "pow") => binary(args, |x: i64, y: u32| -> R { math::pow(x, y) }), - ("math", "log") => binary(args, |x: i64, y: i64| -> R { math::log(x, y) }), - - ("cmp", "gt") => binary(args, |x: i64, y: i64| -> R { math::gt(x, y) }), - ("cmp", "gte") => binary(args, |x: i64, y: i64| -> R { math::gte(x, y) }), - ("cmp", "lt") => binary(args, |x: i64, y: i64| -> R { math::lt(x, y) }), - ("cmp", "lte") => binary(args, |x: i64, y: i64| -> R { math::lte(x, y) }), - ("cmp", "cmp") => binary(args, |x: i64, y: i64| -> R { math::cmp(x, y) }), - - ("array", "sum") => unary(args, |xs: Vec | -> R { math::array_sum(xs) }), - ("array", "dedup") => unary(args, |xs: Vec| -> R, _> { math::dedup(xs) }), - ("array", "intersect") => binary(args, |xs: HashSet, ys: HashSet| -> R, _> { math::intersect(xs, ys) }), - ("array", "diff") => binary(args, |xs: HashSet, ys: HashSet| -> R, _> { math::diff(xs, ys) }), - ("array", "sdiff") => binary(args, |xs: HashSet, ys: HashSet| -> R, _> { math::sdiff(xs, ys) }), - ("array", "slice") => wrap(self.array_slice(args.function_args)), - ("array", "length") => wrap(self.array_length(args.function_args)), - - ("sig", "sign") => wrap(self.sign(args)), - ("sig", "verify") => wrap(self.verify(args)), - ("sig", "get_peer_id") => wrap(self.get_peer_id()), - - ("json", "obj") => wrap(json::obj(args)), - ("json", "put") => wrap(json::put(args)), - ("json", "puts") => wrap(json::puts(args)), - ("json", "parse") => unary(args, |s: String| -> R { json::parse(&s) }), - ("json", "stringify") => unary(args, |v: JValue| -> R { Ok(json::stringify(v)) }), - ("json", "obj_pairs") => unary(args, |vs: Vec<(String, JValue)>| -> R { json::obj_from_pairs(vs) }), - ("json", "puts_pairs") => binary(args, |obj: JValue, vs: Vec<(String, JValue)>| -> R { json::puts_from_pairs(obj, vs) }), - - ("run-console", "print") => wrap_unit(Ok(log::debug!(target: "run-console", "{}", json!(args.function_args)))), + ("stat", "service_memory") => wrap(self.service_mem_stats(args, particle)), + ("stat", "service_stat") => wrap(self.service_stat(args, particle)), + + ("math", "add") => binary(args, |x: i64, y: i64| -> R { math::add(x, y) }), + ("math", "sub") => binary(args, |x: i64, y: i64| -> R { math::sub(x, y) }), + ("math", "mul") => binary(args, |x: i64, y: i64| -> R { math::mul(x, y) }), + ("math", "fmul") => binary(args, |x: f64, y: f64| -> R { math::fmul_floor(x, y) }), + ("math", "div") => binary(args, |x: i64, y: i64| -> R { math::div(x, y) }), + ("math", "rem") => binary(args, |x: i64, y: i64| -> R { math::rem(x, y) }), + ("math", "pow") => binary(args, |x: i64, y: u32| -> R { math::pow(x, y) }), + ("math", "log") => binary(args, |x: i64, y: i64| -> R { math::log(x, y) }), + + ("cmp", "gt") => binary(args, |x: i64, y: i64| -> R { math::gt(x, y) }), + ("cmp", "gte") => binary(args, |x: i64, y: i64| -> R { math::gte(x, y) }), + ("cmp", "lt") => binary(args, |x: i64, y: i64| -> R { math::lt(x, y) }), + ("cmp", "lte") => binary(args, |x: i64, y: i64| -> R { math::lte(x, y) }), + ("cmp", "cmp") => binary(args, |x: i64, y: i64| -> R { math::cmp(x, y) }), + + ("array", "sum") => unary(args, |xs: Vec | -> R { math::array_sum(xs) }), + ("array", "dedup") => unary(args, |xs: Vec| -> R, _> { math::dedup(xs) }), + ("array", "intersect") => binary(args, |xs: HashSet, ys: HashSet| -> R, _> { math::intersect(xs, ys) }), + ("array", "diff") => binary(args, |xs: HashSet, ys: HashSet| -> R, _> { math::diff(xs, ys) }), + ("array", "sdiff") => binary(args, |xs: HashSet, ys: HashSet| -> R, _> { math::sdiff(xs, ys) }), + ("array", "slice") => wrap(self.array_slice(args.function_args)), + ("array", "length") => wrap(self.array_length(args.function_args)), + + ("sig", "sign") => wrap(self.sign(args)), + ("sig", "verify") => wrap(self.verify(args)), + ("sig", "get_peer_id") => wrap(self.get_peer_id()), + + ("insecure_sig", "sign") => wrap(self.insecure_sign(args)), + ("insecure_sig", "verify") => wrap(self.insecure_verify(args)), + ("insecure_sig", "get_peer_id") => wrap(self.insecure_get_peer_id()), + + ("json", "obj") => wrap(json::obj(args)), + ("json", "put") => wrap(json::put(args)), + ("json", "puts") => wrap(json::puts(args)), + ("json", "parse") => unary(args, |s: String| -> R { json::parse(&s) }), + ("json", "stringify") => unary(args, |v: JValue| -> R { Ok(json::stringify(v)) }), + ("json", "obj_pairs") => unary(args, |vs: Vec<(String, JValue)>| -> R { json::obj_from_pairs(vs) }), + ("json", "puts_pairs") => binary(args, |obj: JValue, vs: Vec<(String, JValue)>| -> R { json::puts_from_pairs(obj, vs) }), + + ("run-console", "print") => wrap_unit(Ok(log::debug!(target: "run-console", "{}", json!(args.function_args)))), _ => FunctionOutcome::NotDefined { args, params: particle }, } @@ -943,6 +952,49 @@ where fn get_peer_id(&self) -> Result { Ok(JValue::String(self.root_keypair.get_peer_id().to_base58())) } + + fn insecure_sign(&self, args: Args) -> Result { + let mut args = args.function_args.into_iter(); + let result: Result = try { + let data: Vec = Args::next("data", &mut args)?; + json!(self.insecure_keypair.sign(&data)?.to_vec()) + }; + + match result { + Ok(sig) => Ok(json!({ + "success": true, + "error": [], + "signature": vec![sig] + })), + + Err(error) => Ok(json!({ + "success": false, + "error": vec![JValue::from(error)], + "signature": [] + })), + } + } + + fn insecure_verify(&self, args: Args) -> Result { + let mut args = args.function_args.into_iter(); + let signature: Vec = Args::next("signature", &mut args)?; + let data: Vec = Args::next("data", &mut args)?; + let signature = + Signature::from_bytes(self.insecure_keypair.public().get_key_format(), signature); + + Ok(JValue::Bool( + self.insecure_keypair + .public() + .verify(&data, &signature) + .is_ok(), + )) + } + + fn insecure_get_peer_id(&self) -> Result { + Ok(JValue::String( + self.insecure_keypair.get_peer_id().to_base58(), + )) + } } fn make_module_config(args: Args) -> Result {