Skip to content

Commit

Permalink
precomputed variant fails for non-power-of-two number of shares
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec committed Jul 5, 2023
1 parent 27c55d0 commit 8f45430
Showing 1 changed file with 179 additions and 167 deletions.
346 changes: 179 additions & 167 deletions ferveo/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,179 +374,191 @@ mod test_ferveo_api {
fn test_server_api_tdec_precomputed() {
let rng = &mut StdRng::seed_from_u64(0);

let tau = 1;
let shares_num = 4;
// In precomputed variant, the security threshold is equal to the number of shares
// TODO: Refactor DKG constructor to not require security threshold or this case.
// Or figure out a different way to simplify the precomputed variant API.
let security_threshold = shares_num;

let (messages, validators, validator_keypairs) =
make_test_inputs(rng, tau, security_threshold, shares_num);

// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
let me = validators[0].clone();
let mut dkg =
Dkg::new(tau, shares_num, security_threshold, &validators, &me)
.unwrap();

let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());

// At this point, any given validator should be able to provide a DKG public key
let dkg_public_key = dkg.public_key();

// In the meantime, the client creates a ciphertext and decryption request
let msg = "my-msg".as_bytes().to_vec();
let aad: &[u8] = "my-aad".as_bytes();
let rng = &mut thread_rng();
let ciphertext = tpke::api::encrypt(
SecretBox::new(msg.clone()),
aad,
&dkg_public_key.0,
rng,
)
.unwrap();

// Having aggregated the transcripts, the validators can now create decryption shares
let decryption_shares: Vec<_> = izip!(&validators, &validator_keypairs)
.map(|(validator, validator_keypair)| {
// Each validator holds their own instance of DKG and creates their own aggregate
let mut dkg = Dkg::new(
tau,
shares_num,
security_threshold,
&validators,
validator,
)
.unwrap();
let aggregate = dkg.aggregate_transcripts(&messages).unwrap();
assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
aggregate
.create_decryption_share_precomputed(
&dkg,
&ciphertext,
aad,
validator_keypair,
)
.unwrap()
})
.collect();

// Now, the decryption share can be used to decrypt the ciphertext
// This part is part of the client API

let shared_secret = share_combine_precomputed(&decryption_shares);
let plaintext = decrypt_with_shared_secret(
&ciphertext,
aad,
&SharedSecret(shared_secret),
)
.unwrap();
assert_eq!(plaintext, msg);

// Since we're using a precomputed variant, we need all the shares to be able to decrypt
// So if we remove one share, we should not be able to decrypt
let decryption_shares =
decryption_shares[..shares_num as usize - 1].to_vec();

let shared_secret = share_combine_precomputed(&decryption_shares);
let result = decrypt_with_shared_secret(
&ciphertext,
aad,
&SharedSecret(shared_secret),
);
assert!(result.is_err());
// Works for both power of 2 and non-power of 2
for shares_num in [4, 7] {
let tau = 1;
// In precomputed variant, the security threshold is equal to the number of shares
// TODO: Refactor DKG constructor to not require security threshold or this case.
// Or figure out a different way to simplify the precomputed variant API.
let security_threshold = shares_num;

let (messages, validators, validator_keypairs) =
make_test_inputs(rng, tau, security_threshold, shares_num);

// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
let me = validators[0].clone();
let mut dkg =
Dkg::new(tau, shares_num, security_threshold, &validators, &me)
.unwrap();

let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());

// At this point, any given validator should be able to provide a DKG public key
let dkg_public_key = dkg.public_key();

// In the meantime, the client creates a ciphertext and decryption request
let msg = "my-msg".as_bytes().to_vec();
let aad: &[u8] = "my-aad".as_bytes();
let rng = &mut thread_rng();
let ciphertext = tpke::api::encrypt(
SecretBox::new(msg.clone()),
aad,
&dkg_public_key.0,
rng,
)
.unwrap();

// Having aggregated the transcripts, the validators can now create decryption shares
let decryption_shares: Vec<_> =
izip!(&validators, &validator_keypairs)
.map(|(validator, validator_keypair)| {
// Each validator holds their own instance of DKG and creates their own aggregate
let mut dkg = Dkg::new(
tau,
shares_num,
security_threshold,
&validators,
validator,
)
.unwrap();
let aggregate =
dkg.aggregate_transcripts(&messages).unwrap();
assert!(pvss_aggregated
.verify(shares_num, &messages)
.unwrap());
aggregate
.create_decryption_share_precomputed(
&dkg,
&ciphertext,
aad,
validator_keypair,
)
.unwrap()
})
.collect();

// Now, the decryption share can be used to decrypt the ciphertext
// This part is part of the client API

let shared_secret = share_combine_precomputed(&decryption_shares);
let plaintext = decrypt_with_shared_secret(
&ciphertext,
aad,
&SharedSecret(shared_secret),
)
.unwrap();
assert_eq!(plaintext, msg);

// Since we're using a precomputed variant, we need all the shares to be able to decrypt
// So if we remove one share, we should not be able to decrypt
let decryption_shares =
decryption_shares[..shares_num as usize - 1].to_vec();

let shared_secret = share_combine_precomputed(&decryption_shares);
let result = decrypt_with_shared_secret(
&ciphertext,
aad,
&SharedSecret(shared_secret),
);
assert!(result.is_err());
}
}

