Skip to content

Commit 2bb8aa8

Browse files
authored
k256+primeorder: make batch_normalize_generic constant time (#977)
Uses constant-time selectors rather than branching
1 parent ee94d8d commit 2bb8aa8

File tree

2 files changed

+28
-32
lines changed

2 files changed

+28
-32
lines changed

k256/src/arithmetic/projective.rs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ impl From<AffinePoint> for ProjectivePoint {
259259
impl<const N: usize> BatchNormalize<[ProjectivePoint; N]> for ProjectivePoint {
260260
type Output = [Self::AffineRepr; N];
261261

262+
#[inline]
262263
fn batch_normalize(points: &[Self; N]) -> [Self::AffineRepr; N] {
263264
let mut zs = [FieldElement::ONE; N];
264265
let mut affine_points = [AffinePoint::IDENTITY; N];
@@ -271,6 +272,7 @@ impl<const N: usize> BatchNormalize<[ProjectivePoint; N]> for ProjectivePoint {
271272
impl BatchNormalize<[ProjectivePoint]> for ProjectivePoint {
272273
type Output = Vec<Self::AffineRepr>;
273274

275+
#[inline]
274276
fn batch_normalize(points: &[Self]) -> Vec<Self::AffineRepr> {
275277
let mut zs = vec![FieldElement::ONE; points.len()];
276278
let mut affine_points = vec![AffinePoint::IDENTITY; points.len()];
@@ -290,23 +292,23 @@ where
290292
let out = out.as_mut();
291293

292294
for i in 0..points.len() {
293-
if points[i].z != FieldElement::ZERO {
294-
// Even a single zero value will fail inversion for the entire batch.
295-
// Put a dummy value (above `FieldElement::ONE`) so inversion succeeds
296-
// and treat that case specially later-on.
297-
zs.as_mut()[i] = points[i].z;
298-
}
295+
// Even a single zero value will fail inversion for the entire batch.
296+
// Put a dummy value (above `FieldElement::ONE`) so inversion succeeds
297+
// and treat that case specially later-on.
298+
zs.as_mut()[i].conditional_assign(&points[i].z, !points[i].z.ct_eq(&FieldElement::ZERO));
299299
}
300300

301301
// This is safe to unwrap since we assured that all elements are non-zero
302302
let zs_inverses = <FieldElement as BatchInvert<Z>>::batch_invert(zs).unwrap();
303303

304304
for i in 0..out.len() {
305-
if points[i].z != FieldElement::ZERO {
306-
// If the `z` coordinate is non-zero, we can use it to invert;
307-
// otherwise it defaults to the `IDENTITY` value in initialization.
308-
out[i] = points[i].to_affine_internal(zs_inverses.as_ref()[i])
309-
}
305+
// If the `z` coordinate is non-zero, we can use it to invert;
306+
// otherwise it defaults to the `IDENTITY` value.
307+
out[i] = AffinePoint::conditional_select(
308+
&points[i].to_affine_internal(zs_inverses.as_ref()[i]),
309+
&AffinePoint::IDENTITY,
310+
points[i].z.ct_eq(&FieldElement::ZERO),
311+
);
310312
}
311313
}
312314

@@ -449,13 +451,9 @@ impl Curve for ProjectivePoint {
449451
}
450452

451453
#[cfg(feature = "alloc")]
454+
#[inline]
452455
fn batch_normalize(projective: &[Self], affine: &mut [Self::AffineRepr]) {
453456
assert_eq!(projective.len(), affine.len());
454-
455-
for point in affine.iter_mut() {
456-
*point = AffinePoint::IDENTITY;
457-
}
458-
459457
let mut zs = vec![FieldElement::ONE; projective.len()];
460458
batch_normalize_generic(projective, zs.as_mut_slice(), affine);
461459
}

primeorder/src/projective.rs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,9 @@ where
331331
}
332332

333333
#[cfg(feature = "alloc")]
334+
#[inline]
334335
fn batch_normalize(projective: &[Self], affine: &mut [Self::AffineRepr]) {
335336
assert_eq!(projective.len(), affine.len());
336-
337-
for point in affine.iter_mut() {
338-
*point = AffinePoint::IDENTITY;
339-
}
340-
341337
let mut zs = vec![FieldElement::ONE; projective.len()];
342338
batch_normalize_generic(projective, zs.as_mut_slice(), affine);
343339
}
@@ -350,6 +346,7 @@ where
350346
{
351347
type Output = [Self::AffineRepr; N];
352348

349+
#[inline]
353350
fn batch_normalize(points: &[Self; N]) -> [Self::AffineRepr; N] {
354351
let mut zs = [C::FieldElement::ONE; N];
355352
let mut affine_points = [C::AffinePoint::IDENTITY; N];
@@ -366,6 +363,7 @@ where
366363
{
367364
type Output = Vec<Self::AffineRepr>;
368365

366+
#[inline]
369367
fn batch_normalize(points: &[Self; N]) -> Vec<Self::AffineRepr> {
370368
let mut zs = vec![C::FieldElement::ONE; points.len()];
371369
let mut affine_points = vec![AffinePoint::IDENTITY; points.len()];
@@ -388,23 +386,23 @@ where
388386
let out = out.as_mut();
389387

390388
for i in 0..points.len() {
391-
if points[i].z != C::FieldElement::ZERO {
392-
// Even a single zero value will fail inversion for the entire batch.
393-
// Put a dummy value (above `C::FieldElement::ONE`) so inversion succeeds
394-
// and treat that case specially later-on.
395-
zs.as_mut()[i] = points[i].z;
396-
}
389+
// Even a single zero value will fail inversion for the entire batch.
390+
// Put a dummy value (above `FieldElement::ONE`) so inversion succeeds
391+
// and treat that case specially later-on.
392+
zs.as_mut()[i].conditional_assign(&points[i].z, !points[i].z.ct_eq(&C::FieldElement::ZERO));
397393
}
398394

399395
// This is safe to unwrap since we assured that all elements are non-zero
400396
let zs_inverses = <C::FieldElement as BatchInvert<Z>>::batch_invert(zs).unwrap();
401397

402398
for i in 0..out.len() {
403-
if points[i].z != C::FieldElement::ZERO {
404-
// If the `z` coordinate is non-zero, we can use it to invert;
405-
// otherwise it defaults to the `IDENTITY` value in initialization.
406-
out[i] = points[i].to_affine_internal(zs_inverses.as_ref()[i])
407-
}
399+
// If the `z` coordinate is non-zero, we can use it to invert;
400+
// otherwise it defaults to the `IDENTITY` value.
401+
out[i] = C::AffinePoint::conditional_select(
402+
&points[i].to_affine_internal(zs_inverses.as_ref()[i]),
403+
&C::AffinePoint::IDENTITY,
404+
points[i].z.ct_eq(&C::FieldElement::ZERO),
405+
);
408406
}
409407
}
410408

0 commit comments

Comments
 (0)