Skip to content

Commit

Permalink
feat(Config): convert between versions and expose it in the gen-cli
Browse files Browse the repository at this point in the history
  • Loading branch information
steveej committed Aug 27, 2024
1 parent 9918d68 commit d2b6910
Show file tree
Hide file tree
Showing 9 changed files with 373 additions and 97 deletions.
64 changes: 64 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ thiserror = { workspace = true }
tokio = { version = "1.12.0", features = [ "full" ] }
hc_seed_bundle = "0.2.3"
sodoken = "=0.0.11"
enum-variants-strings = "0.3.0"
strum = { version = "0.26.3", features = ["derive", "strum_macros"] }

[dependencies.argon2min]
git = "https://github.com/Holo-Host/argon2min"
Expand All @@ -38,4 +40,4 @@ wasm-bindgen = ["rand/wasm-bindgen"]
tokio = { version = "1.12.0", features = [ "full" ] }
hc_seed_bundle = "0.2.3"
sodoken = "=0.0.11"
serde_json = "1.0.117"
serde_json = "1.0.117"
117 changes: 115 additions & 2 deletions core/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use arrayref::array_ref;
use ed25519_dalek::{Digest, Sha512, SigningKey, VerifyingKey};
use failure::Error;
use failure::{bail, Error};
use serde::*;
use strum::{EnumDiscriminants, EnumString, VariantNames};

use crate::public_key;
pub const SEED_SIZE: usize = 32;
Expand Down Expand Up @@ -43,6 +44,7 @@ const ARGON2_ADDITIONAL_DATA: &[u8] = b"hpos-config admin ed25519 key v1";

pub type Seed = [u8; SEED_SIZE];

#[cfg_attr(test, derive(Clone, PartialEq))]
#[derive(Debug, Deserialize, Serialize)]
pub struct Admin {
pub email: String,
Expand All @@ -53,12 +55,18 @@ pub struct Admin {
pub public_key: VerifyingKey,
}

#[cfg_attr(test, derive(Clone, PartialEq))]
#[derive(Debug, Deserialize, Serialize)]
pub struct Settings {
pub admin: Admin,
}

#[derive(Debug, Deserialize, Serialize)]
#[cfg_attr(test, derive(Clone, PartialEq))]
#[derive(Debug, Deserialize, Serialize, EnumDiscriminants)]
#[strum_discriminants(
derive(VariantNames, EnumString, strum::Display),
strum(ascii_case_insensitive)
)]
pub enum Config {
#[serde(rename = "v1")]
V1 {
Expand Down Expand Up @@ -146,6 +154,58 @@ impl Config {
| Config::V3 { settings, .. } => settings.admin.public_key,
}
}

/// Try to convert Config instance to the desired ConfigDiscriminants.
/// As some conversions are impossible they will return an error accordingly.
pub fn try_convert(self, desired: ConfigDiscriminants) -> Result<Self, Error> {
match (self, desired) {
(cfg @ Config::V1 { .. }, ConfigDiscriminants::V1) => Ok(cfg),
(Config::V1 { .. }, ConfigDiscriminants::V2) => bail!("cannot convert V1 to V2"),
(Config::V1 { .. }, ConfigDiscriminants::V3) => bail!("cannot convert V1 to V3"),
(
Config::V2 {
device_bundle,
derivation_path,
registration_code,
settings,
},
ConfigDiscriminants::V1,
) => Ok(Config::V2 {
device_bundle,
derivation_path,
registration_code,
settings,
}),
(cfg @ Config::V2 { .. }, ConfigDiscriminants::V2) => Ok(cfg),
(Config::V2 { .. }, ConfigDiscriminants::V3) => bail!("cannot convert V2 to V3"),
(
Config::V3 {
// device_bundle,
// settings,
..
},
ConfigDiscriminants::V1,
) => {
unimplemented!("convert V3 to V1 (lossy)");
}
(
Config::V3 {
device_bundle,
device_derivation_path,
registration_code,
settings,
..
},
ConfigDiscriminants::V2,
) => Ok(Config::V2 {
device_bundle,
derivation_path: device_derivation_path,
registration_code,
settings,
}),
(cfg @ Config::V3 { .. }, ConfigDiscriminants::V3) => Ok(cfg),
}
}
}

