diff --git a/.lock b/.lock new file mode 100644 index 0000000..e69de29 diff --git a/crates.js b/crates.js new file mode 100644 index 0000000..3bade01 --- /dev/null +++ b/crates.js @@ -0,0 +1 @@ +window.ALL_CRATES = ["elastic_elgamal"]; \ No newline at end of file diff --git a/elastic_elgamal/all.html b/elastic_elgamal/all.html new file mode 100644 index 0000000..37d1ef3 --- /dev/null +++ b/elastic_elgamal/all.html @@ -0,0 +1,2 @@ +List of all items in this crate +

List of all items

Structs

Enums

Traits

\ No newline at end of file diff --git a/elastic_elgamal/app/choice/enum.ChoiceVerificationError.html b/elastic_elgamal/app/choice/enum.ChoiceVerificationError.html new file mode 100644 index 0000000..63fbaae --- /dev/null +++ b/elastic_elgamal/app/choice/enum.ChoiceVerificationError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/app/enum.ChoiceVerificationError.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/app/choice/struct.ChoiceParams.html b/elastic_elgamal/app/choice/struct.ChoiceParams.html new file mode 100644 index 0000000..a1de564 --- /dev/null +++ b/elastic_elgamal/app/choice/struct.ChoiceParams.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/app/struct.ChoiceParams.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/app/choice/struct.EncryptedChoice.html b/elastic_elgamal/app/choice/struct.EncryptedChoice.html new file mode 100644 index 0000000..6b1b205 --- /dev/null +++ b/elastic_elgamal/app/choice/struct.EncryptedChoice.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/app/struct.EncryptedChoice.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/app/choice/struct.MultiChoice.html b/elastic_elgamal/app/choice/struct.MultiChoice.html new file mode 100644 index 0000000..05e266b --- /dev/null +++ b/elastic_elgamal/app/choice/struct.MultiChoice.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/app/struct.MultiChoice.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/app/choice/struct.SingleChoice.html b/elastic_elgamal/app/choice/struct.SingleChoice.html new file mode 100644 index 0000000..b61f709 --- /dev/null +++ b/elastic_elgamal/app/choice/struct.SingleChoice.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/app/struct.SingleChoice.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/app/choice/trait.ProveSum.html b/elastic_elgamal/app/choice/trait.ProveSum.html new file mode 100644 index 0000000..3a3e586 --- /dev/null +++ b/elastic_elgamal/app/choice/trait.ProveSum.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/app/trait.ProveSum.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/app/enum.ChoiceVerificationError.html b/elastic_elgamal/app/enum.ChoiceVerificationError.html new file mode 100644 index 0000000..da1d6e7 --- /dev/null +++ b/elastic_elgamal/app/enum.ChoiceVerificationError.html @@ -0,0 +1,27 @@ +ChoiceVerificationError in elastic_elgamal::app - Rust +
#[non_exhaustive]
pub enum ChoiceVerificationError { + OptionsLenMismatch { + expected: usize, + actual: usize, + }, + Sum(VerificationError), + Range(VerificationError), +}
Expand description

Error verifying an EncryptedChoice.

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

OptionsLenMismatch

Fields

§expected: usize

Expected number of options.

+
§actual: usize

Actual number of options.

+

Mismatch between expected and actual number of options in the EncryptedChoice.

+
§

Sum(VerificationError)

Error verifying EncryptedChoice::sum_proof().

+
§

Range(VerificationError)

Trait Implementations§

source§

impl Debug for ChoiceVerificationError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for ChoiceVerificationError

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for ChoiceVerificationError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/app/enum.QuadraticVotingError.html b/elastic_elgamal/app/enum.QuadraticVotingError.html new file mode 100644 index 0000000..0cd9a94 --- /dev/null +++ b/elastic_elgamal/app/enum.QuadraticVotingError.html @@ -0,0 +1,34 @@ +QuadraticVotingError in elastic_elgamal::app - Rust +
#[non_exhaustive]
pub enum QuadraticVotingError { + Variant { + index: usize, + error: VerificationError, + }, + CreditRange(VerificationError), + CreditEquivalence(VerificationError), + OptionsLenMismatch { + expected: usize, + actual: usize, + }, +}
Expand description

Errors that can occur when verifying QuadraticVotingBallots.

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

Variant

Fields

§index: usize

Zero-based option index.

+
§error: VerificationError

Error that occurred during range proof verification.

+

Error verifying a RangeProof for a vote for a particular option.

+
§

CreditRange(VerificationError)

Error verifying a RangeProof for credits.

+
§

CreditEquivalence(VerificationError)

Error verifying the proof of equivalence for credits.

+
§

OptionsLenMismatch

Fields

§expected: usize

Expected number of options.

+
§actual: usize

Actual number of options.

+

Mismatch between expected and actual number of options in the ballot.

+

Trait Implementations§

source§

impl Debug for QuadraticVotingError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for QuadraticVotingError

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for QuadraticVotingError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/app/index.html b/elastic_elgamal/app/index.html new file mode 100644 index 0000000..1f6c89f --- /dev/null +++ b/elastic_elgamal/app/index.html @@ -0,0 +1,13 @@ +elastic_elgamal::app - Rust +

Module elastic_elgamal::app

source ·
Expand description

High-level applications for proofs defined in this crate.

+

For now, the applications are:

+ +

Structs§

Enums§

Traits§

  • Encapsulation of functionality for proving and verifying correctness of the sum of option +ciphertexts in an EncryptedChoice.
\ No newline at end of file diff --git a/elastic_elgamal/app/quadratic_voting/enum.QuadraticVotingError.html b/elastic_elgamal/app/quadratic_voting/enum.QuadraticVotingError.html new file mode 100644 index 0000000..98705d2 --- /dev/null +++ b/elastic_elgamal/app/quadratic_voting/enum.QuadraticVotingError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/app/enum.QuadraticVotingError.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/app/quadratic_voting/struct.QuadraticVotingBallot.html b/elastic_elgamal/app/quadratic_voting/struct.QuadraticVotingBallot.html new file mode 100644 index 0000000..88db6ed --- /dev/null +++ b/elastic_elgamal/app/quadratic_voting/struct.QuadraticVotingBallot.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/app/struct.QuadraticVotingBallot.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/app/quadratic_voting/struct.QuadraticVotingParams.html b/elastic_elgamal/app/quadratic_voting/struct.QuadraticVotingParams.html new file mode 100644 index 0000000..8632966 --- /dev/null +++ b/elastic_elgamal/app/quadratic_voting/struct.QuadraticVotingParams.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/app/struct.QuadraticVotingParams.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/app/sidebar-items.js b/elastic_elgamal/app/sidebar-items.js new file mode 100644 index 0000000..a1f8557 --- /dev/null +++ b/elastic_elgamal/app/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["ChoiceVerificationError","QuadraticVotingError"],"struct":["ChoiceParams","EncryptedChoice","MultiChoice","QuadraticVotingBallot","QuadraticVotingParams","SingleChoice"],"trait":["ProveSum"]}; \ No newline at end of file diff --git a/elastic_elgamal/app/struct.ChoiceParams.html b/elastic_elgamal/app/struct.ChoiceParams.html new file mode 100644 index 0000000..58084e5 --- /dev/null +++ b/elastic_elgamal/app/struct.ChoiceParams.html @@ -0,0 +1,35 @@ +ChoiceParams in elastic_elgamal::app - Rust +
pub struct ChoiceParams<G: Group, S: ProveSum<G>> { /* private fields */ }
Expand description

Parameters of an EncryptedChoice polling.

+

Implementations§

source§

impl<G: Group, S: ProveSum<G>> ChoiceParams<G, S>

source

pub fn receiver(&self) -> &PublicKey<G>

Returns the public key for which the EncryptedChoice are encrypted.

+
source

pub fn options_count(&self) -> usize

Returns the number of options in these parameters.

+
source§

impl<G: Group> ChoiceParams<G, SingleChoice>

source

pub fn single(receiver: PublicKey<G>, options_count: usize) -> Self

Creates parameters for a single-choice polling.

+
§Panics
+

Panics if provided options_count is zero.

+
source§

impl<G: Group> ChoiceParams<G, MultiChoice>

source

pub fn multi(receiver: PublicKey<G>, options_count: usize) -> Self

Creates parameters for a multi-choice polling.

+
§Panics
+

Panics if provided options_count is zero.

+

Trait Implementations§

source§

impl<G: Group, S: ProveSum<G>> Clone for ChoiceParams<G, S>

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group, S: Debug + ProveSum<G>> Debug for ChoiceParams<G, S>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<G, S> Freeze for ChoiceParams<G, S>
where + S: Freeze, + <G as ElementOps>::Element: Freeze,

§

impl<G, S> RefUnwindSafe for ChoiceParams<G, S>

§

impl<G, S> Send for ChoiceParams<G, S>
where + S: Send, + <G as ElementOps>::Element: Send,

§

impl<G, S> Sync for ChoiceParams<G, S>
where + S: Sync, + <G as ElementOps>::Element: Sync,

§

impl<G, S> Unpin for ChoiceParams<G, S>
where + S: Unpin, + <G as ElementOps>::Element: Unpin,

§

impl<G, S> UnwindSafe for ChoiceParams<G, S>
where + S: UnwindSafe, + <G as ElementOps>::Element: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/app/struct.EncryptedChoice.html b/elastic_elgamal/app/struct.EncryptedChoice.html new file mode 100644 index 0000000..20c2d17 --- /dev/null +++ b/elastic_elgamal/app/struct.EncryptedChoice.html @@ -0,0 +1,110 @@ +EncryptedChoice in elastic_elgamal::app - Rust +
pub struct EncryptedChoice<G: Group, S: ProveSum<G>> { /* private fields */ }
Expand description

Zero or more encrypted choices from n options (n >= 1) together with zero-knowledge +proofs of correctness.

+

§Construction

+

The choice is represented as a vector of n choice ciphertexts of Boolean values (0 or 1), +where the ciphertexts for the chosen options encrypt 1 and the other ciphertexts encrypt 0. +This ensures that multiple EncryptedChoices can be added (e.g., within a voting protocol).

+

Zero-knowledge proofs are:

+
    +
  • A RingProof attesting that all n ciphertexts encrypt 0 or 1. +This proof can be obtained via Self::range_proof().
  • +
  • A LogEqualityProof attesting that the encrypted values sum up to 1. Combined with +the range proof, this means that exactly one of encrypted values is 1, and all others are 0. +This proof can be obtained via Self::sum_proof(). This proof is absent for +a MultiChoice setup (sum_proof() just returns ()).
  • +
+

§Examples

§Single-choice setup

+
let mut rng = thread_rng();
+let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+let choice_params = ChoiceParams::single(pk, 5);
+
+let choice = 2;
+let enc = EncryptedChoice::single(&choice_params, choice, &mut rng);
+let choices = enc.verify(&choice_params)?;
+
+// `choices` is a slice of 5 Boolean value ciphertexts
+assert_eq!(choices.len(), 5);
+let lookup_table = DiscreteLogTable::new(0..=1);
+for (idx, &v) in choices.iter().enumerate() {
+    assert_eq!(
+        sk.decrypt(v, &lookup_table),
+        Some((idx == choice) as u64)
+    );
+}
+

§Multi-choice setup

+
let mut rng = thread_rng();
+let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+let choice_params = ChoiceParams::multi(pk, 5);
+
+let choices = [true, false, true, true, false];
+let enc = EncryptedChoice::new(&choice_params, &choices, &mut rng);
+let recovered_choices = enc.verify(&choice_params)?;
+
+let lookup_table = DiscreteLogTable::new(0..=1);
+for (idx, &v) in recovered_choices.iter().enumerate() {
+    assert_eq!(sk.decrypt(v, &lookup_table), Some(choices[idx] as u64));
+}
+

Implementations§

source§

impl<G: Group> EncryptedChoice<G, SingleChoice>

source

pub fn single<R: CryptoRng + RngCore>( + params: &ChoiceParams<G, SingleChoice>, + choice: usize, + rng: &mut R +) -> Self

Creates a new encrypted choice.

+
§Panics
+

Panics if choice exceeds the maximum index allowed by params.

+
source§

impl<G: Group, S: ProveSum<G>> EncryptedChoice<G, S>

source

pub fn new<R: CryptoRng + RngCore>( + params: &ChoiceParams<G, S>, + choices: &[bool], + rng: &mut R +) -> Self

Creates an encrypted multi-choice.

+

For a SingleChoice polling, it is caller’s responsibility to ensure that choices +contains exactly one true value; otherwise, the produced proof will not verify.

+
§Panics
+

Panics if the length of choices differs from the number of options specified in params.

+
source

pub fn verify( + &self, + params: &ChoiceParams<G, S> +) -> Result<&[Ciphertext<G>], ChoiceVerificationError>

Verifies the zero-knowledge proofs in this choice and returns Boolean ciphertexts +for all options.

+
§Errors
+

Returns an error if the choice is malformed or its proofs fail verification.

+
source

pub fn len(&self) -> usize

Returns the number of encrypted choices. This value is equal to +ChoiceParams::options_count() with which the encryption was created.

+
source

pub fn choices_unchecked(&self) -> &[Ciphertext<G>]

Returns ciphertexts for all options without checking the validity of this choice.

+
source

pub fn range_proof(&self) -> &RingProof<G>

Returns the range proof for the choice ciphertexts.

+
source

pub fn sum_proof(&self) -> &S::Proof

Returns the sum proof for the choice ciphertexts.

+

Trait Implementations§

source§

impl<G: Clone + Group, S: Clone + ProveSum<G>> Clone for EncryptedChoice<G, S>
where + S::Proof: Clone,

source§

fn clone(&self) -> EncryptedChoice<G, S>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group, S: Debug + ProveSum<G>> Debug for EncryptedChoice<G, S>
where + S::Proof: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group, S: ProveSum<G>> Deserialize<'de> for EncryptedChoice<G, S>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group, S: ProveSum<G>> Serialize for EncryptedChoice<G, S>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<G, S> Freeze for EncryptedChoice<G, S>
where + <S as ProveSum<G>>::Proof: Freeze, + <G as ScalarOps>::Scalar: Freeze,

§

impl<G, S> RefUnwindSafe for EncryptedChoice<G, S>

§

impl<G, S> Send for EncryptedChoice<G, S>
where + <G as ElementOps>::Element: Send, + <S as ProveSum<G>>::Proof: Send, + <G as ScalarOps>::Scalar: Send,

§

impl<G, S> Sync for EncryptedChoice<G, S>
where + <G as ElementOps>::Element: Sync, + <S as ProveSum<G>>::Proof: Sync, + <G as ScalarOps>::Scalar: Sync,

§

impl<G, S> Unpin for EncryptedChoice<G, S>
where + <G as ElementOps>::Element: Unpin, + <S as ProveSum<G>>::Proof: Unpin, + <G as ScalarOps>::Scalar: Unpin,

§

impl<G, S> UnwindSafe for EncryptedChoice<G, S>
where + <G as ElementOps>::Element: UnwindSafe, + <S as ProveSum<G>>::Proof: UnwindSafe, + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/app/struct.MultiChoice.html b/elastic_elgamal/app/struct.MultiChoice.html new file mode 100644 index 0000000..36ebf1b --- /dev/null +++ b/elastic_elgamal/app/struct.MultiChoice.html @@ -0,0 +1,18 @@ +MultiChoice in elastic_elgamal::app - Rust +
pub struct MultiChoice(/* private fields */);
Expand description

Multi-choice setup for EncryptedChoice, in which it can contain any possible number +of selected options (0..=n, where n is the number of options).

+

§Examples

+

See EncryptedChoice docs for an example of usage.

+

Trait Implementations§

source§

impl Clone for MultiChoice

source§

fn clone(&self) -> MultiChoice

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MultiChoice

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<G: Group> ProveSum<G> for MultiChoice

§

type Proof = ()

Produced / verified proofs.
source§

impl Copy for MultiChoice

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/app/struct.QuadraticVotingBallot.html b/elastic_elgamal/app/struct.QuadraticVotingBallot.html new file mode 100644 index 0000000..c94d8fc --- /dev/null +++ b/elastic_elgamal/app/struct.QuadraticVotingBallot.html @@ -0,0 +1,85 @@ +QuadraticVotingBallot in elastic_elgamal::app - Rust +
pub struct QuadraticVotingBallot<G: Group> { /* private fields */ }
Expand description

Encrypted ballot for quadratic voting together with zero-knowledge proofs of correctness.

+

§Overview

+

Quadratic voting assumes a non-exclusive selection among n >= 1 predefined options. +Unlike with MultiChoice polling, a voter can cast more than +one vote for a single option. The additional votes come at a quadratic expense for the voter, +however. For example, to cast 4 votes for a certain option, a voter needs 16 credits, +while single votes for 4 different options are worth 4 credits.

+

The QuadraticVotingBallot construction assumes that there is a known number of credits +for each ballot (e.g., it is uniform across all eligible voters), and that votes are tallied +by a tallier or a federation of talliers that jointly control a SecretKey. +As such, the ballot is represented as follows:

+
    +
  • ElGamal Ciphertext for each of n options (can be summed across all valid ballots +to get vote totals that will be decrypted by the talliers)
  • +
  • RangeProof for each of these ciphertexts proving that the encrypted value +is in range 0..=V
  • +
  • Ciphertext for the number of credits used by the ballot, and a RangeProof +that it is in range 0..=C
  • +
  • Zero-knowledge SumOfSquaresProof proving that the encrypted number of credits is computed +correctly, i.e., as a sum of squares of the values encrypted in the vote ciphertexts.
  • +
+

Here, C (the number of credits) and V (max votes per option) are the protocol parameters +encapsulated in QuadraticVotingParams.

+

§Examples

+
let mut rng = thread_rng();
+let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+let params = QuadraticVotingParams::new(pk, 5, 20);
+// 5 options, 20 credits (= 4 max votes per option)
+assert_eq!(params.max_votes(), 4);
+
+let votes = [4, 0, 0, 1, 1];
+let ballot = QuadraticVotingBallot::new(&params, &votes, &mut rng);
+let encrypted: Vec<_> = ballot.verify(&params)?.collect();
+
+assert_eq!(encrypted.len(), 5);
+let lookup = DiscreteLogTable::new(0..=params.max_votes());
+let decrypted: Vec<_> = encrypted
+    .into_iter()
+    .map(|vote| sk.decrypt(vote, &lookup).unwrap())
+    .collect();
+assert_eq!(decrypted, votes);
+

Implementations§

source§

impl<G: Group> QuadraticVotingBallot<G>

source

pub fn new<R: CryptoRng + RngCore>( + params: &QuadraticVotingParams<G>, + votes: &[u64], + rng: &mut R +) -> Self

Creates a ballot based on the provided parameters and voter’s votes.

+
§Panics
+

Panics if the length of votes differs from the number of options in params.

+
source

pub fn verify( + &self, + params: &QuadraticVotingParams<G> +) -> Result<impl Iterator<Item = Ciphertext<G>> + '_, QuadraticVotingError>

Verifies this ballot against the provided parameters.

+
§Errors
+
    +
  • Returns an error if verification fails.
  • +
+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for QuadraticVotingBallot<G>

source§

fn clone(&self) -> QuadraticVotingBallot<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for QuadraticVotingBallot<G>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for QuadraticVotingBallot<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for QuadraticVotingBallot<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<G> Freeze for QuadraticVotingBallot<G>
where + <G as ElementOps>::Element: Freeze, + <G as ScalarOps>::Scalar: Freeze,

§

impl<G> RefUnwindSafe for QuadraticVotingBallot<G>

§

impl<G> Send for QuadraticVotingBallot<G>
where + <G as ElementOps>::Element: Send, + <G as ScalarOps>::Scalar: Send,

§

impl<G> Sync for QuadraticVotingBallot<G>
where + <G as ElementOps>::Element: Sync, + <G as ScalarOps>::Scalar: Sync,

§

impl<G> Unpin for QuadraticVotingBallot<G>
where + <G as ElementOps>::Element: Unpin, + <G as ScalarOps>::Scalar: Unpin,

§

impl<G> UnwindSafe for QuadraticVotingBallot<G>
where + <G as ElementOps>::Element: UnwindSafe, + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/app/struct.QuadraticVotingParams.html b/elastic_elgamal/app/struct.QuadraticVotingParams.html new file mode 100644 index 0000000..08b0b22 --- /dev/null +++ b/elastic_elgamal/app/struct.QuadraticVotingParams.html @@ -0,0 +1,56 @@ +QuadraticVotingParams in elastic_elgamal::app - Rust +
pub struct QuadraticVotingParams<G: Group> { /* private fields */ }
Expand description

Quadratic voting parameters prepared for a certain Group.

+

The parameters are:

+ +

See QuadraticVotingBallot for a detailed description of parameters.

+

§Examples

+
let (receiver, _) = Keypair::<Ristretto>::generate(&mut thread_rng())
+    .into_tuple();
+let mut params = QuadraticVotingParams::new(receiver, 5, 20);
+// 5 options, 20 credits.
+assert_eq!(params.options_count(), 5);
+assert_eq!(params.credits(), 20);
+// By default, max votes per option are determined based on credits
+assert_eq!(params.max_votes(), 4); // 4 < sqrt(20) < 5
+
+// It is possible to reduce max votes per ballot.
+params.set_max_votes(3);
+assert_eq!(params.max_votes(), 3);
+

Implementations§

source§

impl<G: Group> QuadraticVotingParams<G>

source

pub fn new(receiver: PublicKey<G>, options: usize, credits: u64) -> Self

Creates new parameters for the specified number of credits allocated per voter.

+

The maximum number of votes per option is automatically set as floor(sqrt(credits)); +it can be changed via Self::set_max_votes().

+
§Panics
+

Panics if the number of options or credits is zero.

+
source

pub fn receiver(&self) -> &PublicKey<G>

Returns the public key for which the QuadraticVotingBallots are encrypted.

+
source

pub fn options_count(&self) -> usize

Returns the number of options.

+
source

pub fn credits(&self) -> u64

Returns the number of credits per ballot.

+
source

pub fn max_votes(&self) -> u64

Returns the maximum number of votes per option.

+
source

pub fn set_max_votes(&mut self, max_votes: u64)

Sets the maximum number of votes per option.

+
§Panics
+

Panics if max_votes * max_votes exceeds credits; in this case, this number of votes +cannot be cast for a single option.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for QuadraticVotingParams<G>

source§

fn clone(&self) -> QuadraticVotingParams<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for QuadraticVotingParams<G>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<G> Freeze for QuadraticVotingParams<G>
where + <G as ElementOps>::Element: Freeze,

§

impl<G> RefUnwindSafe for QuadraticVotingParams<G>
where + <G as ElementOps>::Element: RefUnwindSafe,

§

impl<G> Send for QuadraticVotingParams<G>
where + <G as ElementOps>::Element: Send,

§

impl<G> Sync for QuadraticVotingParams<G>
where + <G as ElementOps>::Element: Sync,

§

impl<G> Unpin for QuadraticVotingParams<G>
where + <G as ElementOps>::Element: Unpin,

§

impl<G> UnwindSafe for QuadraticVotingParams<G>
where + <G as ElementOps>::Element: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/app/struct.SingleChoice.html b/elastic_elgamal/app/struct.SingleChoice.html new file mode 100644 index 0000000..2209135 --- /dev/null +++ b/elastic_elgamal/app/struct.SingleChoice.html @@ -0,0 +1,17 @@ +SingleChoice in elastic_elgamal::app - Rust +
pub struct SingleChoice(/* private fields */);
Expand description

Single-choice setup for EncryptedChoice, in which it can contain a single selected option.

+

§Examples

+

See EncryptedChoice docs for an example of usage.

+

Trait Implementations§

source§

impl Clone for SingleChoice

source§

fn clone(&self) -> SingleChoice

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for SingleChoice

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<G: Group> ProveSum<G> for SingleChoice

§

type Proof = LogEqualityProof<G>

Produced / verified proofs.
source§

impl Copy for SingleChoice

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/app/trait.ProveSum.html b/elastic_elgamal/app/trait.ProveSum.html new file mode 100644 index 0000000..1d222c3 --- /dev/null +++ b/elastic_elgamal/app/trait.ProveSum.html @@ -0,0 +1,8 @@ +ProveSum in elastic_elgamal::app - Rust +
pub trait ProveSum<G: Group>: Clone + Sealed {
+    type Proof: Sized + Serialize + DeserializeOwned;
+}
Expand description

Encapsulation of functionality for proving and verifying correctness of the sum of option +ciphertexts in an EncryptedChoice.

+

This trait is not meant to be implemented for external types.

+

Required Associated Types§

source

type Proof: Sized + Serialize + DeserializeOwned

Produced / verified proofs.

+

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/elastic_elgamal/decryption/struct.CandidateDecryption.html b/elastic_elgamal/decryption/struct.CandidateDecryption.html new file mode 100644 index 0000000..4d1d62b --- /dev/null +++ b/elastic_elgamal/decryption/struct.CandidateDecryption.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../elastic_elgamal/struct.CandidateDecryption.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/decryption/struct.VerifiableDecryption.html b/elastic_elgamal/decryption/struct.VerifiableDecryption.html new file mode 100644 index 0000000..896dff4 --- /dev/null +++ b/elastic_elgamal/decryption/struct.VerifiableDecryption.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../elastic_elgamal/struct.VerifiableDecryption.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/dkg/enum.Error.html b/elastic_elgamal/dkg/enum.Error.html new file mode 100644 index 0000000..1fff41c --- /dev/null +++ b/elastic_elgamal/dkg/enum.Error.html @@ -0,0 +1,27 @@ +Error in elastic_elgamal::dkg - Rust +
#[non_exhaustive]
pub enum Error { + InvalidSecret, + InvalidCommitment, + DuplicateShare, + MalformedParticipantProof(Error), + InconsistentPublicShares(Error), +}
Expand description

Errors that can occur during the distributed key generation.

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

InvalidSecret

Secret received from the party does not correspond to their commitment via +the public polynomial.

+
§

InvalidCommitment

Provided commitment does not correspond to the party’s public key share.

+
§

DuplicateShare

Secret share for this participant was already provided.

+
§

MalformedParticipantProof(Error)

Provided proof of possession or public polynomial is malformed.

+
§

InconsistentPublicShares(Error)

Public shares obtained from accumulated public polynomial are inconsistent.

+

Trait Implementations§

source§

impl Debug for Error

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for Error

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for Error

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

§

impl Freeze for Error

§

impl RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl UnwindSafe for Error

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/dkg/index.html b/elastic_elgamal/dkg/index.html new file mode 100644 index 0000000..3a010ae --- /dev/null +++ b/elastic_elgamal/dkg/index.html @@ -0,0 +1,85 @@ +elastic_elgamal::dkg - Rust +

Module elastic_elgamal::dkg

source ·
Expand description

Committed Pedersen’s distributed key generation (DKG).

+

DKG allows to securely generate shared secret without a need for a trusted +dealer. Compare with Feldman’s verifiable secret sharing implemented in the sharing module +which requires a trusted dealer.

+

This implementation is based on Pedersen’s DKG, which was shown by Gennaro et al. +to contain a flaw allowing an adversary to bias distribution of the shared public key. +We try to prevent this kind of possible attacks by forcing the parties to +commit to their public key shares before receiving public shares from other +parties.

+

§Examples

+

Decentralized key generation for 2-of-3 threshold encryption.

+ +
let mut rng = thread_rng();
+let params = Params::new(3, 2);
+
+// Initialize participants.
+let participants = (0..3).map(|i| {
+    ParticipantCollectingCommitments::<Ristretto>::new(params, i, &mut rng)
+});
+let mut participants: Vec<_> = participants.collect();
+
+// Publish commitments from all participants...
+let commitments: Vec<_> = participants
+    .iter()
+    .map(|participant| participant.commitment())
+    .collect();
+// ...and consume them from each participant's perspective.
+for (i, participant) in participants.iter_mut().enumerate() {
+    for (j, &commitment) in commitments.iter().enumerate() {
+        if i != j {
+            participant.insert_commitment(j, commitment);
+        }
+    }
+}
+
+// Transition all participants to the next stage: exchanging polynomials.
+let mut participants: Vec<_> = participants
+    .into_iter()
+    .map(|participant| participant.finish_commitment_phase())
+    .collect();
+// Publish each participant's polynomial...
+let infos: Vec<_> = participants
+    .iter()
+    .map(|participant| participant.public_info().into_owned())
+    .collect();
+// ...and consume them from each participant's perspective.
+for (i, participant) in participants.iter_mut().enumerate() {
+    for (j, info) in infos.iter().enumerate() {
+        if i != j {
+            participant.insert_public_polynomial(j, info.clone())?;
+        }
+    }
+}
+
+// Transition all participants to the final phase: exchanging secrets.
+let mut participants: Vec<_> = participants
+    .into_iter()
+    .map(|participant| participant.finish_polynomials_phase())
+    .collect();
+// Exchange shares (this should happen over secure peer-to-peer channels).
+for i in 0..3 {
+    for j in 0..3 {
+        if i == j { continue; }
+        let share = participants[i].secret_share_for_participant(j);
+        participants[j].insert_secret_share(i, share)?;
+    }
+}
+
+// Finalize all participants.
+let participants = participants
+    .into_iter()
+    .map(|participant| participant.complete())
+    .collect::<Result<Vec<_>, _>>()?;
+// Check that the shared key is the same for all participants.
+let expected_key = participants[0].key_set().shared_key();
+for participant in &participants {
+    assert_eq!(participant.key_set().shared_key(), expected_key);
+}
+
+// Participants can then jointly decrypt messages as showcased
+// in the example for the `sharing` module.
+

Structs§

Enums§

  • Errors that can occur during the distributed key generation.
\ No newline at end of file diff --git a/elastic_elgamal/dkg/sidebar-items.js b/elastic_elgamal/dkg/sidebar-items.js new file mode 100644 index 0000000..7cae3ee --- /dev/null +++ b/elastic_elgamal/dkg/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Error"],"struct":["Opening","ParticipantCollectingCommitments","ParticipantCollectingPolynomials","ParticipantExchangingSecrets","PublicInfo"]}; \ No newline at end of file diff --git a/elastic_elgamal/dkg/struct.Opening.html b/elastic_elgamal/dkg/struct.Opening.html new file mode 100644 index 0000000..767b2f4 --- /dev/null +++ b/elastic_elgamal/dkg/struct.Opening.html @@ -0,0 +1,18 @@ +Opening in elastic_elgamal::dkg - Rust +
pub struct Opening(/* private fields */);
Expand description

Opening for a hash commitment used in Pedersen’s distributed key generation.

+

Trait Implementations§

source§

impl Clone for Opening

source§

fn clone(&self) -> Opening

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Opening

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Opening

source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where + D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl Serialize for Opening

source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where + S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/dkg/struct.ParticipantCollectingCommitments.html b/elastic_elgamal/dkg/struct.ParticipantCollectingCommitments.html new file mode 100644 index 0000000..a6ff758 --- /dev/null +++ b/elastic_elgamal/dkg/struct.ParticipantCollectingCommitments.html @@ -0,0 +1,58 @@ +ParticipantCollectingCommitments in elastic_elgamal::dkg - Rust +
pub struct ParticipantCollectingCommitments<G: Group> { /* private fields */ }
Expand description

Participant state during the first stage of the committed Pedersen’s distributed key generation.

+

During this stage, participants exchange commitments to their public keys via +a public bulletin board (e.g., a blockchain).

+

Implementations§

source§

impl<G: Group> ParticipantCollectingCommitments<G>

source

pub fn new<R: CryptoRng + RngCore>( + params: Params, + index: usize, + rng: &mut R +) -> Self

Instantiates a distributed key generation participant.

+
§Panics
+

Panics if index is greater or equal to the number of shares.

+
source

pub fn params(&self) -> &Params

Returns params of this threshold ElGamal encryption scheme.

+
source

pub fn index(&self) -> usize

Returns 0-based index of this participant.

+
source

pub fn commitment(&self) -> [u8; 32]

Returns the commitment of participant’s share of the joint public key.

+
§Panics
+

Panics if the commitment is missing which can only happen if this struct got corrupted +(e.g., after deserialization).

+
source

pub fn insert_commitment( + &mut self, + participant_index: usize, + commitment: [u8; 32] +)

Inserts a commitment from the participant with index participant_index.

+
§Panics
+

Panics if commitment for given participant was already provided or +participant_index is out of bounds.

+
source

pub fn missing_commitments(&self) -> impl Iterator<Item = usize> + '_

Returns indices of parties whose commitments were not provided.

+
source

pub fn finish_commitment_phase(self) -> ParticipantCollectingPolynomials<G>

Proceeds to the next step of the DKG protocol, in which participants exchange public +polynomials.

+
§Panics
+

Panics if any commitments are missing. If this is not known statically, check +with Self::missing_commitments() before calling this method.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for ParticipantCollectingCommitments<G>

source§

fn clone(&self) -> ParticipantCollectingCommitments<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for ParticipantCollectingCommitments<G>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for ParticipantCollectingCommitments<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for ParticipantCollectingCommitments<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/dkg/struct.ParticipantCollectingPolynomials.html b/elastic_elgamal/dkg/struct.ParticipantCollectingPolynomials.html new file mode 100644 index 0000000..2685e00 --- /dev/null +++ b/elastic_elgamal/dkg/struct.ParticipantCollectingPolynomials.html @@ -0,0 +1,55 @@ +ParticipantCollectingPolynomials in elastic_elgamal::dkg - Rust +
pub struct ParticipantCollectingPolynomials<G: Group> { /* private fields */ }
Expand description

Participant state during the second stage of the committed Pedersen’s distributed key generation.

+

During this stage, participants exchange public polynomials and openings for the commitments +exchanged on the previous stage. The exchange happens using a public bulletin board +(e.g., a blockchain).

+

Implementations§

source§

impl<G: Group> ParticipantCollectingPolynomials<G>

source

pub fn params(&self) -> &Params

Returns params of this threshold ElGamal encryption scheme.

+
source

pub fn index(&self) -> usize

Returns 0-based index of this participant.

+
source

pub fn public_info(&self) -> PublicInfo<'_, G>

Returns public participant information: participant’s public polynomial, +proof of possession for the corresponding secret polynomial and the opening of +the participant’s public key share commitment.

+
source

pub fn missing_public_polynomials(&self) -> impl Iterator<Item = usize> + '_

Returns the indices of parties whose public polynomials were not provided.

+
source

pub fn insert_public_polynomial( + &mut self, + participant_index: usize, + info: PublicInfo<'_, G> +) -> Result<(), Error>

Inserts public polynomial from participant with index participant_index +their proof of possession of the public polynomial and opening of +their previously provided commitment.

+
§Errors
+

Returns an error if provided polynomial doesn’t correspond to the previous +commitment or the proof of possession is not valid.

+
§Panics
+

Panics if participant_index is out of bounds.

+
source

pub fn finish_polynomials_phase(self) -> ParticipantExchangingSecrets<G>

Proceeds to the next step of the DKG protocol, in which participants exchange +secret shares.

+
§Panics
+

Panics if any public polynomials are missing. If this is not known statically, check +with Self::missing_public_polynomials() before calling this method.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for ParticipantCollectingPolynomials<G>

source§

fn clone(&self) -> ParticipantCollectingPolynomials<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for ParticipantCollectingPolynomials<G>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for ParticipantCollectingPolynomials<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for ParticipantCollectingPolynomials<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/dkg/struct.ParticipantExchangingSecrets.html b/elastic_elgamal/dkg/struct.ParticipantExchangingSecrets.html new file mode 100644 index 0000000..5a25ae2 --- /dev/null +++ b/elastic_elgamal/dkg/struct.ParticipantExchangingSecrets.html @@ -0,0 +1,58 @@ +ParticipantExchangingSecrets in elastic_elgamal::dkg - Rust +
pub struct ParticipantExchangingSecrets<G: Group> { /* private fields */ }
Expand description

Participant state during the third and final stage of the committed Pedersen’s +distributed key generation.

+

During this stage, participants exchange secret shares corresponding to the polynomials +exchanged on the previous stage. The exchange happens using secure peer-to-peer channels +established between pairs of participants.

+

Implementations§

source§

impl<G: Group> ParticipantExchangingSecrets<G>

source

pub fn params(&self) -> &Params

Returns params of this threshold ElGamal encryption scheme.

+
source

pub fn index(&self) -> usize

Returns 0-based index of this participant.

+
source

pub fn secret_share_for_participant( + &self, + participant_index: usize +) -> SecretKey<G>

Returns the secret share for a participant with the specified participant_index.

+
source

pub fn missing_shares(&self) -> impl Iterator<Item = usize> + '_

Returns indices of parties whose secret shares were not provided.

+
source

pub fn insert_secret_share( + &mut self, + participant_index: usize, + secret_share: SecretKey<G> +) -> Result<(), Error>

Inserts a secret share from participant with index participant_index and +checks that the share is valid.

+
§Errors
+

Returns an error if provided secret share doesn’t correspond to the participant’s +public polynomial collected on the previous step of the DKG protocol.

+
§Panics
+

Panics if participant_index is out of bounds.

+
source

pub fn complete(self) -> Result<ActiveParticipant<G>, Error>

Completes the distributed key generation protocol returning an ActiveParticipant.

+
§Errors
+

Returns error if secret shares from some parties were not provided, +or if the PublicKeySet cannot be created from participants’ keys.

+
§Panics
+

Panics if shares from any participants are missing. If this is not known statically, check +with Self::missing_shares() before calling this method.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for ParticipantExchangingSecrets<G>

source§

fn clone(&self) -> ParticipantExchangingSecrets<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for ParticipantExchangingSecrets<G>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for ParticipantExchangingSecrets<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for ParticipantExchangingSecrets<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/dkg/struct.PublicInfo.html b/elastic_elgamal/dkg/struct.PublicInfo.html new file mode 100644 index 0000000..b17410e --- /dev/null +++ b/elastic_elgamal/dkg/struct.PublicInfo.html @@ -0,0 +1,40 @@ +PublicInfo in elastic_elgamal::dkg - Rust +
pub struct PublicInfo<'a, G: Group> {
+    pub polynomial: Vec<G::Element>,
+    pub proof_of_possession: Cow<'a, ProofOfPossession<G>>,
+    pub opening: Opening,
+}
Expand description

Public participant information in the distributed key generation protocol. Returned by +ParticipantCollectingPolynomials::public_info().

+

Fields§

§polynomial: Vec<G::Element>

Participant’s public polynomial.

+
§proof_of_possession: Cow<'a, ProofOfPossession<G>>

Proof of possession for the secret polynomial that corresponds to polynomial.

+
§opening: Opening

Opening for the participant’s key commitment.

+

Implementations§

source§

impl<G: Group> PublicInfo<'_, G>

source

pub fn into_owned(self) -> PublicInfo<'static, G>

Converts this information to the owned form.

+

Trait Implementations§

source§

impl<'a, G: Clone + Group> Clone for PublicInfo<'a, G>
where + G::Element: Clone,

source§

fn clone(&self) -> PublicInfo<'a, G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'a, G: Debug + Group> Debug for PublicInfo<'a, G>
where + G::Element: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, 'a, G: Group> Deserialize<'de> for PublicInfo<'a, G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<'a, G: Group> Serialize for PublicInfo<'a, G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<'a, G> Freeze for PublicInfo<'a, G>
where + <G as ScalarOps>::Scalar: Freeze,

§

impl<'a, G> RefUnwindSafe for PublicInfo<'a, G>

§

impl<'a, G> Send for PublicInfo<'a, G>
where + <G as ElementOps>::Element: Send, + <G as ScalarOps>::Scalar: Sync + Send,

§

impl<'a, G> Sync for PublicInfo<'a, G>
where + <G as ElementOps>::Element: Sync, + <G as ScalarOps>::Scalar: Sync,

§

impl<'a, G> Unpin for PublicInfo<'a, G>
where + <G as ElementOps>::Element: Unpin, + <G as ScalarOps>::Scalar: Unpin,

§

impl<'a, G> UnwindSafe for PublicInfo<'a, G>

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/encryption/struct.Ciphertext.html b/elastic_elgamal/encryption/struct.Ciphertext.html new file mode 100644 index 0000000..2c444a0 --- /dev/null +++ b/elastic_elgamal/encryption/struct.Ciphertext.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../elastic_elgamal/struct.Ciphertext.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/encryption/struct.CiphertextWithValue.html b/elastic_elgamal/encryption/struct.CiphertextWithValue.html new file mode 100644 index 0000000..cfdd80c --- /dev/null +++ b/elastic_elgamal/encryption/struct.CiphertextWithValue.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../elastic_elgamal/struct.CiphertextWithValue.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/encryption/struct.DiscreteLogTable.html b/elastic_elgamal/encryption/struct.DiscreteLogTable.html new file mode 100644 index 0000000..bccacfb --- /dev/null +++ b/elastic_elgamal/encryption/struct.DiscreteLogTable.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../elastic_elgamal/struct.DiscreteLogTable.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/enum.PublicKeyConversionError.html b/elastic_elgamal/enum.PublicKeyConversionError.html new file mode 100644 index 0000000..c66e9ab --- /dev/null +++ b/elastic_elgamal/enum.PublicKeyConversionError.html @@ -0,0 +1,23 @@ +PublicKeyConversionError in elastic_elgamal - Rust +
#[non_exhaustive]
pub enum PublicKeyConversionError { + InvalidByteSize, + InvalidGroupElement, + IdentityKey, +}
Expand description

Errors that can occur when converting other types to PublicKey.

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

InvalidByteSize

Invalid size of the byte buffer.

+
§

InvalidGroupElement

Byte buffer has correct size, but does not represent a group element.

+
§

IdentityKey

Underlying group element is the group identity.

+

Trait Implementations§

source§

impl Clone for PublicKeyConversionError

source§

fn clone(&self) -> PublicKeyConversionError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for PublicKeyConversionError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for PublicKeyConversionError

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for PublicKeyConversionError

1.30.0 · source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/enum.VerificationError.html b/elastic_elgamal/enum.VerificationError.html new file mode 100644 index 0000000..1b41b67 --- /dev/null +++ b/elastic_elgamal/enum.VerificationError.html @@ -0,0 +1,32 @@ +VerificationError in elastic_elgamal - Rust +
#[non_exhaustive]
pub enum VerificationError { + ChallengeMismatch, + LenMismatch { + collection: &'static str, + expected: usize, + actual: usize, + }, +}
Expand description

Error verifying base proofs, such as RingProof, LogEqualityProof +or ProofOfPossession.

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

ChallengeMismatch

Restored challenge scalar does not match the one provided in the proof.

+

This error most likely means that the proof itself is malformed, or that it was created +for a different context than it is being verified for.

+
§

LenMismatch

Fields

§collection: &'static str

Human-readable collection name, such as “public keys”.

+
§expected: usize

Expected size of the collection.

+
§actual: usize

Actual size of the collection.

+

A collection (e.g., number of responses in a RingProof) has a different size +than expected.

+

This error most likely means that the proof is malformed.

+

Trait Implementations§

source§

impl Debug for VerificationError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for VerificationError

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for VerificationError

1.30.0 · source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/group/curve25519/struct.Curve25519Subgroup.html b/elastic_elgamal/group/curve25519/struct.Curve25519Subgroup.html new file mode 100644 index 0000000..1712db8 --- /dev/null +++ b/elastic_elgamal/group/curve25519/struct.Curve25519Subgroup.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/group/struct.Curve25519Subgroup.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/group/generic/struct.Generic.html b/elastic_elgamal/group/generic/struct.Generic.html new file mode 100644 index 0000000..2e392e9 --- /dev/null +++ b/elastic_elgamal/group/generic/struct.Generic.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/group/struct.Generic.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/group/index.html b/elastic_elgamal/group/index.html new file mode 100644 index 0000000..368a044 --- /dev/null +++ b/elastic_elgamal/group/index.html @@ -0,0 +1,12 @@ +elastic_elgamal::group - Rust +

Module elastic_elgamal::group

source ·
Expand description

Traits and implementations for prime-order groups in which +the decisional Diffie–Hellman (DDH), computational Diffie–Hellman (CDH) +and discrete log (DL) problems are believed to be hard.

+

(Decisional Diffie–Hellman assumption is considered stronger than both CDH and DL, +so if DDH is believed to hold for a certain group, it should be good to go.)

+

Such groups can be applied for ElGamal encryption and other cryptographic protocols +from this crate.

+

Structs§

Traits§

  • Helper trait for Group that describes operations on group elements (i.e., EC points +for elliptic curve groups).
  • Prime-order group in which the discrete log problem and decisional / computational +Diffie–Hellman problems are believed to be hard.
  • Helper trait for Group that describes operations on group scalars.
\ No newline at end of file diff --git a/elastic_elgamal/group/ristretto/struct.Ristretto.html b/elastic_elgamal/group/ristretto/struct.Ristretto.html new file mode 100644 index 0000000..438311c --- /dev/null +++ b/elastic_elgamal/group/ristretto/struct.Ristretto.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/group/struct.Ristretto.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/group/sidebar-items.js b/elastic_elgamal/group/sidebar-items.js new file mode 100644 index 0000000..6ffaa52 --- /dev/null +++ b/elastic_elgamal/group/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["Curve25519Subgroup","Generic","RandomBytesProvider","Ristretto"],"trait":["ElementOps","Group","ScalarOps"]}; \ No newline at end of file diff --git a/elastic_elgamal/group/struct.Curve25519Subgroup.html b/elastic_elgamal/group/struct.Curve25519Subgroup.html new file mode 100644 index 0000000..5ec9fff --- /dev/null +++ b/elastic_elgamal/group/struct.Curve25519Subgroup.html @@ -0,0 +1,51 @@ +Curve25519Subgroup in elastic_elgamal::group - Rust +
pub struct Curve25519Subgroup(/* private fields */);
Expand description

Prime-order subgroup of Curve25519 without any transforms performed for EC points.

+

Since the curve has cofactor 8, ElementOps::deserialize_element() implementation +explicitly checks on deserializing each EC point that the point is torsion-free +(belongs to the prime-order subgroup), which is moderately slow (takes ~0.1ms on +a laptop).

+

Prefer using Ristretto if compatibility with other Curve25519 applications is not a concern. +(If it is a concern, beware of cofactor pitfalls!)

+

Trait Implementations§

source§

impl Clone for Curve25519Subgroup

source§

fn clone(&self) -> Curve25519Subgroup

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Curve25519Subgroup

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl ElementOps for Curve25519Subgroup

§

type Element = EdwardsPoint

Element of the group. Arithmetic operations requested here (addition among +elements and multiplication by a Scalar) must be constant-time.
source§

const ELEMENT_SIZE: usize = 32usize

Byte size of a serialized Self::Element.
source§

fn identity() -> Self::Element

Returns the identity of the group (aka point at infinity for EC groups).
source§

fn is_identity(element: &Self::Element) -> bool

Checks if the specified element is the identity.
source§

fn generator() -> Self::Element

Returns the agreed-upon generator of the group.
source§

fn serialize_element(element: &Self::Element, buffer: &mut [u8])

Serializes element into the provided buffer, which is guaranteed to have length +Self::ELEMENT_SIZE.
source§

fn deserialize_element(buffer: &[u8]) -> Option<Self::Element>

Deserializes an element from buffer, which is guaranteed to have length +Self::ELEMENT_SIZE. This method returns None if the buffer +does not correspond to a representation of a valid scalar.
source§

impl Group for Curve25519Subgroup

source§

fn mul_generator(k: &Scalar) -> Self::Element

Multiplies the provided scalar by ElementOps::generator(). This operation must be +constant-time. Read more
source§

fn vartime_mul_generator(k: &Scalar) -> Self::Element

Multiplies the provided scalar by ElementOps::generator(). +Unlike Self::mul_generator(), this operation does not need to be constant-time; +thus, it may employ additional optimizations. Read more
source§

fn multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
where + I: IntoIterator<Item = &'a Self::Scalar>, + J: IntoIterator<Item = Self::Element>,

Multiplies provided scalars by elements. This operation must be constant-time +w.r.t. the given length of elements. Read more
source§

fn vartime_double_mul_generator( + k: &Scalar, + k_element: Self::Element, + r: &Scalar +) -> Self::Element

Calculates k * k_element + r * G, where G is the group generator. This operation +does not need to be constant-time. Read more
source§

fn vartime_multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
where + I: IntoIterator<Item = &'a Self::Scalar>, + J: IntoIterator<Item = Self::Element>,

Multiplies provided scalars by elements. Unlike Self::multi_mul(), +this operation does not need to be constant-time; thus, it may employ +additional optimizations. Read more
source§

impl Hash for Curve25519Subgroup

source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl PartialEq for Curve25519Subgroup

source§

fn eq(&self, other: &Curve25519Subgroup) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl ScalarOps for Curve25519Subgroup

§

type Scalar = Scalar

Scalar type. As per Group contract, scalars must form a prime field. +Arithmetic operations on scalars requested here must be constant-time.
source§

const SCALAR_SIZE: usize = 32usize

Byte size of a serialized Self::Scalar.
source§

fn generate_scalar<R: CryptoRng + RngCore>(rng: &mut R) -> Self::Scalar

Generates a random scalar based on the provided CSPRNG. This operation +must be constant-time.
source§

fn scalar_from_random_bytes(source: RandomBytesProvider<'_>) -> Self::Scalar

Generates a scalar from a source of random bytes. This operation must be constant-time. +The source is guaranteed to return any necessary number of bytes. Read more
source§

fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar

Inverts the scalar, which is guaranteed to be non-zero. This operation does not +need to be constant-time.
source§

fn invert_scalars(scalars: &mut [Self::Scalar])

Inverts scalars in a batch. This operation does not need to be constant-time. Read more
source§

fn serialize_scalar(scalar: &Self::Scalar, buffer: &mut [u8])

Serializes the scalar into the provided buffer, which is guaranteed to have length +Self::SCALAR_SIZE.
source§

fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar>

Deserializes the scalar from buffer, which is guaranteed to have length +Self::SCALAR_SIZE. This method returns None if the buffer +does not correspond to a representation of a valid scalar.
source§

impl Copy for Curve25519Subgroup

source§

impl Eq for Curve25519Subgroup

source§

impl StructuralPartialEq for Curve25519Subgroup

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/group/struct.Generic.html b/elastic_elgamal/group/struct.Generic.html new file mode 100644 index 0000000..8df22e3 --- /dev/null +++ b/elastic_elgamal/group/struct.Generic.html @@ -0,0 +1,62 @@ +Generic in elastic_elgamal::group - Rust +
pub struct Generic<C>(/* private fields */);
Expand description

Generic Group implementation for elliptic curves defined in terms of the traits +from the elliptic-curve crate.

+

§Assumptions

+
    +
  • Arithmetic operations required to be constant-time as per ScalarOps and ElementOps +contracts are indeed constant-time.
  • +
+

Trait Implementations§

source§

impl<C> Clone for Generic<C>

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<C: Debug> Debug for Generic<C>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<C> ElementOps for Generic<C>
where + C: CurveArithmetic, + Scalar<C>: Zeroize, + FieldBytesSize<C>: ModulusSize, + ProjectivePoint<C>: ToEncodedPoint<C> + FromEncodedPoint<C>,

§

type Element = <C as CurveArithmetic>::ProjectivePoint

Element of the group. Arithmetic operations requested here (addition among +elements and multiplication by a Scalar) must be constant-time.
source§

const ELEMENT_SIZE: usize = _

Byte size of a serialized Self::Element.
source§

fn identity() -> Self::Element

Returns the identity of the group (aka point at infinity for EC groups).
source§

fn is_identity(element: &Self::Element) -> bool

Checks if the specified element is the identity.
source§

fn generator() -> Self::Element

Returns the agreed-upon generator of the group.
source§

fn serialize_element(element: &Self::Element, buffer: &mut [u8])

Serializes element into the provided buffer, which is guaranteed to have length +Self::ELEMENT_SIZE.
source§

fn deserialize_element(input: &[u8]) -> Option<Self::Element>

Deserializes an element from buffer, which is guaranteed to have length +Self::ELEMENT_SIZE. This method returns None if the buffer +does not correspond to a representation of a valid scalar.
source§

impl<C> Group for Generic<C>
where + C: CurveArithmetic + 'static, + Scalar<C>: Zeroize, + FieldBytesSize<C>: ModulusSize, + ProjectivePoint<C>: ToEncodedPoint<C> + FromEncodedPoint<C>,

source§

fn mul_generator(k: &Self::Scalar) -> Self::Element

Multiplies the provided scalar by ElementOps::generator(). This operation must be +constant-time. Read more
source§

fn vartime_mul_generator(k: &Self::Scalar) -> Self::Element

Multiplies the provided scalar by ElementOps::generator(). +Unlike Self::mul_generator(), this operation does not need to be constant-time; +thus, it may employ additional optimizations. Read more
source§

fn multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
where + I: IntoIterator<Item = &'a Self::Scalar>, + J: IntoIterator<Item = Self::Element>,

Multiplies provided scalars by elements. This operation must be constant-time +w.r.t. the given length of elements. Read more
source§

fn vartime_double_mul_generator( + k: &Self::Scalar, + k_element: Self::Element, + r: &Self::Scalar +) -> Self::Element

Calculates k * k_element + r * G, where G is the group generator. This operation +does not need to be constant-time. Read more
source§

fn vartime_multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
where + I: IntoIterator<Item = &'a Self::Scalar>, + J: IntoIterator<Item = Self::Element>,

Multiplies provided scalars by elements. Unlike Self::multi_mul(), +this operation does not need to be constant-time; thus, it may employ +additional optimizations. Read more
source§

impl<C> ScalarOps for Generic<C>
where + C: CurveArithmetic, + Scalar<C>: Zeroize,

§

type Scalar = <C as CurveArithmetic>::Scalar

Scalar type. As per Group contract, scalars must form a prime field. +Arithmetic operations on scalars requested here must be constant-time.
source§

const SCALAR_SIZE: usize = <FieldBytesSize<C> as Unsigned>::USIZE

Byte size of a serialized Self::Scalar.
source§

fn generate_scalar<R: CryptoRng + RngCore>(rng: &mut R) -> Self::Scalar

Generates a random scalar based on the provided CSPRNG. This operation +must be constant-time.
source§

fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar

Inverts the scalar, which is guaranteed to be non-zero. This operation does not +need to be constant-time.
source§

fn serialize_scalar(scalar: &Self::Scalar, buffer: &mut [u8])

Serializes the scalar into the provided buffer, which is guaranteed to have length +Self::SCALAR_SIZE.
source§

fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar>

Deserializes the scalar from buffer, which is guaranteed to have length +Self::SCALAR_SIZE. This method returns None if the buffer +does not correspond to a representation of a valid scalar.
source§

fn scalar_from_random_bytes(source: RandomBytesProvider<'_>) -> Self::Scalar

Generates a scalar from a source of random bytes. This operation must be constant-time. +The source is guaranteed to return any necessary number of bytes. Read more
source§

fn invert_scalars(scalars: &mut [Self::Scalar])

Inverts scalars in a batch. This operation does not need to be constant-time. Read more
source§

impl<C> Copy for Generic<C>

Auto Trait Implementations§

§

impl<C> Freeze for Generic<C>

§

impl<C> RefUnwindSafe for Generic<C>
where + C: RefUnwindSafe,

§

impl<C> Send for Generic<C>
where + C: Send,

§

impl<C> Sync for Generic<C>
where + C: Sync,

§

impl<C> Unpin for Generic<C>
where + C: Unpin,

§

impl<C> UnwindSafe for Generic<C>
where + C: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/group/struct.RandomBytesProvider.html b/elastic_elgamal/group/struct.RandomBytesProvider.html new file mode 100644 index 0000000..ed12f8a --- /dev/null +++ b/elastic_elgamal/group/struct.RandomBytesProvider.html @@ -0,0 +1,17 @@ +RandomBytesProvider in elastic_elgamal::group - Rust +
pub struct RandomBytesProvider<'a> { /* private fields */ }
Expand description

Provides an arbitrary number of random bytes.

+

Unlike RngCore::fill_bytes(), a single provider can only be used once.

+

Implementations§

source§

impl<'a> RandomBytesProvider<'a>

source

pub fn fill_bytes(self, dest: &mut [u8])

Writes random bytes into the specified buffer. As follows from the signature, this method +can only be called once for a provider instance.

+

Trait Implementations§

source§

impl Debug for RandomBytesProvider<'_>

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/group/struct.Ristretto.html b/elastic_elgamal/group/struct.Ristretto.html new file mode 100644 index 0000000..38ae81f --- /dev/null +++ b/elastic_elgamal/group/struct.Ristretto.html @@ -0,0 +1,45 @@ +Ristretto in elastic_elgamal::group - Rust +
pub struct Ristretto(/* private fields */);
Expand description

Ristretto transform of Curve25519, also known as ristretto255.

+

Trait Implementations§

source§

impl Clone for Ristretto

source§

fn clone(&self) -> Ristretto

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Ristretto

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl ElementOps for Ristretto

§

type Element = RistrettoPoint

Element of the group. Arithmetic operations requested here (addition among +elements and multiplication by a Scalar) must be constant-time.
source§

const ELEMENT_SIZE: usize = 32usize

Byte size of a serialized Self::Element.
source§

fn identity() -> Self::Element

Returns the identity of the group (aka point at infinity for EC groups).
source§

fn is_identity(element: &Self::Element) -> bool

Checks if the specified element is the identity.
source§

fn generator() -> Self::Element

Returns the agreed-upon generator of the group.
source§

fn serialize_element(element: &Self::Element, buffer: &mut [u8])

Serializes element into the provided buffer, which is guaranteed to have length +Self::ELEMENT_SIZE.
source§

fn deserialize_element(buffer: &[u8]) -> Option<Self::Element>

Deserializes an element from buffer, which is guaranteed to have length +Self::ELEMENT_SIZE. This method returns None if the buffer +does not correspond to a representation of a valid scalar.
source§

impl Group for Ristretto

source§

fn mul_generator(k: &Scalar) -> Self::Element

Multiplies the provided scalar by ElementOps::generator(). This operation must be +constant-time. Read more
source§

fn vartime_mul_generator(k: &Scalar) -> Self::Element

Multiplies the provided scalar by ElementOps::generator(). +Unlike Self::mul_generator(), this operation does not need to be constant-time; +thus, it may employ additional optimizations. Read more
source§

fn multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
where + I: IntoIterator<Item = &'a Self::Scalar>, + J: IntoIterator<Item = Self::Element>,

Multiplies provided scalars by elements. This operation must be constant-time +w.r.t. the given length of elements. Read more
source§

fn vartime_double_mul_generator( + k: &Scalar, + k_element: Self::Element, + r: &Scalar +) -> Self::Element

Calculates k * k_element + r * G, where G is the group generator. This operation +does not need to be constant-time. Read more
source§

fn vartime_multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
where + I: IntoIterator<Item = &'a Self::Scalar>, + J: IntoIterator<Item = Self::Element>,

Multiplies provided scalars by elements. Unlike Self::multi_mul(), +this operation does not need to be constant-time; thus, it may employ +additional optimizations. Read more
source§

impl Hash for Ristretto

source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl PartialEq for Ristretto

source§

fn eq(&self, other: &Ristretto) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl ScalarOps for Ristretto

§

type Scalar = Scalar

Scalar type. As per Group contract, scalars must form a prime field. +Arithmetic operations on scalars requested here must be constant-time.
source§

const SCALAR_SIZE: usize = 32usize

Byte size of a serialized Self::Scalar.
source§

fn generate_scalar<R: CryptoRng + RngCore>(rng: &mut R) -> Self::Scalar

Generates a random scalar based on the provided CSPRNG. This operation +must be constant-time.
source§

fn scalar_from_random_bytes(source: RandomBytesProvider<'_>) -> Self::Scalar

Generates a scalar from a source of random bytes. This operation must be constant-time. +The source is guaranteed to return any necessary number of bytes. Read more
source§

fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar

Inverts the scalar, which is guaranteed to be non-zero. This operation does not +need to be constant-time.
source§

fn invert_scalars(scalars: &mut [Self::Scalar])

Inverts scalars in a batch. This operation does not need to be constant-time. Read more
source§

fn serialize_scalar(scalar: &Self::Scalar, buffer: &mut [u8])

Serializes the scalar into the provided buffer, which is guaranteed to have length +Self::SCALAR_SIZE.
source§

fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar>

Deserializes the scalar from buffer, which is guaranteed to have length +Self::SCALAR_SIZE. This method returns None if the buffer +does not correspond to a representation of a valid scalar.
source§

impl Copy for Ristretto

source§

impl Eq for Ristretto

source§

impl StructuralPartialEq for Ristretto

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/group/trait.ElementOps.html b/elastic_elgamal/group/trait.ElementOps.html new file mode 100644 index 0000000..c28eb24 --- /dev/null +++ b/elastic_elgamal/group/trait.ElementOps.html @@ -0,0 +1,30 @@ +ElementOps in elastic_elgamal::group - Rust +
pub trait ElementOps: ScalarOps {
+    type Element: Copy + Add<Output = Self::Element> + Sub<Output = Self::Element> + Neg<Output = Self::Element> + for<'a> Mul<&'a Self::Scalar, Output = Self::Element> + PartialEq + Debug;
+
+    const ELEMENT_SIZE: usize;
+
+    // Required methods
+    fn identity() -> Self::Element;
+    fn is_identity(element: &Self::Element) -> bool;
+    fn generator() -> Self::Element;
+    fn serialize_element(element: &Self::Element, output: &mut [u8]);
+    fn deserialize_element(buffer: &[u8]) -> Option<Self::Element>;
+}
Expand description

Helper trait for Group that describes operations on group elements (i.e., EC points +for elliptic curve groups).

+

Required Associated Types§

source

type Element: Copy + Add<Output = Self::Element> + Sub<Output = Self::Element> + Neg<Output = Self::Element> + for<'a> Mul<&'a Self::Scalar, Output = Self::Element> + PartialEq + Debug

Element of the group. Arithmetic operations requested here (addition among +elements and multiplication by a Scalar) must be constant-time.

+

Required Associated Constants§

source

const ELEMENT_SIZE: usize

Byte size of a serialized Self::Element.

+

Required Methods§

source

fn identity() -> Self::Element

Returns the identity of the group (aka point at infinity for EC groups).

+
source

fn is_identity(element: &Self::Element) -> bool

Checks if the specified element is the identity.

+
source

fn generator() -> Self::Element

Returns the agreed-upon generator of the group.

+
source

fn serialize_element(element: &Self::Element, output: &mut [u8])

Serializes element into the provided buffer, which is guaranteed to have length +Self::ELEMENT_SIZE.

+
source

fn deserialize_element(buffer: &[u8]) -> Option<Self::Element>

Deserializes an element from buffer, which is guaranteed to have length +Self::ELEMENT_SIZE. This method returns None if the buffer +does not correspond to a representation of a valid scalar.

+

Object Safety§

This trait is not object safe.

Implementors§

source§

impl ElementOps for Curve25519Subgroup

§

type Element = EdwardsPoint

source§

const ELEMENT_SIZE: usize = 32usize

source§

impl ElementOps for Ristretto

§

type Element = RistrettoPoint

source§

const ELEMENT_SIZE: usize = 32usize

source§

impl<C> ElementOps for Generic<C>
where + C: CurveArithmetic, + Scalar<C>: Zeroize, + FieldBytesSize<C>: ModulusSize, + ProjectivePoint<C>: ToEncodedPoint<C> + FromEncodedPoint<C>,

§

type Element = <C as CurveArithmetic>::ProjectivePoint

source§

const ELEMENT_SIZE: usize = _

\ No newline at end of file diff --git a/elastic_elgamal/group/trait.Group.html b/elastic_elgamal/group/trait.Group.html new file mode 100644 index 0000000..a66b68d --- /dev/null +++ b/elastic_elgamal/group/trait.Group.html @@ -0,0 +1,66 @@ +Group in elastic_elgamal::group - Rust +
pub trait Group: Copy + ScalarOps + ElementOps + 'static {
+    // Provided methods
+    fn mul_generator(k: &Self::Scalar) -> Self::Element { ... }
+    fn vartime_mul_generator(k: &Self::Scalar) -> Self::Element { ... }
+    fn multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
+       where I: IntoIterator<Item = &'a Self::Scalar>,
+             J: IntoIterator<Item = Self::Element> { ... }
+    fn vartime_double_mul_generator(
+        k: &Self::Scalar,
+        k_element: Self::Element,
+        r: &Self::Scalar
+    ) -> Self::Element { ... }
+    fn vartime_multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
+       where I: IntoIterator<Item = &'a Self::Scalar>,
+             J: IntoIterator<Item = Self::Element> { ... }
+}
Expand description

Prime-order group in which the discrete log problem and decisional / computational +Diffie–Hellman problems are believed to be hard.

+

Groups conforming to this trait can be used for ElGamal encryption and other +cryptographic protocols defined in this crate.

+

This crate provides the following implementations of this trait:

+
    +
  • Curve25519Subgroup, representation of a prime-order subgroup of Curve25519 +with the conventionally chosen generator.
  • +
  • Ristretto, a transform of Curve25519 which eliminates its co-factor 8 with the help +of the eponymous technique.
  • +
  • Generic implementation defined in terms of traits from the elliptic-curve crate. +(For example, this means secp256k1 support via the k256 crate.)
  • +
+

Provided Methods§

source

fn mul_generator(k: &Self::Scalar) -> Self::Element

Multiplies the provided scalar by ElementOps::generator(). This operation must be +constant-time.

+
§Default implementation
+

Implemented using Mul (which is constant-time as per the ElementOps +contract).

+
source

fn vartime_mul_generator(k: &Self::Scalar) -> Self::Element

Multiplies the provided scalar by ElementOps::generator(). +Unlike Self::mul_generator(), this operation does not need to be constant-time; +thus, it may employ additional optimizations.

+
§Default implementation
+

Implemented by calling Self::mul_generator().

+
source

fn multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
where + I: IntoIterator<Item = &'a Self::Scalar>, + J: IntoIterator<Item = Self::Element>,

Multiplies provided scalars by elements. This operation must be constant-time +w.r.t. the given length of elements.

+
§Default implementation
+

Implemented by straightforward computations, which are constant-time as per +the ElementOps contract.

+
source

fn vartime_double_mul_generator( + k: &Self::Scalar, + k_element: Self::Element, + r: &Self::Scalar +) -> Self::Element

Calculates k * k_element + r * G, where G is the group generator. This operation +does not need to be constant-time.

+
§Default implementation
+

Implemented by straightforward arithmetic.

+
source

fn vartime_multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
where + I: IntoIterator<Item = &'a Self::Scalar>, + J: IntoIterator<Item = Self::Element>,

Multiplies provided scalars by elements. Unlike Self::multi_mul(), +this operation does not need to be constant-time; thus, it may employ +additional optimizations.

+
§Default implementation
+

Implemented by calling Self::multi_mul().

+

Object Safety§

This trait is not object safe.

Implementors§

source§

impl Group for Curve25519Subgroup

source§

impl Group for Ristretto

source§

impl<C> Group for Generic<C>
where + C: CurveArithmetic + 'static, + Scalar<C>: Zeroize, + FieldBytesSize<C>: ModulusSize, + ProjectivePoint<C>: ToEncodedPoint<C> + FromEncodedPoint<C>,

\ No newline at end of file diff --git a/elastic_elgamal/group/trait.ScalarOps.html b/elastic_elgamal/group/trait.ScalarOps.html new file mode 100644 index 0000000..bfaff36 --- /dev/null +++ b/elastic_elgamal/group/trait.ScalarOps.html @@ -0,0 +1,41 @@ +ScalarOps in elastic_elgamal::group - Rust +
pub trait ScalarOps {
+    type Scalar: Copy + Default + From<u64> + From<Self::Scalar> + Neg<Output = Self::Scalar> + Add<Output = Self::Scalar> + for<'a> Add<&'a Self::Scalar, Output = Self::Scalar> + Sub<Output = Self::Scalar> + Mul<Output = Self::Scalar> + for<'a> Mul<&'a Self::Scalar, Output = Self::Scalar> + PartialEq + Zeroize + Debug;
+
+    const SCALAR_SIZE: usize;
+
+    // Required methods
+    fn generate_scalar<R: CryptoRng + RngCore>(rng: &mut R) -> Self::Scalar;
+    fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
+    fn serialize_scalar(scalar: &Self::Scalar, buffer: &mut [u8]);
+    fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar>;
+
+    // Provided methods
+    fn scalar_from_random_bytes(source: RandomBytesProvider<'_>) -> Self::Scalar { ... }
+    fn invert_scalars(scalars: &mut [Self::Scalar]) { ... }
+}
Expand description

Helper trait for Group that describes operations on group scalars.

+

Required Associated Types§

source

type Scalar: Copy + Default + From<u64> + From<Self::Scalar> + Neg<Output = Self::Scalar> + Add<Output = Self::Scalar> + for<'a> Add<&'a Self::Scalar, Output = Self::Scalar> + Sub<Output = Self::Scalar> + Mul<Output = Self::Scalar> + for<'a> Mul<&'a Self::Scalar, Output = Self::Scalar> + PartialEq + Zeroize + Debug

Scalar type. As per Group contract, scalars must form a prime field. +Arithmetic operations on scalars requested here must be constant-time.

+

Required Associated Constants§

source

const SCALAR_SIZE: usize

Byte size of a serialized Self::Scalar.

+

Required Methods§

source

fn generate_scalar<R: CryptoRng + RngCore>(rng: &mut R) -> Self::Scalar

Generates a random scalar based on the provided CSPRNG. This operation +must be constant-time.

+
source

fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar

Inverts the scalar, which is guaranteed to be non-zero. This operation does not +need to be constant-time.

+
source

fn serialize_scalar(scalar: &Self::Scalar, buffer: &mut [u8])

Serializes the scalar into the provided buffer, which is guaranteed to have length +Self::SCALAR_SIZE.

+
source

fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar>

Deserializes the scalar from buffer, which is guaranteed to have length +Self::SCALAR_SIZE. This method returns None if the buffer +does not correspond to a representation of a valid scalar.

+

Provided Methods§

source

fn scalar_from_random_bytes(source: RandomBytesProvider<'_>) -> Self::Scalar

Generates a scalar from a source of random bytes. This operation must be constant-time. +The source is guaranteed to return any necessary number of bytes.

+
§Default implementation
+
    +
  1. Create a ChaCha RNG using 32 bytes read from source as the seed.
  2. +
  3. Call Self::generate_scalar() with the created RNG.
  4. +
+
source

fn invert_scalars(scalars: &mut [Self::Scalar])

Inverts scalars in a batch. This operation does not need to be constant-time.

+
§Default implementation
+

Inverts every scalar successively.

+

Object Safety§

This trait is not object safe.

Implementors§

source§

impl ScalarOps for Curve25519Subgroup

§

type Scalar = Scalar

source§

const SCALAR_SIZE: usize = 32usize

source§

impl ScalarOps for Ristretto

§

type Scalar = Scalar

source§

const SCALAR_SIZE: usize = 32usize

source§

impl<C> ScalarOps for Generic<C>
where + C: CurveArithmetic, + Scalar<C>: Zeroize,

§

type Scalar = <C as CurveArithmetic>::Scalar

source§

const SCALAR_SIZE: usize = <FieldBytesSize<C> as Unsigned>::USIZE

\ No newline at end of file diff --git a/elastic_elgamal/index.html b/elastic_elgamal/index.html new file mode 100644 index 0000000..871a529 --- /dev/null +++ b/elastic_elgamal/index.html @@ -0,0 +1,101 @@ +elastic_elgamal - Rust +

Crate elastic_elgamal

source ·
Expand description

ElGamal encryption and related cryptographic protocols with pluggable crypto backend.

+

§⚠ Warnings

+

While the logic in this crate relies on standard cryptographic assumptions +(complexity of discrete log and computational / decisional Diffie–Hellman problems +in certain groups), it has not been independently verified for correctness or absence +of side-channel attack vectors. Use at your own risk.

+

ElGamal encryption is not a good choice for general-purpose public-key encryption +since it is vulnerable to chosen-ciphertext attacks. For security, +decryption operations should be limited on the application level.

+

§Overview

+ +

§Backends

+

group module exposes a generic framework for plugging a Group +implementation into crypto primitives. It also provides several implementations:

+
    +
  • Ristretto and Curve25519Subgroup implementations based on Curve25519.
  • +
  • Generic implementation allowing to plug in any elliptic curve group conforming to +the traits specified by the elliptic-curve crate. For example, +the secp256k1 curve can be used via the k256 crate.
  • +
+

§Crate features

§std

+

(on by default)

+

Enables support of types from std, such as the Error trait and the HashMap collection.

+

§hashbrown

+

(off by default)

+

Imports hash maps and sets from the eponymous crate +instead of using ones from the Rust std library. This feature is necessary +if the std feature is disabled.

+

§curve25519-dalek

+

(on by default)

+

Implements Group for two prime groups based on Curve25519 using the curve25519-dalek +crate: its prime subgroup, and the Ristretto transform of Curve25519 (aka ristretto255).

+

§curve25519-dalek-ng

+

(off by default)

+

Same in terms of functionality as curve25519-dalek, but uses the curve25519-dalek-ng +crate instead of curve25519-dalek. This may be beneficial for applications that use +bulletproofs or other libraries depending on curve25519-dalek-ng.

+

The curve25519-dalek-ng crate does not compile unless some crypto backend is selected. +You may select the backend by specifying curve25519-dalek-ng as a direct dependency as follows:

+
[dependencies.elastic-elgamal]
+version = "..."
+default-features = false
+features = ["std", "curve25519-dalek-ng"]
+
+[dependencies.curve25519-dalek-ng]
+version = "4"
+features = ["u64_backend"] # or other backend
+
+

This feature is mutually exclusive with curve25519-dalek.

+

§serde

+

(off by default)

+

Enables Serialize / Deserialize +implementations for most types in the crate. +Group scalars, elements and wrapper key types are serialized to human-readable formats +(JSON, YAML, TOML, etc.) as strings that represent corresponding byte buffers using +base64-url encoding without padding. For binary formats, byte buffers are serialized directly.

+

For complex types (e.g., participant states from the sharing module), self-consistency +checks are not performed on deserialization. That is, deserialization of such types +should only be performed from a trusted source or in the presence of additional integrity +checks.

+

§Crate naming

+

“Elastic” refers to pluggable backends, configurable params for threshold encryption, +and the construction of zero-knowledge RingProofs (a proof consists of +a variable number of rings, each of which consists of a variable number of admissible values). +elastic_elgamal is also one of autogenerated Docker container names.

+

Re-exports§

  • pub use crate::proofs::RingProofBuilder;

Modules§

Structs§

  • Candidate for a VerifiableDecryption that is not yet verified. This presentation should be +used for decryption data retrieved from an untrusted source.
  • Ciphertext for ElGamal encryption.
  • ElGamal Ciphertext together with fully retained information about the encrypted value and +randomness used to create the ciphertext.
  • Zero-knowledge proof that an ElGamal ciphertext encrypts the same value as a Pedersen +commitment.
  • Lookup table for discrete logarithms.
  • Keypair for ElGamal encryption and related protocols, consisting of a SecretKey +and the matching PublicKey.
  • Zero-knowledge proof of equality of two discrete logarithms in different bases, +aka Chaum–Pedersen protocol.
  • RangeDecomposition together with values precached for creating and/or verifying +RangeProofs in a certain Group.
  • Zero-knowledge proof of possession of one or more secret scalars.
  • Public key for ElGamal encryption and related protocols.
  • Decomposition of an integer range 0..n into one or more sub-ranges. Decomposing the range +allows constructing RangeProofs with size / computational complexity O(log n).
  • Zero-knowledge proof that an ElGamal ciphertext encrypts a value into a certain range 0..n.
  • Zero-knowledge proof that the one or more encrypted values is each in the a priori known set of +admissible values. (Admissible values may differ among encrypted values.)
  • Secret key for ElGamal encryption and related protocols. This is a thin wrapper around +the Group scalar.
  • Zero-knowledge proof that an ElGamal-encrypted value is equal to a sum of squares +of one or more other ElGamal-encrypted values.
  • Verifiable decryption for a certain Ciphertext in the ElGamal encryption scheme. +Usable both for standalone proofs and in threshold encryption.

Enums§

\ No newline at end of file diff --git a/elastic_elgamal/keys/enum.PublicKeyConversionError.html b/elastic_elgamal/keys/enum.PublicKeyConversionError.html new file mode 100644 index 0000000..d12b31a --- /dev/null +++ b/elastic_elgamal/keys/enum.PublicKeyConversionError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../elastic_elgamal/enum.PublicKeyConversionError.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/keys/struct.Keypair.html b/elastic_elgamal/keys/struct.Keypair.html new file mode 100644 index 0000000..f3f3bc2 --- /dev/null +++ b/elastic_elgamal/keys/struct.Keypair.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../elastic_elgamal/struct.Keypair.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/keys/struct.PublicKey.html b/elastic_elgamal/keys/struct.PublicKey.html new file mode 100644 index 0000000..abb97a7 --- /dev/null +++ b/elastic_elgamal/keys/struct.PublicKey.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../elastic_elgamal/struct.PublicKey.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/keys/struct.SecretKey.html b/elastic_elgamal/keys/struct.SecretKey.html new file mode 100644 index 0000000..6589e65 --- /dev/null +++ b/elastic_elgamal/keys/struct.SecretKey.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../elastic_elgamal/struct.SecretKey.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/proofs/commitment/struct.CommitmentEquivalenceProof.html b/elastic_elgamal/proofs/commitment/struct.CommitmentEquivalenceProof.html new file mode 100644 index 0000000..aa27c71 --- /dev/null +++ b/elastic_elgamal/proofs/commitment/struct.CommitmentEquivalenceProof.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/struct.CommitmentEquivalenceProof.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/proofs/enum.VerificationError.html b/elastic_elgamal/proofs/enum.VerificationError.html new file mode 100644 index 0000000..b3e309c --- /dev/null +++ b/elastic_elgamal/proofs/enum.VerificationError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../elastic_elgamal/enum.VerificationError.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/proofs/log_equality/struct.LogEqualityProof.html b/elastic_elgamal/proofs/log_equality/struct.LogEqualityProof.html new file mode 100644 index 0000000..7f34473 --- /dev/null +++ b/elastic_elgamal/proofs/log_equality/struct.LogEqualityProof.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/struct.LogEqualityProof.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/proofs/mul/struct.SumOfSquaresProof.html b/elastic_elgamal/proofs/mul/struct.SumOfSquaresProof.html new file mode 100644 index 0000000..3b2e9a3 --- /dev/null +++ b/elastic_elgamal/proofs/mul/struct.SumOfSquaresProof.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/struct.SumOfSquaresProof.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/proofs/possession/struct.ProofOfPossession.html b/elastic_elgamal/proofs/possession/struct.ProofOfPossession.html new file mode 100644 index 0000000..7247e79 --- /dev/null +++ b/elastic_elgamal/proofs/possession/struct.ProofOfPossession.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/struct.ProofOfPossession.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/proofs/range/struct.PreparedRange.html b/elastic_elgamal/proofs/range/struct.PreparedRange.html new file mode 100644 index 0000000..399270c --- /dev/null +++ b/elastic_elgamal/proofs/range/struct.PreparedRange.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/struct.PreparedRange.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/proofs/range/struct.RangeDecomposition.html b/elastic_elgamal/proofs/range/struct.RangeDecomposition.html new file mode 100644 index 0000000..cb7e4b4 --- /dev/null +++ b/elastic_elgamal/proofs/range/struct.RangeDecomposition.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/struct.RangeDecomposition.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/proofs/range/struct.RangeProof.html b/elastic_elgamal/proofs/range/struct.RangeProof.html new file mode 100644 index 0000000..d8f3a90 --- /dev/null +++ b/elastic_elgamal/proofs/range/struct.RangeProof.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/struct.RangeProof.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/proofs/ring/struct.RingProof.html b/elastic_elgamal/proofs/ring/struct.RingProof.html new file mode 100644 index 0000000..64f8db7 --- /dev/null +++ b/elastic_elgamal/proofs/ring/struct.RingProof.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/struct.RingProof.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/sharing/enum.Error.html b/elastic_elgamal/sharing/enum.Error.html new file mode 100644 index 0000000..6c9bb59 --- /dev/null +++ b/elastic_elgamal/sharing/enum.Error.html @@ -0,0 +1,28 @@ +Error in elastic_elgamal::sharing - Rust +
#[non_exhaustive]
pub enum Error { + MalformedDealerPolynomial, + InvalidDealerProof(VerificationError), + InvalidSecret, + ParticipantCountMismatch, + MalformedParticipantKeys, +}
Expand description

Errors that can occur during the secret sharing protocol.

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

MalformedDealerPolynomial

Public polynomial received from the dealer is malformed.

+
§

InvalidDealerProof(VerificationError)

Proof of possession supplied with the dealer’s public polynomial is invalid.

+
§

InvalidSecret

Secret received from the dealer does not correspond to their commitment via +the public polynomial.

+
§

ParticipantCountMismatch

Number of participants specified in Params does not match the number +of provided public keys.

+
§

MalformedParticipantKeys

Participants’ public keys do not correspond to a single shared key.

+

Trait Implementations§

source§

impl Debug for Error

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for Error

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for Error

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
1.0.0 · source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

§

impl Freeze for Error

§

impl RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl UnwindSafe for Error

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/sharing/index.html b/elastic_elgamal/sharing/index.html new file mode 100644 index 0000000..b670f36 --- /dev/null +++ b/elastic_elgamal/sharing/index.html @@ -0,0 +1,98 @@ +elastic_elgamal::sharing - Rust +
Expand description

Feldman’s verifiable secret sharing (VSS) for ElGamal encryption.

+

Feldman’s VSS is an extension of Shamir’s secret sharing that provides a degree +of verifiability for the scheme participants and the public. As with other VSS schemes, +the goal is to securely distribute a secret among n participants so that the secret can +be recombined by any t (but not less) of these participants. Unlike distributed key +generation (DKG), VSS assumes a central authority (a dealer) generating the secret +and distributing its shares among participants.

+

§Construction

+

Inputs:

+
    +
  • Total number of participants n
  • +
  • Minimum number of participants necessary to restore secret t
  • +
  • Prime-order group with discrete log assumption with generator G
  • +
+

Assumptions:

+
    +
  • There is a secure broadcast among participants, which acts as a single source of truth +(e.g., a blockchain). The broadcast is synchronous w.r.t. the protocol steps (in practice, +this means that protocol steps take sufficiently long amount of time).
  • +
  • Secure synchronous P2P channels can be established between the dealer and participants.
  • +
  • The adversary is static (corrupts parties before protocol instantiation) and can corrupt +less than a half of participants (including the dealer).
  • +
+

Feldman’s VSS proceeds as follows:

+
    +
  1. The dealer generates a secret x (a scalar in a group with discrete log assumption). +Along with this scalar, the dealer generates t other scalars that are also kept secret. +These scalars form a secret polynomial of degree t: P(z) = x + x_1 * z + x_2 * z^2 + ….
  2. +
  3. The dealer publishes coefficients [x]G, [x_1]G, …, [x_t]G of the public polynomial +corresponding to P: Q(z) = [x]G + [z][x_1]G + [z^2][x_2]G + …. Here, [x]G is the shared +public key, and values Q(i) at i = 1..=n are public key shares of participants.
  4. +
  5. The dealer distributes secret key shares s_i = P(i) among participants i = 1..=n +via secure P2P channels. Each participant can verify share validity by calculating +[s_i]G ?= Q(i).
  6. +
+

If a participant receives an incorrect secret share, the participant broadcasts a complaint +against the dealer. The dealer responds by broadcasting the participant’s share. Either the +share is correct (in which case the complaining participant is at fault), or it is not +(in which case the dealer is at fault).

+

To improve auditability, the implemented version of VSS provides zero-knowledge proofs +of possession both for the dealer and participants. The dealer must broadcast the public +polynomial together with the proof; participants should broadcast proof of knowledge of +a secret share once they receive the share from the dealer.

+

§Distributed key generation

+

Distributed key generation (DKG) differs from the approach implemented in this module +in that there is no centralized dealer trusted by all participants. Instead, the participants +essentially run parallel secret sharing protocol instances where each participant +is a dealer in one of the instances. This approach is implemented +in the dkg module of this crate. Beware that it may not protect +from participants biasing the distribution of the shared public key, e.g. by aborting +the protocol; see Gennaro et al. for more details.

+

§Examples

+

Threshold encryption scheme requiring 2 of 3 participants.

+ +
let mut rng = thread_rng();
+let params = Params::new(3, 2);
+
+// Initialize the dealer.
+let dealer = Dealer::<Ristretto>::new(params, &mut rng);
+let (public_poly, poly_proof) = dealer.public_info();
+let key_set = PublicKeySet::new(params, public_poly, poly_proof)?;
+
+// Initialize participants based on secret shares provided by the dealer.
+let participants = (0..3)
+    .map(|i| ActiveParticipant::new(
+        key_set.clone(),
+        i,
+        dealer.secret_share_for_participant(i),
+    ))
+    .collect::<Result<Vec<_>, _>>()?;
+
+// At last, participants can decrypt messages!
+let encrypted_value = 5_u64;
+let enc = key_set.shared_key().encrypt(encrypted_value, &mut rng);
+let shares_with_proofs = participants
+    .iter()
+    .map(|p| p.decrypt_share(enc, &mut rng))
+    .take(2); // emulate the 3rd participant dropping off
+
+// Emulate share transfer via untrusted network.
+let dec_shares = shares_with_proofs
+    .enumerate()
+    .map(|(i, (share, proof))| {
+        let share = CandidateDecryption::from_bytes(&share.to_bytes()).unwrap();
+        key_set.verify_share(share, enc, i, &proof).unwrap()
+    });
+
+// Combine decryption shares.
+let combined = params.combine_shares(dec_shares.enumerate()).unwrap();
+// Use a lookup table to decrypt back to scalar.
+let lookup_table = DiscreteLogTable::<Ristretto>::new(0..10);
+let dec = combined.decrypt(enc, &lookup_table);
+assert_eq!(dec, Some(encrypted_value));
+

Structs§

Enums§

  • Errors that can occur during the secret sharing protocol.
\ No newline at end of file diff --git a/elastic_elgamal/sharing/key_set/struct.PublicKeySet.html b/elastic_elgamal/sharing/key_set/struct.PublicKeySet.html new file mode 100644 index 0000000..fc60c5b --- /dev/null +++ b/elastic_elgamal/sharing/key_set/struct.PublicKeySet.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/sharing/struct.PublicKeySet.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/sharing/participant/struct.ActiveParticipant.html b/elastic_elgamal/sharing/participant/struct.ActiveParticipant.html new file mode 100644 index 0000000..fb29eb4 --- /dev/null +++ b/elastic_elgamal/sharing/participant/struct.ActiveParticipant.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/sharing/struct.ActiveParticipant.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/sharing/participant/struct.Dealer.html b/elastic_elgamal/sharing/participant/struct.Dealer.html new file mode 100644 index 0000000..a369b70 --- /dev/null +++ b/elastic_elgamal/sharing/participant/struct.Dealer.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../elastic_elgamal/sharing/struct.Dealer.html...

+ + + \ No newline at end of file diff --git a/elastic_elgamal/sharing/sidebar-items.js b/elastic_elgamal/sharing/sidebar-items.js new file mode 100644 index 0000000..4348d54 --- /dev/null +++ b/elastic_elgamal/sharing/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Error"],"struct":["ActiveParticipant","Dealer","Params","PublicKeySet"]}; \ No newline at end of file diff --git a/elastic_elgamal/sharing/struct.ActiveParticipant.html b/elastic_elgamal/sharing/struct.ActiveParticipant.html new file mode 100644 index 0000000..22ef1a0 --- /dev/null +++ b/elastic_elgamal/sharing/struct.ActiveParticipant.html @@ -0,0 +1,60 @@ +ActiveParticipant in elastic_elgamal::sharing - Rust +
pub struct ActiveParticipant<G: Group> { /* private fields */ }
Expand description

Personalized state of a participant of a threshold ElGamal encryption scheme +once the participant receives the secret share from the Dealer. +At this point, the participant can produce VerifiableDecryptions.

+

Implementations§

source§

impl<G: Group> ActiveParticipant<G>

source

pub fn new( + key_set: PublicKeySet<G>, + index: usize, + secret_share: SecretKey<G> +) -> Result<Self, Error>

Creates the participant state based on readily available components.

+
§Errors
+

Returns an error if secret_share does not correspond to the participant’s public key share +in key_set.

+
§Panics
+

Panics if index is greater or equal than the number of participants in key_set.

+
source

pub fn key_set(&self) -> &PublicKeySet<G>

Returns the public key set for the threshold ElGamal encryption scheme this participant +is a part of.

+
source

pub fn index(&self) -> usize

Returns 0-based index of this participant.

+
source

pub fn secret_share(&self) -> &SecretKey<G>

Returns share of the secret key for this participant. This is secret information that +must not be shared.

+
source

pub fn public_key_share(&self) -> &PublicKey<G>

Returns share of the public key for this participant.

+
source

pub fn proof_of_possession<R: CryptoRng + RngCore>( + &self, + rng: &mut R +) -> ProofOfPossession<G>

Generates a ProofOfPossession of the participant’s +secret_share.

+
source

pub fn decrypt_share<R>( + &self, + ciphertext: Ciphertext<G>, + rng: &mut R +) -> (VerifiableDecryption<G>, LogEqualityProof<G>)
where + R: CryptoRng + RngCore,

Creates a VerifiableDecryption for the specified ciphertext together with a proof +of its validity. rng is used to generate the proof.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for ActiveParticipant<G>

source§

fn clone(&self) -> ActiveParticipant<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for ActiveParticipant<G>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for ActiveParticipant<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for ActiveParticipant<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<G> Freeze for ActiveParticipant<G>
where + <G as ElementOps>::Element: Freeze, + <G as ScalarOps>::Scalar: Freeze,

§

impl<G> RefUnwindSafe for ActiveParticipant<G>

§

impl<G> Send for ActiveParticipant<G>
where + <G as ElementOps>::Element: Send, + <G as ScalarOps>::Scalar: Send,

§

impl<G> Sync for ActiveParticipant<G>
where + <G as ElementOps>::Element: Sync, + <G as ScalarOps>::Scalar: Sync,

§

impl<G> Unpin for ActiveParticipant<G>
where + <G as ElementOps>::Element: Unpin, + <G as ScalarOps>::Scalar: Unpin,

§

impl<G> UnwindSafe for ActiveParticipant<G>
where + <G as ElementOps>::Element: UnwindSafe, + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/sharing/struct.Dealer.html b/elastic_elgamal/sharing/struct.Dealer.html new file mode 100644 index 0000000..dde47f0 --- /dev/null +++ b/elastic_elgamal/sharing/struct.Dealer.html @@ -0,0 +1,35 @@ +Dealer in elastic_elgamal::sharing - Rust +
pub struct Dealer<G: Group> { /* private fields */ }
Expand description

Dealer in a Feldman verifiable secret sharing scheme.

+

Implementations§

source§

impl<G: Group> Dealer<G>

source

pub fn new<R: CryptoRng + RngCore>(params: Params, rng: &mut R) -> Self

Instantiates a dealer.

+
source

pub fn public_info(&self) -> (Vec<G::Element>, &ProofOfPossession<G>)

Returns public participant information: dealer’s public polynomial and proof +of possession for the corresponding secret polynomial.

+
source

pub fn secret_share_for_participant(&self, index: usize) -> SecretKey<G>

Returns a secret share for a participant with the specified index.

+
§Panics
+

Panics if index is out of allowed bounds.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for Dealer<G>

source§

fn clone(&self) -> Dealer<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for Dealer<G>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for Dealer<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for Dealer<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<G> Freeze for Dealer<G>
where + <G as ScalarOps>::Scalar: Freeze,

§

impl<G> RefUnwindSafe for Dealer<G>

§

impl<G> Send for Dealer<G>
where + <G as ElementOps>::Element: Send, + <G as ScalarOps>::Scalar: Send,

§

impl<G> Sync for Dealer<G>
where + <G as ElementOps>::Element: Sync, + <G as ScalarOps>::Scalar: Sync,

§

impl<G> Unpin for Dealer<G>
where + <G as ElementOps>::Element: Unpin, + <G as ScalarOps>::Scalar: Unpin,

§

impl<G> UnwindSafe for Dealer<G>
where + <G as ElementOps>::Element: UnwindSafe, + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/sharing/struct.Params.html b/elastic_elgamal/sharing/struct.Params.html new file mode 100644 index 0000000..3e13503 --- /dev/null +++ b/elastic_elgamal/sharing/struct.Params.html @@ -0,0 +1,36 @@ +Params in elastic_elgamal::sharing - Rust +
pub struct Params {
+    pub shares: usize,
+    pub threshold: usize,
+}
Expand description

Parameters of a threshold ElGamal encryption scheme.

+

Fields§

§shares: usize

Total number of shares / participants.

+
§threshold: usize

Number of participants necessary to jointly restore the secret.

+

Implementations§

source§

impl Params

source

pub const fn new(shares: usize, threshold: usize) -> Self

Creates new parameters.

+
§Panics
+

Panics if shares is equal to zero or if threshold is not in 1..=shares.

+
source

pub fn combine_shares<G: Group>( + self, + shares: impl IntoIterator<Item = (usize, VerifiableDecryption<G>)> +) -> Option<VerifiableDecryption<G>>

Combines shares decrypting the specified ciphertext. The shares must be provided +together with the 0-based indexes of the participants they are coming from.

+

Returns the combined decryption, or None if the number of shares is insufficient.

+
§Panics
+

Panics if any index in shares exceeds the maximum participant’s index as per params.

+

Trait Implementations§

source§

impl Clone for Params

source§

fn clone(&self) -> Params

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Params

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de> Deserialize<'de> for Params

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl PartialEq for Params

source§

fn eq(&self, other: &Params) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Serialize for Params

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl Copy for Params

source§

impl Eq for Params

source§

impl StructuralPartialEq for Params

Auto Trait Implementations§

§

impl Freeze for Params

§

impl RefUnwindSafe for Params

§

impl Send for Params

§

impl Sync for Params

§

impl Unpin for Params

§

impl UnwindSafe for Params

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/sharing/struct.PublicKeySet.html b/elastic_elgamal/sharing/struct.PublicKeySet.html new file mode 100644 index 0000000..68f8f80 --- /dev/null +++ b/elastic_elgamal/sharing/struct.PublicKeySet.html @@ -0,0 +1,67 @@ +PublicKeySet in elastic_elgamal::sharing - Rust +
pub struct PublicKeySet<G: Group> { /* private fields */ }
Expand description

Full public information about the participants of a threshold ElGamal encryption scheme +after all participants’ commitments are collected.

+

Implementations§

source§

impl<G: Group> PublicKeySet<G>

source

pub fn new( + params: Params, + public_polynomial: Vec<G::Element>, + proof_of_possession: &ProofOfPossession<G> +) -> Result<Self, Error>

Creates an instance based on information provided by the Dealer.

+
§Errors
+

Returns an error if the information provided by the dealer is malformed.

+
source

pub fn from_participants( + params: Params, + participant_keys: Vec<PublicKey<G>> +) -> Result<Self, Error>

Creates a key set from the parameters and public keys of all participants.

+
§Errors
+

Returns an error if the number of keys in participant_keys does not match the number +of participants in params, or if participant_keys are inconsistent (do not correspond +to a single shared key).

+
source

pub fn params(&self) -> Params

Returns parameters for this scheme.

+
source

pub fn shared_key(&self) -> &PublicKey<G>

Returns the shared public key used in this scheme.

+
source

pub fn participant_key(&self, index: usize) -> Option<&PublicKey<G>>

Returns the public key of a participant with the specified index. If index is +out of bounds, returns None.

+
source

pub fn participant_keys(&self) -> &[PublicKey<G>]

Returns the slice with all participants’ public keys.

+
source

pub fn verify_participant( + &self, + index: usize, + proof: &ProofOfPossession<G> +) -> Result<(), VerificationError>

Verifies a proof of possession of the participant’s secret key.

+

Proofs of possession for participants are not required for protocol correctness. +Still, they can be useful to attribute failures or just as an additional safety mechanism; +see the module docs for details.

+
§Panics
+

Panics if index does not correspond to a participant.

+
§Errors
+

Returns an error if the proof does not verify.

+
source

pub fn verify_share( + &self, + candidate_share: CandidateDecryption<G>, + ciphertext: Ciphertext<G>, + index: usize, + proof: &LogEqualityProof<G> +) -> Result<VerifiableDecryption<G>, VerificationError>

Verifies a candidate decryption share for ciphertext provided by a participant +with the specified index.

+
§Errors
+

Returns an error if the proof does not verify.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for PublicKeySet<G>

source§

fn clone(&self) -> PublicKeySet<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for PublicKeySet<G>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for PublicKeySet<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for PublicKeySet<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<G> Freeze for PublicKeySet<G>
where + <G as ElementOps>::Element: Freeze,

§

impl<G> RefUnwindSafe for PublicKeySet<G>
where + <G as ElementOps>::Element: RefUnwindSafe,

§

impl<G> Send for PublicKeySet<G>
where + <G as ElementOps>::Element: Send,

§

impl<G> Sync for PublicKeySet<G>
where + <G as ElementOps>::Element: Sync,

§

impl<G> Unpin for PublicKeySet<G>
where + <G as ElementOps>::Element: Unpin,

§

impl<G> UnwindSafe for PublicKeySet<G>
where + <G as ElementOps>::Element: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/sidebar-items.js b/elastic_elgamal/sidebar-items.js new file mode 100644 index 0000000..4f9597c --- /dev/null +++ b/elastic_elgamal/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["PublicKeyConversionError","VerificationError"],"mod":["app","dkg","group","sharing"],"struct":["CandidateDecryption","Ciphertext","CiphertextWithValue","CommitmentEquivalenceProof","DiscreteLogTable","Keypair","LogEqualityProof","PreparedRange","ProofOfPossession","PublicKey","RangeDecomposition","RangeProof","RingProof","SecretKey","SumOfSquaresProof","VerifiableDecryption"]}; \ No newline at end of file diff --git a/elastic_elgamal/struct.CandidateDecryption.html b/elastic_elgamal/struct.CandidateDecryption.html new file mode 100644 index 0000000..e3a2677 --- /dev/null +++ b/elastic_elgamal/struct.CandidateDecryption.html @@ -0,0 +1,44 @@ +CandidateDecryption in elastic_elgamal - Rust +
pub struct CandidateDecryption<G: Group> { /* private fields */ }
Expand description

Candidate for a VerifiableDecryption that is not yet verified. This presentation should be +used for decryption data retrieved from an untrusted source.

+

Decryption data can be verified using Self::verify(). The threshold encryption scheme +implemented in the sharing module has its own verification procedure +in PublicKeySet.

+

§Examples

+

See VerifiableDecryption for an example of usage.

+

Implementations§

source§

impl<G: Group> CandidateDecryption<G>

source

pub fn from_bytes(bytes: &[u8]) -> Option<Self>

Deserializes decryption data from bytes. Returns None if the data is malformed.

+
source

pub fn verify( + self, + ciphertext: Ciphertext<G>, + key: &PublicKey<G>, + proof: &LogEqualityProof<G>, + transcript: &mut Transcript +) -> Result<VerifiableDecryption<G>, VerificationError>

Verifies this as decryption for ciphertext under key using the provided +zero-knowledge proof.

+
§Errors
+

Returns an error if proof does not verify.

+
source

pub fn into_unchecked(self) -> VerifiableDecryption<G>

Converts this candidate decryption into a VerifiableDecryption +without verifying it. +This is only semantically correct if the data was verified in some other way.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for CandidateDecryption<G>

source§

fn clone(&self) -> CandidateDecryption<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for CandidateDecryption<G>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for CandidateDecryption<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> From<VerifiableDecryption<G>> for CandidateDecryption<G>

source§

fn from(decryption: VerifiableDecryption<G>) -> Self

Converts to this type from the input type.
source§

impl<G: Group> Serialize for CandidateDecryption<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl<G: Copy + Group> Copy for CandidateDecryption<G>

Auto Trait Implementations§

§

impl<G> Freeze for CandidateDecryption<G>
where + <G as ElementOps>::Element: Freeze,

§

impl<G> RefUnwindSafe for CandidateDecryption<G>
where + <G as ElementOps>::Element: RefUnwindSafe,

§

impl<G> Send for CandidateDecryption<G>
where + <G as ElementOps>::Element: Send,

§

impl<G> Sync for CandidateDecryption<G>
where + <G as ElementOps>::Element: Sync,

§

impl<G> Unpin for CandidateDecryption<G>
where + <G as ElementOps>::Element: Unpin,

§

impl<G> UnwindSafe for CandidateDecryption<G>
where + <G as ElementOps>::Element: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/struct.Ciphertext.html b/elastic_elgamal/struct.Ciphertext.html new file mode 100644 index 0000000..1408090 --- /dev/null +++ b/elastic_elgamal/struct.Ciphertext.html @@ -0,0 +1,85 @@ +Ciphertext in elastic_elgamal - Rust +
pub struct Ciphertext<G: Group> { /* private fields */ }
Expand description

Ciphertext for ElGamal encryption.

+

A ciphertext consists of 2 group elements: the random element R and a blinded encrypted +value B. If the ciphertext encrypts integer value v, it holds that

+
R = [r]G;
+B = [v]G + [r]K = [v]G + [k]R;
+
+

where:

+
    +
  • G is the conventional group generator
  • +
  • r is a random scalar selected by the encrypting party
  • +
  • K and k are the recipient’s public and private keys, respectively.
  • +
+

Ciphertexts are partially homomorphic: they can be added together or multiplied by a scalar +value.

+

§Examples

+

Basic usage and arithmetic for ciphertexts:

+ +
// Generate a keypair for the ciphertext receiver.
+let mut rng = thread_rng();
+let receiver = Keypair::<Ristretto>::generate(&mut rng);
+// Create a couple of ciphertexts.
+let mut enc = receiver.public().encrypt(2_u64, &mut rng);
+enc += receiver.public().encrypt(3_u64, &mut rng) * 4;
+// Check that the ciphertext decrypts to 2 + 3 * 4 = 14.
+let lookup_table = DiscreteLogTable::new(0..20);
+let decrypted = receiver.secret().decrypt(enc, &lookup_table);
+assert_eq!(decrypted, Some(14));
+

Creating a ciphertext of a boolean value together with a proof:

+ +
// Generate a keypair for the ciphertext receiver.
+let mut rng = thread_rng();
+let receiver = Keypair::<Ristretto>::generate(&mut rng);
+// Create and verify a boolean encryption.
+let (enc, proof) =
+    receiver.public().encrypt_bool(false, &mut rng);
+receiver.public().verify_bool(enc, &proof)?;
+

Creating a ciphertext of an integer value together with a range proof:

+ +
// Generate the ciphertext receiver.
+let mut rng = thread_rng();
+let receiver = Keypair::<Ristretto>::generate(&mut rng);
+// Find the optimal range decomposition for our range
+// and specialize it for the Ristretto group.
+let range = RangeDecomposition::optimal(100).into();
+
+let (ciphertext, proof) = receiver
+    .public()
+    .encrypt_range(&range, 42, &mut rng);
+
+// Check that the the proof verifies.
+receiver.public().verify_range(&range, ciphertext, &proof)?;
+

Implementations§

source§

impl<G: Group> Ciphertext<G>

source

pub fn zero() -> Self

Represents encryption of zero value without the blinding factor.

+
source

pub fn non_blinded<T>(value: T) -> Self
where + G::Scalar: From<T>,

Creates a non-blinded encryption of the specified scalar value, i.e., (O, [value]G) +where O is identity and G is the conventional group generator.

+
source

pub fn random_element(&self) -> &G::Element

Returns a reference to the random element.

+
source

pub fn blinded_element(&self) -> &G::Element

Returns a reference to the blinded element.

+
source

pub fn to_bytes(self) -> Vec<u8>

Serializes this ciphertext as two group elements (the random element, +then the blinded value).

+

Trait Implementations§

source§

impl<G: Group> Add for Ciphertext<G>

§

type Output = Ciphertext<G>

The resulting type after applying the + operator.
source§

fn add(self, rhs: Self) -> Self

Performs the + operation. Read more
source§

impl<G: Group> AddAssign for Ciphertext<G>

source§

fn add_assign(&mut self, rhs: Self)

Performs the += operation. Read more
source§

impl<G: Clone + Group> Clone for Ciphertext<G>
where + G::Element: Clone,

source§

fn clone(&self) -> Ciphertext<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Group> Debug for Ciphertext<G>

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for Ciphertext<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group, V: Zeroize> From<CiphertextWithValue<G, V>> for Ciphertext<G>

source§

fn from(ciphertext: CiphertextWithValue<G, V>) -> Self

Converts to this type from the input type.
source§

impl<G: Group> Mul<&<G as ScalarOps>::Scalar> for Ciphertext<G>

§

type Output = Ciphertext<G>

The resulting type after applying the * operator.
source§

fn mul(self, rhs: &G::Scalar) -> Self

Performs the * operation. Read more
source§

impl<G: Group> Mul<u64> for Ciphertext<G>

§

type Output = Ciphertext<G>

The resulting type after applying the * operator.
source§

fn mul(self, rhs: u64) -> Self

Performs the * operation. Read more
source§

impl<G: Group> Neg for Ciphertext<G>

§

type Output = Ciphertext<G>

The resulting type after applying the - operator.
source§

fn neg(self) -> Self::Output

Performs the unary - operation. Read more
source§

impl<G: Group> Serialize for Ciphertext<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl<G: Group> Sub for Ciphertext<G>

§

type Output = Ciphertext<G>

The resulting type after applying the - operator.
source§

fn sub(self, rhs: Self) -> Self

Performs the - operation. Read more
source§

impl<G: Group> SubAssign for Ciphertext<G>

source§

fn sub_assign(&mut self, rhs: Self)

Performs the -= operation. Read more
source§

impl<G: Copy + Group> Copy for Ciphertext<G>
where + G::Element: Copy,

Auto Trait Implementations§

§

impl<G> Freeze for Ciphertext<G>
where + <G as ElementOps>::Element: Freeze,

§

impl<G> RefUnwindSafe for Ciphertext<G>
where + <G as ElementOps>::Element: RefUnwindSafe,

§

impl<G> Send for Ciphertext<G>
where + <G as ElementOps>::Element: Send,

§

impl<G> Sync for Ciphertext<G>
where + <G as ElementOps>::Element: Sync,

§

impl<G> Unpin for Ciphertext<G>
where + <G as ElementOps>::Element: Unpin,

§

impl<G> UnwindSafe for Ciphertext<G>
where + <G as ElementOps>::Element: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

§

impl<T, Rhs, Output> GroupOps<Rhs, Output> for T
where + T: Add<Rhs, Output = Output> + Sub<Rhs, Output = Output> + AddAssign<Rhs> + SubAssign<Rhs>,

\ No newline at end of file diff --git a/elastic_elgamal/struct.CiphertextWithValue.html b/elastic_elgamal/struct.CiphertextWithValue.html new file mode 100644 index 0000000..4ef1cb1 --- /dev/null +++ b/elastic_elgamal/struct.CiphertextWithValue.html @@ -0,0 +1,48 @@ +CiphertextWithValue in elastic_elgamal - Rust +
pub struct CiphertextWithValue<G: Group, V: Zeroize = <G as ScalarOps>::Scalar> { /* private fields */ }
Expand description

ElGamal Ciphertext together with fully retained information about the encrypted value and +randomness used to create the ciphertext.

+

This type can be used to produce certain kinds of proofs, such as +SumOfSquaresProof.

+

Implementations§

source§

impl<G: Group, V> CiphertextWithValue<G, V>
where + V: Copy + Zeroize, + G::Scalar: From<V>,

source

pub fn new<R: CryptoRng + RngCore>( + value: V, + receiver: &PublicKey<G>, + rng: &mut R +) -> Self

Encrypts a value for the specified receiver.

+

This is a lower-level operation compared to PublicKey::encrypt() and should be used +if the resulting ciphertext is necessary to produce proofs.

+
source

pub fn generalize(self) -> CiphertextWithValue<G>

Converts the enclosed value into a scalar.

+
source§

impl<G: Group, V> CiphertextWithValue<G, V>
where + V: Zeroize, + G::Scalar: From<V>,

source

pub fn inner(&self) -> &Ciphertext<G>

Returns a reference to the contained Ciphertext.

+

Trait Implementations§

source§

impl<G: Debug + Group, V: Debug + Zeroize> Debug for CiphertextWithValue<G, V>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<G: Group, V: Zeroize> From<CiphertextWithValue<G, V>> for Ciphertext<G>

source§

fn from(ciphertext: CiphertextWithValue<G, V>) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl<G, V> Freeze for CiphertextWithValue<G, V>
where + V: Freeze, + <G as ElementOps>::Element: Freeze, + <G as ScalarOps>::Scalar: Freeze,

§

impl<G, V> RefUnwindSafe for CiphertextWithValue<G, V>

§

impl<G, V> Send for CiphertextWithValue<G, V>
where + V: Send, + <G as ElementOps>::Element: Send, + <G as ScalarOps>::Scalar: Send,

§

impl<G, V> Sync for CiphertextWithValue<G, V>
where + V: Sync, + <G as ElementOps>::Element: Sync, + <G as ScalarOps>::Scalar: Sync,

§

impl<G, V> Unpin for CiphertextWithValue<G, V>
where + V: Unpin, + <G as ElementOps>::Element: Unpin, + <G as ScalarOps>::Scalar: Unpin,

§

impl<G, V> UnwindSafe for CiphertextWithValue<G, V>
where + V: UnwindSafe, + <G as ElementOps>::Element: UnwindSafe, + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/struct.CommitmentEquivalenceProof.html b/elastic_elgamal/struct.CommitmentEquivalenceProof.html new file mode 100644 index 0000000..c7cd01c --- /dev/null +++ b/elastic_elgamal/struct.CommitmentEquivalenceProof.html @@ -0,0 +1,107 @@ +CommitmentEquivalenceProof in elastic_elgamal - Rust +
pub struct CommitmentEquivalenceProof<G: Group> { /* private fields */ }
Expand description

Zero-knowledge proof that an ElGamal ciphertext encrypts the same value as a Pedersen +commitment.

+

This proof can be used to switch from frameworks applicable to ElGamal ciphertexts, to ones +applicable to Pedersen commitments (e.g., Bulletproofs for range proofs).

+

§Construction

+

We want to prove in zero knowledge the knowledge of scalars r_e, v, r_c such as

+
R = [r_e]G; B = [v]G + [r_e]K;
+// (R, B) is ElGamal ciphertext of `v` for public key `K`
+C = [v]G + [r_c]H;
+// C is Pedersen commitment to `v`
+
+

Here, we assume that the conventional group generator G is shared between encryption and +commitment protocols.

+

An interactive version of the proof can be built as a sigma protocol:

+
    +
  1. Commitment. The prover generates 3 random scalars e_r, e_v and e_c and commits +to them via E_r = [e_r]G, E_b = [e_v]G + [e_r]K, and E_c = [e_v]G + [e_c]H.
  2. +
  3. Challenge. The verifier sends to the prover random scalar c.
  4. +
  5. Response. The prover computes the following scalars and sends them to the verifier.
  6. +
+
s_r = e_r + c * r_e;
+s_v = e_v + c * v;
+s_c = e_c + c * r_c;
+
+

The verification equations are

+
[s_r]G ?= E_r + [c]R;
+[s_v]G + [s_r]K ?= E_b + [c]B;
+[s_v]G + [s_c]H ?= E_c + [c]C;
+
+

A non-interactive version of the proof is obtained by applying Fiat–Shamir transform. +As with other proofs, it is more efficient to represent a proof as the challenge +and responses (i.e., 4 scalars in total).

+

§Examples

+
let blinding_base = // Blinding base for Pedersen commitments
+                    // (e.g., from Bulletproofs)
+let mut rng = thread_rng();
+let (receiver, _) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+
+// Create an ElGamal ciphertext of `value` for `receiver`.
+let value = 424242_u64;
+let ciphertext = CiphertextWithValue::new(value, &receiver, &mut rng)
+    .generalize();
+// Create a blinding factor for the Pedersen commitment of the same value.
+let blinding = SecretKey::generate(&mut rng);
+let (proof, commitment) = CommitmentEquivalenceProof::new(
+    &ciphertext,
+    &receiver,
+    &blinding,
+    blinding_base,
+    &mut Transcript::new(b"custom_proof"),
+    &mut rng,
+);
+// Use `commitment` and `blinding` in other proofs...
+
+proof.verify(
+    &ciphertext.into(),
+    &receiver,
+    commitment,
+    blinding_base,
+    &mut Transcript::new(b"custom_proof"),
+)?;
+

Implementations§

source§

impl<G: Group> CommitmentEquivalenceProof<G>

source

pub fn new<R: RngCore + CryptoRng>( + ciphertext: &CiphertextWithValue<G>, + receiver: &PublicKey<G>, + commitment_blinding: &SecretKey<G>, + commitment_blinding_base: G::Element, + transcript: &mut Transcript, + rng: &mut R +) -> (Self, G::Element)

Creates a proof based on the ciphertext for receiver and commitment_blinding +with commitment_blinding_base for a Pedersen commitment. (The latter two args +correspond to r_c and H in the Construction section, respectively.)

+
§Return value
+

Returns a proof together with the Pedersen commitment.

+
source

pub fn verify( + &self, + ciphertext: &Ciphertext<G>, + receiver: &PublicKey<G>, + commitment: G::Element, + commitment_blinding_base: G::Element, + transcript: &mut Transcript +) -> Result<(), VerificationError>

§Errors
+

Returns an error if this proof does not verify.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for CommitmentEquivalenceProof<G>
where + G::Scalar: Clone,

source§

fn clone(&self) -> CommitmentEquivalenceProof<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for CommitmentEquivalenceProof<G>
where + G::Scalar: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for CommitmentEquivalenceProof<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for CommitmentEquivalenceProof<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/struct.DiscreteLogTable.html b/elastic_elgamal/struct.DiscreteLogTable.html new file mode 100644 index 0000000..ba98d0a --- /dev/null +++ b/elastic_elgamal/struct.DiscreteLogTable.html @@ -0,0 +1,47 @@ +DiscreteLogTable in elastic_elgamal - Rust +
pub struct DiscreteLogTable<G: Group> { /* private fields */ }
Expand description

Lookup table for discrete logarithms.

+

For Ciphertexts to be partially homomorphic, the encrypted values must be +group scalars linearly mapped to group elements: x -> [x]G, where G is the group +generator. After decryption it is necessary to map the decrypted group element back to a scalar +(i.e., get its discrete logarithm with base G). Because of discrete logarithm assumption, +this task is computationally infeasible in the general case; however, if the possible range +of encrypted values is small, it is possible to “cheat” by precomputing mapping [x]G -> x +for all allowed x ahead of time. This is exactly what DiscreteLogTable does.

+

§Examples

+
let mut rng = thread_rng();
+let receiver = Keypair::<Ristretto>::generate(&mut rng);
+let ciphertexts = (0_u64..16)
+    .map(|i| receiver.public().encrypt(i, &mut rng));
+// Assume that we know that the plaintext is in range 0..16,
+// e.g., via a zero-knowledge proof.
+let lookup_table = DiscreteLogTable::new(0..16);
+// Then, we can use the lookup table to decrypt values.
+// A single table may be shared for multiple decryption operations
+// (i.e., it may be constructed ahead of time).
+for (i, enc) in ciphertexts.enumerate() {
+    assert_eq!(
+        receiver.secret().decrypt(enc, &lookup_table),
+        Some(i as u64)
+    );
+}
+

Implementations§

source§

impl<G: Group> DiscreteLogTable<G>

source

pub fn new(values: impl IntoIterator<Item = u64>) -> Self

Creates a lookup table for the specified values.

+
source

pub fn get(&self, decrypted_element: &G::Element) -> Option<u64>

Gets the discrete log of decrypted_element, or None if it is not present among values +stored in this table.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for DiscreteLogTable<G>

source§

fn clone(&self) -> DiscreteLogTable<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for DiscreteLogTable<G>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<G> Freeze for DiscreteLogTable<G>

§

impl<G> RefUnwindSafe for DiscreteLogTable<G>
where + G: RefUnwindSafe,

§

impl<G> Send for DiscreteLogTable<G>
where + G: Send,

§

impl<G> Sync for DiscreteLogTable<G>
where + G: Sync,

§

impl<G> Unpin for DiscreteLogTable<G>
where + G: Unpin,

§

impl<G> UnwindSafe for DiscreteLogTable<G>
where + G: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/struct.Keypair.html b/elastic_elgamal/struct.Keypair.html new file mode 100644 index 0000000..2c50726 --- /dev/null +++ b/elastic_elgamal/struct.Keypair.html @@ -0,0 +1,35 @@ +Keypair in elastic_elgamal - Rust +
pub struct Keypair<G: Group> { /* private fields */ }
Expand description

Keypair for ElGamal encryption and related protocols, consisting of a SecretKey +and the matching PublicKey.

+

Implementations§

source§

impl<G: Group> Keypair<G>

source

pub fn generate<R: RngCore + CryptoRng>(rng: &mut R) -> Self

Generates a random keypair.

+
source

pub fn public(&self) -> &PublicKey<G>

Returns the public part of this keypair.

+
source

pub fn secret(&self) -> &SecretKey<G>

Returns the secret part of this keypair.

+
source

pub fn into_tuple(self) -> (PublicKey<G>, SecretKey<G>)

Returns public and secret keys comprising this keypair.

+

Trait Implementations§

source§

impl<G: Group> Clone for Keypair<G>

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Group> Debug for Keypair<G>

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for Keypair<G>

source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where + D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> From<SecretKey<G>> for Keypair<G>

source§

fn from(secret: SecretKey<G>) -> Self

Converts to this type from the input type.
source§

impl<G: Group> Mul<&<G as ScalarOps>::Scalar> for Keypair<G>

§

type Output = Keypair<G>

The resulting type after applying the * operator.
source§

fn mul(self, k: &G::Scalar) -> Self

Performs the * operation. Read more
source§

impl<G: Group> Serialize for Keypair<G>

source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where + S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<G> Freeze for Keypair<G>
where + <G as ElementOps>::Element: Freeze, + <G as ScalarOps>::Scalar: Freeze,

§

impl<G> RefUnwindSafe for Keypair<G>

§

impl<G> Send for Keypair<G>
where + <G as ElementOps>::Element: Send, + <G as ScalarOps>::Scalar: Send,

§

impl<G> Sync for Keypair<G>
where + <G as ElementOps>::Element: Sync, + <G as ScalarOps>::Scalar: Sync,

§

impl<G> Unpin for Keypair<G>
where + <G as ElementOps>::Element: Unpin, + <G as ScalarOps>::Scalar: Unpin,

§

impl<G> UnwindSafe for Keypair<G>
where + <G as ElementOps>::Element: UnwindSafe, + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/struct.LogEqualityProof.html b/elastic_elgamal/struct.LogEqualityProof.html new file mode 100644 index 0000000..ea48eb4 --- /dev/null +++ b/elastic_elgamal/struct.LogEqualityProof.html @@ -0,0 +1,119 @@ +LogEqualityProof in elastic_elgamal - Rust +
pub struct LogEqualityProof<G: Group> { /* private fields */ }
Expand description

Zero-knowledge proof of equality of two discrete logarithms in different bases, +aka Chaum–Pedersen protocol.

+

§Construction

+

This proof is a result of the Fiat–Shamir transform applied to a standard +ZKP of equality of the two discrete logs in different bases.

+
    +
  • Public parameters of the proof are the two bases G and K in a prime-order group +in which discrete log problem is believed to be hard.
  • +
  • Prover and verifier both know group elements R and B, which presumably have +the same discrete log in bases G and K respectively.
  • +
  • Prover additionally knows the discrete log in question: r = dlog_G(R) = dlog_K(B).
  • +
+

The interactive proof is specified as a sigma protocol (see, e.g., this course) +as follows:

+
    +
  1. Commitment: The prover generates random scalar x. The prover sends to the verifier +X_G = [x]G and X_K = [x]K.
  2. +
  3. Challenge: The verifier sends to the prover random scalar c.
  4. +
  5. Response: The prover computes scalar s = x + cr and sends it to the verifier.
  6. +
+

Verification equations are:

+
[s]G ?= X_G + [c]R;
+[s]K ?= X_K + [c]B.
+
+

In the non-interactive version of the proof, challenge c is derived from hash(M), +where hash() is a cryptographically secure hash function, and M is an optional message +verified together with the proof (cf. public-key digital signatures). If M is set, we +use a proof as a signature of knowledge. This allows to tie the proof to the context, +so it cannot be (re)used in other contexts.

+

To reduce the size of the proof, we use the trick underpinning ring signature constructions. +Namely, we represent the proof as (c, s); during verification, we restore X_G, X_K +from the original verification equations above.

+

§Implementation details

+
    +
  • The proof is serialized as 2 scalars: (c, s).
  • +
  • Proof generation is constant-time. Verification is not constant-time.
  • +
  • Challenge c is derived using Transcript API.
  • +
+

§Examples

+
let mut rng = thread_rng();
+let (log_base, _) =
+    Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+let (power_g, discrete_log) =
+    Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+let power_k = log_base.as_element() * discrete_log.expose_scalar();
+
+let proof = LogEqualityProof::new(
+    &log_base,
+    &discrete_log,
+    (power_g.as_element(), power_k),
+    &mut Transcript::new(b"custom_proof"),
+    &mut rng,
+);
+proof.verify(
+    &log_base,
+    (power_g.as_element(), power_k),
+    &mut Transcript::new(b"custom_proof"),
+)?;
+

Implementations§

source§

impl<G: Group> LogEqualityProof<G>

source

pub fn new<R: CryptoRng + RngCore>( + log_base: &PublicKey<G>, + secret: &SecretKey<G>, + powers: (G::Element, G::Element), + transcript: &mut Transcript, + rng: &mut R +) -> Self

Creates a new proof.

+
§Parameters
+
    +
  • log_base is the second discrete log base (K in the notation above). The first +log base is always the Group generator.
  • +
  • secret is the discrete log (r in the notation above).
  • +
  • powers are [r]G and [r]K, respectively. It is not checked whether r +is a discrete log of these powers; if this is not the case, the constructed proof +will not verify.
  • +
+
source

pub fn verify( + &self, + log_base: &PublicKey<G>, + powers: (G::Element, G::Element), + transcript: &mut Transcript +) -> Result<(), VerificationError>

Verifies this proof.

+
§Parameters
+
    +
  • log_base is the second discrete log base (K in the notation above). The first +log base is always the Group generator.
  • +
  • powers are group elements presumably equal to [r]G and [r]K respectively, +where r is a secret scalar.
  • +
+
§Errors
+

Returns an error if this proof does not verify.

+
source

pub fn to_bytes(self) -> Vec<u8>

Serializes this proof into bytes. As described above, +the is serialized as 2 scalars: (c, s), i.e., challenge and response.

+
source

pub fn from_bytes(bytes: &[u8]) -> Option<Self>

Attempts to parse the proof from bytes. Returns None if bytes do not represent +a well-formed proof.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for LogEqualityProof<G>
where + G::Scalar: Clone,

source§

fn clone(&self) -> LogEqualityProof<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for LogEqualityProof<G>
where + G::Scalar: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for LogEqualityProof<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for LogEqualityProof<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl<G: Copy + Group> Copy for LogEqualityProof<G>
where + G::Scalar: Copy,

Auto Trait Implementations§

§

impl<G> Freeze for LogEqualityProof<G>
where + <G as ScalarOps>::Scalar: Freeze,

§

impl<G> RefUnwindSafe for LogEqualityProof<G>
where + <G as ScalarOps>::Scalar: RefUnwindSafe,

§

impl<G> Send for LogEqualityProof<G>
where + <G as ScalarOps>::Scalar: Send,

§

impl<G> Sync for LogEqualityProof<G>
where + <G as ScalarOps>::Scalar: Sync,

§

impl<G> Unpin for LogEqualityProof<G>
where + <G as ScalarOps>::Scalar: Unpin,

§

impl<G> UnwindSafe for LogEqualityProof<G>
where + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/struct.PreparedRange.html b/elastic_elgamal/struct.PreparedRange.html new file mode 100644 index 0000000..146bb7d --- /dev/null +++ b/elastic_elgamal/struct.PreparedRange.html @@ -0,0 +1,24 @@ +PreparedRange in elastic_elgamal - Rust +
pub struct PreparedRange<G: Group> { /* private fields */ }
Expand description

RangeDecomposition together with values precached for creating and/or verifying +RangeProofs in a certain Group.

+

Implementations§

source§

impl<G: Group> PreparedRange<G>

source

pub fn decomposition(&self) -> &RangeDecomposition

Returns a reference to the contained decomposition.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for PreparedRange<G>
where + G::Element: Clone,

source§

fn clone(&self) -> PreparedRange<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for PreparedRange<G>
where + G::Element: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<G: Group> From<RangeDecomposition> for PreparedRange<G>

source§

fn from(decomposition: RangeDecomposition) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl<G> Freeze for PreparedRange<G>

§

impl<G> RefUnwindSafe for PreparedRange<G>
where + <G as ElementOps>::Element: RefUnwindSafe,

§

impl<G> Send for PreparedRange<G>
where + <G as ElementOps>::Element: Send,

§

impl<G> Sync for PreparedRange<G>
where + <G as ElementOps>::Element: Sync,

§

impl<G> Unpin for PreparedRange<G>
where + <G as ElementOps>::Element: Unpin,

§

impl<G> UnwindSafe for PreparedRange<G>
where + <G as ElementOps>::Element: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/struct.ProofOfPossession.html b/elastic_elgamal/struct.ProofOfPossession.html new file mode 100644 index 0000000..8f147c9 --- /dev/null +++ b/elastic_elgamal/struct.ProofOfPossession.html @@ -0,0 +1,77 @@ +ProofOfPossession in elastic_elgamal - Rust +
pub struct ProofOfPossession<G: Group> { /* private fields */ }
Expand description

Zero-knowledge proof of possession of one or more secret scalars.

+

§Construction

+

The proof is a generalization of the standard Schnorr protocol for proving knowledge +of a discrete log. The difference with the combination of several concurrent Schnorr +protocol instances is that the challenge is shared among all instances (which yields a +~2x proof size reduction).

+

§Implementation notes

+
    +
  • Proof generation is constant-time. Verification is not constant-time.
  • +
+

§Examples

+
let mut rng = thread_rng();
+let keypairs: Vec<_> =
+    (0..5).map(|_| Keypair::<Ristretto>::generate(&mut rng)).collect();
+
+// Prove possession of the generated key pairs.
+let proof = ProofOfPossession::new(
+    &keypairs,
+    &mut Transcript::new(b"custom_proof"),
+    &mut rng,
+);
+proof.verify(
+    keypairs.iter().map(Keypair::public),
+    &mut Transcript::new(b"custom_proof"),
+)?;
+
+// If we change the context of the `Transcript`, the proof will not verify.
+assert!(proof
+    .verify(
+        keypairs.iter().map(Keypair::public),
+        &mut Transcript::new(b"other_proof"),
+    )
+    .is_err());
+// Likewise if the public keys are reordered.
+assert!(proof
+    .verify(
+        keypairs.iter().rev().map(Keypair::public),
+        &mut Transcript::new(b"custom_proof"),
+    )
+    .is_err());
+

Implementations§

source§

impl<G: Group> ProofOfPossession<G>

source

pub fn new<R: CryptoRng + RngCore>( + keypairs: &[Keypair<G>], + transcript: &mut Transcript, + rng: &mut R +) -> Self

Creates a proof of possession with the specified keypairs.

+
source

pub fn verify<'a>( + &self, + public_keys: impl Iterator<Item = &'a PublicKey<G>> + Clone, + transcript: &mut Transcript +) -> Result<(), VerificationError>

Verifies this proof against the provided public_keys.

+
§Errors
+

Returns an error if this proof does not verify.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for ProofOfPossession<G>
where + G::Scalar: Clone,

source§

fn clone(&self) -> ProofOfPossession<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for ProofOfPossession<G>
where + G::Scalar: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for ProofOfPossession<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for ProofOfPossession<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<G> Freeze for ProofOfPossession<G>
where + <G as ScalarOps>::Scalar: Freeze,

§

impl<G> RefUnwindSafe for ProofOfPossession<G>
where + <G as ScalarOps>::Scalar: RefUnwindSafe,

§

impl<G> Send for ProofOfPossession<G>
where + <G as ScalarOps>::Scalar: Send,

§

impl<G> Sync for ProofOfPossession<G>
where + <G as ScalarOps>::Scalar: Sync,

§

impl<G> Unpin for ProofOfPossession<G>
where + <G as ScalarOps>::Scalar: Unpin,

§

impl<G> UnwindSafe for ProofOfPossession<G>
where + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/struct.PublicKey.html b/elastic_elgamal/struct.PublicKey.html new file mode 100644 index 0000000..08f36ef --- /dev/null +++ b/elastic_elgamal/struct.PublicKey.html @@ -0,0 +1,98 @@ +PublicKey in elastic_elgamal - Rust +
pub struct PublicKey<G: Group> { /* private fields */ }
Expand description

Public key for ElGamal encryption and related protocols.

+

§Implementation details

+

We store both the original bytes (which are used in zero-knowledge proofs) +and its decompression into a Group element. +This increases the memory footprint, but speeds up generating / verifying proofs.

+

Implementations§

source§

impl<G: Group> PublicKey<G>

source

pub fn encrypt<T, R: CryptoRng + RngCore>( + &self, + value: T, + rng: &mut R +) -> Ciphertext<G>
where + G::Scalar: From<T>,

Encrypts a value for this key.

+
source

pub fn encrypt_element<R: CryptoRng + RngCore>( + &self, + value: G::Element, + rng: &mut R +) -> Ciphertext<G>

Encrypts a group element.

+
source

pub fn encrypt_zero<R>( + &self, + rng: &mut R +) -> (Ciphertext<G>, LogEqualityProof<G>)
where + R: CryptoRng + RngCore,

Encrypts zero value and provides a zero-knowledge proof of encryption correctness.

+
source

pub fn verify_zero( + &self, + ciphertext: Ciphertext<G>, + proof: &LogEqualityProof<G> +) -> Result<(), VerificationError>

Verifies that this is an encryption of a zero value.

+
§Errors
+

Returns an error if the proof does not verify.

+
source

pub fn encrypt_bool<R: CryptoRng + RngCore>( + &self, + value: bool, + rng: &mut R +) -> (Ciphertext<G>, RingProof<G>)

Encrypts a boolean value (0 or 1) and provides a zero-knowledge proof of encryption +correctness.

+
§Examples
+

See Ciphertext docs for an example of usage.

+
source

pub fn verify_bool( + &self, + ciphertext: Ciphertext<G>, + proof: &RingProof<G> +) -> Result<(), VerificationError>

Verifies a proof of encryption correctness of a boolean value, which was presumably +obtained via Self::encrypt_bool().

+
§Errors
+

Returns an error if the proof does not verify.

+
§Examples
+

See Ciphertext docs for an example of usage.

+
source

pub fn encrypt_range<R: CryptoRng + RngCore>( + &self, + range: &PreparedRange<G>, + value: u64, + rng: &mut R +) -> (Ciphertext<G>, RangeProof<G>)

Encrypts value and provides a zero-knowledge proof that it lies in the specified range.

+
§Panics
+

Panics if value is out of range.

+
§Examples
+

See Ciphertext docs for an example of usage.

+
source

pub fn verify_range( + &self, + range: &PreparedRange<G>, + ciphertext: Ciphertext<G>, + proof: &RangeProof<G> +) -> Result<(), VerificationError>

Verifies proof that ciphertext encrypts a value lying in range.

+

The proof should be created with a call to Self::encrypt_range() with the same +PreparedRange; otherwise, the proof will not verify.

+
§Errors
+

Returns an error if the proof does not verify.

+
source§

impl<G: Group> PublicKey<G>

source

pub fn from_bytes(bytes: &[u8]) -> Result<Self, PublicKeyConversionError>

Deserializes a public key from bytes.

+
§Errors
+

Returns an error if bytes has invalid byte size, does not represent a valid group element +or represents the group identity.

+
source

pub fn as_bytes(&self) -> &[u8]

Returns bytes representing the group element corresponding to this key.

+
source

pub fn as_element(&self) -> G::Element

Returns the group element equivalent to this key.

+

Trait Implementations§

source§

impl<G: Group> Add for PublicKey<G>

§

type Output = PublicKey<G>

The resulting type after applying the + operator.
source§

fn add(self, rhs: Self) -> Self

Performs the + operation. Read more
source§

impl<G: Group> Clone for PublicKey<G>

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Group> Debug for PublicKey<G>

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for PublicKey<G>

source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where + D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> From<&SecretKey<G>> for PublicKey<G>

source§

fn from(secret_key: &SecretKey<G>) -> Self

Converts to this type from the input type.
source§

impl<G: Group> Mul<&<G as ScalarOps>::Scalar> for PublicKey<G>

§

type Output = PublicKey<G>

The resulting type after applying the * operator.
source§

fn mul(self, k: &G::Scalar) -> Self

Performs the * operation. Read more
source§

impl<G: Group> Mul<u64> for PublicKey<G>

§

type Output = PublicKey<G>

The resulting type after applying the * operator.
source§

fn mul(self, k: u64) -> Self

Performs the * operation. Read more
source§

impl<G> PartialEq for PublicKey<G>
where + G: Group,

source§

fn eq(&self, other: &Self) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl<G: Group> Serialize for PublicKey<G>

source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where + S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<G> Freeze for PublicKey<G>
where + <G as ElementOps>::Element: Freeze,

§

impl<G> RefUnwindSafe for PublicKey<G>
where + <G as ElementOps>::Element: RefUnwindSafe,

§

impl<G> Send for PublicKey<G>
where + <G as ElementOps>::Element: Send,

§

impl<G> Sync for PublicKey<G>
where + <G as ElementOps>::Element: Sync,

§

impl<G> Unpin for PublicKey<G>
where + <G as ElementOps>::Element: Unpin,

§

impl<G> UnwindSafe for PublicKey<G>
where + <G as ElementOps>::Element: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/struct.RangeDecomposition.html b/elastic_elgamal/struct.RangeDecomposition.html new file mode 100644 index 0000000..e3628f6 --- /dev/null +++ b/elastic_elgamal/struct.RangeDecomposition.html @@ -0,0 +1,90 @@ +RangeDecomposition in elastic_elgamal - Rust +
pub struct RangeDecomposition { /* private fields */ }
Expand description

Decomposition of an integer range 0..n into one or more sub-ranges. Decomposing the range +allows constructing RangeProofs with size / computational complexity O(log n).

+

§Construction

+

To build efficient RangeProofs, we need to be able to decompose any value x in 0..n +into several components, with each of them being in a smaller predefined range; once we +have such a decomposition, we can build a RingProof around it. +To build a decomposition, we use the following generic construction:

+
0..n = 0..t_0 + k_0 * (0..t_1 + k_1 * (0..t_2 + …)),
+
+

where t_i and k_i are integers greater than 1. If x is a value in 0..n, +it is decomposed as

+
x = x_0 + k_0 * x_1 + k_0 * k_1 * x_2 + …; x_i in 0..t_i.
+
+

For a decomposition to be valid (i.e., to represent any value in 0..n and no other values), +the following statements are sufficient:

+
    +
  • t_i >= k_i (no gaps in values)
  • +
  • n = t_0 + k_0 * (t_1 - 1 + k_1 * …) (exact upper bound).
  • +
+

The size of a RingProof is the sum of upper range bounds t_i (= number of responses) + 1 +(the common challenge). Additionally, we need a ciphertext per each sub-range 0..t_i +(i.e., for each ring in RingProof). In practice, proof size is logarithmic:

+
+ + + + + + + + +
Upper bound nOptimal decompositionProof size
50..56 scalars
100..5 * 2 + 0..28 scalars, 2 elements
200..5 * 4 + 0..410 scalars, 2 elements
50(0..5 * 5 + 0..5) * 2 + 0..213 scalars, 4 elements
64(0..4 * 4 + 0..4) * 4 + 0..413 scalars, 4 elements
100(0..5 * 5 + 0..5) * 4 + 0..415 scalars, 4 elements
256((0..4 * 4 + 0..4) * 4 + 0..4) * 4 + 0..417 scalars, 6 elements
1000((0..8 * 5 + 0..5) * 5 + 0..5) * 5 + 0..524 scalars, 6 elements
+
+

(We do not count one of sub-range ciphertexts since it can be restored from the other +sub-range ciphertexts and the original ciphertext of the value.)

+

§Notes

+
    +
  • Decomposition of some values may be non-unique, but this is fine for our purposes.
  • +
  • Encoding of a value in a certain base is a partial case, with all t_i and k_i equal +to the base. It only works for n being a power of the base.
  • +
  • Other types of decompositions may perform better, but this one has a couple +of nice properties. It works for all ns, and the optimal decomposition can be found +recursively.
  • +
  • If we know how to create / verify range proofs for 0..N, proofs for all ranges 0..n, +n < N can be constructed as a combination of 2 proofs: a proof that encrypted value x +is in 0..N and that n - 1 - x is in 0..N. (The latter is proved for a ciphertext +obtained by the matching linear transform of the original ciphertext of x.) +This does not help us if proofs for 0..N are constructed using RingProofs, +but allows estimating for which n a Bulletproofs-like construction would become +more efficient despite using 2 proofs. If we take N = 2^(2^P) +and the “vanilla” Bulletproof length 2 * P + 9, this threshold is around n = 2000.
  • +
+

§Examples

+

Finding out the optimal decomposition for a certain range:

+ +
let range = RangeDecomposition::optimal(42);
+assert_eq!(range.to_string(), "6 * 0..7 + 0..6");
+assert_eq!(range.proof_size(), 16); // 14 scalars, 2 elements
+
+let range = RangeDecomposition::optimal(100);
+assert_eq!(range.to_string(), "20 * 0..5 + 4 * 0..5 + 0..4");
+assert_eq!(range.proof_size(), 19); // 15 scalars, 4 elements
+

See RangeProof docs for an end-to-end example of usage.

+

Implementations§

source§

impl RangeDecomposition

source

pub fn optimal(upper_bound: u64) -> Self

Finds an optimal decomposition of the range with the given upper_bound in terms +of space of the range proof.

+

Empirically, this method has sublinear complexity, but may work slowly for large values +of upper_bound (say, larger than 1 billion).

+
§Panics
+

Panics if upper_bound is less than 2.

+
source

pub fn upper_bound(&self) -> u64

Returns the exclusive upper bound of the range presentable by this decomposition.

+
source

pub fn proof_size(&self) -> u64

Returns the size of RangeProofs using this decomposition, measured as a total number +of scalars and group elements in the proof. Computational complexity of creating and +verifying proofs is also linear w.r.t. this number.

+

Trait Implementations§

source§

impl Clone for RangeDecomposition

source§

fn clone(&self) -> RangeDecomposition

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for RangeDecomposition

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for RangeDecomposition

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<G: Group> From<RangeDecomposition> for PreparedRange<G>

source§

fn from(decomposition: RangeDecomposition) -> Self

Converts to this type from the input type.
source§

impl PartialEq for RangeDecomposition

source§

fn eq(&self, other: &RangeDecomposition) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl StructuralPartialEq for RangeDecomposition

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

\ No newline at end of file diff --git a/elastic_elgamal/struct.RangeProof.html b/elastic_elgamal/struct.RangeProof.html new file mode 100644 index 0000000..e48e467 --- /dev/null +++ b/elastic_elgamal/struct.RangeProof.html @@ -0,0 +1,114 @@ +RangeProof in elastic_elgamal - Rust +
pub struct RangeProof<G: Group> { /* private fields */ }
Expand description

Zero-knowledge proof that an ElGamal ciphertext encrypts a value into a certain range 0..n.

+

§Construction

+

To make the proof more compact – O(log n) in terms of size and proving / verification +complexity – we use the same trick as for Pedersen commitments (used, e.g., for confidential +transaction amounts in Elements):

+
    +
  1. Represent the encrypted value x as x = x_0 + k_0 * x_1 + k_0 * k_1 * x_2 + …, +where 0 <= x_i < t_i is the decomposition of x as per the RangeDecomposition, +0..t_0 + k_0 * (0..t_1 + …). +As an example, if n is a power of 2, one can choose a decomposition as +the base-2 presentation of x, i.e., t_i = k_i = 2 for all i. +For brevity, denote a multiplier of x_i in x decomposition as K_i, +K_i = k_0 * … * k_{i-1}; K_0 = 1 by extension.
  2. +
  3. Split the ciphertext: E = E_0 + E_1 + …, where E_i encrypts K_i * x_i.
  4. +
  5. Produce a RingProof that for all i the encrypted scalar for E_i +is among 0, K_i, …, K_i * (t_i - 1). The range proof consists of all E_i ciphertexts +and this RingProof.
  6. +
+

As with range proofs for Pedersen commitments, this construction is not optimal +in terms of space or proving / verification complexity for large ranges; +it is linear w.r.t. the bit length of the range. +(Constructions like Bulletproofs are logarithmic w.r.t. the bit length.) +Still, it can be useful for small ranges.

+

§Examples

+
// Generate the ciphertext receiver.
+let mut rng = thread_rng();
+let receiver = Keypair::<Ristretto>::generate(&mut rng);
+// Find the optimal range decomposition for our range
+// and specialize it for the Ristretto group.
+let range = RangeDecomposition::optimal(100).into();
+
+let (ciphertext, proof) = RangeProof::new(
+    receiver.public(),
+    &range,
+    55,
+    &mut Transcript::new(b"test_proof"),
+    &mut rng,
+);
+let ciphertext = Ciphertext::from(ciphertext);
+
+// Check that the ciphertext is valid
+let lookup = DiscreteLogTable::new(0..100);
+assert_eq!(receiver.secret().decrypt(ciphertext, &lookup), Some(55));
+// ...and that the proof verifies.
+proof.verify(
+    receiver.public(),
+    &range,
+    ciphertext,
+    &mut Transcript::new(b"test_proof"),
+)?;
+

Implementations§

source§

impl<G: Group> RangeProof<G>

source

pub fn new<R: RngCore + CryptoRng>( + receiver: &PublicKey<G>, + range: &PreparedRange<G>, + value: u64, + transcript: &mut Transcript, + rng: &mut R +) -> (CiphertextWithValue<G, u64>, Self)

Encrypts value for receiver and creates a zero-knowledge proof that the encrypted value +is in range.

+

This is a lower-level operation; see PublicKey::encrypt_range() for a higher-level +alternative.

+
§Panics
+

Panics if value is outside the range specified by range.

+
source

pub fn from_ciphertext<R: RngCore + CryptoRng>( + receiver: &PublicKey<G>, + range: &PreparedRange<G>, + ciphertext: &CiphertextWithValue<G, u64>, + transcript: &mut Transcript, + rng: &mut R +) -> Self

Creates a proof that a value in ciphertext is in the range.

+

The caller is responsible for providing a ciphertext encrypted for the receiver; +if the ciphertext is encrypted for another public key, the resulting proof will not verify.

+
§Panics
+

Panics if value is outside the range specified by range.

+
source

pub fn verify( + &self, + receiver: &PublicKey<G>, + range: &PreparedRange<G>, + ciphertext: Ciphertext<G>, + transcript: &mut Transcript +) -> Result<(), VerificationError>

Verifies this proof against ciphertext for receiver and the specified range.

+

This is a lower-level operation; see PublicKey::verify_range() for a higher-level +alternative.

+

For a proof to verify, all parameters must be identical to ones provided when creating +the proof. In particular, range must have the same decomposition.

+
§Errors
+

Returns an error if this proof does not verify.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for RangeProof<G>

source§

fn clone(&self) -> RangeProof<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for RangeProof<G>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for RangeProof<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for RangeProof<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<G> Freeze for RangeProof<G>
where + <G as ScalarOps>::Scalar: Freeze,

§

impl<G> RefUnwindSafe for RangeProof<G>

§

impl<G> Send for RangeProof<G>
where + <G as ElementOps>::Element: Send, + <G as ScalarOps>::Scalar: Send,

§

impl<G> Sync for RangeProof<G>
where + <G as ElementOps>::Element: Sync, + <G as ScalarOps>::Scalar: Sync,

§

impl<G> Unpin for RangeProof<G>
where + <G as ElementOps>::Element: Unpin, + <G as ScalarOps>::Scalar: Unpin,

§

impl<G> UnwindSafe for RangeProof<G>
where + <G as ElementOps>::Element: UnwindSafe, + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/struct.RingProof.html b/elastic_elgamal/struct.RingProof.html new file mode 100644 index 0000000..d1ae8cd --- /dev/null +++ b/elastic_elgamal/struct.RingProof.html @@ -0,0 +1,87 @@ +RingProof in elastic_elgamal - Rust +
pub struct RingProof<G: Group> { /* private fields */ }
Expand description

Zero-knowledge proof that the one or more encrypted values is each in the a priori known set of +admissible values. (Admissible values may differ among encrypted values.)

+

§Construction

+

In short, a proof is constructed almost identically to Borromean ring signatures by +Maxwell and Poelstra, with the only major difference being that we work on ElGamal ciphertexts +instead of group elements (= public keys).

+

A proof consists of one or more rings. Each ring proves than a certain +ElGamal ciphertext E = (R, B) for public key K in a group with generator G +encrypts one of distinct admissible values x_0, x_1, …, x_n. +K and G are shared among rings, admissible values are generally not. +Different rings may have different number of admissible values.

+

§Single ring

+

A ring is a challenge e_0 and a set of responses s_0, s_1, …, s_n, which +must satisfy the following verification procedure:

+

For each j in 0..=n, compute

+
R_G(j) = [s_j]G - [e_j]R;
+R_K(j) = [s_j]K - [e_j](B - [x_j]G);
+e_{j+1} = H(j, R_G(j), R_K(j));
+
+

Here, H is a cryptographic hash function. The ring is valid if e_0 = e_{n+1}.

+

This construction is almost identical to Abe–Ohkubo–Suzuki ring signatures, +with the only difference that two group elements are hashed on each iteration instead of one. +If admissible values consist of a single value, this protocol reduces to +LogEqualityProof / Chaum–Pedersen protocol.

+

As with “ordinary” ring signatures, constructing a ring is only feasible when knowing +additional trapdoor information. Namely, the prover must know

+
r = dlog_G(R) = dlog_K(B - [x_j]G)
+
+

for a certain j. (This discrete log r is the random scalar used in ElGamal encryption.) +With this info, the prover constructs the ring as follows:

+
    +
  1. Select random scalar x and compute R_G(j) = [x]G, R_K(j) = [x]K.
  2. +
  3. Compute e_{j+1}, … e_n, …, e_j (“wrapping” around e_0 = e_{n+1}) +as per verification formulas. s_* scalars are selected uniformly at random.
  4. +
  5. Compute s_j using the trapdoor information: s_j = x + e_j * r.
  6. +
+

§Multiple rings

+

Transformation to multiple rings is analogous to one in Borromean ring signatures. +Namely, challenge e_0 is shared among all rings and is computed by hashing +values of R_G and R_K with the maximum index for each of the rings.

+

§Applications

§Voting protocols

+

EncryptedChoice uses RingProof to prove that all encrypted +values are Boolean (0 or 1). Using a common challenge allows to reduce proof size by ~33%.

+

§Range proofs

+

See RangeProof.

+

§Implementation details

+
    +
  • The proof is serialized as the common challenge e_0 followed by s_i scalars for +all the rings.
  • +
  • Standalone proof generation and verification are not exposed in public crate APIs. +Rather, proofs are part of large protocols, such as PublicKey::encrypt_bool() / +PublicKey::verify_bool().
  • +
  • The context of the proof is set using Transcript APIs, which provides hash functions +in the protocol described above. Importantly, the proof itself commits to encrypted values +and ring indexes, but not to the admissible values across the rings. This must be taken +care of in a higher-level protocol, and this is the case for protocols exposed by the crate.
  • +
+

Implementations§

source§

impl<G: Group> RingProof<G>

source

pub fn to_bytes(&self) -> Vec<u8>

Serializes this proof into bytes. As described above, +the proof is serialized as the common challenge e_0 followed by response scalars s_* +corresponding successively to each admissible value in each ring.

+
source

pub fn from_bytes(bytes: &[u8]) -> Option<Self>

Attempts to deserialize a proof from bytes. Returns None if bytes do not represent +a well-formed proof.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for RingProof<G>
where + G::Scalar: Clone,

source§

fn clone(&self) -> RingProof<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for RingProof<G>
where + G::Scalar: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for RingProof<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for RingProof<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<G> Freeze for RingProof<G>
where + <G as ScalarOps>::Scalar: Freeze,

§

impl<G> RefUnwindSafe for RingProof<G>
where + <G as ScalarOps>::Scalar: RefUnwindSafe,

§

impl<G> Send for RingProof<G>
where + <G as ScalarOps>::Scalar: Send,

§

impl<G> Sync for RingProof<G>
where + <G as ScalarOps>::Scalar: Sync,

§

impl<G> Unpin for RingProof<G>
where + <G as ScalarOps>::Scalar: Unpin,

§

impl<G> UnwindSafe for RingProof<G>
where + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/struct.SecretKey.html b/elastic_elgamal/struct.SecretKey.html new file mode 100644 index 0000000..c81c912 --- /dev/null +++ b/elastic_elgamal/struct.SecretKey.html @@ -0,0 +1,42 @@ +SecretKey in elastic_elgamal - Rust +
pub struct SecretKey<G: Group>(/* private fields */);
Expand description

Secret key for ElGamal encryption and related protocols. This is a thin wrapper around +the Group scalar.

+

Implementations§

source§

impl<G: Group> SecretKey<G>

source

pub fn decrypt_to_element(&self, encrypted: Ciphertext<G>) -> G::Element

Decrypts the provided ciphertext and returns the produced group element.

+

As the ciphertext does not include a MAC or another way to assert integrity, +this operation cannot fail. If the ciphertext is not produced properly (e.g., it targets +another receiver), the returned group element will be garbage.

+
source

pub fn decrypt( + &self, + encrypted: Ciphertext<G>, + lookup_table: &DiscreteLogTable<G> +) -> Option<u64>

Decrypts the provided ciphertext and returns the original encrypted value.

+

lookup_table is used to find encrypted values based on the original decrypted +group element. That is, it must contain all valid plaintext values. If the value +is not in the table, this method will return None.

+
source§

impl<G: Group> SecretKey<G>

source

pub fn generate<R: CryptoRng + RngCore>(rng: &mut R) -> Self

Generates a random secret key.

+
source

pub fn from_bytes(bytes: &[u8]) -> Option<Self>

Deserializes a secret key from bytes. If bytes do not represent a valid scalar, +returns None.

+
source

pub fn expose_scalar(&self) -> &G::Scalar

Exposes the scalar equivalent to this key.

+

Trait Implementations§

source§

impl<G: Group> Add for SecretKey<G>

§

type Output = SecretKey<G>

The resulting type after applying the + operator.
source§

fn add(self, rhs: Self) -> Self

Performs the + operation. Read more
source§

impl<G: Group> AddAssign for SecretKey<G>

source§

fn add_assign(&mut self, rhs: Self)

Performs the += operation. Read more
source§

impl<G: Group> Clone for SecretKey<G>

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Group> Debug for SecretKey<G>

source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for SecretKey<G>

source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where + D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Drop for SecretKey<G>

source§

fn drop(&mut self)

Executes the destructor for this type. Read more
source§

impl<G: Group> From<&SecretKey<G>> for PublicKey<G>

source§

fn from(secret_key: &SecretKey<G>) -> Self

Converts to this type from the input type.
source§

impl<G: Group> From<SecretKey<G>> for Keypair<G>

source§

fn from(secret: SecretKey<G>) -> Self

Converts to this type from the input type.
source§

impl<G: Group> Mul<&<G as ScalarOps>::Scalar> for &SecretKey<G>

§

type Output = SecretKey<G>

The resulting type after applying the * operator.
source§

fn mul(self, k: &G::Scalar) -> SecretKey<G>

Performs the * operation. Read more
source§

impl<G: Group> Mul<&<G as ScalarOps>::Scalar> for SecretKey<G>

§

type Output = SecretKey<G>

The resulting type after applying the * operator.
source§

fn mul(self, k: &G::Scalar) -> Self

Performs the * operation. Read more
source§

impl<G: Group> Serialize for SecretKey<G>

source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where + S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl<G: Group> Sub for SecretKey<G>

§

type Output = SecretKey<G>

The resulting type after applying the - operator.
source§

fn sub(self, rhs: Self) -> Self

Performs the - operation. Read more
source§

impl<G: Group> SubAssign for SecretKey<G>

source§

fn sub_assign(&mut self, rhs: Self)

Performs the -= operation. Read more

Auto Trait Implementations§

§

impl<G> Freeze for SecretKey<G>
where + <G as ScalarOps>::Scalar: Freeze,

§

impl<G> RefUnwindSafe for SecretKey<G>
where + <G as ScalarOps>::Scalar: RefUnwindSafe,

§

impl<G> Send for SecretKey<G>
where + <G as ScalarOps>::Scalar: Send,

§

impl<G> Sync for SecretKey<G>
where + <G as ScalarOps>::Scalar: Sync,

§

impl<G> Unpin for SecretKey<G>
where + <G as ScalarOps>::Scalar: Unpin,

§

impl<G> UnwindSafe for SecretKey<G>
where + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

§

impl<T, Rhs, Output> GroupOps<Rhs, Output> for T
where + T: Add<Rhs, Output = Output> + Sub<Rhs, Output = Output> + AddAssign<Rhs> + SubAssign<Rhs>,

\ No newline at end of file diff --git a/elastic_elgamal/struct.SumOfSquaresProof.html b/elastic_elgamal/struct.SumOfSquaresProof.html new file mode 100644 index 0000000..095f43b --- /dev/null +++ b/elastic_elgamal/struct.SumOfSquaresProof.html @@ -0,0 +1,96 @@ +SumOfSquaresProof in elastic_elgamal - Rust +
pub struct SumOfSquaresProof<G: Group> { /* private fields */ }
Expand description

Zero-knowledge proof that an ElGamal-encrypted value is equal to a sum of squares +of one or more other ElGamal-encrypted values.

+

§Construction

+

Consider the case with a single sum element (i.e., proving that an encrypted value is +a square of another encrypted value). The prover wants to prove the knowledge of scalars

+
r_x, x, r_z:
+  R_x = [r_x]G, X = [x]G + [r_x]K;
+  R_z = [r_z]G, Z = [x^2]G + [r_z]K,
+
+

where

+
    +
  • G is the conventional generator of the considered prime-order group
  • +
  • K is a group element equivalent to the receiver’s public key
  • +
  • (R_x, X) and (R_z, Z) are ElGamal ciphertexts of values x and x^2, respectively.
  • +
+

Observe that

+
r'_z := r_z - x * r_x =>
+  R_z = [r'_z]G + [x]R_x; Z = [x]X + [r'_z]K.
+
+

and that proving the knowledge of (r_x, x, r'_z) is equivalent to the initial problem. +The new problem can be solved using a conventional sigma protocol:

+
    +
  1. Commitment. The prover generates random scalars e_r, e_x and e_z and commits +to them via E_r = [e_r]G, E_x = [e_x]G + [e_r]K, E_rz = [e_x]R_x + [e_z]G and +E_z = [e_x]X + [e_z]K.
  2. +
  3. Challenge. The verifier sends to the prover random scalar c.
  4. +
  5. Response. The prover computes the following scalars and sends them to the verifier.
  6. +
+
s_r = e_r + c * r_x;
+s_x = e_x + c * x;
+s_z = e_z + c * (r_z - x * r_x);
+
+

The verification equations are

+
[s_r]G ?= E_r + [c]R_x;
+[s_x]G + [s_r]K ?= E_x + [c]X;
+[s_x]R_x + [s_z]G ?= E_rz + [c]R_z;
+[s_x]X + [s_z]K ?= E_z + [c]Z.
+
+

The case with multiple squares is a straightforward generalization:

+
    +
  • e_r, E_r, e_x, E_x, s_r and s_x are independently defined for each +partial ciphertext in the same way as above.
  • +
  • Commitments E_rz and E_z sum over [e_x]R_x and [e_x]X for all ciphertexts, +respectively.
  • +
  • Response s_z similarly substitutes x * r_x with the corresponding sum.
  • +
+

A non-interactive version of the proof is obtained by applying Fiat–Shamir transform. +As with LogEqualityProof, it is more efficient to represent a proof as the challenge +and responses; in this case, the proof size is 2n + 2 scalars, where n is the number of +partial ciphertexts.

+

Implementations§

source§

impl<G: Group> SumOfSquaresProof<G>

source

pub fn new<'a, R: RngCore + CryptoRng>( + ciphertexts: impl Iterator<Item = &'a CiphertextWithValue<G>>, + sum_of_squares_ciphertext: &CiphertextWithValue<G>, + receiver: &PublicKey<G>, + transcript: &mut Transcript, + rng: &mut R +) -> Self

Creates a new proof that squares of values encrypted in ciphertexts for receiver sum up +to a value encrypted in sum_of_squares_ciphertext.

+

All provided ciphertexts must be encrypted for receiver; otherwise, the created proof +will not verify.

+
source

pub fn verify<'a>( + &self, + ciphertexts: impl Iterator<Item = &'a Ciphertext<G>> + Clone, + sum_of_squares_ciphertext: &Ciphertext<G>, + receiver: &PublicKey<G>, + transcript: &mut Transcript +) -> Result<(), VerificationError>

Verifies this proof against the provided partial ciphertexts and the ciphertext of the +sum of their squares. The order of partial ciphertexts must correspond to their order +when creating the proof.

+
§Errors
+

Returns an error if this proof does not verify.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for SumOfSquaresProof<G>
where + G::Scalar: Clone,

source§

fn clone(&self) -> SumOfSquaresProof<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for SumOfSquaresProof<G>
where + G::Scalar: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for SumOfSquaresProof<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> Serialize for SumOfSquaresProof<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<G> Freeze for SumOfSquaresProof<G>
where + <G as ScalarOps>::Scalar: Freeze,

§

impl<G> RefUnwindSafe for SumOfSquaresProof<G>
where + <G as ScalarOps>::Scalar: RefUnwindSafe,

§

impl<G> Send for SumOfSquaresProof<G>
where + <G as ScalarOps>::Scalar: Send,

§

impl<G> Sync for SumOfSquaresProof<G>
where + <G as ScalarOps>::Scalar: Sync,

§

impl<G> Unpin for SumOfSquaresProof<G>
where + <G as ScalarOps>::Scalar: Unpin,

§

impl<G> UnwindSafe for SumOfSquaresProof<G>
where + <G as ScalarOps>::Scalar: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/elastic_elgamal/struct.VerifiableDecryption.html b/elastic_elgamal/struct.VerifiableDecryption.html new file mode 100644 index 0000000..9752582 --- /dev/null +++ b/elastic_elgamal/struct.VerifiableDecryption.html @@ -0,0 +1,89 @@ +VerifiableDecryption in elastic_elgamal - Rust +
pub struct VerifiableDecryption<G: Group> { /* private fields */ }
Expand description

Verifiable decryption for a certain Ciphertext in the ElGamal encryption scheme. +Usable both for standalone proofs and in threshold encryption.

+

§Construction

+

Decryption is represented by a single group element – the result of combining +a SecretKey scalar x with the random element of the ciphertext R +(i.e., D = [x]R, the Diffie – Hellman construction). +This element can retrieved using Self::as_element() and applied to a ciphertext using +Self::decrypt() or Self::decrypt_to_element().

+

The decryption can be proven with the help of a standard LogEqualityProof. Indeed, +to prove the validity of decryption, it is sufficient to prove dlog_R(D) = dlog_G(K), +where G is the conventional group generator and K = [x]G is the public key for encryption.

+

§Examples

+

VerifiableDecryption can be used either within the threshold encryption scheme provided by +the sharing module, or independently (for example, if another approach +to secret sharing is used, or if the encryption key is not shared at all). +An example of standalone usage is outlined below:

+ +
let mut rng = thread_rng();
+let keys = Keypair::<Ristretto>::generate(&mut rng);
+// Suppose the `keys` holder wants to prove decryption
+// of the following ciphertext:
+let ciphertext = keys.public().encrypt(42_u64, &mut rng);
+let (decryption, proof) = VerifiableDecryption::new(
+    ciphertext,
+    &keys,
+    &mut Transcript::new(b"decryption"),
+    &mut rng,
+);
+
+// This proof can then be universally verified:
+let candidate_decryption = CandidateDecryption::from(decryption);
+let decryption = candidate_decryption.verify(
+    ciphertext,
+    keys.public(),
+    &proof,
+    &mut Transcript::new(b"decryption"),
+)?;
+assert_eq!(
+    decryption.decrypt(ciphertext, &DiscreteLogTable::new(0..50)),
+    Some(42)
+);
+

Implementations§

source§

impl<G: Group> VerifiableDecryption<G>

source

pub fn new<R: CryptoRng + RngCore>( + ciphertext: Ciphertext<G>, + keys: &Keypair<G>, + transcript: &mut Transcript, + rng: &mut R +) -> (Self, LogEqualityProof<G>)

Creates a decryption for the specified ciphertext under keys together with +a zero-knowledge proof of validity.

+

See CandidateDecryption::verify() for the verification counterpart.

+
source

pub fn as_element(&self) -> &G::Element

Returns the group element encapsulated in this decryption.

+
source

pub fn to_bytes(self) -> Vec<u8>

Serializes this decryption into bytes.

+
source

pub fn decrypt_to_element(&self, encrypted: Ciphertext<G>) -> G::Element

Decrypts the provided ciphertext and returns the produced group element.

+

As the ciphertext does not include a MAC or another way to assert integrity, +this operation cannot fail. If the ciphertext is not produced properly (e.g., it targets +another receiver), the returned group element will be garbage.

+
source

pub fn decrypt( + &self, + encrypted: Ciphertext<G>, + lookup_table: &DiscreteLogTable<G> +) -> Option<u64>

Decrypts the provided ciphertext and returns the original encrypted value.

+

lookup_table is used to find encrypted values based on the original decrypted +group element. That is, it must contain all valid plaintext values. If the value +is not in the table, this method will return None.

+

Trait Implementations§

source§

impl<G: Clone + Group> Clone for VerifiableDecryption<G>
where + G::Element: Clone,

source§

fn clone(&self) -> VerifiableDecryption<G>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<G: Debug + Group> Debug for VerifiableDecryption<G>
where + G::Element: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'de, G: Group> Deserialize<'de> for VerifiableDecryption<G>

source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where + __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
source§

impl<G: Group> From<VerifiableDecryption<G>> for CandidateDecryption<G>

source§

fn from(decryption: VerifiableDecryption<G>) -> Self

Converts to this type from the input type.
source§

impl<G: Group> Serialize for VerifiableDecryption<G>

source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where + __S: Serializer,

Serialize this value into the given Serde serializer. Read more
source§

impl<G: Copy + Group> Copy for VerifiableDecryption<G>
where + G::Element: Copy,

Auto Trait Implementations§

§

impl<G> Freeze for VerifiableDecryption<G>
where + <G as ElementOps>::Element: Freeze,

§

impl<G> RefUnwindSafe for VerifiableDecryption<G>
where + <G as ElementOps>::Element: RefUnwindSafe,

§

impl<G> Send for VerifiableDecryption<G>
where + <G as ElementOps>::Element: Send,

§

impl<G> Sync for VerifiableDecryption<G>
where + <G as ElementOps>::Element: Sync,

§

impl<G> Unpin for VerifiableDecryption<G>
where + <G as ElementOps>::Element: Unpin,

§

impl<G> UnwindSafe for VerifiableDecryption<G>
where + <G as ElementOps>::Element: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> DeserializeOwned for T
where + T: for<'de> Deserialize<'de>,

\ No newline at end of file diff --git a/help.html b/help.html new file mode 100644 index 0000000..7a8945b --- /dev/null +++ b/help.html @@ -0,0 +1,2 @@ +Help +

Rustdoc help

Back
\ No newline at end of file diff --git a/search-index.js b/search-index.js new file mode 100644 index 0000000..da87bdb --- /dev/null +++ b/search-index.js @@ -0,0 +1,5 @@ +var searchIndex = new Map(JSON.parse('[\ +["elastic_elgamal",{"doc":"ElGamal encryption and related cryptographic protocols …","t":"FPFFFFPPPFPFFFFGFFFEFFFGNNNNNCNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNCNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNCNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNCNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOFGPPFFPPRKFGFPFPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOPGPPPPFFFFFNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNFTRKFKFFTRKNNNNNNNNNNNNNNMNNNMNNNNNNNNNNNNNNMNNNMNNNNNMNNNNNNNMNNNNNNMNNNNNNNNNNNNMNNNMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNFFGPPPPFPFNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONONNNNNNNNNNNNNNNNNNNNNNNNNNN","n":["CandidateDecryption","ChallengeMismatch","Ciphertext","CiphertextWithValue","CommitmentEquivalenceProof","DiscreteLogTable","IdentityKey","InvalidByteSize","InvalidGroupElement","Keypair","LenMismatch","LogEqualityProof","PreparedRange","ProofOfPossession","PublicKey","PublicKeyConversionError","RangeDecomposition","RangeProof","RingProof","RingProofBuilder","SecretKey","SumOfSquaresProof","VerifiableDecryption","VerificationError","add","add","add","add_assign","add_assign","app","as_bytes","as_element","as_element","blinded_element","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","decomposition","decrypt","decrypt","decrypt_to_element","decrypt_to_element","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","deserialize","dkg","drop","encrypt","encrypt_bool","encrypt_element","encrypt_range","encrypt_zero","eq","eq","expose_scalar","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from_bytes","from_bytes","from_bytes","from_bytes","from_bytes","from_ciphertext","generalize","generate","generate","get","group","inner","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into","into_tuple","into_unchecked","mul","mul","mul","mul","mul","mul","mul","neg","new","new","new","new","new","new","new","new","non_blinded","optimal","proof_size","public","random_element","secret","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","serialize","sharing","sub","sub","sub_assign","sub_assign","to_bytes","to_bytes","to_bytes","to_bytes","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_string","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","upper_bound","verify","verify","verify","verify","verify","verify","verify_bool","verify_range","verify_zero","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","zero","actual","collection","expected","ChoiceParams","ChoiceVerificationError","CreditEquivalence","CreditRange","EncryptedChoice","MultiChoice","OptionsLenMismatch","OptionsLenMismatch","Proof","ProveSum","QuadraticVotingBallot","QuadraticVotingError","QuadraticVotingParams","Range","SingleChoice","Sum","Variant","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","choices_unchecked","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","credits","deserialize","deserialize","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","into","into","into","into","into","into","into","into","len","max_votes","multi","new","new","new","options_count","options_count","range_proof","receiver","receiver","serialize","serialize","set_max_votes","single","single","source","source","sum_proof","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","verify","verify","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","actual","expected","actual","error","expected","index","DuplicateShare","Error","InconsistentPublicShares","InvalidCommitment","InvalidSecret","MalformedParticipantProof","Opening","ParticipantCollectingCommitments","ParticipantCollectingPolynomials","ParticipantExchangingSecrets","PublicInfo","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","commitment","complete","deserialize","deserialize","deserialize","deserialize","deserialize","finish_commitment_phase","finish_polynomials_phase","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","index","index","index","insert_commitment","insert_public_polynomial","insert_secret_share","into","into","into","into","into","into","into_owned","missing_commitments","missing_public_polynomials","missing_shares","new","opening","params","params","params","polynomial","proof_of_possession","public_info","secret_share_for_participant","serialize","serialize","serialize","serialize","serialize","source","to_owned","to_owned","to_owned","to_owned","to_owned","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","vzip","vzip","vzip","vzip","vzip","vzip","Curve25519Subgroup","ELEMENT_SIZE","Element","ElementOps","Generic","Group","RandomBytesProvider","Ristretto","SCALAR_SIZE","Scalar","ScalarOps","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone_into","clone_into","clone_into","deserialize_element","deserialize_element","deserialize_element","deserialize_element","deserialize_scalar","deserialize_scalar","deserialize_scalar","deserialize_scalar","eq","eq","fill_bytes","fmt","fmt","fmt","fmt","from","from","from","from","generate_scalar","generate_scalar","generate_scalar","generate_scalar","generator","generator","generator","generator","hash","hash","identity","identity","identity","identity","into","into","into","into","invert_scalar","invert_scalar","invert_scalar","invert_scalar","invert_scalars","invert_scalars","invert_scalars","is_identity","is_identity","is_identity","is_identity","mul_generator","mul_generator","mul_generator","multi_mul","multi_mul","multi_mul","scalar_from_random_bytes","scalar_from_random_bytes","scalar_from_random_bytes","serialize_element","serialize_element","serialize_element","serialize_element","serialize_scalar","serialize_scalar","serialize_scalar","serialize_scalar","to_owned","to_owned","to_owned","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","vartime_double_mul_generator","vartime_double_mul_generator","vartime_double_mul_generator","vartime_mul_generator","vartime_mul_generator","vartime_mul_generator","vartime_multi_mul","vartime_multi_mul","vartime_multi_mul","vzip","vzip","vzip","vzip","ActiveParticipant","Dealer","Error","InvalidDealerProof","InvalidSecret","MalformedDealerPolynomial","MalformedParticipantKeys","Params","ParticipantCountMismatch","PublicKeySet","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","combine_shares","decrypt_share","deserialize","deserialize","deserialize","deserialize","eq","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from_participants","index","into","into","into","into","into","key_set","new","new","new","new","params","participant_key","participant_keys","proof_of_possession","public_info","public_key_share","secret_share","secret_share_for_participant","serialize","serialize","serialize","serialize","shared_key","shares","source","threshold","to_owned","to_owned","to_owned","to_owned","to_string","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","verify_participant","verify_share","vzip","vzip","vzip","vzip","vzip"],"q":[[0,"elastic_elgamal"],[350,"elastic_elgamal::VerificationError"],[353,"elastic_elgamal::app"],[489,"elastic_elgamal::app::ChoiceVerificationError"],[491,"elastic_elgamal::app::QuadraticVotingError"],[495,"elastic_elgamal::dkg"],[611,"elastic_elgamal::group"],[729,"elastic_elgamal::sharing"],[830,"core::clone"],[831,"core::option"],[832,"core::result"],[833,"serde::de"],[834,"rand_core"],[835,"rand_core"],[836,"core::fmt"],[837,"merlin::transcript"],[838,"core::marker"],[839,"core::iter::traits::collect"],[840,"core::iter::traits::iterator"],[841,"serde::ser"],[842,"alloc::vec"],[843,"alloc::string"],[844,"core::any"],[845,"core::error"],[846,"core::hash"],[847,"curve25519_dalek::scalar"]],"d":["Candidate for a VerifiableDecryption that is not yet …","Restored challenge scalar does not match the one provided …","Ciphertext for ElGamal encryption.","ElGamal Ciphertext together with fully retained …","Zero-knowledge proof that an ElGamal ciphertext encrypts …","Lookup table for discrete logarithms.","Underlying group element is the group identity.","Invalid size of the byte buffer.","Byte buffer has correct size, but does not represent a …","Keypair for ElGamal encryption and related protocols, …","A collection (e.g., number of responses in a RingProof) …","Zero-knowledge proof of equality of two discrete …","RangeDecomposition together with values precached for …","Zero-knowledge proof of possession of one or more secret …","Public key for ElGamal encryption and related protocols.","Errors that can occur when converting other types to …","Decomposition of an integer range 0..n into one or more …","Zero-knowledge proof that an ElGamal ciphertext encrypts a …","Zero-knowledge proof that the one or more encrypted values …","","Secret key for ElGamal encryption and related protocols. …","Zero-knowledge proof that an ElGamal-encrypted value is …","Verifiable decryption for a certain Ciphertext in the …","Error verifying base proofs, such as RingProof, …","","","","","","High-level applications for proofs defined in this crate.","Returns bytes representing the group element corresponding …","Returns the group element equivalent to this key.","Returns the group element encapsulated in this decryption.","Returns a reference to the blinded element.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns a reference to the contained decomposition.","Decrypts the provided ciphertext and returns the original …","Decrypts the provided ciphertext and returns the original …","Decrypts the provided ciphertext and returns the produced …","Decrypts the provided ciphertext and returns the produced …","","","","","","","","","","","","","Committed Pedersen’s distributed key generation (DKG).","","Encrypts a value for this key.","Encrypts a boolean value (0 or 1) and provides a …","Encrypts a group element.","Encrypts value and provides a zero-knowledge proof that it …","Encrypts zero value and provides a zero-knowledge proof of …","","","Exposes the scalar equivalent to this key.","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Deserializes a secret key from bytes. If bytes do not …","Deserializes a public key from bytes.","Deserializes decryption data from bytes. Returns None if …","Attempts to parse the proof from bytes. Returns None if …","Attempts to deserialize a proof from bytes. Returns None …","Creates a proof that a value in ciphertext is in the range.","Converts the enclosed value into a scalar.","Generates a random secret key.","Generates a random keypair.","Gets the discrete log of decrypted_element, or None if it …","Traits and implementations for prime-order groups in which …","Returns a reference to the contained Ciphertext.","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Returns public and secret keys comprising this keypair.","Converts this candidate decryption into a …","","","","","","","","","Creates a decryption for the specified ciphertext under …","Creates a lookup table for the specified values.","Encrypts a value for the specified receiver.","Creates a proof based on the ciphertext for receiver and …","Creates a new proof.","Creates a new proof that squares of values encrypted in …","Creates a proof of possession with the specified keypairs.","Encrypts value for receiver and creates a zero-knowledge …","Creates a non-blinded encryption of the specified scalar …","Finds an optimal decomposition of the range with the given …","Returns the size of RangeProofs using this decomposition, …","Returns the public part of this keypair.","Returns a reference to the random element.","Returns the secret part of this keypair.","","","","","","","","","","","","","Feldman’s verifiable secret sharing (VSS) for ElGamal …","","","","","Serializes this decryption into bytes.","Serializes this ciphertext as two group elements (the …","Serializes this proof into bytes. As described above, the …","Serializes this proof into bytes. As described above, the …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the exclusive upper bound of the range presentable …","Verifies this as decryption for ciphertext under key using …","Errors","Verifies this proof.","Verifies this proof against the provided partial …","Verifies this proof against the provided public_keys.","Verifies this proof against ciphertext for receiver and …","Verifies a proof of encryption correctness of a boolean …","Verifies proof that ciphertext encrypts a value lying in …","Verifies that this is an encryption of a zero value.","","","","","","","","","","","","","","","","","","","Represents encryption of zero value without the blinding …","Actual size of the collection.","Human-readable collection name, such as “public keys”.","Expected size of the collection.","Parameters of an EncryptedChoice polling.","Error verifying an EncryptedChoice.","Error verifying the proof of equivalence for credits.","Error verifying a RangeProof for credits.","Zero or more encrypted choices from n options (n >= 1) …","Multi-choice setup for EncryptedChoice, in which it can …","Mismatch between expected and actual number of options in …","Mismatch between expected and actual number of options in …","Produced / verified proofs.","Encapsulation of functionality for proving and verifying …","Encrypted ballot for quadratic voting together with …","Errors that can occur when verifying QuadraticVotingBallot…","Quadratic voting parameters prepared for a certain Group.","Error verifying EncryptedChoice::range_proof().","Single-choice setup for EncryptedChoice, in which it can …","Error verifying EncryptedChoice::sum_proof().","Error verifying a RangeProof for a vote for a particular …","","","","","","","","","","","","","","","","","Returns ciphertexts for all options without checking the …","","","","","","","","","","","","","Returns the number of credits per ballot.","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Returns the number of encrypted choices. This value is …","Returns the maximum number of votes per option.","Creates parameters for a multi-choice polling.","Creates an encrypted multi-choice.","Creates new parameters for the specified number of credits …","Creates a ballot based on the provided parameters and voter…","Returns the number of options in these parameters.","Returns the number of options.","Returns the range proof for the choice ciphertexts.","Returns the public key for which the EncryptedChoice are …","Returns the public key for which the QuadraticVotingBallot…","","","Sets the maximum number of votes per option.","Creates parameters for a single-choice polling.","Creates a new encrypted choice.","","","Returns the sum proof for the choice ciphertexts.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Verifies the zero-knowledge proofs in this choice and …","Verifies this ballot against the provided parameters.","","","","","","","","","Actual number of options.","Expected number of options.","Actual number of options.","Error that occurred during range proof verification.","Expected number of options.","Zero-based option index.","Secret share for this participant was already provided.","Errors that can occur during the distributed key …","Public shares obtained from accumulated public polynomial …","Provided commitment does not correspond to the party’s …","Secret received from the party does not correspond to …","Provided proof of possession or public polynomial is …","Opening for a hash commitment used in Pedersen’s …","Participant state during the first stage of the committed …","Participant state during the second stage of the committed …","Participant state during the third and final stage of the …","Public participant information in the distributed key …","","","","","","","","","","","","","","","","","","","","","","","Returns the commitment of participant’s share of the …","Completes the distributed key generation protocol …","","","","","","Proceeds to the next step of the DKG protocol, in which …","Proceeds to the next step of the DKG protocol, in which …","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns 0-based index of this participant.","Returns 0-based index of this participant.","Returns 0-based index of this participant.","Inserts a commitment from the participant with index …","Inserts public polynomial from participant with index …","Inserts a secret share from participant with index …","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Converts this information to the owned form.","Returns indices of parties whose commitments were not …","Returns the indices of parties whose public polynomials …","Returns indices of parties whose secret shares were not …","Instantiates a distributed key generation participant.","Opening for the participant’s key commitment.","Returns params of this threshold ElGamal encryption scheme.","Returns params of this threshold ElGamal encryption scheme.","Returns params of this threshold ElGamal encryption scheme.","Participant’s public polynomial.","Proof of possession for the secret polynomial that …","Returns public participant information: participant’s …","Returns the secret share for a participant with the …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Prime-order subgroup of Curve25519 without any transforms …","Byte size of a serialized Self::Element.","Element of the group. Arithmetic operations requested here …","Helper trait for Group that describes operations on group …","Generic Group implementation for elliptic curves defined …","Prime-order group in which the discrete log problem and …","Provides an arbitrary number of random bytes.","Ristretto transform of Curve25519, also known as …","Byte size of a serialized Self::Scalar.","Scalar type. As per Group contract, scalars must form a …","Helper trait for Group that describes operations on group …","","","","","","","","","","","","","","","Deserializes an element from buffer, which is guaranteed …","","","","Deserializes the scalar from buffer, which is guaranteed …","","","","","","Writes random bytes into the specified buffer. As follows …","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Generates a random scalar based on the provided CSPRNG. …","","","","Returns the agreed-upon generator of the group.","","","","","","Returns the identity of the group (aka point at infinity …","","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Inverts the scalar, which is guaranteed to be non-zero. …","","","","Inverts scalars in a batch. This operation does not need …","","","Checks if the specified element is the identity.","","","","Multiplies the provided scalar by ElementOps::generator(). …","","","Multiplies provided scalars by elements. This operation …","","","Generates a scalar from a source of random bytes. This …","","","Serializes element into the provided buffer, which is …","","","","Serializes the scalar into the provided buffer, which is …","","","","","","","","","","","","","","","","","","","Calculates k * k_element + r * G, where G is the group …","","","Multiplies the provided scalar by ElementOps::generator(). …","","","Multiplies provided scalars by elements. Unlike …","","","","","","","Personalized state of a participant of a threshold ElGamal …","Dealer in a Feldman verifiable secret sharing scheme.","Errors that can occur during the secret sharing protocol.","Proof of possession supplied with the dealer’s public …","Secret received from the dealer does not correspond to …","Public polynomial received from the dealer is malformed.","Participants’ public keys do not correspond to a single …","Parameters of a threshold ElGamal encryption scheme.","Number of participants specified in Params does not match …","Full public information about the participants of a …","","","","","","","","","","","","","","","","","","","Combines shares decrypting the specified ciphertext. The …","Creates a VerifiableDecryption for the specified ciphertext…","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Creates a key set from the parameters and public keys of …","Returns 0-based index of this participant.","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Returns the public key set for the threshold ElGamal …","Creates an instance based on information provided by the …","Instantiates a dealer.","Creates the participant state based on readily available …","Creates new parameters.","Returns parameters for this scheme.","Returns the public key of a participant with the specified …","Returns the slice with all participants’ public keys.","Generates a ProofOfPossession of the participant’s …","Returns public participant information: dealer’s public …","Returns share of the public key for this participant.","Returns share of the secret key for this participant. This …","Returns a secret share for a participant with the …","","","","","Returns the shared public key used in this scheme.","Total number of shares / participants.","","Number of participants necessary to jointly restore the …","","","","","","","","","","","","","","","","","","","","","Verifies a proof of possession of the participant’s …","Verifies a candidate decryption share for ciphertext …","","","","",""],"i":[0,35,0,0,0,0,13,13,13,0,35,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,1,4,0,3,3,8,4,1,3,9,8,11,4,12,33,13,14,15,16,17,18,19,20,21,35,1,3,9,8,11,4,12,33,13,14,15,16,17,18,19,20,21,35,1,3,9,8,11,4,12,13,14,15,16,17,18,19,20,21,1,3,9,8,11,4,12,13,14,15,16,17,18,19,20,21,19,1,8,1,8,1,3,9,8,11,4,14,15,16,17,20,21,0,1,3,3,3,3,3,3,18,1,1,3,9,8,11,4,12,33,13,13,14,15,16,17,18,18,19,20,21,35,35,1,3,3,9,9,8,11,11,4,4,12,33,13,14,15,16,17,18,19,19,20,21,35,1,3,11,15,21,20,33,1,9,12,0,33,1,3,9,8,11,4,12,33,13,14,15,16,17,18,19,20,21,35,9,11,1,1,3,3,9,4,4,4,8,12,33,14,15,16,17,20,4,18,18,9,4,9,1,3,9,8,11,4,14,15,16,17,20,21,0,1,4,1,4,8,4,15,21,1,3,9,8,11,4,12,13,14,15,16,17,18,19,20,21,13,18,35,1,3,9,8,11,4,12,33,13,14,15,16,17,18,19,20,21,35,1,3,9,8,11,4,12,33,13,14,15,16,17,18,19,20,21,35,1,3,9,8,11,4,12,33,13,14,15,16,17,18,19,20,21,35,18,11,14,15,16,17,20,3,3,3,1,3,9,8,11,4,12,33,13,14,15,16,17,18,19,20,21,35,4,74,74,74,0,0,53,53,0,0,52,53,46,0,0,0,0,52,0,52,53,47,48,49,45,52,50,51,53,47,48,49,45,52,50,51,53,45,47,48,49,45,50,51,47,48,49,45,50,51,50,45,51,47,48,49,45,52,52,50,51,53,53,47,48,49,45,52,50,51,53,47,48,49,45,52,50,51,53,45,50,49,45,50,51,49,50,45,49,50,45,51,50,49,45,52,53,45,47,48,49,45,50,51,52,53,47,48,49,45,52,50,51,53,47,48,49,45,52,50,51,53,47,48,49,45,52,50,51,53,45,51,47,48,49,45,52,50,51,53,75,75,76,77,76,77,63,0,63,63,63,63,0,0,0,0,0,63,56,57,58,59,60,63,56,57,58,59,60,56,57,58,59,60,56,57,58,59,60,57,60,56,57,58,59,60,57,59,63,63,56,57,58,59,60,63,56,57,58,59,60,57,59,60,57,59,60,63,56,57,58,59,60,58,57,59,60,57,58,57,59,60,58,58,59,60,56,57,58,59,60,63,56,57,58,59,60,63,63,56,57,58,59,60,63,56,57,58,59,60,63,56,57,58,59,60,63,56,57,58,59,60,0,78,78,0,0,0,0,0,79,79,0,68,65,66,67,68,65,66,67,65,66,67,65,66,67,78,65,66,67,79,65,66,67,65,67,68,68,65,66,67,68,65,66,67,79,65,66,67,78,65,66,67,65,67,78,65,66,67,68,65,66,67,79,65,66,67,79,65,67,78,65,66,67,2,65,67,2,65,67,79,65,67,78,65,66,67,79,65,66,67,65,66,67,68,65,66,67,68,65,66,67,68,65,66,67,2,65,67,2,65,67,2,65,67,68,65,66,67,0,0,0,73,73,73,73,0,73,0,71,72,62,73,64,71,72,62,73,64,71,72,62,64,71,72,62,64,64,62,71,72,62,64,64,71,72,62,73,73,64,71,72,62,73,64,71,62,71,72,62,73,64,62,71,72,62,64,71,71,71,62,72,62,62,72,71,72,62,64,71,64,73,64,71,72,62,64,73,71,72,62,73,64,71,72,62,73,64,71,72,62,73,64,71,71,71,72,62,73,64],"f":"````````````````````````{{{b{c}}{b{c}}}{{b{c}}}d}{{{f{c}}{f{c}}}{{f{c}}}d}{{{h{c}}{h{c}}}{{h{c}}}d}{{{b{c}}{b{c}}}jd}{{{h{c}}{h{c}}}jd}`{{{f{c}}}{{n{l}}}d}{{{f{c}}}{}d}{{{A`{c}}}{}d}{{{h{c}}}{}d}{ce{}{}}00000000000000000000000000000000000{{{b{c}}}{{b{c}}}d}{{{f{c}}}{{f{c}}}d}{{{Ab{c}}}{{Ab{c}}}d}{{{A`{c}}}{{A`{c}}}{Add}}{{{Af{c}}}{{Af{c}}}{Add}}{{{h{c}}}{{h{c}}}{Add}}{{{Ah{c}}}{{Ah{c}}}{Add}}{AjAj}{{{Al{c}}}{{Al{c}}}{Add}}{{{An{c}}}{{An{c}}}{Add}}{{{B`{c}}}{{B`{c}}}{Add}}{{{Bb{c}}}{{Bb{c}}}{Add}}{BdBd}{{{Bf{c}}}{{Bf{c}}}{Add}}{{{Bh{c}}}{{Bh{c}}}{Add}}{{{Bj{c}}}{{Bj{c}}}{Add}}{{ce}j{}{}}000000000000000{{{Bf{c}}}Bdd}{{{b{c}}{h{c}}{Ah{c}}}{{Bn{Bl}}}d}{{{A`{c}}{h{c}}{Ah{c}}}{{Bn{Bl}}}d}{{{b{c}}{h{c}}}{}d}{{{A`{c}}{h{c}}}{}d}{c{{C`{{b{e}}}}}Cbd}{c{{C`{{f{e}}}}}Cbd}{c{{C`{{Ab{e}}}}}Cbd}{c{{C`{{A`{e}}}}}Cbd}{c{{C`{{Af{e}}}}}Cbd}{c{{C`{{h{e}}}}}Cbd}{c{{C`{{Al{e}}}}}Cbd}{c{{C`{{An{e}}}}}Cbd}{c{{C`{{B`{e}}}}}Cbd}{c{{C`{{Bb{e}}}}}Cbd}{c{{C`{{Bh{e}}}}}Cbd}{c{{C`{{Bj{e}}}}}Cbd}`{{{b{c}}}jd}{{{f{c}}eg}{{h{c}}}d{}{CdCf}}{{{f{c}}Che}{{Cj{{h{c}}{Bj{c}}}}}d{CdCf}}{{{f{c}}e}{{h{c}}}d{CdCf}}{{{f{c}}{Bf{c}}Ble}{{Cj{{h{c}}{Bh{c}}}}}d{CdCf}}{{{f{c}}e}{{Cj{{h{c}}{An{c}}}}}d{CdCf}}{{{f{c}}{f{c}}}Chd}{{BdBd}Ch}{{{b{c}}}{}d}{{{b{c}}Cl}Cnd}{{{f{c}}Cl}Cnd}{{{Ab{c}}Cl}Cnd}{{{A`{c}}Cl}Cn{D`d}}{{{Af{c}}Cl}Cn{D`d}}{{{h{c}}Cl}Cnd}{{{Ah{c}}Cl}Cn{D`d}}{{{Db{ce}}Cl}Cn{D`d}{D`Dd}}{{AjCl}Cn}0{{{Al{c}}Cl}Cn{D`d}}{{{An{c}}Cl}Cn{D`d}}{{{B`{c}}Cl}Cn{D`d}}{{{Bb{c}}Cl}Cn{D`d}}{{BdCl}Cn}0{{{Bf{c}}Cl}Cn{D`d}}{{{Bh{c}}Cl}Cn{D`d}}{{{Bj{c}}Cl}Cn{D`d}}{{DfCl}Cn}0{cc{}}{{{b{c}}}{{f{c}}}d}11{{{b{c}}}{{Ab{c}}}d}2{{{A`{c}}}{{Af{c}}}d}33{{{Db{ce}}}{{h{c}}}dDd}444444444{Bd{{Bf{c}}}d}555{{{n{l}}}{{Bn{{b{c}}}}}d}{{{n{l}}}{{C`{{f{c}}Aj}}}d}{{{n{l}}}{{Bn{{Af{c}}}}}d}{{{n{l}}}{{Bn{{An{c}}}}}d}{{{n{l}}}{{Bn{{Bj{c}}}}}d}{{{f{c}}{Bf{c}}{Db{cBl}}Dhe}{{Bh{c}}}d{CfCd}}{{{Db{ce}}}{{Db{c}}}d{DjDd}}{c{{b{e}}}{CdCf}d}{c{{Ab{e}}}{CfCd}d}{{{Ah{c}}}{{Bn{Bl}}}d}`;{ce{}{}}00000000000000000{{{Ab{c}}}{{Cj{{f{c}}{b{c}}}}}d}{{{Af{c}}}{{A`{c}}}d}{{{b{c}}}{{b{c}}}d}0{{{f{c}}Bl}{{f{c}}}d}{{{f{c}}}{{f{c}}}d}{{{Ab{c}}}{{Ab{c}}}d}{{{h{c}}}{{h{c}}}d}{{{h{c}}Bl}{{h{c}}}d}{{{h{c}}}ed{}}{{{h{c}}{Ab{c}}Dhe}{{Cj{{A`{c}}{An{c}}}}}d{CdCf}}{c{{Ah{e}}}{{Dn{}{{Dl{Bl}}}}}d}{{c{f{e}}g}{{Db{ec}}}{DjDd}d{CdCf}}{{{Db{c}}{f{c}}{b{c}}Dhe}{{Cj{{Al{c}}}}}d{CfCd}}{{{f{c}}{b{c}}CjDhe}{{An{c}}}d{CdCf}}{{e{Db{c}}{f{c}}Dhg}{{B`{c}}}d{{E`{}{{Dl{{Db{c}}}}}}}{CfCd}}{{{n{{Ab{c}}}}Dhe}{{Bb{c}}}d{CdCf}}{{{f{c}}{Bf{c}}BlDhe}{{Cj{{Db{cBl}}{Bh{c}}}}}d{CfCd}}{c{{h{e}}}{}d}{BlBd}{BdBl}{{{Ab{c}}}{{f{c}}}d}{{{h{c}}}{}d}{{{Ab{c}}}{{b{c}}}d}{{{b{c}}e}C`dEb}{{{f{c}}e}C`dEb}{{{Ab{c}}e}C`dEb}{{{A`{c}}e}C`dEb}{{{Af{c}}e}C`dEb}{{{h{c}}e}C`dEb}{{{Al{c}}e}C`dEb}{{{An{c}}e}C`dEb}{{{B`{c}}e}C`dEb}{{{Bb{c}}e}C`dEb}{{{Bh{c}}e}C`dEb}{{{Bj{c}}e}C`dEb}`{{{b{c}}{b{c}}}{{b{c}}}d}{{{h{c}}{h{c}}}{{h{c}}}d}{{{b{c}}{b{c}}}jd}{{{h{c}}{h{c}}}jd}{{{A`{c}}}{{Ed{l}}}d}{{{h{c}}}{{Ed{l}}}d}{{{An{c}}}{{Ed{l}}}d}{{{Bj{c}}}{{Ed{l}}}d}{ce{}{}}000000000000000{cEf{}}00{c{{C`{e}}}{}{}}00000000000000000000000000000000000{cEh{}}00000000000000000{BdBl}{{{Af{c}}{h{c}}{f{c}}{An{c}}Dh}{{C`{{A`{c}}Df}}}d}{{{Al{c}}{h{c}}{f{c}}Dh}{{C`{jDf}}}d}{{{An{c}}{f{c}}CjDh}{{C`{jDf}}}d}{{{B`{c}}e{h{c}}{f{c}}Dh}{{C`{jDf}}}d{{E`{}{{Dl{{h{c}}}}}}Ad}}{{{Bb{c}}eDh}{{C`{jDf}}}d{{E`{}{{Dl{{f{c}}}}}}Ad}}{{{Bh{c}}{f{c}}{Bf{c}}{h{c}}Dh}{{C`{jDf}}}d}{{{f{c}}{h{c}}{Bj{c}}}{{C`{jDf}}}d}{{{f{c}}{Bf{c}}{h{c}}{Bh{c}}}{{C`{jDf}}}d}{{{f{c}}{h{c}}{An{c}}}{{C`{jDf}}}d}=================={{}{{h{c}}}d}````````````````````>>>>>>>>>>>>>>>>{{{Ej{ce}}}{{n{{h{c}}}}}d{{El{c}}}}{EnEn}{F`F`}{{{Fb{ce}}}{{Fb{ce}}}d{{El{c}}}}{{{Ej{ce}}}{{Ej{ce}}}{Add}{Ad{El{c}}}}{{{Fd{c}}}{{Fd{c}}}{Add}}{{{Ff{c}}}{{Ff{c}}}{Add}}{{ce}j{}{}}00000{{{Fd{c}}}Bld}{c{{C`{{Ej{eg}}}}}Cbd{{El{e}}}}{c{{C`{{Ff{e}}}}}Cbd}{{EnCl}Cn}{{F`Cl}Cn}{{{Fb{ce}}Cl}Cn{D`d}{D`{El{c}}}}{{{Ej{ce}}Cl}Cn{D`d}{D`{El{c}}}}{{FhCl}Cn}0{{{Fd{c}}Cl}Cn{D`d}}{{{Ff{c}}Cl}Cn{D`d}}{{FjCl}Cn}0{cc{}}0000000{ce{}{}}0000000{{{Ej{ce}}}Fld{{El{c}}}}={{{f{c}}Fl}{{Fb{cF`}}}d}{{{Fb{ce}}{n{Ch}}g}{{Ej{ce}}}d{{El{c}}}{CdCf}}{{{f{c}}FlBl}{{Fd{c}}}d}{{{Fd{c}}{n{Bl}}e}{{Ff{c}}}d{CdCf}}{{{Fb{ce}}}Fld{{El{c}}}}{{{Fd{c}}}Fld}{{{Ej{ce}}}{{Bj{c}}}d{{El{c}}}}{{{Fb{ce}}}{{f{c}}}d{{El{c}}}}{{{Fd{c}}}{{f{c}}}d}{{{Ej{ce}}g}C`d{{El{c}}}Eb}{{{Ff{c}}e}C`dEb}{{{Fd{c}}Bl}jd}{{{f{c}}Fl}{{Fb{cEn}}}d}{{{Fb{cEn}}Fle}{{Ej{cEn}}}d{CdCf}}{Fh{{Bn{Fn}}}}{Fj{{Bn{Fn}}}}{{{Ej{ce}}}{}d{{El{c}}}}{ce{}{}}00000{cEf{}}0{c{{C`{e}}}{}{}}000000000000000{cEh{}}0000000{{{Ej{ce}}{Fb{ce}}}{{C`{{n{{h{c}}}}Fh}}}d{{El{c}}}}{{{Ff{c}}{Fd{c}}}{{C`{{`{{E`{}{{Dl{{h{c}}}}}}}}Fj}}}d}55555555`````````````````555555555555{G`G`}{{{Gb{c}}}{{Gb{c}}}{Add}}{{{Gd{c}}}{{Gd{c}}}{Add}}{{{Gf{c}}}{{Gf{c}}}{Add}}{{{Gh{c}}}{{Gh{c}}}{Add}}{{ce}j{}{}}0000{{{Gb{c}}}{{Gj{l}}}d}{{{Gh{c}}}{{C`{{Gl{c}}Gn}}}d}{c{{C`{G`}}}Cb}{c{{C`{{Gb{e}}}}}Cbd}{c{{C`{{Gd{e}}}}}Cbd}{c{{C`{{Gf{e}}}}}Cbd}{c{{C`{{Gh{e}}}}}Cbd}{{{Gb{c}}}{{Gf{c}}}d}{{{Gf{c}}}{{Gh{c}}}d}{{GnCl}Cn}0{{G`Cl}Cn}{{{Gb{c}}Cl}Cn{D`d}}{{{Gd{c}}Cl}Cn{D`d}}{{{Gf{c}}Cl}Cn{D`d}}{{{Gh{c}}Cl}Cn{D`d}}{cc{}}00000{{{Gb{c}}}Fld}{{{Gf{c}}}Fld}{{{Gh{c}}}Fld}{{{Gb{c}}Fl{Gj{l}}}jd}{{{Gf{c}}Fl{Gd{c}}}{{C`{jGn}}}d}{{{Gh{c}}Fl{b{c}}}{{C`{jGn}}}d}{ce{}{}}00000{{{Gd{c}}}{{Gd{c}}}d}{{{Gb{c}}}{{`{{E`{}{{Dl{Fl}}}}}}}d}{{{Gf{c}}}{{`{{E`{}{{Dl{Fl}}}}}}}d}{{{Gh{c}}}{{`{{E`{}{{Dl{Fl}}}}}}}d}{{H`Flc}{{Gb{e}}}{CdCf}d}`{{{Gb{c}}}H`d}{{{Gf{c}}}H`d}{{{Gh{c}}}H`d}``{{{Gf{c}}}{{Gd{c}}}d}{{{Gh{c}}Fl}{{b{c}}}d}{{G`c}C`Eb}{{{Gb{c}}e}C`dEb}{{{Gd{c}}e}C`dEb}{{{Gf{c}}e}C`dEb}{{{Gh{c}}e}C`dEb}{Gn{{Bn{Fn}}}}{ce{}{}}0000{cEf{}}{c{{C`{e}}}{}{}}00000000000{cEh{}}00000333333```````````33333333{HbHb}{{{Hd{c}}}{{Hd{c}}}{}}{HfHf}{{ce}j{}{}}00{{{n{l}}}{{Bn{c}}}{}}0000000{{HbHb}Ch}{{HfHf}Ch}{{Hh{n{l}}}j}{{HhCl}Cn}{{HbCl}Cn}{{{Hd{c}}Cl}CnD`}{{HfCl}Cn}{cc{}}000{ce{CdCf}{}}000{{}c{}}000{{Hbc}jHj}{{Hfc}jHj}2222{ce{}{}}0005555{{{n{c}}}j{}}00{cCh{}}0002{Hlc{}}0{{ei}g{}{{Dn{}{{Dl{c}}}}}{}{{Dn{}{{Dl{g}}}}}}00{Hhc{}}00{{c{n{l}}}j{}}0000000666{c{{C`{e}}}{}{}}0000000{cEh{}}000{{cec}e{}{}}{{HlcHl}c{}}0:77666::::``````````::::::::::{{{Hn{c}}}{{Hn{c}}}{Add}}{{{I`{c}}}{{I`{c}}}{Add}}{{{Gl{c}}}{{Gl{c}}}{Add}}{H`H`}{{ce}j{}{}}000{{H`e}{{Bn{{A`{c}}}}}d{{Dn{}{{Dl{{Cj{Fl{A`{c}}}}}}}}}}{{{Gl{c}}{h{c}}e}{{Cj{{A`{c}}{An{c}}}}}d{CdCf}}{c{{C`{{Hn{e}}}}}Cbd}{c{{C`{{I`{e}}}}}Cbd}{c{{C`{{Gl{e}}}}}Cbd}{c{{C`{H`}}}Cb}{{H`H`}Ch}{{{Hn{c}}Cl}Cn{D`d}}{{{I`{c}}Cl}Cn{D`d}}{{{Gl{c}}Cl}Cn{D`d}}{{IbCl}Cn}0{{H`Cl}Cn}{cc{}}0000{{H`{Ed{{f{c}}}}}{{C`{{Hn{c}}Ib}}}d}{{{Gl{c}}}Fld}{ce{}{}}0000{{{Gl{c}}}{{Hn{c}}}d}{{H`Ed{Bb{c}}}{{C`{{Hn{c}}Ib}}}d}{{H`c}{{I`{e}}}{CdCf}d}{{{Hn{c}}Fl{b{c}}}{{C`{{Gl{c}}Ib}}}d}{{FlFl}H`}{{{Hn{c}}}H`d}{{{Hn{c}}Fl}{{Bn{{f{c}}}}}d}{{{Hn{c}}}{{n{{f{c}}}}}d}{{{Gl{c}}e}{{Bb{c}}}d{CdCf}}{{{I`{c}}}{{Cj{Ed{Bb{c}}}}}d}{{{Gl{c}}}{{f{c}}}d}{{{Gl{c}}}{{b{c}}}d}{{{I`{c}}Fl}{{b{c}}}d}{{{Hn{c}}e}C`dEb}{{{I`{c}}e}C`dEb}{{{Gl{c}}e}C`dEb}{{H`c}C`Eb}{{{Hn{c}}}{{f{c}}}d}`{Ib{{Bn{Fn}}}}`{ce{}{}}000{cEf{}}{c{{C`{e}}}{}{}}000000000{cEh{}}0000{{{Hn{c}}Fl{Bb{c}}}{{C`{jDf}}}d}{{{Hn{c}}{Af{c}}{h{c}}Fl{An{c}}}{{C`{{A`{c}}Df}}}d}55555","c":[],"p":[[5,"SecretKey",0],[10,"Group",611],[5,"PublicKey",0],[5,"Ciphertext",0],[1,"unit"],[1,"u8"],[1,"slice"],[5,"VerifiableDecryption",0],[5,"Keypair",0],[10,"Clone",830],[5,"CandidateDecryption",0],[5,"DiscreteLogTable",0],[6,"PublicKeyConversionError",0],[5,"CommitmentEquivalenceProof",0],[5,"LogEqualityProof",0],[5,"SumOfSquaresProof",0],[5,"ProofOfPossession",0],[5,"RangeDecomposition",0],[5,"PreparedRange",0],[5,"RangeProof",0],[5,"RingProof",0],[1,"u64"],[6,"Option",831],[6,"Result",832],[10,"Deserializer",833],[10,"CryptoRng",834],[10,"RngCore",834],[1,"bool"],[1,"tuple"],[5,"Formatter",835],[8,"Result",835],[10,"Debug",835],[5,"CiphertextWithValue",0],[10,"Zeroize",836],[6,"VerificationError",0],[5,"Transcript",837],[10,"Copy",838],[17,"Item"],[10,"IntoIterator",839],[10,"Iterator",840],[10,"Serializer",841],[5,"Vec",842],[5,"String",843],[5,"TypeId",844],[5,"EncryptedChoice",353],[10,"ProveSum",353],[5,"SingleChoice",353],[5,"MultiChoice",353],[5,"ChoiceParams",353],[5,"QuadraticVotingParams",353],[5,"QuadraticVotingBallot",353],[6,"ChoiceVerificationError",353],[6,"QuadraticVotingError",353],[1,"usize"],[10,"Error",845],[5,"Opening",495],[5,"ParticipantCollectingCommitments",495],[5,"PublicInfo",495],[5,"ParticipantCollectingPolynomials",495],[5,"ParticipantExchangingSecrets",495],[1,"array"],[5,"ActiveParticipant",729],[6,"Error",495],[5,"Params",729],[5,"Curve25519Subgroup",611],[5,"Generic",611],[5,"Ristretto",611],[5,"RandomBytesProvider",611],[10,"Hasher",846],[5,"Scalar",847],[5,"PublicKeySet",729],[5,"Dealer",729],[6,"Error",729],[15,"LenMismatch",350],[15,"OptionsLenMismatch",489],[15,"OptionsLenMismatch",491],[15,"Variant",491],[10,"ElementOps",611],[10,"ScalarOps",611]],"b":[[137,"impl-Display-for-PublicKeyConversionError"],[138,"impl-Debug-for-PublicKeyConversionError"],[143,"impl-Display-for-RangeDecomposition"],[144,"impl-Debug-for-RangeDecomposition"],[148,"impl-Display-for-VerificationError"],[149,"impl-Debug-for-VerificationError"],[205,"impl-Mul%3C%26%3CG+as+ScalarOps%3E::Scalar%3E-for-%26SecretKey%3CG%3E"],[206,"impl-Mul%3C%26%3CG+as+ScalarOps%3E::Scalar%3E-for-SecretKey%3CG%3E"],[207,"impl-Mul%3Cu64%3E-for-PublicKey%3CG%3E"],[208,"impl-Mul%3C%26%3CG+as+ScalarOps%3E::Scalar%3E-for-PublicKey%3CG%3E"],[210,"impl-Mul%3C%26%3CG+as+ScalarOps%3E::Scalar%3E-for-Ciphertext%3CG%3E"],[211,"impl-Mul%3Cu64%3E-for-Ciphertext%3CG%3E"],[406,"impl-Debug-for-ChoiceVerificationError"],[407,"impl-Display-for-ChoiceVerificationError"],[410,"impl-Debug-for-QuadraticVotingError"],[411,"impl-Display-for-QuadraticVotingError"],[537,"impl-Display-for-Error"],[538,"impl-Debug-for-Error"],[767,"impl-Display-for-Error"],[768,"impl-Debug-for-Error"]]}]\ +]')); +if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; +else if (window.initSearch) window.initSearch(searchIndex); diff --git a/settings.html b/settings.html new file mode 100644 index 0000000..2d9e07c --- /dev/null +++ b/settings.html @@ -0,0 +1,2 @@ +Settings +

Rustdoc settings

Back
\ No newline at end of file diff --git a/src-files.js b/src-files.js new file mode 100644 index 0000000..3ca0268 --- /dev/null +++ b/src-files.js @@ -0,0 +1,4 @@ +var srcIndex = new Map(JSON.parse('[\ +["elastic_elgamal",["",[["app",[],["choice.rs","mod.rs","quadratic_voting.rs"]],["group",[],["curve25519.rs","generic.rs","mod.rs","ristretto.rs"]],["keys",[],["impls.rs","mod.rs"]],["proofs",[],["commitment.rs","log_equality.rs","mod.rs","mul.rs","possession.rs","range.rs","ring.rs"]],["sharing",[],["key_set.rs","mod.rs","participant.rs"]]],["decryption.rs","dkg.rs","encryption.rs","lib.rs","serde.rs"]]]\ +]')); +createSrcSidebar(); diff --git a/src/elastic_elgamal/app/choice.rs.html b/src/elastic_elgamal/app/choice.rs.html new file mode 100644 index 0000000..7b8d43f --- /dev/null +++ b/src/elastic_elgamal/app/choice.rs.html @@ -0,0 +1,993 @@ +choice.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+
//! Encrypted choice.
+
+use merlin::Transcript;
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
+use zeroize::Zeroizing;
+
+use core::{fmt, iter, ops};
+
+use crate::{
+    alloc::{vec, Vec},
+    group::Group,
+    Ciphertext, CiphertextWithValue, LogEqualityProof, PublicKey, RingProof, RingProofBuilder,
+    VerificationError,
+};
+
+/// Encapsulation of functionality for proving and verifying correctness of the sum of option
+/// ciphertexts in an [`EncryptedChoice`].
+///
+/// This trait is not meant to be implemented for external types.
+pub trait ProveSum<G: Group>: Clone + crate::sealed::Sealed {
+    /// Produced / verified proofs.
+    #[cfg(not(feature = "serde"))]
+    type Proof: Sized;
+    /// Produced / verified proofs.
+    #[cfg(feature = "serde")]
+    type Proof: Sized + Serialize + DeserializeOwned;
+
+    #[doc(hidden)]
+    fn prove<R: CryptoRng + RngCore>(
+        &self,
+        ciphertext: &CiphertextWithValue<G, u64>,
+        receiver: &PublicKey<G>,
+        rng: &mut R,
+    ) -> Self::Proof;
+
+    #[doc(hidden)]
+    fn verify(
+        &self,
+        ciphertext: &Ciphertext<G>,
+        proof: &Self::Proof,
+        receiver: &PublicKey<G>,
+    ) -> Result<(), ChoiceVerificationError>;
+}
+
+/// Single-choice setup for [`EncryptedChoice`], in which it can contain a single selected option.
+///
+/// # Examples
+///
+/// See [`EncryptedChoice`] docs for an example of usage.
+#[derive(Debug, Clone, Copy)]
+pub struct SingleChoice(());
+
+impl crate::sealed::Sealed for SingleChoice {}
+
+impl<G: Group> ProveSum<G> for SingleChoice {
+    type Proof = LogEqualityProof<G>;
+
+    fn prove<R: CryptoRng + RngCore>(
+        &self,
+        ciphertext: &CiphertextWithValue<G, u64>,
+        receiver: &PublicKey<G>,
+        rng: &mut R,
+    ) -> Self::Proof {
+        LogEqualityProof::new(
+            receiver,
+            ciphertext.randomness(),
+            (
+                ciphertext.inner().random_element,
+                ciphertext.inner().blinded_element - G::generator(),
+            ),
+            &mut Transcript::new(b"choice_encryption_sum"),
+            rng,
+        )
+    }
+
+    fn verify(
+        &self,
+        ciphertext: &Ciphertext<G>,
+        proof: &Self::Proof,
+        receiver: &PublicKey<G>,
+    ) -> Result<(), ChoiceVerificationError> {
+        let powers = (
+            ciphertext.random_element,
+            ciphertext.blinded_element - G::generator(),
+        );
+        proof
+            .verify(
+                receiver,
+                powers,
+                &mut Transcript::new(b"choice_encryption_sum"),
+            )
+            .map_err(ChoiceVerificationError::Sum)
+    }
+}
+
+/// Multi-choice setup for [`EncryptedChoice`], in which it can contain any possible number
+/// of selected options (`0..=n`, where `n` is the number of options).
+///
+/// # Examples
+///
+/// See [`EncryptedChoice`] docs for an example of usage.
+#[derive(Debug, Clone, Copy)]
+pub struct MultiChoice(());
+
+impl crate::sealed::Sealed for MultiChoice {}
+
+impl<G: Group> ProveSum<G> for MultiChoice {
+    type Proof = ();
+
+    fn prove<R: CryptoRng + RngCore>(
+        &self,
+        _ciphertext: &CiphertextWithValue<G, u64>,
+        _receiver: &PublicKey<G>,
+        _rng: &mut R,
+    ) -> Self::Proof {
+        // Do nothing.
+    }
+
+    fn verify(
+        &self,
+        _ciphertext: &Ciphertext<G>,
+        _proof: &Self::Proof,
+        _receiver: &PublicKey<G>,
+    ) -> Result<(), ChoiceVerificationError> {
+        Ok(()) // no failure conditions
+    }
+}
+
+/// Parameters of an [`EncryptedChoice`] polling.
+#[derive(Debug)]
+pub struct ChoiceParams<G: Group, S: ProveSum<G>> {
+    options_count: usize,
+    sum_prover: S,
+    receiver: PublicKey<G>,
+}
+
+impl<G: Group, S: ProveSum<G>> Clone for ChoiceParams<G, S> {
+    fn clone(&self) -> Self {
+        Self {
+            options_count: self.options_count,
+            sum_prover: self.sum_prover.clone(),
+            receiver: self.receiver.clone(),
+        }
+    }
+}
+
+impl<G: Group, S: ProveSum<G>> ChoiceParams<G, S> {
+    fn check_options_count(&self, actual_count: usize) -> Result<(), ChoiceVerificationError> {
+        if self.options_count == actual_count {
+            Ok(())
+        } else {
+            Err(ChoiceVerificationError::OptionsLenMismatch {
+                expected: self.options_count,
+                actual: actual_count,
+            })
+        }
+    }
+
+    /// Returns the public key for which the [`EncryptedChoice`] are encrypted.
+    pub fn receiver(&self) -> &PublicKey<G> {
+        &self.receiver
+    }
+
+    /// Returns the number of options in these parameters.
+    pub fn options_count(&self) -> usize {
+        self.options_count
+    }
+}
+
+impl<G: Group> ChoiceParams<G, SingleChoice> {
+    /// Creates parameters for a single-choice polling.
+    ///
+    /// # Panics
+    ///
+    /// Panics if provided `options_count` is zero.
+    pub fn single(receiver: PublicKey<G>, options_count: usize) -> Self {
+        assert!(options_count > 0, "Number of options must be positive");
+        Self {
+            options_count,
+            sum_prover: SingleChoice(()),
+            receiver,
+        }
+    }
+}
+
+impl<G: Group> ChoiceParams<G, MultiChoice> {
+    /// Creates parameters for a multi-choice polling.
+    ///
+    /// # Panics
+    ///
+    /// Panics if provided `options_count` is zero.
+    pub fn multi(receiver: PublicKey<G>, options_count: usize) -> Self {
+        assert!(options_count > 0, "Number of options must be positive");
+        Self {
+            options_count,
+            sum_prover: MultiChoice(()),
+            receiver,
+        }
+    }
+}
+
+/// Zero or more encrypted choices from `n` options (`n >= 1`) together with zero-knowledge
+/// proofs of correctness.
+///
+/// # Construction
+///
+/// The choice is represented as a vector of `n` *choice ciphertexts* of Boolean values (0 or 1),
+/// where the ciphertexts for the chosen options encrypt 1 and the other ciphertexts encrypt 0.
+/// This ensures that multiple [`EncryptedChoice`]s can be added (e.g., within a voting protocol).
+///
+/// Zero-knowledge proofs are:
+///
+/// - A [`RingProof`] attesting that all `n` ciphertexts encrypt 0 or 1.
+///   This proof can be obtained via [`Self::range_proof()`].
+/// - A [`LogEqualityProof`] attesting that the encrypted values sum up to 1. Combined with
+///   the range proof, this means that exactly one of encrypted values is 1, and all others are 0.
+///   This proof can be obtained via [`Self::sum_proof()`]. This proof is absent for
+///   a [`MultiChoice`] setup (`sum_proof()` just returns `()`).
+///
+/// # Examples
+///
+/// ## Single-choice setup
+///
+/// ```
+/// # use elastic_elgamal::{
+/// #     app::{ChoiceParams, EncryptedChoice}, group::Ristretto, DiscreteLogTable, Keypair,
+/// # };
+/// # use rand::thread_rng;
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// let mut rng = thread_rng();
+/// let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+/// let choice_params = ChoiceParams::single(pk, 5);
+///
+/// let choice = 2;
+/// let enc = EncryptedChoice::single(&choice_params, choice, &mut rng);
+/// let choices = enc.verify(&choice_params)?;
+///
+/// // `choices` is a slice of 5 Boolean value ciphertexts
+/// assert_eq!(choices.len(), 5);
+/// let lookup_table = DiscreteLogTable::new(0..=1);
+/// for (idx, &v) in choices.iter().enumerate() {
+///     assert_eq!(
+///         sk.decrypt(v, &lookup_table),
+///         Some((idx == choice) as u64)
+///     );
+/// }
+/// # Ok(())
+/// # }
+/// ```
+///
+/// ## Multi-choice setup
+///
+/// ```
+/// # use elastic_elgamal::{
+/// #     app::{ChoiceParams, EncryptedChoice}, group::Ristretto, DiscreteLogTable, Keypair,
+/// # };
+/// # use rand::thread_rng;
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// let mut rng = thread_rng();
+/// let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+/// let choice_params = ChoiceParams::multi(pk, 5);
+///
+/// let choices = [true, false, true, true, false];
+/// let enc = EncryptedChoice::new(&choice_params, &choices, &mut rng);
+/// let recovered_choices = enc.verify(&choice_params)?;
+///
+/// let lookup_table = DiscreteLogTable::new(0..=1);
+/// for (idx, &v) in recovered_choices.iter().enumerate() {
+///     assert_eq!(sk.decrypt(v, &lookup_table), Some(choices[idx] as u64));
+/// }
+/// # Ok(())
+/// # }
+/// ```
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct EncryptedChoice<G: Group, S: ProveSum<G>> {
+    choices: Vec<Ciphertext<G>>,
+    range_proof: RingProof<G>,
+    sum_proof: S::Proof,
+}
+
+impl<G: Group> EncryptedChoice<G, SingleChoice> {
+    /// Creates a new encrypted choice.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `choice` exceeds the maximum index allowed by `params`.
+    pub fn single<R: CryptoRng + RngCore>(
+        params: &ChoiceParams<G, SingleChoice>,
+        choice: usize,
+        rng: &mut R,
+    ) -> Self {
+        assert!(
+            choice < params.options_count,
+            "invalid choice {choice}; expected a value in 0..{}",
+            params.options_count
+        );
+        let choices: Vec<_> = (0..params.options_count).map(|i| choice == i).collect();
+        Self::new(params, &Zeroizing::new(choices), rng)
+    }
+}
+
+#[allow(clippy::len_without_is_empty)] // `is_empty()` would always be false
+impl<G: Group, S: ProveSum<G>> EncryptedChoice<G, S> {
+    /// Creates an encrypted multi-choice.
+    ///
+    /// For a [`SingleChoice`] polling, it is caller's responsibility to ensure that `choices`
+    /// contains exactly one `true` value; otherwise, the produced proof will not verify.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the length of `choices` differs from the number of options specified in `params`.
+    pub fn new<R: CryptoRng + RngCore>(
+        params: &ChoiceParams<G, S>,
+        choices: &[bool],
+        rng: &mut R,
+    ) -> Self {
+        assert!(!choices.is_empty(), "No choices provided");
+        assert_eq!(
+            choices.len(),
+            params.options_count,
+            "Mismatch between expected and actual number of choices"
+        );
+
+        let admissible_values = [G::identity(), G::generator()];
+        let mut ring_responses = vec![G::Scalar::default(); 2 * params.options_count];
+        let mut transcript = Transcript::new(b"encrypted_choice_ranges");
+        let mut proof_builder = RingProofBuilder::new(
+            &params.receiver,
+            params.options_count,
+            &mut ring_responses,
+            &mut transcript,
+            rng,
+        );
+
+        let sum = choices.iter().map(|&flag| u64::from(flag)).sum::<u64>();
+        let choices: Vec<_> = choices
+            .iter()
+            .map(|&flag| proof_builder.add_value(&admissible_values, usize::from(flag)))
+            .collect();
+        let range_proof = RingProof::new(proof_builder.build(), ring_responses);
+
+        let sum_ciphertext = choices.iter().cloned().reduce(ops::Add::add).unwrap();
+        let sum_ciphertext = sum_ciphertext.with_value(sum);
+        let sum_proof = params
+            .sum_prover
+            .prove(&sum_ciphertext, &params.receiver, rng);
+        Self {
+            choices: choices.into_iter().map(|choice| choice.inner).collect(),
+            range_proof,
+            sum_proof,
+        }
+    }
+
+    /// Verifies the zero-knowledge proofs in this choice and returns Boolean ciphertexts
+    /// for all options.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the `choice` is malformed or its proofs fail verification.
+    #[allow(clippy::missing_panics_doc)]
+    pub fn verify(
+        &self,
+        params: &ChoiceParams<G, S>,
+    ) -> Result<&[Ciphertext<G>], ChoiceVerificationError> {
+        params.check_options_count(self.choices.len())?;
+        let sum_of_ciphertexts = self.choices.iter().copied().reduce(ops::Add::add);
+        let sum_of_ciphertexts = sum_of_ciphertexts.unwrap();
+        // ^ `unwrap()` is safe; `params` cannot have 0 options by construction
+        params
+            .sum_prover
+            .verify(&sum_of_ciphertexts, &self.sum_proof, &params.receiver)?;
+
+        let admissible_values = [G::identity(), G::generator()];
+        self.range_proof
+            .verify(
+                &params.receiver,
+                iter::repeat(&admissible_values as &[_]).take(self.choices.len()),
+                self.choices.iter().copied(),
+                &mut Transcript::new(b"encrypted_choice_ranges"),
+            )
+            .map(|()| self.choices.as_slice())
+            .map_err(ChoiceVerificationError::Range)
+    }
+
+    /// Returns the number of encrypted choices. This value is equal to
+    /// [`ChoiceParams::options_count()`] with which the encryption was created.
+    pub fn len(&self) -> usize {
+        self.choices.len()
+    }
+
+    /// Returns ciphertexts for all options **without** checking the validity of this choice.
+    pub fn choices_unchecked(&self) -> &[Ciphertext<G>] {
+        &self.choices
+    }
+
+    /// Returns the range proof for the choice ciphertexts.
+    pub fn range_proof(&self) -> &RingProof<G> {
+        &self.range_proof
+    }
+
+    /// Returns the sum proof for the choice ciphertexts.
+    pub fn sum_proof(&self) -> &S::Proof {
+        &self.sum_proof
+    }
+}
+
+/// Error verifying an [`EncryptedChoice`].
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum ChoiceVerificationError {
+    /// Mismatch between expected and actual number of options in the `EncryptedChoice`.
+    OptionsLenMismatch {
+        /// Expected number of options.
+        expected: usize,
+        /// Actual number of options.
+        actual: usize,
+    },
+    /// Error verifying [`EncryptedChoice::sum_proof()`].
+    Sum(VerificationError),
+    /// Error verifying [`EncryptedChoice::range_proof()`].
+    Range(VerificationError),
+}
+
+impl fmt::Display for ChoiceVerificationError {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::OptionsLenMismatch { expected, actual } => write!(
+                formatter,
+                "number of options in the ballot ({actual}) differs from expected ({expected})",
+            ),
+            Self::Sum(err) => write!(formatter, "cannot verify sum proof: {err}"),
+            Self::Range(err) => write!(formatter, "cannot verify range proofs: {err}"),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for ChoiceVerificationError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        match self {
+            Self::Sum(err) | Self::Range(err) => Some(err),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::thread_rng;
+
+    use super::*;
+    use crate::{
+        group::{Generic, Ristretto},
+        Keypair,
+    };
+
+    fn test_bogus_encrypted_choice_does_not_work<G: Group>() {
+        let mut rng = thread_rng();
+        let (receiver, _) = Keypair::<G>::generate(&mut rng).into_tuple();
+        let params = ChoiceParams::single(receiver.clone(), 5);
+
+        let mut choice = EncryptedChoice::single(&params, 2, &mut rng);
+        let (encrypted_one, _) = receiver.encrypt_bool(true, &mut rng);
+        choice.choices[0] = encrypted_one;
+        assert!(choice.verify(&params).is_err());
+
+        let mut choice = EncryptedChoice::single(&params, 4, &mut rng);
+        let (encrypted_zero, _) = receiver.encrypt_bool(false, &mut rng);
+        choice.choices[4] = encrypted_zero;
+        assert!(choice.verify(&params).is_err());
+
+        let mut choice = EncryptedChoice::single(&params, 4, &mut rng);
+        choice.choices[4].blinded_element =
+            choice.choices[4].blinded_element + G::mul_generator(&G::Scalar::from(10));
+        choice.choices[3].blinded_element =
+            choice.choices[3].blinded_element - G::mul_generator(&G::Scalar::from(10));
+        // These modifications leave `choice.sum_proof` correct, but the range proofs
+        // for the last 2 choices should no longer verify.
+        assert!(choice.verify(&params).is_err());
+    }
+
+    #[test]
+    fn bogus_encrypted_choice_does_not_work_for_edwards() {
+        test_bogus_encrypted_choice_does_not_work::<Ristretto>();
+    }
+
+    #[test]
+    fn bogus_encrypted_choice_does_not_work_for_k256() {
+        test_bogus_encrypted_choice_does_not_work::<Generic<k256::Secp256k1>>();
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/app/mod.rs.html b/src/elastic_elgamal/app/mod.rs.html new file mode 100644 index 0000000..746b047 --- /dev/null +++ b/src/elastic_elgamal/app/mod.rs.html @@ -0,0 +1,43 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
//! High-level applications for proofs defined in this crate.
+//!
+//! For now, the applications are:
+//!
+//! - [`EncryptedChoice`]. Single-choice or multi-choice selection from a predefined
+//!   list of options, with summable selection ciphertexts.
+//! - [`QuadraticVotingBallot`]. [Quadratic voting] on a predefined list of options,
+//!   with summable selection ciphertexts.
+//!
+//! [Quadratic voting]: https://en.wikipedia.org/wiki/Quadratic_voting
+
+mod choice;
+mod quadratic_voting;
+
+pub use self::{
+    choice::{
+        ChoiceParams, ChoiceVerificationError, EncryptedChoice, MultiChoice, ProveSum, SingleChoice,
+    },
+    quadratic_voting::{QuadraticVotingBallot, QuadraticVotingError, QuadraticVotingParams},
+};
+
\ No newline at end of file diff --git a/src/elastic_elgamal/app/quadratic_voting.rs.html b/src/elastic_elgamal/app/quadratic_voting.rs.html new file mode 100644 index 0000000..c162c90 --- /dev/null +++ b/src/elastic_elgamal/app/quadratic_voting.rs.html @@ -0,0 +1,943 @@ +quadratic_voting.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+
//! Quadratic voting application.
+
+use merlin::Transcript;
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+use core::fmt;
+
+use crate::{
+    alloc::Vec, group::Group, Ciphertext, PreparedRange, PublicKey, RangeDecomposition, RangeProof,
+    SumOfSquaresProof, VerificationError,
+};
+
+/// [Quadratic voting] parameters prepared for a certain [`Group`].
+///
+/// The parameters are:
+///
+/// - [Receiver key](Self::receiver()) using which votes in [`QuadraticVotingBallot`]s
+///   are encrypted
+/// - [Number of options](Self::options_count()) in the ballot
+/// - [Number of credits](Self::credits()) per ballot
+/// - [Maximum number of votes](Self::max_votes()) per option
+///
+/// See [`QuadraticVotingBallot`] for a detailed description of parameters.
+///
+/// [Quadratic voting]: https://en.wikipedia.org/wiki/Quadratic_voting
+///
+/// # Examples
+///
+/// ```
+/// # use elastic_elgamal::{app::QuadraticVotingParams, group::Ristretto, Keypair};
+/// # use rand::thread_rng;
+/// let (receiver, _) = Keypair::<Ristretto>::generate(&mut thread_rng())
+///     .into_tuple();
+/// let mut params = QuadraticVotingParams::new(receiver, 5, 20);
+/// // 5 options, 20 credits.
+/// assert_eq!(params.options_count(), 5);
+/// assert_eq!(params.credits(), 20);
+/// // By default, max votes per option are determined based on credits
+/// assert_eq!(params.max_votes(), 4); // 4 < sqrt(20) < 5
+///
+/// // It is possible to reduce max votes per ballot.
+/// params.set_max_votes(3);
+/// assert_eq!(params.max_votes(), 3);
+/// ```
+#[derive(Debug, Clone)]
+pub struct QuadraticVotingParams<G: Group> {
+    vote_count_range: PreparedRange<G>,
+    credit_range: PreparedRange<G>,
+    options_count: usize,
+    receiver: PublicKey<G>,
+}
+
+impl<G: Group> QuadraticVotingParams<G> {
+    /// Creates new parameters for the specified number of `credits` allocated per voter.
+    ///
+    /// The maximum number of votes per option is automatically set as `floor(sqrt(credits))`;
+    /// it can be changed via [`Self::set_max_votes()`].
+    ///
+    /// # Panics
+    ///
+    /// Panics if the number of options or credits is zero.
+    pub fn new(receiver: PublicKey<G>, options: usize, credits: u64) -> Self {
+        assert!(options > 0, "Number of options must be positive");
+        assert!(credits > 0, "Number of credits must be positive");
+
+        let max_votes = isqrt(credits);
+        let vote_count_range = RangeDecomposition::optimal(max_votes + 1);
+        let credit_range = RangeDecomposition::optimal(credits + 1);
+        Self {
+            vote_count_range: vote_count_range.into(),
+            credit_range: credit_range.into(),
+            options_count: options,
+            receiver,
+        }
+    }
+
+    /// Returns the public key for which the [`QuadraticVotingBallot`]s are encrypted.
+    pub fn receiver(&self) -> &PublicKey<G> {
+        &self.receiver
+    }
+
+    /// Returns the number of options.
+    pub fn options_count(&self) -> usize {
+        self.options_count
+    }
+
+    /// Returns the number of credits per ballot.
+    pub fn credits(&self) -> u64 {
+        self.credit_range.decomposition().upper_bound() - 1
+    }
+
+    /// Returns the maximum number of votes per option.
+    pub fn max_votes(&self) -> u64 {
+        self.vote_count_range.decomposition().upper_bound() - 1
+    }
+
+    /// Sets the maximum number of votes per option.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `max_votes * max_votes` exceeds `credits`; in this case, this number of votes
+    /// cannot be cast for a single option.
+    pub fn set_max_votes(&mut self, max_votes: u64) {
+        assert!(
+            max_votes * max_votes <= self.credits(),
+            "Vote bound {max_votes} is too large; its square is greater than credit bound {}",
+            self.credits()
+        );
+        self.vote_count_range = RangeDecomposition::optimal(max_votes + 1).into();
+    }
+
+    fn check_options_count(&self, actual_count: usize) -> Result<(), QuadraticVotingError> {
+        if self.options_count == actual_count {
+            Ok(())
+        } else {
+            Err(QuadraticVotingError::OptionsLenMismatch {
+                expected: self.options_count,
+                actual: actual_count,
+            })
+        }
+    }
+}
+
+/// Integer square root of a `u64` number. Uses the digit-by-digit calculation method in base 2;
+/// see https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2)
+fn isqrt(mut x: u64) -> u64 {
+    let mut root = 0_u64;
+    let mut power_of_4 = 1_u64 << 62;
+    while power_of_4 > x {
+        power_of_4 /= 4;
+    }
+    while power_of_4 > 0 {
+        if x >= root + power_of_4 {
+            x -= root + power_of_4;
+            root = root / 2 + power_of_4;
+        } else {
+            root /= 2;
+        }
+        power_of_4 /= 4;
+    }
+    root
+}
+
+/// Encrypted ballot for [quadratic voting] together with zero-knowledge proofs of correctness.
+///
+/// # Overview
+///
+/// Quadratic voting assumes a non-exclusive selection among `n >= 1` predefined options.
+/// Unlike with [`MultiChoice`](crate::app::MultiChoice) polling, a voter can cast more than
+/// one vote for a single option. The additional votes come at a quadratic expense for the voter,
+/// however. For example, to cast 4 votes for a certain option, a voter needs 16 credits,
+/// while single votes for 4 different options are worth 4 credits.
+///
+/// The `QuadraticVotingBallot` construction assumes that there is a known number of credits
+/// for each ballot (e.g., it is uniform across all eligible voters), and that votes are tallied
+/// by a tallier or a federation of talliers that jointly control a [`SecretKey`](crate::SecretKey).
+/// As such, the ballot is represented as follows:
+///
+/// - ElGamal [`Ciphertext`] for each of `n` options (can be summed across all valid ballots
+///   to get vote totals that will be decrypted by the talliers)
+/// - [`RangeProof`] for each of these ciphertexts proving that the encrypted value
+///   is in range `0..=V`
+/// - [`Ciphertext`] for the number of credits used by the ballot, and a [`RangeProof`]
+///   that it is in range `0..=C`
+/// - Zero-knowledge [`SumOfSquaresProof`] proving that the encrypted number of credits is computed
+///   correctly, i.e., as a sum of squares of the values encrypted in the vote ciphertexts.
+///
+/// Here, `C` (the number of credits) and `V` (max votes per option) are the protocol parameters
+/// encapsulated in [`QuadraticVotingParams`].
+///
+/// [quadratic voting]: https://en.wikipedia.org/wiki/Quadratic_voting
+///
+/// # Examples
+///
+/// ```
+/// # use elastic_elgamal::{
+/// #     app::{QuadraticVotingParams, QuadraticVotingBallot}, group::Ristretto, Keypair,
+/// #     DiscreteLogTable,
+/// # };
+/// # use rand::thread_rng;
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// let mut rng = thread_rng();
+/// let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+/// let params = QuadraticVotingParams::new(pk, 5, 20);
+/// // 5 options, 20 credits (= 4 max votes per option)
+/// assert_eq!(params.max_votes(), 4);
+///
+/// let votes = [4, 0, 0, 1, 1];
+/// let ballot = QuadraticVotingBallot::new(&params, &votes, &mut rng);
+/// let encrypted: Vec<_> = ballot.verify(&params)?.collect();
+///
+/// assert_eq!(encrypted.len(), 5);
+/// let lookup = DiscreteLogTable::new(0..=params.max_votes());
+/// let decrypted: Vec<_> = encrypted
+///     .into_iter()
+///     .map(|vote| sk.decrypt(vote, &lookup).unwrap())
+///     .collect();
+/// assert_eq!(decrypted, votes);
+/// # Ok(())
+/// # }
+/// ```
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct QuadraticVotingBallot<G: Group> {
+    votes: Vec<CiphertextWithRangeProof<G>>,
+    credit: CiphertextWithRangeProof<G>,
+    credit_equivalence_proof: SumOfSquaresProof<G>,
+}
+
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+struct CiphertextWithRangeProof<G: Group> {
+    ciphertext: Ciphertext<G>,
+    range_proof: RangeProof<G>,
+}
+
+impl<G: Group> CiphertextWithRangeProof<G> {
+    fn new(ciphertext: Ciphertext<G>, range_proof: RangeProof<G>) -> Self {
+        Self {
+            ciphertext,
+            range_proof,
+        }
+    }
+}
+
+impl<G: Group> QuadraticVotingBallot<G> {
+    /// Creates a ballot based on the provided parameters and voter's `votes`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the length of `votes` differs from the number of options in `params`.
+    pub fn new<R: CryptoRng + RngCore>(
+        params: &QuadraticVotingParams<G>,
+        votes: &[u64],
+        rng: &mut R,
+    ) -> Self {
+        assert_eq!(
+            votes.len(),
+            params.options_count,
+            "Mismatch between expected and actual number of choices"
+        );
+        let credit = votes.iter().map(|&x| x * x).sum::<u64>();
+
+        let votes: Vec<_> = votes
+            .iter()
+            .map(|&vote_count| {
+                let (ciphertext, proof) = RangeProof::new(
+                    &params.receiver,
+                    &params.vote_count_range,
+                    vote_count,
+                    &mut Transcript::new(b"quadratic_voting_variant"),
+                    rng,
+                );
+                (ciphertext.generalize(), proof)
+            })
+            .collect();
+        let (credit, credit_range_proof) = RangeProof::new(
+            &params.receiver,
+            &params.credit_range,
+            credit,
+            &mut Transcript::new(b"quadratic_voting_credit_range"),
+            rng,
+        );
+        let credit = credit.generalize();
+
+        let credit_equivalence_proof = SumOfSquaresProof::new(
+            votes.iter().map(|(ciphertext, _)| ciphertext),
+            &credit,
+            &params.receiver,
+            &mut Transcript::new(b"quadratic_voting_credit_equiv"),
+            rng,
+        );
+
+        Self {
+            votes: votes
+                .into_iter()
+                .map(|(ciphertext, proof)| CiphertextWithRangeProof::new(ciphertext.into(), proof))
+                .collect(),
+            credit: CiphertextWithRangeProof::new(credit.into(), credit_range_proof),
+            credit_equivalence_proof,
+        }
+    }
+
+    /// Verifies this ballot against the provided parameters.
+    ///
+    /// # Errors
+    ///
+    /// - Returns an error if verification fails.
+    pub fn verify(
+        &self,
+        params: &QuadraticVotingParams<G>,
+    ) -> Result<impl Iterator<Item = Ciphertext<G>> + '_, QuadraticVotingError> {
+        params.check_options_count(self.votes.len())?;
+
+        for (i, vote_count) in self.votes.iter().enumerate() {
+            vote_count
+                .range_proof
+                .verify(
+                    &params.receiver,
+                    &params.vote_count_range,
+                    vote_count.ciphertext,
+                    &mut Transcript::new(b"quadratic_voting_variant"),
+                )
+                .map_err(|error| QuadraticVotingError::Variant { index: i, error })?;
+        }
+
+        self.credit
+            .range_proof
+            .verify(
+                &params.receiver,
+                &params.credit_range,
+                self.credit.ciphertext,
+                &mut Transcript::new(b"quadratic_voting_credit_range"),
+            )
+            .map_err(QuadraticVotingError::CreditRange)?;
+
+        self.credit_equivalence_proof
+            .verify(
+                self.votes.iter().map(|c| &c.ciphertext),
+                &self.credit.ciphertext,
+                &params.receiver,
+                &mut Transcript::new(b"quadratic_voting_credit_equiv"),
+            )
+            .map_err(QuadraticVotingError::CreditEquivalence)?;
+
+        Ok(self.votes.iter().map(|c| c.ciphertext))
+    }
+}
+
+/// Errors that can occur when verifying [`QuadraticVotingBallot`]s.
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum QuadraticVotingError {
+    /// Error verifying a [`RangeProof`] for a vote for a particular option.
+    Variant {
+        /// Zero-based option index.
+        index: usize,
+        /// Error that occurred during range proof verification.
+        error: VerificationError,
+    },
+    /// Error verifying a [`RangeProof`] for credits.
+    CreditRange(VerificationError),
+    /// Error verifying the [proof of equivalence](SumOfSquaresProof) for credits.
+    CreditEquivalence(VerificationError),
+    /// Mismatch between expected and actual number of options in the ballot.
+    OptionsLenMismatch {
+        /// Expected number of options.
+        expected: usize,
+        /// Actual number of options.
+        actual: usize,
+    },
+}
+
+impl fmt::Display for QuadraticVotingError {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::Variant { index, error } => write!(
+                formatter,
+                "error verifying range proof for option #{}: {error}",
+                *index + 1
+            ),
+            Self::CreditRange(err) => {
+                write!(formatter, "error verifying range proof for credits: {err}")
+            }
+            Self::CreditEquivalence(err) => {
+                write!(formatter, "error verifying credit equivalence proof: {err}")
+            }
+            Self::OptionsLenMismatch { expected, actual } => write!(
+                formatter,
+                "number of options in the ballot ({actual}) differs from expected ({expected})"
+            ),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for QuadraticVotingError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        match self {
+            Self::Variant { error, .. }
+            | Self::CreditRange(error)
+            | Self::CreditEquivalence(error) => Some(error),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{
+        group::{ElementOps, Ristretto},
+        DiscreteLogTable, Keypair,
+    };
+
+    use rand::thread_rng;
+
+    #[test]
+    fn isqrt_is_correct() {
+        let samples = (0..1_000).chain((0..1_000).map(|x| x * 1_000)).chain([
+            u64::MAX,
+            u64::MAX - 1,
+            1 << 63,
+            1 << 62,
+            (1 << 62) - 1,
+        ]);
+        for sample in samples {
+            let sqrt = isqrt(sample);
+            assert!(sqrt * sqrt <= sample, "sqrt({sample}) ?= {sqrt}");
+
+            let next_square = (sqrt + 1).checked_mul(sqrt + 1);
+            assert!(
+                next_square.map_or(true, |sq| sq > sample),
+                "sqrt({sample}) ?= {sqrt}"
+            );
+        }
+    }
+
+    #[test]
+    fn quadratic_voting() {
+        let mut rng = thread_rng();
+        let (pk, sk) = Keypair::generate(&mut rng).into_tuple();
+        let params = QuadraticVotingParams::<Ristretto>::new(pk, 5, 25);
+        let ballot = QuadraticVotingBallot::new(&params, &[1, 3, 0, 3, 2], &mut rng);
+
+        let choices = ballot.verify(&params).unwrap();
+        let lookup_table = DiscreteLogTable::new(0..=5);
+        let choices: Vec<_> = choices
+            .map(|c| sk.decrypt(c, &lookup_table).unwrap())
+            .collect();
+        assert_eq!(choices, [1, 3, 0, 3, 2]);
+
+        {
+            let mut bogus_ballot = ballot.clone();
+            bogus_ballot.votes[0].ciphertext.blinded_element += Ristretto::generator();
+            let err = bogus_ballot.verify(&params).map(drop).unwrap_err();
+            assert!(matches!(
+                err,
+                QuadraticVotingError::Variant {
+                    index: 0,
+                    error: VerificationError::ChallengeMismatch
+                }
+            ));
+        }
+
+        {
+            let mut bogus_ballot = ballot.clone();
+            bogus_ballot.credit.ciphertext.blinded_element -= Ristretto::generator();
+            let err = bogus_ballot.verify(&params).map(drop).unwrap_err();
+            assert!(matches!(err, QuadraticVotingError::CreditRange(_)));
+        }
+
+        let mut bogus_ballot = ballot.clone();
+        let (ciphertext, proof) = RangeProof::new(
+            &params.receiver,
+            &params.vote_count_range,
+            3, // << overly large
+            &mut Transcript::new(b"quadratic_voting_variant"),
+            &mut rng,
+        );
+        bogus_ballot.votes[0] = CiphertextWithRangeProof::new(ciphertext.into(), proof);
+
+        let err = bogus_ballot.verify(&params).map(drop).unwrap_err();
+        assert!(matches!(err, QuadraticVotingError::CreditEquivalence(_)));
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/decryption.rs.html b/src/elastic_elgamal/decryption.rs.html new file mode 100644 index 0000000..cc1a986 --- /dev/null +++ b/src/elastic_elgamal/decryption.rs.html @@ -0,0 +1,443 @@ +decryption.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+
//! Verifiable decryption.
+
+use merlin::Transcript;
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+#[cfg(feature = "serde")]
+use crate::serde::ElementHelper;
+use crate::{
+    alloc::{vec, Vec},
+    group::Group,
+    proofs::{LogEqualityProof, TranscriptForGroup},
+    Ciphertext, DiscreteLogTable, Keypair, PublicKey, VerificationError,
+};
+
+/// Verifiable decryption for a certain [`Ciphertext`] in the ElGamal encryption scheme.
+/// Usable both for standalone proofs and in threshold encryption.
+///
+/// # Construction
+///
+/// Decryption is represented by a single group element – the result of combining
+/// a [`SecretKey`](crate::SecretKey) scalar `x` with the random element of the ciphertext `R`
+/// (i.e., `D = [x]R`, the Diffie – Hellman construction).
+/// This element can retrieved using [`Self::as_element()`] and applied to a ciphertext using
+/// [`Self::decrypt()`] or [`Self::decrypt_to_element()`].
+///
+/// The decryption can be proven with the help of a standard [`LogEqualityProof`]. Indeed,
+/// to prove the validity of decryption, it is sufficient to prove `dlog_R(D) = dlog_G(K)`,
+/// where `G` is the conventional group generator and `K = [x]G` is the public key for encryption.
+///
+/// # Examples
+///
+/// `VerifiableDecryption` can be used either within the threshold encryption scheme provided by
+/// the [`sharing`](crate::sharing) module, or independently (for example, if another approach
+/// to secret sharing is used, or if the encryption key is not shared at all).
+/// An example of standalone usage is outlined below:
+///
+/// ```
+/// # use elastic_elgamal::{
+/// #     group::Ristretto, CandidateDecryption, VerifiableDecryption, Keypair, DiscreteLogTable,
+/// # };
+/// # use merlin::Transcript;
+/// # use rand::thread_rng;
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// let mut rng = thread_rng();
+/// let keys = Keypair::<Ristretto>::generate(&mut rng);
+/// // Suppose the `keys` holder wants to prove decryption
+/// // of the following ciphertext:
+/// let ciphertext = keys.public().encrypt(42_u64, &mut rng);
+/// let (decryption, proof) = VerifiableDecryption::new(
+///     ciphertext,
+///     &keys,
+///     &mut Transcript::new(b"decryption"),
+///     &mut rng,
+/// );
+///
+/// // This proof can then be universally verified:
+/// let candidate_decryption = CandidateDecryption::from(decryption);
+/// let decryption = candidate_decryption.verify(
+///     ciphertext,
+///     keys.public(),
+///     &proof,
+///     &mut Transcript::new(b"decryption"),
+/// )?;
+/// assert_eq!(
+///     decryption.decrypt(ciphertext, &DiscreteLogTable::new(0..50)),
+///     Some(42)
+/// );
+/// # Ok(())
+/// # }
+/// ```
+#[derive(Debug, Clone, Copy)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct VerifiableDecryption<G: Group> {
+    #[cfg_attr(feature = "serde", serde(with = "ElementHelper::<G>"))]
+    dh_element: G::Element,
+}
+
+impl<G: Group> VerifiableDecryption<G> {
+    pub(crate) fn from_element(dh_element: G::Element) -> Self {
+        Self { dh_element }
+    }
+
+    /// Creates a decryption for the specified `ciphertext` under `keys` together with
+    /// a zero-knowledge proof of validity.
+    ///
+    /// See [`CandidateDecryption::verify()`] for the verification counterpart.
+    pub fn new<R: CryptoRng + RngCore>(
+        ciphertext: Ciphertext<G>,
+        keys: &Keypair<G>,
+        transcript: &mut Transcript,
+        rng: &mut R,
+    ) -> (Self, LogEqualityProof<G>) {
+        // All inputs except from `ciphertext.blinded_element` are committed in the `proof`,
+        // and it is not necessary to commit in order to allow iteratively recomputing
+        // the ciphertext.
+        transcript.start_proof(b"decryption_with_custom_key");
+
+        let dh_element = ciphertext.random_element * keys.secret().expose_scalar();
+        let proof = LogEqualityProof::new(
+            &PublicKey::from_element(ciphertext.random_element),
+            keys.secret(),
+            (keys.public().as_element(), dh_element),
+            transcript,
+            rng,
+        );
+
+        (Self { dh_element }, proof)
+    }
+
+    /// Returns the group element encapsulated in this decryption.
+    pub fn as_element(&self) -> &G::Element {
+        &self.dh_element
+    }
+
+    /// Serializes this decryption into bytes.
+    pub fn to_bytes(self) -> Vec<u8> {
+        let mut bytes = vec![0_u8; G::ELEMENT_SIZE];
+        G::serialize_element(&self.dh_element, &mut bytes);
+        bytes
+    }
+
+    /// Decrypts the provided ciphertext and returns the produced group element.
+    ///
+    /// As the ciphertext does not include a MAC or another way to assert integrity,
+    /// this operation cannot fail. If the ciphertext is not produced properly (e.g., it targets
+    /// another receiver), the returned group element will be garbage.
+    pub fn decrypt_to_element(&self, encrypted: Ciphertext<G>) -> G::Element {
+        encrypted.blinded_element - self.dh_element
+    }
+
+    /// Decrypts the provided ciphertext and returns the original encrypted value.
+    ///
+    /// `lookup_table` is used to find encrypted values based on the original decrypted
+    /// group element. That is, it must contain all valid plaintext values. If the value
+    /// is not in the table, this method will return `None`.
+    pub fn decrypt(
+        &self,
+        encrypted: Ciphertext<G>,
+        lookup_table: &DiscreteLogTable<G>,
+    ) -> Option<u64> {
+        lookup_table.get(&self.decrypt_to_element(encrypted))
+    }
+}
+
+/// Candidate for a [`VerifiableDecryption`] that is not yet verified. This presentation should be
+/// used for decryption data retrieved from an untrusted source.
+///
+/// Decryption data can be verified using [`Self::verify()`]. The threshold encryption scheme
+/// implemented in the [`sharing`](crate::sharing) module has its own verification procedure
+/// in [`PublicKeySet`].
+///
+/// [`PublicKeySet`]: crate::sharing::PublicKeySet
+///
+/// # Examples
+///
+/// See [`VerifiableDecryption`] for an example of usage.
+#[derive(Debug, Clone, Copy)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(transparent, bound = ""))]
+pub struct CandidateDecryption<G: Group> {
+    inner: VerifiableDecryption<G>,
+}
+
+impl<G: Group> CandidateDecryption<G> {
+    /// Deserializes decryption data from `bytes`. Returns `None` if the data is malformed.
+    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
+        if bytes.len() == G::ELEMENT_SIZE {
+            let dh_element = G::deserialize_element(bytes)?;
+            Some(Self {
+                inner: VerifiableDecryption { dh_element },
+            })
+        } else {
+            None
+        }
+    }
+
+    pub(super) fn dh_element(self) -> G::Element {
+        self.inner.dh_element
+    }
+
+    /// Verifies this as decryption for `ciphertext` under `key` using the provided
+    /// zero-knowledge `proof`.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if `proof` does not verify.
+    pub fn verify(
+        self,
+        ciphertext: Ciphertext<G>,
+        key: &PublicKey<G>,
+        proof: &LogEqualityProof<G>,
+        transcript: &mut Transcript,
+    ) -> Result<VerifiableDecryption<G>, VerificationError> {
+        transcript.start_proof(b"decryption_with_custom_key");
+
+        let dh_element = self.dh_element();
+        proof.verify(
+            &PublicKey::from_element(ciphertext.random_element),
+            (key.as_element(), dh_element),
+            transcript,
+        )?;
+        Ok(self.inner)
+    }
+
+    /// Converts this candidate decryption into a [`VerifiableDecryption`]
+    /// **without** verifying it.
+    /// This is only semantically correct if the data was verified in some other way.
+    pub fn into_unchecked(self) -> VerifiableDecryption<G> {
+        self.inner
+    }
+}
+
+impl<G: Group> From<VerifiableDecryption<G>> for CandidateDecryption<G> {
+    fn from(decryption: VerifiableDecryption<G>) -> Self {
+        Self { inner: decryption }
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/dkg.rs.html b/src/elastic_elgamal/dkg.rs.html new file mode 100644 index 0000000..ee97a7f --- /dev/null +++ b/src/elastic_elgamal/dkg.rs.html @@ -0,0 +1,1363 @@ +dkg.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+
//! Committed Pedersen's distributed key generation (DKG).
+//!
+//! DKG allows to securely generate shared secret without a need for a trusted
+//! dealer. Compare with Feldman's verifiable secret sharing implemented in the [`sharing`] module
+//! which requires a trusted dealer.
+//!
+//! This implementation is based on [Pedersen's DKG], which was shown by [Gennaro et al.]
+//! to contain a flaw allowing an adversary to bias distribution of the shared public key.
+//! We try to prevent this kind of possible attacks by forcing the parties to
+//! commit to their public key shares before receiving public shares from other
+//! parties.
+//!
+//! [Pedersen's DKG]: https://link.springer.com/content/pdf/10.1007/3-540-46416-6_47.pdf
+//! [Gennaro et al.]: https://link.springer.com/content/pdf/10.1007/3-540-48910-X_21.pdf
+//!
+//! # Examples
+//!
+//! Decentralized key generation for 2-of-3 threshold encryption.
+//!
+//! ```
+//! # use elastic_elgamal::{
+//! #     group::Ristretto, dkg::*, sharing::Params,
+//! # };
+//! # use rand::thread_rng;
+//! # use std::error::Error as StdError;
+//! # fn main() -> Result<(), Box<dyn StdError>> {
+//! let mut rng = thread_rng();
+//! let params = Params::new(3, 2);
+//!
+//! // Initialize participants.
+//! let participants = (0..3).map(|i| {
+//!     ParticipantCollectingCommitments::<Ristretto>::new(params, i, &mut rng)
+//! });
+//! let mut participants: Vec<_> = participants.collect();
+//!
+//! // Publish commitments from all participants...
+//! let commitments: Vec<_> = participants
+//!     .iter()
+//!     .map(|participant| participant.commitment())
+//!     .collect();
+//! // ...and consume them from each participant's perspective.
+//! for (i, participant) in participants.iter_mut().enumerate() {
+//!     for (j, &commitment) in commitments.iter().enumerate() {
+//!         if i != j {
+//!             participant.insert_commitment(j, commitment);
+//!         }
+//!     }
+//! }
+//!
+//! // Transition all participants to the next stage: exchanging polynomials.
+//! let mut participants: Vec<_> = participants
+//!     .into_iter()
+//!     .map(|participant| participant.finish_commitment_phase())
+//!     .collect();
+//! // Publish each participant's polynomial...
+//! let infos: Vec<_> = participants
+//!     .iter()
+//!     .map(|participant| participant.public_info().into_owned())
+//!     .collect();
+//! // ...and consume them from each participant's perspective.
+//! for (i, participant) in participants.iter_mut().enumerate() {
+//!     for (j, info) in infos.iter().enumerate() {
+//!         if i != j {
+//!             participant.insert_public_polynomial(j, info.clone())?;
+//!         }
+//!     }
+//! }
+//!
+//! // Transition all participants to the final phase: exchanging secrets.
+//! let mut participants: Vec<_> = participants
+//!     .into_iter()
+//!     .map(|participant| participant.finish_polynomials_phase())
+//!     .collect();
+//! // Exchange shares (this should happen over secure peer-to-peer channels).
+//! for i in 0..3 {
+//!     for j in 0..3 {
+//!         if i == j { continue; }
+//!         let share = participants[i].secret_share_for_participant(j);
+//!         participants[j].insert_secret_share(i, share)?;
+//!     }
+//! }
+//!
+//! // Finalize all participants.
+//! let participants = participants
+//!     .into_iter()
+//!     .map(|participant| participant.complete())
+//!     .collect::<Result<Vec<_>, _>>()?;
+//! // Check that the shared key is the same for all participants.
+//! let expected_key = participants[0].key_set().shared_key();
+//! for participant in &participants {
+//!     assert_eq!(participant.key_set().shared_key(), expected_key);
+//! }
+//!
+//! // Participants can then jointly decrypt messages as showcased
+//! // in the example for the `sharing` module.
+//! # Ok(())
+//! # }
+//! ```
+
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+use sha2::{Digest, Sha256};
+use zeroize::Zeroizing;
+
+use core::fmt;
+
+#[cfg(feature = "serde")]
+use crate::serde::{ElementHelper, VecHelper};
+use crate::{
+    alloc::{vec, Cow, Vec},
+    group::Group,
+    proofs::ProofOfPossession,
+    sharing::{self, ActiveParticipant, Dealer, Params, PublicKeySet, PublicPolynomial},
+    PublicKey, SecretKey,
+};
+
+/// Errors that can occur during the distributed key generation.
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum Error {
+    /// Secret received from the party does not correspond to their commitment via
+    /// the public polynomial.
+    InvalidSecret,
+    /// Provided commitment does not correspond to the party's public key share.
+    InvalidCommitment,
+    /// Secret share for this participant was already provided.
+    DuplicateShare,
+    /// Provided proof of possession or public polynomial is malformed.
+    MalformedParticipantProof(sharing::Error),
+    /// Public shares obtained from accumulated public polynomial are inconsistent.
+    InconsistentPublicShares(sharing::Error),
+}
+
+impl fmt::Display for Error {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::InvalidSecret => formatter.write_str(
+                "secret received from the party does not correspond to their commitment via \
+                public polynomial",
+            ),
+            Self::InvalidCommitment => formatter.write_str(
+                "public polynomial received from one of the parties does not correspond \
+                to their commitment",
+            ),
+            Self::DuplicateShare => {
+                formatter.write_str("secret share for this participant was already provided")
+            }
+            Self::MalformedParticipantProof(err) => write!(
+                formatter,
+                "provided proof of possession or public polynomial is malformed: {err}"
+            ),
+            Self::InconsistentPublicShares(err) => write!(
+                formatter,
+                "public shares obtained from accumulated public polynomial \
+                 are inconsistent: {err}"
+            ),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for Error {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        match self {
+            Self::InconsistentPublicShares(err) | Self::MalformedParticipantProof(err) => Some(err),
+            _ => None,
+        }
+    }
+}
+
+fn create_commitment<G: Group>(element: &G::Element, opening: &[u8]) -> [u8; 32] {
+    let mut hasher = Sha256::new();
+    let mut bytes = vec![0_u8; G::ELEMENT_SIZE];
+    G::serialize_element(element, &mut bytes);
+    hasher.update(&bytes);
+    hasher.update(opening);
+    hasher.finalize().into()
+}
+
+/// Opening for a hash commitment used in Pedersen's distributed key generation.
+#[derive(Debug, Clone)]
+pub struct Opening(pub(crate) Zeroizing<[u8; 32]>);
+
+/// Participant state during the first stage of the committed Pedersen's distributed key generation.
+///
+/// During this stage, participants exchange commitments to their public keys via
+/// a public bulletin board (e.g., a blockchain).
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct ParticipantCollectingCommitments<G: Group> {
+    params: Params,
+    index: usize,
+    dealer: Dealer<G>,
+    commitments: Vec<Option<[u8; 32]>>,
+    opening: Opening,
+}
+
+impl<G: Group> ParticipantCollectingCommitments<G> {
+    /// Instantiates a distributed key generation participant.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `index` is greater or equal to the number of shares.
+    pub fn new<R: CryptoRng + RngCore>(params: Params, index: usize, rng: &mut R) -> Self {
+        assert!(index < params.shares);
+
+        let dealer = Dealer::new(params, rng);
+        let mut opening = Zeroizing::new([0_u8; 32]);
+        rng.fill_bytes(&mut *opening);
+
+        let mut commitments = vec![None; params.shares];
+        let (public_poly, _) = dealer.public_info();
+        commitments[index] = Some(create_commitment::<G>(&public_poly[0], opening.as_slice()));
+        Self {
+            params,
+            index,
+            dealer,
+            commitments,
+            opening: Opening(opening),
+        }
+    }
+
+    /// Returns params of this threshold ElGamal encryption scheme.
+    pub fn params(&self) -> &Params {
+        &self.params
+    }
+
+    /// Returns 0-based index of this participant.
+    pub fn index(&self) -> usize {
+        self.index
+    }
+
+    /// Returns the commitment of participant's share of the joint public key.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the commitment is missing which can only happen if this struct got corrupted
+    /// (e.g., after deserialization).
+    pub fn commitment(&self) -> [u8; 32] {
+        self.commitments[self.index].unwrap()
+    }
+
+    /// Inserts a commitment from the participant with index `participant_index`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if commitment for given participant was already provided or
+    /// `participant_index` is out of bounds.
+    pub fn insert_commitment(&mut self, participant_index: usize, commitment: [u8; 32]) {
+        assert!(
+            self.commitments[participant_index].is_none(),
+            "Commitment for participant {participant_index} is already provided"
+        );
+        self.commitments[participant_index] = Some(commitment);
+    }
+
+    /// Returns indices of parties whose commitments were not provided.
+    pub fn missing_commitments(&self) -> impl Iterator<Item = usize> + '_ {
+        self.commitments
+            .iter()
+            .enumerate()
+            .filter_map(|(i, commitment)| commitment.is_none().then_some(i))
+    }
+
+    /// Proceeds to the next step of the DKG protocol, in which participants exchange public
+    /// polynomials.
+    ///
+    /// # Panics
+    ///
+    /// Panics if any commitments are missing. If this is not known statically, check
+    /// with [`Self::missing_commitments()`] before calling this method.
+    pub fn finish_commitment_phase(self) -> ParticipantCollectingPolynomials<G> {
+        if let Some(missing_idx) = self.missing_commitments().next() {
+            panic!("Missing commitment for participant {missing_idx}");
+        }
+
+        let (public_polynomial, _) = self.dealer.public_info();
+        let mut public_polynomials = vec![None; self.params.shares];
+        public_polynomials[self.index] = Some(PublicPolynomial::new(public_polynomial));
+        ParticipantCollectingPolynomials {
+            params: self.params,
+            index: self.index,
+            dealer: self.dealer,
+            opening: self.opening,
+            commitments: self.commitments.into_iter().map(Option::unwrap).collect(),
+            // ^ `unwrap()` is safe due to the above checks
+            public_polynomials,
+        }
+    }
+}
+
+/// Public participant information in the distributed key generation protocol. Returned by
+/// [`ParticipantCollectingPolynomials::public_info()`].
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct PublicInfo<'a, G: Group> {
+    /// Participant's public polynomial.
+    #[cfg_attr(feature = "serde", serde(with = "VecHelper::<ElementHelper<G>, 1>"))]
+    pub polynomial: Vec<G::Element>,
+    /// Proof of possession for the secret polynomial that corresponds to `polynomial`.
+    pub proof_of_possession: Cow<'a, ProofOfPossession<G>>,
+    /// Opening for the participant's key commitment.
+    pub opening: Opening,
+}
+
+impl<G: Group> PublicInfo<'_, G> {
+    /// Converts this information to the owned form.
+    pub fn into_owned(self) -> PublicInfo<'static, G> {
+        PublicInfo {
+            polynomial: self.polynomial,
+            proof_of_possession: Cow::Owned(self.proof_of_possession.into_owned()),
+            opening: self.opening,
+        }
+    }
+}
+
+/// Participant state during the second stage of the committed Pedersen's distributed key generation.
+///
+/// During this stage, participants exchange public polynomials and openings for the commitments
+/// exchanged on the previous stage. The exchange happens using a public bulletin board
+/// (e.g., a blockchain).
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct ParticipantCollectingPolynomials<G: Group> {
+    params: Params,
+    index: usize,
+    dealer: Dealer<G>,
+    opening: Opening,
+    commitments: Vec<[u8; 32]>,
+    public_polynomials: Vec<Option<PublicPolynomial<G>>>,
+}
+
+impl<G: Group> ParticipantCollectingPolynomials<G> {
+    /// Returns params of this threshold ElGamal encryption scheme.
+    pub fn params(&self) -> &Params {
+        &self.params
+    }
+
+    /// Returns 0-based index of this participant.
+    pub fn index(&self) -> usize {
+        self.index
+    }
+
+    /// Returns public participant information: participant's public polynomial,
+    /// proof of possession for the corresponding secret polynomial and the opening of
+    /// the participant's public key share commitment.
+    pub fn public_info(&self) -> PublicInfo<'_, G> {
+        let (polynomial, proof) = self.dealer.public_info();
+        PublicInfo {
+            polynomial,
+            proof_of_possession: Cow::Borrowed(proof),
+            opening: self.opening.clone(),
+        }
+    }
+
+    /// Returns the indices of parties whose public polynomials were not provided.
+    pub fn missing_public_polynomials(&self) -> impl Iterator<Item = usize> + '_ {
+        self.public_polynomials
+            .iter()
+            .enumerate()
+            .filter_map(|(i, poly)| poly.is_none().then_some(i))
+    }
+
+    /// Inserts public polynomial from participant with index `participant_index`
+    /// their proof of possession of the public polynomial and opening of
+    /// their previously provided commitment.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if provided polynomial doesn't correspond to the previous
+    /// commitment or the proof of possession is not valid.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `participant_index` is out of bounds.
+    pub fn insert_public_polynomial(
+        &mut self,
+        participant_index: usize,
+        info: PublicInfo<'_, G>,
+    ) -> Result<(), Error> {
+        let opening = info.opening.0.as_slice();
+        let commitment = create_commitment::<G>(&info.polynomial[0], opening);
+        if self.commitments[participant_index] != commitment {
+            // provided commitment doesn't match the given public key share
+            return Err(Error::InvalidCommitment);
+        }
+
+        PublicKeySet::validate(self.params, &info.polynomial, &info.proof_of_possession)
+            .map_err(Error::MalformedParticipantProof)?;
+        self.public_polynomials[participant_index] = Some(PublicPolynomial::new(info.polynomial));
+        Ok(())
+    }
+
+    /// Proceeds to the next step of the DKG protocol, in which participants exchange
+    /// secret shares.
+    ///
+    /// # Panics
+    ///
+    /// Panics if any public polynomials are missing. If this is not known statically, check
+    /// with [`Self::missing_public_polynomials()`] before calling this method.
+    pub fn finish_polynomials_phase(self) -> ParticipantExchangingSecrets<G> {
+        if let Some(missing_idx) = self.missing_public_polynomials().next() {
+            panic!("Missing public polynomial for participant {missing_idx}");
+        }
+
+        let mut shares_received = vec![false; self.params.shares];
+        shares_received[self.index] = true;
+        ParticipantExchangingSecrets {
+            params: self.params,
+            index: self.index,
+            public_polynomials: self.public_polynomials.into_iter().flatten().collect(),
+            accumulated_share: self.dealer.secret_share_for_participant(self.index),
+            dealer: self.dealer,
+            shares_received,
+        }
+    }
+}
+
+/// Participant state during the third and final stage of the committed Pedersen's
+/// distributed key generation.
+///
+/// During this stage, participants exchange secret shares corresponding to the polynomials
+/// exchanged on the previous stage. The exchange happens using secure peer-to-peer channels
+/// established between pairs of participants.
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct ParticipantExchangingSecrets<G: Group> {
+    params: Params,
+    index: usize,
+    dealer: Dealer<G>,
+    public_polynomials: Vec<PublicPolynomial<G>>,
+    accumulated_share: SecretKey<G>,
+    shares_received: Vec<bool>,
+}
+
+impl<G: Group> ParticipantExchangingSecrets<G> {
+    /// Returns params of this threshold ElGamal encryption scheme.
+    pub fn params(&self) -> &Params {
+        &self.params
+    }
+
+    /// Returns 0-based index of this participant.
+    pub fn index(&self) -> usize {
+        self.index
+    }
+
+    /// Returns the secret share for a participant with the specified `participant_index`.
+    pub fn secret_share_for_participant(&self, participant_index: usize) -> SecretKey<G> {
+        self.dealer.secret_share_for_participant(participant_index)
+    }
+
+    /// Returns indices of parties whose secret shares were not provided.
+    pub fn missing_shares(&self) -> impl Iterator<Item = usize> + '_ {
+        self.shares_received
+            .iter()
+            .enumerate()
+            .filter_map(|(i, &is_received)| (!is_received).then_some(i))
+    }
+
+    /// Inserts a secret share from participant with index `participant_index` and
+    /// checks that the share is valid.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if provided secret share doesn't correspond to the participant's
+    /// public polynomial collected on the previous step of the DKG protocol.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `participant_index` is out of bounds.
+    pub fn insert_secret_share(
+        &mut self,
+        participant_index: usize,
+        secret_share: SecretKey<G>,
+    ) -> Result<(), Error> {
+        if self.shares_received[participant_index] {
+            return Err(Error::DuplicateShare);
+        }
+
+        let polynomial = &self.public_polynomials[participant_index];
+        let idx = (self.index as u64 + 1).into();
+        let public_share = PublicKey::<G>::from_element(polynomial.value_at(idx));
+
+        if public_share.as_element() != G::mul_generator(secret_share.expose_scalar()) {
+            // point corresponding to the received secret share doesn't lie
+            // on the public polynomial
+            return Err(Error::InvalidSecret);
+        }
+
+        self.accumulated_share += secret_share;
+        self.shares_received[participant_index] = true;
+        Ok(())
+    }
+
+    /// Completes the distributed key generation protocol returning an [`ActiveParticipant`].
+    ///
+    /// # Errors
+    ///
+    /// Returns error if secret shares from some parties were not provided,
+    /// or if the [`PublicKeySet`] cannot be created from participants' keys.
+    ///
+    /// # Panics
+    ///
+    /// Panics if shares from any participants are missing. If this is not known statically, check
+    /// with [`Self::missing_shares()`] before calling this method.
+    pub fn complete(self) -> Result<ActiveParticipant<G>, Error> {
+        if let Some(missing_idx) = self.missing_shares().next() {
+            panic!("Missing secret share from participant {missing_idx}");
+        }
+
+        let accumulated_polynomial = self
+            .public_polynomials
+            .into_iter()
+            .reduce(|mut acc, poly| {
+                acc += &poly;
+                acc
+            })
+            .unwrap(); // safe: we have at least ourselves as a participant
+
+        let participant_keys = (0..self.params.shares)
+            .map(|idx| {
+                let idx = (idx as u64 + 1).into();
+                PublicKey::from_element(accumulated_polynomial.value_at(idx))
+            })
+            .collect();
+        let key_set = PublicKeySet::from_participants(self.params, participant_keys)
+            .map_err(Error::InconsistentPublicShares)?;
+
+        let active_participant =
+            ActiveParticipant::new(key_set, self.index, self.accumulated_share)
+                .map_err(Error::InconsistentPublicShares)?;
+        Ok(active_participant)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::thread_rng;
+
+    use super::*;
+    use crate::{encryption::DiscreteLogTable, group::Ristretto, sharing::Params};
+
+    #[test]
+    fn dkg_shared_2_of_3_key() {
+        let mut rng = thread_rng();
+        let params = Params::new(3, 2);
+
+        let mut alice = ParticipantCollectingCommitments::<Ristretto>::new(params, 0, &mut rng);
+        assert_eq!(alice.params().shares, params.shares);
+        assert_eq!(alice.params().threshold, params.threshold);
+        assert_eq!(alice.index(), 0);
+        let mut bob = ParticipantCollectingCommitments::<Ristretto>::new(params, 1, &mut rng);
+        assert_eq!(bob.index(), 1);
+        let mut carol = ParticipantCollectingCommitments::<Ristretto>::new(params, 2, &mut rng);
+        assert_eq!(carol.index(), 2);
+
+        assert_eq!(
+            alice.missing_commitments().collect::<Vec<_>>(),
+            [bob.index(), carol.index()]
+        );
+        exchange_commitments(&mut alice, &mut bob, &mut carol);
+
+        let mut alice = alice.finish_commitment_phase();
+        assert_eq!(alice.params().shares, params.shares);
+        assert_eq!(alice.params().threshold, params.threshold);
+        assert_eq!(alice.index(), 0);
+        let mut bob = bob.finish_commitment_phase();
+        assert_eq!(bob.index(), 1);
+        let mut carol = carol.finish_commitment_phase();
+        assert_eq!(carol.index(), 2);
+
+        assert_eq!(
+            alice.missing_public_polynomials().collect::<Vec<_>>(),
+            [bob.index(), carol.index()]
+        );
+        exchange_polynomials(&mut alice, &mut bob, &mut carol).unwrap();
+
+        let mut alice = alice.finish_polynomials_phase();
+        assert_eq!(alice.params().shares, params.shares);
+        assert_eq!(alice.params().threshold, params.threshold);
+        assert_eq!(alice.index(), 0);
+        let mut bob = bob.finish_polynomials_phase();
+        assert_eq!(bob.index(), 1);
+        let mut carol = carol.finish_polynomials_phase();
+        assert_eq!(carol.index(), 2);
+
+        exchange_secret_shares(&mut alice, &mut bob, &mut carol).unwrap();
+
+        let alice = alice.complete().unwrap();
+        let bob = bob.complete().unwrap();
+        carol.complete().unwrap();
+        let key_set = alice.key_set();
+
+        let ciphertext = key_set.shared_key().encrypt(15_u64, &mut rng);
+        let (alice_share, proof) = alice.decrypt_share(ciphertext, &mut rng);
+        key_set
+            .verify_share(alice_share.into(), ciphertext, alice.index(), &proof)
+            .unwrap();
+
+        let (bob_share, proof) = bob.decrypt_share(ciphertext, &mut rng);
+        key_set
+            .verify_share(bob_share.into(), ciphertext, bob.index(), &proof)
+            .unwrap();
+
+        let combined = params
+            .combine_shares([(alice.index(), alice_share), (bob.index(), bob_share)])
+            .unwrap();
+        let lookup_table = DiscreteLogTable::<Ristretto>::new(0..20);
+
+        assert_eq!(combined.decrypt(ciphertext, &lookup_table), Some(15));
+    }
+
+    fn exchange_commitments(
+        alice: &mut ParticipantCollectingCommitments<Ristretto>,
+        bob: &mut ParticipantCollectingCommitments<Ristretto>,
+        carol: &mut ParticipantCollectingCommitments<Ristretto>,
+    ) {
+        let alice_commitment = alice.commitment();
+        let bob_commitment = bob.commitment();
+        let carol_commitment = carol.commitment();
+
+        alice.insert_commitment(bob.index(), bob_commitment);
+        alice.insert_commitment(carol.index(), carol_commitment);
+        bob.insert_commitment(alice.index(), alice_commitment);
+        bob.insert_commitment(carol.index(), carol_commitment);
+        carol.insert_commitment(alice.index(), alice_commitment);
+        carol.insert_commitment(bob.index(), bob_commitment);
+    }
+
+    fn exchange_polynomials(
+        alice: &mut ParticipantCollectingPolynomials<Ristretto>,
+        bob: &mut ParticipantCollectingPolynomials<Ristretto>,
+        carol: &mut ParticipantCollectingPolynomials<Ristretto>,
+    ) -> Result<(), Error> {
+        let alice_info = alice.public_info().into_owned();
+        let bob_info = bob.public_info().into_owned();
+        let carol_info = carol.public_info().into_owned();
+
+        alice.insert_public_polynomial(bob.index(), bob_info.clone())?;
+        alice.insert_public_polynomial(carol.index(), carol_info.clone())?;
+        bob.insert_public_polynomial(alice.index(), alice_info.clone())?;
+        bob.insert_public_polynomial(carol.index(), carol_info)?;
+        carol.insert_public_polynomial(alice.index(), alice_info)?;
+        carol.insert_public_polynomial(bob.index(), bob_info)?;
+        Ok(())
+    }
+
+    fn exchange_secret_shares(
+        alice: &mut ParticipantExchangingSecrets<Ristretto>,
+        bob: &mut ParticipantExchangingSecrets<Ristretto>,
+        carol: &mut ParticipantExchangingSecrets<Ristretto>,
+    ) -> Result<(), Error> {
+        alice.insert_secret_share(bob.index(), bob.secret_share_for_participant(alice.index()))?;
+        alice.insert_secret_share(
+            carol.index(),
+            carol.secret_share_for_participant(alice.index()),
+        )?;
+
+        bob.insert_secret_share(
+            alice.index(),
+            alice.secret_share_for_participant(bob.index()),
+        )?;
+        bob.insert_secret_share(
+            carol.index(),
+            carol.secret_share_for_participant(bob.index()),
+        )?;
+
+        carol.insert_secret_share(
+            alice.index(),
+            alice.secret_share_for_participant(carol.index()),
+        )?;
+        carol.insert_secret_share(bob.index(), bob.secret_share_for_participant(carol.index()))?;
+        Ok(())
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/encryption.rs.html b/src/elastic_elgamal/encryption.rs.html new file mode 100644 index 0000000..e837165 --- /dev/null +++ b/src/elastic_elgamal/encryption.rs.html @@ -0,0 +1,1017 @@ +encryption.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+
//! `Ciphertext` and closely related types.
+
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+use zeroize::{Zeroize, Zeroizing};
+
+use core::{fmt, marker::PhantomData, ops};
+
+#[cfg(feature = "serde")]
+use crate::serde::ElementHelper;
+use crate::{
+    alloc::{vec, HashMap, Vec},
+    group::{Group, ScalarOps},
+    PublicKey, SecretKey,
+};
+
+/// Ciphertext for ElGamal encryption.
+///
+/// A ciphertext consists of 2 group elements: the random element `R` and a blinded encrypted
+/// value `B`. If the ciphertext encrypts integer value `v`, it holds that
+///
+/// ```text
+/// R = [r]G;
+/// B = [v]G + [r]K = [v]G + [k]R;
+/// ```
+///
+/// where:
+///
+/// - `G` is the conventional group generator
+/// - `r` is a random scalar selected by the encrypting party
+/// - `K` and `k` are the recipient's public and private keys, respectively.
+///
+/// Ciphertexts are partially homomorphic: they can be added together or multiplied by a scalar
+/// value.
+///
+/// # Examples
+///
+/// Basic usage and arithmetic for ciphertexts:
+///
+/// ```
+/// # use elastic_elgamal::{group::Ristretto, DiscreteLogTable, Ciphertext, Keypair};
+/// # use rand::thread_rng;
+/// // Generate a keypair for the ciphertext receiver.
+/// let mut rng = thread_rng();
+/// let receiver = Keypair::<Ristretto>::generate(&mut rng);
+/// // Create a couple of ciphertexts.
+/// let mut enc = receiver.public().encrypt(2_u64, &mut rng);
+/// enc += receiver.public().encrypt(3_u64, &mut rng) * 4;
+/// // Check that the ciphertext decrypts to 2 + 3 * 4 = 14.
+/// let lookup_table = DiscreteLogTable::new(0..20);
+/// let decrypted = receiver.secret().decrypt(enc, &lookup_table);
+/// assert_eq!(decrypted, Some(14));
+/// ```
+///
+/// Creating a ciphertext of a boolean value together with a proof:
+///
+/// ```
+/// # use elastic_elgamal::{group::Ristretto, Ciphertext, Keypair};
+/// # use rand::thread_rng;
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// // Generate a keypair for the ciphertext receiver.
+/// let mut rng = thread_rng();
+/// let receiver = Keypair::<Ristretto>::generate(&mut rng);
+/// // Create and verify a boolean encryption.
+/// let (enc, proof) =
+///     receiver.public().encrypt_bool(false, &mut rng);
+/// receiver.public().verify_bool(enc, &proof)?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// Creating a ciphertext of an integer value together with a range proof:
+///
+/// ```
+/// # use elastic_elgamal::{group::Ristretto, Keypair, RangeDecomposition};
+/// # use rand::thread_rng;
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// // Generate the ciphertext receiver.
+/// let mut rng = thread_rng();
+/// let receiver = Keypair::<Ristretto>::generate(&mut rng);
+/// // Find the optimal range decomposition for our range
+/// // and specialize it for the Ristretto group.
+/// let range = RangeDecomposition::optimal(100).into();
+///
+/// let (ciphertext, proof) = receiver
+///     .public()
+///     .encrypt_range(&range, 42, &mut rng);
+///
+/// // Check that the the proof verifies.
+/// receiver.public().verify_range(&range, ciphertext, &proof)?;
+/// # Ok(())
+/// # }
+/// ```
+#[derive(Clone, Copy)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub struct Ciphertext<G: Group> {
+    #[cfg_attr(feature = "serde", serde(with = "ElementHelper::<G>"))]
+    pub(crate) random_element: G::Element,
+    #[cfg_attr(feature = "serde", serde(with = "ElementHelper::<G>"))]
+    pub(crate) blinded_element: G::Element,
+}
+
+impl<G: Group> fmt::Debug for Ciphertext<G> {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        formatter
+            .debug_struct("Ciphertext")
+            .field("random_element", &self.random_element)
+            .field("blinded_element", &self.blinded_element)
+            .finish()
+    }
+}
+
+impl<G: Group> Ciphertext<G> {
+    /// Represents encryption of zero value without the blinding factor.
+    pub fn zero() -> Self {
+        Self {
+            random_element: G::identity(),
+            blinded_element: G::identity(),
+        }
+    }
+
+    /// Creates a non-blinded encryption of the specified scalar `value`, i.e., `(O, [value]G)`
+    /// where `O` is identity and `G` is the conventional group generator.
+    pub fn non_blinded<T>(value: T) -> Self
+    where
+        G::Scalar: From<T>,
+    {
+        let scalar = Zeroizing::new(G::Scalar::from(value));
+        Self {
+            random_element: G::identity(),
+            blinded_element: G::mul_generator(&scalar),
+        }
+    }
+
+    /// Returns a reference to the random element.
+    pub fn random_element(&self) -> &G::Element {
+        &self.random_element
+    }
+
+    /// Returns a reference to the blinded element.
+    pub fn blinded_element(&self) -> &G::Element {
+        &self.blinded_element
+    }
+
+    /// Serializes this ciphertext as two group elements (the random element,
+    /// then the blinded value).
+    pub fn to_bytes(self) -> Vec<u8> {
+        let mut bytes = vec![0_u8; 2 * G::ELEMENT_SIZE];
+        G::serialize_element(&self.random_element, &mut bytes[..G::ELEMENT_SIZE]);
+        G::serialize_element(&self.blinded_element, &mut bytes[G::ELEMENT_SIZE..]);
+        bytes
+    }
+}
+
+impl<G: Group> ops::Add for Ciphertext<G> {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        Self {
+            random_element: self.random_element + rhs.random_element,
+            blinded_element: self.blinded_element + rhs.blinded_element,
+        }
+    }
+}
+
+impl<G: Group> ops::AddAssign for Ciphertext<G> {
+    fn add_assign(&mut self, rhs: Self) {
+        *self = *self + rhs;
+    }
+}
+
+impl<G: Group> ops::Sub for Ciphertext<G> {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        Self {
+            random_element: self.random_element - rhs.random_element,
+            blinded_element: self.blinded_element - rhs.blinded_element,
+        }
+    }
+}
+
+impl<G: Group> ops::SubAssign for Ciphertext<G> {
+    fn sub_assign(&mut self, rhs: Self) {
+        *self = *self - rhs;
+    }
+}
+
+impl<G: Group> ops::Mul<&G::Scalar> for Ciphertext<G> {
+    type Output = Self;
+
+    fn mul(self, rhs: &G::Scalar) -> Self {
+        Self {
+            random_element: self.random_element * rhs,
+            blinded_element: self.blinded_element * rhs,
+        }
+    }
+}
+
+impl<G: Group> ops::Mul<u64> for Ciphertext<G> {
+    type Output = Self;
+
+    fn mul(self, rhs: u64) -> Self {
+        let scalar = G::Scalar::from(rhs);
+        self * &scalar
+    }
+}
+
+impl<G: Group> ops::Neg for Ciphertext<G> {
+    type Output = Self;
+
+    fn neg(self) -> Self::Output {
+        Self {
+            random_element: -self.random_element,
+            blinded_element: -self.blinded_element,
+        }
+    }
+}
+
+/// Lookup table for discrete logarithms.
+///
+/// For [`Ciphertext`]s to be partially homomorphic, the encrypted values must be
+/// group scalars linearly mapped to group elements: `x -> [x]G`, where `G` is the group
+/// generator. After decryption it is necessary to map the decrypted group element back to a scalar
+/// (i.e., get its discrete logarithm with base `G`). Because of discrete logarithm assumption,
+/// this task is computationally infeasible in the general case; however, if the possible range
+/// of encrypted values is small, it is possible to "cheat" by precomputing mapping `[x]G -> x`
+/// for all allowed `x` ahead of time. This is exactly what `DiscreteLogTable` does.
+///
+/// # Examples
+///
+/// ```
+/// # use elastic_elgamal::{group::Ristretto, DiscreteLogTable, Ciphertext, Keypair};
+/// # use rand::thread_rng;
+/// let mut rng = thread_rng();
+/// let receiver = Keypair::<Ristretto>::generate(&mut rng);
+/// let ciphertexts = (0_u64..16)
+///     .map(|i| receiver.public().encrypt(i, &mut rng));
+/// // Assume that we know that the plaintext is in range 0..16,
+/// // e.g., via a zero-knowledge proof.
+/// let lookup_table = DiscreteLogTable::new(0..16);
+/// // Then, we can use the lookup table to decrypt values.
+/// // A single table may be shared for multiple decryption operations
+/// // (i.e., it may be constructed ahead of time).
+/// for (i, enc) in ciphertexts.enumerate() {
+///     assert_eq!(
+///         receiver.secret().decrypt(enc, &lookup_table),
+///         Some(i as u64)
+///     );
+/// }
+/// ```
+#[derive(Debug, Clone)]
+pub struct DiscreteLogTable<G: Group> {
+    inner: HashMap<Vec<u8>, u64>,
+    _t: PhantomData<G>,
+}
+
+impl<G: Group> DiscreteLogTable<G> {
+    /// Creates a lookup table for the specified `values`.
+    pub fn new(values: impl IntoIterator<Item = u64>) -> Self {
+        let lookup_table = values
+            .into_iter()
+            .filter(|&value| value != 0)
+            .map(|i| {
+                let element = G::vartime_mul_generator(&G::Scalar::from(i));
+                let mut bytes = vec![0_u8; G::ELEMENT_SIZE];
+                G::serialize_element(&element, &mut bytes);
+                (bytes, i)
+            })
+            .collect();
+
+        Self {
+            inner: lookup_table,
+            _t: PhantomData,
+        }
+    }
+
+    /// Gets the discrete log of `decrypted_element`, or `None` if it is not present among `values`
+    /// stored in this table.
+    pub fn get(&self, decrypted_element: &G::Element) -> Option<u64> {
+        if G::is_identity(decrypted_element) {
+            // The identity element may have a special serialization (e.g., in SEC standard
+            // for elliptic curves), so we check it separately.
+            Some(0)
+        } else {
+            let mut bytes = vec![0_u8; G::ELEMENT_SIZE];
+            G::serialize_element(decrypted_element, &mut bytes);
+            self.inner.get(&bytes).copied()
+        }
+    }
+}
+
+/// [`Ciphertext`] together with the random scalar used to create it.
+#[derive(Debug, Clone)]
+#[doc(hidden)] // only public for benchmarking
+pub struct ExtendedCiphertext<G: Group> {
+    pub(crate) inner: Ciphertext<G>,
+    pub(crate) random_scalar: SecretKey<G>,
+}
+
+impl<G: Group> ExtendedCiphertext<G> {
+    /// Creates a ciphertext of `value` for the specified `receiver`.
+    pub(crate) fn new<R: CryptoRng + RngCore>(
+        value: G::Element,
+        receiver: &PublicKey<G>,
+        rng: &mut R,
+    ) -> Self {
+        let random_scalar = SecretKey::<G>::generate(rng);
+        let random_element = G::mul_generator(random_scalar.expose_scalar());
+        let dh_element = receiver.as_element() * random_scalar.expose_scalar();
+        let blinded_element = value + dh_element;
+
+        Self {
+            inner: Ciphertext {
+                random_element,
+                blinded_element,
+            },
+            random_scalar,
+        }
+    }
+
+    pub(crate) fn zero() -> Self {
+        Self {
+            inner: Ciphertext::zero(),
+            random_scalar: SecretKey::new(G::Scalar::from(0_u64)),
+        }
+    }
+
+    pub(crate) fn with_value<V>(self, value: V) -> CiphertextWithValue<G, V>
+    where
+        V: Zeroize,
+        G::Scalar: From<V>,
+    {
+        CiphertextWithValue {
+            inner: self,
+            value: Zeroizing::new(value),
+        }
+    }
+}
+
+impl<G: Group> ops::Add for ExtendedCiphertext<G> {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self::Output {
+        Self {
+            inner: self.inner + rhs.inner,
+            random_scalar: self.random_scalar + rhs.random_scalar,
+        }
+    }
+}
+
+impl<G: Group> ops::AddAssign for ExtendedCiphertext<G> {
+    fn add_assign(&mut self, rhs: Self) {
+        self.inner += rhs.inner;
+        self.random_scalar += rhs.random_scalar;
+    }
+}
+
+impl<G: Group> ops::Sub for ExtendedCiphertext<G> {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self::Output {
+        Self {
+            inner: self.inner - rhs.inner,
+            random_scalar: self.random_scalar - rhs.random_scalar,
+        }
+    }
+}
+
+/// ElGamal [`Ciphertext`] together with fully retained information about the encrypted value and
+/// randomness used to create the ciphertext.
+///
+/// This type can be used to produce certain kinds of proofs, such as
+/// [`SumOfSquaresProof`](crate::SumOfSquaresProof).
+#[derive(Debug)]
+pub struct CiphertextWithValue<G: Group, V: Zeroize = <G as ScalarOps>::Scalar> {
+    inner: ExtendedCiphertext<G>,
+    value: Zeroizing<V>,
+}
+
+impl<G: Group, V: Zeroize> From<CiphertextWithValue<G, V>> for Ciphertext<G> {
+    fn from(ciphertext: CiphertextWithValue<G, V>) -> Self {
+        ciphertext.inner.inner
+    }
+}
+
+impl<G: Group, V> CiphertextWithValue<G, V>
+where
+    V: Copy + Zeroize,
+    G::Scalar: From<V>,
+{
+    /// Encrypts a value for the specified receiver.
+    ///
+    /// This is a lower-level operation compared to [`PublicKey::encrypt()`] and should be used
+    /// if the resulting ciphertext is necessary to produce proofs.
+    pub fn new<R: CryptoRng + RngCore>(value: V, receiver: &PublicKey<G>, rng: &mut R) -> Self {
+        let scalar = Zeroizing::new(G::Scalar::from(value));
+        let element = G::mul_generator(&scalar);
+        ExtendedCiphertext::new(element, receiver, rng).with_value(value)
+    }
+
+    /// Converts the enclosed value into a scalar.
+    pub fn generalize(self) -> CiphertextWithValue<G> {
+        CiphertextWithValue {
+            inner: self.inner,
+            value: Zeroizing::new(G::Scalar::from(*self.value)),
+        }
+    }
+}
+
+impl<G: Group, V> CiphertextWithValue<G, V>
+where
+    V: Zeroize,
+    G::Scalar: From<V>,
+{
+    /// Returns a reference to the contained [`Ciphertext`].
+    pub fn inner(&self) -> &Ciphertext<G> {
+        &self.inner.inner
+    }
+
+    pub(crate) fn extended_ciphertext(&self) -> &ExtendedCiphertext<G> {
+        &self.inner
+    }
+
+    pub(crate) fn randomness(&self) -> &SecretKey<G> {
+        &self.inner.random_scalar
+    }
+
+    pub(crate) fn value(&self) -> &V {
+        &self.value
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::{thread_rng, Rng};
+
+    use super::*;
+    use crate::{curve25519::scalar::Scalar as Curve25519Scalar, group::Ristretto, Keypair};
+
+    #[test]
+    fn ciphertext_addition() {
+        let mut rng = thread_rng();
+        let numbers: Vec<_> = (0..10).map(|_| u64::from(rng.gen::<u32>())).collect();
+        let sum = numbers.iter().copied().sum::<u64>();
+
+        let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+        let ciphertexts = numbers.into_iter().map(|x| pk.encrypt(x, &mut rng));
+        let sum_ciphertext = ciphertexts.reduce(ops::Add::add).unwrap();
+        let decrypted = sk.decrypt_to_element(sum_ciphertext);
+
+        assert_eq!(decrypted, Ristretto::vartime_mul_generator(&sum.into()));
+    }
+
+    #[test]
+    fn ciphertext_mul_by_u64() {
+        let mut rng = thread_rng();
+        let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+        for _ in 0..100 {
+            let x = rng.gen::<u64>();
+            let multiplier = rng.gen::<u64>();
+            let ciphertext = pk.encrypt(x, &mut rng);
+            let decrypted = sk.decrypt_to_element(ciphertext * multiplier);
+
+            let expected_decryption =
+                Curve25519Scalar::from(x) * Curve25519Scalar::from(multiplier);
+            assert_eq!(
+                decrypted,
+                Ristretto::vartime_mul_generator(&expected_decryption)
+            );
+        }
+    }
+
+    #[test]
+    fn ciphertext_negation() {
+        let mut rng = thread_rng();
+        let (pk, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+        for _ in 0..100 {
+            let x = rng.gen::<u64>();
+            let ciphertext = pk.encrypt(x, &mut rng);
+            let neg_ciphertext = -ciphertext;
+            let decrypted = sk.decrypt_to_element(neg_ciphertext);
+
+            assert_eq!(
+                decrypted,
+                Ristretto::vartime_mul_generator(&-Curve25519Scalar::from(x))
+            );
+        }
+    }
+
+    #[test]
+    fn non_blinded_ciphertext() {
+        let mut rng = thread_rng();
+        let (_, sk) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+        for _ in 0..100 {
+            let x = rng.gen::<u64>();
+            let ciphertext = Ciphertext::non_blinded(x);
+            let decrypted = sk.decrypt_to_element(ciphertext);
+
+            assert_eq!(
+                decrypted,
+                Ristretto::vartime_mul_generator(&Curve25519Scalar::from(x))
+            );
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/group/curve25519.rs.html b/src/elastic_elgamal/group/curve25519.rs.html new file mode 100644 index 0000000..de5ec88 --- /dev/null +++ b/src/elastic_elgamal/group/curve25519.rs.html @@ -0,0 +1,399 @@ +curve25519.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+
use rand_core::{CryptoRng, RngCore};
+
+use core::convert::TryInto;
+
+use crate::curve25519::{
+    constants::{ED25519_BASEPOINT_POINT, ED25519_BASEPOINT_TABLE},
+    edwards::{CompressedEdwardsY, EdwardsPoint},
+    scalar::Scalar,
+    traits::{Identity, IsIdentity, MultiscalarMul, VartimeMultiscalarMul},
+};
+use crate::group::{ElementOps, Group, RandomBytesProvider, ScalarOps};
+
+/// Prime-order subgroup of Curve25519 without any transforms performed for EC points.
+///
+/// Since the curve has cofactor 8, [`ElementOps::deserialize_element()`] implementation
+/// explicitly checks on deserializing each EC point that the point is torsion-free
+/// (belongs to the prime-order subgroup), which is moderately slow (takes ~0.1ms on
+/// a laptop).
+///
+/// Prefer using [`Ristretto`] if compatibility with other Curve25519 applications is not a concern.
+/// (If it *is* a concern, beware of [cofactor pitfalls]!)
+///
+/// [`Ristretto`]: crate::group::Ristretto
+/// [cofactor pitfalls]: https://ristretto.group/why_ristretto.html#pitfalls-of-a-cofactor
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[cfg_attr(
+    docsrs,
+    doc(cfg(any(feature = "curve25519-dalek", feature = "curve25519-dalek-ng")))
+)]
+pub struct Curve25519Subgroup(());
+
+impl ScalarOps for Curve25519Subgroup {
+    type Scalar = Scalar;
+
+    const SCALAR_SIZE: usize = 32;
+
+    fn generate_scalar<R: CryptoRng + RngCore>(rng: &mut R) -> Self::Scalar {
+        let mut scalar_bytes = [0_u8; 64];
+        rng.fill_bytes(&mut scalar_bytes[..]);
+        Scalar::from_bytes_mod_order_wide(&scalar_bytes)
+    }
+
+    fn scalar_from_random_bytes(source: RandomBytesProvider<'_>) -> Self::Scalar {
+        let mut scalar_bytes = [0_u8; 64];
+        source.fill_bytes(&mut scalar_bytes);
+        Scalar::from_bytes_mod_order_wide(&scalar_bytes)
+    }
+
+    fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
+        scalar.invert()
+    }
+
+    fn invert_scalars(scalars: &mut [Self::Scalar]) {
+        Scalar::batch_invert(scalars);
+    }
+
+    fn serialize_scalar(scalar: &Self::Scalar, buffer: &mut [u8]) {
+        buffer.copy_from_slice(&scalar.to_bytes());
+    }
+
+    #[cfg(feature = "curve25519-dalek")]
+    fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar> {
+        let bytes: &[u8; 32] = buffer.try_into().expect("input has incorrect byte size");
+        Scalar::from_canonical_bytes(*bytes).into()
+    }
+
+    #[cfg(feature = "curve25519-dalek-ng")]
+    fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar> {
+        let bytes: &[u8; 32] = buffer.try_into().expect("input has incorrect byte size");
+        Scalar::from_canonical_bytes(*bytes)
+    }
+}
+
+impl ElementOps for Curve25519Subgroup {
+    type Element = EdwardsPoint;
+
+    const ELEMENT_SIZE: usize = 32;
+
+    fn identity() -> Self::Element {
+        EdwardsPoint::identity()
+    }
+
+    fn is_identity(element: &Self::Element) -> bool {
+        element.is_identity()
+    }
+
+    fn generator() -> Self::Element {
+        ED25519_BASEPOINT_POINT
+    }
+
+    fn serialize_element(element: &Self::Element, buffer: &mut [u8]) {
+        buffer.copy_from_slice(&element.compress().to_bytes());
+    }
+
+    #[cfg(feature = "curve25519-dalek")]
+    fn deserialize_element(buffer: &[u8]) -> Option<Self::Element> {
+        CompressedEdwardsY::from_slice(buffer)
+            .ok()?
+            .decompress()
+            .filter(EdwardsPoint::is_torsion_free)
+    }
+
+    #[cfg(feature = "curve25519-dalek-ng")]
+    fn deserialize_element(buffer: &[u8]) -> Option<Self::Element> {
+        CompressedEdwardsY::from_slice(buffer)
+            .decompress()
+            .filter(EdwardsPoint::is_torsion_free)
+    }
+}
+
+impl Group for Curve25519Subgroup {
+    #[cfg(feature = "curve25519-dalek")]
+    fn mul_generator(k: &Scalar) -> Self::Element {
+        k * ED25519_BASEPOINT_TABLE
+    }
+
+    #[cfg(feature = "curve25519-dalek-ng")]
+    fn mul_generator(k: &Scalar) -> Self::Element {
+        k * &ED25519_BASEPOINT_TABLE
+    }
+
+    fn vartime_mul_generator(k: &Scalar) -> Self::Element {
+        #[cfg(feature = "curve25519-dalek")]
+        let zero = Scalar::ZERO;
+        #[cfg(feature = "curve25519-dalek-ng")]
+        let zero = Scalar::zero();
+
+        EdwardsPoint::vartime_double_scalar_mul_basepoint(&zero, &EdwardsPoint::identity(), k)
+    }
+
+    fn multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
+    where
+        I: IntoIterator<Item = &'a Self::Scalar>,
+        J: IntoIterator<Item = Self::Element>,
+    {
+        EdwardsPoint::multiscalar_mul(scalars, elements)
+    }
+
+    fn vartime_double_mul_generator(
+        k: &Scalar,
+        k_element: Self::Element,
+        r: &Scalar,
+    ) -> Self::Element {
+        EdwardsPoint::vartime_double_scalar_mul_basepoint(k, &k_element, r)
+    }
+
+    fn vartime_multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
+    where
+        I: IntoIterator<Item = &'a Self::Scalar>,
+        J: IntoIterator<Item = Self::Element>,
+    {
+        EdwardsPoint::vartime_multiscalar_mul(scalars, elements)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::thread_rng;
+
+    use super::*;
+    use crate::{
+        curve25519::{constants::EIGHT_TORSION, scalar::Scalar, traits::Identity},
+        PublicKeyConversionError,
+    };
+
+    type PublicKey = crate::PublicKey<Curve25519Subgroup>;
+
+    #[test]
+    fn mangled_point_is_invalid_public_key() {
+        let mut rng = thread_rng();
+        for _ in 0..100 {
+            let mut point =
+                Curve25519Subgroup::mul_generator(&Curve25519Subgroup::generate_scalar(&mut rng));
+            point += EIGHT_TORSION[1];
+            assert!(!point.is_torsion_free());
+            let bytes = point.compress().to_bytes();
+            assert!(matches!(
+                PublicKey::from_bytes(&bytes).unwrap_err(),
+                PublicKeyConversionError::InvalidGroupElement
+            ));
+        }
+    }
+
+    #[test]
+    fn small_order_points_are_invalid_public_keys() {
+        let small_order = Scalar::from(8_u32);
+        // First element of `EIGHT_TORSION` is the point at infinity; since it
+        // would be processed differently, we skip it.
+        for point in EIGHT_TORSION.iter().skip(1) {
+            assert_eq!(point * small_order, EdwardsPoint::identity());
+            let bytes = point.compress().to_bytes();
+            assert!(matches!(
+                PublicKey::from_bytes(&bytes).unwrap_err(),
+                PublicKeyConversionError::InvalidGroupElement
+            ));
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/group/generic.rs.html b/src/elastic_elgamal/group/generic.rs.html new file mode 100644 index 0000000..3daad1f --- /dev/null +++ b/src/elastic_elgamal/group/generic.rs.html @@ -0,0 +1,277 @@ +generic.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+
use elliptic_curve::{
+    ff::PrimeField,
+    generic_array::{typenum::Unsigned, GenericArray},
+    sec1::{EncodedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint},
+    CurveArithmetic, Field, FieldBytesSize, Group as _, ProjectivePoint, Scalar,
+};
+use rand_core::{CryptoRng, RngCore};
+use zeroize::Zeroize;
+
+use core::marker::PhantomData;
+
+use super::{ElementOps, Group, ScalarOps};
+
+/// Generic [`Group`] implementation for elliptic curves defined in terms of the traits
+/// from the [`elliptic-curve`] crate.
+///
+/// # Assumptions
+///
+/// - Arithmetic operations required to be constant-time as per [`ScalarOps`] and [`ElementOps`]
+///   contracts are indeed constant-time.
+///
+/// [`elliptic-curve`]: https://docs.rs/elliptic-curve/
+#[derive(Debug)]
+pub struct Generic<C>(PhantomData<C>);
+
+impl<C> Clone for Generic<C> {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+impl<C> Copy for Generic<C> {}
+
+impl<C> ScalarOps for Generic<C>
+where
+    C: CurveArithmetic,
+    Scalar<C>: Zeroize,
+{
+    type Scalar = Scalar<C>;
+
+    const SCALAR_SIZE: usize = <FieldBytesSize<C> as Unsigned>::USIZE;
+
+    fn generate_scalar<R: CryptoRng + RngCore>(rng: &mut R) -> Self::Scalar {
+        Scalar::<C>::random(rng)
+    }
+
+    fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
+        scalar.invert().unwrap()
+    }
+
+    fn serialize_scalar(scalar: &Self::Scalar, buffer: &mut [u8]) {
+        buffer.copy_from_slice(scalar.to_repr().as_ref());
+    }
+
+    fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar> {
+        // For most curves, cloning will be resolved as a copy.
+        Scalar::<C>::from_repr(GenericArray::from_slice(buffer).clone()).into()
+    }
+}
+
+impl<C> ElementOps for Generic<C>
+where
+    C: CurveArithmetic,
+    Scalar<C>: Zeroize,
+    FieldBytesSize<C>: ModulusSize,
+    ProjectivePoint<C>: ToEncodedPoint<C> + FromEncodedPoint<C>,
+{
+    type Element = ProjectivePoint<C>;
+
+    const ELEMENT_SIZE: usize = <FieldBytesSize<C> as Unsigned>::USIZE + 1;
+
+    #[inline]
+    fn identity() -> Self::Element {
+        C::ProjectivePoint::identity()
+    }
+
+    #[inline]
+    fn is_identity(element: &Self::Element) -> bool {
+        element.is_identity().into()
+    }
+
+    #[inline]
+    fn generator() -> Self::Element {
+        C::ProjectivePoint::generator()
+    }
+
+    fn serialize_element(element: &Self::Element, buffer: &mut [u8]) {
+        let encoded_point = element.to_encoded_point(true);
+        buffer.copy_from_slice(encoded_point.as_bytes());
+    }
+
+    fn deserialize_element(input: &[u8]) -> Option<Self::Element> {
+        let encoded_point = EncodedPoint::<C>::from_bytes(input).ok()?;
+        ProjectivePoint::<C>::from_encoded_point(&encoded_point).into()
+    }
+}
+
+impl<C> Group for Generic<C>
+where
+    C: CurveArithmetic + 'static,
+    Scalar<C>: Zeroize,
+    FieldBytesSize<C>: ModulusSize,
+    ProjectivePoint<C>: ToEncodedPoint<C> + FromEncodedPoint<C>,
+{
+    // Default implementations are fine.
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::thread_rng;
+
+    use super::*;
+
+    type K256 = Generic<k256::Secp256k1>;
+
+    #[test]
+    fn scalar_roundtrip() {
+        let mut rng = thread_rng();
+        let mut buffer = [0_u8; K256::SCALAR_SIZE];
+        for _ in 0..100 {
+            let scalar = K256::generate_scalar(&mut rng);
+            K256::serialize_scalar(&scalar, &mut buffer);
+            assert_eq!(K256::deserialize_scalar(&buffer).unwrap(), scalar);
+        }
+    }
+
+    #[test]
+    fn point_roundtrip() {
+        let mut rng = thread_rng();
+        let mut buffer = [0_u8; K256::ELEMENT_SIZE];
+        for _ in 0..100 {
+            let point = K256::mul_generator(&K256::generate_scalar(&mut rng));
+            K256::serialize_element(&point, &mut buffer);
+            assert_eq!(K256::deserialize_element(&buffer).unwrap(), point);
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/group/mod.rs.html b/src/elastic_elgamal/group/mod.rs.html new file mode 100644 index 0000000..0df4505 --- /dev/null +++ b/src/elastic_elgamal/group/mod.rs.html @@ -0,0 +1,509 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+
//! Traits and implementations for prime-order groups in which
+//! the [decisional Diffie–Hellman][DDH] (DDH), [computational Diffie–Hellman][CDH] (CDH)
+//! and [discrete log][DLP] (DL) problems are believed to be hard.
+//!
+//! (Decisional Diffie–Hellman assumption is considered stronger than both CDH and DL,
+//! so if DDH is believed to hold for a certain group, it should be good to go.)
+//!
+//! Such groups can be applied for ElGamal encryption and other cryptographic protocols
+//! from this crate.
+//!
+//! [DDH]: https://en.wikipedia.org/wiki/Decisional_Diffie%E2%80%93Hellman_assumption
+//! [CDH]: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_problem
+//! [DLP]: https://en.wikipedia.org/wiki/Discrete_logarithm
+
+use merlin::Transcript;
+use rand_chacha::ChaChaRng;
+use rand_core::{CryptoRng, RngCore, SeedableRng};
+use zeroize::Zeroize;
+
+use core::{fmt, ops, str};
+
+#[cfg(any(feature = "curve25519-dalek", feature = "curve25519-dalek-ng"))]
+mod curve25519;
+mod generic;
+#[cfg(any(feature = "curve25519-dalek", feature = "curve25519-dalek-ng"))]
+mod ristretto;
+
+pub use self::generic::Generic;
+#[cfg(any(feature = "curve25519-dalek", feature = "curve25519-dalek-ng"))]
+pub use self::{curve25519::Curve25519Subgroup, ristretto::Ristretto};
+
+/// Provides an arbitrary number of random bytes.
+///
+/// Unlike [`RngCore::fill_bytes()`], a single provider can only be used once.
+pub struct RandomBytesProvider<'a> {
+    transcript: &'a mut Transcript,
+    label: &'static [u8],
+}
+
+impl fmt::Debug for RandomBytesProvider<'_> {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let label = str::from_utf8(self.label).unwrap_or("(non-utf8 label)");
+        formatter
+            .debug_struct("RandomBytesProvider")
+            .field("label", &label)
+            .finish()
+    }
+}
+
+impl<'a> RandomBytesProvider<'a> {
+    pub(crate) fn new(transcript: &'a mut Transcript, label: &'static [u8]) -> Self {
+        Self { transcript, label }
+    }
+
+    /// Writes random bytes into the specified buffer. As follows from the signature, this method
+    /// can only be called once for a provider instance.
+    pub fn fill_bytes(self, dest: &mut [u8]) {
+        self.transcript.challenge_bytes(self.label, dest);
+    }
+}
+
+/// Helper trait for [`Group`] that describes operations on group scalars.
+pub trait ScalarOps {
+    /// Scalar type. As per [`Group`] contract, scalars must form a prime field.
+    /// Arithmetic operations on scalars requested here must be constant-time.
+    type Scalar: Copy
+        + Default
+        + From<u64>
+        + From<Self::Scalar> // `PublicKey::encrypt()` doesn't work without this
+        + ops::Neg<Output = Self::Scalar>
+        + ops::Add<Output = Self::Scalar>
+        + for<'a> ops::Add<&'a Self::Scalar, Output = Self::Scalar>
+        + ops::Sub<Output = Self::Scalar>
+        + ops::Mul<Output = Self::Scalar>
+        + for<'a> ops::Mul<&'a Self::Scalar, Output = Self::Scalar>
+        + PartialEq
+        + Zeroize
+        + fmt::Debug;
+
+    /// Byte size of a serialized [`Self::Scalar`].
+    const SCALAR_SIZE: usize;
+
+    /// Generates a random scalar based on the provided CSPRNG. This operation
+    /// must be constant-time.
+    fn generate_scalar<R: CryptoRng + RngCore>(rng: &mut R) -> Self::Scalar;
+
+    /// Generates a scalar from a `source` of random bytes. This operation must be constant-time.
+    /// The `source` is guaranteed to return any necessary number of bytes.
+    ///
+    /// # Default implementation
+    ///
+    /// 1. Create a [ChaCha RNG] using 32 bytes read from `source` as the seed.
+    /// 2. Call [`Self::generate_scalar()`] with the created RNG.
+    ///
+    /// [ChaCha RNG]: https://docs.rs/rand_chacha/
+    fn scalar_from_random_bytes(source: RandomBytesProvider<'_>) -> Self::Scalar {
+        let mut rng_seed = <ChaChaRng as SeedableRng>::Seed::default();
+        source.fill_bytes(&mut rng_seed);
+        let mut rng = ChaChaRng::from_seed(rng_seed);
+        Self::generate_scalar(&mut rng)
+    }
+
+    /// Inverts the `scalar`, which is guaranteed to be non-zero. This operation does not
+    /// need to be constant-time.
+    fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
+
+    /// Inverts scalars in a batch. This operation does not need to be constant-time.
+    ///
+    /// # Default implementation
+    ///
+    /// Inverts every scalar successively.
+    fn invert_scalars(scalars: &mut [Self::Scalar]) {
+        for scalar in scalars {
+            *scalar = Self::invert_scalar(*scalar);
+        }
+    }
+
+    /// Serializes the scalar into the provided `buffer`, which is guaranteed to have length
+    /// [`Self::SCALAR_SIZE`].
+    fn serialize_scalar(scalar: &Self::Scalar, buffer: &mut [u8]);
+
+    /// Deserializes the scalar from `buffer`, which is guaranteed to have length
+    /// [`Self::SCALAR_SIZE`]. This method returns `None` if the buffer
+    /// does not correspond to a representation of a valid scalar.
+    fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar>;
+}
+
+/// Helper trait for [`Group`] that describes operations on group elements (i.e., EC points
+/// for elliptic curve groups).
+pub trait ElementOps: ScalarOps {
+    /// Element of the group. Arithmetic operations requested here (addition among
+    /// elements and multiplication by a `Scalar`) must be constant-time.
+    type Element: Copy
+        + ops::Add<Output = Self::Element>
+        + ops::Sub<Output = Self::Element>
+        + ops::Neg<Output = Self::Element>
+        + for<'a> ops::Mul<&'a Self::Scalar, Output = Self::Element>
+        + PartialEq
+        + fmt::Debug;
+
+    /// Byte size of a serialized [`Self::Element`].
+    const ELEMENT_SIZE: usize;
+
+    /// Returns the identity of the group (aka point at infinity for EC groups).
+    fn identity() -> Self::Element;
+
+    /// Checks if the specified element is the identity.
+    fn is_identity(element: &Self::Element) -> bool;
+
+    /// Returns the agreed-upon generator of the group.
+    fn generator() -> Self::Element;
+
+    /// Serializes `element` into the provided `buffer`, which is guaranteed to have length
+    /// [`Self::ELEMENT_SIZE`].
+    fn serialize_element(element: &Self::Element, output: &mut [u8]);
+
+    /// Deserializes an element from `buffer`, which is guaranteed to have length
+    /// [`Self::ELEMENT_SIZE`]. This method returns `None` if the buffer
+    /// does not correspond to a representation of a valid scalar.
+    fn deserialize_element(buffer: &[u8]) -> Option<Self::Element>;
+}
+
+/// Prime-order group in which the discrete log problem and decisional / computational
+/// Diffie–Hellman problems are believed to be hard.
+///
+/// Groups conforming to this trait can be used for ElGamal encryption and other
+/// cryptographic protocols defined in this crate.
+///
+/// This crate provides the following implementations of this trait:
+///
+/// - [`Curve25519Subgroup`], representation of a prime-order subgroup of Curve25519
+///   with the conventionally chosen generator.
+/// - [`Ristretto`], a transform of Curve25519 which eliminates its co-factor 8 with the help
+///   of the [eponymous technique][ristretto].
+/// - [`Generic`] implementation defined in terms of traits from the [`elliptic-curve`] crate.
+///   (For example, this means secp256k1 support via the [`k256`] crate.)
+///
+/// [ristretto]: https://ristretto.group/
+/// [`elliptic-curve`]: https://docs.rs/elliptic-curve/
+/// [`k256`]: https://docs.rs/k256/
+pub trait Group: Copy + ScalarOps + ElementOps + 'static {
+    /// Multiplies the provided scalar by [`ElementOps::generator()`]. This operation must be
+    /// constant-time.
+    ///
+    /// # Default implementation
+    ///
+    /// Implemented using [`Mul`](ops::Mul) (which is constant-time as per the [`ElementOps`]
+    /// contract).
+    fn mul_generator(k: &Self::Scalar) -> Self::Element {
+        Self::generator() * k
+    }
+
+    /// Multiplies the provided scalar by [`ElementOps::generator()`].
+    /// Unlike [`Self::mul_generator()`], this operation does not need to be constant-time;
+    /// thus, it may employ additional optimizations.
+    ///
+    /// # Default implementation
+    ///
+    /// Implemented by calling [`Self::mul_generator()`].
+    #[inline]
+    fn vartime_mul_generator(k: &Self::Scalar) -> Self::Element {
+        Self::mul_generator(k)
+    }
+
+    /// Multiplies provided `scalars` by `elements`. This operation must be constant-time
+    /// w.r.t. the given length of elements.
+    ///
+    /// # Default implementation
+    ///
+    /// Implemented by straightforward computations, which are constant-time as per
+    /// the [`ElementOps`] contract.
+    fn multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
+    where
+        I: IntoIterator<Item = &'a Self::Scalar>,
+        J: IntoIterator<Item = Self::Element>,
+    {
+        let mut output = Self::identity();
+        for (scalar, element) in scalars.into_iter().zip(elements) {
+            output = output + element * scalar;
+        }
+        output
+    }
+
+    /// Calculates `k * k_element + r * G`, where `G` is the group generator. This operation
+    /// does not need to be constant-time.
+    ///
+    /// # Default implementation
+    ///
+    /// Implemented by straightforward arithmetic.
+    fn vartime_double_mul_generator(
+        k: &Self::Scalar,
+        k_element: Self::Element,
+        r: &Self::Scalar,
+    ) -> Self::Element {
+        k_element * k + Self::generator() * r
+    }
+
+    /// Multiplies provided `scalars` by `elements`. Unlike [`Self::multi_mul()`],
+    /// this operation does not need to be constant-time; thus, it may employ
+    /// additional optimizations.
+    ///
+    /// # Default implementation
+    ///
+    /// Implemented by calling [`Self::multi_mul()`].
+    #[inline]
+    fn vartime_multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
+    where
+        I: IntoIterator<Item = &'a Self::Scalar>,
+        J: IntoIterator<Item = Self::Element>,
+    {
+        Self::multi_mul(scalars, elements)
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/group/ristretto.rs.html b/src/elastic_elgamal/group/ristretto.rs.html new file mode 100644 index 0000000..c0c17e0 --- /dev/null +++ b/src/elastic_elgamal/group/ristretto.rs.html @@ -0,0 +1,387 @@ +ristretto.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+
use rand_core::{CryptoRng, RngCore};
+
+use core::convert::TryInto;
+
+use crate::curve25519::{
+    constants::{RISTRETTO_BASEPOINT_POINT, RISTRETTO_BASEPOINT_TABLE},
+    ristretto::{CompressedRistretto, RistrettoPoint},
+    scalar::Scalar,
+    traits::{Identity, IsIdentity, MultiscalarMul, VartimeMultiscalarMul},
+};
+use crate::group::{ElementOps, Group, RandomBytesProvider, ScalarOps};
+
+/// [Ristretto](https://ristretto.group/) transform of Curve25519, also known as ristretto255.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[cfg_attr(
+    docsrs,
+    doc(cfg(any(feature = "curve25519-dalek", feature = "curve25519-dalek-ng")))
+)]
+pub struct Ristretto(());
+
+impl ScalarOps for Ristretto {
+    type Scalar = Scalar;
+
+    const SCALAR_SIZE: usize = 32;
+
+    fn generate_scalar<R: CryptoRng + RngCore>(rng: &mut R) -> Self::Scalar {
+        let mut scalar_bytes = [0_u8; 64];
+        rng.fill_bytes(&mut scalar_bytes[..]);
+        Scalar::from_bytes_mod_order_wide(&scalar_bytes)
+    }
+
+    fn scalar_from_random_bytes(source: RandomBytesProvider<'_>) -> Self::Scalar {
+        let mut scalar_bytes = [0_u8; 64];
+        source.fill_bytes(&mut scalar_bytes);
+        Scalar::from_bytes_mod_order_wide(&scalar_bytes)
+    }
+
+    fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
+        scalar.invert()
+    }
+
+    fn invert_scalars(scalars: &mut [Self::Scalar]) {
+        Scalar::batch_invert(scalars);
+    }
+
+    fn serialize_scalar(scalar: &Self::Scalar, buffer: &mut [u8]) {
+        buffer.copy_from_slice(&scalar.to_bytes());
+    }
+
+    #[cfg(feature = "curve25519-dalek")]
+    fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar> {
+        let bytes: &[u8; 32] = buffer.try_into().expect("input has incorrect byte size");
+        Scalar::from_canonical_bytes(*bytes).into()
+    }
+
+    #[cfg(feature = "curve25519-dalek-ng")]
+    fn deserialize_scalar(buffer: &[u8]) -> Option<Self::Scalar> {
+        let bytes: &[u8; 32] = buffer.try_into().expect("input has incorrect byte size");
+        Scalar::from_canonical_bytes(*bytes)
+    }
+}
+
+impl ElementOps for Ristretto {
+    type Element = RistrettoPoint;
+
+    const ELEMENT_SIZE: usize = 32;
+
+    fn identity() -> Self::Element {
+        RistrettoPoint::identity()
+    }
+
+    fn is_identity(element: &Self::Element) -> bool {
+        element.is_identity()
+    }
+
+    fn generator() -> Self::Element {
+        RISTRETTO_BASEPOINT_POINT
+    }
+
+    fn serialize_element(element: &Self::Element, buffer: &mut [u8]) {
+        buffer.copy_from_slice(&element.compress().to_bytes());
+    }
+
+    #[cfg(feature = "curve25519-dalek")]
+    fn deserialize_element(buffer: &[u8]) -> Option<Self::Element> {
+        CompressedRistretto::from_slice(buffer).ok()?.decompress()
+    }
+
+    #[cfg(feature = "curve25519-dalek-ng")]
+    fn deserialize_element(buffer: &[u8]) -> Option<Self::Element> {
+        CompressedRistretto::from_slice(buffer).decompress()
+    }
+}
+
+impl Group for Ristretto {
+    #[cfg(feature = "curve25519-dalek")]
+    fn mul_generator(k: &Scalar) -> Self::Element {
+        k * RISTRETTO_BASEPOINT_TABLE
+    }
+
+    #[cfg(feature = "curve25519-dalek-ng")]
+    fn mul_generator(k: &Scalar) -> Self::Element {
+        k * &RISTRETTO_BASEPOINT_TABLE
+    }
+
+    fn vartime_mul_generator(k: &Scalar) -> Self::Element {
+        #[cfg(feature = "curve25519-dalek")]
+        let zero = Scalar::ZERO;
+        #[cfg(feature = "curve25519-dalek-ng")]
+        let zero = Scalar::zero();
+
+        RistrettoPoint::vartime_double_scalar_mul_basepoint(&zero, &RistrettoPoint::identity(), k)
+    }
+
+    fn multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
+    where
+        I: IntoIterator<Item = &'a Self::Scalar>,
+        J: IntoIterator<Item = Self::Element>,
+    {
+        RistrettoPoint::multiscalar_mul(scalars, elements)
+    }
+
+    fn vartime_double_mul_generator(
+        k: &Scalar,
+        k_element: Self::Element,
+        r: &Scalar,
+    ) -> Self::Element {
+        RistrettoPoint::vartime_double_scalar_mul_basepoint(k, &k_element, r)
+    }
+
+    fn vartime_multi_mul<'a, I, J>(scalars: I, elements: J) -> Self::Element
+    where
+        I: IntoIterator<Item = &'a Self::Scalar>,
+        J: IntoIterator<Item = Self::Element>,
+    {
+        RistrettoPoint::vartime_multiscalar_mul(scalars, elements)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::thread_rng;
+
+    use super::*;
+    use crate::{
+        app::{ChoiceParams, EncryptedChoice},
+        group::Curve25519Subgroup,
+        DiscreteLogTable,
+    };
+
+    type SecretKey = crate::SecretKey<Ristretto>;
+    type Keypair = crate::Keypair<Ristretto>;
+
+    #[test]
+    fn encrypt_and_decrypt() {
+        let mut rng = thread_rng();
+        let keypair = Keypair::generate(&mut rng);
+        let value = Ristretto::generate_scalar(&mut rng);
+        let encrypted = keypair.public().encrypt(value, &mut rng);
+        let decryption = keypair.secret().decrypt_to_element(encrypted);
+        assert_eq!(decryption, Ristretto::vartime_mul_generator(&value));
+    }
+
+    #[test]
+    fn encrypt_choice() {
+        let mut rng = thread_rng();
+        let (pk, sk) = Keypair::generate(&mut rng).into_tuple();
+        let choice_params = ChoiceParams::single(pk, 5);
+        let encrypted = EncryptedChoice::single(&choice_params, 3, &mut rng);
+        let choices = encrypted.verify(&choice_params).unwrap();
+
+        let lookup_table = DiscreteLogTable::new(0..=1);
+        for (i, &choice) in choices.iter().enumerate() {
+            let decryption = sk.decrypt(choice, &lookup_table);
+            assert_eq!(decryption.unwrap(), u64::from(i == 3));
+        }
+    }
+
+    #[test]
+    fn edwards_and_ristretto_public_keys_differ() {
+        type SubgroupSecretKey = crate::SecretKey<Curve25519Subgroup>;
+        type SubgroupKeypair = crate::Keypair<Curve25519Subgroup>;
+
+        for _ in 0..1_000 {
+            let secret_key = SecretKey::generate(&mut thread_rng());
+            let keypair = Keypair::from(secret_key.clone());
+            let secret_key = SubgroupSecretKey::new(*secret_key.expose_scalar());
+            let ed_keypair = SubgroupKeypair::from(secret_key);
+            assert_ne!(keypair.public().as_bytes(), ed_keypair.public().as_bytes());
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/keys/impls.rs.html b/src/elastic_elgamal/keys/impls.rs.html new file mode 100644 index 0000000..ace783c --- /dev/null +++ b/src/elastic_elgamal/keys/impls.rs.html @@ -0,0 +1,365 @@ +impls.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+
//! Operations on public / secret keys.
+
+use merlin::Transcript;
+use rand_core::{CryptoRng, RngCore};
+
+use core::iter;
+
+use crate::{
+    alloc::vec, encryption::ExtendedCiphertext, group::Group, Ciphertext, DiscreteLogTable,
+    LogEqualityProof, PreparedRange, PublicKey, RangeProof, RingProof, RingProofBuilder, SecretKey,
+    VerificationError,
+};
+
+impl<G: Group> PublicKey<G> {
+    /// Encrypts a value for this key.
+    pub fn encrypt<T, R: CryptoRng + RngCore>(&self, value: T, rng: &mut R) -> Ciphertext<G>
+    where
+        G::Scalar: From<T>,
+    {
+        let scalar = G::Scalar::from(value);
+        let element = G::mul_generator(&scalar);
+        ExtendedCiphertext::new(element, self, rng).inner
+    }
+
+    /// Encrypts a group element.
+    pub fn encrypt_element<R: CryptoRng + RngCore>(
+        &self,
+        value: G::Element,
+        rng: &mut R,
+    ) -> Ciphertext<G> {
+        ExtendedCiphertext::new(value, self, rng).inner
+    }
+
+    /// Encrypts zero value and provides a zero-knowledge proof of encryption correctness.
+    pub fn encrypt_zero<R>(&self, rng: &mut R) -> (Ciphertext<G>, LogEqualityProof<G>)
+    where
+        R: CryptoRng + RngCore,
+    {
+        let random_scalar = SecretKey::<G>::generate(rng);
+        let random_element = G::mul_generator(&random_scalar.0);
+        let blinded_element = self.element * &random_scalar.0;
+        let ciphertext = Ciphertext {
+            random_element,
+            blinded_element,
+        };
+
+        let proof = LogEqualityProof::new(
+            self,
+            &random_scalar,
+            (random_element, blinded_element),
+            &mut Transcript::new(b"zero_encryption"),
+            rng,
+        );
+
+        (ciphertext, proof)
+    }
+
+    /// Verifies that this is an encryption of a zero value.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the `proof` does not verify.
+    pub fn verify_zero(
+        &self,
+        ciphertext: Ciphertext<G>,
+        proof: &LogEqualityProof<G>,
+    ) -> Result<(), VerificationError> {
+        proof.verify(
+            self,
+            (ciphertext.random_element, ciphertext.blinded_element),
+            &mut Transcript::new(b"zero_encryption"),
+        )
+    }
+
+    /// Encrypts a boolean value (0 or 1) and provides a zero-knowledge proof of encryption
+    /// correctness.
+    ///
+    /// # Examples
+    ///
+    /// See [`Ciphertext`] docs for an example of usage.
+    pub fn encrypt_bool<R: CryptoRng + RngCore>(
+        &self,
+        value: bool,
+        rng: &mut R,
+    ) -> (Ciphertext<G>, RingProof<G>) {
+        let mut transcript = Transcript::new(b"bool_encryption");
+        let admissible_values = [G::identity(), G::generator()];
+        let mut ring_responses = vec![G::Scalar::default(); 2];
+        let mut builder = RingProofBuilder::new(self, 1, &mut ring_responses, &mut transcript, rng);
+        let ciphertext = builder.add_value(&admissible_values, usize::from(value));
+        let proof = RingProof::new(builder.build(), ring_responses);
+        (ciphertext.inner, proof)
+    }
+
+    /// Verifies a proof of encryption correctness of a boolean value, which was presumably
+    /// obtained via [`Self::encrypt_bool()`].
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the `proof` does not verify.
+    ///
+    /// # Examples
+    ///
+    /// See [`Ciphertext`] docs for an example of usage.
+    pub fn verify_bool(
+        &self,
+        ciphertext: Ciphertext<G>,
+        proof: &RingProof<G>,
+    ) -> Result<(), VerificationError> {
+        let admissible_values = [G::identity(), G::generator()];
+        proof.verify(
+            self,
+            iter::once(&admissible_values as &[_]),
+            iter::once(ciphertext),
+            &mut Transcript::new(b"bool_encryption"),
+        )
+    }
+
+    /// Encrypts `value` and provides a zero-knowledge proof that it lies in the specified `range`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `value` is out of `range`.
+    ///
+    /// # Examples
+    ///
+    /// See [`Ciphertext`] docs for an example of usage.
+    pub fn encrypt_range<R: CryptoRng + RngCore>(
+        &self,
+        range: &PreparedRange<G>,
+        value: u64,
+        rng: &mut R,
+    ) -> (Ciphertext<G>, RangeProof<G>) {
+        let mut transcript = Transcript::new(b"ciphertext_range");
+        let (ciphertext, proof) = RangeProof::new(self, range, value, &mut transcript, rng);
+        (ciphertext.into(), proof)
+    }
+
+    /// Verifies `proof` that `ciphertext` encrypts a value lying in `range`.
+    ///
+    /// The `proof` should be created with a call to [`Self::encrypt_range()`] with the same
+    /// [`PreparedRange`]; otherwise, the proof will not verify.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the `proof` does not verify.
+    pub fn verify_range(
+        &self,
+        range: &PreparedRange<G>,
+        ciphertext: Ciphertext<G>,
+        proof: &RangeProof<G>,
+    ) -> Result<(), VerificationError> {
+        let mut transcript = Transcript::new(b"ciphertext_range");
+        proof.verify(self, range, ciphertext, &mut transcript)
+    }
+}
+
+impl<G: Group> SecretKey<G> {
+    /// Decrypts the provided ciphertext and returns the produced group element.
+    ///
+    /// As the ciphertext does not include a MAC or another way to assert integrity,
+    /// this operation cannot fail. If the ciphertext is not produced properly (e.g., it targets
+    /// another receiver), the returned group element will be garbage.
+    pub fn decrypt_to_element(&self, encrypted: Ciphertext<G>) -> G::Element {
+        let dh_element = encrypted.random_element * &self.0;
+        encrypted.blinded_element - dh_element
+    }
+
+    /// Decrypts the provided ciphertext and returns the original encrypted value.
+    ///
+    /// `lookup_table` is used to find encrypted values based on the original decrypted
+    /// group element. That is, it must contain all valid plaintext values. If the value
+    /// is not in the table, this method will return `None`.
+    pub fn decrypt(
+        &self,
+        encrypted: Ciphertext<G>,
+        lookup_table: &DiscreteLogTable<G>,
+    ) -> Option<u64> {
+        lookup_table.get(&self.decrypt_to_element(encrypted))
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/keys/mod.rs.html b/src/elastic_elgamal/keys/mod.rs.html new file mode 100644 index 0000000..5cb741f --- /dev/null +++ b/src/elastic_elgamal/keys/mod.rs.html @@ -0,0 +1,647 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+
//! Cryptographic keys for ElGamal encryption.
+
+use base64ct::{Base64UrlUnpadded, Encoding};
+use rand_core::{CryptoRng, RngCore};
+use zeroize::Zeroize;
+
+use core::{fmt, ops};
+
+use crate::{
+    alloc::{vec, Vec},
+    group::Group,
+};
+
+mod impls;
+
+/// Secret key for ElGamal encryption and related protocols. This is a thin wrapper around
+/// the [`Group`] scalar.
+pub struct SecretKey<G: Group>(G::Scalar);
+
+impl<G: Group> fmt::Debug for SecretKey<G> {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        formatter
+            .debug_struct("SecretKey")
+            .field("public", &PublicKey::from(self))
+            .finish()
+    }
+}
+
+impl<G: Group> Clone for SecretKey<G> {
+    fn clone(&self) -> Self {
+        SecretKey(self.0)
+    }
+}
+
+impl<G: Group> Drop for SecretKey<G> {
+    fn drop(&mut self) {
+        self.0.zeroize();
+    }
+}
+
+impl<G: Group> SecretKey<G> {
+    pub(crate) fn new(scalar: G::Scalar) -> Self {
+        SecretKey(scalar)
+    }
+
+    /// Generates a random secret key.
+    pub fn generate<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
+        SecretKey(G::generate_scalar(rng))
+    }
+
+    /// Deserializes a secret key from bytes. If bytes do not represent a valid scalar,
+    /// returns `None`.
+    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
+        if bytes.len() != G::SCALAR_SIZE {
+            return None;
+        }
+        G::deserialize_scalar(bytes).map(SecretKey)
+    }
+
+    /// Exposes the scalar equivalent to this key.
+    pub fn expose_scalar(&self) -> &G::Scalar {
+        &self.0
+    }
+}
+
+impl<G: Group> ops::Add for SecretKey<G> {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        Self(self.0 + rhs.0)
+    }
+}
+
+impl<G: Group> ops::AddAssign for SecretKey<G> {
+    fn add_assign(&mut self, rhs: Self) {
+        self.0 = self.0 + rhs.0;
+    }
+}
+
+impl<G: Group> ops::Sub for SecretKey<G> {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        Self(self.0 - rhs.0)
+    }
+}
+
+impl<G: Group> ops::SubAssign for SecretKey<G> {
+    fn sub_assign(&mut self, rhs: Self) {
+        self.0 = self.0 - rhs.0;
+    }
+}
+
+impl<G: Group> ops::Mul<&G::Scalar> for SecretKey<G> {
+    type Output = Self;
+
+    fn mul(self, &k: &G::Scalar) -> Self {
+        Self(self.0 * k)
+    }
+}
+
+impl<G: Group> ops::Mul<&G::Scalar> for &SecretKey<G> {
+    type Output = SecretKey<G>;
+
+    fn mul(self, &k: &G::Scalar) -> SecretKey<G> {
+        SecretKey(self.0 * k)
+    }
+}
+
+/// Public key for ElGamal encryption and related protocols.
+///
+/// # Implementation details
+///
+/// We store both the original bytes (which are used in zero-knowledge proofs)
+/// and its decompression into a [`Group`] element.
+/// This increases the memory footprint, but speeds up generating / verifying proofs.
+pub struct PublicKey<G: Group> {
+    bytes: Vec<u8>,
+    element: G::Element,
+}
+
+impl<G: Group> Clone for PublicKey<G> {
+    fn clone(&self) -> Self {
+        PublicKey {
+            bytes: self.bytes.clone(),
+            element: self.element,
+        }
+    }
+}
+
+impl<G: Group> fmt::Debug for PublicKey<G> {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        formatter
+            .debug_tuple("PublicKey")
+            .field(&Base64UrlUnpadded::encode_string(&self.bytes))
+            .finish()
+    }
+}
+
+impl<G> PartialEq for PublicKey<G>
+where
+    G: Group,
+{
+    fn eq(&self, other: &Self) -> bool {
+        self.bytes == other.bytes
+    }
+}
+
+impl<G: Group> PublicKey<G> {
+    /// Deserializes a public key from bytes.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if `bytes` has invalid byte size, does not represent a valid group element
+    /// or represents the group identity.
+    pub fn from_bytes(bytes: &[u8]) -> Result<Self, PublicKeyConversionError> {
+        if bytes.len() != G::ELEMENT_SIZE {
+            return Err(PublicKeyConversionError::InvalidByteSize);
+        }
+
+        let element =
+            G::deserialize_element(bytes).ok_or(PublicKeyConversionError::InvalidGroupElement)?;
+        if G::is_identity(&element) {
+            Err(PublicKeyConversionError::IdentityKey)
+        } else {
+            Ok(Self {
+                bytes: bytes.to_vec(),
+                element,
+            })
+        }
+    }
+
+    pub(crate) fn from_element(element: G::Element) -> Self {
+        let mut element_bytes = vec![0_u8; G::ELEMENT_SIZE];
+        G::serialize_element(&element, &mut element_bytes);
+        PublicKey {
+            element,
+            bytes: element_bytes,
+        }
+    }
+
+    /// Returns bytes representing the group element corresponding to this key.
+    pub fn as_bytes(&self) -> &[u8] {
+        &self.bytes
+    }
+
+    /// Returns the group element equivalent to this key.
+    pub fn as_element(&self) -> G::Element {
+        self.element
+    }
+}
+
+impl<G: Group> From<&SecretKey<G>> for PublicKey<G> {
+    fn from(secret_key: &SecretKey<G>) -> Self {
+        let element = G::mul_generator(&secret_key.0);
+        Self::from_element(element)
+    }
+}
+
+/// Errors that can occur when converting other types to [`PublicKey`].
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub enum PublicKeyConversionError {
+    /// Invalid size of the byte buffer.
+    InvalidByteSize,
+    /// Byte buffer has correct size, but does not represent a group element.
+    InvalidGroupElement,
+    /// Underlying group element is the group identity.
+    IdentityKey,
+}
+
+impl fmt::Display for PublicKeyConversionError {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        formatter.write_str(match self {
+            Self::InvalidByteSize => "invalid size of the byte buffer",
+            Self::InvalidGroupElement => {
+                "byte buffer has correct size, but does not represent a group element"
+            }
+            Self::IdentityKey => "underlying group element is the group identity",
+        })
+    }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for PublicKeyConversionError {}
+
+impl<G: Group> ops::Add<Self> for PublicKey<G> {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        let element = self.element + rhs.element;
+        Self::from_element(element)
+    }
+}
+
+impl<G: Group> ops::Mul<&G::Scalar> for PublicKey<G> {
+    type Output = Self;
+
+    fn mul(self, k: &G::Scalar) -> Self {
+        let element = self.element * k;
+        Self::from_element(element)
+    }
+}
+
+impl<G: Group> ops::Mul<u64> for PublicKey<G> {
+    type Output = Self;
+
+    fn mul(self, k: u64) -> Self {
+        self * &G::Scalar::from(k)
+    }
+}
+
+/// Keypair for ElGamal encryption and related protocols, consisting of a [`SecretKey`]
+/// and the matching [`PublicKey`].
+pub struct Keypair<G: Group> {
+    secret: SecretKey<G>,
+    public: PublicKey<G>,
+}
+
+impl<G: Group> fmt::Debug for Keypair<G> {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        formatter
+            .debug_struct("Keypair")
+            .field("public", &self.public)
+            .finish_non_exhaustive()
+    }
+}
+
+impl<G: Group> Clone for Keypair<G> {
+    fn clone(&self) -> Self {
+        Keypair {
+            secret: self.secret.clone(),
+            public: self.public.clone(),
+        }
+    }
+}
+
+impl<G: Group> Keypair<G> {
+    /// Generates a random keypair.
+    pub fn generate<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
+        let secret = SecretKey::generate(rng);
+        Keypair {
+            public: PublicKey::from(&secret),
+            secret,
+        }
+    }
+
+    /// Returns the public part of this keypair.
+    pub fn public(&self) -> &PublicKey<G> {
+        &self.public
+    }
+
+    /// Returns the secret part of this keypair.
+    pub fn secret(&self) -> &SecretKey<G> {
+        &self.secret
+    }
+
+    /// Returns public and secret keys comprising this keypair.
+    pub fn into_tuple(self) -> (PublicKey<G>, SecretKey<G>) {
+        (self.public, self.secret)
+    }
+}
+
+impl<G: Group> From<SecretKey<G>> for Keypair<G> {
+    fn from(secret: SecretKey<G>) -> Self {
+        Self {
+            public: PublicKey::from(&secret),
+            secret,
+        }
+    }
+}
+
+impl<G: Group> ops::Mul<&G::Scalar> for Keypair<G> {
+    type Output = Self;
+
+    fn mul(self, k: &G::Scalar) -> Self {
+        Keypair {
+            secret: self.secret * k,
+            public: self.public * k,
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/lib.rs.html b/src/elastic_elgamal/lib.rs.html new file mode 100644 index 0000000..ba3581b --- /dev/null +++ b/src/elastic_elgamal/lib.rs.html @@ -0,0 +1,407 @@ +lib.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+
//! [ElGamal encryption] and related cryptographic protocols with pluggable crypto backend.
+//!
+//! # ⚠ Warnings
+//!
+//! While the logic in this crate relies on standard cryptographic assumptions
+//! (complexity of discrete log and computational / decisional Diffie–Hellman problems
+//! in certain groups), it has not been independently verified for correctness or absence
+//! of side-channel attack vectors. **Use at your own risk.**
+//!
+//! ElGamal encryption is not a good choice for general-purpose public-key encryption
+//! since it is vulnerable to [chosen-ciphertext attacks][CCA]. For security,
+//! decryption operations should be limited on the application level.
+//!
+//! # Overview
+//!
+//! - [`Ciphertext`] provides ElGamal encryption. This and other protocols use
+//!   [`PublicKey`], [`SecretKey`] and [`Keypair`] to represent participants' keys.
+//! - Besides basic encryption, `PublicKey` also provides zero-knowledge proofs of
+//!   [zero encryption](PublicKey::encrypt_zero()) and of
+//!   [Boolean value encryption](PublicKey::encrypt_bool()). These are useful in higher-level
+//!   protocols, e.g., re-encryption.
+//! - Zero-knowledge range proofs for ElGamal ciphertexts are provided via [`RangeProof`]s
+//!   and a high-level [`PublicKey` method](PublicKey::encrypt_range()).
+//! - Proof of equivalence between an ElGamal ciphertext and a Pedersen commitment
+//!   is available as [`CommitmentEquivalenceProof`].
+//! - [`sharing`] module exposes a threshold encryption scheme based
+//!   on [Feldman's verifiable secret sharing][feldman-vss], including verifiable distributed
+//!   decryption.
+//! - [`dkg`] module implements distributed key generation using [Pedersen's scheme][pedersen-dkg]
+//!   with hash commitments.
+//! - [`app`] module provides higher-level protocols utilizing zero-knowledge proofs
+//!   and ElGamal encryption, such as provable encryption of m-of-n choice and a simple version
+//!   of [quadratic voting].
+//!
+//! # Backends
+//!
+//! [`group`] module exposes a generic framework for plugging a [`Group`]
+//! implementation into crypto primitives. It also provides several implementations:
+//!
+//! - [`Ristretto`] and [`Curve25519Subgroup`] implementations based on Curve25519.
+//! - [`Generic`] implementation allowing to plug in any elliptic curve group conforming to
+//!   the traits specified by the [`elliptic-curve`] crate. For example,
+//!   the secp256k1 curve can be used via the [`k256`] crate.
+//!
+//! # Crate features
+//!
+//! ## `std`
+//!
+//! *(on by default)*
+//!
+//! Enables support of types from `std`, such as the `Error` trait and the `HashMap` collection.
+//!
+//! ## `hashbrown`
+//!
+//! *(off by default)*
+//!
+//! Imports hash maps and sets from the [eponymous crate][`hashbrown`]
+//! instead of using ones from the Rust std library. This feature is necessary
+//! if the `std` feature is disabled.
+//!
+//! ## `curve25519-dalek`
+//!
+//! *(on by default)*
+//!
+//! Implements [`Group`] for two prime groups based on Curve25519 using the [`curve25519-dalek`]
+//! crate: its prime subgroup, and the Ristretto transform of Curve25519 (aka ristretto255).
+//!
+//! ## `curve25519-dalek-ng`
+//!
+//! *(off by default)*
+//!
+//! Same in terms of functionality as `curve25519-dalek`, but uses the [`curve25519-dalek-ng`]
+//! crate instead of [`curve25519-dalek`]. This may be beneficial for applications that use
+//! [`bulletproofs`] or other libraries depending on `curve25519-dalek-ng`.
+//!
+//! The `curve25519-dalek-ng` crate does not compile unless some crypto backend is selected.
+//! You may select the backend by specifying `curve25519-dalek-ng` as a direct dependency as follows:
+//!
+//! ```toml
+//! [dependencies.elastic-elgamal]
+//! version = "..."
+//! default-features = false
+//! features = ["std", "curve25519-dalek-ng"]
+//!
+//! [dependencies.curve25519-dalek-ng]
+//! version = "4"
+//! features = ["u64_backend"] # or other backend
+//! ```
+//!
+//! This feature is mutually exclusive with `curve25519-dalek`.
+//!
+//! ## `serde`
+//!
+//! *(off by default)*
+//!
+//! Enables [`Serialize`](::serde::Serialize) / [`Deserialize`](::serde::Deserialize)
+//! implementations for most types in the crate.
+//! Group scalars, elements and wrapper key types are serialized to human-readable formats
+//! (JSON, YAML, TOML, etc.) as strings that represent corresponding byte buffers using
+//! base64-url encoding without padding. For binary formats, byte buffers are serialized directly.
+//!
+//! For complex types (e.g., participant states from the [`sharing`] module), self-consistency
+//! checks are **not** performed on deserialization. That is, deserialization of such types
+//! should only be performed from a trusted source or in the presence of additional integrity
+//! checks.
+//!
+//! # Crate naming
+//!
+//! "Elastic" refers to pluggable backends, configurable params for threshold encryption,
+//! and the construction of zero-knowledge [`RingProof`]s (a proof consists of
+//! a variable number of rings, each of which consists of a variable number of admissible values).
+//! `elastic_elgamal` is also one of [autogenerated Docker container names][docker-rng].
+//!
+//! [ElGamal encryption]: https://en.wikipedia.org/wiki/ElGamal_encryption
+//! [CCA]: https://en.wikipedia.org/wiki/Chosen-ciphertext_attack
+//! [feldman-vss]: https://www.cs.umd.edu/~gasarch/TOPICS/secretsharing/feldmanVSS.pdf
+//! [pedersen-dkg]: https://link.springer.com/content/pdf/10.1007/3-540-46416-6_47.pdf
+//! [`Group`]: group::Group
+//! [`Ristretto`]: group::Ristretto
+//! [`Curve25519Subgroup`]: group::Curve25519Subgroup
+//! [`curve25519-dalek`]: https://docs.rs/curve25519-dalek/
+//! [`curve25519-dalek-ng`]: https://docs.rs/curve25519-dalek-ng/
+//! [`bulletproofs`]: https://docs.rs/bulletproofs/
+//! [`Generic`]: group::Generic
+//! [`elliptic-curve`]: https://docs.rs/elliptic-curve/
+//! [`k256`]: https://docs.rs/k256/
+//! [`hashbrown`]: https://docs.rs/hashbrown/
+//! [docker-rng]: https://github.com/moby/moby/blob/master/pkg/namesgenerator/names-generator.go
+//! [quadratic voting]: https://en.wikipedia.org/wiki/Quadratic_voting
+
+#![cfg_attr(not(feature = "std"), no_std)]
+// Documentation settings.
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![doc(html_root_url = "https://docs.rs/elastic-elgamal/0.3.0")]
+// Linter settings.
+#![warn(missing_debug_implementations, missing_docs, bare_trait_objects)]
+#![warn(clippy::all, clippy::pedantic)]
+#![allow(
+    clippy::must_use_candidate,
+    clippy::module_name_repetitions,
+    clippy::doc_markdown
+)]
+
+pub mod app;
+mod decryption;
+pub mod dkg;
+mod encryption;
+pub mod group;
+mod keys;
+mod proofs;
+#[cfg(feature = "serde")]
+mod serde;
+pub mod sharing;
+
+// Polyfill for `alloc` types.
+mod alloc {
+    #[cfg(not(feature = "std"))]
+    extern crate alloc as std;
+
+    pub use std::{borrow::Cow, string::ToString, vec, vec::Vec};
+
+    #[cfg(all(not(feature = "std"), not(feature = "hashbrown")))]
+    compile_error!(
+        "One of `std` or `hashbrown` features must be enabled in order \
+         to get a hash map implementation"
+    );
+
+    #[cfg(feature = "hashbrown")]
+    pub use hashbrown::HashMap;
+    #[cfg(not(feature = "hashbrown"))]
+    pub use std::collections::HashMap;
+}
+
+// Polyfill for Curve25519 types.
+#[cfg(any(feature = "curve25519-dalek-ng", feature = "curve25519-dalek"))]
+mod curve25519 {
+    #[cfg(all(feature = "curve25519-dalek-ng", feature = "curve25519-dalek"))]
+    compile_error!("`curve25519-dalek-ng` and `curve25519-dalek` features are mutually exclusive");
+
+    #[cfg(feature = "curve25519-dalek")]
+    pub use curve25519_dalek::*;
+    #[cfg(feature = "curve25519-dalek-ng")]
+    pub use curve25519_dalek_ng::*;
+}
+
+mod sealed {
+    pub trait Sealed {}
+}
+
+pub use crate::{
+    decryption::{CandidateDecryption, VerifiableDecryption},
+    encryption::{Ciphertext, CiphertextWithValue, DiscreteLogTable},
+    keys::{Keypair, PublicKey, PublicKeyConversionError, SecretKey},
+    proofs::{
+        CommitmentEquivalenceProof, LogEqualityProof, PreparedRange, ProofOfPossession,
+        RangeDecomposition, RangeProof, RingProof, RingProofBuilder, SumOfSquaresProof,
+        VerificationError,
+    },
+};
+
+#[cfg(doctest)]
+doc_comment::doctest!("../README.md");
+
\ No newline at end of file diff --git a/src/elastic_elgamal/proofs/commitment.rs.html b/src/elastic_elgamal/proofs/commitment.rs.html new file mode 100644 index 0000000..ec232e0 --- /dev/null +++ b/src/elastic_elgamal/proofs/commitment.rs.html @@ -0,0 +1,647 @@ +commitment.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+
//! Zero-knowledge proof of ElGamal encryption and Pedersen commitment equivalence.
+
+use merlin::Transcript;
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+#[cfg(feature = "serde")]
+use crate::serde::ScalarHelper;
+use crate::{
+    group::Group,
+    proofs::{TranscriptForGroup, VerificationError},
+    Ciphertext, CiphertextWithValue, PublicKey, SecretKey,
+};
+
+/// Zero-knowledge proof that an ElGamal ciphertext encrypts the same value as a Pedersen
+/// commitment.
+///
+/// This proof can be used to switch from frameworks applicable to ElGamal ciphertexts, to ones
+/// applicable to Pedersen commitments (e.g., [Bulletproofs] for range proofs).
+///
+/// [Bulletproofs]: https://crypto.stanford.edu/bulletproofs/
+///
+/// # Construction
+///
+/// We want to prove in zero knowledge the knowledge of scalars `r_e`, `v`, `r_c` such as
+///
+/// ```text
+/// R = [r_e]G; B = [v]G + [r_e]K;
+/// // (R, B) is ElGamal ciphertext of `v` for public key `K`
+/// C = [v]G + [r_c]H;
+/// // C is Pedersen commitment to `v`
+/// ```
+///
+/// Here, we assume that the conventional group generator `G` is shared between encryption and
+/// commitment protocols.
+///
+/// An interactive version of the proof can be built as a sigma protocol:
+///
+/// 1. **Commitment.** The prover generates 3 random scalars `e_r`, `e_v` and `e_c` and commits
+///    to them via `E_r = [e_r]G`, `E_b = [e_v]G + [e_r]K`, and `E_c = [e_v]G + [e_c]H`.
+/// 2. **Challenge.** The verifier sends to the prover random scalar `c`.
+/// 3. **Response.** The prover computes the following scalars and sends them to the verifier.
+///
+/// ```text
+/// s_r = e_r + c * r_e;
+/// s_v = e_v + c * v;
+/// s_c = e_c + c * r_c;
+/// ```
+///
+/// The verification equations are
+///
+/// ```text
+/// [s_r]G ?= E_r + [c]R;
+/// [s_v]G + [s_r]K ?= E_b + [c]B;
+/// [s_v]G + [s_c]H ?= E_c + [c]C;
+/// ```
+///
+/// A non-interactive version of the proof is obtained by applying [Fiat–Shamir transform][fst].
+/// As with other proofs, it is more efficient to represent a proof as the challenge
+/// and responses (i.e., 4 scalars in total).
+///
+/// [fst]: https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic
+///
+/// # Examples
+///
+/// ```
+/// # use elastic_elgamal::{
+/// #     group::{ElementOps, ScalarOps, Group, Ristretto},
+/// #     Keypair, SecretKey, CommitmentEquivalenceProof, CiphertextWithValue,
+/// # };
+/// # use merlin::Transcript;
+/// # use rand::thread_rng;
+/// #
+/// # const BLINDING_BASE: &[u8] = &[
+/// #     140, 146, 64, 180, 86, 169, 230, 220, 101, 195, 119, 161, 4,
+/// #     141, 116, 95, 148, 160, 140, 219, 127, 68, 203, 205, 123, 70,
+/// #     243, 64, 72, 135, 17, 52,
+/// # ];
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// let blinding_base = // Blinding base for Pedersen commitments
+///                     // (e.g., from Bulletproofs)
+/// #    Ristretto::deserialize_element(BLINDING_BASE).unwrap();
+/// let mut rng = thread_rng();
+/// let (receiver, _) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+///
+/// // Create an ElGamal ciphertext of `value` for `receiver`.
+/// let value = 424242_u64;
+/// let ciphertext = CiphertextWithValue::new(value, &receiver, &mut rng)
+///     .generalize();
+/// // Create a blinding factor for the Pedersen commitment of the same value.
+/// let blinding = SecretKey::generate(&mut rng);
+/// let (proof, commitment) = CommitmentEquivalenceProof::new(
+///     &ciphertext,
+///     &receiver,
+///     &blinding,
+///     blinding_base,
+///     &mut Transcript::new(b"custom_proof"),
+///     &mut rng,
+/// );
+/// // Use `commitment` and `blinding` in other proofs...
+///
+/// proof.verify(
+///     &ciphertext.into(),
+///     &receiver,
+///     commitment,
+///     blinding_base,
+///     &mut Transcript::new(b"custom_proof"),
+/// )?;
+/// # Ok(())
+/// # }
+/// ```
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct CommitmentEquivalenceProof<G: Group> {
+    #[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
+    challenge: G::Scalar,
+    #[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
+    randomness_response: G::Scalar,
+    #[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
+    value_response: G::Scalar,
+    #[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
+    commitment_response: G::Scalar,
+}
+
+impl<G: Group> CommitmentEquivalenceProof<G> {
+    /// Creates a proof based on the `ciphertext` for `receiver` and `commitment_blinding`
+    /// with `commitment_blinding_base` for a Pedersen commitment. (The latter two args
+    /// correspond to `r_c` and `H` in the [*Construction*](#construction) section, respectively.)
+    ///
+    /// # Return value
+    ///
+    /// Returns a proof together with the Pedersen commitment.
+    pub fn new<R: RngCore + CryptoRng>(
+        ciphertext: &CiphertextWithValue<G>,
+        receiver: &PublicKey<G>,
+        commitment_blinding: &SecretKey<G>,
+        commitment_blinding_base: G::Element,
+        transcript: &mut Transcript,
+        rng: &mut R,
+    ) -> (Self, G::Element) {
+        let commitment = G::multi_mul(
+            [ciphertext.value(), commitment_blinding.expose_scalar()],
+            [G::generator(), commitment_blinding_base],
+        );
+
+        transcript.start_proof(b"commitment_equivalence");
+        transcript.append_element_bytes(b"K", receiver.as_bytes());
+        transcript.append_element::<G>(b"R", &ciphertext.inner().random_element);
+        transcript.append_element::<G>(b"B", &ciphertext.inner().blinded_element);
+        transcript.append_element::<G>(b"C", &commitment);
+
+        let random_scalar = SecretKey::<G>::generate(rng);
+        let value_scalar = SecretKey::<G>::generate(rng);
+        let commitment_scalar = SecretKey::<G>::generate(rng);
+        let random_commitment = G::mul_generator(random_scalar.expose_scalar());
+        transcript.append_element::<G>(b"[e_r]G", &random_commitment);
+
+        let value_element = G::mul_generator(value_scalar.expose_scalar());
+        let enc_blinding_commitment =
+            value_element + receiver.as_element() * random_scalar.expose_scalar();
+        transcript.append_element::<G>(b"[e_v]G + [e_r]K", &enc_blinding_commitment);
+        let commitment_commitment =
+            value_element + commitment_blinding_base * commitment_scalar.expose_scalar();
+        transcript.append_element::<G>(b"[e_v]G + [e_c]H", &commitment_commitment);
+
+        let challenge = transcript.challenge_scalar::<G>(b"c");
+        let randomness_response =
+            challenge * ciphertext.randomness().expose_scalar() + random_scalar.expose_scalar();
+        let value_response = challenge * ciphertext.value() + value_scalar.expose_scalar();
+        let commitment_response =
+            challenge * commitment_blinding.expose_scalar() + commitment_scalar.expose_scalar();
+
+        let proof = Self {
+            challenge,
+            randomness_response,
+            value_response,
+            commitment_response,
+        };
+        (proof, commitment)
+    }
+
+    /// # Errors
+    ///
+    /// Returns an error if this proof does not verify.
+    pub fn verify(
+        &self,
+        ciphertext: &Ciphertext<G>,
+        receiver: &PublicKey<G>,
+        commitment: G::Element,
+        commitment_blinding_base: G::Element,
+        transcript: &mut Transcript,
+    ) -> Result<(), VerificationError> {
+        transcript.start_proof(b"commitment_equivalence");
+        transcript.append_element_bytes(b"K", receiver.as_bytes());
+        transcript.append_element::<G>(b"R", &ciphertext.random_element);
+        transcript.append_element::<G>(b"B", &ciphertext.blinded_element);
+        transcript.append_element::<G>(b"C", &commitment);
+
+        let neg_challenge = -self.challenge;
+        let random_commitment = G::vartime_double_mul_generator(
+            &neg_challenge,
+            ciphertext.random_element,
+            &self.randomness_response,
+        );
+        transcript.append_element::<G>(b"[e_r]G", &random_commitment);
+
+        let enc_blinding_commitment = G::vartime_multi_mul(
+            [
+                &self.value_response,
+                &self.randomness_response,
+                &neg_challenge,
+            ],
+            [
+                G::generator(),
+                receiver.as_element(),
+                ciphertext.blinded_element,
+            ],
+        );
+        transcript.append_element::<G>(b"[e_v]G + [e_r]K", &enc_blinding_commitment);
+
+        let commitment_commitment = G::vartime_multi_mul(
+            [
+                &self.value_response,
+                &self.commitment_response,
+                &neg_challenge,
+            ],
+            [G::generator(), commitment_blinding_base, commitment],
+        );
+        transcript.append_element::<G>(b"[e_v]G + [e_c]H", &commitment_commitment);
+
+        let expected_challenge = transcript.challenge_scalar::<G>(b"c");
+        if expected_challenge == self.challenge {
+            Ok(())
+        } else {
+            Err(VerificationError::ChallengeMismatch)
+        }
+    }
+}
+
+#[cfg(all(test, feature = "curve25519-dalek-ng"))]
+mod tests {
+    use super::*;
+    use crate::{
+        group::{ElementOps, Ristretto},
+        Keypair,
+    };
+
+    use bulletproofs::PedersenGens;
+    use rand::thread_rng;
+
+    #[test]
+    fn equivalence_proof_basics() {
+        let mut rng = thread_rng();
+        let (receiver, _) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+        let value = 1234_u64;
+        let ciphertext = CiphertextWithValue::new(value, &receiver, &mut rng).generalize();
+
+        let commitment_gens = PedersenGens::default();
+        assert_eq!(commitment_gens.B, Ristretto::generator());
+        let blinding = SecretKey::generate(&mut rng);
+
+        let (proof, commitment) = CommitmentEquivalenceProof::new(
+            &ciphertext,
+            &receiver,
+            &blinding,
+            commitment_gens.B_blinding,
+            &mut Transcript::new(b"test"),
+            &mut rng,
+        );
+        assert_eq!(
+            commitment,
+            commitment_gens.commit(*ciphertext.value(), *blinding.expose_scalar())
+        );
+
+        let ciphertext = ciphertext.into();
+        proof
+            .verify(
+                &ciphertext,
+                &receiver,
+                commitment,
+                commitment_gens.B_blinding,
+                &mut Transcript::new(b"test"),
+            )
+            .unwrap();
+
+        let other_ciphertext = receiver.encrypt(8_u64, &mut rng);
+        let err = proof
+            .verify(
+                &other_ciphertext,
+                &receiver,
+                commitment,
+                commitment_gens.B_blinding,
+                &mut Transcript::new(b"test"),
+            )
+            .unwrap_err();
+        assert!(matches!(err, VerificationError::ChallengeMismatch));
+
+        let err = proof
+            .verify(
+                &ciphertext,
+                &receiver,
+                commitment + Ristretto::generator(),
+                commitment_gens.B_blinding,
+                &mut Transcript::new(b"test"),
+            )
+            .unwrap_err();
+        assert!(matches!(err, VerificationError::ChallengeMismatch));
+
+        let err = proof
+            .verify(
+                &ciphertext,
+                &receiver,
+                commitment,
+                commitment_gens.B_blinding,
+                &mut Transcript::new(b"other_test"),
+            )
+            .unwrap_err();
+        assert!(matches!(err, VerificationError::ChallengeMismatch));
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/proofs/log_equality.rs.html b/src/elastic_elgamal/proofs/log_equality.rs.html new file mode 100644 index 0000000..0244df3 --- /dev/null +++ b/src/elastic_elgamal/proofs/log_equality.rs.html @@ -0,0 +1,487 @@ +log_equality.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+
//! [`LogEqualityProof`] and related logic.
+
+use merlin::Transcript;
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+#[cfg(feature = "serde")]
+use crate::serde::ScalarHelper;
+use crate::{
+    alloc::{vec, Vec},
+    group::Group,
+    proofs::{TranscriptForGroup, VerificationError},
+    PublicKey, SecretKey,
+};
+
+/// Zero-knowledge proof of equality of two discrete logarithms in different bases,
+/// aka Chaum–Pedersen protocol.
+///
+/// # Construction
+///
+/// This proof is a result of the [Fiat–Shamir transform][fst] applied to a standard
+/// ZKP of equality of the two discrete logs in different bases.
+///
+/// - Public parameters of the proof are the two bases `G` and `K` in a prime-order group
+///   in which discrete log problem is believed to be hard.
+/// - Prover and verifier both know group elements `R` and `B`, which presumably have
+///   the same discrete log in bases `G` and `K` respectively.
+/// - Prover additionally knows the discrete log in question: `r = dlog_G(R) = dlog_K(B)`.
+///
+/// The interactive proof is specified as a sigma protocol (see, e.g., [this course])
+/// as follows:
+///
+/// 1. **Commitment:** The prover generates random scalar `x`. The prover sends to the verifier
+///    `X_G = [x]G` and `X_K = [x]K`.
+/// 2. **Challenge:** The verifier sends to the prover random scalar `c`.
+/// 3. **Response:** The prover computes scalar `s = x + cr` and sends it to the verifier.
+///
+/// Verification equations are:
+///
+/// ```text
+/// [s]G ?= X_G + [c]R;
+/// [s]K ?= X_K + [c]B.
+/// ```
+///
+/// In the non-interactive version of the proof, challenge `c` is derived from `hash(M)`,
+/// where `hash()` is a cryptographically secure hash function, and `M` is an optional message
+/// verified together with the proof (cf. public-key digital signatures). If `M` is set, we
+/// use a proof as a *signature of knowledge*. This allows to tie the proof to the context,
+/// so it cannot be (re)used in other contexts.
+///
+/// To reduce the size of the proof, we use the trick underpinning ring signature constructions.
+/// Namely, we represent the proof as `(c, s)`; during verification, we restore `X_G`, `X_K`
+/// from the original verification equations above.
+///
+/// # Implementation details
+///
+/// - The proof is serialized as 2 scalars: `(c, s)`.
+/// - Proof generation is constant-time. Verification is **not** constant-time.
+/// - Challenge `c` is derived using [`Transcript`] API.
+///
+/// # Examples
+///
+/// ```
+/// # use elastic_elgamal::{group::Ristretto, Keypair, SecretKey, LogEqualityProof};
+/// # use merlin::Transcript;
+/// # use rand::thread_rng;
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// let mut rng = thread_rng();
+/// let (log_base, _) =
+///     Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+/// let (power_g, discrete_log) =
+///     Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+/// let power_k = log_base.as_element() * discrete_log.expose_scalar();
+///
+/// let proof = LogEqualityProof::new(
+///     &log_base,
+///     &discrete_log,
+///     (power_g.as_element(), power_k),
+///     &mut Transcript::new(b"custom_proof"),
+///     &mut rng,
+/// );
+/// proof.verify(
+///     &log_base,
+///     (power_g.as_element(), power_k),
+///     &mut Transcript::new(b"custom_proof"),
+/// )?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [fst]: https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic
+/// [this course]: http://www.cs.au.dk/~ivan/Sigma.pdf
+#[derive(Debug, Clone, Copy)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct LogEqualityProof<G: Group> {
+    #[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
+    challenge: G::Scalar,
+    #[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
+    response: G::Scalar,
+}
+
+impl<G: Group> LogEqualityProof<G> {
+    /// Creates a new proof.
+    ///
+    /// # Parameters
+    ///
+    /// - `log_base` is the second discrete log base (`K` in the notation above). The first
+    ///   log base is always the [`Group`] generator.
+    /// - `secret` is the discrete log (`r` in the notation above).
+    /// - `powers` are `[r]G` and `[r]K`, respectively. It is **not** checked whether `r`
+    ///   is a discrete log of these powers; if this is not the case, the constructed proof
+    ///   will not [`verify`](Self::verify()).
+    pub fn new<R: CryptoRng + RngCore>(
+        log_base: &PublicKey<G>,
+        secret: &SecretKey<G>,
+        powers: (G::Element, G::Element),
+        transcript: &mut Transcript,
+        rng: &mut R,
+    ) -> Self {
+        transcript.start_proof(b"log_eq");
+        transcript.append_element_bytes(b"K", log_base.as_bytes());
+        transcript.append_element::<G>(b"[r]G", &powers.0);
+        transcript.append_element::<G>(b"[r]K", &powers.1);
+
+        let random_scalar = SecretKey::<G>::generate(rng);
+        transcript.append_element::<G>(b"[x]G", &G::mul_generator(random_scalar.expose_scalar()));
+        transcript.append_element::<G>(
+            b"[x]K",
+            &(log_base.as_element() * random_scalar.expose_scalar()),
+        );
+        let challenge = transcript.challenge_scalar::<G>(b"c");
+        let response = challenge * secret.expose_scalar() + random_scalar.expose_scalar();
+
+        Self {
+            challenge,
+            response,
+        }
+    }
+
+    /// Verifies this proof.
+    ///
+    /// # Parameters
+    ///
+    /// - `log_base` is the second discrete log base (`K` in the notation above). The first
+    ///   log base is always the [`Group`] generator.
+    /// - `powers` are group elements presumably equal to `[r]G` and `[r]K` respectively,
+    ///   where `r` is a secret scalar.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if this proof does not verify.
+    pub fn verify(
+        &self,
+        log_base: &PublicKey<G>,
+        powers: (G::Element, G::Element),
+        transcript: &mut Transcript,
+    ) -> Result<(), VerificationError> {
+        let commitments = (
+            G::vartime_double_mul_generator(&-self.challenge, powers.0, &self.response),
+            G::vartime_multi_mul(
+                &[-self.challenge, self.response],
+                [powers.1, log_base.as_element()],
+            ),
+        );
+
+        transcript.start_proof(b"log_eq");
+        transcript.append_element_bytes(b"K", log_base.as_bytes());
+        transcript.append_element::<G>(b"[r]G", &powers.0);
+        transcript.append_element::<G>(b"[r]K", &powers.1);
+        transcript.append_element::<G>(b"[x]G", &commitments.0);
+        transcript.append_element::<G>(b"[x]K", &commitments.1);
+        let expected_challenge = transcript.challenge_scalar::<G>(b"c");
+
+        if expected_challenge == self.challenge {
+            Ok(())
+        } else {
+            Err(VerificationError::ChallengeMismatch)
+        }
+    }
+
+    /// Serializes this proof into bytes. As described [above](#implementation-details),
+    /// the is serialized as 2 scalars: `(c, s)`, i.e., challenge and response.
+    pub fn to_bytes(self) -> Vec<u8> {
+        let mut bytes = vec![0_u8; 2 * G::SCALAR_SIZE];
+        G::serialize_scalar(&self.challenge, &mut bytes[..G::SCALAR_SIZE]);
+        G::serialize_scalar(&self.response, &mut bytes[G::SCALAR_SIZE..]);
+        bytes
+    }
+
+    /// Attempts to parse the proof from `bytes`. Returns `None` if `bytes` do not represent
+    /// a well-formed proof.
+    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
+        if bytes.len() != 2 * G::SCALAR_SIZE {
+            return None;
+        }
+
+        let challenge = G::deserialize_scalar(&bytes[..G::SCALAR_SIZE])?;
+        let response = G::deserialize_scalar(&bytes[G::SCALAR_SIZE..])?;
+        Some(Self {
+            challenge,
+            response,
+        })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::thread_rng;
+
+    use super::*;
+    use crate::group::Ristretto;
+
+    type Keypair = crate::Keypair<Ristretto>;
+
+    #[test]
+    fn log_equality_basics() {
+        let mut rng = thread_rng();
+        let log_base = Keypair::generate(&mut rng).public().clone();
+
+        for _ in 0..100 {
+            let (generator_val, secret) = Keypair::generate(&mut rng).into_tuple();
+            let key_val = log_base.as_element() * secret.expose_scalar();
+            let proof = LogEqualityProof::new(
+                &log_base,
+                &secret,
+                (generator_val.as_element(), key_val),
+                &mut Transcript::new(b"testing_log_equality"),
+                &mut rng,
+            );
+
+            proof
+                .verify(
+                    &log_base,
+                    (generator_val.as_element(), key_val),
+                    &mut Transcript::new(b"testing_log_equality"),
+                )
+                .unwrap();
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/proofs/mod.rs.html b/src/elastic_elgamal/proofs/mod.rs.html new file mode 100644 index 0000000..d3bb266 --- /dev/null +++ b/src/elastic_elgamal/proofs/mod.rs.html @@ -0,0 +1,245 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+
//! Zero-knowledge proofs.
+
+use merlin::Transcript;
+
+use core::fmt;
+
+use crate::{
+    alloc::vec,
+    group::{Group, RandomBytesProvider},
+};
+
+mod commitment;
+mod log_equality;
+mod mul;
+mod possession;
+mod range;
+mod ring;
+
+pub use self::{
+    commitment::CommitmentEquivalenceProof,
+    log_equality::LogEqualityProof,
+    mul::SumOfSquaresProof,
+    possession::ProofOfPossession,
+    range::{PreparedRange, RangeDecomposition, RangeProof},
+    ring::{RingProof, RingProofBuilder},
+};
+
+/// Extension trait for Merlin transcripts used in constructing our proofs.
+pub(crate) trait TranscriptForGroup {
+    fn start_proof(&mut self, proof_label: &'static [u8]);
+
+    fn append_element_bytes(&mut self, label: &'static [u8], element_bytes: &[u8]);
+
+    fn append_element<G: Group>(&mut self, label: &'static [u8], element: &G::Element);
+
+    fn challenge_scalar<G: Group>(&mut self, label: &'static [u8]) -> G::Scalar;
+}
+
+impl TranscriptForGroup for Transcript {
+    fn start_proof(&mut self, proof_label: &'static [u8]) {
+        self.append_message(b"dom-sep", proof_label);
+    }
+
+    fn append_element_bytes(&mut self, label: &'static [u8], element_bytes: &[u8]) {
+        self.append_message(label, element_bytes);
+    }
+
+    fn append_element<G: Group>(&mut self, label: &'static [u8], element: &G::Element) {
+        let mut output = vec![0_u8; G::ELEMENT_SIZE];
+        G::serialize_element(element, &mut output);
+        self.append_element_bytes(label, &output);
+    }
+
+    fn challenge_scalar<G: Group>(&mut self, label: &'static [u8]) -> G::Scalar {
+        G::scalar_from_random_bytes(RandomBytesProvider::new(self, label))
+    }
+}
+
+/// Error verifying base proofs, such as [`RingProof`], [`LogEqualityProof`]
+/// or [`ProofOfPossession`].
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum VerificationError {
+    /// Restored challenge scalar does not match the one provided in the proof.
+    ///
+    /// This error most likely means that the proof itself is malformed, or that it was created
+    /// for a different context than it is being verified for.
+    ChallengeMismatch,
+    /// A collection (e.g., number of responses in a [`RingProof`]) has a different size
+    /// than expected.
+    ///
+    /// This error most likely means that the proof is malformed.
+    LenMismatch {
+        /// Human-readable collection name, such as "public keys".
+        collection: &'static str,
+        /// Expected size of the collection.
+        expected: usize,
+        /// Actual size of the collection.
+        actual: usize,
+    },
+}
+
+impl VerificationError {
+    pub(crate) fn check_lengths(
+        collection: &'static str,
+        expected: usize,
+        actual: usize,
+    ) -> Result<(), Self> {
+        if expected == actual {
+            Ok(())
+        } else {
+            Err(Self::LenMismatch {
+                collection,
+                expected,
+                actual,
+            })
+        }
+    }
+}
+
+impl fmt::Display for VerificationError {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::ChallengeMismatch => formatter.write_str(
+                "restored challenge scalar does not match the one provided in the proof",
+            ),
+
+            Self::LenMismatch {
+                collection,
+                expected,
+                actual,
+            } => write!(
+                formatter,
+                "number of {collection} ({actual}) differs from expected ({expected})",
+            ),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for VerificationError {}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/proofs/mul.rs.html b/src/elastic_elgamal/proofs/mul.rs.html new file mode 100644 index 0000000..25a17b1 --- /dev/null +++ b/src/elastic_elgamal/proofs/mul.rs.html @@ -0,0 +1,909 @@ +mul.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+
//! Proofs related to multiplication.
+
+use merlin::Transcript;
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+use zeroize::Zeroizing;
+
+use core::iter;
+
+#[cfg(feature = "serde")]
+use crate::serde::{ScalarHelper, VecHelper};
+use crate::{
+    alloc::Vec, group::Group, proofs::TranscriptForGroup, Ciphertext, CiphertextWithValue,
+    PublicKey, SecretKey, VerificationError,
+};
+
+/// Zero-knowledge proof that an ElGamal-encrypted value is equal to a sum of squares
+/// of one or more other ElGamal-encrypted values.
+///
+/// # Construction
+///
+/// Consider the case with a single sum element (i.e., proving that an encrypted value is
+/// a square of another encrypted value). The prover wants to prove the knowledge of scalars
+///
+/// ```text
+/// r_x, x, r_z:
+///   R_x = [r_x]G, X = [x]G + [r_x]K;
+///   R_z = [r_z]G, Z = [x^2]G + [r_z]K,
+/// ```
+///
+/// where
+///
+/// - `G` is the conventional generator of the considered prime-order group
+/// - `K` is a group element equivalent to the receiver's public key
+/// - `(R_x, X)` and `(R_z, Z)` are ElGamal ciphertexts of values `x` and `x^2`, respectively.
+///
+/// Observe that
+///
+/// ```text
+/// r'_z := r_z - x * r_x =>
+///   R_z = [r'_z]G + [x]R_x; Z = [x]X + [r'_z]K.
+/// ```
+///
+/// and that proving the knowledge of `(r_x, x, r'_z)` is equivalent to the initial problem.
+/// The new problem can be solved using a conventional sigma protocol:
+///
+/// 1. **Commitment.** The prover generates random scalars `e_r`, `e_x` and `e_z` and commits
+///    to them via `E_r = [e_r]G`, `E_x = [e_x]G + [e_r]K`, `E_rz = [e_x]R_x + [e_z]G` and
+///    `E_z = [e_x]X + [e_z]K`.
+/// 2. **Challenge.** The verifier sends to the prover random scalar `c`.
+/// 3. **Response.** The prover computes the following scalars and sends them to the verifier.
+///
+/// ```text
+/// s_r = e_r + c * r_x;
+/// s_x = e_x + c * x;
+/// s_z = e_z + c * (r_z - x * r_x);
+/// ```
+///
+/// The verification equations are
+///
+/// ```text
+/// [s_r]G ?= E_r + [c]R_x;
+/// [s_x]G + [s_r]K ?= E_x + [c]X;
+/// [s_x]R_x + [s_z]G ?= E_rz + [c]R_z;
+/// [s_x]X + [s_z]K ?= E_z + [c]Z.
+/// ```
+///
+/// The case with multiple squares is a straightforward generalization:
+///
+/// - `e_r`, `E_r`, `e_x`, `E_x`, `s_r` and `s_x` are independently defined for each
+///   partial ciphertext in the same way as above.
+/// - Commitments `E_rz` and `E_z` sum over `[e_x]R_x` and `[e_x]X` for all ciphertexts,
+///   respectively.
+/// - Response `s_z` similarly substitutes `x * r_x` with the corresponding sum.
+///
+/// A non-interactive version of the proof is obtained by applying [Fiat–Shamir transform][fst].
+/// As with [`LogEqualityProof`], it is more efficient to represent a proof as the challenge
+/// and responses; in this case, the proof size is `2n + 2` scalars, where `n` is the number of
+/// partial ciphertexts.
+///
+/// [fst]: https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic
+/// [`LogEqualityProof`]: crate::LogEqualityProof
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct SumOfSquaresProof<G: Group> {
+    #[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
+    challenge: G::Scalar,
+    #[cfg_attr(feature = "serde", serde(with = "VecHelper::<ScalarHelper<G>, 2>"))]
+    ciphertext_responses: Vec<G::Scalar>,
+    #[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
+    sum_response: G::Scalar,
+}
+
+impl<G: Group> SumOfSquaresProof<G> {
+    fn initialize_transcript(transcript: &mut Transcript, receiver: &PublicKey<G>) {
+        transcript.start_proof(b"sum_of_squares");
+        transcript.append_element_bytes(b"K", receiver.as_bytes());
+    }
+
+    /// Creates a new proof that squares of values encrypted in `ciphertexts` for `receiver` sum up
+    /// to a value encrypted in `sum_of_squares_ciphertext`.
+    ///
+    /// All provided ciphertexts must be encrypted for `receiver`; otherwise, the created proof
+    /// will not verify.
+    #[allow(clippy::needless_collect)] // false positive
+    pub fn new<'a, R: RngCore + CryptoRng>(
+        ciphertexts: impl Iterator<Item = &'a CiphertextWithValue<G>>,
+        sum_of_squares_ciphertext: &CiphertextWithValue<G>,
+        receiver: &PublicKey<G>,
+        transcript: &mut Transcript,
+        rng: &mut R,
+    ) -> Self {
+        Self::initialize_transcript(transcript, receiver);
+
+        let sum_scalar = SecretKey::<G>::generate(rng);
+        let mut sum_random_scalar = sum_of_squares_ciphertext.randomness().clone();
+
+        let partial_scalars: Vec<_> = ciphertexts
+            .map(|ciphertext| {
+                transcript.append_element::<G>(b"R_x", &ciphertext.inner().random_element);
+                transcript.append_element::<G>(b"X", &ciphertext.inner().blinded_element);
+
+                let random_scalar = SecretKey::<G>::generate(rng);
+                let random_commitment = G::mul_generator(random_scalar.expose_scalar());
+                transcript.append_element::<G>(b"[e_r]G", &random_commitment);
+                let value_scalar = SecretKey::<G>::generate(rng);
+                let value_commitment = G::mul_generator(value_scalar.expose_scalar())
+                    + receiver.as_element() * random_scalar.expose_scalar();
+                transcript.append_element::<G>(b"[e_x]G + [e_r]K", &value_commitment);
+
+                let neg_value = Zeroizing::new(-*ciphertext.value());
+                sum_random_scalar += ciphertext.randomness() * &neg_value;
+                (ciphertext, random_scalar, value_scalar)
+            })
+            .collect();
+
+        let scalars = partial_scalars
+            .iter()
+            .map(|(_, _, value_scalar)| value_scalar.expose_scalar())
+            .chain(iter::once(sum_scalar.expose_scalar()));
+        let random_sum_commitment = {
+            let elements = partial_scalars
+                .iter()
+                .map(|(ciphertext, ..)| ciphertext.inner().random_element)
+                .chain(iter::once(G::generator()));
+            G::multi_mul(scalars.clone(), elements)
+        };
+        let value_sum_commitment = {
+            let elements = partial_scalars
+                .iter()
+                .map(|(ciphertext, ..)| ciphertext.inner().blinded_element)
+                .chain(iter::once(receiver.as_element()));
+            G::multi_mul(scalars, elements)
+        };
+
+        transcript.append_element::<G>(b"R_z", &sum_of_squares_ciphertext.inner().random_element);
+        transcript.append_element::<G>(b"Z", &sum_of_squares_ciphertext.inner().blinded_element);
+        transcript.append_element::<G>(b"[e_x]R_x + [e_z]G", &random_sum_commitment);
+        transcript.append_element::<G>(b"[e_x]X + [e_z]K", &value_sum_commitment);
+        let challenge = transcript.challenge_scalar::<G>(b"c");
+
+        let ciphertext_responses = partial_scalars
+            .into_iter()
+            .flat_map(|(ciphertext, random_scalar, value_scalar)| {
+                [
+                    challenge * ciphertext.randomness().expose_scalar()
+                        + random_scalar.expose_scalar(),
+                    challenge * ciphertext.value() + value_scalar.expose_scalar(),
+                ]
+            })
+            .collect();
+        let sum_response =
+            challenge * sum_random_scalar.expose_scalar() + sum_scalar.expose_scalar();
+
+        Self {
+            challenge,
+            ciphertext_responses,
+            sum_response,
+        }
+    }
+
+    /// Verifies this proof against the provided partial ciphertexts and the ciphertext of the
+    /// sum of their squares. The order of partial ciphertexts must correspond to their order
+    /// when creating the proof.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if this proof does not verify.
+    pub fn verify<'a>(
+        &self,
+        ciphertexts: impl Iterator<Item = &'a Ciphertext<G>> + Clone,
+        sum_of_squares_ciphertext: &Ciphertext<G>,
+        receiver: &PublicKey<G>,
+        transcript: &mut Transcript,
+    ) -> Result<(), VerificationError> {
+        let ciphertexts_count = ciphertexts.clone().count();
+        VerificationError::check_lengths(
+            "ciphertext responses",
+            self.ciphertext_responses.len(),
+            ciphertexts_count * 2,
+        )?;
+
+        Self::initialize_transcript(transcript, receiver);
+        let neg_challenge = -self.challenge;
+
+        for (response_chunk, ciphertext) in
+            self.ciphertext_responses.chunks(2).zip(ciphertexts.clone())
+        {
+            transcript.append_element::<G>(b"R_x", &ciphertext.random_element);
+            transcript.append_element::<G>(b"X", &ciphertext.blinded_element);
+
+            let r_response = &response_chunk[0];
+            let v_response = &response_chunk[1];
+            let random_commitment = G::vartime_double_mul_generator(
+                &-self.challenge,
+                ciphertext.random_element,
+                r_response,
+            );
+            transcript.append_element::<G>(b"[e_r]G", &random_commitment);
+            let value_commitment = G::vartime_multi_mul(
+                [v_response, r_response, &neg_challenge],
+                [
+                    G::generator(),
+                    receiver.as_element(),
+                    ciphertext.blinded_element,
+                ],
+            );
+            transcript.append_element::<G>(b"[e_x]G + [e_r]K", &value_commitment);
+        }
+
+        let scalars = OddItems::new(self.ciphertext_responses.iter())
+            .chain([&self.sum_response, &neg_challenge]);
+        let random_sum_commitment = {
+            let elements = ciphertexts
+                .clone()
+                .map(|c| c.random_element)
+                .chain([G::generator(), sum_of_squares_ciphertext.random_element]);
+            G::vartime_multi_mul(scalars.clone(), elements)
+        };
+        let value_sum_commitment = {
+            let elements = ciphertexts.map(|c| c.blinded_element).chain([
+                receiver.as_element(),
+                sum_of_squares_ciphertext.blinded_element,
+            ]);
+            G::vartime_multi_mul(scalars, elements)
+        };
+
+        transcript.append_element::<G>(b"R_z", &sum_of_squares_ciphertext.random_element);
+        transcript.append_element::<G>(b"Z", &sum_of_squares_ciphertext.blinded_element);
+        transcript.append_element::<G>(b"[e_x]R_x + [e_z]G", &random_sum_commitment);
+        transcript.append_element::<G>(b"[e_x]X + [e_z]K", &value_sum_commitment);
+        let expected_challenge = transcript.challenge_scalar::<G>(b"c");
+
+        if expected_challenge == self.challenge {
+            Ok(())
+        } else {
+            Err(VerificationError::ChallengeMismatch)
+        }
+    }
+}
+
+/// Thin wrapper around an iterator that drops its even-indexed elements. This is necessary
+/// because `Ristretto::vartime_multi_mul()` panics otherwise, which is caused by an imprecise
+/// `Iterator::size_hint()` value.
+#[derive(Debug, Clone)]
+struct OddItems<I> {
+    iter: I,
+    ended: bool,
+}
+
+impl<I: Iterator> OddItems<I> {
+    fn new(iter: I) -> Self {
+        Self { iter, ended: false }
+    }
+}
+
+impl<I: Iterator> Iterator for OddItems<I> {
+    type Item = I::Item;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.ended {
+            return None;
+        }
+        self.ended = self.iter.next().is_none();
+        if self.ended {
+            return None;
+        }
+
+        let item = self.iter.next();
+        self.ended = item.is_none();
+        item
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let (min, max) = self.iter.size_hint();
+        (min / 2, max.map(|max| max / 2))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{group::Ristretto, Keypair};
+
+    use rand::thread_rng;
+
+    #[test]
+    fn sum_of_squares_proof_basics() {
+        let mut rng = thread_rng();
+        let (receiver, _) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+        let ciphertext = CiphertextWithValue::new(3_u64, &receiver, &mut rng).generalize();
+        let sq_ciphertext = CiphertextWithValue::new(9_u64, &receiver, &mut rng).generalize();
+
+        let proof = SumOfSquaresProof::new(
+            [&ciphertext].into_iter(),
+            &sq_ciphertext,
+            &receiver,
+            &mut Transcript::new(b"test"),
+            &mut rng,
+        );
+
+        let ciphertext = ciphertext.into();
+        let sq_ciphertext = sq_ciphertext.into();
+        proof
+            .verify(
+                [&ciphertext].into_iter(),
+                &sq_ciphertext,
+                &receiver,
+                &mut Transcript::new(b"test"),
+            )
+            .unwrap();
+
+        let other_ciphertext = receiver.encrypt(8_u64, &mut rng);
+        let err = proof
+            .verify(
+                [&ciphertext].into_iter(),
+                &other_ciphertext,
+                &receiver,
+                &mut Transcript::new(b"test"),
+            )
+            .unwrap_err();
+        assert!(matches!(err, VerificationError::ChallengeMismatch));
+
+        let err = proof
+            .verify(
+                [&other_ciphertext].into_iter(),
+                &sq_ciphertext,
+                &receiver,
+                &mut Transcript::new(b"test"),
+            )
+            .unwrap_err();
+        assert!(matches!(err, VerificationError::ChallengeMismatch));
+
+        let err = proof
+            .verify(
+                [&ciphertext].into_iter(),
+                &sq_ciphertext,
+                &receiver,
+                &mut Transcript::new(b"other_transcript"),
+            )
+            .unwrap_err();
+        assert!(matches!(err, VerificationError::ChallengeMismatch));
+    }
+
+    #[test]
+    fn sum_of_squares_proof_with_bogus_inputs() {
+        let mut rng = thread_rng();
+        let (receiver, _) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+        let ciphertext = CiphertextWithValue::new(3_u64, &receiver, &mut rng).generalize();
+        let sq_ciphertext = CiphertextWithValue::new(10_u64, &receiver, &mut rng).generalize();
+
+        let proof = SumOfSquaresProof::new(
+            [&ciphertext].into_iter(),
+            &sq_ciphertext,
+            &receiver,
+            &mut Transcript::new(b"test"),
+            &mut rng,
+        );
+
+        let ciphertext = ciphertext.into();
+        let sq_ciphertext = sq_ciphertext.into();
+        let err = proof
+            .verify(
+                [&ciphertext].into_iter(),
+                &sq_ciphertext,
+                &receiver,
+                &mut Transcript::new(b"test"),
+            )
+            .unwrap_err();
+        assert!(matches!(err, VerificationError::ChallengeMismatch));
+    }
+
+    #[test]
+    fn sum_of_squares_proof_with_several_squares() {
+        let mut rng = thread_rng();
+        let (receiver, _) = Keypair::<Ristretto>::generate(&mut rng).into_tuple();
+        let ciphertexts =
+            [3_u64, 1, 4, 1].map(|x| CiphertextWithValue::new(x, &receiver, &mut rng).generalize());
+        let sq_ciphertext = CiphertextWithValue::new(27_u64, &receiver, &mut rng).generalize();
+
+        let proof = SumOfSquaresProof::new(
+            ciphertexts.iter(),
+            &sq_ciphertext,
+            &receiver,
+            &mut Transcript::new(b"test"),
+            &mut rng,
+        );
+
+        let sq_ciphertext = sq_ciphertext.into();
+        proof
+            .verify(
+                ciphertexts.iter().map(CiphertextWithValue::inner),
+                &sq_ciphertext,
+                &receiver,
+                &mut Transcript::new(b"test"),
+            )
+            .unwrap();
+
+        // The proof will not verify if ciphertexts are rearranged.
+        let err = proof
+            .verify(
+                ciphertexts.iter().rev().map(CiphertextWithValue::inner),
+                &sq_ciphertext,
+                &receiver,
+                &mut Transcript::new(b"test"),
+            )
+            .unwrap_err();
+        assert!(matches!(err, VerificationError::ChallengeMismatch));
+
+        let err = proof
+            .verify(
+                ciphertexts.iter().take(2).map(CiphertextWithValue::inner),
+                &sq_ciphertext,
+                &receiver,
+                &mut Transcript::new(b"test"),
+            )
+            .unwrap_err();
+        assert!(matches!(err, VerificationError::LenMismatch { .. }));
+    }
+
+    #[test]
+    fn odd_items() {
+        let odd_items = OddItems::new(iter::once(1).chain([2, 3, 4]));
+        assert_eq!(odd_items.size_hint(), (2, Some(2)));
+        assert_eq!(odd_items.collect::<Vec<_>>(), [2, 4]);
+
+        let other_items = OddItems::new(0..7);
+        assert_eq!(other_items.size_hint(), (3, Some(3)));
+        assert_eq!(other_items.collect::<Vec<_>>(), [1, 3, 5]);
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/proofs/possession.rs.html b/src/elastic_elgamal/proofs/possession.rs.html new file mode 100644 index 0000000..884db2a --- /dev/null +++ b/src/elastic_elgamal/proofs/possession.rs.html @@ -0,0 +1,379 @@ +possession.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+
//! [`ProofOfPossession`] and related logic.
+
+use merlin::Transcript;
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+#[cfg(feature = "serde")]
+use crate::serde::{ScalarHelper, VecHelper};
+use crate::{
+    alloc::Vec,
+    group::Group,
+    proofs::{TranscriptForGroup, VerificationError},
+    Keypair, PublicKey, SecretKey,
+};
+
+/// Zero-knowledge proof of possession of one or more secret scalars.
+///
+/// # Construction
+///
+/// The proof is a generalization of the standard Schnorr protocol for proving knowledge
+/// of a discrete log. The difference with the combination of several concurrent Schnorr
+/// protocol instances is that the challenge is shared among all instances (which yields a
+/// ~2x proof size reduction).
+///
+/// # Implementation notes
+///
+/// - Proof generation is constant-time. Verification is **not** constant-time.
+///
+/// # Examples
+///
+/// ```
+/// # use elastic_elgamal::{group::Ristretto, Keypair, ProofOfPossession};
+/// # use merlin::Transcript;
+/// # use rand::thread_rng;
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// let mut rng = thread_rng();
+/// let keypairs: Vec<_> =
+///     (0..5).map(|_| Keypair::<Ristretto>::generate(&mut rng)).collect();
+///
+/// // Prove possession of the generated key pairs.
+/// let proof = ProofOfPossession::new(
+///     &keypairs,
+///     &mut Transcript::new(b"custom_proof"),
+///     &mut rng,
+/// );
+/// proof.verify(
+///     keypairs.iter().map(Keypair::public),
+///     &mut Transcript::new(b"custom_proof"),
+/// )?;
+///
+/// // If we change the context of the `Transcript`, the proof will not verify.
+/// assert!(proof
+///     .verify(
+///         keypairs.iter().map(Keypair::public),
+///         &mut Transcript::new(b"other_proof"),
+///     )
+///     .is_err());
+/// // Likewise if the public keys are reordered.
+/// assert!(proof
+///     .verify(
+///         keypairs.iter().rev().map(Keypair::public),
+///         &mut Transcript::new(b"custom_proof"),
+///     )
+///     .is_err());
+/// # Ok(())
+/// # }
+/// ```
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct ProofOfPossession<G: Group> {
+    #[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
+    challenge: G::Scalar,
+    #[cfg_attr(feature = "serde", serde(with = "VecHelper::<ScalarHelper<G>, 1>"))]
+    responses: Vec<G::Scalar>,
+}
+
+impl<G: Group> ProofOfPossession<G> {
+    /// Creates a proof of possession with the specified `keypairs`.
+    pub fn new<R: CryptoRng + RngCore>(
+        keypairs: &[Keypair<G>],
+        transcript: &mut Transcript,
+        rng: &mut R,
+    ) -> Self {
+        Self::from_keys(
+            keypairs.iter().map(Keypair::secret),
+            keypairs.iter().map(Keypair::public),
+            transcript,
+            rng,
+        )
+    }
+
+    pub(crate) fn from_keys<'a, R: CryptoRng + RngCore>(
+        secrets: impl Iterator<Item = &'a SecretKey<G>>,
+        public_keys: impl Iterator<Item = &'a PublicKey<G>>,
+        transcript: &mut Transcript,
+        rng: &mut R,
+    ) -> Self {
+        transcript.start_proof(b"multi_pop");
+        let mut key_count = 0;
+        for public_key in public_keys {
+            transcript.append_element_bytes(b"K", public_key.as_bytes());
+            key_count += 1;
+        }
+
+        let random_scalars: Vec<_> = (0..key_count)
+            .map(|_| {
+                let randomness = SecretKey::<G>::generate(rng);
+                let random_element = G::mul_generator(randomness.expose_scalar());
+                transcript.append_element::<G>(b"R", &random_element);
+                randomness
+            })
+            .collect();
+
+        let challenge = transcript.challenge_scalar::<G>(b"c");
+        let responses = secrets
+            .zip(random_scalars)
+            .map(|(log, mut randomness)| {
+                randomness += log * &challenge;
+                *randomness.expose_scalar()
+            })
+            .collect();
+
+        Self {
+            challenge,
+            responses,
+        }
+    }
+
+    /// Verifies this proof against the provided `public_keys`.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if this proof does not verify.
+    pub fn verify<'a>(
+        &self,
+        public_keys: impl Iterator<Item = &'a PublicKey<G>> + Clone,
+        transcript: &mut Transcript,
+    ) -> Result<(), VerificationError> {
+        let mut key_count = 0;
+        transcript.start_proof(b"multi_pop");
+        for public_key in public_keys.clone() {
+            transcript.append_element_bytes(b"K", public_key.as_bytes());
+            key_count += 1;
+        }
+        VerificationError::check_lengths("public keys", self.responses.len(), key_count)?;
+
+        for (public_key, response) in public_keys.zip(&self.responses) {
+            let random_element = G::vartime_double_mul_generator(
+                &-self.challenge,
+                public_key.as_element(),
+                response,
+            );
+            transcript.append_element::<G>(b"R", &random_element);
+        }
+
+        let expected_challenge = transcript.challenge_scalar::<G>(b"c");
+        if expected_challenge == self.challenge {
+            Ok(())
+        } else {
+            Err(VerificationError::ChallengeMismatch)
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::thread_rng;
+
+    use super::*;
+    use crate::group::Ristretto;
+
+    type Keypair = crate::Keypair<Ristretto>;
+
+    #[test]
+    fn proof_of_possession_basics() {
+        let mut rng = thread_rng();
+        let poly: Vec<_> = (0..5).map(|_| Keypair::generate(&mut rng)).collect();
+
+        ProofOfPossession::new(&poly, &mut Transcript::new(b"test_multi_PoP"), &mut rng)
+            .verify(
+                poly.iter().map(Keypair::public),
+                &mut Transcript::new(b"test_multi_PoP"),
+            )
+            .unwrap();
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/proofs/range.rs.html b/src/elastic_elgamal/proofs/range.rs.html new file mode 100644 index 0000000..1ef9ffc --- /dev/null +++ b/src/elastic_elgamal/proofs/range.rs.html @@ -0,0 +1,1609 @@ +range.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+
//! Range proofs for ElGamal ciphertexts.
+
+use merlin::Transcript;
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+use subtle::{ConditionallySelectable, ConstantTimeGreater};
+use zeroize::Zeroizing;
+
+use core::{convert::TryFrom, fmt};
+
+use crate::{
+    alloc::{vec, HashMap, ToString, Vec},
+    encryption::{CiphertextWithValue, ExtendedCiphertext},
+    group::Group,
+    proofs::{RingProof, RingProofBuilder, TranscriptForGroup},
+    Ciphertext, PublicKey, VerificationError,
+};
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+struct RingSpec {
+    size: u64,
+    step: u64,
+}
+
+/// Decomposition of an integer range `0..n` into one or more sub-ranges. Decomposing the range
+/// allows constructing [`RangeProof`]s with size / computational complexity `O(log n)`.
+///
+/// # Construction
+///
+/// To build efficient `RangeProof`s, we need to be able to decompose any value `x` in `0..n`
+/// into several components, with each of them being in a smaller predefined range; once we
+/// have such a decomposition, we can build a [`RingProof`] around it.
+/// To build a decomposition, we use the following generic construction:
+///
+/// ```text
+/// 0..n = 0..t_0 + k_0 * (0..t_1 + k_1 * (0..t_2 + …)),
+/// ```
+///
+/// where `t_i` and `k_i` are integers greater than 1. If `x` is a value in `0..n`,
+/// it is decomposed as
+///
+/// ```text
+/// x = x_0 + k_0 * x_1 + k_0 * k_1 * x_2 + …; x_i in 0..t_i.
+/// ```
+///
+/// For a decomposition to be valid (i.e., to represent any value in `0..n` and no other values),
+/// the following statements are sufficient:
+///
+/// - `t_i >= k_i` (no gaps in values)
+/// - `n = t_0 + k_0 * (t_1 - 1 + k_1 * …)` (exact upper bound).
+///
+/// The size of a `RingProof` is the sum of upper range bounds `t_i` (= number of responses) + 1
+/// (the common challenge). Additionally, we need a ciphertext per each sub-range `0..t_i`
+/// (i.e., for each ring in `RingProof`). In practice, proof size is logarithmic:
+///
+/// | Upper bound `n`| Optimal decomposition | Proof size |
+/// |---------------:|-----------------------|-----------:|
+/// | 5              | `0..5`                | 6 scalars  |
+/// | 10             | `0..5 * 2 + 0..2`     | 8 scalars, 2 elements |
+/// | 20             | `0..5 * 4 + 0..4`     | 10 scalars, 2 elements |
+/// | 50             | `(0..5 * 5 + 0..5) * 2 + 0..2` | 13 scalars, 4 elements |
+/// | 64             | `(0..4 * 4 + 0..4) * 4 + 0..4` | 13 scalars, 4 elements |
+/// | 100            | `(0..5 * 5 + 0..5) * 4 + 0..4` | 15 scalars, 4 elements |
+/// | 256            | `((0..4 * 4 + 0..4) * 4 + 0..4) * 4 + 0..4` | 17 scalars, 6 elements |
+/// | 1000           | `((0..8 * 5 + 0..5) * 5 + 0..5) * 5 + 0..5` | 24 scalars, 6 elements |
+///
+/// (We do not count one of sub-range ciphertexts since it can be restored from the other
+/// sub-range ciphertexts and the original ciphertext of the value.)
+///
+/// ## Notes
+///
+/// - Decomposition of some values may be non-unique, but this is fine for our purposes.
+/// - Encoding of a value in a certain base is a partial case, with all `t_i` and `k_i` equal
+///   to the base. It only works for `n` being a power of the base.
+/// - Other types of decompositions may perform better, but this one has a couple
+///   of nice properties. It works for all `n`s, and the optimal decomposition can be found
+///   recursively.
+/// - If we know how to create / verify range proofs for `0..N`, proofs for all ranges `0..n`,
+///   `n < N` can be constructed as a combination of 2 proofs: a proof that encrypted value `x`
+///   is in `0..N` and that `n - 1 - x` is in `0..N`. (The latter is proved for a ciphertext
+///   obtained by the matching linear transform of the original ciphertext of `x`.)
+///   This does not help us if proofs for `0..N` are constructed using [`RingProof`]s,
+///   but allows estimating for which `n` a [Bulletproofs]-like construction would become
+///   more efficient despite using 2 proofs. If we take `N = 2^(2^P)`
+///   and the "vanilla" Bulletproof length `2 * P + 9`, this threshold is around `n = 2000`.
+///
+/// [Bulletproofs]: https://crypto.stanford.edu/bulletproofs/
+///
+/// # Examples
+///
+/// Finding out the optimal decomposition for a certain range:
+///
+/// ```
+/// # use elastic_elgamal::RangeDecomposition;
+/// let range = RangeDecomposition::optimal(42);
+/// assert_eq!(range.to_string(), "6 * 0..7 + 0..6");
+/// assert_eq!(range.proof_size(), 16); // 14 scalars, 2 elements
+///
+/// let range = RangeDecomposition::optimal(100);
+/// assert_eq!(range.to_string(), "20 * 0..5 + 4 * 0..5 + 0..4");
+/// assert_eq!(range.proof_size(), 19); // 15 scalars, 4 elements
+/// ```
+///
+/// See [`RangeProof`] docs for an end-to-end example of usage.
+#[derive(Debug, Clone, PartialEq)]
+pub struct RangeDecomposition {
+    rings: Vec<RingSpec>,
+}
+
+impl fmt::Display for RangeDecomposition {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        for (i, ring_spec) in self.rings.iter().enumerate() {
+            if ring_spec.step > 1 {
+                write!(formatter, "{} * ", ring_spec.step)?;
+            }
+            write!(formatter, "0..{}", ring_spec.size)?;
+
+            if i + 1 < self.rings.len() {
+                formatter.write_str(" + ")?;
+            }
+        }
+        Ok(())
+    }
+}
+
+/// `RangeDecomposition` together with optimized parameters.
+#[derive(Debug, Clone)]
+struct OptimalDecomposition {
+    decomposition: RangeDecomposition,
+    optimal_len: u64,
+}
+
+#[allow(
+    clippy::cast_possible_truncation,
+    clippy::cast_precision_loss,
+    clippy::cast_sign_loss
+)]
+impl RangeDecomposition {
+    /// Finds an optimal decomposition of the range with the given `upper_bound` in terms
+    /// of space of the range proof.
+    ///
+    /// Empirically, this method has sublinear complexity, but may work slowly for large values
+    /// of `upper_bound` (say, larger than 1 billion).
+    ///
+    /// # Panics
+    ///
+    /// Panics if `upper_bound` is less than 2.
+    pub fn optimal(upper_bound: u64) -> Self {
+        assert!(upper_bound >= 2, "`upper_bound` must be greater than 1");
+
+        let mut optimal_values = HashMap::new();
+        Self::optimize(upper_bound, &mut optimal_values).decomposition
+    }
+
+    fn just(capacity: u64) -> Self {
+        let spec = RingSpec {
+            size: capacity,
+            step: 1,
+        };
+        Self { rings: vec![spec] }
+    }
+
+    fn combine_mul(self, new_ring_size: u64, multiplier: u64) -> Self {
+        let mut combined_rings = self.rings;
+        for spec in &mut combined_rings {
+            spec.step *= multiplier;
+        }
+        combined_rings.push(RingSpec {
+            size: new_ring_size,
+            step: 1,
+        });
+
+        Self {
+            rings: combined_rings,
+        }
+    }
+
+    /// Returns the exclusive upper bound of the range presentable by this decomposition.
+    pub fn upper_bound(&self) -> u64 {
+        self.rings
+            .iter()
+            .map(|spec| (spec.size - 1) * spec.step)
+            .sum::<u64>()
+            + 1
+    }
+
+    /// Returns the total number of items in all rings.
+    fn rings_size(&self) -> u64 {
+        self.rings.iter().map(|spec| spec.size).sum::<u64>()
+    }
+
+    /// Returns the size of [`RangeProof`]s using this decomposition, measured as a total number
+    /// of scalars and group elements in the proof. Computational complexity of creating and
+    /// verifying proofs is also linear w.r.t. this number.
+    pub fn proof_size(&self) -> u64 {
+        self.rings_size() + 2 * self.rings.len() as u64 - 1
+    }
+
+    fn decompose(&self, value_indexes: &mut Vec<usize>, mut secret_value: u64) {
+        for ring_spec in &self.rings {
+            let mut value_index = secret_value / ring_spec.step;
+            let ring_max_value = ring_spec.size - 1;
+            let overflow = value_index.ct_gt(&ring_max_value);
+            value_index.conditional_assign(&ring_max_value, overflow);
+            value_indexes.push(value_index as usize);
+            secret_value -= value_index * ring_spec.step;
+        }
+
+        debug_assert_eq!(secret_value, 0, "unused secret value for {self:?}");
+    }
+
+    /// We decompose our range `0..n` as `0..t + k * 0..T`, where `t >= 2`, `T >= 2`,
+    /// `k >= 2`. For all values in the range to be presentable, we need `t >= k` (otherwise,
+    /// there will be gaps) and
+    ///
+    /// ```text
+    /// n - 1 = t - 1 + k * (T - 1) <=> n = t + k * (T - 1)
+    /// ```
+    ///
+    /// (to accurately represent the upper bound). For valid decompositions, we apply the
+    /// same decomposition recursively to `0..T`. If `P(n)` is the optimal proof length for
+    /// range `0..n`, we thus obtain
+    ///
+    /// ```text
+    /// P(n) = min_(t, k) { t + 2 + P((n - t) / k + 1) }.
+    /// ```
+    ///
+    /// Here, `t` is the number of commitments (= number of scalars for ring `0..t`), plus
+    /// 2 group elements in a partial ElGamal ciphertext corresponding to the ring.
+    ///
+    /// We additionally trim the solution space using a lower-bound estimate
+    ///
+    /// ```text
+    /// P(n) >= 3 * log2(n),
+    /// ```
+    ///
+    /// which can be proven recursively.
+    fn optimize(
+        upper_bound: u64,
+        optimal_values: &mut HashMap<u64, OptimalDecomposition>,
+    ) -> OptimalDecomposition {
+        if let Some(opt) = optimal_values.get(&upper_bound) {
+            return opt.clone();
+        }
+
+        let mut opt = OptimalDecomposition {
+            optimal_len: upper_bound + 2,
+            decomposition: RangeDecomposition::just(upper_bound),
+        };
+
+        for first_ring_size in 2_u64.. {
+            if first_ring_size + 2 > opt.optimal_len {
+                // Any further estimate will be worse than the current optimum.
+                break;
+            }
+
+            let remaining_capacity = upper_bound - first_ring_size;
+            for multiplier in 2_u64..=first_ring_size {
+                if remaining_capacity % multiplier != 0 {
+                    continue;
+                }
+                let inner_upper_bound = remaining_capacity / multiplier + 1;
+                if inner_upper_bound < 2 {
+                    // Since `inner_upper_bound` decreases w.r.t. `multiplier`, we can
+                    // break here.
+                    break;
+                }
+
+                let best_estimate =
+                    first_ring_size + 2 + Self::lower_len_estimate(inner_upper_bound);
+                if best_estimate > opt.optimal_len {
+                    continue;
+                }
+
+                let inner_opt = Self::optimize(inner_upper_bound, optimal_values);
+                let candidate_len = first_ring_size + 2 + inner_opt.optimal_len;
+                let candidate_rings = 1 + inner_opt.decomposition.rings.len();
+
+                if candidate_len < opt.optimal_len
+                    || (candidate_len == opt.optimal_len
+                        && candidate_rings < opt.decomposition.rings.len())
+                {
+                    opt.optimal_len = candidate_len;
+                    opt.decomposition = inner_opt
+                        .decomposition
+                        .combine_mul(first_ring_size, multiplier);
+                }
+            }
+        }
+
+        debug_assert!(
+            opt.optimal_len >= Self::lower_len_estimate(upper_bound),
+            "Lower len estimate {est} is invalid for {bound}: {opt:?}",
+            est = Self::lower_len_estimate(upper_bound),
+            bound = upper_bound,
+            opt = opt
+        );
+        optimal_values.insert(upper_bound, opt.clone());
+        opt
+    }
+
+    #[cfg(feature = "std")]
+    fn lower_len_estimate(upper_bound: u64) -> u64 {
+        ((upper_bound as f64).log2() * 3.0).ceil() as u64
+    }
+
+    #[cfg(not(feature = "std"))]
+    fn lower_len_estimate(upper_bound: u64) -> u64 {
+        Self::int_lower_len_estimate(upper_bound)
+    }
+
+    // We may not have floating-point arithmetics on no-std targets; thus, we use
+    // a less precise estimate.
+    #[cfg(any(test, not(feature = "std")))]
+    #[inline]
+    fn int_lower_len_estimate(upper_bound: u64) -> u64 {
+        let log2_upper_bound = if upper_bound == 0 {
+            0
+        } else {
+            63 - u64::from(upper_bound.leading_zeros()) // rounded down
+        };
+        log2_upper_bound * 3
+    }
+}
+
+/// [`RangeDecomposition`] together with values precached for creating and/or verifying
+/// [`RangeProof`]s in a certain [`Group`].
+#[derive(Debug, Clone)]
+pub struct PreparedRange<G: Group> {
+    inner: RangeDecomposition,
+    admissible_values: Vec<Vec<G::Element>>,
+}
+
+impl<G: Group> From<RangeDecomposition> for PreparedRange<G> {
+    fn from(decomposition: RangeDecomposition) -> Self {
+        Self::new(decomposition)
+    }
+}
+
+impl<G: Group> PreparedRange<G> {
+    fn new(inner: RangeDecomposition) -> Self {
+        let admissible_values = Vec::with_capacity(inner.rings.len());
+        let admissible_values = inner.rings.iter().fold(admissible_values, |mut acc, spec| {
+            let ring_values: Vec<_> = (0..spec.size)
+                .map(|i| G::vartime_mul_generator(&(i * spec.step).into()))
+                .collect();
+            acc.push(ring_values);
+            acc
+        });
+
+        Self {
+            inner,
+            admissible_values,
+        }
+    }
+
+    /// Returns a reference to the contained decomposition.
+    pub fn decomposition(&self) -> &RangeDecomposition {
+        &self.inner
+    }
+
+    /// Decomposes the provided `secret_value` into value indexes in constituent rings.
+    fn decompose(&self, secret_value: u64) -> Zeroizing<Vec<usize>> {
+        assert!(
+            secret_value < self.inner.upper_bound(),
+            "Secret value must be in range 0..{}",
+            self.inner.upper_bound()
+        );
+        // We immediately allocate the necessary capacity for `decomposition`.
+        let mut decomposition = Zeroizing::new(Vec::with_capacity(self.admissible_values.len()));
+        self.inner.decompose(&mut decomposition, secret_value);
+        decomposition
+    }
+}
+
+/// Zero-knowledge proof that an ElGamal ciphertext encrypts a value into a certain range `0..n`.
+///
+/// # Construction
+///
+/// To make the proof more compact – `O(log n)` in terms of size and proving / verification
+/// complexity – we use the same trick as for [Pedersen commitments] (used, e.g., for confidential
+/// transaction amounts in [Elements]):
+///
+/// 1. Represent the encrypted value `x` as `x = x_0 + k_0 * x_1 + k_0 * k_1 * x_2 + …`,
+///    where `0 <= x_i < t_i` is the decomposition of `x` as per the [`RangeDecomposition`],
+///    `0..t_0 + k_0 * (0..t_1 + …)`.
+///    As an example, if `n` is a power of 2, one can choose a decomposition as
+///    the base-2 presentation of `x`, i.e., `t_i = k_i = 2` for all `i`.
+///    For brevity, denote a multiplier of `x_i` in `x` decomposition as `K_i`,
+///    `K_i = k_0 * … * k_{i-1}`; `K_0 = 1` by extension.
+/// 2. Split the ciphertext: `E = E_0 + E_1 + …`, where `E_i` encrypts `K_i * x_i`.
+/// 3. Produce a [`RingProof`] that for all `i` the encrypted scalar for `E_i`
+///    is among 0, `K_i`, …, `K_i * (t_i - 1)`. The range proof consists of all `E_i` ciphertexts
+///    and this `RingProof`.
+///
+/// As with range proofs for Pedersen commitments, this construction is not optimal
+/// in terms of space or proving / verification complexity for large ranges;
+/// it is linear w.r.t. the bit length of the range.
+/// (Constructions like [Bulletproofs] are *logarithmic* w.r.t. the bit length.)
+/// Still, it can be useful for small ranges.
+///
+/// [Pedersen commitments]: https://en.wikipedia.org/wiki/Commitment_scheme
+/// [Elements]: https://elementsproject.org/features/confidential-transactions/investigation
+/// [Bulletproofs]: https://crypto.stanford.edu/bulletproofs/
+///
+/// # Examples
+///
+/// ```
+/// # use elastic_elgamal::{
+/// #     group::Ristretto, DiscreteLogTable, Keypair, RangeDecomposition, RangeProof, Ciphertext,
+/// # };
+/// # use merlin::Transcript;
+/// # use rand::thread_rng;
+/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+/// // Generate the ciphertext receiver.
+/// let mut rng = thread_rng();
+/// let receiver = Keypair::<Ristretto>::generate(&mut rng);
+/// // Find the optimal range decomposition for our range
+/// // and specialize it for the Ristretto group.
+/// let range = RangeDecomposition::optimal(100).into();
+///
+/// let (ciphertext, proof) = RangeProof::new(
+///     receiver.public(),
+///     &range,
+///     55,
+///     &mut Transcript::new(b"test_proof"),
+///     &mut rng,
+/// );
+/// let ciphertext = Ciphertext::from(ciphertext);
+///
+/// // Check that the ciphertext is valid
+/// let lookup = DiscreteLogTable::new(0..100);
+/// assert_eq!(receiver.secret().decrypt(ciphertext, &lookup), Some(55));
+/// // ...and that the proof verifies.
+/// proof.verify(
+///     receiver.public(),
+///     &range,
+///     ciphertext,
+///     &mut Transcript::new(b"test_proof"),
+/// )?;
+/// # Ok(())
+/// # }
+/// ```
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct RangeProof<G: Group> {
+    partial_ciphertexts: Vec<Ciphertext<G>>,
+    #[cfg_attr(feature = "serde", serde(flatten))]
+    inner: RingProof<G>,
+}
+
+impl<G: Group> RangeProof<G> {
+    /// Encrypts `value` for `receiver` and creates a zero-knowledge proof that the encrypted value
+    /// is in `range`.
+    ///
+    /// This is a lower-level operation; see [`PublicKey::encrypt_range()`] for a higher-level
+    /// alternative.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `value` is outside the range specified by `range`.
+    pub fn new<R: RngCore + CryptoRng>(
+        receiver: &PublicKey<G>,
+        range: &PreparedRange<G>,
+        value: u64,
+        transcript: &mut Transcript,
+        rng: &mut R,
+    ) -> (CiphertextWithValue<G, u64>, Self) {
+        let ciphertext = CiphertextWithValue::new(value, receiver, rng);
+        let proof = Self::from_ciphertext(receiver, range, &ciphertext, transcript, rng);
+        (ciphertext, proof)
+    }
+
+    /// Creates a proof that a value in `ciphertext` is in the `range`.
+    ///
+    /// The caller is responsible for providing a `ciphertext` encrypted for the `receiver`;
+    /// if the ciphertext is encrypted for another public key, the resulting proof will not verify.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `value` is outside the range specified by `range`.
+    pub fn from_ciphertext<R: RngCore + CryptoRng>(
+        receiver: &PublicKey<G>,
+        range: &PreparedRange<G>,
+        ciphertext: &CiphertextWithValue<G, u64>,
+        transcript: &mut Transcript,
+        rng: &mut R,
+    ) -> Self {
+        let value_indexes = range.decompose(*ciphertext.value());
+        debug_assert_eq!(value_indexes.len(), range.admissible_values.len());
+        transcript.start_proof(b"encryption_range_proof");
+        transcript.append_message(b"range", range.inner.to_string().as_bytes());
+
+        let ring_responses_size = usize::try_from(range.inner.rings_size())
+            .expect("Integer overflow when allocating ring responses");
+        let mut ring_responses = vec![G::Scalar::default(); ring_responses_size];
+
+        let mut proof_builder = RingProofBuilder::new(
+            receiver,
+            range.admissible_values.len(),
+            &mut ring_responses,
+            transcript,
+            rng,
+        );
+
+        let mut cumulative_ciphertext = ExtendedCiphertext::zero();
+        let mut it = value_indexes.iter().zip(&range.admissible_values);
+
+        let partial_ciphertexts = it
+            .by_ref()
+            .take(value_indexes.len() - 1)
+            .map(|(value_index, admissible_values)| {
+                let ciphertext = proof_builder.add_value(admissible_values, *value_index);
+                let inner = ciphertext.inner;
+                cumulative_ciphertext += ciphertext;
+                inner
+            })
+            .collect();
+
+        let last_partial_ciphertext =
+            ciphertext.extended_ciphertext().clone() - cumulative_ciphertext;
+        let (&value_index, admissible_values) = it.next().unwrap();
+        // ^ `unwrap()` is safe by construction
+        proof_builder.add_precomputed_value(
+            last_partial_ciphertext,
+            admissible_values,
+            value_index,
+        );
+
+        Self {
+            partial_ciphertexts,
+            inner: RingProof::new(proof_builder.build(), ring_responses),
+        }
+    }
+
+    /// Verifies this proof against `ciphertext` for `receiver` and the specified `range`.
+    ///
+    /// This is a lower-level operation; see [`PublicKey::verify_range()`] for a higher-level
+    /// alternative.
+    ///
+    /// For a proof to verify, all parameters must be identical to ones provided when creating
+    /// the proof. In particular, `range` must have the same decomposition.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if this proof does not verify.
+    pub fn verify(
+        &self,
+        receiver: &PublicKey<G>,
+        range: &PreparedRange<G>,
+        ciphertext: Ciphertext<G>,
+        transcript: &mut Transcript,
+    ) -> Result<(), VerificationError> {
+        // Check decomposition / proof consistency.
+        VerificationError::check_lengths(
+            "admissible values",
+            self.partial_ciphertexts.len() + 1,
+            range.admissible_values.len(),
+        )?;
+
+        transcript.start_proof(b"encryption_range_proof");
+        transcript.append_message(b"range", range.inner.to_string().as_bytes());
+
+        let ciphertext_sum = self
+            .partial_ciphertexts
+            .iter()
+            .fold(Ciphertext::zero(), |acc, ciphertext| acc + *ciphertext);
+        let ciphertexts = self
+            .partial_ciphertexts
+            .iter()
+            .copied()
+            .chain(Some(ciphertext - ciphertext_sum));
+
+        let admissible_values = range.admissible_values.iter().map(Vec::as_slice);
+        self.inner
+            .verify(receiver, admissible_values, ciphertexts, transcript)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::{thread_rng, Rng};
+    use test_casing::test_casing;
+
+    use super::*;
+    use crate::{
+        group::{ElementOps, Ristretto},
+        Keypair,
+    };
+
+    #[test]
+    fn optimal_value_small() {
+        let value = RangeDecomposition::optimal(5);
+        assert_eq!(value.rings.as_ref(), [RingSpec { size: 5, step: 1 }]);
+
+        let value = RangeDecomposition::optimal(16);
+        assert_eq!(
+            value.rings.as_ref(),
+            [RingSpec { size: 4, step: 4 }, RingSpec { size: 4, step: 1 }]
+        );
+
+        let value = RangeDecomposition::optimal(60);
+        assert_eq!(
+            value.rings.as_ref(),
+            [
+                RingSpec { size: 5, step: 12 },
+                RingSpec { size: 4, step: 3 },
+                RingSpec { size: 3, step: 1 },
+            ]
+        );
+
+        let value = RangeDecomposition::optimal(1_000);
+        assert_eq!(
+            value.to_string(),
+            "125 * 0..8 + 25 * 0..5 + 5 * 0..5 + 0..5"
+        );
+    }
+
+    #[test]
+    fn optimal_values_with_additives() {
+        let value = RangeDecomposition::optimal(17);
+        assert_eq!(
+            value.rings.as_ref(),
+            [RingSpec { size: 4, step: 4 }, RingSpec { size: 5, step: 1 }]
+        );
+
+        let value = RangeDecomposition::optimal(101);
+        assert_eq!(
+            value.rings.as_ref(),
+            [
+                RingSpec { size: 5, step: 20 },
+                RingSpec { size: 5, step: 4 },
+                RingSpec { size: 5, step: 1 }
+            ]
+        );
+    }
+
+    #[test]
+    fn large_optimal_values() {
+        let value = RangeDecomposition::optimal(12_345);
+        assert_eq!(
+            value.to_string(),
+            "2880 * 0..4 + 720 * 0..5 + 90 * 0..9 + 15 * 0..7 + 3 * 0..5 + 0..3"
+        );
+        assert_eq!(value.upper_bound(), 12_345);
+
+        let value = RangeDecomposition::optimal(777_777);
+        assert_eq!(
+            value.to_string(),
+            "125440 * 0..6 + 25088 * 0..6 + 3136 * 0..8 + 784 * 0..4 + 196 * 0..4 + \
+             49 * 0..5 + 7 * 0..7 + 0..7"
+        );
+        assert_eq!(value.upper_bound(), 777_777);
+
+        let value = RangeDecomposition::optimal(12_345_678);
+        assert_eq!(
+            value.to_string(),
+            "3072000 * 0..4 + 768000 * 0..4 + 192000 * 0..4 + 48000 * 0..5 + 9600 * 0..6 + \
+             1200 * 0..8 + 300 * 0..4 + 75 * 0..5 + 15 * 0..5 + 3 * 0..6 + 0..3"
+        );
+        assert_eq!(value.upper_bound(), 12_345_678);
+    }
+
+    #[test_casing(4, [1_000, 9_999, 12_345, 54_321])]
+    fn decomposing_for_larger_range(upper_bound: u64) {
+        let decomposition = RangeDecomposition::optimal(upper_bound);
+        let mut rng = thread_rng();
+
+        let values = (0..1_000)
+            .map(|_| rng.gen_range(0..upper_bound))
+            .chain(0..5)
+            .chain((upper_bound - 5)..upper_bound);
+
+        for secret_value in values {
+            let mut value_indexes = vec![];
+            decomposition.decompose(&mut value_indexes, secret_value);
+
+            let restored = value_indexes
+                .iter()
+                .zip(&decomposition.rings)
+                .fold(0, |acc, (&idx, spec)| acc + idx as u64 * spec.step);
+            assert_eq!(
+                restored, secret_value,
+                "Cannot restore secret value {secret_value}; decomposed as {value_indexes:?}"
+            );
+        }
+    }
+
+    #[test]
+    fn decomposing_for_small_range() {
+        let decomposition = RangeDecomposition::optimal(17);
+        assert_eq!(decomposition.to_string(), "4 * 0..4 + 0..5");
+        let mut value_indexes = vec![];
+        decomposition.decompose(&mut value_indexes, 16);
+        assert_eq!(value_indexes, [3, 4]);
+        // 3 * 4 + 4 = 16
+    }
+
+    #[test]
+    fn decomposing_for_range() {
+        let decomposition = RangeDecomposition::optimal(1_000);
+        let mut value_indexes = vec![];
+        decomposition.decompose(&mut value_indexes, 567);
+        assert_eq!(value_indexes, [4, 2, 3, 2]);
+        // 2 + 3 * 5 + 2 * 25 + 4 * 125 = 567
+    }
+
+    #[test_casing(4, [12, 15, 20, 50])]
+    fn range_proof_basics(upper_bound: u64) {
+        let decomposition = RangeDecomposition::optimal(upper_bound).into();
+
+        let mut rng = thread_rng();
+        let receiver = Keypair::<Ristretto>::generate(&mut rng);
+        let (ciphertext, proof) = RangeProof::new(
+            receiver.public(),
+            &decomposition,
+            10,
+            &mut Transcript::new(b"test"),
+            &mut rng,
+        );
+        let ciphertext = ciphertext.into();
+
+        proof
+            .verify(
+                receiver.public(),
+                &decomposition,
+                ciphertext,
+                &mut Transcript::new(b"test"),
+            )
+            .unwrap();
+
+        // Should not verify with another transcript context
+        assert!(proof
+            .verify(
+                receiver.public(),
+                &decomposition,
+                ciphertext,
+                &mut Transcript::new(b"other"),
+            )
+            .is_err());
+
+        // ...or with another receiver
+        let other_receiver = Keypair::<Ristretto>::generate(&mut rng);
+        assert!(proof
+            .verify(
+                other_receiver.public(),
+                &decomposition,
+                ciphertext,
+                &mut Transcript::new(b"test"),
+            )
+            .is_err());
+
+        // ...or with another ciphertext
+        let other_ciphertext = receiver.public().encrypt(10_u64, &mut rng);
+        assert!(proof
+            .verify(
+                receiver.public(),
+                &decomposition,
+                other_ciphertext,
+                &mut Transcript::new(b"test"),
+            )
+            .is_err());
+
+        let mut mangled_ciphertext = ciphertext;
+        mangled_ciphertext.blinded_element += Ristretto::generator();
+        assert!(proof
+            .verify(
+                receiver.public(),
+                &decomposition,
+                mangled_ciphertext,
+                &mut Transcript::new(b"test"),
+            )
+            .is_err());
+
+        // ...or with another decomposition
+        let other_decomposition = RangeDecomposition::just(15).into();
+        assert!(proof
+            .verify(
+                receiver.public(),
+                &other_decomposition,
+                ciphertext,
+                &mut Transcript::new(b"test"),
+            )
+            .is_err());
+    }
+
+    #[test]
+    #[cfg(feature = "std")]
+    fn int_lower_len_estimate_is_always_not_more_than_exact() {
+        let samples = (0..1_000).chain((1..1_000).map(|i| i * 1_000));
+        for sample in samples {
+            let floating_point_estimate = RangeDecomposition::lower_len_estimate(sample);
+            let int_estimate = RangeDecomposition::int_lower_len_estimate(sample);
+            assert!(
+                floating_point_estimate >= int_estimate,
+                "Unexpected estimates for {sample}: floating-point = {floating_point_estimate}, \
+                 int = {int_estimate}"
+            );
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/proofs/ring.rs.html b/src/elastic_elgamal/proofs/ring.rs.html new file mode 100644 index 0000000..9e0103a --- /dev/null +++ b/src/elastic_elgamal/proofs/ring.rs.html @@ -0,0 +1,1581 @@ +ring.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+
//! Ring proofs.
+
+use merlin::Transcript;
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+use core::{fmt, mem};
+
+#[cfg(feature = "serde")]
+use crate::serde::{ScalarHelper, VecHelper};
+use crate::{
+    alloc::{vec, Vec},
+    encryption::ExtendedCiphertext,
+    group::Group,
+    proofs::{TranscriptForGroup, VerificationError},
+    Ciphertext, PublicKey, SecretKey,
+};
+
+/// An incomplete ring proving that the encrypted value is in the a priori known set of
+/// admissible values.
+struct Ring<'a, G: Group> {
+    // Public parameters of the ring.
+    index: usize,
+    admissible_values: &'a [G::Element],
+    ciphertext: Ciphertext<G>,
+
+    // ZKP-related public values.
+    transcript: Transcript,
+    responses: &'a mut [G::Scalar],
+    terminal_commitments: (G::Element, G::Element),
+
+    // Secret values.
+    value_index: usize,
+    discrete_log: SecretKey<G>,
+    random_scalar: SecretKey<G>,
+}
+
+impl<G: Group> fmt::Debug for Ring<'_, G> {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        formatter
+            .debug_struct("Ring")
+            .field("index", &self.index)
+            .field("admissible_values", &self.admissible_values)
+            .field("ciphertext", &self.ciphertext)
+            .field("responses", &self.responses)
+            .field("terminal_commitments", &self.terminal_commitments)
+            .finish()
+    }
+}
+
+impl<'a, G: Group> Ring<'a, G> {
+    #[allow(clippy::too_many_arguments)] // fine for a private function
+    fn new<R: CryptoRng + RngCore>(
+        index: usize,
+        log_base: G::Element,
+        ciphertext: ExtendedCiphertext<G>,
+        admissible_values: &'a [G::Element],
+        value_index: usize,
+        transcript: &Transcript,
+        responses: &'a mut [G::Scalar],
+        rng: &mut R,
+    ) -> Self {
+        assert!(
+            !admissible_values.is_empty(),
+            "No admissible values supplied"
+        );
+        assert!(
+            value_index < admissible_values.len(),
+            "Specified value index is out of bounds"
+        );
+        debug_assert_eq!(
+            responses.len(),
+            admissible_values.len(),
+            "Number of responses doesn't match number of admissible values"
+        );
+
+        let random_element = ciphertext.inner.random_element;
+        let blinded_value = ciphertext.inner.blinded_element;
+        debug_assert!(
+            {
+                let expected_blinded_value = log_base * ciphertext.random_scalar.expose_scalar()
+                    + admissible_values[value_index];
+                expected_blinded_value == blinded_value
+            },
+            "Specified ciphertext does not match the specified `value_index`"
+        );
+
+        let mut transcript = transcript.clone();
+        transcript.start_proof(b"ring_enc");
+        transcript.append_message(b"enc", &ciphertext.inner.to_bytes());
+        // NB: we don't add `admissible_values` to the transcript since we assume that
+        // they are fixed in the higher-level protocol.
+        transcript.append_u64(b"i", index as u64);
+
+        // Choose a random scalar to use in the equation matching the known discrete log.
+        let random_scalar = SecretKey::<G>::generate(rng);
+        let mut commitments = (
+            G::mul_generator(random_scalar.expose_scalar()),
+            log_base * random_scalar.expose_scalar(),
+        );
+
+        let it = admissible_values.iter().enumerate().skip(value_index + 1);
+        for (eq_index, &admissible_value) in it {
+            let mut eq_transcript = transcript.clone();
+            eq_transcript.append_u64(b"j", eq_index as u64 - 1);
+            eq_transcript.append_element::<G>(b"R_G", &commitments.0);
+            eq_transcript.append_element::<G>(b"R_K", &commitments.1);
+            let challenge = eq_transcript.challenge_scalar::<G>(b"c");
+
+            let response = G::generate_scalar(rng);
+            responses[eq_index] = response;
+            let dh_element = blinded_value - admissible_value;
+            commitments = (
+                G::mul_generator(&response) - random_element * &challenge,
+                G::multi_mul([&response, &-challenge], [log_base, dh_element]),
+            );
+        }
+
+        Self {
+            index,
+            value_index,
+            admissible_values,
+            ciphertext: ciphertext.inner,
+            transcript,
+            responses,
+            terminal_commitments: commitments,
+            discrete_log: ciphertext.random_scalar,
+            random_scalar,
+        }
+    }
+
+    /// Completes the ring by calculating the common challenge and closing all rings using it.
+    ///
+    /// # Return value
+    ///
+    /// Returns the common challenge.
+    fn aggregate<R: CryptoRng + RngCore>(
+        rings: Vec<Self>,
+        log_base: G::Element,
+        transcript: &mut Transcript,
+        rng: &mut R,
+    ) -> G::Scalar {
+        debug_assert!(
+            rings.iter().enumerate().all(|(i, ring)| i == ring.index),
+            "Rings have bogus indexes"
+        );
+
+        for ring in &rings {
+            let commitments = &ring.terminal_commitments;
+            transcript.append_element::<G>(b"R_G", &commitments.0);
+            transcript.append_element::<G>(b"R_K", &commitments.1);
+        }
+
+        let common_challenge = transcript.challenge_scalar::<G>(b"c");
+        for ring in rings {
+            ring.finalize(log_base, common_challenge, rng);
+        }
+        common_challenge
+    }
+
+    fn finalize<R: CryptoRng + RngCore>(
+        self,
+        log_base: G::Element,
+        common_challenge: G::Scalar,
+        rng: &mut R,
+    ) {
+        // Compute remaining responses for non-reversible equations.
+        let mut challenge = common_challenge;
+        let it = self.admissible_values[..self.value_index]
+            .iter()
+            .enumerate();
+        for (eq_index, &admissible_value) in it {
+            let response = G::generate_scalar(rng);
+            self.responses[eq_index] = response;
+            let dh_element = self.ciphertext.blinded_element - admissible_value;
+            let commitments = (
+                G::mul_generator(&response) - self.ciphertext.random_element * &challenge,
+                G::multi_mul([&response, &-challenge], [log_base, dh_element]),
+            );
+
+            let mut eq_transcript = self.transcript.clone();
+            eq_transcript.append_u64(b"j", eq_index as u64);
+            eq_transcript.append_element::<G>(b"R_G", &commitments.0);
+            eq_transcript.append_element::<G>(b"R_K", &commitments.1);
+            challenge = eq_transcript.challenge_scalar::<G>(b"c");
+        }
+
+        // Finally, compute the response for equation #`value_index`, using our knowledge
+        // of the trapdoor.
+        debug_assert_eq!(self.responses[self.value_index], G::Scalar::from(0_u64));
+        self.responses[self.value_index] =
+            challenge * self.discrete_log.expose_scalar() + self.random_scalar.expose_scalar();
+    }
+}
+
+/// Zero-knowledge proof that the one or more encrypted values is each in the a priori known set of
+/// admissible values. (Admissible values may differ among encrypted values.)
+///
+/// # Construction
+///
+/// In short, a proof is constructed almost identically to [Borromean ring signatures] by
+/// Maxwell and Poelstra, with the only major difference being that we work on ElGamal ciphertexts
+/// instead of group elements (= public keys).
+///
+/// A proof consists of one or more *rings*. Each ring proves than a certain
+/// ElGamal ciphertext `E = (R, B)` for public key `K` in a group with generator `G`
+/// encrypts one of distinct admissible values `x_0`, `x_1`, ..., `x_n`.
+/// `K` and `G` are shared among rings, admissible values are generally not.
+/// Different rings may have different number of admissible values.
+///
+/// ## Single ring
+///
+/// A ring is a challenge `e_0` and a set of responses `s_0`, `s_1`, ..., `s_n`, which
+/// must satisfy the following verification procedure:
+///
+/// For each `j` in `0..=n`, compute
+///
+/// ```text
+/// R_G(j) = [s_j]G - [e_j]R;
+/// R_K(j) = [s_j]K - [e_j](B - [x_j]G);
+/// e_{j+1} = H(j, R_G(j), R_K(j));
+/// ```
+///
+/// Here, `H` is a cryptographic hash function. The ring is valid if `e_0 = e_{n+1}`.
+///
+/// This construction is almost identical to [Abe–Ohkubo–Suzuki ring signatures][ring],
+/// with the only difference that two group elements are hashed on each iteration instead of one.
+/// If admissible values consist of a single value, this protocol reduces to
+/// [`LogEqualityProof`] / Chaum–Pedersen protocol.
+///
+/// As with "ordinary" ring signatures, constructing a ring is only feasible when knowing
+/// additional *trapdoor information*. Namely, the prover must know
+///
+/// ```text
+/// r = dlog_G(R) = dlog_K(B - [x_j]G)
+/// ```
+///
+/// for a certain `j`. (This discrete log `r` is the random scalar used in ElGamal encryption.)
+/// With this info, the prover constructs the ring as follows:
+///
+/// 1. Select random scalar `x` and compute `R_G(j) = [x]G`, `R_K(j) = [x]K`.
+/// 2. Compute `e_{j+1}`, ... `e_n`, ..., `e_j` ("wrapping" around `e_0 = e_{n+1}`)
+///    as per verification formulas. `s_*` scalars are selected uniformly at random.
+/// 3. Compute `s_j` using the trapdoor information: `s_j = x + e_j * r`.
+///
+/// ## Multiple rings
+///
+/// Transformation to multiple rings is analogous to one in [Borromean ring signatures].
+/// Namely, challenge `e_0` is shared among all rings and is computed by hashing
+/// values of `R_G` and `R_K` with the maximum index for each of the rings.
+///
+/// # Applications
+///
+/// ## Voting protocols
+///
+/// [`EncryptedChoice`](crate::app::EncryptedChoice) uses `RingProof` to prove that all encrypted
+/// values are Boolean (0 or 1). Using a common challenge allows to reduce proof size by ~33%.
+///
+/// ## Range proofs
+///
+/// See [`RangeProof`](crate::RangeProof).
+///
+/// # Implementation details
+///
+/// - The proof is serialized as the common challenge `e_0` followed by `s_i` scalars for
+///   all the rings.
+/// - Standalone proof generation and verification are not exposed in public crate APIs.
+///   Rather, proofs are part of large protocols, such as [`PublicKey::encrypt_bool()`] /
+///   [`PublicKey::verify_bool()`].
+/// - The context of the proof is set using [`Transcript`] APIs, which provides hash functions
+///   in the protocol described above. Importantly, the proof itself commits to encrypted values
+///   and ring indexes, but not to the admissible values across the rings. This must be taken
+///   care of in a higher-level protocol, and this is the case for protocols exposed by the crate.
+///
+/// [`LogEqualityProof`]: crate::LogEqualityProof
+/// [Borromean ring signatures]: https://raw.githubusercontent.com/Blockstream/borromean_paper/master/borromean_draft_0.01_34241bb.pdf
+/// [ring]: https://link.springer.com/content/pdf/10.1007/3-540-36178-2_26.pdf
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct RingProof<G: Group> {
+    #[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
+    common_challenge: G::Scalar,
+    #[cfg_attr(feature = "serde", serde(with = "VecHelper::<ScalarHelper<G>, 2>"))]
+    ring_responses: Vec<G::Scalar>,
+}
+
+impl<G: Group> RingProof<G> {
+    fn initialize_transcript(transcript: &mut Transcript, receiver: &PublicKey<G>) {
+        transcript.start_proof(b"multi_ring_enc");
+        transcript.append_element_bytes(b"K", receiver.as_bytes());
+    }
+
+    pub(crate) fn new(common_challenge: G::Scalar, ring_responses: Vec<G::Scalar>) -> Self {
+        Self {
+            common_challenge,
+            ring_responses,
+        }
+    }
+
+    pub(crate) fn verify<'a>(
+        &self,
+        receiver: &PublicKey<G>,
+        admissible_values: impl Iterator<Item = &'a [G::Element]> + Clone,
+        ciphertexts: impl Iterator<Item = Ciphertext<G>>,
+        transcript: &mut Transcript,
+    ) -> Result<(), VerificationError> {
+        // Do quick preliminary checks.
+        let total_rings_size: usize = admissible_values.clone().map(<[_]>::len).sum();
+        VerificationError::check_lengths(
+            "items in all rings",
+            self.total_rings_size(),
+            total_rings_size,
+        )?;
+
+        Self::initialize_transcript(transcript, receiver);
+        // We add common commitments to the `transcript` as we cycle through rings,
+        // so we need a separate transcript copy to initialize ring transcripts.
+        let initial_ring_transcript = transcript.clone();
+
+        let it = admissible_values.zip(ciphertexts).enumerate();
+        let mut starting_response = 0;
+        for (ring_index, (values, ciphertext)) in it {
+            let mut challenge = self.common_challenge;
+            let mut commitments = (G::generator(), G::generator());
+
+            let mut ring_transcript = initial_ring_transcript.clone();
+            ring_transcript.start_proof(b"ring_enc");
+            ring_transcript.append_message(b"enc", &ciphertext.to_bytes());
+            ring_transcript.append_u64(b"i", ring_index as u64);
+
+            for (eq_index, (&admissible_value, response)) in values
+                .iter()
+                .zip(&self.ring_responses[starting_response..])
+                .enumerate()
+            {
+                let dh_element = ciphertext.blinded_element - admissible_value;
+                let neg_challenge = -challenge;
+
+                commitments = (
+                    G::vartime_double_mul_generator(
+                        &neg_challenge,
+                        ciphertext.random_element,
+                        response,
+                    ),
+                    G::vartime_multi_mul(
+                        [response, &neg_challenge],
+                        [receiver.as_element(), dh_element],
+                    ),
+                );
+
+                // We can skip deriving the challenge for the last equation; it's not used anyway.
+                if eq_index + 1 < values.len() {
+                    let mut eq_transcript = ring_transcript.clone();
+                    eq_transcript.append_u64(b"j", eq_index as u64);
+                    eq_transcript.append_element::<G>(b"R_G", &commitments.0);
+                    eq_transcript.append_element::<G>(b"R_K", &commitments.1);
+                    challenge = eq_transcript.challenge_scalar::<G>(b"c");
+                }
+            }
+
+            starting_response += values.len();
+            transcript.append_element::<G>(b"R_G", &commitments.0);
+            transcript.append_element::<G>(b"R_K", &commitments.1);
+        }
+
+        let expected_challenge = transcript.challenge_scalar::<G>(b"c");
+        if expected_challenge == self.common_challenge {
+            Ok(())
+        } else {
+            Err(VerificationError::ChallengeMismatch)
+        }
+    }
+
+    pub(crate) fn total_rings_size(&self) -> usize {
+        self.ring_responses.len()
+    }
+
+    /// Serializes this proof into bytes. As described [above](#implementation-details),
+    /// the proof is serialized as the common challenge `e_0` followed by response scalars `s_*`
+    /// corresponding successively to each admissible value in each ring.
+    pub fn to_bytes(&self) -> Vec<u8> {
+        let mut bytes = vec![0_u8; G::SCALAR_SIZE * (1 + self.total_rings_size())];
+        G::serialize_scalar(&self.common_challenge, &mut bytes[..G::SCALAR_SIZE]);
+
+        let chunks = bytes[G::SCALAR_SIZE..].chunks_mut(G::SCALAR_SIZE);
+        for (response, buffer) in self.ring_responses.iter().zip(chunks) {
+            G::serialize_scalar(response, buffer);
+        }
+        bytes
+    }
+
+    /// Attempts to deserialize a proof from bytes. Returns `None` if `bytes` do not represent
+    /// a well-formed proof.
+    #[allow(clippy::missing_panics_doc)] // triggered by `debug_assert`
+    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
+        if bytes.len() % G::SCALAR_SIZE != 0 || bytes.len() < 3 * G::SCALAR_SIZE {
+            return None;
+        }
+        let common_challenge = G::deserialize_scalar(&bytes[..G::SCALAR_SIZE])?;
+
+        let ring_responses: Option<Vec<_>> = bytes[G::SCALAR_SIZE..]
+            .chunks(G::SCALAR_SIZE)
+            .map(G::deserialize_scalar)
+            .collect();
+        let ring_responses = ring_responses?;
+        debug_assert!(ring_responses.len() >= 2);
+
+        Some(Self {
+            common_challenge,
+            ring_responses,
+        })
+    }
+}
+
+/// **NB.** Separate method calls of the builder depend on the position of the encrypted values
+/// within admissible ones. This means that if a proof is constructed with interruptions between
+/// method calls, there is a chance for an adversary to perform a timing attack.
+#[doc(hidden)] // only public for benchmarking
+pub struct RingProofBuilder<'a, G: Group, R> {
+    receiver: &'a PublicKey<G>,
+    transcript: &'a mut Transcript,
+    rings: Vec<Ring<'a, G>>,
+    ring_responses: &'a mut [G::Scalar],
+    rng: &'a mut R,
+}
+
+impl<G: Group, R: fmt::Debug> fmt::Debug for RingProofBuilder<'_, G, R> {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        formatter
+            .debug_struct("RingProofBuilder")
+            .field("receiver", self.receiver)
+            .field("rings", &self.rings)
+            .field("rng", self.rng)
+            .finish()
+    }
+}
+
+impl<'a, G: Group, R: RngCore + CryptoRng> RingProofBuilder<'a, G, R> {
+    /// Starts building a [`RingProof`].
+    pub fn new(
+        receiver: &'a PublicKey<G>,
+        ring_count: usize,
+        ring_responses: &'a mut [G::Scalar],
+        transcript: &'a mut Transcript,
+        rng: &'a mut R,
+    ) -> Self {
+        RingProof::<G>::initialize_transcript(transcript, receiver);
+        Self {
+            receiver,
+            transcript,
+            rings: Vec::with_capacity(ring_count),
+            ring_responses,
+            rng,
+        }
+    }
+
+    /// Adds a value among `admissible_values` as a new ring to this proof.
+    pub fn add_value(
+        &mut self,
+        admissible_values: &'a [G::Element],
+        value_index: usize,
+    ) -> ExtendedCiphertext<G> {
+        let ext_ciphertext =
+            ExtendedCiphertext::new(admissible_values[value_index], self.receiver, self.rng);
+        self.add_precomputed_value(ext_ciphertext.clone(), admissible_values, value_index);
+        ext_ciphertext
+    }
+
+    pub(crate) fn add_precomputed_value(
+        &mut self,
+        ciphertext: ExtendedCiphertext<G>,
+        admissible_values: &'a [G::Element],
+        value_index: usize,
+    ) {
+        let ring_responses = mem::take(&mut self.ring_responses);
+        let (responses_for_ring, rest) = ring_responses.split_at_mut(admissible_values.len());
+        self.ring_responses = rest;
+
+        let ring = Ring::new(
+            self.rings.len(),
+            self.receiver.as_element(),
+            ciphertext,
+            admissible_values,
+            value_index,
+            &*self.transcript,
+            responses_for_ring,
+            self.rng,
+        );
+        self.rings.push(ring);
+    }
+
+    /// Finishes building all rings and returns a common challenge.
+    pub fn build(self) -> G::Scalar {
+        debug_assert!(
+            self.ring_responses.is_empty(),
+            "Not all ring_responses were used"
+        );
+        Ring::aggregate(
+            self.rings,
+            self.receiver.as_element(),
+            self.transcript,
+            self.rng,
+        )
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::{thread_rng, Rng};
+    use test_casing::test_casing;
+
+    use core::iter;
+
+    use super::*;
+    use crate::{
+        curve25519::{ristretto::RistrettoPoint, scalar::Scalar as Scalar25519, traits::Identity},
+        group::{ElementOps, Ristretto},
+    };
+
+    type Keypair = crate::Keypair<Ristretto>;
+
+    #[test]
+    fn single_ring_with_2_elements_works() {
+        let mut rng = thread_rng();
+        let keypair = Keypair::generate(&mut rng);
+        let log_base = keypair.public().as_element();
+        let admissible_values = [RistrettoPoint::identity(), Ristretto::generator()];
+
+        let value = RistrettoPoint::identity();
+        let ext_ciphertext = ExtendedCiphertext::new(value, keypair.public(), &mut rng);
+        let ciphertext = ext_ciphertext.inner;
+
+        let mut transcript = Transcript::new(b"test_ring_encryption");
+        RingProof::initialize_transcript(&mut transcript, keypair.public());
+
+        let mut ring_responses = vec![Scalar25519::default(); 2];
+        let signature_ring = Ring::new(
+            0,
+            log_base,
+            ext_ciphertext,
+            &admissible_values,
+            0,
+            &transcript,
+            &mut ring_responses,
+            &mut rng,
+        );
+        let common_challenge =
+            Ring::aggregate(vec![signature_ring], log_base, &mut transcript, &mut rng);
+
+        RingProof::new(common_challenge, ring_responses)
+            .verify(
+                keypair.public(),
+                iter::once(&admissible_values as &[_]),
+                iter::once(ciphertext),
+                &mut Transcript::new(b"test_ring_encryption"),
+            )
+            .unwrap();
+
+        // Check a proof for encryption of 1.
+        let value = Ristretto::generator();
+        let ext_ciphertext = ExtendedCiphertext::new(value, keypair.public(), &mut rng);
+        let ciphertext = ext_ciphertext.inner;
+
+        let mut transcript = Transcript::new(b"test_ring_encryption");
+        RingProof::initialize_transcript(&mut transcript, keypair.public());
+        let mut ring_responses = vec![Scalar25519::default(); 2];
+        let signature_ring = Ring::new(
+            0,
+            log_base,
+            ext_ciphertext,
+            &admissible_values,
+            1,
+            &transcript,
+            &mut ring_responses,
+            &mut rng,
+        );
+        let common_challenge =
+            Ring::aggregate(vec![signature_ring], log_base, &mut transcript, &mut rng);
+
+        RingProof::new(common_challenge, ring_responses)
+            .verify(
+                keypair.public(),
+                iter::once(&admissible_values as &[_]),
+                iter::once(ciphertext),
+                &mut Transcript::new(b"test_ring_encryption"),
+            )
+            .unwrap();
+    }
+
+    #[test]
+    fn single_ring_with_4_elements_works() {
+        let mut rng = thread_rng();
+        let keypair = Keypair::generate(&mut rng);
+        let log_base = keypair.public().as_element();
+        let admissible_values: Vec<_> = (0_u32..4)
+            .map(|i| Ristretto::mul_generator(&Scalar25519::from(i)))
+            .collect();
+
+        for _ in 0..100 {
+            let val: u32 = rng.gen_range(0..4);
+            let element_val = Ristretto::mul_generator(&Scalar25519::from(val));
+            let ext_ciphertext = ExtendedCiphertext::new(element_val, keypair.public(), &mut rng);
+            let ciphertext = ext_ciphertext.inner;
+
+            let mut transcript = Transcript::new(b"test_ring_encryption");
+            RingProof::initialize_transcript(&mut transcript, keypair.public());
+
+            let mut ring_responses = vec![Scalar25519::default(); 4];
+            let signature_ring = Ring::new(
+                0,
+                log_base,
+                ext_ciphertext,
+                &admissible_values,
+                val as usize,
+                &transcript,
+                &mut ring_responses,
+                &mut rng,
+            );
+            let common_challenge =
+                Ring::aggregate(vec![signature_ring], log_base, &mut transcript, &mut rng);
+
+            RingProof::new(common_challenge, ring_responses)
+                .verify(
+                    keypair.public(),
+                    iter::once(admissible_values.as_slice()),
+                    iter::once(ciphertext),
+                    &mut Transcript::new(b"test_ring_encryption"),
+                )
+                .unwrap();
+        }
+    }
+
+    #[test_casing(5, 3..=7)]
+    fn multiple_rings_with_boolean_flags_work(ring_count: usize) {
+        let mut rng = thread_rng();
+        let keypair = Keypair::generate(&mut rng);
+        let log_base = keypair.public().as_element();
+        let admissible_values = [RistrettoPoint::identity(), Ristretto::generator()];
+
+        for _ in 0..20 {
+            let mut transcript = Transcript::new(b"test_ring_encryption");
+            RingProof::initialize_transcript(&mut transcript, keypair.public());
+
+            let mut ring_responses = vec![Scalar25519::default(); ring_count * 2];
+
+            let (ciphertexts, rings): (Vec<_>, Vec<_>) = ring_responses
+                .chunks_mut(2)
+                .enumerate()
+                .map(|(ring_index, ring_responses)| {
+                    let val: u32 = rng.gen_range(0..=1);
+                    let element_val = Ristretto::mul_generator(&Scalar25519::from(val));
+                    let ext_ciphertext =
+                        ExtendedCiphertext::new(element_val, keypair.public(), &mut rng);
+                    let ciphertext = ext_ciphertext.inner;
+
+                    let signature_ring = Ring::new(
+                        ring_index,
+                        log_base,
+                        ext_ciphertext,
+                        &admissible_values,
+                        val as usize,
+                        &transcript,
+                        ring_responses,
+                        &mut rng,
+                    );
+
+                    (ciphertext, signature_ring)
+                })
+                .unzip();
+
+            let common_challenge = Ring::aggregate(rings, log_base, &mut transcript, &mut rng);
+
+            RingProof::new(common_challenge, ring_responses)
+                .verify(
+                    keypair.public(),
+                    iter::repeat(&admissible_values as &[_]).take(ring_count),
+                    ciphertexts.into_iter(),
+                    &mut Transcript::new(b"test_ring_encryption"),
+                )
+                .unwrap();
+        }
+    }
+
+    #[test]
+    fn multiple_rings_with_base4_value_encoding_work() {
+        // We're testing ciphertexts of `u8` integers, hence 4 rings with 4 elements (=2 bits) each.
+        const RING_COUNT: u8 = 4;
+
+        // Admissible values are `[O, G, [2]G, [3]G]` for the first ring,
+        // `[O, [4]G, [8]G, [12]G]` for the second ring, etc.
+        let admissible_values: Vec<_> = (0..RING_COUNT)
+            .map(|ring_index| {
+                let power: u32 = 1 << (2 * u32::from(ring_index));
+                [
+                    RistrettoPoint::identity(),
+                    Ristretto::mul_generator(&Scalar25519::from(power)),
+                    Ristretto::mul_generator(&Scalar25519::from(power * 2)),
+                    Ristretto::mul_generator(&Scalar25519::from(power * 3)),
+                ]
+            })
+            .collect();
+
+        let mut rng = thread_rng();
+        let keypair = Keypair::generate(&mut rng);
+        let log_base = keypair.public().as_element();
+
+        for _ in 0..20 {
+            let overall_value: u8 = rng.gen();
+            let mut transcript = Transcript::new(b"test_ring_encryption");
+            RingProof::initialize_transcript(&mut transcript, keypair.public());
+
+            let mut ring_responses = vec![Scalar25519::default(); RING_COUNT as usize * 4];
+
+            let (ciphertexts, rings): (Vec<_>, Vec<_>) = ring_responses
+                .chunks_mut(4)
+                .enumerate()
+                .map(|(ring_index, ring_responses)| {
+                    let mask = 3 << (2 * ring_index);
+                    let val = overall_value & mask;
+                    let val_index = (val >> (2 * ring_index)) as usize;
+                    assert!(val_index < 4);
+
+                    let element_val = Ristretto::mul_generator(&Scalar25519::from(val));
+                    let ext_ciphertext =
+                        ExtendedCiphertext::new(element_val, keypair.public(), &mut rng);
+                    let ciphertext = ext_ciphertext.inner;
+
+                    let signature_ring = Ring::new(
+                        ring_index,
+                        log_base,
+                        ext_ciphertext,
+                        &admissible_values[ring_index],
+                        val_index,
+                        &transcript,
+                        ring_responses,
+                        &mut rng,
+                    );
+
+                    (ciphertext, signature_ring)
+                })
+                .unzip();
+
+            let common_challenge = Ring::aggregate(rings, log_base, &mut transcript, &mut rng);
+            let admissible_values = admissible_values.iter().map(|values| values as &[_]);
+
+            RingProof::new(common_challenge, ring_responses)
+                .verify(
+                    keypair.public(),
+                    admissible_values,
+                    ciphertexts.into_iter(),
+                    &mut Transcript::new(b"test_ring_encryption"),
+                )
+                .unwrap();
+        }
+    }
+
+    #[test_casing(5, 3..=7)]
+    #[allow(clippy::needless_collect)]
+    // ^-- false positive; `builder` is captured by the iterator and moved by creating a `proof`
+    fn proof_builder_works(ring_count: usize) {
+        let mut rng = thread_rng();
+        let keypair = Keypair::generate(&mut rng);
+        let mut transcript = Transcript::new(b"test_ring_encryption");
+        let admissible_values = [RistrettoPoint::identity(), Ristretto::generator()];
+        let mut ring_responses = vec![Scalar25519::default(); ring_count * 2];
+
+        let mut builder = RingProofBuilder::new(
+            keypair.public(),
+            ring_count,
+            &mut ring_responses,
+            &mut transcript,
+            &mut rng,
+        );
+        let ciphertexts: Vec<_> = (0..ring_count)
+            .map(|i| builder.add_value(&admissible_values, i & 1).inner)
+            .collect();
+
+        RingProof::new(builder.build(), ring_responses)
+            .verify(
+                keypair.public(),
+                iter::repeat(&admissible_values as &[_]).take(ring_count),
+                ciphertexts.into_iter(),
+                &mut Transcript::new(b"test_ring_encryption"),
+            )
+            .unwrap();
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/serde.rs.html b/src/elastic_elgamal/serde.rs.html new file mode 100644 index 0000000..83750d5 --- /dev/null +++ b/src/elastic_elgamal/serde.rs.html @@ -0,0 +1,1077 @@ +serde.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+
//! (De)serialization utils.
+
+use base64ct::{Base64UrlUnpadded, Encoding};
+use serde::{
+    de::{DeserializeOwned, Error as DeError, SeqAccess, Unexpected, Visitor},
+    Deserialize, Deserializer, Serialize, Serializer,
+};
+use zeroize::Zeroizing;
+
+use core::{fmt, marker::PhantomData};
+
+use crate::{
+    alloc::{vec, ToString, Vec},
+    dkg::Opening,
+    group::Group,
+    Keypair, PublicKey, SecretKey,
+};
+
+fn serialize_bytes<S>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    if serializer.is_human_readable() {
+        serializer.serialize_str(&Base64UrlUnpadded::encode_string(value))
+    } else {
+        serializer.serialize_bytes(value)
+    }
+}
+
+fn deserialize_bytes<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    struct Base64Visitor;
+
+    impl Visitor<'_> for Base64Visitor {
+        type Value = Vec<u8>;
+
+        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+            formatter.write_str("base64url-encoded data")
+        }
+
+        fn visit_str<E: DeError>(self, value: &str) -> Result<Self::Value, E> {
+            Base64UrlUnpadded::decode_vec(value)
+                .map_err(|_| E::invalid_value(Unexpected::Str(value), &self))
+        }
+
+        fn visit_bytes<E: DeError>(self, value: &[u8]) -> Result<Self::Value, E> {
+            Ok(value.to_vec())
+        }
+
+        fn visit_byte_buf<E: DeError>(self, value: Vec<u8>) -> Result<Self::Value, E> {
+            Ok(value)
+        }
+    }
+
+    struct BytesVisitor;
+
+    impl Visitor<'_> for BytesVisitor {
+        type Value = Vec<u8>;
+
+        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+            formatter.write_str("byte buffer")
+        }
+
+        fn visit_bytes<E: DeError>(self, value: &[u8]) -> Result<Self::Value, E> {
+            Ok(value.to_vec())
+        }
+
+        fn visit_byte_buf<E: DeError>(self, value: Vec<u8>) -> Result<Self::Value, E> {
+            Ok(value)
+        }
+    }
+
+    if deserializer.is_human_readable() {
+        deserializer.deserialize_str(Base64Visitor)
+    } else {
+        deserializer.deserialize_byte_buf(BytesVisitor)
+    }
+}
+
+impl Serialize for Opening {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serialize_bytes(self.0.as_slice(), serializer)
+    }
+}
+
+impl<'de> Deserialize<'de> for Opening {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let bytes = Zeroizing::new(deserialize_bytes(deserializer)?);
+        let mut opening = Opening(Zeroizing::new([0_u8; 32]));
+        if bytes.len() == 32 {
+            opening.0.copy_from_slice(&bytes);
+            Ok(opening)
+        } else {
+            Err(D::Error::invalid_length(bytes.len(), &"32"))
+        }
+    }
+}
+
+impl<G: Group> Serialize for PublicKey<G> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serialize_bytes(self.as_bytes(), serializer)
+    }
+}
+
+impl<'de, G: Group> Deserialize<'de> for PublicKey<G> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let bytes = deserialize_bytes(deserializer)?;
+        Self::from_bytes(&bytes).map_err(D::Error::custom)
+    }
+}
+
+impl<G: Group> Serialize for SecretKey<G> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut bytes = Zeroizing::new(vec![0_u8; G::SCALAR_SIZE]);
+        G::serialize_scalar(self.expose_scalar(), &mut bytes);
+        serialize_bytes(&bytes, serializer)
+    }
+}
+
+impl<'de, G: Group> Deserialize<'de> for SecretKey<G> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let bytes = Zeroizing::new(deserialize_bytes(deserializer)?);
+        Self::from_bytes(&bytes)
+            .ok_or_else(|| D::Error::custom("bytes do not represent a group scalar"))
+    }
+}
+
+impl<G: Group> Serialize for Keypair<G> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        self.secret().serialize(serializer)
+    }
+}
+
+impl<'de, G: Group> Deserialize<'de> for Keypair<G> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        SecretKey::<G>::deserialize(deserializer).map(From::from)
+    }
+}
+
+/// Common functionality for serialization helpers.
+pub(crate) trait Helper: Serialize + DeserializeOwned {
+    const PLURAL_DESCRIPTION: &'static str;
+    type Target;
+
+    fn from_target(target: &Self::Target) -> Self;
+    fn into_target(self) -> Self::Target;
+}
+
+/// Helper type to deserialize scalars.
+///
+/// **NB.** Scalars are assumed to be public! Secret scalars must be serialized via `SecretKey`.
+#[derive(Debug)]
+pub(crate) struct ScalarHelper<G: Group>(G::Scalar);
+
+impl<G: Group> ScalarHelper<G> {
+    pub fn serialize<S>(scalar: &G::Scalar, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut bytes = vec![0_u8; G::SCALAR_SIZE];
+        G::serialize_scalar(scalar, &mut bytes);
+        serialize_bytes(&bytes, serializer)
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<G::Scalar, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let bytes = deserialize_bytes(deserializer)?;
+        if bytes.len() == G::SCALAR_SIZE {
+            G::deserialize_scalar(&bytes)
+                .ok_or_else(|| D::Error::custom("bytes do not represent a group scalar"))
+        } else {
+            let expected_len = G::SCALAR_SIZE.to_string();
+            Err(D::Error::invalid_length(
+                bytes.len(),
+                &expected_len.as_str(),
+            ))
+        }
+    }
+}
+
+impl<G: Group> Serialize for ScalarHelper<G> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        Self::serialize(&self.0, serializer)
+    }
+}
+
+impl<'de, G: Group> Deserialize<'de> for ScalarHelper<G> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        Self::deserialize(deserializer).map(Self)
+    }
+}
+
+impl<G: Group> Helper for ScalarHelper<G> {
+    const PLURAL_DESCRIPTION: &'static str = "group scalars";
+    type Target = G::Scalar;
+
+    fn from_target(target: &Self::Target) -> Self {
+        Self(*target)
+    }
+
+    fn into_target(self) -> Self::Target {
+        self.0
+    }
+}
+
+/// Helper type to deserialize group elements.
+#[derive(Debug)]
+pub(crate) struct ElementHelper<G: Group>(G::Element);
+
+impl<G: Group> ElementHelper<G> {
+    pub fn serialize<S>(element: &G::Element, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut bytes = vec![0_u8; G::ELEMENT_SIZE];
+        G::serialize_element(element, &mut bytes);
+        serialize_bytes(&bytes, serializer)
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<G::Element, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let bytes = deserialize_bytes(deserializer)?;
+        if bytes.len() == G::ELEMENT_SIZE {
+            G::deserialize_element(&bytes)
+                .ok_or_else(|| D::Error::custom("bytes do not represent a group element"))
+        } else {
+            let expected_len = G::ELEMENT_SIZE.to_string();
+            Err(D::Error::invalid_length(
+                bytes.len(),
+                &expected_len.as_str(),
+            ))
+        }
+    }
+}
+
+impl<G: Group> Serialize for ElementHelper<G> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        Self::serialize(&self.0, serializer)
+    }
+}
+
+impl<'de, G: Group> Deserialize<'de> for ElementHelper<G> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        Self::deserialize(deserializer).map(Self)
+    }
+}
+
+impl<G: Group> Helper for ElementHelper<G> {
+    const PLURAL_DESCRIPTION: &'static str = "group elements";
+    type Target = G::Element;
+
+    fn from_target(target: &Self::Target) -> Self {
+        Self(*target)
+    }
+
+    fn into_target(self) -> Self::Target {
+        self.0
+    }
+}
+
+pub(crate) struct VecHelper<T, const MIN: usize>(PhantomData<T>);
+
+impl<T: Helper, const MIN: usize> VecHelper<T, MIN> {
+    fn new() -> Self {
+        Self(PhantomData)
+    }
+
+    pub fn serialize<S>(values: &[T::Target], serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        debug_assert!(values.len() >= MIN);
+        serializer.collect_seq(values.iter().map(T::from_target))
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<T::Target>, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        deserializer.deserialize_seq(Self::new())
+    }
+}
+
+impl<'de, T: Helper, const MIN: usize> Visitor<'de> for VecHelper<T, MIN> {
+    type Value = Vec<T::Target>;
+
+    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(formatter, "at least {MIN} {}", T::PLURAL_DESCRIPTION)
+    }
+
+    fn visit_seq<S>(self, mut access: S) -> Result<Self::Value, S::Error>
+    where
+        S: SeqAccess<'de>,
+    {
+        let mut scalars: Vec<T::Target> = if let Some(size) = access.size_hint() {
+            if size < MIN {
+                return Err(S::Error::invalid_length(size, &self));
+            }
+            Vec::with_capacity(size)
+        } else {
+            Vec::new()
+        };
+
+        while let Some(value) = access.next_element::<T>()? {
+            scalars.push(value.into_target());
+        }
+        if scalars.len() >= MIN {
+            Ok(scalars)
+        } else {
+            Err(S::Error::invalid_length(scalars.len(), &self))
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::thread_rng;
+
+    use super::*;
+    use crate::group::Ristretto;
+
+    #[test]
+    fn opening_roundtrip() {
+        let opening = Opening(Zeroizing::new([6; 32]));
+        let json = serde_json::to_value(&opening).unwrap();
+        assert!(json.is_string(), "{json:?}");
+        let opening_copy: Opening = serde_json::from_value(json).unwrap();
+        assert_eq!(opening_copy.0, opening.0);
+    }
+
+    #[test]
+    fn key_roundtrip() {
+        let keypair = Keypair::<Ristretto>::generate(&mut thread_rng());
+        let json = serde_json::to_value(&keypair).unwrap();
+        assert!(json.is_string(), "{json:?}");
+        let keypair_copy: Keypair<Ristretto> = serde_json::from_value(json).unwrap();
+        assert_eq!(keypair_copy.public(), keypair.public());
+
+        let json = serde_json::to_value(keypair.public()).unwrap();
+        assert!(json.is_string(), "{json:?}");
+        let public_key: PublicKey<Ristretto> = serde_json::from_value(json).unwrap();
+        assert_eq!(public_key, *keypair.public());
+
+        let json = serde_json::to_value(keypair.secret()).unwrap();
+        assert!(json.is_string(), "{json:?}");
+        let secret_key: SecretKey<Ristretto> = serde_json::from_value(json).unwrap();
+        assert_eq!(secret_key.expose_scalar(), keypair.secret().expose_scalar());
+    }
+
+    #[test]
+    fn public_key_deserialization_with_incorrect_length() {
+        let err = serde_json::from_str::<PublicKey<Ristretto>>("\"dGVzdA\"").unwrap_err();
+        let err_string = err.to_string();
+        assert!(
+            err_string.contains("invalid size of the byte buffer"),
+            "{err_string}"
+        );
+    }
+
+    #[test]
+    fn public_key_deserialization_of_non_element() {
+        let err = serde_json::from_str::<PublicKey<Ristretto>>(
+            "\"tNDkeYUVQWgh34d-RqaElOk7yFB8d2qCh5f4Vi2euT0\"",
+        )
+        .unwrap_err();
+        let err_string = err.to_string();
+        assert!(
+            err_string.contains("does not represent a group element"),
+            "{err_string}"
+        );
+    }
+
+    #[test]
+    fn secret_key_deserialization_with_incorrect_length() {
+        let err = serde_json::from_str::<SecretKey<Ristretto>>("\"dGVzdA\"").unwrap_err();
+        let err_string = err.to_string();
+        assert!(
+            err_string.contains("bytes do not represent a group scalar"),
+            "{err_string}"
+        );
+    }
+
+    #[test]
+    fn secret_key_deserialization_of_invalid_scalar() {
+        // Last `_8` chars set the upper byte of the scalar bytes to 0xff, which is invalid
+        // (all scalars are less than 2^253).
+        let err = serde_json::from_str::<SecretKey<Ristretto>>(
+            "\"nN3xf7lSOX0_zs6QPBwWHYi0Dkx2Ln_z1MPwnbzaM_8\"",
+        )
+        .unwrap_err();
+        let err_string = err.to_string();
+        assert!(
+            err_string.contains("bytes do not represent a group scalar"),
+            "{err_string}"
+        );
+    }
+
+    #[derive(Debug, PartialEq, Serialize, Deserialize)]
+    #[serde(bound = "")]
+    struct TestObject<G: Group> {
+        #[serde(with = "ScalarHelper::<G>")]
+        scalar: G::Scalar,
+        #[serde(with = "ElementHelper::<G>")]
+        element: G::Element,
+        #[serde(with = "VecHelper::<ScalarHelper<G>, 2>")]
+        more_scalars: Vec<G::Scalar>,
+    }
+
+    impl TestObject<Ristretto> {
+        fn sample() -> Self {
+            Self {
+                scalar: 12345_u64.into(),
+                element: Ristretto::mul_generator(&54321_u64.into()),
+                more_scalars: vec![7_u64.into(), 890_u64.into()],
+            }
+        }
+    }
+
+    #[test]
+    fn helpers_roundtrip() {
+        let object = TestObject::sample();
+        let json = serde_json::to_value(&object).unwrap();
+        let object_copy: TestObject<Ristretto> = serde_json::from_value(json).unwrap();
+        assert_eq!(object_copy, object);
+    }
+
+    #[test]
+    fn scalar_helper_invalid_scalar() {
+        let object = TestObject::sample();
+        let mut json = serde_json::to_value(object).unwrap();
+        json.as_object_mut()
+            .unwrap()
+            .insert("scalar".into(), "dGVzdA".into());
+
+        let err = serde_json::from_value::<TestObject<Ristretto>>(json.clone()).unwrap_err();
+        let err_string = err.to_string();
+        assert!(
+            err_string.contains("invalid length 4, expected 32"),
+            "{err_string}"
+        );
+
+        json.as_object_mut().unwrap().insert(
+            "scalar".into(),
+            "nN3xf7lSOX0_zs6QPBwWHYi0Dkx2Ln_z1MPwnbzaM_8".into(),
+        );
+        let err = serde_json::from_value::<TestObject<Ristretto>>(json).unwrap_err();
+        let err_string = err.to_string();
+        assert!(
+            err_string.contains("bytes do not represent a group scalar"),
+            "{err_string}"
+        );
+    }
+
+    #[test]
+    fn element_helper_invalid_element() {
+        let object = TestObject::sample();
+        let mut json = serde_json::to_value(object).unwrap();
+        json.as_object_mut()
+            .unwrap()
+            .insert("element".into(), "dGVzdA".into());
+
+        let err = serde_json::from_value::<TestObject<Ristretto>>(json.clone()).unwrap_err();
+        let err_string = err.to_string();
+        assert!(
+            err_string.contains("invalid length 4, expected 32"),
+            "{err_string}"
+        );
+
+        json.as_object_mut().unwrap().insert(
+            "element".into(),
+            "nN3xf7lSOX0_zs6QPBwWHYi0Dkx2Ln_z1MPwnbzaM_8".into(),
+        );
+        let err = serde_json::from_value::<TestObject<Ristretto>>(json).unwrap_err();
+        let err_string = err.to_string();
+        assert!(
+            err_string.contains("bytes do not represent a group element"),
+            "{err_string}"
+        );
+    }
+
+    #[test]
+    fn vec_helper_invalid_length() {
+        let object = TestObject::sample();
+        let mut json = serde_json::to_value(object).unwrap();
+        let more_scalars = &mut json.as_object_mut().unwrap()["more_scalars"];
+        more_scalars.as_array_mut().unwrap().pop();
+
+        let err = serde_json::from_value::<TestObject<Ristretto>>(json).unwrap_err();
+        let err_string = err.to_string();
+        assert!(
+            err_string.contains("invalid length 1, expected at least 2 group scalars"),
+            "{err_string}"
+        );
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/sharing/key_set.rs.html b/src/elastic_elgamal/sharing/key_set.rs.html new file mode 100644 index 0000000..d24848c --- /dev/null +++ b/src/elastic_elgamal/sharing/key_set.rs.html @@ -0,0 +1,553 @@ +key_set.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+
//! `PublicKeySet` and associated helpers.
+
+use merlin::Transcript;
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+use core::iter;
+
+use super::{lagrange_coefficients, Error, Params, PublicPolynomial};
+
+use crate::{
+    alloc::Vec,
+    group::Group,
+    proofs::{LogEqualityProof, ProofOfPossession, TranscriptForGroup, VerificationError},
+    CandidateDecryption, Ciphertext, PublicKey, VerifiableDecryption,
+};
+
+/// Full public information about the participants of a threshold ElGamal encryption scheme
+/// after all participants' commitments are collected.
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct PublicKeySet<G: Group> {
+    params: Params,
+    shared_key: PublicKey<G>,
+    participant_keys: Vec<PublicKey<G>>,
+}
+
+impl<G: Group> PublicKeySet<G> {
+    pub(crate) fn validate(
+        params: Params,
+        public_polynomial: &[G::Element],
+        proof_of_possession: &ProofOfPossession<G>,
+    ) -> Result<(), Error> {
+        if public_polynomial.len() != params.threshold {
+            return Err(Error::MalformedDealerPolynomial);
+        }
+
+        let mut transcript = Transcript::new(b"elgamal_share_poly");
+        transcript.append_u64(b"n", params.shares as u64);
+        transcript.append_u64(b"t", params.threshold as u64);
+
+        let public_poly_keys: Vec<_> = public_polynomial
+            .iter()
+            .copied()
+            .map(PublicKey::from_element)
+            .collect();
+        proof_of_possession
+            .verify(public_poly_keys.iter(), &mut transcript)
+            .map_err(Error::InvalidDealerProof)?;
+        Ok(())
+    }
+
+    /// Creates an instance based on information provided by the [`Dealer`].
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the information provided by the dealer is malformed.
+    ///
+    /// [`Dealer`]: crate::sharing::Dealer
+    pub fn new(
+        params: Params,
+        public_polynomial: Vec<G::Element>,
+        proof_of_possession: &ProofOfPossession<G>,
+    ) -> Result<Self, Error> {
+        Self::validate(params, &public_polynomial, proof_of_possession)?;
+
+        let public_poly = PublicPolynomial::<G>(public_polynomial);
+        let shared_key = PublicKey::from_element(public_poly.value_at_zero());
+        let participant_keys = (0..params.shares)
+            .map(|idx| PublicKey::from_element(public_poly.value_at((idx as u64 + 1).into())))
+            .collect();
+
+        Ok(Self {
+            params,
+            shared_key,
+            participant_keys,
+        })
+    }
+
+    /// Creates a key set from the parameters and public keys of all participants.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the number of keys in `participant_keys` does not match the number
+    /// of participants in `params`, or if `participant_keys` are inconsistent (do not correspond
+    /// to a single shared key).
+    pub fn from_participants(
+        params: Params,
+        participant_keys: Vec<PublicKey<G>>,
+    ) -> Result<Self, Error> {
+        if params.shares != participant_keys.len() {
+            return Err(Error::ParticipantCountMismatch);
+        }
+
+        // Reconstruct the shared key based on first `t` participant keys.
+        let indexes: Vec<_> = (0..params.threshold).collect();
+        let (denominators, scale) = lagrange_coefficients::<G>(&indexes);
+        let starting_keys = participant_keys
+            .iter()
+            .map(PublicKey::as_element)
+            .take(params.threshold);
+        let shared_key = G::vartime_multi_mul(&denominators, starting_keys.clone());
+        let shared_key = PublicKey::from_element(shared_key * &scale);
+
+        // Check that the remaining participant keys are correct.
+
+        // Prepare multiplicative inverses for `1..=n`.
+        let mut inverses: Vec<_> = (1_u64..=params.shares as u64)
+            .map(G::Scalar::from)
+            .collect();
+        G::invert_scalars(&mut inverses);
+
+        for (x, key) in participant_keys.iter().enumerate().skip(params.threshold) {
+            let mut key_scale = indexes
+                .iter()
+                .map(|&idx| G::Scalar::from((x - idx) as u64))
+                .fold(G::Scalar::from(1), |acc, value| acc * value);
+
+            let key_denominators: Vec<_> = denominators
+                .iter()
+                .enumerate()
+                .map(|(idx, &d)| d * G::Scalar::from(idx as u64 + 1) * inverses[x - idx - 1])
+                .collect();
+
+            // We've ignored the sign in the calculations above. The sign is negative iff
+            // threshold `t` is even; indeed, all `t` multiplicands in `key_scale` are negative,
+            // as well as the `1 / (idx - x)` multiplicand in each of `key_denominators`.
+            if params.threshold % 2 == 0 {
+                key_scale = -key_scale;
+            }
+
+            let interpolated_key = G::vartime_multi_mul(&key_denominators, starting_keys.clone());
+            let interpolated_key = interpolated_key * &key_scale;
+            if interpolated_key != key.as_element() {
+                return Err(Error::MalformedParticipantKeys);
+            }
+        }
+
+        Ok(Self {
+            params,
+            shared_key,
+            participant_keys,
+        })
+    }
+
+    /// Returns parameters for this scheme.
+    pub fn params(&self) -> Params {
+        self.params
+    }
+
+    /// Returns the shared public key used in this scheme.
+    pub fn shared_key(&self) -> &PublicKey<G> {
+        &self.shared_key
+    }
+
+    /// Returns the public key of a participant with the specified `index`. If `index` is
+    /// out of bounds, returns `None`.
+    pub fn participant_key(&self, index: usize) -> Option<&PublicKey<G>> {
+        self.participant_keys.get(index)
+    }
+
+    /// Returns the slice with all participants' public keys.
+    pub fn participant_keys(&self) -> &[PublicKey<G>] {
+        &self.participant_keys
+    }
+
+    pub(super) fn commit(&self, transcript: &mut Transcript) {
+        transcript.append_u64(b"n", self.params.shares as u64);
+        transcript.append_u64(b"t", self.params.threshold as u64);
+        transcript.append_element_bytes(b"K", self.shared_key.as_bytes());
+    }
+
+    /// Verifies a proof of possession of the participant's secret key.
+    ///
+    /// Proofs of possession for participants are not required for protocol correctness.
+    /// Still, they can be useful to attribute failures or just as an additional safety mechanism;
+    /// see [the module docs](index.html) for details.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `index` does not correspond to a participant.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the `proof` does not verify.
+    pub fn verify_participant(
+        &self,
+        index: usize,
+        proof: &ProofOfPossession<G>,
+    ) -> Result<(), VerificationError> {
+        let participant_key = self.participant_key(index).unwrap_or_else(|| {
+            panic!(
+                "participant index {index} out of bounds, expected a value in 0..{}",
+                self.participant_keys.len()
+            );
+        });
+        let mut transcript = Transcript::new(b"elgamal_participant_pop");
+        self.commit(&mut transcript);
+        transcript.append_u64(b"i", index as u64);
+        proof.verify(iter::once(participant_key), &mut transcript)
+    }
+
+    /// Verifies a candidate decryption share for `ciphertext` provided by a participant
+    /// with the specified `index`.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the `proof` does not verify.
+    pub fn verify_share(
+        &self,
+        candidate_share: CandidateDecryption<G>,
+        ciphertext: Ciphertext<G>,
+        index: usize,
+        proof: &LogEqualityProof<G>,
+    ) -> Result<VerifiableDecryption<G>, VerificationError> {
+        let key_share = self.participant_keys[index].as_element();
+        let dh_element = candidate_share.dh_element();
+        let mut transcript = Transcript::new(b"elgamal_decryption_share");
+        self.commit(&mut transcript);
+        transcript.append_u64(b"i", index as u64);
+
+        proof.verify(
+            &PublicKey::from_element(ciphertext.random_element),
+            (key_share, dh_element),
+            &mut transcript,
+        )?;
+        Ok(VerifiableDecryption::from_element(dh_element))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::thread_rng;
+
+    use super::*;
+    use crate::{
+        group::{ElementOps, Ristretto},
+        sharing::Dealer,
+    };
+
+    #[test]
+    fn restoring_key_set_from_participant_keys_errors() {
+        let mut rng = thread_rng();
+        let params = Params::new(10, 7);
+
+        let dealer = Dealer::<Ristretto>::new(params, &mut rng);
+        let (public_poly, _) = dealer.public_info();
+        let public_poly = PublicPolynomial::<Ristretto>(public_poly);
+        let participant_keys: Vec<PublicKey<Ristretto>> = (1..=params.shares)
+            .map(|i| PublicKey::from_element(public_poly.value_at((i as u64).into())))
+            .collect();
+
+        // Check that `participant_keys` are computed correctly.
+        PublicKeySet::from_participants(params, participant_keys.clone()).unwrap();
+
+        let err =
+            PublicKeySet::from_participants(params, participant_keys[1..].to_vec()).unwrap_err();
+        assert!(matches!(err, Error::ParticipantCountMismatch));
+
+        // Order of keys matters!
+        let mut bogus_keys = participant_keys.clone();
+        bogus_keys.swap(1, 5);
+        let err = PublicKeySet::from_participants(params, bogus_keys).unwrap_err();
+        assert!(matches!(err, Error::MalformedParticipantKeys));
+
+        for i in 0..params.shares {
+            let mut bogus_keys = participant_keys.clone();
+            bogus_keys[i] =
+                PublicKey::from_element(bogus_keys[i].as_element() + Ristretto::generator());
+            let err = PublicKeySet::from_participants(params, bogus_keys).unwrap_err();
+            assert!(matches!(err, Error::MalformedParticipantKeys));
+        }
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/sharing/mod.rs.html b/src/elastic_elgamal/sharing/mod.rs.html new file mode 100644 index 0000000..c560a65 --- /dev/null +++ b/src/elastic_elgamal/sharing/mod.rs.html @@ -0,0 +1,755 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+
//! [Feldman's verifiable secret sharing][feldman-vss] (VSS) for ElGamal encryption.
+//!
+//! Feldman's VSS is an extension of [Shamir's secret sharing][sss] that provides a degree
+//! of verifiability for the scheme participants and the public. As with other VSS schemes,
+//! the goal is to securely distribute a secret among `n` participants so that the secret can
+//! be recombined by any `t` (but not less) of these participants. Unlike distributed key
+//! generation (DKG), VSS assumes a central authority (a *dealer*) generating the secret
+//! and distributing its shares among participants.
+//!
+//! # Construction
+//!
+//! **Inputs:**
+//!
+//! - Total number of participants `n`
+//! - Minimum number of participants necessary to restore secret `t`
+//! - Prime-order group with discrete log assumption with generator `G`
+//!
+//! **Assumptions:**
+//!
+//! - There is a secure broadcast among participants, which acts as a single source of truth
+//!   (e.g., a blockchain). The broadcast is synchronous w.r.t. the protocol steps (in practice,
+//!   this means that protocol steps take sufficiently long amount of time).
+//! - Secure synchronous P2P channels can be established between the dealer and participants.
+//! - The adversary is static (corrupts parties before protocol instantiation) and can corrupt
+//!   less than a half of participants (including the dealer).
+//!
+//! Feldman's VSS proceeds as follows:
+//!
+//! 1. The dealer generates a secret `x` (a scalar in a group with discrete log assumption).
+//!    Along with this scalar, the dealer generates `t` other scalars that are also kept secret.
+//!    These scalars form a secret polynomial of degree `t`: `P(z) = x + x_1 * z + x_2 * z^2 + …`.
+//! 2. The dealer publishes coefficients `[x]G`, `[x_1]G`, ..., `[x_t]G` of the *public polynomial*
+//!    corresponding to `P`: `Q(z) = [x]G + [z][x_1]G + [z^2][x_2]G + …`. Here, `[x]G` is the shared
+//!    public key, and values `Q(i)` at `i = 1..=n` are public key shares of participants.
+//! 3. The dealer distributes secret key shares `s_i = P(i)` among participants `i = 1..=n`
+//!    via secure P2P channels. Each participant can verify share validity by calculating
+//!    `[s_i]G ?= Q(i)`.
+//!
+//! If a participant receives an incorrect secret share, the participant broadcasts a *complaint*
+//! against the dealer. The dealer responds by broadcasting the participant's share. Either the
+//! share is correct (in which case the complaining participant is at fault), or it is not
+//! (in which case the dealer is at fault).
+//!
+//! To improve auditability, the implemented version of VSS provides zero-knowledge proofs
+//! of possession both for the dealer and participants. The dealer must broadcast the public
+//! polynomial together with the proof; participants should broadcast proof of knowledge of
+//! a secret share once they receive the share from the dealer.
+//!
+//! # Distributed key generation
+//!
+//! Distributed key generation (DKG) differs from the approach implemented in this module
+//! in that there is no centralized dealer trusted by all participants. Instead, the participants
+//! essentially run parallel secret sharing protocol instances where  each participant
+//! is a dealer in one of the instances. This approach is implemented
+//! in the [`dkg`](crate::dkg) module of this crate. Beware that it may not protect
+//! from participants biasing the distribution of the shared public key, e.g. by aborting
+//! the protocol; see [Gennaro et al.] for more details.
+//!
+//! [sss]: https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing
+//! [feldman-vss]: https://www.cs.umd.edu/~gasarch/TOPICS/secretsharing/feldmanVSS.pdf
+//! [Gennaro et al.]: https://link.springer.com/content/pdf/10.1007/3-540-48910-X_21.pdf
+//!
+//! # Examples
+//!
+//! Threshold encryption scheme requiring 2 of 3 participants.
+//!
+//! ```
+//! # use elastic_elgamal::{
+//! #     group::Ristretto, sharing::*, CandidateDecryption, Ciphertext, DiscreteLogTable,
+//! # };
+//! # use rand::thread_rng;
+//! # use std::error::Error as StdError;
+//! # fn main() -> Result<(), Box<dyn StdError>> {
+//! let mut rng = thread_rng();
+//! let params = Params::new(3, 2);
+//!
+//! // Initialize the dealer.
+//! let dealer = Dealer::<Ristretto>::new(params, &mut rng);
+//! let (public_poly, poly_proof) = dealer.public_info();
+//! let key_set = PublicKeySet::new(params, public_poly, poly_proof)?;
+//!
+//! // Initialize participants based on secret shares provided by the dealer.
+//! let participants = (0..3)
+//!     .map(|i| ActiveParticipant::new(
+//!         key_set.clone(),
+//!         i,
+//!         dealer.secret_share_for_participant(i),
+//!     ))
+//!     .collect::<Result<Vec<_>, _>>()?;
+//!
+//! // At last, participants can decrypt messages!
+//! let encrypted_value = 5_u64;
+//! let enc = key_set.shared_key().encrypt(encrypted_value, &mut rng);
+//! let shares_with_proofs = participants
+//!     .iter()
+//!     .map(|p| p.decrypt_share(enc, &mut rng))
+//!     .take(2); // emulate the 3rd participant dropping off
+//!
+//! // Emulate share transfer via untrusted network.
+//! let dec_shares = shares_with_proofs
+//!     .enumerate()
+//!     .map(|(i, (share, proof))| {
+//!         let share = CandidateDecryption::from_bytes(&share.to_bytes()).unwrap();
+//!         key_set.verify_share(share, enc, i, &proof).unwrap()
+//!     });
+//!
+//! // Combine decryption shares.
+//! let combined = params.combine_shares(dec_shares.enumerate()).unwrap();
+//! // Use a lookup table to decrypt back to scalar.
+//! let lookup_table = DiscreteLogTable::<Ristretto>::new(0..10);
+//! let dec = combined.decrypt(enc, &lookup_table);
+//! assert_eq!(dec, Some(encrypted_value));
+//! # Ok(())
+//! # }
+//! ```
+
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+#[cfg(feature = "serde")]
+use crate::serde::{ElementHelper, VecHelper};
+
+use core::{cmp::Ordering, fmt, ops};
+
+use crate::{alloc::Vec, group::Group, proofs::VerificationError, VerifiableDecryption};
+
+mod key_set;
+mod participant;
+
+pub use self::{
+    key_set::PublicKeySet,
+    participant::{ActiveParticipant, Dealer},
+};
+
+/// Computes multipliers for the Lagrange polynomial interpolation based on the function value
+/// at the given points. The indexes are zero-based, hence points are determined as
+/// `indexes.iter().map(|&i| i + 1)`.
+///
+/// The returned scalars need to be additionally scaled by the common multiplier, equal
+/// to the product of all points, which is returned as the second value.
+fn lagrange_coefficients<G: Group>(indexes: &[usize]) -> (Vec<G::Scalar>, G::Scalar) {
+    // `false` corresponds to positive sign, `true` to negative. This is in order
+    // to make XOR work as expected.
+
+    let mut denominators: Vec<_> = indexes
+        .iter()
+        .map(|&index| {
+            let (sign, denominator) = indexes
+                .iter()
+                .map(|&other_index| match index.cmp(&other_index) {
+                    Ordering::Greater => (true, G::Scalar::from((index - other_index) as u64)),
+                    Ordering::Less => (false, G::Scalar::from((other_index - index) as u64)),
+                    Ordering::Equal => (false, G::Scalar::from(index as u64 + 1)),
+                })
+                .fold(
+                    (false, G::Scalar::from(1)),
+                    |(sign, magnitude), (elem_sign, elem_magnitude)| {
+                        (sign ^ elem_sign, magnitude * elem_magnitude)
+                    },
+                );
+
+            if sign {
+                -denominator
+            } else {
+                denominator
+            }
+        })
+        .collect();
+    G::invert_scalars(&mut denominators);
+
+    let scale = indexes
+        .iter()
+        .map(|&index| G::Scalar::from(index as u64 + 1))
+        .fold(G::Scalar::from(1), |acc, value| acc * value);
+    (denominators, scale)
+}
+
+/// Structure representing public polynomial consisting of group elements.
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(transparent, bound = ""))]
+pub(crate) struct PublicPolynomial<G: Group>(
+    #[cfg_attr(feature = "serde", serde(with = "VecHelper::<ElementHelper<G>, 1>"))]
+    Vec<G::Element>,
+);
+
+impl<G: Group> PublicPolynomial<G> {
+    pub(crate) fn new(values: Vec<G::Element>) -> Self {
+        Self(values)
+    }
+
+    fn value_at_zero(&self) -> G::Element {
+        self.0[0]
+    }
+
+    /// Computes value of this public polynomial at the specified point in variable time.
+    pub(crate) fn value_at(&self, x: G::Scalar) -> G::Element {
+        let mut val = G::Scalar::from(1_u64);
+        let scalars: Vec<_> = (0..self.0.len())
+            .map(|_| {
+                let output = val;
+                val = val * x;
+                output
+            })
+            .collect();
+
+        G::vartime_multi_mul(&scalars, self.0.iter().copied())
+    }
+}
+
+impl<G: Group> ops::AddAssign<&Self> for PublicPolynomial<G> {
+    fn add_assign(&mut self, rhs: &Self) {
+        debug_assert_eq!(
+            self.0.len(),
+            rhs.0.len(),
+            "cannot add polynomials of different degrees"
+        );
+
+        for (val, &rhs_val) in self.0.iter_mut().zip(&rhs.0) {
+            *val = *val + rhs_val;
+        }
+    }
+}
+
+/// Errors that can occur during the secret sharing protocol.
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum Error {
+    /// Public polynomial received from the dealer is malformed.
+    MalformedDealerPolynomial,
+    /// Proof of possession supplied with the dealer's public polynomial is invalid.
+    InvalidDealerProof(VerificationError),
+    /// Secret received from the dealer does not correspond to their commitment via
+    /// the public polynomial.
+    InvalidSecret,
+    /// Number of participants specified in [`Params`] does not match the number
+    /// of provided public keys.
+    ParticipantCountMismatch,
+    /// Participants' public keys do not correspond to a single shared key.
+    MalformedParticipantKeys,
+}
+
+impl fmt::Display for Error {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::MalformedDealerPolynomial => {
+                formatter.write_str("public polynomial received from the dealer is malformed")
+            }
+            Self::InvalidDealerProof(err) => write!(
+                formatter,
+                "proof of possession supplied with the dealer's public polynomial \
+                 is invalid: {err}"
+            ),
+            Self::InvalidSecret => formatter.write_str(
+                "secret received from the dealer does not correspond to their commitment via \
+                 public polynomial",
+            ),
+            Self::ParticipantCountMismatch => formatter.write_str(
+                "number of participants specified in `Params` does not match the number \
+                 of provided public keys",
+            ),
+            Self::MalformedParticipantKeys => formatter
+                .write_str("participants' public keys do not correspond to a single shared key"),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for Error {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        match self {
+            Self::InvalidDealerProof(err) => Some(err),
+            _ => None,
+        }
+    }
+}
+
+/// Parameters of a threshold ElGamal encryption scheme.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub struct Params {
+    /// Total number of shares / participants.
+    pub shares: usize,
+    /// Number of participants necessary to jointly restore the secret.
+    pub threshold: usize,
+}
+
+impl Params {
+    /// Creates new parameters.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `shares` is equal to zero or if `threshold` is not in `1..=shares`.
+    pub const fn new(shares: usize, threshold: usize) -> Self {
+        assert!(shares > 0);
+        assert!(threshold > 0 && threshold <= shares);
+        Self { shares, threshold }
+    }
+
+    /// Combines shares decrypting the specified `ciphertext`. The shares must be provided
+    /// together with the 0-based indexes of the participants they are coming from.
+    ///
+    /// Returns the combined decryption, or `None` if the number of shares is insufficient.
+    ///
+    /// # Panics
+    ///
+    /// Panics if any index in `shares` exceeds the maximum participant's index as per `params`.
+    pub fn combine_shares<G: Group>(
+        self,
+        shares: impl IntoIterator<Item = (usize, VerifiableDecryption<G>)>,
+    ) -> Option<VerifiableDecryption<G>> {
+        let (indexes, shares): (Vec<_>, Vec<_>) = shares
+            .into_iter()
+            .take(self.threshold)
+            .map(|(index, share)| (index, *share.as_element()))
+            .unzip();
+        if shares.len() < self.threshold {
+            return None;
+        }
+        assert!(
+            indexes.iter().all(|&index| index < self.shares),
+            "Invalid share indexes {:?}; expected values in 0..{}",
+            indexes.iter().copied(),
+            self.shares
+        );
+
+        let (denominators, scale) = lagrange_coefficients::<G>(&indexes);
+        let restored_value = G::vartime_multi_mul(&denominators, shares);
+        let dh_element = restored_value * &scale;
+        Some(VerifiableDecryption::from_element(dh_element))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{curve25519::scalar::Scalar as Scalar25519, group::Ristretto};
+
+    #[test]
+    fn lagrange_coeffs_are_computed_correctly() {
+        // d_0 = 2 / (2 - 1) = 2
+        // d_1 = 1 / (1 - 2) = -1
+        let (coeffs, scale) = lagrange_coefficients::<Ristretto>(&[0, 1]);
+        assert_eq!(
+            coeffs,
+            [Scalar25519::from(1_u32), -Scalar25519::from(2_u32).invert()]
+        );
+        assert_eq!(scale, Scalar25519::from(2_u32));
+
+        // d_0 = 3 / (3 - 1) = 3/2
+        // d_1 = 1 / (1 - 3) = -1/2
+        let (coeffs, scale) = lagrange_coefficients::<Ristretto>(&[0, 2]);
+        assert_eq!(
+            coeffs,
+            [
+                Scalar25519::from(2_u32).invert(),
+                -Scalar25519::from(6_u32).invert(),
+            ]
+        );
+        assert_eq!(scale, Scalar25519::from(3_u32));
+
+        // d_0 = 4 * 5 / (4 - 1) * (5 - 1) = 20/12 = 5/3
+        // d_1 = 1 * 5 / (1 - 4) * (5 - 4) = -5/3
+        // d_2 = 1 * 4 / (1 - 5) * (4 - 5) = 4/4 = 1
+        let (coeffs, scale) = lagrange_coefficients::<Ristretto>(&[0, 3, 4]);
+        assert_eq!(
+            coeffs,
+            [
+                Scalar25519::from(12_u32).invert(),
+                -Scalar25519::from(12_u32).invert(),
+                Scalar25519::from(20_u32).invert(),
+            ]
+        );
+        assert_eq!(scale, Scalar25519::from(20_u32));
+    }
+}
+
\ No newline at end of file diff --git a/src/elastic_elgamal/sharing/participant.rs.html b/src/elastic_elgamal/sharing/participant.rs.html new file mode 100644 index 0000000..b04d74f --- /dev/null +++ b/src/elastic_elgamal/sharing/participant.rs.html @@ -0,0 +1,489 @@ +participant.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+
//! Types representing participant state.
+
+// TODO: Use a publicly verifiable scheme, e.g. Schoenmakers?
+// https://www.win.tue.nl/~berry/papers/crypto99.pdf
+
+use merlin::Transcript;
+use rand_core::{CryptoRng, RngCore};
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
+
+use core::iter;
+
+use crate::{
+    alloc::Vec,
+    group::Group,
+    proofs::{LogEqualityProof, ProofOfPossession},
+    sharing::{Error, Params, PublicKeySet},
+    Ciphertext, Keypair, PublicKey, SecretKey, VerifiableDecryption,
+};
+
+/// Dealer in a [Feldman verifiable secret sharing][feldman-vss] scheme.
+///
+/// [feldman-vss]: https://www.cs.umd.edu/~gasarch/TOPICS/secretsharing/feldmanVSS.pdf
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct Dealer<G: Group> {
+    params: Params,
+    polynomial: Vec<Keypair<G>>,
+    proof_of_possession: ProofOfPossession<G>,
+}
+
+impl<G: Group> Dealer<G> {
+    /// Instantiates a dealer.
+    pub fn new<R: CryptoRng + RngCore>(params: Params, rng: &mut R) -> Self {
+        let polynomial: Vec<_> = (0..params.threshold)
+            .map(|_| Keypair::<G>::generate(rng))
+            .collect();
+
+        let mut transcript = Transcript::new(b"elgamal_share_poly");
+        transcript.append_u64(b"n", params.shares as u64);
+        transcript.append_u64(b"t", params.threshold as u64);
+
+        let proof_of_possession = ProofOfPossession::new(&polynomial, &mut transcript, rng);
+
+        Self {
+            params,
+            polynomial,
+            proof_of_possession,
+        }
+    }
+
+    /// Returns public participant information: dealer's public polynomial and proof
+    /// of possession for the corresponding secret polynomial.
+    pub fn public_info(&self) -> (Vec<G::Element>, &ProofOfPossession<G>) {
+        let public_polynomial = self
+            .polynomial
+            .iter()
+            .map(|pair| pair.public().as_element())
+            .collect();
+        (public_polynomial, &self.proof_of_possession)
+    }
+
+    /// Returns a secret share for a participant with the specified `index`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `index` is out of allowed bounds.
+    pub fn secret_share_for_participant(&self, index: usize) -> SecretKey<G> {
+        assert!(
+            index < self.params.shares,
+            "participant index {index} out of bounds, expected a value in 0..{}",
+            self.params.shares
+        );
+
+        let power = G::Scalar::from(index as u64 + 1);
+        let mut poly_value = SecretKey::new(G::Scalar::from(0));
+        for keypair in self.polynomial.iter().rev() {
+            poly_value = poly_value * &power + keypair.secret().clone();
+        }
+        poly_value
+    }
+}
+
+/// Personalized state of a participant of a threshold ElGamal encryption scheme
+/// once the participant receives the secret share from the [`Dealer`].
+/// At this point, the participant can produce [`VerifiableDecryption`]s.
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "serde", serde(bound = ""))]
+pub struct ActiveParticipant<G: Group> {
+    key_set: PublicKeySet<G>,
+    index: usize,
+    secret_share: SecretKey<G>,
+}
+
+impl<G: Group> ActiveParticipant<G> {
+    /// Creates the participant state based on readily available components.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if `secret_share` does not correspond to the participant's public key share
+    /// in `key_set`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `index` is greater or equal than the number of participants in `key_set`.
+    pub fn new(
+        key_set: PublicKeySet<G>,
+        index: usize,
+        secret_share: SecretKey<G>,
+    ) -> Result<Self, Error> {
+        let expected_element = key_set.participant_keys()[index].as_element();
+        if G::mul_generator(secret_share.expose_scalar()) == expected_element {
+            Ok(Self {
+                key_set,
+                index,
+                secret_share,
+            })
+        } else {
+            Err(Error::InvalidSecret)
+        }
+    }
+
+    /// Returns the public key set for the threshold ElGamal encryption scheme this participant
+    /// is a part of.
+    pub fn key_set(&self) -> &PublicKeySet<G> {
+        &self.key_set
+    }
+
+    /// Returns 0-based index of this participant.
+    pub fn index(&self) -> usize {
+        self.index
+    }
+
+    /// Returns share of the secret key for this participant. This is secret information that
+    /// must not be shared.
+    pub fn secret_share(&self) -> &SecretKey<G> {
+        &self.secret_share
+    }
+
+    /// Returns share of the public key for this participant.
+    pub fn public_key_share(&self) -> &PublicKey<G> {
+        &self.key_set.participant_keys()[self.index]
+    }
+
+    /// Generates a [`ProofOfPossession`] of the participant's
+    /// [`secret_share`](Self::secret_share()).
+    pub fn proof_of_possession<R: CryptoRng + RngCore>(&self, rng: &mut R) -> ProofOfPossession<G> {
+        let mut transcript = Transcript::new(b"elgamal_participant_pop");
+        self.key_set.commit(&mut transcript);
+        transcript.append_u64(b"i", self.index as u64);
+        ProofOfPossession::from_keys(
+            iter::once(&self.secret_share),
+            iter::once(self.public_key_share()),
+            &mut transcript,
+            rng,
+        )
+    }
+
+    /// Creates a [`VerifiableDecryption`] for the specified `ciphertext` together with a proof
+    /// of its validity. `rng` is used to generate the proof.
+    pub fn decrypt_share<R>(
+        &self,
+        ciphertext: Ciphertext<G>,
+        rng: &mut R,
+    ) -> (VerifiableDecryption<G>, LogEqualityProof<G>)
+    where
+        R: CryptoRng + RngCore,
+    {
+        let dh_element = ciphertext.random_element * self.secret_share.expose_scalar();
+        let our_public_key = self.public_key_share().as_element();
+        let mut transcript = Transcript::new(b"elgamal_decryption_share");
+        self.key_set.commit(&mut transcript);
+        transcript.append_u64(b"i", self.index as u64);
+
+        let proof = LogEqualityProof::new(
+            &PublicKey::from_element(ciphertext.random_element),
+            &self.secret_share,
+            (our_public_key, dh_element),
+            &mut transcript,
+            rng,
+        );
+        (VerifiableDecryption::from_element(dh_element), proof)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::thread_rng;
+
+    use super::*;
+    use crate::{curve25519::scalar::Scalar as Scalar25519, group::Ristretto};
+
+    #[test]
+    fn shared_2_of_3_key() {
+        let mut rng = thread_rng();
+        let params = Params::new(3, 2);
+
+        let dealer = Dealer::<Ristretto>::new(params, &mut rng);
+        let (public_poly, public_poly_proof) = dealer.public_info();
+        let key_set = PublicKeySet::new(params, public_poly, public_poly_proof).unwrap();
+
+        let alice_share = dealer.secret_share_for_participant(0);
+        let alice = ActiveParticipant::new(key_set.clone(), 0, alice_share).unwrap();
+        let bob_share = dealer.secret_share_for_participant(1);
+        let bob = ActiveParticipant::new(key_set.clone(), 1, bob_share).unwrap();
+        let carol_share = dealer.secret_share_for_participant(2);
+        let carol = ActiveParticipant::new(key_set.clone(), 2, carol_share).unwrap();
+
+        key_set
+            .verify_participant(0, &alice.proof_of_possession(&mut rng))
+            .unwrap();
+        key_set
+            .verify_participant(1, &bob.proof_of_possession(&mut rng))
+            .unwrap();
+        key_set
+            .verify_participant(2, &carol.proof_of_possession(&mut rng))
+            .unwrap();
+        assert!(key_set
+            .verify_participant(1, &alice.proof_of_possession(&mut rng))
+            .is_err());
+
+        let ciphertext = key_set.shared_key().encrypt(15_u64, &mut rng);
+        let (alice_share, proof) = alice.decrypt_share(ciphertext, &mut rng);
+        key_set
+            .verify_share(alice_share.into(), ciphertext, 0, &proof)
+            .unwrap();
+
+        let (bob_share, proof) = bob.decrypt_share(ciphertext, &mut rng);
+        key_set
+            .verify_share(bob_share.into(), ciphertext, 1, &proof)
+            .unwrap();
+
+        // We need to find `a0` from the following equations:
+        // a0 +   a1 = alice_share.dh_element;
+        // a0 + 2*a1 = bob_share.dh_element;
+        let composite_dh_element =
+            *alice_share.as_element() * Scalar25519::from(2_u64) - *bob_share.as_element();
+        let message = Ristretto::mul_generator(&Scalar25519::from(15_u64));
+        assert_eq!(composite_dh_element, ciphertext.blinded_element - message);
+    }
+}
+
\ No newline at end of file diff --git a/static.files/COPYRIGHT-23e9bde6c69aea69.txt b/static.files/COPYRIGHT-23e9bde6c69aea69.txt new file mode 100644 index 0000000..1447df7 --- /dev/null +++ b/static.files/COPYRIGHT-23e9bde6c69aea69.txt @@ -0,0 +1,50 @@ +# REUSE-IgnoreStart + +These documentation pages include resources by third parties. This copyright +file applies only to those resources. The following third party resources are +included, and carry their own copyright notices and license terms: + +* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2): + + Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ + with Reserved Font Name Fira Sans. + + Copyright (c) 2014, Telefonica S.A. + + Licensed under the SIL Open Font License, Version 1.1. + See FiraSans-LICENSE.txt. + +* rustdoc.css, main.js, and playpen.js: + + Copyright 2015 The Rust Developers. + Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or + the MIT license (LICENSE-MIT.txt) at your option. + +* normalize.css: + + Copyright (c) Nicolas Gallagher and Jonathan Neal. + Licensed under the MIT license (see LICENSE-MIT.txt). + +* Source Code Pro (SourceCodePro-Regular.ttf.woff2, + SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2): + + Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), + with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark + of Adobe Systems Incorporated in the United States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceCodePro-LICENSE.txt. + +* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2, + SourceSerif4-It.ttf.woff2): + + Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name + 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United + States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceSerif4-LICENSE.md. + +This copyright file is intended to be distributed with rustdoc output. + +# REUSE-IgnoreEnd diff --git a/static.files/FiraSans-LICENSE-db4b642586e02d97.txt b/static.files/FiraSans-LICENSE-db4b642586e02d97.txt new file mode 100644 index 0000000..d7e9c14 --- /dev/null +++ b/static.files/FiraSans-LICENSE-db4b642586e02d97.txt @@ -0,0 +1,98 @@ +// REUSE-IgnoreStart + +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. +with Reserved Font Name < Fira >, + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 b/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 new file mode 100644 index 0000000..7a1e5fc Binary files /dev/null and b/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 differ diff --git a/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 b/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 new file mode 100644 index 0000000..e766e06 Binary files /dev/null and b/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 differ diff --git a/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt b/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/static.files/LICENSE-MIT-65090b722b3f6c56.txt b/static.files/LICENSE-MIT-65090b722b3f6c56.txt new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/static.files/LICENSE-MIT-65090b722b3f6c56.txt @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 b/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 new file mode 100644 index 0000000..1866ad4 Binary files /dev/null and b/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 differ diff --git a/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt b/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt new file mode 100644 index 0000000..4b3edc2 --- /dev/null +++ b/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt @@ -0,0 +1,103 @@ +// REUSE-IgnoreStart + +Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/), + +with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, +NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, +Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, +NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, +Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 b/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 new file mode 100644 index 0000000..462c34e Binary files /dev/null and b/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 differ diff --git a/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt b/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt new file mode 100644 index 0000000..0d2941e --- /dev/null +++ b/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt @@ -0,0 +1,97 @@ +// REUSE-IgnoreStart + +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 b/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 new file mode 100644 index 0000000..10b558e Binary files /dev/null and b/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 differ diff --git a/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 b/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 new file mode 100644 index 0000000..5ec64ee Binary files /dev/null and b/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 differ diff --git a/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 b/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 new file mode 100644 index 0000000..181a07f Binary files /dev/null and b/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 differ diff --git a/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 b/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 new file mode 100644 index 0000000..2ae08a7 Binary files /dev/null and b/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 differ diff --git a/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md b/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md new file mode 100644 index 0000000..175fa4f --- /dev/null +++ b/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md @@ -0,0 +1,98 @@ + + +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. +Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + + diff --git a/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 b/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 new file mode 100644 index 0000000..0263fc3 Binary files /dev/null and b/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 differ diff --git a/static.files/clipboard-7571035ce49a181d.svg b/static.files/clipboard-7571035ce49a181d.svg new file mode 100644 index 0000000..8adbd99 --- /dev/null +++ b/static.files/clipboard-7571035ce49a181d.svg @@ -0,0 +1 @@ + diff --git a/static.files/favicon-16x16-8b506e7a72182f1c.png b/static.files/favicon-16x16-8b506e7a72182f1c.png new file mode 100644 index 0000000..ea4b45c Binary files /dev/null and b/static.files/favicon-16x16-8b506e7a72182f1c.png differ diff --git a/static.files/favicon-2c020d218678b618.svg b/static.files/favicon-2c020d218678b618.svg new file mode 100644 index 0000000..8b34b51 --- /dev/null +++ b/static.files/favicon-2c020d218678b618.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/static.files/favicon-32x32-422f7d1d52889060.png b/static.files/favicon-32x32-422f7d1d52889060.png new file mode 100644 index 0000000..69b8613 Binary files /dev/null and b/static.files/favicon-32x32-422f7d1d52889060.png differ diff --git a/static.files/main-12cf3b4f4f9dc36d.js b/static.files/main-12cf3b4f4f9dc36d.js new file mode 100644 index 0000000..1d8d1cc --- /dev/null +++ b/static.files/main-12cf3b4f4f9dc36d.js @@ -0,0 +1,11 @@ +"use strict";window.RUSTDOC_TOOLTIP_HOVER_MS=300;window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS=450;function resourcePath(basename,extension){return getVar("root-path")+basename+getVar("resource-suffix")+extension}function hideMain(){addClass(document.getElementById(MAIN_ID),"hidden")}function showMain(){removeClass(document.getElementById(MAIN_ID),"hidden")}function blurHandler(event,parentElem,hideCallback){if(!parentElem.contains(document.activeElement)&&!parentElem.contains(event.relatedTarget)){hideCallback()}}window.rootPath=getVar("root-path");window.currentCrate=getVar("current-crate");function setMobileTopbar(){const mobileTopbar=document.querySelector(".mobile-topbar");const locationTitle=document.querySelector(".sidebar h2.location");if(mobileTopbar){const mobileTitle=document.createElement("h2");mobileTitle.className="location";if(hasClass(document.querySelector(".rustdoc"),"crate")){mobileTitle.innerHTML=`Crate ${window.currentCrate}`}else if(locationTitle){mobileTitle.innerHTML=locationTitle.innerHTML}mobileTopbar.appendChild(mobileTitle)}}function getVirtualKey(ev){if("key"in ev&&typeof ev.key!=="undefined"){return ev.key}const c=ev.charCode||ev.keyCode;if(c===27){return"Escape"}return String.fromCharCode(c)}const MAIN_ID="main-content";const SETTINGS_BUTTON_ID="settings-menu";const ALTERNATIVE_DISPLAY_ID="alternative-display";const NOT_DISPLAYED_ID="not-displayed";const HELP_BUTTON_ID="help-button";function getSettingsButton(){return document.getElementById(SETTINGS_BUTTON_ID)}function getHelpButton(){return document.getElementById(HELP_BUTTON_ID)}function getNakedUrl(){return window.location.href.split("?")[0].split("#")[0]}function insertAfter(newNode,referenceNode){referenceNode.parentNode.insertBefore(newNode,referenceNode.nextSibling)}function getOrCreateSection(id,classes){let el=document.getElementById(id);if(!el){el=document.createElement("section");el.id=id;el.className=classes;insertAfter(el,document.getElementById(MAIN_ID))}return el}function getAlternativeDisplayElem(){return getOrCreateSection(ALTERNATIVE_DISPLAY_ID,"content hidden")}function getNotDisplayedElem(){return getOrCreateSection(NOT_DISPLAYED_ID,"hidden")}function switchDisplayedElement(elemToDisplay){const el=getAlternativeDisplayElem();if(el.children.length>0){getNotDisplayedElem().appendChild(el.firstElementChild)}if(elemToDisplay===null){addClass(el,"hidden");showMain();return}el.appendChild(elemToDisplay);hideMain();removeClass(el,"hidden")}function browserSupportsHistoryApi(){return window.history&&typeof window.history.pushState==="function"}function preLoadCss(cssUrl){const link=document.createElement("link");link.href=cssUrl;link.rel="preload";link.as="style";document.getElementsByTagName("head")[0].appendChild(link)}(function(){const isHelpPage=window.location.pathname.endsWith("/help.html");function loadScript(url,errorCallback){const script=document.createElement("script");script.src=url;if(errorCallback!==undefined){script.onerror=errorCallback}document.head.append(script)}getSettingsButton().onclick=event=>{if(event.ctrlKey||event.altKey||event.metaKey){return}window.hideAllModals(false);addClass(getSettingsButton(),"rotate");event.preventDefault();loadScript(getVar("static-root-path")+getVar("settings-js"));setTimeout(()=>{const themes=getVar("themes").split(",");for(const theme of themes){if(theme!==""){preLoadCss(getVar("root-path")+theme+".css")}}},0)};window.searchState={loadingText:"Loading search results...",input:document.getElementsByClassName("search-input")[0],outputElement:()=>{let el=document.getElementById("search");if(!el){el=document.createElement("section");el.id="search";getNotDisplayedElem().appendChild(el)}return el},title:document.title,titleBeforeSearch:document.title,timeout:null,currentTab:0,focusedByTab:[null,null,null],clearInputTimeout:()=>{if(searchState.timeout!==null){clearTimeout(searchState.timeout);searchState.timeout=null}},isDisplayed:()=>searchState.outputElement().parentElement.id===ALTERNATIVE_DISPLAY_ID,focus:()=>{searchState.input.focus()},defocus:()=>{searchState.input.blur()},showResults:search=>{if(search===null||typeof search==="undefined"){search=searchState.outputElement()}switchDisplayedElement(search);searchState.mouseMovedAfterSearch=false;document.title=searchState.title},removeQueryParameters:()=>{document.title=searchState.titleBeforeSearch;if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.hash)}},hideResults:()=>{switchDisplayedElement(null);searchState.removeQueryParameters()},getQueryStringParams:()=>{const params={};window.location.search.substring(1).split("&").map(s=>{const pair=s.split("=").map(x=>x.replace(/\+/g," "));params[decodeURIComponent(pair[0])]=typeof pair[1]==="undefined"?null:decodeURIComponent(pair[1])});return params},setup:()=>{const search_input=searchState.input;if(!searchState.input){return}let searchLoaded=false;function sendSearchForm(){document.getElementsByClassName("search-form")[0].submit()}function loadSearch(){if(!searchLoaded){searchLoaded=true;loadScript(getVar("static-root-path")+getVar("search-js"),sendSearchForm);loadScript(resourcePath("search-index",".js"),sendSearchForm)}}search_input.addEventListener("focus",()=>{search_input.origPlaceholder=search_input.placeholder;search_input.placeholder="Type your search here.";loadSearch()});if(search_input.value!==""){loadSearch()}const params=searchState.getQueryStringParams();if(params.search!==undefined){searchState.setLoadingSearch();loadSearch()}},setLoadingSearch:()=>{const search=searchState.outputElement();search.innerHTML="

"+searchState.loadingText+"

";searchState.showResults(search)},};const toggleAllDocsId="toggle-all-docs";let savedHash="";function handleHashes(ev){if(ev!==null&&searchState.isDisplayed()&&ev.newURL){switchDisplayedElement(null);const hash=ev.newURL.slice(ev.newURL.indexOf("#")+1);if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.search+"#"+hash)}const elem=document.getElementById(hash);if(elem){elem.scrollIntoView()}}const pageId=window.location.hash.replace(/^#/,"");if(savedHash!==pageId){savedHash=pageId;if(pageId!==""){expandSection(pageId)}}if(savedHash.startsWith("impl-")){const splitAt=savedHash.indexOf("/");if(splitAt!==-1){const implId=savedHash.slice(0,splitAt);const assocId=savedHash.slice(splitAt+1);const implElem=document.getElementById(implId);if(implElem&&implElem.parentElement.tagName==="SUMMARY"&&implElem.parentElement.parentElement.tagName==="DETAILS"){onEachLazy(implElem.parentElement.parentElement.querySelectorAll(`[id^="${assocId}"]`),item=>{const numbered=/([^-]+)-([0-9]+)/.exec(item.id);if(item.id===assocId||(numbered&&numbered[1]===assocId)){openParentDetails(item);item.scrollIntoView();setTimeout(()=>{window.location.replace("#"+item.id)},0)}})}}}}function onHashChange(ev){hideSidebar();handleHashes(ev)}function openParentDetails(elem){while(elem){if(elem.tagName==="DETAILS"){elem.open=true}elem=elem.parentNode}}function expandSection(id){openParentDetails(document.getElementById(id))}function handleEscape(ev){searchState.clearInputTimeout();searchState.hideResults();ev.preventDefault();searchState.defocus();window.hideAllModals(true)}function handleShortcut(ev){const disableShortcuts=getSettingValue("disable-shortcuts")==="true";if(ev.ctrlKey||ev.altKey||ev.metaKey||disableShortcuts){return}if(document.activeElement.tagName==="INPUT"&&document.activeElement.type!=="checkbox"&&document.activeElement.type!=="radio"){switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break}}else{switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break;case"s":case"S":ev.preventDefault();searchState.focus();break;case"+":ev.preventDefault();expandAllDocs();break;case"-":ev.preventDefault();collapseAllDocs();break;case"?":showHelp();break;default:break}}}document.addEventListener("keypress",handleShortcut);document.addEventListener("keydown",handleShortcut);function addSidebarItems(){if(!window.SIDEBAR_ITEMS){return}const sidebar=document.getElementsByClassName("sidebar-elems")[0];function block(shortty,id,longty){const filtered=window.SIDEBAR_ITEMS[shortty];if(!filtered){return}const modpath=hasClass(document.querySelector(".rustdoc"),"mod")?"../":"";const h3=document.createElement("h3");h3.innerHTML=`${longty}`;const ul=document.createElement("ul");ul.className="block "+shortty;for(const name of filtered){let path;if(shortty==="mod"){path=`${modpath}${name}/index.html`}else{path=`${modpath}${shortty}.${name}.html`}let current_page=document.location.href.toString();if(current_page.endsWith("/")){current_page+="index.html"}const link=document.createElement("a");link.href=path;if(path===current_page){link.className="current"}link.textContent=name;const li=document.createElement("li");li.appendChild(link);ul.appendChild(li)}sidebar.appendChild(h3);sidebar.appendChild(ul)}if(sidebar){block("primitive","primitives","Primitive Types");block("mod","modules","Modules");block("macro","macros","Macros");block("struct","structs","Structs");block("enum","enums","Enums");block("constant","constants","Constants");block("static","static","Statics");block("trait","traits","Traits");block("fn","functions","Functions");block("type","types","Type Aliases");block("union","unions","Unions");block("foreigntype","foreign-types","Foreign Types");block("keyword","keywords","Keywords");block("opaque","opaque-types","Opaque Types");block("attr","attributes","Attribute Macros");block("derive","derives","Derive Macros");block("traitalias","trait-aliases","Trait Aliases")}}window.register_implementors=imp=>{const implementors=document.getElementById("implementors-list");const synthetic_implementors=document.getElementById("synthetic-implementors-list");const inlined_types=new Set();const TEXT_IDX=0;const SYNTHETIC_IDX=1;const TYPES_IDX=2;if(synthetic_implementors){onEachLazy(synthetic_implementors.getElementsByClassName("impl"),el=>{const aliases=el.getAttribute("data-aliases");if(!aliases){return}aliases.split(",").forEach(alias=>{inlined_types.add(alias)})})}let currentNbImpls=implementors.getElementsByClassName("impl").length;const traitName=document.querySelector(".main-heading h1 > .trait").textContent;const baseIdName="impl-"+traitName+"-";const libs=Object.getOwnPropertyNames(imp);const script=document.querySelector("script[data-ignore-extern-crates]");const ignoreExternCrates=new Set((script?script.getAttribute("data-ignore-extern-crates"):"").split(","));for(const lib of libs){if(lib===window.currentCrate||ignoreExternCrates.has(lib)){continue}const structs=imp[lib];struct_loop:for(const struct of structs){const list=struct[SYNTHETIC_IDX]?synthetic_implementors:implementors;if(struct[SYNTHETIC_IDX]){for(const struct_type of struct[TYPES_IDX]){if(inlined_types.has(struct_type)){continue struct_loop}inlined_types.add(struct_type)}}const code=document.createElement("h3");code.innerHTML=struct[TEXT_IDX];addClass(code,"code-header");onEachLazy(code.getElementsByTagName("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});const currentId=baseIdName+currentNbImpls;const anchor=document.createElement("a");anchor.href="#"+currentId;addClass(anchor,"anchor");const display=document.createElement("div");display.id=currentId;addClass(display,"impl");display.appendChild(anchor);display.appendChild(code);list.appendChild(display);currentNbImpls+=1}}};if(window.pending_implementors){window.register_implementors(window.pending_implementors)}window.register_type_impls=imp=>{if(!imp||!imp[window.currentCrate]){return}window.pending_type_impls=null;const idMap=new Map();let implementations=document.getElementById("implementations-list");let trait_implementations=document.getElementById("trait-implementations-list");let trait_implementations_header=document.getElementById("trait-implementations");const script=document.querySelector("script[data-self-path]");const selfPath=script?script.getAttribute("data-self-path"):null;const mainContent=document.querySelector("#main-content");const sidebarSection=document.querySelector(".sidebar section");let methods=document.querySelector(".sidebar .block.method");let associatedTypes=document.querySelector(".sidebar .block.associatedtype");let associatedConstants=document.querySelector(".sidebar .block.associatedconstant");let sidebarTraitList=document.querySelector(".sidebar .block.trait-implementation");for(const impList of imp[window.currentCrate]){const types=impList.slice(2);const text=impList[0];const isTrait=impList[1]!==0;const traitName=impList[1];if(types.indexOf(selfPath)===-1){continue}let outputList=isTrait?trait_implementations:implementations;if(outputList===null){const outputListName=isTrait?"Trait Implementations":"Implementations";const outputListId=isTrait?"trait-implementations-list":"implementations-list";const outputListHeaderId=isTrait?"trait-implementations":"implementations";const outputListHeader=document.createElement("h2");outputListHeader.id=outputListHeaderId;outputListHeader.innerText=outputListName;outputList=document.createElement("div");outputList.id=outputListId;if(isTrait){const link=document.createElement("a");link.href=`#${outputListHeaderId}`;link.innerText="Trait Implementations";const h=document.createElement("h3");h.appendChild(link);trait_implementations=outputList;trait_implementations_header=outputListHeader;sidebarSection.appendChild(h);sidebarTraitList=document.createElement("ul");sidebarTraitList.className="block trait-implementation";sidebarSection.appendChild(sidebarTraitList);mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}else{implementations=outputList;if(trait_implementations){mainContent.insertBefore(outputListHeader,trait_implementations_header);mainContent.insertBefore(outputList,trait_implementations_header)}else{const mainContent=document.querySelector("#main-content");mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}}}const template=document.createElement("template");template.innerHTML=text;onEachLazy(template.content.querySelectorAll("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});onEachLazy(template.content.querySelectorAll("[id]"),el=>{let i=0;if(idMap.has(el.id)){i=idMap.get(el.id)}else if(document.getElementById(el.id)){i=1;while(document.getElementById(`${el.id}-${2 * i}`)){i=2*i}while(document.getElementById(`${el.id}-${i}`)){i+=1}}if(i!==0){const oldHref=`#${el.id}`;const newHref=`#${el.id}-${i}`;el.id=`${el.id}-${i}`;onEachLazy(template.content.querySelectorAll("a[href]"),link=>{if(link.getAttribute("href")===oldHref){link.href=newHref}})}idMap.set(el.id,i+1)});const templateAssocItems=template.content.querySelectorAll("section.tymethod, "+"section.method, section.associatedtype, section.associatedconstant");if(isTrait){const li=document.createElement("li");const a=document.createElement("a");a.href=`#${template.content.querySelector(".impl").id}`;a.textContent=traitName;li.appendChild(a);sidebarTraitList.append(li)}else{onEachLazy(templateAssocItems,item=>{let block=hasClass(item,"associatedtype")?associatedTypes:(hasClass(item,"associatedconstant")?associatedConstants:(methods));if(!block){const blockTitle=hasClass(item,"associatedtype")?"Associated Types":(hasClass(item,"associatedconstant")?"Associated Constants":("Methods"));const blockClass=hasClass(item,"associatedtype")?"associatedtype":(hasClass(item,"associatedconstant")?"associatedconstant":("method"));const blockHeader=document.createElement("h3");const blockLink=document.createElement("a");blockLink.href="#implementations";blockLink.innerText=blockTitle;blockHeader.appendChild(blockLink);block=document.createElement("ul");block.className=`block ${blockClass}`;const insertionReference=methods||sidebarTraitList;if(insertionReference){const insertionReferenceH=insertionReference.previousElementSibling;sidebarSection.insertBefore(blockHeader,insertionReferenceH);sidebarSection.insertBefore(block,insertionReferenceH)}else{sidebarSection.appendChild(blockHeader);sidebarSection.appendChild(block)}if(hasClass(item,"associatedtype")){associatedTypes=block}else if(hasClass(item,"associatedconstant")){associatedConstants=block}else{methods=block}}const li=document.createElement("li");const a=document.createElement("a");a.innerText=item.id.split("-")[0].split(".")[1];a.href=`#${item.id}`;li.appendChild(a);block.appendChild(li)})}outputList.appendChild(template.content)}for(const list of[methods,associatedTypes,associatedConstants,sidebarTraitList]){if(!list){continue}const newChildren=Array.prototype.slice.call(list.children);newChildren.sort((a,b)=>{const aI=a.innerText;const bI=b.innerText;return aIbI?1:0});list.replaceChildren(...newChildren)}};if(window.pending_type_impls){window.register_type_impls(window.pending_type_impls)}function addSidebarCrates(){if(!window.ALL_CRATES){return}const sidebarElems=document.getElementsByClassName("sidebar-elems")[0];if(!sidebarElems){return}const h3=document.createElement("h3");h3.innerHTML="Crates";const ul=document.createElement("ul");ul.className="block crate";for(const crate of window.ALL_CRATES){const link=document.createElement("a");link.href=window.rootPath+crate+"/index.html";link.textContent=crate;const li=document.createElement("li");if(window.rootPath!=="./"&&crate===window.currentCrate){li.className="current"}li.appendChild(link);ul.appendChild(li)}sidebarElems.appendChild(h3);sidebarElems.appendChild(ul)}function expandAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);removeClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hasClass(e,"type-contents-toggle")&&!hasClass(e,"more-examples-toggle")){e.open=true}});innerToggle.title="collapse all docs";innerToggle.children[0].innerText="\u2212"}function collapseAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);addClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(e.parentNode.id!=="implementations-list"||(!hasClass(e,"implementors-toggle")&&!hasClass(e,"type-contents-toggle"))){e.open=false}});innerToggle.title="expand all docs";innerToggle.children[0].innerText="+"}function toggleAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);if(!innerToggle){return}if(hasClass(innerToggle,"will-expand")){expandAllDocs()}else{collapseAllDocs()}}(function(){const toggles=document.getElementById(toggleAllDocsId);if(toggles){toggles.onclick=toggleAllDocs}const hideMethodDocs=getSettingValue("auto-hide-method-docs")==="true";const hideImplementations=getSettingValue("auto-hide-trait-implementations")==="true";const hideLargeItemContents=getSettingValue("auto-hide-large-items")!=="false";function setImplementorsTogglesOpen(id,open){const list=document.getElementById(id);if(list!==null){onEachLazy(list.getElementsByClassName("implementors-toggle"),e=>{e.open=open})}}if(hideImplementations){setImplementorsTogglesOpen("trait-implementations-list",false);setImplementorsTogglesOpen("blanket-implementations-list",false)}onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hideLargeItemContents&&hasClass(e,"type-contents-toggle")){e.open=true}if(hideMethodDocs&&hasClass(e,"method-toggle")){e.open=false}})}());window.rustdoc_add_line_numbers_to_examples=()=>{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");if(line_numbers.length>0){return}const count=x.textContent.split("\n").length;const elems=[];for(let i=0;i{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");for(const node of line_numbers){parent.removeChild(node)}})};if(getSettingValue("line-numbers")==="true"){window.rustdoc_add_line_numbers_to_examples()}function showSidebar(){window.hideAllModals(false);const sidebar=document.getElementsByClassName("sidebar")[0];addClass(sidebar,"shown")}function hideSidebar(){const sidebar=document.getElementsByClassName("sidebar")[0];removeClass(sidebar,"shown")}window.addEventListener("resize",()=>{if(window.CURRENT_TOOLTIP_ELEMENT){const base=window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE;const force_visible=base.TOOLTIP_FORCE_VISIBLE;hideTooltip(false);if(force_visible){showTooltip(base);base.TOOLTIP_FORCE_VISIBLE=true}}});const mainElem=document.getElementById(MAIN_ID);if(mainElem){mainElem.addEventListener("click",hideSidebar)}onEachLazy(document.querySelectorAll("a[href^='#']"),el=>{el.addEventListener("click",()=>{expandSection(el.hash.slice(1));hideSidebar()})});onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"),el=>{el.addEventListener("click",e=>{if(e.target.tagName!=="SUMMARY"&&e.target.tagName!=="A"){e.preventDefault()}})});function showTooltip(e){const notable_ty=e.getAttribute("data-notable-ty");if(!window.NOTABLE_TRAITS&¬able_ty){const data=document.getElementById("notable-traits-data");if(data){window.NOTABLE_TRAITS=JSON.parse(data.innerText)}else{throw new Error("showTooltip() called with notable without any notable traits!")}}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE===e){clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);return}window.hideAllModals(false);const wrapper=document.createElement("div");if(notable_ty){wrapper.innerHTML="
"+window.NOTABLE_TRAITS[notable_ty]+"
"}else{if(e.getAttribute("title")!==null){e.setAttribute("data-title",e.getAttribute("title"));e.removeAttribute("title")}if(e.getAttribute("data-title")!==null){const titleContent=document.createElement("div");titleContent.className="content";titleContent.appendChild(document.createTextNode(e.getAttribute("data-title")));wrapper.appendChild(titleContent)}}wrapper.className="tooltip popover";const focusCatcher=document.createElement("div");focusCatcher.setAttribute("tabindex","0");focusCatcher.onfocus=hideTooltip;wrapper.appendChild(focusCatcher);const pos=e.getBoundingClientRect();wrapper.style.top=(pos.top+window.scrollY+pos.height)+"px";wrapper.style.left=0;wrapper.style.right="auto";wrapper.style.visibility="hidden";const body=document.getElementsByTagName("body")[0];body.appendChild(wrapper);const wrapperPos=wrapper.getBoundingClientRect();const finalPos=pos.left+window.scrollX-wrapperPos.width+24;if(finalPos>0){wrapper.style.left=finalPos+"px"}else{wrapper.style.setProperty("--popover-arrow-offset",(wrapperPos.right-pos.right+4)+"px")}wrapper.style.visibility="";window.CURRENT_TOOLTIP_ELEMENT=wrapper;window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE=e;clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);wrapper.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}clearTooltipHoverTimeout(e)};wrapper.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&!e.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(wrapper,"fade-out")}}}function setTooltipHoverTimeout(element,show){clearTooltipHoverTimeout(element);if(!show&&!window.CURRENT_TOOLTIP_ELEMENT){return}if(show&&window.CURRENT_TOOLTIP_ELEMENT){return}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE!==element){return}element.TOOLTIP_HOVER_TIMEOUT=setTimeout(()=>{if(show){showTooltip(element)}else if(!element.TOOLTIP_FORCE_VISIBLE){hideTooltip(false)}},show?window.RUSTDOC_TOOLTIP_HOVER_MS:window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS)}function clearTooltipHoverTimeout(element){if(element.TOOLTIP_HOVER_TIMEOUT!==undefined){removeClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out");clearTimeout(element.TOOLTIP_HOVER_TIMEOUT);delete element.TOOLTIP_HOVER_TIMEOUT}}function tooltipBlurHandler(event){if(window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.contains(event.relatedTarget)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(event.relatedTarget)){setTimeout(()=>hideTooltip(false),0)}}function hideTooltip(focus){if(window.CURRENT_TOOLTIP_ELEMENT){if(window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE){if(focus){window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus()}window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE=false}const body=document.getElementsByTagName("body")[0];body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);window.CURRENT_TOOLTIP_ELEMENT=null}}onEachLazy(document.getElementsByClassName("tooltip"),e=>{e.onclick=()=>{e.TOOLTIP_FORCE_VISIBLE=e.TOOLTIP_FORCE_VISIBLE?false:true;if(window.CURRENT_TOOLTIP_ELEMENT&&!e.TOOLTIP_FORCE_VISIBLE){hideTooltip(true)}else{showTooltip(e);window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex","0");window.CURRENT_TOOLTIP_ELEMENT.focus();window.CURRENT_TOOLTIP_ELEMENT.onblur=tooltipBlurHandler}return false};e.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointermove=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out")}}});const sidebar_menu_toggle=document.getElementsByClassName("sidebar-menu-toggle")[0];if(sidebar_menu_toggle){sidebar_menu_toggle.addEventListener("click",()=>{const sidebar=document.getElementsByClassName("sidebar")[0];if(!hasClass(sidebar,"shown")){showSidebar()}else{hideSidebar()}})}function helpBlurHandler(event){blurHandler(event,getHelpButton(),window.hidePopoverMenus)}function buildHelpMenu(){const book_info=document.createElement("span");const channel=getVar("channel");book_info.className="top";book_info.innerHTML=`You can find more information in \ +the rustdoc book.`;const shortcuts=[["?","Show this help dialog"],["S","Focus the search field"],["↑","Move up in search results"],["↓","Move down in search results"],["← / →","Switch result tab (when results focused)"],["⏎","Go to active search result"],["+","Expand all sections"],["-","Collapse all sections"],].map(x=>"
"+x[0].split(" ").map((y,index)=>((index&1)===0?""+y+"":" "+y+" ")).join("")+"
"+x[1]+"
").join("");const div_shortcuts=document.createElement("div");addClass(div_shortcuts,"shortcuts");div_shortcuts.innerHTML="

Keyboard Shortcuts

"+shortcuts+"
";const infos=[`For a full list of all search features, take a look here.`,"Prefix searches with a type followed by a colon (e.g., fn:) to \ + restrict the search to a given item kind.","Accepted kinds are: fn, mod, struct, \ + enum, trait, type, macro, \ + and const.","Search functions by type signature (e.g., vec -> usize or \ + -> vec or String, enum:Cow -> bool)","You can look for items with an exact name by putting double quotes around \ + your request: \"string\"","Look for functions that accept or return \ + slices and \ + arrays by writing \ + square brackets (e.g., -> [u8] or [] -> Option)","Look for items inside another one by searching for a path: vec::Vec",].map(x=>"

"+x+"

").join("");const div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="

Search Tricks

"+infos;const rustdoc_version=document.createElement("span");rustdoc_version.className="bottom";const rustdoc_version_code=document.createElement("code");rustdoc_version_code.innerText="rustdoc "+getVar("rustdoc-version");rustdoc_version.appendChild(rustdoc_version_code);const container=document.createElement("div");if(!isHelpPage){container.className="popover"}container.id="help";container.style.display="none";const side_by_side=document.createElement("div");side_by_side.className="side-by-side";side_by_side.appendChild(div_shortcuts);side_by_side.appendChild(div_infos);container.appendChild(book_info);container.appendChild(side_by_side);container.appendChild(rustdoc_version);if(isHelpPage){const help_section=document.createElement("section");help_section.appendChild(container);document.getElementById("main-content").appendChild(help_section);container.style.display="block"}else{const help_button=getHelpButton();help_button.appendChild(container);container.onblur=helpBlurHandler;help_button.onblur=helpBlurHandler;help_button.children[0].onblur=helpBlurHandler}return container}window.hideAllModals=switchFocus=>{hideSidebar();window.hidePopoverMenus();hideTooltip(switchFocus)};window.hidePopoverMenus=()=>{onEachLazy(document.querySelectorAll(".search-form .popover"),elem=>{elem.style.display="none"})};function getHelpMenu(buildNeeded){let menu=getHelpButton().querySelector(".popover");if(!menu&&buildNeeded){menu=buildHelpMenu()}return menu}function showHelp(){getHelpButton().querySelector("a").focus();const menu=getHelpMenu(true);if(menu.style.display==="none"){window.hideAllModals();menu.style.display=""}}if(isHelpPage){showHelp();document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault()})}else{document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault();const menu=getHelpMenu(true);const shouldShowHelp=menu.style.display==="none";if(shouldShowHelp){showHelp()}else{window.hidePopoverMenus()}})}setMobileTopbar();addSidebarItems();addSidebarCrates();onHashChange(null);window.addEventListener("hashchange",onHashChange);searchState.setup()}());(function(){const SIDEBAR_MIN=100;const SIDEBAR_MAX=500;const RUSTDOC_MOBILE_BREAKPOINT=700;const BODY_MIN=400;const SIDEBAR_VANISH_THRESHOLD=SIDEBAR_MIN/2;const sidebarButton=document.getElementById("sidebar-button");if(sidebarButton){sidebarButton.addEventListener("click",e=>{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false");if(document.querySelector(".rustdoc.src")){window.rustdocToggleSrcSidebar()}e.preventDefault()})}let currentPointerId=null;let desiredSidebarSize=null;let pendingSidebarResizingFrame=false;const resizer=document.querySelector(".sidebar-resizer");const sidebar=document.querySelector(".sidebar");if(!resizer||!sidebar){return}const isSrcPage=hasClass(document.body,"src");function hideSidebar(){if(isSrcPage){window.rustdocCloseSourceSidebar();updateLocalStorage("src-sidebar-width",null);document.documentElement.style.removeProperty("--src-sidebar-width");sidebar.style.removeProperty("--src-sidebar-width");resizer.style.removeProperty("--src-sidebar-width")}else{addClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","true");updateLocalStorage("desktop-sidebar-width",null);document.documentElement.style.removeProperty("--desktop-sidebar-width");sidebar.style.removeProperty("--desktop-sidebar-width");resizer.style.removeProperty("--desktop-sidebar-width")}}function showSidebar(){if(isSrcPage){window.rustdocShowSourceSidebar()}else{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false")}}function changeSidebarSize(size){if(isSrcPage){updateLocalStorage("src-sidebar-width",size);sidebar.style.setProperty("--src-sidebar-width",size+"px");resizer.style.setProperty("--src-sidebar-width",size+"px")}else{updateLocalStorage("desktop-sidebar-width",size);sidebar.style.setProperty("--desktop-sidebar-width",size+"px");resizer.style.setProperty("--desktop-sidebar-width",size+"px")}}function isSidebarHidden(){return isSrcPage?!hasClass(document.documentElement,"src-sidebar-expanded"):hasClass(document.documentElement,"hide-sidebar")}function resize(e){if(currentPointerId===null||currentPointerId!==e.pointerId){return}e.preventDefault();const pos=e.clientX-3;if(pos=SIDEBAR_MIN){if(isSidebarHidden()){showSidebar()}const constrainedPos=Math.min(pos,window.innerWidth-BODY_MIN,SIDEBAR_MAX);changeSidebarSize(constrainedPos);desiredSidebarSize=constrainedPos;if(pendingSidebarResizingFrame!==false){clearTimeout(pendingSidebarResizingFrame)}pendingSidebarResizingFrame=setTimeout(()=>{if(currentPointerId===null||pendingSidebarResizingFrame===false){return}pendingSidebarResizingFrame=false;document.documentElement.style.setProperty("--resizing-sidebar-width",desiredSidebarSize+"px")},100)}}window.addEventListener("resize",()=>{if(window.innerWidth=(window.innerWidth-BODY_MIN)){changeSidebarSize(window.innerWidth-BODY_MIN)}else if(desiredSidebarSize!==null&&desiredSidebarSize>SIDEBAR_MIN){changeSidebarSize(desiredSidebarSize)}});function stopResize(e){if(currentPointerId===null){return}if(e){e.preventDefault()}desiredSidebarSize=sidebar.getBoundingClientRect().width;removeClass(resizer,"active");window.removeEventListener("pointermove",resize,false);window.removeEventListener("pointerup",stopResize,false);removeClass(document.documentElement,"sidebar-resizing");document.documentElement.style.removeProperty("--resizing-sidebar-width");if(resizer.releasePointerCapture){resizer.releasePointerCapture(currentPointerId);currentPointerId=null}}function initResize(e){if(currentPointerId!==null||e.altKey||e.ctrlKey||e.metaKey||e.button!==0){return}if(resizer.setPointerCapture){resizer.setPointerCapture(e.pointerId);if(!resizer.hasPointerCapture(e.pointerId)){resizer.releasePointerCapture(e.pointerId);return}currentPointerId=e.pointerId}window.hideAllModals(false);e.preventDefault();window.addEventListener("pointermove",resize,false);window.addEventListener("pointercancel",stopResize,false);window.addEventListener("pointerup",stopResize,false);addClass(resizer,"active");addClass(document.documentElement,"sidebar-resizing");const pos=e.clientX-sidebar.offsetLeft-3;document.documentElement.style.setProperty("--resizing-sidebar-width",pos+"px");desiredSidebarSize=null}resizer.addEventListener("pointerdown",initResize,false)}());(function(){let reset_button_timeout=null;const but=document.getElementById("copy-path");if(!but){return}but.onclick=()=>{const parent=but.parentElement;const path=[];onEach(parent.childNodes,child=>{if(child.tagName==="A"){path.push(child.textContent)}});const el=document.createElement("textarea");el.value=path.join("::");el.setAttribute("readonly","");el.style.position="absolute";el.style.left="-9999px";document.body.appendChild(el);el.select();document.execCommand("copy");document.body.removeChild(el);but.children[0].style.display="none";let tmp;if(but.childNodes.length<2){tmp=document.createTextNode("✓");but.appendChild(tmp)}else{onEachLazy(but.childNodes,e=>{if(e.nodeType===Node.TEXT_NODE){tmp=e;return true}});tmp.textContent="✓"}if(reset_button_timeout!==null){window.clearTimeout(reset_button_timeout)}function reset_button(){tmp.textContent="";reset_button_timeout=null;but.children[0].style.display=""}reset_button_timeout=window.setTimeout(reset_button,1000)}}()) \ No newline at end of file diff --git a/static.files/normalize-76eba96aa4d2e634.css b/static.files/normalize-76eba96aa4d2e634.css new file mode 100644 index 0000000..469959f --- /dev/null +++ b/static.files/normalize-76eba96aa4d2e634.css @@ -0,0 +1,2 @@ + /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} \ No newline at end of file diff --git a/static.files/noscript-04d5337699b92874.css b/static.files/noscript-04d5337699b92874.css new file mode 100644 index 0000000..fbd55f5 --- /dev/null +++ b/static.files/noscript-04d5337699b92874.css @@ -0,0 +1 @@ + #main-content .attributes{margin-left:0 !important;}#copy-path,#sidebar-button,.sidebar-resizer{display:none !important;}nav.sub{display:none;}.src .sidebar{display:none;}.notable-traits{display:none;}:root{--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}@media (prefers-color-scheme:dark){:root{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}} \ No newline at end of file diff --git a/static.files/rust-logo-151179464ae7ed46.svg b/static.files/rust-logo-151179464ae7ed46.svg new file mode 100644 index 0000000..62424d8 --- /dev/null +++ b/static.files/rust-logo-151179464ae7ed46.svg @@ -0,0 +1,61 @@ + + + diff --git a/static.files/rustdoc-e935ef01ae1c1829.css b/static.files/rustdoc-e935ef01ae1c1829.css new file mode 100644 index 0000000..37ac48c --- /dev/null +++ b/static.files/rustdoc-e935ef01ae1c1829.css @@ -0,0 +1,24 @@ + :root{--nav-sub-mobile-padding:8px;--search-typename-width:6.75rem;--desktop-sidebar-width:200px;--src-sidebar-width:300px;--desktop-sidebar-z-index:100;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:400;src:local('Fira Sans'),url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:500;src:local('Fira Sans Medium'),url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:400;src:local('Source Serif 4'),url("SourceSerif4-Regular-46f98efaafac5295.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:italic;font-weight:400;src:local('Source Serif 4 Italic'),url("SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:700;src:local('Source Serif 4 Bold'),url("SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:400;src:url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:italic;font-weight:400;src:url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:600;src:url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'NanumBarunGothic';src:url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2");font-display:swap;unicode-range:U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF;}*{box-sizing:border-box;}body{font:1rem/1.5 "Source Serif 4",NanumBarunGothic,serif;margin:0;position:relative;overflow-wrap:break-word;overflow-wrap:anywhere;font-feature-settings:"kern","liga";background-color:var(--main-background-color);color:var(--main-color);}h1{font-size:1.5rem;}h2{font-size:1.375rem;}h3{font-size:1.25rem;}h1,h2,h3,h4,h5,h6{font-weight:500;}h1,h2,h3,h4{margin:25px 0 15px 0;padding-bottom:6px;}.docblock h3,.docblock h4,h5,h6{margin:15px 0 5px 0;}.docblock>h2:first-child,.docblock>h3:first-child,.docblock>h4:first-child,.docblock>h5:first-child,.docblock>h6:first-child{margin-top:0;}.main-heading h1{margin:0;padding:0;flex-grow:1;overflow-wrap:break-word;overflow-wrap:anywhere;}.main-heading{display:flex;flex-wrap:wrap;padding-bottom:6px;margin-bottom:15px;}.content h2,.top-doc .docblock>h3,.top-doc .docblock>h4{border-bottom:1px solid var(--headings-border-bottom-color);}h1,h2{line-height:1.25;padding-top:3px;padding-bottom:9px;}h3.code-header{font-size:1.125rem;}h4.code-header{font-size:1rem;}.code-header{font-weight:600;margin:0;padding:0;white-space:pre-wrap;}#crate-search,h1,h2,h3,h4,h5,h6,.sidebar,.mobile-topbar,.search-input,.search-results .result-name,.item-name>a,.out-of-band,span.since,a.src,#help-button>a,summary.hideme,.scraped-example-list,ul.all-items{font-family:"Fira Sans",Arial,NanumBarunGothic,sans-serif;}#toggle-all-docs,a.anchor,.section-header a,#src-sidebar a,.rust a,.sidebar h2 a,.sidebar h3 a,.mobile-topbar h2 a,h1 a,.search-results a,.stab,.result-name i{color:var(--main-color);}span.enum,a.enum,span.struct,a.struct,span.union,a.union,span.primitive,a.primitive,span.type,a.type,span.foreigntype,a.foreigntype{color:var(--type-link-color);}span.trait,a.trait,span.traitalias,a.traitalias{color:var(--trait-link-color);}span.associatedtype,a.associatedtype,span.constant,a.constant,span.static,a.static{color:var(--assoc-item-link-color);}span.fn,a.fn,span.method,a.method,span.tymethod,a.tymethod{color:var(--function-link-color);}span.attr,a.attr,span.derive,a.derive,span.macro,a.macro{color:var(--macro-link-color);}span.mod,a.mod{color:var(--mod-link-color);}span.keyword,a.keyword{color:var(--keyword-link-color);}a{color:var(--link-color);text-decoration:none;}ol,ul{padding-left:24px;}ul ul,ol ul,ul ol,ol ol{margin-bottom:.625em;}p,.docblock>.warning{margin:0 0 .75em 0;}p:last-child,.docblock>.warning:last-child{margin:0;}button{padding:1px 6px;cursor:pointer;}button#toggle-all-docs{padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.rustdoc{display:flex;flex-direction:row;flex-wrap:nowrap;}main{position:relative;flex-grow:1;padding:10px 15px 40px 45px;min-width:0;}.src main{padding:15px;}.width-limiter{max-width:960px;margin-right:auto;}details:not(.toggle) summary{margin-bottom:.6em;}code,pre,a.test-arrow,.code-header{font-family:"Source Code Pro",monospace;}.docblock code,.docblock-short code{border-radius:3px;padding:0 0.125em;}.docblock pre code,.docblock-short pre code{padding:0;}pre{padding:14px;line-height:1.5;}pre.item-decl{overflow-x:auto;}.item-decl .type-contents-toggle{contain:initial;}.src .content pre{padding:20px;}.rustdoc.src .example-wrap pre.src-line-numbers{padding:20px 0 20px 4px;}img{max-width:100%;}.logo-container{line-height:0;display:block;}.rust-logo{filter:var(--rust-logo-filter);}.sidebar{font-size:0.875rem;flex:0 0 var(--desktop-sidebar-width);width:var(--desktop-sidebar-width);overflow-y:scroll;overscroll-behavior:contain;position:sticky;height:100vh;top:0;left:0;z-index:var(--desktop-sidebar-z-index);}.rustdoc.src .sidebar{flex-basis:50px;width:50px;border-right:1px solid;overflow-x:hidden;overflow-y:hidden;}.hide-sidebar .sidebar,.hide-sidebar .sidebar-resizer{display:none;}.sidebar-resizer{touch-action:none;width:9px;cursor:col-resize;z-index:calc(var(--desktop-sidebar-z-index) + 1);position:fixed;height:100%;left:calc(var(--desktop-sidebar-width) + 1px);}.rustdoc.src .sidebar-resizer{left:49px;}.src-sidebar-expanded .src .sidebar-resizer{left:var(--src-sidebar-width);}.sidebar-resizing{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;}.sidebar-resizing*{cursor:col-resize !important;}.sidebar-resizing .sidebar{position:fixed;}.sidebar-resizing>body{padding-left:var(--resizing-sidebar-width);}.sidebar-resizer:hover,.sidebar-resizer:active,.sidebar-resizer:focus,.sidebar-resizer.active{width:10px;margin:0;left:var(--desktop-sidebar-width);border-left:solid 1px var(--sidebar-resizer-hover);}.src-sidebar-expanded .rustdoc.src .sidebar-resizer:hover,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:active,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:focus,.src-sidebar-expanded .rustdoc.src .sidebar-resizer.active{left:calc(var(--src-sidebar-width) - 1px);}@media (pointer:coarse){.sidebar-resizer{display:none !important;}}.sidebar-resizer.active{padding:0 140px;width:2px;margin-left:-140px;border-left:none;}.sidebar-resizer.active:before{border-left:solid 2px var(--sidebar-resizer-active);display:block;height:100%;content:"";}.sidebar,.mobile-topbar,.sidebar-menu-toggle,#src-sidebar{background-color:var(--sidebar-background-color);}.src .sidebar>*{visibility:hidden;}.src-sidebar-expanded .src .sidebar{overflow-y:auto;flex-basis:var(--src-sidebar-width);width:var(--src-sidebar-width);}.src-sidebar-expanded .src .sidebar>*{visibility:visible;}#all-types{margin-top:1em;}*{scrollbar-width:initial;scrollbar-color:var(--scrollbar-color);}.sidebar{scrollbar-width:thin;scrollbar-color:var(--scrollbar-color);}::-webkit-scrollbar{width:12px;}.sidebar::-webkit-scrollbar{width:8px;}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0;background-color:var(--scrollbar-track-background-color);}.sidebar::-webkit-scrollbar-track{background-color:var(--scrollbar-track-background-color);}::-webkit-scrollbar-thumb,.sidebar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb-background-color);}.hidden{display:none !important;}.logo-container>img{height:48px;width:48px;}ul.block,.block li{padding:0;margin:0;list-style:none;}.sidebar-elems a,.sidebar>h2 a{display:block;padding:0.25rem;margin-left:-0.25rem;margin-right:0.25rem;}.sidebar h2{overflow-wrap:anywhere;padding:0;margin:0.7rem 0;}.sidebar h3{font-size:1.125rem;padding:0;margin:0;}.sidebar-elems,.sidebar>.version,.sidebar>h2{padding-left:24px;}.sidebar a{color:var(--sidebar-link-color);}.sidebar .current,.sidebar .current a,.sidebar-crate a.logo-container:hover+h2 a,.sidebar a:hover:not(.logo-container){background-color:var(--sidebar-current-link-background-color);}.sidebar-elems .block{margin-bottom:2em;}.sidebar-elems .block li a{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}.sidebar-crate{display:flex;align-items:center;justify-content:center;margin:14px 32px 1rem;row-gap:10px;column-gap:32px;flex-wrap:wrap;}.sidebar-crate h2{flex-grow:1;margin:0 -8px;align-self:start;}.sidebar-crate .logo-container{margin:0 -16px 0 -16px;text-align:center;}.sidebar-crate h2 a{display:block;margin:0 calc(-24px + 0.25rem) 0 -0.2rem;padding:calc((16px - 0.57rem ) / 2 ) 0.25rem;padding-left:0.2rem;}.sidebar-crate h2 .version{display:block;font-weight:normal;font-size:1rem;overflow-wrap:break-word;}.sidebar-crate+.version{margin-top:-1rem;margin-bottom:1rem;}.mobile-topbar{display:none;}.rustdoc .example-wrap{display:flex;position:relative;margin-bottom:10px;}.rustdoc .example-wrap:last-child{margin-bottom:0px;}.rustdoc .example-wrap pre{margin:0;flex-grow:1;}.rustdoc:not(.src) .example-wrap pre{overflow:auto hidden;}.rustdoc .example-wrap pre.example-line-numbers,.rustdoc .example-wrap pre.src-line-numbers{flex-grow:0;min-width:fit-content;overflow:initial;text-align:right;-webkit-user-select:none;user-select:none;padding:14px 8px;color:var(--src-line-numbers-span-color);}.rustdoc .example-wrap pre.src-line-numbers{padding:14px 0;}.src-line-numbers a,.src-line-numbers span{color:var(--src-line-numbers-span-color);padding:0 8px;}.src-line-numbers :target{background-color:transparent;border-right:none;padding:0 8px;}.src-line-numbers .line-highlighted{background-color:var(--src-line-number-highlighted-background-color);}.search-loading{text-align:center;}.docblock-short{overflow-wrap:break-word;overflow-wrap:anywhere;}.docblock :not(pre)>code,.docblock-short code{white-space:pre-wrap;}.top-doc .docblock h2{font-size:1.375rem;}.top-doc .docblock h3{font-size:1.25rem;}.top-doc .docblock h4,.top-doc .docblock h5{font-size:1.125rem;}.top-doc .docblock h6{font-size:1rem;}.docblock h5{font-size:1rem;}.docblock h6{font-size:0.875rem;}.docblock{margin-left:24px;position:relative;}.docblock>:not(.more-examples-toggle):not(.example-wrap){max-width:100%;overflow-x:auto;}.out-of-band{flex-grow:0;font-size:1.125rem;}.docblock code,.docblock-short code,pre,.rustdoc.src .example-wrap{background-color:var(--code-block-background-color);}#main-content{position:relative;}.docblock table{margin:.5em 0;border-collapse:collapse;}.docblock table td,.docblock table th{padding:.5em;border:1px solid var(--border-color);}.docblock table tbody tr:nth-child(2n){background:var(--table-alt-row-background-color);}div.where{white-space:pre-wrap;font-size:0.875rem;}.item-info{display:block;margin-left:24px;}.item-info code{font-size:0.875rem;}#main-content>.item-info{margin-left:0;}nav.sub{flex-grow:1;flex-flow:row nowrap;margin:4px 0 25px 0;display:flex;align-items:center;}.search-form{position:relative;display:flex;height:34px;flex-grow:1;}.src nav.sub{margin:0 0 15px 0;}.section-header{display:block;position:relative;}.section-header:hover>.anchor,.impl:hover>.anchor,.trait-impl:hover>.anchor,.variant:hover>.anchor{display:initial;}.anchor{display:none;position:absolute;left:-0.5em;background:none !important;}.anchor.field{left:-5px;}.section-header>.anchor{left:-15px;padding-right:8px;}h2.section-header>.anchor{padding-right:6px;}a.doc-anchor{color:var(--main-color);display:none;position:absolute;left:-17px;padding-right:5px;padding-left:3px;}*:hover>.doc-anchor{display:block;}.top-doc>.docblock>*:first-child>.doc-anchor{display:none !important;}.main-heading a:hover,.example-wrap .rust a:hover,.all-items a:hover,.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),.docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,.item-info a{text-decoration:underline;}.crate.block li.current a{font-weight:500;}table,.item-table{overflow-wrap:break-word;}.item-table{display:table;padding:0;margin:0;}.item-table>li{display:table-row;}.item-table>li>div{display:table-cell;}.item-table>li>.item-name{padding-right:1.25rem;}.search-results-title{margin-top:0;white-space:nowrap;display:flex;align-items:baseline;}#crate-search-div{position:relative;min-width:5em;}#crate-search{min-width:115px;padding:0 23px 0 4px;max-width:100%;text-overflow:ellipsis;border:1px solid var(--border-color);border-radius:4px;outline:none;cursor:pointer;-moz-appearance:none;-webkit-appearance:none;text-indent:0.01px;background-color:var(--main-background-color);color:inherit;line-height:1.5;font-weight:500;}#crate-search:hover,#crate-search:focus{border-color:var(--crate-search-hover-border);}#crate-search-div::after{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0;content:"";background-repeat:no-repeat;background-size:20px;background-position:calc(100% - 2px) 56%;background-image:url('data:image/svg+xml, \ + ');filter:var(--crate-search-div-filter);}#crate-search-div:hover::after,#crate-search-div:focus-within::after{filter:var(--crate-search-div-hover-filter);}#crate-search>option{font-size:1rem;}.search-input{-webkit-appearance:none;outline:none;border:1px solid var(--border-color);border-radius:2px;padding:8px;font-size:1rem;flex-grow:1;background-color:var(--button-background-color);color:var(--search-color);}.search-input:focus{border-color:var(--search-input-focused-border-color);}.search-results{display:none;}.search-results.active{display:block;}.search-results>a{display:flex;margin-left:2px;margin-right:2px;border-bottom:1px solid var(--search-result-border-color);gap:1em;}.search-results>a>div.desc{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex:2;}.search-results a:hover,.search-results a:focus{background-color:var(--search-result-link-focus-background-color);}.search-results .result-name{display:flex;align-items:center;justify-content:start;flex:3;}.search-results .result-name .alias{color:var(--search-results-alias-color);}.search-results .result-name .grey{color:var(--search-results-grey-color);}.search-results .result-name .typename{color:var(--search-results-grey-color);font-size:0.875rem;width:var(--search-typename-width);}.search-results .result-name .path{word-break:break-all;max-width:calc(100% - var(--search-typename-width));display:inline-block;}.search-results .result-name .path>*{display:inline;}.popover{position:absolute;top:100%;right:0;z-index:calc(var(--desktop-sidebar-z-index) + 1);margin-top:7px;border-radius:3px;border:1px solid var(--border-color);background-color:var(--main-background-color);color:var(--main-color);--popover-arrow-offset:11px;}.popover::before{content:'';position:absolute;right:var(--popover-arrow-offset);border:solid var(--border-color);border-width:1px 1px 0 0;background-color:var(--main-background-color);padding:4px;transform:rotate(-45deg);top:-5px;}.setting-line{margin:1.2em 0.6em;}.setting-radio input,.setting-check input{margin-right:0.3em;height:1.2rem;width:1.2rem;border:2px solid var(--settings-input-border-color);outline:none;-webkit-appearance:none;cursor:pointer;}.setting-radio input{border-radius:50%;}.setting-radio span,.setting-check span{padding-bottom:1px;}.setting-radio{margin-top:0.1em;margin-bottom:0.1em;min-width:3.8em;padding:0.3em;display:inline-flex;align-items:center;cursor:pointer;}.setting-radio+.setting-radio{margin-left:0.5em;}.setting-check{margin-right:20px;display:flex;align-items:center;cursor:pointer;}.setting-radio input:checked{box-shadow:inset 0 0 0 3px var(--main-background-color);background-color:var(--settings-input-color);}.setting-check input:checked{background-color:var(--settings-input-color);border-width:1px;content:url('data:image/svg+xml,\ + \ + ');}.setting-radio input:focus,.setting-check input:focus{box-shadow:0 0 1px 1px var(--settings-input-color);}.setting-radio input:checked:focus{box-shadow:inset 0 0 0 3px var(--main-background-color),0 0 2px 2px var(--settings-input-color);}.setting-radio input:hover,.setting-check input:hover{border-color:var(--settings-input-color) !important;}#help.popover{max-width:600px;--popover-arrow-offset:48px;}#help dt{float:left;clear:left;margin-right:0.5rem;}#help span.top,#help span.bottom{text-align:center;display:block;font-size:1.125rem;}#help span.top{margin:10px 0;border-bottom:1px solid var(--border-color);padding-bottom:4px;margin-bottom:6px;}#help span.bottom{clear:both;border-top:1px solid var(--border-color);}.side-by-side>div{width:50%;float:left;padding:0 20px 20px 17px;}.item-info .stab{display:block;padding:3px;margin-bottom:5px;}.item-name .stab{margin-left:0.3125em;}.stab{padding:0 2px;font-size:0.875rem;font-weight:normal;color:var(--main-color);background-color:var(--stab-background-color);width:fit-content;white-space:pre-wrap;border-radius:3px;display:inline;vertical-align:baseline;}.stab.portability>code{background:none;color:var(--stab-code-color);}.stab .emoji,.item-info .stab::before{font-size:1.25rem;}.stab .emoji{margin-right:0.3rem;}.item-info .stab::before{content:"\0";width:0;display:inline-block;color:transparent;}.emoji{text-shadow:1px 0 0 black,-1px 0 0 black,0 1px 0 black,0 -1px 0 black;}.since{font-weight:normal;font-size:initial;}.rightside{padding-left:12px;float:right;}.rightside:not(a),.out-of-band{color:var(--right-side-color);}pre.rust{tab-size:4;-moz-tab-size:4;}pre.rust .kw{color:var(--code-highlight-kw-color);}pre.rust .kw-2{color:var(--code-highlight-kw-2-color);}pre.rust .lifetime{color:var(--code-highlight-lifetime-color);}pre.rust .prelude-ty{color:var(--code-highlight-prelude-color);}pre.rust .prelude-val{color:var(--code-highlight-prelude-val-color);}pre.rust .string{color:var(--code-highlight-string-color);}pre.rust .number{color:var(--code-highlight-number-color);}pre.rust .bool-val{color:var(--code-highlight-literal-color);}pre.rust .self{color:var(--code-highlight-self-color);}pre.rust .attr{color:var(--code-highlight-attribute-color);}pre.rust .macro,pre.rust .macro-nonterminal{color:var(--code-highlight-macro-color);}pre.rust .question-mark{font-weight:bold;color:var(--code-highlight-question-mark-color);}pre.rust .comment{color:var(--code-highlight-comment-color);}pre.rust .doccomment{color:var(--code-highlight-doc-comment-color);}.rustdoc.src .example-wrap pre.rust a{background:var(--codeblock-link-background);}.example-wrap.compile_fail,.example-wrap.should_panic{border-left:2px solid var(--codeblock-error-color);}.ignore.example-wrap{border-left:2px solid var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover,.example-wrap.should_panic:hover{border-left:2px solid var(--codeblock-error-hover-color);}.example-wrap.ignore:hover{border-left:2px solid var(--codeblock-ignore-hover-color);}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip{color:var(--codeblock-error-color);}.example-wrap.ignore .tooltip{color:var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover .tooltip,.example-wrap.should_panic:hover .tooltip{color:var(--codeblock-error-hover-color);}.example-wrap.ignore:hover .tooltip{color:var(--codeblock-ignore-hover-color);}.example-wrap .tooltip{position:absolute;display:block;left:-25px;top:5px;margin:0;line-height:1;}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip,.example-wrap.ignore .tooltip{font-weight:bold;font-size:1.25rem;}.content .docblock .warning{border-left:2px solid var(--warning-border-color);padding:14px;position:relative;overflow-x:visible !important;}.content .docblock .warning::before{color:var(--warning-border-color);content:"ⓘ";position:absolute;left:-25px;top:5px;font-weight:bold;font-size:1.25rem;}.top-doc>.docblock>.warning:first-child::before{top:20px;}a.test-arrow{visibility:hidden;position:absolute;padding:5px 10px 5px 10px;border-radius:5px;font-size:1.375rem;top:5px;right:5px;z-index:1;color:var(--test-arrow-color);background-color:var(--test-arrow-background-color);}a.test-arrow:hover{color:var(--test-arrow-hover-color);background-color:var(--test-arrow-hover-background-color);}.example-wrap:hover .test-arrow{visibility:visible;}.code-attribute{font-weight:300;color:var(--code-attribute-color);}.item-spacer{width:100%;height:12px;display:block;}.out-of-band>span.since{font-size:1.25rem;}.sub-variant h4{font-size:1rem;font-weight:400;margin-top:0;margin-bottom:0;}.sub-variant{margin-left:24px;margin-bottom:40px;}.sub-variant>.sub-variant-field{margin-left:24px;}:target{padding-right:3px;background-color:var(--target-background-color);border-right:3px solid var(--target-border-color);}.code-header a.tooltip{color:inherit;margin-right:15px;position:relative;}.code-header a.tooltip:hover{color:var(--link-color);}a.tooltip:hover::after{position:absolute;top:calc(100% - 10px);left:-15px;right:-15px;height:20px;content:"\00a0";}.fade-out{opacity:0;transition:opacity 0.45s cubic-bezier(0,0,0.1,1.0);}.popover.tooltip .content{margin:0.25em 0.5em;}.popover.tooltip .content pre,.popover.tooltip .content code{background:transparent;margin:0;padding:0;font-size:1.25rem;white-space:pre-wrap;}.popover.tooltip .content>h3:first-child{margin:0 0 5px 0;}.search-failed{text-align:center;margin-top:20px;display:none;}.search-failed.active{display:block;}.search-failed>ul{text-align:left;max-width:570px;margin-left:auto;margin-right:auto;}#search-tabs{display:flex;flex-direction:row;gap:1px;margin-bottom:4px;}#search-tabs button{text-align:center;font-size:1.125rem;border:0;border-top:2px solid;flex:1;line-height:1.5;color:inherit;}#search-tabs button:not(.selected){background-color:var(--search-tab-button-not-selected-background);border-top-color:var(--search-tab-button-not-selected-border-top-color);}#search-tabs button:hover,#search-tabs button.selected{background-color:var(--search-tab-button-selected-background);border-top-color:var(--search-tab-button-selected-border-top-color);}#search-tabs .count{font-size:1rem;font-variant-numeric:tabular-nums;color:var(--search-tab-title-count-color);}#search .error code{border-radius:3px;background-color:var(--search-error-code-background-color);}.search-corrections{font-weight:normal;}#src-sidebar{width:100%;overflow:auto;}#src-sidebar div.files>a:hover,details.dir-entry summary:hover,#src-sidebar div.files>a:focus,details.dir-entry summary:focus{background-color:var(--src-sidebar-background-hover);}#src-sidebar div.files>a.selected{background-color:var(--src-sidebar-background-selected);}.src-sidebar-title{position:sticky;top:0;display:flex;padding:8px 8px 0 48px;margin-bottom:7px;background:var(--sidebar-background-color);border-bottom:1px solid var(--border-color);}#settings-menu,#help-button{margin-left:4px;display:flex;}#sidebar-button{display:none;line-height:0;}.hide-sidebar #sidebar-button,.src #sidebar-button{display:flex;margin-right:4px;position:fixed;left:6px;height:34px;width:34px;background-color:var(--main-background-color);z-index:1;}.src #sidebar-button{left:8px;z-index:calc(var(--desktop-sidebar-z-index) + 1);}.hide-sidebar .src #sidebar-button{position:static;}#settings-menu>a,#help-button>a,#sidebar-button>a{display:flex;align-items:center;justify-content:center;background-color:var(--button-background-color);border:1px solid var(--border-color);border-radius:2px;color:var(--settings-button-color);font-size:20px;width:33px;}#settings-menu>a:hover,#settings-menu>a:focus,#help-button>a:hover,#help-button>a:focus,#sidebar-button>a:hover,#sidebar-button>a:focus{border-color:var(--settings-button-border-focus);}#sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');width:22px;height:22px;}#copy-path{color:var(--copy-path-button-color);background:var(--main-background-color);height:34px;margin-left:10px;padding:0;padding-left:2px;border:0;width:33px;}#copy-path>img{filter:var(--copy-path-img-filter);}#copy-path:hover>img{filter:var(--copy-path-img-hover-filter);}@keyframes rotating{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}#settings-menu.rotate>a img{animation:rotating 2s linear infinite;}kbd{display:inline-block;padding:3px 5px;font:15px monospace;line-height:10px;vertical-align:middle;border:solid 1px var(--border-color);border-radius:3px;color:var(--kbd-color);background-color:var(--kbd-background);box-shadow:inset 0 -1px 0 var(--kbd-box-shadow-color);}ul.all-items>li{list-style:none;}details.dir-entry{padding-left:4px;}details.dir-entry>summary{margin:0 0 0 -4px;padding:0 0 0 4px;cursor:pointer;}details.dir-entry div.folders,details.dir-entry div.files{padding-left:23px;}details.dir-entry a{display:block;}details.toggle{contain:layout;position:relative;}details.toggle>summary.hideme{cursor:pointer;font-size:1rem;}details.toggle>summary{list-style:none;outline:none;}details.toggle>summary::-webkit-details-marker,details.toggle>summary::marker{display:none;}details.toggle>summary.hideme>span{margin-left:9px;}details.toggle>summary::before{background:url('data:image/svg+xml,') no-repeat top left;content:"";cursor:pointer;width:16px;height:16px;display:inline-block;vertical-align:middle;opacity:.5;filter:var(--toggle-filter);}details.toggle>summary.hideme>span,.more-examples-toggle summary,.more-examples-toggle .hide-more{color:var(--toggles-color);}details.toggle>summary::after{content:"Expand";overflow:hidden;width:0;height:0;position:absolute;}details.toggle>summary.hideme::after{content:"";}details.toggle>summary:focus::before,details.toggle>summary:hover::before{opacity:1;}details.toggle>summary:focus-visible::before{outline:1px dotted #000;outline-offset:1px;}details.non-exhaustive{margin-bottom:8px;}details.toggle>summary.hideme::before{position:relative;}details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;top:4px;}.impl-items>details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;}details.toggle[open] >summary.hideme{position:absolute;}details.toggle[open] >summary.hideme>span{display:none;}details.toggle[open] >summary::before{background:url('data:image/svg+xml,') no-repeat top left;}details.toggle[open] >summary::after{content:"Collapse";}.docblock summary>*{display:inline-block;}.docblock>.example-wrap:first-child .tooltip{margin-top:16px;}.src #sidebar-button>a:before,.sidebar-menu-toggle:before{content:url('data:image/svg+xml,\ + ');opacity:0.75;}.sidebar-menu-toggle:hover:before,.sidebar-menu-toggle:active:before,.sidebar-menu-toggle:focus:before{opacity:1;}.src #sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');opacity:0.75;}@media (max-width:850px){#search-tabs .count{display:block;}}@media (max-width:700px){*[id]{scroll-margin-top:45px;}.rustdoc{display:block;}main{padding-left:15px;padding-top:0px;}.main-heading{flex-direction:column;}.out-of-band{text-align:left;margin-left:initial;padding:initial;}.out-of-band .since::before{content:"Since ";}.sidebar .logo-container,.sidebar .location,.sidebar-resizer{display:none;}.sidebar{position:fixed;top:45px;left:-1000px;z-index:11;height:calc(100vh - 45px);width:200px;}.src main,.rustdoc.src .sidebar{top:0;padding:0;height:100vh;border:0;}.src .search-form{margin-left:40px;}.hide-sidebar .search-form{margin-left:32px;}.hide-sidebar .src .search-form{margin-left:0;}.sidebar.shown,.src-sidebar-expanded .src .sidebar,.rustdoc:not(.src) .sidebar:focus-within{left:0;}.mobile-topbar h2{padding-bottom:0;margin:auto 0.5em auto auto;overflow:hidden;font-size:24px;white-space:nowrap;text-overflow:ellipsis;}.mobile-topbar .logo-container>img{max-width:35px;max-height:35px;margin:5px 0 5px 20px;}.mobile-topbar{display:flex;flex-direction:row;position:sticky;z-index:10;font-size:2rem;height:45px;width:100%;left:0;top:0;}.hide-sidebar .mobile-topbar{display:none;}.sidebar-menu-toggle{width:45px;border:none;line-height:0;}.hide-sidebar .sidebar-menu-toggle{display:none;}.sidebar-elems{margin-top:1em;}.anchor{display:none !important;}#main-content>details.toggle>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}#copy-path,#help-button{display:none;}#sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');width:22px;height:22px;}.sidebar-menu-toggle:before{filter:var(--mobile-sidebar-menu-filter);}.sidebar-menu-toggle:hover{background:var(--main-background-color);}.item-table,.item-row,.item-table>li,.item-table>li>div,.search-results>a,.search-results>a>div{display:block;}.search-results>a{padding:5px 0px;}.search-results>a>div.desc,.item-table>li>div.desc{padding-left:2em;}.search-results .result-name{display:block;}.search-results .result-name .typename{width:initial;margin-right:0;}.search-results .result-name .typename,.search-results .result-name .path{display:inline;}.src-sidebar-expanded .src .sidebar{position:fixed;max-width:100vw;width:100vw;}.src .src-sidebar-title{padding-top:0;}details.toggle:not(.top-doc)>summary{margin-left:10px;}.impl-items>details.toggle>summary:not(.hideme)::before,#main-content>details.toggle:not(.top-doc)>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}.impl-items>.item-info{margin-left:34px;}.src nav.sub{margin:0;padding:var(--nav-sub-mobile-padding);}}@media (min-width:701px){.scraped-example-title{position:absolute;z-index:10;background:var(--main-background-color);bottom:8px;right:5px;padding:2px 4px;box-shadow:0 0 4px var(--main-background-color);}}@media print{nav.sidebar,nav.sub,.out-of-band,a.src,#copy-path,details.toggle[open] >summary::before,details.toggle>summary::before,details.toggle.top-doc>summary{display:none;}.docblock{margin-left:0;}main{padding:10px;}}@media (max-width:464px){.docblock{margin-left:12px;}.docblock code{overflow-wrap:break-word;overflow-wrap:anywhere;}nav.sub{flex-direction:column;}.search-form{align-self:stretch;}}.variant,.implementors-toggle>summary,.impl,#implementors-list>.docblock,.impl-items>section,.impl-items>.toggle>summary,.methods>section,.methods>.toggle>summary{margin-bottom:0.75em;}.variants>.docblock,.implementors-toggle>.docblock,.impl-items>.toggle[open]:not(:last-child),.methods>.toggle[open]:not(:last-child),.implementors-toggle[open]:not(:last-child){margin-bottom:2em;}#trait-implementations-list .impl-items>.toggle:not(:last-child),#synthetic-implementations-list .impl-items>.toggle:not(:last-child),#blanket-implementations-list .impl-items>.toggle:not(:last-child){margin-bottom:1em;}.scraped-example-list .scrape-help{margin-left:10px;padding:0 4px;font-weight:normal;font-size:12px;position:relative;bottom:1px;border:1px solid var(--scrape-example-help-border-color);border-radius:50px;color:var(--scrape-example-help-color);}.scraped-example-list .scrape-help:hover{border-color:var(--scrape-example-help-hover-border-color);color:var(--scrape-example-help-hover-color);}.scraped-example{position:relative;}.scraped-example .code-wrapper{position:relative;display:flex;flex-direction:row;flex-wrap:wrap;width:100%;}.scraped-example:not(.expanded) .code-wrapper{max-height:calc(1.5em * 5 + 10px);}.scraped-example:not(.expanded) .code-wrapper pre{overflow-y:hidden;padding-bottom:0;max-height:calc(1.5em * 5 + 10px);}.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper,.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre{max-height:calc(1.5em * 10 + 10px);}.scraped-example .code-wrapper .next,.scraped-example .code-wrapper .prev,.scraped-example .code-wrapper .expand{color:var(--main-color);position:absolute;top:0.25em;z-index:1;padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.scraped-example .code-wrapper .prev{right:2.25em;}.scraped-example .code-wrapper .next{right:1.25em;}.scraped-example .code-wrapper .expand{right:0.25em;}.scraped-example:not(.expanded) .code-wrapper::before,.scraped-example:not(.expanded) .code-wrapper::after{content:" ";width:100%;height:5px;position:absolute;z-index:1;}.scraped-example:not(.expanded) .code-wrapper::before{top:0;background:linear-gradient(to bottom,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example:not(.expanded) .code-wrapper::after{bottom:0;background:linear-gradient(to top,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example .code-wrapper .example-wrap{width:100%;overflow-y:hidden;margin-bottom:0;}.scraped-example:not(.expanded) .code-wrapper .example-wrap{overflow-x:hidden;}.scraped-example .example-wrap .rust span.highlight{background:var(--scrape-example-code-line-highlight);}.scraped-example .example-wrap .rust span.highlight.focus{background:var(--scrape-example-code-line-highlight-focus);}.more-examples-toggle{max-width:calc(100% + 25px);margin-top:10px;margin-left:-25px;}.more-examples-toggle .hide-more{margin-left:25px;cursor:pointer;}.more-scraped-examples{margin-left:25px;position:relative;}.toggle-line{position:absolute;top:5px;bottom:0;right:calc(100% + 10px);padding:0 4px;cursor:pointer;}.toggle-line-inner{min-width:2px;height:100%;background:var(--scrape-example-toggle-line-background);}.toggle-line:hover .toggle-line-inner{background:var(--scrape-example-toggle-line-hover-background);}.more-scraped-examples .scraped-example,.example-links{margin-top:20px;}.more-scraped-examples .scraped-example:first-child{margin-top:5px;}.example-links ul{margin-bottom:0;}:root[data-theme="light"]{--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="dark"]{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="ayu"]{--main-background-color:#0f1419;--main-color:#c5c5c5;--settings-input-color:#ffb454;--settings-input-border-color:#999;--settings-button-color:#fff;--settings-button-border-focus:#e0e0e0;--sidebar-background-color:#14191f;--sidebar-background-color-hover:rgba(70,70,70,0.33);--code-block-background-color:#191f26;--scrollbar-track-background-color:transparent;--scrollbar-thumb-background-color:#5c6773;--scrollbar-color:#5c6773 #24292f;--headings-border-bottom-color:#5c6773;--border-color:#5c6773;--button-background-color:#141920;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#5c6773;--copy-path-button-color:#fff;--copy-path-img-filter:invert(70%);--copy-path-img-hover-filter:invert(100%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ffa0a5;--trait-link-color:#39afd7;--assoc-item-link-color:#39afd7;--function-link-color:#fdd687;--macro-link-color:#a37acc;--keyword-link-color:#39afd7;--mod-link-color:#39afd7;--link-color:#39afd7;--sidebar-link-color:#53b1db;--sidebar-current-link-background-color:transparent;--search-result-link-focus-background-color:#3c3c3c;--search-result-border-color:#aaa3;--search-color:#fff;--search-error-code-background-color:#4f4c4c;--search-results-alias-color:#c5c5c5;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:none;--search-tab-button-not-selected-background:transparent !important;--search-tab-button-selected-border-top-color:none;--search-tab-button-selected-background:#141920 !important;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ff7733;--code-highlight-kw-2-color:#ff7733;--code-highlight-lifetime-color:#ff7733;--code-highlight-prelude-color:#69f2df;--code-highlight-prelude-val-color:#ff7733;--code-highlight-number-color:#b8cc52;--code-highlight-string-color:#b8cc52;--code-highlight-literal-color:#ff7733;--code-highlight-attribute-color:#e6e1cf;--code-highlight-self-color:#36a3d9;--code-highlight-macro-color:#a37acc;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#788797;--code-highlight-doc-comment-color:#a1ac88;--src-line-numbers-span-color:#5c6773;--src-line-number-highlighted-background-color:rgba(255,236,164,0.06);--test-arrow-color:#788797;--test-arrow-background-color:rgba(57,175,215,0.09);--test-arrow-hover-color:#c5c5c5;--test-arrow-hover-background-color:rgba(57,175,215,0.368);--target-background-color:rgba(255,236,164,0.06);--target-border-color:rgba(255,180,76,0.85);--kbd-color:#c5c5c5;--kbd-background:#314559;--kbd-box-shadow-color:#5c6773;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) brightness(94%) contrast(94%);--crate-search-div-hover-filter:invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) brightness(113%) contrast(76%);--crate-search-hover-border:#e0e0e0;--src-sidebar-background-selected:#14191f;--src-sidebar-background-hover:#14191f;--table-alt-row-background-color:#191f26;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(15,20,25,1);--scrape-example-code-wrapper-background-end:rgba(15,20,25,0);--sidebar-resizer-hover:hsl(34,50%,33%);--sidebar-resizer-active:hsl(34,100%,66%);}:root[data-theme="ayu"] h1,:root[data-theme="ayu"] h2,:root[data-theme="ayu"] h3,:root[data-theme="ayu"] h4,:where(:root[data-theme="ayu"]) h1 a,:root[data-theme="ayu"] .sidebar h2 a,:root[data-theme="ayu"] .sidebar h3 a{color:#fff;}:root[data-theme="ayu"] .docblock code{color:#ffb454;}:root[data-theme="ayu"] .docblock a>code{color:#39AFD7 !important;}:root[data-theme="ayu"] .code-header,:root[data-theme="ayu"] .docblock pre>code,:root[data-theme="ayu"] pre,:root[data-theme="ayu"] pre>code,:root[data-theme="ayu"] .item-info code,:root[data-theme="ayu"] .rustdoc.source .example-wrap{color:#e6e1cf;}:root[data-theme="ayu"] .sidebar .current,:root[data-theme="ayu"] .sidebar .current a,:root[data-theme="ayu"] .sidebar a:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:hover,:root[data-theme="ayu"] details.dir-entry summary:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:focus,:root[data-theme="ayu"] details.dir-entry summary:focus,:root[data-theme="ayu"] #src-sidebar div.files>a.selected{color:#ffb44c;}:root[data-theme="ayu"] .sidebar-elems .location{color:#ff7733;}:root[data-theme="ayu"] .src-line-numbers .line-highlighted{color:#708090;padding-right:7px;border-right:1px solid #ffb44c;}:root[data-theme="ayu"] .search-results a:hover,:root[data-theme="ayu"] .search-results a:focus{color:#fff !important;background-color:#3c3c3c;}:root[data-theme="ayu"] .search-results a{color:#0096cf;}:root[data-theme="ayu"] .search-results a div.desc{color:#c5c5c5;}:root[data-theme="ayu"] .result-name .primitive>i,:root[data-theme="ayu"] .result-name .keyword>i{color:#788797;}:root[data-theme="ayu"] #search-tabs>button.selected{border-bottom:1px solid #ffb44c !important;border-top:none;}:root[data-theme="ayu"] #search-tabs>button:not(.selected){border:none;background-color:transparent !important;}:root[data-theme="ayu"] #search-tabs>button:hover{border-bottom:1px solid rgba(242,151,24,0.3);}:root[data-theme="ayu"] #settings-menu>a img,:root[data-theme="ayu"] #sidebar-button>a:before{filter:invert(100);} \ No newline at end of file diff --git a/static.files/scrape-examples-ef1e698c1d417c0c.js b/static.files/scrape-examples-ef1e698c1d417c0c.js new file mode 100644 index 0000000..ba830e3 --- /dev/null +++ b/static.files/scrape-examples-ef1e698c1d417c0c.js @@ -0,0 +1 @@ +"use strict";(function(){const DEFAULT_MAX_LINES=5;const HIDDEN_MAX_LINES=10;function scrollToLoc(elt,loc,isHidden){const lines=elt.querySelector(".src-line-numbers");let scrollOffset;const maxLines=isHidden?HIDDEN_MAX_LINES:DEFAULT_MAX_LINES;if(loc[1]-loc[0]>maxLines){const line=Math.max(0,loc[0]-1);scrollOffset=lines.children[line].offsetTop}else{const wrapper=elt.querySelector(".code-wrapper");const halfHeight=wrapper.offsetHeight/2;const offsetTop=lines.children[loc[0]].offsetTop;const lastLine=lines.children[loc[1]];const offsetBot=lastLine.offsetTop+lastLine.offsetHeight;const offsetMid=(offsetTop+offsetBot)/2;scrollOffset=offsetMid-halfHeight}lines.scrollTo(0,scrollOffset);elt.querySelector(".rust").scrollTo(0,scrollOffset)}function updateScrapedExample(example,isHidden){const locs=JSON.parse(example.attributes.getNamedItem("data-locs").textContent);let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=example.querySelector(".scraped-example-title a");if(locs.length>1){const onChangeLoc=changeIndex=>{removeClass(highlights[locIndex],"focus");changeIndex();scrollToLoc(example,locs[locIndex][0],isHidden);addClass(highlights[locIndex],"focus");const url=locs[locIndex][1];const title=locs[locIndex][2];link.href=url;link.innerHTML=title};example.querySelector(".prev").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length})});example.querySelector(".next").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length})})}const expandButton=example.querySelector(".expand");if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");scrollToLoc(example,locs[0][0],isHidden)}else{addClass(example,"expanded")}})}scrollToLoc(example,locs[0][0],isHidden)}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>updateScrapedExample(el,false));onEachLazy(document.querySelectorAll(".more-examples-toggle"),toggle=>{onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"),button=>{button.addEventListener("click",()=>{toggle.open=false})});const moreExamples=toggle.querySelectorAll(".scraped-example");toggle.querySelector("summary").addEventListener("click",()=>{setTimeout(()=>{onEachLazy(moreExamples,el=>updateScrapedExample(el,true))})},{once:true})})})() \ No newline at end of file diff --git a/static.files/search-42d8da7a6b9792c2.js b/static.files/search-42d8da7a6b9792c2.js new file mode 100644 index 0000000..1d178d5 --- /dev/null +++ b/static.files/search-42d8da7a6b9792c2.js @@ -0,0 +1,5 @@ +"use strict";if(!Array.prototype.toSpliced){Array.prototype.toSpliced=function(){const me=this.slice();Array.prototype.splice.apply(me,arguments);return me}}(function(){const itemTypes=["keyword","primitive","mod","externcrate","import","struct","enum","fn","type","static","trait","impl","tymethod","method","structfield","variant","macro","associatedtype","constant","associatedconstant","union","foreigntype","existential","attr","derive","traitalias","generic",];const longItemTypes=["keyword","primitive type","module","extern crate","re-export","struct","enum","function","type alias","static","trait","","trait method","method","struct field","enum variant","macro","assoc type","constant","assoc const","union","foreign type","existential type","attribute macro","derive macro","trait alias",];const TY_GENERIC=itemTypes.indexOf("generic");const ROOT_PATH=typeof window!=="undefined"?window.rootPath:"../";const UNBOXING_LIMIT=5;function printTab(nb){let iter=0;let foundCurrentTab=false;let foundCurrentResultSet=false;onEachLazy(document.getElementById("search-tabs").childNodes,elem=>{if(nb===iter){addClass(elem,"selected");foundCurrentTab=true}else{removeClass(elem,"selected")}iter+=1});const isTypeSearch=(nb>0||iter===1);iter=0;onEachLazy(document.getElementById("results").childNodes,elem=>{if(nb===iter){addClass(elem,"active");foundCurrentResultSet=true}else{removeClass(elem,"active")}iter+=1});if(foundCurrentTab&&foundCurrentResultSet){searchState.currentTab=nb;const correctionsElem=document.getElementsByClassName("search-corrections");if(isTypeSearch){removeClass(correctionsElem[0],"hidden")}else{addClass(correctionsElem[0],"hidden")}}else if(nb!==0){printTab(0)}}const editDistanceState={current:[],prev:[],prevPrev:[],calculate:function calculate(a,b,limit){if(a.lengthlimit){return limit+1}while(b.length>0&&b[0]===a[0]){a=a.substring(1);b=b.substring(1)}while(b.length>0&&b[b.length-1]===a[a.length-1]){a=a.substring(0,a.length-1);b=b.substring(0,b.length-1)}if(b.length===0){return minDist}const aLength=a.length;const bLength=b.length;for(let i=0;i<=bLength;++i){this.current[i]=0;this.prev[i]=i;this.prevPrev[i]=Number.MAX_VALUE}for(let i=1;i<=aLength;++i){this.current[0]=i;const aIdx=i-1;for(let j=1;j<=bLength;++j){const bIdx=j-1;const substitutionCost=a[aIdx]===b[bIdx]?0:1;this.current[j]=Math.min(this.prev[j]+1,this.current[j-1]+1,this.prev[j-1]+substitutionCost);if((i>1)&&(j>1)&&(a[aIdx]===b[bIdx-1])&&(a[aIdx-1]===b[bIdx])){this.current[j]=Math.min(this.current[j],this.prevPrev[j-2]+1)}}const prevPrevTmp=this.prevPrev;this.prevPrev=this.prev;this.prev=this.current;this.current=prevPrevTmp}const distance=this.prev[bLength];return distance<=limit?distance:(limit+1)},};function editDistance(a,b,limit){return editDistanceState.calculate(a,b,limit)}function initSearch(rawSearchIndex){const MAX_RESULTS=200;const NO_TYPE_FILTER=-1;let searchIndex;let functionTypeFingerprint;let currentResults;const typeNameIdMap=new Map();const ALIASES=new Map();const typeNameIdOfArray=buildTypeMapIndex("array");const typeNameIdOfSlice=buildTypeMapIndex("slice");const typeNameIdOfArrayOrSlice=buildTypeMapIndex("[]");const typeNameIdOfTuple=buildTypeMapIndex("tuple");const typeNameIdOfUnit=buildTypeMapIndex("unit");const typeNameIdOfTupleOrUnit=buildTypeMapIndex("()");const typeNameIdOfFn=buildTypeMapIndex("fn");const typeNameIdOfFnMut=buildTypeMapIndex("fnmut");const typeNameIdOfFnOnce=buildTypeMapIndex("fnonce");const typeNameIdOfHof=buildTypeMapIndex("->");function buildTypeMapIndex(name,isAssocType){if(name===""||name===null){return null}if(typeNameIdMap.has(name)){const obj=typeNameIdMap.get(name);obj.assocOnly=isAssocType&&obj.assocOnly;return obj.id}else{const id=typeNameIdMap.size;typeNameIdMap.set(name,{id,assocOnly:isAssocType});return id}}function isSpecialStartCharacter(c){return"<\"".indexOf(c)!==-1}function isEndCharacter(c){return"=,>-])".indexOf(c)!==-1}function itemTypeFromName(typename){const index=itemTypes.findIndex(i=>i===typename);if(index<0){throw["Unknown type filter ",typename]}return index}function getStringElem(query,parserState,isInGenerics){if(isInGenerics){throw["Unexpected ","\""," in generics"]}else if(query.literalSearch){throw["Cannot have more than one literal search element"]}else if(parserState.totalElems-parserState.genericsElems>0){throw["Cannot use literal search when there is more than one element"]}parserState.pos+=1;const start=parserState.pos;const end=getIdentEndPosition(parserState);if(parserState.pos>=parserState.length){throw["Unclosed ","\""]}else if(parserState.userQuery[end]!=="\""){throw["Unexpected ",parserState.userQuery[end]," in a string element"]}else if(start===end){throw["Cannot have empty string element"]}parserState.pos+=1;query.literalSearch=true}function isPathStart(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="::"}function isReturnArrow(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="->"}function isIdentCharacter(c){return(c==="_"||(c>="0"&&c<="9")||(c>="a"&&c<="z")||(c>="A"&&c<="Z"))}function isSeparatorCharacter(c){return c===","||c==="="}function isPathSeparator(c){return c===":"||c===" "}function prevIs(parserState,lookingFor){let pos=parserState.pos;while(pos>0){const c=parserState.userQuery[pos-1];if(c===lookingFor){return true}else if(c!==" "){break}pos-=1}return false}function isLastElemGeneric(elems,parserState){return(elems.length>0&&elems[elems.length-1].generics.length>0)||prevIs(parserState,">")}function skipWhitespace(parserState){while(parserState.pos0){throw["Cannot have more than one element if you use quotes"]}const typeFilter=parserState.typeFilter;parserState.typeFilter=null;if(name==="!"){if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive never type ","!"," and ",typeFilter," both specified",]}if(generics.length!==0){throw["Never type ","!"," does not accept generic parameters",]}const bindingName=parserState.isInBinding;parserState.isInBinding=null;return makePrimitiveElement("never",{bindingName})}const quadcolon=/::\s*::/.exec(path);if(path.startsWith("::")){throw["Paths cannot start with ","::"]}else if(path.endsWith("::")){throw["Paths cannot end with ","::"]}else if(quadcolon!==null){throw["Unexpected ",quadcolon[0]]}const pathSegments=path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/);if(pathSegments.length===0||(pathSegments.length===1&&pathSegments[0]==="")){if(generics.length>0||prevIs(parserState,">")){throw["Found generics without a path"]}else{throw["Unexpected ",parserState.userQuery[parserState.pos]]}}for(const[i,pathSegment]of pathSegments.entries()){if(pathSegment==="!"){if(i!==0){throw["Never type ","!"," is not associated item"]}pathSegments[i]="never"}}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}const bindingName=parserState.isInBinding;parserState.isInBinding=null;const bindings=new Map();const pathLast=pathSegments[pathSegments.length-1];return{name:name.trim(),id:null,fullPath:pathSegments,pathWithoutLast:pathSegments.slice(0,pathSegments.length-1),pathLast,normalizedPathLast:pathLast.replace(/_/g,""),generics:generics.filter(gen=>{if(gen.bindingName!==null){if(gen.name!==null){gen.bindingName.generics.unshift(gen)}bindings.set(gen.bindingName.name,gen.bindingName.generics);return false}return true}),bindings,typeFilter,bindingName,}}function getIdentEndPosition(parserState){const start=parserState.pos;let end=parserState.pos;let foundExclamation=-1;while(parserState.pos0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]]}else{throw["Unexpected ",c]}}parserState.pos+=1;end=parserState.pos}if(foundExclamation!==-1&&foundExclamation!==start&&isIdentCharacter(parserState.userQuery[foundExclamation-1])){if(parserState.typeFilter===null){parserState.typeFilter="macro"}else if(parserState.typeFilter!=="macro"){throw["Invalid search type: macro ","!"," and ",parserState.typeFilter," both specified",]}end=foundExclamation}return end}function getFilteredNextElem(query,parserState,elems,isInGenerics){const start=parserState.pos;if(parserState.userQuery[parserState.pos]===":"&&!isPathStart(parserState)){throw["Expected type filter before ",":"]}getNextElem(query,parserState,elems,isInGenerics);if(parserState.userQuery[parserState.pos]===":"&&!isPathStart(parserState)){if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}if(elems.length===0){throw["Expected type filter before ",":"]}else if(query.literalSearch){throw["Cannot use quotes on type filter"]}const typeFilterElem=elems.pop();checkExtraTypeFilterCharacters(start,parserState);parserState.typeFilter=typeFilterElem.name;parserState.pos+=1;parserState.totalElems-=1;query.literalSearch=false;getNextElem(query,parserState,elems,isInGenerics)}}function getNextElem(query,parserState,elems,isInGenerics){const generics=[];skipWhitespace(parserState);let start=parserState.pos;let end;if("[(".indexOf(parserState.userQuery[parserState.pos])!==-1){let endChar=")";let name="()";let friendlyName="tuple";if(parserState.userQuery[parserState.pos]==="["){endChar="]";name="[]";friendlyName="slice"}parserState.pos+=1;const{foundSeparator}=getItemsBefore(query,parserState,generics,endChar);const typeFilter=parserState.typeFilter;const bindingName=parserState.isInBinding;parserState.typeFilter=null;parserState.isInBinding=null;for(const gen of generics){if(gen.bindingName!==null){throw["Type parameter ","=",` cannot be within ${friendlyName} `,name]}}if(name==="()"&&!foundSeparator&&generics.length===1&&typeFilter===null){elems.push(generics[0])}else if(name==="()"&&generics.length===1&&generics[0].name==="->"){generics[0].typeFilter=typeFilter;elems.push(generics[0])}else{if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive ",name," and ",typeFilter," both specified",]}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}elems.push(makePrimitiveElement(name,{bindingName,generics}))}}else{const isStringElem=parserState.userQuery[start]==="\"";if(isStringElem){start+=1;getStringElem(query,parserState,isInGenerics);end=parserState.pos-1}else{end=getIdentEndPosition(parserState)}if(parserState.pos=end){throw["Found generics without a path"]}parserState.pos+=1;getItemsBefore(query,parserState,generics,">")}else if(parserState.pos=end){throw["Found generics without a path"]}if(parserState.isInBinding){throw["Unexpected ","("," after ","="]}parserState.pos+=1;const typeFilter=parserState.typeFilter;parserState.typeFilter=null;getItemsBefore(query,parserState,generics,")");skipWhitespace(parserState);if(isReturnArrow(parserState)){parserState.pos+=2;skipWhitespace(parserState);getFilteredNextElem(query,parserState,generics,isInGenerics);generics[generics.length-1].bindingName=makePrimitiveElement("output")}else{generics.push(makePrimitiveElement(null,{bindingName:makePrimitiveElement("output"),typeFilter:null,}))}parserState.typeFilter=typeFilter}if(isStringElem){skipWhitespace(parserState)}if(start>=end&&generics.length===0){return}if(parserState.userQuery[parserState.pos]==="="){if(parserState.isInBinding){throw["Cannot write ","="," twice in a binding"]}if(!isInGenerics){throw["Type parameter ","="," must be within generics list"]}const name=parserState.userQuery.slice(start,end).trim();if(name==="!"){throw["Type parameter ","="," key cannot be ","!"," never type"]}if(name.includes("!")){throw["Type parameter ","="," key cannot be ","!"," macro"]}if(name.includes("::")){throw["Type parameter ","="," key cannot contain ","::"," path"]}if(name.includes(":")){throw["Type parameter ","="," key cannot contain ",":"," type"]}parserState.isInBinding={name,generics}}else{elems.push(createQueryElement(query,parserState,parserState.userQuery.slice(start,end),generics,isInGenerics))}}}function getItemsBefore(query,parserState,elems,endChar){let foundStopChar=true;let foundSeparator=false;const oldTypeFilter=parserState.typeFilter;parserState.typeFilter=null;const oldIsInBinding=parserState.isInBinding;parserState.isInBinding=null;let hofParameters=null;let extra="";if(endChar===">"){extra="<"}else if(endChar==="]"){extra="["}else if(endChar===")"){extra="("}else if(endChar===""){extra="->"}else{extra=endChar}while(parserState.pos"," after ","="]}hofParameters=[...elems];elems.length=0;parserState.pos+=2;foundStopChar=true;foundSeparator=false;continue}else if(c===" "){parserState.pos+=1;continue}else if(isSeparatorCharacter(c)){parserState.pos+=1;foundStopChar=true;foundSeparator=true;continue}else if(c===":"&&isPathStart(parserState)){throw["Unexpected ","::",": paths cannot start with ","::"]}else if(isEndCharacter(c)){throw["Unexpected ",c," after ",extra]}if(!foundStopChar){let extra=[];if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(endChar!==""){throw["Expected ",",",", ","=",", or ",endChar,...extra,", found ",c,]}throw["Expected ",","," or ","=",...extra,", found ",c,]}const posBefore=parserState.pos;getFilteredNextElem(query,parserState,elems,endChar!=="");if(endChar!==""&&parserState.pos>=parserState.length){throw["Unclosed ",extra]}if(posBefore===parserState.pos){parserState.pos+=1}foundStopChar=false}if(parserState.pos>=parserState.length&&endChar!==""){throw["Unclosed ",extra]}parserState.pos+=1;if(hofParameters){foundSeparator=false;if([...elems,...hofParameters].some(x=>x.bindingName)||parserState.isInBinding){throw["Unexpected ","="," within ","->"]}const hofElem=makePrimitiveElement("->",{generics:hofParameters,bindings:new Map([["output",[...elems]]]),typeFilter:null,});elems.length=0;elems[0]=hofElem}parserState.typeFilter=oldTypeFilter;parserState.isInBinding=oldIsInBinding;return{foundSeparator}}function checkExtraTypeFilterCharacters(start,parserState){const query=parserState.userQuery.slice(start,parserState.pos).trim();for(const c in query){if(!isIdentCharacter(query[c])){throw["Unexpected ",query[c]," in type filter (before ",":",")",]}}}function parseInput(query,parserState){let foundStopChar=true;while(parserState.pos"){if(isReturnArrow(parserState)){break}throw["Unexpected ",c," (did you mean ","->","?)"]}else if(parserState.pos>0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]]}throw["Unexpected ",c]}else if(c===" "){skipWhitespace(parserState);continue}if(!foundStopChar){let extra="";if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(parserState.typeFilter!==null){throw["Expected ",","," or ","->",...extra,", found ",c,]}throw["Expected ",",",", ",":"," or ","->",...extra,", found ",c,]}const before=query.elems.length;getFilteredNextElem(query,parserState,query.elems,false);if(query.elems.length===before){parserState.pos+=1}foundStopChar=false}if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}while(parserState.pos"]}break}else{parserState.pos+=1}}}function newParsedQuery(userQuery){return{original:userQuery,userQuery:userQuery.toLowerCase(),elems:[],returned:[],foundElems:0,totalElems:0,literalSearch:false,error:null,correction:null,proposeCorrectionFrom:null,proposeCorrectionTo:null,typeFingerprint:new Uint32Array(4),}}function buildUrl(search,filterCrates){let extra="?search="+encodeURIComponent(search);if(filterCrates!==null){extra+="&filter-crate="+encodeURIComponent(filterCrates)}return getNakedUrl()+extra+window.location.hash}function getFilterCrates(){const elem=document.getElementById("crate-search");if(elem&&elem.value!=="all crates"&&rawSearchIndex.has(elem.value)){return elem.value}return null}function parseQuery(userQuery){function convertTypeFilterOnElem(elem){if(elem.typeFilter!==null){let typeFilter=elem.typeFilter;if(typeFilter==="const"){typeFilter="constant"}elem.typeFilter=itemTypeFromName(typeFilter)}else{elem.typeFilter=NO_TYPE_FILTER}for(const elem2 of elem.generics){convertTypeFilterOnElem(elem2)}for(const constraints of elem.bindings.values()){for(const constraint of constraints){convertTypeFilterOnElem(constraint)}}}userQuery=userQuery.trim().replace(/\r|\n|\t/g," ");const parserState={length:userQuery.length,pos:0,totalElems:0,genericsElems:0,typeFilter:null,isInBinding:null,userQuery:userQuery.toLowerCase(),};let query=newParsedQuery(userQuery);try{parseInput(query,parserState);for(const elem of query.elems){convertTypeFilterOnElem(elem)}for(const elem of query.returned){convertTypeFilterOnElem(elem)}}catch(err){query=newParsedQuery(userQuery);query.error=err;return query}if(!query.literalSearch){query.literalSearch=parserState.totalElems>1}query.foundElems=query.elems.length+query.returned.length;query.totalElems=parserState.totalElems;return query}function createQueryResults(results_in_args,results_returned,results_others,parsedQuery){return{"in_args":results_in_args,"returned":results_returned,"others":results_others,"query":parsedQuery,}}function execQuery(parsedQuery,filterCrates,currentCrate){const results_others=new Map(),results_in_args=new Map(),results_returned=new Map();function transformResults(results){const duplicates=new Set();const out=[];for(const result of results){if(result.id!==-1){const obj=searchIndex[result.id];obj.dist=result.dist;const res=buildHrefAndPath(obj);obj.displayPath=pathSplitter(res[0]);obj.fullPath=obj.displayPath+obj.name;obj.fullPath+="|"+obj.ty;if(duplicates.has(obj.fullPath)){continue}duplicates.add(obj.fullPath);obj.href=res[1];out.push(obj);if(out.length>=MAX_RESULTS){break}}}return out}function sortResults(results,isType,preferredCrate){const userQuery=parsedQuery.userQuery;const result_list=[];for(const result of results.values()){result.item=searchIndex[result.id];result.word=searchIndex[result.id].word;result_list.push(result)}result_list.sort((aaa,bbb)=>{let a,b;a=(aaa.word!==userQuery);b=(bbb.word!==userQuery);if(a!==b){return a-b}a=(aaa.index<0);b=(bbb.index<0);if(a!==b){return a-b}a=aaa.path_dist;b=bbb.path_dist;if(a!==b){return a-b}a=aaa.index;b=bbb.index;if(a!==b){return a-b}a=(aaa.dist);b=(bbb.dist);if(a!==b){return a-b}a=aaa.item.deprecated;b=bbb.item.deprecated;if(a!==b){return a-b}a=(aaa.item.crate!==preferredCrate);b=(bbb.item.crate!==preferredCrate);if(a!==b){return a-b}a=aaa.word.length;b=bbb.word.length;if(a!==b){return a-b}a=aaa.word;b=bbb.word;if(a!==b){return(a>b?+1:-1)}a=(aaa.item.desc==="");b=(bbb.item.desc==="");if(a!==b){return a-b}a=aaa.item.ty;b=bbb.item.ty;if(a!==b){return a-b}a=aaa.item.path;b=bbb.item.path;if(a!==b){return(a>b?+1:-1)}return 0});return transformResults(result_list)}function unifyFunctionTypes(fnTypesIn,queryElems,whereClause,mgensIn,solutionCb,unboxingDepth){if(unboxingDepth>=UNBOXING_LIMIT){return false}const mgens=mgensIn===null?null:new Map(mgensIn);if(queryElems.length===0){return!solutionCb||solutionCb(mgens)}if(!fnTypesIn||fnTypesIn.length===0){return false}const ql=queryElems.length;const fl=fnTypesIn.length;if(ql===1&&queryElems[0].generics.length===0&&queryElems[0].bindings.size===0){const queryElem=queryElems[0];for(const fnType of fnTypesIn){if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgens)){continue}if(fnType.id<0&&queryElem.id<0){if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==queryElem.id){continue}const mgensScratch=new Map(mgens);mgensScratch.set(fnType.id,queryElem.id);if(!solutionCb||solutionCb(mgensScratch)){return true}}else if(!solutionCb||solutionCb(mgens?new Map(mgens):null)){return true}}for(const fnType of fnTypesIn){if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth+1)){continue}if(fnType.id<0){if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){continue}const mgensScratch=new Map(mgens);mgensScratch.set(fnType.id,0);if(unifyFunctionTypes(whereClause[(-fnType.id)-1],queryElems,whereClause,mgensScratch,solutionCb,unboxingDepth+1)){return true}}else if(unifyFunctionTypes([...fnType.generics,...Array.from(fnType.bindings.values()).flat()],queryElems,whereClause,mgens?new Map(mgens):null,solutionCb,unboxingDepth+1)){return true}}return false}const fnTypes=fnTypesIn.slice();const flast=fl-1;const qlast=ql-1;const queryElem=queryElems[qlast];let queryElemsTmp=null;for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgens)){continue}let mgensScratch;if(fnType.id<0){mgensScratch=new Map(mgens);if(mgensScratch.has(fnType.id)&&mgensScratch.get(fnType.id)!==queryElem.id){continue}mgensScratch.set(fnType.id,queryElem.id)}else{mgensScratch=mgens}fnTypes[i]=fnTypes[flast];fnTypes.length=flast;if(!queryElemsTmp){queryElemsTmp=queryElems.slice(0,qlast)}const passesUnification=unifyFunctionTypes(fnTypes,queryElemsTmp,whereClause,mgensScratch,mgensScratch=>{if(fnType.generics.length===0&&queryElem.generics.length===0&&fnType.bindings.size===0&&queryElem.bindings.size===0){return!solutionCb||solutionCb(mgensScratch)}const solution=unifyFunctionTypeCheckBindings(fnType,queryElem,whereClause,mgensScratch,unboxingDepth);if(!solution){return false}const simplifiedGenerics=solution.simplifiedGenerics;for(const simplifiedMgens of solution.mgens){const passesUnification=unifyFunctionTypes(simplifiedGenerics,queryElem.generics,whereClause,simplifiedMgens,solutionCb,unboxingDepth);if(passesUnification){return true}}return false},unboxingDepth);if(passesUnification){return true}fnTypes[flast]=fnTypes[i];fnTypes[i]=fnType;fnTypes.length=fl}for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth+1)){continue}let mgensScratch;if(fnType.id<0){mgensScratch=new Map(mgens);if(mgensScratch.has(fnType.id)&&mgensScratch.get(fnType.id)!==0){continue}mgensScratch.set(fnType.id,0)}else{mgensScratch=mgens}const generics=fnType.id<0?whereClause[(-fnType.id)-1]:fnType.generics;const bindings=fnType.bindings?Array.from(fnType.bindings.values()).flat():[];const passesUnification=unifyFunctionTypes(fnTypes.toSpliced(i,1,...generics,...bindings),queryElems,whereClause,mgensScratch,solutionCb,unboxingDepth+1);if(passesUnification){return true}}return false}function unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgensIn){if(!typePassesFilter(queryElem.typeFilter,fnType.ty)){return false}if(fnType.id<0&&queryElem.id<0){if(mgensIn){if(mgensIn.has(fnType.id)&&mgensIn.get(fnType.id)!==queryElem.id){return false}for(const[fid,qid]of mgensIn.entries()){if(fnType.id!==fid&&queryElem.id===qid){return false}if(fnType.id===fid&&queryElem.id!==qid){return false}}}return true}else{if(queryElem.id===typeNameIdOfArrayOrSlice&&(fnType.id===typeNameIdOfSlice||fnType.id===typeNameIdOfArray)){}else if(queryElem.id===typeNameIdOfTupleOrUnit&&(fnType.id===typeNameIdOfTuple||fnType.id===typeNameIdOfUnit)){}else if(queryElem.id===typeNameIdOfHof&&(fnType.id===typeNameIdOfFn||fnType.id===typeNameIdOfFnMut||fnType.id===typeNameIdOfFnOnce)){}else if(fnType.id!==queryElem.id||queryElem.id===null){return false}if((fnType.generics.length+fnType.bindings.size)===0&&queryElem.generics.length!==0){return false}if(fnType.bindings.size0){const fnTypePath=fnType.path!==undefined&&fnType.path!==null?fnType.path.split("::"):[];if(queryElemPathLength>fnTypePath.length){return false}let i=0;for(const path of fnTypePath){if(path===queryElem.pathWithoutLast[i]){i+=1;if(i>=queryElemPathLength){break}}}if(i0){let mgensSolutionSet=[mgensIn];for(const[name,constraints]of queryElem.bindings.entries()){if(mgensSolutionSet.length===0){return false}if(!fnType.bindings.has(name)){return false}const fnTypeBindings=fnType.bindings.get(name);mgensSolutionSet=mgensSolutionSet.flatMap(mgens=>{const newSolutions=[];unifyFunctionTypes(fnTypeBindings,constraints,whereClause,mgens,newMgens=>{newSolutions.push(newMgens);return false},unboxingDepth);return newSolutions})}if(mgensSolutionSet.length===0){return false}const binds=Array.from(fnType.bindings.entries()).flatMap(entry=>{const[name,constraints]=entry;if(queryElem.bindings.has(name)){return[]}else{return constraints}});if(simplifiedGenerics.length>0){simplifiedGenerics=[...simplifiedGenerics,...binds]}else{simplifiedGenerics=binds}return{simplifiedGenerics,mgens:mgensSolutionSet}}return{simplifiedGenerics,mgens:[mgensIn]}}function unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth){if(unboxingDepth>=UNBOXING_LIMIT){return false}if(fnType.id<0&&queryElem.id>=0){if(!whereClause){return false}if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){return false}const mgensTmp=new Map(mgens);mgensTmp.set(fnType.id,null);return checkIfInList(whereClause[(-fnType.id)-1],queryElem,whereClause,mgensTmp,unboxingDepth)}else if(fnType.generics.length>0||fnType.bindings.size>0){const simplifiedGenerics=[...fnType.generics,...Array.from(fnType.bindings.values()).flat(),];return checkIfInList(simplifiedGenerics,queryElem,whereClause,mgens,unboxingDepth)}return false}function checkIfInList(list,elem,whereClause,mgens,unboxingDepth){for(const entry of list){if(checkType(entry,elem,whereClause,mgens,unboxingDepth)){return true}}return false}function checkType(row,elem,whereClause,mgens,unboxingDepth){if(unboxingDepth>=UNBOXING_LIMIT){return false}if(row.bindings.size===0&&elem.bindings.size===0){if(elem.id<0&&mgens===null){return row.id<0||checkIfInList(row.generics,elem,whereClause,mgens,unboxingDepth+1)}if(row.id>0&&elem.id>0&&elem.pathWithoutLast.length===0&&typePassesFilter(elem.typeFilter,row.ty)&&elem.generics.length===0&&elem.id!==typeNameIdOfArrayOrSlice&&elem.id!==typeNameIdOfTupleOrUnit&&elem.id!==typeNameIdOfHof){return row.id===elem.id||checkIfInList(row.generics,elem,whereClause,mgens,unboxingDepth)}}return unifyFunctionTypes([row],[elem],whereClause,mgens,null,unboxingDepth)}function checkPath(contains,ty){if(contains.length===0){return 0}const maxPathEditDistance=Math.floor(contains.reduce((acc,next)=>acc+next.length,0)/3);let ret_dist=maxPathEditDistance+1;const path=ty.path.split("::");if(ty.parent&&ty.parent.name){path.push(ty.parent.name.toLowerCase())}const length=path.length;const clength=contains.length;pathiter:for(let i=length-clength;i>=0;i-=1){let dist_total=0;for(let x=0;xmaxPathEditDistance){continue pathiter}dist_total+=dist}}ret_dist=Math.min(ret_dist,Math.round(dist_total/clength))}return ret_dist>maxPathEditDistance?null:ret_dist}function typePassesFilter(filter,type){if(filter<=NO_TYPE_FILTER||filter===type)return true;const name=itemTypes[type];switch(itemTypes[filter]){case"constant":return name==="associatedconstant";case"fn":return name==="method"||name==="tymethod";case"type":return name==="primitive"||name==="associatedtype";case"trait":return name==="traitalias"}return false}function createAliasFromItem(item){return{crate:item.crate,name:item.name,path:item.path,desc:item.desc,ty:item.ty,parent:item.parent,type:item.type,is_alias:true,deprecated:item.deprecated,implDisambiguator:item.implDisambiguator,}}function handleAliases(ret,query,filterCrates,currentCrate){const lowerQuery=query.toLowerCase();const aliases=[];const crateAliases=[];if(filterCrates!==null){if(ALIASES.has(filterCrates)&&ALIASES.get(filterCrates).has(lowerQuery)){const query_aliases=ALIASES.get(filterCrates).get(lowerQuery);for(const alias of query_aliases){aliases.push(createAliasFromItem(searchIndex[alias]))}}}else{for(const[crate,crateAliasesIndex]of ALIASES){if(crateAliasesIndex.has(lowerQuery)){const pushTo=crate===currentCrate?crateAliases:aliases;const query_aliases=crateAliasesIndex.get(lowerQuery);for(const alias of query_aliases){pushTo.push(createAliasFromItem(searchIndex[alias]))}}}}const sortFunc=(aaa,bbb)=>{if(aaa.path{alias.alias=query;const res=buildHrefAndPath(alias);alias.displayPath=pathSplitter(res[0]);alias.fullPath=alias.displayPath+alias.name;alias.href=res[1];ret.others.unshift(alias);if(ret.others.length>MAX_RESULTS){ret.others.pop()}};aliases.forEach(pushFunc);crateAliases.forEach(pushFunc)}function addIntoResults(results,fullId,id,index,dist,path_dist,maxEditDistance){if(dist<=maxEditDistance||index!==-1){if(results.has(fullId)){const result=results.get(fullId);if(result.dontValidate||result.dist<=dist){return}}results.set(fullId,{id:id,index:index,dontValidate:parsedQuery.literalSearch,dist:dist,path_dist:path_dist,})}}function handleSingleArg(row,pos,elem,results_others,results_in_args,results_returned,maxEditDistance){if(!row||(filterCrates!==null&&row.crate!==filterCrates)){return}let path_dist=0;const fullId=row.id;const tfpDist=compareTypeFingerprints(fullId,parsedQuery.typeFingerprint);if(tfpDist!==null){const in_args=row.type&&row.type.inputs&&checkIfInList(row.type.inputs,elem,row.type.where_clause,null,0);const returned=row.type&&row.type.output&&checkIfInList(row.type.output,elem,row.type.where_clause,null,0);if(in_args){results_in_args.max_dist=Math.max(results_in_args.max_dist||0,tfpDist);const maxDist=results_in_args.sizenormalizedIndex&&normalizedIndex!==-1)){index=normalizedIndex}if(elem.fullPath.length>1){path_dist=checkPath(elem.pathWithoutLast,row);if(path_dist===null){return}}if(parsedQuery.literalSearch){if(row.word===elem.pathLast){addIntoResults(results_others,fullId,pos,index,0,path_dist)}return}const dist=editDistance(row.normalizedName,elem.normalizedPathLast,maxEditDistance);if(index===-1&&dist>maxEditDistance){return}addIntoResults(results_others,fullId,pos,index,dist,path_dist,maxEditDistance)}function handleArgs(row,pos,results){if(!row||(filterCrates!==null&&row.crate!==filterCrates)||!row.type){return}const tfpDist=compareTypeFingerprints(row.id,parsedQuery.typeFingerprint);if(tfpDist===null){return}if(results.size>=MAX_RESULTS&&tfpDist>results.max_dist){return}if(!unifyFunctionTypes(row.type.inputs,parsedQuery.elems,row.type.where_clause,null,mgens=>{return unifyFunctionTypes(row.type.output,parsedQuery.returned,row.type.where_clause,mgens,null,0)},0)){return}results.max_dist=Math.max(results.max_dist||0,tfpDist);addIntoResults(results,row.id,pos,0,tfpDist,0,Number.MAX_VALUE)}function innerRunQuery(){const queryLen=parsedQuery.elems.reduce((acc,next)=>acc+next.pathLast.length,0)+parsedQuery.returned.reduce((acc,next)=>acc+next.pathLast.length,0);const maxEditDistance=Math.floor(queryLen/3);const genericSymbols=new Map();function convertNameToId(elem,isAssocType){if(typeNameIdMap.has(elem.normalizedPathLast)&&(isAssocType||!typeNameIdMap.get(elem.normalizedPathLast).assocOnly)){elem.id=typeNameIdMap.get(elem.normalizedPathLast).id}else if(!parsedQuery.literalSearch){let match=null;let matchDist=maxEditDistance+1;let matchName="";for(const[name,{id,assocOnly}]of typeNameIdMap){const dist=editDistance(name,elem.normalizedPathLast,maxEditDistance);if(dist<=matchDist&&dist<=maxEditDistance&&(isAssocType||!assocOnly)){if(dist===matchDist&&matchName>name){continue}match=id;matchDist=dist;matchName=name}}if(match!==null){parsedQuery.correction=matchName}elem.id=match}if((elem.id===null&&parsedQuery.totalElems>1&&elem.typeFilter===-1&&elem.generics.length===0&&elem.bindings.size===0)||elem.typeFilter===TY_GENERIC){if(genericSymbols.has(elem.name)){elem.id=genericSymbols.get(elem.name)}else{elem.id=-(genericSymbols.size+1);genericSymbols.set(elem.name,elem.id)}if(elem.typeFilter===-1&&elem.name.length>=3){const maxPartDistance=Math.floor(elem.name.length/3);let matchDist=maxPartDistance+1;let matchName="";for(const name of typeNameIdMap.keys()){const dist=editDistance(name,elem.name,maxPartDistance);if(dist<=matchDist&&dist<=maxPartDistance){if(dist===matchDist&&matchName>name){continue}matchDist=dist;matchName=name}}if(matchName!==""){parsedQuery.proposeCorrectionFrom=elem.name;parsedQuery.proposeCorrectionTo=matchName}}elem.typeFilter=TY_GENERIC}if(elem.generics.length>0&&elem.typeFilter===TY_GENERIC){parsedQuery.error=["Generic type parameter ",elem.name," does not accept generic parameters",]}for(const elem2 of elem.generics){convertNameToId(elem2)}elem.bindings=new Map(Array.from(elem.bindings.entries()).map(entry=>{const[name,constraints]=entry;if(!typeNameIdMap.has(name)){parsedQuery.error=["Type parameter ",name," does not exist",];return[null,[]]}for(const elem2 of constraints){convertNameToId(elem2)}return[typeNameIdMap.get(name).id,constraints]}))}const fps=new Set();for(const elem of parsedQuery.elems){convertNameToId(elem);buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint,fps)}for(const elem of parsedQuery.returned){convertNameToId(elem);buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint,fps)}if(parsedQuery.foundElems===1&&parsedQuery.returned.length===0){if(parsedQuery.elems.length===1){const elem=parsedQuery.elems[0];for(let i=0,nSearchIndex=searchIndex.length;i0){const sortQ=(a,b)=>{const ag=a.generics.length===0&&a.bindings.size===0;const bg=b.generics.length===0&&b.bindings.size===0;if(ag!==bg){return ag-bg}const ai=a.id>0;const bi=b.id>0;return ai-bi};parsedQuery.elems.sort(sortQ);parsedQuery.returned.sort(sortQ);for(let i=0,nSearchIndex=searchIndex.length;i");if(tmp.endsWith("")){return tmp.slice(0,tmp.length-6)}return tmp}function addTab(array,query,display){const extraClass=display?" active":"";const output=document.createElement("div");if(array.length>0){output.className="search-results "+extraClass;array.forEach(item=>{const name=item.name;const type=itemTypes[item.ty];const longType=longItemTypes[item.ty];const typeName=longType.length!==0?`${longType}`:"?";const link=document.createElement("a");link.className="result-"+type;link.href=item.href;const resultName=document.createElement("div");resultName.className="result-name";resultName.insertAdjacentHTML("beforeend",`${typeName}`);link.appendChild(resultName);let alias=" ";if(item.is_alias){alias=`
\ +${item.alias} - see \ +
`}resultName.insertAdjacentHTML("beforeend",`
${alias}\ +${item.displayPath}${name}\ +
`);const description=document.createElement("div");description.className="desc";description.insertAdjacentHTML("beforeend",item.desc);link.appendChild(description);output.appendChild(link)})}else if(query.error===null){output.className="search-failed"+extraClass;output.innerHTML="No results :(
"+"Try on DuckDuckGo?

"+"Or try looking in one of these:"}return[output,array.length]}function makeTabHeader(tabNb,text,nbElems){const fmtNbElems=nbElems<10?`\u{2007}(${nbElems})\u{2007}\u{2007}`:nbElems<100?`\u{2007}(${nbElems})\u{2007}`:`\u{2007}(${nbElems})`;if(searchState.currentTab===tabNb){return""}return""}function showResults(results,go_to_first,filterCrates){const search=searchState.outputElement();if(go_to_first||(results.others.length===1&&getSettingValue("go-to-only-result")==="true")){window.onunload=()=>{};searchState.removeQueryParameters();const elem=document.createElement("a");elem.href=results.others[0].href;removeClass(elem,"active");document.body.appendChild(elem);elem.click();return}if(results.query===undefined){results.query=parseQuery(searchState.input.value)}currentResults=results.query.userQuery;const ret_others=addTab(results.others,results.query,true);const ret_in_args=addTab(results.in_args,results.query,false);const ret_returned=addTab(results.returned,results.query,false);let currentTab=searchState.currentTab;if((currentTab===0&&ret_others[1]===0)||(currentTab===1&&ret_in_args[1]===0)||(currentTab===2&&ret_returned[1]===0)){if(ret_others[1]!==0){currentTab=0}else if(ret_in_args[1]!==0){currentTab=1}else if(ret_returned[1]!==0){currentTab=2}}let crates="";if(rawSearchIndex.size>1){crates=" in 
"}let output=`

Results${crates}

`;if(results.query.error!==null){const error=results.query.error;error.forEach((value,index)=>{value=value.split("<").join("<").split(">").join(">");if(index%2!==0){error[index]=`${value.replaceAll(" ", " ")}`}else{error[index]=value}});output+=`

Query parser error: "${error.join("")}".

`;output+="
"+makeTabHeader(0,"In Names",ret_others[1])+"
";currentTab=0}else if(results.query.foundElems<=1&&results.query.returned.length===0){output+="
"+makeTabHeader(0,"In Names",ret_others[1])+makeTabHeader(1,"In Parameters",ret_in_args[1])+makeTabHeader(2,"In Return Types",ret_returned[1])+"
"}else{const signatureTabTitle=results.query.elems.length===0?"In Function Return Types":results.query.returned.length===0?"In Function Parameters":"In Function Signatures";output+="
"+makeTabHeader(0,signatureTabTitle,ret_others[1])+"
";currentTab=0}if(results.query.correction!==null){const orig=results.query.returned.length>0?results.query.returned[0].name:results.query.elems[0].name;output+="

"+`Type "${orig}" not found. `+"Showing results for closest type name "+`"${results.query.correction}" instead.

`}if(results.query.proposeCorrectionFrom!==null){const orig=results.query.proposeCorrectionFrom;const targ=results.query.proposeCorrectionTo;output+="

"+`Type "${orig}" not found and used as generic parameter. `+`Consider searching for "${targ}" instead.

`}const resultsElem=document.createElement("div");resultsElem.id="results";resultsElem.appendChild(ret_others[0]);resultsElem.appendChild(ret_in_args[0]);resultsElem.appendChild(ret_returned[0]);search.innerHTML=output;const crateSearch=document.getElementById("crate-search");if(crateSearch){crateSearch.addEventListener("input",updateCrate)}search.appendChild(resultsElem);searchState.showResults(search);const elems=document.getElementById("search-tabs").childNodes;searchState.focusedByTab=[];let i=0;for(const elem of elems){const j=i;elem.onclick=()=>printTab(j);searchState.focusedByTab.push(null);i+=1}printTab(currentTab)}function updateSearchHistory(url){if(!browserSupportsHistoryApi()){return}const params=searchState.getQueryStringParams();if(!history.state&&!params.search){history.pushState(null,"",url)}else{history.replaceState(null,"",url)}}function search(forced){const query=parseQuery(searchState.input.value.trim());let filterCrates=getFilterCrates();if(!forced&&query.userQuery===currentResults){if(query.userQuery.length>0){putBackSearch()}return}searchState.setLoadingSearch();const params=searchState.getQueryStringParams();if(filterCrates===null&¶ms["filter-crate"]!==undefined){filterCrates=params["filter-crate"]}searchState.title="Results for "+query.original+" - Rust";updateSearchHistory(buildUrl(query.original,filterCrates));showResults(execQuery(query,filterCrates,window.currentCrate),params.go_to_first,filterCrates)}function buildItemSearchTypeAll(types,lowercasePaths){return types.length>0?types.map(type=>buildItemSearchType(type,lowercasePaths)):EMPTY_GENERICS_ARRAY}const EMPTY_BINDINGS_MAP=new Map();const EMPTY_GENERICS_ARRAY=[];let TYPES_POOL=new Map();function buildItemSearchType(type,lowercasePaths,isAssocType){const PATH_INDEX_DATA=0;const GENERICS_DATA=1;const BINDINGS_DATA=2;let pathIndex,generics,bindings;if(typeof type==="number"){pathIndex=type;generics=EMPTY_GENERICS_ARRAY;bindings=EMPTY_BINDINGS_MAP}else{pathIndex=type[PATH_INDEX_DATA];generics=buildItemSearchTypeAll(type[GENERICS_DATA],lowercasePaths);if(type.length>BINDINGS_DATA&&type[BINDINGS_DATA].length>0){bindings=new Map(type[BINDINGS_DATA].map(binding=>{const[assocType,constraints]=binding;return[buildItemSearchType(assocType,lowercasePaths,true).id,buildItemSearchTypeAll(constraints,lowercasePaths),]}))}else{bindings=EMPTY_BINDINGS_MAP}}let result;if(pathIndex<0){result={id:pathIndex,ty:TY_GENERIC,path:null,generics,bindings,}}else if(pathIndex===0){result={id:null,ty:null,path:null,generics,bindings,}}else{const item=lowercasePaths[pathIndex-1];result={id:buildTypeMapIndex(item.name,isAssocType),ty:item.ty,path:item.path,generics,bindings,}}const cr=TYPES_POOL.get(result.id);if(cr){if(cr.generics.length===result.generics.length&&cr.generics!==result.generics&&cr.generics.every((x,i)=>result.generics[i]===x)){result.generics=cr.generics}if(cr.bindings.size===result.bindings.size&&cr.bindings!==result.bindings){let ok=true;for(const[k,v]of cr.bindings.entries()){const v2=result.bindings.get(v);if(!v2){ok=false;break}if(v!==v2&&v.length===v2.length&&v.every((x,i)=>v2[i]===x)){result.bindings.set(k,v)}else if(v!==v2){ok=false;break}}if(ok){result.bindings=cr.bindings}}if(cr.ty===result.ty&&cr.path===result.path&&cr.bindings===result.bindings&&cr.generics===result.generics&&cr.ty===result.ty){return cr}}TYPES_POOL.set(result.id,result);return result}function buildFunctionSearchType(itemFunctionDecoder,lowercasePaths){const c=itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset);itemFunctionDecoder.offset+=1;const[zero,ua,la,ob,cb]=["0","@","`","{","}"].map(c=>c.charCodeAt(0));if(c===la){return null}if(c>=zero&&c>1];itemFunctionDecoder.offset+=1;return sign?-value:value}const functionSearchType=decodeList();const INPUTS_DATA=0;const OUTPUT_DATA=1;let inputs,output;if(typeof functionSearchType[INPUTS_DATA]==="number"){inputs=[buildItemSearchType(functionSearchType[INPUTS_DATA],lowercasePaths)]}else{inputs=buildItemSearchTypeAll(functionSearchType[INPUTS_DATA],lowercasePaths)}if(functionSearchType.length>1){if(typeof functionSearchType[OUTPUT_DATA]==="number"){output=[buildItemSearchType(functionSearchType[OUTPUT_DATA],lowercasePaths)]}else{output=buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA],lowercasePaths)}}else{output=[]}const where_clause=[];const l=functionSearchType.length;for(let i=2;i16){itemFunctionDecoder.backrefQueue.pop()}return ret}function buildFunctionTypeFingerprint(type,output,fps){let input=type.id;if(input===typeNameIdOfArray||input===typeNameIdOfSlice){input=typeNameIdOfArrayOrSlice}if(input===typeNameIdOfTuple||input===typeNameIdOfUnit){input=typeNameIdOfTupleOrUnit}if(input===typeNameIdOfFn||input===typeNameIdOfFnMut||input===typeNameIdOfFnOnce){input=typeNameIdOfHof}const hashint1=k=>{k=(~~k+0x7ed55d16)+(k<<12);k=(k ^ 0xc761c23c)^(k>>>19);k=(~~k+0x165667b1)+(k<<5);k=(~~k+0xd3a2646c)^(k<<9);k=(~~k+0xfd7046c5)+(k<<3);return(k ^ 0xb55a4f09)^(k>>>16)};const hashint2=k=>{k=~k+(k<<15);k ^=k>>>12;k+=k<<2;k ^=k>>>4;k=Math.imul(k,2057);return k ^(k>>16)};if(input!==null){const h0a=hashint1(input);const h0b=hashint2(input);const h1a=~~(h0a+Math.imul(h0b,2));const h1b=~~(h0a+Math.imul(h0b,3));const h2a=~~(h0a+Math.imul(h0b,4));const h2b=~~(h0a+Math.imul(h0b,5));output[0]|=(1<<(h0a%32))|(1<<(h1b%32));output[1]|=(1<<(h1a%32))|(1<<(h2b%32));output[2]|=(1<<(h2a%32))|(1<<(h0b%32));fps.add(input)}for(const g of type.generics){buildFunctionTypeFingerprint(g,output,fps)}const fb={id:null,ty:0,generics:EMPTY_GENERICS_ARRAY,bindings:EMPTY_BINDINGS_MAP,};for(const[k,v]of type.bindings.entries()){fb.id=k;fb.generics=v;buildFunctionTypeFingerprint(fb,output,fps)}output[3]=fps.size}function compareTypeFingerprints(fullId,queryFingerprint){const fh0=functionTypeFingerprint[fullId*4];const fh1=functionTypeFingerprint[(fullId*4)+1];const fh2=functionTypeFingerprint[(fullId*4)+2];const[qh0,qh1,qh2]=queryFingerprint;const[in0,in1,in2]=[fh0&qh0,fh1&qh1,fh2&qh2];if((in0 ^ qh0)||(in1 ^ qh1)||(in2 ^ qh2)){return null}return functionTypeFingerprint[(fullId*4)+3]}function buildIndex(rawSearchIndex){searchIndex=[];const charA="A".charCodeAt(0);let currentIndex=0;let id=0;for(const crate of rawSearchIndex.values()){id+=crate.t.length+1}functionTypeFingerprint=new Uint32Array((id+1)*4);id=0;for(const[crate,crateCorpus]of rawSearchIndex){const crateRow={crate:crate,ty:3,name:crate,path:"",desc:crateCorpus.doc,parent:undefined,type:null,id:id,word:crate,normalizedName:crate.indexOf("_")===-1?crate:crate.replace(/_/g,""),deprecated:null,implDisambiguator:null,};id+=1;searchIndex.push(crateRow);currentIndex+=1;const itemTypes=crateCorpus.t;const itemNames=crateCorpus.n;const itemPaths=new Map(crateCorpus.q);const itemDescs=crateCorpus.d;const itemParentIdxs=crateCorpus.i;const itemFunctionDecoder={string:crateCorpus.f,offset:0,backrefQueue:[],};const deprecatedItems=new Set(crateCorpus.c);const implDisambiguator=new Map(crateCorpus.b);const paths=crateCorpus.p;const aliases=crateCorpus.a;const lowercasePaths=[];let len=paths.length;let lastPath=itemPaths.get(0);for(let i=0;i2){path=itemPaths.has(elem[2])?itemPaths.get(elem[2]):lastPath;lastPath=path}lowercasePaths.push({ty:ty,name:name.toLowerCase(),path:path});paths[i]={ty:ty,name:name,path:path}}lastPath="";len=itemTypes.length;for(let i=0;i0?paths[itemParentIdxs[i]-1]:undefined,type,id:id,word,normalizedName:word.indexOf("_")===-1?word:word.replace(/_/g,""),deprecated:deprecatedItems.has(i),implDisambiguator:implDisambiguator.has(i)?implDisambiguator.get(i):null,};id+=1;searchIndex.push(row);lastPath=row.path}if(aliases){const currentCrateAliases=new Map();ALIASES.set(crate,currentCrateAliases);for(const alias_name in aliases){if(!Object.prototype.hasOwnProperty.call(aliases,alias_name)){continue}let currentNameAliases;if(currentCrateAliases.has(alias_name)){currentNameAliases=currentCrateAliases.get(alias_name)}else{currentNameAliases=[];currentCrateAliases.set(alias_name,currentNameAliases)}for(const local_alias of aliases[alias_name]){currentNameAliases.push(local_alias+currentIndex)}}}currentIndex+=itemTypes.length}TYPES_POOL=new Map()}function onSearchSubmit(e){e.preventDefault();searchState.clearInputTimeout();search()}function putBackSearch(){const search_input=searchState.input;if(!searchState.input){return}if(search_input.value!==""&&!searchState.isDisplayed()){searchState.showResults();if(browserSupportsHistoryApi()){history.replaceState(null,"",buildUrl(search_input.value,getFilterCrates()))}document.title=searchState.title}}function registerSearchEvents(){const params=searchState.getQueryStringParams();if(searchState.input.value===""){searchState.input.value=params.search||""}const searchAfter500ms=()=>{searchState.clearInputTimeout();if(searchState.input.value.length===0){searchState.hideResults()}else{searchState.timeout=setTimeout(search,500)}};searchState.input.onkeyup=searchAfter500ms;searchState.input.oninput=searchAfter500ms;document.getElementsByClassName("search-form")[0].onsubmit=onSearchSubmit;searchState.input.onchange=e=>{if(e.target!==document.activeElement){return}searchState.clearInputTimeout();setTimeout(search,0)};searchState.input.onpaste=searchState.input.onchange;searchState.outputElement().addEventListener("keydown",e=>{if(e.altKey||e.ctrlKey||e.shiftKey||e.metaKey){return}if(e.which===38){const previous=document.activeElement.previousElementSibling;if(previous){previous.focus()}else{searchState.focus()}e.preventDefault()}else if(e.which===40){const next=document.activeElement.nextElementSibling;if(next){next.focus()}const rect=document.activeElement.getBoundingClientRect();if(window.innerHeight-rect.bottom{if(e.which===40){focusSearchResult();e.preventDefault()}});searchState.input.addEventListener("focus",()=>{putBackSearch()});searchState.input.addEventListener("blur",()=>{searchState.input.placeholder=searchState.input.origPlaceholder});if(browserSupportsHistoryApi()){const previousTitle=document.title;window.addEventListener("popstate",e=>{const params=searchState.getQueryStringParams();document.title=previousTitle;currentResults=null;if(params.search&¶ms.search.length>0){searchState.input.value=params.search;e.preventDefault();search()}else{searchState.input.value="";searchState.hideResults()}})}window.onpageshow=()=>{const qSearch=searchState.getQueryStringParams().search;if(searchState.input.value===""&&qSearch){searchState.input.value=qSearch}search()}}function updateCrate(ev){if(ev.target.value==="all crates"){const query=searchState.input.value.trim();updateSearchHistory(buildUrl(query,null))}currentResults=null;search(true)}buildIndex(rawSearchIndex);if(typeof window!=="undefined"){registerSearchEvents();if(window.searchState.getQueryStringParams().search){search()}}if(typeof exports!=="undefined"){exports.initSearch=initSearch;exports.execQuery=execQuery;exports.parseQuery=parseQuery}}if(typeof window!=="undefined"){window.initSearch=initSearch;if(window.searchIndex!==undefined){initSearch(window.searchIndex)}}else{initSearch(new Map())}})() \ No newline at end of file diff --git a/static.files/settings-4313503d2e1961c2.js b/static.files/settings-4313503d2e1961c2.js new file mode 100644 index 0000000..ab425fe --- /dev/null +++ b/static.files/settings-4313503d2e1961c2.js @@ -0,0 +1,17 @@ +"use strict";(function(){const isSettingsPage=window.location.pathname.endsWith("/settings.html");function changeSetting(settingName,value){if(settingName==="theme"){const useSystem=value==="system preference"?"true":"false";updateLocalStorage("use-system-theme",useSystem)}updateLocalStorage(settingName,value);switch(settingName){case"theme":case"preferred-dark-theme":case"preferred-light-theme":updateTheme();updateLightAndDark();break;case"line-numbers":if(value===true){window.rustdoc_add_line_numbers_to_examples()}else{window.rustdoc_remove_line_numbers_from_examples()}break;case"hide-sidebar":if(value===true){addClass(document.documentElement,"hide-sidebar")}else{removeClass(document.documentElement,"hide-sidebar")}break}}function showLightAndDark(){removeClass(document.getElementById("preferred-light-theme"),"hidden");removeClass(document.getElementById("preferred-dark-theme"),"hidden")}function hideLightAndDark(){addClass(document.getElementById("preferred-light-theme"),"hidden");addClass(document.getElementById("preferred-dark-theme"),"hidden")}function updateLightAndDark(){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||(useSystem===null&&getSettingValue("theme")===null)){showLightAndDark()}else{hideLightAndDark()}}function setEvents(settingsElement){updateLightAndDark();onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"),toggle=>{const settingId=toggle.id;const settingValue=getSettingValue(settingId);if(settingValue!==null){toggle.checked=settingValue==="true"}toggle.onchange=()=>{changeSetting(toggle.id,toggle.checked)}});onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"),elem=>{const settingId=elem.name;let settingValue=getSettingValue(settingId);if(settingId==="theme"){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||settingValue===null){settingValue=useSystem==="false"?"light":"system preference"}}if(settingValue!==null&&settingValue!=="null"){elem.checked=settingValue===elem.value}elem.addEventListener("change",ev=>{changeSetting(ev.target.name,ev.target.value)})})}function buildSettingsPageSections(settings){let output="";for(const setting of settings){const js_data_name=setting["js_name"];const setting_name=setting["name"];if(setting["options"]!==undefined){output+=`\ +
+
${setting_name}
+
`;onEach(setting["options"],option=>{const checked=option===setting["default"]?" checked":"";const full=`${js_data_name}-${option.replace(/ /g,"-")}`;output+=`\ + `});output+=`\ +
+
`}else{const checked=setting["default"]===true?" checked":"";output+=`\ +
\ + \ +
`}}return output}function buildSettingsPage(){const theme_names=getVar("themes").split(",").filter(t=>t);theme_names.push("light","dark","ayu");const settings=[{"name":"Theme","js_name":"theme","default":"system preference","options":theme_names.concat("system preference"),},{"name":"Preferred light theme","js_name":"preferred-light-theme","default":"light","options":theme_names,},{"name":"Preferred dark theme","js_name":"preferred-dark-theme","default":"dark","options":theme_names,},{"name":"Auto-hide item contents for large items","js_name":"auto-hide-large-items","default":true,},{"name":"Auto-hide item methods' documentation","js_name":"auto-hide-method-docs","default":false,},{"name":"Auto-hide trait implementation documentation","js_name":"auto-hide-trait-implementations","default":false,},{"name":"Directly go to item in search if there is only one result","js_name":"go-to-only-result","default":false,},{"name":"Show line numbers on code examples","js_name":"line-numbers","default":false,},{"name":"Hide persistent navigation bar","js_name":"hide-sidebar","default":false,},{"name":"Disable keyboard shortcuts","js_name":"disable-shortcuts","default":false,},];const elementKind=isSettingsPage?"section":"div";const innerHTML=`
${buildSettingsPageSections(settings)}
`;const el=document.createElement(elementKind);el.id="settings";if(!isSettingsPage){el.className="popover"}el.innerHTML=innerHTML;if(isSettingsPage){document.getElementById(MAIN_ID).appendChild(el)}else{el.setAttribute("tabindex","-1");getSettingsButton().appendChild(el)}return el}const settingsMenu=buildSettingsPage();function displaySettings(){settingsMenu.style.display="";onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"),el=>{const val=getSettingValue(el.id);const checked=val==="true";if(checked!==el.checked&&val!==null){el.checked=checked}})}function settingsBlurHandler(event){blurHandler(event,getSettingsButton(),window.hidePopoverMenus)}if(isSettingsPage){getSettingsButton().onclick=event=>{event.preventDefault()}}else{const settingsButton=getSettingsButton();const settingsMenu=document.getElementById("settings");settingsButton.onclick=event=>{if(settingsMenu.contains(event.target)){return}event.preventDefault();const shouldDisplaySettings=settingsMenu.style.display==="none";window.hideAllModals();if(shouldDisplaySettings){displaySettings()}};settingsButton.onblur=settingsBlurHandler;settingsButton.querySelector("a").onblur=settingsBlurHandler;onEachLazy(settingsMenu.querySelectorAll("input"),el=>{el.onblur=settingsBlurHandler});settingsMenu.onblur=settingsBlurHandler}setTimeout(()=>{setEvents(settingsMenu);if(!isSettingsPage){displaySettings()}removeClass(getSettingsButton(),"rotate")},0)})() \ No newline at end of file diff --git a/static.files/src-script-e66d777a5a92e9b2.js b/static.files/src-script-e66d777a5a92e9b2.js new file mode 100644 index 0000000..d0aebb8 --- /dev/null +++ b/static.files/src-script-e66d777a5a92e9b2.js @@ -0,0 +1 @@ +"use strict";(function(){const rootPath=getVar("root-path");const NAME_OFFSET=0;const DIRS_OFFSET=1;const FILES_OFFSET=2;const RUSTDOC_MOBILE_BREAKPOINT=700;function closeSidebarIfMobile(){if(window.innerWidth{removeClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","false")};window.rustdocShowSourceSidebar=()=>{addClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","true")};window.rustdocToggleSrcSidebar=()=>{if(document.documentElement.classList.contains("src-sidebar-expanded")){window.rustdocCloseSourceSidebar()}else{window.rustdocShowSourceSidebar()}};function createSrcSidebar(){const container=document.querySelector("nav.sidebar");const sidebar=document.createElement("div");sidebar.id="src-sidebar";let hasFoundFile=false;for(const[key,source]of srcIndex){source[NAME_OFFSET]=key;hasFoundFile=createDirEntry(source,sidebar,"",hasFoundFile)}container.appendChild(sidebar);const selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus()}}function highlightSrcLines(){const match=window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);if(!match){return}let from=parseInt(match[1],10);let to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10)}if(to{onEachLazy(e.getElementsByTagName("a"),i_e=>{removeClass(i_e,"line-highlighted")})});for(let i=from;i<=to;++i){elem=document.getElementById(i);if(!elem){break}addClass(elem,"line-highlighted")}}const handleSrcHighlight=(function(){let prev_line_id=0;const set_fragment=name=>{const x=window.scrollX,y=window.scrollY;if(browserSupportsHistoryApi()){history.replaceState(null,null,"#"+name);highlightSrcLines()}else{location.replace("#"+name)}window.scrollTo(x,y)};return ev=>{let cur_line_id=parseInt(ev.target.id,10);if(isNaN(cur_line_id)||ev.ctrlKey||ev.altKey||ev.metaKey){return}ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){const tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp}set_fragment(prev_line_id+"-"+cur_line_id)}else{prev_line_id=cur_line_id;set_fragment(cur_line_id)}}}());window.addEventListener("hashchange",highlightSrcLines);onEachLazy(document.getElementsByClassName("src-line-numbers"),el=>{el.addEventListener("click",handleSrcHighlight)});highlightSrcLines();window.createSrcSidebar=createSrcSidebar})() \ No newline at end of file diff --git a/static.files/storage-4c98445ec4002617.js b/static.files/storage-4c98445ec4002617.js new file mode 100644 index 0000000..b378b85 --- /dev/null +++ b/static.files/storage-4c98445ec4002617.js @@ -0,0 +1 @@ +"use strict";const builtinThemes=["light","dark","ayu"];const darkThemes=["dark","ayu"];window.currentTheme=document.getElementById("themeStyle");const settingsDataset=(function(){const settingsElement=document.getElementById("default-settings");return settingsElement&&settingsElement.dataset?settingsElement.dataset:null})();function getSettingValue(settingName){const current=getCurrentValue(settingName);if(current===null&&settingsDataset!==null){const def=settingsDataset[settingName.replace(/-/g,"_")];if(def!==undefined){return def}}return current}const localStoredTheme=getSettingValue("theme");function hasClass(elem,className){return elem&&elem.classList&&elem.classList.contains(className)}function addClass(elem,className){if(elem&&elem.classList){elem.classList.add(className)}}function removeClass(elem,className){if(elem&&elem.classList){elem.classList.remove(className)}}function onEach(arr,func){for(const elem of arr){if(func(elem)){return true}}return false}function onEachLazy(lazyArray,func){return onEach(Array.prototype.slice.call(lazyArray),func)}function updateLocalStorage(name,value){try{window.localStorage.setItem("rustdoc-"+name,value)}catch(e){}}function getCurrentValue(name){try{return window.localStorage.getItem("rustdoc-"+name)}catch(e){return null}}const getVar=(function getVar(name){const el=document.querySelector("head > meta[name='rustdoc-vars']");return el?el.attributes["data-"+name].value:null});function switchTheme(newThemeName,saveTheme){const themeNames=getVar("themes").split(",").filter(t=>t);themeNames.push(...builtinThemes);if(themeNames.indexOf(newThemeName)===-1){return}if(saveTheme){updateLocalStorage("theme",newThemeName)}document.documentElement.setAttribute("data-theme",newThemeName);if(builtinThemes.indexOf(newThemeName)!==-1){if(window.currentTheme){window.currentTheme.parentNode.removeChild(window.currentTheme);window.currentTheme=null}}else{const newHref=getVar("root-path")+encodeURIComponent(newThemeName)+getVar("resource-suffix")+".css";if(!window.currentTheme){if(document.readyState==="loading"){document.write(``);window.currentTheme=document.getElementById("themeStyle")}else{window.currentTheme=document.createElement("link");window.currentTheme.rel="stylesheet";window.currentTheme.id="themeStyle";window.currentTheme.href=newHref;document.documentElement.appendChild(window.currentTheme)}}else if(newHref!==window.currentTheme.href){window.currentTheme.href=newHref}}}const updateTheme=(function(){const mql=window.matchMedia("(prefers-color-scheme: dark)");function updateTheme(){if(getSettingValue("use-system-theme")!=="false"){const lightTheme=getSettingValue("preferred-light-theme")||"light";const darkTheme=getSettingValue("preferred-dark-theme")||"dark";updateLocalStorage("use-system-theme","true");switchTheme(mql.matches?darkTheme:lightTheme,true)}else{switchTheme(getSettingValue("theme"),false)}}mql.addEventListener("change",updateTheme);return updateTheme})();if(getSettingValue("use-system-theme")!=="false"&&window.matchMedia){if(getSettingValue("use-system-theme")===null&&getSettingValue("preferred-dark-theme")===null&&darkThemes.indexOf(localStoredTheme)>=0){updateLocalStorage("preferred-dark-theme",localStoredTheme)}}updateTheme();if(getSettingValue("source-sidebar-show")==="true"){addClass(document.documentElement,"src-sidebar-expanded")}if(getSettingValue("hide-sidebar")==="true"){addClass(document.documentElement,"hide-sidebar")}function updateSidebarWidth(){const desktopSidebarWidth=getSettingValue("desktop-sidebar-width");if(desktopSidebarWidth&&desktopSidebarWidth!=="null"){document.documentElement.style.setProperty("--desktop-sidebar-width",desktopSidebarWidth+"px")}const srcSidebarWidth=getSettingValue("src-sidebar-width");if(srcSidebarWidth&&srcSidebarWidth!=="null"){document.documentElement.style.setProperty("--src-sidebar-width",srcSidebarWidth+"px")}}updateSidebarWidth();window.addEventListener("pageshow",ev=>{if(ev.persisted){setTimeout(updateTheme,0);setTimeout(updateSidebarWidth,0)}}) \ No newline at end of file diff --git a/static.files/wheel-7b819b6101059cd0.svg b/static.files/wheel-7b819b6101059cd0.svg new file mode 100644 index 0000000..83c07f6 --- /dev/null +++ b/static.files/wheel-7b819b6101059cd0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/trait.impl/core/clone/trait.Clone.js b/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 0000000..157d74c --- /dev/null +++ b/trait.impl/core/clone/trait.Clone.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Clone for PublicKeyConversionError"],["impl Clone for MultiChoice"],["impl Clone for SingleChoice"],["impl Clone for Opening"],["impl Clone for Curve25519Subgroup"],["impl Clone for Ristretto"],["impl Clone for Params"],["impl Clone for RangeDecomposition"],["impl<'a, G: Clone + Group> Clone for PublicInfo<'a, G>
where\n G::Element: Clone,
"],["impl<C> Clone for Generic<C>"],["impl<G: Group> Clone for Keypair<G>"],["impl<G: Group> Clone for PublicKey<G>"],["impl<G: Group> Clone for SecretKey<G>"],["impl<G: Group, S: ProveSum<G>> Clone for ChoiceParams<G, S>"],["impl<G: Clone + Group> Clone for QuadraticVotingBallot<G>"],["impl<G: Clone + Group> Clone for QuadraticVotingParams<G>"],["impl<G: Clone + Group> Clone for ParticipantCollectingCommitments<G>"],["impl<G: Clone + Group> Clone for ParticipantCollectingPolynomials<G>"],["impl<G: Clone + Group> Clone for ParticipantExchangingSecrets<G>"],["impl<G: Clone + Group> Clone for ActiveParticipant<G>"],["impl<G: Clone + Group> Clone for Dealer<G>"],["impl<G: Clone + Group> Clone for PublicKeySet<G>"],["impl<G: Clone + Group> Clone for CandidateDecryption<G>"],["impl<G: Clone + Group> Clone for Ciphertext<G>
where\n G::Element: Clone,
"],["impl<G: Clone + Group> Clone for CommitmentEquivalenceProof<G>
where\n G::Scalar: Clone,
"],["impl<G: Clone + Group> Clone for DiscreteLogTable<G>"],["impl<G: Clone + Group> Clone for LogEqualityProof<G>
where\n G::Scalar: Clone,
"],["impl<G: Clone + Group> Clone for PreparedRange<G>
where\n G::Element: Clone,
"],["impl<G: Clone + Group> Clone for ProofOfPossession<G>
where\n G::Scalar: Clone,
"],["impl<G: Clone + Group> Clone for RangeProof<G>"],["impl<G: Clone + Group> Clone for RingProof<G>
where\n G::Scalar: Clone,
"],["impl<G: Clone + Group> Clone for SumOfSquaresProof<G>
where\n G::Scalar: Clone,
"],["impl<G: Clone + Group> Clone for VerifiableDecryption<G>
where\n G::Element: Clone,
"],["impl<G: Clone + Group, S: Clone + ProveSum<G>> Clone for EncryptedChoice<G, S>
where\n S::Proof: Clone,
"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/cmp/trait.Eq.js b/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 0000000..d83ee5c --- /dev/null +++ b/trait.impl/core/cmp/trait.Eq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Eq for Curve25519Subgroup"],["impl Eq for Ristretto"],["impl Eq for Params"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/cmp/trait.PartialEq.js b/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 0000000..e881128 --- /dev/null +++ b/trait.impl/core/cmp/trait.PartialEq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl PartialEq for Curve25519Subgroup"],["impl PartialEq for Ristretto"],["impl PartialEq for Params"],["impl PartialEq for RangeDecomposition"],["impl<G> PartialEq for PublicKey<G>
where\n G: Group,
"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/convert/trait.From.js b/trait.impl/core/convert/trait.From.js new file mode 100644 index 0000000..efe7855 --- /dev/null +++ b/trait.impl/core/convert/trait.From.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl<G: Group> From<&SecretKey<G>> for PublicKey<G>"],["impl<G: Group> From<RangeDecomposition> for PreparedRange<G>"],["impl<G: Group> From<SecretKey<G>> for Keypair<G>"],["impl<G: Group> From<VerifiableDecryption<G>> for CandidateDecryption<G>"],["impl<G: Group, V: Zeroize> From<CiphertextWithValue<G, V>> for Ciphertext<G>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/error/trait.Error.js b/trait.impl/core/error/trait.Error.js new file mode 100644 index 0000000..d828123 --- /dev/null +++ b/trait.impl/core/error/trait.Error.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Error for ChoiceVerificationError"],["impl Error for QuadraticVotingError"],["impl Error for Error"],["impl Error for PublicKeyConversionError"],["impl Error for VerificationError"],["impl Error for Error"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/fmt/trait.Debug.js b/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 0000000..8fed270 --- /dev/null +++ b/trait.impl/core/fmt/trait.Debug.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Debug for ChoiceVerificationError"],["impl Debug for QuadraticVotingError"],["impl Debug for Error"],["impl Debug for PublicKeyConversionError"],["impl Debug for VerificationError"],["impl Debug for Error"],["impl Debug for MultiChoice"],["impl Debug for SingleChoice"],["impl Debug for Opening"],["impl Debug for Curve25519Subgroup"],["impl Debug for RandomBytesProvider<'_>"],["impl Debug for Ristretto"],["impl Debug for Params"],["impl Debug for RangeDecomposition"],["impl<'a, G: Debug + Group> Debug for PublicInfo<'a, G>
where\n G::Element: Debug,
"],["impl<C: Debug> Debug for Generic<C>"],["impl<G: Group> Debug for Ciphertext<G>"],["impl<G: Group> Debug for Keypair<G>"],["impl<G: Group> Debug for PublicKey<G>"],["impl<G: Group> Debug for SecretKey<G>"],["impl<G: Debug + Group> Debug for QuadraticVotingBallot<G>"],["impl<G: Debug + Group> Debug for QuadraticVotingParams<G>"],["impl<G: Debug + Group> Debug for ParticipantCollectingCommitments<G>"],["impl<G: Debug + Group> Debug for ParticipantCollectingPolynomials<G>"],["impl<G: Debug + Group> Debug for ParticipantExchangingSecrets<G>"],["impl<G: Debug + Group> Debug for ActiveParticipant<G>"],["impl<G: Debug + Group> Debug for Dealer<G>"],["impl<G: Debug + Group> Debug for PublicKeySet<G>"],["impl<G: Debug + Group> Debug for CandidateDecryption<G>"],["impl<G: Debug + Group> Debug for CommitmentEquivalenceProof<G>
where\n G::Scalar: Debug,
"],["impl<G: Debug + Group> Debug for DiscreteLogTable<G>"],["impl<G: Debug + Group> Debug for LogEqualityProof<G>
where\n G::Scalar: Debug,
"],["impl<G: Debug + Group> Debug for PreparedRange<G>
where\n G::Element: Debug,
"],["impl<G: Debug + Group> Debug for ProofOfPossession<G>
where\n G::Scalar: Debug,
"],["impl<G: Debug + Group> Debug for RangeProof<G>"],["impl<G: Debug + Group> Debug for RingProof<G>
where\n G::Scalar: Debug,
"],["impl<G: Debug + Group> Debug for SumOfSquaresProof<G>
where\n G::Scalar: Debug,
"],["impl<G: Debug + Group> Debug for VerifiableDecryption<G>
where\n G::Element: Debug,
"],["impl<G: Debug + Group, S: Debug + ProveSum<G>> Debug for ChoiceParams<G, S>"],["impl<G: Debug + Group, S: Debug + ProveSum<G>> Debug for EncryptedChoice<G, S>
where\n S::Proof: Debug,
"],["impl<G: Debug + Group, V: Debug + Zeroize> Debug for CiphertextWithValue<G, V>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/fmt/trait.Display.js b/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 0000000..8bb619f --- /dev/null +++ b/trait.impl/core/fmt/trait.Display.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Display for ChoiceVerificationError"],["impl Display for QuadraticVotingError"],["impl Display for Error"],["impl Display for PublicKeyConversionError"],["impl Display for VerificationError"],["impl Display for Error"],["impl Display for RangeDecomposition"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/hash/trait.Hash.js b/trait.impl/core/hash/trait.Hash.js new file mode 100644 index 0000000..974cc25 --- /dev/null +++ b/trait.impl/core/hash/trait.Hash.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Hash for Curve25519Subgroup"],["impl Hash for Ristretto"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Copy.js b/trait.impl/core/marker/trait.Copy.js new file mode 100644 index 0000000..2a7dc61 --- /dev/null +++ b/trait.impl/core/marker/trait.Copy.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Copy for MultiChoice"],["impl Copy for SingleChoice"],["impl Copy for Curve25519Subgroup"],["impl Copy for Ristretto"],["impl Copy for Params"],["impl<C> Copy for Generic<C>"],["impl<G: Copy + Group> Copy for CandidateDecryption<G>"],["impl<G: Copy + Group> Copy for Ciphertext<G>
where\n G::Element: Copy,
"],["impl<G: Copy + Group> Copy for LogEqualityProof<G>
where\n G::Scalar: Copy,
"],["impl<G: Copy + Group> Copy for VerifiableDecryption<G>
where\n G::Element: Copy,
"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Freeze.js b/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 0000000..4a7f9c3 --- /dev/null +++ b/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Freeze for ChoiceVerificationError",1,["elastic_elgamal::app::choice::ChoiceVerificationError"]],["impl Freeze for QuadraticVotingError",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingError"]],["impl Freeze for Error",1,["elastic_elgamal::dkg::Error"]],["impl Freeze for PublicKeyConversionError",1,["elastic_elgamal::keys::PublicKeyConversionError"]],["impl Freeze for VerificationError",1,["elastic_elgamal::proofs::VerificationError"]],["impl Freeze for Error",1,["elastic_elgamal::sharing::Error"]],["impl Freeze for MultiChoice",1,["elastic_elgamal::app::choice::MultiChoice"]],["impl Freeze for SingleChoice",1,["elastic_elgamal::app::choice::SingleChoice"]],["impl Freeze for Opening",1,["elastic_elgamal::dkg::Opening"]],["impl Freeze for Curve25519Subgroup",1,["elastic_elgamal::group::curve25519::Curve25519Subgroup"]],["impl Freeze for Ristretto",1,["elastic_elgamal::group::ristretto::Ristretto"]],["impl Freeze for Params",1,["elastic_elgamal::sharing::Params"]],["impl Freeze for RangeDecomposition",1,["elastic_elgamal::proofs::range::RangeDecomposition"]],["impl<'a> Freeze for RandomBytesProvider<'a>",1,["elastic_elgamal::group::RandomBytesProvider"]],["impl<'a, G> Freeze for PublicInfo<'a, G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::dkg::PublicInfo"]],["impl<C> Freeze for Generic<C>",1,["elastic_elgamal::group::generic::Generic"]],["impl<G> Freeze for QuadraticVotingBallot<G>
where\n <G as ElementOps>::Element: Freeze,\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingBallot"]],["impl<G> Freeze for QuadraticVotingParams<G>
where\n <G as ElementOps>::Element: Freeze,
",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingParams"]],["impl<G> Freeze for ParticipantCollectingCommitments<G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::dkg::ParticipantCollectingCommitments"]],["impl<G> Freeze for ParticipantCollectingPolynomials<G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::dkg::ParticipantCollectingPolynomials"]],["impl<G> Freeze for ParticipantExchangingSecrets<G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::dkg::ParticipantExchangingSecrets"]],["impl<G> Freeze for ActiveParticipant<G>
where\n <G as ElementOps>::Element: Freeze,\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::sharing::participant::ActiveParticipant"]],["impl<G> Freeze for Dealer<G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::sharing::participant::Dealer"]],["impl<G> Freeze for PublicKeySet<G>
where\n <G as ElementOps>::Element: Freeze,
",1,["elastic_elgamal::sharing::key_set::PublicKeySet"]],["impl<G> Freeze for CandidateDecryption<G>
where\n <G as ElementOps>::Element: Freeze,
",1,["elastic_elgamal::decryption::CandidateDecryption"]],["impl<G> Freeze for Ciphertext<G>
where\n <G as ElementOps>::Element: Freeze,
",1,["elastic_elgamal::encryption::Ciphertext"]],["impl<G> Freeze for CommitmentEquivalenceProof<G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::proofs::commitment::CommitmentEquivalenceProof"]],["impl<G> Freeze for DiscreteLogTable<G>",1,["elastic_elgamal::encryption::DiscreteLogTable"]],["impl<G> Freeze for Keypair<G>
where\n <G as ElementOps>::Element: Freeze,\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::keys::Keypair"]],["impl<G> Freeze for LogEqualityProof<G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::proofs::log_equality::LogEqualityProof"]],["impl<G> Freeze for PreparedRange<G>",1,["elastic_elgamal::proofs::range::PreparedRange"]],["impl<G> Freeze for ProofOfPossession<G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::proofs::possession::ProofOfPossession"]],["impl<G> Freeze for PublicKey<G>
where\n <G as ElementOps>::Element: Freeze,
",1,["elastic_elgamal::keys::PublicKey"]],["impl<G> Freeze for RangeProof<G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::proofs::range::RangeProof"]],["impl<G> Freeze for RingProof<G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::proofs::ring::RingProof"]],["impl<G> Freeze for SecretKey<G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::keys::SecretKey"]],["impl<G> Freeze for SumOfSquaresProof<G>
where\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::proofs::mul::SumOfSquaresProof"]],["impl<G> Freeze for VerifiableDecryption<G>
where\n <G as ElementOps>::Element: Freeze,
",1,["elastic_elgamal::decryption::VerifiableDecryption"]],["impl<G, S> Freeze for ChoiceParams<G, S>
where\n S: Freeze,\n <G as ElementOps>::Element: Freeze,
",1,["elastic_elgamal::app::choice::ChoiceParams"]],["impl<G, S> Freeze for EncryptedChoice<G, S>
where\n <S as ProveSum<G>>::Proof: Freeze,\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::app::choice::EncryptedChoice"]],["impl<G, V> Freeze for CiphertextWithValue<G, V>
where\n V: Freeze,\n <G as ElementOps>::Element: Freeze,\n <G as ScalarOps>::Scalar: Freeze,
",1,["elastic_elgamal::encryption::CiphertextWithValue"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Send.js b/trait.impl/core/marker/trait.Send.js new file mode 100644 index 0000000..1f9890a --- /dev/null +++ b/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Send for ChoiceVerificationError",1,["elastic_elgamal::app::choice::ChoiceVerificationError"]],["impl Send for QuadraticVotingError",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingError"]],["impl Send for Error",1,["elastic_elgamal::dkg::Error"]],["impl Send for PublicKeyConversionError",1,["elastic_elgamal::keys::PublicKeyConversionError"]],["impl Send for VerificationError",1,["elastic_elgamal::proofs::VerificationError"]],["impl Send for Error",1,["elastic_elgamal::sharing::Error"]],["impl Send for MultiChoice",1,["elastic_elgamal::app::choice::MultiChoice"]],["impl Send for SingleChoice",1,["elastic_elgamal::app::choice::SingleChoice"]],["impl Send for Opening",1,["elastic_elgamal::dkg::Opening"]],["impl Send for Curve25519Subgroup",1,["elastic_elgamal::group::curve25519::Curve25519Subgroup"]],["impl Send for Ristretto",1,["elastic_elgamal::group::ristretto::Ristretto"]],["impl Send for Params",1,["elastic_elgamal::sharing::Params"]],["impl Send for RangeDecomposition",1,["elastic_elgamal::proofs::range::RangeDecomposition"]],["impl<'a> Send for RandomBytesProvider<'a>",1,["elastic_elgamal::group::RandomBytesProvider"]],["impl<'a, G> Send for PublicInfo<'a, G>
where\n <G as ElementOps>::Element: Send,\n <G as ScalarOps>::Scalar: Sync + Send,
",1,["elastic_elgamal::dkg::PublicInfo"]],["impl<C> Send for Generic<C>
where\n C: Send,
",1,["elastic_elgamal::group::generic::Generic"]],["impl<G> Send for QuadraticVotingBallot<G>
where\n <G as ElementOps>::Element: Send,\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingBallot"]],["impl<G> Send for QuadraticVotingParams<G>
where\n <G as ElementOps>::Element: Send,
",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingParams"]],["impl<G> Send for ParticipantCollectingCommitments<G>
where\n <G as ElementOps>::Element: Send,\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::dkg::ParticipantCollectingCommitments"]],["impl<G> Send for ParticipantCollectingPolynomials<G>
where\n <G as ElementOps>::Element: Send,\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::dkg::ParticipantCollectingPolynomials"]],["impl<G> Send for ParticipantExchangingSecrets<G>
where\n <G as ElementOps>::Element: Send,\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::dkg::ParticipantExchangingSecrets"]],["impl<G> Send for ActiveParticipant<G>
where\n <G as ElementOps>::Element: Send,\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::sharing::participant::ActiveParticipant"]],["impl<G> Send for Dealer<G>
where\n <G as ElementOps>::Element: Send,\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::sharing::participant::Dealer"]],["impl<G> Send for PublicKeySet<G>
where\n <G as ElementOps>::Element: Send,
",1,["elastic_elgamal::sharing::key_set::PublicKeySet"]],["impl<G> Send for CandidateDecryption<G>
where\n <G as ElementOps>::Element: Send,
",1,["elastic_elgamal::decryption::CandidateDecryption"]],["impl<G> Send for Ciphertext<G>
where\n <G as ElementOps>::Element: Send,
",1,["elastic_elgamal::encryption::Ciphertext"]],["impl<G> Send for CommitmentEquivalenceProof<G>
where\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::proofs::commitment::CommitmentEquivalenceProof"]],["impl<G> Send for DiscreteLogTable<G>
where\n G: Send,
",1,["elastic_elgamal::encryption::DiscreteLogTable"]],["impl<G> Send for Keypair<G>
where\n <G as ElementOps>::Element: Send,\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::keys::Keypair"]],["impl<G> Send for LogEqualityProof<G>
where\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::proofs::log_equality::LogEqualityProof"]],["impl<G> Send for PreparedRange<G>
where\n <G as ElementOps>::Element: Send,
",1,["elastic_elgamal::proofs::range::PreparedRange"]],["impl<G> Send for ProofOfPossession<G>
where\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::proofs::possession::ProofOfPossession"]],["impl<G> Send for PublicKey<G>
where\n <G as ElementOps>::Element: Send,
",1,["elastic_elgamal::keys::PublicKey"]],["impl<G> Send for RangeProof<G>
where\n <G as ElementOps>::Element: Send,\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::proofs::range::RangeProof"]],["impl<G> Send for RingProof<G>
where\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::proofs::ring::RingProof"]],["impl<G> Send for SecretKey<G>
where\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::keys::SecretKey"]],["impl<G> Send for SumOfSquaresProof<G>
where\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::proofs::mul::SumOfSquaresProof"]],["impl<G> Send for VerifiableDecryption<G>
where\n <G as ElementOps>::Element: Send,
",1,["elastic_elgamal::decryption::VerifiableDecryption"]],["impl<G, S> Send for ChoiceParams<G, S>
where\n S: Send,\n <G as ElementOps>::Element: Send,
",1,["elastic_elgamal::app::choice::ChoiceParams"]],["impl<G, S> Send for EncryptedChoice<G, S>
where\n <G as ElementOps>::Element: Send,\n <S as ProveSum<G>>::Proof: Send,\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::app::choice::EncryptedChoice"]],["impl<G, V> Send for CiphertextWithValue<G, V>
where\n V: Send,\n <G as ElementOps>::Element: Send,\n <G as ScalarOps>::Scalar: Send,
",1,["elastic_elgamal::encryption::CiphertextWithValue"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.StructuralPartialEq.js b/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 0000000..65867bd --- /dev/null +++ b/trait.impl/core/marker/trait.StructuralPartialEq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl StructuralPartialEq for Curve25519Subgroup"],["impl StructuralPartialEq for Ristretto"],["impl StructuralPartialEq for Params"],["impl StructuralPartialEq for RangeDecomposition"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Sync.js b/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 0000000..2fc4cff --- /dev/null +++ b/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Sync for ChoiceVerificationError",1,["elastic_elgamal::app::choice::ChoiceVerificationError"]],["impl Sync for QuadraticVotingError",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingError"]],["impl Sync for Error",1,["elastic_elgamal::dkg::Error"]],["impl Sync for PublicKeyConversionError",1,["elastic_elgamal::keys::PublicKeyConversionError"]],["impl Sync for VerificationError",1,["elastic_elgamal::proofs::VerificationError"]],["impl Sync for Error",1,["elastic_elgamal::sharing::Error"]],["impl Sync for MultiChoice",1,["elastic_elgamal::app::choice::MultiChoice"]],["impl Sync for SingleChoice",1,["elastic_elgamal::app::choice::SingleChoice"]],["impl Sync for Opening",1,["elastic_elgamal::dkg::Opening"]],["impl Sync for Curve25519Subgroup",1,["elastic_elgamal::group::curve25519::Curve25519Subgroup"]],["impl Sync for Ristretto",1,["elastic_elgamal::group::ristretto::Ristretto"]],["impl Sync for Params",1,["elastic_elgamal::sharing::Params"]],["impl Sync for RangeDecomposition",1,["elastic_elgamal::proofs::range::RangeDecomposition"]],["impl<'a> Sync for RandomBytesProvider<'a>",1,["elastic_elgamal::group::RandomBytesProvider"]],["impl<'a, G> Sync for PublicInfo<'a, G>
where\n <G as ElementOps>::Element: Sync,\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::dkg::PublicInfo"]],["impl<C> Sync for Generic<C>
where\n C: Sync,
",1,["elastic_elgamal::group::generic::Generic"]],["impl<G> Sync for QuadraticVotingBallot<G>
where\n <G as ElementOps>::Element: Sync,\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingBallot"]],["impl<G> Sync for QuadraticVotingParams<G>
where\n <G as ElementOps>::Element: Sync,
",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingParams"]],["impl<G> Sync for ParticipantCollectingCommitments<G>
where\n <G as ElementOps>::Element: Sync,\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::dkg::ParticipantCollectingCommitments"]],["impl<G> Sync for ParticipantCollectingPolynomials<G>
where\n <G as ElementOps>::Element: Sync,\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::dkg::ParticipantCollectingPolynomials"]],["impl<G> Sync for ParticipantExchangingSecrets<G>
where\n <G as ElementOps>::Element: Sync,\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::dkg::ParticipantExchangingSecrets"]],["impl<G> Sync for ActiveParticipant<G>
where\n <G as ElementOps>::Element: Sync,\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::sharing::participant::ActiveParticipant"]],["impl<G> Sync for Dealer<G>
where\n <G as ElementOps>::Element: Sync,\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::sharing::participant::Dealer"]],["impl<G> Sync for PublicKeySet<G>
where\n <G as ElementOps>::Element: Sync,
",1,["elastic_elgamal::sharing::key_set::PublicKeySet"]],["impl<G> Sync for CandidateDecryption<G>
where\n <G as ElementOps>::Element: Sync,
",1,["elastic_elgamal::decryption::CandidateDecryption"]],["impl<G> Sync for Ciphertext<G>
where\n <G as ElementOps>::Element: Sync,
",1,["elastic_elgamal::encryption::Ciphertext"]],["impl<G> Sync for CommitmentEquivalenceProof<G>
where\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::proofs::commitment::CommitmentEquivalenceProof"]],["impl<G> Sync for DiscreteLogTable<G>
where\n G: Sync,
",1,["elastic_elgamal::encryption::DiscreteLogTable"]],["impl<G> Sync for Keypair<G>
where\n <G as ElementOps>::Element: Sync,\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::keys::Keypair"]],["impl<G> Sync for LogEqualityProof<G>
where\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::proofs::log_equality::LogEqualityProof"]],["impl<G> Sync for PreparedRange<G>
where\n <G as ElementOps>::Element: Sync,
",1,["elastic_elgamal::proofs::range::PreparedRange"]],["impl<G> Sync for ProofOfPossession<G>
where\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::proofs::possession::ProofOfPossession"]],["impl<G> Sync for PublicKey<G>
where\n <G as ElementOps>::Element: Sync,
",1,["elastic_elgamal::keys::PublicKey"]],["impl<G> Sync for RangeProof<G>
where\n <G as ElementOps>::Element: Sync,\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::proofs::range::RangeProof"]],["impl<G> Sync for RingProof<G>
where\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::proofs::ring::RingProof"]],["impl<G> Sync for SecretKey<G>
where\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::keys::SecretKey"]],["impl<G> Sync for SumOfSquaresProof<G>
where\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::proofs::mul::SumOfSquaresProof"]],["impl<G> Sync for VerifiableDecryption<G>
where\n <G as ElementOps>::Element: Sync,
",1,["elastic_elgamal::decryption::VerifiableDecryption"]],["impl<G, S> Sync for ChoiceParams<G, S>
where\n S: Sync,\n <G as ElementOps>::Element: Sync,
",1,["elastic_elgamal::app::choice::ChoiceParams"]],["impl<G, S> Sync for EncryptedChoice<G, S>
where\n <G as ElementOps>::Element: Sync,\n <S as ProveSum<G>>::Proof: Sync,\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::app::choice::EncryptedChoice"]],["impl<G, V> Sync for CiphertextWithValue<G, V>
where\n V: Sync,\n <G as ElementOps>::Element: Sync,\n <G as ScalarOps>::Scalar: Sync,
",1,["elastic_elgamal::encryption::CiphertextWithValue"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/marker/trait.Unpin.js b/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 0000000..df104a4 --- /dev/null +++ b/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Unpin for ChoiceVerificationError",1,["elastic_elgamal::app::choice::ChoiceVerificationError"]],["impl Unpin for QuadraticVotingError",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingError"]],["impl Unpin for Error",1,["elastic_elgamal::dkg::Error"]],["impl Unpin for PublicKeyConversionError",1,["elastic_elgamal::keys::PublicKeyConversionError"]],["impl Unpin for VerificationError",1,["elastic_elgamal::proofs::VerificationError"]],["impl Unpin for Error",1,["elastic_elgamal::sharing::Error"]],["impl Unpin for MultiChoice",1,["elastic_elgamal::app::choice::MultiChoice"]],["impl Unpin for SingleChoice",1,["elastic_elgamal::app::choice::SingleChoice"]],["impl Unpin for Opening",1,["elastic_elgamal::dkg::Opening"]],["impl Unpin for Curve25519Subgroup",1,["elastic_elgamal::group::curve25519::Curve25519Subgroup"]],["impl Unpin for Ristretto",1,["elastic_elgamal::group::ristretto::Ristretto"]],["impl Unpin for Params",1,["elastic_elgamal::sharing::Params"]],["impl Unpin for RangeDecomposition",1,["elastic_elgamal::proofs::range::RangeDecomposition"]],["impl<'a> Unpin for RandomBytesProvider<'a>",1,["elastic_elgamal::group::RandomBytesProvider"]],["impl<'a, G> Unpin for PublicInfo<'a, G>
where\n <G as ElementOps>::Element: Unpin,\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::dkg::PublicInfo"]],["impl<C> Unpin for Generic<C>
where\n C: Unpin,
",1,["elastic_elgamal::group::generic::Generic"]],["impl<G> Unpin for QuadraticVotingBallot<G>
where\n <G as ElementOps>::Element: Unpin,\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingBallot"]],["impl<G> Unpin for QuadraticVotingParams<G>
where\n <G as ElementOps>::Element: Unpin,
",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingParams"]],["impl<G> Unpin for ParticipantCollectingCommitments<G>
where\n <G as ElementOps>::Element: Unpin,\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::dkg::ParticipantCollectingCommitments"]],["impl<G> Unpin for ParticipantCollectingPolynomials<G>
where\n <G as ElementOps>::Element: Unpin,\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::dkg::ParticipantCollectingPolynomials"]],["impl<G> Unpin for ParticipantExchangingSecrets<G>
where\n <G as ElementOps>::Element: Unpin,\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::dkg::ParticipantExchangingSecrets"]],["impl<G> Unpin for ActiveParticipant<G>
where\n <G as ElementOps>::Element: Unpin,\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::sharing::participant::ActiveParticipant"]],["impl<G> Unpin for Dealer<G>
where\n <G as ElementOps>::Element: Unpin,\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::sharing::participant::Dealer"]],["impl<G> Unpin for PublicKeySet<G>
where\n <G as ElementOps>::Element: Unpin,
",1,["elastic_elgamal::sharing::key_set::PublicKeySet"]],["impl<G> Unpin for CandidateDecryption<G>
where\n <G as ElementOps>::Element: Unpin,
",1,["elastic_elgamal::decryption::CandidateDecryption"]],["impl<G> Unpin for Ciphertext<G>
where\n <G as ElementOps>::Element: Unpin,
",1,["elastic_elgamal::encryption::Ciphertext"]],["impl<G> Unpin for CommitmentEquivalenceProof<G>
where\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::proofs::commitment::CommitmentEquivalenceProof"]],["impl<G> Unpin for DiscreteLogTable<G>
where\n G: Unpin,
",1,["elastic_elgamal::encryption::DiscreteLogTable"]],["impl<G> Unpin for Keypair<G>
where\n <G as ElementOps>::Element: Unpin,\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::keys::Keypair"]],["impl<G> Unpin for LogEqualityProof<G>
where\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::proofs::log_equality::LogEqualityProof"]],["impl<G> Unpin for PreparedRange<G>
where\n <G as ElementOps>::Element: Unpin,
",1,["elastic_elgamal::proofs::range::PreparedRange"]],["impl<G> Unpin for ProofOfPossession<G>
where\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::proofs::possession::ProofOfPossession"]],["impl<G> Unpin for PublicKey<G>
where\n <G as ElementOps>::Element: Unpin,
",1,["elastic_elgamal::keys::PublicKey"]],["impl<G> Unpin for RangeProof<G>
where\n <G as ElementOps>::Element: Unpin,\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::proofs::range::RangeProof"]],["impl<G> Unpin for RingProof<G>
where\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::proofs::ring::RingProof"]],["impl<G> Unpin for SecretKey<G>
where\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::keys::SecretKey"]],["impl<G> Unpin for SumOfSquaresProof<G>
where\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::proofs::mul::SumOfSquaresProof"]],["impl<G> Unpin for VerifiableDecryption<G>
where\n <G as ElementOps>::Element: Unpin,
",1,["elastic_elgamal::decryption::VerifiableDecryption"]],["impl<G, S> Unpin for ChoiceParams<G, S>
where\n S: Unpin,\n <G as ElementOps>::Element: Unpin,
",1,["elastic_elgamal::app::choice::ChoiceParams"]],["impl<G, S> Unpin for EncryptedChoice<G, S>
where\n <G as ElementOps>::Element: Unpin,\n <S as ProveSum<G>>::Proof: Unpin,\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::app::choice::EncryptedChoice"]],["impl<G, V> Unpin for CiphertextWithValue<G, V>
where\n V: Unpin,\n <G as ElementOps>::Element: Unpin,\n <G as ScalarOps>::Scalar: Unpin,
",1,["elastic_elgamal::encryption::CiphertextWithValue"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/ops/arith/trait.Add.js b/trait.impl/core/ops/arith/trait.Add.js new file mode 100644 index 0000000..18879b3 --- /dev/null +++ b/trait.impl/core/ops/arith/trait.Add.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl<G: Group> Add for Ciphertext<G>"],["impl<G: Group> Add for PublicKey<G>"],["impl<G: Group> Add for SecretKey<G>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/ops/arith/trait.AddAssign.js b/trait.impl/core/ops/arith/trait.AddAssign.js new file mode 100644 index 0000000..bdb4ac6 --- /dev/null +++ b/trait.impl/core/ops/arith/trait.AddAssign.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl<G: Group> AddAssign for Ciphertext<G>"],["impl<G: Group> AddAssign for SecretKey<G>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/ops/arith/trait.Mul.js b/trait.impl/core/ops/arith/trait.Mul.js new file mode 100644 index 0000000..ddc2354 --- /dev/null +++ b/trait.impl/core/ops/arith/trait.Mul.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl<G: Group> Mul<&<G as ScalarOps>::Scalar> for &SecretKey<G>"],["impl<G: Group> Mul<&<G as ScalarOps>::Scalar> for Ciphertext<G>"],["impl<G: Group> Mul<&<G as ScalarOps>::Scalar> for Keypair<G>"],["impl<G: Group> Mul<&<G as ScalarOps>::Scalar> for PublicKey<G>"],["impl<G: Group> Mul<&<G as ScalarOps>::Scalar> for SecretKey<G>"],["impl<G: Group> Mul<u64> for Ciphertext<G>"],["impl<G: Group> Mul<u64> for PublicKey<G>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/ops/arith/trait.Neg.js b/trait.impl/core/ops/arith/trait.Neg.js new file mode 100644 index 0000000..b4f96af --- /dev/null +++ b/trait.impl/core/ops/arith/trait.Neg.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl<G: Group> Neg for Ciphertext<G>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/ops/arith/trait.Sub.js b/trait.impl/core/ops/arith/trait.Sub.js new file mode 100644 index 0000000..4e95bfd --- /dev/null +++ b/trait.impl/core/ops/arith/trait.Sub.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl<G: Group> Sub for Ciphertext<G>"],["impl<G: Group> Sub for SecretKey<G>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/ops/arith/trait.SubAssign.js b/trait.impl/core/ops/arith/trait.SubAssign.js new file mode 100644 index 0000000..ead7a7e --- /dev/null +++ b/trait.impl/core/ops/arith/trait.SubAssign.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl<G: Group> SubAssign for Ciphertext<G>"],["impl<G: Group> SubAssign for SecretKey<G>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/ops/drop/trait.Drop.js b/trait.impl/core/ops/drop/trait.Drop.js new file mode 100644 index 0000000..143c7db --- /dev/null +++ b/trait.impl/core/ops/drop/trait.Drop.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl<G: Group> Drop for SecretKey<G>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 0000000..2cd651e --- /dev/null +++ b/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl RefUnwindSafe for ChoiceVerificationError",1,["elastic_elgamal::app::choice::ChoiceVerificationError"]],["impl RefUnwindSafe for QuadraticVotingError",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingError"]],["impl RefUnwindSafe for Error",1,["elastic_elgamal::dkg::Error"]],["impl RefUnwindSafe for PublicKeyConversionError",1,["elastic_elgamal::keys::PublicKeyConversionError"]],["impl RefUnwindSafe for VerificationError",1,["elastic_elgamal::proofs::VerificationError"]],["impl RefUnwindSafe for Error",1,["elastic_elgamal::sharing::Error"]],["impl RefUnwindSafe for MultiChoice",1,["elastic_elgamal::app::choice::MultiChoice"]],["impl RefUnwindSafe for SingleChoice",1,["elastic_elgamal::app::choice::SingleChoice"]],["impl RefUnwindSafe for Opening",1,["elastic_elgamal::dkg::Opening"]],["impl RefUnwindSafe for Curve25519Subgroup",1,["elastic_elgamal::group::curve25519::Curve25519Subgroup"]],["impl RefUnwindSafe for Ristretto",1,["elastic_elgamal::group::ristretto::Ristretto"]],["impl RefUnwindSafe for Params",1,["elastic_elgamal::sharing::Params"]],["impl RefUnwindSafe for RangeDecomposition",1,["elastic_elgamal::proofs::range::RangeDecomposition"]],["impl<'a> RefUnwindSafe for RandomBytesProvider<'a>",1,["elastic_elgamal::group::RandomBytesProvider"]],["impl<'a, G> RefUnwindSafe for PublicInfo<'a, G>",1,["elastic_elgamal::dkg::PublicInfo"]],["impl<C> RefUnwindSafe for Generic<C>
where\n C: RefUnwindSafe,
",1,["elastic_elgamal::group::generic::Generic"]],["impl<G> RefUnwindSafe for QuadraticVotingBallot<G>",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingBallot"]],["impl<G> RefUnwindSafe for QuadraticVotingParams<G>
where\n <G as ElementOps>::Element: RefUnwindSafe,
",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingParams"]],["impl<G> RefUnwindSafe for ParticipantCollectingCommitments<G>",1,["elastic_elgamal::dkg::ParticipantCollectingCommitments"]],["impl<G> RefUnwindSafe for ParticipantCollectingPolynomials<G>",1,["elastic_elgamal::dkg::ParticipantCollectingPolynomials"]],["impl<G> RefUnwindSafe for ParticipantExchangingSecrets<G>",1,["elastic_elgamal::dkg::ParticipantExchangingSecrets"]],["impl<G> RefUnwindSafe for ActiveParticipant<G>",1,["elastic_elgamal::sharing::participant::ActiveParticipant"]],["impl<G> RefUnwindSafe for Dealer<G>",1,["elastic_elgamal::sharing::participant::Dealer"]],["impl<G> RefUnwindSafe for PublicKeySet<G>
where\n <G as ElementOps>::Element: RefUnwindSafe,
",1,["elastic_elgamal::sharing::key_set::PublicKeySet"]],["impl<G> RefUnwindSafe for CandidateDecryption<G>
where\n <G as ElementOps>::Element: RefUnwindSafe,
",1,["elastic_elgamal::decryption::CandidateDecryption"]],["impl<G> RefUnwindSafe for Ciphertext<G>
where\n <G as ElementOps>::Element: RefUnwindSafe,
",1,["elastic_elgamal::encryption::Ciphertext"]],["impl<G> RefUnwindSafe for CommitmentEquivalenceProof<G>
where\n <G as ScalarOps>::Scalar: RefUnwindSafe,
",1,["elastic_elgamal::proofs::commitment::CommitmentEquivalenceProof"]],["impl<G> RefUnwindSafe for DiscreteLogTable<G>
where\n G: RefUnwindSafe,
",1,["elastic_elgamal::encryption::DiscreteLogTable"]],["impl<G> RefUnwindSafe for Keypair<G>",1,["elastic_elgamal::keys::Keypair"]],["impl<G> RefUnwindSafe for LogEqualityProof<G>
where\n <G as ScalarOps>::Scalar: RefUnwindSafe,
",1,["elastic_elgamal::proofs::log_equality::LogEqualityProof"]],["impl<G> RefUnwindSafe for PreparedRange<G>
where\n <G as ElementOps>::Element: RefUnwindSafe,
",1,["elastic_elgamal::proofs::range::PreparedRange"]],["impl<G> RefUnwindSafe for ProofOfPossession<G>
where\n <G as ScalarOps>::Scalar: RefUnwindSafe,
",1,["elastic_elgamal::proofs::possession::ProofOfPossession"]],["impl<G> RefUnwindSafe for PublicKey<G>
where\n <G as ElementOps>::Element: RefUnwindSafe,
",1,["elastic_elgamal::keys::PublicKey"]],["impl<G> RefUnwindSafe for RangeProof<G>",1,["elastic_elgamal::proofs::range::RangeProof"]],["impl<G> RefUnwindSafe for RingProof<G>
where\n <G as ScalarOps>::Scalar: RefUnwindSafe,
",1,["elastic_elgamal::proofs::ring::RingProof"]],["impl<G> RefUnwindSafe for SecretKey<G>
where\n <G as ScalarOps>::Scalar: RefUnwindSafe,
",1,["elastic_elgamal::keys::SecretKey"]],["impl<G> RefUnwindSafe for SumOfSquaresProof<G>
where\n <G as ScalarOps>::Scalar: RefUnwindSafe,
",1,["elastic_elgamal::proofs::mul::SumOfSquaresProof"]],["impl<G> RefUnwindSafe for VerifiableDecryption<G>
where\n <G as ElementOps>::Element: RefUnwindSafe,
",1,["elastic_elgamal::decryption::VerifiableDecryption"]],["impl<G, S> RefUnwindSafe for ChoiceParams<G, S>",1,["elastic_elgamal::app::choice::ChoiceParams"]],["impl<G, S> RefUnwindSafe for EncryptedChoice<G, S>",1,["elastic_elgamal::app::choice::EncryptedChoice"]],["impl<G, V> RefUnwindSafe for CiphertextWithValue<G, V>",1,["elastic_elgamal::encryption::CiphertextWithValue"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 0000000..87e7bde --- /dev/null +++ b/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl UnwindSafe for ChoiceVerificationError",1,["elastic_elgamal::app::choice::ChoiceVerificationError"]],["impl UnwindSafe for QuadraticVotingError",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingError"]],["impl UnwindSafe for Error",1,["elastic_elgamal::dkg::Error"]],["impl UnwindSafe for PublicKeyConversionError",1,["elastic_elgamal::keys::PublicKeyConversionError"]],["impl UnwindSafe for VerificationError",1,["elastic_elgamal::proofs::VerificationError"]],["impl UnwindSafe for Error",1,["elastic_elgamal::sharing::Error"]],["impl UnwindSafe for MultiChoice",1,["elastic_elgamal::app::choice::MultiChoice"]],["impl UnwindSafe for SingleChoice",1,["elastic_elgamal::app::choice::SingleChoice"]],["impl UnwindSafe for Opening",1,["elastic_elgamal::dkg::Opening"]],["impl UnwindSafe for Curve25519Subgroup",1,["elastic_elgamal::group::curve25519::Curve25519Subgroup"]],["impl UnwindSafe for Ristretto",1,["elastic_elgamal::group::ristretto::Ristretto"]],["impl UnwindSafe for Params",1,["elastic_elgamal::sharing::Params"]],["impl UnwindSafe for RangeDecomposition",1,["elastic_elgamal::proofs::range::RangeDecomposition"]],["impl<'a> !UnwindSafe for RandomBytesProvider<'a>",1,["elastic_elgamal::group::RandomBytesProvider"]],["impl<'a, G> UnwindSafe for PublicInfo<'a, G>",1,["elastic_elgamal::dkg::PublicInfo"]],["impl<C> UnwindSafe for Generic<C>
where\n C: UnwindSafe,
",1,["elastic_elgamal::group::generic::Generic"]],["impl<G> UnwindSafe for QuadraticVotingBallot<G>
where\n <G as ElementOps>::Element: UnwindSafe,\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingBallot"]],["impl<G> UnwindSafe for QuadraticVotingParams<G>
where\n <G as ElementOps>::Element: UnwindSafe,
",1,["elastic_elgamal::app::quadratic_voting::QuadraticVotingParams"]],["impl<G> UnwindSafe for ParticipantCollectingCommitments<G>
where\n <G as ElementOps>::Element: UnwindSafe,\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::dkg::ParticipantCollectingCommitments"]],["impl<G> UnwindSafe for ParticipantCollectingPolynomials<G>
where\n <G as ElementOps>::Element: UnwindSafe,\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::dkg::ParticipantCollectingPolynomials"]],["impl<G> UnwindSafe for ParticipantExchangingSecrets<G>
where\n <G as ElementOps>::Element: UnwindSafe,\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::dkg::ParticipantExchangingSecrets"]],["impl<G> UnwindSafe for ActiveParticipant<G>
where\n <G as ElementOps>::Element: UnwindSafe,\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::sharing::participant::ActiveParticipant"]],["impl<G> UnwindSafe for Dealer<G>
where\n <G as ElementOps>::Element: UnwindSafe,\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::sharing::participant::Dealer"]],["impl<G> UnwindSafe for PublicKeySet<G>
where\n <G as ElementOps>::Element: UnwindSafe,
",1,["elastic_elgamal::sharing::key_set::PublicKeySet"]],["impl<G> UnwindSafe for CandidateDecryption<G>
where\n <G as ElementOps>::Element: UnwindSafe,
",1,["elastic_elgamal::decryption::CandidateDecryption"]],["impl<G> UnwindSafe for Ciphertext<G>
where\n <G as ElementOps>::Element: UnwindSafe,
",1,["elastic_elgamal::encryption::Ciphertext"]],["impl<G> UnwindSafe for CommitmentEquivalenceProof<G>
where\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::proofs::commitment::CommitmentEquivalenceProof"]],["impl<G> UnwindSafe for DiscreteLogTable<G>
where\n G: UnwindSafe,
",1,["elastic_elgamal::encryption::DiscreteLogTable"]],["impl<G> UnwindSafe for Keypair<G>
where\n <G as ElementOps>::Element: UnwindSafe,\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::keys::Keypair"]],["impl<G> UnwindSafe for LogEqualityProof<G>
where\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::proofs::log_equality::LogEqualityProof"]],["impl<G> UnwindSafe for PreparedRange<G>
where\n <G as ElementOps>::Element: UnwindSafe,
",1,["elastic_elgamal::proofs::range::PreparedRange"]],["impl<G> UnwindSafe for ProofOfPossession<G>
where\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::proofs::possession::ProofOfPossession"]],["impl<G> UnwindSafe for PublicKey<G>
where\n <G as ElementOps>::Element: UnwindSafe,
",1,["elastic_elgamal::keys::PublicKey"]],["impl<G> UnwindSafe for RangeProof<G>
where\n <G as ElementOps>::Element: UnwindSafe,\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::proofs::range::RangeProof"]],["impl<G> UnwindSafe for RingProof<G>
where\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::proofs::ring::RingProof"]],["impl<G> UnwindSafe for SecretKey<G>
where\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::keys::SecretKey"]],["impl<G> UnwindSafe for SumOfSquaresProof<G>
where\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::proofs::mul::SumOfSquaresProof"]],["impl<G> UnwindSafe for VerifiableDecryption<G>
where\n <G as ElementOps>::Element: UnwindSafe,
",1,["elastic_elgamal::decryption::VerifiableDecryption"]],["impl<G, S> UnwindSafe for ChoiceParams<G, S>
where\n S: UnwindSafe,\n <G as ElementOps>::Element: UnwindSafe,
",1,["elastic_elgamal::app::choice::ChoiceParams"]],["impl<G, S> UnwindSafe for EncryptedChoice<G, S>
where\n <G as ElementOps>::Element: UnwindSafe,\n <S as ProveSum<G>>::Proof: UnwindSafe,\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::app::choice::EncryptedChoice"]],["impl<G, V> UnwindSafe for CiphertextWithValue<G, V>
where\n V: UnwindSafe,\n <G as ElementOps>::Element: UnwindSafe,\n <G as ScalarOps>::Scalar: UnwindSafe,
",1,["elastic_elgamal::encryption::CiphertextWithValue"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/elastic_elgamal/app/choice/trait.ProveSum.js b/trait.impl/elastic_elgamal/app/choice/trait.ProveSum.js new file mode 100644 index 0000000..730a7b3 --- /dev/null +++ b/trait.impl/elastic_elgamal/app/choice/trait.ProveSum.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/elastic_elgamal/group/trait.ElementOps.js b/trait.impl/elastic_elgamal/group/trait.ElementOps.js new file mode 100644 index 0000000..730a7b3 --- /dev/null +++ b/trait.impl/elastic_elgamal/group/trait.ElementOps.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/elastic_elgamal/group/trait.Group.js b/trait.impl/elastic_elgamal/group/trait.Group.js new file mode 100644 index 0000000..730a7b3 --- /dev/null +++ b/trait.impl/elastic_elgamal/group/trait.Group.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/elastic_elgamal/group/trait.ScalarOps.js b/trait.impl/elastic_elgamal/group/trait.ScalarOps.js new file mode 100644 index 0000000..730a7b3 --- /dev/null +++ b/trait.impl/elastic_elgamal/group/trait.ScalarOps.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/serde/de/trait.Deserialize.js b/trait.impl/serde/de/trait.Deserialize.js new file mode 100644 index 0000000..27798a8 --- /dev/null +++ b/trait.impl/serde/de/trait.Deserialize.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl<'de> Deserialize<'de> for Opening"],["impl<'de> Deserialize<'de> for Params"],["impl<'de, 'a, G: Group> Deserialize<'de> for PublicInfo<'a, G>"],["impl<'de, G: Group> Deserialize<'de> for QuadraticVotingBallot<G>"],["impl<'de, G: Group> Deserialize<'de> for ParticipantCollectingCommitments<G>"],["impl<'de, G: Group> Deserialize<'de> for ParticipantCollectingPolynomials<G>"],["impl<'de, G: Group> Deserialize<'de> for ParticipantExchangingSecrets<G>"],["impl<'de, G: Group> Deserialize<'de> for ActiveParticipant<G>"],["impl<'de, G: Group> Deserialize<'de> for Dealer<G>"],["impl<'de, G: Group> Deserialize<'de> for PublicKeySet<G>"],["impl<'de, G: Group> Deserialize<'de> for CandidateDecryption<G>"],["impl<'de, G: Group> Deserialize<'de> for Ciphertext<G>"],["impl<'de, G: Group> Deserialize<'de> for CommitmentEquivalenceProof<G>"],["impl<'de, G: Group> Deserialize<'de> for Keypair<G>"],["impl<'de, G: Group> Deserialize<'de> for LogEqualityProof<G>"],["impl<'de, G: Group> Deserialize<'de> for ProofOfPossession<G>"],["impl<'de, G: Group> Deserialize<'de> for PublicKey<G>"],["impl<'de, G: Group> Deserialize<'de> for RangeProof<G>"],["impl<'de, G: Group> Deserialize<'de> for RingProof<G>"],["impl<'de, G: Group> Deserialize<'de> for SecretKey<G>"],["impl<'de, G: Group> Deserialize<'de> for SumOfSquaresProof<G>"],["impl<'de, G: Group> Deserialize<'de> for VerifiableDecryption<G>"],["impl<'de, G: Group, S: ProveSum<G>> Deserialize<'de> for EncryptedChoice<G, S>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/trait.impl/serde/ser/trait.Serialize.js b/trait.impl/serde/ser/trait.Serialize.js new file mode 100644 index 0000000..bc5fab1 --- /dev/null +++ b/trait.impl/serde/ser/trait.Serialize.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"elastic_elgamal":[["impl Serialize for Opening"],["impl Serialize for Params"],["impl<'a, G: Group> Serialize for PublicInfo<'a, G>"],["impl<G: Group> Serialize for QuadraticVotingBallot<G>"],["impl<G: Group> Serialize for ParticipantCollectingCommitments<G>"],["impl<G: Group> Serialize for ParticipantCollectingPolynomials<G>"],["impl<G: Group> Serialize for ParticipantExchangingSecrets<G>"],["impl<G: Group> Serialize for ActiveParticipant<G>"],["impl<G: Group> Serialize for Dealer<G>"],["impl<G: Group> Serialize for PublicKeySet<G>"],["impl<G: Group> Serialize for CandidateDecryption<G>"],["impl<G: Group> Serialize for Ciphertext<G>"],["impl<G: Group> Serialize for CommitmentEquivalenceProof<G>"],["impl<G: Group> Serialize for Keypair<G>"],["impl<G: Group> Serialize for LogEqualityProof<G>"],["impl<G: Group> Serialize for ProofOfPossession<G>"],["impl<G: Group> Serialize for PublicKey<G>"],["impl<G: Group> Serialize for RangeProof<G>"],["impl<G: Group> Serialize for RingProof<G>"],["impl<G: Group> Serialize for SecretKey<G>"],["impl<G: Group> Serialize for SumOfSquaresProof<G>"],["impl<G: Group> Serialize for VerifiableDecryption<G>"],["impl<G: Group, S: ProveSum<G>> Serialize for EncryptedChoice<G, S>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/type.impl/curve25519_dalek/edwards/struct.EdwardsBasepointTable.js b/type.impl/curve25519_dalek/edwards/struct.EdwardsBasepointTable.js new file mode 100644 index 0000000..61e0942 --- /dev/null +++ b/type.impl/curve25519_dalek/edwards/struct.EdwardsBasepointTable.js @@ -0,0 +1,3 @@ +(function() {var type_impls = { +"elastic_elgamal":[] +};if (window.register_type_impls) {window.register_type_impls(type_impls);} else {window.pending_type_impls = type_impls;}})() \ No newline at end of file