Skip to content

Commit 8a9d6fb

Browse files
committed
Merge #13: Revert s overflow handling
df69673 halfagg: Note that invalid sigs can have valid aggsig and add test (Tim Ruffing) 6460917 Revert "halfagg: fail inc_agg if input scalars overflow" (Tim Ruffing) Pull request description: ACKs for top commit: jonasnick: ACK df69673 Tree-SHA512: 84e70bf39533bd48f917f7e692f1422be54c86aae2d854cdd661e473d1a6a2f194e2b43779981c86196633985b98b061ce20c4cb30ba8280e3f3669c71638664
2 parents 7e657d4 + df69673 commit 8a9d6fb

File tree

3 files changed

+63
-10
lines changed

3 files changed

+63
-10
lines changed

hacspec-halfagg/src/halfagg.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ pub fn inc_aggregate(
5454
let (pk, msg) = pm_aggd[i];
5555
pmr[i] = (pk, msg, Bytes32::from_slice(aggsig, 32 * i, 32));
5656
}
57-
let mut s = scalar_from_bytes_strict(Bytes32::from_seq(&aggsig.slice(32 * v, 32)))
58-
.ok_or(Error::MalformedSignature)?;
57+
let mut s = Scalar::from_byte_seq_be(&aggsig.slice(32 * v, 32));
5958

