Skip to content

Commit

Permalink
drop hardcoded sign key, replace with support for N keys via settings
Browse files Browse the repository at this point in the history
  • Loading branch information
andymck committed Aug 22, 2023
1 parent 95df894 commit 9134d8a
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 28 deletions.
47 changes: 24 additions & 23 deletions denylist/src/denylist.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
use crate::{client::DenyListClient, models::metadata::Asset, Error, Result};
use crate::{client::DenyListClient, models::metadata::Asset, Error, Result, Settings};
use bytes::Buf;
use helium_crypto::{PublicKey, PublicKeyBinary, Verify};
use serde::Serialize;
use std::{fs, hash::Hasher, path, str::FromStr};
use std::{fs, hash::Hasher, path};
use twox_hash::XxHash64;
use xorf::{Filter as XorFilter, Xor32};

pub const SERIAL_SIZE: usize = 32;

/// the pubkey used to verify the signature of denylist updates
// TODO: is there a better home for this key ?
const PUB_KEY_B58: &str = "1SbEYKju337P6aYsRd9DT2k4qgK5ZK62kXbSvnJgqeaxK3hqQrYURZjL";
/// a copy of the last saved filter bin downloaded from github
/// if present will be used to initialise the denylist upon verifier startup
// TODO: look at using the tempfile crate to handle this
Expand All @@ -23,6 +19,7 @@ pub struct DenyList {
pub client: DenyListClient,
#[serde(skip_serializing)]
pub filter: Xor32,
pub valid_sign_keys: Vec<PublicKey>,
}

impl TryFrom<Vec<PublicKeyBinary>> for DenyList {
Expand All @@ -35,12 +32,13 @@ impl TryFrom<Vec<PublicKeyBinary>> for DenyList {
tag_name: 0,
client,
filter,
valid_sign_keys: vec![],
})
}
}

impl DenyList {
pub fn new() -> Result<Self> {
pub fn new(settings: Settings) -> Result<Self> {
tracing::debug!("initializing new denylist");
// if exists default to the local saved filter bin,
// otherwise default to empty filter
Expand All @@ -54,14 +52,17 @@ impl DenyList {
);
Vec::new()
});
let filter = filter_from_bin(&bin).unwrap_or_else(|_| Xor32::from(Vec::new()));
let valid_sign_keys = settings.valid_sign_keys()?;
let filter =
filter_from_bin(&bin, &valid_sign_keys).unwrap_or_else(|_| Xor32::from(Vec::new()));
let client = DenyListClient::new()?;
Ok(Self {
// default tag to 0, proper tag name will be set on first
// call to update_to_latest
tag_name: 0,
client,
filter,
valid_sign_keys,
})
}

Expand Down Expand Up @@ -93,7 +94,7 @@ impl DenyList {
tracing::debug!("found asset for tag");
let asset_url = &asset.browser_download_url;
let bin = self.client.get_bin(asset_url).await?;
if let Ok(filter) = filter_from_bin(&bin) {
if let Ok(filter) = filter_from_bin(&bin, &self.valid_sign_keys) {
self.filter = filter;
self.tag_name = new_tag_name;
save_local_filter_bin(&bin, FILTER_BIN_PATH)?;
Expand All @@ -113,7 +114,7 @@ impl DenyList {
}

/// deconstruct bytes into the filter component parts
pub fn filter_from_bin(bin: &Vec<u8>) -> Result<Xor32> {
pub fn filter_from_bin(bin: &Vec<u8>, valid_sign_keys: &Vec<PublicKey>) -> Result<Xor32> {
if bin.is_empty() {
return Err(Error::InvalidBinary("invalid filter bin".to_string()));
}
Expand All @@ -122,21 +123,21 @@ pub fn filter_from_bin(bin: &Vec<u8>) -> Result<Xor32> {
let _version = buf.get_u8();
let signature_len = buf.get_u16_le() as usize;
let signature = buf.copy_to_bytes(signature_len).to_vec();
let pubkey = PublicKey::from_str(PUB_KEY_B58)?;
match pubkey.verify(buf, &signature) {
Ok(_) => {
tracing::info!("updating filter to latest");
let _serial = buf.get_u32_le();
let xor = bincode::deserialize::<Xor32>(buf)?;
Ok(xor)
}
Err(_) => {
tracing::warn!("filter signature verification failed");
Err(Error::InvalidBinary(
"filter signature verification failed".to_string(),
))
for pubkey in valid_sign_keys {
match pubkey.verify(buf, &signature) {
Ok(_) => {
tracing::info!("updating filter to latest");
let _serial = buf.get_u32_le();
let xor = bincode::deserialize::<Xor32>(buf)?;
return Ok(xor);
}
Err(_) => continue,
}
}
tracing::warn!("filter signature verification failed");
Err(Error::InvalidBinary(
"filter signature verification failed".to_string(),
))
}

fn public_key_hash<R: AsRef<[u8]>>(public_key: R) -> u64 {
Expand Down
18 changes: 14 additions & 4 deletions denylist/src/settings.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{Error, Result};
use config::{Config, Environment, File};
use helium_crypto::PublicKey;
use serde::Deserialize;
use std::{path::Path, time::Duration};
use std::{path::Path, str::FromStr, time::Duration};

#[derive(Debug, Deserialize, Clone)]
pub struct Settings {
Expand All @@ -15,6 +15,9 @@ pub struct Settings {
/// Cadence at which we poll for an updated denylist (secs)
#[serde(default = "default_trigger_interval")]
pub trigger: u64,
// comma seperated string of b58 encoded pubkeys
// used to verify signature of denylist filters
pub valid_sign_keys: String,
}

pub fn default_log() -> String {
Expand All @@ -36,7 +39,7 @@ impl Settings {
/// Environemnt overrides have the same name as the entries in the settings
/// file in uppercase and prefixed with "DENYLIST_". For example
/// "DENYLIST_LOG" will override the log setting.
pub fn new<P: AsRef<Path>>(path: Option<P>) -> Result<Self> {
pub fn new<P: AsRef<Path>>(path: Option<P>) -> Result<Self, config::ConfigError> {
let mut builder = Config::builder();

if let Some(file) = path {
Expand All @@ -50,10 +53,17 @@ impl Settings {
.add_source(Environment::with_prefix("DENYLIST").separator("_"))
.build()
.and_then(|config| config.try_deserialize())
.map_err(Error::from)
}

pub fn trigger_interval(&self) -> Duration {
Duration::from_secs(self.trigger)
}

pub fn valid_sign_keys(&self) -> Result<Vec<PublicKey>, helium_crypto::Error> {
let seperator: char = ',';
self.valid_sign_keys
.split(seperator)
.map(PublicKey::from_str)
.collect()
}
}
2 changes: 1 addition & 1 deletion iot_verifier/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl Runner {
let beacon_max_retries = settings.beacon_max_retries;
let witness_max_retries = settings.witness_max_retries;
let deny_list_latest_url = settings.denylist.denylist_url.clone();
let mut deny_list = DenyList::new()?;
let mut deny_list = DenyList::new(settings.denylist.clone())?;
// force update to latest in order to update the tag name
// when first run, the denylist will load the local filter
// but we dont save the tag name so it defaults to 0
Expand Down

0 comments on commit 9134d8a

Please sign in to comment.