Skip to content

Commit

Permalink
test: update tests for dkgs with relaxed constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec committed Jan 31, 2024
1 parent 514221e commit 7756bfe
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 278 deletions.
9 changes: 5 additions & 4 deletions ferveo-python/examples/server_api_precomputed.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ def gen_eth_addr(i: int) -> str:

tau = 1
shares_num = 4
validators_num = shares_num + 2
# In precomputed variant, security threshold must be equal to shares_num
security_threshold = shares_num

validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
validator_keypairs = [Keypair.random() for _ in range(0, validators_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key(), i)
for i, keypair in enumerate(validator_keypairs)
Expand Down Expand Up @@ -52,11 +53,11 @@ def gen_eth_addr(i: int) -> str:

# Server can aggregate the transcripts
server_aggregate = dkg.aggregate_transcripts(messages)
assert server_aggregate.verify(shares_num, messages)
assert server_aggregate.verify(validators_num, messages)

# And the client can also aggregate and verify the transcripts
client_aggregate = AggregatedTranscript(messages)
assert client_aggregate.verify(shares_num, messages)
assert client_aggregate.verify(validators_num, messages)

# In the meantime, the client creates a ciphertext and decryption request
msg = "abc".encode()
Expand All @@ -76,7 +77,7 @@ def gen_eth_addr(i: int) -> str:

# We can also obtain the aggregated transcript from the side-channel (deserialize)
aggregate = AggregatedTranscript(messages)
assert aggregate.verify(shares_num, messages)
assert aggregate.verify(validators_num, messages)

# The ciphertext is obtained from the client

Expand Down
9 changes: 5 additions & 4 deletions ferveo-python/examples/server_api_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ def gen_eth_addr(i: int) -> str:
tau = 1
security_threshold = 3
shares_num = 4
validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
validators_num = shares_num + 2
validator_keypairs = [Keypair.random() for _ in range(0, validators_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key(), i)
for i, keypair in enumerate(validator_keypairs)
Expand Down Expand Up @@ -52,11 +53,11 @@ def gen_eth_addr(i: int) -> str:

# Server can aggregate the transcripts
server_aggregate = dkg.aggregate_transcripts(messages)
assert server_aggregate.verify(shares_num, messages)
assert server_aggregate.verify(validators_num, messages)

# And the client can also aggregate and verify the transcripts
client_aggregate = AggregatedTranscript(messages)
assert client_aggregate.verify(shares_num, messages)
assert client_aggregate.verify(validators_num, messages)

# In the meantime, the client creates a ciphertext and decryption request
msg = "abc".encode()
Expand All @@ -79,7 +80,7 @@ def gen_eth_addr(i: int) -> str:

# We can also obtain the aggregated transcript from the side-channel (deserialize)
aggregate = AggregatedTranscript(messages)
assert aggregate.verify(shares_num, messages)
assert aggregate.verify(validators_num, messages)

# The ciphertext is obtained from the client

Expand Down
63 changes: 47 additions & 16 deletions ferveo-python/test/test_ferveo.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@ def combine_shares_for_variant(v: FerveoVariant, decryption_shares):
raise ValueError("Unknown variant")


def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_to_use):
def scenario_for_variant(variant: FerveoVariant, shares_num, validators_num, threshold, shares_to_use):
if variant not in [FerveoVariant.Simple, FerveoVariant.Precomputed]:
raise ValueError("Unknown variant: " + variant)

if validators_num < shares_num:
raise ValueError("validators_num must be >= shares_num")

tau = 1
validator_keypairs = [Keypair.random() for _ in range(0, shares_num)]
validator_keypairs = [Keypair.random() for _ in range(0, validators_num)]
validators = [
Validator(gen_eth_addr(i), keypair.public_key(), i)
for i, keypair in enumerate(validator_keypairs)
Expand All @@ -68,7 +71,7 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
me=validators[0],
)
pvss_aggregated = dkg.aggregate_transcripts(messages)
assert pvss_aggregated.verify(shares_num, messages)
assert pvss_aggregated.verify(validators_num, messages)

dkg_pk_bytes = bytes(dkg.public_key)
dkg_pk = DkgPublicKey.from_bytes(dkg_pk_bytes)
Expand All @@ -87,7 +90,7 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t
me=validator,
)
pvss_aggregated = dkg.aggregate_transcripts(messages)
assert pvss_aggregated.verify(shares_num, messages)
assert pvss_aggregated.verify(validators_num, messages)

