Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drop hardcoded sign key, replace with support for N keys via settings #610

Merged
merged 8 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>,
jeffgrunewald marked this conversation as resolved.
Show resolved Hide resolved
}

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> {
jeffgrunewald marked this conversation as resolved.
Show resolved Hide resolved
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");
jeffgrunewald marked this conversation as resolved.
Show resolved Hide resolved
let _serial = buf.get_u32_le();
let xor = bincode::deserialize::<Xor32>(buf)?;
madninja marked this conversation as resolved.
Show resolved Hide resolved
return Ok(xor);
}
Err(_) => continue,
jeffgrunewald marked this conversation as resolved.
Show resolved Hide resolved
}
}
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,10 @@ pub struct Settings {
/// Cadence at which we poll for an updated denylist (secs)
#[serde(default = "default_trigger_interval")]
pub trigger: u64,
// vec of b58 helium encoded pubkeys
// used to verify signature of denylist filters
#[serde(default)]
pub valid_sign_keys: Vec<String>,
}

pub fn default_log() -> String {
Expand All @@ -36,7 +40,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> {
jeffgrunewald marked this conversation as resolved.
Show resolved Hide resolved
let mut builder = Config::builder();

if let Some(file) = path {
Expand All @@ -50,10 +54,16 @@ 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> {
self.valid_sign_keys
.iter()
.map(|pubkey| PublicKey::from_str(pubkey))
.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())?;
jeffgrunewald marked this conversation as resolved.
Show resolved Hide resolved
// 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