Skip to content

Commit 949444a

Browse files
committed
Merge #14: halfagg: Fix z_0 = 1 as in CZ22
f268acf halfagg: Run rustfmt (Tim Ruffing) 5f9a3d6 halfagg: Fix z_0 = 1 as in CZ22 (Tim Ruffing) 1ab7cd8 halfagg: Extract computation of z into function randomizer() (Tim Ruffing) Pull request description: ACKs for top commit: jonasnick: ACK f268acf Tree-SHA512: d66cd077dc7c41f2b5beb0197ff5965cc58e5067df1040e3b4863d579159b9e32e4b22dff1b7d527dc9a0c2142bbb51aaf559e401095baacd261ba76860c5dfc
2 parents 8a9d6fb + f268acf commit 949444a

File tree

3 files changed

+32
-29
lines changed

3 files changed

+32
-29
lines changed

hacspec-halfagg/src/halfagg.rs

+16-12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const HALFAGG_RANDOMIZER: TaggedHashHalfAggPrefix = TaggedHashHalfAggPrefix([
2020
0x48u8, 0x61u8, 0x6cu8, 0x66u8, 0x41u8, 0x67u8, 0x67u8, 0x2fu8, 0x72u8, 0x61u8, 0x6eu8, 0x64u8,
2121
0x6fu8, 0x6du8, 0x69u8, 0x7au8, 0x65u8, 0x72u8,
2222
]);
23+
2324
pub fn hash_halfagg(input: &Seq<(PublicKey, Message, Bytes32)>) -> Bytes32 {
2425
let mut c = ByteSeq::new(0);
2526
for i in 0..input.len() {
@@ -29,6 +30,19 @@ pub fn hash_halfagg(input: &Seq<(PublicKey, Message, Bytes32)>) -> Bytes32 {
2930
tagged_hash(&PublicByteSeq::from_seq(&HALFAGG_RANDOMIZER), &c)
3031
}
3132

33+
pub fn randomizer(pmr: &Seq<(PublicKey, Message, Bytes32)>, index: usize) -> Scalar {
34+
if index == 0 {
35+
Scalar::ONE()
36+
} else {
37+
// TODO: The following line hashes i elements and therefore leads to
38+
// quadratic runtime. Instead, we should cache the intermediate result
39+
// and only hash the new element.
40+
scalar_from_bytes(hash_halfagg(
41+
&Seq::<(PublicKey, Message, Bytes32)>::from_slice(pmr, 0, index + 1),
42+
))
43+
}
44+
}
45+
3246
pub type AggregateResult = Result<AggSig, Error>;
3347
pub fn aggregate(pms: &Seq<(PublicKey, Message, Signature)>) -> AggregateResult {
3448
let aggsig = AggSig::new(32);
@@ -59,12 +73,7 @@ pub fn inc_aggregate(
5973
for i in v..v + u {
6074
let (pk, msg, sig) = pms_to_agg[i - v];
6175
pmr[i] = (pk, msg, Bytes32::from_slice(&sig, 0, 32));
62-
// TODO: The following line hashes i elements and therefore leads to
63-
// quadratic runtime. Instead, we should cache the intermediate result
64-
// and only hash the new element.
65-
let z = scalar_from_bytes(hash_halfagg(
66-
&Seq::<(PublicKey, Message, Bytes32)>::from_slice(&pmr, 0, i + 1),
67-
));
76+
let z = randomizer(&pmr, i);
6877
s = s + z * Scalar::from_byte_seq_be(&Bytes32::from_slice(&sig, 32, 32));
6978
}
7079
let mut ret = Seq::<U8>::new(0);
@@ -113,12 +122,7 @@ pub fn verify_aggregate(aggsig: &AggSig, pm_aggd: &Seq<(PublicKey, Message)>) ->
113122
let r = r_res.unwrap();
114123
let e = scalar_from_bytes(hash_challenge(rx, bytes_from_point(p), msg));
115124
pmr[i] = (pk, msg, rx);
116-
// TODO: The following line hashes i elements and therefore leads to
117-
// quadratic runtime. Instead, we should cache the intermediate result
118-
// and only hash the new element.
119-
let z = scalar_from_bytes(hash_halfagg(
120-
&Seq::<(PublicKey, Message, Bytes32)>::from_slice(&pmr, 0, i + 1),
121-
));
125+
let z = randomizer(&pmr, i);
122126
terms[2 * i] = (z, r);
123127
terms[2 * i + 1] = (z * e, p);
124128
}

hacspec-halfagg/tests/tests.rs

+7-14
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,9 @@ fn test_verify_vectors_process(
7979
fn test_verify_vectors() {
8080
#[rustfmt::skip]
8181
let vectors_raw = vec![
82-
(vec![],
83-
"0000000000000000000000000000000000000000000000000000000000000000"),
84-
(vec![("1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "0202020202020202020202020202020202020202020202020202020202020202"),],
85-
"b070aafcea439a4f6f1bbfc2eb66d29d24b0cab74d6b745c3cfb009cc8fe4aa8108f33907612fb748419ebc4004b3169e16e35d5f12b693b6bbc3d4a6982f2f6"),
86-
(vec![("1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "0202020202020202020202020202020202020202020202020202020202020202"),
87-
("462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b", "0505050505050505050505050505050505050505050505050505050505050505"),],
88-
"b070aafcea439a4f6f1bbfc2eb66d29d24b0cab74d6b745c3cfb009cc8fe4aa8a3afbdb45a6a34bf7c8c00f1b6d7e7d375b54540f13716c87b62e51e2f4f22ffc211db48479c2f546d52b07955e764eb6a142d577245f40a44f5dee468da4244"),
82+
(vec![], "0000000000000000000000000000000000000000000000000000000000000000"),
83+
(vec![("1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "0202020202020202020202020202020202020202020202020202020202020202"),], "b070aafcea439a4f6f1bbfc2eb66d29d24b0cab74d6b745c3cfb009cc8fe4aa80e066c34819936549ff49b6fd4d41edfc401a367b87ddd59fee38177961c225f"),
84+
(vec![("1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "0202020202020202020202020202020202020202020202020202020202020202"),("462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b", "0505050505050505050505050505050505050505050505050505050505050505"),], "b070aafcea439a4f6f1bbfc2eb66d29d24b0cab74d6b745c3cfb009cc8fe4aa8a3afbdb45a6a34bf7c8c00f1b6d7e7d375b54540f13716c87b62e51e2f4f22ffbf8913ec53226a34892d60252a7052614ca79ae939986828d81d2311957371ad"),
8985
];
9086
let vectors = test_verify_vectors_process(&vectors_raw);
9187
// Uncomment to generate and print test vectors:
@@ -154,12 +150,7 @@ fn test_aggregate_verify_strange() {
154150
for i in 0..2 {
155151
let (pk, msg, sig) = pms_triples[i];
156152
pmr = pmr.push(&(pk, msg, Bytes32::from_slice(&sig, 0, 32)));
157-
// TODO: The following line hashes i elements and therefore leads to
158-
// quadratic runtime. Instead, we should cache the intermediate result
159-
// and only hash the new element.
160-
z = z.push(&scalar_from_bytes(hash_halfagg(
161-
&Seq::<(PublicKey, Message, Bytes32)>::from_slice(&pmr, 0, i + 1),
162-
)));
153+
z = z.push(&randomizer(&pmr, i));
163154
}
164155

165156
// Shift signatures appropriately
@@ -220,5 +211,7 @@ fn test_edge_cases() {
220211
inc_aggregate(&aggsig, &empty_pm, &big_pms).unwrap_err()
221212
== hacspec_halfagg::Error::AggSigTooBig
222213
);
223-
assert!(verify_aggregate(&aggsig, &big_pm).unwrap_err() == hacspec_halfagg::Error::AggSigTooBig);
214+
assert!(
215+
verify_aggregate(&aggsig, &big_pm).unwrap_err() == hacspec_halfagg::Error::AggSigTooBig
216+
);
224217
}

half-aggregation.mediawiki

+9-3
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Moreover, they came up with an elegant approach to incremental aggregation that
6161
* Incremental aggregation allows non-interactively aggregating additional BIP 340 signatures into an existing half-aggregate signature.
6262
* A half-aggregate signature of ''u'' BIP 340 input signatures is serialized as the ''(u+1)⋅32''-byte array ''r<sub>1</sub> || ... || r<sub>u</sub> || bytes(s)'' where ''r<sub>i</sub>'' is a 32-byte array from input signature ''i'' and ''s'' is a scalar aggregate (see below for details).
6363
* This document does ''not'' specify the aggregation of multiple aggregate signatures (yet). It is possible, but requires changing the encoding of an aggregate signature. Since it is not possible to undo the aggregation of the s-values, when verifying of such an aggregate signature the randomizers need to be the same as when verifying the individual aggregate signature. Therefore, the aggregate signature needs to encode a tree that reveals how the individual signatures were aggregated and how the resulting aggregate signatures were reaggregated.
64-
* There is a possible optimization where the first randomizer ''z<sub>0</sub>'' is set to the constant ''1'' which speeds up verification because ''z<sub>0</sub>⋅R<sub>0</sub> = R<sub>0</sub>''. This specification does not make use of this optimization yet (TODO).
64+
* The first randomizer ''z<sub>0</sub>'' is fixed to the constant ''1'', which speeds up verification because ''z<sub>0</sub>⋅R<sub>0</sub> = R<sub>0</sub>''. This optimization has been suggested and proven secure by [https://eprint.iacr.org/2022/222.pdf Chen and Zhao].
6565
* The maximum number of signatures that can be aggregated is ''2<sup>16</sup> - 1''. Having a maximum value is supposed to prevent integer overflows. This specific value was a conservative choice and may be raised in the future (TODO).
6666
6767
== Description ==
@@ -147,7 +147,10 @@ Input:
147147
** Let ''(pk<sub>i</sub>, m<sub>i</sub>, sig<sub>i</sub>) = pms_to_agg<sub>i-v</sub>''
148148
** Let ''r<sub>i</sub> = sig<sub>i</sub>[0:32]''
149149
** Let ''s<sub>i</sub> = int(sig<sub>i</sub>[32:64])''
150-
** Let ''z<sub>i</sub> = int(hash<sub>HalfAgg/randomizer</sub>(r<sub>0</sub> || pk<sub>0</sub> || m<sub>0</sub> || ... || r<sub>i</sub> || pk<sub>i</sub> || m<sub>i</sub>)) mod n''
150+
** If ''i = 0'':
151+
*** Let ''z<sub>i</sub> = 1''
152+
** Else:
153+
*** Let ''z<sub>i</sub> = int(hash<sub>HalfAgg/randomizer</sub>(r<sub>0</sub> || pk<sub>0</sub> || m<sub>0</sub> || ... || r<sub>i</sub> || pk<sub>i</sub> || m<sub>i</sub>)) mod n''
151154
* Let ''s = int(aggsig[(v⋅32:(v+1)⋅32]) + z<sub>v</sub>⋅s<sub>v</sub> + ... + z<sub>v+u-1</sub>⋅s<sub>v+u-1</sub> mod n''
152155
* Return ''r<sub>0</sub> || ... || r<sub>v+u-1</sub> || bytes(s)''
153156
@@ -169,7 +172,10 @@ The algorithm ''VerifyAggregate(aggsig, pm_aggd<sub>0..u-1</sub>)'' is defined a
169172
** Let ''r<sub>i</sub> = aggsig[i⋅32:(i+1)⋅32]''
170173
** Let ''R<sub>i</sub> = lift_x(int(r<sub>i</sub>))''; fail if that fails
171174
** Let ''e<sub>i</sub> = int(hash<sub>BIP0340/challenge</sub>(bytes(r<sub>i</sub>) || pk<sub>i</sub> || m<sub>i</sub>)) mod n''
172-
** Let ''z<sub>i</sub> = int(hash<sub>HalfAgg/randomizer</sub>(r<sub>0</sub> || pk<sub>0</sub> || m<sub>0</sub> || ... || r<sub>i</sub> || pk<sub>i</sub> || m<sub>i</sub>)) mod n''
175+
** If ''i = 0'':
176+
*** Let ''z<sub>i</sub> = 1''
177+
** Else:
178+
*** Let ''z<sub>i</sub> = int(hash<sub>HalfAgg/randomizer</sub>(r<sub>0</sub> || pk<sub>0</sub> || m<sub>0</sub> || ... || r<sub>i</sub> || pk<sub>i</sub> || m<sub>i</sub>)) mod n''
173179
* Let ''s = int(aggsig[u⋅32:(u+1)⋅32]); fail if ''s &ge; n''
174180
* Fail if ''s⋅G &ne; z<sub>0</sub>⋅(R<sub>0</sub> + e<sub>0</sub>⋅P<sub>0</sub>) + ... + z<sub>u-1</sub>⋅(R<sub>u-1</sub> + e<sub>u-1</sub>⋅P<sub>u-1</sub>)''
175181
* Return success iff no failure occurred before reaching this point.

0 commit comments

Comments
 (0)