Skip to content

Commit

Permalink
signer: Add rune check to the signer
Browse files Browse the repository at this point in the history
We create a context now and check the context agains the rune.

Signed-off-by: Peter Neuroth <[email protected]>
  • Loading branch information
nepet committed Jan 11, 2024
1 parent fc29d85 commit a42d118
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 15 deletions.
13 changes: 13 additions & 0 deletions libs/gl-client/src/runes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ pub struct Context {
pub method: String,
// The public key associated with the request.
pub pubkey: String,
// The unique id.
pub unique_id: String,
// The timestamp associated with the request.
pub time: SystemTime,
// Todo (nepet): Add param field that uses enum or serde to store the params of a call.
Expand All @@ -180,6 +182,7 @@ impl Check for Context {
/// * `Ok(())` if the check is successful, an `Err` containing a `RuneError` otherwise.
fn check_alternative(&self, alt: &Alternative) -> anyhow::Result<(), RuneError> {
let value = match alt.get_field().as_str() {
"" => self.unique_id.clone(),
"method" => self.method.clone(),
"pubkey" => self.pubkey.clone(),
"time" => self
Expand Down Expand Up @@ -322,41 +325,47 @@ mod tests {
method: String::new(),
pubkey: String::from("020000000000000000"),
time: SystemTime::now(),
unique_id: String::new(),
};
assert!(r1.are_restrictions_met(ctx).is_ok());
// Check with method="ListFunds", pubkey=020000000000000000
let ctx = Context {
method: String::from("ListFunds"),
pubkey: String::from("020000000000000000"),
time: SystemTime::now(),
unique_id: String::new(),
};
assert!(r1.are_restrictions_met(ctx).is_ok());
// Check with method="GetInfo", pubkey=""
let ctx = Context {
method: String::from("GetInfo"),
pubkey: String::new(),
time: SystemTime::now(),
unique_id: String::new(),
};
assert!(r2.are_restrictions_met(ctx).is_ok());
// Check with method="GetInfo", pubkey="020000000000000000"
let ctx = Context {
method: String::from("GetInfo"),
pubkey: String::from("020000000000000000"),
time: SystemTime::now(),
unique_id: String::new(),
};
assert!(r2.are_restrictions_met(ctx).is_ok());
// Check with method="GetInfo", pubkey=""
let ctx = Context {
method: String::from("GetInfo"),
pubkey: String::new(),
time: SystemTime::now(),
unique_id: String::new(),
};
assert!(r3.are_restrictions_met(ctx).is_ok());
// Check with method="", pubkey="020000"
let ctx = Context {
method: String::new(),
pubkey: String::from("020000000000000000"),
time: SystemTime::now(),
unique_id: String::new(),
};
assert!(r4.are_restrictions_met(ctx).is_ok());

Expand All @@ -366,27 +375,31 @@ mod tests {
method: String::from("ListFunds"),
pubkey: String::from("030000"),
time: SystemTime::now(),
unique_id: String::new(),
};
assert!(r1.are_restrictions_met(ctx).is_err());
// Check with method="ListFunds", pubkey=030000, wrong method.
let ctx = Context {
method: String::from("ListFunds"),
pubkey: String::from("030000"),
time: SystemTime::now(),
unique_id: String::new(),
};
assert!(r2.are_restrictions_met(ctx).is_err());
// Check with pubkey=030000, pubkey present.
let ctx = Context {
method: String::new(),
pubkey: String::from("030000"),
time: SystemTime::now(),
unique_id: String::new(),
};
assert!(r3.are_restrictions_met(ctx).is_err());
// Check with method="GetInfo", method present.
let ctx = Context {
method: String::from("GetInfo"),
pubkey: String::new(),
time: SystemTime::now(),
unique_id: String::new(),
};
assert!(r4.are_restrictions_met(ctx).is_err());
}
Expand Down
82 changes: 67 additions & 15 deletions libs/gl-client/src/signer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::pb::scheduler::{scheduler_client::SchedulerClient, NodeInfoRequest, U
/// caller thread, streaming incoming requests, verifying them,
/// signing if ok, and then shipping the response to the node.
use crate::pb::{node_client::NodeClient, Empty, HsmRequest, HsmRequestContext, HsmResponse};
use crate::runes;
use crate::signer::resolve::Resolver;
use crate::tls::TlsConfig;
use crate::{node, node::Client};
Expand All @@ -18,12 +19,12 @@ use lightning_signer::node::NodeServices;
use lightning_signer::policy::filter::FilterRule;
use lightning_signer::util::crypto_utils;
use log::{debug, info, trace, warn};
use runeauth::{Condition, MapChecker, Restriction, Rune, RuneError};
use std::collections::HashMap;
use runeauth::{Condition, Restriction, Rune, RuneError};
use std::convert::TryFrom;
use std::convert::TryInto;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::SystemTime;
use tokio::sync::mpsc;
use tokio::time::{sleep, Duration};
use tonic::transport::{Endpoint, Uri};
Expand Down Expand Up @@ -285,14 +286,10 @@ impl Signer {
return Err(anyhow!("rune is missing pubkey field"));
}

let mut checks: HashMap<String, String> = HashMap::new();
checks.insert("pubkey".to_string(), hex::encode(request.pubkey));

// Runes only check on the version if the unique id field is set. The id
// and the version are part of the empty field.
if let Some(device_id) = rune.get_id() {
checks.insert("".to_string(), format!("{}-{}", device_id, RUNE_VERSION));
}
let unique_id = match rune.get_id() {
Some(id) => format!("{}-{}", id, RUNE_VERSION),
None => String::new(),
};

// Check that the request points to `cln.Node`.
let mut parts = request.uri.split('/');
Expand Down Expand Up @@ -324,12 +321,21 @@ impl Signer {
return Err(anyhow!("can not extract uri form request"));
}
};
checks.insert("method".to_string(), method.to_string());

match self
.master_rune
.check_with_reason(&rune64, MapChecker { map: checks })
{
println!("GOT METHOD {}", method);

let ctx = runes::Context {
method,
pubkey: hex::encode(request.pubkey),
time: SystemTime::now(),
unique_id,
};

println!(
"MATCH RUNE {}, {}, {:?}, {}",
ctx.method, ctx.pubkey, ctx.time, ctx.unique_id
);
match self.master_rune.check_with_reason(&rune64, ctx) {
Ok(_) => Ok(()),
Err(e) => Err(e.into()),
}
Expand Down Expand Up @@ -1164,4 +1170,50 @@ mod tests {
};
assert!(signer.verify_rune(r).is_err());
}

#[test]
fn test_empty_rune_is_valid() {
let signer =
Signer::new(vec![0u8; 32], Network::Bitcoin, TlsConfig::new().unwrap()).unwrap();

// This is just a placeholder public key, could also be a different one;
let pubkey = signer.node_id();
let pubkey_rest = format!("pubkey={}", hex::encode(&pubkey));

let rune = signer.create_rune(None, vec![vec![&pubkey_rest]]).unwrap();
let uri = "/cln.Node/Pay".to_string();
assert!(signer
.verify_rune(crate::pb::PendingRequest {
request: vec![],
uri,
signature: vec![],
pubkey,
timestamp: 0,
rune: general_purpose::URL_SAFE.decode(rune).unwrap(),
})
.is_ok());
}

#[test]
fn test_empty_rune_checks_pubkey() {
let signer =
Signer::new(vec![0u8; 32], Network::Bitcoin, TlsConfig::new().unwrap()).unwrap();

// This is just a placeholder public key, could also be a different one;
let pubkey = signer.node_id();
let pubkey_rest = format!("pubkey={}", hex::encode(&pubkey));

let rune = signer.create_rune(None, vec![vec![&pubkey_rest]]).unwrap();
let uri = "/cln.Node/Pay".to_string();
assert!(signer
.verify_rune(crate::pb::PendingRequest {
request: vec![],
uri,
signature: vec![],
pubkey: hex::decode("33aabb").unwrap(),
timestamp: 0,
rune: general_purpose::URL_SAFE.decode(rune).unwrap(),
})
.is_err());
}
}

0 comments on commit a42d118

Please sign in to comment.