// fn generate_keypair(
Expand Down Expand Up @@ -182,3 +242,56 @@ pub fn admin_keypair_from(

Ok(SigningKey::from_bytes(&hash))
}

#[cfg(test)]
mod tests {
use crate::{test_utils::generate_test_hpos_config, Config};

#[tokio::test]
async fn convert_v3_to_v2() {
let (config, _) = generate_test_hpos_config().await.unwrap();

// ensure we start with a v3 config
let v3 = config
.clone()
.try_convert(crate::config::ConfigDiscriminants::V3)
.unwrap();
assert_eq!(config, v3);

let v2 = config
.clone()
.try_convert(crate::config::ConfigDiscriminants::V2)
.unwrap();

if let Config::V2 {
device_bundle,
derivation_path,
registration_code,
settings,
} = v2
{
let v2_device_bundle = device_bundle;
let v2_derivation_path = derivation_path;
let v2_registration_code = registration_code;
let v2_settings = settings;

if let Config::V3 {
device_bundle,
device_derivation_path,
registration_code,
settings,
..
} = config
{
assert_eq!(v2_device_bundle, device_bundle);
assert_eq!(v2_derivation_path, device_derivation_path);
assert_eq!(v2_registration_code, registration_code);
assert_eq!(v2_settings, settings);
} else {
panic!("config isn't v3");
};
} else {
panic!("must match v2");
}
}
}
6 changes: 6 additions & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,10 @@ pub mod public_key;
pub mod types;
pub mod utils;

#[cfg(test)]
pub mod tests;

#[cfg(test)]
pub mod test_utils;

pub use config::{admin_keypair_from, Config};
59 changes: 59 additions & 0 deletions core/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use ed25519_dalek::VerifyingKey;
use failure::Error;

use crate::Config;

/// Can be used akin to a test fixture
pub(crate) async fn generate_test_hpos_config() -> Result<(Config, VerifyingKey), Error> {
// emulate the UI

let master = hc_seed_bundle::UnlockedSeedBundle::new_random()
.await
.unwrap();

let passphrase = sodoken::BufRead::from(b"test-passphrase".to_vec());
let revocation_bundle = master.derive(0).await.unwrap();
let revocation_pub_key = revocation_bundle.get_sign_pub_key().read_lock().to_vec();

let device_derivation_path = 2;
let device_bundle = master.derive(device_derivation_path).await.unwrap();
let device_bundle_encoded_bytes = device_bundle
.lock()
.add_pwhash_cipher(passphrase)
.lock()
.await
.unwrap();
let device_bundle_base64 = base64::encode(&device_bundle_encoded_bytes);

// derive the holoport ID

let holoport_id = device_bundle.derive(1).await.unwrap();

let holoport_id = holoport_id.get_sign_pub_key().read_lock().to_vec();

// initialize a new Config struct
let email = "[email protected]".to_string();
let password = "password".to_string();
let registration_code = "registration-code".to_string();
let rev_key_bytes = revocation_pub_key[0..32].try_into().unwrap();
let revocation_pub_key = VerifyingKey::from_bytes(&rev_key_bytes).unwrap();
let holoport_id_bytes = holoport_id[0..32].try_into().unwrap();
let holoport_id = VerifyingKey::from_bytes(&holoport_id_bytes).unwrap();

Config::new(
// email: String,
email.clone(),
// password: String,
password,
// registration_code: String,
registration_code,
// revocation_pub_key: VerifyingKey,
revocation_pub_key,
// derivation_path: String,
device_derivation_path.to_string(),
// device_bundle: String,
device_bundle_base64.clone(),
// device_pub_key: VerifyingKey,
holoport_id,
)
}
Loading

0 comments on commit d2b6910

Please sign in to comment.