decryption_share = decryption_share_for_variant(variant, pvss_aggregated)(
dkg, ciphertext.header, aad, validator_keypair
Expand All @@ -113,27 +116,55 @@ def scenario_for_variant(variant: FerveoVariant, shares_num, threshold, shares_t


def test_simple_tdec_has_enough_messages():
scenario_for_variant(
FerveoVariant.Simple, shares_num=4, threshold=3, shares_to_use=3
)
shares_num = 4
threshold = shares_num - 1
for validators_num in [shares_num, 6]:
scenario_for_variant(
FerveoVariant.Simple,
shares_num=shares_num,
validators_num=validators_num,
threshold=threshold,
shares_to_use=threshold
)


def test_simple_tdec_doesnt_have_enough_messages():
scenario_for_variant(
FerveoVariant.Simple, shares_num=4, threshold=3, shares_to_use=2
)
shares_num = 4
threshold = shares_num - 1
for validators_num in [shares_num, 6]:
scenario_for_variant(
FerveoVariant.Simple,
shares_num=shares_num,
validators_num=validators_num,
threshold=threshold,
shares_to_use=threshold - 1
)


def test_precomputed_tdec_has_enough_messages():
scenario_for_variant(
FerveoVariant.Precomputed, shares_num=4, threshold=4, shares_to_use=4
)
shares_num = 4
threshold = shares_num # in precomputed variant, we need all shares
for validators_num in [shares_num, 6]:
scenario_for_variant(
FerveoVariant.Precomputed,
shares_num=shares_num,
validators_num=validators_num,
threshold=threshold,
shares_to_use=threshold
)


def test_precomputed_tdec_doesnt_have_enough_messages():
scenario_for_variant(
FerveoVariant.Precomputed, shares_num=4, threshold=4, shares_to_use=3
)
shares_num = 4
threshold = shares_num # in precomputed variant, we need all shares
for validators_num in [shares_num, 6]:
scenario_for_variant(
FerveoVariant.Simple,
shares_num=shares_num,
validators_num=validators_num,
threshold=threshold,
shares_to_use=threshold
)


PARAMS = [
Expand Down
169 changes: 78 additions & 91 deletions ferveo-wasm/examples/node/src/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,16 @@ const genEthAddr = (i: number) => {
return EthereumAddress.fromString(ethAddr);
};

const tau = 1;
function setupTest(sharesNum :number, threshold: number) {
const TAU = 1;

function setupTest(
sharesNum: number,
validatorsNum: number,
threshold: number
) {
const validatorKeypairs: Keypair[] = [];
const validators: Validator[] = [];
for (let i = 0; i < sharesNum; i++) {
for (let i = 0; i < validatorsNum; i++) {
const keypair = Keypair.random();
validatorKeypairs.push(keypair);
const validator = new Validator(genEthAddr(i), keypair.publicKey, i);
Expand All @@ -37,22 +42,22 @@ function setupTest(sharesNum :number, threshold: number) {
// validator, including themselves
const messages: ValidatorMessage[] = [];
validators.forEach((sender) => {
const dkg = new Dkg(tau, sharesNum, threshold, validators, sender);
const dkg = new Dkg(TAU, sharesNum, threshold, validators, sender);
const transcript = dkg.generateTranscript();
const message = new ValidatorMessage(sender, transcript);
messages.push(message);
});

// Now that every validator holds a dkg instance and a transcript for every other validator,
// every validator can aggregate the transcripts
const dkg = new Dkg(tau, sharesNum, threshold, validators, validators[0]);
const dkg = new Dkg(TAU, sharesNum, threshold, validators, validators[0]);

const serverAggregate = dkg.aggregateTranscript(messages);
expect(serverAggregate.verify(sharesNum, messages)).toBe(true);
expect(serverAggregate.verify(validatorsNum, messages)).toBe(true);

// Client can also aggregate the transcripts and verify them
const clientAggregate = new AggregatedTranscript(messages);
expect(clientAggregate.verify(sharesNum, messages)).toBe(true);
expect(clientAggregate.verify(validatorsNum, messages)).toBe(true);

// In the meantime, the client creates a ciphertext and decryption request
const msg = Buffer.from("my-msg");
Expand All @@ -73,94 +78,76 @@ function setupTest(sharesNum :number, threshold: number) {
// This test suite replicates tests from ferveo-wasm/tests/node.rs
describe("ferveo-wasm", () => {
it("simple tdec variant", () => {
const sharesNum = 4;
const threshold = 3;
const {
validatorKeypairs,
validators,
messages,
msg,
aad,
ciphertext,
} = setupTest(sharesNum, threshold);

// Having aggregated the transcripts, the validators can now create decryption shares
const decryptionShares: DecryptionShareSimple[] = [];
zip(validators, validatorKeypairs).forEach(([validator, keypair]) => {
expect(validator.publicKey.equals(keypair.publicKey)).toBe(true);

const dkg = new Dkg(tau, sharesNum, threshold, validators, validator);
const aggregate = dkg.aggregateTranscript(messages);
const isValid = aggregate.verify(sharesNum, messages);
expect(isValid).toBe(true);

const decryptionShare = aggregate.createDecryptionShareSimple(
dkg,
ciphertext.header,
aad,
keypair
);
decryptionShares.push(decryptionShare);
const sharesNum = 4;
const threshold = 3;
[sharesNum, sharesNum + 2].forEach((validatorsNum) => {
const { validatorKeypairs, validators, messages, msg, aad, ciphertext } =
setupTest(sharesNum, validatorsNum, threshold);

// Having aggregated the transcripts, the validators can now create decryption shares
const decryptionShares: DecryptionShareSimple[] = [];
zip(validators, validatorKeypairs).forEach(([validator, keypair]) => {
expect(validator.publicKey.equals(keypair.publicKey)).toBe(true);

const dkg = new Dkg(TAU, sharesNum, threshold, validators, validator);
const aggregate = dkg.aggregateTranscript(messages);
const isValid = aggregate.verify(validatorsNum, messages);
expect(isValid).toBe(true);

const decryptionShare = aggregate.createDecryptionShareSimple(
dkg,
ciphertext.header,
aad,
keypair
);
decryptionShares.push(decryptionShare);
});

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

const sharedSecret = combineDecryptionSharesSimple(decryptionShares);

// The client should have access to the public parameters of the DKG

const plaintext = decryptWithSharedSecret(ciphertext, aad, sharedSecret);
expect(Buffer.from(plaintext)).toEqual(msg);
});

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

const sharedSecret = combineDecryptionSharesSimple(
decryptionShares,
);

// The client should have access to the public parameters of the DKG

const plaintext = decryptWithSharedSecret(
ciphertext,
aad,
sharedSecret,
);
expect(Buffer.from(plaintext)).toEqual(msg);
});

it("precomputed tdec variant", () => {
const sharesNum = 4;
const threshold = sharesNum; // threshold is equal to sharesNum in precomputed variant
const {
validatorKeypairs,
validators,
messages,
msg,
aad,
ciphertext,
} = setupTest(sharesNum, threshold);

// Having aggregated the transcripts, the validators can now create decryption shares
const decryptionShares: DecryptionSharePrecomputed[] = [];
zip(validators, validatorKeypairs).forEach(([validator, keypair]) => {
const dkg = new Dkg(tau, sharesNum, threshold, validators, validator);
const aggregate = dkg.aggregateTranscript(messages);
const isValid = aggregate.verify(sharesNum, messages);
expect(isValid).toBe(true);

const decryptionShare = aggregate.createDecryptionSharePrecomputed(
dkg,
ciphertext.header,
aad,
keypair
);
decryptionShares.push(decryptionShare);
const sharesNum = 4;
const threshold = sharesNum; // threshold is equal to sharesNum in precomputed variant
[sharesNum, sharesNum + 2].forEach((validatorsNum) => {
const { validatorKeypairs, validators, messages, msg, aad, ciphertext } =
setupTest(sharesNum, validatorsNum, threshold);

// Having aggregated the transcripts, the validators can now create decryption shares
const decryptionShares: DecryptionSharePrecomputed[] = [];
zip(validators, validatorKeypairs).forEach(([validator, keypair]) => {
const dkg = new Dkg(TAU, sharesNum, threshold, validators, validator);
const aggregate = dkg.aggregateTranscript(messages);
const isValid = aggregate.verify(validatorsNum, messages);
expect(isValid).toBe(true);

const decryptionShare = aggregate.createDecryptionSharePrecomputed(
dkg,
ciphertext.header,
aad,
keypair
);
decryptionShares.push(decryptionShare);
});

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

const sharedSecret = combineDecryptionSharesPrecomputed(decryptionShares);

// The client should have access to the public parameters of the DKG

const plaintext = decryptWithSharedSecret(ciphertext, aad, sharedSecret);
expect(Buffer.from(plaintext)).toEqual(msg);
});

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

const sharedSecret = combineDecryptionSharesPrecomputed(decryptionShares);

// The client should have access to the public parameters of the DKG

const plaintext = decryptWithSharedSecret(
ciphertext,
aad,
sharedSecret,
);
expect(Buffer.from(plaintext)).toEqual(msg);
});
});
Loading

0 comments on commit 7756bfe

Please sign in to comment.