mirror of
https://github.com/sonr-io/crypto.git
synced 2026-08-02 15:31:38 +00:00
No commit suggestions generated
This commit is contained in:
Executable
+282
@@ -0,0 +1,282 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
// Package curves: Field implementation IS NOT constant time as it leverages math/big for big number operations.
|
||||
package curves
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
ed25519SubGroupOrderOnce sync.Once
|
||||
ed25519SubGroupOrder *big.Int
|
||||
)
|
||||
|
||||
// Field is a finite field.
|
||||
type Field struct {
|
||||
*big.Int
|
||||
}
|
||||
|
||||
// Element is a group element within a finite field.
|
||||
type Element struct {
|
||||
Modulus *Field `json:"modulus"`
|
||||
Value *big.Int `json:"value"`
|
||||
}
|
||||
|
||||
// ElementJSON is used in JSON<>Element conversions.
|
||||
// For years, big.Int hasn't properly supported JSON unmarshaling
|
||||
// https://github.com/golang/go/issues/28154
|
||||
type ElementJSON struct {
|
||||
Modulus string `json:"modulus"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// Marshal Element to JSON
|
||||
func (x *Element) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(ElementJSON{
|
||||
Modulus: x.Modulus.String(),
|
||||
Value: x.Value.String(),
|
||||
})
|
||||
}
|
||||
|
||||
func (x *Element) UnmarshalJSON(bytes []byte) error {
|
||||
var e ElementJSON
|
||||
err := json.Unmarshal(bytes, &e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Convert the strings to big.Ints
|
||||
modulus, ok := new(big.Int).SetString(e.Modulus, 10)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to unmarshal modulus string '%v' to big.Int", e.Modulus)
|
||||
}
|
||||
x.Modulus = &Field{modulus}
|
||||
x.Value, ok = new(big.Int).SetString(e.Value, 10)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to unmarshal value string '%v' to big.Int", e.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The probability of returning true for a randomly chosen
|
||||
// non-prime is at most ¼ⁿ. 64 is a widely used standard
|
||||
// that is more than sufficient.
|
||||
const millerRabinRounds = 64
|
||||
|
||||
// New is a constructor for a Field.
|
||||
func NewField(modulus *big.Int) *Field {
|
||||
// For our purposes we never expect to be dealing with a non-prime field. This provides some protection against
|
||||
// accidentally doing that.
|
||||
if !modulus.ProbablyPrime(millerRabinRounds) {
|
||||
panic(fmt.Sprintf("modulus: %x is not a prime", modulus))
|
||||
}
|
||||
|
||||
return &Field{modulus}
|
||||
}
|
||||
|
||||
func newElement(field *Field, value *big.Int) *Element {
|
||||
if !field.IsValid(value) {
|
||||
panic(fmt.Sprintf("value: %x is not within field: %x", value, field))
|
||||
}
|
||||
|
||||
return &Element{field, value}
|
||||
}
|
||||
|
||||
// IsValid returns whether or not the value is within [0, modulus)
|
||||
func (f Field) IsValid(value *big.Int) bool {
|
||||
// value < modulus && value >= 0
|
||||
return value.Cmp(f.Int) < 0 && value.Sign() >= 0
|
||||
}
|
||||
|
||||
func (f Field) NewElement(value *big.Int) *Element {
|
||||
return newElement(&f, value)
|
||||
}
|
||||
|
||||
func (f Field) Zero() *Element {
|
||||
return newElement(&f, big.NewInt(0))
|
||||
}
|
||||
|
||||
func (f Field) One() *Element {
|
||||
return newElement(&f, big.NewInt(1))
|
||||
}
|
||||
|
||||
func (f Field) RandomElement(r io.Reader) (*Element, error) {
|
||||
if r == nil {
|
||||
r = rand.Reader
|
||||
}
|
||||
var randInt *big.Int
|
||||
var err error
|
||||
// Ed25519 needs to do special handling
|
||||
// in case the value is used in
|
||||
// Scalar multiplications with points
|
||||
if f.Int.Cmp(Ed25519Order()) == 0 {
|
||||
scalar := NewEd25519Scalar()
|
||||
randInt, err = scalar.RandomWithReader(r)
|
||||
} else {
|
||||
// Read a random integer within the field. This is defined as [0, max) so we don't need to
|
||||
// explicitly check it is within the field. If it is not, NewElement will panic anyways.
|
||||
randInt, err = rand.Int(r, f.Int)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newElement(&f, randInt), nil
|
||||
}
|
||||
|
||||
// ElementFromBytes initializes a new field element from big-endian bytes
|
||||
func (f Field) ElementFromBytes(bytes []byte) *Element {
|
||||
return newElement(&f, new(big.Int).SetBytes(bytes))
|
||||
}
|
||||
|
||||
// ReducedElementFromBytes initializes a new field element from big-endian bytes and reduces it by
|
||||
// the modulus of the field.
|
||||
//
|
||||
// WARNING: If this is used with cryptographic constructions which rely on a uniform distribution of
|
||||
// values, this may introduce a bias to the value of the returned field element. This happens when
|
||||
// the integer range of the provided bytes is not an integer multiple of the field order.
|
||||
//
|
||||
// Assume we are working in field which a modulus of 3 and the range of the uniform random bytes we
|
||||
// provide as input is 5. Thus, the set of field elements is {0, 1, 2} and the set of integer values
|
||||
// for the input bytes is: {0, 1, 2, 3, 4}. What is the distribution of the output values produced
|
||||
// by this function?
|
||||
//
|
||||
// ReducedElementFromBytes(0) => 0
|
||||
// ReducedElementFromBytes(1) => 1
|
||||
// ReducedElementFromBytes(2) => 2
|
||||
// ReducedElementFromBytes(3) => 0
|
||||
// ReducedElementFromBytes(4) => 1
|
||||
//
|
||||
// For a value space V and random value v, a uniform distribution is defined as P[V = v] = 1/|V|
|
||||
// where |V| is to the order of the field. Using the results from above, we see that P[v = 0] = 2/5,
|
||||
// P[v = 1] = 2/5, and P[v = 2] = 1/5. For a uniform distribution we would expect these to each be
|
||||
// equal to 1/3. As they do not, this does not return uniform output for that example.
|
||||
//
|
||||
// To see why this is okay if the range is a multiple of the field order, change the input range to
|
||||
// 6 and notice that now each output has a probability of 2/6 = 1/3, and the output is uniform.
|
||||
func (f Field) ReducedElementFromBytes(bytes []byte) *Element {
|
||||
value := new(big.Int).SetBytes(bytes)
|
||||
value.Mod(value, f.Int)
|
||||
return newElement(&f, value)
|
||||
}
|
||||
|
||||
func (x Element) Field() *Field {
|
||||
return x.Modulus
|
||||
}
|
||||
|
||||
// Add returns the sum x+y
|
||||
func (x Element) Add(y *Element) *Element {
|
||||
x.validateFields(y)
|
||||
|
||||
sum := new(big.Int).Add(x.Value, y.Value)
|
||||
sum.Mod(sum, x.Modulus.Int)
|
||||
return newElement(x.Modulus, sum)
|
||||
}
|
||||
|
||||
// Sub returns the difference x-y
|
||||
func (x Element) Sub(y *Element) *Element {
|
||||
x.validateFields(y)
|
||||
|
||||
difference := new(big.Int).Sub(x.Value, y.Value)
|
||||
difference.Mod(difference, x.Modulus.Int)
|
||||
return newElement(x.Modulus, difference)
|
||||
}
|
||||
|
||||
// Neg returns the field negation
|
||||
func (x Element) Neg() *Element {
|
||||
z := new(big.Int).Neg(x.Value)
|
||||
z.Mod(z, x.Modulus.Int)
|
||||
return newElement(x.Modulus, z)
|
||||
}
|
||||
|
||||
// Mul returns the product x*y
|
||||
func (x Element) Mul(y *Element) *Element {
|
||||
x.validateFields(y)
|
||||
|
||||
product := new(big.Int).Mul(x.Value, y.Value)
|
||||
product.Mod(product, x.Modulus.Int)
|
||||
return newElement(x.Modulus, product)
|
||||
}
|
||||
|
||||
// Div returns the quotient x/y
|
||||
func (x Element) Div(y *Element) *Element {
|
||||
x.validateFields(y)
|
||||
|
||||
yInv := new(big.Int).ModInverse(y.Value, x.Modulus.Int)
|
||||
quotient := new(big.Int).Mul(x.Value, yInv)
|
||||
quotient.Mod(quotient, x.Modulus.Int)
|
||||
return newElement(x.Modulus, quotient)
|
||||
}
|
||||
|
||||
// Pow computes x^y reduced by the modulus
|
||||
func (x Element) Pow(y *Element) *Element {
|
||||
x.validateFields(y)
|
||||
|
||||
return newElement(x.Modulus, new(big.Int).Exp(x.Value, y.Value, x.Modulus.Int))
|
||||
}
|
||||
|
||||
func (x Element) Invert() *Element {
|
||||
return newElement(x.Modulus, new(big.Int).ModInverse(x.Value, x.Modulus.Int))
|
||||
}
|
||||
|
||||
func (x Element) Sqrt() *Element {
|
||||
return newElement(x.Modulus, new(big.Int).ModSqrt(x.Value, x.Modulus.Int))
|
||||
}
|
||||
|
||||
// BigInt returns value as a big.Int
|
||||
func (x Element) BigInt() *big.Int {
|
||||
return x.Value
|
||||
}
|
||||
|
||||
// Bytes returns the value as bytes
|
||||
func (x Element) Bytes() []byte {
|
||||
return x.BigInt().Bytes()
|
||||
}
|
||||
|
||||
// IsEqual returns x == y
|
||||
func (x Element) IsEqual(y *Element) bool {
|
||||
if !x.isEqualFields(y) {
|
||||
return false
|
||||
}
|
||||
|
||||
return x.Value.Cmp(y.Value) == 0
|
||||
}
|
||||
|
||||
// Clone returns a new copy of the element
|
||||
func (x Element) Clone() *Element {
|
||||
return x.Modulus.ElementFromBytes(x.Bytes())
|
||||
}
|
||||
|
||||
func (x Element) isEqualFields(y *Element) bool {
|
||||
return x.Modulus.Int.Cmp(y.Modulus.Int) == 0
|
||||
}
|
||||
|
||||
func (x Element) validateFields(y *Element) {
|
||||
if !x.isEqualFields(y) {
|
||||
panic("fields must match for valid binary operation")
|
||||
}
|
||||
}
|
||||
|
||||
// SubgroupOrder returns the order of the Ed25519 base Point.
|
||||
func Ed25519Order() *big.Int {
|
||||
ed25519SubGroupOrderOnce.Do(func() {
|
||||
order, ok := new(big.Int).SetString(
|
||||
"1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED",
|
||||
16,
|
||||
)
|
||||
if !ok {
|
||||
panic("invalid hex string provided. This should never happen as it is constant.")
|
||||
}
|
||||
ed25519SubGroupOrder = order
|
||||
})
|
||||
|
||||
return ed25519SubGroupOrder
|
||||
}
|
||||
Reference in New Issue
Block a user