Skip to content

Commit

Permalink
field: add Element.SetWideBytes
Browse files Browse the repository at this point in the history
Fixes #17
  • Loading branch information
FiloSottile committed Jul 21, 2021
1 parent 3b51003 commit 390f27c
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 4 deletions.
5 changes: 3 additions & 2 deletions field/fe.go
Expand Up @@ -193,7 +193,8 @@ func (v *Element) Set(a *Element) *Element {
//
// Consistent with RFC 7748, the most significant bit (the high bit of the
// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1)
// are accepted. Note that this is laxer than specified by RFC 8032.
// are accepted. Note that this is laxer than specified by RFC 8032, but
// consistent with most Ed25519 implementations.
func (v *Element) SetBytes(x []byte) (*Element, error) {
if len(x) != 32 {
return nil, errors.New("edwards25519: invalid field element input size")
Expand All @@ -211,7 +212,7 @@ func (v *Element) SetBytes(x []byte) (*Element, error) {
// Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51).
v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1
v.l3 &= maskLow51Bits
// Bits 204:251 (bytes 24:32, bits 192:256, shift 12, mask 51).
// Bits 204:255 (bytes 24:32, bits 192:256, shift 12, mask 51).
// Note: not bytes 25:33, shift 4, to avoid overread.
v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12
v.l4 &= maskLow51Bits
Expand Down
50 changes: 50 additions & 0 deletions field/fe_extra.go
@@ -0,0 +1,50 @@
// Copyright (c) 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package field

import "errors"

// This file contains additional functionality that is not included in the
// upstream crypto/ed25519/internal/edwards25519/field package.

// SetWideBytes sets v to x, where x is a 64-byte little-endian encoding, which
// is reduced modulo the field order. If x is not of the right length,
// SetWideBytes returns nil and an error, and the receiver is unchanged.
//
// SetWideBytes is not necessary to select a uniformly distributed value, and is
// only provided for compatibility: SetBytes can be used instead as the chance
// of bias is less than 2⁻²⁵⁰.
func (v *Element) SetWideBytes(x []byte) (*Element, error) {
if len(x) != 64 {
return nil, errors.New("edwards25519: invalid SetWideBytes input size")
}

// Split the 64 bytes into two elements, and extract the most significant
// bit of each, which is ignored by SetBytes.
lo, _ := new(Element).SetBytes(x[:32])
loMSB := uint64(x[31] >> 7)
hi, _ := new(Element).SetBytes(x[32:])
hiMSB := uint64(x[63] >> 7)

// The output we want is
//
// v = lo + loMSB * 2²⁵⁵ + hi * 2²⁵⁶ + hiMSB * 2⁵¹¹
//
// which applying the reduction identity comes out to
//
// v = lo + loMSB * 19 + hi * 2 * 19 + hiMSB * 2 * 19²
//
// l0 will be the sum of a 52 bits value (lo.l0), plus a 5 bits value
// (loMSB * 19), a 6 bits value (hi.l0 * 2 * 19), and a 10 bits value
// (hiMSB * 2 * 19²), so it fits in a uint64.

v.l0 = lo.l0 + loMSB*19 + hi.l0*2*19 + hiMSB*2*19*19
v.l1 = lo.l1 + hi.l1*2*19
v.l2 = lo.l2 + hi.l2*2*19
v.l3 = lo.l3 + hi.l3*2*19
v.l4 = lo.l4 + hi.l4*2*19

return v.carryPropagate(), nil
}
37 changes: 37 additions & 0 deletions field/fe_extra_test.go
@@ -0,0 +1,37 @@
// Copyright (c) 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package field

import (
"math/big"
"testing"
"testing/quick"
)

var bigP = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 255), big.NewInt(19))

func TestSetWideBytes(t *testing.T) {
f1 := func(in [64]byte, fe Element) bool {
fe1 := new(Element).Set(&fe)

if out, err := fe.SetWideBytes([]byte{42}); err == nil || out != nil ||
fe.Equal(fe1) != 1 {
return false
}

if out, err := fe.SetWideBytes(in[:]); err != nil || out != &fe {
return false
}

b := new(big.Int).SetBytes(swapEndianness(in[:]))
fe1.fromBig(b.Mod(b, bigP))

return fe.Equal(fe1) == 1 && isInBounds(&fe) && isInBounds(fe1)
}
if err := quick.Check(f1, nil); err != nil {
t.Error(err)
}

}
2 changes: 2 additions & 0 deletions field/fe_generic.go
Expand Up @@ -254,6 +254,8 @@ func (v *Element) carryPropagateGeneric() *Element {
c3 := v.l3 >> 51
c4 := v.l4 >> 51

// c4 is at most 64 - 51 = 13 bits, so c4*19 is at most 18 bits, and
// the final l0 will be at most 52 bits. Similarly for the rest.
v.l0 = v.l0&maskLow51Bits + c4*19
v.l1 = v.l1&maskLow51Bits + c0
v.l2 = v.l2&maskLow51Bits + c1
Expand Down
4 changes: 2 additions & 2 deletions field/fe_test.go
Expand Up @@ -218,8 +218,8 @@ func TestBytesBigEquivalence(t *testing.T) {
return false
}

buf := make([]byte, 32) // pad with zeroes
copy(buf, swapEndianness(fe1.toBig().Bytes()))
buf := make([]byte, 32)
buf = swapEndianness(fe1.toBig().FillBytes(buf))

return bytes.Equal(fe.Bytes(), buf) && isInBounds(&fe) && isInBounds(&fe1)
}
Expand Down

0 comments on commit 390f27c

Please sign in to comment.