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

Tests and cleanup #241

Merged
merged 4 commits into from
Oct 10, 2024
Merged
Changes from all commits
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
85 changes: 60 additions & 25 deletions src/hsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ const DOMAIN: Domain = Domain::all();
const ID: Id = 0x1;
const SEED_LEN: usize = 32;
const KEY_LEN: usize = 32;
const SHARE_LEN: usize = KEY_LEN + 1;
const LABEL: &str = "backup";
const VERIFIER_FILE: &str = "verifier.json";

const SHARES: usize = 5;
const THRESHOLD: usize = 3;
Expand Down Expand Up @@ -229,11 +231,11 @@ impl Hsm {
Scalar,
ProjectivePoint,
ChaCha20Rng,
{ KEY_LEN + 1 },
SHARE_LEN,
>(*nzs.as_ref(), None, &mut rng)
.map_err(|e| HsmError::SplitKeyFailed { e })?;

let verifier_path = self.out_dir.join("verifier.json");
let verifier_path = self.out_dir.join(VERIFIER_FILE);
debug!(
"Serializing verifier as json to: {}",
verifier_path.display()
Expand Down Expand Up @@ -424,18 +426,15 @@ impl Hsm {
info!("Restoring HSM from backup");
info!("Restoring backup / wrap key from shares");
// vector used to collect shares
let mut shares: Vec<Share<{ KEY_LEN + 1 }>> = Vec::new();
let mut shares: Vec<Share<SHARE_LEN>> = Vec::new();

// deserialize verifier:
// verifier was serialized to output/verifier.json in the provisioning ceremony
// it must be included in and deserialized from the ceremony inputs
let verifier = self.out_dir.join("verifier.json");
let verifier = self.out_dir.join(VERIFIER_FILE);
let verifier = fs::read_to_string(verifier)?;
let verifier: FeldmanVerifier<
Scalar,
ProjectivePoint,
{ KEY_LEN + 1 },
> = serde_json::from_str(&verifier)?;
let verifier: FeldmanVerifier<Scalar, ProjectivePoint, SHARE_LEN> =
serde_json::from_str(&verifier)?;

// get enough shares to recover backup key
for _ in 1..=THRESHOLD {
Expand Down Expand Up @@ -499,7 +498,7 @@ impl Hsm {
};

// construct a Share from the decoded hex string
let share: Share<{ KEY_LEN + 1 }> =
let share: Share<SHARE_LEN> =
match Share::try_from(&share_vec[..]) {
Ok(share) => share,
Err(_) => {
Expand Down Expand Up @@ -536,7 +535,7 @@ impl Hsm {

let scalar = Feldman::<THRESHOLD, SHARES>::combine_shares::<
Scalar,
{ KEY_LEN + 1 },
SHARE_LEN,
>(&shares)
.map_err(|e| HsmError::CombineKeyFailed { e })?;

Expand Down Expand Up @@ -936,13 +935,15 @@ mod tests {
secret
}

fn deserialize_share(share: &str) -> Result<Share<{ KEY_LEN + 1 }>> {
fn deserialize_share(share: &str) -> Result<Share<SHARE_LEN>> {
// filter out whitespace to keep hex::decode happy
let share: String =
share.chars().filter(|c| !c.is_whitespace()).collect();
let share = hex::decode(share)?;
let share = hex::decode(share)
.context("failed to decode share from hex string")?;

Ok(Share::try_from(&share[..])?)
Ok(Share::try_from(&share[..])
.context("Failed to construct Share from bytes.")?)
}

#[test]
Expand All @@ -958,7 +959,7 @@ mod tests {
Scalar,
ProjectivePoint,
ThreadRng,
{ KEY_LEN + 1 },
SHARE_LEN,
>(*nzs.as_ref(), None, &mut rng)
.map_err(|e| anyhow::anyhow!("failed to split secret: {}", e))?;

Expand All @@ -968,7 +969,7 @@ mod tests {

let scalar = Feldman::<THRESHOLD, SHARES>::combine_shares::<
Scalar,
{ KEY_LEN + 1 },
SHARE_LEN,
>(&shares)
.map_err(|e| anyhow::anyhow!("failed to combine secret: {}", e))?;

Expand All @@ -984,13 +985,9 @@ mod tests {
// deserialize a verifier & use it to verify the shares in SHARE_ARRAY
#[test]
fn verify_shares() -> Result<()> {
use vsss_rs::FeldmanVerifier;

let verifier: FeldmanVerifier<
Scalar,
ProjectivePoint,
{ KEY_LEN + 1 },
> = serde_json::from_str(VERIFIER)?;
let verifier: FeldmanVerifier<Scalar, ProjectivePoint, SHARE_LEN> =
serde_json::from_str(VERIFIER)
.context("Failed to deserialize FeldmanVerifier from JSON.")?;

for share in SHARE_ARRAY {
let share = deserialize_share(share)?;
Expand All @@ -1000,16 +997,54 @@ mod tests {
Ok(())
}

#[test]
fn verify_zero_share() -> Result<()> {
let verifier: FeldmanVerifier<Scalar, ProjectivePoint, SHARE_LEN> =
serde_json::from_str(VERIFIER)
.context("Failed to deserialize FeldmanVerifier from JSON.")?;

let share: Share<SHARE_LEN> =
Share::try_from([0u8; SHARE_LEN].as_ref())
.context("Failed to create Share from static array.")?;

assert!(!verifier.verify(&share));

Ok(())
}

// TODO: I had expected that changing a single bit in a share would case
// the verifier to fail but that seems to be very wrong.
#[test]
fn verify_share_with_changed_byte() -> Result<()> {
let verifier: FeldmanVerifier<Scalar, ProjectivePoint, SHARE_LEN> =
serde_json::from_str(VERIFIER)
.context("Failed to deserialize FeldmanVerifier from JSON.")?;

let mut share = deserialize_share(SHARE_ARRAY[0])?;
println!("share: {}", share.0[0]);
share.0[1] = 0xff;
share.0[2] = 0xff;
share.0[3] = 0xff;
// If we don't change the next byte this test will start failing.
// I had (wrongly?) expected that the share would fail to verify w/
// a single changed byte
share.0[4] = 0xff;

assert!(!verifier.verify(&share));

Ok(())
}

#[test]
fn recover_secret() -> Result<()> {
let mut shares: Vec<Share<{ KEY_LEN + 1 }>> = Vec::new();
let mut shares: Vec<Share<SHARE_LEN>> = Vec::new();
for share in SHARE_ARRAY {
shares.push(deserialize_share(share)?);
}

let scalar = Feldman::<THRESHOLD, SHARES>::combine_shares::<
Scalar,
{ KEY_LEN + 1 },
SHARE_LEN,
>(&shares)
.map_err(|e| anyhow::anyhow!("failed to combine secret: {}", e))?;

Expand Down
Loading