Skip to content

Commit

Permalink
use Montgomery ladder for scalar pow (#31)
Browse files Browse the repository at this point in the history
* use Montgomery ladder for scalar pow

Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com>

* update deps

Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com>

---------

Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com>
  • Loading branch information
bytemare committed Feb 6, 2023
1 parent 2ce2010 commit 52bda0f
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 65 deletions.
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ go 1.19

require (
filippo.io/edwards25519 v1.0.0
filippo.io/nistec v0.0.0-20220513155737-c4b6d02e738c
filippo.io/nistec v0.0.0-20220825075812-a82cab4ea6f0
github.com/bytemare/hash2curve v0.1.2
github.com/gtank/ristretto255 v0.1.2
)

require (
github.com/bytemare/hash v0.1.3 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/sys v0.4.0 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/nistec v0.0.0-20220513155737-c4b6d02e738c h1:x4epP2lA8b5UYoIFjcVpN+MfJQeX5M5Yilmc1VH0YDw=
filippo.io/nistec v0.0.0-20220513155737-c4b6d02e738c/go.mod h1:84fxC9mi+MhC2AERXI4LSa8cmSVOzrFikg6hZ4IfCyw=
filippo.io/nistec v0.0.0-20220825075812-a82cab4ea6f0 h1:infQBtlEPAdRCqMIoddLS8K27zaaz05FLnrXskk0TtE=
filippo.io/nistec v0.0.0-20220825075812-a82cab4ea6f0/go.mod h1:84fxC9mi+MhC2AERXI4LSa8cmSVOzrFikg6hZ4IfCyw=
github.com/bytemare/hash v0.1.3 h1:E2v/+gqvLTjaR8W2JdhqaB2L9161yFBlSXDnYEyMt94=
github.com/bytemare/hash v0.1.3/go.mod h1:5WJSSK+ftRTLt9fOMHT+S4eXTTAb0Uz+NJJZKHLKovM=
github.com/bytemare/hash2curve v0.1.2 h1:V/TSdU/WsfYS3Bk73ap+odLCOOm2/B02rKE8lb91djI=
Expand All @@ -10,5 +12,9 @@ github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uM
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 h1:Y7NOhdqIOU8kYI7BxsgL38d0ot0raxvcW+EMQU2QrT4=
golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
65 changes: 34 additions & 31 deletions internal/edwards25519/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,46 +164,49 @@ func (s *Scalar) square() {

// Pow sets s to s**scalar modulo the group order, and returns s. If scalar is nil, it returns 1.
func (s *Scalar) Pow(scalar internal.Scalar) internal.Scalar {
sc := assert(scalar)
exponent := sc.scalar.Bytes()
msbyte := getMSByte(exponent)
msbit := getMSBit(exponent[msbyte])

result := s.copy()
dummy := s.copy()

for i := 31; i >= 0; i-- {
exp := exponent[i]
firstByte := i == msbyte
s1 := s.copy()
s2 := s.copy()
s2.square()

bytes := assert(scalar).scalar.Bytes()
msbyte := getMSByte(bytes)
msbit := getMSBit(bytes[msbyte])

// First round over the most significant byte
b := bytes[msbyte]
for j := msbit - 1; j >= 0; j-- {
bit := b & byte(1<<byte(j))
if bit == 0 {
s2.multiply(s1)
s1.square()
} else {
s1.multiply(s2)
s2.square()
}
}

for i := msbyte - 1; i >= 0; i-- {
b = bytes[i]
for j := 7; j >= 0; j-- {
run := i < msbyte || (firstByte && j < msbit)
currentBitValue := exp & byte(1<<byte(j))

if run {
result.square()

if currentBitValue != 0 {
result.multiply(s)
} else {
dummy.multiply(s)
}
bit := b & byte(1<<byte(j))
if bit == 0 {
s2.multiply(s1)
s1.square()
} else {
dummy.square()
dummy.multiply(s)
s1.multiply(s2)
s2.square()
}
}
}

switch {
case sc.IsZero():
s.set(&scOne.scalar)
case sc.scalar.Equal(&scOne.scalar) == 1:
s.set(&s.scalar)
default:
s.set(&result.scalar)
if scalar.IsZero() {
s1.One()
} else {
s2.One()
}

s.set(&s1.scalar)

return s
}

Expand Down
65 changes: 34 additions & 31 deletions internal/ristretto/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,46 +152,49 @@ func (s *Scalar) square() {

// Pow sets s to s**scalar modulo the group order, and returns s. If scalar is nil, it returns 1.
func (s *Scalar) Pow(scalar internal.Scalar) internal.Scalar {
sc := assert(scalar)
exponent := sc.scalar.Encode(nil)
msbyte := getMSByte(exponent)
msbit := getMSBit(exponent[msbyte])

result := s.copy()
dummy := s.copy()

for i := 31; i >= 0; i-- {
exp := exponent[i]
firstByte := i == msbyte
s1 := s.copy()
s2 := s.copy()
s2.square()

bytes := assert(scalar).Encode()
msbyte := getMSByte(bytes)
msbit := getMSBit(bytes[msbyte])

// First round over the most significant byte
b := bytes[msbyte]
for j := msbit - 1; j >= 0; j-- {
bit := b & byte(1<<byte(j))
if bit == 0 {
s2.multiply(s1)
s1.square()
} else {
s1.multiply(s2)
s2.square()
}
}

for i := msbyte - 1; i >= 0; i-- {
b = bytes[i]
for j := 7; j >= 0; j-- {
run := i < msbyte || (firstByte && j < msbit)
currentBitValue := exp & byte(1<<byte(j))

if run {
result.square()

if currentBitValue != 0 {
result.multiply(s)
} else {
dummy.multiply(s)
}
bit := b & byte(1<<byte(j))
if bit == 0 {
s2.multiply(s1)
s1.square()
} else {
dummy.square()
dummy.multiply(s)
s1.multiply(s2)
s2.square()
}
}
}

switch {
case sc.IsZero():
s.set(&scOne.scalar)
case sc.scalar.Equal(&scOne.scalar) == 1:
s.set(&s.scalar)
default:
s.set(&result.scalar)
if scalar.IsZero() {
s1.One()
} else {
s2.One()
}

s.set(&s1.scalar)

return s
}

Expand Down
13 changes: 13 additions & 0 deletions tests/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ func benchAll(b *testing.B, f func(*testing.B, *testGroup)) {
}
}

func BenchmarkPow(b *testing.B) {
benchAll(b, func(b *testing.B, group *testGroup) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
base := group.id.NewScalar().Random()
exp := group.id.NewScalar().Random()
res := base.Pow(exp)
res.Equal(base)
}
})
}

func BenchmarkHashToGroup(b *testing.B) {
msg := make([]byte, 256)
dst := make([]byte, 10)
Expand Down

0 comments on commit 52bda0f

Please sign in to comment.