From a72cc4887e6f4a74c50a12213524044636f1d9b6 Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Thu, 10 Oct 2024 13:59:51 -0700 Subject: [PATCH] Prio3, Poplar1: Set `xof` to `XofTurboShake128` Previously we imagined making this generic in case we wanted to define variants with different XOFs. We don't have nay in this document, so fix XofTurboShake128 as the XOF as early as possible. While at it, don't overload `Xof` in parameter tables, as this is now just an abstract base class. Likewise for `Idpf` and `Flp`. --- draft-irtf-cfrg-vdaf.md | 109 +++++++++++++++++++--------------------- poc/vdaf_poc/xof.py | 4 +- 2 files changed, 53 insertions(+), 60 deletions(-) diff --git a/draft-irtf-cfrg-vdaf.md b/draft-irtf-cfrg-vdaf.md index 2e351ccd..d492b8bd 100644 --- a/draft-irtf-cfrg-vdaf.md +++ b/draft-irtf-cfrg-vdaf.md @@ -2175,7 +2175,7 @@ def derive_seed(cls, Pre-conditions: - - `len(seed) == Xof.SEED_SIZE` + - `len(seed) == cls.SEED_SIZE` """ xof = cls(seed, dst, binder) return xof.next(cls.SEED_SIZE) @@ -2210,7 +2210,7 @@ def expand_into_vec(cls, Pre-conditions: - `field` is sub-class of `Field` - - `len(seed) == Xof.SEED_SIZE` + - `len(seed) == cls.SEED_SIZE` - `length > 0` """ xof = cls(seed, dst, binder) @@ -2261,9 +2261,8 @@ While XofTurboShake128 as described above can be securely used in all cases where a XOF is needed in the VDAFs described in this document, there are some cases where a more efficient instantiation based on a blockcipher in a fixed-key mode of operation is possible. This is limited to the XOF used inside -the Idpf {{idpf}} implementation in Poplar1 {{idpf-bbcggi21}}. It is NOT -RECOMMENDED to use this XOF anywhere else. See {{security}} for a more detailed -discussion. +the IDPF implementation in Poplar1 {{idpf-bbcggi21}}. It is NOT RECOMMENDED to +use this XOF anywhere else. See {{security}} for a more detailed discussion. The following XOF, denoted XofFixedKeyAes128, uses the AES-128 blockcipher {{AES}}. The length of the domain separation string `dst` MUST NOT exceed 65535 @@ -2626,18 +2625,20 @@ subsections. These methods refer to constants enumerated in | Parameter | Value | |:------------------|:------------------------------------------------| -| `VERIFY_KEY_SIZE` | `Xof.SEED_SIZE` | -| `RAND_SIZE` | `Xof.SEED_SIZE * SHARES if flp.JOINT_RAND_LEN == 0 else 2 * Xof.SEED_SIZE * SHARES` | +| `flp` | an instance of `Flp` ({{flp}}) | +| `xof` | `XofTurboShake128` ({{xof-turboshake128}}) | +| `VERIFY_KEY_SIZE` | `xof.SEED_SIZE` | +| `RAND_SIZE` | `xof.SEED_SIZE * SHARES if flp.JOINT_RAND_LEN == 0 else 2 * xof.SEED_SIZE * SHARES` | | `NONCE_SIZE` | `16` | | `ROUNDS` | `1` | | `SHARES` | in `[2, 256)` | -| `Measurement` | `Flp.Measurement` | +| `Measurement` | as defined by `flp` | | `AggParam` | `None` | | `PublicShare` | `Optional[list[bytes]]` | | `InputShare` | `tuple[list[F], list[F], Optional[bytes]] | tuple[bytes, Optional[bytes]]` | | `OutShare` | `list[F]` | | `AggShare` | `list[F]` | -| `AggResult` | `Flp.AggResult` | +| `AggResult` | as defined by `flp` | | `PrepState` | `tuple[list[F], Optional[bytes]]` | | `PrepShare` | `tuple[list[F], Optional[bytes]]` | | `PrepMessage` | `Optional[bytes]` | @@ -3822,7 +3823,6 @@ All gadgets are listed in {{gadgets}}. | `Valid` | `Count(Field64)` (this section) | | `Field` | `Field64` ({{fields}}) | | `PROOFS` | `1` | -| `Xof` | `XofTurboShake128` ({{xof-turboshake128}}) | {: title="Parameters for Prio3Count."} Our first variant of Prio3 is for a simple counter: each measurement is either @@ -3872,7 +3872,6 @@ class Count(Valid[int, int, F]): | `Valid` | `Sum(Field64, max_measurement)` (this section) | | `Field` | `Field64` ({{fields}}) | | `PROOFS` | `1` | -| `Xof` | `XofTurboShake128` ({{xof-turboshake128}}) | {: title="Parameters for Prio3Sum."} The next variant of Prio3 supports summing of integers in a pre-determined @@ -3965,7 +3964,6 @@ class Sum(Valid[int, int, F]): | `Valid` | `SumVec(Field128, length, bits, chunk_lengh)` (this section) | | `Field` | `Field128` ({{fields}}) | | `PROOFS` | `1` | -| `Xof` | `XofTurboShake128` ({{xof-turboshake128}}) | {: title="Parameters for Prio3SumVec."} This instance of Prio3 supports summing vectors of integers. It has three @@ -4110,7 +4108,6 @@ length will result in proofs up to 50% larger than the optimal proof size. | `Valid` | `Histogram(Field128, length, chunk_lengh)` (this section) | | `Field` | `Field128` ({{fields}}) | | `PROOFS` | `1` | -| `Xof` | `XofTurboShake128` ({{xof-turboshake128}}) | {: title="Parameters for Prio3Histogram."} This variant of Prio3 allows for estimating the distribution of some quantity @@ -4228,7 +4225,6 @@ class Histogram(Valid[int, list[int], F]): | `Valid` | `MultihotCountVec(Field128, length, max_weight, chunk_lengh)` (this section) | | `Field` | `Field128` ({{fields}}) | | `PROOFS` | `1` | -| `Xof` | `XofTurboShake128` ({{xof-turboshake128}}) | {: title="Parameters for Prio3MultihotCountVec."} For this instance of Prio3, each measurement is a vector of ones and zeros, @@ -4437,7 +4433,7 @@ is zero everywhere except for at most one element, which is equal to one. The remainder of this section is structured as follows. IDPFs are defined in {{idpf}}; a concrete instantiation is given {{idpf-bbcggi21}}. The Poplar1 VDAF is defined in {{poplar1-construction}} in terms of a generic IDPF. Finally, a -concrete instantiation of Poplar1 is specified in {{poplar1-inst}}; +concrete instantiation of Poplar1 is specified in {{poplar1-construction}}; test vectors can be found in {{test-vectors}}. ## Incremental Distributed Point Functions (IDPFs) {#idpf} @@ -4453,10 +4449,10 @@ returns an additive share of `beta[L]` if `prefix` is the `L`-bit prefix of Each of the programmed points `beta` is a vector of elements of some finite field. We distinguish two types of fields: one for inner nodes (denoted -`FieldInner`), and one for leaf nodes (`FieldLeaf`). (Our -instantiation of Poplar1 ({{poplar1-inst}}) will use a much larger field for -leaf nodes than for inner nodes. This is to ensure the IDPF is "extractable" as -defined in {{BBCGGI21}}, Definition 1.) +`FieldInner`), and one for leaf nodes (`FieldLeaf`). (Our instantiation of +Poplar1 ({{poplar1-construction}}) will use a much larger field for leaf nodes +than for inner nodes. This is to ensure the IDPF is "extractable" as defined in +{{BBCGGI21}}, Definition 1.) A concrete IDPF defines the types and constants enumerated in {{idpf-param}}. In the remainder we write `Output` as shorthand for the type @@ -4483,7 +4479,7 @@ elements.) The scheme is comprised of the following algorithms: `[0, BITS - 1)`. * `beta_leaf` MUST have length `VALUE_LEN`. * `rand` MUST be generated by a CSPRNG and have length `RAND_SIZE`. - * `nonce` MUST be of length `Idpf.NONCE_SIZE` and chosen uniformly at + * `nonce` MUST be of length `idpf.NONCE_SIZE` and chosen uniformly at random by the Client (see {{nonce-requirements}}). * `idpf.eval(agg_id: int, public_share: PublicShare, key: bytes, level: int, @@ -4573,30 +4569,33 @@ as needed. ## Construction {#poplar1-construction} This section specifies `Poplar1`, an implementation of the `Vdaf` interface -({{vdaf}}). It is defined in terms of any `Idpf` ({{idpf}}) for which -`SHARES == 2` and `VALUE_LEN == 2` and an implementation of `Xof` -({{xof}}). The associated constants and types required by the `Vdaf` interface -are defined in {{poplar1-param}}. The methods required for sharding, -preparation, aggregation, and unsharding are described in the remaining -subsections. These methods make use of constants defined in {{poplar1-const}}. - -| Parameter | Value | -|:------------------|:-------------------------------------| -| `VERIFY_KEY_SIZE` | `Xof.SEED_SIZE` | -| `RAND_SIZE` | `Xof.SEED_SIZE * 3 + Idpf.RAND_SIZE` | -| `NONCE_SIZE` | `16` | -| `ROUNDS` | `2` | -| `SHARES` | `2` | -| `Measurement` | `tuple[bool, ...]` | -| `AggParam` | `tuple[int, Sequence[tuple[bool, ...]]]` | -| `PublicShare` | same as the IDPF | +({{vdaf}}). It is defined in terms of the `Idpf` implementation of +{{idpf-bbcggi21}} with `SHARES == 2` and `VALUE_LEN == 2` and +`XofTurboShake128` as specified in {{xof-turboshake128}}. The associated +constants and types required by the `Vdaf` interface are defined in +{{poplar1-param}}. The methods required for sharding, preparation, aggregation, +and unsharding are described in the remaining subsections. These methods make +use of constants defined in {{poplar1-const}}. + +| Parameter | Value | +|:------------------|:-------------------------------------------| +| `idpf` | as specified in {{idpf-bbcggi21}} | +| `xof` | `XofTurboShake128` ({{xof-turboshake128}}) | +| `VERIFY_KEY_SIZE` | `xof.SEED_SIZE` | +| `RAND_SIZE` | `xof.SEED_SIZE * 3 + idpf.RAND_SIZE` | +| `NONCE_SIZE` | `16` | +| `ROUNDS` | `2` | +| `SHARES` | `2` | +| `Measurement` | `tuple[bool, ...]` | +| `AggParam` | `tuple[int, Sequence[tuple[bool, ...]]]` | +| `PublicShare` | same as the IDPF | | `InputShare` | `tuple[bytes, bytes, list[FieldInner], list[FieldLeaf]]` | -| `OutShare` | `FieldVec` | -| `AggShare` | `FieldVec` | -| `AggResult` | `list[int]` | -| `PrepState` | `tuple[bytes, int, FieldVec]` | -| `PrepShare` | `FieldVec` | -| `PrepMessage` | `Optional[FieldVec]` | +| `OutShare` | `FieldVec` | +| `AggShare` | `FieldVec` | +| `AggResult` | `list[int]` | +| `PrepState` | `tuple[bytes, int, FieldVec]` | +| `PrepShare` | `FieldVec` | +| `PrepMessage` | `Optional[FieldVec]` | {: #poplar1-param title="VDAF parameters for Poplar1."} | Variable | Value | @@ -5254,14 +5253,14 @@ of other artifacts used internally. For performance reasons, we instantiate this object using XofFixedKeyAes128 ({{xof-fixed-key-aes128}}) wherever possible. See {{xof-vs-ro}} for more information. -| Parameter | Value | -|:-----------|:------------------------| -| SHARES | `2` | -| BITS | any positive integer | -| VALUE_LEN | any positive integer | -| KEY_SIZE | `Xof.SEED_SIZE` | -| FieldInner | `Field64` ({{fields}}) | -| FieldLeaf | `Field255` ({{fields}}) | +| Parameter | Value | +|:-------------|:------------------------------| +| `SHARES` | `2` | +| `BITS` | any positive integer | +| `VALUE_LEN` | any positive integer | +| `KEY_SIZE` | `XofFixedKeyAes128.SEED_SIZE` ({{xof-fixed-key-aes128}}) | +| `FieldInner` | `Field64` ({{fields}}) | +| `FieldLeaf` | `Field255` ({{fields}}) | {: #idpf-bbcggi21-param title="Constants and type definitions for the IDPF of BBCGGI21."} ### Overview @@ -5564,12 +5563,6 @@ def current_xof(self, return XofTurboShake128(seed, dst, nonce) ~~~ -## Instantiation {#poplar1-inst} - -By default, Poplar1 is instantiated with the IDPF in {{idpf-bbcggi21}} (`VALUE_LEN -== 2`) and XofTurboShake128 ({{xof-turboshake128}}). This VDAF is suitable for -any positive value of `BITS`. Test vectors can be found in {{test-vectors}}. - # Security Considerations {#security} VDAFs ({{vdaf}}) have two essential security goals: @@ -5931,7 +5924,7 @@ The initial contents of the registry are as follows: | `0x00000003` | Prio3SumVec | VDAF | {{prio3sumvec}} of RFC XXXX | | `0x00000004` | Prio3Histogram | VDAF | {{prio3histogram}} of RFC XXXX | | `0x00000005` | Prio3MultihotCountVec | VDAF | {{prio3multihotcountvec}} of RFC XXXX | -| `0x00000006` | Poplar1 | VDAF | {{poplar1-inst}} of RFC XXXX | +| `0x00000006` | Poplar1 | VDAF | {{poplar1-construction}} of RFC XXXX | | `0xFFFF0000` to `0xFFFFFFFF` | Reserved for Private Use | n/a | n/a | {: #codepoints title="Verifiable Distributed Aggregation Function Identifiers Registry"} diff --git a/poc/vdaf_poc/xof.py b/poc/vdaf_poc/xof.py index 64219383..f96324dc 100644 --- a/poc/vdaf_poc/xof.py +++ b/poc/vdaf_poc/xof.py @@ -61,7 +61,7 @@ def derive_seed(cls, Pre-conditions: - - `len(seed) == Xof.SEED_SIZE` + - `len(seed) == cls.SEED_SIZE` """ xof = cls(seed, dst, binder) return xof.next(cls.SEED_SIZE) @@ -97,7 +97,7 @@ def expand_into_vec(cls, Pre-conditions: - `field` is sub-class of `Field` - - `len(seed) == Xof.SEED_SIZE` + - `len(seed) == cls.SEED_SIZE` - `length > 0` """ xof = cls(seed, dst, binder)