Skip to content

Commit dbf3416

Browse files
authored
Merge pull request #1 from meyira/lena/add-range-proofs
First draft of range proofs
2 parents 24dac62 + 5e7c232 commit dbf3416

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed

draft-yun-cfrg-arc.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,165 @@ def VerifyPresentationProof(serverPrivateKey, serverPublicKey, requestContext, p
14251425
return verifier.Verify(presentation.proof)
14261426
~~~
14271427

1428+
## Range Proof for Arbitrary Values
1429+
1430+
This section specifies a range proof in the framework of
1431+
{{!SIGMA=I-D.draft-irtf-cfrg-sigma-protocols-00}} to prove a secret value `v` lies
1432+
in an arbitrary interval `[0,upper_bound)`. Before specifying the proof system, we first
1433+
give a brief overview of how it works. For simplicity, assume that `upper_bound` is a
1434+
power of two, that is, `upper_bound == 2^k` for some `k`.
1435+
1436+
To prove a value lies in `[0,(2^k)-1)`, we prove it has a valid `k`-bit representation.
1437+
This is proven by committing to the full value `v`, then all bits of the bit decomposition
1438+
`b` of the value `v`, and then proving each coefficient of the bit decomposition is
1439+
actually `0` or `1` and that the sum of the bits amounts to the full value `v`. This involves the following steps:
1440+
1441+
1. Commit to the bits of `v`. That is, for each bit `b[i]` of the bit decomposition of `v`, let
1442+
`D[i] = b[i] * generatorG + s[i] * generatorH`, where `s[i]` is a blinding scalar.
1443+
2. Prove that `b[i]` is in `{0,1}` by computing proving the algebraic relation `b[i] *
1444+
(b[i]-1) == 0` holds. This quadratic relation can be linearized by
1445+
adding an auxilary witness `s2[i]` and adding the linear relation
1446+
`D[i] == b[i] * D[i] + s2[i] * generatorH` to the equation system. A valid witness `s2[i]` can only
1447+
be computed by the prover if `b[i]` is in `{0,1}`. Successfully computing a witness for
1448+
any other value requires the prover to break the discrete logarithm problem.
1449+
3. Having verified the proof the above relation, the verifier checks the sum by computing
1450+
1451+
~~~
1452+
C == D[0] * 2^0 + D[1] * 2^1 + D[2] * 2^2 + ... + D[k-1] * 2^{k-1}
1453+
~~~
1454+
1455+
The third step is verified outside of the proof by adding the commitments
1456+
homomorphically.
1457+
1458+
To support the general case, where `upper_bound` is not necessarily a power of two,
1459+
we extend the range proof for arbitrary ranges by decomposing the range
1460+
up to the second highest power of two and adding an additional, non-binary range that
1461+
covers the remaining range. This is detailed in `ComputeBases` below.
1462+
1463+
~~~
1464+
def ComputeBases(upper_bound):
1465+
1466+
Inputs:
1467+
1468+
- upper_bound: the maximum value of the range (exclusive), as integer.
1469+
1470+
Outputs:
1471+
1472+
- bases: an array of Scalar bases to represent elements, sorted in descending order. A base is
1473+
either a power of two or a unique remainder that can be used to represent any integer
1474+
in [0, upper_bound).
1475+
1476+
# compute bases to express the commitment as a linear combination of the bit decomposition
1477+
remainder=upper_bound
1478+
bases=[]
1479+
# Generate all but the last power-of-two base.
1480+
for i in range(ceil(log2(upper_bound)) - 1):
1481+
base = 2 ** i
1482+
remainder -= base
1483+
bases.append((G.Scalar(base))
1484+
1485+
if remainder != 0:
1486+
bases.append(remainder - 1)
1487+
1488+
# call sorted on array to ensure the additional base is in correct order
1489+
return sorted(bases, reverse=True)
1490+
~~~
1491+
1492+
Using the bases from `ComputeBases`, the function `ComputeStatementAndWitnesses`
1493+
represents the secret value `v` as a linear combination of the bases, using the resulting
1494+
bit representation to generate the cryptographic commitments and witness values for the
1495+
range proof.
1496+
1497+
~~~
1498+
def ComputeStatementAndWitnesses(v, upper_bound):
1499+
1500+
Inputs:
1501+
1502+
- v: the scalar we want to prove is in range [0, upper_bound)
1503+
- r: randomness for commitment to v
1504+
- upper_bound: the maximum integer value of the range
1505+
1506+
Outputs:
1507+
1508+
- statement: proof statement for the relation
1509+
- [s,s2]: the witness for the equations appended to the statement (the bit
1510+
decomposition, the secret shares of r, and the auxiliary witness s2. Each s2[i] is either zero when
1511+
b[i] is set) or s[i] when b[i] is zero.
1512+
- C: the commitment to v
1513+
- D: the commitments to the bit decomposition of v
1514+
1515+
Parameters:
1516+
- G: group
1517+
- generatorG: Element, equivalent to G.GeneratorG()
1518+
- generatorH: Element, equivalent to G.GeneratorH()
1519+
1520+
Exceptions:
1521+
- NumberTooBigError, raised when v is out of range
1522+
1523+
if G.ScalarToInt(v) >= upper_bound:
1524+
raise NumberTooBigError
1525+
1526+
bases = ComputeBases(upper_bound)
1527+
1528+
# Compute bit decomposition of v.
1529+
b = []
1530+
v_remainder = G.ScalarToInt(v)
1531+
for base in bases:
1532+
# Implementation note: In order to avoid leaking v via a timing channel, this code should be written to be constant time.
1533+
if v_remainder >= base:
1534+
v_remainder -= G.ScalarToInt(base)
1535+
b.append(G.Scalar(1))
1536+
else:
1537+
b.append(G.Scalar(0))
1538+
1539+
# array of group elements where the i-th element corresponds to b[i] * generatorG + s * generatorH
1540+
D = []
1541+
# blinding elements for Pedersen commitment, secret shares of r
1542+
s = []
1543+
# complementing blinders for proof of bit-ness
1544+
s2 = []
1545+
partial_sum = G.Scalar(0)
1546+
for i in range(len(bases) - 1):
1547+
s.append(G.random_scalar())
1548+
partial_sum += s[i]
1549+
s2.append((G.Scalar(1) - b[i]) * s[i])
1550+
D.append(b[i] * generatorG + s[i] * generatorH)
1551+
idx = len(bases) - 1
1552+
s[idx] = r - partial_sum
1553+
s2.append((G.Scalar(1) - b[idx]) * s[idx])
1554+
D.append(b[idx] * generatorG + s[idx] * generatorH)
1555+
1556+
# Compute the Pedersen commitment to the full value of v, using the provided r.
1557+
C = v * generatorG + r * generatorH
1558+
1559+
# start computing the linear relation
1560+
statement = LinearRelation(G)
1561+
1562+
[var_G, var_H, var_C] = statement.allocate_elements(3)
1563+
1564+
# allocate variables for decomposed statements
1565+
vars_b = statement.allocate_scalars(len(b))
1566+
# allocate blinding elements for Pedersen commitment
1567+
vars_s = statement.allocateScalars(len(b))
1568+
# allocate complementing blinders for proof of bit-ness
1569+
vars_s2 = statement.allocateScalars(len(b))
1570+
# allocate bit commitment values
1571+
vars_D = statement.allocateElements(len(b))
1572+
1573+
# Add equations proving each b[i] is in {0,1}
1574+
# For each base, we prove:
1575+
# D[i] = b[i] * generatorG + s[i] * generatorH (b[i] is committed in D[i])
1576+
# b[i] * (b[i] - 1) = 0 (b[i] is 0 or 1)
1577+
for i in range(len(b)):
1578+
statement.set_elements([(vars_D[i], D[i])])
1579+
# add Pedersen commitment to the ith bit.
1580+
statement.append_equation(vars_D[i], [(vars_b[i], var_G), (vars_s[i], var_H)])
1581+
# add statement that b[i] is in {0,1}
1582+
statement.append_equation(vars_D[i], [(vars_b[i], vars_D[i]), (vars_s2[i], var_H)])
1583+
1584+
return (statement, [r, v, b, s, s2], [C,D])
1585+
~~~
1586+
14281587
# Ciphersuites {#ciphersuites}
14291588

14301589
A ciphersuite (also referred to as 'suite' in this document) for the protocol

0 commit comments

Comments
 (0)