2021-02-22 17:48:14 +00:00

189 lines
4.7 KiB
Go

package bn256
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"math/big"
)
// FpUint64Size is the number of uint64 chunks to represent a field element
const FpUint64Size = 4
type gfP [FpUint64Size]uint64
func newGFp(x int64) (out *gfP) {
if x >= 0 {
out = &gfP{uint64(x)}
} else {
out = &gfP{uint64(-x)}
gfpNeg(out, out)
}
montEncode(out, out)
return out
}
func (e *gfP) String() string {
return fmt.Sprintf("%16.16x%16.16x%16.16x%16.16x", e[3], e[2], e[1], e[0])
}
/*
func byteToUint64(in []byte) (uint64, error) {
if len(in) > 8 {
return 0, errors.New("the input bytes length should be equal to 8 (or smaller)")
}
// Takes the bytes in the little endian order
// The byte 0x64 translate in a uint64 of the shape 0x64 (= 0x0000000000000064) rather than 0x6400000000000000
res := binary.LittleEndian.Uint64(in)
return res, nil
}
*/
// Makes sure that the
func padBytes(bb []byte) ([]byte, error) {
if len(bb) > 32 {
return []byte{}, errors.New("Cannot pad the given byte slice as the length exceed the padding length")
}
if len(bb) == 32 {
return bb, nil
}
padSlice := make([]byte, 32)
index := len(padSlice) - len(bb)
copy(padSlice[index:], bb)
return padSlice, nil
}
// Convert a big.Int into gfP
func newGFpFromBigInt(in *big.Int) (out *gfP) {
// in >= P, so we mod it to get back in the field
// (ie: we get the smallest representative of the equivalence class mod P)
if res := in.Cmp(P); res >= 0 {
// We need to mod P to get back into the field
in.Mod(in, P)
}
inBytes := in.Bytes()
// We want to work on byte slices of length 32 to re-assemble our GFpe element
if len(inBytes) < 32 {
// Safe to ignore the err as we are in the if so the condition is satisfied
inBytes, _ = padBytes(inBytes)
}
out = &gfP{}
var n uint64
// Now we have the guarantee that inBytes has length 32 so it makes sense to run this for
// loop safely (we won't exceed the boundaries of the container)
for i := 0; i < FpUint64Size; i++ {
buf := bytes.NewBuffer(inBytes[i*8 : (i+1)*8])
binary.Read(buf, binary.BigEndian, &n)
out[(FpUint64Size-1)-i] = n // In gfP field elements are represented as little-endian 64-bit words
}
return out
}
// Returns a new element of GFp montgomery encoded
func newMontEncodedGFpFromBigInt(in *big.Int) *gfP {
res := newGFpFromBigInt(in)
montEncode(res, res)
return res
}
// Convert a gfP into a big.Int
func (e *gfP) gFpToBigInt() (*big.Int, error) {
str := e.String()
out := new(big.Int)
_, ok := out.SetString(str, 16)
if !ok {
return nil, errors.New("couldn't create big.Int from gfP element")
}
return out, nil
}
func (e *gfP) Set(f *gfP) {
e[0] = f[0]
e[1] = f[1]
e[2] = f[2]
e[3] = f[3]
}
func (e *gfP) Invert(f *gfP) {
bits := [4]uint64{0x3c208c16d87cfd45, 0x97816a916871ca8d, 0xb85045b68181585d, 0x30644e72e131a029}
sum, power := &gfP{}, &gfP{}
sum.Set(rN1)
power.Set(f)
for word := 0; word < 4; word++ {
for bit := uint(0); bit < 64; bit++ {
if (bits[word]>>bit)&1 == 1 {
gfpMul(sum, sum, power)
}
gfpMul(power, power, power)
}
}
gfpMul(sum, sum, r3)
e.Set(sum)
}
func (e *gfP) Marshal(out []byte) {
for w := uint(0); w < 4; w++ {
for b := uint(0); b < 8; b++ {
out[8*w+b] = byte(e[3-w] >> (56 - 8*b))
}
}
}
func (e *gfP) Unmarshal(in []byte) error {
// Unmarshal the bytes into little endian form
for w := uint(0); w < 4; w++ {
for b := uint(0); b < 8; b++ {
e[3-w] += uint64(in[8*w+b]) << (56 - 8*b)
}
}
// Ensure the point respects the curve modulus
for i := 3; i >= 0; i-- {
if e[i] < p2[i] {
return nil
}
if e[i] > p2[i] {
return errors.New("bn256: coordinate exceeds modulus")
}
}
return errors.New("bn256: coordinate equals modulus")
}
// Note: This function is only used to distinguish between points with the same x-coordinates
// when doing point compression.
// An ordered field must be infinite and we are working over a finite field here
func gfpCmp(a, b *gfP) int {
for i := FpUint64Size - 1; i >= 0; i-- { // Remember that the gfP elements are written as little-endian 64-bit words
if a[i] > b[i] { // As soon as we figure out that the MSByte of A > MSByte of B, we return
return 1
} else if a[i] == b[i] { // If the current bytes are equal we continue as we cannot conclude on A and B relation
continue
} else { // a[i] < b[i] so we can directly conclude and we return
return -1
}
}
return 0
}
// In Montgomery representation, an element x is represented by xR mod p, where
// R is a power of 2 corresponding to the number of machine-words that can contain p.
// (where p is the characteristic of the prime field we work over)
// See: https://web.wpi.edu/Pubs/ETD/Available/etd-0430102-120529/unrestricted/thesis.pdf
func montEncode(c, a *gfP) { gfpMul(c, a, r2) }
func montDecode(c, a *gfP) { gfpMul(c, a, &gfP{1}) }