ceremonyclient/nekryptology/pkg/core/curves/ed448_curve.go
Cassandra Heart dbd95bd9e9
v2.1.0 (#439)
* v2.1.0 [omit consensus and adjacent] - this commit will be amended with the full release after the file copy is complete

* 2.1.0 main node rollup
2025-09-30 02:48:15 -05:00

685 lines
14 KiB
Go

//
// Copyright Quilibrium, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"crypto/subtle"
"errors"
"fmt"
"io"
"math/big"
"github.com/cloudflare/circl/ecc/goldilocks"
"github.com/cloudflare/circl/math/fp448"
"golang.org/x/crypto/sha3"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/internal"
)
type ScalarEd448 struct {
value *goldilocks.Scalar
}
type PointEd448 struct {
value *goldilocks.Point
}
var gscOne = goldilocks.Scalar{
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
}
var ed448Order = goldilocks.Scalar{
0xf3, 0x44, 0x58, 0xab, 0x92, 0xc2, 0x78, 0x23,
0x55, 0x8f, 0xc5, 0x8d, 0x72, 0xc2, 0x6c, 0x21,
0x90, 0x36, 0xd6, 0xae, 0x49, 0xdb, 0x4e, 0xc4,
0xe9, 0x23, 0xca, 0x7c, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f,
}
func (s *ScalarEd448) Random(reader io.Reader) Scalar {
if reader == nil {
return nil
}
var seed [57]byte
_, _ = reader.Read(seed[:])
return s.Hash(seed[:])
}
func (s *ScalarEd448) Hash(bytes []byte) Scalar {
raw := [114]byte{}
h := sha3.NewShake256()
_, _ = h.Write(bytes)
_, _ = h.Read(raw[:])
value := &goldilocks.Scalar{}
raw[0] &= 0xFC
raw[55] |= 0x80
raw[56] = 0x00
value.FromBytes(raw[:57])
return &ScalarEd448{value}
}
func (s *ScalarEd448) Zero() Scalar {
return &ScalarEd448{
value: &goldilocks.Scalar{},
}
}
func (s *ScalarEd448) One() Scalar {
value := &goldilocks.Scalar{}
value.FromBytes(gscOne[:])
return &ScalarEd448{value}
}
func (s *ScalarEd448) IsZero() bool {
i := byte(0)
for _, b := range s.value {
i |= b
}
return i == 0
}
func (s *ScalarEd448) IsOne() bool {
data := s.value
i := byte(0)
for j := 1; j < len(data); j++ {
i |= data[j]
}
return i == 0 && data[0] == 1
}
func (s *ScalarEd448) IsOdd() bool {
return s.value[0]&1 == 1
}
func (s *ScalarEd448) IsEven() bool {
return s.value[0]&1 == 0
}
func (s *ScalarEd448) New(input int) Scalar {
var data [56]byte
i := input
if input < 0 {
i = -input
}
data[0] = byte(i)
data[1] = byte(i >> 8)
data[2] = byte(i >> 16)
data[3] = byte(i >> 24)
value := &goldilocks.Scalar{}
value.FromBytes(data[:])
if input < 0 {
value.Neg()
}
return &ScalarEd448{
value,
}
}
func (s *ScalarEd448) Cmp(rhs Scalar) int {
r := s.Sub(rhs)
if r != nil && r.IsZero() {
return 0
} else {
return -2
}
}
func (s *ScalarEd448) Square() Scalar {
value := &goldilocks.Scalar{}
value.Mul(s.value, s.value)
return &ScalarEd448{value}
}
func (s *ScalarEd448) Double() Scalar {
value := &goldilocks.Scalar{}
value.Add(s.value, s.value)
return &ScalarEd448{value}
}
func (s *ScalarEd448) Invert() (Scalar, error) {
ret := new(big.Int)
order := new(big.Int)
buf := internal.ReverseScalarBytes(s.value[:])
orderBuf := internal.ReverseScalarBytes(ed448Order[:])
ret.SetBytes(buf)
order.SetBytes(orderBuf)
value := &goldilocks.Scalar{}
ret = ret.ModInverse(ret, order)
value.FromBytes(internal.ReverseScalarBytes(ret.Bytes()))
return &ScalarEd448{value}, nil
}
func (s *ScalarEd448) Sqrt() (Scalar, error) {
return nil, errors.New("not supported")
}
func (s *ScalarEd448) Cube() Scalar {
value := &goldilocks.Scalar{}
value.Mul(s.value, s.value)
value.Mul(value, s.value)
return &ScalarEd448{value}
}
func (s *ScalarEd448) Add(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarEd448)
if ok {
value := &goldilocks.Scalar{}
value.Add(s.value, r.value)
return &ScalarEd448{value}
} else {
return nil
}
}
func (s *ScalarEd448) Sub(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarEd448)
if ok {
value := &goldilocks.Scalar{}
value.Sub(s.value, r.value)
return &ScalarEd448{value}
} else {
return nil
}
}
func (s *ScalarEd448) Mul(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarEd448)
if ok {
value := &goldilocks.Scalar{}
value.Mul(s.value, r.value)
return &ScalarEd448{value}
} else {
return nil
}
}
func (s *ScalarEd448) MulAdd(y, z Scalar) Scalar {
yy, ok := y.(*ScalarEd448)
if !ok {
return nil
}
zz, ok := z.(*ScalarEd448)
if !ok {
return nil
}
value := &goldilocks.Scalar{}
value.Mul(s.value, yy.value)
value.Add(value, zz.value)
return &ScalarEd448{value}
}
func (s *ScalarEd448) Div(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarEd448)
if ok {
value, err := r.Invert()
if err != nil {
return nil
}
i, _ := value.(*ScalarEd448)
i.value.Mul(i.value, s.value)
return &ScalarEd448{value: i.value}
} else {
return nil
}
}
func (s *ScalarEd448) Neg() Scalar {
value := &goldilocks.Scalar{}
copy(value[:], s.value[:])
value.Neg()
return &ScalarEd448{value}
}
func (s *ScalarEd448) SetBigInt(x *big.Int) (Scalar, error) {
if x == nil {
return nil, fmt.Errorf("invalid value")
}
order := new(big.Int)
orderBuf := internal.ReverseScalarBytes(ed448Order[:])
order.SetBytes(orderBuf)
var v big.Int
buf := v.Mod(x, order).Bytes()
var rBuf [56]byte
copy(rBuf[:], internal.ReverseScalarBytes(buf))
value := &goldilocks.Scalar{}
value.FromBytes(rBuf[:])
return &ScalarEd448{value}, nil
}
func (s *ScalarEd448) BigInt() *big.Int {
var ret big.Int
buf := internal.ReverseScalarBytes(s.value[:])
return ret.SetBytes(buf)
}
func (s *ScalarEd448) Bytes() []byte {
return s.value[:]
}
// SetBytes takes input a 56-byte long array and returns a Ed448 scalar.
// The input must be 56-byte long and must be a reduced bytes.
func (s *ScalarEd448) SetBytes(input []byte) (Scalar, error) {
if len(input) != 56 {
return nil, fmt.Errorf("invalid byte sequence")
}
value := &goldilocks.Scalar{}
value.FromBytes(input[:])
return &ScalarEd448{value}, nil
}
// SetBytesWide takes input a 112-byte long byte array, reduce it and return an
// Ed448 scalar. If bytes is not of the right length, it returns nil and an
// error
func (s *ScalarEd448) SetBytesWide(bytes []byte) (Scalar, error) {
if len(bytes) != 56 {
return nil, fmt.Errorf("invalid byte sequence")
}
value := &goldilocks.Scalar{}
value.FromBytes(bytes[:])
return &ScalarEd448{value}, nil
}
// This function takes an input x and sets s = x, where x is a 56-byte
// little-endian encoding of s, then it returns the corresponding Ed448 scalar.
// If the input is not a canonical encoding of s, it returns nil and an error.
func (s *ScalarEd448) SetBytesCanonical(bytes []byte) (Scalar, error) {
return s.SetBytes(bytes)
}
func (s *ScalarEd448) Point() Point {
return new(PointEd448).Identity()
}
func (s *ScalarEd448) Clone() Scalar {
value := &goldilocks.Scalar{}
value.FromBytes(s.value[:])
return &ScalarEd448{value}
}
func (s *ScalarEd448) MarshalBinary() ([]byte, error) {
return scalarMarshalBinary(s)
}
func (s *ScalarEd448) UnmarshalBinary(input []byte) error {
sc, err := scalarUnmarshalBinary(input)
if err != nil {
return err
}
ss, ok := sc.(*ScalarEd448)
if !ok {
return fmt.Errorf("invalid scalar")
}
s.value = ss.value
return nil
}
func (s *ScalarEd448) MarshalText() ([]byte, error) {
return scalarMarshalText(s)
}
func (s *ScalarEd448) UnmarshalText(input []byte) error {
sc, err := scalarUnmarshalText(input)
if err != nil {
return err
}
ss, ok := sc.(*ScalarEd448)
if !ok {
return fmt.Errorf("invalid scalar")
}
s.value = ss.value
return nil
}
func (s *ScalarEd448) MarshalJSON() ([]byte, error) {
return scalarMarshalJson(s)
}
func (s *ScalarEd448) UnmarshalJSON(input []byte) error {
sc, err := scalarUnmarshalJson(input)
if err != nil {
return err
}
S, ok := sc.(*ScalarEd448)
if !ok {
return fmt.Errorf("invalid type")
}
s.value = S.value
return nil
}
func (p *PointEd448) Random(reader io.Reader) Point {
var seed [114]byte
_, _ = reader.Read(seed[:])
return p.Hash(seed[:])
}
func (p *PointEd448) Hash(bytes []byte) Point {
hashBytes := make([]byte, 114)
h := sha3.NewShake256()
_, _ = h.Write(bytes)
_, _ = h.Read(hashBytes[:])
value := &goldilocks.Scalar{}
hashBytes[0] &= 0xFC
hashBytes[55] |= 0x80
hashBytes[56] = 0x00
value.FromBytes(hashBytes[:57])
point := (goldilocks.Curve{}).ScalarBaseMult(value)
return &PointEd448{value: point}
}
func (p *PointEd448) Identity() Point {
return &PointEd448{
value: (goldilocks.Curve{}).Identity(),
}
}
func (p *PointEd448) Generator() Point {
return &PointEd448{
value: (goldilocks.Curve{}).Generator(),
}
}
func (p *PointEd448) IsIdentity() bool {
return p.Equal(p.Identity())
}
func (p *PointEd448) IsNegative() bool {
// Negative points don't really exist in Ed448
return false
}
func (p *PointEd448) IsOnCurve() bool {
err := (goldilocks.Curve{}).Identity().UnmarshalBinary(
p.ToAffineCompressed(),
)
return err == nil
}
func (p *PointEd448) Double() Point {
value, err := p.value.MarshalBinary()
if err != nil {
return nil
}
clone := &goldilocks.Point{}
if err := clone.UnmarshalBinary(value); err != nil {
return nil
}
clone.Double()
return &PointEd448{value: clone}
}
func (p *PointEd448) Scalar() Scalar {
return new(ScalarEd448).Zero()
}
func (p *PointEd448) Neg() Point {
value, err := p.value.MarshalBinary()
if err != nil {
return nil
}
clone := &goldilocks.Point{}
if err := clone.UnmarshalBinary(value); err != nil {
return nil
}
clone.Neg()
return &PointEd448{value: clone}
}
func (p *PointEd448) Add(rhs Point) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*PointEd448)
if ok {
value, err := p.value.MarshalBinary()
if err != nil {
return nil
}
clone := &goldilocks.Point{}
if err := clone.UnmarshalBinary(value); err != nil {
return nil
}
clone.Add(r.value)
return &PointEd448{value: clone}
} else {
return nil
}
}
func (p *PointEd448) Sub(rhs Point) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*PointEd448)
if ok {
value, err := r.value.MarshalBinary()
if err != nil {
return nil
}
clone := &goldilocks.Point{}
if err := clone.UnmarshalBinary(value); err != nil {
return nil
}
clone.Neg()
clone.Add(p.value)
return &PointEd448{value: clone}
} else {
return nil
}
}
func (p *PointEd448) Mul(rhs Scalar) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*ScalarEd448)
if ok {
value, err := p.value.MarshalBinary()
if err != nil {
return nil
}
clone := &goldilocks.Point{}
if err := clone.UnmarshalBinary(value); err != nil {
return nil
}
clone = (goldilocks.Curve{}).ScalarMult(r.value, clone)
return &PointEd448{value: clone}
} else {
return nil
}
}
func (p *PointEd448) Equal(rhs Point) bool {
r, ok := rhs.(*PointEd448)
if ok {
return p.value.IsEqual(r.value)
} else {
return false
}
}
func (p *PointEd448) Set(x, y *big.Int) (Point, error) {
// check is identity
xx := subtle.ConstantTimeCompare(x.Bytes(), []byte{})
yy := subtle.ConstantTimeCompare(y.Bytes(), []byte{})
if (xx | yy) == 1 {
return p.Identity(), nil
}
xElem := &fp448.Elt{}
yElem := &fp448.Elt{}
copy(xElem[:], internal.ReverseScalarBytes(x.Bytes()))
copy(yElem[:], internal.ReverseScalarBytes(y.Bytes()))
point, err := goldilocks.FromAffine(xElem, yElem)
if err != nil {
return nil, err
}
return &PointEd448{value: point}, nil
}
func (p *PointEd448) ToAffineCompressed() []byte {
affineCompressed, err := p.value.MarshalBinary()
if err != nil {
return nil
}
return affineCompressed
}
func (p *PointEd448) ToAffineUncompressed() []byte {
x, y := p.value.ToAffine()
var out [112]byte
copy(out[:56], x[:])
copy(out[56:], y[:])
return out[:]
}
func (p *PointEd448) FromAffineCompressed(inBytes []byte) (Point, error) {
pt := (&goldilocks.Point{})
err := pt.UnmarshalBinary(inBytes)
if err != nil {
return nil, err
}
return &PointEd448{value: pt}, nil
}
func (p *PointEd448) FromAffineUncompressed(inBytes []byte) (Point, error) {
if len(inBytes) != 112 {
return nil, fmt.Errorf("invalid byte sequence")
}
x := &fp448.Elt{}
copy(x[:], inBytes[:56])
y := &fp448.Elt{}
copy(y[:], inBytes[56:])
value, err := goldilocks.FromAffine(x, y)
if err != nil {
return nil, err
}
return &PointEd448{value}, nil
}
func (p *PointEd448) CurveName() string {
return ED448Name
}
func (p *PointEd448) SumOfProducts(points []Point, scalars []Scalar) Point {
// Unfortunately the primitives don't have have multi-scalar mult
// implementation so we're left to do it the slow way
nScalars := make([]*ScalarEd448, len(scalars))
nPoints := make([]*PointEd448, len(points))
for i, sc := range scalars {
s, ok := sc.(*ScalarEd448)
if !ok {
return nil
}
nScalars[i] = s
}
for i, pt := range points {
pp, ok := pt.(*PointEd448)
if !ok {
return nil
}
nPoints[i] = pp
}
accum := p.Identity().(*PointEd448)
for i, p := range nPoints {
s := nScalars[i]
accum = accum.Add(p.Mul(s)).(*PointEd448)
}
return &PointEd448{value: accum.value}
}
func (p *PointEd448) MarshalBinary() ([]byte, error) {
return pointMarshalBinary(p)
}
func (p *PointEd448) UnmarshalBinary(input []byte) error {
pt, err := pointUnmarshalBinary(input)
if err != nil {
return err
}
ppt, ok := pt.(*PointEd448)
if !ok {
return fmt.Errorf("invalid point")
}
p.value = ppt.value
return nil
}
func (p *PointEd448) MarshalText() ([]byte, error) {
return pointMarshalText(p)
}
func (p *PointEd448) UnmarshalText(input []byte) error {
pt, err := pointUnmarshalText(input)
if err != nil {
return err
}
ppt, ok := pt.(*PointEd448)
if !ok {
return fmt.Errorf("invalid point")
}
p.value = ppt.value
return nil
}
func (p *PointEd448) MarshalJSON() ([]byte, error) {
return pointMarshalJson(p)
}
func (p *PointEd448) UnmarshalJSON(input []byte) error {
pt, err := pointUnmarshalJson(input)
if err != nil {
return err
}
P, ok := pt.(*PointEd448)
if !ok {
return fmt.Errorf("invalid type")
}
p.value = P.value
return nil
}