#[test]
fn test_server_api_tdec_simple() {
let rng = &mut StdRng::seed_from_u64(0);

let tau = 1;
let shares_num = 4;
let security_threshold = 3;

let (messages, validators, validator_keypairs) =
make_test_inputs(rng, tau, security_threshold, shares_num);

// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
let mut dkg = Dkg::new(
tau,
shares_num,
security_threshold,
&validators,
&validators[0],
)
.unwrap();

let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());

// At this point, any given validator should be able to provide a DKG public key
let public_key = dkg.public_key();

// In the meantime, the client creates a ciphertext and decryption request
let msg = "my-msg".as_bytes().to_vec();
let aad: &[u8] = "my-aad".as_bytes();
let rng = &mut thread_rng();
let ciphertext = tpke::api::encrypt(
SecretBox::new(msg.clone()),
aad,
&public_key.0,
rng,
)
.unwrap();

// Having aggregated the transcripts, the validators can now create decryption shares
let decryption_shares: Vec<_> = izip!(&validators, &validator_keypairs)
.map(|(validator, validator_keypair)| {
// Each validator holds their own instance of DKG and creates their own aggregate
let mut dkg = Dkg::new(
tau,
shares_num,
security_threshold,
&validators,
validator,
)
.unwrap();
let aggregate = dkg.aggregate_transcripts(&messages).unwrap();
assert!(aggregate.verify(shares_num, &messages).unwrap());
aggregate
.create_decryption_share_simple(
&dkg,
&ciphertext,
aad,
validator_keypair,
)
.unwrap()
})
.collect();

// Now, the decryption share can be used to decrypt the ciphertext
// This part is part of the client API

// In simple variant, we only need `security_threshold` shares to be able to decrypt
let decryption_shares =
decryption_shares[..security_threshold as usize].to_vec();

let shared_secret = combine_shares_simple(&decryption_shares);
let plaintext =
decrypt_with_shared_secret(&ciphertext, aad, &shared_secret)
.unwrap();
assert_eq!(plaintext, msg);

// Let's say that we've only received `security_threshold - 1` shares
// In this case, we should not be able to decrypt
let decryption_shares =
decryption_shares[..security_threshold as usize - 1].to_vec();

let shared_secret = combine_shares_simple(&decryption_shares);
let result =
decrypt_with_shared_secret(&ciphertext, aad, &shared_secret);
assert!(result.is_err());
// Works for both power of 2 and non-power of 2
for shares_num in [4, 7] {
let tau = 1;
let security_threshold = shares_num / 2 + 1;

let (messages, validators, validator_keypairs) =
make_test_inputs(rng, tau, security_threshold, shares_num);

// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
let mut dkg = Dkg::new(
tau,
shares_num,
security_threshold,
&validators,
&validators[0],
)
.unwrap();

let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());

// At this point, any given validator should be able to provide a DKG public key
let public_key = dkg.public_key();

// In the meantime, the client creates a ciphertext and decryption request
let msg = "my-msg".as_bytes().to_vec();
let aad: &[u8] = "my-aad".as_bytes();
let rng = &mut thread_rng();
let ciphertext = tpke::api::encrypt(
SecretBox::new(msg.clone()),
aad,
&public_key.0,
rng,
)
.unwrap();

// Having aggregated the transcripts, the validators can now create decryption shares
let decryption_shares: Vec<_> =
izip!(&validators, &validator_keypairs)
.map(|(validator, validator_keypair)| {
// Each validator holds their own instance of DKG and creates their own aggregate
let mut dkg = Dkg::new(
tau,
shares_num,
security_threshold,
&validators,
validator,
)
.unwrap();
let aggregate =
dkg.aggregate_transcripts(&messages).unwrap();
assert!(aggregate
.verify(shares_num, &messages)
.unwrap());
aggregate
.create_decryption_share_simple(
&dkg,
&ciphertext,
aad,
validator_keypair,
)
.unwrap()
})
.collect();

// Now, the decryption share can be used to decrypt the ciphertext
// This part is part of the client API

// In simple variant, we only need `security_threshold` shares to be able to decrypt
let decryption_shares =
decryption_shares[..security_threshold as usize].to_vec();

let shared_secret = combine_shares_simple(&decryption_shares);
let plaintext =
decrypt_with_shared_secret(&ciphertext, aad, &shared_secret)
.unwrap();
assert_eq!(plaintext, msg);

// Let's say that we've only received `security_threshold - 1` shares
// In this case, we should not be able to decrypt
let decryption_shares =
decryption_shares[..security_threshold as usize - 1].to_vec();

let shared_secret = combine_shares_simple(&decryption_shares);
let result =
decrypt_with_shared_secret(&ciphertext, aad, &shared_secret);
assert!(result.is_err());
}
}

#[test]
Expand Down

0 comments on commit 8f45430

Please sign in to comment.