6059
for i in v..v + u {
6160
let (pk, msg, sig) = pms_to_agg[i - v];
@@ -66,9 +65,7 @@ pub fn inc_aggregate(
6665
let z = scalar_from_bytes(hash_halfagg(
6766
&Seq::<(PublicKey, Message, Bytes32)>::from_slice(&pmr, 0, i + 1),
6867
));
69-
let si = scalar_from_bytes_strict(Bytes32::from_slice(&sig, 32, 32))
70-
.ok_or(Error::MalformedSignature)?;
71-
s = s + z * si;
68+
s = s + z * Scalar::from_byte_seq_be(&Bytes32::from_slice(&sig, 32, 32));
7269
}
7370
let mut ret = Seq::<U8>::new(0);
7471
for i in 0..pmr.len() {

hacspec-halfagg/tests/tests.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use hacspec_bip_340::*;
2+
use hacspec_dev::rand::*;
23
use hacspec_halfagg::*;
34
use hacspec_lib::*;
45

@@ -129,6 +130,60 @@ fn test_aggregate_verify() {
129130
}
130131
}
131132

133+
/// Constructs two invalid signatures whose aggregate signature is valid
134+
#[test]
135+
fn test_aggregate_verify_strange() {
136+
let mut pms_triples = Seq::<(PublicKey, Message, Signature)>::new(0);
137+
for i in 0..2 {
138+
let sk = [i as u8 + 1; 32];
139+
let sk = SecretKey::from_public_array(sk);
140+
let msg = [i as u8 + 2; 32];
141+
let msg = Message::from_public_array(msg);
142+
let aux_rand = [i as u8 + 3; 32];
143+
let aux_rand = AuxRand::from_public_array(aux_rand);
144+
let sig = sign(msg, sk, aux_rand).unwrap();
145+
pms_triples = pms_triples.push(&(pubkey_gen(sk).unwrap(), msg, sig));
146+
}
147+
let aggsig = aggregate(&pms_triples).unwrap();
148+
let pm_tuples = strip_sigs(&pms_triples);
149+
assert!(verify_aggregate(&aggsig, &pm_tuples).is_ok());
150+
151+
// Compute z values like in IncAggegrate
152+
let mut pmr = Seq::<(PublicKey, Message, Bytes32)>::new(0);
153+
let mut z = Seq::new(0);
154+
for i in 0..2 {
155+
let (pk, msg, sig) = pms_triples[i];
156+
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+
)));
163+
}
164+
165+
// Shift signatures appropriately
166+
let sagg = scalar_from_bytes(Bytes32::from_seq(&aggsig.slice(32 * 2, 32)));
167+
let s1: [u8; 32] = random_bytes();
168+
let s1 = scalar_from_bytes(Bytes32::from_public_array(s1));
169+
// Division is ordinary integer division, so use inv() explicitly
170+
let s0 = (sagg - z[1] * s1) * (z[0] as Scalar).inv();
171+
172+
let (pk0, msg0, sig0): (PublicKey, Message, Signature) = pms_triples[0];
173+
let (pk1, msg1, sig1): (PublicKey, Message, Signature) = pms_triples[1];
174+
let sig0_invalid = sig0.update(32, &bytes_from_scalar(s0));
175+
let sig1_invalid = sig1.update(32, &bytes_from_scalar(s1));
176+
assert!(!verify(msg0, pk0, sig0_invalid).is_ok());
177+
assert!(!verify(msg1, pk1, sig1_invalid).is_ok());
178+
179+
let mut pms_strange = Seq::<(PublicKey, Message, Signature)>::new(0);
180+
pms_strange = pms_strange.push(&(pk0, msg0, sig0_invalid));
181+
pms_strange = pms_strange.push(&(pk1, msg1, sig1_invalid));
182+
let aggsig_strange = aggregate(&pms_strange).unwrap();
183+
let pm_strange = strip_sigs(&pms_strange);
184+
assert!(verify_aggregate(&aggsig_strange, &pm_strange).is_ok());
185+
}
186+
132187
#[test]
133188
fn test_edge_cases() {
134189
let empty_pm = Seq::<(PublicKey, Message)>::new(0);

half-aggregation.mediawiki

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ The following conventions are used, with constants as defined for [https://www.s
115115
==== Aggregate ====
116116

117117
''Aggregate'' takes an array of public key, message and signature triples and returns an aggregate signature.
118-
If for every triple ''(p, m, s)'' we have that ''Verify(p, m, s)'' (as defined in BIP 340) returns true, then the returned aggregate signature and the array of ''(p, m)'' tuples passes ''VerifyAggregate''.
118+
If every triple ''(p, m, s)'' is valid (i.e., ''Verify(p, m, s)'' as defined in BIP 340 returns true), then the returned aggregate signature and the array of ''(p, m)'' tuples passes ''VerifyAggregate''.
119+
(However, the inverse does not hold: given suitable valid triples, it is possible to construct an input array to ''Aggregate'' which contains invalid triples, but for which ''VerifyAggregate'' will accept the aggregate signature returned by ''Aggregate''. If this is undesired, input triples should be verified individually before passing them to ''Aggregate''.)
119120

120121
Input:
121122
* ''pms<sub>0..u-1</sub>'': an array of ''u'' triples, where the first element of each triple is a 32-byte public key, the second element is a 32-byte message and the third element is a 64-byte BIP 340 signature
@@ -128,7 +129,8 @@ Input:
128129

129130
''IncAggregate'' takes an aggregate signature, an array of public key and message tuples corresponding to the aggregate signature, and an additional array of public key, message and signature triples.
130131
It aggregates the additional array of triples into the existing aggregate signature and returns the resulting new aggregate signature.
131-
In other words, if ''VerifyAggregate(aggsig, pm_aggd)'' passes and for every triple ''(p, m, s)'' in ''pms_to_agg'' we have that ''Verify(p, m, s)'' (as defined in BIP 340) returns true, then the returned aggregate signature along with the array of ''(p, m)'' tuples of ''pm_aggd'' and ''pms_to_agg'' passes ''VerifyAggregate''.
132+
In other words, if ''VerifyAggregate(aggsig, pm_aggd)'' passes and every triple ''(p, m, s)'' in ''pms_to_agg'' is valid (i.e., ''Verify(p, m, s)'' as defined in BIP 340 returns true), then the returned aggregate signature along with the array of ''(p, m)'' tuples of ''pm_aggd'' and ''pms_to_agg'' passes ''VerifyAggregate''.
133+
(However, the inverse does not hold: given a suitable valid aggregate signature and suitable valid triples, it is possible to construct inputs to ''IncAggregate'' which contain an invalid aggregate signature or invalid triples, but for which ''VerifyAggregate'' will accept the aggregate signature returned by ''IncAggregate''. If this is undesired, the input triples and the input aggregate signature should be verified individually before passing them to ''IncAggregate''.)
132134

133135
Input:
134136
* ''aggsig'' : a byte array
@@ -144,10 +146,9 @@ Input:
144146
* For ''i = v .. v+u-1'':
145147
** Let ''(pk<sub>i</sub>, m<sub>i</sub>, sig<sub>i</sub>) = pms_to_agg<sub>i-v</sub>''
146148
** Let ''r<sub>i</sub> = sig<sub>i</sub>[0:32]''
147-
** Let ''s<sub>i</sub> = int(sig<sub>i</sub>[32:64])''; fail if ''s<sub>i</sub> &ge; n''
149+
** Let ''s<sub>i</sub> = int(sig<sub>i</sub>[32:64])''
148150
** 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''
149-
* Let ''q = int(aggsig[(v⋅32:(v+1)⋅32]))''; fail if ''q &ge; n''
150-
* Let ''s = q + z<sub>v</sub>⋅s<sub>v</sub> + ... + z<sub>v+u-1</sub>⋅s<sub>v+u-1</sub> mod n''
151+
* 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''
151152
* Return ''r<sub>0</sub> || ... || r<sub>v+u-1</sub> || bytes(s)''
152153
153154
==== VerifyAggregate ====

0 commit comments

Comments
 (0)