Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Field2 #507

Merged
merged 1 commit into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 22 additions & 23 deletions draft-irtf-cfrg-vdaf.md
Original file line number Diff line number Diff line change
Expand Up @@ -4994,7 +4994,7 @@ The public share of the IDPF scheme in {{idpf-bbcggi21}} consists of a sequence
of "correction words". A correction word has three components:

1. the XOF seed of type `bytes`;
2. the control bits of type `tuple[Field2, Field2]`; and
2. the control bits of type `tuple[bool, bool]`; and
3. the payload of type `list[Field64]` for the first `BITS-1` words and
`list[Field255]` for the last word.

Expand Down Expand Up @@ -5032,7 +5032,7 @@ last bytes are not zero, it throws an error:
~~~ python
control_bits = []
for i in range(length):
control_bits.append(Field2(
control_bits.append(bool(
(packed_control_bits[i // 8] >> (i % 8)) & 1
))
leftover_bits = packed_control_bits[-1] >> (
Expand Down Expand Up @@ -5269,8 +5269,7 @@ full detail.

The description of the IDPF-key generation algorithm makes use of auxiliary
functions `extend()` and `convert()` defined in
{{idpf-bbcggi21-helper-functions}}. In the following, we let `Field2` denote
the field `GF(2)`.
{{idpf-bbcggi21-helper-functions}}.

~~~ python
def gen(
Expand All @@ -5296,34 +5295,34 @@ def gen(
]

seed = key.copy()
ctrl = [Field2(0), Field2(1)]
ctrl = [False, True]
public_share = []
for level in range(self.BITS):
keep = int(alpha[level])
bit = alpha[level]
keep = int(bit)
lose = 1 - keep
bit = Field2(keep)

(s0, t0) = self.extend(level, seed[0], ctx, nonce)
(s1, t1) = self.extend(level, seed[1], ctx, nonce)
seed_cw = xor(s0[lose], s1[lose])
ctrl_cw = (
t0[0] + t1[0] + bit + Field2(1),
t0[1] + t1[1] + bit,
t0[0] ^ t1[0] ^ (not bit),
t0[1] ^ t1[1] ^ bit,
)

# Implementation note: these conditional XORs and
# input-dependent array indices should be replaced with
# constant-time selects in practice in order to reduce
# leakage via timing side channels.
if ctrl[0].int():
if ctrl[0]:
x0 = xor(s0[keep], seed_cw)
ctrl[0] = t0[keep] + ctrl_cw[keep]
ctrl[0] = t0[keep] ^ ctrl_cw[keep]
else:
x0 = s0[keep]
ctrl[0] = t0[keep]
if ctrl[1].int():
if ctrl[1]:
x1 = xor(s1[keep], seed_cw)
ctrl[1] = t1[keep] + ctrl_cw[keep]
ctrl[1] = t1[keep] ^ ctrl_cw[keep]
else:
x1 = s1[keep]
ctrl[1] = t1[keep]
Expand All @@ -5344,7 +5343,7 @@ def gen(
# replaced with a constant time select or a constant time
# multiplication in practice in order to reduce leakage via
# timing side channels.
if ctrl[1].int():
if ctrl[1]:
for i in range(len(w_cw)):
w_cw[i] = -w_cw[i]

Expand Down Expand Up @@ -5386,7 +5385,7 @@ def eval(
# `prefix`. Each node in the tree is represented by a seed
# (`seed`) and a control bit (`ctrl`).
seed = key
ctrl = Field2(agg_id)
ctrl = bool(agg_id)
y: FieldVec
for current_level in range(level + 1):
bit = int(prefix[current_level])
Expand Down Expand Up @@ -5426,12 +5425,12 @@ def eval(
def eval_next(
self,
prev_seed: bytes,
prev_ctrl: Field2,
prev_ctrl: bool,
correction_word: CorrectionWord,
level: int,
bit: int,
ctx: bytes,
nonce: bytes) -> tuple[bytes, Field2, FieldVec]:
nonce: bytes) -> tuple[bytes, bool, FieldVec]:
"""
Compute the next node in the IDPF tree along the path determined
by a candidate prefix. The next node is determined by `bit`, the
Expand All @@ -5447,11 +5446,11 @@ def eval_next(
# input-dependent array indices should be replaced with
# constant-time selects in practice in order to reduce leakage
# via timing side channels.
if prev_ctrl.int():
if prev_ctrl:
s[0] = xor(s[0], seed_cw)
s[1] = xor(s[1], seed_cw)
t[0] += ctrl_cw[0]
t[1] += ctrl_cw[1]
t[0] ^= ctrl_cw[0]
t[1] ^= ctrl_cw[1]

next_ctrl = t[bit]
convert_output = self.convert(level, s[bit], ctx, nonce)
Expand All @@ -5460,7 +5459,7 @@ def eval_next(
# Implementation note: this conditional addition should be
# replaced with a constant-time select in practice in order to
# reduce leakage via timing side channels.
if next_ctrl.int():
if next_ctrl:
for i in range(len(y)):
y[i] += w_cw[i]

Expand All @@ -5475,7 +5474,7 @@ def extend(
level: int,
seed: bytes,
ctx: bytes,
nonce: bytes) -> tuple[list[bytes], list[Field2]]:
nonce: bytes) -> tuple[list[bytes], list[bool]]:
xof = self.current_xof(
level,
seed,
Expand All @@ -5489,7 +5488,7 @@ def extend(
# Use the least significant bits as the control bit correction,
# and then zero it out. This gives effectively 127 bits of
# security, but reduces the number of AES calls needed by 1/3.
t = [Field2(s[0][0] & 1), Field2(s[1][0] & 1)]
t = [bool(s[0][0] & 1), bool(s[1][0] & 1)]
s[0][0] &= 0xFE
s[1][0] &= 0xFE
return ([bytes(s[0]), bytes(s[1])], t)
Expand Down
12 changes: 2 additions & 10 deletions poc/tests/test_field.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import random
import unittest

from vdaf_poc.field import (Field, Field2, Field64, Field96, Field128,
Field255, NttField, poly_eval, poly_interp)
from vdaf_poc.field import (Field, Field64, Field96, Field128, Field255,
NttField, poly_eval, poly_interp)


class TestFields(unittest.TestCase):
Expand Down Expand Up @@ -60,14 +60,6 @@ def test_field128(self) -> None:
def test_field255(self) -> None:
self.run_field_test(Field255)

def test_field2(self) -> None:
# Test GF(2).
self.assertEqual(Field2(1).int(), 1)
self.assertEqual(Field2(0).int(), 0)
self.assertEqual(Field2(1) + Field2(1), Field2(0))
self.assertEqual(Field2(1) * Field2(1), Field2(1))
self.assertEqual(-Field2(1), Field2(1))

def test_interp(self) -> None:
# Test polynomial interpolation.
cls = Field64
Expand Down
7 changes: 0 additions & 7 deletions poc/vdaf_poc/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,6 @@ def gen(cls) -> Self:
raise NotImplementedError()


class Field2(Field):
"""The finite field GF(2)."""

MODULUS = 2
ENCODED_SIZE = 1


class Field64(NttField):
"""The finite field GF(2^32 * 4294967295 + 1)."""

Expand Down
50 changes: 25 additions & 25 deletions poc/vdaf_poc/idpf_bbcggi21.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import Sequence, TypeAlias, cast

from vdaf_poc.common import format_dst, front, vec_add, vec_neg, vec_sub, xor
from vdaf_poc.field import Field, Field2, Field64, Field255
from vdaf_poc.field import Field, Field64, Field255
from vdaf_poc.idpf import Idpf
from vdaf_poc.xof import Xof, XofFixedKeyAes128, XofTurboShake128

Expand All @@ -24,7 +24,7 @@
# types, which aids with self-documentation.

FieldVec: TypeAlias = list[Field64] | list[Field255]
CorrectionWord: TypeAlias = tuple[bytes, tuple[Field2, Field2], FieldVec]
CorrectionWord: TypeAlias = tuple[bytes, tuple[bool, bool], FieldVec]


class IdpfBBCGGI21(Idpf[Field64, Field255, list[CorrectionWord]]):
Expand Down Expand Up @@ -89,34 +89,34 @@ def gen(
]

seed = key.copy()
ctrl = [Field2(0), Field2(1)]
ctrl = [False, True]
public_share = []
for level in range(self.BITS):
keep = int(alpha[level])
bit = alpha[level]
keep = int(bit)
lose = 1 - keep
bit = Field2(keep)

(s0, t0) = self.extend(level, seed[0], ctx, nonce)
(s1, t1) = self.extend(level, seed[1], ctx, nonce)
seed_cw = xor(s0[lose], s1[lose])
ctrl_cw = (
t0[0] + t1[0] + bit + Field2(1),
t0[1] + t1[1] + bit,
t0[0] ^ t1[0] ^ (not bit),
t0[1] ^ t1[1] ^ bit,
)

# Implementation note: these conditional XORs and
# input-dependent array indices should be replaced with
# constant-time selects in practice in order to reduce
# leakage via timing side channels.
if ctrl[0].int():
if ctrl[0]:
x0 = xor(s0[keep], seed_cw)
ctrl[0] = t0[keep] + ctrl_cw[keep]
ctrl[0] = t0[keep] ^ ctrl_cw[keep]
else:
x0 = s0[keep]
ctrl[0] = t0[keep]
if ctrl[1].int():
if ctrl[1]:
x1 = xor(s1[keep], seed_cw)
ctrl[1] = t1[keep] + ctrl_cw[keep]
ctrl[1] = t1[keep] ^ ctrl_cw[keep]
else:
x1 = s1[keep]
ctrl[1] = t1[keep]
Expand All @@ -137,7 +137,7 @@ def gen(
# replaced with a constant time select or a constant time
# multiplication in practice in order to reduce leakage via
# timing side channels.
if ctrl[1].int():
if ctrl[1]:
for i in range(len(w_cw)):
w_cw[i] = -w_cw[i]

Expand Down Expand Up @@ -177,7 +177,7 @@ def eval(
# `prefix`. Each node in the tree is represented by a seed
# (`seed`) and a control bit (`ctrl`).
seed = key
ctrl = Field2(agg_id)
ctrl = bool(agg_id)
y: FieldVec
for current_level in range(level + 1):
bit = int(prefix[current_level])
Expand Down Expand Up @@ -217,12 +217,12 @@ def eval(
def eval_next(
self,
prev_seed: bytes,
prev_ctrl: Field2,
prev_ctrl: bool,
correction_word: CorrectionWord,
level: int,
bit: int,
ctx: bytes,
nonce: bytes) -> tuple[bytes, Field2, FieldVec]:
nonce: bytes) -> tuple[bytes, bool, FieldVec]:
"""
Compute the next node in the IDPF tree along the path determined
by a candidate prefix. The next node is determined by `bit`, the
Expand All @@ -238,11 +238,11 @@ def eval_next(
# input-dependent array indices should be replaced with
# constant-time selects in practice in order to reduce leakage
# via timing side channels.
if prev_ctrl.int():
if prev_ctrl:
s[0] = xor(s[0], seed_cw)
s[1] = xor(s[1], seed_cw)
t[0] += ctrl_cw[0]
t[1] += ctrl_cw[1]
t[0] ^= ctrl_cw[0]
t[1] ^= ctrl_cw[1]

next_ctrl = t[bit]
convert_output = self.convert(level, s[bit], ctx, nonce)
Expand All @@ -251,7 +251,7 @@ def eval_next(
# Implementation note: this conditional addition should be
# replaced with a constant-time select in practice in order to
# reduce leakage via timing side channels.
if next_ctrl.int():
if next_ctrl:
for i in range(len(y)):
y[i] += w_cw[i]

Expand All @@ -267,7 +267,7 @@ def extend(
level: int,
seed: bytes,
ctx: bytes,
nonce: bytes) -> tuple[list[bytes], list[Field2]]:
nonce: bytes) -> tuple[list[bytes], list[bool]]:
xof = self.current_xof(
level,
seed,
Expand All @@ -281,7 +281,7 @@ def extend(
# Use the least significant bits as the control bit correction,
# and then zero it out. This gives effectively 127 bits of
# security, but reduces the number of AES calls needed by 1/3.
t = [Field2(s[0][0] & 1), Field2(s[1][0] & 1)]
t = [bool(s[0][0] & 1), bool(s[1][0] & 1)]
s[0][0] &= 0xFE
s[1][0] &= 0xFE
return ([bytes(s[0]), bytes(s[1])], t)
Expand Down Expand Up @@ -356,28 +356,28 @@ def test_vec_encode_public_share(self, public_share: list[CorrectionWord]) -> by
return self.encode_public_share(public_share)


def pack_bits(control_bits: list[Field2]) -> bytes:
def pack_bits(control_bits: list[bool]) -> bytes:
packed_len = (len(control_bits) + 7) // 8
# NOTE: The following is excerpted in the document, de-indented. Thee width
# should be limited to 69 columns after de-indenting, or 73 columns before,
# to avoid warnings from xml2rfc.
# ===================================================================
packed_control_buf = [int(0)] * packed_len
for i, bit in enumerate(control_bits):
packed_control_buf[i // 8] |= bit.int() << (i % 8)
packed_control_buf[i // 8] |= bit << (i % 8)
packed_control_bits = bytes(packed_control_buf)
# NOTE: End of exerpt.
return packed_control_bits


def unpack_bits(packed_control_bits: bytes, length: int) -> list[Field2]:
def unpack_bits(packed_control_bits: bytes, length: int) -> list[bool]:
# NOTE: The following is excerpted in the document, de-indented. Thee width
# should be limited to 69 columns after de-indenting, or 73 columns before,
# to avoid warnings from xml2rfc.
# ===================================================================
control_bits = []
for i in range(length):
control_bits.append(Field2(
control_bits.append(bool(
(packed_control_bits[i // 8] >> (i % 8)) & 1
))
leftover_bits = packed_control_bits[-1] >> (
Expand Down