secp256k1: Expose IsZeroBit on mod n scalar type.

This exposes a new function on the ModNScalar type named IsZeroBit which
operates similarly to IsZero except it returns a 0 or 1.

This is being provided because it is not possible in Go to convert from
a bool to numeric value in constant time and many constant-time
operations require a numeric value.

Since the type is now exported for external use, consumers would
otherwise have no way to perform the conversion.
This commit is contained in:
Dave Collins 2022-07-23 00:54:23 -05:00
parent 67b4d3dbf7
commit d5cfa73a2f
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
2 changed files with 30 additions and 5 deletions

View File

@ -1,4 +1,4 @@
// Copyright (c) 2020 The Decred developers
// Copyright (c) 2020-2022 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -100,7 +100,7 @@ var (
// arithmetic over the secp256k1 group order. This means all arithmetic is
// performed modulo:
//
// 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
// 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
//
// It only implements the arithmetic needed for elliptic curve operations,
// however, the operations that are not implemented can typically be worked
@ -174,6 +174,19 @@ func (s *ModNScalar) Zero() {
s.n[7] = 0
}
// IsZeroBit returns 1 when the scalar is equal to zero or 0 otherwise in
// constant time.
//
// Note that a bool is not used here because it is not possible in Go to convert
// from a bool to numeric value in constant time and many constant-time
// operations require a numeric value. See IsZero for the version that returns
// a bool.
func (s *ModNScalar) IsZeroBit() uint32 {
// The scalar can only be zero if no bits are set in any of the words.
bits := s.n[0] | s.n[1] | s.n[2] | s.n[3] | s.n[4] | s.n[5] | s.n[6] | s.n[7]
return constantTimeEq(bits, 0)
}
// IsZero returns whether or not the scalar is equal to zero in constant time.
func (s *ModNScalar) IsZero() bool {
// The scalar can only be zero if no bits are set in any of the words.

View File

@ -80,28 +80,40 @@ func TestModNScalarZero(t *testing.T) {
}
}
// TestModNScalarIsZero ensures that checking if a scalar is zero works as
// expected.
// TestModNScalarIsZero ensures that checking if a scalar is zero via IsZero and
// IsZeroBit works as expected.
func TestModNScalarIsZero(t *testing.T) {
var s ModNScalar
if !s.IsZero() {
t.Errorf("new scalar is not zero - got %v (rawints %x)", s, s.n)
}
if s.IsZeroBit() != 1 {
t.Errorf("new scalar is not zero - got %v (rawints %x)", s, s.n)
}
s.SetInt(1)
if s.IsZero() {
t.Errorf("claims zero for nonzero scalar - got %v (rawints %x)", s, s.n)
}
if s.IsZeroBit() == 1 {
t.Errorf("claims zero for nonzero scalar - got %v (rawints %x)", s, s.n)
}
s.SetInt(0)
if !s.IsZero() {
t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n)
}
if s.IsZeroBit() != 1 {
t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n)
}
s.SetInt(1)
s.Zero()
if !s.IsZero() {
t.Errorf("claims zero for nonzero scalar - got %v (rawints %x)", s, s.n)
t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n)
}
if s.IsZeroBit() != 1 {
t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n)
}
}