feature/1220 origin handle exists method (#1241)

* feat: add docs and CI workflow for publishing to onsonr.dev

* (refactor): Move hway,motr executables to their own repos

* feat: simplify devnet and testnet configurations

* refactor: update import path for didcrypto package

* docs(networks): Add README with project overview, architecture, and community links

* refactor: Move network configurations to deploy directory

* build: update golang version to 1.23

* refactor: move logger interface to appropriate package

* refactor: Move devnet configuration to networks/devnet

* chore: improve release process with date variable

* (chore): Move Crypto Library

* refactor: improve code structure and readability in DID module

* feat: integrate Trunk CI checks

* ci: optimize CI workflow by removing redundant build jobs

---------

Co-authored-by: Darp Alakun <i@prad.nu>
This commit is contained in:
Prad Nukala
2025-01-06 17:06:10 +00:00
committed by GitHub
co-authored by root
parent 9bd5e41fa0
commit 807b2e86ec
483 changed files with 8067 additions and 12559 deletions
+14
View File
@@ -0,0 +1,14 @@
---
aliases: [README]
tags: []
title: README
linter-yaml-title-alias: README
date created: Wednesday, April 17th 2024, 4:11:40 pm
date modified: Thursday, April 18th 2024, 8:19:25 am
---
## Core Package
The core package contains a set of primitives, including but not limited to various
elliptic curves, hashes, and commitment schemes. These primitives are used internally
and can also be used independently on their own externally.
+115
View File
@@ -0,0 +1,115 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package core
import (
"crypto/hmac"
crand "crypto/rand"
"crypto/sha256"
"crypto/subtle"
"encoding/json"
"fmt"
"hash"
)
// Size of random values and hash outputs are determined by our hash function
const Size = sha256.Size
type (
// Commitment to a given message which can be later revealed.
// This is sent to and held by a verifier until the corresponding
// witness is provided.
Commitment []byte
// Witness is sent to and opened by the verifier. This proves that
// committed message hasn't been altered by later information.
Witness struct {
Msg []byte
r [Size]byte
}
// witnessJSON is used for un/marshaling.
witnessJSON struct {
Msg []byte
R [Size]byte
}
)
// MarshalJSON encodes Witness in JSON
func (w Witness) MarshalJSON() ([]byte, error) {
return json.Marshal(witnessJSON{w.Msg, w.r})
}
// UnmarshalJSON decodes JSON into a Witness struct
func (w *Witness) UnmarshalJSON(data []byte) error {
witness := &witnessJSON{}
err := json.Unmarshal(data, witness)
if err != nil {
return err
}
w.Msg = witness.Msg
w.r = witness.R
return nil
}
// Commit to a given message. Uses SHA256 as the hash function.
func Commit(msg []byte) (Commitment, *Witness, error) {
// Initialize our decommitment
d := Witness{msg, [Size]byte{}}
// Generate a random nonce of the required length
n, err := crand.Read(d.r[:])
// Ensure no errors retrieving nonce
if err != nil {
return nil, nil, err
}
// Ensure we read all the bytes expected
if n != Size {
return nil, nil, fmt.Errorf("failed to read %v bytes from crypto/rand: received %v bytes", Size, n)
}
// Compute the commitment: HMAC(Sha2, msg, key)
c, err := ComputeHMAC(sha256.New, msg, d.r[:])
if err != nil {
return nil, nil, err
}
return c, &d, nil
}
// Open a commitment and return true if the commitment/decommitment pair are valid.
// reference: spec.§2.4: Commitment Scheme
func Open(c Commitment, d Witness) (bool, error) {
// Ensure commitment is well-formed.
if len(c) != Size {
return false, fmt.Errorf("invalid commitment, wrong length. %v != %v", len(c), Size)
}
// Re-compute the commitment: HMAC(Sha2, msg, key)
cʹ, err := ComputeHMAC(sha256.New, d.Msg, d.r[:])
if err != nil {
return false, err
}
return subtle.ConstantTimeCompare(cʹ, c) == 1, nil
}
// ComputeHMAC computes HMAC(hash_fn, msg, key)
// Takes in a hash function to use for HMAC
func ComputeHMAC(f func() hash.Hash, msg []byte, k []byte) ([]byte, error) {
if f == nil {
return nil, fmt.Errorf("hash function cannot be nil")
}
mac := hmac.New(f, k)
w, err := mac.Write(msg)
if w != len(msg) {
return nil, fmt.Errorf("bytes written to hash doesn't match expected: %v != %v", w, len(msg))
} else if err != nil {
return nil, err
}
return mac.Sum(nil), nil
}
+374
View File
@@ -0,0 +1,374 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package core
import (
"bytes"
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
// An entry into our test table
type entry struct {
// Input
msg []byte
// Result (actual, not expected)
commit Commitment
decommit *Witness
err error
}
// Test inputs and placeholders for results that will be filled in
// during init()
var testResults = []entry{
{[]byte("This is a test message"), nil, nil, nil},
{[]byte("short msg"), nil, nil, nil},
{[]byte("This input field is intentionally longer than the SHA256 block size to ensure that the entire message is processed"),
nil, nil, nil},
{[]byte{0xFB, 0x1A, 0x18, 0x47, 0x39, 0x3C, 0x9F, 0x45, 0x5F, 0x29, 0x4C, 0x51, 0x42, 0x30, 0xA6, 0xB9},
nil, nil, nil},
// msg = \epsilon (empty string)
{[]byte{}, nil, nil, nil},
// msg == nil
{nil, nil, nil, nil},
}
// Run our inputs through commit and record the outputs
func init() {
for i := range testResults {
entry := &testResults[i]
entry.commit, entry.decommit, entry.err = Commit(entry.msg)
}
}
// Computing commitments should never produce errors
func TestCommitWithoutErrors(t *testing.T) {
for _, entry := range testResults {
if entry.err != nil {
t.Errorf("received Commit(%v): %v", entry.msg, entry.err)
}
}
}
// Commitments should be 256b == 64B in length
func TestCommitmentsAreExpectedLength(t *testing.T) {
const expLen = 256 / 8
for _, entry := range testResults {
if len(entry.commit) != expLen {
t.Errorf("commitment is not expected length: %v != %v", len(entry.commit), expLen)
}
}
}
// Decommit cannot be nil
func TestCommmitProducesDecommit(t *testing.T) {
for _, entry := range testResults {
if entry.decommit == nil {
t.Errorf("decommit cannot be nil: Commit(%v)", entry.msg)
}
}
}
// Decommit value should contain the same message
func TestCommmitProducesDecommitWithSameMessage(t *testing.T) {
for _, entry := range testResults {
if !bytes.Equal(entry.msg, entry.decommit.Msg) {
t.Errorf("decommit.msg != msg: %v != %v", entry.msg, entry.decommit.Msg)
}
}
}
// Commitments should be unique
func TestCommmitProducesDistinctCommitments(t *testing.T) {
seen := make(map[[Size]byte]bool)
// Check the pre-computed commitments for uniquness
for _, entry := range testResults {
// Slices cannot be used as hash keys, so we need to copy into
// an array. Oh, go-lang.
var cee [Size]byte
copy(cee[:], entry.commit)
// Ensure each commit is unique
if seen[cee] {
t.Errorf("duplicate commit found: %v", cee)
}
seen[cee] = true
}
}
// Commitments should be unique even for the same message since the nonce is
// randomly selected
func TestCommmitDistinctCommitments(t *testing.T) {
seen := make(map[[Size]byte]bool)
msg := []byte("black lives matter")
const iterations = 1000
// Check the pre-computed commitments for uniquness
for i := 0; i < iterations; i++ {
// Compute a commitment
c, _, err := Commit(msg)
if err != nil {
t.Error(err)
}
// Slices cannot be used as hash keys, so copy into an array
var cee [Size]byte
copy(cee[:], []byte(c))
// Ensure each commit is unique
if seen[cee] {
t.Errorf("duplicate commit found: %v", cee)
}
seen[cee] = true
}
}
// Nonces must be 256b = 64B
func TestCommmitNonceIsExpectedLength(t *testing.T) {
const expLen = 256 / 8
// Check the pre-computed nonces
for _, entry := range testResults {
if len(entry.decommit.r) != expLen {
t.Errorf("nonce is not expected length: %v != %v", len(entry.decommit.r), expLen)
}
}
}
// Randomly selected nonces will be unique with overwhelming probability
func TestCommmitProducesDistinctNonces(t *testing.T) {
seen := make(map[[Size]byte]bool)
msg := []byte("black lives matter")
const iterations = 1000
// Check the pre-computed commitments for uniquness
for i := 0; i < iterations; i++ {
// Compute a commitment
_, dee, err := Commit(msg)
if err != nil {
t.Error(err)
}
// Ensure each nonce is unique
if seen[dee.r] {
t.Errorf("duplicate nonce found: %v", dee.r)
}
seen[dee.r] = true
}
}
func TestOpenOnValidCommitments(t *testing.T) {
for _, entry := range testResults {
// Open each commitment
ok, err := Open(entry.commit, *entry.decommit)
// There should be no error
if err != nil {
t.Error(err)
}
// The commitments should verify
if !ok {
t.Errorf("commitment failed to open: %v", entry.msg)
}
}
}
func TestOpenOnModifiedNonce(t *testing.T) {
for _, entry := range testResults {
dʹ := copyWitness(entry.decommit)
// Modify the nonce
dʹ.r[0] ^= 0x40
// Open and check for failure
ok, err := Open(entry.commit, *dʹ)
assertFailedOpen(t, ok, err)
}
}
func TestOpenOnZeroPrefixNonce(t *testing.T) {
for _, entry := range testResults {
dʹ := copyWitness(entry.decommit)
// Modify the nonce
dʹ.r[0] = 0x00
dʹ.r[1] = 0x00
dʹ.r[2] = 0x00
dʹ.r[3] = 0x00
dʹ.r[4] = 0x00
dʹ.r[5] = 0x00
dʹ.r[6] = 0x00
dʹ.r[7] = 0x00
dʹ.r[8] = 0x00
dʹ.r[9] = 0x00
dʹ.r[10] = 0x00
// Open and check for failure
ok, err := Open(entry.commit, *dʹ)
assertFailedOpen(t, ok, err)
}
}
// Makes a deep copy of a Witness
func copyWitness(d *Witness) *Witness {
msg := make([]byte, len(d.Msg))
var r [Size]byte
copy(msg, d.Msg)
copy(r[:], d.r[:])
return &Witness{msg, r}
}
// Asserts that err != nil, and ok == false.
func assertFailedOpen(t *testing.T, ok bool, err error) {
// There should be no error
if err != nil {
t.Error(err)
}
// But the commitments should fail
if ok {
t.Error("commitment was verified but was expected to fail")
}
}
// An unrelated message should fail on open
func TestOpenOnNewMessage(t *testing.T) {
for _, entry := range testResults {
dʹ := copyWitness(entry.decommit)
// Use a distinct message
dʹ.Msg = []byte("no one expects the spanish inquisition")
// Open and check for failure
ok, err := Open(entry.commit, *dʹ)
assertFailedOpen(t, ok, err)
}
}
// An appended message should fail on open
func TestOpenOnAppendedMessage(t *testing.T) {
for _, entry := range testResults {
dʹ := copyWitness(entry.decommit)
// Modify the message
dʹ.Msg = []byte("no one expects the spanish inquisition")
// Open and check for failure
ok, err := Open(entry.commit, *dʹ)
assertFailedOpen(t, ok, err)
}
}
// A modified message should fail on open
func TestOpenOnModifiedMessage(t *testing.T) {
for _, entry := range testResults {
// Skip the empty string message for this test case
if len(entry.msg) == 0 {
continue
}
// Modify the message _in situ_
dʹ := copyWitness(entry.decommit)
dʹ.Msg[1] ^= 0x99
// Open and check for failure
ok, err := Open(entry.commit, *dʹ)
assertFailedOpen(t, ok, err)
}
}
// A modified commitment should fail on open
func TestOpenOnModifiedCommitment(t *testing.T) {
for _, entry := range testResults {
// Copy and then modify the commitment
cʹ := make([]byte, Size)
copy(cʹ[:], entry.commit)
cʹ[6] ^= 0x33
// Open and check for failure
ok, err := Open(cʹ, *entry.decommit)
assertFailedOpen(t, ok, err)
}
}
// An empty decommit should fail to open
func TestOpenOnDefaultDecommitObject(t *testing.T) {
for _, entry := range testResults {
// Open and check for failure
ok, err := Open(entry.commit, Witness{})
assertFailedOpen(t, ok, err)
}
}
// A nil commit should return an error
func TestOpenOnNilCommitment(t *testing.T) {
_, err := Open(nil, Witness{})
assertError(t, err)
}
// Verifies that err != nil
func assertError(t *testing.T, err error) {
if err == nil {
t.Error("expected an error but received nil")
}
}
// Ill-formed commitment should produce an error
func TestOpenOnLongCommitment(t *testing.T) {
tooLong := make([]byte, Size+1)
_, err := Open(tooLong, Witness{})
assertError(t, err)
}
// Ill-formed commitment should produce an error
func TestOpenOnShortCommitment(t *testing.T) {
tooShort := make([]byte, Size-1)
_, err := Open(tooShort, Witness{})
assertError(t, err)
}
// Tests that marshal-unmarshal is the identity function
func TestWitnessMarshalRoundTrip(t *testing.T) {
expected := &Witness{
[]byte("I'm the dude. So that's what you call me"),
[Size]byte{0xAC},
}
// Marhal and test
jsonBytes, err := json.Marshal(expected)
require.NoError(t, err)
require.NotNil(t, jsonBytes)
// Unmarshal and test
actual := &Witness{}
require.NoError(t, json.Unmarshal(jsonBytes, actual))
require.Equal(t, expected.Msg, actual.Msg)
require.Equal(t, expected.r, actual.r)
}
// Tests that marshal-unmarshal is the identity function
func TestCommitmentMarshalRoundTrip(t *testing.T) {
expected := Commitment([]byte("That or uh his-dudeness or duder or el duderino."))
// Marhal and test
jsonBytes, err := json.Marshal(expected)
require.NoError(t, err)
require.NotNil(t, jsonBytes)
// Unmarshal and test
actual := Commitment{}
require.NoError(t, json.Unmarshal(jsonBytes, &actual))
require.Equal(t, []byte(expected), []byte(actual))
}
+1226
View File
File diff suppressed because it is too large Load Diff
+1129
View File
File diff suppressed because it is too large Load Diff
+863
View File
@@ -0,0 +1,863 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"crypto/elliptic"
"encoding/hex"
"encoding/json"
"fmt"
"hash"
"io"
"math/big"
"sync"
"github.com/onsonr/sonr/crypto/core/curves/native/bls12381"
)
var (
k256Initonce sync.Once
k256 Curve
bls12381g1Initonce sync.Once
bls12381g1 Curve
bls12381g2Initonce sync.Once
bls12381g2 Curve
bls12377g1Initonce sync.Once
bls12377g1 Curve
bls12377g2Initonce sync.Once
bls12377g2 Curve
p256Initonce sync.Once
p256 Curve
ed25519Initonce sync.Once
ed25519 Curve
pallasInitonce sync.Once
pallas Curve
)
const (
K256Name = "secp256k1"
BLS12381G1Name = "BLS12381G1"
BLS12381G2Name = "BLS12381G2"
BLS12831Name = "BLS12831"
P256Name = "P-256"
ED25519Name = "ed25519"
PallasName = "pallas"
BLS12377G1Name = "BLS12377G1"
BLS12377G2Name = "BLS12377G2"
BLS12377Name = "BLS12377"
)
const scalarBytes = 32
// Scalar represents an element of the scalar field \mathbb{F}_q
// of the elliptic curve construction.
type Scalar interface {
// Random returns a random scalar using the provided reader
// to retrieve bytes
Random(reader io.Reader) Scalar
// Hash the specific bytes in a manner to yield a
// uniformly distributed scalar
Hash(bytes []byte) Scalar
// Zero returns the additive identity element
Zero() Scalar
// One returns the multiplicative identity element
One() Scalar
// IsZero returns true if this element is the additive identity element
IsZero() bool
// IsOne returns true if this element is the multiplicative identity element
IsOne() bool
// IsOdd returns true if this element is odd
IsOdd() bool
// IsEven returns true if this element is even
IsEven() bool
// New returns an element with the value equal to `value`
New(value int) Scalar
// Cmp returns
// -2 if this element is in a different field than rhs
// -1 if this element is less than rhs
// 0 if this element is equal to rhs
// 1 if this element is greater than rhs
Cmp(rhs Scalar) int
// Square returns element*element
Square() Scalar
// Double returns element+element
Double() Scalar
// Invert returns element^-1 mod p
Invert() (Scalar, error)
// Sqrt computes the square root of this element if it exists.
Sqrt() (Scalar, error)
// Cube returns element*element*element
Cube() Scalar
// Add returns element+rhs
Add(rhs Scalar) Scalar
// Sub returns element-rhs
Sub(rhs Scalar) Scalar
// Mul returns element*rhs
Mul(rhs Scalar) Scalar
// MulAdd returns element * y + z mod p
MulAdd(y, z Scalar) Scalar
// Div returns element*rhs^-1 mod p
Div(rhs Scalar) Scalar
// Neg returns -element mod p
Neg() Scalar
// SetBigInt returns this element set to the value of v
SetBigInt(v *big.Int) (Scalar, error)
// BigInt returns this element as a big integer
BigInt() *big.Int
// Point returns the associated point for this scalar
Point() Point
// Bytes returns the canonical byte representation of this scalar
Bytes() []byte
// SetBytes creates a scalar from the canonical representation expecting the exact number of bytes needed to represent the scalar
SetBytes(bytes []byte) (Scalar, error)
// SetBytesWide creates a scalar expecting double the exact number of bytes needed to represent the scalar which is reduced by the modulus
SetBytesWide(bytes []byte) (Scalar, error)
// Clone returns a cloned Scalar of this value
Clone() Scalar
}
type PairingScalar interface {
Scalar
SetPoint(p Point) PairingScalar
}
func unmarshalScalar(input []byte) (*Curve, []byte, error) {
sep := byte(':')
i := 0
for ; i < len(input); i++ {
if input[i] == sep {
break
}
}
name := string(input[:i])
curve := GetCurveByName(name)
if curve == nil {
return nil, nil, fmt.Errorf("unrecognized curve")
}
return curve, input[i+1:], nil
}
func scalarMarshalBinary(scalar Scalar) ([]byte, error) {
// All scalars are 32 bytes long
// The last 32 bytes are the actual value
// The first remaining bytes are the curve name
// separated by a colon
name := []byte(scalar.Point().CurveName())
output := make([]byte, len(name)+1+scalarBytes)
copy(output[:len(name)], name)
output[len(name)] = byte(':')
copy(output[len(name)+1:], scalar.Bytes())
return output, nil
}
func scalarUnmarshalBinary(input []byte) (Scalar, error) {
// All scalars are 32 bytes long
// The first 32 bytes are the actual value
// The remaining bytes are the curve name
if len(input) < scalarBytes+1+len(P256Name) {
return nil, fmt.Errorf("invalid byte sequence")
}
sc, data, err := unmarshalScalar(input)
if err != nil {
return nil, err
}
return sc.Scalar.SetBytes(data)
}
func scalarMarshalText(scalar Scalar) ([]byte, error) {
// All scalars are 32 bytes long
// For text encoding we put the curve name first for readability
// separated by a colon, then the hex encoding of the scalar
// which avoids the base64 weakness with strict mode or not
name := []byte(scalar.Point().CurveName())
output := make([]byte, len(name)+1+scalarBytes*2)
copy(output[:len(name)], name)
output[len(name)] = byte(':')
_ = hex.Encode(output[len(name)+1:], scalar.Bytes())
return output, nil
}
func scalarUnmarshalText(input []byte) (Scalar, error) {
if len(input) < scalarBytes*2+len(P256Name)+1 {
return nil, fmt.Errorf("invalid byte sequence")
}
curve, data, err := unmarshalScalar(input)
if err != nil {
return nil, err
}
var t [scalarBytes]byte
_, err = hex.Decode(t[:], data)
if err != nil {
return nil, err
}
return curve.Scalar.SetBytes(t[:])
}
func scalarMarshalJson(scalar Scalar) ([]byte, error) {
m := make(map[string]string, 2)
m["type"] = scalar.Point().CurveName()
m["value"] = hex.EncodeToString(scalar.Bytes())
return json.Marshal(m)
}
func scalarUnmarshalJson(input []byte) (Scalar, error) {
var m map[string]string
err := json.Unmarshal(input, &m)
if err != nil {
return nil, err
}
curve := GetCurveByName(m["type"])
if curve == nil {
return nil, fmt.Errorf("invalid type")
}
s, err := hex.DecodeString(m["value"])
if err != nil {
return nil, err
}
S, err := curve.Scalar.SetBytes(s)
if err != nil {
return nil, err
}
return S, nil
}
// Point represents an elliptic curve point
type Point interface {
Random(reader io.Reader) Point
Hash(bytes []byte) Point
Identity() Point
Generator() Point
IsIdentity() bool
IsNegative() bool
IsOnCurve() bool
Double() Point
Scalar() Scalar
Neg() Point
Add(rhs Point) Point
Sub(rhs Point) Point
Mul(rhs Scalar) Point
Equal(rhs Point) bool
Set(x, y *big.Int) (Point, error)
ToAffineCompressed() []byte
ToAffineUncompressed() []byte
FromAffineCompressed(bytes []byte) (Point, error)
FromAffineUncompressed(bytes []byte) (Point, error)
CurveName() string
SumOfProducts(points []Point, scalars []Scalar) Point
}
type PairingPoint interface {
Point
OtherGroup() PairingPoint
Pairing(rhs PairingPoint) Scalar
MultiPairing(...PairingPoint) Scalar
}
func pointMarshalBinary(point Point) ([]byte, error) {
// Always stores points in compressed form
// The first bytes are the curve name
// separated by a colon followed by the compressed point
// bytes
t := point.ToAffineCompressed()
name := []byte(point.CurveName())
output := make([]byte, len(name)+1+len(t))
copy(output[:len(name)], name)
output[len(name)] = byte(':')
copy(output[len(output)-len(t):], t)
return output, nil
}
func pointUnmarshalBinary(input []byte) (Point, error) {
if len(input) < scalarBytes+1+len(P256Name) {
return nil, fmt.Errorf("invalid byte sequence")
}
sep := byte(':')
i := 0
for ; i < len(input); i++ {
if input[i] == sep {
break
}
}
name := string(input[:i])
curve := GetCurveByName(name)
if curve == nil {
return nil, fmt.Errorf("unrecognized curve")
}
return curve.Point.FromAffineCompressed(input[i+1:])
}
func pointMarshalText(point Point) ([]byte, error) {
// Always stores points in compressed form
// The first bytes are the curve name
// separated by a colon followed by the compressed point
// bytes
t := point.ToAffineCompressed()
name := []byte(point.CurveName())
output := make([]byte, len(name)+1+len(t)*2)
copy(output[:len(name)], name)
output[len(name)] = byte(':')
hex.Encode(output[len(output)-len(t)*2:], t)
return output, nil
}
func pointUnmarshalText(input []byte) (Point, error) {
if len(input) < scalarBytes*2+1+len(P256Name) {
return nil, fmt.Errorf("invalid byte sequence")
}
sep := byte(':')
i := 0
for ; i < len(input); i++ {
if input[i] == sep {
break
}
}
name := string(input[:i])
curve := GetCurveByName(name)
if curve == nil {
return nil, fmt.Errorf("unrecognized curve")
}
buffer := make([]byte, (len(input)-i)/2)
_, err := hex.Decode(buffer, input[i+1:])
if err != nil {
return nil, err
}
return curve.Point.FromAffineCompressed(buffer)
}
func pointMarshalJSON(point Point) ([]byte, error) {
m := make(map[string]string, 2)
m["type"] = point.CurveName()
m["value"] = hex.EncodeToString(point.ToAffineCompressed())
return json.Marshal(m)
}
func pointUnmarshalJSON(input []byte) (Point, error) {
var m map[string]string
err := json.Unmarshal(input, &m)
if err != nil {
return nil, err
}
curve := GetCurveByName(m["type"])
if curve == nil {
return nil, fmt.Errorf("invalid type")
}
p, err := hex.DecodeString(m["value"])
if err != nil {
return nil, err
}
P, err := curve.Point.FromAffineCompressed(p)
if err != nil {
return nil, err
}
return P, nil
}
// Curve represents a named elliptic curve with a scalar field and point group
type Curve struct {
Scalar Scalar
Point Point
Name string
}
func (c Curve) ScalarBaseMult(sc Scalar) Point {
return c.Point.Generator().Mul(sc)
}
func (c Curve) NewGeneratorPoint() Point {
return c.Point.Generator()
}
func (c Curve) NewIdentityPoint() Point {
return c.Point.Identity()
}
func (c Curve) NewScalar() Scalar {
return c.Scalar.Zero()
}
// ToEllipticCurve returns the equivalent of this curve as the go interface `elliptic.Curve`
func (c Curve) ToEllipticCurve() (elliptic.Curve, error) {
err := fmt.Errorf("can't convert %s", c.Name)
switch c.Name {
case K256Name:
return K256Curve(), nil
case BLS12381G1Name:
return nil, err
case BLS12381G2Name:
return nil, err
case BLS12831Name:
return nil, err
case P256Name:
return NistP256Curve(), nil
case ED25519Name:
return nil, err
case PallasName:
return nil, err
case BLS12377G1Name:
return nil, err
case BLS12377G2Name:
return nil, err
case BLS12377Name:
return nil, err
default:
return nil, err
}
}
// PairingCurve represents a named elliptic curve
// that supports pairings
type PairingCurve struct {
Scalar PairingScalar
PointG1 PairingPoint
PointG2 PairingPoint
GT Scalar
Name string
}
func (c PairingCurve) ScalarG1BaseMult(sc Scalar) PairingPoint {
return c.PointG1.Generator().Mul(sc).(PairingPoint)
}
func (c PairingCurve) ScalarG2BaseMult(sc Scalar) PairingPoint {
return c.PointG2.Generator().Mul(sc).(PairingPoint)
}
func (c PairingCurve) NewG1GeneratorPoint() PairingPoint {
return c.PointG1.Generator().(PairingPoint)
}
func (c PairingCurve) NewG2GeneratorPoint() PairingPoint {
return c.PointG2.Generator().(PairingPoint)
}
func (c PairingCurve) NewG1IdentityPoint() PairingPoint {
return c.PointG1.Identity().(PairingPoint)
}
func (c PairingCurve) NewG2IdentityPoint() PairingPoint {
return c.PointG2.Identity().(PairingPoint)
}
func (c PairingCurve) NewScalar() PairingScalar {
return c.Scalar.Zero().(PairingScalar)
}
// GetCurveByName returns the correct `Curve` given the name
func GetCurveByName(name string) *Curve {
switch name {
case K256Name:
return K256()
case BLS12381G1Name:
return BLS12381G1()
case BLS12381G2Name:
return BLS12381G2()
case BLS12831Name:
return BLS12381G1()
case P256Name:
return P256()
case ED25519Name:
return ED25519()
case PallasName:
return PALLAS()
case BLS12377G1Name:
return BLS12377G1()
case BLS12377G2Name:
return BLS12377G2()
case BLS12377Name:
return BLS12377G1()
default:
return nil
}
}
func GetPairingCurveByName(name string) *PairingCurve {
switch name {
case BLS12381G1Name:
return BLS12381(BLS12381G1().NewIdentityPoint())
case BLS12381G2Name:
return BLS12381(BLS12381G2().NewIdentityPoint())
case BLS12831Name:
return BLS12381(BLS12381G1().NewIdentityPoint())
default:
return nil
}
}
// BLS12381G1 returns the BLS12-381 curve with points in G1
func BLS12381G1() *Curve {
bls12381g1Initonce.Do(bls12381g1Init)
return &bls12381g1
}
func bls12381g1Init() {
bls12381g1 = Curve{
Scalar: &ScalarBls12381{
Value: bls12381.Bls12381FqNew(),
point: new(PointBls12381G1),
},
Point: new(PointBls12381G1).Identity(),
Name: BLS12381G1Name,
}
}
// BLS12381G2 returns the BLS12-381 curve with points in G2
func BLS12381G2() *Curve {
bls12381g2Initonce.Do(bls12381g2Init)
return &bls12381g2
}
func bls12381g2Init() {
bls12381g2 = Curve{
Scalar: &ScalarBls12381{
Value: bls12381.Bls12381FqNew(),
point: new(PointBls12381G2),
},
Point: new(PointBls12381G2).Identity(),
Name: BLS12381G2Name,
}
}
func BLS12381(preferredPoint Point) *PairingCurve {
return &PairingCurve{
Scalar: &ScalarBls12381{
Value: bls12381.Bls12381FqNew(),
point: preferredPoint,
},
PointG1: &PointBls12381G1{
Value: new(bls12381.G1).Identity(),
},
PointG2: &PointBls12381G2{
Value: new(bls12381.G2).Identity(),
},
GT: &ScalarBls12381Gt{
Value: new(bls12381.Gt).SetOne(),
},
Name: BLS12831Name,
}
}
// BLS12377G1 returns the BLS12-377 curve with points in G1
func BLS12377G1() *Curve {
bls12377g1Initonce.Do(bls12377g1Init)
return &bls12377g1
}
func bls12377g1Init() {
bls12377g1 = Curve{
Scalar: &ScalarBls12377{
value: new(big.Int),
point: new(PointBls12377G1),
},
Point: new(PointBls12377G1).Identity(),
Name: BLS12377G1Name,
}
}
// BLS12377G2 returns the BLS12-377 curve with points in G2
func BLS12377G2() *Curve {
bls12377g2Initonce.Do(bls12377g2Init)
return &bls12377g2
}
func bls12377g2Init() {
bls12377g2 = Curve{
Scalar: &ScalarBls12377{
value: new(big.Int),
point: new(PointBls12377G2),
},
Point: new(PointBls12377G2).Identity(),
Name: BLS12377G2Name,
}
}
// K256 returns the secp256k1 curve
func K256() *Curve {
k256Initonce.Do(k256Init)
return &k256
}
func k256Init() {
k256 = Curve{
Scalar: new(ScalarK256).Zero(),
Point: new(PointK256).Identity(),
Name: K256Name,
}
}
func P256() *Curve {
p256Initonce.Do(p256Init)
return &p256
}
func p256Init() {
p256 = Curve{
Scalar: new(ScalarP256).Zero(),
Point: new(PointP256).Identity(),
Name: P256Name,
}
}
func ED25519() *Curve {
ed25519Initonce.Do(ed25519Init)
return &ed25519
}
func ed25519Init() {
ed25519 = Curve{
Scalar: new(ScalarEd25519).Zero(),
Point: new(PointEd25519).Identity(),
Name: ED25519Name,
}
}
func PALLAS() *Curve {
pallasInitonce.Do(pallasInit)
return &pallas
}
func pallasInit() {
pallas = Curve{
Scalar: new(ScalarPallas).Zero(),
Point: new(PointPallas).Identity(),
Name: PallasName,
}
}
// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-11#appendix-G.2.1
func osswu3mod4(u *big.Int, p *sswuParams) (x, y *big.Int) {
params := p.Params
field := NewField(p.Params.P)
tv1 := field.NewElement(u)
tv1 = tv1.Mul(tv1) // tv1 = u^2
tv3 := field.NewElement(p.Z).Mul(tv1) // tv3 = Z * tv1
tv2 := tv3.Mul(tv3) // tv2 = tv3^2
xd := tv2.Add(tv3) // xd = tv2 + tv3
x1n := xd.Add(field.One()) // x1n = (xd + 1)
x1n = x1n.Mul(field.NewElement(p.B)) // x1n * B
aNeg := field.NewElement(p.A).Neg()
xd = xd.Mul(aNeg) // xd = -A * xd
if xd.Value.Cmp(big.NewInt(0)) == 0 {
xd = field.NewElement(p.Z).Mul(field.NewElement(p.A)) // xd = Z * A
}
tv2 = xd.Mul(xd) // tv2 = xd^2
gxd := tv2.Mul(xd) // gxd = tv2 * xd
tv2 = tv2.Mul(field.NewElement(p.A)) // tv2 = A * tv2
gx1 := x1n.Mul(x1n) // gx1 = x1n^2
gx1 = gx1.Add(tv2) // gx1 = gx1 + tv2
gx1 = gx1.Mul(x1n) // gx1 = gx1 * x1n
tv2 = gxd.Mul(field.NewElement(p.B)) // tv2 = B * gxd
gx1 = gx1.Add(tv2) // gx1 = gx1 + tv2
tv4 := gxd.Mul(gxd) // tv4 = gxd^2
tv2 = gx1.Mul(gxd) // tv2 = gx1 * gxd
tv4 = tv4.Mul(tv2) // tv4 = tv4 * tv2
y1 := tv4.Pow(field.NewElement(p.C1))
y1 = y1.Mul(tv2) // y1 = y1 * tv2
x2n := tv3.Mul(x1n) // x2n = tv3 * x1n
y2 := y1.Mul(field.NewElement(p.C2)) // y2 = y1 * c2
y2 = y2.Mul(tv1) // y2 = y2 * tv1
y2 = y2.Mul(field.NewElement(u)) // y2 = y2 * u
tv2 = y1.Mul(y1) // tv2 = y1^2
tv2 = tv2.Mul(gxd) // tv2 = tv2 * gxd
e2 := tv2.Value.Cmp(gx1.Value) == 0
// If e2, x = x1, else x = x2
if e2 {
x = x1n.Value
} else {
x = x2n.Value
}
// xn / xd
x.Mul(x, new(big.Int).ModInverse(xd.Value, params.P))
x.Mod(x, params.P)
// If e2, y = y1, else y = y2
if e2 {
y = y1.Value
} else {
y = y2.Value
}
uBytes := u.Bytes()
yBytes := y.Bytes()
usign := uBytes[len(uBytes)-1] & 1
ysign := yBytes[len(yBytes)-1] & 1
// Fix sign of y
if usign != ysign {
y.Neg(y)
y.Mod(y, params.P)
}
return
}
func expandMsgXmd(h hash.Hash, msg, domain []byte, outLen int) ([]byte, error) {
domainLen := uint8(len(domain))
if domainLen > 255 {
return nil, fmt.Errorf("invalid domain length")
}
// DST_prime = DST || I2OSP(len(DST), 1)
// b_0 = H(Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime)
_, _ = h.Write(make([]byte, h.BlockSize()))
_, _ = h.Write(msg)
_, _ = h.Write([]byte{uint8(outLen >> 8), uint8(outLen)})
_, _ = h.Write([]byte{0})
_, _ = h.Write(domain)
_, _ = h.Write([]byte{domainLen})
b0 := h.Sum(nil)
// b_1 = H(b_0 || I2OSP(1, 1) || DST_prime)
h.Reset()
_, _ = h.Write(b0)
_, _ = h.Write([]byte{1})
_, _ = h.Write(domain)
_, _ = h.Write([]byte{domainLen})
b1 := h.Sum(nil)
// b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
ell := (outLen + h.Size() - 1) / h.Size()
bi := b1
out := make([]byte, outLen)
for i := 1; i < ell; i++ {
h.Reset()
// b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
tmp := make([]byte, h.Size())
for j := 0; j < h.Size(); j++ {
tmp[j] = b0[j] ^ bi[j]
}
_, _ = h.Write(tmp)
_, _ = h.Write([]byte{1 + uint8(i)})
_, _ = h.Write(domain)
_, _ = h.Write([]byte{domainLen})
// b_1 || ... || b_(ell - 1)
copy(out[(i-1)*h.Size():i*h.Size()], bi[:])
bi = h.Sum(nil)
}
// b_ell
copy(out[(ell-1)*h.Size():], bi[:])
return out[:outLen], nil
}
func bhex(s string) *big.Int {
r, _ := new(big.Int).SetString(s, 16)
return r
}
type sswuParams struct {
Params *elliptic.CurveParams
C1, C2, A, B, Z *big.Int
}
// sumOfProductsPippenger implements a version of Pippenger's algorithm.
//
// The algorithm works as follows:
//
// Let `n` be a number of point-scalar pairs.
// Let `w` be a window of bits (6..8, chosen based on `n`, see cost factor).
//
// 1. Prepare `2^(w-1) - 1` buckets with indices `[1..2^(w-1))` initialized with identity points.
// Bucket 0 is not needed as it would contain points multiplied by 0.
// 2. Convert scalars to a radix-`2^w` representation with signed digits in `[-2^w/2, 2^w/2]`.
// Note: only the last digit may equal `2^w/2`.
// 3. Starting with the last window, for each point `i=[0..n)` add it to a a bucket indexed by
// the point's scalar's value in the window.
// 4. Once all points in a window are sorted into buckets, add buckets by multiplying each
// by their index. Efficient way of doing it is to start with the last bucket and compute two sums:
// intermediate sum from the last to the first, and the full sum made of all intermediate sums.
// 5. Shift the resulting sum of buckets by `w` bits by using `w` doublings.
// 6. Add to the return value.
// 7. Repeat the loop.
//
// Approximate cost w/o wNAF optimizations (A = addition, D = doubling):
//
// ```ascii
// cost = (n*A + 2*(2^w/2)*A + w*D + A)*256/w
//
// | | | | |
// | | | | looping over 256/w windows
// | | | adding to the result
// sorting points | shifting the sum by w bits (to the next window, starting from last window)
// one by one |
// into buckets adding/subtracting all buckets
// multiplied by their indexes
// using a sum of intermediate sums
//
// ```
//
// For large `n`, dominant factor is (n*256/w) additions.
// However, if `w` is too big and `n` is not too big, then `(2^w/2)*A` could dominate.
// Therefore, the optimal choice of `w` grows slowly as `n` grows.
//
// # For constant time we use a fixed window of 6
//
// This algorithm is adapted from section 4 of <https://eprint.iacr.org/2012/549.pdf>.
// and https://cacr.uwaterloo.ca/techreports/2010/cacr2010-26.pdf
func sumOfProductsPippenger(points []Point, scalars []*big.Int) Point {
if len(points) != len(scalars) {
return nil
}
const w = 6
bucketSize := (1 << w) - 1
windows := make([]Point, 255/w+1)
for i := range windows {
windows[i] = points[0].Identity()
}
bucket := make([]Point, bucketSize)
for j := 0; j < len(windows); j++ {
for i := 0; i < bucketSize; i++ {
bucket[i] = points[0].Identity()
}
for i := 0; i < len(scalars); i++ {
index := bucketSize & int(new(big.Int).Rsh(scalars[i], uint(w*j)).Int64())
if index != 0 {
bucket[index-1] = bucket[index-1].Add(points[i])
}
}
acc, sum := windows[j].Identity(), windows[j].Identity()
for i := bucketSize - 1; i >= 0; i-- {
sum = sum.Add(bucket[i])
acc = acc.Add(sum)
}
windows[j] = acc
}
acc := windows[0].Identity()
for i := len(windows) - 1; i >= 0; i-- {
for j := 0; j < w; j++ {
acc = acc.Double()
}
acc = acc.Add(windows[i])
}
return acc
}
+251
View File
@@ -0,0 +1,251 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"crypto/elliptic"
"encoding/json"
"fmt"
"math/big"
"github.com/onsonr/sonr/crypto/core"
"github.com/dustinxie/ecc"
"github.com/onsonr/sonr/crypto/internal"
)
var curveNameToID = map[string]byte{
"secp256k1": 0,
"P-224": 1,
"P-256": 2,
"P-384": 3,
"P-521": 4,
}
var curveIDToName = map[byte]func() elliptic.Curve{
0: ecc.P256k1,
1: elliptic.P224,
2: elliptic.P256,
3: elliptic.P384,
4: elliptic.P521,
}
var curveMapper = map[string]func() elliptic.Curve{
"secp256k1": ecc.P256k1,
"P-224": elliptic.P224,
"P-256": elliptic.P256,
"P-384": elliptic.P384,
"P-521": elliptic.P521,
}
// EcPoint represents an elliptic curve Point
type EcPoint struct {
Curve elliptic.Curve
X, Y *big.Int
}
// EcPointJSON encapsulates the data that is serialized to JSON
// used internally and not for external use. Public so other pieces
// can use for serialization
type EcPointJSON struct {
X *big.Int `json:"x"`
Y *big.Int `json:"y"`
CurveName string `json:"curve_name"`
}
// MarshalJSON serializes EcPoint to JSON
func (a EcPoint) MarshalJSON() ([]byte, error) {
return json.Marshal(EcPointJSON{
CurveName: a.Curve.Params().Name,
X: a.X,
Y: a.Y,
})
}
// UnmarshalJSON deserializes JSON to EcPoint
func (a *EcPoint) UnmarshalJSON(bytes []byte) error {
data := new(EcPointJSON)
err := json.Unmarshal(bytes, data)
if err != nil {
return err
}
if mapper, ok := curveMapper[data.CurveName]; ok {
a.Curve = mapper()
a.X = data.X
a.Y = data.Y
return nil
}
return fmt.Errorf("unknown curve deserialized")
}
// MarshalBinary serializes EcPoint to binary
func (a *EcPoint) MarshalBinary() ([]byte, error) {
result := [65]byte{}
if code, ok := curveNameToID[a.Curve.Params().Name]; ok {
result[0] = code
a.X.FillBytes(result[1:33])
a.Y.FillBytes(result[33:65])
return result[:], nil
}
return nil, fmt.Errorf("unknown curve serialized")
}
// UnmarshalBinary deserializes binary to EcPoint
func (a *EcPoint) UnmarshalBinary(data []byte) error {
if mapper, ok := curveIDToName[data[0]]; ok {
a.Curve = mapper()
a.X = new(big.Int).SetBytes(data[1:33])
a.Y = new(big.Int).SetBytes(data[33:65])
return nil
}
return fmt.Errorf("unknown curve deserialized")
}
// IsValid checks if the point is valid
func (a EcPoint) IsValid() bool {
return a.IsOnCurve() || a.IsIdentity()
}
// IsOnCurve checks if the point is on the curve
func (a EcPoint) IsOnCurve() bool {
return a.Curve.IsOnCurve(a.X, a.Y)
}
// IsIdentity returns true if this Point is the Point at infinity
func (a EcPoint) IsIdentity() bool {
x := core.ConstantTimeEqByte(a.X, core.Zero)
y := core.ConstantTimeEqByte(a.Y, core.Zero)
return (x & y) == 1
}
// Equals return true if a and b have the same x,y coordinates
func (a EcPoint) Equals(b *EcPoint) bool {
if !sameCurve(&a, b) {
return false
}
x := core.ConstantTimeEqByte(a.X, b.X)
y := core.ConstantTimeEqByte(a.Y, b.Y)
return (x & y) == 1
}
// IsBasePoint returns true if this Point is curve's base Point
func (a EcPoint) IsBasePoint() bool {
p := a.Curve.Params()
x := core.ConstantTimeEqByte(a.X, p.Gx)
y := core.ConstantTimeEqByte(a.Y, p.Gy)
return (x & y) == 1
}
// reduceModN normalizes the Scalar to a positive element smaller than the base Point order.
func reduceModN(curve elliptic.Curve, k *big.Int) *big.Int {
return new(big.Int).Mod(k, curve.Params().N)
}
// Add performs elliptic curve addition on two points
func (a *EcPoint) Add(b *EcPoint) (*EcPoint, error) {
if a == nil || b == nil {
return nil, internal.ErrNilArguments
}
if !sameCurve(a, b) {
return nil, internal.ErrPointsDistinctCurves
}
p := &EcPoint{Curve: a.Curve}
p.X, p.Y = a.Curve.Add(a.X, a.Y, b.X, b.Y)
if !p.IsValid() {
return nil, internal.ErrNotOnCurve
}
return p, nil
}
// Neg returns the negation of a Weierstrass Point.
func (a *EcPoint) Neg() (*EcPoint, error) {
if a == nil {
return nil, internal.ErrNilArguments
}
p := &EcPoint{Curve: a.Curve, X: a.X, Y: new(big.Int).Sub(a.Curve.Params().P, a.Y)}
if !p.IsValid() {
return nil, internal.ErrNotOnCurve
}
return p, nil
}
// ScalarMult multiplies this Point by a Scalar
func (a *EcPoint) ScalarMult(k *big.Int) (*EcPoint, error) {
if a == nil || k == nil {
return nil, fmt.Errorf("cannot multiply nil Point or element")
}
n := reduceModN(a.Curve, k)
p := new(EcPoint)
p.Curve = a.Curve
p.X, p.Y = a.Curve.ScalarMult(a.X, a.Y, n.Bytes())
if !p.IsValid() {
return nil, fmt.Errorf("result not on the curve")
}
return p, nil
}
// NewScalarBaseMult creates a Point from the base Point multiplied by a field element
func NewScalarBaseMult(curve elliptic.Curve, k *big.Int) (*EcPoint, error) {
if curve == nil || k == nil {
return nil, fmt.Errorf("nil parameters are not supported")
}
n := reduceModN(curve, k)
p := new(EcPoint)
p.Curve = curve
p.X, p.Y = curve.ScalarBaseMult(n.Bytes())
if !p.IsValid() {
return nil, fmt.Errorf("result not on the curve")
}
return p, nil
}
// Bytes returns the bytes represented by this Point with x || y
func (a EcPoint) Bytes() []byte {
fieldSize := internal.CalcFieldSize(a.Curve)
out := make([]byte, fieldSize*2)
a.X.FillBytes(out[0:fieldSize])
a.Y.FillBytes(out[fieldSize : fieldSize*2])
return out
}
// PointFromBytesUncompressed outputs uncompressed X || Y similar to
// https://www.secg.org/sec1-v1.99.dif.pdf section 2.2 and 2.3
func PointFromBytesUncompressed(curve elliptic.Curve, b []byte) (*EcPoint, error) {
fieldSize := internal.CalcFieldSize(curve)
if len(b) != fieldSize*2 {
return nil, fmt.Errorf("invalid number of bytes")
}
p := &EcPoint{
Curve: curve,
X: new(big.Int).SetBytes(b[:fieldSize]),
Y: new(big.Int).SetBytes(b[fieldSize:]),
}
if !p.IsValid() {
return nil, fmt.Errorf("invalid Point")
}
return p, nil
}
// sameCurve determines if points a,b appear to be from the same curve
func sameCurve(a, b *EcPoint) bool {
if a == b {
return true
}
if a == nil || b == nil {
return false
}
aParams := a.Curve.Params()
bParams := b.Curve.Params()
return aParams.P.Cmp(bParams.P) == 0 &&
aParams.N.Cmp(bParams.N) == 0 &&
aParams.B.Cmp(bParams.B) == 0 &&
aParams.BitSize == bParams.BitSize &&
aParams.Gx.Cmp(bParams.Gx) == 0 &&
aParams.Gy.Cmp(bParams.Gy) == 0 &&
aParams.Name == bParams.Name
}
+369
View File
@@ -0,0 +1,369 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"bytes"
"crypto/elliptic"
"math/big"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core"
tt "github.com/onsonr/sonr/crypto/internal"
)
func TestIsIdentity(t *testing.T) {
// Should be Point at infinity
identity := &EcPoint{btcec.S256(), core.Zero, core.Zero}
require.True(t, identity.IsIdentity())
}
func TestNewScalarBaseMultZero(t *testing.T) {
// Should be Point at infinity
curve := btcec.S256()
num := big.NewInt(0)
p, err := NewScalarBaseMult(curve, num)
if err != nil {
t.Errorf("NewScalarBaseMult failed: %v", err)
}
if p == nil {
t.Errorf("NewScalarBaseMult failed when it should've succeeded.")
}
}
func TestNewScalarBaseMultOne(t *testing.T) {
// Should be base Point
curve := btcec.S256()
num := big.NewInt(1)
p, err := NewScalarBaseMult(curve, num)
if err != nil {
t.Errorf("NewScalarBaseMult failed: %v", err)
}
if p == nil {
t.Errorf("NewScalarBaseMult failed when it should've succeeded.")
t.FailNow()
}
if !bytes.Equal(p.Bytes(), append(curve.Gx.Bytes(), curve.Gy.Bytes()...)) {
t.Errorf("NewScalarBaseMult should've returned the base Point.")
}
}
func TestNewScalarBaseMultNeg(t *testing.T) {
curve := btcec.S256()
num := big.NewInt(-1)
p, err := NewScalarBaseMult(curve, num)
if err != nil {
t.Errorf("NewScalarBaseMult failed: %v", err)
}
if p == nil {
t.Errorf("NewScalarBaseMult failed when it should've succeeded.")
t.FailNow()
}
num.Mod(num, curve.N)
e, err := NewScalarBaseMult(curve, num)
if err != nil {
t.Errorf("NewScalarBaseMult failed: %v", err)
}
if e == nil {
t.Errorf("NewScalarBaseMult failed when it should've succeeded.")
t.FailNow()
}
if !bytes.Equal(p.Bytes(), e.Bytes()) {
t.Errorf("NewScalarBaseMult should've returned the %v, found: %v", e, p)
}
}
func TestScalarMultZero(t *testing.T) {
// Should be Point at infinity
curve := btcec.S256()
p := &EcPoint{
Curve: curve,
X: curve.Gx,
Y: curve.Gy,
}
num := big.NewInt(0)
q, err := p.ScalarMult(num)
if err != nil {
t.Errorf("ScalarMult failed: %v", err)
}
if q == nil {
t.Errorf("ScalarMult failed when it should've succeeded.")
t.FailNow()
}
if !q.IsIdentity() {
t.Errorf("ScalarMult should've returned the identity Point.")
}
}
func TestScalarMultOne(t *testing.T) {
// Should be base Point
curve := btcec.S256()
p := &EcPoint{
Curve: curve,
X: curve.Gx,
Y: curve.Gy,
}
num := big.NewInt(1)
q, err := p.ScalarMult(num)
if err != nil {
t.Errorf("ScalarMult failed: %v", err)
}
if q == nil {
t.Errorf("ScalarMult failed when it should've succeeded.")
t.FailNow()
}
if !bytes.Equal(q.Bytes(), append(curve.Gx.Bytes(), curve.Gy.Bytes()...)) {
t.Errorf("ScalarMult should've returned the base Point.")
}
}
func TestScalarMultNeg(t *testing.T) {
curve := btcec.S256()
p := &EcPoint{
Curve: curve,
X: curve.Gx,
Y: curve.Gy,
}
num := big.NewInt(-1)
q, err := p.ScalarMult(num)
if err != nil {
t.Errorf("ScalarMult failed: %v", err)
}
if q == nil {
t.Errorf("ScalarMult failed when it should've succeeded.")
}
num.Mod(num, curve.N)
e, err := p.ScalarMult(num)
if err != nil {
t.Errorf("ScalarMult failed: %v", err)
}
if e == nil {
t.Errorf("ScalarMult failed when it should've succeeded.")
t.FailNow()
}
if !bytes.Equal(q.Bytes(), e.Bytes()) {
t.Errorf("ScalarMult should've returned the %v, found: %v", e, p)
}
}
func TestEcPointAddSimple(t *testing.T) {
curve := btcec.S256()
num := big.NewInt(1)
p1, _ := NewScalarBaseMult(curve, num)
p2, _ := NewScalarBaseMult(curve, num)
p3, err := p1.Add(p2)
if err != nil {
t.Errorf("EcPoint.Add failed: %v", err)
}
num = big.NewInt(2)
ep, _ := NewScalarBaseMult(curve, num)
if !bytes.Equal(ep.Bytes(), p3.Bytes()) {
t.Errorf("EcPoint.Add failed: should equal %v, found: %v", ep, p3)
}
}
func TestEcPointAddCommunicative(t *testing.T) {
curve := btcec.S256()
a, _ := core.Rand(curve.Params().N)
b, _ := core.Rand(curve.Params().N)
p1, _ := NewScalarBaseMult(curve, a)
p2, _ := NewScalarBaseMult(curve, b)
p3, err := p1.Add(p2)
if err != nil {
t.Errorf("EcPoint.Add failed: %v", err)
}
p4, err := p2.Add(p1)
if err != nil {
t.Errorf("EcPoint.Add failed: %v", err)
}
if !bytes.Equal(p3.Bytes(), p4.Bytes()) {
t.Errorf("EcPoint.Add Communicative not valid")
}
}
func TestEcPointAddNeg(t *testing.T) {
curve := btcec.S256()
num := big.NewInt(-1)
p1, _ := NewScalarBaseMult(curve, num)
num.Abs(num)
p2, _ := NewScalarBaseMult(curve, num)
p3, err := p1.Add(p2)
if err != nil {
t.Errorf("EcPoint.Add failed: %v", err)
}
zero := make([]byte, 64)
if !bytes.Equal(zero, p3.Bytes()) {
t.Errorf("Expected value to be zero, found: %v", p3)
}
}
func TestEcPointBytes(t *testing.T) {
curve := btcec.S256()
point, err := NewScalarBaseMult(curve, big.NewInt(2))
require.NoError(t, err)
data := point.Bytes()
point2, err := PointFromBytesUncompressed(curve, data)
require.NoError(t, err)
if point.X.Cmp(point2.X) != 0 && point.Y.Cmp(point2.Y) != 0 {
t.Errorf("Points are not equal. Expected %v, found %v", point, point2)
}
curve2 := elliptic.P224()
p2, err := NewScalarBaseMult(curve2, big.NewInt(2))
require.NoError(t, err)
dta := p2.Bytes()
point3, err := PointFromBytesUncompressed(curve2, dta)
require.NoError(t, err)
if p2.X.Cmp(point3.X) != 0 && p2.Y.Cmp(point3.Y) != 0 {
t.Errorf("Points are not equal. Expected %v, found %v", p2, point3)
}
curve3 := elliptic.P521()
p3, err := NewScalarBaseMult(curve3, big.NewInt(2))
require.NoError(t, err)
data = p3.Bytes()
point4, err := PointFromBytesUncompressed(curve3, data)
require.NoError(t, err)
if p3.X.Cmp(point4.X) != 0 && p3.Y.Cmp(point4.Y) != 0 {
t.Errorf("Points are not equal. Expected %v, found %v", p3, point4)
}
}
func TestEcPointBytesDifferentCurves(t *testing.T) {
k256 := btcec.S256()
p224 := elliptic.P224()
p256 := elliptic.P256()
kp, err := NewScalarBaseMult(k256, big.NewInt(1))
require.NoError(t, err)
data := kp.Bytes()
_, err = PointFromBytesUncompressed(p224, data)
require.Error(t, err)
_, err = PointFromBytesUncompressed(p256, data)
require.Error(t, err)
}
func TestEcPointBytesInvalidNumberBytes(t *testing.T) {
curve := btcec.S256()
for i := 1; i < 64; i++ {
data := make([]byte, i)
_, err := PointFromBytesUncompressed(curve, data)
require.Error(t, err)
}
for i := 65; i < 128; i++ {
data := make([]byte, i)
_, err := PointFromBytesUncompressed(curve, data)
require.Error(t, err)
}
}
func TestEcPointMultRandom(t *testing.T) {
curve := btcec.S256()
r, err := core.Rand(curve.N)
require.NoError(t, err)
pt, err := NewScalarBaseMult(curve, r)
require.NoError(t, err)
require.NotNil(t, pt)
data := pt.Bytes()
pt2, err := PointFromBytesUncompressed(curve, data)
require.NoError(t, err)
if pt.X.Cmp(pt2.X) != 0 || pt.Y.Cmp(pt2.Y) != 0 {
t.Errorf("Points are not equal. Expected: %v, found: %v", pt, pt2)
}
}
func TestIsBasePoint(t *testing.T) {
k256 := btcec.S256()
p224 := elliptic.P224()
p256 := elliptic.P256()
notG_p224, err := NewScalarBaseMult(p224, tt.B10("9876453120"))
require.NoError(t, err)
tests := []struct {
name string
curve elliptic.Curve
x, y *big.Int
expected bool
}{
{"k256-positive", k256, k256.Gx, k256.Gy, true},
{"p224-positive", p224, p224.Params().Gx, p224.Params().Gy, true},
{"p256-positive", p256, p256.Params().Gx, p256.Params().Gy, true},
{"p224-negative", p224, notG_p224.X, notG_p224.Y, false},
{"p256-negative-wrong-curve", p256, notG_p224.X, notG_p224.Y, false},
{"k256-negative-doubleGx", k256, k256.Gx, k256.Gx, false},
{"k256-negative-doubleGy", k256, k256.Gy, k256.Gy, false},
{"k256-negative-xy-swap", k256, k256.Gy, k256.Gx, false},
{"k256-negative-oh-oh", k256, core.Zero, core.Zero, false},
}
// Run all the tests!
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := EcPoint{test.curve, test.x, test.y}.IsBasePoint()
require.Equal(t, test.expected, actual)
})
}
}
func TestEquals(t *testing.T) {
k256 := btcec.S256()
p224 := elliptic.P224()
p256 := elliptic.P256()
P_p224, _ := NewScalarBaseMult(p224, tt.B10("9876453120"))
P1_p224, _ := NewScalarBaseMult(p224, tt.B10("9876453120"))
P_k256 := &EcPoint{k256, P_p224.X, P_p224.Y}
id_p224 := &EcPoint{p224, core.Zero, core.Zero}
id_k256 := &EcPoint{k256, core.Zero, core.Zero}
id_p256 := &EcPoint{p256, core.Zero, core.Zero}
tests := []struct {
name string
x, y *EcPoint
expected bool
}{
{"p224 same pointer", P_p224, P_p224, true},
{"p224 same Point", P_p224, P1_p224, true},
{"p224 identity", id_p224, id_p224, true},
{"p256 identity", id_p256, id_p256, true},
{"k256 identity", id_k256, id_k256, true},
{"negative-same x different y", P_p224, &EcPoint{p224, P_p224.X, core.One}, false},
{"negative-same y different x", P_p224, &EcPoint{p224, core.Two, P_k256.Y}, false},
{"negative-wrong curve", P_p224, P_k256, false},
{"negative-wrong curve reversed", P_k256, P_p224, false},
{"Point is not the identity", P_p224, id_p224, false},
{"negative nil", P1_p224, nil, false},
{"identities on wrong curve", id_p256, id_k256, false},
}
// Run all the tests!
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := test.x.Equals(test.y)
require.Equal(t, test.expected, actual)
})
}
}
+351
View File
@@ -0,0 +1,351 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"crypto/elliptic"
crand "crypto/rand"
"crypto/sha512"
"fmt"
"io"
"math/big"
"filippo.io/edwards25519"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/bwesterb/go-ristretto"
"github.com/onsonr/sonr/crypto/core"
"github.com/onsonr/sonr/crypto/core/curves/native/bls12381"
"github.com/onsonr/sonr/crypto/internal"
)
type EcScalar interface {
Add(x, y *big.Int) *big.Int
Sub(x, y *big.Int) *big.Int
Neg(x *big.Int) *big.Int
Mul(x, y *big.Int) *big.Int
Hash(input []byte) *big.Int
Div(x, y *big.Int) *big.Int
Random() (*big.Int, error)
IsValid(x *big.Int) bool
Bytes(x *big.Int) []byte // fixed-length byte array
}
type K256Scalar struct{}
// Static interface assertion
var _ EcScalar = (*K256Scalar)(nil)
// warning: the Euclidean alg which Mod uses is not constant-time.
func NewK256Scalar() *K256Scalar {
return &K256Scalar{}
}
func (k K256Scalar) Add(x, y *big.Int) *big.Int {
v := new(big.Int).Add(x, y)
v.Mod(v, btcec.S256().N)
return v
}
func (k K256Scalar) Sub(x, y *big.Int) *big.Int {
v := new(big.Int).Sub(x, y)
v.Mod(v, btcec.S256().N)
return v
}
func (k K256Scalar) Neg(x *big.Int) *big.Int {
v := new(big.Int).Sub(btcec.S256().N, x)
v.Mod(v, btcec.S256().N)
return v
}
func (k K256Scalar) Mul(x, y *big.Int) *big.Int {
v := new(big.Int).Mul(x, y)
v.Mod(v, btcec.S256().N)
return v
}
func (k K256Scalar) Div(x, y *big.Int) *big.Int {
t := new(big.Int).ModInverse(y, btcec.S256().N)
return k.Mul(x, t)
}
func (k K256Scalar) Hash(input []byte) *big.Int {
return new(ScalarK256).Hash(input).BigInt()
}
func (k K256Scalar) Random() (*big.Int, error) {
b := make([]byte, 48)
n, err := crand.Read(b)
if err != nil {
return nil, err
}
if n != 48 {
return nil, fmt.Errorf("insufficient bytes read")
}
v := new(big.Int).SetBytes(b)
v.Mod(v, btcec.S256().N)
return v, nil
}
func (k K256Scalar) IsValid(x *big.Int) bool {
return core.In(x, btcec.S256().N) == nil
}
func (k K256Scalar) Bytes(x *big.Int) []byte {
bytes := make([]byte, 32)
x.FillBytes(bytes) // big-endian; will left-pad.
return bytes
}
type P256Scalar struct{}
// Static interface assertion
var _ EcScalar = (*P256Scalar)(nil)
func NewP256Scalar() *P256Scalar {
return &P256Scalar{}
}
func (k P256Scalar) Add(x, y *big.Int) *big.Int {
v := new(big.Int).Add(x, y)
v.Mod(v, elliptic.P256().Params().N)
return v
}
func (k P256Scalar) Sub(x, y *big.Int) *big.Int {
v := new(big.Int).Sub(x, y)
v.Mod(v, elliptic.P256().Params().N)
return v
}
func (k P256Scalar) Neg(x *big.Int) *big.Int {
v := new(big.Int).Sub(elliptic.P256().Params().N, x)
v.Mod(v, elliptic.P256().Params().N)
return v
}
func (k P256Scalar) Mul(x, y *big.Int) *big.Int {
v := new(big.Int).Mul(x, y)
v.Mod(v, elliptic.P256().Params().N)
return v
}
func (k P256Scalar) Div(x, y *big.Int) *big.Int {
t := new(big.Int).ModInverse(y, elliptic.P256().Params().N)
return k.Mul(x, t)
}
func (k P256Scalar) Hash(input []byte) *big.Int {
return new(ScalarP256).Hash(input).BigInt()
}
func (k P256Scalar) Random() (*big.Int, error) {
b := make([]byte, 48)
n, err := crand.Read(b)
if err != nil {
return nil, err
}
if n != 48 {
return nil, fmt.Errorf("insufficient bytes read")
}
v := new(big.Int).SetBytes(b)
v.Mod(v, elliptic.P256().Params().N)
return v, nil
}
func (k P256Scalar) IsValid(x *big.Int) bool {
return core.In(x, elliptic.P256().Params().N) == nil
}
func (k P256Scalar) Bytes(x *big.Int) []byte {
bytes := make([]byte, 32)
x.FillBytes(bytes) // big-endian; will left-pad.
return bytes
}
type Bls12381Scalar struct{}
// Static interface assertion
var _ EcScalar = (*Bls12381Scalar)(nil)
func NewBls12381Scalar() *Bls12381Scalar {
return &Bls12381Scalar{}
}
func (k Bls12381Scalar) Add(x, y *big.Int) *big.Int {
a := bls12381.Bls12381FqNew().SetBigInt(x)
b := bls12381.Bls12381FqNew().SetBigInt(y)
return a.Add(a, b).BigInt()
}
func (k Bls12381Scalar) Sub(x, y *big.Int) *big.Int {
a := bls12381.Bls12381FqNew().SetBigInt(x)
b := bls12381.Bls12381FqNew().SetBigInt(y)
return a.Sub(a, b).BigInt()
}
func (k Bls12381Scalar) Neg(x *big.Int) *big.Int {
a := bls12381.Bls12381FqNew().SetBigInt(x)
return a.Neg(a).BigInt()
}
func (k Bls12381Scalar) Mul(x, y *big.Int) *big.Int {
a := bls12381.Bls12381FqNew().SetBigInt(x)
b := bls12381.Bls12381FqNew().SetBigInt(y)
return a.Mul(a, b).BigInt()
}
func (k Bls12381Scalar) Div(x, y *big.Int) *big.Int {
c := bls12381.Bls12381FqNew()
a := bls12381.Bls12381FqNew().SetBigInt(x)
b := bls12381.Bls12381FqNew().SetBigInt(y)
_, wasInverted := c.Invert(b)
c.Mul(a, c)
tt := map[bool]int{false: 0, true: 1}
return a.CMove(a, c, tt[wasInverted]).BigInt()
}
func (k Bls12381Scalar) Hash(input []byte) *big.Int {
return new(ScalarBls12381).Hash(input).BigInt()
}
func (k Bls12381Scalar) Random() (*big.Int, error) {
a := BLS12381G1().NewScalar().Random(crand.Reader)
if a == nil {
return nil, fmt.Errorf("invalid random value")
}
return a.BigInt(), nil
}
func (k Bls12381Scalar) Bytes(x *big.Int) []byte {
bytes := make([]byte, 32)
x.FillBytes(bytes) // big-endian; will left-pad.
return bytes
}
func (k Bls12381Scalar) IsValid(x *big.Int) bool {
a := bls12381.Bls12381FqNew().SetBigInt(x)
return a.BigInt().Cmp(x) == 0
}
// taken from https://datatracker.ietf.org/doc/html/rfc8032
var ed25519N, _ = new(big.Int).SetString("1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED", 16)
type Ed25519Scalar struct{}
// Static interface assertion
var _ EcScalar = (*Ed25519Scalar)(nil)
func NewEd25519Scalar() *Ed25519Scalar {
return &Ed25519Scalar{}
}
func (k Ed25519Scalar) Add(x, y *big.Int) *big.Int {
a, err := internal.BigInt2Ed25519Scalar(x)
if err != nil {
panic(err)
}
b, err := internal.BigInt2Ed25519Scalar(y)
if err != nil {
panic(err)
}
a.Add(a, b)
return new(big.Int).SetBytes(internal.ReverseScalarBytes(a.Bytes()))
}
func (k Ed25519Scalar) Sub(x, y *big.Int) *big.Int {
a, err := internal.BigInt2Ed25519Scalar(x)
if err != nil {
panic(err)
}
b, err := internal.BigInt2Ed25519Scalar(y)
if err != nil {
panic(err)
}
a.Subtract(a, b)
return new(big.Int).SetBytes(internal.ReverseScalarBytes(a.Bytes()))
}
func (k Ed25519Scalar) Neg(x *big.Int) *big.Int {
a, err := internal.BigInt2Ed25519Scalar(x)
if err != nil {
panic(err)
}
a.Negate(a)
return new(big.Int).SetBytes(internal.ReverseScalarBytes(a.Bytes()))
}
func (k Ed25519Scalar) Mul(x, y *big.Int) *big.Int {
a, err := internal.BigInt2Ed25519Scalar(x)
if err != nil {
panic(err)
}
b, err := internal.BigInt2Ed25519Scalar(y)
if err != nil {
panic(err)
}
a.Multiply(a, b)
return new(big.Int).SetBytes(internal.ReverseScalarBytes(a.Bytes()))
}
func (k Ed25519Scalar) Div(x, y *big.Int) *big.Int {
b, err := internal.BigInt2Ed25519Scalar(y)
if err != nil {
panic(err)
}
b.Invert(b)
a, err := internal.BigInt2Ed25519Scalar(x)
if err != nil {
panic(err)
}
a.Multiply(a, b)
return new(big.Int).SetBytes(internal.ReverseScalarBytes(a.Bytes()))
}
func (k Ed25519Scalar) Hash(input []byte) *big.Int {
v := new(ristretto.Scalar).Derive(input)
var data [32]byte
v.BytesInto(&data)
return new(big.Int).SetBytes(internal.ReverseScalarBytes(data[:]))
}
func (k Ed25519Scalar) Bytes(x *big.Int) []byte {
a, err := internal.BigInt2Ed25519Scalar(x)
if err != nil {
panic(err)
}
return internal.ReverseScalarBytes(a.Bytes())
}
func (k Ed25519Scalar) Random() (*big.Int, error) {
return k.RandomWithReader(crand.Reader)
}
func (k Ed25519Scalar) RandomWithReader(r io.Reader) (*big.Int, error) {
b := make([]byte, 64)
n, err := r.Read(b)
if err != nil {
return nil, err
}
if n != 64 {
return nil, fmt.Errorf("insufficient bytes read")
}
digest := sha512.Sum512(b)
var hBytes [32]byte
copy(hBytes[:], digest[:])
s, err := edwards25519.NewScalar().SetBytesWithClamping(hBytes[:])
if err != nil {
return nil, err
}
return new(big.Int).SetBytes(internal.ReverseScalarBytes(s.Bytes())), nil
}
func (k Ed25519Scalar) IsValid(x *big.Int) bool {
return x.Cmp(ed25519N) == -1
}
+39
View File
@@ -0,0 +1,39 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"crypto/ecdsa"
"math/big"
)
// EcdsaVerify runs a curve- or algorithm-specific ECDSA verification function on input
// an ECDSA public (verification) key, a message digest, and an ECDSA signature.
// It must return true if all the parameters are sane and the ECDSA signature is valid,
// and false otherwise
type EcdsaVerify func(pubKey *EcPoint, hash []byte, signature *EcdsaSignature) bool
// EcdsaSignature represents a (composite) digital signature
type EcdsaSignature struct {
R *big.Int
S *big.Int
V int
}
// Static type assertion
var _ EcdsaVerify = VerifyEcdsa
// Verifies ECDSA signature using core types.
func VerifyEcdsa(pk *EcPoint, hash []byte, sig *EcdsaSignature) bool {
return ecdsa.Verify(
&ecdsa.PublicKey{
Curve: pk.Curve,
X: pk.X,
Y: pk.Y,
},
hash, sig.R, sig.S)
}
+788
View File
@@ -0,0 +1,788 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"bytes"
"crypto/sha512"
"crypto/subtle"
"fmt"
"io"
"math/big"
"filippo.io/edwards25519"
"filippo.io/edwards25519/field"
"github.com/bwesterb/go-ristretto"
ed "github.com/bwesterb/go-ristretto/edwards25519"
"github.com/onsonr/sonr/crypto/internal"
)
type ScalarEd25519 struct {
value *edwards25519.Scalar
}
type PointEd25519 struct {
value *edwards25519.Point
}
var scOne, _ = edwards25519.NewScalar().SetCanonicalBytes([]byte{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})
func (s *ScalarEd25519) Random(reader io.Reader) Scalar {
if reader == nil {
return nil
}
var seed [64]byte
_, _ = reader.Read(seed[:])
return s.Hash(seed[:])
}
func (s *ScalarEd25519) Hash(bytes []byte) Scalar {
v := new(ristretto.Scalar).Derive(bytes)
var data [32]byte
v.BytesInto(&data)
value, err := edwards25519.NewScalar().SetCanonicalBytes(data[:])
if err != nil {
return nil
}
return &ScalarEd25519{value}
}
func (s *ScalarEd25519) Zero() Scalar {
return &ScalarEd25519{
value: edwards25519.NewScalar(),
}
}
func (s *ScalarEd25519) One() Scalar {
return &ScalarEd25519{
value: edwards25519.NewScalar().Set(scOne),
}
}
func (s *ScalarEd25519) IsZero() bool {
i := byte(0)
for _, b := range s.value.Bytes() {
i |= b
}
return i == 0
}
func (s *ScalarEd25519) IsOne() bool {
data := s.value.Bytes()
i := byte(0)
for j := 1; j < len(data); j++ {
i |= data[j]
}
return i == 0 && data[0] == 1
}
func (s *ScalarEd25519) IsOdd() bool {
return s.value.Bytes()[0]&1 == 1
}
func (s *ScalarEd25519) IsEven() bool {
return s.value.Bytes()[0]&1 == 0
}
func (s *ScalarEd25519) New(input int) Scalar {
var data [64]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, err := edwards25519.NewScalar().SetUniformBytes(data[:])
if err != nil {
return nil
}
if input < 0 {
value.Negate(value)
}
return &ScalarEd25519{
value,
}
}
func (s *ScalarEd25519) Cmp(rhs Scalar) int {
r := s.Sub(rhs)
if r != nil && r.IsZero() {
return 0
} else {
return -2
}
}
func (s *ScalarEd25519) Square() Scalar {
value := edwards25519.NewScalar().Multiply(s.value, s.value)
return &ScalarEd25519{value}
}
func (s *ScalarEd25519) Double() Scalar {
return &ScalarEd25519{
value: edwards25519.NewScalar().Add(s.value, s.value),
}
}
func (s *ScalarEd25519) Invert() (Scalar, error) {
return &ScalarEd25519{
value: edwards25519.NewScalar().Invert(s.value),
}, nil
}
func (s *ScalarEd25519) Sqrt() (Scalar, error) {
bi25519, _ := new(big.Int).SetString("1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED", 16)
x := s.BigInt()
x.ModSqrt(x, bi25519)
return s.SetBigInt(x)
}
func (s *ScalarEd25519) Cube() Scalar {
value := edwards25519.NewScalar().Multiply(s.value, s.value)
value.Multiply(value, s.value)
return &ScalarEd25519{value}
}
func (s *ScalarEd25519) Add(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarEd25519)
if ok {
return &ScalarEd25519{
value: edwards25519.NewScalar().Add(s.value, r.value),
}
} else {
return nil
}
}
func (s *ScalarEd25519) Sub(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarEd25519)
if ok {
return &ScalarEd25519{
value: edwards25519.NewScalar().Subtract(s.value, r.value),
}
} else {
return nil
}
}
func (s *ScalarEd25519) Mul(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarEd25519)
if ok {
return &ScalarEd25519{
value: edwards25519.NewScalar().Multiply(s.value, r.value),
}
} else {
return nil
}
}
func (s *ScalarEd25519) MulAdd(y, z Scalar) Scalar {
yy, ok := y.(*ScalarEd25519)
if !ok {
return nil
}
zz, ok := z.(*ScalarEd25519)
if !ok {
return nil
}
return &ScalarEd25519{value: edwards25519.NewScalar().MultiplyAdd(s.value, yy.value, zz.value)}
}
func (s *ScalarEd25519) Div(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarEd25519)
if ok {
value := edwards25519.NewScalar().Invert(r.value)
value.Multiply(value, s.value)
return &ScalarEd25519{value}
} else {
return nil
}
}
func (s *ScalarEd25519) Neg() Scalar {
return &ScalarEd25519{
value: edwards25519.NewScalar().Negate(s.value),
}
}
func (s *ScalarEd25519) SetBigInt(x *big.Int) (Scalar, error) {
if x == nil {
return nil, fmt.Errorf("invalid value")
}
bi25519, _ := new(big.Int).SetString("1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED", 16)
var v big.Int
buf := v.Mod(x, bi25519).Bytes()
var rBuf [32]byte
for i := 0; i < len(buf) && i < 32; i++ {
rBuf[i] = buf[len(buf)-i-1]
}
value, err := edwards25519.NewScalar().SetCanonicalBytes(rBuf[:])
if err != nil {
return nil, err
}
return &ScalarEd25519{value}, nil
}
func (s *ScalarEd25519) BigInt() *big.Int {
var ret big.Int
buf := internal.ReverseScalarBytes(s.value.Bytes())
return ret.SetBytes(buf)
}
func (s *ScalarEd25519) Bytes() []byte {
return s.value.Bytes()
}
// SetBytes takes input a 32-byte long array and returns a ed25519 scalar.
// The input must be 32-byte long and must be a reduced bytes.
func (s *ScalarEd25519) SetBytes(input []byte) (Scalar, error) {
if len(input) != 32 {
return nil, fmt.Errorf("invalid byte sequence")
}
value, err := edwards25519.NewScalar().SetCanonicalBytes(input)
if err != nil {
return nil, err
}
return &ScalarEd25519{value}, nil
}
// SetBytesWide takes input a 64-byte long byte array, reduce it and return an ed25519 scalar.
// It uses SetUniformBytes of fillipo.io/edwards25519 - https://github.com/FiloSottile/edwards25519/blob/v1.0.0-rc.1/scalar.go#L85
// If bytes is not of the right length, it returns nil and an error
func (s *ScalarEd25519) SetBytesWide(bytes []byte) (Scalar, error) {
value, err := edwards25519.NewScalar().SetUniformBytes(bytes)
if err != nil {
return nil, err
}
return &ScalarEd25519{value}, nil
}
// SetBytesClamping uses SetBytesWithClamping of fillipo.io/edwards25519- https://github.com/FiloSottile/edwards25519/blob/v1.0.0-rc.1/scalar.go#L135
// which applies the buffer pruning described in RFC 8032, Section 5.1.5 (also known as clamping)
// and sets bytes to the result. The input must be 32-byte long, and it is not modified.
// If bytes is not of the right length, SetBytesWithClamping returns nil and an error, and the receiver is unchanged.
func (s *ScalarEd25519) SetBytesClamping(bytes []byte) (Scalar, error) {
value, err := edwards25519.NewScalar().SetBytesWithClamping(bytes)
if err != nil {
return nil, err
}
return &ScalarEd25519{value}, nil
}
// SetBytesCanonical uses SetCanonicalBytes of fillipo.io/edwards25519.
// https://github.com/FiloSottile/edwards25519/blob/v1.0.0-rc.1/scalar.go#L98
// This function takes an input x and sets s = x, where x is a 32-byte little-endian
// encoding of s, then it returns the corresponding ed25519 scalar. If the input is
// not a canonical encoding of s, it returns nil and an error.
func (s *ScalarEd25519) SetBytesCanonical(bytes []byte) (Scalar, error) {
return s.SetBytes(bytes)
}
func (s *ScalarEd25519) Point() Point {
return new(PointEd25519).Identity()
}
func (s *ScalarEd25519) Clone() Scalar {
return &ScalarEd25519{
value: edwards25519.NewScalar().Set(s.value),
}
}
func (s *ScalarEd25519) MarshalBinary() ([]byte, error) {
return scalarMarshalBinary(s)
}
func (s *ScalarEd25519) UnmarshalBinary(input []byte) error {
sc, err := scalarUnmarshalBinary(input)
if err != nil {
return err
}
ss, ok := sc.(*ScalarEd25519)
if !ok {
return fmt.Errorf("invalid scalar")
}
s.value = ss.value
return nil
}
func (s *ScalarEd25519) MarshalText() ([]byte, error) {
return scalarMarshalText(s)
}
func (s *ScalarEd25519) UnmarshalText(input []byte) error {
sc, err := scalarUnmarshalText(input)
if err != nil {
return err
}
ss, ok := sc.(*ScalarEd25519)
if !ok {
return fmt.Errorf("invalid scalar")
}
s.value = ss.value
return nil
}
func (s *ScalarEd25519) GetEdwardsScalar() *edwards25519.Scalar {
return edwards25519.NewScalar().Set(s.value)
}
func (s *ScalarEd25519) SetEdwardsScalar(sc *edwards25519.Scalar) *ScalarEd25519 {
return &ScalarEd25519{value: edwards25519.NewScalar().Set(sc)}
}
func (s *ScalarEd25519) MarshalJSON() ([]byte, error) {
return scalarMarshalJson(s)
}
func (s *ScalarEd25519) UnmarshalJSON(input []byte) error {
sc, err := scalarUnmarshalJson(input)
if err != nil {
return err
}
S, ok := sc.(*ScalarEd25519)
if !ok {
return fmt.Errorf("invalid type")
}
s.value = S.value
return nil
}
func (p *PointEd25519) Random(reader io.Reader) Point {
var seed [64]byte
_, _ = reader.Read(seed[:])
return p.Hash(seed[:])
}
func (p *PointEd25519) Hash(bytes []byte) Point {
/// Perform hashing to the group using the Elligator2 map
///
/// See https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-11#section-6.7.1
h := sha512.Sum512(bytes)
var res [32]byte
copy(res[:], h[:32])
signBit := (res[31] & 0x80) >> 7
fe := new(ed.FieldElement).SetBytes(&res).BytesInto(&res)
m1 := elligatorEncode(fe)
return toEdwards(m1, signBit)
}
func (p *PointEd25519) Identity() Point {
return &PointEd25519{
value: edwards25519.NewIdentityPoint(),
}
}
func (p *PointEd25519) Generator() Point {
return &PointEd25519{
value: edwards25519.NewGeneratorPoint(),
}
}
func (p *PointEd25519) IsIdentity() bool {
return p.Equal(p.Identity())
}
func (p *PointEd25519) IsNegative() bool {
// Negative points don't really exist in ed25519
return false
}
func (p *PointEd25519) IsOnCurve() bool {
_, err := edwards25519.NewIdentityPoint().SetBytes(p.ToAffineCompressed())
return err == nil
}
func (p *PointEd25519) Double() Point {
return &PointEd25519{value: edwards25519.NewIdentityPoint().Add(p.value, p.value)}
}
func (p *PointEd25519) Scalar() Scalar {
return new(ScalarEd25519).Zero()
}
func (p *PointEd25519) Neg() Point {
return &PointEd25519{value: edwards25519.NewIdentityPoint().Negate(p.value)}
}
func (p *PointEd25519) Add(rhs Point) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*PointEd25519)
if ok {
return &PointEd25519{value: edwards25519.NewIdentityPoint().Add(p.value, r.value)}
} else {
return nil
}
}
func (p *PointEd25519) Sub(rhs Point) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*PointEd25519)
if ok {
rTmp := edwards25519.NewIdentityPoint().Negate(r.value)
return &PointEd25519{value: edwards25519.NewIdentityPoint().Add(p.value, rTmp)}
} else {
return nil
}
}
func (p *PointEd25519) Mul(rhs Scalar) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*ScalarEd25519)
if ok {
value := edwards25519.NewIdentityPoint().ScalarMult(r.value, p.value)
return &PointEd25519{value}
} else {
return nil
}
}
// MangleScalarBitsAndMulByBasepointToProducePublicKey
// is a function for mangling the bits of a (formerly
// mathematically well-defined) "scalar" and multiplying it to produce a
// public key.
func (p *PointEd25519) MangleScalarBitsAndMulByBasepointToProducePublicKey(rhs *ScalarEd25519) *PointEd25519 {
data := rhs.value.Bytes()
s, err := edwards25519.NewScalar().SetBytesWithClamping(data[:])
if err != nil {
return nil
}
value := edwards25519.NewIdentityPoint().ScalarBaseMult(s)
return &PointEd25519{value}
}
func (p *PointEd25519) Equal(rhs Point) bool {
r, ok := rhs.(*PointEd25519)
if ok {
// We would like to check that the point (X/Z, Y/Z) is equal to
// the point (X'/Z', Y'/Z') without converting into affine
// coordinates (x, y) and (x', y'), which requires two inversions.
// We have that X = xZ and X' = x'Z'. Thus, x = x' is equivalent to
// (xZ)Z' = (x'Z')Z, and similarly for the y-coordinate.
return p.value.Equal(r.value) == 1
//lhs1 := new(ed.FieldElement).Mul(&p.value.X, &r.value.Z)
//rhs1 := new(ed.FieldElement).Mul(&r.value.X, &p.value.Z)
//lhs2 := new(ed.FieldElement).Mul(&p.value.Y, &r.value.Z)
//rhs2 := new(ed.FieldElement).Mul(&r.value.Y, &p.value.Z)
//
//return lhs1.Equals(rhs1) && lhs2.Equals(rhs2)
} else {
return false
}
}
func (p *PointEd25519) 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 := new(ed.FieldElement).SetBigInt(x)
yElem := new(ed.FieldElement).SetBigInt(y)
var data [32]byte
var affine [64]byte
xElem.BytesInto(&data)
copy(affine[:32], data[:])
yElem.BytesInto(&data)
copy(affine[32:], data[:])
return p.FromAffineUncompressed(affine[:])
}
// sqrtRatio sets r to the non-negative square root of the ratio of u and v.
//
// If u/v is square, sqrtRatio returns r and 1. If u/v is not square, SqrtRatio
// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00,
// and returns r and 0.
func sqrtRatio(u, v *ed.FieldElement) (r *ed.FieldElement, wasSquare bool) {
sqrtM1 := ed.FieldElement{
533094393274173, 2016890930128738, 18285341111199,
134597186663265, 1486323764102114,
}
a := new(ed.FieldElement)
b := new(ed.FieldElement)
r = new(ed.FieldElement)
// r = (u * v3) * (u * v7)^((p-5)/8)
v2 := a.Square(v)
uv3 := b.Mul(u, b.Mul(v2, v))
uv7 := a.Mul(uv3, a.Square(v2))
r.Mul(uv3, r.Exp22523(uv7))
check := a.Mul(v, a.Square(r)) // check = v * r^2
uNeg := b.Neg(u)
correctSignSqrt := check.Equals(u)
flippedSignSqrt := check.Equals(uNeg)
flippedSignSqrtI := check.Equals(uNeg.Mul(uNeg, &sqrtM1))
rPrime := b.Mul(r, &sqrtM1) // r_prime = SQRT_M1 * r
// r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r)
cselect(r, rPrime, r, flippedSignSqrt || flippedSignSqrtI)
r.Abs(r) // Choose the nonnegative square root.
return r, correctSignSqrt || flippedSignSqrt
}
// cselect sets v to a if cond == 1, and to b if cond == 0.
func cselect(v, a, b *ed.FieldElement, cond bool) *ed.FieldElement {
const mask64Bits uint64 = (1 << 64) - 1
m := uint64(0)
if cond {
m = mask64Bits
}
v[0] = (m & a[0]) | (^m & b[0])
v[1] = (m & a[1]) | (^m & b[1])
v[2] = (m & a[2]) | (^m & b[2])
v[3] = (m & a[3]) | (^m & b[3])
v[4] = (m & a[4]) | (^m & b[4])
return v
}
func (p *PointEd25519) ToAffineCompressed() []byte {
return p.value.Bytes()
}
func (p *PointEd25519) ToAffineUncompressed() []byte {
x, y, z, _ := p.value.ExtendedCoordinates()
recip := new(field.Element).Invert(z)
x.Multiply(x, recip)
y.Multiply(y, recip)
var out [64]byte
copy(out[:32], x.Bytes())
copy(out[32:], y.Bytes())
return out[:]
}
func (p *PointEd25519) FromAffineCompressed(inBytes []byte) (Point, error) {
pt, err := edwards25519.NewIdentityPoint().SetBytes(inBytes)
if err != nil {
return nil, err
}
return &PointEd25519{value: pt}, nil
}
func (p *PointEd25519) FromAffineUncompressed(inBytes []byte) (Point, error) {
if len(inBytes) != 64 {
return nil, fmt.Errorf("invalid byte sequence")
}
if bytes.Equal(inBytes, []byte{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, 0, 0, 0, 0, 0, 0, 0, 0, 0}) {
return &PointEd25519{value: edwards25519.NewIdentityPoint()}, nil
}
x, err := new(field.Element).SetBytes(inBytes[:32])
if err != nil {
return nil, err
}
y, err := new(field.Element).SetBytes(inBytes[32:])
if err != nil {
return nil, err
}
z := new(field.Element).One()
t := new(field.Element).Multiply(x, y)
value, err := edwards25519.NewIdentityPoint().SetExtendedCoordinates(x, y, z, t)
if err != nil {
return nil, err
}
return &PointEd25519{value}, nil
}
func (p *PointEd25519) CurveName() string {
return ED25519Name
}
func (p *PointEd25519) SumOfProducts(points []Point, scalars []Scalar) Point {
nScalars := make([]*edwards25519.Scalar, len(scalars))
nPoints := make([]*edwards25519.Point, len(points))
for i, sc := range scalars {
s, err := edwards25519.NewScalar().SetCanonicalBytes(sc.Bytes())
if err != nil {
return nil
}
nScalars[i] = s
}
for i, pt := range points {
pp, ok := pt.(*PointEd25519)
if !ok {
return nil
}
nPoints[i] = pp.value
}
pt := edwards25519.NewIdentityPoint().MultiScalarMult(nScalars, nPoints)
return &PointEd25519{value: pt}
}
func (p *PointEd25519) VarTimeDoubleScalarBaseMult(a Scalar, A Point, b Scalar) Point {
AA, ok := A.(*PointEd25519)
if !ok {
return nil
}
aa, ok := a.(*ScalarEd25519)
if !ok {
return nil
}
bb, ok := b.(*ScalarEd25519)
if !ok {
return nil
}
value := edwards25519.NewIdentityPoint().VarTimeDoubleScalarBaseMult(aa.value, AA.value, bb.value)
return &PointEd25519{value}
}
func (p *PointEd25519) MarshalBinary() ([]byte, error) {
return pointMarshalBinary(p)
}
func (p *PointEd25519) UnmarshalBinary(input []byte) error {
pt, err := pointUnmarshalBinary(input)
if err != nil {
return err
}
ppt, ok := pt.(*PointEd25519)
if !ok {
return fmt.Errorf("invalid point")
}
p.value = ppt.value
return nil
}
func (p *PointEd25519) MarshalText() ([]byte, error) {
return pointMarshalText(p)
}
func (p *PointEd25519) UnmarshalText(input []byte) error {
pt, err := pointUnmarshalText(input)
if err != nil {
return err
}
ppt, ok := pt.(*PointEd25519)
if !ok {
return fmt.Errorf("invalid point")
}
p.value = ppt.value
return nil
}
func (p *PointEd25519) MarshalJSON() ([]byte, error) {
return pointMarshalJSON(p)
}
func (p *PointEd25519) UnmarshalJSON(input []byte) error {
pt, err := pointUnmarshalJSON(input)
if err != nil {
return err
}
P, ok := pt.(*PointEd25519)
if !ok {
return fmt.Errorf("invalid type")
}
p.value = P.value
return nil
}
func (p *PointEd25519) GetEdwardsPoint() *edwards25519.Point {
return edwards25519.NewIdentityPoint().Set(p.value)
}
func (p *PointEd25519) SetEdwardsPoint(pt *edwards25519.Point) *PointEd25519 {
return &PointEd25519{value: edwards25519.NewIdentityPoint().Set(pt)}
}
// Attempt to convert to an `EdwardsPoint`, using the supplied
// choice of sign for the `EdwardsPoint`.
// - `sign`: a `u8` donating the desired sign of the resulting
// `EdwardsPoint`. `0` denotes positive and `1` negative.
func toEdwards(u *ed.FieldElement, sign byte) *PointEd25519 {
one := new(ed.FieldElement).SetOne()
// To decompress the Montgomery u coordinate to an
// `EdwardsPoint`, we apply the birational map to obtain the
// Edwards y coordinate, then do Edwards decompression.
//
// The birational map is y = (u-1)/(u+1).
//
// The exceptional points are the zeros of the denominator,
// i.e., u = -1.
//
// But when u = -1, v^2 = u*(u^2+486662*u+1) = 486660.
//
// Since this is nonsquare mod p, u = -1 corresponds to a point
// on the twist, not the curve, so we can reject it early.
if u.Equals(new(ed.FieldElement).Neg(one)) {
return nil
}
// y = (u-1)/(u+1)
yLhs := new(ed.FieldElement).Sub(u, one)
yRhs := new(ed.FieldElement).Add(u, one)
yInv := new(ed.FieldElement).Inverse(yRhs)
y := new(ed.FieldElement).Mul(yLhs, yInv)
yBytes := y.Bytes()
yBytes[31] ^= sign << 7
pt, err := edwards25519.NewIdentityPoint().SetBytes(yBytes[:])
if err != nil {
return nil
}
pt.MultByCofactor(pt)
return &PointEd25519{value: pt}
}
// Perform the Elligator2 mapping to a Montgomery point encoded as a 32 byte value
//
// See <https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-11#section-6.7.1>
func elligatorEncode(r0 *ed.FieldElement) *ed.FieldElement {
montgomeryA := &ed.FieldElement{
486662, 0, 0, 0, 0,
}
// montgomeryANeg is equal to -486662.
montgomeryANeg := &ed.FieldElement{
2251799813198567,
2251799813685247,
2251799813685247,
2251799813685247,
2251799813685247,
}
t := new(ed.FieldElement)
one := new(ed.FieldElement).SetOne()
// 2r^2
d1 := new(ed.FieldElement).Add(one, t.DoubledSquare(r0))
// A/(1+2r^2)
d := new(ed.FieldElement).Mul(montgomeryANeg, t.Inverse(d1))
dsq := new(ed.FieldElement).Square(d)
au := new(ed.FieldElement).Mul(montgomeryA, d)
inner := new(ed.FieldElement).Add(dsq, au)
inner.Add(inner, one)
// d^3 + Ad^2 + d
eps := new(ed.FieldElement).Mul(d, inner)
_, wasSquare := sqrtRatio(eps, one)
zero := new(ed.FieldElement).SetZero()
aTemp := new(ed.FieldElement).SetZero()
// 0 or A if non-square
cselect(aTemp, zero, montgomeryA, wasSquare)
// d, or d+A if non-square
u := new(ed.FieldElement).Add(d, aTemp)
// d or -d-A if non-square
cselect(u, u, new(ed.FieldElement).Neg(u), wasSquare)
return u
}
+403
View File
@@ -0,0 +1,403 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
crand "crypto/rand"
"encoding/hex"
"math/big"
"testing"
ed "filippo.io/edwards25519"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/internal"
)
func TestScalarEd25519Random(t *testing.T) {
ed25519 := ED25519()
sc := ed25519.Scalar.Random(testRng())
s, ok := sc.(*ScalarEd25519)
require.True(t, ok)
expected := toRSc("feaa6a9d6dda758da6145f7d411a3af9f8a120698e0093faa97085b384c3f00e")
require.Equal(t, s.value.Equal(expected), 1)
// Try 10 random values
for i := 0; i < 10; i++ {
sc := ed25519.Scalar.Random(crand.Reader)
_, ok := sc.(*ScalarEd25519)
require.True(t, ok)
require.True(t, !sc.IsZero())
}
}
func TestScalarEd25519Hash(t *testing.T) {
var b [32]byte
ed25519 := ED25519()
sc := ed25519.Scalar.Hash(b[:])
s, ok := sc.(*ScalarEd25519)
require.True(t, ok)
expected := toRSc("9d574494a02d72f5ff311cf0fb844d0fdd6103b17255274e029bdeed7207d409")
require.Equal(t, s.value.Equal(expected), 1)
}
func TestScalarEd25519Zero(t *testing.T) {
ed25519 := ED25519()
sc := ed25519.Scalar.Zero()
require.True(t, sc.IsZero())
require.True(t, sc.IsEven())
}
func TestScalarEd25519One(t *testing.T) {
ed25519 := ED25519()
sc := ed25519.Scalar.One()
require.True(t, sc.IsOne())
require.True(t, sc.IsOdd())
}
func TestScalarEd25519New(t *testing.T) {
ed25519 := ED25519()
three := ed25519.Scalar.New(3)
require.True(t, three.IsOdd())
four := ed25519.Scalar.New(4)
require.True(t, four.IsEven())
neg1 := ed25519.Scalar.New(-1)
require.True(t, neg1.IsEven())
neg2 := ed25519.Scalar.New(-2)
require.True(t, neg2.IsOdd())
}
func TestScalarEd25519Square(t *testing.T) {
ed25519 := ED25519()
three := ed25519.Scalar.New(3)
nine := ed25519.Scalar.New(9)
require.Equal(t, three.Square().Cmp(nine), 0)
}
func TestScalarEd25519Cube(t *testing.T) {
ed25519 := ED25519()
three := ed25519.Scalar.New(3)
twentySeven := ed25519.Scalar.New(27)
require.Equal(t, three.Cube().Cmp(twentySeven), 0)
}
func TestScalarEd25519Double(t *testing.T) {
ed25519 := ED25519()
three := ed25519.Scalar.New(3)
six := ed25519.Scalar.New(6)
require.Equal(t, three.Double().Cmp(six), 0)
}
func TestScalarEd25519Neg(t *testing.T) {
ed25519 := ED25519()
one := ed25519.Scalar.One()
neg1 := ed25519.Scalar.New(-1)
require.Equal(t, one.Neg().Cmp(neg1), 0)
lotsOfThrees := ed25519.Scalar.New(333333)
expected := ed25519.Scalar.New(-333333)
require.Equal(t, lotsOfThrees.Neg().Cmp(expected), 0)
}
func TestScalarEd25519Invert(t *testing.T) {
ed25519 := ED25519()
nine := ed25519.Scalar.New(9)
actual, _ := nine.Invert()
sa, _ := actual.(*ScalarEd25519)
expected := toRSc("c3d9c4db0516043013b1e1ce8637dc92e3388ee3388ee3388ee3388ee3388e03")
require.Equal(t, sa.value.Equal(expected), 1)
}
func TestScalarEd25519Sqrt(t *testing.T) {
ed25519 := ED25519()
nine := ed25519.Scalar.New(9)
actual, err := nine.Sqrt()
sa, _ := actual.(*ScalarEd25519)
expected := toRSc("03")
require.NoError(t, err)
require.Equal(t, sa.value.Equal(expected), 1)
}
func TestScalarEd25519Add(t *testing.T) {
ed25519 := ED25519()
nine := ed25519.Scalar.New(9)
six := ed25519.Scalar.New(6)
fifteen := nine.Add(six)
require.NotNil(t, fifteen)
expected := ed25519.Scalar.New(15)
require.Equal(t, expected.Cmp(fifteen), 0)
upper := ed25519.Scalar.New(-3)
actual := upper.Add(nine)
require.NotNil(t, actual)
require.Equal(t, actual.Cmp(six), 0)
}
func TestScalarEd25519Sub(t *testing.T) {
ed25519 := ED25519()
nine := ed25519.Scalar.New(9)
six := ed25519.Scalar.New(6)
expected := ed25519.Scalar.New(-3)
actual := six.Sub(nine)
require.Equal(t, expected.Cmp(actual), 0)
actual = nine.Sub(six)
require.Equal(t, actual.Cmp(ed25519.Scalar.New(3)), 0)
}
func TestScalarEd25519Mul(t *testing.T) {
ed25519 := ED25519()
nine := ed25519.Scalar.New(9)
six := ed25519.Scalar.New(6)
actual := nine.Mul(six)
require.Equal(t, actual.Cmp(ed25519.Scalar.New(54)), 0)
upper := ed25519.Scalar.New(-1)
require.Equal(t, upper.Mul(upper).Cmp(ed25519.Scalar.New(1)), 0)
}
func TestScalarEd25519Div(t *testing.T) {
ed25519 := ED25519()
nine := ed25519.Scalar.New(9)
actual := nine.Div(nine)
require.Equal(t, actual.Cmp(ed25519.Scalar.New(1)), 0)
require.Equal(t, ed25519.Scalar.New(54).Div(nine).Cmp(ed25519.Scalar.New(6)), 0)
}
func TestScalarEd25519Serialize(t *testing.T) {
ed25519 := ED25519()
sc := ed25519.Scalar.New(255)
sequence := sc.Bytes()
require.Equal(t, len(sequence), 32)
require.Equal(t, sequence, []byte{0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0})
ret, err := ed25519.Scalar.SetBytes(sequence)
require.NoError(t, err)
require.Equal(t, ret.Cmp(sc), 0)
// Try 10 random values
for i := 0; i < 10; i++ {
sc = ed25519.Scalar.Random(crand.Reader)
sequence = sc.Bytes()
require.Equal(t, len(sequence), 32)
ret, err = ed25519.Scalar.SetBytes(sequence)
require.NoError(t, err)
require.Equal(t, ret.Cmp(sc), 0)
}
}
func TestScalarEd25519Nil(t *testing.T) {
ed25519 := ED25519()
one := ed25519.Scalar.New(1)
require.Nil(t, one.Add(nil))
require.Nil(t, one.Sub(nil))
require.Nil(t, one.Mul(nil))
require.Nil(t, one.Div(nil))
require.Nil(t, ed25519.Scalar.Random(nil))
require.Equal(t, one.Cmp(nil), -2)
_, err := ed25519.Scalar.SetBigInt(nil)
require.Error(t, err)
}
func TestPointEd25519Random(t *testing.T) {
ed25519 := ED25519()
sc := ed25519.Point.Random(testRng())
s, ok := sc.(*PointEd25519)
require.True(t, ok)
expected := toRPt("6011540c6231421a70ced5f577432531f198d318facfaad6e52cc42fba6e6fc5")
require.True(t, s.Equal(&PointEd25519{expected}))
// Try 25 random values
for i := 0; i < 25; i++ {
sc := ed25519.Point.Random(crand.Reader)
_, ok := sc.(*PointEd25519)
require.True(t, ok)
require.True(t, !sc.IsIdentity())
pBytes := sc.ToAffineCompressed()
_, err := ed.NewIdentityPoint().SetBytes(pBytes)
require.NoError(t, err)
}
}
func TestPointEd25519Hash(t *testing.T) {
var b [32]byte
ed25519 := ED25519()
sc := ed25519.Point.Hash(b[:])
s, ok := sc.(*PointEd25519)
require.True(t, ok)
expected := toRPt("b4d75c3bb03ca644ab6c6d2a955c911003d8cfa719415de93a6b85eeb0c8dd97")
require.True(t, s.Equal(&PointEd25519{expected}))
// Fuzz test
for i := 0; i < 25; i++ {
_, _ = crand.Read(b[:])
sc = ed25519.Point.Hash(b[:])
require.NotNil(t, sc)
}
}
func TestPointEd25519Identity(t *testing.T) {
ed25519 := ED25519()
sc := ed25519.Point.Identity()
require.True(t, sc.IsIdentity())
require.Equal(t, sc.ToAffineCompressed(), []byte{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})
}
func TestPointEd25519Generator(t *testing.T) {
ed25519 := ED25519()
sc := ed25519.Point.Generator()
s, ok := sc.(*PointEd25519)
require.True(t, ok)
require.Equal(t, s.ToAffineCompressed(), []byte{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})
}
func TestPointEd25519Set(t *testing.T) {
ed25519 := ED25519()
iden, err := ed25519.Point.Set(big.NewInt(0), big.NewInt(0))
require.NoError(t, err)
require.True(t, iden.IsIdentity())
xBytes, _ := hex.DecodeString("1ad5258f602d56c9b2a7259560c72c695cdcd6fd31e2a4c0fe536ecdd3366921")
yBytes, _ := hex.DecodeString("5866666666666666666666666666666666666666666666666666666666666666")
x := new(big.Int).SetBytes(internal.ReverseScalarBytes(xBytes))
y := new(big.Int).SetBytes(internal.ReverseScalarBytes(yBytes))
newPoint, err := ed25519.Point.Set(x, y)
require.NoError(t, err)
require.NotEqualf(t, iden, newPoint, "after setting valid x and y, the point should NOT be identity point")
emptyX := new(big.Int).SetBytes(internal.ReverseScalarBytes([]byte{}))
identityPoint, err := ed25519.Point.Set(emptyX, y)
require.NoError(t, err)
require.Equalf(t, iden, identityPoint, "When x is empty, the point will be identity")
}
func TestPointEd25519Double(t *testing.T) {
ed25519 := ED25519()
g := ed25519.Point.Generator()
g2 := g.Double()
require.True(t, g2.Equal(g.Mul(ed25519.Scalar.New(2))))
i := ed25519.Point.Identity()
require.True(t, i.Double().Equal(i))
}
func TestPointEd25519Neg(t *testing.T) {
ed25519 := ED25519()
g := ed25519.Point.Generator().Neg()
require.True(t, g.Neg().Equal(ed25519.Point.Generator()))
require.True(t, ed25519.Point.Identity().Neg().Equal(ed25519.Point.Identity()))
}
func TestPointEd25519Add(t *testing.T) {
ed25519 := ED25519()
pt := ed25519.Point.Generator()
require.True(t, pt.Add(pt).Equal(pt.Double()))
require.True(t, pt.Mul(ed25519.Scalar.New(3)).Equal(pt.Add(pt).Add(pt)))
}
func TestPointEd25519Sub(t *testing.T) {
ed25519 := ED25519()
g := ed25519.Point.Generator()
pt := ed25519.Point.Generator().Mul(ed25519.Scalar.New(4))
require.True(t, pt.Sub(g).Sub(g).Sub(g).Equal(g))
require.True(t, pt.Sub(g).Sub(g).Sub(g).Sub(g).IsIdentity())
}
func TestPointEd25519Mul(t *testing.T) {
ed25519 := ED25519()
g := ed25519.Point.Generator()
pt := ed25519.Point.Generator().Mul(ed25519.Scalar.New(4))
require.True(t, g.Double().Double().Equal(pt))
}
func TestPointEd25519Serialize(t *testing.T) {
ed25519 := ED25519()
ss := ed25519.Scalar.Random(testRng())
g := ed25519.Point.Generator()
ppt := g.Mul(ss)
expectedC := []byte{0x7f, 0x5b, 0xa, 0xd9, 0xb8, 0xce, 0xb7, 0x7, 0x4c, 0x10, 0xc8, 0xb4, 0x27, 0xe8, 0xd2, 0x28, 0x50, 0x42, 0x6c, 0x0, 0x8a, 0x3, 0x72, 0x2b, 0x7c, 0x3c, 0x37, 0x6f, 0xf8, 0x8f, 0x42, 0x5d}
expectedU := []byte{0x70, 0xad, 0x4, 0xa1, 0x6, 0x8, 0x9f, 0x47, 0xe1, 0xe8, 0x9b, 0x9c, 0x81, 0x5a, 0xfb, 0xb9, 0x85, 0x6a, 0x2c, 0xa, 0xbc, 0xff, 0xe, 0xc6, 0xa0, 0xb0, 0xac, 0x75, 0xc, 0xd8, 0x59, 0x53, 0x7f, 0x5b, 0xa, 0xd9, 0xb8, 0xce, 0xb7, 0x7, 0x4c, 0x10, 0xc8, 0xb4, 0x27, 0xe8, 0xd2, 0x28, 0x50, 0x42, 0x6c, 0x0, 0x8a, 0x3, 0x72, 0x2b, 0x7c, 0x3c, 0x37, 0x6f, 0xf8, 0x8f, 0x42, 0x5d}
require.Equal(t, ppt.ToAffineCompressed(), expectedC)
require.Equal(t, ppt.ToAffineUncompressed(), expectedU)
retP, err := ppt.FromAffineCompressed(ppt.ToAffineCompressed())
require.NoError(t, err)
require.True(t, ppt.Equal(retP))
retP, err = ppt.FromAffineUncompressed(ppt.ToAffineUncompressed())
require.NoError(t, err)
require.True(t, ppt.Equal(retP))
// smoke test
for i := 0; i < 25; i++ {
s := ed25519.Scalar.Random(crand.Reader)
pt := g.Mul(s)
cmprs := pt.ToAffineCompressed()
require.Equal(t, len(cmprs), 32)
retC, err := pt.FromAffineCompressed(cmprs)
require.NoError(t, err)
require.True(t, pt.Equal(retC))
un := pt.ToAffineUncompressed()
require.Equal(t, len(un), 64)
retU, err := pt.FromAffineUncompressed(un)
require.NoError(t, err)
require.True(t, pt.Equal(retU))
}
}
func TestPointEd25519Nil(t *testing.T) {
ed25519 := ED25519()
one := ed25519.Point.Generator()
require.Nil(t, one.Add(nil))
require.Nil(t, one.Sub(nil))
require.Nil(t, one.Mul(nil))
require.Nil(t, ed25519.Scalar.Random(nil))
require.False(t, one.Equal(nil))
_, err := ed25519.Scalar.SetBigInt(nil)
require.Error(t, err)
}
func TestPointEd25519SumOfProducts(t *testing.T) {
lhs := new(PointEd25519).Generator().Mul(new(ScalarEd25519).New(50))
points := make([]Point, 5)
for i := range points {
points[i] = new(PointEd25519).Generator()
}
scalars := []Scalar{
new(ScalarEd25519).New(8),
new(ScalarEd25519).New(9),
new(ScalarEd25519).New(10),
new(ScalarEd25519).New(11),
new(ScalarEd25519).New(12),
}
rhs := lhs.SumOfProducts(points, scalars)
require.NotNil(t, rhs)
require.True(t, lhs.Equal(rhs))
}
func TestPointEd25519VarTimeDoubleScalarBaseMult(t *testing.T) {
curve := ED25519()
h := curve.Point.Hash([]byte("TestPointEd25519VarTimeDoubleScalarBaseMult"))
a := curve.Scalar.New(23)
b := curve.Scalar.New(77)
H, ok := h.(*PointEd25519)
require.True(t, ok)
rhs := H.VarTimeDoubleScalarBaseMult(a, H, b)
lhs := h.Mul(a).Add(curve.Point.Generator().Mul(b))
require.True(t, lhs.Equal(rhs))
}
func toRSc(hx string) *ed.Scalar {
e, _ := hex.DecodeString(hx)
var data [32]byte
copy(data[:], e)
value, _ := new(ed.Scalar).SetCanonicalBytes(data[:])
return value
}
func toRPt(hx string) *ed.Point {
e, _ := hex.DecodeString(hx)
var data [32]byte
copy(data[:], e)
pt, _ := new(PointEd25519).FromAffineCompressed(data[:])
return pt.(*PointEd25519).value
}
+280
View File
@@ -0,0 +1,280 @@
//
// 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
var 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
}
+301
View File
@@ -0,0 +1,301 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"encoding/json"
"errors"
"fmt"
"math/big"
"testing"
"github.com/stretchr/testify/require"
)
var (
one = big.NewInt(1)
modulus, modulusOk = new(big.Int).SetString(
"1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED",
16,
)
oneBelowModulus = zero().Sub(modulus, one)
oneAboveModulus = zero().Add(modulus, one)
field25519 = NewField(modulus)
)
type buggedReader struct{}
func (r buggedReader) Read(p []byte) (n int, err error) {
return 0, errors.New("EOF")
}
func zero() *big.Int {
return new(big.Int)
}
func assertElementZero(t *testing.T, e *Element) {
require.Equal(t, zero().Bytes(), e.Bytes())
}
type binaryOperation func(*Element) *Element
func assertUnequalFieldsPanic(t *testing.T, b binaryOperation) {
altField := NewField(big.NewInt(23))
altElement := altField.NewElement(one)
require.PanicsWithValue(
t,
"fields must match for valid binary operation",
func() { b(altElement) },
)
}
func TestFieldModulus(t *testing.T) {
require.True(t, modulusOk)
}
func TestNewField(t *testing.T) {
require.PanicsWithValue(
t,
fmt.Sprintf("modulus: %x is not a prime", oneBelowModulus),
func() { NewField(oneBelowModulus) },
)
require.NotPanics(
t,
func() { NewField(modulus) },
)
}
func TestNewElement(t *testing.T) {
require.PanicsWithValue(
t,
fmt.Sprintf("value: %x is not within field: %x", modulus, field25519.Int),
func() { newElement(field25519, modulus) },
)
require.NotPanics(
t,
func() { newElement(field25519, oneBelowModulus) },
)
}
func TestElementIsValid(t *testing.T) {
require.False(t, field25519.IsValid(zero().Neg(one)))
require.False(t, field25519.IsValid(modulus))
require.False(t, field25519.IsValid(oneAboveModulus))
require.True(t, field25519.IsValid(oneBelowModulus))
}
func TestFieldNewElement(t *testing.T) {
element := field25519.NewElement(oneBelowModulus)
require.Equal(t, oneBelowModulus, element.Value)
require.Equal(t, field25519, element.Field())
}
func TestZeroElement(t *testing.T) {
require.Equal(t, zero(), field25519.Zero().Value)
require.Equal(t, field25519, field25519.Zero().Field())
}
func TestOneElement(t *testing.T) {
require.Equal(t, field25519.One().Value, one)
require.Equal(t, field25519.One().Field(), field25519)
}
func TestRandomElement(t *testing.T) {
randomElement1, err := field25519.RandomElement(nil)
require.NoError(t, err)
randomElement2, err := field25519.RandomElement(nil)
require.NoError(t, err)
randomElement3, err := field25519.RandomElement(new(buggedReader))
require.Error(t, err)
require.Equal(t, field25519, randomElement1.Field())
require.Equal(t, field25519, randomElement2.Field())
require.NotEqual(t, randomElement1.Value, randomElement2.Value)
require.Nil(t, randomElement3)
}
func TestElementFromBytes(t *testing.T) {
element := field25519.ElementFromBytes(oneBelowModulus.Bytes())
require.Equal(t, field25519, element.Field())
require.Equal(t, oneBelowModulus, element.Value)
}
func TestReducedElementFromBytes(t *testing.T) {
element := field25519.ReducedElementFromBytes(oneBelowModulus.Bytes())
require.Equal(t, field25519, element.Field())
require.Equal(t, oneBelowModulus, element.Value)
element = field25519.ReducedElementFromBytes(oneAboveModulus.Bytes())
require.Equal(t, field25519, element.Field())
require.Equal(t, one, element.Value)
}
func TestAddElement(t *testing.T) {
element1 := field25519.NewElement(one)
element2 := field25519.NewElement(big.NewInt(2))
element3 := field25519.NewElement(oneBelowModulus)
element4 := &Element{field25519, modulus}
require.Equal(t, element2, element1.Add(element1))
require.Equal(t, big.NewInt(3), element1.Add(element2).Value)
require.Equal(t, big.NewInt(3), element2.Add(element1).Value)
require.Equal(t, one, element1.Add(element4).Value)
require.Equal(t, one, element3.Add(element2).Value)
assertElementZero(t, element1.Add(element3))
assertUnequalFieldsPanic(t, element1.Add)
}
func TestSubElement(t *testing.T) {
element1 := field25519.NewElement(one)
element2 := field25519.NewElement(big.NewInt(2))
element3 := field25519.NewElement(oneBelowModulus)
element4 := &Element{field25519, modulus}
assertElementZero(t, element1.Sub(element1))
require.Equal(t, element3, element1.Sub(element2))
require.Equal(t, element1, element2.Sub(element1))
require.Equal(t, element1, element1.Sub(element4))
require.Equal(t, element3, element4.Sub(element1))
require.Equal(t, element1, element4.Sub(element3))
require.Equal(t, element3, element3.Sub(element4))
assertUnequalFieldsPanic(t, element1.Sub)
}
func TestMulElement(t *testing.T) {
element1 := field25519.NewElement(one)
element2 := field25519.NewElement(big.NewInt(2))
element3 := field25519.NewElement(oneBelowModulus)
element4 := field25519.NewElement(zero())
expectedProduct, ok := new(big.Int).SetString(
"1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3eb",
16,
)
require.True(t, ok)
assertElementZero(t, element1.Mul(element4))
assertElementZero(t, element4.Mul(element1))
require.Equal(t, element3, element1.Mul(element3))
require.Equal(t, element3, element3.Mul(element1))
require.Equal(t, expectedProduct, element3.Mul(element2).Value)
require.Equal(t, expectedProduct, element2.Mul(element3).Value)
assertUnequalFieldsPanic(t, element1.Mul)
}
func TestDivElement(t *testing.T) {
element1 := field25519.NewElement(one)
element2 := field25519.NewElement(big.NewInt(2))
element3 := field25519.NewElement(oneBelowModulus)
element4 := field25519.NewElement(zero())
expectedQuotient1, ok := new(big.Int).SetString(
"80000000000000000000000000000000a6f7cef517bce6b2c09318d2e7ae9f6",
16,
)
require.True(t, ok)
expectedQuotient2, ok := new(big.Int).SetString(
"1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3eb",
16,
)
require.True(t, ok)
assertElementZero(t, element4.Div(element3))
require.Equal(t, element3, element3.Div(element1))
require.Equal(t, expectedQuotient1, element3.Div(element2).Value)
require.Equal(t, expectedQuotient2, element2.Div(element3).Value)
require.Panics(t, func() { element3.Div(element4) })
assertUnequalFieldsPanic(t, element1.Div)
}
func TestIsEqualElement(t *testing.T) {
element1 := field25519.NewElement(oneBelowModulus)
element2 := field25519.NewElement(big.NewInt(23))
element3 := field25519.NewElement(oneBelowModulus)
altField := NewField(big.NewInt(23))
altElement1 := altField.NewElement(one)
require.False(t, element1.IsEqual(element2))
require.True(t, element1.IsEqual(element3))
require.True(t, element1.IsEqual(element1))
require.False(t, element1.IsEqual(altElement1))
}
func TestBigIntElement(t *testing.T) {
element := field25519.NewElement(oneBelowModulus)
require.Equal(t, oneBelowModulus, element.BigInt())
}
func TestBytesElement(t *testing.T) {
element := field25519.NewElement(oneBelowModulus)
require.Equal(
t,
[]byte{
0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0xde, 0xf9, 0xde, 0xa2,
0xf7, 0x9c, 0xd6, 0x58, 0x12, 0x63, 0x1a, 0x5c, 0xf5,
0xd3, 0xec,
},
element.Bytes(),
)
}
func TestCloneElement(t *testing.T) {
element := field25519.NewElement(oneBelowModulus)
clone := element.Clone()
require.Equal(t, clone, element)
clone.Value.Add(one, one)
require.NotEqual(t, clone, element)
}
// Tests un/marshaling Element
func TestElementMarshalJsonRoundTrip(t *testing.T) {
reallyBigInt1, ok := new(big.Int).SetString("12365234878725472538962348629568356835892346729834725643857832", 10)
require.True(t, ok)
reallyBigInt2, ok := new(big.Int).SetString("123652348787DEF9DEA2F79CD65812631A5CF5D3ED46729834725643857832", 16)
require.True(t, ok)
ins := []*Element{
newElement(field25519, big.NewInt(300)),
newElement(field25519, big.NewInt(300000)),
newElement(field25519, big.NewInt(12812798)),
newElement(field25519, big.NewInt(17)),
newElement(field25519, big.NewInt(5066680)),
newElement(field25519, big.NewInt(3005)),
newElement(field25519, big.NewInt(317)),
newElement(field25519, big.NewInt(323)),
newElement(field25519, reallyBigInt1),
newElement(field25519, reallyBigInt2),
newElement(field25519, oneBelowModulus),
}
// Run all the tests!
for _, in := range ins {
bytes, err := json.Marshal(in)
require.NoError(t, err)
require.NotNil(t, bytes)
// Unmarshal and test
out := &Element{}
err = json.Unmarshal(bytes, &out)
require.NoError(t, err)
require.NotNil(t, out)
require.NotNil(t, out.Modulus)
require.NotNil(t, out.Value)
require.Equal(t, in.Modulus.Bytes(), out.Modulus.Bytes())
require.Equal(t, in.Value.Bytes(), out.Value.Bytes())
}
}
+446
View File
@@ -0,0 +1,446 @@
package curves
import (
crand "crypto/rand"
"crypto/sha256"
"io"
"math/big"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
mod "github.com/onsonr/sonr/crypto/core"
"github.com/onsonr/sonr/crypto/internal"
)
func BenchmarkK256(b *testing.B) {
// 1000 points
b.Run("1000 point add - btcec", func(b *testing.B) {
b.StopTimer()
points := make([]*BenchPoint, 1000)
for i := range points {
points[i] = points[i].Random(crand.Reader).(*BenchPoint)
}
acc := new(BenchPoint).Identity()
b.StartTimer()
for _, pt := range points {
acc = acc.Add(pt)
}
})
b.Run("1000 point add - ct k256", func(b *testing.B) {
b.StopTimer()
curve := K256()
points := make([]*PointK256, 1000)
for i := range points {
points[i] = curve.NewIdentityPoint().Random(crand.Reader).(*PointK256)
}
acc := curve.NewIdentityPoint()
b.StartTimer()
for _, pt := range points {
acc = acc.Add(pt)
}
})
b.Run("1000 point double - btcec", func(b *testing.B) {
b.StopTimer()
acc := new(BenchPoint).Generator()
b.StartTimer()
for i := 0; i < 1000; i++ {
acc = acc.Double()
}
})
b.Run("1000 point double - ct k256", func(b *testing.B) {
b.StopTimer()
acc := new(PointK256).Generator()
b.StartTimer()
for i := 0; i < 1000; i++ {
acc = acc.Double()
}
})
b.Run("1000 point multiply - btcec", func(b *testing.B) {
b.StopTimer()
scalars := make([]*BenchScalar, 1000)
for i := range scalars {
s := new(BenchScalar).Random(crand.Reader)
scalars[i] = s.(*BenchScalar)
}
acc := new(BenchPoint).Generator().Mul(new(BenchScalar).New(2))
b.StartTimer()
for _, sc := range scalars {
acc = acc.Mul(sc)
}
})
b.Run("1000 point multiply - ct k256", func(b *testing.B) {
b.StopTimer()
scalars := make([]*ScalarK256, 1000)
for i := range scalars {
s := new(ScalarK256).Random(crand.Reader)
scalars[i] = s.(*ScalarK256)
}
acc := new(PointK256).Generator()
b.StartTimer()
for _, sc := range scalars {
acc = acc.Mul(sc)
}
})
b.Run("1000 scalar invert - btcec", func(b *testing.B) {
b.StopTimer()
scalars := make([]*BenchScalar, 1000)
for i := range scalars {
s := new(BenchScalar).Random(crand.Reader)
scalars[i] = s.(*BenchScalar)
}
b.StartTimer()
for _, sc := range scalars {
_, _ = sc.Invert()
}
})
b.Run("1000 scalar invert - ct k256", func(b *testing.B) {
b.StopTimer()
scalars := make([]*ScalarK256, 1000)
for i := range scalars {
s := new(ScalarK256).Random(crand.Reader)
scalars[i] = s.(*ScalarK256)
}
b.StartTimer()
for _, sc := range scalars {
_, _ = sc.Invert()
}
})
b.Run("1000 scalar sqrt - btcec", func(b *testing.B) {
b.StopTimer()
scalars := make([]*BenchScalar, 1000)
for i := range scalars {
s := new(BenchScalar).Random(crand.Reader)
scalars[i] = s.(*BenchScalar)
}
b.StartTimer()
for _, sc := range scalars {
_, _ = sc.Sqrt()
}
})
b.Run("1000 scalar sqrt - ct k256", func(b *testing.B) {
b.StopTimer()
scalars := make([]*ScalarK256, 1000)
for i := range scalars {
s := new(ScalarK256).Random(crand.Reader)
scalars[i] = s.(*ScalarK256)
}
b.StartTimer()
for _, sc := range scalars {
_, _ = sc.Sqrt()
}
})
}
type BenchScalar struct {
value *big.Int
}
func (s *BenchScalar) Random(reader io.Reader) Scalar {
var v [32]byte
_, _ = reader.Read(v[:])
value := new(big.Int).SetBytes(v[:])
return &BenchScalar{
value: value.Mod(value, btcec.S256().N),
}
}
func (s *BenchScalar) Hash(bytes []byte) Scalar {
h := sha256.Sum256(bytes)
value := new(big.Int).SetBytes(h[:])
return &BenchScalar{
value: value.Mod(value, btcec.S256().N),
}
}
func (s *BenchScalar) Zero() Scalar {
return &BenchScalar{
value: big.NewInt(0),
}
}
func (s *BenchScalar) One() Scalar {
return &BenchScalar{
value: big.NewInt(1),
}
}
func (s *BenchScalar) IsZero() bool {
return s.value.Cmp(big.NewInt(0)) == 0
}
func (s *BenchScalar) IsOne() bool {
return s.value.Cmp(big.NewInt(1)) == 0
}
func (s *BenchScalar) IsOdd() bool {
return s.value.Bit(0) == 1
}
func (s *BenchScalar) IsEven() bool {
return s.value.Bit(0) == 0
}
func (s *BenchScalar) New(value int) Scalar {
v := big.NewInt(int64(value))
return &BenchScalar{
value: v.Mod(v, btcec.S256().N),
}
}
func (s *BenchScalar) Cmp(rhs Scalar) int {
r := rhs.(*BenchScalar)
return s.value.Cmp(r.value)
}
func (s *BenchScalar) Square() Scalar {
v := new(big.Int).Mul(s.value, s.value)
return &BenchScalar{
value: v.Mod(v, btcec.S256().N),
}
}
func (s *BenchScalar) Double() Scalar {
v := new(big.Int).Add(s.value, s.value)
return &BenchScalar{
value: v.Mod(v, btcec.S256().N),
}
}
func (s *BenchScalar) Invert() (Scalar, error) {
return &BenchScalar{
value: new(big.Int).ModInverse(s.value, btcec.S256().N),
}, nil
}
func (s *BenchScalar) Sqrt() (Scalar, error) {
return &BenchScalar{
value: new(big.Int).ModSqrt(s.value, btcec.S256().N),
}, nil
}
func (s *BenchScalar) Cube() Scalar {
v := new(big.Int).Mul(s.value, s.value)
v.Mul(v, s.value)
return &BenchScalar{
value: v.Mod(v, btcec.S256().N),
}
}
func (s *BenchScalar) Add(rhs Scalar) Scalar {
r := rhs.(*BenchScalar)
v := new(big.Int).Add(s.value, r.value)
return &BenchScalar{
value: v.Mod(v, btcec.S256().N),
}
}
func (s *BenchScalar) Sub(rhs Scalar) Scalar {
r := rhs.(*BenchScalar)
v := new(big.Int).Sub(s.value, r.value)
return &BenchScalar{
value: v.Mod(v, btcec.S256().N),
}
}
func (s *BenchScalar) Mul(rhs Scalar) Scalar {
r := rhs.(*BenchScalar)
v := new(big.Int).Mul(s.value, r.value)
return &BenchScalar{
value: v.Mod(v, btcec.S256().N),
}
}
func (s *BenchScalar) MulAdd(y, z Scalar) Scalar {
yy := y.(*BenchScalar)
zz := z.(*BenchScalar)
v := new(big.Int).Mul(s.value, yy.value)
v.Add(v, zz.value)
return &BenchScalar{
value: v.Mod(v, btcec.S256().N),
}
}
func (s *BenchScalar) Div(rhs Scalar) Scalar {
r := rhs.(*BenchScalar)
v := new(big.Int).ModInverse(r.value, btcec.S256().N)
v.Mul(v, s.value)
return &BenchScalar{
value: v.Mod(v, btcec.S256().N),
}
}
func (s *BenchScalar) Neg() Scalar {
v, _ := mod.Neg(s.value, btcec.S256().N)
return &BenchScalar{
value: v,
}
}
func (s *BenchScalar) SetBigInt(v *big.Int) (Scalar, error) {
return &BenchScalar{
value: new(big.Int).Set(v),
}, nil
}
func (s *BenchScalar) BigInt() *big.Int {
return new(big.Int).Set(s.value)
}
func (s *BenchScalar) Point() Point {
return (&BenchPoint{}).Identity()
}
func (s *BenchScalar) Bytes() []byte {
return internal.ReverseScalarBytes(s.value.Bytes())
}
func (s *BenchScalar) SetBytes(bytes []byte) (Scalar, error) {
value := new(big.Int).SetBytes(internal.ReverseScalarBytes(bytes))
value.Mod(value, btcec.S256().N)
return &BenchScalar{
value,
}, nil
}
func (s *BenchScalar) SetBytesWide(bytes []byte) (Scalar, error) {
value := new(big.Int).SetBytes(internal.ReverseScalarBytes(bytes))
value.Mod(value, btcec.S256().N)
return &BenchScalar{
value,
}, nil
}
func (s *BenchScalar) Clone() Scalar {
return &BenchScalar{
value: new(big.Int).Set(s.value),
}
}
type BenchPoint struct {
x, y *big.Int
}
func (p *BenchPoint) Random(reader io.Reader) Point {
var k [32]byte
curve := btcec.S256()
_, _ = reader.Read(k[:])
x, y := curve.ScalarBaseMult(k[:])
for !curve.IsOnCurve(x, y) {
_, _ = reader.Read(k[:])
x, y = curve.ScalarBaseMult(k[:])
}
return &BenchPoint{x, y}
}
func (p *BenchPoint) Hash(bytes []byte) Point {
return nil
}
func (p *BenchPoint) Identity() Point {
return &BenchPoint{x: big.NewInt(0), y: big.NewInt(0)}
}
func (p *BenchPoint) Generator() Point {
return &BenchPoint{
x: new(big.Int).Set(btcec.S256().Gx),
y: new(big.Int).Set(btcec.S256().Gy),
}
}
func (p *BenchPoint) IsIdentity() bool {
return false
}
func (p *BenchPoint) IsNegative() bool {
return false
}
func (p *BenchPoint) IsOnCurve() bool {
return btcec.S256().IsOnCurve(p.x, p.y)
}
func (p *BenchPoint) Double() Point {
x, y := btcec.S256().Double(p.x, p.y)
return &BenchPoint{
x, y,
}
}
func (p *BenchPoint) Scalar() Scalar {
return &BenchScalar{value: big.NewInt(0)}
}
func (p *BenchPoint) Neg() Point {
y, _ := mod.Neg(p.y, btcec.S256().P)
return &BenchPoint{
x: new(big.Int).Set(p.x), y: y,
}
}
func (p *BenchPoint) Add(rhs Point) Point {
r := rhs.(*BenchPoint)
x, y := btcec.S256().Add(p.x, p.y, r.x, r.y)
return &BenchPoint{
x, y,
}
}
func (p *BenchPoint) Sub(rhs Point) Point {
t := rhs.Neg().(*BenchPoint)
return t.Add(p)
}
func (p *BenchPoint) Mul(rhs Scalar) Point {
k := rhs.Bytes()
x, y := btcec.S256().ScalarMult(p.x, p.y, k)
return &BenchPoint{
x, y,
}
}
func (p *BenchPoint) Equal(rhs Point) bool {
r := rhs.(*BenchPoint)
return p.x.Cmp(r.x) == 0 && p.y.Cmp(r.y) == 0
}
func (p *BenchPoint) Set(x, y *big.Int) (Point, error) {
return &BenchPoint{
x, y,
}, nil
}
func (p *BenchPoint) ToAffineCompressed() []byte {
return nil
}
func (p *BenchPoint) ToAffineUncompressed() []byte {
return nil
}
func (p *BenchPoint) FromAffineCompressed(bytes []byte) (Point, error) {
return nil, nil
}
func (p *BenchPoint) FromAffineUncompressed(bytes []byte) (Point, error) {
return nil, nil
}
func (p *BenchPoint) CurveName() string {
return btcec.S256().Name
}
func (p *BenchPoint) SumOfProducts(points []Point, scalars []Scalar) Point {
biScalars := make([]*big.Int, len(scalars))
for i := 0; i < len(scalars); i++ {
biScalars[i] = scalars[i].BigInt()
}
return sumOfProductsPippenger(points, biScalars)
}
//func rhsK256(x *big.Int) *big.Int {
// // y^2 = x^3 + B
// x3, _ := mod.Exp(x, big.NewInt(3), btcec.S256().P)
// x3.Add(x3, btcec.S256().B)
// return x3.ModSqrt(x3, btcec.S256().P)
//}
+670
View File
@@ -0,0 +1,670 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"crypto/elliptic"
"fmt"
"io"
"math/big"
"sync"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/onsonr/sonr/crypto/core/curves/native"
secp256k1 "github.com/onsonr/sonr/crypto/core/curves/native/k256"
"github.com/onsonr/sonr/crypto/core/curves/native/k256/fp"
"github.com/onsonr/sonr/crypto/core/curves/native/k256/fq"
"github.com/onsonr/sonr/crypto/internal"
)
var (
oldK256Initonce sync.Once
oldK256 Koblitz256
)
type Koblitz256 struct {
*elliptic.CurveParams
}
func oldK256InitAll() {
curve := btcec.S256()
oldK256.CurveParams = new(elliptic.CurveParams)
oldK256.P = curve.P
oldK256.N = curve.N
oldK256.Gx = curve.Gx
oldK256.Gy = curve.Gy
oldK256.B = curve.B
oldK256.BitSize = curve.BitSize
oldK256.Name = K256Name
}
func K256Curve() *Koblitz256 {
oldK256Initonce.Do(oldK256InitAll)
return &oldK256
}
func (curve *Koblitz256) Params() *elliptic.CurveParams {
return curve.CurveParams
}
func (curve *Koblitz256) IsOnCurve(x, y *big.Int) bool {
_, err := secp256k1.K256PointNew().SetBigInt(x, y)
return err == nil
}
func (curve *Koblitz256) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
p1, err := secp256k1.K256PointNew().SetBigInt(x1, y1)
if err != nil {
return nil, nil
}
p2, err := secp256k1.K256PointNew().SetBigInt(x2, y2)
if err != nil {
return nil, nil
}
return p1.Add(p1, p2).BigInt()
}
func (curve *Koblitz256) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
p1, err := secp256k1.K256PointNew().SetBigInt(x1, y1)
if err != nil {
return nil, nil
}
return p1.Double(p1).BigInt()
}
func (curve *Koblitz256) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
p1, err := secp256k1.K256PointNew().SetBigInt(Bx, By)
if err != nil {
return nil, nil
}
var bytes [32]byte
copy(bytes[:], internal.ReverseScalarBytes(k))
s, err := fq.K256FqNew().SetBytes(&bytes)
if err != nil {
return nil, nil
}
return p1.Mul(p1, s).BigInt()
}
func (curve *Koblitz256) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
var bytes [32]byte
copy(bytes[:], internal.ReverseScalarBytes(k))
s, err := fq.K256FqNew().SetBytes(&bytes)
if err != nil {
return nil, nil
}
p1 := secp256k1.K256PointNew().Generator()
return p1.Mul(p1, s).BigInt()
}
type ScalarK256 struct {
value *native.Field
}
type PointK256 struct {
value *native.EllipticPoint
}
func (s *ScalarK256) Random(reader io.Reader) Scalar {
if reader == nil {
return nil
}
var seed [64]byte
_, _ = reader.Read(seed[:])
return s.Hash(seed[:])
}
func (s *ScalarK256) Hash(bytes []byte) Scalar {
dst := []byte("secp256k1_XMD:SHA-256_SSWU_RO_")
xmd := native.ExpandMsgXmd(native.EllipticPointHasherSha256(), bytes, dst, 48)
var t [64]byte
copy(t[:48], internal.ReverseScalarBytes(xmd))
return &ScalarK256{
value: fq.K256FqNew().SetBytesWide(&t),
}
}
func (s *ScalarK256) Zero() Scalar {
return &ScalarK256{
value: fq.K256FqNew().SetZero(),
}
}
func (s *ScalarK256) One() Scalar {
return &ScalarK256{
value: fq.K256FqNew().SetOne(),
}
}
func (s *ScalarK256) IsZero() bool {
return s.value.IsZero() == 1
}
func (s *ScalarK256) IsOne() bool {
return s.value.IsOne() == 1
}
func (s *ScalarK256) IsOdd() bool {
return s.value.Bytes()[0]&1 == 1
}
func (s *ScalarK256) IsEven() bool {
return s.value.Bytes()[0]&1 == 0
}
func (s *ScalarK256) New(value int) Scalar {
t := fq.K256FqNew()
v := big.NewInt(int64(value))
if value < 0 {
v.Mod(v, t.Params.BiModulus)
}
return &ScalarK256{
value: t.SetBigInt(v),
}
}
func (s *ScalarK256) Cmp(rhs Scalar) int {
r, ok := rhs.(*ScalarK256)
if ok {
return s.value.Cmp(r.value)
} else {
return -2
}
}
func (s *ScalarK256) Square() Scalar {
return &ScalarK256{
value: fq.K256FqNew().Square(s.value),
}
}
func (s *ScalarK256) Double() Scalar {
return &ScalarK256{
value: fq.K256FqNew().Double(s.value),
}
}
func (s *ScalarK256) Invert() (Scalar, error) {
value, wasInverted := fq.K256FqNew().Invert(s.value)
if !wasInverted {
return nil, fmt.Errorf("inverse doesn't exist")
}
return &ScalarK256{
value,
}, nil
}
func (s *ScalarK256) Sqrt() (Scalar, error) {
value, wasSquare := fq.K256FqNew().Sqrt(s.value)
if !wasSquare {
return nil, fmt.Errorf("not a square")
}
return &ScalarK256{
value,
}, nil
}
func (s *ScalarK256) Cube() Scalar {
value := fq.K256FqNew().Mul(s.value, s.value)
value.Mul(value, s.value)
return &ScalarK256{
value,
}
}
func (s *ScalarK256) Add(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarK256)
if ok {
return &ScalarK256{
value: fq.K256FqNew().Add(s.value, r.value),
}
} else {
return nil
}
}
func (s *ScalarK256) Sub(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarK256)
if ok {
return &ScalarK256{
value: fq.K256FqNew().Sub(s.value, r.value),
}
} else {
return nil
}
}
func (s *ScalarK256) Mul(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarK256)
if ok {
return &ScalarK256{
value: fq.K256FqNew().Mul(s.value, r.value),
}
} else {
return nil
}
}
func (s *ScalarK256) MulAdd(y, z Scalar) Scalar {
return s.Mul(y).Add(z)
}
func (s *ScalarK256) Div(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarK256)
if ok {
v, wasInverted := fq.K256FqNew().Invert(r.value)
if !wasInverted {
return nil
}
v.Mul(v, s.value)
return &ScalarK256{value: v}
} else {
return nil
}
}
func (s *ScalarK256) Neg() Scalar {
return &ScalarK256{
value: fq.K256FqNew().Neg(s.value),
}
}
func (s *ScalarK256) SetBigInt(v *big.Int) (Scalar, error) {
if v == nil {
return nil, fmt.Errorf("'v' cannot be nil")
}
value := fq.K256FqNew().SetBigInt(v)
return &ScalarK256{
value,
}, nil
}
func (s *ScalarK256) BigInt() *big.Int {
return s.value.BigInt()
}
func (s *ScalarK256) Bytes() []byte {
t := s.value.Bytes()
return internal.ReverseScalarBytes(t[:])
}
func (s *ScalarK256) SetBytes(bytes []byte) (Scalar, error) {
if len(bytes) != 32 {
return nil, fmt.Errorf("invalid length")
}
var seq [32]byte
copy(seq[:], internal.ReverseScalarBytes(bytes))
value, err := fq.K256FqNew().SetBytes(&seq)
if err != nil {
return nil, err
}
return &ScalarK256{
value,
}, nil
}
func (s *ScalarK256) SetBytesWide(bytes []byte) (Scalar, error) {
if len(bytes) != 64 {
return nil, fmt.Errorf("invalid length")
}
var seq [64]byte
copy(seq[:], bytes)
return &ScalarK256{
value: fq.K256FqNew().SetBytesWide(&seq),
}, nil
}
func (s *ScalarK256) Point() Point {
return new(PointK256).Identity()
}
func (s *ScalarK256) Clone() Scalar {
return &ScalarK256{
value: fq.K256FqNew().Set(s.value),
}
}
func (s *ScalarK256) MarshalBinary() ([]byte, error) {
return scalarMarshalBinary(s)
}
func (s *ScalarK256) UnmarshalBinary(input []byte) error {
sc, err := scalarUnmarshalBinary(input)
if err != nil {
return err
}
ss, ok := sc.(*ScalarK256)
if !ok {
return fmt.Errorf("invalid scalar")
}
s.value = ss.value
return nil
}
func (s *ScalarK256) MarshalText() ([]byte, error) {
return scalarMarshalText(s)
}
func (s *ScalarK256) UnmarshalText(input []byte) error {
sc, err := scalarUnmarshalText(input)
if err != nil {
return err
}
ss, ok := sc.(*ScalarK256)
if !ok {
return fmt.Errorf("invalid scalar")
}
s.value = ss.value
return nil
}
func (s *ScalarK256) MarshalJSON() ([]byte, error) {
return scalarMarshalJson(s)
}
func (s *ScalarK256) UnmarshalJSON(input []byte) error {
sc, err := scalarUnmarshalJson(input)
if err != nil {
return err
}
S, ok := sc.(*ScalarK256)
if !ok {
return fmt.Errorf("invalid type")
}
s.value = S.value
return nil
}
func (p *PointK256) Random(reader io.Reader) Point {
var seed [64]byte
_, _ = reader.Read(seed[:])
return p.Hash(seed[:])
}
func (p *PointK256) Hash(bytes []byte) Point {
value, err := secp256k1.K256PointNew().Hash(bytes, native.EllipticPointHasherSha256())
// TODO: change hash to return an error also
if err != nil {
return nil
}
return &PointK256{value}
}
func (p *PointK256) Identity() Point {
return &PointK256{
value: secp256k1.K256PointNew().Identity(),
}
}
func (p *PointK256) Generator() Point {
return &PointK256{
value: secp256k1.K256PointNew().Generator(),
}
}
func (p *PointK256) IsIdentity() bool {
return p.value.IsIdentity()
}
func (p *PointK256) IsNegative() bool {
return p.value.GetY().Value[0]&1 == 1
}
func (p *PointK256) IsOnCurve() bool {
return p.value.IsOnCurve()
}
func (p *PointK256) Double() Point {
value := secp256k1.K256PointNew().Double(p.value)
return &PointK256{value}
}
func (p *PointK256) Scalar() Scalar {
return new(ScalarK256).Zero()
}
func (p *PointK256) Neg() Point {
value := secp256k1.K256PointNew().Neg(p.value)
return &PointK256{value}
}
func (p *PointK256) Add(rhs Point) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*PointK256)
if ok {
value := secp256k1.K256PointNew().Add(p.value, r.value)
return &PointK256{value}
} else {
return nil
}
}
func (p *PointK256) Sub(rhs Point) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*PointK256)
if ok {
value := secp256k1.K256PointNew().Sub(p.value, r.value)
return &PointK256{value}
} else {
return nil
}
}
func (p *PointK256) Mul(rhs Scalar) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*ScalarK256)
if ok {
value := secp256k1.K256PointNew().Mul(p.value, r.value)
return &PointK256{value}
} else {
return nil
}
}
func (p *PointK256) Equal(rhs Point) bool {
r, ok := rhs.(*PointK256)
if ok {
return p.value.Equal(r.value) == 1
} else {
return false
}
}
func (p *PointK256) Set(x, y *big.Int) (Point, error) {
value, err := secp256k1.K256PointNew().SetBigInt(x, y)
if err != nil {
return nil, err
}
return &PointK256{value}, nil
}
func (p *PointK256) ToAffineCompressed() []byte {
var x [33]byte
x[0] = byte(2)
t := secp256k1.K256PointNew().ToAffine(p.value)
x[0] |= t.Y.Bytes()[0] & 1
xBytes := t.X.Bytes()
copy(x[1:], internal.ReverseScalarBytes(xBytes[:]))
return x[:]
}
func (p *PointK256) ToAffineUncompressed() []byte {
var out [65]byte
out[0] = byte(4)
t := secp256k1.K256PointNew().ToAffine(p.value)
arr := t.X.Bytes()
copy(out[1:33], internal.ReverseScalarBytes(arr[:]))
arr = t.Y.Bytes()
copy(out[33:], internal.ReverseScalarBytes(arr[:]))
return out[:]
}
func (p *PointK256) FromAffineCompressed(bytes []byte) (Point, error) {
var raw [native.FieldBytes]byte
if len(bytes) != 33 {
return nil, fmt.Errorf("invalid byte sequence")
}
sign := int(bytes[0])
if sign != 2 && sign != 3 {
return nil, fmt.Errorf("invalid sign byte")
}
sign &= 0x1
copy(raw[:], internal.ReverseScalarBytes(bytes[1:]))
x, err := fp.K256FpNew().SetBytes(&raw)
if err != nil {
return nil, err
}
value := secp256k1.K256PointNew().Identity()
rhs := fp.K256FpNew()
p.value.Arithmetic.RhsEq(rhs, x)
// test that rhs is quadratic residue
// if not, then this Point is at infinity
y, wasQr := fp.K256FpNew().Sqrt(rhs)
if wasQr {
// fix the sign
sigY := int(y.Bytes()[0] & 1)
if sigY != sign {
y.Neg(y)
}
value.X = x
value.Y = y
value.Z.SetOne()
}
return &PointK256{value}, nil
}
func (p *PointK256) FromAffineUncompressed(bytes []byte) (Point, error) {
var arr [native.FieldBytes]byte
if len(bytes) != 65 {
return nil, fmt.Errorf("invalid byte sequence")
}
if bytes[0] != 4 {
return nil, fmt.Errorf("invalid sign byte")
}
copy(arr[:], internal.ReverseScalarBytes(bytes[1:33]))
x, err := fp.K256FpNew().SetBytes(&arr)
if err != nil {
return nil, err
}
copy(arr[:], internal.ReverseScalarBytes(bytes[33:]))
y, err := fp.K256FpNew().SetBytes(&arr)
if err != nil {
return nil, err
}
value := secp256k1.K256PointNew()
value.X = x
value.Y = y
value.Z.SetOne()
return &PointK256{value}, nil
}
func (p *PointK256) CurveName() string {
return p.value.Params.Name
}
func (p *PointK256) SumOfProducts(points []Point, scalars []Scalar) Point {
nPoints := make([]*native.EllipticPoint, len(points))
nScalars := make([]*native.Field, len(scalars))
for i, pt := range points {
ptv, ok := pt.(*PointK256)
if !ok {
return nil
}
nPoints[i] = ptv.value
}
for i, sc := range scalars {
s, ok := sc.(*ScalarK256)
if !ok {
return nil
}
nScalars[i] = s.value
}
value := secp256k1.K256PointNew()
_, err := value.SumOfProducts(nPoints, nScalars)
if err != nil {
return nil
}
return &PointK256{value}
}
func (p *PointK256) X() *native.Field {
return p.value.GetX()
}
func (p *PointK256) Y() *native.Field {
return p.value.GetY()
}
func (p *PointK256) Params() *elliptic.CurveParams {
return K256Curve().Params()
}
func (p *PointK256) MarshalBinary() ([]byte, error) {
return pointMarshalBinary(p)
}
func (p *PointK256) UnmarshalBinary(input []byte) error {
pt, err := pointUnmarshalBinary(input)
if err != nil {
return err
}
ppt, ok := pt.(*PointK256)
if !ok {
return fmt.Errorf("invalid point")
}
p.value = ppt.value
return nil
}
func (p *PointK256) MarshalText() ([]byte, error) {
return pointMarshalText(p)
}
func (p *PointK256) UnmarshalText(input []byte) error {
pt, err := pointUnmarshalText(input)
if err != nil {
return err
}
ppt, ok := pt.(*PointK256)
if !ok {
return fmt.Errorf("invalid point")
}
p.value = ppt.value
return nil
}
func (p *PointK256) MarshalJSON() ([]byte, error) {
return pointMarshalJSON(p)
}
func (p *PointK256) UnmarshalJSON(input []byte) error {
pt, err := pointUnmarshalJSON(input)
if err != nil {
return err
}
P, ok := pt.(*PointK256)
if !ok {
return fmt.Errorf("invalid type")
}
p.value = P.value
return nil
}
+423
View File
@@ -0,0 +1,423 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
crand "crypto/rand"
"math/big"
"sync"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/stretchr/testify/require"
)
type mockReader struct {
index int
seed []byte
}
var (
mockRngInitonce sync.Once
mockRng mockReader
)
func newMockReader() {
mockRng.index = 0
mockRng.seed = make([]byte, 32)
for i := range mockRng.seed {
mockRng.seed[i] = 1
}
}
func testRng() *mockReader {
mockRngInitonce.Do(newMockReader)
return &mockRng
}
func (m *mockReader) Read(p []byte) (n int, err error) {
limit := len(m.seed)
for i := range p {
p[i] = m.seed[m.index]
m.index += 1
m.index %= limit
}
n = len(p)
err = nil
return
}
func TestScalarK256Random(t *testing.T) {
curve := K256()
sc := curve.Scalar.Random(testRng())
s, ok := sc.(*ScalarK256)
require.True(t, ok)
expected, _ := new(big.Int).SetString("2f71aaec5e14d747c72e46cdcaffffe6f542f38b3f0925469ceb24ac1c65885d", 16)
require.Equal(t, s.value.BigInt(), expected)
// Try 10 random values
for i := 0; i < 10; i++ {
sc := curve.Scalar.Random(crand.Reader)
_, ok := sc.(*ScalarK256)
require.True(t, ok)
require.True(t, !sc.IsZero())
}
}
func TestScalarK256Hash(t *testing.T) {
var b [32]byte
k256 := K256()
sc := k256.Scalar.Hash(b[:])
s, ok := sc.(*ScalarK256)
require.True(t, ok)
expected, _ := new(big.Int).SetString("e5cb3500b809a8202de0834a805068bc21bde09bd6367815e7523a37adf8f52e", 16)
require.Equal(t, s.value.BigInt(), expected)
}
func TestScalarK256Zero(t *testing.T) {
k256 := K256()
sc := k256.Scalar.Zero()
require.True(t, sc.IsZero())
require.True(t, sc.IsEven())
}
func TestScalarK256One(t *testing.T) {
k256 := K256()
sc := k256.Scalar.One()
require.True(t, sc.IsOne())
require.True(t, sc.IsOdd())
}
func TestScalarK256New(t *testing.T) {
k256 := K256()
three := k256.Scalar.New(3)
require.True(t, three.IsOdd())
four := k256.Scalar.New(4)
require.True(t, four.IsEven())
neg1 := k256.Scalar.New(-1)
require.True(t, neg1.IsEven())
neg2 := k256.Scalar.New(-2)
require.True(t, neg2.IsOdd())
}
func TestScalarK256Square(t *testing.T) {
k256 := K256()
three := k256.Scalar.New(3)
nine := k256.Scalar.New(9)
require.Equal(t, three.Square().Cmp(nine), 0)
}
func TestScalarK256Cube(t *testing.T) {
k256 := K256()
three := k256.Scalar.New(3)
twentySeven := k256.Scalar.New(27)
require.Equal(t, three.Cube().Cmp(twentySeven), 0)
}
func TestScalarK256Double(t *testing.T) {
k256 := K256()
three := k256.Scalar.New(3)
six := k256.Scalar.New(6)
require.Equal(t, three.Double().Cmp(six), 0)
}
func TestScalarK256Neg(t *testing.T) {
k256 := K256()
one := k256.Scalar.One()
neg1 := k256.Scalar.New(-1)
require.Equal(t, one.Neg().Cmp(neg1), 0)
lotsOfThrees := k256.Scalar.New(333333)
expected := k256.Scalar.New(-333333)
require.Equal(t, lotsOfThrees.Neg().Cmp(expected), 0)
}
func TestScalarK256Invert(t *testing.T) {
k256 := K256()
nine := k256.Scalar.New(9)
actual, _ := nine.Invert()
sa, _ := actual.(*ScalarK256)
bn, _ := new(big.Int).SetString("8e38e38e38e38e38e38e38e38e38e38d842841d57dd303af6a9150f8e5737996", 16)
expected, err := k256.Scalar.SetBigInt(bn)
require.NoError(t, err)
require.Equal(t, sa.Cmp(expected), 0)
}
func TestScalarK256Sqrt(t *testing.T) {
k256 := K256()
nine := k256.Scalar.New(9)
actual, err := nine.Sqrt()
sa, _ := actual.(*ScalarK256)
expected := k256.Scalar.New(3)
require.NoError(t, err)
require.Equal(t, sa.Cmp(expected), 0)
}
func TestScalarK256Add(t *testing.T) {
k256 := K256()
nine := k256.Scalar.New(9)
six := k256.Scalar.New(6)
fifteen := nine.Add(six)
require.NotNil(t, fifteen)
expected := k256.Scalar.New(15)
require.Equal(t, expected.Cmp(fifteen), 0)
n := new(big.Int).Set(btcec.S256().N)
n.Sub(n, big.NewInt(3))
upper, err := k256.Scalar.SetBigInt(n)
require.NoError(t, err)
actual := upper.Add(nine)
require.NotNil(t, actual)
require.Equal(t, actual.Cmp(six), 0)
}
func TestScalarK256Sub(t *testing.T) {
k256 := K256()
nine := k256.Scalar.New(9)
six := k256.Scalar.New(6)
n := new(big.Int).Set(btcec.S256().N)
n.Sub(n, big.NewInt(3))
expected, err := k256.Scalar.SetBigInt(n)
require.NoError(t, err)
actual := six.Sub(nine)
require.Equal(t, expected.Cmp(actual), 0)
actual = nine.Sub(six)
require.Equal(t, actual.Cmp(k256.Scalar.New(3)), 0)
}
func TestScalarK256Mul(t *testing.T) {
k256 := K256()
nine := k256.Scalar.New(9)
six := k256.Scalar.New(6)
actual := nine.Mul(six)
require.Equal(t, actual.Cmp(k256.Scalar.New(54)), 0)
n := new(big.Int).Set(btcec.S256().N)
n.Sub(n, big.NewInt(1))
upper, err := k256.Scalar.SetBigInt(n)
require.NoError(t, err)
require.Equal(t, upper.Mul(upper).Cmp(k256.Scalar.New(1)), 0)
}
func TestScalarK256Div(t *testing.T) {
k256 := K256()
nine := k256.Scalar.New(9)
actual := nine.Div(nine)
require.Equal(t, actual.Cmp(k256.Scalar.New(1)), 0)
require.Equal(t, k256.Scalar.New(54).Div(nine).Cmp(k256.Scalar.New(6)), 0)
}
func TestScalarK256Serialize(t *testing.T) {
k256 := K256()
sc := k256.Scalar.New(255)
sequence := sc.Bytes()
require.Equal(t, len(sequence), 32)
require.Equal(t, sequence, []byte{0x00, 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, 0xff})
ret, err := k256.Scalar.SetBytes(sequence)
require.NoError(t, err)
require.Equal(t, ret.Cmp(sc), 0)
// Try 10 random values
for i := 0; i < 10; i++ {
sc = k256.Scalar.Random(crand.Reader)
sequence = sc.Bytes()
require.Equal(t, len(sequence), 32)
ret, err = k256.Scalar.SetBytes(sequence)
require.NoError(t, err)
require.Equal(t, ret.Cmp(sc), 0)
}
}
func TestScalarK256Nil(t *testing.T) {
k256 := K256()
one := k256.Scalar.New(1)
require.Nil(t, one.Add(nil))
require.Nil(t, one.Sub(nil))
require.Nil(t, one.Mul(nil))
require.Nil(t, one.Div(nil))
require.Nil(t, k256.Scalar.Random(nil))
require.Equal(t, one.Cmp(nil), -2)
_, err := k256.Scalar.SetBigInt(nil)
require.Error(t, err)
}
func TestPointK256Random(t *testing.T) {
curve := K256()
sc := curve.Point.Random(testRng())
s, ok := sc.(*PointK256)
require.True(t, ok)
expectedX, _ := new(big.Int).SetString("c6e18a1d7cf834462675b31581639a18e14fd0f73f8dfd5fe2993f88f6fbe008", 16)
expectedY, _ := new(big.Int).SetString("b65fab3243c5d07cef005d7fb335ebe8019efd954e95e68c86ef9b3bd7bccd36", 16)
require.Equal(t, s.X().BigInt(), expectedX)
require.Equal(t, s.Y().BigInt(), expectedY)
// Try 10 random values
for i := 0; i < 10; i++ {
sc := curve.Point.Random(crand.Reader)
_, ok := sc.(*PointK256)
require.True(t, ok)
require.True(t, !sc.IsIdentity())
}
}
func TestPointK256Hash(t *testing.T) {
var b [32]byte
curve := K256()
sc := curve.Point.Hash(b[:])
s, ok := sc.(*PointK256)
require.True(t, ok)
expectedX, _ := new(big.Int).SetString("95d0ad42f68ddb5a808469dd75fa866890dcc7d039844e0e2d58a6d25bd9a66b", 16)
expectedY, _ := new(big.Int).SetString("f37c564d05168dab4413caacdb8e3426143fc5fb24a470ccd8a51856c11d163c", 16)
require.Equal(t, s.X().BigInt(), expectedX)
require.Equal(t, s.Y().BigInt(), expectedY)
}
func TestPointK256Identity(t *testing.T) {
k256 := K256()
sc := k256.Point.Identity()
require.True(t, sc.IsIdentity())
require.Equal(t, sc.ToAffineCompressed(), []byte{2, 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})
}
func TestPointK256Generator(t *testing.T) {
curve := K256()
sc := curve.Point.Generator()
s, ok := sc.(*PointK256)
require.True(t, ok)
require.Equal(t, s.X().BigInt().Cmp(btcec.S256().Gx), 0)
require.Equal(t, s.Y().BigInt().Cmp(btcec.S256().Gy), 0)
}
func TestPointK256Set(t *testing.T) {
k256 := K256()
iden, err := k256.Point.Set(big.NewInt(0), big.NewInt(0))
require.NoError(t, err)
require.True(t, iden.IsIdentity())
_, err = k256.Point.Set(btcec.S256().Gx, btcec.S256().Gy)
require.NoError(t, err)
}
func TestPointK256Double(t *testing.T) {
curve := K256()
g := curve.Point.Generator()
g2 := g.Double()
require.True(t, g2.Equal(g.Mul(curve.Scalar.New(2))))
i := curve.Point.Identity()
require.True(t, i.Double().Equal(i))
gg := curve.Point.Generator().Add(curve.Point.Generator())
require.True(t, g2.Equal(gg))
}
func TestPointK256Neg(t *testing.T) {
k256 := K256()
g := k256.Point.Generator().Neg()
require.True(t, g.Neg().Equal(k256.Point.Generator()))
require.True(t, k256.Point.Identity().Neg().Equal(k256.Point.Identity()))
}
func TestPointK256Add(t *testing.T) {
curve := K256()
pt := curve.Point.Generator().(*PointK256)
pt1 := pt.Add(pt).(*PointK256)
pt2 := pt.Double().(*PointK256)
pt3 := pt.Mul(curve.Scalar.New(2)).(*PointK256)
require.True(t, pt1.Equal(pt2))
require.True(t, pt1.Equal(pt3))
require.True(t, pt.Add(pt).Equal(pt.Double()))
require.True(t, pt.Mul(curve.Scalar.New(3)).Equal(pt.Add(pt).Add(pt)))
}
func TestPointK256Sub(t *testing.T) {
curve := K256()
g := curve.Point.Generator()
pt := curve.Point.Generator().Mul(curve.Scalar.New(4))
require.True(t, pt.Sub(g).Sub(g).Sub(g).Equal(g))
require.True(t, pt.Sub(g).Sub(g).Sub(g).Sub(g).IsIdentity())
}
func TestPointK256Mul(t *testing.T) {
curve := K256()
g := curve.Point.Generator()
pt := curve.Point.Generator().Mul(curve.Scalar.New(4))
require.True(t, g.Double().Double().Equal(pt))
}
func TestPointK256Serialize(t *testing.T) {
curve := K256()
ss := curve.Scalar.Random(testRng())
g := curve.Point.Generator()
ppt := g.Mul(ss).(*PointK256)
require.Equal(t, ppt.ToAffineCompressed(), []byte{0x2, 0x1b, 0xa7, 0x7e, 0x98, 0xd6, 0xd8, 0x49, 0x45, 0xa4, 0x75, 0xd8, 0x6, 0xc0, 0x94, 0x5b, 0x8c, 0xf0, 0x5b, 0x8a, 0xb2, 0x76, 0xbb, 0x9f, 0x6e, 0x52, 0x9a, 0x11, 0x9c, 0x79, 0xdd, 0xf6, 0x5a})
require.Equal(t, ppt.ToAffineUncompressed(), []byte{0x4, 0x1b, 0xa7, 0x7e, 0x98, 0xd6, 0xd8, 0x49, 0x45, 0xa4, 0x75, 0xd8, 0x6, 0xc0, 0x94, 0x5b, 0x8c, 0xf0, 0x5b, 0x8a, 0xb2, 0x76, 0xbb, 0x9f, 0x6e, 0x52, 0x9a, 0x11, 0x9c, 0x79, 0xdd, 0xf6, 0x5a, 0xb2, 0x96, 0x7c, 0x59, 0x4, 0xeb, 0x9a, 0xaa, 0xa9, 0x1d, 0x4d, 0xd0, 0x2d, 0xc6, 0x37, 0xee, 0x4a, 0x95, 0x51, 0x60, 0xab, 0xab, 0xf7, 0xdb, 0x30, 0x7d, 0x7d, 0x0, 0x68, 0x6c, 0xcf, 0xf6})
retP, err := ppt.FromAffineCompressed(ppt.ToAffineCompressed())
require.NoError(t, err)
require.True(t, ppt.Equal(retP))
retP, err = ppt.FromAffineUncompressed(ppt.ToAffineUncompressed())
require.NoError(t, err)
require.True(t, ppt.Equal(retP))
// smoke test
for i := 0; i < 25; i++ {
s := curve.Scalar.Random(crand.Reader)
pt := g.Mul(s)
cmprs := pt.ToAffineCompressed()
require.Equal(t, len(cmprs), 33)
retC, err := pt.FromAffineCompressed(cmprs)
require.NoError(t, err)
require.True(t, pt.Equal(retC))
un := pt.ToAffineUncompressed()
require.Equal(t, len(un), 65)
retU, err := pt.FromAffineUncompressed(un)
require.NoError(t, err)
require.True(t, pt.Equal(retU))
}
}
func TestPointK256Nil(t *testing.T) {
k256 := K256()
one := k256.Point.Generator()
require.Nil(t, one.Add(nil))
require.Nil(t, one.Sub(nil))
require.Nil(t, one.Mul(nil))
require.Nil(t, k256.Scalar.Random(nil))
require.False(t, one.Equal(nil))
_, err := k256.Scalar.SetBigInt(nil)
require.Error(t, err)
}
func TestPointK256SumOfProducts(t *testing.T) {
lhs := new(PointK256).Generator().Mul(new(ScalarK256).New(50))
points := make([]Point, 5)
for i := range points {
points[i] = new(PointK256).Generator()
}
scalars := []Scalar{
new(ScalarK256).New(8),
new(ScalarK256).New(9),
new(ScalarK256).New(10),
new(ScalarK256).New(11),
new(ScalarK256).New(12),
}
rhs := lhs.SumOfProducts(points, scalars)
require.NotNil(t, rhs)
require.True(t, lhs.Equal(rhs))
for j := 0; j < 25; j++ {
lhs = lhs.Identity()
for i := range points {
points[i] = new(PointK256).Random(crand.Reader)
scalars[i] = new(ScalarK256).Random(crand.Reader)
lhs = lhs.Add(points[i].Mul(scalars[i]))
}
rhs = lhs.SumOfProducts(points, scalars)
require.NotNil(t, rhs)
require.True(t, lhs.Equal(rhs))
}
}
+48
View File
@@ -0,0 +1,48 @@
package bls12381
import (
"math/bits"
"github.com/onsonr/sonr/crypto/core/curves/native"
)
var fqModulusBytes = [native.FieldBytes]byte{0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4, 0xbd, 0x53, 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29, 0x53, 0xa7, 0xed, 0x73}
const (
// The BLS parameter x for BLS12-381 is -0xd201000000010000
paramX = uint64(0xd201000000010000)
Limbs = 6
FieldBytes = 48
WideFieldBytes = 96
DoubleWideFieldBytes = 192
)
// mac Multiply and Accumulate - compute a + (b * c) + d, return the result and new carry
func mac(a, b, c, d uint64) (uint64, uint64) {
hi, lo := bits.Mul64(b, c)
carry2, carry := bits.Add64(a, d, 0)
hi, _ = bits.Add64(hi, 0, carry)
lo, carry = bits.Add64(lo, carry2, 0)
hi, _ = bits.Add64(hi, 0, carry)
return lo, hi
}
// adc Add w/Carry
func adc(x, y, carry uint64) (uint64, uint64) {
sum := x + y + carry
// The sum will overflow if both top bits are set (x & y) or if one of them
// is (x | y), and a carry from the lower place happened. If such a carry
// happens, the top bit will be 1 + 0 + 1 = 0 (&^ sum).
carryOut := ((x & y) | ((x | y) &^ sum)) >> 63
carryOut |= ((x & carry) | ((x | carry) &^ sum)) >> 63
carryOut |= ((y & carry) | ((y | carry) &^ sum)) >> 63
return sum, carryOut
}
// sbb Subtract with borrow
func sbb(x, y, borrow uint64) (uint64, uint64) {
diff := x - (y + borrow)
borrowOut := ((^x & y) | (^(x ^ y) & diff)) >> 63
return diff, borrowOut
}
+697
View File
@@ -0,0 +1,697 @@
package bls12381
import (
"encoding/binary"
"fmt"
"io"
"math/big"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/internal"
)
// fp field element mod p
type fp [Limbs]uint64
var (
modulus = fp{
0xb9feffffffffaaab,
0x1eabfffeb153ffff,
0x6730d2a0f6b0f624,
0x64774b84f38512bf,
0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a,
}
halfModulus = fp{
0xdcff_7fff_ffff_d556,
0x0f55_ffff_58a9_ffff,
0xb398_6950_7b58_7b12,
0xb23b_a5c2_79c2_895f,
0x258d_d3db_21a5_d66b,
0x0d00_88f5_1cbf_f34d,
}
// 2^256 mod p
r = fp{
0x760900000002fffd,
0xebf4000bc40c0002,
0x5f48985753c758ba,
0x77ce585370525745,
0x5c071a97a256ec6d,
0x15f65ec3fa80e493,
}
// 2^512 mod p
r2 = fp{
0xf4df1f341c341746,
0x0a76e6a609d104f1,
0x8de5476c4c95b6d5,
0x67eb88a9939d83c0,
0x9a793e85b519952d,
0x11988fe592cae3aa,
}
// 2^768 mod p
r3 = fp{
0xed48ac6bd94ca1e0,
0x315f831e03a7adf8,
0x9a53352a615e29dd,
0x34c04e5e921e1761,
0x2512d43565724728,
0x0aa6346091755d4d,
}
biModulus = new(big.Int).SetBytes([]byte{
0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab,
},
)
)
// inv = -(p^{-1} mod 2^64) mod 2^64
const (
inv = 0x89f3_fffc_fffc_fffd
hashBytes = 64
)
// IsZero returns 1 if fp == 0, 0 otherwise
func (f *fp) IsZero() int {
t := f[0]
t |= f[1]
t |= f[2]
t |= f[3]
t |= f[4]
t |= f[5]
return int(((int64(t) | int64(-t)) >> 63) + 1)
}
// IsNonZero returns 1 if fp != 0, 0 otherwise
func (f *fp) IsNonZero() int {
t := f[0]
t |= f[1]
t |= f[2]
t |= f[3]
t |= f[4]
t |= f[5]
return int(-((int64(t) | int64(-t)) >> 63))
}
// IsOne returns 1 if fp == 1, 0 otherwise
func (f *fp) IsOne() int {
return f.Equal(&r)
}
// Cmp returns -1 if f < rhs
// 0 if f == rhs
// 1 if f > rhs
func (f *fp) Cmp(rhs *fp) int {
gt := uint64(0)
lt := uint64(0)
for i := 5; i >= 0; i-- {
// convert to two 64-bit numbers where
// the leading bits are zeros and hold no meaning
// so rhs - f actually means gt
// and f - rhs actually means lt.
rhsH := rhs[i] >> 32
rhsL := rhs[i] & 0xffffffff
lhsH := f[i] >> 32
lhsL := f[i] & 0xffffffff
// Check the leading bit
// if negative then f > rhs
// if positive then f < rhs
gt |= (rhsH - lhsH) >> 32 & 1 &^ lt
lt |= (lhsH - rhsH) >> 32 & 1 &^ gt
gt |= (rhsL - lhsL) >> 32 & 1 &^ lt
lt |= (lhsL - rhsL) >> 32 & 1 &^ gt
}
// Make the result -1 for <, 0 for =, 1 for >
return int(gt) - int(lt)
}
// Equal returns 1 if fp == rhs, 0 otherwise
func (f *fp) Equal(rhs *fp) int {
t := f[0] ^ rhs[0]
t |= f[1] ^ rhs[1]
t |= f[2] ^ rhs[2]
t |= f[3] ^ rhs[3]
t |= f[4] ^ rhs[4]
t |= f[5] ^ rhs[5]
return int(((int64(t) | int64(-t)) >> 63) + 1)
}
// LexicographicallyLargest returns 1 if
// this element is strictly lexicographically larger than its negation
// 0 otherwise
func (f *fp) LexicographicallyLargest() int {
var ff fp
ff.fromMontgomery(f)
_, borrow := sbb(ff[0], halfModulus[0], 0)
_, borrow = sbb(ff[1], halfModulus[1], borrow)
_, borrow = sbb(ff[2], halfModulus[2], borrow)
_, borrow = sbb(ff[3], halfModulus[3], borrow)
_, borrow = sbb(ff[4], halfModulus[4], borrow)
_, borrow = sbb(ff[5], halfModulus[5], borrow)
return (int(borrow) - 1) & 1
}
// Sgn0 returns the lowest bit value
func (f *fp) Sgn0() int {
t := new(fp).fromMontgomery(f)
return int(t[0] & 1)
}
// SetOne fp = r
func (f *fp) SetOne() *fp {
f[0] = r[0]
f[1] = r[1]
f[2] = r[2]
f[3] = r[3]
f[4] = r[4]
f[5] = r[5]
return f
}
// SetZero fp = 0
func (f *fp) SetZero() *fp {
f[0] = 0
f[1] = 0
f[2] = 0
f[3] = 0
f[4] = 0
f[5] = 0
return f
}
// SetUint64 fp = rhs
func (f *fp) SetUint64(rhs uint64) *fp {
f[0] = rhs
f[1] = 0
f[2] = 0
f[3] = 0
f[4] = 0
f[5] = 0
return f.toMontgomery(f)
}
// Random generates a random field element
func (f *fp) Random(reader io.Reader) (*fp, error) {
var t [WideFieldBytes]byte
n, err := reader.Read(t[:])
if err != nil {
return nil, err
}
if n != WideFieldBytes {
return nil, fmt.Errorf("can only read %d when %d are needed", n, WideFieldBytes)
}
return f.Hash(t[:]), nil
}
// Hash converts the byte sequence into a field element
func (f *fp) Hash(input []byte) *fp {
dst := []byte("BLS12381_XMD:SHA-256_SSWU_RO_")
xmd := native.ExpandMsgXmd(native.EllipticPointHasherSha256(), input, dst, hashBytes)
var t [WideFieldBytes]byte
copy(t[:hashBytes], internal.ReverseScalarBytes(xmd))
return f.SetBytesWide(&t)
}
// toMontgomery converts this field to montgomery form
func (f *fp) toMontgomery(a *fp) *fp {
// arg.R^0 * R^2 / R = arg.R
return f.Mul(a, &r2)
}
// fromMontgomery converts this field from montgomery form
func (f *fp) fromMontgomery(a *fp) *fp {
// Mul by 1 is division by 2^256 mod q
// out.Mul(arg, &[native.FieldLimbs]uint64{1, 0, 0, 0})
return f.montReduce(&[Limbs * 2]uint64{a[0], a[1], a[2], a[3], a[4], a[5], 0, 0, 0, 0, 0, 0})
}
// Neg performs modular negation
func (f *fp) Neg(a *fp) *fp {
// Subtract `arg` from `modulus`. Ignore final borrow
// since it can't underflow.
var t [Limbs]uint64
var borrow uint64
t[0], borrow = sbb(modulus[0], a[0], 0)
t[1], borrow = sbb(modulus[1], a[1], borrow)
t[2], borrow = sbb(modulus[2], a[2], borrow)
t[3], borrow = sbb(modulus[3], a[3], borrow)
t[4], borrow = sbb(modulus[4], a[4], borrow)
t[5], _ = sbb(modulus[5], a[5], borrow)
// t could be `modulus` if `arg`=0. Set mask=0 if self=0
// and 0xff..ff if `arg`!=0
mask := a[0] | a[1] | a[2] | a[3] | a[4] | a[5]
mask = -((mask | -mask) >> 63)
f[0] = t[0] & mask
f[1] = t[1] & mask
f[2] = t[2] & mask
f[3] = t[3] & mask
f[4] = t[4] & mask
f[5] = t[5] & mask
return f
}
// Square performs modular square
func (f *fp) Square(a *fp) *fp {
var r [2 * Limbs]uint64
var carry uint64
r[1], carry = mac(0, a[0], a[1], 0)
r[2], carry = mac(0, a[0], a[2], carry)
r[3], carry = mac(0, a[0], a[3], carry)
r[4], carry = mac(0, a[0], a[4], carry)
r[5], r[6] = mac(0, a[0], a[5], carry)
r[3], carry = mac(r[3], a[1], a[2], 0)
r[4], carry = mac(r[4], a[1], a[3], carry)
r[5], carry = mac(r[5], a[1], a[4], carry)
r[6], r[7] = mac(r[6], a[1], a[5], carry)
r[5], carry = mac(r[5], a[2], a[3], 0)
r[6], carry = mac(r[6], a[2], a[4], carry)
r[7], r[8] = mac(r[7], a[2], a[5], carry)
r[7], carry = mac(r[7], a[3], a[4], 0)
r[8], r[9] = mac(r[8], a[3], a[5], carry)
r[9], r[10] = mac(r[9], a[4], a[5], 0)
r[11] = r[10] >> 63
r[10] = (r[10] << 1) | r[9]>>63
r[9] = (r[9] << 1) | r[8]>>63
r[8] = (r[8] << 1) | r[7]>>63
r[7] = (r[7] << 1) | r[6]>>63
r[6] = (r[6] << 1) | r[5]>>63
r[5] = (r[5] << 1) | r[4]>>63
r[4] = (r[4] << 1) | r[3]>>63
r[3] = (r[3] << 1) | r[2]>>63
r[2] = (r[2] << 1) | r[1]>>63
r[1] = r[1] << 1
r[0], carry = mac(0, a[0], a[0], 0)
r[1], carry = adc(0, r[1], carry)
r[2], carry = mac(r[2], a[1], a[1], carry)
r[3], carry = adc(0, r[3], carry)
r[4], carry = mac(r[4], a[2], a[2], carry)
r[5], carry = adc(0, r[5], carry)
r[6], carry = mac(r[6], a[3], a[3], carry)
r[7], carry = adc(0, r[7], carry)
r[8], carry = mac(r[8], a[4], a[4], carry)
r[9], carry = adc(0, r[9], carry)
r[10], carry = mac(r[10], a[5], a[5], carry)
r[11], _ = adc(0, r[11], carry)
return f.montReduce(&r)
}
// Double this element
func (f *fp) Double(a *fp) *fp {
return f.Add(a, a)
}
// Mul performs modular multiplication
func (f *fp) Mul(arg1, arg2 *fp) *fp {
// Schoolbook multiplication
var r [2 * Limbs]uint64
var carry uint64
r[0], carry = mac(0, arg1[0], arg2[0], 0)
r[1], carry = mac(0, arg1[0], arg2[1], carry)
r[2], carry = mac(0, arg1[0], arg2[2], carry)
r[3], carry = mac(0, arg1[0], arg2[3], carry)
r[4], carry = mac(0, arg1[0], arg2[4], carry)
r[5], r[6] = mac(0, arg1[0], arg2[5], carry)
r[1], carry = mac(r[1], arg1[1], arg2[0], 0)
r[2], carry = mac(r[2], arg1[1], arg2[1], carry)
r[3], carry = mac(r[3], arg1[1], arg2[2], carry)
r[4], carry = mac(r[4], arg1[1], arg2[3], carry)
r[5], carry = mac(r[5], arg1[1], arg2[4], carry)
r[6], r[7] = mac(r[6], arg1[1], arg2[5], carry)
r[2], carry = mac(r[2], arg1[2], arg2[0], 0)
r[3], carry = mac(r[3], arg1[2], arg2[1], carry)
r[4], carry = mac(r[4], arg1[2], arg2[2], carry)
r[5], carry = mac(r[5], arg1[2], arg2[3], carry)
r[6], carry = mac(r[6], arg1[2], arg2[4], carry)
r[7], r[8] = mac(r[7], arg1[2], arg2[5], carry)
r[3], carry = mac(r[3], arg1[3], arg2[0], 0)
r[4], carry = mac(r[4], arg1[3], arg2[1], carry)
r[5], carry = mac(r[5], arg1[3], arg2[2], carry)
r[6], carry = mac(r[6], arg1[3], arg2[3], carry)
r[7], carry = mac(r[7], arg1[3], arg2[4], carry)
r[8], r[9] = mac(r[8], arg1[3], arg2[5], carry)
r[4], carry = mac(r[4], arg1[4], arg2[0], 0)
r[5], carry = mac(r[5], arg1[4], arg2[1], carry)
r[6], carry = mac(r[6], arg1[4], arg2[2], carry)
r[7], carry = mac(r[7], arg1[4], arg2[3], carry)
r[8], carry = mac(r[8], arg1[4], arg2[4], carry)
r[9], r[10] = mac(r[9], arg1[4], arg2[5], carry)
r[5], carry = mac(r[5], arg1[5], arg2[0], 0)
r[6], carry = mac(r[6], arg1[5], arg2[1], carry)
r[7], carry = mac(r[7], arg1[5], arg2[2], carry)
r[8], carry = mac(r[8], arg1[5], arg2[3], carry)
r[9], carry = mac(r[9], arg1[5], arg2[4], carry)
r[10], r[11] = mac(r[10], arg1[5], arg2[5], carry)
return f.montReduce(&r)
}
// MulBy3b returns arg * 12 or 3 * b
func (f *fp) MulBy3b(arg *fp) *fp {
var a, t fp
a.Double(arg) // 2
t.Double(&a) // 4
a.Double(&t) // 8
a.Add(&a, &t) // 12
return f.Set(&a)
}
// Add performs modular addition
func (f *fp) Add(arg1, arg2 *fp) *fp {
var t fp
var carry uint64
t[0], carry = adc(arg1[0], arg2[0], 0)
t[1], carry = adc(arg1[1], arg2[1], carry)
t[2], carry = adc(arg1[2], arg2[2], carry)
t[3], carry = adc(arg1[3], arg2[3], carry)
t[4], carry = adc(arg1[4], arg2[4], carry)
t[5], _ = adc(arg1[5], arg2[5], carry)
// Subtract the modulus to ensure the value
// is smaller.
return f.Sub(&t, &modulus)
}
// Sub performs modular subtraction
func (f *fp) Sub(arg1, arg2 *fp) *fp {
d0, borrow := sbb(arg1[0], arg2[0], 0)
d1, borrow := sbb(arg1[1], arg2[1], borrow)
d2, borrow := sbb(arg1[2], arg2[2], borrow)
d3, borrow := sbb(arg1[3], arg2[3], borrow)
d4, borrow := sbb(arg1[4], arg2[4], borrow)
d5, borrow := sbb(arg1[5], arg2[5], borrow)
// If underflow occurred on the final limb, borrow 0xff...ff, otherwise
// borrow = 0x00...00. Conditionally mask to add the modulus
borrow = -borrow
d0, carry := adc(d0, modulus[0]&borrow, 0)
d1, carry = adc(d1, modulus[1]&borrow, carry)
d2, carry = adc(d2, modulus[2]&borrow, carry)
d3, carry = adc(d3, modulus[3]&borrow, carry)
d4, carry = adc(d4, modulus[4]&borrow, carry)
d5, _ = adc(d5, modulus[5]&borrow, carry)
f[0] = d0
f[1] = d1
f[2] = d2
f[3] = d3
f[4] = d4
f[5] = d5
return f
}
// Sqrt performs modular square root
func (f *fp) Sqrt(a *fp) (*fp, int) {
// Shank's method, as p = 3 (mod 4). This means
// exponentiate by (p+1)/4. This only works for elements
// that are actually quadratic residue,
// so check the result at the end.
var c, z fp
z.pow(a, &fp{
0xee7fbfffffffeaab,
0x07aaffffac54ffff,
0xd9cc34a83dac3d89,
0xd91dd2e13ce144af,
0x92c6e9ed90d2eb35,
0x0680447a8e5ff9a6,
})
c.Square(&z)
wasSquare := c.Equal(a)
f.CMove(f, &z, wasSquare)
return f, wasSquare
}
// Invert performs modular inverse
func (f *fp) Invert(a *fp) (*fp, int) {
// Exponentiate by p - 2
t := &fp{}
t.pow(a, &fp{
0xb9feffffffffaaa9,
0x1eabfffeb153ffff,
0x6730d2a0f6b0f624,
0x64774b84f38512bf,
0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a,
})
wasInverted := a.IsNonZero()
f.CMove(a, t, wasInverted)
return f, wasInverted
}
// SetBytes converts a little endian byte array into a field element
// return 0 if the bytes are not in the field, 1 if they are
func (f *fp) SetBytes(arg *[FieldBytes]byte) (*fp, int) {
var borrow uint64
t := &fp{}
t[0] = binary.LittleEndian.Uint64(arg[:8])
t[1] = binary.LittleEndian.Uint64(arg[8:16])
t[2] = binary.LittleEndian.Uint64(arg[16:24])
t[3] = binary.LittleEndian.Uint64(arg[24:32])
t[4] = binary.LittleEndian.Uint64(arg[32:40])
t[5] = binary.LittleEndian.Uint64(arg[40:])
// Try to subtract the modulus
_, borrow = sbb(t[0], modulus[0], 0)
_, borrow = sbb(t[1], modulus[1], borrow)
_, borrow = sbb(t[2], modulus[2], borrow)
_, borrow = sbb(t[3], modulus[3], borrow)
_, borrow = sbb(t[4], modulus[4], borrow)
_, borrow = sbb(t[5], modulus[5], borrow)
// If the element is smaller than modulus then the
// subtraction will underflow, producing a borrow value
// of 1. Otherwise, it'll be zero.
mask := int(borrow)
return f.CMove(f, t.toMontgomery(t), mask), mask
}
// SetBytesWide takes 96 bytes as input and treats them as a 512-bit number.
// Attributed to https://github.com/zcash/pasta_curves/blob/main/src/fields/Fp.rs#L255
// We reduce an arbitrary 768-bit number by decomposing it into two 384-bit digits
// with the higher bits multiplied by 2^384. Thus, we perform two reductions
//
// 1. the lower bits are multiplied by r^2, as normal
// 2. the upper bits are multiplied by r^2 * 2^384 = r^3
//
// and computing their sum in the field. It remains to see that arbitrary 384-bit
// numbers can be placed into Montgomery form safely using the reduction. The
// reduction works so long as the product is less than r=2^384 multiplied by
// the modulus. This holds because for any `c` smaller than the modulus, we have
// that (2^384 - 1)*c is an acceptable product for the reduction. Therefore, the
// reduction always works so long as `c` is in the field; in this case it is either the
// constant `r2` or `r3`.
func (f *fp) SetBytesWide(a *[WideFieldBytes]byte) *fp {
d0 := &fp{
binary.LittleEndian.Uint64(a[:8]),
binary.LittleEndian.Uint64(a[8:16]),
binary.LittleEndian.Uint64(a[16:24]),
binary.LittleEndian.Uint64(a[24:32]),
binary.LittleEndian.Uint64(a[32:40]),
binary.LittleEndian.Uint64(a[40:48]),
}
d1 := &fp{
binary.LittleEndian.Uint64(a[48:56]),
binary.LittleEndian.Uint64(a[56:64]),
binary.LittleEndian.Uint64(a[64:72]),
binary.LittleEndian.Uint64(a[72:80]),
binary.LittleEndian.Uint64(a[80:88]),
binary.LittleEndian.Uint64(a[88:96]),
}
// d0*r2 + d1*r3
d0.Mul(d0, &r2)
d1.Mul(d1, &r3)
return f.Add(d0, d1)
}
// SetBigInt initializes an element from big.Int
// The value is reduced by the modulus
func (f *fp) SetBigInt(bi *big.Int) *fp {
var buffer [FieldBytes]byte
t := new(big.Int).Set(bi)
t.Mod(t, biModulus)
t.FillBytes(buffer[:])
copy(buffer[:], internal.ReverseScalarBytes(buffer[:]))
_, _ = f.SetBytes(&buffer)
return f
}
// Set copies a into fp
func (f *fp) Set(a *fp) *fp {
f[0] = a[0]
f[1] = a[1]
f[2] = a[2]
f[3] = a[3]
f[4] = a[4]
f[5] = a[5]
return f
}
// SetLimbs converts an array into a field element
// by converting to montgomery form
func (f *fp) SetLimbs(a *[Limbs]uint64) *fp {
return f.toMontgomery((*fp)(a))
}
// SetRaw converts a raw array into a field element
// Assumes input is already in montgomery form
func (f *fp) SetRaw(a *[Limbs]uint64) *fp {
f[0] = a[0]
f[1] = a[1]
f[2] = a[2]
f[3] = a[3]
f[4] = a[4]
f[5] = a[5]
return f
}
// Bytes converts a field element to a little endian byte array
func (f *fp) Bytes() [FieldBytes]byte {
var out [FieldBytes]byte
t := new(fp).fromMontgomery(f)
binary.LittleEndian.PutUint64(out[:8], t[0])
binary.LittleEndian.PutUint64(out[8:16], t[1])
binary.LittleEndian.PutUint64(out[16:24], t[2])
binary.LittleEndian.PutUint64(out[24:32], t[3])
binary.LittleEndian.PutUint64(out[32:40], t[4])
binary.LittleEndian.PutUint64(out[40:], t[5])
return out
}
// BigInt converts this element into the big.Int struct
func (f *fp) BigInt() *big.Int {
buffer := f.Bytes()
return new(big.Int).SetBytes(internal.ReverseScalarBytes(buffer[:]))
}
// Raw converts this element into the a [FieldLimbs]uint64
func (f *fp) Raw() [Limbs]uint64 {
t := new(fp).fromMontgomery(f)
return *t
}
// CMove performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f *fp) CMove(arg1, arg2 *fp, choice int) *fp {
mask := uint64(-choice)
f[0] = arg1[0] ^ ((arg1[0] ^ arg2[0]) & mask)
f[1] = arg1[1] ^ ((arg1[1] ^ arg2[1]) & mask)
f[2] = arg1[2] ^ ((arg1[2] ^ arg2[2]) & mask)
f[3] = arg1[3] ^ ((arg1[3] ^ arg2[3]) & mask)
f[4] = arg1[4] ^ ((arg1[4] ^ arg2[4]) & mask)
f[5] = arg1[5] ^ ((arg1[5] ^ arg2[5]) & mask)
return f
}
// CNeg conditionally negates a if choice == 1
func (f *fp) CNeg(a *fp, choice int) *fp {
var t fp
t.Neg(a)
return f.CMove(f, &t, choice)
}
// Exp raises base^exp.
func (f *fp) Exp(base, exp *fp) *fp {
e := (&fp{}).fromMontgomery(exp)
return f.pow(base, e)
}
func (f *fp) pow(base, e *fp) *fp {
var tmp, res fp
res.SetOne()
for i := len(e) - 1; i >= 0; i-- {
for j := 63; j >= 0; j-- {
res.Square(&res)
tmp.Mul(&res, base)
res.CMove(&res, &tmp, int(e[i]>>j)&1)
}
}
f[0] = res[0]
f[1] = res[1]
f[2] = res[2]
f[3] = res[3]
f[4] = res[4]
f[5] = res[5]
return f
}
// montReduce performs the montgomery reduction
func (f *fp) montReduce(r *[2 * Limbs]uint64) *fp {
// Taken from Algorithm 14.32 in Handbook of Applied Cryptography
var r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, carry, k uint64
var rr fp
k = r[0] * inv
_, carry = mac(r[0], k, modulus[0], 0)
r1, carry = mac(r[1], k, modulus[1], carry)
r2, carry = mac(r[2], k, modulus[2], carry)
r3, carry = mac(r[3], k, modulus[3], carry)
r4, carry = mac(r[4], k, modulus[4], carry)
r5, carry = mac(r[5], k, modulus[5], carry)
r6, r7 = adc(r[6], 0, carry)
k = r1 * inv
_, carry = mac(r1, k, modulus[0], 0)
r2, carry = mac(r2, k, modulus[1], carry)
r3, carry = mac(r3, k, modulus[2], carry)
r4, carry = mac(r4, k, modulus[3], carry)
r5, carry = mac(r5, k, modulus[4], carry)
r6, carry = mac(r6, k, modulus[5], carry)
r7, r8 = adc(r7, r[7], carry)
k = r2 * inv
_, carry = mac(r2, k, modulus[0], 0)
r3, carry = mac(r3, k, modulus[1], carry)
r4, carry = mac(r4, k, modulus[2], carry)
r5, carry = mac(r5, k, modulus[3], carry)
r6, carry = mac(r6, k, modulus[4], carry)
r7, carry = mac(r7, k, modulus[5], carry)
r8, r9 = adc(r8, r[8], carry)
k = r3 * inv
_, carry = mac(r3, k, modulus[0], 0)
r4, carry = mac(r4, k, modulus[1], carry)
r5, carry = mac(r5, k, modulus[2], carry)
r6, carry = mac(r6, k, modulus[3], carry)
r7, carry = mac(r7, k, modulus[4], carry)
r8, carry = mac(r8, k, modulus[5], carry)
r9, r10 = adc(r9, r[9], carry)
k = r4 * inv
_, carry = mac(r4, k, modulus[0], 0)
r5, carry = mac(r5, k, modulus[1], carry)
r6, carry = mac(r6, k, modulus[2], carry)
r7, carry = mac(r7, k, modulus[3], carry)
r8, carry = mac(r8, k, modulus[4], carry)
r9, carry = mac(r9, k, modulus[5], carry)
r10, r11 = adc(r10, r[10], carry)
k = r5 * inv
_, carry = mac(r5, k, modulus[0], 0)
rr[0], carry = mac(r6, k, modulus[1], carry)
rr[1], carry = mac(r7, k, modulus[2], carry)
rr[2], carry = mac(r8, k, modulus[3], carry)
rr[3], carry = mac(r9, k, modulus[4], carry)
rr[4], carry = mac(r10, k, modulus[5], carry)
rr[5], _ = adc(r11, r[11], carry)
return f.Sub(&rr, &modulus)
}
+231
View File
@@ -0,0 +1,231 @@
package bls12381
import "io"
// fp12 represents an element a + b w of fp^12 = fp^6 / w^2 - v.
type fp12 struct {
A, B fp6
}
// SetFp creates an element from a lower field
func (f *fp12) SetFp(a *fp) *fp12 {
f.A.SetFp(a)
f.B.SetZero()
return f
}
// SetFp2 creates an element from a lower field
func (f *fp12) SetFp2(a *fp2) *fp12 {
f.A.SetFp2(a)
f.B.SetZero()
return f
}
// SetFp6 creates an element from a lower field
func (f *fp12) SetFp6(a *fp6) *fp12 {
f.A.Set(a)
f.B.SetZero()
return f
}
// Set copies the value `a`
func (f *fp12) Set(a *fp12) *fp12 {
f.A.Set(&a.A)
f.B.Set(&a.B)
return f
}
// SetZero fp6 to zero
func (f *fp12) SetZero() *fp12 {
f.A.SetZero()
f.B.SetZero()
return f
}
// SetOne fp6 to multiplicative identity element
func (f *fp12) SetOne() *fp12 {
f.A.SetOne()
f.B.SetZero()
return f
}
// Random generates a random field element
func (f *fp12) Random(reader io.Reader) (*fp12, error) {
a, err := new(fp6).Random(reader)
if err != nil {
return nil, err
}
b, err := new(fp6).Random(reader)
if err != nil {
return nil, err
}
f.A.Set(a)
f.B.Set(b)
return f, nil
}
// Square computes arg^2
func (f *fp12) Square(arg *fp12) *fp12 {
var ab, apb, aTick, bTick, t fp6
ab.Mul(&arg.A, &arg.B)
apb.Add(&arg.A, &arg.B)
aTick.MulByNonResidue(&arg.B)
aTick.Add(&aTick, &arg.A)
aTick.Mul(&aTick, &apb)
aTick.Sub(&aTick, &ab)
t.MulByNonResidue(&ab)
aTick.Sub(&aTick, &t)
bTick.Double(&ab)
f.A.Set(&aTick)
f.B.Set(&bTick)
return f
}
// Invert computes this element's field inversion
func (f *fp12) Invert(arg *fp12) (*fp12, int) {
var a, b, t fp6
a.Square(&arg.A)
b.Square(&arg.B)
b.MulByNonResidue(&b)
a.Sub(&a, &b)
_, wasInverted := t.Invert(&a)
a.Mul(&arg.A, &t)
t.Neg(&t)
b.Mul(&arg.B, &t)
f.A.CMove(&f.A, &a, wasInverted)
f.B.CMove(&f.B, &b, wasInverted)
return f, wasInverted
}
// Add computes arg1+arg2
func (f *fp12) Add(arg1, arg2 *fp12) *fp12 {
f.A.Add(&arg1.A, &arg2.A)
f.B.Add(&arg1.B, &arg2.B)
return f
}
// Sub computes arg1-arg2
func (f *fp12) Sub(arg1, arg2 *fp12) *fp12 {
f.A.Sub(&arg1.A, &arg2.A)
f.B.Sub(&arg1.B, &arg2.B)
return f
}
// Mul computes arg1*arg2
func (f *fp12) Mul(arg1, arg2 *fp12) *fp12 {
var aa, bb, a2b2, a, b fp6
aa.Mul(&arg1.A, &arg2.A)
bb.Mul(&arg1.B, &arg2.B)
a2b2.Add(&arg2.A, &arg2.B)
b.Add(&arg1.A, &arg1.B)
b.Mul(&b, &a2b2)
b.Sub(&b, &aa)
b.Sub(&b, &bb)
a.MulByNonResidue(&bb)
a.Add(&a, &aa)
f.A.Set(&a)
f.B.Set(&b)
return f
}
// Neg computes the field negation
func (f *fp12) Neg(arg *fp12) *fp12 {
f.A.Neg(&arg.A)
f.B.Neg(&arg.B)
return f
}
// MulByABD computes arg * a * b * c
func (f *fp12) MulByABD(arg *fp12, a, b, d *fp2) *fp12 {
var aa, bb, aTick, bTick fp6
var bd fp2
aa.MulByAB(&arg.A, a, b)
bb.MulByB(&arg.B, d)
bd.Add(b, d)
bTick.Add(&arg.A, &arg.B)
bTick.MulByAB(&bTick, a, &bd)
bTick.Sub(&bTick, &aa)
bTick.Sub(&bTick, &bb)
aTick.MulByNonResidue(&bb)
aTick.Add(&aTick, &aa)
f.A.Set(&aTick)
f.B.Set(&bTick)
return f
}
// Conjugate computes the field conjugation
func (f *fp12) Conjugate(arg *fp12) *fp12 {
f.A.Set(&arg.A)
f.B.Neg(&arg.B)
return f
}
// FrobeniusMap raises this element to p.
func (f *fp12) FrobeniusMap(arg *fp12) *fp12 {
var a, b, up1epm1div6 fp6
// (u + 1)^((p - 1) / 6)
up1epm1div6.A = fp2{
A: fp{
0x07089552b319d465,
0xc6695f92b50a8313,
0x97e83cccd117228f,
0xa35baecab2dc29ee,
0x1ce393ea5daace4d,
0x08f2220fb0fb66eb,
},
B: fp{
0xb2f66aad4ce5d646,
0x5842a06bfc497cec,
0xcf4895d42599d394,
0xc11b9cba40a8e8d0,
0x2e3813cbe5a0de89,
0x110eefda88847faf,
},
}
a.FrobeniusMap(&arg.A)
b.FrobeniusMap(&arg.B)
// b' = b' * (u + 1)^((p - 1) / 6)
b.Mul(&b, &up1epm1div6)
f.A.Set(&a)
f.B.Set(&b)
return f
}
// Equal returns 1 if fp12 == rhs, 0 otherwise
func (f *fp12) Equal(rhs *fp12) int {
return f.A.Equal(&rhs.A) & f.B.Equal(&rhs.B)
}
// IsZero returns 1 if fp6 == 0, 0 otherwise
func (f *fp12) IsZero() int {
return f.A.IsZero() & f.B.IsZero()
}
// IsOne returns 1 if fp12 == 1, 0 otherwise
func (f *fp12) IsOne() int {
return f.A.IsOne() & f.B.IsZero()
}
// CMove performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f *fp12) CMove(arg1, arg2 *fp12, choice int) *fp12 {
f.A.CMove(&arg1.A, &arg2.A, choice)
f.B.CMove(&arg1.B, &arg2.B, choice)
return f
}
+417
View File
@@ -0,0 +1,417 @@
package bls12381
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFp12Arithmetic(t *testing.T) {
var aa, bb, cc, d, e, f fp12
a := fp12{
A: fp6{
A: fp2{
A: fp{
0x47f9cb98b1b82d58,
0x5fe911eba3aa1d9d,
0x96bf1b5f4dd81db3,
0x8100d27cc9259f5b,
0xafa20b9674640eab,
0x09bbcea7d8d9497d,
},
B: fp{
0x0303cb98b1662daa,
0xd93110aa0a621d5a,
0xbfa9820c5be4a468,
0x0ba3643ecb05a348,
0xdc3534bb1f1c25a6,
0x06c305bb19c0e1c1,
},
},
B: fp2{
A: fp{
0x46f9cb98b162d858,
0x0be9109cf7aa1d57,
0xc791bc55fece41d2,
0xf84c57704e385ec2,
0xcb49c1d9c010e60f,
0x0acdb8e158bfe3c8,
},
B: fp{
0x8aefcb98b15f8306,
0x3ea1108fe4f21d54,
0xcf79f69fa1b7df3b,
0xe4f54aa1d16b1a3c,
0xba5e4ef86105a679,
0x0ed86c0797bee5cf,
},
},
C: fp2{
A: fp{
0xcee5cb98b15c2db4,
0x71591082d23a1d51,
0xd76230e944a17ca4,
0xd19e3dd3549dd5b6,
0xa972dc1701fa66e3,
0x12e31f2dd6bde7d6,
},
B: fp{
0xad2acb98b1732d9d,
0x2cfd10dd06961d64,
0x07396b86c6ef24e8,
0xbd76e2fdb1bfc820,
0x6afea7f6de94d0d5,
0x10994b0c5744c040,
},
},
},
B: fp6{
A: fp2{
A: fp{
0x47f9cb98b1b82d58,
0x5fe911eba3aa1d9d,
0x96bf1b5f4dd81db3,
0x8100d27cc9259f5b,
0xafa20b9674640eab,
0x09bbcea7d8d9497d,
},
B: fp{
0x0303cb98b1662daa,
0xd93110aa0a621d5a,
0xbfa9820c5be4a468,
0x0ba3643ecb05a348,
0xdc3534bb1f1c25a6,
0x06c305bb19c0e1c1,
},
},
B: fp2{
A: fp{
0x46f9cb98b162d858,
0x0be9109cf7aa1d57,
0xc791bc55fece41d2,
0xf84c57704e385ec2,
0xcb49c1d9c010e60f,
0x0acdb8e158bfe3c8,
},
B: fp{
0x8aefcb98b15f8306,
0x3ea1108fe4f21d54,
0xcf79f69fa1b7df3b,
0xe4f54aa1d16b1a3c,
0xba5e4ef86105a679,
0x0ed86c0797bee5cf,
},
},
C: fp2{
A: fp{
0xcee5cb98b15c2db4,
0x71591082d23a1d51,
0xd76230e944a17ca4,
0xd19e3dd3549dd5b6,
0xa972dc1701fa66e3,
0x12e31f2dd6bde7d6,
},
B: fp{
0xad2acb98b1732d9d,
0x2cfd10dd06961d64,
0x07396b86c6ef24e8,
0xbd76e2fdb1bfc820,
0x6afea7f6de94d0d5,
0x10994b0c5744c040,
},
},
},
}
b := fp12{
A: fp6{
A: fp2{
A: fp{
0x47f9_cb98_b1b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd8_1db3,
0x8100_d272_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
},
B: fp{
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
},
},
B: fp2{
A: fp{
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0xc791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d9_c010_e60f,
0x0acd_b8e1_58bf_e348,
},
B: fp{
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a1b7_df3b,
0xe4f5_4aa1_d16b_1a3c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
},
},
C: fp2{
A: fp{
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_30e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
},
B: fp{
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_5744_c040,
},
},
},
B: fp6{
A: fp2{
A: fp{
0x47f9_cb98_b1b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd2_1db3,
0x8100_d27c_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
},
B: fp{
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
},
},
B: fp2{
A: fp{
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0xc791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d9_c010_e60f,
0x0acd_b8e1_58bf_e3c8,
},
B: fp{
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a117_df3b,
0xe4f5_4aa1_d16b_1a3c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
},
},
C: fp2{
A: fp{
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_30e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
},
B: fp{
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_5744_c040,
},
},
},
}
c := fp12{
A: fp6{
A: fp2{
A: fp{
0x47f9_cb98_71b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd8_1db3,
0x8100_d27c_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
},
B: fp{
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
},
},
B: fp2{
A: fp{
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0x7791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d9_c010_e60f,
0x0acd_b8e1_58bf_e3c8,
},
B: fp{
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a1b7_df3b,
0xe4f5_4aa1_d16b_133c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
},
},
C: fp2{
A: fp{
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_40e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
},
B: fp{
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_1744_c040,
},
},
},
B: fp6{
A: fp2{
A: fp{
0x47f9_cb98_b1b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd8_1db3,
0x8100_d27c_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
},
B: fp{
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
},
},
B: fp2{
A: fp{
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0xc791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d3_c010_e60f,
0x0acd_b8e1_58bf_e3c8,
},
B: fp{
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a1b7_df3b,
0xe4f5_4aa1_d16b_1a3c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
},
},
C: fp2{
A: fp{
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_30e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
},
B: fp{
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_5744_1040,
},
},
},
}
aa.Square(&a)
aa.Invert(&aa)
aa.Square(&aa)
aa.Add(&aa, &c)
bb.Square(&b)
bb.Invert(&bb)
bb.Square(&bb)
bb.Add(&bb, &aa)
cc.Square(&c)
cc.Invert(&cc)
cc.Square(&cc)
cc.Add(&cc, &bb)
d.Square(&aa)
e.Mul(&aa, &aa)
require.Equal(t, 1, e.Equal(&d))
d.Square(&bb)
e.Mul(&bb, &bb)
require.Equal(t, 1, e.Equal(&d))
d.Square(&cc)
e.Mul(&cc, &cc)
require.Equal(t, 1, e.Equal(&d))
d.Square(&cc)
e.Add(&aa, &bb)
d.Mul(&d, &e)
e.Mul(&cc, &cc)
e.Mul(&e, &aa)
f.Mul(&cc, &cc)
f.Mul(&f, &bb)
e.Add(&e, &f)
require.Equal(t, 1, e.Equal(&d))
d.Invert(&aa)
e.Invert(&bb)
f.Mul(&aa, &bb)
f.Invert(&f)
require.Equal(t, 1, f.Equal(e.Mul(&e, &d)))
require.Equal(t, 1, d.Mul(&d, &aa).IsOne())
require.Equal(t, 0, aa.Equal(d.FrobeniusMap(&aa)))
d.FrobeniusMap(&aa).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d)
require.Equal(t, 1, aa.Equal(&d))
}
+344
View File
@@ -0,0 +1,344 @@
package bls12381
import (
"io"
)
// fp2 is a point in p^2
type fp2 struct {
A, B fp
}
// Set copies a into fp2
func (f *fp2) Set(a *fp2) *fp2 {
f.A.Set(&a.A)
f.B.Set(&a.B)
return f
}
// SetZero fp2 = 0
func (f *fp2) SetZero() *fp2 {
f.A.SetZero()
f.B.SetZero()
return f
}
// SetOne fp2 to the multiplicative identity element
func (f *fp2) SetOne() *fp2 {
f.A.SetOne()
f.B.SetZero()
return f
}
// SetFp creates an element from a lower field
func (f *fp2) SetFp(a *fp) *fp2 {
f.A.Set(a)
f.B.SetZero()
return f
}
// Random generates a random field element
func (f *fp2) Random(reader io.Reader) (*fp2, error) {
a, err := new(fp).Random(reader)
if err != nil {
return nil, err
}
b, err := new(fp).Random(reader)
if err != nil {
return nil, err
}
f.A = *a
f.B = *b
return f, nil
}
// IsZero returns 1 if fp2 == 0, 0 otherwise
func (f *fp2) IsZero() int {
return f.A.IsZero() & f.B.IsZero()
}
// IsOne returns 1 if fp2 == 1, 0 otherwise
func (f *fp2) IsOne() int {
return f.A.IsOne() & f.B.IsZero()
}
// Equal returns 1 if f == rhs, 0 otherwise
func (f *fp2) Equal(rhs *fp2) int {
return f.A.Equal(&rhs.A) & f.B.Equal(&rhs.B)
}
// LexicographicallyLargest returns 1 if
// this element is strictly lexicographically larger than its negation
// 0 otherwise
func (f *fp2) LexicographicallyLargest() int {
// If this element's B coefficient is lexicographically largest
// then it is lexicographically largest. Otherwise, in the event
// the B coefficient is zero and the A coefficient is
// lexicographically largest, then this element is lexicographically
// largest.
return f.B.LexicographicallyLargest() |
f.B.IsZero()&f.A.LexicographicallyLargest()
}
// Sgn0 returns the lowest bit value
func (f *fp2) Sgn0() int {
// if A = 0 return B.Sgn0 else A.Sgn0
a := f.A.IsZero()
t := f.B.Sgn0() & a
a = -a + 1
t |= f.A.Sgn0() & a
return t
}
// FrobeniusMap raises this element to p.
func (f *fp2) FrobeniusMap(a *fp2) *fp2 {
// This is always just a conjugation. If you're curious why, here's
// an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/
return f.Conjugate(a)
}
// Conjugate computes the conjugation of this element
func (f *fp2) Conjugate(a *fp2) *fp2 {
f.A.Set(&a.A)
f.B.Neg(&a.B)
return f
}
// MulByNonResidue computes the following:
// multiply a + bu by u + 1, getting
// au + a + bu^2 + bu
// and because u^2 = -1, we get
// (a - b) + (a + b)u
func (f *fp2) MulByNonResidue(a *fp2) *fp2 {
var aa, bb fp
aa.Sub(&a.A, &a.B)
bb.Add(&a.A, &a.B)
f.A.Set(&aa)
f.B.Set(&bb)
return f
}
// Square computes the square of this element
func (f *fp2) Square(arg *fp2) *fp2 {
var a, b, c fp
// Complex squaring:
//
// v0 = a * b
// a' = (a + b) * (a + \beta*b) - v0 - \beta * v0
// b' = 2 * v0
//
// In BLS12-381's F_{p^2}, our \beta is -1, so we
// can modify this formula:
//
// a' = (a + b) * (a - b)
// b' = 2 * a * b
a.Add(&arg.A, &arg.B)
b.Sub(&arg.A, &arg.B)
c.Add(&arg.A, &arg.A)
f.A.Mul(&a, &b)
f.B.Mul(&c, &arg.B)
return f
}
// Add performs field addition
func (f *fp2) Add(arg1, arg2 *fp2) *fp2 {
f.A.Add(&arg1.A, &arg2.A)
f.B.Add(&arg1.B, &arg2.B)
return f
}
// Double doubles specified element
func (f *fp2) Double(a *fp2) *fp2 {
f.A.Double(&a.A)
f.B.Double(&a.B)
return f
}
// Sub performs field subtraction
func (f *fp2) Sub(arg1, arg2 *fp2) *fp2 {
f.A.Sub(&arg1.A, &arg2.A)
f.B.Sub(&arg1.B, &arg2.B)
return f
}
// Mul computes Karatsuba multiplication
func (f *fp2) Mul(arg1, arg2 *fp2) *fp2 {
var v0, v1, t, a, b fp
// Karatsuba multiplication:
//
// v0 = a0 * b0
// v1 = a1 * b1
// c0 = v0 + \beta * v1
// c1 = (a0 + a1) * (b0 + b1) - v0 - v1
//
// In BLS12-381's F_{p^2}, our \beta is -1, so we
// can modify this formula. (Also, since we always
// subtract v1, we can compute v1 = -a1 * b1.)
//
// v0 = a0 * a1
// v1 = (-b0) * b1
// a' = v0 + v1
// b' = (a0 + b0) * (a1 + b1) - v0 + v1
v0.Mul(&arg1.A, &arg2.A)
v1.Mul(new(fp).Neg(&arg1.B), &arg2.B)
a.Add(&v0, &v1)
b.Add(&arg1.A, &arg1.B)
t.Add(&arg2.A, &arg2.B)
b.Mul(&b, &t)
b.Sub(&b, &v0)
b.Add(&b, &v1)
f.A.Set(&a)
f.B.Set(&b)
return f
}
func (f *fp2) Mul0(arg1 *fp2, arg2 *fp) *fp2 {
f.A.Mul(&arg1.A, arg2)
f.B.Mul(&arg1.B, arg2)
return f
}
// MulBy3b returns arg * 12 or 3 * b
func (f *fp2) MulBy3b(arg *fp2) *fp2 {
return f.Mul(arg, &curveG23B)
}
// Neg performs field negation
func (f *fp2) Neg(a *fp2) *fp2 {
f.A.Neg(&a.A)
f.B.Neg(&a.B)
return f
}
// Sqrt performs field square root
func (f *fp2) Sqrt(a *fp2) (*fp2, int) {
// Algorithm 9, https://eprint.iacr.org/2012/685.pdf
// with constant time modifications.
var a1, alpha, x0, t, res, res2 fp2
e1 := a.IsZero()
// a1 = self^((p - 3) / 4)
a1.pow(a, &[Limbs]uint64{
0xee7fbfffffffeaaa,
0x07aaffffac54ffff,
0xd9cc34a83dac3d89,
0xd91dd2e13ce144af,
0x92c6e9ed90d2eb35,
0x0680447a8e5ff9a6,
})
// alpha = a1^2 * a = a^((p - 3) / 2 + 1) = a^((p - 1) / 2)
alpha.Square(&a1)
alpha.Mul(&alpha, a)
// x0 = self^((p + 1) / 4)
x0.Mul(&a1, a)
// In the event that alpha = -1, the element is order p - 1. So
// we're just trying to get the square of an element of the subfield
// fp. This is given by x0 * u, since u = sqrt(-1). Since the element
// x0 = a + bu has b = 0, the solution is therefore au.
res2.A.Neg(&x0.B)
res2.B.Set(&x0.A)
// alpha == -1
e2 := alpha.Equal(&fp2{
A: fp{
0x43f5fffffffcaaae,
0x32b7fff2ed47fffd,
0x07e83a49a2e99d69,
0xeca8f3318332bb7a,
0xef148d1ea0f4c069,
0x040ab3263eff0206,
},
B: fp{},
})
// Otherwise, the correct solution is (1 + alpha)^((p - 1) // 2) * x0
t.SetOne()
t.Add(&t, &alpha)
t.pow(&t, &[Limbs]uint64{
0xdcff7fffffffd555,
0x0f55ffff58a9ffff,
0xb39869507b587b12,
0xb23ba5c279c2895f,
0x258dd3db21a5d66b,
0x0d0088f51cbff34d,
})
t.Mul(&t, &x0)
// if a = 0, then its zero
res.CMove(&res2, &res, e1)
// if alpha = -1, its not (1 + alpha)^((p - 1) // 2) * x0
// but au
res.CMove(&t, &res, e2)
// is the result^2 = a
t.Square(&res)
e3 := t.Equal(a)
f.CMove(f, &res, e3)
return f, e3
}
// Invert computes the multiplicative inverse of this field
// element, returning the original value of fp2
// in the case that this element is zero.
func (f *fp2) Invert(arg *fp2) (*fp2, int) {
// We wish to find the multiplicative inverse of a nonzero
// element a + bu in fp2. We leverage an identity
//
// (a + bu)(a - bu) = a^2 + b^2
//
// which holds because u^2 = -1. This can be rewritten as
//
// (a + bu)(a - bu)/(a^2 + b^2) = 1
//
// because a^2 + b^2 = 0 has no nonzero solutions for (a, b).
// This gives that (a - bu)/(a^2 + b^2) is the inverse
// of (a + bu). Importantly, this can be computing using
// only a single inversion in fp.
var a, b, t fp
a.Square(&arg.A)
b.Square(&arg.B)
a.Add(&a, &b)
_, wasInverted := t.Invert(&a)
// a * t
a.Mul(&arg.A, &t)
// b * -t
b.Neg(&t)
b.Mul(&b, &arg.B)
f.A.CMove(&f.A, &a, wasInverted)
f.B.CMove(&f.B, &b, wasInverted)
return f, wasInverted
}
// CMove performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f *fp2) CMove(arg1, arg2 *fp2, choice int) *fp2 {
f.A.CMove(&arg1.A, &arg2.A, choice)
f.B.CMove(&arg1.B, &arg2.B, choice)
return f
}
// CNeg conditionally negates a if choice == 1
func (f *fp2) CNeg(a *fp2, choice int) *fp2 {
var t fp2
t.Neg(a)
return f.CMove(f, &t, choice)
}
func (f *fp2) pow(base *fp2, exp *[Limbs]uint64) *fp2 {
res := (&fp2{}).SetOne()
tmp := (&fp2{}).SetZero()
for i := len(exp) - 1; i >= 0; i-- {
for j := 63; j >= 0; j-- {
res.Square(res)
tmp.Mul(res, base)
res.CMove(res, tmp, int(exp[i]>>j)&1)
}
}
return f.Set(res)
}
+424
View File
@@ -0,0 +1,424 @@
package bls12381
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFp2Square(t *testing.T) {
a := fp2{
A: fp{
0xc9a2183163ee70d4,
0xbc3770a7196b5c91,
0xa247f8c1304c5f44,
0xb01fc2a3726c80b5,
0xe1d293e5bbd919c9,
0x04b78e80020ef2ca,
},
B: fp{
0x952ea4460462618f,
0x238d5eddf025c62f,
0xf6c94b012ea92e72,
0x03ce24eac1c93808,
0x055950f945da483c,
0x010a768d0df4eabc,
},
}
b := fp2{
A: fp{
0xa1e09175a4d2c1fe,
0x8b33acfc204eff12,
0xe24415a11b456e42,
0x61d996b1b6ee1936,
0x1164dbe8667c853c,
0x0788557acc7d9c79,
},
B: fp{
0xda6a87cc6f48fa36,
0x0fc7b488277c1903,
0x9445ac4adc448187,
0x02616d5bc9099209,
0xdbed46772db58d48,
0x11b94d5076c7b7b1,
},
}
require.Equal(t, &b, a.Square(&a))
}
func TestFp2Mul(t *testing.T) {
a := fp2{
A: fp{
0xc9a2183163ee70d4,
0xbc3770a7196b5c91,
0xa247f8c1304c5f44,
0xb01fc2a3726c80b5,
0xe1d293e5bbd919c9,
0x04b78e80020ef2ca,
},
B: fp{
0x952ea4460462618f,
0x238d5eddf025c62f,
0xf6c94b012ea92e72,
0x03ce24eac1c93808,
0x055950f945da483c,
0x010a768d0df4eabc,
},
}
b := fp2{
A: fp{
0xa1e09175a4d2c1fe,
0x8b33acfc204eff12,
0xe24415a11b456e42,
0x61d996b1b6ee1936,
0x1164dbe8667c853c,
0x0788557acc7d9c79,
},
B: fp{
0xda6a87cc6f48fa36,
0x0fc7b488277c1903,
0x9445ac4adc448187,
0x02616d5bc9099209,
0xdbed46772db58d48,
0x11b94d5076c7b7b1,
},
}
c := fp2{
A: fp{
0xf597483e27b4e0f7,
0x610fbadf811dae5f,
0x8432af917714327a,
0x6a9a9603cf88f09e,
0xf05a7bf8bad0eb01,
0x09549131c003ffae,
},
B: fp{
0x963b02d0f93d37cd,
0xc95ce1cdb30a73d4,
0x308725fa3126f9b8,
0x56da3c167fab0d50,
0x6b5086b5f4b6d6af,
0x09c39f062f18e9f2,
},
}
require.Equal(t, 1, c.Equal(new(fp2).Mul(&a, &b)))
}
func TestFp2Add(t *testing.T) {
a := fp2{
A: fp{
0xc9a2183163ee70d4,
0xbc3770a7196b5c91,
0xa247f8c1304c5f44,
0xb01fc2a3726c80b5,
0xe1d293e5bbd919c9,
0x04b78e80020ef2ca,
},
B: fp{
0x952ea4460462618f,
0x238d5eddf025c62f,
0xf6c94b012ea92e72,
0x03ce24eac1c93808,
0x055950f945da483c,
0x010a768d0df4eabc,
},
}
b := fp2{
A: fp{
0xa1e09175a4d2c1fe,
0x8b33acfc204eff12,
0xe24415a11b456e42,
0x61d996b1b6ee1936,
0x1164dbe8667c853c,
0x0788557acc7d9c79,
},
B: fp{
0xda6a87cc6f48fa36,
0x0fc7b488277c1903,
0x9445ac4adc448187,
0x02616d5bc9099209,
0xdbed46772db58d48,
0x11b94d5076c7b7b1,
},
}
c := fp2{
A: fp{
0x6b82a9a708c132d2,
0x476b1da339ba5ba4,
0x848c0e624b91cd87,
0x11f95955295a99ec,
0xf3376fce22559f06,
0x0c3fe3face8c8f43,
},
B: fp{
0x6f992c1273ab5bc5,
0x3355136617a1df33,
0x8b0ef74c0aedaff9,
0x062f92468ad2ca12,
0xe1469770738fd584,
0x12c3c3dd84bca26d,
},
}
require.Equal(t, 1, c.Equal(new(fp2).Add(&a, &b)))
}
func TestFp2Sub(t *testing.T) {
a := fp2{
A: fp{
0xc9a2183163ee70d4,
0xbc3770a7196b5c91,
0xa247f8c1304c5f44,
0xb01fc2a3726c80b5,
0xe1d293e5bbd919c9,
0x04b78e80020ef2ca,
},
B: fp{
0x952ea4460462618f,
0x238d5eddf025c62f,
0xf6c94b012ea92e72,
0x03ce24eac1c93808,
0x055950f945da483c,
0x010a768d0df4eabc,
},
}
b := fp2{
A: fp{
0xa1e09175a4d2c1fe,
0x8b33acfc204eff12,
0xe24415a11b456e42,
0x61d996b1b6ee1936,
0x1164dbe8667c853c,
0x0788557acc7d9c79,
},
B: fp{
0xda6a87cc6f48fa36,
0x0fc7b488277c1903,
0x9445ac4adc448187,
0x02616d5bc9099209,
0xdbed46772db58d48,
0x11b94d5076c7b7b1,
},
}
c := fp2{
A: fp{
0xe1c086bbbf1b5981,
0x4fafc3a9aa705d7e,
0x2734b5c10bb7e726,
0xb2bd7776af037a3e,
0x1b895fb398a84164,
0x17304aef6f113cec,
},
B: fp{
0x74c31c7995191204,
0x3271aa5479fdad2b,
0xc9b471574915a30f,
0x65e40313ec44b8be,
0x7487b2385b7067cb,
0x09523b26d0ad19a4,
},
}
require.Equal(t, 1, c.Equal(new(fp2).Sub(&a, &b)))
}
func TestFp2Neg(t *testing.T) {
a := fp2{
A: fp{
0xc9a2183163ee70d4,
0xbc3770a7196b5c91,
0xa247f8c1304c5f44,
0xb01fc2a3726c80b5,
0xe1d293e5bbd919c9,
0x04b78e80020ef2ca,
},
B: fp{
0x952ea4460462618f,
0x238d5eddf025c62f,
0xf6c94b012ea92e72,
0x03ce24eac1c93808,
0x055950f945da483c,
0x010a768d0df4eabc,
},
}
b := fp2{
A: fp{
0xf05ce7ce9c1139d7,
0x62748f5797e8a36d,
0xc4e8d9dfc66496df,
0xb45788e181189209,
0x694913d08772930d,
0x1549836a3770f3cf,
},
B: fp{
0x24d05bb9fb9d491c,
0xfb1ea120c12e39d0,
0x7067879fc807c7b1,
0x60a9269a31bbdab6,
0x45c256bcfd71649b,
0x18f69b5d2b8afbde,
},
}
require.Equal(t, 1, b.Equal(new(fp2).Neg(&a)))
}
func TestFp2Sqrt(t *testing.T) {
a := fp2{
A: fp{
0x2beed14627d7f9e9,
0xb6614e06660e5dce,
0x06c4cc7c2f91d42c,
0x996d78474b7a63cc,
0xebaebc4c820d574e,
0x18865e12d93fd845,
},
B: fp{
0x7d828664baf4f566,
0xd17e663996ec7339,
0x679ead55cb4078d0,
0xfe3b2260e001ec28,
0x305993d043d91b68,
0x0626f03c0489b72d,
},
}
asq, wasSquare := (&fp2{}).Sqrt(&a)
require.Equal(t, 1, wasSquare)
require.Equal(t, 1, a.Equal(asq.Square(asq)))
b := fp2{
A: fp{
0x6631000000105545,
0x211400400eec000d,
0x3fa7af30c820e316,
0xc52a8b8d6387695d,
0x9fb4e61d1e83eac5,
0x005cb922afe84dc7,
},
B: fp{},
}
bsq, wasSquare := (&fp2{}).Sqrt(&b)
require.Equal(t, 1, wasSquare)
require.Equal(t, 1, b.Equal(bsq.Square(bsq)))
c := fp2{
A: fp{
0x44f600000051ffae,
0x86b8014199480043,
0xd7159952f1f3794a,
0x755d6e3dfe1ffc12,
0xd36cd6db5547e905,
0x02f8c8ecbf1867bb,
},
B: fp{},
}
csq, wasSquare := (&fp2{}).Sqrt(&c)
require.Equal(t, 1, wasSquare)
require.Equal(t, 1, c.Equal(csq.Square(csq)))
d := fp2{
A: fp{
0xc5fa1bc8fd00d7f6,
0x3830ca454606003b,
0x2b287f1104b102da,
0xa7fb30f28230f23e,
0x339cdb9ee953dbf0,
0x0d78ec51d989fc57,
},
B: fp{
0x27ec4898cf87f613,
0x9de1394e1abb05a5,
0x0947f85dc170fc14,
0x586fbc696b6114b7,
0x2b3475a4077d7169,
0x13e1c895cc4b6c22,
},
}
_, wasSquare = (&fp2{}).Sqrt(&d)
require.Equal(t, 0, wasSquare)
_, wasSquare = (&fp2{}).Sqrt(&fp2{})
require.Equal(t, 1, wasSquare)
}
func TestFp2Invert(t *testing.T) {
a := fp2{
A: fp{
0x1128ecad67549455,
0x9e7a1cff3a4ea1a8,
0xeb208d51e08bcf27,
0xe98ad40811f5fc2b,
0x736c3a59232d511d,
0x10acd42d29cfcbb6,
},
B: fp{
0xd328e37cc2f58d41,
0x948df0858a605869,
0x6032f9d56f93a573,
0x2be483ef3fffdc87,
0x30ef61f88f483c2a,
0x1333f55a35725be0,
},
}
b := fp2{
A: fp{
0x0581a1333d4f48a6,
0x58242f6ef0748500,
0x0292c955349e6da5,
0xba37721ddd95fcd0,
0x70d167903aa5dfc5,
0x11895e118b58a9d5,
},
B: fp{
0x0eda09d2d7a85d17,
0x8808e137a7d1a2cf,
0x43ae2625c1ff21db,
0xf85ac9fdf7a74c64,
0x8fccdda5b8da9738,
0x08e84f0cb32cd17d,
},
}
ainv, wasInverted := (&fp2{}).Invert(&a)
require.Equal(t, 1, wasInverted)
require.Equal(t, 1, b.Equal(ainv))
_, wasInverted = (&fp2{}).Invert(&fp2{})
require.Equal(t, 0, wasInverted)
}
func TestFp2LexicographicallyLargest(t *testing.T) {
require.Equal(t, 0, new(fp2).SetZero().LexicographicallyLargest())
require.Equal(t, 0, new(fp2).SetOne().LexicographicallyLargest())
a := fp2{
A: fp{
0x1128_ecad_6754_9455,
0x9e7a_1cff_3a4e_a1a8,
0xeb20_8d51_e08b_cf27,
0xe98a_d408_11f5_fc2b,
0x736c_3a59_232d_511d,
0x10ac_d42d_29cf_cbb6,
},
B: fp{
0xd328_e37c_c2f5_8d41,
0x948d_f085_8a60_5869,
0x6032_f9d5_6f93_a573,
0x2be4_83ef_3fff_dc87,
0x30ef_61f8_8f48_3c2a,
0x1333_f55a_3572_5be0,
},
}
require.Equal(t, 1, a.LexicographicallyLargest())
aNeg := new(fp2).Neg(&a)
require.Equal(t, 0, aNeg.LexicographicallyLargest())
a.B.SetZero()
require.Equal(t, 0, a.LexicographicallyLargest())
aNeg.B.SetZero()
require.Equal(t, 1, aNeg.LexicographicallyLargest())
}
+340
View File
@@ -0,0 +1,340 @@
package bls12381
import "io"
// fp6 represents an element
// a + b v + c v^2 of fp^6 = fp^2 / v^3 - u - 1.
type fp6 struct {
A, B, C fp2
}
// Set fp6 = a
func (f *fp6) Set(a *fp6) *fp6 {
f.A.Set(&a.A)
f.B.Set(&a.B)
f.C.Set(&a.C)
return f
}
// SetFp creates an element from a lower field
func (f *fp6) SetFp(a *fp) *fp6 {
f.A.SetFp(a)
f.B.SetZero()
f.C.SetZero()
return f
}
// SetFp2 creates an element from a lower field
func (f *fp6) SetFp2(a *fp2) *fp6 {
f.A.Set(a)
f.B.SetZero()
f.C.SetZero()
return f
}
// SetZero fp6 to zero
func (f *fp6) SetZero() *fp6 {
f.A.SetZero()
f.B.SetZero()
f.C.SetZero()
return f
}
// SetOne fp6 to multiplicative identity element
func (f *fp6) SetOne() *fp6 {
f.A.SetOne()
f.B.SetZero()
f.C.SetZero()
return f
}
// Random generates a random field element
func (f *fp6) Random(reader io.Reader) (*fp6, error) {
a, err := new(fp2).Random(reader)
if err != nil {
return nil, err
}
b, err := new(fp2).Random(reader)
if err != nil {
return nil, err
}
c, err := new(fp2).Random(reader)
if err != nil {
return nil, err
}
f.A.Set(a)
f.B.Set(b)
f.C.Set(c)
return f, nil
}
// Add computes arg1+arg2
func (f *fp6) Add(arg1, arg2 *fp6) *fp6 {
f.A.Add(&arg1.A, &arg2.A)
f.B.Add(&arg1.B, &arg2.B)
f.C.Add(&arg1.C, &arg2.C)
return f
}
// Double computes arg1+arg1
func (f *fp6) Double(arg *fp6) *fp6 {
return f.Add(arg, arg)
}
// Sub computes arg1-arg2
func (f *fp6) Sub(arg1, arg2 *fp6) *fp6 {
f.A.Sub(&arg1.A, &arg2.A)
f.B.Sub(&arg1.B, &arg2.B)
f.C.Sub(&arg1.C, &arg2.C)
return f
}
// Mul computes arg1*arg2
func (f *fp6) Mul(arg1, arg2 *fp6) *fp6 {
var aa, bb, cc, s, t1, t2, t3 fp2
aa.Mul(&arg1.A, &arg2.A)
bb.Mul(&arg1.B, &arg2.B)
cc.Mul(&arg1.C, &arg2.C)
t1.Add(&arg2.B, &arg2.C)
s.Add(&arg1.B, &arg1.C)
t1.Mul(&t1, &s)
t1.Sub(&t1, &bb)
t1.Sub(&t1, &cc)
t1.MulByNonResidue(&t1)
t1.Add(&t1, &aa)
t3.Add(&arg2.A, &arg2.C)
s.Add(&arg1.A, &arg1.C)
t3.Mul(&t3, &s)
t3.Sub(&t3, &aa)
t3.Add(&t3, &bb)
t3.Sub(&t3, &cc)
t2.Add(&arg2.A, &arg2.B)
s.Add(&arg1.A, &arg1.B)
t2.Mul(&t2, &s)
t2.Sub(&t2, &aa)
t2.Sub(&t2, &bb)
cc.MulByNonResidue(&cc)
t2.Add(&t2, &cc)
f.A.Set(&t1)
f.B.Set(&t2)
f.C.Set(&t3)
return f
}
// MulByB scales this field by a scalar in the B coefficient
func (f *fp6) MulByB(arg *fp6, b *fp2) *fp6 {
var bB, t1, t2 fp2
bB.Mul(&arg.B, b)
// (b + c) * arg2 - bB
t1.Add(&arg.B, &arg.C)
t1.Mul(&t1, b)
t1.Sub(&t1, &bB)
t1.MulByNonResidue(&t1)
t2.Add(&arg.A, &arg.B)
t2.Mul(&t2, b)
t2.Sub(&t2, &bB)
f.A.Set(&t1)
f.B.Set(&t2)
f.C.Set(&bB)
return f
}
// MulByAB scales this field by scalars in the A and B coefficients
func (f *fp6) MulByAB(arg *fp6, a, b *fp2) *fp6 {
var aA, bB, t1, t2, t3 fp2
aA.Mul(&arg.A, a)
bB.Mul(&arg.B, b)
t1.Add(&arg.B, &arg.C)
t1.Mul(&t1, b)
t1.Sub(&t1, &bB)
t1.MulByNonResidue(&t1)
t1.Add(&t1, &aA)
t2.Add(a, b)
t3.Add(&arg.A, &arg.B)
t2.Mul(&t2, &t3)
t2.Sub(&t2, &aA)
t2.Sub(&t2, &bB)
t3.Add(&arg.A, &arg.C)
t3.Mul(&t3, a)
t3.Sub(&t3, &aA)
t3.Add(&t3, &bB)
f.A.Set(&t1)
f.B.Set(&t2)
f.C.Set(&t3)
return f
}
// MulByNonResidue multiplies by quadratic nonresidue v.
func (f *fp6) MulByNonResidue(arg *fp6) *fp6 {
// Given a + bv + cv^2, this produces
// av + bv^2 + cv^3
// but because v^3 = u + 1, we have
// c(u + 1) + av + bv^2
var a, b, c fp2
a.MulByNonResidue(&arg.C)
b.Set(&arg.A)
c.Set(&arg.B)
f.A.Set(&a)
f.B.Set(&b)
f.C.Set(&c)
return f
}
// FrobeniusMap raises this element to p.
func (f *fp6) FrobeniusMap(arg *fp6) *fp6 {
var a, b, c fp2
pm1Div3 := fp2{
A: fp{},
B: fp{
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x03f97d6e83d050d2,
0x18f0206554638741,
},
}
p2m2Div3 := fp2{
A: fp{
0x890dc9e4867545c3,
0x2af322533285a5d5,
0x50880866309b7e2c,
0xa20d1b8c7e881024,
0x14e4f04fe2db9068,
0x14e56d3f1564853a,
},
B: fp{},
}
a.FrobeniusMap(&arg.A)
b.FrobeniusMap(&arg.B)
c.FrobeniusMap(&arg.C)
// b = b * (u + 1)^((p - 1) / 3)
b.Mul(&b, &pm1Div3)
// c = c * (u + 1)^((2p - 2) / 3)
c.Mul(&c, &p2m2Div3)
f.A.Set(&a)
f.B.Set(&b)
f.C.Set(&c)
return f
}
// Square computes fp6^2
func (f *fp6) Square(arg *fp6) *fp6 {
var s0, s1, s2, s3, s4, ab, bc fp2
s0.Square(&arg.A)
ab.Mul(&arg.A, &arg.B)
s1.Double(&ab)
s2.Sub(&arg.A, &arg.B)
s2.Add(&s2, &arg.C)
s2.Square(&s2)
bc.Mul(&arg.B, &arg.C)
s3.Double(&bc)
s4.Square(&arg.C)
f.A.MulByNonResidue(&s3)
f.A.Add(&f.A, &s0)
f.B.MulByNonResidue(&s4)
f.B.Add(&f.B, &s1)
// s1 + s2 + s3 - s0 - s4
f.C.Add(&s1, &s2)
f.C.Add(&f.C, &s3)
f.C.Sub(&f.C, &s0)
f.C.Sub(&f.C, &s4)
return f
}
// Invert computes this element's field inversion
func (f *fp6) Invert(arg *fp6) (*fp6, int) {
var a, b, c, s, t fp2
// a' = a^2 - (b * c).mul_by_nonresidue()
a.Mul(&arg.B, &arg.C)
a.MulByNonResidue(&a)
t.Square(&arg.A)
a.Sub(&t, &a)
// b' = (c^2).mul_by_nonresidue() - (a * b)
b.Square(&arg.C)
b.MulByNonResidue(&b)
t.Mul(&arg.A, &arg.B)
b.Sub(&b, &t)
// c' = b^2 - (a * c)
c.Square(&arg.B)
t.Mul(&arg.A, &arg.C)
c.Sub(&c, &t)
// t = ((b * c') + (c * b')).mul_by_nonresidue() + (a * a')
s.Mul(&arg.B, &c)
t.Mul(&arg.C, &b)
s.Add(&s, &t)
s.MulByNonResidue(&s)
t.Mul(&arg.A, &a)
s.Add(&s, &t)
_, wasInverted := t.Invert(&s)
// newA = a' * t^-1
s.Mul(&a, &t)
f.A.CMove(&f.A, &s, wasInverted)
// newB = b' * t^-1
s.Mul(&b, &t)
f.B.CMove(&f.B, &s, wasInverted)
// newC = c' * t^-1
s.Mul(&c, &t)
f.C.CMove(&f.C, &s, wasInverted)
return f, wasInverted
}
// Neg computes the field negation
func (f *fp6) Neg(arg *fp6) *fp6 {
f.A.Neg(&arg.A)
f.B.Neg(&arg.B)
f.C.Neg(&arg.C)
return f
}
// IsZero returns 1 if fp6 == 0, 0 otherwise
func (f *fp6) IsZero() int {
return f.A.IsZero() & f.B.IsZero() & f.C.IsZero()
}
// IsOne returns 1 if fp6 == 1, 0 otherwise
func (f *fp6) IsOne() int {
return f.A.IsOne() & f.B.IsZero() & f.B.IsZero()
}
// Equal returns 1 if fp6 == rhs, 0 otherwise
func (f *fp6) Equal(rhs *fp6) int {
return f.A.Equal(&rhs.A) & f.B.Equal(&rhs.B) & f.C.Equal(&rhs.C)
}
// CMove performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f *fp6) CMove(arg1, arg2 *fp6, choice int) *fp6 {
f.A.CMove(&arg1.A, &arg2.A, choice)
f.B.CMove(&arg1.B, &arg2.B, choice)
f.C.CMove(&arg1.C, &arg2.C, choice)
return f
}
+217
View File
@@ -0,0 +1,217 @@
package bls12381
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFp6Arithmetic(t *testing.T) {
a := fp6{
A: fp2{
A: fp{
0x47f9cb98b1b82d58,
0x5fe911eba3aa1d9d,
0x96bf1b5f4dd81db3,
0x8100d27cc9259f5b,
0xafa20b9674640eab,
0x09bbcea7d8d9497d,
},
B: fp{
0x0303cb98b1662daa,
0xd93110aa0a621d5a,
0xbfa9820c5be4a468,
0x0ba3643ecb05a348,
0xdc3534bb1f1c25a6,
0x06c305bb19c0e1c1,
},
},
B: fp2{
A: fp{
0x46f9cb98b162d858,
0x0be9109cf7aa1d57,
0xc791bc55fece41d2,
0xf84c57704e385ec2,
0xcb49c1d9c010e60f,
0x0acdb8e158bfe3c8,
},
B: fp{
0x8aefcb98b15f8306,
0x3ea1108fe4f21d54,
0xcf79f69fa1b7df3b,
0xe4f54aa1d16b1a3c,
0xba5e4ef86105a679,
0x0ed86c0797bee5cf,
},
},
C: fp2{
A: fp{
0xcee5cb98b15c2db4,
0x71591082d23a1d51,
0xd76230e944a17ca4,
0xd19e3dd3549dd5b6,
0xa972dc1701fa66e3,
0x12e31f2dd6bde7d6,
},
B: fp{
0xad2acb98b1732d9d,
0x2cfd10dd06961d64,
0x07396b86c6ef24e8,
0xbd76e2fdb1bfc820,
0x6afea7f6de94d0d5,
0x10994b0c5744c040,
},
},
}
b := fp6{
A: fp2{
A: fp{
0xf120cb98b16fd84b,
0x5fb510cff3de1d61,
0x0f21a5d069d8c251,
0xaa1fd62f34f2839a,
0x5a1335157f89913f,
0x14a3fe329643c247,
},
B: fp{
0x3516cb98b16c82f9,
0x926d10c2e1261d5f,
0x1709e01a0cc25fba,
0x96c8c960b8253f14,
0x4927c234207e51a9,
0x18aeb158d542c44e,
},
},
B: fp2{
A: fp{
0xbf0dcb98b16982fc,
0xa67910b71d1a1d5c,
0xb7c147c2b8fb06ff,
0x1efa710d47d2e7ce,
0xed20a79c7e27653c,
0x02b85294dac1dfba,
},
B: fp{
0x9d52cb98b18082e5,
0x621d111151761d6f,
0xe79882603b48af43,
0x0ad31637a4f4da37,
0xaeac737c5ac1cf2e,
0x006e7e735b48b824,
},
},
C: fp2{
A: fp{
0xe148cb98b17d2d93,
0x94d511043ebe1d6c,
0xef80bca9de324cac,
0xf77c0969282795b1,
0x9dc1009afbb68f97,
0x047931999a47ba2b,
},
B: fp{
0x253ecb98b179d841,
0xc78d10f72c061d6a,
0xf768f6f3811bea15,
0xe424fc9aab5a512b,
0x8cd58db99cab5001,
0x0883e4bfd946bc32,
},
},
}
c := fp6{
A: fp2{
A: fp{
0x6934cb98b17682ef,
0xfa4510ea194e1d67,
0xff51313d2405877e,
0xd0cdefcc2e8d0ca5,
0x7bea1ad83da0106b,
0x0c8e97e61845be39,
},
B: fp{
0x4779cb98b18d82d8,
0xb5e911444daa1d7a,
0x2f286bdaa6532fc2,
0xbca694f68baeff0f,
0x3d75e6b81a3a7a5d,
0x0a44c3c498cc96a3,
},
},
B: fp2{
A: fp{
0x8b6fcb98b18a2d86,
0xe8a111373af21d77,
0x3710a624493ccd2b,
0xa94f88280ee1ba89,
0x2c8a73d6bb2f3ac7,
0x0e4f76ead7cb98aa,
},
B: fp{
0xcf65cb98b186d834,
0x1b59112a283a1d74,
0x3ef8e06dec266a95,
0x95f87b5992147603,
0x1b9f00f55c23fb31,
0x125a2a1116ca9ab1,
},
},
C: fp2{
A: fp{
0x135bcb98b18382e2,
0x4e11111d15821d72,
0x46e11ab78f1007fe,
0x82a16e8b1547317d,
0x0ab38e13fd18bb9b,
0x1664dd3755c99cb8,
},
B: fp{
0xce65cb98b1318334,
0xc7590fdb7c3a1d2e,
0x6fcb81649d1c8eb3,
0x0d44004d1727356a,
0x3746b738a7d0d296,
0x136c144a96b134fc,
},
},
}
d := new(fp6).Square(&a)
e := new(fp6).Mul(&a, &a)
require.Equal(t, 1, e.Equal(d))
d.Square(&b)
e.Mul(&b, &b)
require.Equal(t, 1, e.Equal(d))
d.Square(&c)
e.Mul(&c, &c)
require.Equal(t, 1, e.Equal(d))
// (a + b) * c^2
d.Add(&a, &b)
d.Mul(d, new(fp6).Square(&c))
e.Mul(&c, &c)
e.Mul(e, &a)
tt := new(fp6).Mul(&c, &c)
tt.Mul(tt, &b)
e.Add(e, tt)
require.Equal(t, 1, d.Equal(e))
_, wasInverted := d.Invert(&a)
require.Equal(t, 1, wasInverted)
_, wasInverted = e.Invert(&b)
require.Equal(t, 1, wasInverted)
tt.Mul(&a, &b)
_, wasInverted = tt.Invert(tt)
require.Equal(t, 1, wasInverted)
d.Mul(d, e)
require.Equal(t, 1, tt.Equal(d))
_, _ = d.Invert(&a)
e.SetOne()
require.Equal(t, 1, e.Equal(d.Mul(d, &a)))
}
+297
View File
@@ -0,0 +1,297 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls12381
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/internal"
)
func TestFpSetOne(t *testing.T) {
var fp fp
fp.SetOne()
require.NotNil(t, fp)
require.Equal(t, fp, r)
}
func TestFpSetUint64(t *testing.T) {
var act fp
act.SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act[0], uint64(0xf6ea9fde37db5e8c))
}
func TestFpAdd(t *testing.T) {
var lhs, rhs, exp, res fp
lhs.SetOne()
rhs.SetOne()
exp.SetUint64(2)
res.Add(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(&exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
res.Add(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, exp, res)
}
}
func TestFpSub(t *testing.T) {
var lhs, rhs, exp, res fp
lhs.SetOne()
rhs.SetOne()
exp.SetZero()
res.Sub(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(&exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
res.Sub(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, exp, res)
}
}
func TestFpMul(t *testing.T) {
var lhs, rhs, exp, res fp
lhs.SetOne()
rhs.SetOne()
exp.SetOne()
res.Mul(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(&exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
res.Mul(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, exp, res)
}
}
func TestFpDouble(t *testing.T) {
var a, e, res fp
a.SetUint64(2)
e.SetUint64(4)
require.Equal(t, &e, res.Double(&a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a.SetUint64(uint64(tv))
e.SetUint64(ttv)
require.Equal(t, &e, res.Double(&a))
}
}
func TestFpSquare(t *testing.T) {
var a, e, res fp
a.SetUint64(4)
e.SetUint64(16)
require.Equal(t, 1, e.Equal(res.Square(&a)))
a.SetUint64(2854263694)
e.SetUint64(8146821234886525636)
require.Equal(t, 1, e.Equal(res.Square(&a)))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, 1, e.Equal(res.Square(&a)), "exp = %d, j = %d", exp, j)
}
}
func TestFpNeg(t *testing.T) {
var g, a, e fp
g.SetLimbs(&[Limbs]uint64{7, 0, 0, 0, 0, 0})
a.SetOne()
a.Neg(&a)
e.SetRaw(&[Limbs]uint64{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206})
require.Equal(t, 1, e.Equal(&a))
a.Neg(&g)
e.SetRaw(&[Limbs]uint64{0x21baffffffe90017, 0x445bffa5cba3ffed, 0xd028c5627db257bc, 0x14275ad5a2de0d96, 0x3e7434202365960e, 0x0249d4217f792796})
require.Equal(t, e, a)
}
func TestFpExp(t *testing.T) {
var a, e, by fp
e.SetUint64(8)
a.SetUint64(2)
by.SetUint64(3)
require.Equal(t, &e, a.Exp(&a, &by))
}
func TestFpSqrt(t *testing.T) {
var t1, t2, t3 fp
t1.SetUint64(2)
t2.Neg(&t1)
t3.Square(&t1)
_, wasSquare := t3.Sqrt(&t3)
require.Equal(t, 1, wasSquare)
require.Equal(t, 1, t1.Equal(&t3)|t2.Equal(&t3))
t1.SetUint64(5)
_, wasSquare = t1.Sqrt(&t1)
require.Equal(t, 0, wasSquare)
}
func TestFpInvert(t *testing.T) {
var two, twoInv, a, lhs, rhs, rhsInv fp
twoInv.SetRaw(&[Limbs]uint64{0x1804000000015554, 0x855000053ab00001, 0x633cb57c253c276f, 0x6e22d1ec31ebb502, 0xd3916126f2d14ca2, 0x17fbb8571a006596})
two.SetUint64(2)
_, inverted := a.Invert(&two)
require.Equal(t, 1, inverted)
require.Equal(t, &a, &twoInv)
lhs.SetUint64(9)
rhs.SetUint64(3)
_, inverted = rhsInv.Invert(&rhs)
require.Equal(t, 1, inverted)
require.Equal(t, &rhs, lhs.Mul(&lhs, &rhsInv))
rhs.SetZero()
_, inverted = lhs.Invert(&rhs)
require.Equal(t, 0, inverted)
}
func TestFpCMove(t *testing.T) {
var t1, t2, tt fp
t1.SetUint64(5)
t2.SetUint64(10)
require.Equal(t, &t1, tt.CMove(&t1, &t2, 0))
require.Equal(t, &t2, tt.CMove(&t1, &t2, 1))
}
func TestFpBytes(t *testing.T) {
var t1, t2 fp
t1.SetUint64(99)
seq := t1.Bytes()
_, suc := t2.SetBytes(&seq)
require.Equal(t, 1, suc)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, suc = t2.SetBytes(&seq)
require.Equal(t, 1, suc)
require.Equal(t, t1, t2)
}
}
func TestFpBigInt(t *testing.T) {
var t1, t2, e fp
t1.SetBigInt(big.NewInt(9999))
t2.SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e.SetRaw(&[Limbs]uint64{0x922af810e5e35f31, 0x6bc75973ed382d59, 0xd4716c9d4d491d42, 0x69d98d1ebeeb3f6e, 0x7e425d7b46d4a82b, 0x12d04b0965870e92})
b := new(big.Int).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Neg(&e)
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFpSetBytesWideBigInt(t *testing.T) {
var a fp
var tv2 [96]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, biModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a.SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
func TestFpToMontgomery(t *testing.T) {
var v fp
v.SetUint64(2)
require.Equal(t, fp{0x321300000006554f, 0xb93c0018d6c40005, 0x57605e0db0ddbb51, 0x8b256521ed1f9bcb, 0x6cf28d7901622c03, 0x11ebab9dbb81e28c}, v)
}
func TestFpFromMontgomery(t *testing.T) {
var v fp
e := fp{2, 0, 0, 0, 0, 0}
v.SetUint64(2)
v.fromMontgomery(&v)
require.Equal(t, e, v)
}
func TestFpLexicographicallyLargest(t *testing.T) {
require.Equal(t, 0, new(fp).SetZero().LexicographicallyLargest())
require.Equal(t, 0, new(fp).SetOne().LexicographicallyLargest())
require.Equal(t, 0, (&fp{
0xa1fafffffffe5557,
0x995bfff976a3fffe,
0x03f41d24d174ceb4,
0xf6547998c1995dbd,
0x778a468f507a6034,
0x020559931f7f8103,
}).LexicographicallyLargest())
require.Equal(t, 1, (&fp{
0x1804000000015554,
0x855000053ab00001,
0x633cb57c253c276f,
0x6e22d1ec31ebb502,
0xd3916126f2d14ca2,
0x17fbb8571a006596,
}).LexicographicallyLargest())
require.Equal(t, 1, (&fp{
0x43f5fffffffcaaae,
0x32b7fff2ed47fffd,
0x07e83a49a2e99d69,
0xeca8f3318332bb7a,
0xef148d1ea0f4c069,
0x040ab3263eff0206,
}).LexicographicallyLargest())
}
+416
View File
@@ -0,0 +1,416 @@
package bls12381
import (
"encoding/binary"
"math/big"
"sync"
"github.com/onsonr/sonr/crypto/core/curves/native"
)
type Fq [native.FieldLimbs]uint64
var (
bls12381FqInitonce sync.Once
bls12381FqParams native.FieldParams
)
// 2^S * t = MODULUS - 1 with t odd
const fqS = 32
// qInv = -(q^{-1} mod 2^64) mod 2^64
const qInv = 0xfffffffeffffffff
// fqGenerator = 7 (multiplicative fqGenerator of r-1 order, that is also quadratic nonresidue)
var fqGenerator = [native.FieldLimbs]uint64{0x0000000efffffff1, 0x17e363d300189c0f, 0xff9c57876f8457b0, 0x351332208fc5a8c4}
// fqModulus
var fqModulus = [native.FieldLimbs]uint64{0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48}
func Bls12381FqNew() *native.Field {
return &native.Field{
Value: [native.FieldLimbs]uint64{},
Params: getBls12381FqParams(),
Arithmetic: bls12381FqArithmetic{},
}
}
func bls12381FqParamsInit() {
bls12381FqParams = native.FieldParams{
R: [native.FieldLimbs]uint64{0x00000001fffffffe, 0x5884b7fa00034802, 0x998c4fefecbc4ff5, 0x1824b159acc5056f},
R2: [native.FieldLimbs]uint64{0xc999e990f3f29c6d, 0x2b6cedcb87925c23, 0x05d314967254398f, 0x0748d9d99f59ff11},
R3: [native.FieldLimbs]uint64{0xc62c1807439b73af, 0x1b3e0d188cf06990, 0x73d13c71c7b5f418, 0x6e2a5bb9c8db33e9},
Modulus: [native.FieldLimbs]uint64{0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48},
BiModulus: new(big.Int).SetBytes([]byte{
0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, 0x09, 0xa1, 0xd8, 0x05, 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
}),
}
}
func getBls12381FqParams() *native.FieldParams {
bls12381FqInitonce.Do(bls12381FqParamsInit)
return &bls12381FqParams
}
// bls12381FqArithmetic is a struct with all the methods needed for working
// in mod q
type bls12381FqArithmetic struct{}
// ToMontgomery converts this field to montgomery form
func (f bls12381FqArithmetic) ToMontgomery(out, arg *[native.FieldLimbs]uint64) {
// arg.R^0 * R^2 / R = arg.R
f.Mul(out, arg, &getBls12381FqParams().R2)
}
// FromMontgomery converts this field from montgomery form
func (f bls12381FqArithmetic) FromMontgomery(out, arg *[native.FieldLimbs]uint64) {
// Mul by 1 is division by 2^256 mod q
// f.Mul(out, arg, &[native.FieldLimbs]uint64{1, 0, 0, 0})
f.montReduce(out, &[native.FieldLimbs * 2]uint64{arg[0], arg[1], arg[2], arg[3], 0, 0, 0, 0})
}
// Neg performs modular negation
func (f bls12381FqArithmetic) Neg(out, arg *[native.FieldLimbs]uint64) {
// Subtract `arg` from `fqModulus`. Ignore final borrow
// since it can't underflow.
var t [native.FieldLimbs]uint64
var borrow uint64
t[0], borrow = sbb(fqModulus[0], arg[0], 0)
t[1], borrow = sbb(fqModulus[1], arg[1], borrow)
t[2], borrow = sbb(fqModulus[2], arg[2], borrow)
t[3], _ = sbb(fqModulus[3], arg[3], borrow)
// t could be `fqModulus` if `arg`=0. Set mask=0 if self=0
// and 0xff..ff if `arg`!=0
mask := t[0] | t[1] | t[2] | t[3]
mask = -((mask | -mask) >> 63)
out[0] = t[0] & mask
out[1] = t[1] & mask
out[2] = t[2] & mask
out[3] = t[3] & mask
}
// Square performs modular square
func (f bls12381FqArithmetic) Square(out, arg *[native.FieldLimbs]uint64) {
var r [2 * native.FieldLimbs]uint64
var carry uint64
r[1], carry = mac(0, arg[0], arg[1], 0)
r[2], carry = mac(0, arg[0], arg[2], carry)
r[3], r[4] = mac(0, arg[0], arg[3], carry)
r[3], carry = mac(r[3], arg[1], arg[2], 0)
r[4], r[5] = mac(r[4], arg[1], arg[3], carry)
r[5], r[6] = mac(r[5], arg[2], arg[3], 0)
r[7] = r[6] >> 63
r[6] = (r[6] << 1) | r[5]>>63
r[5] = (r[5] << 1) | r[4]>>63
r[4] = (r[4] << 1) | r[3]>>63
r[3] = (r[3] << 1) | r[2]>>63
r[2] = (r[2] << 1) | r[1]>>63
r[1] = r[1] << 1
r[0], carry = mac(0, arg[0], arg[0], 0)
r[1], carry = adc(0, r[1], carry)
r[2], carry = mac(r[2], arg[1], arg[1], carry)
r[3], carry = adc(0, r[3], carry)
r[4], carry = mac(r[4], arg[2], arg[2], carry)
r[5], carry = adc(0, r[5], carry)
r[6], carry = mac(r[6], arg[3], arg[3], carry)
r[7], _ = adc(0, r[7], carry)
f.montReduce(out, &r)
}
// Mul performs modular multiplication
func (f bls12381FqArithmetic) Mul(out, arg1, arg2 *[native.FieldLimbs]uint64) {
// Schoolbook multiplication
var r [2 * native.FieldLimbs]uint64
var carry uint64
r[0], carry = mac(0, arg1[0], arg2[0], 0)
r[1], carry = mac(0, arg1[0], arg2[1], carry)
r[2], carry = mac(0, arg1[0], arg2[2], carry)
r[3], r[4] = mac(0, arg1[0], arg2[3], carry)
r[1], carry = mac(r[1], arg1[1], arg2[0], 0)
r[2], carry = mac(r[2], arg1[1], arg2[1], carry)
r[3], carry = mac(r[3], arg1[1], arg2[2], carry)
r[4], r[5] = mac(r[4], arg1[1], arg2[3], carry)
r[2], carry = mac(r[2], arg1[2], arg2[0], 0)
r[3], carry = mac(r[3], arg1[2], arg2[1], carry)
r[4], carry = mac(r[4], arg1[2], arg2[2], carry)
r[5], r[6] = mac(r[5], arg1[2], arg2[3], carry)
r[3], carry = mac(r[3], arg1[3], arg2[0], 0)
r[4], carry = mac(r[4], arg1[3], arg2[1], carry)
r[5], carry = mac(r[5], arg1[3], arg2[2], carry)
r[6], r[7] = mac(r[6], arg1[3], arg2[3], carry)
f.montReduce(out, &r)
}
// Add performs modular addition
func (f bls12381FqArithmetic) Add(out, arg1, arg2 *[native.FieldLimbs]uint64) {
var t [native.FieldLimbs]uint64
var carry uint64
t[0], carry = adc(arg1[0], arg2[0], 0)
t[1], carry = adc(arg1[1], arg2[1], carry)
t[2], carry = adc(arg1[2], arg2[2], carry)
t[3], _ = adc(arg1[3], arg2[3], carry)
// Subtract the fqModulus to ensure the value
// is smaller.
f.Sub(out, &t, &fqModulus)
}
// Sub performs modular subtraction
func (f bls12381FqArithmetic) Sub(out, arg1, arg2 *[native.FieldLimbs]uint64) {
d0, borrow := sbb(arg1[0], arg2[0], 0)
d1, borrow := sbb(arg1[1], arg2[1], borrow)
d2, borrow := sbb(arg1[2], arg2[2], borrow)
d3, borrow := sbb(arg1[3], arg2[3], borrow)
// If underflow occurred on the final limb, borrow 0xff...ff, otherwise
// borrow = 0x00...00. Conditionally mask to add the fqModulus
borrow = -borrow
d0, carry := adc(d0, fqModulus[0]&borrow, 0)
d1, carry = adc(d1, fqModulus[1]&borrow, carry)
d2, carry = adc(d2, fqModulus[2]&borrow, carry)
d3, _ = adc(d3, fqModulus[3]&borrow, carry)
out[0] = d0
out[1] = d1
out[2] = d2
out[3] = d3
}
// Sqrt performs modular square root
func (f bls12381FqArithmetic) Sqrt(wasSquare *int, out, arg *[native.FieldLimbs]uint64) {
// See sqrt_ts_ct at
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.4
// c1 := fqS
// c2 := (q - 1) / (2^c1)
c2 := [4]uint64{
0xfffe5bfeffffffff,
0x09a1d80553bda402,
0x299d7d483339d808,
0x0000000073eda753,
}
// c3 := (c2 - 1) / 2
c3 := [native.FieldLimbs]uint64{
0x7fff2dff7fffffff,
0x04d0ec02a9ded201,
0x94cebea4199cec04,
0x0000000039f6d3a9,
}
// c4 := fqGenerator
var c5 [native.FieldLimbs]uint64
native.Pow(&c5, &fqGenerator, &c2, getBls12381FqParams(), f)
// c5 := [native.FieldLimbs]uint64{0x1015708f7e368fe1, 0x31c6c5456ecc4511, 0x5281fe8998a19ea1, 0x0279089e10c63fe8}
var z, t, b, c, tv [native.FieldLimbs]uint64
native.Pow(&z, arg, &c3, getBls12381FqParams(), f)
f.Square(&t, &z)
f.Mul(&t, &t, arg)
f.Mul(&z, &z, arg)
copy(b[:], t[:])
copy(c[:], c5[:])
for i := fqS; i >= 2; i-- {
for j := 1; j <= i-2; j++ {
f.Square(&b, &b)
}
// if b == 1 flag = 0 else flag = 1
flag := -(&native.Field{
Value: b,
Params: getBls12381FqParams(),
Arithmetic: f,
}).IsOne() + 1
f.Mul(&tv, &z, &c)
f.Selectznz(&z, &z, &tv, flag)
f.Square(&c, &c)
f.Mul(&tv, &t, &c)
f.Selectznz(&t, &t, &tv, flag)
copy(b[:], t[:])
}
f.Square(&c, &z)
*wasSquare = (&native.Field{
Value: c,
Params: getBls12381FqParams(),
Arithmetic: f,
}).Equal(&native.Field{
Value: *arg,
Params: getBls12381FqParams(),
Arithmetic: f,
})
f.Selectznz(out, out, &z, *wasSquare)
}
// Invert performs modular inverse
func (f bls12381FqArithmetic) Invert(wasInverted *int, out, arg *[native.FieldLimbs]uint64) {
// Using an addition chain from
// https://github.com/kwantam/addchain
var t0, t1, t2, t3, t4, t5, t6, t7, t8 [native.FieldLimbs]uint64
var t9, t11, t12, t13, t14, t15, t16, t17 [native.FieldLimbs]uint64
f.Square(&t0, arg)
f.Mul(&t1, &t0, arg)
f.Square(&t16, &t0)
f.Square(&t6, &t16)
f.Mul(&t5, &t6, &t0)
f.Mul(&t0, &t6, &t16)
f.Mul(&t12, &t5, &t16)
f.Square(&t2, &t6)
f.Mul(&t7, &t5, &t6)
f.Mul(&t15, &t0, &t5)
f.Square(&t17, &t12)
f.Mul(&t1, &t1, &t17)
f.Mul(&t3, &t7, &t2)
f.Mul(&t8, &t1, &t17)
f.Mul(&t4, &t8, &t2)
f.Mul(&t9, &t8, &t7)
f.Mul(&t7, &t4, &t5)
f.Mul(&t11, &t4, &t17)
f.Mul(&t5, &t9, &t17)
f.Mul(&t14, &t7, &t15)
f.Mul(&t13, &t11, &t12)
f.Mul(&t12, &t11, &t17)
f.Mul(&t15, &t15, &t12)
f.Mul(&t16, &t16, &t15)
f.Mul(&t3, &t3, &t16)
f.Mul(&t17, &t17, &t3)
f.Mul(&t0, &t0, &t17)
f.Mul(&t6, &t6, &t0)
f.Mul(&t2, &t2, &t6)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t17)
native.Pow2k(&t0, &t0, 9, f)
f.Mul(&t0, &t0, &t16)
native.Pow2k(&t0, &t0, 9, f)
f.Mul(&t0, &t0, &t15)
native.Pow2k(&t0, &t0, 9, f)
f.Mul(&t0, &t0, &t15)
native.Pow2k(&t0, &t0, 7, f)
f.Mul(&t0, &t0, &t14)
native.Pow2k(&t0, &t0, 7, f)
f.Mul(&t0, &t0, &t13)
native.Pow2k(&t0, &t0, 10, f)
f.Mul(&t0, &t0, &t12)
native.Pow2k(&t0, &t0, 9, f)
f.Mul(&t0, &t0, &t11)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t8)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, arg)
native.Pow2k(&t0, &t0, 14, f)
f.Mul(&t0, &t0, &t9)
native.Pow2k(&t0, &t0, 10, f)
f.Mul(&t0, &t0, &t8)
native.Pow2k(&t0, &t0, 15, f)
f.Mul(&t0, &t0, &t7)
native.Pow2k(&t0, &t0, 10, f)
f.Mul(&t0, &t0, &t6)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t5)
native.Pow2k(&t0, &t0, 16, f)
f.Mul(&t0, &t0, &t3)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 7, f)
f.Mul(&t0, &t0, &t4)
native.Pow2k(&t0, &t0, 9, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t3)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t3)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 5, f)
f.Mul(&t0, &t0, &t1)
native.Pow2k(&t0, &t0, 5, f)
f.Mul(&t0, &t0, &t1)
*wasInverted = (&native.Field{
Value: *arg,
Params: getBls12381FqParams(),
Arithmetic: f,
}).IsNonZero()
f.Selectznz(out, out, &t0, *wasInverted)
}
// FromBytes converts a little endian byte array into a field element
func (f bls12381FqArithmetic) FromBytes(out *[native.FieldLimbs]uint64, arg *[native.FieldBytes]byte) {
out[0] = binary.LittleEndian.Uint64(arg[:8])
out[1] = binary.LittleEndian.Uint64(arg[8:16])
out[2] = binary.LittleEndian.Uint64(arg[16:24])
out[3] = binary.LittleEndian.Uint64(arg[24:])
}
// ToBytes converts a field element to a little endian byte array
func (f bls12381FqArithmetic) ToBytes(out *[native.FieldBytes]byte, arg *[native.FieldLimbs]uint64) {
binary.LittleEndian.PutUint64(out[:8], arg[0])
binary.LittleEndian.PutUint64(out[8:16], arg[1])
binary.LittleEndian.PutUint64(out[16:24], arg[2])
binary.LittleEndian.PutUint64(out[24:], arg[3])
}
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f bls12381FqArithmetic) Selectznz(out, arg1, arg2 *[native.FieldLimbs]uint64, choice int) {
b := uint64(-choice)
out[0] = arg1[0] ^ ((arg1[0] ^ arg2[0]) & b)
out[1] = arg1[1] ^ ((arg1[1] ^ arg2[1]) & b)
out[2] = arg1[2] ^ ((arg1[2] ^ arg2[2]) & b)
out[3] = arg1[3] ^ ((arg1[3] ^ arg2[3]) & b)
}
func (f bls12381FqArithmetic) montReduce(out *[native.FieldLimbs]uint64, r *[2 * native.FieldLimbs]uint64) {
// Taken from Algorithm 14.32 in Handbook of Applied Cryptography
var r1, r2, r3, r4, r5, r6, carry, carry2, k uint64
var rr [native.FieldLimbs]uint64
k = r[0] * qInv
_, carry = mac(r[0], k, fqModulus[0], 0)
r1, carry = mac(r[1], k, fqModulus[1], carry)
r2, carry = mac(r[2], k, fqModulus[2], carry)
r3, carry = mac(r[3], k, fqModulus[3], carry)
r4, carry2 = adc(r[4], 0, carry)
k = r1 * qInv
_, carry = mac(r1, k, fqModulus[0], 0)
r2, carry = mac(r2, k, fqModulus[1], carry)
r3, carry = mac(r3, k, fqModulus[2], carry)
r4, carry = mac(r4, k, fqModulus[3], carry)
r5, carry2 = adc(r[5], carry2, carry)
k = r2 * qInv
_, carry = mac(r2, k, fqModulus[0], 0)
r3, carry = mac(r3, k, fqModulus[1], carry)
r4, carry = mac(r4, k, fqModulus[2], carry)
r5, carry = mac(r5, k, fqModulus[3], carry)
r6, carry2 = adc(r[6], carry2, carry)
k = r3 * qInv
_, carry = mac(r3, k, fqModulus[0], 0)
rr[0], carry = mac(r4, k, fqModulus[1], carry)
rr[1], carry = mac(r5, k, fqModulus[2], carry)
rr[2], carry = mac(r6, k, fqModulus[3], carry)
rr[3], _ = adc(r[7], carry2, carry)
f.Sub(out, &rr, &fqModulus)
}
+342
View File
@@ -0,0 +1,342 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls12381
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/internal"
)
func TestFqSetOne(t *testing.T) {
fq := Bls12381FqNew().SetOne()
require.NotNil(t, fq)
require.Equal(t, fq.Value, getBls12381FqParams().R)
}
func TestFqSetUint64(t *testing.T) {
act := Bls12381FqNew().SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act.Value[0], uint64(0xbc98da2820121c89))
}
func TestFqAdd(t *testing.T) {
lhs := Bls12381FqNew().SetOne()
rhs := Bls12381FqNew().SetOne()
exp := Bls12381FqNew().SetUint64(2)
res := Bls12381FqNew().Add(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := Bls12381FqNew().Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqSub(t *testing.T) {
lhs := Bls12381FqNew().SetOne()
rhs := Bls12381FqNew().SetOne()
exp := Bls12381FqNew().SetZero()
res := Bls12381FqNew().Sub(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := Bls12381FqNew().Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqMul(t *testing.T) {
lhs := Bls12381FqNew().SetOne()
rhs := Bls12381FqNew().SetOne()
exp := Bls12381FqNew().SetOne()
res := Bls12381FqNew().Mul(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := Bls12381FqNew().Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqDouble(t *testing.T) {
a := Bls12381FqNew().SetUint64(2)
e := Bls12381FqNew().SetUint64(4)
require.Equal(t, e, Bls12381FqNew().Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = Bls12381FqNew().SetUint64(uint64(tv))
e = Bls12381FqNew().SetUint64(ttv)
require.Equal(t, e, Bls12381FqNew().Double(a))
}
}
func TestFqSquare(t *testing.T) {
a := Bls12381FqNew().SetUint64(4)
e := Bls12381FqNew().SetUint64(16)
require.Equal(t, 1, e.Equal(a.Square(a)))
a.SetUint64(2854263694)
e.SetUint64(8146821234886525636)
require.Equal(t, 1, e.Equal(a.Square(a)))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, 1, e.Equal(a.Square(a)), "exp = %d, j = %d", exp, j)
}
}
func TestFqNeg(t *testing.T) {
g := Bls12381FqNew().SetRaw(&fqGenerator)
a := Bls12381FqNew().SetOne()
a.Neg(a)
e := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0xfffffffd00000003, 0xfb38ec08fffb13fc, 0x99ad88181ce5880f, 0x5bc8f5f97cd877d8})
require.Equal(t, 1, e.Equal(a))
a.Neg(g)
e = Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0xfffffff000000010, 0x3bda402fffe5bfef, 0x339d80809a1d8055, 0x3eda753299d7d483})
require.Equal(t, e, a)
}
func TestFqExp(t *testing.T) {
e := Bls12381FqNew().SetUint64(8)
a := Bls12381FqNew().SetUint64(2)
by := Bls12381FqNew().SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFqSqrt(t *testing.T) {
t1 := Bls12381FqNew().SetUint64(2)
t2 := Bls12381FqNew().Neg(t1)
t3 := Bls12381FqNew().Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.Equal(t, 1, t1.Equal(t3)|t2.Equal(t3))
t1.SetUint64(5)
_, wasSquare = Bls12381FqNew().Sqrt(t1)
require.False(t, wasSquare)
}
func TestFqInvert(t *testing.T) {
twoInv := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0xffffffff, 0xac425bfd0001a401, 0xccc627f7f65e27fa, 0xc1258acd66282b7})
two := Bls12381FqNew().SetUint64(2)
a, inverted := Bls12381FqNew().Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnity := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0xb9b58d8c5f0e466a, 0x5b1b4c801819d7ec, 0x0af53ae352a31e64, 0x5bf3adda19e9b27b})
rootOfUnityInv := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0x4256481adcf3219a, 0x45f37b7f96b6cad3, 0xf9c3f1d75f7a3b27, 0x2d2fc049658afd43})
a, inverted = Bls12381FqNew().Invert(rootOfUnity)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := Bls12381FqNew().SetUint64(9)
rhs := Bls12381FqNew().SetUint64(3)
rhsInv, inverted := Bls12381FqNew().Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, Bls12381FqNew().Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = Bls12381FqNew().Invert(rhs)
require.False(t, inverted)
}
func TestFqCMove(t *testing.T) {
t1 := Bls12381FqNew().SetUint64(5)
t2 := Bls12381FqNew().SetUint64(10)
require.Equal(t, t1, Bls12381FqNew().CMove(t1, t2, 0))
require.Equal(t, t2, Bls12381FqNew().CMove(t1, t2, 1))
}
func TestFqBytes(t *testing.T) {
t1 := Bls12381FqNew().SetUint64(99)
seq := t1.Bytes()
t2, err := Bls12381FqNew().SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFqCmp(t *testing.T) {
tests := []struct {
a *native.Field
b *native.Field
e int
}{
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{2731658267414164836, 14655288906067898431, 6537465423330262322, 8306191141697566219}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{6472764012681988529, 10848812988401906064, 2961825807536828898, 4282183981941645679}),
e: 1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{8023004109510539223, 4652004072850285717, 1877219145646046927, 383214385093921911}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{10099384440823804262, 16139476942229308465, 8636966320777393798, 5435928725024696785}),
e: -1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{3741840066202388211, 12165774400417314871, 16619312580230515379, 16195032234110087705}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{3905865991286066744, 543690822309071825, 17963103015950210055, 3745476720756119742}),
e: 1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{16660853697936147788, 7799793619412111108, 13515141085171033220, 2641079731236069032}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{17790588295388238399, 571847801379669440, 14537208974498222469, 12792570372087452754}),
e: -1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{3912839285384959186, 2701177075110484070, 6453856448115499033, 6475797457962597458}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{1282566391665688512, 13503640416992806563, 2962240104675990153, 3374904770947067689}),
e: 1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{5716631803409360103, 7859567470082614154, 12747956220853330146, 18434584096087315020}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{16317076441459028418, 12854146980376319601, 2258436689269031143, 9531877130792223752}),
e: 1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{17955191469941083403, 10350326247207200880, 17263512235150705075, 12700328451238078022}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{6767595547459644695, 7146403825494928147, 12269344038346710612, 9122477829383225603}),
e: 1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{17099388671847024438, 6426264987820696548, 10641143464957227405, 7709745403700754098}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{10799154372990268556, 17178492485719929374, 5705777922258988797, 8051037767683567782}),
e: -1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{4567139260680454325, 1629385880182139061, 16607020832317899145, 1261011562621553200}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{13487234491304534488, 17872642955936089265, 17651026784972590233, 9468934643333871559}),
e: -1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{18071070103467571798, 11787850505799426140, 10631355976141928593, 4867785203635092610}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{12596443599426461624, 10176122686151524591, 17075755296887483439, 6726169532695070719}),
e: -1,
},
}
for _, test := range tests {
require.Equal(t, test.e, test.a.Cmp(test.b))
require.Equal(t, -test.e, test.b.Cmp(test.a))
require.Equal(t, 0, test.a.Cmp(test.a))
require.Equal(t, 0, test.b.Cmp(test.b))
}
}
func TestFqBigInt(t *testing.T) {
t1 := Bls12381FqNew().SetBigInt(big.NewInt(9999))
t2 := Bls12381FqNew().SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0x673053fc60e06500, 0x86e6d480b4f76ada, 0x7fc68f9fefa23291, 0x3fb17f49bdda126d})
b := new(big.Int).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Neg(e)
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFqSetBytesWide(t *testing.T) {
e := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0xc759fba87ff8c5a6, 0x9ef5194839e7df44, 0x21375d22b678bf0e, 0x38b105387033fd57})
a := Bls12381FqNew().SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
func TestFqSetBytesWideBigInt(t *testing.T) {
params := getBls12381FqParams()
var tv2 [64]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, params.BiModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a := Bls12381FqNew().SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
func TestFqToMontgomery(t *testing.T) {
v := Bls12381FqNew().SetUint64(2)
require.Equal(t, [native.FieldLimbs]uint64{0x3fffffffc, 0xb1096ff400069004, 0x33189fdfd9789fea, 0x304962b3598a0adf}, v.Value)
}
func TestFqFromMontgomery(t *testing.T) {
e := [native.FieldLimbs]uint64{2, 0, 0, 0}
a := [native.FieldLimbs]uint64{0, 0, 0, 0}
v := Bls12381FqNew().SetUint64(2)
v.Arithmetic.FromMontgomery(&a, &v.Value)
require.Equal(t, e, a)
}
File diff suppressed because it is too large Load Diff
+412
View File
@@ -0,0 +1,412 @@
package bls12381
import (
crand "crypto/rand"
"encoding/hex"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves/native"
)
func TestG1IsOnCurve(t *testing.T) {
require.Equal(t, 1, new(G1).Identity().IsOnCurve())
require.Equal(t, 1, new(G1).Generator().IsOnCurve())
z := fp{
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
}
gen := new(G1).Generator()
test := G1{
x: *(gen.x.Mul(&gen.x, &z)),
y: *(gen.y.Mul(&gen.y, &z)),
z: z,
}
require.Equal(t, 1, test.IsOnCurve())
test.x = z
require.Equal(t, 0, test.IsOnCurve())
}
func TestG1Equality(t *testing.T) {
a := new(G1).Generator()
b := new(G1).Identity()
require.Equal(t, 1, a.Equal(a))
require.Equal(t, 1, b.Equal(b))
require.Equal(t, 0, a.Equal(b))
require.Equal(t, 0, b.Equal(a))
z := fp{
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
}
c := G1{}
c.x.Mul(&a.x, &z)
c.y.Mul(&a.y, &z)
c.z.Set(&z)
require.Equal(t, 1, c.IsOnCurve())
require.Equal(t, 1, a.Equal(&c))
require.Equal(t, 0, b.Equal(&c))
c.y.Neg(&c.y)
require.Equal(t, 1, c.IsOnCurve())
require.Equal(t, 0, a.Equal(&c))
c.y.Neg(&c.y)
c.x.Set(&z)
require.Equal(t, 0, c.IsOnCurve())
}
func TestG1Double(t *testing.T) {
t0 := new(G1).Identity()
t0.Double(t0)
require.Equal(t, 1, t0.IsIdentity())
require.Equal(t, 1, t0.IsOnCurve())
t0.Double(t0.Generator())
require.Equal(t, 0, t0.IsIdentity())
require.Equal(t, 1, t0.IsOnCurve())
e := G1{
x: fp{
0x53e978ce58a9ba3c,
0x3ea0583c4f3d65f9,
0x4d20bb47f0012960,
0xa54c664ae5b2b5d9,
0x26b552a39d7eb21f,
0x0008895d26e68785,
},
y: fp{
0x70110b3298293940,
0xda33c5393f1f6afc,
0xb86edfd16a5aa785,
0xaec6d1c9e7b1c895,
0x25cfc2b522d11720,
0x06361c83f8d09b15,
},
z: r,
}
require.Equal(t, 1, e.Equal(t0))
}
func TestG1Add(t *testing.T) {
g := new(G1).Generator()
a := new(G1).Identity()
b := new(G1).Identity()
c := new(G1).Add(a, b)
require.Equal(t, 1, c.IsIdentity())
require.Equal(t, 1, c.IsOnCurve())
b.Generator()
z := fp{
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
}
b.x.Mul(&b.x, &z)
b.y.Mul(&b.y, &z)
b.z.Set(&z)
c.Add(a, b)
require.Equal(t, 0, c.IsIdentity())
require.Equal(t, 1, g.Equal(c))
a.Generator()
a.Double(a)
a.Double(a)
b.Generator()
b.Double(b)
c.Add(a, b)
d := new(G1).Generator()
for i := 0; i < 5; i++ {
d.Add(d, g)
}
require.Equal(t, 0, c.IsIdentity())
require.Equal(t, 1, c.IsOnCurve())
require.Equal(t, 0, d.IsIdentity())
require.Equal(t, 1, d.IsOnCurve())
require.Equal(t, 1, c.Equal(d))
beta := fp{
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x03f97d6e83d050d2,
0x18f0206554638741,
}
beta.Square(&beta)
a.Generator()
a.Double(a)
a.Double(a)
b.x.Mul(&a.x, &beta)
b.y.Neg(&a.y)
b.z.Set(&a.z)
require.Equal(t, 1, a.IsOnCurve())
require.Equal(t, 1, b.IsOnCurve())
c.Add(a, b)
d.x.Set(&fp{
0x29e1e987ef68f2d0,
0xc5f3ec531db03233,
0xacd6c4b6ca19730f,
0x18ad9e827bc2bab7,
0x46e3b2c5785cc7a9,
0x07e571d42d22ddd6,
})
d.y.Set(&fp{
0x94d117a7e5a539e7,
0x8e17ef673d4b5d22,
0x9d746aaf508a33ea,
0x8c6d883d2516c9a2,
0x0bc3b8d5fb0447f7,
0x07bfa4c7210f4f44,
})
d.z.SetOne()
require.Equal(t, 1, c.Equal(d))
}
func TestG1Sub(t *testing.T) {
a := new(G1).Generator()
b := new(G1).Generator()
require.Equal(t, 1, a.Sub(a, b).IsIdentity())
b.Double(b)
a.Generator()
require.Equal(t, 1, b.Sub(b, a).Equal(a))
}
func TestG1Mul(t *testing.T) {
g := new(G1).Generator()
a := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{
0x2b568297a56da71c,
0xd8c39ecb0ef375d1,
0x435c38da67bfbf96,
0x8088a05026b659b2,
})
b := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{
0x785fdd9b26ef8b85,
0xc997f25837695c18,
0x4c8dbc39e7b756c1,
0x70d9b6cc6d87df20,
})
c := Bls12381FqNew().Mul(a, b)
t1 := new(G1).Generator()
t1.Mul(t1, a)
t1.Mul(t1, b)
require.Equal(t, 1, t1.Equal(g.Mul(g, c)))
}
func TestG1Neg(t *testing.T) {
a := new(G1).Generator()
b := new(G1).Neg(a)
require.Equal(t, 1, new(G1).Add(a, b).IsIdentity())
require.Equal(t, 1, new(G1).Sub(a, b.Neg(b)).IsIdentity())
a.Identity()
require.Equal(t, 1, a.Neg(a).IsIdentity())
}
func TestG1InCorrectSubgroup(t *testing.T) {
// ZCash test vector
a := G1{
x: fp{
0x0abaf895b97e43c8,
0xba4c6432eb9b61b0,
0x12506f52adfe307f,
0x75028c3439336b72,
0x84744f05b8e9bd71,
0x113d554fb09554f7,
},
y: fp{
0x73e90e88f5cf01c0,
0x37007b65dd3197e2,
0x5cf9a1992f0d7c78,
0x4f83c10b9eb3330d,
0xf6a63f6f07f60961,
0x0c53b5b97e634df3,
},
z: *(new(fp).SetOne()),
}
require.Equal(t, 0, a.InCorrectSubgroup())
require.Equal(t, 1, new(G1).Identity().InCorrectSubgroup())
require.Equal(t, 1, new(G1).Generator().InCorrectSubgroup())
}
func TestG1MulByX(t *testing.T) {
// multiplying by `x` a point in G1 is the same as multiplying by
// the equivalent scalar.
generator := new(G1).Generator()
x := Bls12381FqNew().SetUint64(paramX)
x.Neg(x)
lhs := new(G1).Mul(generator, x)
rhs := new(G1).MulByX(generator)
require.Equal(t, 1, lhs.Equal(rhs))
pt := new(G1).Generator()
s := Bls12381FqNew().SetUint64(42)
pt.Mul(pt, s)
lhs.Mul(pt, x)
rhs.MulByX(pt)
require.Equal(t, 1, lhs.Equal(rhs))
}
func TestG1ClearCofactor(t *testing.T) {
// the generator (and the identity) are always on the curve,
// even after clearing the cofactor
generator := new(G1).Generator()
generator.ClearCofactor(generator)
require.Equal(t, 1, generator.IsOnCurve())
id := new(G1).Identity()
id.ClearCofactor(id)
require.Equal(t, 1, id.IsOnCurve())
z := fp{
0x3d2d1c670671394e,
0x0ee3a800a2f7c1ca,
0x270f4f21da2e5050,
0xe02840a53f1be768,
0x55debeb597512690,
0x08bd25353dc8f791,
}
point := G1{
x: fp{
0x48af5ff540c817f0,
0xd73893acaf379d5a,
0xe6c43584e18e023c,
0x1eda39c30f188b3e,
0xf618c6d3ccc0f8d8,
0x0073542cd671e16c,
},
y: fp{
0x57bf8be79461d0ba,
0xfc61459cee3547c3,
0x0d23567df1ef147b,
0x0ee187bcce1d9b64,
0xb0c8cfbe9dc8fdc1,
0x1328661767ef368b,
},
z: *(&fp{}).Set(&z),
}
point.x.Mul(&point.x, &z)
point.z.Square(&z)
point.z.Mul(&point.z, &z)
require.Equal(t, 1, point.IsOnCurve())
require.Equal(t, 0, point.InCorrectSubgroup())
clearedPoint := new(G1).ClearCofactor(&point)
require.Equal(t, 1, clearedPoint.IsOnCurve())
require.Equal(t, 1, clearedPoint.InCorrectSubgroup())
// in BLS12-381 the cofactor in G1 can be
// cleared multiplying by (1-x)
hEff := Bls12381FqNew().SetOne()
hEff.Add(hEff, Bls12381FqNew().SetUint64(paramX))
point.Mul(&point, hEff)
require.Equal(t, 1, clearedPoint.Equal(&point))
}
func TestG1Hash(t *testing.T) {
dst := []byte("QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_")
tests := []struct {
input, expected string
}{
{"", "052926add2207b76ca4fa57a8734416c8dc95e24501772c814278700eed6d1e4e8cf62d9c09db0fac349612b759e79a108ba738453bfed09cb546dbb0783dbb3a5f1f566ed67bb6be0e8c67e2e81a4cc68ee29813bb7994998f3eae0c9c6a265"},
{"abc", "03567bc5ef9c690c2ab2ecdf6a96ef1c139cc0b2f284dca0a9a7943388a49a3aee664ba5379a7655d3c68900be2f69030b9c15f3fe6e5cf4211f346271d7b01c8f3b28be689c8429c85b67af215533311f0b8dfaaa154fa6b88176c229f2885d"},
{"abcdef0123456789", "11e0b079dea29a68f0383ee94fed1b940995272407e3bb916bbf268c263ddd57a6a27200a784cbc248e84f357ce82d9803a87ae2caf14e8ee52e51fa2ed8eefe80f02457004ba4d486d6aa1f517c0889501dc7413753f9599b099ebcbbd2d709"},
{"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "15f68eaa693b95ccb85215dc65fa81038d69629f70aeee0d0f677cf22285e7bf58d7cb86eefe8f2e9bc3f8cb84fac4881807a1d50c29f430b8cafc4f8638dfeeadf51211e1602a5f184443076715f91bb90a48ba1e370edce6ae1062f5e6dd38"},
{"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "082aabae8b7dedb0e78aeb619ad3bfd9277a2f77ba7fad20ef6aabdc6c31d19ba5a6d12283553294c1825c4b3ca2dcfe05b84ae5a942248eea39e1d91030458c40153f3b654ab7872d779ad1e942856a20c438e8d99bc8abfbf74729ce1f7ac8"},
}
pt := new(G1).Identity()
ept := new(G1).Identity()
var b [WideFieldBytes]byte
for _, tst := range tests {
i := []byte(tst.input)
e, _ := hex.DecodeString(tst.expected)
copy(b[:], e)
_, _ = ept.FromUncompressed(&b)
pt.Hash(native.EllipticPointHasherSha256(), i, dst)
require.Equal(t, 1, pt.Equal(ept))
}
}
func TestSerialization(t *testing.T) {
a := new(G1).Hash(native.EllipticPointHasherSha256(), []byte("a"), []byte("BLS12381G1_XMD:SHA-256_SSWU_RO_"))
b := new(G1).Hash(native.EllipticPointHasherSha256(), []byte("b"), []byte("BLS12381G1_XMD:SHA-256_SSWU_RO_"))
aBytes := a.ToCompressed()
bBytes := b.ToCompressed()
aa, err := new(G1).FromCompressed(&aBytes)
require.NoError(t, err)
require.Equal(t, 1, a.Equal(aa))
bb, err := new(G1).FromCompressed(&bBytes)
require.NoError(t, err)
require.Equal(t, 1, b.Equal(bb))
auBytes := a.ToUncompressed()
buBytes := b.ToUncompressed()
_, err = aa.FromUncompressed(&auBytes)
require.NoError(t, err)
require.Equal(t, 1, a.Equal(aa))
_, err = bb.FromUncompressed(&buBytes)
require.NoError(t, err)
require.Equal(t, 1, b.Equal(bb))
bBytes = a.ToCompressed()
a.Neg(a)
aBytes = a.ToCompressed()
_, err = aa.FromCompressed(&aBytes)
require.NoError(t, err)
require.Equal(t, 1, a.Equal(aa))
_, err = aa.FromCompressed(&bBytes)
require.NoError(t, err)
require.Equal(t, 0, a.Equal(aa))
require.Equal(t, 1, aa.Equal(a.Neg(a)))
}
func TestSumOfProducts(t *testing.T) {
var b [64]byte
h0, _ := new(G1).Random(crand.Reader)
_, _ = crand.Read(b[:])
s := Bls12381FqNew().SetBytesWide(&b)
_, _ = crand.Read(b[:])
sTilde := Bls12381FqNew().SetBytesWide(&b)
_, _ = crand.Read(b[:])
c := Bls12381FqNew().SetBytesWide(&b)
lhs := new(G1).Mul(h0, s)
rhs, _ := new(G1).SumOfProducts([]*G1{h0}, []*native.Field{s})
require.Equal(t, 1, lhs.Equal(rhs))
u := new(G1).Mul(h0, s)
uTilde := new(G1).Mul(h0, sTilde)
sHat := Bls12381FqNew().Mul(c, s)
sHat.Sub(sTilde, sHat)
rhs.Mul(u, c)
rhs.Add(rhs, new(G1).Mul(h0, sHat))
require.Equal(t, 1, uTilde.Equal(rhs))
_, _ = rhs.SumOfProducts([]*G1{u, h0}, []*native.Field{c, sHat})
require.Equal(t, 1, uTilde.Equal(rhs))
}
File diff suppressed because it is too large Load Diff
+554
View File
@@ -0,0 +1,554 @@
package bls12381
import (
crand "crypto/rand"
"encoding/hex"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves/native"
)
func TestG2IsOnCurve(t *testing.T) {
require.Equal(t, 1, new(G2).Identity().IsOnCurve())
require.Equal(t, 1, new(G2).Generator().IsOnCurve())
z := fp2{
A: fp{
0xba7a_fa1f_9a6f_e250,
0xfa0f_5b59_5eaf_e731,
0x3bdc_4776_94c3_06e7,
0x2149_be4b_3949_fa24,
0x64aa_6e06_49b2_078c,
0x12b1_08ac_3364_3c3e,
},
B: fp{
0x1253_25df_3d35_b5a8,
0xdc46_9ef5_555d_7fe3,
0x02d7_16d2_4431_06a9,
0x05a1_db59_a6ff_37d0,
0x7cf7_784e_5300_bb8f,
0x16a8_8922_c7a5_e844,
},
}
test := new(G2).Generator()
test.x.Mul(&test.x, &z)
test.y.Mul(&test.y, &z)
test.z.Set(&z)
require.Equal(t, 1, test.IsOnCurve())
test.x.Set(&z)
require.Equal(t, 0, test.IsOnCurve())
}
func TestG2Equal(t *testing.T) {
a := new(G2).Generator()
b := new(G2).Identity()
require.Equal(t, 1, a.Equal(a))
require.Equal(t, 0, a.Equal(b))
require.Equal(t, 1, b.Equal(b))
}
func TestG2ToAffine(t *testing.T) {
a := new(G2).Generator()
z := fp2{
A: fp{
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
},
B: fp{
0x125325df3d35b5a8,
0xdc469ef5555d7fe3,
0x02d716d2443106a9,
0x05a1db59a6ff37d0,
0x7cf7784e5300bb8f,
0x16a88922c7a5e844,
},
}
a.x.Mul(&a.x, &z)
a.y.Mul(&a.y, &z)
a.z.Set(&z)
require.Equal(t, 1, a.ToAffine(a).Equal(new(G2).Generator()))
}
func TestG2Double(t *testing.T) {
a := new(G2).Identity()
require.Equal(t, 1, a.Double(a).IsIdentity())
a.Generator()
a.Double(a)
e := G2{
x: fp2{
A: fp{
0xe9d9e2da9620f98b,
0x54f1199346b97f36,
0x3db3b820376bed27,
0xcfdb31c9b0b64f4c,
0x41d7c12786354493,
0x05710794c255c064,
},
B: fp{
0xd6c1d3ca6ea0d06e,
0xda0cbd905595489f,
0x4f5352d43479221d,
0x8ade5d736f8c97e0,
0x48cc8433925ef70e,
0x08d7ea71ea91ef81,
},
},
y: fp2{
A: fp{
0x15ba26eb4b0d186f,
0x0d086d64b7e9e01e,
0xc8b848dd652f4c78,
0xeecf46a6123bae4f,
0x255e8dd8b6dc812a,
0x164142af21dcf93f,
},
B: fp{
0xf9b4a1a895984db4,
0xd417b114cccff748,
0x6856301fc89f086e,
0x41c777878931e3da,
0x3556b155066a2105,
0x00acf7d325cb89cf,
},
},
z: *((&fp2{}).SetOne()),
}
require.Equal(t, 1, e.Equal(a))
}
func TestG2Add(t *testing.T) {
a := new(G2).Identity()
b := new(G2).Identity()
c := new(G2).Add(a, b)
require.Equal(t, 1, c.IsIdentity())
b.Generator()
c.Add(a, b)
require.Equal(t, 1, c.Equal(b))
a.Generator()
a.Double(a)
a.Double(a)
b.Double(b)
c.Add(a, b)
d := new(G2).Generator()
e := new(G2).Generator()
for i := 0; i < 5; i++ {
e.Add(e, d)
}
require.Equal(t, 1, e.Equal(c))
// Degenerate case
beta := fp2{
A: fp{
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x03f97d6e83d050d2,
0x18f0206554638741,
},
B: fp{},
}
beta.Square(&beta)
b.x.Mul(&a.x, &beta)
b.y.Neg(&a.y)
b.z.Set(&a.z)
require.Equal(t, 1, b.IsOnCurve())
c.Add(a, b)
e.x.Set(&fp2{
A: fp{
0x705abc799ca773d3,
0xfe132292c1d4bf08,
0xf37ece3e07b2b466,
0x887e1c43f447e301,
0x1e0970d033bc77e8,
0x1985c81e20a693f2,
},
B: fp{
0x1d79b25db36ab924,
0x23948e4d529639d3,
0x471ba7fb0d006297,
0x2c36d4b4465dc4c0,
0x82bbc3cfec67f538,
0x051d2728b67bf952,
},
})
e.y.Set(&fp2{
A: fp{
0x41b1bbf6576c0abf,
0xb6cc93713f7a0f9a,
0x6b65b43e48f3f01f,
0xfb7a4cfcaf81be4f,
0x3e32dadc6ec22cb6,
0x0bb0fc49d79807e3,
},
B: fp{
0x7d1397788f5f2ddf,
0xab2907144ff0d8e8,
0x5b7573e0cdb91f92,
0x4cb8932dd31daf28,
0x62bbfac6db052a54,
0x11f95c16d14c3bbe,
},
})
e.z.SetOne()
require.Equal(t, 1, e.Equal(c))
}
func TestG2Neg(t *testing.T) {
a := new(G2).Generator()
b := new(G2).Neg(a)
require.Equal(t, 1, new(G2).Add(a, b).IsIdentity())
require.Equal(t, 1, new(G2).Sub(a, b.Neg(b)).IsIdentity())
a.Identity()
require.Equal(t, 1, a.Neg(a).IsIdentity())
}
func TestG2Mul(t *testing.T) {
g := new(G2).Generator()
a := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{
0x2b56_8297_a56d_a71c,
0xd8c3_9ecb_0ef3_75d1,
0x435c_38da_67bf_bf96,
0x8088_a050_26b6_59b2,
})
b := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{
0x785f_dd9b_26ef_8b85,
0xc997_f258_3769_5c18,
0x4c8d_bc39_e7b7_56c1,
0x70d9_b6cc_6d87_df20,
})
c := Bls12381FqNew().Mul(a, b)
t1 := new(G2).Generator()
t1.Mul(t1, a)
t1.Mul(t1, b)
require.Equal(t, 1, t1.Equal(g.Mul(g, c)))
}
func TestG2InCorrectSubgroup(t *testing.T) {
a := G2{
x: fp2{
A: fp{
0x89f550c813db6431,
0xa50be8c456cd8a1a,
0xa45b374114cae851,
0xbb6190f5bf7fff63,
0x970ca02c3ba80bc7,
0x02b85d24e840fbac,
},
B: fp{
0x6888bc53d70716dc,
0x3dea6b4117682d70,
0xd8f5f930500ca354,
0x6b5ecb6556f5c155,
0xc96bef0434778ab0,
0x05081505515006ad,
},
},
y: fp2{
A: fp{
0x3cf1ea0d434b0f40,
0x1a0dc610e603e333,
0x7f89956160c72fa0,
0x25ee03decf6431c5,
0xeee8e206ec0fe137,
0x097592b226dfef28,
},
B: fp{
0x71e8bb5f29247367,
0xa5fe049e211831ce,
0x0ce6b354502a3896,
0x93b012000997314e,
0x6759f3b6aa5b42ac,
0x156944c4dfe92bbb,
},
},
z: *(&fp2{}).SetOne(),
}
require.Equal(t, 0, a.InCorrectSubgroup())
require.Equal(t, 1, new(G2).Identity().InCorrectSubgroup())
require.Equal(t, 1, new(G2).Generator().InCorrectSubgroup())
}
func TestG2MulByX(t *testing.T) {
// multiplying by `x` a point in G2 is the same as multiplying by
// the equivalent scalar.
x := Bls12381FqNew().SetUint64(paramX)
x.Neg(x)
t1 := new(G2).Generator()
t1.MulByX(t1)
t2 := new(G2).Generator()
t2.Mul(t2, x)
require.Equal(t, 1, t1.Equal(t2))
point := new(G2).Generator()
a := Bls12381FqNew().SetUint64(42)
point.Mul(point, a)
t1.MulByX(point)
t2.Mul(point, x)
require.Equal(t, 1, t1.Equal(t2))
}
func TestG2Psi(t *testing.T) {
generator := new(G2).Generator()
z := fp2{
A: fp{
0x0ef2ddffab187c0a,
0x2424522b7d5ecbfc,
0xc6f341a3398054f4,
0x5523ddf409502df0,
0xd55c0b5a88e0dd97,
0x066428d704923e52,
},
B: fp{
0x538bbe0c95b4878d,
0xad04a50379522881,
0x6d5c05bf5c12fb64,
0x4ce4a069a2d34787,
0x59ea6c8d0dffaeaf,
0x0d42a083a75bd6f3,
},
}
// `point` is a random point in the curve
point := G2{
x: fp2{
A: fp{
0xee4c8cb7c047eaf2,
0x44ca22eee036b604,
0x33b3affb2aefe101,
0x15d3e45bbafaeb02,
0x7bfc2154cd7419a4,
0x0a2d0c2b756e5edc,
},
B: fp{
0xfc224361029a8777,
0x4cbf2baab8740924,
0xc5008c6ec6592c89,
0xecc2c57b472a9c2d,
0x8613eafd9d81ffb1,
0x10fe54daa2d3d495,
},
},
y: fp2{
A: fp{
0x7de7edc43953b75c,
0x58be1d2de35e87dc,
0x5731d30b0e337b40,
0xbe93b60cfeaae4c9,
0x8b22c203764bedca,
0x01616c8d1033b771,
},
B: fp{
0xea126fe476b5733b,
0x85cee68b5dae1652,
0x98247779f7272b04,
0xa649c8b468c6e808,
0xb5b9a62dff0c4e45,
0x1555b67fc7bbe73d,
},
},
z: *(&fp2{}).Set(&z),
}
point.x.Mul(&point.x, &z)
point.z.Square(&point.z)
point.z.Mul(&point.z, &z)
require.Equal(t, 1, point.IsOnCurve())
// psi2(P) = psi(psi(P))
tv1 := new(G2).psi2(generator)
tv2 := new(G2).psi(generator)
tv2.psi(tv2)
require.Equal(t, 1, tv1.Equal(tv2))
tv1.psi2(&point)
tv2.psi(&point)
tv2.psi(tv2)
require.Equal(t, 1, tv1.Equal(tv2))
// psi(P) is a morphism
tv1.Double(generator)
tv1.psi(tv1)
tv2.psi(generator)
tv2.Double(tv2)
require.Equal(t, 1, tv1.Equal(tv2))
tv1.psi(&point)
tv2.psi(generator)
tv1.Add(tv1, tv2)
tv2.Set(&point)
tv3 := new(G2).Generator()
tv2.Add(tv2, tv3)
tv2.psi(tv2)
require.Equal(t, 1, tv1.Equal(tv2))
}
func TestG2ClearCofactor(t *testing.T) {
z := fp2{
A: fp{
0x0ef2ddffab187c0a,
0x2424522b7d5ecbfc,
0xc6f341a3398054f4,
0x5523ddf409502df0,
0xd55c0b5a88e0dd97,
0x066428d704923e52,
},
B: fp{
0x538bbe0c95b4878d,
0xad04a50379522881,
0x6d5c05bf5c12fb64,
0x4ce4a069a2d34787,
0x59ea6c8d0dffaeaf,
0x0d42a083a75bd6f3,
},
}
// `point` is a random point in the curve
point := G2{
x: fp2{
A: fp{
0xee4c8cb7c047eaf2,
0x44ca22eee036b604,
0x33b3affb2aefe101,
0x15d3e45bbafaeb02,
0x7bfc2154cd7419a4,
0x0a2d0c2b756e5edc,
},
B: fp{
0xfc224361029a8777,
0x4cbf2baab8740924,
0xc5008c6ec6592c89,
0xecc2c57b472a9c2d,
0x8613eafd9d81ffb1,
0x10fe54daa2d3d495,
},
},
y: fp2{
A: fp{
0x7de7edc43953b75c,
0x58be1d2de35e87dc,
0x5731d30b0e337b40,
0xbe93b60cfeaae4c9,
0x8b22c203764bedca,
0x01616c8d1033b771,
},
B: fp{
0xea126fe476b5733b,
0x85cee68b5dae1652,
0x98247779f7272b04,
0xa649c8b468c6e808,
0xb5b9a62dff0c4e45,
0x1555b67fc7bbe73d,
},
},
z: fp2{},
}
point.x.Mul(&point.x, &z)
point.z.Square(&z)
point.z.Mul(&point.z, &z)
require.Equal(t, 1, point.IsOnCurve())
require.Equal(t, 0, point.InCorrectSubgroup())
clearedPoint := new(G2).ClearCofactor(&point)
require.Equal(t, 1, clearedPoint.IsOnCurve())
require.Equal(t, 1, clearedPoint.InCorrectSubgroup())
// the generator (and the identity) are always on the curve,
// even after clearing the cofactor
generator := new(G2).Generator()
generator.ClearCofactor(generator)
require.Equal(t, 1, generator.InCorrectSubgroup())
id := new(G2).Identity()
id.ClearCofactor(id)
require.Equal(t, 1, id.InCorrectSubgroup())
// test the effect on q-torsion points multiplying by h_eff modulo q
// h_eff % q = 0x2b116900400069009a40200040001ffff
hEffModq := [native.FieldBytes]byte{
0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, 0x16,
0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
}
generator.Generator()
generator.multiply(generator, &hEffModq)
point.Generator().ClearCofactor(&point)
require.Equal(t, 1, point.Equal(generator))
point.ClearCofactor(clearedPoint)
require.Equal(t, 1, point.Equal(clearedPoint.multiply(clearedPoint, &hEffModq)))
}
func TestG2Hash(t *testing.T) {
dst := []byte("QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_")
tests := []struct {
input, expected string
}{
{"", "05cb8437535e20ecffaef7752baddf98034139c38452458baeefab379ba13dff5bf5dd71b72418717047f5b0f37da03d0141ebfbdca40eb85b87142e130ab689c673cf60f1a3e98d69335266f30d9b8d4ac44c1038e9dcdd5393faf5c41fb78a12424ac32561493f3fe3c260708a12b7c620e7be00099a974e259ddc7d1f6395c3c811cdd19f1e8dbf3e9ecfdcbab8d60503921d7f6a12805e72940b963c0cf3471c7b2a524950ca195d11062ee75ec076daf2d4bc358c4b190c0c98064fdd92"},
{"abc", "139cddbccdc5e91b9623efd38c49f81a6f83f175e80b06fc374de9eb4b41dfe4ca3a230ed250fbe3a2acf73a41177fd802c2d18e033b960562aae3cab37a27ce00d80ccd5ba4b7fe0e7a210245129dbec7780ccc7954725f4168aff2787776e600aa65dae3c8d732d10ecd2c50f8a1baf3001578f71c694e03866e9f3d49ac1e1ce70dd94a733534f106d4cec0eddd161787327b68159716a37440985269cf584bcb1e621d3a7202be6ea05c4cfe244aeb197642555a0645fb87bf7466b2ba48"},
{"abcdef0123456789", "190d119345b94fbd15497bcba94ecf7db2cbfd1e1fe7da034d26cbba169fb3968288b3fafb265f9ebd380512a71c3f2c121982811d2491fde9ba7ed31ef9ca474f0e1501297f68c298e9f4c0028add35aea8bb83d53c08cfc007c1e005723cd00bb5e7572275c567462d91807de765611490205a941a5a6af3b1691bfe596c31225d3aabdf15faff860cb4ef17c7c3be05571a0f8d3c08d094576981f4a3b8eda0a8e771fcdcc8ecceaf1356a6acf17574518acb506e435b639353c2e14827c8"},
{"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "0934aba516a52d8ae479939a91998299c76d39cc0c035cd18813bec433f587e2d7a4fef038260eef0cef4d02aae3eb9119a84dd7248a1066f737cc34502ee5555bd3c19f2ecdb3c7d9e24dc65d4e25e50d83f0f77105e955d78f4762d33c17da09bcccfa036b4847c9950780733633f13619994394c23ff0b32fa6b795844f4a0673e20282d07bc69641cee04f5e566214f81cd421617428bc3b9fe25afbb751d934a00493524bc4e065635b0555084dd54679df1536101b2c979c0152d09192"},
{"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "11fca2ff525572795a801eed17eb12785887c7b63fb77a42be46ce4a34131d71f7a73e95fee3f812aea3de78b4d0156901a6ba2f9a11fa5598b2d8ace0fbe0a0eacb65deceb476fbbcb64fd24557c2f4b18ecfc5663e54ae16a84f5ab7f6253403a47f8e6d1763ba0cad63d6114c0accbef65707825a511b251a660a9b3994249ae4e63fac38b23da0c398689ee2ab520b6798718c8aed24bc19cb27f866f1c9effcdbf92397ad6448b5c9db90d2b9da6cbabf48adc1adf59a1a28344e79d57e"},
}
pt := new(G2).Identity()
ept := new(G2).Identity()
var b [DoubleWideFieldBytes]byte
for _, tst := range tests {
i := []byte(tst.input)
e, _ := hex.DecodeString(tst.expected)
copy(b[:], e)
_, _ = ept.FromUncompressed(&b)
pt.Hash(native.EllipticPointHasherSha256(), i, dst)
require.Equal(t, 1, pt.Equal(ept))
}
}
func TestG2SumOfProducts(t *testing.T) {
var b [64]byte
h0, _ := new(G2).Random(crand.Reader)
_, _ = crand.Read(b[:])
s := Bls12381FqNew().SetBytesWide(&b)
_, _ = crand.Read(b[:])
sTilde := Bls12381FqNew().SetBytesWide(&b)
_, _ = crand.Read(b[:])
c := Bls12381FqNew().SetBytesWide(&b)
lhs := new(G2).Mul(h0, s)
rhs, _ := new(G2).SumOfProducts([]*G2{h0}, []*native.Field{s})
require.Equal(t, 1, lhs.Equal(rhs))
u := new(G2).Mul(h0, s)
uTilde := new(G2).Mul(h0, sTilde)
sHat := Bls12381FqNew().Mul(c, s)
sHat.Sub(sTilde, sHat)
rhs.Mul(u, c)
rhs.Add(rhs, new(G2).Mul(h0, sHat))
require.Equal(t, 1, uTilde.Equal(rhs))
_, _ = rhs.SumOfProducts([]*G2{u, h0}, []*native.Field{c, sHat})
require.Equal(t, 1, uTilde.Equal(rhs))
}
+434
View File
@@ -0,0 +1,434 @@
package bls12381
import (
"io"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/internal"
)
// GtFieldBytes is the number of bytes needed to represent this field
const GtFieldBytes = 576
// Gt is the target group
type Gt fp12
// Random generates a random field element
func (gt *Gt) Random(reader io.Reader) (*Gt, error) {
_, err := (*fp12)(gt).Random(reader)
return gt, err
}
// FinalExponentiation performs a "final exponentiation" routine to convert the result
// of a Miller loop into an element of `Gt` with help of efficient squaring
// operation in the so-called `cyclotomic subgroup` of `Fq6` so that
// it can be compared with other elements of `Gt`.
func (gt *Gt) FinalExponentiation(a *Gt) *Gt {
var t0, t1, t2, t3, t4, t5, t6, t fp12
t0.FrobeniusMap((*fp12)(a))
t0.FrobeniusMap(&t0)
t0.FrobeniusMap(&t0)
t0.FrobeniusMap(&t0)
t0.FrobeniusMap(&t0)
t0.FrobeniusMap(&t0)
// Shouldn't happen since we enforce `a` to be non-zero but just in case
_, wasInverted := t1.Invert((*fp12)(a))
t2.Mul(&t0, &t1)
t1.Set(&t2)
t2.FrobeniusMap(&t2)
t2.FrobeniusMap(&t2)
t2.Mul(&t2, &t1)
t1.cyclotomicSquare(&t2)
t1.Conjugate(&t1)
t3.cyclotomicExp(&t2)
t4.cyclotomicSquare(&t3)
t5.Mul(&t1, &t3)
t1.cyclotomicExp(&t5)
t0.cyclotomicExp(&t1)
t6.cyclotomicExp(&t0)
t6.Mul(&t6, &t4)
t4.cyclotomicExp(&t6)
t5.Conjugate(&t5)
t4.Mul(&t4, &t5)
t4.Mul(&t4, &t2)
t5.Conjugate(&t2)
t1.Mul(&t1, &t2)
t1.FrobeniusMap(&t1)
t1.FrobeniusMap(&t1)
t1.FrobeniusMap(&t1)
t6.Mul(&t6, &t5)
t6.FrobeniusMap(&t6)
t3.Mul(&t3, &t0)
t3.FrobeniusMap(&t3)
t3.FrobeniusMap(&t3)
t3.Mul(&t3, &t1)
t3.Mul(&t3, &t6)
t.Mul(&t3, &t4)
(*fp12)(gt).CMove((*fp12)(gt), &t, wasInverted)
return gt
}
// IsZero returns 1 if gt == 0, 0 otherwise
func (gt *Gt) IsZero() int {
return (*fp12)(gt).IsZero()
}
// IsOne returns 1 if gt == 1, 0 otherwise
func (gt *Gt) IsOne() int {
return (*fp12)(gt).IsOne()
}
// SetOne gt = one
func (gt *Gt) SetOne() *Gt {
(*fp12)(gt).SetOne()
return gt
}
// Set copies a into gt
func (gt *Gt) Set(a *Gt) *Gt {
gt.A.Set(&a.A)
gt.B.Set(&a.B)
return gt
}
// Bytes returns the Gt field byte representation
func (gt *Gt) Bytes() [GtFieldBytes]byte {
var out [GtFieldBytes]byte
t := gt.A.A.A.Bytes()
copy(out[:FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.A.A.B.Bytes()
copy(out[FieldBytes:2*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.A.B.A.Bytes()
copy(out[2*FieldBytes:3*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.A.B.B.Bytes()
copy(out[3*FieldBytes:4*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.A.C.A.Bytes()
copy(out[4*FieldBytes:5*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.A.C.B.Bytes()
copy(out[5*FieldBytes:6*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.A.A.Bytes()
copy(out[6*FieldBytes:7*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.A.B.Bytes()
copy(out[7*FieldBytes:8*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.B.A.Bytes()
copy(out[8*FieldBytes:9*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.B.B.Bytes()
copy(out[9*FieldBytes:10*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.C.A.Bytes()
copy(out[10*FieldBytes:11*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.C.B.Bytes()
copy(out[11*FieldBytes:12*FieldBytes], internal.ReverseScalarBytes(t[:]))
return out
}
// SetBytes attempts to convert a big-endian byte representation of
// a scalar into a `Gt`, failing if the input is not canonical.
func (gt *Gt) SetBytes(input *[GtFieldBytes]byte) (*Gt, int) {
var t [FieldBytes]byte
var valid [12]int
copy(t[:], internal.ReverseScalarBytes(input[:FieldBytes]))
_, valid[0] = gt.A.A.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[FieldBytes:2*FieldBytes]))
_, valid[1] = gt.A.A.B.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[2*FieldBytes:3*FieldBytes]))
_, valid[2] = gt.A.B.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[3*FieldBytes:4*FieldBytes]))
_, valid[3] = gt.A.B.B.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[4*FieldBytes:5*FieldBytes]))
_, valid[4] = gt.A.C.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[5*FieldBytes:6*FieldBytes]))
_, valid[5] = gt.A.C.B.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[6*FieldBytes:7*FieldBytes]))
_, valid[6] = gt.B.A.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[7*FieldBytes:8*FieldBytes]))
_, valid[7] = gt.B.A.B.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[8*FieldBytes:9*FieldBytes]))
_, valid[8] = gt.B.B.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[9*FieldBytes:10*FieldBytes]))
_, valid[9] = gt.B.B.B.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[10*FieldBytes:11*FieldBytes]))
_, valid[10] = gt.B.C.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[11*FieldBytes:12*FieldBytes]))
_, valid[11] = gt.B.C.B.SetBytes(&t)
return gt, valid[0] & valid[1] &
valid[2] & valid[3] &
valid[4] & valid[5] &
valid[6] & valid[7] &
valid[8] & valid[9] &
valid[10] & valid[11]
}
// Equal returns 1 if gt == rhs, 0 otherwise
func (gt *Gt) Equal(rhs *Gt) int {
return (*fp12)(gt).Equal((*fp12)(rhs))
}
// Generator returns the base point
func (gt *Gt) Generator() *Gt {
// pairing(&G1::generator(), &G2::generator())
gt.Set((*Gt)(&fp12{
A: fp6{
A: fp2{
A: fp{
0x1972e433a01f85c5,
0x97d32b76fd772538,
0xc8ce546fc96bcdf9,
0xcef63e7366d40614,
0xa611342781843780,
0x13f3448a3fc6d825,
},
B: fp{
0xd26331b02e9d6995,
0x9d68a482f7797e7d,
0x9c9b29248d39ea92,
0xf4801ca2e13107aa,
0xa16c0732bdbcb066,
0x083ca4afba360478,
},
},
B: fp2{
A: fp{
0x59e261db0916b641,
0x2716b6f4b23e960d,
0xc8e55b10a0bd9c45,
0x0bdb0bd99c4deda8,
0x8cf89ebf57fdaac5,
0x12d6b7929e777a5e,
},
B: fp{
0x5fc85188b0e15f35,
0x34a06e3a8f096365,
0xdb3126a6e02ad62c,
0xfc6f5aa97d9a990b,
0xa12f55f5eb89c210,
0x1723703a926f8889,
},
},
C: fp2{
A: fp{
0x93588f2971828778,
0x43f65b8611ab7585,
0x3183aaf5ec279fdf,
0xfa73d7e18ac99df6,
0x64e176a6a64c99b0,
0x179fa78c58388f1f,
},
B: fp{
0x672a0a11ca2aef12,
0x0d11b9b52aa3f16b,
0xa44412d0699d056e,
0xc01d0177221a5ba5,
0x66e0cede6c735529,
0x05f5a71e9fddc339,
},
},
},
B: fp6{
A: fp2{
A: fp{
0xd30a88a1b062c679,
0x5ac56a5d35fc8304,
0xd0c834a6a81f290d,
0xcd5430c2da3707c7,
0xf0c27ff780500af0,
0x09245da6e2d72eae,
},
B: fp{
0x9f2e0676791b5156,
0xe2d1c8234918fe13,
0x4c9e459f3c561bf4,
0xa3e85e53b9d3e3c1,
0x820a121e21a70020,
0x15af618341c59acc,
},
},
B: fp2{
A: fp{
0x7c95658c24993ab1,
0x73eb38721ca886b9,
0x5256d749477434bc,
0x8ba41902ea504a8b,
0x04a3d3f80c86ce6d,
0x18a64a87fb686eaa,
},
B: fp{
0xbb83e71bb920cf26,
0x2a5277ac92a73945,
0xfc0ee59f94f046a0,
0x7158cdf3786058f7,
0x7cc1061b82f945f6,
0x03f847aa9fdbe567,
},
},
C: fp2{
A: fp{
0x8078dba56134e657,
0x1cd7ec9a43998a6e,
0xb1aa599a1a993766,
0xc9a0f62f0842ee44,
0x8e159be3b605dffa,
0x0c86ba0d4af13fc2,
},
B: fp{
0xe80ff2a06a52ffb1,
0x7694ca48721a906c,
0x7583183e03b08514,
0xf567afdd40cee4e2,
0x9a6d96d2e526a5fc,
0x197e9f49861f2242,
},
},
},
}))
return gt
}
// Add adds this value to another value.
func (gt *Gt) Add(arg1, arg2 *Gt) *Gt {
(*fp12)(gt).Mul((*fp12)(arg1), (*fp12)(arg2))
return gt
}
// Double this value
func (gt *Gt) Double(a *Gt) *Gt {
(*fp12)(gt).Square((*fp12)(a))
return gt
}
// Sub subtracts the two values
func (gt *Gt) Sub(arg1, arg2 *Gt) *Gt {
var t fp12
t.Conjugate((*fp12)(arg2))
(*fp12)(gt).Mul((*fp12)(arg1), &t)
return gt
}
// Neg negates this value
func (gt *Gt) Neg(a *Gt) *Gt {
(*fp12)(gt).Conjugate((*fp12)(a))
return gt
}
// Mul multiplies this value by the input scalar
func (gt *Gt) Mul(a *Gt, s *native.Field) *Gt {
var f, p fp12
f.Set((*fp12)(a))
bytes := s.Bytes()
precomputed := [16]fp12{}
precomputed[1].Set(&f)
for i := 2; i < 16; i += 2 {
precomputed[i].Square(&precomputed[i>>1])
precomputed[i+1].Mul(&precomputed[i], &f)
}
for i := 0; i < 256; i += 4 {
// Brouwer / windowing method. window size of 4.
for j := 0; j < 4; j++ {
p.Square(&p)
}
window := bytes[32-1-i>>3] >> (4 - i&0x04) & 0x0F
p.Mul(&p, &precomputed[window])
}
(*fp12)(gt).Set(&p)
return gt
}
// Square this value
func (gt *Gt) Square(a *Gt) *Gt {
(*fp12)(gt).cyclotomicSquare((*fp12)(a))
return gt
}
// Invert this value
func (gt *Gt) Invert(a *Gt) (*Gt, int) {
_, wasInverted := (*fp12)(gt).Invert((*fp12)(a))
return gt, wasInverted
}
func fp4Square(a, b, arg1, arg2 *fp2) {
var t0, t1, t2 fp2
t0.Square(arg1)
t1.Square(arg2)
t2.MulByNonResidue(&t1)
a.Add(&t2, &t0)
t2.Add(arg1, arg2)
t2.Square(&t2)
t2.Sub(&t2, &t0)
b.Sub(&t2, &t1)
}
func (f *fp12) cyclotomicSquare(a *fp12) *fp12 {
// Adaptation of Algorithm 5.5.4, Guide to Pairing-Based Cryptography
// Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions
// https://eprint.iacr.org/2009/565.pdf
var z0, z1, z2, z3, z4, z5, t0, t1, t2, t3 fp2
z0.Set(&a.A.A)
z4.Set(&a.A.B)
z3.Set(&a.A.C)
z2.Set(&a.B.A)
z1.Set(&a.B.B)
z5.Set(&a.B.C)
fp4Square(&t0, &t1, &z0, &z1)
z0.Sub(&t0, &z0)
z0.Double(&z0)
z0.Add(&z0, &t0)
z1.Add(&t1, &z1)
z1.Double(&z1)
z1.Add(&z1, &t1)
fp4Square(&t0, &t1, &z2, &z3)
fp4Square(&t2, &t3, &z4, &z5)
z4.Sub(&t0, &z4)
z4.Double(&z4)
z4.Add(&z4, &t0)
z5.Add(&z5, &t1)
z5.Double(&z5)
z5.Add(&z5, &t1)
t0.MulByNonResidue(&t3)
z2.Add(&z2, &t0)
z2.Double(&z2)
z2.Add(&z2, &t0)
z3.Sub(&t2, &z3)
z3.Double(&z3)
z3.Add(&z3, &t2)
f.A.A.Set(&z0)
f.A.B.Set(&z4)
f.A.C.Set(&z3)
f.B.A.Set(&z2)
f.B.B.Set(&z1)
f.B.C.Set(&z5)
return f
}
func (f *fp12) cyclotomicExp(a *fp12) *fp12 {
var t fp12
t.SetOne()
foundOne := 0
for i := 63; i >= 0; i-- {
b := int((paramX >> i) & 1)
if foundOne == 1 {
t.cyclotomicSquare(&t)
} else {
foundOne = b
}
if b == 1 {
t.Mul(&t, a)
}
}
f.Conjugate(&t)
return f
}
+254
View File
@@ -0,0 +1,254 @@
package bls12381
const coefficientsG2 = 68
type Engine struct {
pairs []pair
}
type pair struct {
g1 G1
g2 G2
}
type g2Prepared struct {
identity int
coefficients []coefficients
}
type coefficients struct {
a, b, c fp2
}
func (c *coefficients) CMove(arg1, arg2 *coefficients, choice int) *coefficients {
c.a.CMove(&arg1.a, &arg2.a, choice)
c.b.CMove(&arg1.b, &arg2.b, choice)
c.c.CMove(&arg1.c, &arg2.c, choice)
return c
}
// AddPair adds a pair of points to be paired
func (e *Engine) AddPair(g1 *G1, g2 *G2) *Engine {
var p pair
p.g1.ToAffine(g1)
p.g2.ToAffine(g2)
if p.g1.IsIdentity()|p.g2.IsIdentity() == 0 {
e.pairs = append(e.pairs, p)
}
return e
}
// AddPairInvG1 adds a pair of points to be paired. G1 point is negated
func (e *Engine) AddPairInvG1(g1 *G1, g2 *G2) *Engine {
var p G1
p.Neg(g1)
return e.AddPair(&p, g2)
}
// AddPairInvG2 adds a pair of points to be paired. G2 point is negated
func (e *Engine) AddPairInvG2(g1 *G1, g2 *G2) *Engine {
var p G2
p.Neg(g2)
return e.AddPair(g1, &p)
}
func (e *Engine) Reset() *Engine {
e.pairs = []pair{}
return e
}
func (e *Engine) Check() bool {
return e.pairing().IsOne() == 1
}
func (e *Engine) Result() *Gt {
return e.pairing()
}
func (e *Engine) pairing() *Gt {
f := new(Gt).SetOne()
if len(e.pairs) == 0 {
return f
}
coeffs := e.computeCoeffs()
e.millerLoop((*fp12)(f), coeffs)
return f.FinalExponentiation(f)
}
func (e *Engine) millerLoop(f *fp12, coeffs []g2Prepared) {
newF := new(fp12).SetZero()
found := 0
cIdx := 0
for i := 63; i >= 0; i-- {
x := int(((paramX >> 1) >> i) & 1)
if found == 0 {
found |= x
continue
}
// doubling
for j, terms := range coeffs {
identity := e.pairs[j].g1.IsIdentity() | terms.identity
newF.Set(f)
ell(newF, terms.coefficients[cIdx], &e.pairs[j].g1)
f.CMove(newF, f, identity)
}
cIdx++
if x == 1 {
// adding
for j, terms := range coeffs {
identity := e.pairs[j].g1.IsIdentity() | terms.identity
newF.Set(f)
ell(newF, terms.coefficients[cIdx], &e.pairs[j].g1)
f.CMove(newF, f, identity)
}
cIdx++
}
f.Square(f)
}
for j, terms := range coeffs {
identity := e.pairs[j].g1.IsIdentity() | terms.identity
newF.Set(f)
ell(newF, terms.coefficients[cIdx], &e.pairs[j].g1)
f.CMove(newF, f, identity)
}
f.Conjugate(f)
}
func (e *Engine) computeCoeffs() []g2Prepared {
coeffs := make([]g2Prepared, len(e.pairs))
for i, p := range e.pairs {
identity := p.g2.IsIdentity()
q := new(G2).Generator()
q.CMove(&p.g2, q, identity)
c := new(G2).Set(q)
cfs := make([]coefficients, coefficientsG2)
found := 0
k := 0
for j := 63; j >= 0; j-- {
x := int(((paramX >> 1) >> j) & 1)
if found == 0 {
found |= x
continue
}
cfs[k] = doublingStep(c)
k++
if x == 1 {
cfs[k] = additionStep(c, q)
k++
}
}
cfs[k] = doublingStep(c)
coeffs[i] = g2Prepared{
coefficients: cfs, identity: identity,
}
}
return coeffs
}
func ell(f *fp12, coeffs coefficients, p *G1) {
var x, y fp2
x.A.Mul(&coeffs.a.A, &p.y)
x.B.Mul(&coeffs.a.B, &p.y)
y.A.Mul(&coeffs.b.A, &p.x)
y.B.Mul(&coeffs.b.B, &p.x)
f.MulByABD(f, &coeffs.c, &y, &x)
}
func doublingStep(p *G2) coefficients {
// Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf
var t0, t1, t2, t3, t4, t5, t6, zsqr fp2
t0.Square(&p.x)
t1.Square(&p.y)
t2.Square(&t1)
t3.Add(&t1, &p.x)
t3.Square(&t3)
t3.Sub(&t3, &t0)
t3.Sub(&t3, &t2)
t3.Double(&t3)
t4.Double(&t0)
t4.Add(&t4, &t0)
t6.Add(&p.x, &t4)
t5.Square(&t4)
zsqr.Square(&p.z)
p.x.Sub(&t5, &t3)
p.x.Sub(&p.x, &t3)
p.z.Add(&p.z, &p.y)
p.z.Square(&p.z)
p.z.Sub(&p.z, &t1)
p.z.Sub(&p.z, &zsqr)
p.y.Sub(&t3, &p.x)
p.y.Mul(&p.y, &t4)
t2.Double(&t2)
t2.Double(&t2)
t2.Double(&t2)
p.y.Sub(&p.y, &t2)
t3.Mul(&t4, &zsqr)
t3.Double(&t3)
t3.Neg(&t3)
t6.Square(&t6)
t6.Sub(&t6, &t0)
t6.Sub(&t6, &t5)
t1.Double(&t1)
t1.Double(&t1)
t6.Sub(&t6, &t1)
t0.Mul(&p.z, &zsqr)
t0.Double(&t0)
return coefficients{
a: t0, b: t3, c: t6,
}
}
func additionStep(r, q *G2) coefficients {
// Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf
var zsqr, ysqr fp2
var t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10 fp2
zsqr.Square(&r.z)
ysqr.Square(&q.y)
t0.Mul(&zsqr, &q.x)
t1.Add(&q.y, &r.z)
t1.Square(&t1)
t1.Sub(&t1, &ysqr)
t1.Sub(&t1, &zsqr)
t1.Mul(&t1, &zsqr)
t2.Sub(&t0, &r.x)
t3.Square(&t2)
t4.Double(&t3)
t4.Double(&t4)
t5.Mul(&t4, &t2)
t6.Sub(&t1, &r.y)
t6.Sub(&t6, &r.y)
t9.Mul(&t6, &q.x)
t7.Mul(&t4, &r.x)
r.x.Square(&t6)
r.x.Sub(&r.x, &t5)
r.x.Sub(&r.x, &t7)
r.x.Sub(&r.x, &t7)
r.z.Add(&r.z, &t2)
r.z.Square(&r.z)
r.z.Sub(&r.z, &zsqr)
r.z.Sub(&r.z, &t3)
t10.Add(&q.y, &r.z)
t8.Sub(&t7, &r.x)
t8.Mul(&t8, &t6)
t0.Mul(&r.y, &t5)
t0.Double(&t0)
r.y.Sub(&t8, &t0)
t10.Square(&t10)
t10.Sub(&t10, &ysqr)
zsqr.Square(&r.z)
t10.Sub(&t10, &zsqr)
t9.Double(&t9)
t9.Sub(&t9, &t10)
t10.Double(&r.z)
t6.Neg(&t6)
t1.Double(&t6)
return coefficients{
a: t10, b: t1, c: t9,
}
}
+64
View File
@@ -0,0 +1,64 @@
package bls12381
import (
crand "crypto/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves/native"
)
func TestSinglePairing(t *testing.T) {
g := new(G1).Generator()
h := new(G2).Generator()
e := new(Engine)
e.AddPair(g, h)
p := e.Result()
p.Neg(p)
e.Reset()
e.AddPairInvG2(g, h)
q := e.Result()
e.Reset()
e.AddPairInvG1(g, h)
r := e.Result()
require.Equal(t, 1, p.Equal(q))
require.Equal(t, 1, q.Equal(r))
}
func TestMultiPairing(t *testing.T) {
const Tests = 10
e1 := new(Engine)
e2 := new(Engine)
g1s := make([]*G1, Tests)
g2s := make([]*G2, Tests)
sc := make([]*native.Field, Tests)
res := make([]*Gt, Tests)
expected := new(Gt).SetOne()
for i := 0; i < Tests; i++ {
var bytes [64]byte
g1s[i] = new(G1).Generator()
g2s[i] = new(G2).Generator()
sc[i] = Bls12381FqNew()
_, _ = crand.Read(bytes[:])
sc[i].SetBytesWide(&bytes)
if i&1 == 0 {
g1s[i].Mul(g1s[i], sc[i])
} else {
g2s[i].Mul(g2s[i], sc[i])
}
e1.AddPair(g1s[i], g2s[i])
e2.AddPair(g1s[i], g2s[i])
res[i] = e1.Result()
e1.Reset()
expected.Add(expected, res[i])
}
actual := e2.Result()
require.Equal(t, 1, expected.Equal(actual))
}
+388
View File
@@ -0,0 +1,388 @@
package native
import (
"encoding/binary"
"fmt"
"math/big"
"github.com/onsonr/sonr/crypto/internal"
)
// FieldLimbs is the number of limbs needed to represent this field
const FieldLimbs = 4
// FieldBytes is the number of bytes needed to represent this field
const FieldBytes = 32
// WideFieldBytes is the number of bytes needed for safe conversion
// to this field to avoid bias when reduced
const WideFieldBytes = 64
// Field represents a field element
type Field struct {
// Value is the field elements value
Value [FieldLimbs]uint64
// Params are the field parameters
Params *FieldParams
// Arithmetic are the field methods
Arithmetic FieldArithmetic
}
// FieldParams are the field parameters
type FieldParams struct {
// R is 2^256 mod Modulus
R [FieldLimbs]uint64
// R2 is 2^512 mod Modulus
R2 [FieldLimbs]uint64
// R3 is 2^768 mod Modulus
R3 [FieldLimbs]uint64
// Modulus of the field
Modulus [FieldLimbs]uint64
// Modulus as big.Int
BiModulus *big.Int
}
// FieldArithmetic are the methods that can be done on a field
type FieldArithmetic interface {
// ToMontgomery converts this field to montgomery form
ToMontgomery(out, arg *[FieldLimbs]uint64)
// FromMontgomery converts this field from montgomery form
FromMontgomery(out, arg *[FieldLimbs]uint64)
// Neg performs modular negation
Neg(out, arg *[FieldLimbs]uint64)
// Square performs modular square
Square(out, arg *[FieldLimbs]uint64)
// Mul performs modular multiplication
Mul(out, arg1, arg2 *[FieldLimbs]uint64)
// Add performs modular addition
Add(out, arg1, arg2 *[FieldLimbs]uint64)
// Sub performs modular subtraction
Sub(out, arg1, arg2 *[FieldLimbs]uint64)
// Sqrt performs modular square root
Sqrt(wasSquare *int, out, arg *[FieldLimbs]uint64)
// Invert performs modular inverse
Invert(wasInverted *int, out, arg *[FieldLimbs]uint64)
// FromBytes converts a little endian byte array into a field element
FromBytes(out *[FieldLimbs]uint64, arg *[FieldBytes]byte)
// ToBytes converts a field element to a little endian byte array
ToBytes(out *[FieldBytes]byte, arg *[FieldLimbs]uint64)
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
Selectznz(out, arg1, arg2 *[FieldLimbs]uint64, choice int)
}
// Cmp returns -1 if f < rhs
// 0 if f == rhs
// 1 if f > rhs
func (f *Field) Cmp(rhs *Field) int {
return cmpHelper(&f.Value, &rhs.Value)
}
// cmpHelper returns -1 if lhs < rhs
// -1 if lhs == rhs
// 1 if lhs > rhs
// Public only for convenience for some internal implementations
func cmpHelper(lhs, rhs *[FieldLimbs]uint64) int {
gt := uint64(0)
lt := uint64(0)
for i := 3; i >= 0; i-- {
// convert to two 64-bit numbers where
// the leading bits are zeros and hold no meaning
// so rhs - fp actually means gt
// and fp - rhs actually means lt.
rhsH := rhs[i] >> 32
rhsL := rhs[i] & 0xffffffff
lhsH := lhs[i] >> 32
lhsL := lhs[i] & 0xffffffff
// Check the leading bit
// if negative then fp > rhs
// if positive then fp < rhs
gt |= (rhsH - lhsH) >> 32 & 1 &^ lt
lt |= (lhsH - rhsH) >> 32 & 1 &^ gt
gt |= (rhsL - lhsL) >> 32 & 1 &^ lt
lt |= (lhsL - rhsL) >> 32 & 1 &^ gt
}
// Make the result -1 for <, 0 for =, 1 for >
return int(gt) - int(lt)
}
// Equal returns 1 if f == rhs, 0 otherwise
func (f *Field) Equal(rhs *Field) int {
return equalHelper(&f.Value, &rhs.Value)
}
func equalHelper(lhs, rhs *[FieldLimbs]uint64) int {
t := lhs[0] ^ rhs[0]
t |= lhs[1] ^ rhs[1]
t |= lhs[2] ^ rhs[2]
t |= lhs[3] ^ rhs[3]
return int(((int64(t) | int64(-t)) >> 63) + 1)
}
// IsZero returns 1 if f == 0, 0 otherwise
func (f *Field) IsZero() int {
t := f.Value[0]
t |= f.Value[1]
t |= f.Value[2]
t |= f.Value[3]
return int(((int64(t) | int64(-t)) >> 63) + 1)
}
// IsNonZero returns 1 if f != 0, 0 otherwise
func (f *Field) IsNonZero() int {
t := f.Value[0]
t |= f.Value[1]
t |= f.Value[2]
t |= f.Value[3]
return int(-((int64(t) | int64(-t)) >> 63))
}
// IsOne returns 1 if f == 1, 0 otherwise
func (f *Field) IsOne() int {
return equalHelper(&f.Value, &f.Params.R)
}
// Set f = rhs
func (f *Field) Set(rhs *Field) *Field {
f.Value[0] = rhs.Value[0]
f.Value[1] = rhs.Value[1]
f.Value[2] = rhs.Value[2]
f.Value[3] = rhs.Value[3]
f.Params = rhs.Params
f.Arithmetic = rhs.Arithmetic
return f
}
// SetUint64 f = rhs
func (f *Field) SetUint64(rhs uint64) *Field {
t := &[FieldLimbs]uint64{rhs, 0, 0, 0}
f.Arithmetic.ToMontgomery(&f.Value, t)
return f
}
// SetOne f = r
func (f *Field) SetOne() *Field {
f.Value[0] = f.Params.R[0]
f.Value[1] = f.Params.R[1]
f.Value[2] = f.Params.R[2]
f.Value[3] = f.Params.R[3]
return f
}
// SetZero f = 0
func (f *Field) SetZero() *Field {
f.Value[0] = 0
f.Value[1] = 0
f.Value[2] = 0
f.Value[3] = 0
return f
}
// SetBytesWide takes 64 bytes as input and treats them as a 512-bit number.
// Attributed to https://github.com/zcash/pasta_curves/blob/main/src/fields/Fp.rs#L255
// We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits
// with the higher bits multiplied by 2^256. Thus, we perform two reductions
//
// 1. the lower bits are multiplied by r^2, as normal
// 2. the upper bits are multiplied by r^2 * 2^256 = r^3
//
// and computing their sum in the field. It remains to see that arbitrary 256-bit
// numbers can be placed into Montgomery form safely using the reduction. The
// reduction works so long as the product is less than r=2^256 multiplied by
// the modulus. This holds because for any `c` smaller than the modulus, we have
// that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the
// reduction always works so long as `c` is in the field; in this case it is either the
// constant `r2` or `r3`.
func (f *Field) SetBytesWide(input *[WideFieldBytes]byte) *Field {
d0 := [FieldLimbs]uint64{
binary.LittleEndian.Uint64(input[:8]),
binary.LittleEndian.Uint64(input[8:16]),
binary.LittleEndian.Uint64(input[16:24]),
binary.LittleEndian.Uint64(input[24:32]),
}
d1 := [FieldLimbs]uint64{
binary.LittleEndian.Uint64(input[32:40]),
binary.LittleEndian.Uint64(input[40:48]),
binary.LittleEndian.Uint64(input[48:56]),
binary.LittleEndian.Uint64(input[56:64]),
}
// f.Arithmetic.ToMontgomery(&d0, &d0)
// f.Arithmetic.Mul(&d1, &d1, &f.Params.R2)
// f.Arithmetic.Add(&f.Value, &d0, &d0)
// Convert to Montgomery form
tv1 := &[FieldLimbs]uint64{}
tv2 := &[FieldLimbs]uint64{}
// d0*r2 + d1*r3
f.Arithmetic.Mul(tv1, &d0, &f.Params.R2)
f.Arithmetic.Mul(tv2, &d1, &f.Params.R3)
f.Arithmetic.Add(&f.Value, tv1, tv2)
return f
}
// SetBytes attempts to convert a little endian byte representation
// of a scalar into a `Fp`, failing if input is not canonical
func (f *Field) SetBytes(input *[FieldBytes]byte) (*Field, error) {
d0 := [FieldLimbs]uint64{0, 0, 0, 0}
f.Arithmetic.FromBytes(&d0, input)
if cmpHelper(&d0, &f.Params.Modulus) != -1 {
return nil, fmt.Errorf("invalid byte sequence")
}
return f.SetLimbs(&d0), nil
}
// SetBigInt initializes an element from big.Int
// The value is reduced by the modulus
func (f *Field) SetBigInt(bi *big.Int) *Field {
var buffer [FieldBytes]byte
t := new(big.Int).Set(bi)
t.Mod(t, f.Params.BiModulus)
t.FillBytes(buffer[:])
copy(buffer[:], internal.ReverseScalarBytes(buffer[:]))
_, _ = f.SetBytes(&buffer)
return f
}
// SetRaw converts a raw array into a field element
// Assumes input is already in montgomery form
func (f *Field) SetRaw(input *[FieldLimbs]uint64) *Field {
f.Value[0] = input[0]
f.Value[1] = input[1]
f.Value[2] = input[2]
f.Value[3] = input[3]
return f
}
// SetLimbs converts an array into a field element
// by converting to montgomery form
func (f *Field) SetLimbs(input *[FieldLimbs]uint64) *Field {
f.Arithmetic.ToMontgomery(&f.Value, input)
return f
}
// Bytes converts this element into a byte representation
// in little endian byte order
func (f *Field) Bytes() [FieldBytes]byte {
var output [FieldBytes]byte
tv := &[FieldLimbs]uint64{}
f.Arithmetic.FromMontgomery(tv, &f.Value)
f.Arithmetic.ToBytes(&output, tv)
return output
}
// BigInt converts this element into the big.Int struct
func (f *Field) BigInt() *big.Int {
buffer := f.Bytes()
return new(big.Int).SetBytes(internal.ReverseScalarBytes(buffer[:]))
}
// Raw converts this element into the a [FieldLimbs]uint64
func (f *Field) Raw() [FieldLimbs]uint64 {
res := &[FieldLimbs]uint64{}
f.Arithmetic.FromMontgomery(res, &f.Value)
return *res
}
// Double this element
func (f *Field) Double(a *Field) *Field {
f.Arithmetic.Add(&f.Value, &a.Value, &a.Value)
return f
}
// Square this element
func (f *Field) Square(a *Field) *Field {
f.Arithmetic.Square(&f.Value, &a.Value)
return f
}
// Sqrt this element, if it exists. If true, then value
// is a square root. If false, value is a QNR
func (f *Field) Sqrt(a *Field) (*Field, bool) {
wasSquare := 0
f.Arithmetic.Sqrt(&wasSquare, &f.Value, &a.Value)
return f, wasSquare == 1
}
// Invert this element i.e. compute the multiplicative inverse
// return false, zero if this element is zero.
func (f *Field) Invert(a *Field) (*Field, bool) {
wasInverted := 0
f.Arithmetic.Invert(&wasInverted, &f.Value, &a.Value)
return f, wasInverted == 1
}
// Mul returns the result from multiplying this element by rhs
func (f *Field) Mul(lhs, rhs *Field) *Field {
f.Arithmetic.Mul(&f.Value, &lhs.Value, &rhs.Value)
return f
}
// Sub returns the result from subtracting rhs from this element
func (f *Field) Sub(lhs, rhs *Field) *Field {
f.Arithmetic.Sub(&f.Value, &lhs.Value, &rhs.Value)
return f
}
// Add returns the result from adding rhs to this element
func (f *Field) Add(lhs, rhs *Field) *Field {
f.Arithmetic.Add(&f.Value, &lhs.Value, &rhs.Value)
return f
}
// Neg returns negation of this element
func (f *Field) Neg(input *Field) *Field {
f.Arithmetic.Neg(&f.Value, &input.Value)
return f
}
// Exp raises base^exp
func (f *Field) Exp(base, exp *Field) *Field {
e := [FieldLimbs]uint64{}
f.Arithmetic.FromMontgomery(&e, &exp.Value)
Pow(&f.Value, &base.Value, &e, f.Params, f.Arithmetic)
return f
}
// CMove sets f = lhs if choice == 0 and f = rhs if choice == 1
func (f *Field) CMove(lhs, rhs *Field, choice int) *Field {
f.Arithmetic.Selectznz(&f.Value, &lhs.Value, &rhs.Value, choice)
return f
}
// Pow raises base^exp. The result is written to out.
// Public only for convenience for some internal implementations
func Pow(out, base, exp *[FieldLimbs]uint64, params *FieldParams, arithmetic FieldArithmetic) {
res := [FieldLimbs]uint64{params.R[0], params.R[1], params.R[2], params.R[3]}
tmp := [FieldLimbs]uint64{}
for i := len(exp) - 1; i >= 0; i-- {
for j := 63; j >= 0; j-- {
arithmetic.Square(&res, &res)
arithmetic.Mul(&tmp, &res, base)
arithmetic.Selectznz(&res, &res, &tmp, int(exp[i]>>j)&1)
}
}
out[0] = res[0]
out[1] = res[1]
out[2] = res[2]
out[3] = res[3]
}
// Pow2k raises arg to the power `2^k`. This result is written to out.
// Public only for convenience for some internal implementations
func Pow2k(out, arg *[FieldLimbs]uint64, k int, arithmetic FieldArithmetic) {
var t [FieldLimbs]uint64
t[0] = arg[0]
t[1] = arg[1]
t[2] = arg[2]
t[3] = arg[3]
for i := 0; i < k; i++ {
arithmetic.Square(&t, &t)
}
out[0] = t[0]
out[1] = t[1]
out[2] = t[2]
out[3] = t[3]
}
+107
View File
@@ -0,0 +1,107 @@
package native
import (
"hash"
"golang.org/x/crypto/sha3"
)
// OversizeDstSalt is the salt used to hash a dst over MaxDstLen
var OversizeDstSalt = []byte("H2C-OVERSIZE-DST-")
// MaxDstLen the max size for dst in hash to curve
const MaxDstLen = 255
func getDomainXmd(h hash.Hash, domain []byte) []byte {
var out []byte
if len(domain) > MaxDstLen {
h.Reset()
_, _ = h.Write(OversizeDstSalt)
_, _ = h.Write(domain)
out = h.Sum(nil)
} else {
out = domain
}
return out
}
func getDomainXof(h sha3.ShakeHash, domain []byte) []byte {
var out []byte
if len(domain) > MaxDstLen {
h.Reset()
_, _ = h.Write(OversizeDstSalt)
_, _ = h.Write(domain)
var tv [64]byte
_, _ = h.Read(tv[:])
out = tv[:]
} else {
out = domain
}
return out
}
// ExpandMsgXmd expands the msg with the domain to output a byte array
// with outLen in size using a fixed size hash.
// See https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-13#section-5.4.1
func ExpandMsgXmd(h *EllipticPointHasher, msg, domain []byte, outLen int) []byte {
domain = getDomainXmd(h.xmd, domain)
domainLen := byte(len(domain))
h.xmd.Reset()
// DST_prime = DST || I2OSP(len(DST), 1)
// b_0 = H(Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime)
_, _ = h.xmd.Write(make([]byte, h.xmd.BlockSize()))
_, _ = h.xmd.Write(msg)
_, _ = h.xmd.Write([]byte{uint8(outLen >> 8), uint8(outLen)})
_, _ = h.xmd.Write([]byte{0})
_, _ = h.xmd.Write(domain)
_, _ = h.xmd.Write([]byte{domainLen})
b0 := h.xmd.Sum(nil)
// b_1 = H(b_0 || I2OSP(1, 1) || DST_prime)
h.xmd.Reset()
_, _ = h.xmd.Write(b0)
_, _ = h.xmd.Write([]byte{1})
_, _ = h.xmd.Write(domain)
_, _ = h.xmd.Write([]byte{domainLen})
b1 := h.xmd.Sum(nil)
// b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
ell := (outLen + h.xmd.Size() - 1) / h.xmd.Size()
bi := b1
out := make([]byte, outLen)
for i := 1; i < ell; i++ {
h.xmd.Reset()
// b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
tmp := make([]byte, h.xmd.Size())
for j := 0; j < h.xmd.Size(); j++ {
tmp[j] = b0[j] ^ bi[j]
}
_, _ = h.xmd.Write(tmp)
_, _ = h.xmd.Write([]byte{1 + uint8(i)})
_, _ = h.xmd.Write(domain)
_, _ = h.xmd.Write([]byte{domainLen})
// b_1 || ... || b_(ell - 1)
copy(out[(i-1)*h.xmd.Size():i*h.xmd.Size()], bi[:])
bi = h.xmd.Sum(nil)
}
// b_ell
copy(out[(ell-1)*h.xmd.Size():], bi[:])
return out[:outLen]
}
// ExpandMsgXof expands the msg with the domain to output a byte array
// with outLen in size using a xof hash
// See https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-13#section-5.4.2
func ExpandMsgXof(h *EllipticPointHasher, msg, domain []byte, outLen int) []byte {
domain = getDomainXof(h.xof, domain)
domainLen := byte(len(domain))
h.xof.Reset()
_, _ = h.xof.Write(msg)
_, _ = h.xof.Write([]byte{uint8(outLen >> 8), uint8(outLen)})
_, _ = h.xof.Write(domain)
_, _ = h.xof.Write([]byte{domainLen})
out := make([]byte, outLen)
_, _ = h.xof.Read(out)
return out
}
+62
View File
@@ -0,0 +1,62 @@
package native
// IsogenyParams are the parameters needed to map from an isogeny to the main curve
type IsogenyParams struct {
XNum [][FieldLimbs]uint64
XDen [][FieldLimbs]uint64
YNum [][FieldLimbs]uint64
YDen [][FieldLimbs]uint64
}
// Map from the isogeny curve to the main curve using the parameters
func (p *IsogenyParams) Map(xIn, yIn *Field) (x, y *Field) {
var xNum, xDen, yNum, yDen, tv [FieldLimbs]uint64
var wasInverted int
xnumL := len(p.XNum)
xdenL := len(p.XDen)
ynumL := len(p.YNum)
ydenL := len(p.YDen)
degree := 0
for _, i := range []int{xnumL, xdenL, ynumL, ydenL} {
if degree < i {
degree = i
}
}
xs := make([][FieldLimbs]uint64, degree)
xs[0] = xIn.Params.R // x[0] = x^0
xs[1] = xIn.Value // x[1] = x^1
xIn.Arithmetic.Square(&xs[2], &xIn.Value) // x[2] = x^2
for i := 3; i < degree; i++ {
// x[i] = x^i
xIn.Arithmetic.Mul(&xs[i], &xs[i-1], &xIn.Value)
}
computeIsoK(&xNum, &xs, &p.XNum, xIn.Arithmetic)
computeIsoK(&xDen, &xs, &p.XDen, xIn.Arithmetic)
computeIsoK(&yNum, &xs, &p.YNum, xIn.Arithmetic)
computeIsoK(&yDen, &xs, &p.YDen, xIn.Arithmetic)
xIn.Arithmetic.Invert(&wasInverted, &xDen, &xDen)
x = new(Field).Set(xIn)
xIn.Arithmetic.Mul(&tv, &xNum, &xDen)
xIn.Arithmetic.Selectznz(&x.Value, &x.Value, &tv, wasInverted)
yIn.Arithmetic.Invert(&wasInverted, &yDen, &yDen)
y = new(Field).Set(yIn)
yIn.Arithmetic.Mul(&tv, &yNum, &yDen)
yIn.Arithmetic.Selectznz(&y.Value, &y.Value, &tv, wasInverted)
yIn.Arithmetic.Mul(&y.Value, &y.Value, &yIn.Value)
return
}
func computeIsoK(out *[FieldLimbs]uint64, xxs, k *[][FieldLimbs]uint64, f FieldArithmetic) {
var tv [FieldLimbs]uint64
for i := range *k {
f.Mul(&tv, &(*xxs)[i], &(*k)[i])
f.Add(out, out, &tv)
}
}
+175
View File
@@ -0,0 +1,175 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
"math/big"
"sync"
"github.com/onsonr/sonr/crypto/core/curves/native"
)
var (
k256FpInitonce sync.Once
k256FpParams native.FieldParams
)
func K256FpNew() *native.Field {
return &native.Field{
Value: [native.FieldLimbs]uint64{},
Params: getK256FpParams(),
Arithmetic: k256FpArithmetic{},
}
}
func k256FpParamsInit() {
k256FpParams = native.FieldParams{
R: [native.FieldLimbs]uint64{0x00000001000003d1, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
R2: [native.FieldLimbs]uint64{0x000007a2000e90a1, 0x0000000000000001, 0x0000000000000000, 0x0000000000000000},
R3: [native.FieldLimbs]uint64{0x002bb1e33795f671, 0x0000000100000b73, 0x0000000000000000, 0x0000000000000000},
Modulus: [native.FieldLimbs]uint64{0xfffffffefffffc2f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff},
BiModulus: new(big.Int).SetBytes([]byte{
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, 0xfe, 0xff, 0xff, 0xfc, 0x2f,
}),
}
}
func getK256FpParams() *native.FieldParams {
k256FpInitonce.Do(k256FpParamsInit)
return &k256FpParams
}
// k256FpArithmetic is a struct with all the methods needed for working
// in mod p
type k256FpArithmetic struct{}
// ToMontgomery converts this field to montgomery form
func (f k256FpArithmetic) ToMontgomery(out, arg *[native.FieldLimbs]uint64) {
ToMontgomery((*MontgomeryDomainFieldElement)(out), (*NonMontgomeryDomainFieldElement)(arg))
}
// FromMontgomery converts this field from montgomery form
func (f k256FpArithmetic) FromMontgomery(out, arg *[native.FieldLimbs]uint64) {
FromMontgomery((*NonMontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Neg performs modular negation
func (f k256FpArithmetic) Neg(out, arg *[native.FieldLimbs]uint64) {
Opp((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Square performs modular square
func (f k256FpArithmetic) Square(out, arg *[native.FieldLimbs]uint64) {
Square((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Mul performs modular multiplication
func (f k256FpArithmetic) Mul(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Mul((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Add performs modular addition
func (f k256FpArithmetic) Add(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Add((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Sub performs modular subtraction
func (f k256FpArithmetic) Sub(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Sub((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Sqrt performs modular square root
func (f k256FpArithmetic) Sqrt(wasSquare *int, out, arg *[native.FieldLimbs]uint64) {
// p is congruent to 3 mod 4 we can compute
// sqrt using elem^(p+1)/4 mod p
// 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c
var s, t [native.FieldLimbs]uint64
params := getK256FpParams()
native.Pow(&s, arg, &[native.FieldLimbs]uint64{
0xffffffffbfffff0c,
0xffffffffffffffff,
0xffffffffffffffff,
0x3fffffffffffffff,
}, params, f)
f.Square(&t, &s)
tv1 := &native.Field{Value: t, Params: params, Arithmetic: f}
tv2 := &native.Field{Value: *arg, Params: params, Arithmetic: f}
*wasSquare = tv1.Equal(tv2)
f.Selectznz(out, out, &s, *wasSquare)
}
// Invert performs modular inverse
func (f k256FpArithmetic) Invert(wasInverted *int, out, arg *[native.FieldLimbs]uint64) {
// The binary representation of (p - 2) has 5 groups of 1s, with lengths in
// { 1, 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each group:
// [1], [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
var s, x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223 [native.FieldLimbs]uint64
native.Pow2k(&x2, arg, 1, f)
f.Mul(&x2, &x2, arg)
native.Pow2k(&x3, &x2, 1, f)
f.Mul(&x3, &x3, arg)
native.Pow2k(&x6, &x3, 3, f)
f.Mul(&x6, &x6, &x3)
native.Pow2k(&x9, &x6, 3, f)
f.Mul(&x9, &x9, &x3)
native.Pow2k(&x11, &x9, 2, f)
f.Mul(&x11, &x11, &x2)
native.Pow2k(&x22, &x11, 11, f)
f.Mul(&x22, &x22, &x11)
native.Pow2k(&x44, &x22, 22, f)
f.Mul(&x44, &x44, &x22)
native.Pow2k(&x88, &x44, 44, f)
f.Mul(&x88, &x88, &x44)
native.Pow2k(&x176, &x88, 88, f)
f.Mul(&x176, &x176, &x88)
native.Pow2k(&x220, &x176, 44, f)
f.Mul(&x220, &x220, &x44)
native.Pow2k(&x223, &x220, 3, f)
f.Mul(&x223, &x223, &x3)
// Use sliding window over the group
native.Pow2k(&s, &x223, 23, f)
f.Mul(&s, &s, &x22)
native.Pow2k(&s, &s, 5, f)
f.Mul(&s, &s, arg)
native.Pow2k(&s, &s, 3, f)
f.Mul(&s, &s, &x2)
native.Pow2k(&s, &s, 2, f)
f.Mul(&s, &s, arg)
tv := &native.Field{Value: *arg, Params: getK256FpParams(), Arithmetic: f}
*wasInverted = tv.IsNonZero()
f.Selectznz(out, out, &s, *wasInverted)
}
// FromBytes converts a little endian byte array into a field element
func (f k256FpArithmetic) FromBytes(out *[native.FieldLimbs]uint64, arg *[native.FieldBytes]byte) {
FromBytes(out, arg)
}
// ToBytes converts a field element to a little endian byte array
func (f k256FpArithmetic) ToBytes(out *[native.FieldBytes]byte, arg *[native.FieldLimbs]uint64) {
ToBytes(out, arg)
}
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f k256FpArithmetic) Selectznz(out, arg1, arg2 *[native.FieldLimbs]uint64, choice int) {
Selectznz(out, uint1(choice), arg1, arg2)
}
+328
View File
@@ -0,0 +1,328 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/internal"
)
func TestFpSetOne(t *testing.T) {
fp := K256FpNew().SetOne()
require.NotNil(t, fp)
require.Equal(t, fp.Value, getK256FpParams().R)
}
func TestFpSetUint64(t *testing.T) {
act := K256FpNew().SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act.Value[0], uint64(0x1000000000000000))
}
func TestFpAdd(t *testing.T) {
lhs := K256FpNew().SetOne()
rhs := K256FpNew().SetOne()
exp := K256FpNew().SetUint64(2)
res := K256FpNew().Add(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, res.Equal(exp), 1)
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := K256FpNew().Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpSub(t *testing.T) {
lhs := K256FpNew().SetOne()
rhs := K256FpNew().SetOne()
exp := K256FpNew().SetZero()
res := K256FpNew().Sub(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := K256FpNew().Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpMul(t *testing.T) {
lhs := K256FpNew().SetOne()
rhs := K256FpNew().SetOne()
exp := K256FpNew().SetOne()
res := K256FpNew().Mul(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := K256FpNew().Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpDouble(t *testing.T) {
a := K256FpNew().SetUint64(2)
e := K256FpNew().SetUint64(4)
require.Equal(t, e, K256FpNew().Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = K256FpNew().SetUint64(uint64(tv))
e = K256FpNew().SetUint64(ttv)
require.Equal(t, e, K256FpNew().Double(a))
}
}
func TestFpSquare(t *testing.T) {
a := K256FpNew().SetUint64(4)
e := K256FpNew().SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFpNeg(t *testing.T) {
a := K256FpNew().SetOne()
a.Neg(a)
e := K256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xfffffffdfffff85e, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff})
require.Equal(t, e, a)
}
func TestFpExp(t *testing.T) {
e := K256FpNew().SetUint64(8)
a := K256FpNew().SetUint64(2)
by := K256FpNew().SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFpSqrt(t *testing.T) {
t1 := K256FpNew().SetUint64(2)
t2 := K256FpNew().Neg(t1)
t3 := K256FpNew().Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.Equal(t, 1, t1.Equal(t3)|t2.Equal(t3))
t1.SetUint64(5)
_, wasSquare = K256FpNew().Sqrt(t1)
require.False(t, wasSquare)
}
func TestFpInvert(t *testing.T) {
twoInv := K256FpNew().SetLimbs(&[native.FieldLimbs]uint64{
0xffffffff7ffffe18,
0xffffffffffffffff,
0xffffffffffffffff,
0x7fffffffffffffff,
})
two := K256FpNew().SetUint64(2)
a, inverted := K256FpNew().Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
seven := K256FpNew().SetUint64(7)
sevenInv := K256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xdb6db6dab6db6afd, 0x6db6db6db6db6db6, 0xb6db6db6db6db6db, 0xdb6db6db6db6db6d})
a, inverted = K256FpNew().Invert(seven)
require.True(t, inverted)
require.Equal(t, a, sevenInv)
lhs := K256FpNew().SetUint64(9)
rhs := K256FpNew().SetUint64(3)
rhsInv, inverted := K256FpNew().Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, K256FpNew().Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = K256FpNew().Invert(rhs)
require.False(t, inverted)
}
func TestFpCMove(t *testing.T) {
t1 := K256FpNew().SetUint64(5)
t2 := K256FpNew().SetUint64(10)
require.Equal(t, t1, K256FpNew().CMove(t1, t2, 0))
require.Equal(t, t2, K256FpNew().CMove(t1, t2, 1))
}
func TestFpBytes(t *testing.T) {
t1 := K256FpNew().SetUint64(99)
seq := t1.Bytes()
t2, err := K256FpNew().SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFpCmp(t *testing.T) {
tests := []struct {
a *native.Field
b *native.Field
e int
}{
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{2731658267414164836, 14655288906067898431, 6537465423330262322, 8306191141697566219}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{6472764012681988529, 10848812988401906064, 2961825807536828898, 4282183981941645679}),
e: 1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{8023004109510539223, 4652004072850285717, 1877219145646046927, 383214385093921911}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{10099384440823804262, 16139476942229308465, 8636966320777393798, 5435928725024696785}),
e: -1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{3741840066202388211, 12165774400417314871, 16619312580230515379, 16195032234110087705}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{3905865991286066744, 543690822309071825, 17963103015950210055, 3745476720756119742}),
e: 1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{16660853697936147788, 7799793619412111108, 13515141085171033220, 2641079731236069032}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{17790588295388238399, 571847801379669440, 14537208974498222469, 12792570372087452754}),
e: -1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{3912839285384959186, 2701177075110484070, 6453856448115499033, 6475797457962597458}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{1282566391665688512, 13503640416992806563, 2962240104675990153, 3374904770947067689}),
e: 1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{5716631803409360103, 7859567470082614154, 12747956220853330146, 18434584096087315020}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{16317076441459028418, 12854146980376319601, 2258436689269031143, 9531877130792223752}),
e: 1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{17955191469941083403, 10350326247207200880, 17263512235150705075, 12700328451238078022}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{6767595547459644695, 7146403825494928147, 12269344038346710612, 9122477829383225603}),
e: 1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{17099388671847024438, 6426264987820696548, 10641143464957227405, 7709745403700754098}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{10799154372990268556, 17178492485719929374, 5705777922258988797, 8051037767683567782}),
e: -1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{4567139260680454325, 1629385880182139061, 16607020832317899145, 1261011562621553200}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{13487234491304534488, 17872642955936089265, 17651026784972590233, 9468934643333871559}),
e: -1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{18071070103467571798, 11787850505799426140, 10631355976141928593, 4867785203635092610}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{12596443599426461624, 10176122686151524591, 17075755296887483439, 6726169532695070719}),
e: -1,
},
}
for _, test := range tests {
require.Equal(t, test.e, test.a.Cmp(test.b))
require.Equal(t, -test.e, test.b.Cmp(test.a))
require.Equal(t, 0, test.a.Cmp(test.a))
require.Equal(t, 0, test.b.Cmp(test.b))
}
}
func TestFpBigInt(t *testing.T) {
t1 := K256FpNew().SetBigInt(big.NewInt(9999))
t2 := K256FpNew().SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := K256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xc6c6c6c63939371d, 0xc6c6c6c6c6c6c6c6, 0x8d8d8dd28485081d, 0x8484848484848484})
b := new(big.Int).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Value[0] = 0x39393938c6c6c512
e.Value[1] = 0x3939393939393939
e.Value[2] = 0x7272722d7b7af7e2
e.Value[3] = 0x7b7b7b7b7b7b7b7b
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFpSetBytesWide(t *testing.T) {
e := K256FpNew().SetRaw(&[native.FieldLimbs]uint64{0x6aa784623e2d641e, 0x7c40617d755bae27, 0x206b7be66ed7b71b, 0x6d1e4fc581e19dc2})
a := K256FpNew().SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
func TestFpSetBytesWideBigInt(t *testing.T) {
params := getK256FpParams()
var tv2 [64]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, params.BiModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a := K256FpNew().SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
File diff suppressed because it is too large Load Diff
+287
View File
@@ -0,0 +1,287 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
"math/big"
"sync"
"github.com/onsonr/sonr/crypto/core/curves/native"
)
var (
k256FqInitonce sync.Once
k256FqParams native.FieldParams
)
func K256FqNew() *native.Field {
return &native.Field{
Value: [native.FieldLimbs]uint64{},
Params: getK256FqParams(),
Arithmetic: k256FqArithmetic{},
}
}
func k256FqParamsInit() {
k256FqParams = native.FieldParams{
R: [native.FieldLimbs]uint64{0x402da1732fc9bebf, 0x4551231950b75fc4, 0x0000000000000001, 0x0000000000000000},
R2: [native.FieldLimbs]uint64{0x896cf21467d7d140, 0x741496c20e7cf878, 0xe697f5e45bcd07c6, 0x9d671cd581c69bc5},
R3: [native.FieldLimbs]uint64{0x7bc0cfe0e9ff41ed, 0x0017648444d4322c, 0xb1b31347f1d0b2da, 0x555d800c18ef116d},
Modulus: [native.FieldLimbs]uint64{0xbfd25e8cd0364141, 0xbaaedce6af48a03b, 0xfffffffffffffffe, 0xffffffffffffffff},
BiModulus: new(big.Int).SetBytes([]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41,
},
),
}
}
func getK256FqParams() *native.FieldParams {
k256FqInitonce.Do(k256FqParamsInit)
return &k256FqParams
}
// k256FqArithmetic is a struct with all the methods needed for working
// in mod q
type k256FqArithmetic struct{}
// ToMontgomery converts this field to montgomery form
func (f k256FqArithmetic) ToMontgomery(out, arg *[native.FieldLimbs]uint64) {
ToMontgomery((*MontgomeryDomainFieldElement)(out), (*NonMontgomeryDomainFieldElement)(arg))
}
// FromMontgomery converts this field from montgomery form
func (f k256FqArithmetic) FromMontgomery(out, arg *[native.FieldLimbs]uint64) {
FromMontgomery((*NonMontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Neg performs modular negation
func (f k256FqArithmetic) Neg(out, arg *[native.FieldLimbs]uint64) {
Opp((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Square performs modular square
func (f k256FqArithmetic) Square(out, arg *[native.FieldLimbs]uint64) {
Square((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Mul performs modular multiplication
func (f k256FqArithmetic) Mul(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Mul((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Add performs modular addition
func (f k256FqArithmetic) Add(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Add((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Sub performs modular subtraction
func (f k256FqArithmetic) Sub(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Sub((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Sqrt performs modular square root
func (f k256FqArithmetic) Sqrt(wasSquare *int, out, arg *[native.FieldLimbs]uint64) {
// See sqrt_ts_ct at
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.4
// c1 := 6
// c2 := (q - 1) / (2^c1)
// c2 := [4]uint64{
// 0xeeff497a3340d905,
// 0xfaeabb739abd2280,
// 0xffffffffffffffff,
// 0x03ffffffffffffff,
//}
// c3 := (c2 - 1) / 2
c3 := [native.FieldLimbs]uint64{
0x777fa4bd19a06c82,
0xfd755db9cd5e9140,
0xffffffffffffffff,
0x01ffffffffffffff,
}
// c4 := generator
// c5 := new(Fq).pow(generator, c2)
c5 := [native.FieldLimbs]uint64{0x944cf2a220910e04, 0x815c829c780589f4, 0x55980b07bc222113, 0xc702b0d248825b36}
var z, t, b, c, tv [native.FieldLimbs]uint64
native.Pow(&z, arg, &c3, getK256FqParams(), f)
Square((*MontgomeryDomainFieldElement)(&t), (*MontgomeryDomainFieldElement)(&z))
Mul((*MontgomeryDomainFieldElement)(&t), (*MontgomeryDomainFieldElement)(&t), (*MontgomeryDomainFieldElement)(arg))
Mul((*MontgomeryDomainFieldElement)(&z), (*MontgomeryDomainFieldElement)(&z), (*MontgomeryDomainFieldElement)(arg))
copy(b[:], t[:])
copy(c[:], c5[:])
for i := s; i >= 2; i-- {
for j := 1; j <= i-2; j++ {
Square((*MontgomeryDomainFieldElement)(&b), (*MontgomeryDomainFieldElement)(&b))
}
// if b == 1 flag = 0 else flag = 1
flag := -(&native.Field{
Value: b,
Params: getK256FqParams(),
Arithmetic: f,
}).IsOne() + 1
Mul((*MontgomeryDomainFieldElement)(&tv), (*MontgomeryDomainFieldElement)(&z), (*MontgomeryDomainFieldElement)(&c))
Selectznz(&z, uint1(flag), &z, &tv)
Square((*MontgomeryDomainFieldElement)(&c), (*MontgomeryDomainFieldElement)(&c))
Mul((*MontgomeryDomainFieldElement)(&tv), (*MontgomeryDomainFieldElement)(&t), (*MontgomeryDomainFieldElement)(&c))
Selectznz(&t, uint1(flag), &t, &tv)
copy(b[:], t[:])
}
Square((*MontgomeryDomainFieldElement)(&c), (*MontgomeryDomainFieldElement)(&z))
*wasSquare = (&native.Field{
Value: c,
Params: getK256FqParams(),
Arithmetic: f,
}).Equal(&native.Field{
Value: *arg,
Params: getK256FqParams(),
Arithmetic: f,
})
Selectznz(out, uint1(*wasSquare), out, &z)
}
// Invert performs modular inverse
func (f k256FqArithmetic) Invert(wasInverted *int, out, arg *[native.FieldLimbs]uint64) {
// Using an addition chain from
// https://briansmith.org/ecc-inversion-addition-chains-01#secp256k1_scalar_inversion
var x1, x10, x11, x101, x111, x1001, x1011, x1101 [native.FieldLimbs]uint64
var x6, x8, x14, x28, x56, tmp [native.FieldLimbs]uint64
copy(x1[:], arg[:])
native.Pow2k(&x10, arg, 1, f)
Mul((*MontgomeryDomainFieldElement)(&x11), (*MontgomeryDomainFieldElement)(&x10), (*MontgomeryDomainFieldElement)(&x1))
Mul((*MontgomeryDomainFieldElement)(&x101), (*MontgomeryDomainFieldElement)(&x10), (*MontgomeryDomainFieldElement)(&x11))
Mul((*MontgomeryDomainFieldElement)(&x111), (*MontgomeryDomainFieldElement)(&x10), (*MontgomeryDomainFieldElement)(&x101))
Mul((*MontgomeryDomainFieldElement)(&x1001), (*MontgomeryDomainFieldElement)(&x10), (*MontgomeryDomainFieldElement)(&x111))
Mul((*MontgomeryDomainFieldElement)(&x1011), (*MontgomeryDomainFieldElement)(&x10), (*MontgomeryDomainFieldElement)(&x1001))
Mul((*MontgomeryDomainFieldElement)(&x1101), (*MontgomeryDomainFieldElement)(&x10), (*MontgomeryDomainFieldElement)(&x1011))
native.Pow2k(&x6, &x1101, 2, f)
Mul((*MontgomeryDomainFieldElement)(&x6), (*MontgomeryDomainFieldElement)(&x6), (*MontgomeryDomainFieldElement)(&x1011))
native.Pow2k(&x8, &x6, 2, f)
Mul((*MontgomeryDomainFieldElement)(&x8), (*MontgomeryDomainFieldElement)(&x8), (*MontgomeryDomainFieldElement)(&x11))
native.Pow2k(&x14, &x8, 6, f)
Mul((*MontgomeryDomainFieldElement)(&x14), (*MontgomeryDomainFieldElement)(&x14), (*MontgomeryDomainFieldElement)(&x6))
native.Pow2k(&x28, &x14, 14, f)
Mul((*MontgomeryDomainFieldElement)(&x28), (*MontgomeryDomainFieldElement)(&x28), (*MontgomeryDomainFieldElement)(&x14))
native.Pow2k(&x56, &x28, 28, f)
Mul((*MontgomeryDomainFieldElement)(&x56), (*MontgomeryDomainFieldElement)(&x56), (*MontgomeryDomainFieldElement)(&x28))
native.Pow2k(&tmp, &x56, 56, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x56))
native.Pow2k(&tmp, &tmp, 14, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x14))
native.Pow2k(&tmp, &tmp, 3, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x101))
native.Pow2k(&tmp, &tmp, 4, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x111))
native.Pow2k(&tmp, &tmp, 4, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x101))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1011))
native.Pow2k(&tmp, &tmp, 4, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1011))
native.Pow2k(&tmp, &tmp, 4, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x111))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x111))
native.Pow2k(&tmp, &tmp, 6, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1101))
native.Pow2k(&tmp, &tmp, 4, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x101))
native.Pow2k(&tmp, &tmp, 3, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x111))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1001))
native.Pow2k(&tmp, &tmp, 6, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x101))
native.Pow2k(&tmp, &tmp, 10, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x111))
native.Pow2k(&tmp, &tmp, 4, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x111))
native.Pow2k(&tmp, &tmp, 9, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x8))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1001))
native.Pow2k(&tmp, &tmp, 6, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1011))
native.Pow2k(&tmp, &tmp, 4, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1101))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x11))
native.Pow2k(&tmp, &tmp, 6, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1101))
native.Pow2k(&tmp, &tmp, 10, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1101))
native.Pow2k(&tmp, &tmp, 4, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1001))
native.Pow2k(&tmp, &tmp, 6, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1))
native.Pow2k(&tmp, &tmp, 8, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x6))
*wasInverted = (&native.Field{
Value: *arg,
Params: getK256FqParams(),
Arithmetic: f,
}).IsNonZero()
Selectznz(out, uint1(*wasInverted), out, &tmp)
}
// FromBytes converts a little endian byte array into a field element
func (f k256FqArithmetic) FromBytes(out *[native.FieldLimbs]uint64, arg *[native.FieldBytes]byte) {
FromBytes(out, arg)
}
// ToBytes converts a field element to a little endian byte array
func (f k256FqArithmetic) ToBytes(out *[native.FieldBytes]byte, arg *[native.FieldLimbs]uint64) {
ToBytes(out, arg)
}
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f k256FqArithmetic) Selectznz(out, arg1, arg2 *[native.FieldLimbs]uint64, choice int) {
Selectznz(out, uint1(choice), arg1, arg2)
}
// generator = 7 mod q is a generator of the `q - 1` order multiplicative
// subgroup, or in other words a primitive element of the field.
// generator^t where t * 2^s + 1 = q
var generator = &[native.FieldLimbs]uint64{0xc13f6a264e843739, 0xe537f5b135039e5d, 0x0000000000000008, 0x0000000000000000}
// s satisfies the equation 2^s * t = q - 1 with t odd.
var s = 6
+328
View File
@@ -0,0 +1,328 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/internal"
)
func TestFqSetOne(t *testing.T) {
fq := K256FqNew().SetOne()
require.NotNil(t, fq)
require.Equal(t, fq.Value, getK256FqParams().R)
}
func TestFqSetUint64(t *testing.T) {
act := K256FqNew().SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act.Value[0], uint64(0xF000000000000000))
}
func TestFqAdd(t *testing.T) {
lhs := K256FqNew().SetOne()
rhs := K256FqNew().SetOne()
exp := K256FqNew().SetUint64(2)
res := K256FqNew().Add(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := K256FqNew().Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqSub(t *testing.T) {
lhs := K256FqNew().SetOne()
rhs := K256FqNew().SetOne()
exp := K256FqNew().SetZero()
res := K256FqNew().Sub(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := K256FqNew().Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqMul(t *testing.T) {
lhs := K256FqNew().SetOne()
rhs := K256FqNew().SetOne()
exp := K256FqNew().SetOne()
res := K256FqNew().Mul(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := K256FqNew().Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqDouble(t *testing.T) {
a := K256FqNew().SetUint64(2)
e := K256FqNew().SetUint64(4)
require.Equal(t, e, K256FqNew().Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = K256FqNew().SetUint64(uint64(tv))
e = K256FqNew().SetUint64(ttv)
require.Equal(t, e, K256FqNew().Double(a))
}
}
func TestFqSquare(t *testing.T) {
a := K256FqNew().SetUint64(4)
e := K256FqNew().SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFqNeg(t *testing.T) {
g := K256FqNew().SetRaw(generator)
a := K256FqNew().SetOne()
a.Neg(a)
e := K256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x7fa4bd19a06c8282, 0x755db9cd5e914077, 0xfffffffffffffffd, 0xffffffffffffffff})
require.Equal(t, e, a)
a.Neg(g)
e = K256FqNew().SetRaw(&[native.FieldLimbs]uint64{0xfe92f46681b20a08, 0xd576e7357a4501dd, 0xfffffffffffffff5, 0xffffffffffffffff})
require.Equal(t, e, a)
}
func TestFqExp(t *testing.T) {
e := K256FqNew().SetUint64(8)
a := K256FqNew().SetUint64(2)
by := K256FqNew().SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFqSqrt(t *testing.T) {
t1 := K256FqNew().SetUint64(2)
t2 := K256FqNew().Neg(t1)
t3 := K256FqNew().Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.Equal(t, 1, t1.Equal(t3)|t2.Equal(t3))
t1.SetUint64(5)
_, wasSquare = K256FqNew().Sqrt(t1)
require.False(t, wasSquare)
}
func TestFqInvert(t *testing.T) {
twoInv := K256FqNew().SetLimbs(&[native.FieldLimbs]uint64{0xdfe92f46681b20a1, 0x5d576e7357a4501d, 0xffffffffffffffff, 0x7fffffffffffffff})
two := K256FqNew().SetUint64(2)
a, inverted := K256FqNew().Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnity := K256FqNew().SetLimbs(&[native.FieldLimbs]uint64{0x8619a9e760c01d0c, 0xa883c4fba37998df, 0x45607580b6eabd98, 0xf252b002544b2f99})
rootOfUnityInv := K256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x7d99f8e21447e314, 0x5b60c477e7728d4c, 0xd78befc191f58654, 0x6897e5ff7824360f})
a, inverted = K256FqNew().Invert(rootOfUnity)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := K256FqNew().SetUint64(9)
rhs := K256FqNew().SetUint64(3)
rhsInv, inverted := K256FqNew().Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, K256FqNew().Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = K256FqNew().Invert(rhs)
require.False(t, inverted)
}
func TestFqCMove(t *testing.T) {
t1 := K256FqNew().SetUint64(5)
t2 := K256FqNew().SetUint64(10)
require.Equal(t, t1, K256FqNew().CMove(t1, t2, 0))
require.Equal(t, t2, K256FqNew().CMove(t1, t2, 1))
}
func TestFqBytes(t *testing.T) {
t1 := K256FqNew().SetUint64(99)
seq := t1.Bytes()
t2, err := K256FqNew().SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFqCmp(t *testing.T) {
tests := []struct {
a *native.Field
b *native.Field
e int
}{
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{2731658267414164836, 14655288906067898431, 6537465423330262322, 8306191141697566219}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{6472764012681988529, 10848812988401906064, 2961825807536828898, 4282183981941645679}),
e: 1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{8023004109510539223, 4652004072850285717, 1877219145646046927, 383214385093921911}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{10099384440823804262, 16139476942229308465, 8636966320777393798, 5435928725024696785}),
e: -1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{3741840066202388211, 12165774400417314871, 16619312580230515379, 16195032234110087705}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{3905865991286066744, 543690822309071825, 17963103015950210055, 3745476720756119742}),
e: 1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{16660853697936147788, 7799793619412111108, 13515141085171033220, 2641079731236069032}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{17790588295388238399, 571847801379669440, 14537208974498222469, 12792570372087452754}),
e: -1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{3912839285384959186, 2701177075110484070, 6453856448115499033, 6475797457962597458}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{1282566391665688512, 13503640416992806563, 2962240104675990153, 3374904770947067689}),
e: 1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{5716631803409360103, 7859567470082614154, 12747956220853330146, 18434584096087315020}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{16317076441459028418, 12854146980376319601, 2258436689269031143, 9531877130792223752}),
e: 1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{17955191469941083403, 10350326247207200880, 17263512235150705075, 12700328451238078022}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{6767595547459644695, 7146403825494928147, 12269344038346710612, 9122477829383225603}),
e: 1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{17099388671847024438, 6426264987820696548, 10641143464957227405, 7709745403700754098}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{10799154372990268556, 17178492485719929374, 5705777922258988797, 8051037767683567782}),
e: -1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{4567139260680454325, 1629385880182139061, 16607020832317899145, 1261011562621553200}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{13487234491304534488, 17872642955936089265, 17651026784972590233, 9468934643333871559}),
e: -1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{18071070103467571798, 11787850505799426140, 10631355976141928593, 4867785203635092610}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{12596443599426461624, 10176122686151524591, 17075755296887483439, 6726169532695070719}),
e: -1,
},
}
for _, test := range tests {
require.Equal(t, test.e, test.a.Cmp(test.b))
require.Equal(t, -test.e, test.b.Cmp(test.a))
require.Equal(t, 0, test.a.Cmp(test.a))
require.Equal(t, 0, test.b.Cmp(test.b))
}
}
func TestFqBigInt(t *testing.T) {
t1 := K256FqNew().SetBigInt(big.NewInt(9999))
t2 := K256FqNew().SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := K256FqNew().SetRaw(&[native.FieldLimbs]uint64{0xa764d3f6f152f222, 0x3b5dc8aacb9297b7, 0xb015fa9d2b3efdc6, 0x567360cef000f24a})
b := new(big.Int).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Value[0] = 0x186d8a95dee34f1f
e.Value[1] = 0x7f51143be3b60884
e.Value[2] = 0x4fea0562d4c10238
e.Value[3] = 0xa98c9f310fff0db5
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFqSetBytesWide(t *testing.T) {
e := K256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x70620a92b4f2eb7a, 0xd04588eb9c228a3a, 0xccb71ae40a10491c, 0x61cf39d70a8b33b7})
a := K256FqNew().SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
func TestFpSetBytesWideBigInt(t *testing.T) {
params := getK256FqParams()
var tv2 [64]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, params.BiModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a := K256FqNew().SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
File diff suppressed because it is too large Load Diff
+446
View File
@@ -0,0 +1,446 @@
package k256
import (
"sync"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/core/curves/native/k256/fp"
"github.com/onsonr/sonr/crypto/internal"
)
var (
k256PointInitonce sync.Once
k256PointParams native.EllipticPointParams
k256PointSswuInitOnce sync.Once
k256PointSswuParams native.SswuParams
k256PointIsogenyInitOnce sync.Once
k256PointIsogenyParams native.IsogenyParams
)
func K256PointNew() *native.EllipticPoint {
return &native.EllipticPoint{
X: fp.K256FpNew(),
Y: fp.K256FpNew(),
Z: fp.K256FpNew(),
Params: getK256PointParams(),
Arithmetic: &k256PointArithmetic{},
}
}
func k256PointParamsInit() {
k256PointParams = native.EllipticPointParams{
A: fp.K256FpNew(),
B: fp.K256FpNew().SetUint64(7),
Gx: fp.K256FpNew().SetLimbs(&[native.FieldLimbs]uint64{
0x59f2815b16f81798,
0x029bfcdb2dce28d9,
0x55a06295ce870b07,
0x79be667ef9dcbbac,
}),
Gy: fp.K256FpNew().SetLimbs(&[native.FieldLimbs]uint64{
0x9c47d08ffb10d4b8,
0xfd17b448a6855419,
0x5da4fbfc0e1108a8,
0x483ada7726a3c465,
}),
BitSize: 256,
Name: "secp256k1",
}
}
func getK256PointParams() *native.EllipticPointParams {
k256PointInitonce.Do(k256PointParamsInit)
return &k256PointParams
}
func getK256PointSswuParams() *native.SswuParams {
k256PointSswuInitOnce.Do(k256PointSswuParamsInit)
return &k256PointSswuParams
}
func k256PointSswuParamsInit() {
// Taken from https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.7
//params := btcec.S256().Params()
//
//// c1 = (q - 3) / 4
//c1 := new(big.Int).Set(params.P)
//c1.Sub(c1, big.NewInt(3))
//c1.Rsh(c1, 2)
//
//a, _ := new(big.Int).SetString("3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533", 16)
//b := big.NewInt(1771)
//z := big.NewInt(-11)
//z.Mod(z, params.P)
//// sqrt(-z^3)
//zTmp := new(big.Int).Exp(z, big.NewInt(3), nil)
//zTmp = zTmp.Neg(zTmp)
//zTmp.Mod(zTmp, params.P)
//c2 := new(big.Int).ModSqrt(zTmp, params.P)
//
//var tBytes [32]byte
//c1.FillBytes(tBytes[:])
//newC1 := [native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(tBytes[24:32]),
// binary.BigEndian.Uint64(tBytes[16:24]),
// binary.BigEndian.Uint64(tBytes[8:16]),
// binary.BigEndian.Uint64(tBytes[:8]),
//}
//fp.K256FpNew().Arithmetic.ToMontgomery(&newC1, &newC1)
//c2.FillBytes(tBytes[:])
//newC2 := [native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(tBytes[24:32]),
// binary.BigEndian.Uint64(tBytes[16:24]),
// binary.BigEndian.Uint64(tBytes[8:16]),
// binary.BigEndian.Uint64(tBytes[:8]),
//}
//fp.K256FpNew().Arithmetic.ToMontgomery(&newC2, &newC2)
//a.FillBytes(tBytes[:])
//newA := [native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(tBytes[24:32]),
// binary.BigEndian.Uint64(tBytes[16:24]),
// binary.BigEndian.Uint64(tBytes[8:16]),
// binary.BigEndian.Uint64(tBytes[:8]),
//}
//fp.K256FpNew().Arithmetic.ToMontgomery(&newA, &newA)
//b.FillBytes(tBytes[:])
//newB := [native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(tBytes[24:32]),
// binary.BigEndian.Uint64(tBytes[16:24]),
// binary.BigEndian.Uint64(tBytes[8:16]),
// binary.BigEndian.Uint64(tBytes[:8]),
//}
//fp.K256FpNew().Arithmetic.ToMontgomery(&newB, &newB)
//z.FillBytes(tBytes[:])
//newZ := [native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(tBytes[24:32]),
// binary.BigEndian.Uint64(tBytes[16:24]),
// binary.BigEndian.Uint64(tBytes[8:16]),
// binary.BigEndian.Uint64(tBytes[:8]),
//}
//fp.K256FpNew().Arithmetic.ToMontgomery(&newZ, &newZ)
k256PointSswuParams = native.SswuParams{
// (q -3) // 4
C1: [native.FieldLimbs]uint64{0xffffffffbfffff0b, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffffff},
// sqrt(-z^3)
C2: [native.FieldLimbs]uint64{0x5b57ba53a30d1520, 0x908f7cef34a762eb, 0x190b0ffe068460c8, 0x98a9828e8f00ff62},
// 0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533
A: [native.FieldLimbs]uint64{0xdb714ce7b18444a1, 0x4458ce38a32a19a2, 0xa0e58ae2837bfbf0, 0x505aabc49336d959},
// 1771
B: [native.FieldLimbs]uint64{0x000006eb001a66db, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
// -11
Z: [native.FieldLimbs]uint64{0xfffffff3ffffd234, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff},
}
}
func k256PointIsogenyInit() {
k256PointIsogenyParams = native.IsogenyParams{
XNum: [][native.FieldLimbs]uint64{
{
0x0000003b1c72a8b4,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
{
0xd5bd51a17b2edf46,
0x2cc06f7c86b86bcd,
0x50b37e74f3294a00,
0xeb32314a9da73679,
},
{
0x48c18b1b0d2191bd,
0x5a3f74c29bfccce3,
0xbe55a02e5e8bd357,
0x09bf218d11fff905,
},
{
0x000000001c71c789,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
},
XDen: [][native.FieldLimbs]uint64{
{
0x8af79c1ffdf1e7fa,
0xb84bc22235735eb5,
0x82ee5655a55ace04,
0xce4b32dea0a2becb,
},
{
0x8ecde3f3762e1fa5,
0x2c3b1ad77be333fd,
0xb102a1a152ea6e12,
0x57b82df5a1ffc133,
},
{
0x00000001000003d1,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
},
YNum: [][native.FieldLimbs]uint64{
{
0xffffffce425e12c3,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
},
{
0xba60d5fd6e56922e,
0x4ec198c898a435f2,
0x27e77a577b9764ab,
0xb3b80a1197651d12,
},
{
0xa460c58d0690c6f6,
0xad1fba614dfe6671,
0xdf2ad0172f45e9ab,
0x84df90c688fffc82,
},
{
0x00000000097b4283,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
},
YDen: [][native.FieldLimbs]uint64{
{
0xfffffd0afff4b6fb,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
},
{
0xa0e6d461f9d5bf90,
0x28e34666a05a1c20,
0x88cb0300f0106a0e,
0x6ae1989be1e83c62,
},
{
0x5634d5edb1453160,
0x4258a84339d4cdfc,
0x8983f271fc5fa51b,
0x039444f072ffa1cd,
},
{
0x00000001000003d1,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
},
}
}
func getK256PointIsogenyParams() *native.IsogenyParams {
k256PointIsogenyInitOnce.Do(k256PointIsogenyInit)
return &k256PointIsogenyParams
}
type k256PointArithmetic struct{}
func (k k256PointArithmetic) Hash(out *native.EllipticPoint, hash *native.EllipticPointHasher, msg, dst []byte) error {
var u []byte
sswuParams := getK256PointSswuParams()
isoParams := getK256PointIsogenyParams()
switch hash.Type() {
case native.XMD:
u = native.ExpandMsgXmd(hash, msg, dst, 96)
case native.XOF:
u = native.ExpandMsgXof(hash, msg, dst, 96)
}
var buf [64]byte
copy(buf[:48], internal.ReverseScalarBytes(u[:48]))
u0 := fp.K256FpNew().SetBytesWide(&buf)
copy(buf[:48], internal.ReverseScalarBytes(u[48:]))
u1 := fp.K256FpNew().SetBytesWide(&buf)
r0x, r0y := sswuParams.Osswu3mod4(u0)
r1x, r1y := sswuParams.Osswu3mod4(u1)
q0x, q0y := isoParams.Map(r0x, r0y)
q1x, q1y := isoParams.Map(r1x, r1y)
out.X = q0x
out.Y = q0y
out.Z.SetOne()
tv := &native.EllipticPoint{
X: q1x,
Y: q1y,
Z: fp.K256FpNew().SetOne(),
}
k.Add(out, out, tv)
return nil
}
func (k k256PointArithmetic) Double(out, arg *native.EllipticPoint) {
// Addition formula from Renes-Costello-Batina 2015
// (https://eprint.iacr.org/2015/1060 Algorithm 9)
var yy, zz, xy2, bzz, bzz3, bzz9 [native.FieldLimbs]uint64
var yyMBzz9, yyPBzz3, yyzz, yyzz8, t [native.FieldLimbs]uint64
var x, y, z [native.FieldLimbs]uint64
f := arg.X.Arithmetic
f.Square(&yy, &arg.Y.Value)
f.Square(&zz, &arg.Z.Value)
f.Mul(&xy2, &arg.X.Value, &arg.Y.Value)
f.Add(&xy2, &xy2, &xy2)
f.Mul(&bzz, &zz, &arg.Params.B.Value)
f.Add(&bzz3, &bzz, &bzz)
f.Add(&bzz3, &bzz3, &bzz)
f.Add(&bzz9, &bzz3, &bzz3)
f.Add(&bzz9, &bzz9, &bzz3)
f.Neg(&yyMBzz9, &bzz9)
f.Add(&yyMBzz9, &yyMBzz9, &yy)
f.Add(&yyPBzz3, &yy, &bzz3)
f.Mul(&yyzz, &yy, &zz)
f.Add(&yyzz8, &yyzz, &yyzz)
f.Add(&yyzz8, &yyzz8, &yyzz8)
f.Add(&yyzz8, &yyzz8, &yyzz8)
f.Add(&t, &yyzz8, &yyzz8)
f.Add(&t, &t, &yyzz8)
f.Mul(&t, &t, &arg.Params.B.Value)
f.Mul(&x, &xy2, &yyMBzz9)
f.Mul(&y, &yyMBzz9, &yyPBzz3)
f.Add(&y, &y, &t)
f.Mul(&z, &yy, &arg.Y.Value)
f.Mul(&z, &z, &arg.Z.Value)
f.Add(&z, &z, &z)
f.Add(&z, &z, &z)
f.Add(&z, &z, &z)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
}
func (k k256PointArithmetic) Add(out, arg1, arg2 *native.EllipticPoint) {
// Addition formula from Renes-Costello-Batina 2015
// (https://eprint.iacr.org/2015/1060 Algorithm 7).
var xx, yy, zz, nXxYy, nYyZz, nXxZz [native.FieldLimbs]uint64
var tv1, tv2, xyPairs, yzPairs, xzPairs [native.FieldLimbs]uint64
var bzz, bzz3, yyMBzz3, yyPBzz3, byz [native.FieldLimbs]uint64
var byz3, xx3, bxx9, x, y, z [native.FieldLimbs]uint64
f := arg1.X.Arithmetic
f.Mul(&xx, &arg1.X.Value, &arg2.X.Value)
f.Mul(&yy, &arg1.Y.Value, &arg2.Y.Value)
f.Mul(&zz, &arg1.Z.Value, &arg2.Z.Value)
f.Add(&nXxYy, &xx, &yy)
f.Neg(&nXxYy, &nXxYy)
f.Add(&nYyZz, &yy, &zz)
f.Neg(&nYyZz, &nYyZz)
f.Add(&nXxZz, &xx, &zz)
f.Neg(&nXxZz, &nXxZz)
f.Add(&tv1, &arg1.X.Value, &arg1.Y.Value)
f.Add(&tv2, &arg2.X.Value, &arg2.Y.Value)
f.Mul(&xyPairs, &tv1, &tv2)
f.Add(&xyPairs, &xyPairs, &nXxYy)
f.Add(&tv1, &arg1.Y.Value, &arg1.Z.Value)
f.Add(&tv2, &arg2.Y.Value, &arg2.Z.Value)
f.Mul(&yzPairs, &tv1, &tv2)
f.Add(&yzPairs, &yzPairs, &nYyZz)
f.Add(&tv1, &arg1.X.Value, &arg1.Z.Value)
f.Add(&tv2, &arg2.X.Value, &arg2.Z.Value)
f.Mul(&xzPairs, &tv1, &tv2)
f.Add(&xzPairs, &xzPairs, &nXxZz)
f.Mul(&bzz, &zz, &arg1.Params.B.Value)
f.Add(&bzz3, &bzz, &bzz)
f.Add(&bzz3, &bzz3, &bzz)
f.Neg(&yyMBzz3, &bzz3)
f.Add(&yyMBzz3, &yyMBzz3, &yy)
f.Add(&yyPBzz3, &yy, &bzz3)
f.Mul(&byz, &yzPairs, &arg1.Params.B.Value)
f.Add(&byz3, &byz, &byz)
f.Add(&byz3, &byz3, &byz)
f.Add(&xx3, &xx, &xx)
f.Add(&xx3, &xx3, &xx)
f.Add(&bxx9, &xx3, &xx3)
f.Add(&bxx9, &bxx9, &xx3)
f.Mul(&bxx9, &bxx9, &arg1.Params.B.Value)
f.Mul(&tv1, &xyPairs, &yyMBzz3)
f.Mul(&tv2, &byz3, &xzPairs)
f.Neg(&tv2, &tv2)
f.Add(&x, &tv1, &tv2)
f.Mul(&tv1, &yyPBzz3, &yyMBzz3)
f.Mul(&tv2, &bxx9, &xzPairs)
f.Add(&y, &tv1, &tv2)
f.Mul(&tv1, &yzPairs, &yyPBzz3)
f.Mul(&tv2, &xx3, &xyPairs)
f.Add(&z, &tv1, &tv2)
e1 := arg1.Z.IsZero()
e2 := arg2.Z.IsZero()
// If arg1 is identity set it to arg2
f.Selectznz(&z, &z, &arg2.Z.Value, e1)
f.Selectznz(&y, &y, &arg2.Y.Value, e1)
f.Selectznz(&x, &x, &arg2.X.Value, e1)
// If arg2 is identity set it to arg1
f.Selectznz(&z, &z, &arg1.Z.Value, e2)
f.Selectznz(&y, &y, &arg1.Y.Value, e2)
f.Selectznz(&x, &x, &arg1.X.Value, e2)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
}
func (k k256PointArithmetic) IsOnCurve(arg *native.EllipticPoint) bool {
affine := K256PointNew()
k.ToAffine(affine, arg)
lhs := fp.K256FpNew().Square(affine.Y)
rhs := fp.K256FpNew()
k.RhsEq(rhs, affine.X)
return lhs.Equal(rhs) == 1
}
func (k k256PointArithmetic) ToAffine(out, arg *native.EllipticPoint) {
var wasInverted int
var zero, x, y, z [native.FieldLimbs]uint64
f := arg.X.Arithmetic
f.Invert(&wasInverted, &z, &arg.Z.Value)
f.Mul(&x, &arg.X.Value, &z)
f.Mul(&y, &arg.Y.Value, &z)
out.Z.SetOne()
// If point at infinity this does nothing
f.Selectznz(&x, &zero, &x, wasInverted)
f.Selectznz(&y, &zero, &y, wasInverted)
f.Selectznz(&z, &zero, &out.Z.Value, wasInverted)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
out.Params = arg.Params
out.Arithmetic = arg.Arithmetic
}
func (k k256PointArithmetic) RhsEq(out, x *native.Field) {
// Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7
out.Square(x)
out.Mul(out, x)
out.Add(out, getK256PointParams().B)
}
+19
View File
@@ -0,0 +1,19 @@
package k256_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/core/curves/native/k256"
)
func TestK256PointArithmetic_Hash(t *testing.T) {
var b [32]byte
sc, err := k256.K256PointNew().Hash(b[:], native.EllipticPointHasherSha256())
require.NoError(t, err)
require.True(t, !sc.IsIdentity())
require.True(t, sc.IsOnCurve())
}
+82
View File
@@ -0,0 +1,82 @@
package native
// SswuParams for computing the Simplified SWU mapping
// for hash to curve implementations
type SswuParams struct {
C1, C2, A, B, Z [FieldLimbs]uint64
}
// Osswu3mod4 computes the simplified map optmized for 3 mod 4 primes
// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-11#appendix-G.2.1
func (p *SswuParams) Osswu3mod4(u *Field) (x, y *Field) {
var tv1, tv2, tv3, tv4, xd, x1n, x2n, gxd, gx1, aNeg, zA, y1, y2 [FieldLimbs]uint64
var wasInverted int
u.Arithmetic.Mul(&tv1, &u.Value, &u.Value) // tv1 = u^2
u.Arithmetic.Mul(&tv3, &p.Z, &tv1) // tv3 = z * tv1
u.Arithmetic.Square(&tv2, &tv3) // tv2 = tv3^2
u.Arithmetic.Add(&xd, &tv2, &tv3) // xd = tv2 + tv3
u.Arithmetic.Add(&x1n, &u.Params.R, &xd) // x1n = (xd + 1)
u.Arithmetic.Mul(&x1n, &x1n, &p.B) // x1n * B
u.Arithmetic.Neg(&aNeg, &p.A)
u.Arithmetic.Mul(&xd, &xd, &aNeg) // xd = -A * xd
xdIsZero := (&Field{
Value: xd,
}).IsZero()
u.Arithmetic.Mul(&zA, &p.Z, &p.A)
u.Arithmetic.Selectznz(&xd, &xd, &zA, xdIsZero) // xd = z * A if xd == 0
u.Arithmetic.Square(&tv2, &xd) // tv2 = xd^2
u.Arithmetic.Mul(&gxd, &tv2, &xd) // gxd = tv2 * xd
u.Arithmetic.Mul(&tv2, &tv2, &p.A) // tv2 = A * tv2
u.Arithmetic.Square(&gx1, &x1n) // gx1 = x1n^2
u.Arithmetic.Add(&gx1, &gx1, &tv2) // gx1 = gx1 + tv2
u.Arithmetic.Mul(&gx1, &gx1, &x1n) // gx1 = gx1 * x1n
u.Arithmetic.Mul(&tv2, &gxd, &p.B) // tv2 = B * gxd
u.Arithmetic.Add(&gx1, &gx1, &tv2) // gx1 = gx1 + tv2
u.Arithmetic.Square(&tv4, &gxd) // tv4 = gxd^2
u.Arithmetic.Mul(&tv2, &gx1, &gxd) // tv2 = gx1 * gxd
u.Arithmetic.Mul(&tv4, &tv4, &tv2) // tv4 = tv4 * tv2
Pow(&y1, &tv4, &p.C1, u.Params, u.Arithmetic) // y1 = tv4^C1
u.Arithmetic.Mul(&y1, &y1, &tv2) //y1 = y1 * tv2
u.Arithmetic.Mul(&x2n, &tv3, &x1n) // x2n = tv3 * x1n
u.Arithmetic.Mul(&y2, &y1, &p.C2) // y2 = y1 * c2
u.Arithmetic.Mul(&y2, &y2, &tv1) // y2 = y2 * tv1
u.Arithmetic.Mul(&y2, &y2, &u.Value) // y2 = y2 * u
u.Arithmetic.Square(&tv2, &y1) // tv2 = y1^2
u.Arithmetic.Mul(&tv2, &tv2, &gxd) // tv2 = tv2 * gxd
e2 := (&Field{Value: tv2}).Equal(&Field{Value: gx1})
x = new(Field).Set(u)
y = new(Field).Set(u)
// If e2, x = x1, else x = x2
u.Arithmetic.Selectznz(&x.Value, &x2n, &x1n, e2)
// xn / xd
u.Arithmetic.Invert(&wasInverted, &tv1, &xd)
u.Arithmetic.Mul(&tv1, &x.Value, &tv1)
u.Arithmetic.Selectznz(&x.Value, &x.Value, &tv1, wasInverted)
// If e2, y = y1, else y = y2
u.Arithmetic.Selectznz(&y.Value, &y2, &y1, e2)
uBytes := u.Bytes()
yBytes := y.Bytes()
usign := uBytes[0] & 1
ysign := yBytes[0] & 1
// Fix sign of y
if usign != ysign {
y.Neg(y)
}
return
}
+172
View File
@@ -0,0 +1,172 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
"math/big"
"sync"
"github.com/onsonr/sonr/crypto/core/curves/native"
)
var (
p256FpInitonce sync.Once
p256FpParams native.FieldParams
)
func P256FpNew() *native.Field {
return &native.Field{
Value: [native.FieldLimbs]uint64{},
Params: getP256FpParams(),
Arithmetic: p256FpArithmetic{},
}
}
func p256FpParamsInit() {
// See FIPS 186-3, section D.2.3
p256FpParams = native.FieldParams{
R: [native.FieldLimbs]uint64{0x0000000000000001, 0xffffffff00000000, 0xffffffffffffffff, 0x00000000fffffffe},
R2: [native.FieldLimbs]uint64{0x0000000000000003, 0xfffffffbffffffff, 0xfffffffffffffffe, 0x00000004fffffffd},
R3: [native.FieldLimbs]uint64{0xfffffffd0000000a, 0xffffffedfffffff7, 0x00000005fffffffc, 0x0000001800000001},
Modulus: [native.FieldLimbs]uint64{0xffffffffffffffff, 0x00000000ffffffff, 0x0000000000000000, 0xffffffff00000001},
BiModulus: new(big.Int).SetBytes([]byte{
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}),
}
}
func getP256FpParams() *native.FieldParams {
p256FpInitonce.Do(p256FpParamsInit)
return &p256FpParams
}
// p256FpArithmetic is a struct with all the methods needed for working
// in mod q
type p256FpArithmetic struct{}
// ToMontgomery converts this field to montgomery form
func (f p256FpArithmetic) ToMontgomery(out, arg *[native.FieldLimbs]uint64) {
ToMontgomery((*MontgomeryDomainFieldElement)(out), (*NonMontgomeryDomainFieldElement)(arg))
}
// FromMontgomery converts this field from montgomery form
func (f p256FpArithmetic) FromMontgomery(out, arg *[native.FieldLimbs]uint64) {
FromMontgomery((*NonMontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Neg performs modular negation
func (f p256FpArithmetic) Neg(out, arg *[native.FieldLimbs]uint64) {
Opp((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Square performs modular square
func (f p256FpArithmetic) Square(out, arg *[native.FieldLimbs]uint64) {
Square((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Mul performs modular multiplication
func (f p256FpArithmetic) Mul(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Mul((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Add performs modular addition
func (f p256FpArithmetic) Add(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Add((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Sub performs modular subtraction
func (f p256FpArithmetic) Sub(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Sub((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Sqrt performs modular square root
func (f p256FpArithmetic) Sqrt(wasSquare *int, out, arg *[native.FieldLimbs]uint64) {
// Use p = 3 mod 4 by Euler's criterion means
// arg^((p+1)/4 mod p
var t, c [native.FieldLimbs]uint64
c1 := [native.FieldLimbs]uint64{
0x0000_0000_0000_0000,
0x0000_0000_4000_0000,
0x4000_0000_0000_0000,
0x3fff_ffff_c000_0000,
}
native.Pow(&t, arg, &c1, getP256FpParams(), f)
Square((*MontgomeryDomainFieldElement)(&c), (*MontgomeryDomainFieldElement)(&t))
*wasSquare = (&native.Field{Value: c, Params: getP256FpParams(), Arithmetic: f}).Equal(&native.Field{
Value: *arg, Params: getP256FpParams(), Arithmetic: f,
})
Selectznz(out, uint1(*wasSquare), out, &t)
}
// Invert performs modular inverse
func (f p256FpArithmetic) Invert(wasInverted *int, out, arg *[native.FieldLimbs]uint64) {
// Fermat's Little Theorem
// a ^ (p - 2) mod p
//
// The exponent pattern (from high to low) is:
// - 32 bits of value 1
// - 31 bits of value 0
// - 1 bit of value 1
// - 96 bits of value 0
// - 94 bits of value 1
// - 1 bit of value 0
// - 1 bit of value 1
// To speed up the square-and-multiply algorithm, precompute a^(2^31-1).
//
// Courtesy of Thomas Pornin
//
var t, r [native.FieldLimbs]uint64
copy(t[:], arg[:])
for i := 0; i < 30; i++ {
f.Square(&t, &t)
f.Mul(&t, &t, arg)
}
copy(r[:], t[:])
for i := 224; i >= 0; i-- {
f.Square(&r, &r)
switch i {
case 0:
fallthrough
case 2:
fallthrough
case 192:
fallthrough
case 224:
f.Mul(&r, &r, arg)
case 3:
fallthrough
case 34:
fallthrough
case 65:
f.Mul(&r, &r, &t)
}
}
*wasInverted = (&native.Field{
Value: *arg,
Params: getP256FpParams(),
Arithmetic: f,
}).IsNonZero()
Selectznz(out, uint1(*wasInverted), out, &r)
}
// FromBytes converts a little endian byte array into a field element
func (f p256FpArithmetic) FromBytes(out *[native.FieldLimbs]uint64, arg *[native.FieldBytes]byte) {
FromBytes(out, arg)
}
// ToBytes converts a field element to a little endian byte array
func (f p256FpArithmetic) ToBytes(out *[native.FieldBytes]byte, arg *[native.FieldLimbs]uint64) {
ToBytes(out, arg)
}
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f p256FpArithmetic) Selectznz(out, arg1, arg2 *[native.FieldLimbs]uint64, choice int) {
Selectznz(out, uint1(choice), arg1, arg2)
}
+328
View File
@@ -0,0 +1,328 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/internal"
)
func TestFpSetOne(t *testing.T) {
fq := P256FpNew().SetOne()
require.NotNil(t, fq)
require.Equal(t, fq.Value, getP256FpParams().R)
}
func TestFpSetUint64(t *testing.T) {
act := P256FpNew().SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act.Value[0], uint64(0x100000000fffffff))
}
func TestFpAdd(t *testing.T) {
lhs := P256FpNew().SetOne()
rhs := P256FpNew().SetOne()
exp := P256FpNew().SetUint64(2)
res := P256FpNew().Add(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := P256FpNew().Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpSub(t *testing.T) {
lhs := P256FpNew().SetOne()
rhs := P256FpNew().SetOne()
exp := P256FpNew().SetZero()
res := P256FpNew().Sub(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := P256FpNew().Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpMul(t *testing.T) {
lhs := P256FpNew().SetOne()
rhs := P256FpNew().SetOne()
exp := P256FpNew().SetOne()
res := P256FpNew().Mul(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := P256FpNew().Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpDouble(t *testing.T) {
a := P256FpNew().SetUint64(2)
e := P256FpNew().SetUint64(4)
require.Equal(t, e, P256FpNew().Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = P256FpNew().SetUint64(uint64(tv))
e = P256FpNew().SetUint64(ttv)
require.Equal(t, e, P256FpNew().Double(a))
}
}
func TestFpSquare(t *testing.T) {
a := P256FpNew().SetUint64(4)
e := P256FpNew().SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFpNeg(t *testing.T) {
g := P256FpNew().SetUint64(7)
a := P256FpNew().SetOne()
a.Neg(a)
e := P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xfffffffffffffffe, 0x00000001ffffffff, 0x0000000000000000, 0xfffffffe00000002})
require.Equal(t, e, a)
a.Neg(g)
e = P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xfffffffffffffff8, 0x00000007ffffffff, 0x0000000000000000, 0xfffffff800000008})
require.Equal(t, e, a)
}
func TestFpExp(t *testing.T) {
e := P256FpNew().SetUint64(8)
a := P256FpNew().SetUint64(2)
by := P256FpNew().SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFpSqrt(t *testing.T) {
t1 := P256FpNew().SetUint64(2)
t2 := P256FpNew().Neg(t1)
t3 := P256FpNew().Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.Equal(t, 1, t1.Equal(t3)|t2.Equal(t3))
t1.SetUint64(3)
_, wasSquare = P256FpNew().Sqrt(t1)
require.False(t, wasSquare)
}
func TestFpInvert(t *testing.T) {
twoInv := P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x8000000000000000})
two := P256FpNew().SetUint64(2)
a, inverted := P256FpNew().Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnity := P256FpNew().SetLimbs(&[native.FieldLimbs]uint64{0x8619a9e760c01d0c, 0xa883c4fba37998df, 0x45607580b6eabd98, 0xf252b002544b2f99})
rootOfUnityInv := P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0x2609d8ab477f96d1, 0x6e5f128fb20a0f24, 0xe4d636874d99643b, 0x376080cc1f2a3735})
a, inverted = P256FpNew().Invert(rootOfUnity)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := P256FpNew().SetUint64(9)
rhs := P256FpNew().SetUint64(3)
rhsInv, inverted := P256FpNew().Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, P256FpNew().Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = P256FpNew().Invert(rhs)
require.False(t, inverted)
}
func TestFpCMove(t *testing.T) {
t1 := P256FpNew().SetUint64(5)
t2 := P256FpNew().SetUint64(10)
require.Equal(t, t1, P256FpNew().CMove(t1, t2, 0))
require.Equal(t, t2, P256FpNew().CMove(t1, t2, 1))
}
func TestFpBytes(t *testing.T) {
t1 := P256FpNew().SetUint64(99)
seq := t1.Bytes()
t2, err := P256FpNew().SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFpCmp(t *testing.T) {
tests := []struct {
a *native.Field
b *native.Field
e int
}{
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{2731658267414164836, 14655288906067898431, 6537465423330262322, 8306191141697566219}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{6472764012681988529, 10848812988401906064, 2961825807536828898, 4282183981941645679}),
e: 1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{8023004109510539223, 4652004072850285717, 1877219145646046927, 383214385093921911}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{10099384440823804262, 16139476942229308465, 8636966320777393798, 5435928725024696785}),
e: -1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{3741840066202388211, 12165774400417314871, 16619312580230515379, 16195032234110087705}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{3905865991286066744, 543690822309071825, 17963103015950210055, 3745476720756119742}),
e: 1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{16660853697936147788, 7799793619412111108, 13515141085171033220, 2641079731236069032}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{17790588295388238399, 571847801379669440, 14537208974498222469, 12792570372087452754}),
e: -1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{3912839285384959186, 2701177075110484070, 6453856448115499033, 6475797457962597458}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{1282566391665688512, 13503640416992806563, 2962240104675990153, 3374904770947067689}),
e: 1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{5716631803409360103, 7859567470082614154, 12747956220853330146, 18434584096087315020}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{16317076441459028418, 12854146980376319601, 2258436689269031143, 9531877130792223752}),
e: 1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{17955191469941083403, 10350326247207200880, 17263512235150705075, 12700328451238078022}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{6767595547459644695, 7146403825494928147, 12269344038346710612, 9122477829383225603}),
e: 1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{17099388671847024438, 6426264987820696548, 10641143464957227405, 7709745403700754098}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{10799154372990268556, 17178492485719929374, 5705777922258988797, 8051037767683567782}),
e: -1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{4567139260680454325, 1629385880182139061, 16607020832317899145, 1261011562621553200}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{13487234491304534488, 17872642955936089265, 17651026784972590233, 9468934643333871559}),
e: -1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{18071070103467571798, 11787850505799426140, 10631355976141928593, 4867785203635092610}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{12596443599426461624, 10176122686151524591, 17075755296887483439, 6726169532695070719}),
e: -1,
},
}
for _, test := range tests {
require.Equal(t, test.e, test.a.Cmp(test.b))
require.Equal(t, -test.e, test.b.Cmp(test.a))
require.Equal(t, 0, test.a.Cmp(test.a))
require.Equal(t, 0, test.b.Cmp(test.b))
}
}
func TestFpBigInt(t *testing.T) {
t1 := P256FpNew().SetBigInt(big.NewInt(9999))
t2 := P256FpNew().SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0x515151513f3f3f3e, 0xc9c9c9cb36363636, 0xb7b7b7b79c9c9c9c, 0xfffffffeaeaeaeaf})
b := new(big.Int).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Value[0] = 0xaeaeaeaec0c0c0c1
e.Value[1] = 0x36363635c9c9c9c9
e.Value[2] = 0x4848484863636363
e.Value[3] = 0x0000000051515151
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFpSetBytesWide(t *testing.T) {
e := P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xccdefd48c77805bc, 0xe935dc2db86364d6, 0xca8ee6e5870a020e, 0x4c94bf4467f3b5bf})
a := P256FpNew().SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
func TestFpSetBytesWideBigInt(t *testing.T) {
params := getP256FpParams()
var tv2 [64]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, params.BiModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a := P256FpNew().SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
File diff suppressed because it is too large Load Diff
+295
View File
@@ -0,0 +1,295 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
"math/big"
"sync"
"github.com/onsonr/sonr/crypto/core/curves/native"
)
var (
p256FqInitonce sync.Once
p256FqParams native.FieldParams
)
func P256FqNew() *native.Field {
return &native.Field{
Value: [native.FieldLimbs]uint64{},
Params: getP256FqParams(),
Arithmetic: p256FqArithmetic{},
}
}
func p256FqParamsInit() {
// See FIPS 186-3, section D.2.3
p256FqParams = native.FieldParams{
R: [native.FieldLimbs]uint64{0x0c46353d039cdaaf, 0x4319055258e8617b, 0x0000000000000000, 0x00000000ffffffff},
R2: [native.FieldLimbs]uint64{0x83244c95be79eea2, 0x4699799c49bd6fa6, 0x2845b2392b6bec59, 0x66e12d94f3d95620},
R3: [native.FieldLimbs]uint64{0xac8ebec90b65a624, 0x111f28ae0c0555c9, 0x2543b9246ba5e93f, 0x503a54e76407be65},
Modulus: [native.FieldLimbs]uint64{0xf3b9cac2fc632551, 0xbce6faada7179e84, 0xffffffffffffffff, 0xffffffff00000000},
BiModulus: new(big.Int).SetBytes([]byte{
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51,
}),
}
}
func getP256FqParams() *native.FieldParams {
p256FqInitonce.Do(p256FqParamsInit)
return &p256FqParams
}
// p256FqArithmetic is a struct with all the methods needed for working
// in mod q
type p256FqArithmetic struct{}
// ToMontgomery converts this field to montgomery form
func (f p256FqArithmetic) ToMontgomery(out, arg *[native.FieldLimbs]uint64) {
ToMontgomery((*MontgomeryDomainFieldElement)(out), (*NonMontgomeryDomainFieldElement)(arg))
}
// FromMontgomery converts this field from montgomery form
func (f p256FqArithmetic) FromMontgomery(out, arg *[native.FieldLimbs]uint64) {
FromMontgomery((*NonMontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Neg performs modular negation
func (f p256FqArithmetic) Neg(out, arg *[native.FieldLimbs]uint64) {
Opp((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Square performs modular square
func (f p256FqArithmetic) Square(out, arg *[native.FieldLimbs]uint64) {
Square((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Mul performs modular multiplication
func (f p256FqArithmetic) Mul(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Mul((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Add performs modular addition
func (f p256FqArithmetic) Add(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Add((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Sub performs modular subtraction
func (f p256FqArithmetic) Sub(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Sub((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg1), (*MontgomeryDomainFieldElement)(arg2))
}
// Sqrt performs modular square root
func (f p256FqArithmetic) Sqrt(wasSquare *int, out, arg *[native.FieldLimbs]uint64) {
// See sqrt_ts_ct at
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.4
// c1 := s
// c2 := (q - 1) / (2^c1)
// c2 := [4]uint64{
// 0x4f3b9cac2fc63255,
// 0xfbce6faada7179e8,
// 0x0fffffffffffffff,
// 0x0ffffffff0000000,
// }
// c3 := (c2 - 1) / 2
c3 := [native.FieldLimbs]uint64{
0x279dce5617e3192a,
0xfde737d56d38bcf4,
0x07ffffffffffffff,
0x07fffffff8000000,
}
// c4 := generator
// c5 := new(Fq).pow(generator, c2)
c5 := [native.FieldLimbs]uint64{0x1015708f7e368fe1, 0x31c6c5456ecc4511, 0x5281fe8998a19ea1, 0x0279089e10c63fe8}
var z, t, b, c, tv [native.FieldLimbs]uint64
native.Pow(&z, arg, &c3, getP256FqParams(), f)
Square((*MontgomeryDomainFieldElement)(&t), (*MontgomeryDomainFieldElement)(&z))
Mul((*MontgomeryDomainFieldElement)(&t), (*MontgomeryDomainFieldElement)(&t), (*MontgomeryDomainFieldElement)(arg))
Mul((*MontgomeryDomainFieldElement)(&z), (*MontgomeryDomainFieldElement)(&z), (*MontgomeryDomainFieldElement)(arg))
copy(b[:], t[:])
copy(c[:], c5[:])
for i := s; i >= 2; i-- {
for j := 1; j <= i-2; j++ {
Square((*MontgomeryDomainFieldElement)(&b), (*MontgomeryDomainFieldElement)(&b))
}
// if b == 1 flag = 0 else flag = 1
flag := -(&native.Field{
Value: b,
Params: getP256FqParams(),
Arithmetic: f,
}).IsOne() + 1
Mul((*MontgomeryDomainFieldElement)(&tv), (*MontgomeryDomainFieldElement)(&z), (*MontgomeryDomainFieldElement)(&c))
Selectznz(&z, uint1(flag), &z, &tv)
Square((*MontgomeryDomainFieldElement)(&c), (*MontgomeryDomainFieldElement)(&c))
Mul((*MontgomeryDomainFieldElement)(&tv), (*MontgomeryDomainFieldElement)(&t), (*MontgomeryDomainFieldElement)(&c))
Selectznz(&t, uint1(flag), &t, &tv)
copy(b[:], t[:])
}
Square((*MontgomeryDomainFieldElement)(&c), (*MontgomeryDomainFieldElement)(&z))
*wasSquare = (&native.Field{
Value: c,
Params: getP256FqParams(),
Arithmetic: f,
}).Equal(&native.Field{
Value: *arg,
Params: getP256FqParams(),
Arithmetic: f,
})
Selectznz(out, uint1(*wasSquare), out, &z)
}
// Invert performs modular inverse
func (f p256FqArithmetic) Invert(wasInverted *int, out, arg *[native.FieldLimbs]uint64) {
// Using an addition chain from
// https://briansmith.org/ecc-inversion-addition-chains-01#p256_field_inversion
var x1, x10, x11, x101, x111, x1010, x1111, x10101, x101010, x101111 [native.FieldLimbs]uint64
var x6, x8, x16, x32, tmp [native.FieldLimbs]uint64
copy(x1[:], arg[:])
native.Pow2k(&x10, arg, 1, f)
Mul((*MontgomeryDomainFieldElement)(&x11), (*MontgomeryDomainFieldElement)(&x10), (*MontgomeryDomainFieldElement)(&x1))
Mul((*MontgomeryDomainFieldElement)(&x101), (*MontgomeryDomainFieldElement)(&x10), (*MontgomeryDomainFieldElement)(&x11))
Mul((*MontgomeryDomainFieldElement)(&x111), (*MontgomeryDomainFieldElement)(&x10), (*MontgomeryDomainFieldElement)(&x101))
native.Pow2k(&x1010, &x101, 1, f)
Mul((*MontgomeryDomainFieldElement)(&x1111), (*MontgomeryDomainFieldElement)(&x101), (*MontgomeryDomainFieldElement)(&x1010))
native.Pow2k(&x10101, &x1010, 1, f)
Mul((*MontgomeryDomainFieldElement)(&x10101), (*MontgomeryDomainFieldElement)(&x10101), (*MontgomeryDomainFieldElement)(&x1))
native.Pow2k(&x101010, &x10101, 1, f)
Mul((*MontgomeryDomainFieldElement)(&x101111), (*MontgomeryDomainFieldElement)(&x101), (*MontgomeryDomainFieldElement)(&x101010))
Mul((*MontgomeryDomainFieldElement)(&x6), (*MontgomeryDomainFieldElement)(&x10101), (*MontgomeryDomainFieldElement)(&x101010))
native.Pow2k(&x8, &x6, 2, f)
Mul((*MontgomeryDomainFieldElement)(&x8), (*MontgomeryDomainFieldElement)(&x8), (*MontgomeryDomainFieldElement)(&x11))
native.Pow2k(&x16, &x8, 8, f)
Mul((*MontgomeryDomainFieldElement)(&x16), (*MontgomeryDomainFieldElement)(&x16), (*MontgomeryDomainFieldElement)(&x8))
native.Pow2k(&x32, &x16, 16, f)
Mul((*MontgomeryDomainFieldElement)(&x32), (*MontgomeryDomainFieldElement)(&x32), (*MontgomeryDomainFieldElement)(&x16))
native.Pow2k(&tmp, &x32, 64, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x32))
native.Pow2k(&tmp, &tmp, 32, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x32))
native.Pow2k(&tmp, &tmp, 6, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x101111))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x111))
native.Pow2k(&tmp, &tmp, 4, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x11))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1111))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x10101))
native.Pow2k(&tmp, &tmp, 4, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x101))
native.Pow2k(&tmp, &tmp, 3, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x101))
native.Pow2k(&tmp, &tmp, 3, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x101))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x111))
native.Pow2k(&tmp, &tmp, 9, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x101111))
native.Pow2k(&tmp, &tmp, 6, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1111))
native.Pow2k(&tmp, &tmp, 2, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1))
native.Pow2k(&tmp, &tmp, 6, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1111))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x111))
native.Pow2k(&tmp, &tmp, 4, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x111))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x111))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x101))
native.Pow2k(&tmp, &tmp, 3, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x11))
native.Pow2k(&tmp, &tmp, 10, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x101111))
native.Pow2k(&tmp, &tmp, 2, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x11))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x11))
native.Pow2k(&tmp, &tmp, 5, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x11))
native.Pow2k(&tmp, &tmp, 3, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1))
native.Pow2k(&tmp, &tmp, 7, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x10101))
native.Pow2k(&tmp, &tmp, 6, f)
Mul((*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&tmp), (*MontgomeryDomainFieldElement)(&x1111))
*wasInverted = (&native.Field{
Value: *arg,
Params: getP256FqParams(),
Arithmetic: f,
}).IsNonZero()
Selectznz(out, uint1(*wasInverted), out, &tmp)
}
// FromBytes converts a little endian byte array into a field element
func (f p256FqArithmetic) FromBytes(out *[native.FieldLimbs]uint64, arg *[native.FieldBytes]byte) {
FromBytes(out, arg)
}
// ToBytes converts a field element to a little endian byte array
func (f p256FqArithmetic) ToBytes(out *[native.FieldBytes]byte, arg *[native.FieldLimbs]uint64) {
ToBytes(out, arg)
}
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f p256FqArithmetic) Selectznz(out, arg1, arg2 *[native.FieldLimbs]uint64, choice int) {
Selectznz(out, uint1(choice), arg1, arg2)
}
// generator = 7 mod q is a generator of the `q - 1` order multiplicative
// subgroup, or in other words a primitive element of the field.
// generator^t where t * 2^s + 1 = q
var generator = &[native.FieldLimbs]uint64{0x55eb74ab1949fac9, 0xd5af25406e5aaa5d, 0x0000000000000001, 0x00000006fffffff9}
// s satisfies the equation 2^s * t = q - 1 with t odd.
var s = 4
// rootOfUnity
var rootOfUnity = &[native.FieldLimbs]uint64{0x0592d7fbb41e6602, 0x1546cad004378daf, 0xba807ace842a3dfc, 0xffc97f062a770992}
+328
View File
@@ -0,0 +1,328 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/internal"
)
func TestFqSetOne(t *testing.T) {
fq := P256FqNew().SetOne()
require.NotNil(t, fq)
require.Equal(t, fq.Value, getP256FqParams().R)
}
func TestFqSetUint64(t *testing.T) {
act := P256FqNew().SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act.Value[0], uint64(0xb3f3986dec632551))
}
func TestFqAdd(t *testing.T) {
lhs := P256FqNew().SetOne()
rhs := P256FqNew().SetOne()
exp := P256FqNew().SetUint64(2)
res := P256FqNew().Add(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := P256FqNew().Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqSub(t *testing.T) {
lhs := P256FqNew().SetOne()
rhs := P256FqNew().SetOne()
exp := P256FqNew().SetZero()
res := P256FqNew().Sub(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := P256FqNew().Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqMul(t *testing.T) {
lhs := P256FqNew().SetOne()
rhs := P256FqNew().SetOne()
exp := P256FqNew().SetOne()
res := P256FqNew().Mul(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := P256FqNew().Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqDouble(t *testing.T) {
a := P256FqNew().SetUint64(2)
e := P256FqNew().SetUint64(4)
require.Equal(t, e, P256FqNew().Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = P256FqNew().SetUint64(uint64(tv))
e = P256FqNew().SetUint64(ttv)
require.Equal(t, e, P256FqNew().Double(a))
}
}
func TestFqSquare(t *testing.T) {
a := P256FqNew().SetUint64(4)
e := P256FqNew().SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFqNeg(t *testing.T) {
g := P256FqNew().SetRaw(generator)
a := P256FqNew().SetOne()
a.Neg(a)
e := P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0xe7739585f8c64aa2, 0x79cdf55b4e2f3d09, 0xffffffffffffffff, 0xfffffffe00000001})
require.Equal(t, e, a)
a.Neg(g)
e = P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x9dce5617e3192a88, 0xe737d56d38bcf427, 0xfffffffffffffffd, 0xfffffff800000007})
require.Equal(t, e, a)
}
func TestFqExp(t *testing.T) {
e := P256FqNew().SetUint64(8)
a := P256FqNew().SetUint64(2)
by := P256FqNew().SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFqSqrt(t *testing.T) {
t1 := P256FqNew().SetUint64(2)
t2 := P256FqNew().Neg(t1)
t3 := P256FqNew().Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.Equal(t, 1, t1.Equal(t3)|t2.Equal(t3))
t1.SetUint64(7)
_, wasSquare = t3.Sqrt(t1)
require.False(t, wasSquare)
}
func TestFqInvert(t *testing.T) {
twoInv := P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x8000000000000000})
two := P256FqNew().SetUint64(2)
a, inverted := P256FqNew().Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnityInv := P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0xcfbf53b9618acf96, 0xf17e5c39df7bd05b, 0xc7acb1f83e3ad9ad, 0x4659a42b394ff7df})
rootOU := P256FqNew().SetRaw(rootOfUnity)
a, inverted = P256FqNew().Invert(rootOU)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := P256FqNew().SetUint64(9)
rhs := P256FqNew().SetUint64(3)
rhsInv, inverted := P256FqNew().Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, P256FqNew().Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = P256FqNew().Invert(rhs)
require.False(t, inverted)
}
func TestFqCMove(t *testing.T) {
t1 := P256FqNew().SetUint64(5)
t2 := P256FqNew().SetUint64(10)
require.Equal(t, t1, P256FqNew().CMove(t1, t2, 0))
require.Equal(t, t2, P256FqNew().CMove(t1, t2, 1))
}
func TestFqBytes(t *testing.T) {
t1 := P256FqNew().SetUint64(99)
seq := t1.Bytes()
t2, err := P256FqNew().SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFqCmp(t *testing.T) {
tests := []struct {
a *native.Field
b *native.Field
e int
}{
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{2731658267414164836, 14655288906067898431, 6537465423330262322, 8306191141697566219}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{6472764012681988529, 10848812988401906064, 2961825807536828898, 4282183981941645679}),
e: 1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{8023004109510539223, 4652004072850285717, 1877219145646046927, 383214385093921911}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{10099384440823804262, 16139476942229308465, 8636966320777393798, 5435928725024696785}),
e: -1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{3741840066202388211, 12165774400417314871, 16619312580230515379, 16195032234110087705}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{3905865991286066744, 543690822309071825, 17963103015950210055, 3745476720756119742}),
e: 1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{16660853697936147788, 7799793619412111108, 13515141085171033220, 2641079731236069032}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{17790588295388238399, 571847801379669440, 14537208974498222469, 12792570372087452754}),
e: -1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{3912839285384959186, 2701177075110484070, 6453856448115499033, 6475797457962597458}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{1282566391665688512, 13503640416992806563, 2962240104675990153, 3374904770947067689}),
e: 1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{5716631803409360103, 7859567470082614154, 12747956220853330146, 18434584096087315020}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{16317076441459028418, 12854146980376319601, 2258436689269031143, 9531877130792223752}),
e: 1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{17955191469941083403, 10350326247207200880, 17263512235150705075, 12700328451238078022}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{6767595547459644695, 7146403825494928147, 12269344038346710612, 9122477829383225603}),
e: 1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{17099388671847024438, 6426264987820696548, 10641143464957227405, 7709745403700754098}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{10799154372990268556, 17178492485719929374, 5705777922258988797, 8051037767683567782}),
e: -1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{4567139260680454325, 1629385880182139061, 16607020832317899145, 1261011562621553200}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{13487234491304534488, 17872642955936089265, 17651026784972590233, 9468934643333871559}),
e: -1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{18071070103467571798, 11787850505799426140, 10631355976141928593, 4867785203635092610}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{12596443599426461624, 10176122686151524591, 17075755296887483439, 6726169532695070719}),
e: -1,
},
}
for _, test := range tests {
require.Equal(t, test.e, test.a.Cmp(test.b))
require.Equal(t, -test.e, test.b.Cmp(test.a))
require.Equal(t, 0, test.a.Cmp(test.a))
require.Equal(t, 0, test.b.Cmp(test.b))
}
}
func TestFqBigInt(t *testing.T) {
t1 := P256FqNew().SetBigInt(big.NewInt(9999))
t2 := P256FqNew().SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x21dcaaadf1cb6aa0, 0x568de5f5990a98d7, 0x354f43b0d837fac5, 0x3e02532cb23f481a})
b := new(big.Int).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Value[0] = 0xd1dd20150a97bab1
e.Value[1] = 0x665914b80e0d05ad
e.Value[2] = 0xcab0bc4f27c8053a
e.Value[3] = 0xc1fdacd24dc0b7e6
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFqSetBytesWide(t *testing.T) {
e := P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0xe2b8d4b0e576c8fa, 0x9d2b215f85d3bdf7, 0xf6070a872442640c, 0xcf15d1e49c990b88})
a := P256FqNew().SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
func TestFpSetBytesWideBigInt(t *testing.T) {
params := getP256FqParams()
var tv2 [64]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, params.BiModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a := P256FqNew().SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
File diff suppressed because it is too large Load Diff
+317
View File
@@ -0,0 +1,317 @@
package p256
import (
"sync"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/core/curves/native/p256/fp"
"github.com/onsonr/sonr/crypto/internal"
)
var (
p256PointInitonce sync.Once
p256PointParams native.EllipticPointParams
p256PointSswuInitOnce sync.Once
p256PointSswuParams native.SswuParams
)
func P256PointNew() *native.EllipticPoint {
return &native.EllipticPoint{
X: fp.P256FpNew(),
Y: fp.P256FpNew(),
Z: fp.P256FpNew(),
Params: getP256PointParams(),
Arithmetic: &p256PointArithmetic{},
}
}
func p256PointParamsInit() {
// How these values were derived
// left for informational purposes
// params := elliptic.P256().Params()
// a := big.NewInt(-3)
// a.Mod(a, params.P)
// capA := fp.P256FpNew().SetBigInt(a)
// capB := fp.P256FpNew().SetBigInt(params.B)
// gx := fp.P256FpNew().SetBigInt(params.Gx)
// gy := fp.P256FpNew().SetBigInt(params.Gy)
p256PointParams = native.EllipticPointParams{
A: fp.P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xfffffffffffffffc, 0x00000003ffffffff, 0x0000000000000000, 0xfffffffc00000004}),
B: fp.P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xd89cdf6229c4bddf, 0xacf005cd78843090, 0xe5a220abf7212ed6, 0xdc30061d04874834}),
Gx: fp.P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0x79e730d418a9143c, 0x75ba95fc5fedb601, 0x79fb732b77622510, 0x18905f76a53755c6}),
Gy: fp.P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xddf25357ce95560a, 0x8b4ab8e4ba19e45c, 0xd2e88688dd21f325, 0x8571ff1825885d85}),
BitSize: 256,
Name: "P256",
}
}
func getP256PointParams() *native.EllipticPointParams {
p256PointInitonce.Do(p256PointParamsInit)
return &p256PointParams
}
func getP256PointSswuParams() *native.SswuParams {
p256PointSswuInitOnce.Do(p256PointSswuParamsInit)
return &p256PointSswuParams
}
func p256PointSswuParamsInit() {
// How these values were derived
// left for informational purposes
//params := elliptic.P256().Params()
//
//// c1 = (q - 3) / 4
//c1 := new(big.Int).Set(params.P)
//c1.Sub(c1, big.NewInt(3))
//c1.Rsh(c1, 2)
//
//a := big.NewInt(-3)
//a.Mod(a, params.P)
//b := new(big.Int).Set(params.B)
//z := big.NewInt(-10)
//z.Mod(z, params.P)
//// sqrt(-Z^3)
//zTmp := new(big.Int).Exp(z, big.NewInt(3), nil)
//zTmp = zTmp.Neg(zTmp)
//zTmp.Mod(zTmp, params.P)
//c2 := new(big.Int).ModSqrt(zTmp, params.P)
//
//var capC1Bytes [32]byte
//c1.FillBytes(capC1Bytes[:])
//capC1 := fp.P256FpNew().SetRaw(&[native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(capC1Bytes[24:]),
// binary.BigEndian.Uint64(capC1Bytes[16:24]),
// binary.BigEndian.Uint64(capC1Bytes[8:16]),
// binary.BigEndian.Uint64(capC1Bytes[:8]),
//})
//capC2 := fp.P256FpNew().SetBigInt(c2)
//capA := fp.P256FpNew().SetBigInt(a)
//capB := fp.P256FpNew().SetBigInt(b)
//capZ := fp.P256FpNew().SetBigInt(z)
p256PointSswuParams = native.SswuParams{
C1: [native.FieldLimbs]uint64{0xffffffffffffffff, 0x000000003fffffff, 0x4000000000000000, 0x3fffffffc0000000},
C2: [native.FieldLimbs]uint64{0x53e43951f64fdbe7, 0xb2806c63966a1a66, 0x1ac5d59c3298bf50, 0xa3323851ba997e27},
A: [native.FieldLimbs]uint64{0xfffffffffffffffc, 0x00000003ffffffff, 0x0000000000000000, 0xfffffffc00000004},
B: [native.FieldLimbs]uint64{0xd89cdf6229c4bddf, 0xacf005cd78843090, 0xe5a220abf7212ed6, 0xdc30061d04874834},
Z: [native.FieldLimbs]uint64{0xfffffffffffffff5, 0x0000000affffffff, 0x0000000000000000, 0xfffffff50000000b},
}
}
type p256PointArithmetic struct{}
func (k p256PointArithmetic) Hash(out *native.EllipticPoint, hash *native.EllipticPointHasher, msg, dst []byte) error {
var u []byte
sswuParams := getP256PointSswuParams()
switch hash.Type() {
case native.XMD:
u = native.ExpandMsgXmd(hash, msg, dst, 96)
case native.XOF:
u = native.ExpandMsgXof(hash, msg, dst, 96)
}
var buf [64]byte
copy(buf[:48], internal.ReverseScalarBytes(u[:48]))
u0 := fp.P256FpNew().SetBytesWide(&buf)
copy(buf[:48], internal.ReverseScalarBytes(u[48:]))
u1 := fp.P256FpNew().SetBytesWide(&buf)
q0x, q0y := sswuParams.Osswu3mod4(u0)
q1x, q1y := sswuParams.Osswu3mod4(u1)
out.X = q0x
out.Y = q0y
out.Z.SetOne()
tv := &native.EllipticPoint{
X: q1x,
Y: q1y,
Z: fp.P256FpNew().SetOne(),
}
k.Add(out, out, tv)
return nil
}
func (k p256PointArithmetic) Double(out, arg *native.EllipticPoint) {
// Addition formula from Renes-Costello-Batina 2015
// (https://eprint.iacr.org/2015/1060 Algorithm 6)
var xx, yy, zz, xy2, yz2, xz2, bzz, bzz3 [native.FieldLimbs]uint64
var yyMBzz3, yyPBzz3, yFrag, xFrag, zz3 [native.FieldLimbs]uint64
var bxz2, bxz6, xx3Mzz3, x, y, z [native.FieldLimbs]uint64
b := getP256PointParams().B.Value
f := arg.X.Arithmetic
f.Square(&xx, &arg.X.Value)
f.Square(&yy, &arg.Y.Value)
f.Square(&zz, &arg.Z.Value)
f.Mul(&xy2, &arg.X.Value, &arg.Y.Value)
f.Add(&xy2, &xy2, &xy2)
f.Mul(&yz2, &arg.Y.Value, &arg.Z.Value)
f.Add(&yz2, &yz2, &yz2)
f.Mul(&xz2, &arg.X.Value, &arg.Z.Value)
f.Add(&xz2, &xz2, &xz2)
f.Mul(&bzz, &b, &zz)
f.Sub(&bzz, &bzz, &xz2)
f.Add(&bzz3, &bzz, &bzz)
f.Add(&bzz3, &bzz3, &bzz)
f.Sub(&yyMBzz3, &yy, &bzz3)
f.Add(&yyPBzz3, &yy, &bzz3)
f.Mul(&yFrag, &yyPBzz3, &yyMBzz3)
f.Mul(&xFrag, &yyMBzz3, &xy2)
f.Add(&zz3, &zz, &zz)
f.Add(&zz3, &zz3, &zz)
f.Mul(&bxz2, &b, &xz2)
f.Sub(&bxz2, &bxz2, &zz3)
f.Sub(&bxz2, &bxz2, &xx)
f.Add(&bxz6, &bxz2, &bxz2)
f.Add(&bxz6, &bxz6, &bxz2)
f.Add(&xx3Mzz3, &xx, &xx)
f.Add(&xx3Mzz3, &xx3Mzz3, &xx)
f.Sub(&xx3Mzz3, &xx3Mzz3, &zz3)
f.Mul(&x, &bxz6, &yz2)
f.Sub(&x, &xFrag, &x)
f.Mul(&y, &xx3Mzz3, &bxz6)
f.Add(&y, &yFrag, &y)
f.Mul(&z, &yz2, &yy)
f.Add(&z, &z, &z)
f.Add(&z, &z, &z)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
}
func (k p256PointArithmetic) Add(out, arg1, arg2 *native.EllipticPoint) {
// Addition formula from Renes-Costello-Batina 2015
// (https://eprint.iacr.org/2015/1060 Algorithm 4).
var xx, yy, zz, zz3, bxz, bxz3 [native.FieldLimbs]uint64
var tv1, xyPairs, yzPairs, xzPairs [native.FieldLimbs]uint64
var bzz, bzz3, yyMBzz3, yyPBzz3 [native.FieldLimbs]uint64
var xx3Mzz3, x, y, z [native.FieldLimbs]uint64
f := arg1.X.Arithmetic
b := getP256PointParams().B.Value
f.Mul(&xx, &arg1.X.Value, &arg2.X.Value)
f.Mul(&yy, &arg1.Y.Value, &arg2.Y.Value)
f.Mul(&zz, &arg1.Z.Value, &arg2.Z.Value)
f.Add(&tv1, &arg2.X.Value, &arg2.Y.Value)
f.Add(&xyPairs, &arg1.X.Value, &arg1.Y.Value)
f.Mul(&xyPairs, &xyPairs, &tv1)
f.Sub(&xyPairs, &xyPairs, &xx)
f.Sub(&xyPairs, &xyPairs, &yy)
f.Add(&tv1, &arg2.Y.Value, &arg2.Z.Value)
f.Add(&yzPairs, &arg1.Y.Value, &arg1.Z.Value)
f.Mul(&yzPairs, &yzPairs, &tv1)
f.Sub(&yzPairs, &yzPairs, &yy)
f.Sub(&yzPairs, &yzPairs, &zz)
f.Add(&tv1, &arg2.X.Value, &arg2.Z.Value)
f.Add(&xzPairs, &arg1.X.Value, &arg1.Z.Value)
f.Mul(&xzPairs, &xzPairs, &tv1)
f.Sub(&xzPairs, &xzPairs, &xx)
f.Sub(&xzPairs, &xzPairs, &zz)
f.Mul(&bzz, &b, &zz)
f.Sub(&bzz, &xzPairs, &bzz)
f.Add(&bzz3, &bzz, &bzz)
f.Add(&bzz3, &bzz3, &bzz)
f.Sub(&yyMBzz3, &yy, &bzz3)
f.Add(&yyPBzz3, &yy, &bzz3)
f.Add(&zz3, &zz, &zz)
f.Add(&zz3, &zz3, &zz)
f.Mul(&bxz, &b, &xzPairs)
f.Sub(&bxz, &bxz, &zz3)
f.Sub(&bxz, &bxz, &xx)
f.Add(&bxz3, &bxz, &bxz)
f.Add(&bxz3, &bxz3, &bxz)
f.Add(&xx3Mzz3, &xx, &xx)
f.Add(&xx3Mzz3, &xx3Mzz3, &xx)
f.Sub(&xx3Mzz3, &xx3Mzz3, &zz3)
f.Mul(&tv1, &yzPairs, &bxz3)
f.Mul(&x, &yyPBzz3, &xyPairs)
f.Sub(&x, &x, &tv1)
f.Mul(&tv1, &xx3Mzz3, &bxz3)
f.Mul(&y, &yyPBzz3, &yyMBzz3)
f.Add(&y, &y, &tv1)
f.Mul(&tv1, &xyPairs, &xx3Mzz3)
f.Mul(&z, &yyMBzz3, &yzPairs)
f.Add(&z, &z, &tv1)
e1 := arg1.Z.IsZero()
e2 := arg2.Z.IsZero()
// If arg1 is identity set it to arg2
f.Selectznz(&z, &z, &arg2.Z.Value, e1)
f.Selectznz(&y, &y, &arg2.Y.Value, e1)
f.Selectznz(&x, &x, &arg2.X.Value, e1)
// If arg2 is identity set it to arg1
f.Selectznz(&z, &z, &arg1.Z.Value, e2)
f.Selectznz(&y, &y, &arg1.Y.Value, e2)
f.Selectznz(&x, &x, &arg1.X.Value, e2)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
}
func (k p256PointArithmetic) IsOnCurve(arg *native.EllipticPoint) bool {
affine := P256PointNew()
k.ToAffine(affine, arg)
lhs := fp.P256FpNew().Square(affine.Y)
rhs := fp.P256FpNew()
k.RhsEq(rhs, affine.X)
return lhs.Equal(rhs) == 1
}
func (k p256PointArithmetic) ToAffine(out, arg *native.EllipticPoint) {
var wasInverted int
var zero, x, y, z [native.FieldLimbs]uint64
f := arg.X.Arithmetic
f.Invert(&wasInverted, &z, &arg.Z.Value)
f.Mul(&x, &arg.X.Value, &z)
f.Mul(&y, &arg.Y.Value, &z)
out.Z.SetOne()
// If point at infinity this does nothing
f.Selectznz(&x, &zero, &x, wasInverted)
f.Selectznz(&y, &zero, &y, wasInverted)
f.Selectznz(&z, &zero, &out.Z.Value, wasInverted)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
out.Params = arg.Params
out.Arithmetic = arg.Arithmetic
}
func (k p256PointArithmetic) RhsEq(out, x *native.Field) {
// Elliptic curve equation for p256 is: y^2 = x^3 ax + b
out.Square(x)
out.Mul(out, x)
out.Add(out, getP256PointParams().B)
out.Add(out, fp.P256FpNew().Mul(getP256PointParams().A, x))
}
+38
View File
@@ -0,0 +1,38 @@
package p256_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/core/curves/native/p256"
"github.com/onsonr/sonr/crypto/core/curves/native/p256/fp"
)
func TestP256PointArithmetic_Double(t *testing.T) {
g := p256.P256PointNew().Generator()
pt1 := p256.P256PointNew().Double(g)
pt2 := p256.P256PointNew().Add(g, g)
pt3 := p256.P256PointNew().Mul(g, fp.P256FpNew().SetUint64(2))
e1 := pt1.Equal(pt2)
e2 := pt1.Equal(pt3)
e3 := pt2.Equal(pt3)
require.Equal(t, 1, e1)
require.Equal(t, 1, e2)
require.Equal(t, 1, e3)
}
func TestP256PointArithmetic_Hash(t *testing.T) {
var b [32]byte
sc, err := p256.P256PointNew().Hash(b[:], native.EllipticPointHasherSha256())
sc1 := curves.P256().NewIdentityPoint().Hash(b[:])
fmt.Printf("%v\n", sc1)
require.NoError(t, err)
require.True(t, !sc.IsIdentity())
require.True(t, sc.IsOnCurve())
}
+10
View File
@@ -0,0 +1,10 @@
---
aliases: [README]
tags: []
title: README
linter-yaml-title-alias: README
date created: Wednesday, April 17th 2024, 4:11:40 pm
date modified: Thursday, April 18th 2024, 8:19:25 am
---
## Pallas and Pasta Curve
+369
View File
@@ -0,0 +1,369 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
"encoding/binary"
"fmt"
"math/big"
"github.com/onsonr/sonr/crypto/internal"
)
type Fp fiat_pasta_fp_montgomery_domain_field_element
// r = 2^256 mod p
var r = &Fp{0x34786d38fffffffd, 0x992c350be41914ad, 0xffffffffffffffff, 0x3fffffffffffffff}
// r2 = 2^512 mod p
var r2 = &Fp{0x8c78ecb30000000f, 0xd7d30dbd8b0de0e7, 0x7797a99bc3c95d18, 0x096d41af7b9cb714}
// r3 = 2^768 mod p
var r3 = &Fp{0xf185a5993a9e10f9, 0xf6a68f3b6ac5b1d1, 0xdf8d1014353fd42c, 0x2ae309222d2d9910}
// generator = 5 mod p is a generator of the `p - 1` order multiplicative
// subgroup, or in other words a primitive element of the field.
var generator = &Fp{0xa1a55e68ffffffed, 0x74c2a54b4f4982f3, 0xfffffffffffffffd, 0x3fffffffffffffff}
var s = 32
// modulus representation
// p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
var modulus = &Fp{0x992d30ed00000001, 0x224698fc094cf91b, 0x0000000000000000, 0x4000000000000000}
var biModulus = new(big.Int).SetBytes([]byte{
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x22, 0x46, 0x98, 0xfc, 0x09, 0x4c, 0xf9, 0x1b,
0x99, 0x2d, 0x30, 0xed, 0x00, 0x00, 0x00, 0x01,
})
// Cmp returns -1 if fp < rhs
// 0 if fp == rhs
// 1 if fp > rhs
func (fp *Fp) Cmp(rhs *Fp) int {
gt := 0
lt := 0
for i := len(fp) - 1; i >= 0; i-- {
gt |= int((rhs[i]-fp[i])>>63) &^ lt
lt |= int((fp[i]-rhs[i])>>63) &^ gt
}
return gt - lt
}
// Equal returns true if fp == rhs
func (fp *Fp) Equal(rhs *Fp) bool {
t := fp[0] ^ rhs[0]
t |= fp[1] ^ rhs[1]
t |= fp[2] ^ rhs[2]
t |= fp[3] ^ rhs[3]
return t == 0
}
// IsZero returns true if fp == 0
func (fp *Fp) IsZero() bool {
t := fp[0]
t |= fp[1]
t |= fp[2]
t |= fp[3]
return t == 0
}
// IsOne returns true if fp == R
func (fp *Fp) IsOne() bool {
return fp.Equal(r)
}
func (fp *Fp) IsOdd() bool {
tv := new(fiat_pasta_fp_non_montgomery_domain_field_element)
fiat_pasta_fp_from_montgomery(tv, (*fiat_pasta_fp_montgomery_domain_field_element)(fp))
return tv[0]&0x01 == 0x01
}
// Set fp == rhs
func (fp *Fp) Set(rhs *Fp) *Fp {
fp[0] = rhs[0]
fp[1] = rhs[1]
fp[2] = rhs[2]
fp[3] = rhs[3]
return fp
}
// SetUint64 sets fp == rhs
func (fp *Fp) SetUint64(rhs uint64) *Fp {
r := &fiat_pasta_fp_non_montgomery_domain_field_element{rhs, 0, 0, 0}
fiat_pasta_fp_to_montgomery((*fiat_pasta_fp_montgomery_domain_field_element)(fp), r)
return fp
}
func (fp *Fp) SetBool(rhs bool) *Fp {
if rhs {
fp.SetOne()
} else {
fp.SetZero()
}
return fp
}
// SetOne fp == R
func (fp *Fp) SetOne() *Fp {
return fp.Set(r)
}
// SetZero fp == 0
func (fp *Fp) SetZero() *Fp {
fp[0] = 0
fp[1] = 0
fp[2] = 0
fp[3] = 0
return fp
}
// SetBytesWide takes 64 bytes as input and treats them as a 512-bit number.
// Attributed to https://github.com/zcash/pasta_curves/blob/main/src/fields/fp.rs#L255
// We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits
// with the higher bits multiplied by 2^256. Thus, we perform two reductions
//
// 1. the lower bits are multiplied by R^2, as normal
// 2. the upper bits are multiplied by R^2 * 2^256 = R^3
//
// and computing their sum in the field. It remains to see that arbitrary 256-bit
// numbers can be placed into Montgomery form safely using the reduction. The
// reduction works so long as the product is less than R=2^256 multiplied by
// the modulus. This holds because for any `c` smaller than the modulus, we have
// that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the
// reduction always works so long as `c` is in the field; in this case it is either the
// constant `r2` or `r3`.
func (fp *Fp) SetBytesWide(input *[64]byte) *Fp {
d0 := fiat_pasta_fp_montgomery_domain_field_element{
binary.LittleEndian.Uint64(input[:8]),
binary.LittleEndian.Uint64(input[8:16]),
binary.LittleEndian.Uint64(input[16:24]),
binary.LittleEndian.Uint64(input[24:32]),
}
d1 := fiat_pasta_fp_montgomery_domain_field_element{
binary.LittleEndian.Uint64(input[32:40]),
binary.LittleEndian.Uint64(input[40:48]),
binary.LittleEndian.Uint64(input[48:56]),
binary.LittleEndian.Uint64(input[56:64]),
}
// Convert to Montgomery form
tv1 := new(fiat_pasta_fp_montgomery_domain_field_element)
tv2 := new(fiat_pasta_fp_montgomery_domain_field_element)
// d0 * r2 + d1 * r3
fiat_pasta_fp_mul(tv1, &d0, (*fiat_pasta_fp_montgomery_domain_field_element)(r2))
fiat_pasta_fp_mul(tv2, &d1, (*fiat_pasta_fp_montgomery_domain_field_element)(r3))
fiat_pasta_fp_add((*fiat_pasta_fp_montgomery_domain_field_element)(fp), tv1, tv2)
return fp
}
// SetBytes attempts to convert a little endian byte representation
// of a scalar into a `Fp`, failing if input is not canonical
func (fp *Fp) SetBytes(input *[32]byte) (*Fp, error) {
d0 := &Fp{
binary.LittleEndian.Uint64(input[:8]),
binary.LittleEndian.Uint64(input[8:16]),
binary.LittleEndian.Uint64(input[16:24]),
binary.LittleEndian.Uint64(input[24:32]),
}
if d0.Cmp(modulus) != -1 {
return nil, fmt.Errorf("invalid byte sequence")
}
fiat_pasta_fp_from_bytes((*[4]uint64)(fp), input)
fiat_pasta_fp_to_montgomery((*fiat_pasta_fp_montgomery_domain_field_element)(fp), (*fiat_pasta_fp_non_montgomery_domain_field_element)(fp))
return fp, nil
}
// SetBigInt initializes an element from big.Int
// The value is reduced by the modulus
func (fp *Fp) SetBigInt(bi *big.Int) *Fp {
var buffer [32]byte
r := new(big.Int).Set(bi)
r.Mod(r, biModulus)
r.FillBytes(buffer[:])
copy(buffer[:], internal.ReverseScalarBytes(buffer[:]))
_, _ = fp.SetBytes(&buffer)
return fp
}
// SetRaw converts a raw array into a field element
func (fp *Fp) SetRaw(array *[4]uint64) *Fp {
fiat_pasta_fp_to_montgomery((*fiat_pasta_fp_montgomery_domain_field_element)(fp), (*fiat_pasta_fp_non_montgomery_domain_field_element)(array))
return fp
}
// Bytes converts this element into a byte representation
// in little endian byte order
func (fp *Fp) Bytes() [32]byte {
var output [32]byte
tv := new(fiat_pasta_fp_non_montgomery_domain_field_element)
fiat_pasta_fp_from_montgomery(tv, (*fiat_pasta_fp_montgomery_domain_field_element)(fp))
fiat_pasta_fp_to_bytes(&output, (*[4]uint64)(tv))
return output
}
// BigInt converts this element into the big.Int struct
func (fp *Fp) BigInt() *big.Int {
buffer := fp.Bytes()
return new(big.Int).SetBytes(internal.ReverseScalarBytes(buffer[:]))
}
// Double this element
func (fp *Fp) Double(elem *Fp) *Fp {
delem := (*fiat_pasta_fp_montgomery_domain_field_element)(elem)
fiat_pasta_fp_add((*fiat_pasta_fp_montgomery_domain_field_element)(fp), delem, delem)
return fp
}
// Square this element
func (fp *Fp) Square(elem *Fp) *Fp {
delem := (*fiat_pasta_fp_montgomery_domain_field_element)(elem)
fiat_pasta_fp_square((*fiat_pasta_fp_montgomery_domain_field_element)(fp), delem)
return fp
}
// Sqrt this element, if it exists. If true, then value
// is a square root. If false, value is a QNR
func (fp *Fp) Sqrt(elem *Fp) (*Fp, bool) {
return fp.tonelliShanks(elem)
}
// See sqrt_ts_ct at
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.4
func (fp *Fp) tonelliShanks(elem *Fp) (*Fp, bool) {
// c1 := 32
// c2 := (q - 1) / (2^c1)
// c2 := [4]uint64{
// 0x094cf91b992d30ed,
// 0x00000000224698fc,
// 0x0000000000000000,
// 0x0000000040000000,
// }
// c3 := (c2 - 1) / 2
c3 := [4]uint64{
0x04a67c8dcc969876,
0x0000000011234c7e,
0x0000000000000000,
0x0000000020000000,
}
// c4 := generator
// c5 := new(Fp).pow(&generator, c2)
c5 := &Fp{
0xa28db849bad6dbf0,
0x9083cd03d3b539df,
0xfba6b9ca9dc8448e,
0x3ec928747b89c6da,
}
z := new(Fp).pow(elem, c3)
t := new(Fp).Square(z)
t.Mul(t, elem)
z.Mul(z, elem)
b := new(Fp).Set(t)
c := new(Fp).Set(c5)
flags := map[bool]int{
true: 1,
false: 0,
}
for i := s; i >= 2; i-- {
for j := 1; j <= i-2; j++ {
b.Square(b)
}
z.CMove(z, new(Fp).Mul(z, c), flags[!b.IsOne()])
c.Square(c)
t.CMove(t, new(Fp).Mul(t, c), flags[!b.IsOne()])
b.Set(t)
}
wasSquare := c.Square(z).Equal(elem)
return fp.Set(z), wasSquare
}
// Invert this element i.e. compute the multiplicative inverse
// return false, zero if this element is zero
func (fp *Fp) Invert(elem *Fp) (*Fp, bool) {
// computes elem^(p - 2) mod p
exp := [4]uint64{
0x992d30ecffffffff,
0x224698fc094cf91b,
0x0000000000000000,
0x4000000000000000,
}
return fp.pow(elem, exp), !elem.IsZero()
}
// Mul returns the result from multiplying this element by rhs
func (fp *Fp) Mul(lhs, rhs *Fp) *Fp {
dlhs := (*fiat_pasta_fp_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fp_montgomery_domain_field_element)(rhs)
fiat_pasta_fp_mul((*fiat_pasta_fp_montgomery_domain_field_element)(fp), dlhs, drhs)
return fp
}
// Sub returns the result from subtracting rhs from this element
func (fp *Fp) Sub(lhs, rhs *Fp) *Fp {
dlhs := (*fiat_pasta_fp_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fp_montgomery_domain_field_element)(rhs)
fiat_pasta_fp_sub((*fiat_pasta_fp_montgomery_domain_field_element)(fp), dlhs, drhs)
return fp
}
// Add returns the result from adding rhs to this element
func (fp *Fp) Add(lhs, rhs *Fp) *Fp {
dlhs := (*fiat_pasta_fp_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fp_montgomery_domain_field_element)(rhs)
fiat_pasta_fp_add((*fiat_pasta_fp_montgomery_domain_field_element)(fp), dlhs, drhs)
return fp
}
// Neg returns negation of this element
func (fp *Fp) Neg(elem *Fp) *Fp {
delem := (*fiat_pasta_fp_montgomery_domain_field_element)(elem)
fiat_pasta_fp_opp((*fiat_pasta_fp_montgomery_domain_field_element)(fp), delem)
return fp
}
// Exp exponentiates this element by exp
func (fp *Fp) Exp(base, exp *Fp) *Fp {
// convert exponent to integer form
tv := &fiat_pasta_fp_non_montgomery_domain_field_element{}
fiat_pasta_fp_from_montgomery(tv, (*fiat_pasta_fp_montgomery_domain_field_element)(exp))
e := (*[4]uint64)(tv)
return fp.pow(base, *e)
}
func (fp *Fp) pow(base *Fp, exp [4]uint64) *Fp {
res := new(Fp).SetOne()
tmp := new(Fp)
for i := len(exp) - 1; i >= 0; i-- {
for j := 63; j >= 0; j-- {
res.Square(res)
tmp.Mul(res, base)
res.CMove(res, tmp, int(exp[i]>>j)&1)
}
}
return fp.Set(res)
}
// CMove selects lhs if choice == 0 and rhs if choice == 1
func (fp *Fp) CMove(lhs, rhs *Fp, choice int) *Fp {
dlhs := (*[4]uint64)(lhs)
drhs := (*[4]uint64)(rhs)
fiat_pasta_fp_selectznz((*[4]uint64)(fp), fiat_pasta_fp_uint1(choice), dlhs, drhs)
return fp
}
// ToRaw converts this element into the a [4]uint64
func (fp *Fp) ToRaw() [4]uint64 {
res := &fiat_pasta_fp_non_montgomery_domain_field_element{}
fiat_pasta_fp_from_montgomery(res, (*fiat_pasta_fp_montgomery_domain_field_element)(fp))
return *(*[4]uint64)(res)
}
+249
View File
@@ -0,0 +1,249 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
)
func TestFpSetOne(t *testing.T) {
fp := new(Fp).SetOne()
require.NotNil(t, fp)
require.True(t, fp.Equal(r))
}
func TestFpSetUint64(t *testing.T) {
act := new(Fp).SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, int(act[0]), 0x592d30ed00000001)
}
func TestFpAdd(t *testing.T) {
lhs := new(Fp).SetOne()
rhs := new(Fp).SetOne()
exp := new(Fp).SetUint64(2)
res := new(Fp).Add(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := new(Fp).Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpSub(t *testing.T) {
lhs := new(Fp).SetOne()
rhs := new(Fp).SetOne()
exp := new(Fp).SetZero()
res := new(Fp).Sub(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := new(Fp).Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpMul(t *testing.T) {
lhs := new(Fp).SetOne()
rhs := new(Fp).SetOne()
exp := new(Fp).SetOne()
res := new(Fp).Mul(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := new(Fp).Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpDouble(t *testing.T) {
a := new(Fp).SetUint64(2)
e := new(Fp).SetUint64(4)
require.Equal(t, e, new(Fp).Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = new(Fp).SetUint64(uint64(tv))
e = new(Fp).SetUint64(ttv)
require.Equal(t, e, new(Fp).Double(a))
}
}
func TestFpSquare(t *testing.T) {
a := new(Fp).SetUint64(4)
e := new(Fp).SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFpNeg(t *testing.T) {
a := new(Fp).SetOne()
a.Neg(a)
e := &Fp{7256640077462241284, 9879318615658062958, 0, 0}
require.Equal(t, e, a)
a.Neg(generator)
e = &Fp{0xf787d28400000014, 0xad83f3b0ba037627, 0x2, 0x0}
require.Equal(t, e, a)
}
func TestFpExp(t *testing.T) {
e := new(Fp).SetUint64(8)
a := new(Fp).SetUint64(2)
by := new(Fp).SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFpSqrt(t *testing.T) {
t1 := new(Fp).SetUint64(2)
t2 := new(Fp).Neg(t1)
t3 := new(Fp).Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.True(t, t1.Equal(t3) || t2.Equal(t3))
t1.SetUint64(5)
_, wasSquare = new(Fp).Sqrt(t1)
require.False(t, wasSquare)
}
func TestFpInvert(t *testing.T) {
twoInv := &Fp{0xcc96987680000001, 0x11234c7e04a67c8d, 0x0000000000000000, 0x2000000000000000}
fiat_pasta_fp_to_montgomery((*fiat_pasta_fp_montgomery_domain_field_element)(twoInv), (*fiat_pasta_fp_non_montgomery_domain_field_element)(twoInv))
two := new(Fp).SetUint64(2)
a, inverted := new(Fp).Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnity := &Fp{0xbdad6fabd87ea32f, 0xea322bf2b7bb7584, 0x362120830561f81a, 0x2bce74deac30ebda}
fiat_pasta_fp_to_montgomery((*fiat_pasta_fp_montgomery_domain_field_element)(rootOfUnity), (*fiat_pasta_fp_non_montgomery_domain_field_element)(rootOfUnity))
rootOfUnityInv := &Fp{0xf0b87c7db2ce91f6, 0x84a0a1d8859f066f, 0xb4ed8e647196dad1, 0x2cd5282c53116b5c}
fiat_pasta_fp_to_montgomery((*fiat_pasta_fp_montgomery_domain_field_element)(rootOfUnityInv), (*fiat_pasta_fp_non_montgomery_domain_field_element)(rootOfUnityInv))
a, inverted = new(Fp).Invert(rootOfUnity)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := new(Fp).SetUint64(9)
rhs := new(Fp).SetUint64(3)
rhsInv, inverted := new(Fp).Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, new(Fp).Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = new(Fp).Invert(rhs)
require.False(t, inverted)
}
func TestFpCMove(t *testing.T) {
t1 := new(Fp).SetUint64(5)
t2 := new(Fp).SetUint64(10)
require.Equal(t, t1, new(Fp).CMove(t1, t2, 0))
require.Equal(t, t2, new(Fp).CMove(t1, t2, 1))
}
func TestFpBytes(t *testing.T) {
t1 := new(Fp).SetUint64(99)
seq := t1.Bytes()
t2, err := new(Fp).SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFpBigInt(t *testing.T) {
t1 := new(Fp).SetBigInt(big.NewInt(9999))
t2 := new(Fp).SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := &Fp{0x8c6bc70550c87761, 0xce2c6c48e7063731, 0xf1275fd1e4607cd6, 0x3e6762e63501edbd}
b := new(big.Int).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e[0] = 0xcc169e7af3788a0
e[1] = 0x541a2cb32246c1ea
e[2] = 0xed8a02e1b9f8329
e[3] = 0x1989d19cafe1242
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFpSetBool(t *testing.T) {
require.Equal(t, new(Fp).SetOne(), new(Fp).SetBool(true))
require.Equal(t, new(Fp).SetZero(), new(Fp).SetBool(false))
}
func TestFpSetBytesWide(t *testing.T) {
e := &Fp{0x3daec14d565241d9, 0x0b7af45b6073944b, 0xea5b8bd611a5bd4c, 0x150160330625db3d}
fiat_pasta_fp_to_montgomery((*fiat_pasta_fp_montgomery_domain_field_element)(e), (*fiat_pasta_fp_non_montgomery_domain_field_element)(e))
a := new(Fp).SetBytesWide(&[64]byte{
0xa1, 0x78, 0x76, 0x29, 0x41, 0x56, 0x15, 0xee,
0x65, 0xbe, 0xfd, 0xdb, 0x6b, 0x15, 0x3e, 0xd8,
0xb5, 0xa0, 0x8b, 0xc6, 0x34, 0xd8, 0xcc, 0xd9,
0x58, 0x27, 0x27, 0x12, 0xe3, 0xed, 0x08, 0xf5,
0x89, 0x8e, 0x22, 0xf8, 0xcb, 0xf7, 0x8d, 0x03,
0x41, 0x4b, 0xc7, 0xa3, 0xe4, 0xa1, 0x05, 0x35,
0xb3, 0x2d, 0xb8, 0x5e, 0x77, 0x6f, 0xa4, 0xbf,
0x1d, 0x47, 0x2f, 0x26, 0x7e, 0xe2, 0xeb, 0x26,
})
require.Equal(t, e, a)
}
File diff suppressed because it is too large Load Diff
+363
View File
@@ -0,0 +1,363 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
"encoding/binary"
"fmt"
"math/big"
"github.com/onsonr/sonr/crypto/internal"
)
type Fq fiat_pasta_fq_montgomery_domain_field_element
// r = 2^256 mod p
var r = &Fq{0x5b2b3e9cfffffffd, 0x992c350be3420567, 0xffffffffffffffff, 0x3fffffffffffffff}
// r2 = 2^512 mod p
var r2 = &Fq{0xfc9678ff0000000f, 0x67bb433d891a16e3, 0x7fae231004ccf590, 0x096d41af7ccfdaa9}
// r3 = 2^768 mod p
var r3 = &Fq{0x008b421c249dae4c, 0xe13bda50dba41326, 0x88fececb8e15cb63, 0x07dd97a06e6792c8}
// generator = 5 mod p is a generator of the `p - 1` order multiplicative
// subgroup, or in other words a primitive element of the field.
var generator = &Fq{0x96bc8c8cffffffed, 0x74c2a54b49f7778e, 0xfffffffffffffffd, 0x3fffffffffffffff}
var s = 32
// modulus representation
// p = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
var modulus = &Fq{0x8c46eb2100000001, 0x224698fc0994a8dd, 0x0000000000000000, 0x4000000000000000}
var BiModulus = new(big.Int).SetBytes([]byte{
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x22, 0x46, 0x98, 0xfc, 0x09, 0x94, 0xa8, 0xdd,
0x8c, 0x46, 0xeb, 0x21, 0x00, 0x00, 0x00, 0x01,
})
// Cmp returns -1 if fp < rhs
// 0 if fp == rhs
// 1 if fp > rhs
func (fq *Fq) Cmp(rhs *Fq) int {
gt := 0
lt := 0
for i := len(fq) - 1; i >= 0; i-- {
gt |= int((rhs[i]-fq[i])>>63) &^ lt
lt |= int((fq[i]-rhs[i])>>63) &^ gt
}
return gt - lt
}
// Equal returns true if fp == rhs
func (fq *Fq) Equal(rhs *Fq) bool {
t := fq[0] ^ rhs[0]
t |= fq[1] ^ rhs[1]
t |= fq[2] ^ rhs[2]
t |= fq[3] ^ rhs[3]
return t == 0
}
// IsZero returns true if fp == 0
func (fq *Fq) IsZero() bool {
t := fq[0]
t |= fq[1]
t |= fq[2]
t |= fq[3]
return t == 0
}
// IsOne returns true if fp == r
func (fq *Fq) IsOne() bool {
return fq.Equal(r)
}
// Set fp == rhs
func (fq *Fq) Set(rhs *Fq) *Fq {
fq[0] = rhs[0]
fq[1] = rhs[1]
fq[2] = rhs[2]
fq[3] = rhs[3]
return fq
}
// SetUint64 sets fp == rhs
func (fq *Fq) SetUint64(rhs uint64) *Fq {
r := &fiat_pasta_fq_non_montgomery_domain_field_element{rhs, 0, 0, 0}
fiat_pasta_fq_to_montgomery((*fiat_pasta_fq_montgomery_domain_field_element)(fq), r)
return fq
}
func (fq *Fq) SetBool(rhs bool) *Fq {
if rhs {
fq.SetOne()
} else {
fq.SetZero()
}
return fq
}
// SetOne fp == r
func (fq *Fq) SetOne() *Fq {
return fq.Set(r)
}
// SetZero fp == 0
func (fq *Fq) SetZero() *Fq {
fq[0] = 0
fq[1] = 0
fq[2] = 0
fq[3] = 0
return fq
}
// SetBytesWide takes 64 bytes as input and treats them as a 512-bit number.
// Attributed to https://github.com/zcash/pasta_curves/blob/main/src/fields/fq.rs#L255
// We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits
// with the higher bits multiplied by 2^256. Thus, we perform two reductions
//
// 1. the lower bits are multiplied by r^2, as normal
// 2. the upper bits are multiplied by r^2 * 2^256 = r^3
//
// and computing their sum in the field. It remains to see that arbitrary 256-bit
// numbers can be placed into Montgomery form safely using the reduction. The
// reduction works so long as the product is less than r=2^256 multiplied by
// the modulus. This holds because for any `c` smaller than the modulus, we have
// that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the
// reduction always works so long as `c` is in the field; in this case it is either the
// constant `r2` or `r3`.
func (fq *Fq) SetBytesWide(input *[64]byte) *Fq {
d0 := fiat_pasta_fq_montgomery_domain_field_element{
binary.LittleEndian.Uint64(input[:8]),
binary.LittleEndian.Uint64(input[8:16]),
binary.LittleEndian.Uint64(input[16:24]),
binary.LittleEndian.Uint64(input[24:32]),
}
d1 := fiat_pasta_fq_montgomery_domain_field_element{
binary.LittleEndian.Uint64(input[32:40]),
binary.LittleEndian.Uint64(input[40:48]),
binary.LittleEndian.Uint64(input[48:56]),
binary.LittleEndian.Uint64(input[56:64]),
}
// Convert to Montgomery form
tv1 := &fiat_pasta_fq_montgomery_domain_field_element{}
tv2 := &fiat_pasta_fq_montgomery_domain_field_element{}
// d0 * r2 + d1 * r3
fiat_pasta_fq_mul(tv1, &d0, (*fiat_pasta_fq_montgomery_domain_field_element)(r2))
fiat_pasta_fq_mul(tv2, &d1, (*fiat_pasta_fq_montgomery_domain_field_element)(r3))
fiat_pasta_fq_add((*fiat_pasta_fq_montgomery_domain_field_element)(fq), tv1, tv2)
return fq
}
// SetBytes attempts to convert a little endian byte representation
// of a scalar into a `Fq`, failing if input is not canonical
func (fq *Fq) SetBytes(input *[32]byte) (*Fq, error) {
d0 := &Fq{
binary.LittleEndian.Uint64(input[:8]),
binary.LittleEndian.Uint64(input[8:16]),
binary.LittleEndian.Uint64(input[16:24]),
binary.LittleEndian.Uint64(input[24:32]),
}
if d0.Cmp(modulus) != -1 {
return nil, fmt.Errorf("invalid byte sequence")
}
fiat_pasta_fq_from_bytes((*[4]uint64)(fq), input)
fiat_pasta_fq_to_montgomery((*fiat_pasta_fq_montgomery_domain_field_element)(fq), (*fiat_pasta_fq_non_montgomery_domain_field_element)(fq))
return fq, nil
}
// SetBigInt initializes an element from big.Int
// The value is reduced by the modulus
func (fq *Fq) SetBigInt(bi *big.Int) *Fq {
var buffer [32]byte
r := new(big.Int).Set(bi)
r.Mod(r, BiModulus)
r.FillBytes(buffer[:])
copy(buffer[:], internal.ReverseScalarBytes(buffer[:]))
_, _ = fq.SetBytes(&buffer)
return fq
}
// SetRaw converts a raw array into a field element
func (fq *Fq) SetRaw(array *[4]uint64) *Fq {
fiat_pasta_fq_to_montgomery((*fiat_pasta_fq_montgomery_domain_field_element)(fq), (*fiat_pasta_fq_non_montgomery_domain_field_element)(array))
return fq
}
// Bytes converts this element into a byte representation
// in little endian byte order
func (fq *Fq) Bytes() [32]byte {
var output [32]byte
tv := &fiat_pasta_fq_non_montgomery_domain_field_element{}
fiat_pasta_fq_from_montgomery(tv, (*fiat_pasta_fq_montgomery_domain_field_element)(fq))
fiat_pasta_fq_to_bytes(&output, (*[4]uint64)(tv))
return output
}
// BigInt converts this element into the big.Int struct
func (fq *Fq) BigInt() *big.Int {
buffer := fq.Bytes()
return new(big.Int).SetBytes(internal.ReverseScalarBytes(buffer[:]))
}
// Double this element
func (fq *Fq) Double(elem *Fq) *Fq {
delem := (*fiat_pasta_fq_montgomery_domain_field_element)(elem)
fiat_pasta_fq_add((*fiat_pasta_fq_montgomery_domain_field_element)(fq), delem, delem)
return fq
}
// Square this element
func (fq *Fq) Square(elem *Fq) *Fq {
delem := (*fiat_pasta_fq_montgomery_domain_field_element)(elem)
fiat_pasta_fq_square((*fiat_pasta_fq_montgomery_domain_field_element)(fq), delem)
return fq
}
// Sqrt this element, if it exists. If true, then value
// is a square root. If false, value is a QNR
func (fq *Fq) Sqrt(elem *Fq) (*Fq, bool) {
return fq.tonelliShanks(elem)
}
// See sqrt_ts_ct at
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.4
func (fq *Fq) tonelliShanks(elem *Fq) (*Fq, bool) {
// c1 := 32
// c2 := (q - 1) / (2^c1)
//c2 := [4]uint64{
// 0x0994a8dd8c46eb21,
// 0x00000000224698fc,
// 0x0000000000000000,
// 0x0000000040000000,
//}
// c3 := (c2 - 1) / 2
c3 := [4]uint64{
0x04ca546ec6237590,
0x0000000011234c7e,
0x0000000000000000,
0x0000000020000000,
}
// c4 := generator
// c5 := new(Fq).pow(&generator, c2)
c5 := &Fq{
0x218077428c9942de,
0xcc49578921b60494,
0xac2e5d27b2efbee2,
0xb79fa897f2db056,
}
z := new(Fq).pow(elem, c3)
t := new(Fq).Square(z)
t.Mul(t, elem)
z.Mul(z, elem)
b := new(Fq).Set(t)
c := new(Fq).Set(c5)
flags := map[bool]int{
true: 1,
false: 0,
}
for i := s; i >= 2; i-- {
for j := 1; j <= i-2; j++ {
b.Square(b)
}
z.CMove(z, new(Fq).Mul(z, c), flags[!b.IsOne()])
c.Square(c)
t.CMove(t, new(Fq).Mul(t, c), flags[!b.IsOne()])
b.Set(t)
}
wasSquare := c.Square(z).Equal(elem)
return fq.Set(z), wasSquare
}
// Invert this element i.e. compute the multiplicative inverse
// return false, zero if this element is zero
func (fq *Fq) Invert(elem *Fq) (*Fq, bool) {
// computes elem^(p - 2) mod p
exp := [4]uint64{
0x8c46eb20ffffffff,
0x224698fc0994a8dd,
0x0000000000000000,
0x4000000000000000,
}
return fq.pow(elem, exp), !elem.IsZero()
}
// Mul returns the result from multiplying this element by rhs
func (fq *Fq) Mul(lhs, rhs *Fq) *Fq {
dlhs := (*fiat_pasta_fq_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fq_montgomery_domain_field_element)(rhs)
fiat_pasta_fq_mul((*fiat_pasta_fq_montgomery_domain_field_element)(fq), dlhs, drhs)
return fq
}
// Sub returns the result from subtracting rhs from this element
func (fq *Fq) Sub(lhs, rhs *Fq) *Fq {
dlhs := (*fiat_pasta_fq_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fq_montgomery_domain_field_element)(rhs)
fiat_pasta_fq_sub((*fiat_pasta_fq_montgomery_domain_field_element)(fq), dlhs, drhs)
return fq
}
// Add returns the result from adding rhs to this element
func (fq *Fq) Add(lhs, rhs *Fq) *Fq {
dlhs := (*fiat_pasta_fq_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fq_montgomery_domain_field_element)(rhs)
fiat_pasta_fq_add((*fiat_pasta_fq_montgomery_domain_field_element)(fq), dlhs, drhs)
return fq
}
// Neg returns negation of this element
func (fq *Fq) Neg(elem *Fq) *Fq {
delem := (*fiat_pasta_fq_montgomery_domain_field_element)(elem)
fiat_pasta_fq_opp((*fiat_pasta_fq_montgomery_domain_field_element)(fq), delem)
return fq
}
// Exp exponentiates this element by exp
func (fq *Fq) Exp(base, exp *Fq) *Fq {
// convert exponent to integer form
tv := &fiat_pasta_fq_non_montgomery_domain_field_element{}
fiat_pasta_fq_from_montgomery(tv, (*fiat_pasta_fq_montgomery_domain_field_element)(exp))
e := (*[4]uint64)(tv)
return fq.pow(base, *e)
}
func (fq *Fq) pow(base *Fq, exp [4]uint64) *Fq {
res := new(Fq).SetOne()
tmp := new(Fq)
for i := len(exp) - 1; i >= 0; i-- {
for j := 63; j >= 0; j-- {
res.Square(res)
tmp.Mul(res, base)
res.CMove(res, tmp, int(exp[i]>>j)&1)
}
}
return fq.Set(res)
}
// CMove selects lhs if choice == 0 and rhs if choice == 1
func (fq *Fq) CMove(lhs, rhs *Fq, choice int) *Fq {
dlhs := (*[4]uint64)(lhs)
drhs := (*[4]uint64)(rhs)
fiat_pasta_fq_selectznz((*[4]uint64)(fq), fiat_pasta_fq_uint1(choice), dlhs, drhs)
return fq
}
// ToRaw converts this element into the a [4]uint64
func (fq *Fq) ToRaw() [4]uint64 {
res := &fiat_pasta_fq_non_montgomery_domain_field_element{}
fiat_pasta_fq_from_montgomery(res, (*fiat_pasta_fq_montgomery_domain_field_element)(fq))
return *(*[4]uint64)(res)
}
+249
View File
@@ -0,0 +1,249 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
)
func TestFqSetOne(t *testing.T) {
fq := new(Fq).SetOne()
require.NotNil(t, fq)
require.True(t, fq.Equal(r))
}
func TestFqSetUint64(t *testing.T) {
act := new(Fq).SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, int(act[0]), 0x4c46eb2100000001)
}
func TestFqAdd(t *testing.T) {
lhs := new(Fq).SetOne()
rhs := new(Fq).SetOne()
exp := new(Fq).SetUint64(2)
res := new(Fq).Add(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := new(Fq).Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqSub(t *testing.T) {
lhs := new(Fq).SetOne()
rhs := new(Fq).SetOne()
exp := new(Fq).SetZero()
res := new(Fq).Sub(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := new(Fq).Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqMul(t *testing.T) {
lhs := new(Fq).SetOne()
rhs := new(Fq).SetOne()
exp := new(Fq).SetOne()
res := new(Fq).Mul(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := new(Fq).Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqDouble(t *testing.T) {
a := new(Fq).SetUint64(2)
e := new(Fq).SetUint64(4)
require.Equal(t, e, new(Fq).Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = new(Fq).SetUint64(uint64(tv))
e = new(Fq).SetUint64(ttv)
require.Equal(t, e, new(Fq).Double(a))
}
}
func TestFqSquare(t *testing.T) {
a := new(Fq).SetUint64(4)
e := new(Fq).SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFqNeg(t *testing.T) {
a := new(Fq).SetOne()
a.Neg(a)
e := &Fq{0x311bac8400000004, 0x891a63f02652a376, 0, 0}
require.Equal(t, e, a)
a.Neg(generator)
e = &Fq{0xf58a5e9400000014, 0xad83f3b0bf9d314e, 0x2, 0x0}
require.Equal(t, e, a)
}
func TestFqExp(t *testing.T) {
e := new(Fq).SetUint64(8)
a := new(Fq).SetUint64(2)
by := new(Fq).SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFqSqrt(t *testing.T) {
t1 := new(Fq).SetUint64(2)
t2 := new(Fq).Neg(t1)
t3 := new(Fq).Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.True(t, t1.Equal(t3) || t2.Equal(t3))
t1.SetUint64(5)
_, wasSquare = new(Fq).Sqrt(t1)
require.False(t, wasSquare)
}
func TestFqInvert(t *testing.T) {
twoInv := &Fq{0xc623759080000001, 0x11234c7e04ca546e, 0x0000000000000000, 0x2000000000000000}
fiat_pasta_fq_to_montgomery((*fiat_pasta_fq_montgomery_domain_field_element)(twoInv), (*fiat_pasta_fq_non_montgomery_domain_field_element)(twoInv))
two := new(Fq).SetUint64(2)
a, inverted := new(Fq).Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnity := &Fq{0xa70e2c1102b6d05f, 0x9bb97ea3c106f049, 0x9e5c4dfd492ae26e, 0x2de6a9b8746d3f58}
fiat_pasta_fq_to_montgomery((*fiat_pasta_fq_montgomery_domain_field_element)(rootOfUnity), (*fiat_pasta_fq_non_montgomery_domain_field_element)(rootOfUnity))
rootOfUnityInv := &Fq{0x57eecda0a84b6836, 0x4ad38b9084b8a80c, 0xf4c8f353124086c1, 0x2235e1a7415bf936}
fiat_pasta_fq_to_montgomery((*fiat_pasta_fq_montgomery_domain_field_element)(rootOfUnityInv), (*fiat_pasta_fq_non_montgomery_domain_field_element)(rootOfUnityInv))
a, inverted = new(Fq).Invert(rootOfUnity)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := new(Fq).SetUint64(9)
rhs := new(Fq).SetUint64(3)
rhsInv, inverted := new(Fq).Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, new(Fq).Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = new(Fq).Invert(rhs)
require.False(t, inverted)
}
func TestFqCMove(t *testing.T) {
t1 := new(Fq).SetUint64(5)
t2 := new(Fq).SetUint64(10)
require.Equal(t, t1, new(Fq).CMove(t1, t2, 0))
require.Equal(t, t2, new(Fq).CMove(t1, t2, 1))
}
func TestFqBytes(t *testing.T) {
t1 := new(Fq).SetUint64(99)
seq := t1.Bytes()
t2, err := new(Fq).SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFqBigInt(t *testing.T) {
t1 := new(Fq).SetBigInt(big.NewInt(9999))
t2 := new(Fq).SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := &Fq{0x7bb1416dea3d6ae3, 0x62f9108a340aa525, 0x303b3f30fcaa477f, 0x11c9ef5422d80a4d}
b := new(big.Int).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e[0] = 0x1095a9b315c2951e
e[1] = 0xbf4d8871d58a03b8
e[2] = 0xcfc4c0cf0355b880
e[3] = 0x2e3610abdd27f5b2
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFqSetBool(t *testing.T) {
require.Equal(t, new(Fq).SetOne(), new(Fq).SetBool(true))
require.Equal(t, new(Fq).SetZero(), new(Fq).SetBool(false))
}
func TestFqSetBytesWide(t *testing.T) {
e := &Fq{0xe22bd0d1b22cc43e, 0x6b84e5b52490a7c8, 0x264262941ac9e229, 0x27dcfdf361ce4254}
fiat_pasta_fq_to_montgomery((*fiat_pasta_fq_montgomery_domain_field_element)(e), (*fiat_pasta_fq_non_montgomery_domain_field_element)(e))
a := new(Fq).SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package pasta
+468
View File
@@ -0,0 +1,468 @@
package native
import (
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
"io"
"math/big"
"github.com/pkg/errors"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/sha3"
)
// EllipticPointHashType is to indicate which expand operation is used
// for hash to curve operations
type EllipticPointHashType uint
// EllipticPointHashName is to indicate the hash function is used
// for hash to curve operations
type EllipticPointHashName uint
const (
// XMD - use ExpandMsgXmd
XMD EllipticPointHashType = iota
// XOF - use ExpandMsgXof
XOF
)
const (
SHA256 EllipticPointHashName = iota
SHA512
SHA3_256
SHA3_384
SHA3_512
BLAKE2B
SHAKE128
SHAKE256
)
// EllipticPoint represents a Weierstrauss elliptic curve point
type EllipticPoint struct {
X *Field
Y *Field
Z *Field
Params *EllipticPointParams
Arithmetic EllipticPointArithmetic
}
// EllipticPointParams are the Weierstrauss curve parameters
// such as the name, the coefficients the generator point,
// and the prime bit size
type EllipticPointParams struct {
Name string
A *Field
B *Field
Gx *Field
Gy *Field
BitSize int
}
// EllipticPointHasher is the type of hashing methods for
// hashing byte sequences to curve point.
type EllipticPointHasher struct {
name EllipticPointHashName
hashType EllipticPointHashType
xmd hash.Hash
xof sha3.ShakeHash
}
// Name returns the hash name for this hasher
func (e *EllipticPointHasher) Name() string {
return e.name.String()
}
// Type returns the hash type for this hasher
func (e *EllipticPointHasher) Type() EllipticPointHashType {
return e.hashType
}
// Xmd returns the hash method for ExpandMsgXmd
func (e *EllipticPointHasher) Xmd() hash.Hash {
return e.xmd
}
// Xof returns the hash method for ExpandMsgXof
func (e *EllipticPointHasher) Xof() sha3.ShakeHash {
return e.xof
}
// EllipticPointHasherSha256 creates a point hasher that uses Sha256
func EllipticPointHasherSha256() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHA256,
hashType: XMD,
xmd: sha256.New(),
}
}
// EllipticPointHasherSha512 creates a point hasher that uses Sha512
func EllipticPointHasherSha512() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHA512,
hashType: XMD,
xmd: sha512.New(),
}
}
// EllipticPointHasherSha3256 creates a point hasher that uses Sha3256
func EllipticPointHasherSha3256() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHA3_256,
hashType: XMD,
xmd: sha3.New256(),
}
}
// EllipticPointHasherSha3384 creates a point hasher that uses Sha3384
func EllipticPointHasherSha3384() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHA3_384,
hashType: XMD,
xmd: sha3.New384(),
}
}
// EllipticPointHasherSha3512 creates a point hasher that uses Sha3512
func EllipticPointHasherSha3512() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHA3_512,
hashType: XMD,
xmd: sha3.New512(),
}
}
// EllipticPointHasherBlake2b creates a point hasher that uses Blake2b
func EllipticPointHasherBlake2b() *EllipticPointHasher {
h, _ := blake2b.New(64, []byte{})
return &EllipticPointHasher{
name: BLAKE2B,
hashType: XMD,
xmd: h,
}
}
// EllipticPointHasherShake128 creates a point hasher that uses Shake128
func EllipticPointHasherShake128() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHAKE128,
hashType: XOF,
xof: sha3.NewShake128(),
}
}
// EllipticPointHasherShake256 creates a point hasher that uses Shake256
func EllipticPointHasherShake256() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHAKE128,
hashType: XOF,
xof: sha3.NewShake256(),
}
}
// EllipticPointArithmetic are the methods that specific curves
// need to implement for higher abstractions to wrap the point
type EllipticPointArithmetic interface {
// Hash a byte sequence to the curve using the specified hasher
// and dst and store the result in out
Hash(out *EllipticPoint, hasher *EllipticPointHasher, bytes, dst []byte) error
// Double arg and store the result in out
Double(out, arg *EllipticPoint)
// Add arg1 with arg2 and store the result in out
Add(out, arg1, arg2 *EllipticPoint)
// IsOnCurve tests arg if it represents a valid point on the curve
IsOnCurve(arg *EllipticPoint) bool
// ToAffine converts arg to affine coordinates storing the result in out
ToAffine(out, arg *EllipticPoint)
// RhsEq computes the right-hand side of the ecc equation
RhsEq(out, x *Field)
}
func (t EllipticPointHashType) String() string {
switch t {
case XMD:
return "XMD"
case XOF:
return "XOF"
}
return "unknown"
}
func (n EllipticPointHashName) String() string {
switch n {
case SHA256:
return "SHA-256"
case SHA512:
return "SHA-512"
case SHA3_256:
return "SHA3-256"
case SHA3_384:
return "SHA3-384"
case SHA3_512:
return "SHA3-512"
case BLAKE2B:
return "BLAKE2b"
case SHAKE128:
return "SHAKE-128"
case SHAKE256:
return "SHAKE-256"
}
return "unknown"
}
// Random creates a random point on the curve
// from the specified reader
func (p *EllipticPoint) Random(reader io.Reader) (*EllipticPoint, error) {
var seed [WideFieldBytes]byte
n, err := reader.Read(seed[:])
if err != nil {
return nil, errors.Wrap(err, "random could not read from stream")
}
if n != WideFieldBytes {
return nil, fmt.Errorf("insufficient bytes read %d when %d are needed", n, WideFieldBytes)
}
dst := []byte(fmt.Sprintf("%s_XMD:SHA-256_SSWU_RO_", p.Params.Name))
err = p.Arithmetic.Hash(p, EllipticPointHasherSha256(), seed[:], dst)
if err != nil {
return nil, errors.Wrap(err, "ecc hash failed")
}
return p, nil
}
// Hash uses the hasher to map bytes to a valid point
func (p *EllipticPoint) Hash(bytes []byte, hasher *EllipticPointHasher) (*EllipticPoint, error) {
dst := []byte(fmt.Sprintf("%s_%s:%s_SSWU_RO_", p.Params.Name, hasher.hashType, hasher.name))
err := p.Arithmetic.Hash(p, hasher, bytes, dst)
if err != nil {
return nil, errors.Wrap(err, "hash failed")
}
return p, nil
}
// Identity returns the identity point
func (p *EllipticPoint) Identity() *EllipticPoint {
p.X.SetZero()
p.Y.SetZero()
p.Z.SetZero()
return p
}
// Generator returns the base point for the curve
func (p *EllipticPoint) Generator() *EllipticPoint {
p.X.Set(p.Params.Gx)
p.Y.Set(p.Params.Gy)
p.Z.SetOne()
return p
}
// IsIdentity returns true if this point is at infinity
func (p *EllipticPoint) IsIdentity() bool {
return p.Z.IsZero() == 1
}
// Double this point
func (p *EllipticPoint) Double(point *EllipticPoint) *EllipticPoint {
p.Set(point)
p.Arithmetic.Double(p, point)
return p
}
// Neg negates this point
func (p *EllipticPoint) Neg(point *EllipticPoint) *EllipticPoint {
p.Set(point)
p.Y.Neg(p.Y)
return p
}
// Add adds the two points
func (p *EllipticPoint) Add(lhs, rhs *EllipticPoint) *EllipticPoint {
p.Set(lhs)
p.Arithmetic.Add(p, lhs, rhs)
return p
}
// Sub subtracts the two points
func (p *EllipticPoint) Sub(lhs, rhs *EllipticPoint) *EllipticPoint {
p.Set(lhs)
p.Arithmetic.Add(p, lhs, new(EllipticPoint).Neg(rhs))
return p
}
// Mul multiplies this point by the input scalar
func (p *EllipticPoint) Mul(point *EllipticPoint, scalar *Field) *EllipticPoint {
bytes := scalar.Bytes()
precomputed := [16]*EllipticPoint{}
precomputed[0] = new(EllipticPoint).Set(point).Identity()
precomputed[1] = new(EllipticPoint).Set(point)
for i := 2; i < 16; i += 2 {
precomputed[i] = new(EllipticPoint).Set(point).Double(precomputed[i>>1])
precomputed[i+1] = new(EllipticPoint).Set(point).Add(precomputed[i], point)
}
p.Identity()
for i := 0; i < 256; i += 4 {
// Brouwer / windowing method. window size of 4.
for j := 0; j < 4; j++ {
p.Double(p)
}
window := bytes[32-1-i>>3] >> (4 - i&0x04) & 0x0F
p.Add(p, precomputed[window])
}
return p
}
// Equal returns 1 if the two points are equal 0 otherwise.
func (p *EllipticPoint) Equal(rhs *EllipticPoint) int {
var x1, x2, y1, y2 Field
x1.Arithmetic = p.X.Arithmetic
x2.Arithmetic = p.X.Arithmetic
y1.Arithmetic = p.Y.Arithmetic
y2.Arithmetic = p.Y.Arithmetic
x1.Mul(p.X, rhs.Z)
x2.Mul(rhs.X, p.Z)
y1.Mul(p.Y, rhs.Z)
y2.Mul(rhs.Y, p.Z)
e1 := p.Z.IsZero()
e2 := rhs.Z.IsZero()
// Both at infinity or coordinates are the same
return (e1 & e2) | (^e1 & ^e2)&x1.Equal(&x2)&y1.Equal(&y2)
}
// Set copies clone into p
func (p *EllipticPoint) Set(clone *EllipticPoint) *EllipticPoint {
p.X = new(Field).Set(clone.X)
p.Y = new(Field).Set(clone.Y)
p.Z = new(Field).Set(clone.Z)
p.Params = clone.Params
p.Arithmetic = clone.Arithmetic
return p
}
// BigInt returns the x and y as big.Ints in affine
func (p *EllipticPoint) BigInt() (x, y *big.Int) {
t := new(EllipticPoint).Set(p)
p.Arithmetic.ToAffine(t, p)
x = t.X.BigInt()
y = t.Y.BigInt()
return
}
// SetBigInt creates a point from affine x, y
// and returns the point if it is on the curve
func (p *EllipticPoint) SetBigInt(x, y *big.Int) (*EllipticPoint, error) {
xx := &Field{
Params: p.Params.Gx.Params,
Arithmetic: p.Params.Gx.Arithmetic,
}
xx.SetBigInt(x)
yy := &Field{
Params: p.Params.Gx.Params,
Arithmetic: p.Params.Gx.Arithmetic,
}
yy.SetBigInt(y)
pp := new(EllipticPoint).Set(p)
zero := new(Field).Set(xx).SetZero()
one := new(Field).Set(xx).SetOne()
isIdentity := xx.IsZero() & yy.IsZero()
pp.X = xx.CMove(xx, zero, isIdentity)
pp.Y = yy.CMove(yy, zero, isIdentity)
pp.Z = one.CMove(one, zero, isIdentity)
if !p.Arithmetic.IsOnCurve(pp) && isIdentity == 0 {
return nil, fmt.Errorf("invalid coordinates")
}
return p.Set(pp), nil
}
// GetX returns the affine X coordinate
func (p *EllipticPoint) GetX() *Field {
t := new(EllipticPoint).Set(p)
p.Arithmetic.ToAffine(t, p)
return t.X
}
// GetY returns the affine Y coordinate
func (p *EllipticPoint) GetY() *Field {
t := new(EllipticPoint).Set(p)
p.Arithmetic.ToAffine(t, p)
return t.Y
}
// IsOnCurve determines if this point represents a valid curve point
func (p *EllipticPoint) IsOnCurve() bool {
return p.Arithmetic.IsOnCurve(p)
}
// ToAffine converts the point into affine coordinates
func (p *EllipticPoint) ToAffine(clone *EllipticPoint) *EllipticPoint {
p.Arithmetic.ToAffine(p, clone)
return p
}
// SumOfProducts computes the multi-exponentiation for the specified
// points and scalars and stores the result in `p`.
// Returns an error if the lengths of the arguments is not equal.
func (p *EllipticPoint) SumOfProducts(points []*EllipticPoint, scalars []*Field) (*EllipticPoint, error) {
const Upper = 256
const W = 4
const Windows = Upper / W // careful--use ceiling division in case this doesn't divide evenly
if len(points) != len(scalars) {
return nil, fmt.Errorf("length mismatch")
}
bucketSize := 1 << W
windows := make([]*EllipticPoint, Windows)
bytes := make([][32]byte, len(scalars))
buckets := make([]*EllipticPoint, bucketSize)
for i, scalar := range scalars {
bytes[i] = scalar.Bytes()
}
for i := range windows {
windows[i] = new(EllipticPoint).Set(p).Identity()
}
for i := 0; i < bucketSize; i++ {
buckets[i] = new(EllipticPoint).Set(p).Identity()
}
sum := new(EllipticPoint).Set(p)
for j := 0; j < len(windows); j++ {
for i := 0; i < bucketSize; i++ {
buckets[i].Identity()
}
for i := 0; i < len(scalars); i++ {
// j*W to get the nibble
// >> 3 to convert to byte, / 8
// (W * j & W) gets the nibble, mod W
// 1 << W - 1 to get the offset
index := bytes[i][j*W>>3] >> (W * j & W) & (1<<W - 1) // little-endian
buckets[index].Add(buckets[index], points[i])
}
sum.Identity()
for i := bucketSize - 1; i > 0; i-- {
sum.Add(sum, buckets[i])
windows[j].Add(windows[j], sum)
}
}
p.Identity()
for i := len(windows) - 1; i >= 0; i-- {
for j := 0; j < W; j++ {
p.Double(p)
}
p.Add(p, windows[i])
}
return p, nil
}
+756
View File
@@ -0,0 +1,756 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"crypto/elliptic"
crand "crypto/rand"
"crypto/sha256"
"crypto/subtle"
"fmt"
"io"
"math/big"
"testing"
"github.com/onsonr/sonr/crypto/core"
)
func BenchmarkP256(b *testing.B) {
// 1000 points
b.Run("1000 point hash - p256", func(b *testing.B) {
b.StopTimer()
points := make([][]byte, 1000)
for i := range points {
t := make([]byte, 32)
_, _ = crand.Read(t)
points[i] = t
}
acc := new(BenchPointP256).Identity()
b.StartTimer()
for _, pt := range points {
acc = acc.Hash(pt)
}
})
b.Run("1000 point hash - ct p256", func(b *testing.B) {
b.StopTimer()
points := make([][]byte, 1000)
for i := range points {
t := make([]byte, 32)
_, _ = crand.Read(t)
points[i] = t
}
acc := new(PointP256).Identity()
b.StartTimer()
for _, pt := range points {
acc = acc.Hash(pt)
}
})
b.Run("1000 point add - p256", func(b *testing.B) {
b.StopTimer()
points := make([]*BenchPointP256, 1000)
for i := range points {
points[i] = points[i].Random(crand.Reader).(*BenchPointP256)
}
acc := new(BenchPointP256).Identity()
b.StartTimer()
for _, pt := range points {
acc = acc.Add(pt)
}
})
b.Run("1000 point add - ct p256", func(b *testing.B) {
b.StopTimer()
curve := P256()
points := make([]*PointP256, 1000)
for i := range points {
points[i] = curve.NewIdentityPoint().Random(crand.Reader).(*PointP256)
}
acc := curve.NewIdentityPoint()
b.StartTimer()
for _, pt := range points {
acc = acc.Add(pt)
}
})
b.Run("1000 point double - p256", func(b *testing.B) {
b.StopTimer()
acc := new(BenchPointP256).Generator()
b.StartTimer()
for i := 0; i < 1000; i++ {
acc = acc.Double()
}
})
b.Run("1000 point double - ct p256", func(b *testing.B) {
b.StopTimer()
acc := new(PointP256).Generator()
b.StartTimer()
for i := 0; i < 1000; i++ {
acc = acc.Double()
}
})
b.Run("1000 point multiply - p256", func(b *testing.B) {
b.StopTimer()
scalars := make([]*BenchScalarP256, 1000)
for i := range scalars {
s := new(BenchScalarP256).Random(crand.Reader)
scalars[i] = s.(*BenchScalarP256)
}
acc := new(BenchPointP256).Generator().Mul(new(BenchScalarP256).New(2))
b.StartTimer()
for _, sc := range scalars {
acc = acc.Mul(sc)
}
})
b.Run("1000 point multiply - ct p256", func(b *testing.B) {
b.StopTimer()
scalars := make([]*ScalarP256, 1000)
for i := range scalars {
s := new(ScalarP256).Random(crand.Reader)
scalars[i] = s.(*ScalarP256)
}
acc := new(PointP256).Generator()
b.StartTimer()
for _, sc := range scalars {
acc = acc.Mul(sc)
}
})
b.Run("1000 scalar invert - p256", func(b *testing.B) {
b.StopTimer()
scalars := make([]*BenchScalarP256, 1000)
for i := range scalars {
s := new(BenchScalarP256).Random(crand.Reader)
scalars[i] = s.(*BenchScalarP256)
}
b.StartTimer()
for _, sc := range scalars {
_, _ = sc.Invert()
}
})
b.Run("1000 scalar invert - ct p256", func(b *testing.B) {
b.StopTimer()
scalars := make([]*ScalarP256, 1000)
for i := range scalars {
s := new(ScalarP256).Random(crand.Reader)
scalars[i] = s.(*ScalarP256)
}
b.StartTimer()
for _, sc := range scalars {
_, _ = sc.Invert()
}
})
b.Run("1000 scalar sqrt - p256", func(b *testing.B) {
b.StopTimer()
scalars := make([]*BenchScalarP256, 1000)
for i := range scalars {
s := new(BenchScalarP256).Random(crand.Reader)
scalars[i] = s.(*BenchScalarP256)
}
b.StartTimer()
for _, sc := range scalars {
_, _ = sc.Sqrt()
}
})
b.Run("1000 scalar sqrt - ct p256", func(b *testing.B) {
b.StopTimer()
scalars := make([]*ScalarP256, 1000)
for i := range scalars {
s := new(ScalarP256).Random(crand.Reader)
scalars[i] = s.(*ScalarP256)
}
b.StartTimer()
for _, sc := range scalars {
_, _ = sc.Sqrt()
}
})
}
type BenchScalarP256 struct {
value *big.Int
}
type BenchPointP256 struct {
x, y *big.Int
}
func (s *BenchScalarP256) Random(reader io.Reader) Scalar {
if reader == nil {
return nil
}
var seed [64]byte
_, _ = reader.Read(seed[:])
return s.Hash(seed[:])
}
func (s *BenchScalarP256) Hash(bytes []byte) Scalar {
xmd, err := expandMsgXmd(sha256.New(), bytes, []byte("P256_XMD:SHA-256_SSWU_RO_"), 48)
if err != nil {
return nil
}
v := new(big.Int).SetBytes(xmd)
return &BenchScalarP256{
value: v.Mod(v, elliptic.P256().Params().N),
}
}
func (s *BenchScalarP256) Zero() Scalar {
return &BenchScalarP256{
value: big.NewInt(0),
}
}
func (s *BenchScalarP256) One() Scalar {
return &BenchScalarP256{
value: big.NewInt(1),
}
}
func (s *BenchScalarP256) IsZero() bool {
return subtle.ConstantTimeCompare(s.value.Bytes(), []byte{}) == 1
}
func (s *BenchScalarP256) IsOne() bool {
return subtle.ConstantTimeCompare(s.value.Bytes(), []byte{1}) == 1
}
func (s *BenchScalarP256) IsOdd() bool {
return s.value.Bit(0) == 1
}
func (s *BenchScalarP256) IsEven() bool {
return s.value.Bit(0) == 0
}
func (s *BenchScalarP256) New(value int) Scalar {
v := big.NewInt(int64(value))
if value < 0 {
v.Mod(v, elliptic.P256().Params().N)
}
return &BenchScalarP256{
value: v,
}
}
func (s *BenchScalarP256) Cmp(rhs Scalar) int {
r, ok := rhs.(*BenchScalarP256)
if ok {
return s.value.Cmp(r.value)
} else {
return -2
}
}
func (s *BenchScalarP256) Square() Scalar {
return &BenchScalarP256{
value: new(big.Int).Exp(s.value, big.NewInt(2), elliptic.P256().Params().N),
}
}
func (s *BenchScalarP256) Double() Scalar {
v := new(big.Int).Add(s.value, s.value)
return &BenchScalarP256{
value: v.Mod(v, elliptic.P256().Params().N),
}
}
func (s *BenchScalarP256) Invert() (Scalar, error) {
return &BenchScalarP256{
value: new(big.Int).ModInverse(s.value, elliptic.P256().Params().N),
}, nil
}
func (s *BenchScalarP256) Sqrt() (Scalar, error) {
return &BenchScalarP256{
value: new(big.Int).ModSqrt(s.value, elliptic.P256().Params().N),
}, nil
}
func (s *BenchScalarP256) Cube() Scalar {
return &BenchScalarP256{
value: new(big.Int).Exp(s.value, big.NewInt(3), elliptic.P256().Params().N),
}
}
func (s *BenchScalarP256) Add(rhs Scalar) Scalar {
r, ok := rhs.(*BenchScalarP256)
if ok {
v := new(big.Int).Add(s.value, r.value)
return &BenchScalarP256{
value: v.Mod(v, elliptic.P256().Params().N),
}
} else {
return nil
}
}
func (s *BenchScalarP256) Sub(rhs Scalar) Scalar {
r, ok := rhs.(*BenchScalarP256)
if ok {
v := new(big.Int).Sub(s.value, r.value)
return &BenchScalarP256{
value: v.Mod(v, elliptic.P256().Params().N),
}
} else {
return nil
}
}
func (s *BenchScalarP256) Mul(rhs Scalar) Scalar {
r, ok := rhs.(*BenchScalarP256)
if ok {
v := new(big.Int).Mul(s.value, r.value)
return &BenchScalarP256{
value: v.Mod(v, elliptic.P256().Params().N),
}
} else {
return nil
}
}
func (s *BenchScalarP256) MulAdd(y, z Scalar) Scalar {
return s.Mul(y).Add(z)
}
func (s *BenchScalarP256) Div(rhs Scalar) Scalar {
n := elliptic.P256().Params().N
r, ok := rhs.(*BenchScalarP256)
if ok {
v := new(big.Int).ModInverse(r.value, n)
v.Mul(v, s.value)
return &BenchScalarP256{
value: v.Mod(v, n),
}
} else {
return nil
}
}
func (s *BenchScalarP256) Neg() Scalar {
z := new(big.Int).Neg(s.value)
return &BenchScalarP256{
value: z.Mod(z, elliptic.P256().Params().N),
}
}
func (s *BenchScalarP256) SetBigInt(v *big.Int) (Scalar, error) {
if v == nil {
return nil, fmt.Errorf("invalid value")
}
t := new(big.Int).Mod(v, elliptic.P256().Params().N)
if t.Cmp(v) != 0 {
return nil, fmt.Errorf("invalid value")
}
return &BenchScalarP256{
value: t,
}, nil
}
func (s *BenchScalarP256) BigInt() *big.Int {
return new(big.Int).Set(s.value)
}
func (s *BenchScalarP256) Bytes() []byte {
var out [32]byte
return s.value.FillBytes(out[:])
}
func (s *BenchScalarP256) SetBytes(bytes []byte) (Scalar, error) {
value := new(big.Int).SetBytes(bytes)
t := new(big.Int).Mod(value, elliptic.P256().Params().N)
if t.Cmp(value) != 0 {
return nil, fmt.Errorf("invalid byte sequence")
}
return &BenchScalarP256{
value: t,
}, nil
}
func (s *BenchScalarP256) SetBytesWide(bytes []byte) (Scalar, error) {
if len(bytes) < 32 || len(bytes) > 128 {
return nil, fmt.Errorf("invalid byte sequence")
}
value := new(big.Int).SetBytes(bytes)
value.Mod(value, elliptic.P256().Params().N)
return &BenchScalarP256{
value,
}, nil
}
func (s *BenchScalarP256) Point() Point {
return new(BenchPointP256).Identity()
}
func (s *BenchScalarP256) Clone() Scalar {
return &BenchScalarP256{
value: new(big.Int).Set(s.value),
}
}
func (s *BenchScalarP256) MarshalBinary() ([]byte, error) {
return scalarMarshalBinary(s)
}
func (s *BenchScalarP256) UnmarshalBinary(input []byte) error {
sc, err := scalarUnmarshalBinary(input)
if err != nil {
return err
}
ss, ok := sc.(*BenchScalarP256)
if !ok {
return fmt.Errorf("invalid scalar")
}
s.value = ss.value
return nil
}
func (s *BenchScalarP256) MarshalText() ([]byte, error) {
return scalarMarshalText(s)
}
func (s *BenchScalarP256) UnmarshalText(input []byte) error {
sc, err := scalarUnmarshalText(input)
if err != nil {
return err
}
ss, ok := sc.(*BenchScalarP256)
if !ok {
return fmt.Errorf("invalid scalar")
}
s.value = ss.value
return nil
}
func (s *BenchScalarP256) MarshalJSON() ([]byte, error) {
return scalarMarshalJson(s)
}
func (s *BenchScalarP256) UnmarshalJSON(input []byte) error {
sc, err := scalarUnmarshalJson(input)
if err != nil {
return err
}
S, ok := sc.(*BenchScalarP256)
if !ok {
return fmt.Errorf("invalid type")
}
s.value = S.value
return nil
}
func (p *BenchPointP256) Random(reader io.Reader) Point {
var seed [64]byte
_, _ = reader.Read(seed[:])
return p.Hash(seed[:])
}
func (p *BenchPointP256) Hash(bytes []byte) Point {
curve := elliptic.P256()
domain := []byte("P256_XMD:SHA-256_SSWU_RO_")
uniformBytes, _ := expandMsgXmd(sha256.New(), bytes, domain, 96)
u0 := new(big.Int).SetBytes(uniformBytes[:48])
u1 := new(big.Int).SetBytes(uniformBytes[48:])
u0.Mod(u0, curve.Params().P)
u1.Mod(u1, curve.Params().P)
ssParams := p256SswuParams()
q0x, q0y := osswu3mod4(u0, ssParams)
q1x, q1y := osswu3mod4(u1, ssParams)
x, y := curve.Add(q0x, q0y, q1x, q1y)
return &BenchPointP256{
x, y,
}
}
func (p *BenchPointP256) Identity() Point {
return &BenchPointP256{
x: big.NewInt(0), y: big.NewInt(0),
}
}
func (p *BenchPointP256) Generator() Point {
curve := elliptic.P256().Params()
return &BenchPointP256{
x: new(big.Int).Set(curve.Gx),
y: new(big.Int).Set(curve.Gy),
}
}
func (p *BenchPointP256) IsIdentity() bool {
x := core.ConstantTimeEqByte(p.x, core.Zero)
y := core.ConstantTimeEqByte(p.y, core.Zero)
return (x & y) == 1
}
func (p *BenchPointP256) IsNegative() bool {
return p.y.Bit(0) == 1
}
func (p *BenchPointP256) IsOnCurve() bool {
return elliptic.P256().IsOnCurve(p.x, p.y)
}
func (p *BenchPointP256) Double() Point {
curve := elliptic.P256()
x, y := curve.Double(p.x, p.y)
return &BenchPointP256{x, y}
}
func (p *BenchPointP256) Scalar() Scalar {
return new(BenchScalarP256).Zero()
}
func (p *BenchPointP256) Neg() Point {
y := new(big.Int).Sub(elliptic.P256().Params().P, p.y)
y.Mod(y, elliptic.P256().Params().P)
return &BenchPointP256{x: p.x, y: y}
}
func (p *BenchPointP256) Add(rhs Point) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*BenchPointP256)
if ok {
x, y := elliptic.P256().Add(p.x, p.y, r.x, r.y)
return &BenchPointP256{x, y}
} else {
return nil
}
}
func (p *BenchPointP256) Sub(rhs Point) Point {
if rhs == nil {
return nil
}
r, ok := rhs.Neg().(*BenchPointP256)
if ok {
x, y := elliptic.P256().Add(p.x, p.y, r.x, r.y)
return &BenchPointP256{x, y}
} else {
return nil
}
}
func (p *BenchPointP256) Mul(rhs Scalar) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*BenchScalarP256)
if ok {
x, y := elliptic.P256().ScalarMult(p.x, p.y, r.value.Bytes())
return &BenchPointP256{x, y}
} else {
return nil
}
}
func (p *BenchPointP256) Equal(rhs Point) bool {
r, ok := rhs.(*BenchPointP256)
if ok {
x := core.ConstantTimeEqByte(p.x, r.x)
y := core.ConstantTimeEqByte(p.y, r.y)
return (x & y) == 1
} else {
return false
}
}
func (p *BenchPointP256) Set(x, y *big.Int) (Point, error) {
// check is identity or on curve
xx := subtle.ConstantTimeCompare(x.Bytes(), []byte{})
yy := subtle.ConstantTimeCompare(y.Bytes(), []byte{})
// Checks are constant time
onCurve := elliptic.P256().IsOnCurve(x, y)
if !onCurve && (xx&yy) != 1 {
return nil, fmt.Errorf("invalid coordinates")
}
x = new(big.Int).Set(x)
y = new(big.Int).Set(y)
return &BenchPointP256{x, y}, nil
}
func (p *BenchPointP256) ToAffineCompressed() []byte {
var x [33]byte
x[0] = byte(2)
x[0] |= byte(p.y.Bit(0))
p.x.FillBytes(x[1:])
return x[:]
}
func (p *BenchPointP256) ToAffineUncompressed() []byte {
var out [65]byte
out[0] = byte(4)
p.x.FillBytes(out[1:33])
p.y.FillBytes(out[33:])
return out[:]
}
func (p *BenchPointP256) FromAffineCompressed(bytes []byte) (Point, error) {
if len(bytes) != 33 {
return nil, fmt.Errorf("invalid byte sequence")
}
sign := int(bytes[0])
if sign != 2 && sign != 3 {
return nil, fmt.Errorf("invalid sign byte")
}
sign &= 0x1
x := new(big.Int).SetBytes(bytes[1:])
rhs := rhsP256(x, elliptic.P256().Params())
// test that rhs is quadratic residue
// if not, then this Point is at infinity
y := new(big.Int).ModSqrt(rhs, elliptic.P256().Params().P)
if y != nil {
// fix the sign
if int(y.Bit(0)) != sign {
y.Neg(y)
y.Mod(y, elliptic.P256().Params().P)
}
} else {
x = new(big.Int)
y = new(big.Int)
}
return &BenchPointP256{
x, y,
}, nil
}
func (p *BenchPointP256) FromAffineUncompressed(bytes []byte) (Point, error) {
if len(bytes) != 65 {
return nil, fmt.Errorf("invalid byte sequence")
}
if bytes[0] != 4 {
return nil, fmt.Errorf("invalid sign byte")
}
x := new(big.Int).SetBytes(bytes[1:33])
y := new(big.Int).SetBytes(bytes[33:])
return &BenchPointP256{x, y}, nil
}
func (p *BenchPointP256) CurveName() string {
return elliptic.P256().Params().Name
}
func (p *BenchPointP256) SumOfProducts(points []Point, scalars []Scalar) Point {
nScalars := make([]*big.Int, len(scalars))
for i, sc := range scalars {
s, ok := sc.(*BenchScalarP256)
if !ok {
return nil
}
nScalars[i] = s.value
}
return sumOfProductsPippenger(points, nScalars)
}
func (p *BenchPointP256) X() *big.Int {
return new(big.Int).Set(p.x)
}
func (p *BenchPointP256) Y() *big.Int {
return new(big.Int).Set(p.y)
}
func (p *BenchPointP256) Params() *elliptic.CurveParams {
return elliptic.P256().Params()
}
func (p *BenchPointP256) MarshalBinary() ([]byte, error) {
return pointMarshalBinary(p)
}
func (p *BenchPointP256) UnmarshalBinary(input []byte) error {
pt, err := pointUnmarshalBinary(input)
if err != nil {
return err
}
ppt, ok := pt.(*BenchPointP256)
if !ok {
return fmt.Errorf("invalid point")
}
p.x = ppt.x
p.y = ppt.y
return nil
}
func (p *BenchPointP256) MarshalText() ([]byte, error) {
return pointMarshalText(p)
}
func (p *BenchPointP256) UnmarshalText(input []byte) error {
pt, err := pointUnmarshalText(input)
if err != nil {
return err
}
ppt, ok := pt.(*BenchPointP256)
if !ok {
return fmt.Errorf("invalid point")
}
p.x = ppt.x
p.y = ppt.y
return nil
}
func (p *BenchPointP256) MarshalJSON() ([]byte, error) {
return pointMarshalJSON(p)
}
func (p *BenchPointP256) UnmarshalJSON(input []byte) error {
pt, err := pointUnmarshalJSON(input)
if err != nil {
return err
}
P, ok := pt.(*BenchPointP256)
if !ok {
return fmt.Errorf("invalid type")
}
p.x = P.x
p.y = P.y
return nil
}
// From https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2
func p256SswuParams() *sswuParams {
params := elliptic.P256().Params()
// c1 = (q - 3) / 4
c1 := new(big.Int).Set(params.P)
c1.Sub(c1, big.NewInt(3))
c1.Rsh(c1, 2)
a := big.NewInt(-3)
a.Mod(a, params.P)
b := new(big.Int).Set(params.B)
z := big.NewInt(-10)
z.Mod(z, params.P)
// sqrt(-Z^3)
zTmp := new(big.Int).Exp(z, big.NewInt(3), nil)
zTmp = zTmp.Neg(zTmp)
zTmp.Mod(zTmp, params.P)
c2 := new(big.Int).ModSqrt(zTmp, params.P)
return &sswuParams{
params, c1, c2, a, b, z,
}
}
// rhs of the curve equation
func rhsP256(x *big.Int, params *elliptic.CurveParams) *big.Int {
f := NewField(params.P)
r := f.NewElement(x)
r2 := r.Mul(r)
// x^3-3x+B
a := r.Mul(f.NewElement(big.NewInt(3)))
r = r2.Mul(r)
return r.Add(a.Neg()).Add(f.NewElement(params.B)).Value
}
+668
View File
@@ -0,0 +1,668 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"crypto/elliptic"
"fmt"
"io"
"math/big"
"sync"
"github.com/onsonr/sonr/crypto/core/curves/native"
p256n "github.com/onsonr/sonr/crypto/core/curves/native/p256"
"github.com/onsonr/sonr/crypto/core/curves/native/p256/fp"
"github.com/onsonr/sonr/crypto/core/curves/native/p256/fq"
"github.com/onsonr/sonr/crypto/internal"
)
var (
oldP256InitOnce sync.Once
oldP256 NistP256
)
type NistP256 struct {
*elliptic.CurveParams
}
func oldP256InitAll() {
curve := elliptic.P256()
oldP256.CurveParams = curve.Params()
oldP256.P = curve.Params().P
oldP256.N = curve.Params().N
oldP256.Gx = curve.Params().Gx
oldP256.Gy = curve.Params().Gy
oldP256.B = curve.Params().B
oldP256.BitSize = curve.Params().BitSize
oldP256.Name = curve.Params().Name
}
func NistP256Curve() *NistP256 {
oldP256InitOnce.Do(oldP256InitAll)
return &oldP256
}
func (curve *NistP256) Params() *elliptic.CurveParams {
return curve.CurveParams
}
func (curve *NistP256) IsOnCurve(x, y *big.Int) bool {
_, err := p256n.P256PointNew().SetBigInt(x, y)
return err == nil
}
func (curve *NistP256) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
p1, err := p256n.P256PointNew().SetBigInt(x1, y1)
if err != nil {
return nil, nil
}
p2, err := p256n.P256PointNew().SetBigInt(x2, y2)
if err != nil {
return nil, nil
}
return p1.Add(p1, p2).BigInt()
}
func (curve *NistP256) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
p1, err := p256n.P256PointNew().SetBigInt(x1, y1)
if err != nil {
return nil, nil
}
return p1.Double(p1).BigInt()
}
func (curve *NistP256) ScalarMul(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
p1, err := p256n.P256PointNew().SetBigInt(Bx, By)
if err != nil {
return nil, nil
}
var bytes [32]byte
copy(bytes[:], internal.ReverseScalarBytes(k))
s, err := fq.P256FqNew().SetBytes(&bytes)
if err != nil {
return nil, nil
}
return p1.Mul(p1, s).BigInt()
}
func (curve *NistP256) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
var bytes [32]byte
copy(bytes[:], internal.ReverseScalarBytes(k))
s, err := fq.P256FqNew().SetBytes(&bytes)
if err != nil {
return nil, nil
}
p1 := p256n.P256PointNew().Generator()
return p1.Mul(p1, s).BigInt()
}
type ScalarP256 struct {
value *native.Field
}
type PointP256 struct {
value *native.EllipticPoint
}
func (s *ScalarP256) Random(reader io.Reader) Scalar {
if reader == nil {
return nil
}
var seed [64]byte
_, _ = reader.Read(seed[:])
return s.Hash(seed[:])
}
func (s *ScalarP256) Hash(bytes []byte) Scalar {
dst := []byte("P256_XMD:SHA-256_SSWU_RO_")
xmd := native.ExpandMsgXmd(native.EllipticPointHasherSha256(), bytes, dst, 48)
var t [64]byte
copy(t[:48], internal.ReverseScalarBytes(xmd))
return &ScalarP256{
value: fq.P256FqNew().SetBytesWide(&t),
}
}
func (s *ScalarP256) Zero() Scalar {
return &ScalarP256{
value: fq.P256FqNew().SetZero(),
}
}
func (s *ScalarP256) One() Scalar {
return &ScalarP256{
value: fq.P256FqNew().SetOne(),
}
}
func (s *ScalarP256) IsZero() bool {
return s.value.IsZero() == 1
}
func (s *ScalarP256) IsOne() bool {
return s.value.IsOne() == 1
}
func (s *ScalarP256) IsOdd() bool {
return s.value.Bytes()[0]&1 == 1
}
func (s *ScalarP256) IsEven() bool {
return s.value.Bytes()[0]&1 == 0
}
func (s *ScalarP256) New(value int) Scalar {
t := fq.P256FqNew()
v := big.NewInt(int64(value))
if value < 0 {
v.Mod(v, t.Params.BiModulus)
}
return &ScalarP256{
value: t.SetBigInt(v),
}
}
func (s *ScalarP256) Cmp(rhs Scalar) int {
r, ok := rhs.(*ScalarP256)
if ok {
return s.value.Cmp(r.value)
} else {
return -2
}
}
func (s *ScalarP256) Square() Scalar {
return &ScalarP256{
value: fq.P256FqNew().Square(s.value),
}
}
func (s *ScalarP256) Double() Scalar {
return &ScalarP256{
value: fq.P256FqNew().Double(s.value),
}
}
func (s *ScalarP256) Invert() (Scalar, error) {
value, wasInverted := fq.P256FqNew().Invert(s.value)
if !wasInverted {
return nil, fmt.Errorf("inverse doesn't exist")
}
return &ScalarP256{
value,
}, nil
}
func (s *ScalarP256) Sqrt() (Scalar, error) {
value, wasSquare := fq.P256FqNew().Sqrt(s.value)
if !wasSquare {
return nil, fmt.Errorf("not a square")
}
return &ScalarP256{
value,
}, nil
}
func (s *ScalarP256) Cube() Scalar {
value := fq.P256FqNew().Mul(s.value, s.value)
value.Mul(value, s.value)
return &ScalarP256{
value,
}
}
func (s *ScalarP256) Add(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarP256)
if ok {
return &ScalarP256{
value: fq.P256FqNew().Add(s.value, r.value),
}
} else {
return nil
}
}
func (s *ScalarP256) Sub(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarP256)
if ok {
return &ScalarP256{
value: fq.P256FqNew().Sub(s.value, r.value),
}
} else {
return nil
}
}
func (s *ScalarP256) Mul(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarP256)
if ok {
return &ScalarP256{
value: fq.P256FqNew().Mul(s.value, r.value),
}
} else {
return nil
}
}
func (s *ScalarP256) MulAdd(y, z Scalar) Scalar {
return s.Mul(y).Add(z)
}
func (s *ScalarP256) Div(rhs Scalar) Scalar {
r, ok := rhs.(*ScalarP256)
if ok {
v, wasInverted := fq.P256FqNew().Invert(r.value)
if !wasInverted {
return nil
}
v.Mul(v, s.value)
return &ScalarP256{value: v}
} else {
return nil
}
}
func (s *ScalarP256) Neg() Scalar {
return &ScalarP256{
value: fq.P256FqNew().Neg(s.value),
}
}
func (s *ScalarP256) SetBigInt(v *big.Int) (Scalar, error) {
if v == nil {
return nil, fmt.Errorf("'v' cannot be nil")
}
value := fq.P256FqNew().SetBigInt(v)
return &ScalarP256{
value,
}, nil
}
func (s *ScalarP256) BigInt() *big.Int {
return s.value.BigInt()
}
func (s *ScalarP256) Bytes() []byte {
t := s.value.Bytes()
return internal.ReverseScalarBytes(t[:])
}
func (s *ScalarP256) SetBytes(bytes []byte) (Scalar, error) {
if len(bytes) != 32 {
return nil, fmt.Errorf("invalid length")
}
var seq [32]byte
copy(seq[:], internal.ReverseScalarBytes(bytes))
value, err := fq.P256FqNew().SetBytes(&seq)
if err != nil {
return nil, err
}
return &ScalarP256{
value,
}, nil
}
func (s *ScalarP256) SetBytesWide(bytes []byte) (Scalar, error) {
if len(bytes) != 64 {
return nil, fmt.Errorf("invalid length")
}
var seq [64]byte
copy(seq[:], bytes)
return &ScalarP256{
value: fq.P256FqNew().SetBytesWide(&seq),
}, nil
}
func (s *ScalarP256) Point() Point {
return new(PointP256).Identity()
}
func (s *ScalarP256) Clone() Scalar {
return &ScalarP256{
value: fq.P256FqNew().Set(s.value),
}
}
func (s *ScalarP256) MarshalBinary() ([]byte, error) {
return scalarMarshalBinary(s)
}
func (s *ScalarP256) UnmarshalBinary(input []byte) error {
sc, err := scalarUnmarshalBinary(input)
if err != nil {
return err
}
ss, ok := sc.(*ScalarP256)
if !ok {
return fmt.Errorf("invalid scalar")
}
s.value = ss.value
return nil
}
func (s *ScalarP256) MarshalText() ([]byte, error) {
return scalarMarshalText(s)
}
func (s *ScalarP256) UnmarshalText(input []byte) error {
sc, err := scalarUnmarshalText(input)
if err != nil {
return err
}
ss, ok := sc.(*ScalarP256)
if !ok {
return fmt.Errorf("invalid scalar")
}
s.value = ss.value
return nil
}
func (s *ScalarP256) MarshalJSON() ([]byte, error) {
return scalarMarshalJson(s)
}
func (s *ScalarP256) UnmarshalJSON(input []byte) error {
sc, err := scalarUnmarshalJson(input)
if err != nil {
return err
}
S, ok := sc.(*ScalarP256)
if !ok {
return fmt.Errorf("invalid type")
}
s.value = S.value
return nil
}
func (p *PointP256) Random(reader io.Reader) Point {
var seed [64]byte
_, _ = reader.Read(seed[:])
return p.Hash(seed[:])
}
func (p *PointP256) Hash(bytes []byte) Point {
value, err := p256n.P256PointNew().Hash(bytes, native.EllipticPointHasherSha256())
// TODO: change hash to return an error also
if err != nil {
return nil
}
return &PointP256{value}
}
func (p *PointP256) Identity() Point {
return &PointP256{
value: p256n.P256PointNew().Identity(),
}
}
func (p *PointP256) Generator() Point {
return &PointP256{
value: p256n.P256PointNew().Generator(),
}
}
func (p *PointP256) IsIdentity() bool {
return p.value.IsIdentity()
}
func (p *PointP256) IsNegative() bool {
return p.value.GetY().Value[0]&1 == 1
}
func (p *PointP256) IsOnCurve() bool {
return p.value.IsOnCurve()
}
func (p *PointP256) Double() Point {
value := p256n.P256PointNew().Double(p.value)
return &PointP256{value}
}
func (p *PointP256) Scalar() Scalar {
return new(ScalarP256).Zero()
}
func (p *PointP256) Neg() Point {
value := p256n.P256PointNew().Neg(p.value)
return &PointP256{value}
}
func (p *PointP256) Add(rhs Point) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*PointP256)
if ok {
value := p256n.P256PointNew().Add(p.value, r.value)
return &PointP256{value}
} else {
return nil
}
}
func (p *PointP256) Sub(rhs Point) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*PointP256)
if ok {
value := p256n.P256PointNew().Sub(p.value, r.value)
return &PointP256{value}
} else {
return nil
}
}
func (p *PointP256) Mul(rhs Scalar) Point {
if rhs == nil {
return nil
}
r, ok := rhs.(*ScalarP256)
if ok {
value := p256n.P256PointNew().Mul(p.value, r.value)
return &PointP256{value}
} else {
return nil
}
}
func (p *PointP256) Equal(rhs Point) bool {
r, ok := rhs.(*PointP256)
if ok {
return p.value.Equal(r.value) == 1
} else {
return false
}
}
func (p *PointP256) Set(x, y *big.Int) (Point, error) {
value, err := p256n.P256PointNew().SetBigInt(x, y)
if err != nil {
return nil, err
}
return &PointP256{value}, nil
}
func (p *PointP256) ToAffineCompressed() []byte {
var x [33]byte
x[0] = byte(2)
t := p256n.P256PointNew().ToAffine(p.value)
x[0] |= t.Y.Bytes()[0] & 1
xBytes := t.X.Bytes()
copy(x[1:], internal.ReverseScalarBytes(xBytes[:]))
return x[:]
}
func (p *PointP256) ToAffineUncompressed() []byte {
var out [65]byte
out[0] = byte(4)
t := p256n.P256PointNew().ToAffine(p.value)
arr := t.X.Bytes()
copy(out[1:33], internal.ReverseScalarBytes(arr[:]))
arr = t.Y.Bytes()
copy(out[33:], internal.ReverseScalarBytes(arr[:]))
return out[:]
}
func (p *PointP256) FromAffineCompressed(bytes []byte) (Point, error) {
var raw [native.FieldBytes]byte
if len(bytes) != 33 {
return nil, fmt.Errorf("invalid byte sequence")
}
sign := int(bytes[0])
if sign != 2 && sign != 3 {
return nil, fmt.Errorf("invalid sign byte")
}
sign &= 0x1
copy(raw[:], internal.ReverseScalarBytes(bytes[1:]))
x, err := fp.P256FpNew().SetBytes(&raw)
if err != nil {
return nil, err
}
value := p256n.P256PointNew().Identity()
rhs := fp.P256FpNew()
p.value.Arithmetic.RhsEq(rhs, x)
// test that rhs is quadratic residue
// if not, then this Point is at infinity
y, wasQr := fp.P256FpNew().Sqrt(rhs)
if wasQr {
// fix the sign
sigY := int(y.Bytes()[0] & 1)
if sigY != sign {
y.Neg(y)
}
value.X = x
value.Y = y
value.Z.SetOne()
}
return &PointP256{value}, nil
}
func (p *PointP256) FromAffineUncompressed(bytes []byte) (Point, error) {
var arr [native.FieldBytes]byte
if len(bytes) != 65 {
return nil, fmt.Errorf("invalid byte sequence")
}
if bytes[0] != 4 {
return nil, fmt.Errorf("invalid sign byte")
}
copy(arr[:], internal.ReverseScalarBytes(bytes[1:33]))
x, err := fp.P256FpNew().SetBytes(&arr)
if err != nil {
return nil, err
}
copy(arr[:], internal.ReverseScalarBytes(bytes[33:]))
y, err := fp.P256FpNew().SetBytes(&arr)
if err != nil {
return nil, err
}
value := p256n.P256PointNew()
value.X = x
value.Y = y
value.Z.SetOne()
return &PointP256{value}, nil
}
func (p *PointP256) CurveName() string {
return elliptic.P256().Params().Name
}
func (p *PointP256) SumOfProducts(points []Point, scalars []Scalar) Point {
nPoints := make([]*native.EllipticPoint, len(points))
nScalars := make([]*native.Field, len(scalars))
for i, pt := range points {
ptv, ok := pt.(*PointP256)
if !ok {
return nil
}
nPoints[i] = ptv.value
}
for i, sc := range scalars {
s, ok := sc.(*ScalarP256)
if !ok {
return nil
}
nScalars[i] = s.value
}
value := p256n.P256PointNew()
_, err := value.SumOfProducts(nPoints, nScalars)
if err != nil {
return nil
}
return &PointP256{value}
}
func (p *PointP256) X() *native.Field {
return p.value.GetX()
}
func (p *PointP256) Y() *native.Field {
return p.value.GetY()
}
func (p *PointP256) Params() *elliptic.CurveParams {
return elliptic.P256().Params()
}
func (p *PointP256) MarshalBinary() ([]byte, error) {
return pointMarshalBinary(p)
}
func (p *PointP256) UnmarshalBinary(input []byte) error {
pt, err := pointUnmarshalBinary(input)
if err != nil {
return err
}
ppt, ok := pt.(*PointP256)
if !ok {
return fmt.Errorf("invalid point")
}
p.value = ppt.value
return nil
}
func (p *PointP256) MarshalText() ([]byte, error) {
return pointMarshalText(p)
}
func (p *PointP256) UnmarshalText(input []byte) error {
pt, err := pointUnmarshalText(input)
if err != nil {
return err
}
ppt, ok := pt.(*PointP256)
if !ok {
return fmt.Errorf("invalid point")
}
p.value = ppt.value
return nil
}
func (p *PointP256) MarshalJSON() ([]byte, error) {
return pointMarshalJSON(p)
}
func (p *PointP256) UnmarshalJSON(input []byte) error {
pt, err := pointUnmarshalJSON(input)
if err != nil {
return err
}
P, ok := pt.(*PointP256)
if !ok {
return fmt.Errorf("invalid type")
}
p.value = P.value
return nil
}
+365
View File
@@ -0,0 +1,365 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
"crypto/elliptic"
crand "crypto/rand"
"math/big"
"testing"
"github.com/stretchr/testify/require"
)
func TestScalarP256Random(t *testing.T) {
p256 := P256()
sc := p256.Scalar.Random(testRng())
s, ok := sc.(*ScalarP256)
require.True(t, ok)
expected := bhex("02ca487e56f8cb1ad9666027b2282d1159792d39a5e05f0bc696f85de5acc6d4")
require.Equal(t, s.value.BigInt(), expected)
// Try 10 random values
for i := 0; i < 10; i++ {
sc := p256.Scalar.Random(crand.Reader)
_, ok := sc.(*ScalarP256)
require.True(t, ok)
require.True(t, !sc.IsZero())
}
}
func TestScalarP256Hash(t *testing.T) {
var b [32]byte
p256 := P256()
sc := p256.Scalar.Hash(b[:])
s, ok := sc.(*ScalarP256)
require.True(t, ok)
expected := bhex("43ca74a23022b221e60c8499781afff2a2776ec23362712df90a3080b5557f90")
require.Equal(t, s.value.BigInt(), expected)
}
func TestScalarP256Zero(t *testing.T) {
p256 := P256()
sc := p256.Scalar.Zero()
require.True(t, sc.IsZero())
require.True(t, sc.IsEven())
}
func TestScalarP256One(t *testing.T) {
p256 := P256()
sc := p256.Scalar.One()
require.True(t, sc.IsOne())
require.True(t, sc.IsOdd())
}
func TestScalarP256New(t *testing.T) {
p256 := P256()
three := p256.Scalar.New(3)
require.True(t, three.IsOdd())
four := p256.Scalar.New(4)
require.True(t, four.IsEven())
neg1 := p256.Scalar.New(-1)
require.True(t, neg1.IsEven())
neg2 := p256.Scalar.New(-2)
require.True(t, neg2.IsOdd())
}
func TestScalarP256Square(t *testing.T) {
p256 := P256()
three := p256.Scalar.New(3)
nine := p256.Scalar.New(9)
require.Equal(t, three.Square().Cmp(nine), 0)
}
func TestScalarP256Cube(t *testing.T) {
p256 := P256()
three := p256.Scalar.New(3)
twentySeven := p256.Scalar.New(27)
require.Equal(t, three.Cube().Cmp(twentySeven), 0)
}
func TestScalarP256Double(t *testing.T) {
p256 := P256()
three := p256.Scalar.New(3)
six := p256.Scalar.New(6)
require.Equal(t, three.Double().Cmp(six), 0)
}
func TestScalarP256Neg(t *testing.T) {
p256 := P256()
one := p256.Scalar.One()
neg1 := p256.Scalar.New(-1)
require.Equal(t, one.Neg().Cmp(neg1), 0)
lotsOfThrees := p256.Scalar.New(333333)
expected := p256.Scalar.New(-333333)
require.Equal(t, lotsOfThrees.Neg().Cmp(expected), 0)
}
func TestScalarP256Invert(t *testing.T) {
p256 := P256()
nine := p256.Scalar.New(9)
actual, _ := nine.Invert()
sa, _ := actual.(*ScalarP256)
bn := bhex("8e38e38daaaaaaab38e38e38e38e38e368f2197ceb0d1f2d6af570a536e1bf66")
expected, err := p256.Scalar.SetBigInt(bn)
require.NoError(t, err)
require.Equal(t, sa.Cmp(expected), 0)
}
func TestScalarP256Sqrt(t *testing.T) {
p256 := P256()
nine := p256.Scalar.New(9)
actual, err := nine.Sqrt()
sa, _ := actual.(*ScalarP256)
expected := p256.Scalar.New(3)
require.NoError(t, err)
require.Equal(t, sa.Cmp(expected), 0)
}
func TestScalarP256Add(t *testing.T) {
p256 := P256()
nine := p256.Scalar.New(9)
six := p256.Scalar.New(6)
fifteen := nine.Add(six)
require.NotNil(t, fifteen)
expected := p256.Scalar.New(15)
require.Equal(t, expected.Cmp(fifteen), 0)
n := new(big.Int).Set(elliptic.P256().Params().N)
n.Sub(n, big.NewInt(3))
upper, err := p256.Scalar.SetBigInt(n)
require.NoError(t, err)
actual := upper.Add(nine)
require.NotNil(t, actual)
require.Equal(t, actual.Cmp(six), 0)
}
func TestScalarP256Sub(t *testing.T) {
p256 := P256()
nine := p256.Scalar.New(9)
six := p256.Scalar.New(6)
n := new(big.Int).Set(elliptic.P256().Params().N)
n.Sub(n, big.NewInt(3))
expected, err := p256.Scalar.SetBigInt(n)
require.NoError(t, err)
actual := six.Sub(nine)
require.Equal(t, expected.Cmp(actual), 0)
actual = nine.Sub(six)
require.Equal(t, actual.Cmp(p256.Scalar.New(3)), 0)
}
func TestScalarP256Mul(t *testing.T) {
p256 := P256()
nine := p256.Scalar.New(9)
six := p256.Scalar.New(6)
actual := nine.Mul(six)
require.Equal(t, actual.Cmp(p256.Scalar.New(54)), 0)
n := new(big.Int).Set(elliptic.P256().Params().N)
n.Sub(n, big.NewInt(1))
upper, err := p256.Scalar.SetBigInt(n)
require.NoError(t, err)
require.Equal(t, upper.Mul(upper).Cmp(p256.Scalar.New(1)), 0)
}
func TestScalarP256Div(t *testing.T) {
p256 := P256()
nine := p256.Scalar.New(9)
actual := nine.Div(nine)
require.Equal(t, actual.Cmp(p256.Scalar.New(1)), 0)
require.Equal(t, p256.Scalar.New(54).Div(nine).Cmp(p256.Scalar.New(6)), 0)
}
func TestScalarP256Serialize(t *testing.T) {
p256 := P256()
sc := p256.Scalar.New(255)
sequence := sc.Bytes()
require.Equal(t, len(sequence), 32)
require.Equal(t, sequence, []byte{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, 0xff})
ret, err := p256.Scalar.SetBytes(sequence)
require.NoError(t, err)
require.Equal(t, ret.Cmp(sc), 0)
// Try 10 random values
for i := 0; i < 10; i++ {
sc = p256.Scalar.Random(crand.Reader)
sequence = sc.Bytes()
require.Equal(t, len(sequence), 32)
ret, err = p256.Scalar.SetBytes(sequence)
require.NoError(t, err)
require.Equal(t, ret.Cmp(sc), 0)
}
}
func TestScalarP256Nil(t *testing.T) {
p256 := P256()
one := p256.Scalar.New(1)
require.Nil(t, one.Add(nil))
require.Nil(t, one.Sub(nil))
require.Nil(t, one.Mul(nil))
require.Nil(t, one.Div(nil))
require.Nil(t, p256.Scalar.Random(nil))
require.Equal(t, one.Cmp(nil), -2)
_, err := p256.Scalar.SetBigInt(nil)
require.Error(t, err)
}
func TestPointP256Random(t *testing.T) {
p256 := P256()
sc := p256.Point.Random(testRng())
s, ok := sc.(*PointP256)
require.True(t, ok)
expectedX, _ := new(big.Int).SetString("7d31a079d75687cd0dd1118996f726c3e4d52806a5124d23c1faeee9fadb2201", 16)
expectedY, _ := new(big.Int).SetString("da62629181a0e2ec6943c263bbe81f53d87cb94d0039a707309f415f04d47bab", 16)
require.Equal(t, s.X().BigInt(), expectedX)
require.Equal(t, s.Y().BigInt(), expectedY)
// Try 10 random values
for i := 0; i < 10; i++ {
sc := p256.Point.Random(crand.Reader)
_, ok := sc.(*PointP256)
require.True(t, ok)
require.True(t, !sc.IsIdentity())
}
}
func TestPointP256Hash(t *testing.T) {
var b [32]byte
p256 := P256()
sc := p256.Point.Hash(b[:])
s, ok := sc.(*PointP256)
require.True(t, ok)
expectedX, _ := new(big.Int).SetString("10f1699c24cf20f5322b92bfab4fdf14814ee1472ffc6b1ef2ec36be3051925d", 16)
expectedY, _ := new(big.Int).SetString("99efe055f13c3e5e3fc65be12043a89d0482b806c7a9945c276b7aee887c1de0", 16)
require.Equal(t, s.X().BigInt(), expectedX)
require.Equal(t, s.Y().BigInt(), expectedY)
}
func TestPointP256Identity(t *testing.T) {
p256 := P256()
sc := p256.Point.Identity()
require.True(t, sc.IsIdentity())
require.Equal(t, sc.ToAffineCompressed(), []byte{2, 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})
}
func TestPointP256Generator(t *testing.T) {
p256 := P256()
sc := p256.Point.Generator()
s, ok := sc.(*PointP256)
require.True(t, ok)
require.Equal(t, s.X().BigInt(), elliptic.P256().Params().Gx)
require.Equal(t, s.Y().BigInt(), elliptic.P256().Params().Gy)
}
func TestPointP256Set(t *testing.T) {
p256 := P256()
iden, err := p256.Point.Set(big.NewInt(0), big.NewInt(0))
require.NoError(t, err)
require.True(t, iden.IsIdentity())
_, err = p256.Point.Set(elliptic.P256().Params().Gx, elliptic.P256().Params().Gy)
require.NoError(t, err)
}
func TestPointP256Double(t *testing.T) {
p256 := P256()
g := p256.Point.Generator()
g2 := g.Double()
require.True(t, g2.Equal(g.Mul(p256.Scalar.New(2))))
i := p256.Point.Identity()
require.True(t, i.Double().Equal(i))
}
func TestPointP256Neg(t *testing.T) {
p256 := P256()
g := p256.Point.Generator().Neg()
require.True(t, g.Neg().Equal(p256.Point.Generator()))
require.True(t, p256.Point.Identity().Neg().Equal(p256.Point.Identity()))
}
func TestPointP256Add(t *testing.T) {
p256 := P256()
pt := p256.Point.Generator()
require.True(t, pt.Add(pt).Equal(pt.Double()))
require.True(t, pt.Mul(p256.Scalar.New(3)).Equal(pt.Add(pt).Add(pt)))
}
func TestPointP256Sub(t *testing.T) {
p256 := P256()
g := p256.Point.Generator()
pt := p256.Point.Generator().Mul(p256.Scalar.New(4))
require.True(t, pt.Sub(g).Sub(g).Sub(g).Equal(g))
require.True(t, pt.Sub(g).Sub(g).Sub(g).Sub(g).IsIdentity())
}
func TestPointP256Mul(t *testing.T) {
p256 := P256()
g := p256.Point.Generator()
pt := p256.Point.Generator().Mul(p256.Scalar.New(4))
require.True(t, g.Double().Double().Equal(pt))
}
func TestPointP256Serialize(t *testing.T) {
p256 := P256()
ss := p256.Scalar.Random(testRng())
g := p256.Point.Generator()
ppt := g.Mul(ss)
require.Equal(t, ppt.ToAffineCompressed(), []byte{0x3, 0x1b, 0xfa, 0x44, 0xfb, 0x6, 0xa4, 0x6d, 0xe1, 0x38, 0x72, 0x39, 0x9a, 0x69, 0xd4, 0x71, 0x38, 0x3f, 0x8b, 0x13, 0x39, 0x60, 0xdc, 0x61, 0x91, 0x22, 0x70, 0x58, 0x7c, 0xdf, 0x82, 0x33, 0xba})
require.Equal(t, ppt.ToAffineUncompressed(), []byte{0x4, 0x1b, 0xfa, 0x44, 0xfb, 0x6, 0xa4, 0x6d, 0xe1, 0x38, 0x72, 0x39, 0x9a, 0x69, 0xd4, 0x71, 0x38, 0x3f, 0x8b, 0x13, 0x39, 0x60, 0xdc, 0x61, 0x91, 0x22, 0x70, 0x58, 0x7c, 0xdf, 0x82, 0x33, 0xba, 0x48, 0xdf, 0x58, 0xac, 0x70, 0xf3, 0x9b, 0x9d, 0x5d, 0x84, 0x6e, 0x2d, 0x75, 0xc9, 0x6, 0x1e, 0xd9, 0x62, 0x8b, 0x15, 0x0, 0x65, 0x69, 0x79, 0xfb, 0x42, 0xc, 0x35, 0xce, 0x2e, 0x8b, 0xfd})
retP, err := ppt.FromAffineCompressed(ppt.ToAffineCompressed())
require.NoError(t, err)
require.True(t, ppt.Equal(retP))
retP, err = ppt.FromAffineUncompressed(ppt.ToAffineUncompressed())
require.NoError(t, err)
require.True(t, ppt.Equal(retP))
// smoke test
for i := 0; i < 25; i++ {
s := p256.Scalar.Random(crand.Reader)
pt := g.Mul(s)
cmprs := pt.ToAffineCompressed()
require.Equal(t, len(cmprs), 33)
retC, err := pt.FromAffineCompressed(cmprs)
require.NoError(t, err)
require.True(t, pt.Equal(retC))
un := pt.ToAffineUncompressed()
require.Equal(t, len(un), 65)
retU, err := pt.FromAffineUncompressed(un)
require.NoError(t, err)
require.True(t, pt.Equal(retU))
}
}
func TestPointP256Nil(t *testing.T) {
p256 := P256()
one := p256.Point.Generator()
require.Nil(t, one.Add(nil))
require.Nil(t, one.Sub(nil))
require.Nil(t, one.Mul(nil))
require.Nil(t, p256.Scalar.Random(nil))
require.False(t, one.Equal(nil))
_, err := p256.Scalar.SetBigInt(nil)
require.Error(t, err)
}
func TestPointP256SumOfProducts(t *testing.T) {
lhs := new(PointP256).Generator().Mul(new(ScalarP256).New(50))
points := make([]Point, 5)
for i := range points {
points[i] = new(PointP256).Generator()
}
scalars := []Scalar{
new(ScalarP256).New(8),
new(ScalarP256).New(9),
new(ScalarP256).New(10),
new(ScalarP256).New(11),
new(ScalarP256).New(12),
}
rhs := lhs.SumOfProducts(points, scalars)
require.NotNil(t, rhs)
require.True(t, lhs.Equal(rhs))
}
+1176
View File
File diff suppressed because it is too large Load Diff
+138
View File
@@ -0,0 +1,138 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package curves
import (
crand "crypto/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/core/curves/native/pasta/fp"
"github.com/onsonr/sonr/crypto/core/curves/native/pasta/fq"
)
func TestPointPallasAddDoubleMul(t *testing.T) {
g := new(Ep).Generator()
id := new(Ep).Identity()
require.Equal(t, g.Add(g, id), g)
g2 := new(Ep).Add(g, g)
require.True(t, new(Ep).Double(g).Equal(g2))
require.Equal(t, new(Ep).Double(g), new(Ep).Add(g, g))
g3 := new(Ep).Add(g, g2)
require.True(t, g3.Equal(new(Ep).Mul(g, new(fq.Fq).SetUint64(3))))
g4 := new(Ep).Add(g3, g)
require.True(t, g4.Equal(new(Ep).Double(g2)))
require.True(t, g4.Equal(new(Ep).Mul(g, new(fq.Fq).SetUint64(4))))
}
func TestPointPallasHash(t *testing.T) {
h0 := new(Ep).Hash(nil)
require.True(t, h0.IsOnCurve())
h1 := new(Ep).Hash([]byte{})
require.True(t, h1.IsOnCurve())
require.True(t, h0.Equal(h1))
h2 := new(Ep).Hash([]byte{1})
require.True(t, h2.IsOnCurve())
}
func TestPointPallasNeg(t *testing.T) {
g := new(Ep).Generator()
g.Neg(g)
require.True(t, g.Neg(g).Equal(new(Ep).Generator()))
id := new(Ep).Identity()
require.True(t, new(Ep).Neg(id).Equal(id))
}
func TestPointPallasRandom(t *testing.T) {
a := new(Ep).Random(testRng())
require.NotNil(t, a.x)
require.NotNil(t, a.y)
require.NotNil(t, a.z)
require.True(t, a.IsOnCurve())
e := &Ep{
x: &fp.Fp{
0x7263083d01d4859c,
0x65a03323b5a3d204,
0xe71d73222b136668,
0x1d1b1bcf1256b539,
},
y: &fp.Fp{
0x8cc2516ffe23e1bb,
0x5418f941eeaca812,
0x16c9af658a846f29,
0x11c572091c418668,
},
z: &fp.Fp{
0xa879589adb77a88e,
0x5444a531a19f2406,
0x637ff77c51dda524,
0x0369e90d219ce821,
},
}
require.True(t, a.Equal(e))
}
func TestPointPallasSerialize(t *testing.T) {
ss := new(ScalarPallas).Random(testRng()).(*ScalarPallas)
g := new(Ep).Generator()
ppt := new(Ep).Mul(g, ss.value)
require.Equal(t, ppt.ToAffineCompressed(), []byte{0x1c, 0x6d, 0x47, 0x1f, 0x4a, 0x81, 0xcd, 0x8, 0x4e, 0xb3, 0x17, 0x9a, 0xcd, 0x17, 0xe2, 0x9a, 0x24, 0x69, 0xb, 0x4e, 0x69, 0x5f, 0x35, 0x1a, 0x92, 0x12, 0x95, 0xc9, 0xe6, 0xd3, 0x7a, 0x0})
require.Equal(t, ppt.ToAffineUncompressed(), []byte{0x1c, 0x6d, 0x47, 0x1f, 0x4a, 0x81, 0xcd, 0x8, 0x4e, 0xb3, 0x17, 0x9a, 0xcd, 0x17, 0xe2, 0x9a, 0x24, 0x69, 0xb, 0x4e, 0x69, 0x5f, 0x35, 0x1a, 0x92, 0x12, 0x95, 0xc9, 0xe6, 0xd3, 0x7a, 0x0, 0x80, 0x5c, 0xa1, 0x56, 0x6d, 0x1b, 0x87, 0x5f, 0xb0, 0x2e, 0xae, 0x85, 0x4e, 0x86, 0xa9, 0xcd, 0xde, 0x37, 0x6a, 0xc8, 0x4a, 0x80, 0xf6, 0x43, 0xaa, 0xe6, 0x2c, 0x2d, 0x15, 0xdb, 0xda, 0x29})
retP, err := new(Ep).FromAffineCompressed(ppt.ToAffineCompressed())
require.NoError(t, err)
require.True(t, ppt.Equal(retP))
retP, err = new(Ep).FromAffineUncompressed(ppt.ToAffineUncompressed())
require.NoError(t, err)
require.True(t, ppt.Equal(retP))
// smoke test
for i := 0; i < 25; i++ {
s := new(ScalarPallas).Random(crand.Reader).(*ScalarPallas)
pt := new(Ep).Mul(g, s.value)
cmprs := pt.ToAffineCompressed()
require.Equal(t, len(cmprs), 32)
retC, err := new(Ep).FromAffineCompressed(cmprs)
require.NoError(t, err)
require.True(t, pt.Equal(retC))
un := pt.ToAffineUncompressed()
require.Equal(t, len(un), 64)
retU, err := new(Ep).FromAffineUncompressed(un)
require.NoError(t, err)
require.True(t, pt.Equal(retU))
}
}
func TestPointPallasCMove(t *testing.T) {
a := new(Ep).Random(crand.Reader)
b := new(Ep).Random(crand.Reader)
require.True(t, new(Ep).CMove(a, b, 1).Equal(b))
require.True(t, new(Ep).CMove(a, b, 0).Equal(a))
}
func TestPointPallasSumOfProducts(t *testing.T) {
lhs := new(Ep).Generator()
lhs.Mul(lhs, new(fq.Fq).SetUint64(50))
points := make([]*Ep, 5)
for i := range points {
points[i] = new(Ep).Generator()
}
scalars := []Scalar{
new(ScalarPallas).New(8),
new(ScalarPallas).New(9),
new(ScalarPallas).New(10),
new(ScalarPallas).New(11),
new(ScalarPallas).New(12),
}
rhs := lhs.SumOfProducts(points, scalars)
require.NotNil(t, rhs)
require.True(t, lhs.Equal(rhs))
}
+315
View File
@@ -0,0 +1,315 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Copyright 2011 ThePiachu. All rights reserved.
// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
// * The name of ThePiachu may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package secp256k1
import (
"crypto/elliptic"
"math/big"
)
const (
// number of bits in a big.Word
wordBits = 32 << (uint64(^big.Word(0)) >> 63)
// number of bytes in a big.Word
wordBytes = wordBits / 8
)
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
Bz := new(big.Int).SetInt64(1)
x, y, z := new(big.Int), new(big.Int), new(big.Int)
for _, byte := range k {
for bitNum := 0; bitNum < 8; bitNum++ {
x, y, z = bitCurve.doubleJacobian(x, y, z)
if byte&0x80 == 0x80 {
x, y, z = bitCurve.addJacobian(Bx, By, Bz, x, y, z)
}
byte <<= 1
}
}
return bitCurve.affineFromJacobian(x, y, z)
}
// readBits encodes the absolute value of bigint as big-endian bytes. Callers
// must ensure that buf has enough space. If buf is too short the result will
// be incomplete.
func readBits(bigint *big.Int, buf []byte) {
i := len(buf)
for _, d := range bigint.Bits() {
for j := 0; j < wordBytes && i > 0; j++ {
i--
buf[i] = byte(d)
d >>= 8
}
}
}
// This code is from https://github.com/ThePiachu/GoBit and implements
// several Koblitz elliptic curves over prime fields.
//
// The curve methods, internally, on Jacobian coordinates. For a given
// (x, y) position on the curve, the Jacobian coordinates are (x1, y1,
// z1) where x = x1/z1² and y = y1/z1³. The greatest speedups come
// when the whole calculation can be performed within the transform
// (as in ScalarMult and ScalarBaseMult). But even for Add and Double,
// it's faster to apply and reverse the transform than to operate in
// affine coordinates.
// A BitCurve represents a Koblitz Curve with a=0.
// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html
type BitCurve struct {
P *big.Int // the order of the underlying field
N *big.Int // the order of the base point
B *big.Int // the constant of the BitCurve equation
Gx, Gy *big.Int // (x,y) of the base point
BitSize int // the size of the underlying field
}
func (bitCurve *BitCurve) Params() *elliptic.CurveParams {
return &elliptic.CurveParams{
P: bitCurve.P,
N: bitCurve.N,
B: bitCurve.B,
Gx: bitCurve.Gx,
Gy: bitCurve.Gy,
BitSize: bitCurve.BitSize,
}
}
// IsOnCurve returns true if the given (x,y) lies on the BitCurve.
func (bitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool {
// y² = x³ + b
y2 := new(big.Int).Mul(y, y) // y²
y2.Mod(y2, bitCurve.P) // y²%P
x3 := new(big.Int).Mul(x, x) // x²
x3.Mul(x3, x) // x³
x3.Add(x3, bitCurve.B) // x³+B
x3.Mod(x3, bitCurve.P) //(x³+B)%P
return x3.Cmp(y2) == 0
}
// affineFromJacobian reverses the Jacobian transform. See the comment at the
// top of the file.
func (bitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
if z.Sign() == 0 {
return new(big.Int), new(big.Int)
}
zinv := new(big.Int).ModInverse(z, bitCurve.P)
zinvsq := new(big.Int).Mul(zinv, zinv)
xOut = new(big.Int).Mul(x, zinvsq)
xOut.Mod(xOut, bitCurve.P)
zinvsq.Mul(zinvsq, zinv)
yOut = new(big.Int).Mul(y, zinvsq)
yOut.Mod(yOut, bitCurve.P)
return
}
// Add returns the sum of (x1,y1) and (x2,y2)
func (bitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
// If one point is at infinity, return the other point.
// Adding the point at infinity to any point will preserve the other point.
if x1.Sign() == 0 && y1.Sign() == 0 {
return x2, y2
}
if x2.Sign() == 0 && y2.Sign() == 0 {
return x1, y1
}
z := new(big.Int).SetInt64(1)
if x1.Cmp(x2) == 0 && y1.Cmp(y2) == 0 {
return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z))
}
return bitCurve.affineFromJacobian(bitCurve.addJacobian(x1, y1, z, x2, y2, z))
}
// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
// (x2, y2, z2) and returns their sum, also in Jacobian form.
func (bitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
z1z1 := new(big.Int).Mul(z1, z1)
z1z1.Mod(z1z1, bitCurve.P)
z2z2 := new(big.Int).Mul(z2, z2)
z2z2.Mod(z2z2, bitCurve.P)
u1 := new(big.Int).Mul(x1, z2z2)
u1.Mod(u1, bitCurve.P)
u2 := new(big.Int).Mul(x2, z1z1)
u2.Mod(u2, bitCurve.P)
h := new(big.Int).Sub(u2, u1)
if h.Sign() == -1 {
h.Add(h, bitCurve.P)
}
i := new(big.Int).Lsh(h, 1)
i.Mul(i, i)
j := new(big.Int).Mul(h, i)
s1 := new(big.Int).Mul(y1, z2)
s1.Mul(s1, z2z2)
s1.Mod(s1, bitCurve.P)
s2 := new(big.Int).Mul(y2, z1)
s2.Mul(s2, z1z1)
s2.Mod(s2, bitCurve.P)
r := new(big.Int).Sub(s2, s1)
if r.Sign() == -1 {
r.Add(r, bitCurve.P)
}
r.Lsh(r, 1)
v := new(big.Int).Mul(u1, i)
x3 := new(big.Int).Set(r)
x3.Mul(x3, x3)
x3.Sub(x3, j)
x3.Sub(x3, v)
x3.Sub(x3, v)
x3.Mod(x3, bitCurve.P)
y3 := new(big.Int).Set(r)
v.Sub(v, x3)
y3.Mul(y3, v)
s1.Mul(s1, j)
s1.Lsh(s1, 1)
y3.Sub(y3, s1)
y3.Mod(y3, bitCurve.P)
z3 := new(big.Int).Add(z1, z2)
z3.Mul(z3, z3)
z3.Sub(z3, z1z1)
if z3.Sign() == -1 {
z3.Add(z3, bitCurve.P)
}
z3.Sub(z3, z2z2)
if z3.Sign() == -1 {
z3.Add(z3, bitCurve.P)
}
z3.Mul(z3, h)
z3.Mod(z3, bitCurve.P)
return x3, y3, z3
}
// Double returns 2*(x,y)
func (bitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
z1 := new(big.Int).SetInt64(1)
return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z1))
}
// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and
// returns its double, also in Jacobian form.
func (bitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) {
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
a := new(big.Int).Mul(x, x) // X1²
b := new(big.Int).Mul(y, y) // Y1²
c := new(big.Int).Mul(b, b) // B²
d := new(big.Int).Add(x, b) // X1+B
d.Mul(d, d) //(X1+B)²
d.Sub(d, a) //(X1+B)²-A
d.Sub(d, c) //(X1+B)²-A-C
d.Mul(d, big.NewInt(2)) // 2*((X1+B)²-A-C)
e := new(big.Int).Mul(big.NewInt(3), a) // 3*A
f := new(big.Int).Mul(e, e) // E²
x3 := new(big.Int).Mul(big.NewInt(2), d) // 2*D
x3.Sub(f, x3) // F-2*D
x3.Mod(x3, bitCurve.P)
y3 := new(big.Int).Sub(d, x3) // D-X3
y3.Mul(e, y3) // E*(D-X3)
y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) // E*(D-X3)-8*C
y3.Mod(y3, bitCurve.P)
z3 := new(big.Int).Mul(y, z) // Y1*Z1
z3.Mul(big.NewInt(2), z3) // 3*Y1*Z1
z3.Mod(z3, bitCurve.P)
return x3, y3, z3
}
// ScalarBaseMult returns k*G, where G is the base point of the group and k is
// an integer in big-endian form.
func (bitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
return bitCurve.ScalarMult(bitCurve.Gx, bitCurve.Gy, k)
}
// Marshal converts a point into the form specified in section 4.3.6 of ANSI
// X9.62.
func (bitCurve *BitCurve) Marshal(x, y *big.Int) []byte {
byteLen := (bitCurve.BitSize + 7) >> 3
ret := make([]byte, 1+2*byteLen)
ret[0] = 4 // uncompressed point flag
readBits(x, ret[1:1+byteLen])
readBits(y, ret[1+byteLen:])
return ret
}
// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On
// error, x = nil.
func (bitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) {
byteLen := (bitCurve.BitSize + 7) >> 3
if len(data) != 1+2*byteLen {
return
}
if data[0] != 4 { // uncompressed form
return
}
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
y = new(big.Int).SetBytes(data[1+byteLen:])
return
}
var theCurve = new(BitCurve)
func init() {
// See SEC 2 section 2.7.1
// curve parameters taken from:
// http://www.secg.org/sec2-v2.pdf
theCurve.P, _ = new(big.Int).SetString("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 0)
theCurve.N, _ = new(big.Int).SetString("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 0)
theCurve.B, _ = new(big.Int).SetString("0x0000000000000000000000000000000000000000000000000000000000000007", 0)
theCurve.Gx, _ = new(big.Int).SetString("0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 0)
theCurve.Gy, _ = new(big.Int).SetString("0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 0)
theCurve.BitSize = 256
}
// S256 returns a BitCurve which implements secp256k1.
func S256() *BitCurve {
return theCurve
}
+11
View File
@@ -0,0 +1,11 @@
package curves
import (
"crypto/elliptic"
"github.com/dustinxie/ecc"
)
func SP256() elliptic.Curve {
return ecc.P256k1()
}
+272
View File
@@ -0,0 +1,272 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package core
import (
"bytes"
"crypto/elliptic"
"crypto/sha256"
"fmt"
"hash"
"math"
"math/big"
"github.com/btcsuite/btcd/btcec/v2"
"golang.org/x/crypto/hkdf"
"github.com/onsonr/sonr/crypto/internal"
)
type HashField struct {
// F_p^k
Order *big.Int // p^k
Characteristic *big.Int // p
ExtensionDegree *big.Int // k
}
type Params struct {
F *HashField
SecurityParameter int
Hash func() hash.Hash
L int
}
func getParams(curve elliptic.Curve) (*Params, error) {
switch curve.Params().Name {
case btcec.S256().Name, elliptic.P256().Params().Name:
return &Params{
F: &HashField{
Order: curve.Params().P,
Characteristic: curve.Params().P,
ExtensionDegree: new(big.Int).SetInt64(1),
},
SecurityParameter: 128,
Hash: sha256.New,
L: 48,
}, nil
case "Bls12381G1":
return &Params{
F: &HashField{
Order: curve.Params().P,
Characteristic: curve.Params().P,
ExtensionDegree: new(big.Int).SetInt64(1),
},
SecurityParameter: 128,
Hash: sha256.New,
L: 48,
}, nil
case "ed25519":
return &Params{
F: &HashField{
Order: curve.Params().P,
Characteristic: curve.Params().P,
ExtensionDegree: new(big.Int).SetInt64(1),
},
SecurityParameter: 128,
Hash: sha256.New,
L: 48,
}, nil
default:
return nil, fmt.Errorf("not implemented: %s", curve.Params().Name)
}
}
func I2OSP(b, n int) []byte {
os := new(big.Int).SetInt64(int64(b)).Bytes()
if n > len(os) {
var buf bytes.Buffer
buf.Write(make([]byte, n-len(os)))
buf.Write(os)
return buf.Bytes()
}
return os[:n]
}
func OS2IP(os []byte) *big.Int {
return new(big.Int).SetBytes(os)
}
func hashThis(f func() hash.Hash, this []byte) ([]byte, error) {
h := f()
w, err := h.Write(this)
if w != len(this) {
return nil, fmt.Errorf("bytes written to hash doesn't match expected")
} else if err != nil {
return nil, err
}
v := h.Sum(nil)
return v, nil
}
func concat(xs ...[]byte) []byte {
var result []byte
for _, x := range xs {
result = append(result, x...)
}
return result
}
func xor(b1, b2 []byte) []byte {
// b1 and b2 must be same length
result := make([]byte, len(b1))
for i := range b1 {
result[i] = b1[i] ^ b2[i]
}
return result
}
func ExpandMessageXmd(f func() hash.Hash, msg, DST []byte, lenInBytes int) ([]byte, error) {
// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-10#section-5.4.1
// step 1
ell := int(math.Ceil(float64(lenInBytes) / float64(f().Size())))
// step 2
if ell > 255 {
return nil, fmt.Errorf("ell > 255")
}
// step 3
dstPrime := append(DST, I2OSP(len(DST), 1)...)
// step 4
zPad := I2OSP(0, f().BlockSize())
// step 5 & 6
msgPrime := concat(zPad, msg, I2OSP(lenInBytes, 2), I2OSP(0, 1), dstPrime)
var err error
b := make([][]byte, ell+1)
// step 7
b[0], err = hashThis(f, msgPrime)
if err != nil {
return nil, err
}
// step 8
b[1], err = hashThis(f, concat(b[0], I2OSP(1, 1), dstPrime))
if err != nil {
return nil, err
}
// step 9
for i := 2; i <= ell; i++ {
// step 10
b[i], err = hashThis(f, concat(xor(b[0], b[i-1]), I2OSP(i, 1), dstPrime))
if err != nil {
return nil, err
}
}
// step 11
uniformBytes := concat(b[1:]...)
// step 12
return uniformBytes[:lenInBytes], nil
}
func hashToField(msg []byte, count int, curve elliptic.Curve) ([][]*big.Int, error) {
// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-10#section-5.3
parameters, err := getParams(curve)
if err != nil {
return nil, err
}
f := parameters.Hash
DST := []byte("Coinbase_tECDSA")
m := int(parameters.F.ExtensionDegree.Int64())
L := parameters.L
// step 1
lenInBytes := count * m * L
// step 2
uniformBytes, err := ExpandMessageXmd(f, msg, DST, lenInBytes)
if err != nil {
return nil, err
}
u := make([][]*big.Int, count)
// step 3
for i := 0; i < count; i++ {
e := make([]*big.Int, m)
// step 4
for j := 0; j < m; j++ {
// step 5
elmOffset := L * (j + i*m)
// step 6
tv := uniformBytes[elmOffset : elmOffset+L]
// step 7
e[j] = new(big.Int).Mod(OS2IP(tv), parameters.F.Characteristic)
}
// step 8
u[i] = e
}
// step 9
return u, nil
}
func Hash(msg []byte, curve elliptic.Curve) (*big.Int, error) {
u, err := hashToField(msg, 1, curve)
if err != nil {
return nil, err
}
return u[0][0], nil
}
// fiatShamir computes the HKDF over many values
// iteratively such that each value is hashed separately
// and based on preceding values
//
// The first value is computed as okm_0 = KDF(f || value) where
// f is a byte slice of 32 0xFF
// salt is zero-filled byte slice with length equal to the hash output length
// info is the protocol name
// okm is the 32 byte output
//
// The each subsequent iteration is computed by as okm_i = KDF(f_i || value || okm_{i-1})
// where f_i = 2^b - 1 - i such that there are 0xFF bytes prior to the value.
// f_1 changes the first byte to 0xFE, f_2 to 0xFD. The previous okm is appended to the value
// to provide cryptographic domain separation.
// See https://signal.org/docs/specifications/x3dh/#cryptographic-notation
// and https://signal.org/docs/specifications/xeddsa/#hash-functions
// for more details.
// This uses the KDF function similar to X3DH for each `value`
// But changes the key just like XEdDSA where the prefix bytes change by a single bit
func FiatShamir(values ...*big.Int) ([]byte, error) {
// Don't accept any nil arguments
if AnyNil(values...) {
return nil, internal.ErrNilArguments
}
info := []byte("Coinbase tECDSA 1.0")
salt := make([]byte, 32)
okm := make([]byte, 32)
f := bytes.Repeat([]byte{0xFF}, 32)
for _, b := range values {
ikm := append(f, b.Bytes()...)
ikm = append(ikm, okm...)
kdf := hkdf.New(sha256.New, ikm, salt, info)
n, err := kdf.Read(okm)
if err != nil {
return nil, err
}
if n != len(okm) {
return nil, fmt.Errorf("unable to read expected number of bytes want=%v got=%v", len(okm), n)
}
internal.ByteSub(f)
}
return okm, nil
}
+132
View File
@@ -0,0 +1,132 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package core
import (
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"hash"
"math/big"
"strconv"
"testing"
"github.com/stretchr/testify/require"
)
func TestExpandMessageXmd(t *testing.T) {
// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-10#appendix-K.1
tests := []struct {
f func() hash.Hash
msg []byte
lenInBytesHex string
expectedHex string
}{
{sha256.New, []byte(""), "20", "f659819a6473c1835b25ea59e3d38914c98b374f0970b7e4c92181df928fca88"},
{sha256.New, []byte("abc"), "20", "1c38f7c211ef233367b2420d04798fa4698080a8901021a795a1151775fe4da7"},
{sha256.New, []byte("abcdef0123456789"), "20", "8f7e7b66791f0da0dbb5ec7c22ec637f79758c0a48170bfb7c4611bd304ece89"},
{sha256.New, []byte("q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"), "20", "72d5aa5ec810370d1f0013c0df2f1d65699494ee2a39f72e1716b1b964e1c642"},
{sha256.New, []byte("a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), "20", "3b8e704fc48336aca4c2a12195b720882f2162a4b7b13a9c350db46f429b771b"},
{sha256.New, []byte(""), "80", "8bcffd1a3cae24cf9cd7ab85628fd111bb17e3739d3b53f89580d217aa79526f1708354a76a402d3569d6a9d19ef3de4d0b991e4f54b9f20dcde9b95a66824cbdf6c1a963a1913d43fd7ac443a02fc5d9d8d77e2071b86ab114a9f34150954a7531da568a1ea8c760861c0cde2005afc2c114042ee7b5848f5303f0611cf297f"},
{sha256.New, []byte("abc"), "80", "fe994ec51bdaa821598047b3121c149b364b178606d5e72bfbb713933acc29c186f316baecf7ea22212f2496ef3f785a27e84a40d8b299cec56032763eceeff4c61bd1fe65ed81decafff4a31d0198619c0aa0c6c51fca15520789925e813dcfd318b542f8799441271f4db9ee3b8092a7a2e8d5b75b73e28fb1ab6b4573c192"},
{sha256.New, []byte("abcdef0123456789"), "80", "c9ec7941811b1e19ce98e21db28d22259354d4d0643e301175e2f474e030d32694e9dd5520dde93f3600d8edad94e5c364903088a7228cc9eff685d7eaac50d5a5a8229d083b51de4ccc3733917f4b9535a819b445814890b7029b5de805bf62b33a4dc7e24acdf2c924e9fe50d55a6b832c8c84c7f82474b34e48c6d43867be"},
{sha256.New, []byte("q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"), "80", "48e256ddba722053ba462b2b93351fc966026e6d6db493189798181c5f3feea377b5a6f1d8368d7453faef715f9aecb078cd402cbd548c0e179c4ed1e4c7e5b048e0a39d31817b5b24f50db58bb3720fe96ba53db947842120a068816ac05c159bb5266c63658b4f000cbf87b1209a225def8ef1dca917bcda79a1e42acd8069"},
{sha256.New, []byte("a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), "80", "396962db47f749ec3b5042ce2452b619607f27fd3939ece2746a7614fb83a1d097f554df3927b084e55de92c7871430d6b95c2a13896d8a33bc48587b1f66d21b128a1a8240d5b0c26dfe795a1a842a0807bb148b77c2ef82ed4b6c9f7fcb732e7f94466c8b51e52bf378fba044a31f5cb44583a892f5969dcd73b3fa128816e"},
{sha512.New, []byte(""), "20", "2eaa1f7b5715f4736e6a5dbe288257abf1faa028680c1d938cd62ac699ead642"},
{sha512.New, []byte("abc"), "20", "0eeda81f69376c80c0f8986496f22f21124cb3c562cf1dc608d2c13005553b0f"},
{sha512.New, []byte("abcdef0123456789"), "20", "2e375fc05e05e80dbf3083796fde2911789d9e8847e1fcebf4ca4b36e239b338"},
{sha512.New, []byte("q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"), "20", "c37f9095fe7fe4f01c03c3540c1229e6ac8583b07510085920f62ec66acc0197"},
{sha512.New, []byte("a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), "20", "af57a7f56e9ed2aa88c6eab45c8c6e7638ae02da7c92cc04f6648c874ebd560e"},
{sha512.New, []byte(""), "80", "0687ce02eba5eb3faf1c3c539d1f04babd3c0f420edae244eeb2253b6c6d6865145c31458e824b4e87ca61c3442dc7c8c9872b0b7250aa33e0668ccebbd2b386de658ca11a1dcceb51368721ae6dcd2d4bc86eaebc4e0d11fa02ad053289c9b28a03da6c942b2e12c14e88dbde3b0ba619d6214f47212b628f3e1b537b66efcf"},
{sha512.New, []byte("abc"), "80", "779ae4fd8a92f365e4df96b9fde97b40486bb005c1a2096c86f55f3d92875d89045fbdbc4a0e9f2d3e1e6bcd870b2d7131d868225b6fe72881a81cc5166b5285393f71d2e68bb0ac603479959370d06bdbe5f0d8bfd9af9494d1e4029bd68ab35a561341dd3f866b3ef0c95c1fdfaab384ce24a23427803dda1db0c7d8d5344a"},
{sha512.New, []byte("abcdef0123456789"), "80", "f0953d28846a50e9f88b7ae35b643fc43733c9618751b569a73960c655c068db7b9f044ad5a40d49d91c62302eaa26163c12abfa982e2b5d753049e000adf7630ae117aeb1fb9b61fc724431ac68b369e12a9481b4294384c3c890d576a79264787bc8076e7cdabe50c044130e480501046920ff090c1a091c88391502f0fbac"},
{sha512.New, []byte("q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"), "80", "64d3e59f0bc3c5e653011c914b419ba8310390a9585311fddb26791d26663bd71971c347e1b5e88ba9274d2445ed9dcf48eea9528d807b7952924159b7c27caa4f25a2ea94df9508e70a7012dfce0e8021b37e59ea21b80aa9af7f1a1f2efa4fbe523c4266ce7d342acaacd438e452c501c131156b4945515e9008d2b155c258"},
{sha512.New, []byte("a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), "80", "01524feea5b22f6509f6b1e805c97df94faf4d821b01aadeebc89e9daaed0733b4544e50852fd3e019d58eaad6d267a134c8bc2c08bc46c10bfeff3ee03110bcd8a0d695d75a34092bd8b677bdd369a13325549abab54f4ac907b712bdd3567f38c4554c51902b735b81f43a7ef6f938c7690d107c052c7e7b795ac635b3200a"},
}
for _, test := range tests {
t.Run(fmt.Sprintf("msg: %s", test.msg), func(t *testing.T) {
DST := []byte("QUUX-V01-CS02-with-expander")
lenInBytes, err := strconv.ParseInt(test.lenInBytesHex, 16, 64)
require.NoError(t, err)
expected, err := hex.DecodeString(test.expectedHex)
require.NoError(t, err)
actual, err := ExpandMessageXmd(test.f, test.msg, DST, int(lenInBytes))
require.NoError(t, err)
require.Equal(t, actual, expected)
})
}
}
func TestFiatShamirDeterministic(t *testing.T) {
a := big.NewInt(1)
hash, err := FiatShamir(a)
require.Nil(t, err)
require.Equal(t, hash, []byte{0x3d, 0x95, 0xc0, 0x9f, 0x41, 0x33, 0x25, 0xbb, 0x10, 0x77, 0x2d, 0x83, 0x1d, 0x3e, 0x67, 0x98, 0xce, 0x7b, 0xde, 0xd5, 0x7f, 0x9, 0x7e, 0xfc, 0x77, 0x23, 0xfc, 0x49, 0x36, 0x82, 0xdd, 0x6a})
a = big.NewInt(0xaaa)
hash, err = FiatShamir(a)
require.Nil(t, err)
require.Equal(t, hash, []byte{0x61, 0xa9, 0x7f, 0x6, 0x74, 0xec, 0x47, 0xf5, 0xac, 0x30, 0xa0, 0x4c, 0x34, 0xdb, 0x51, 0x97, 0x60, 0x7e, 0xb2, 0x4a, 0x97, 0x9, 0xa5, 0xb9, 0x1c, 0x89, 0x30, 0x39, 0xb7, 0x29, 0xe6, 0x30})
}
func TestFiatShamirEqual(t *testing.T) {
pi, err := FiatShamir(One)
require.Nil(t, err)
pi_, err := FiatShamir(One)
require.Nil(t, err)
require.Equal(t, pi, pi_)
}
func TestFiatShamirNotEqual(t *testing.T) {
pi, err := FiatShamir(One)
require.Nil(t, err)
pi_, err := FiatShamir(Two)
require.Nil(t, err)
require.NotEqual(t, pi, pi_)
}
func TestFiatShamirOrderDependent(t *testing.T) {
a := big.NewInt(1)
b := big.NewInt(100)
pi, err := FiatShamir(a, b)
require.Nil(t, err)
pi_, err := FiatShamir(a, b)
require.Nil(t, err)
require.Equal(t, pi, pi_)
q, _ := FiatShamir(b, a)
require.NotEqual(t, pi, q)
}
func TestFiatShamirExtensionAttackResistance(t *testing.T) {
a := big.NewInt(0x00FF)
b := big.NewInt(0xFF00)
c := big.NewInt(0x00)
d := big.NewInt(0xFFFF00)
pi, err := FiatShamir(a, b)
require.Nil(t, err)
pi_, err := FiatShamir(c, d)
require.Nil(t, err)
require.NotEqual(t, pi, pi_)
a = big.NewInt(0x0000)
b = big.NewInt(0xFFFF)
c = big.NewInt(0x0000F)
d = big.NewInt(0xFFF)
q, err := FiatShamir(a, b)
require.Nil(t, err)
q_, err := FiatShamir(c, d)
require.Nil(t, err)
require.NotEqual(t, q, q_)
}
+178
View File
@@ -0,0 +1,178 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// Package core contains convenience functions for modular arithmetic.
// Package core contains a set of primitives, including but not limited to various
// elliptic curves, hashes, and commitment schemes. These primitives are used internally
// and can also be used independently on their own externally.
package core
import (
crand "crypto/rand"
"crypto/subtle"
"fmt"
"math/big"
"github.com/onsonr/sonr/crypto/internal"
)
var (
// Zero is additive identity in the set of integers
Zero = big.NewInt(0)
// One is the multiplicative identity in the set of integers
One = big.NewInt(1)
// Two is the odd prime
Two = big.NewInt(2)
)
// ConstantTimeEqByte determines if a, b have identical byte serialization
// and signs. It uses the crypto/subtle package to get a constant time comparison
// over byte representations. Return value is a byte which may be
// useful in bitwise operations. Returns 0x1 if the two values have the
// identical sign and byte representation; 0x0 otherwise.
func ConstantTimeEqByte(a, b *big.Int) byte {
if a == nil && a == b {
return 1
}
if a == nil || b == nil {
return 0
}
// Determine if the byte representations are the same
var sameBytes byte
if subtle.ConstantTimeCompare(a.Bytes(), b.Bytes()) == 1 {
sameBytes = 1
} else {
sameBytes = 0
}
// Determine if the signs are the same
var sameSign byte
if a.Sign() == b.Sign() {
sameSign = 1
} else {
sameSign = 0
}
// Report the conjunction
return sameBytes & sameSign
}
// ConstantTimeEq determines if a, b have identical byte serialization
// and uses the crypto/subtle package to get a constant time comparison
// over byte representations.
func ConstantTimeEq(a, b *big.Int) bool {
return ConstantTimeEqByte(a, b) == 1
}
// In determines ring membership before modular reduction: x ∈ Z_m
// returns nil if 0 ≤ x < m
func In(x, m *big.Int) error {
if AnyNil(x, m) {
return internal.ErrNilArguments
}
// subtle doesn't support constant time big.Int compare
// just use big.Cmp for now
// x ∈ Z_m ⇔ 0 ≤ x < m
if x.Cmp(Zero) != -1 && x.Cmp(m) == -1 {
return nil
}
return internal.ErrZmMembership
}
// Add (modular addition): z = x+y (modulo m)
func Add(x, y, m *big.Int) (*big.Int, error) {
if AnyNil(x, y) {
return nil, internal.ErrNilArguments
}
z := new(big.Int).Add(x, y)
// Compute the residue if one is specified, otherwise
// we leave the value as an unbound integer
if m != nil {
z.Mod(z, m)
}
return z, nil
}
// Mul (modular multiplication): z = x*y (modulo m)
func Mul(x, y, m *big.Int) (*big.Int, error) {
if AnyNil(x, y) {
return nil, internal.ErrNilArguments
}
z := new(big.Int).Mul(x, y)
// Compute the residue if one is specified, otherwise
// we leave the value as an unbound integer
if m != nil {
z.Mod(z, m)
}
return z, nil
}
// Exp (modular exponentiation): z = x^y (modulo m)
func Exp(x, y, m *big.Int) (*big.Int, error) {
if AnyNil(x, y) {
return nil, internal.ErrNilArguments
}
// This wrapper looks silly, but it makes the calling code read more consistently.
return new(big.Int).Exp(x, y, m), nil
}
// Neg (modular negation): z = -x (modulo m)
func Neg(x, m *big.Int) (*big.Int, error) {
if AnyNil(x, m) {
return nil, internal.ErrNilArguments
}
z := new(big.Int).Neg(x)
z.Mod(z, m)
return z, nil
}
// Inv (modular inverse): returns y such that xy = 1 (modulo m).
func Inv(x, m *big.Int) (*big.Int, error) {
if AnyNil(x, m) {
return nil, internal.ErrNilArguments
}
z := new(big.Int).ModInverse(x, m)
if z == nil {
return nil, fmt.Errorf("cannot compute the multiplicative inverse")
}
return z, nil
}
// Rand generates a cryptographically secure random integer in the range: 1 < r < m.
func Rand(m *big.Int) (*big.Int, error) {
if m == nil {
return nil, internal.ErrNilArguments
}
// Select a random element, but not zero or one
// The reason is the random element may be used as a Scalar or an exponent.
// An exponent of 1 is generally acceptable because the generator can't be
// 1. If a Scalar is combined with another Scalar like in fiat-shamir, it
// offers no hiding properties when multiplied.
for {
result, err := crand.Int(crand.Reader, m)
if err != nil {
return nil, err
}
if result.Cmp(One) == 1 { // result > 1
return result, nil
}
}
}
// AnyNil determines if any of values are nil
func AnyNil(values ...*big.Int) bool {
for _, x := range values {
if x == nil {
return true
}
}
return false
}
+565
View File
@@ -0,0 +1,565 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package core
import (
"math/big"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/internal"
)
var (
four = big.NewInt(4)
// Large numbers for testing -- computing with independent tooling
// x,y 100-digit numbers
x, _ = new(big.Int).SetString("7146643783615963513942641287213372249533955323510461217840179896547799100626220786140425637990097431", 10)
y, _ = new(big.Int).SetString("1747698065194620177681258504464368264357359841192790848951902311522815739310792522712583635858354245", 10)
sumxy, _ = new(big.Int).SetString("8894341848810583691623899791677740513891315164703252066792082208070614839937013308853009273848451676", 10)
xy, _ = new(big.Int).SetString("12490175513260779219420155073726764321605372267033815716483640700978475653623775696463227582174703069158832890348206546318843052423532258178885792744599932977235221784868792263260215861775082862444595", 10)
// 101-digit modulus
m, _ = new(big.Int).SetString("85832751158419329546684678412285185885848111422509523329716452068504806021136687603399722116388773253", 10)
// 99-digit modulus
n, _ = new(big.Int).SetString("604464499356780653111583485887412477603580949137220100557796699530113283915988830359783807274682723", 10)
)
func TestConstantTimeEqByteSound(t *testing.T) {
hundoDigit := internal.B10("3593421565679030456559622742114065111786271367498220644136232358421457354322411370928949366452183472")
tests := []struct {
name string
a, b *big.Int
expected byte
}{
{"positive: 5", internal.B10("5"), internal.B10("5"), 1},
{"positive: 100", internal.B10("100"), internal.B10("100"), 1},
{"positive: -1204", internal.B10("-1204"), internal.B10("-1204"), 1},
{"positive: 100 digits", hundoDigit, hundoDigit, 1},
{"positive: 0", internal.B10("0"), internal.B10("0"), 1},
{"positive: 0/-0", internal.B10("0"), internal.B10("-0"), 1},
{"positive: -0/-0", internal.B10("-0"), internal.B10("-0"), 1},
{"negative: 5/-5", internal.B10("5"), internal.B10("-5"), 0},
{"negative: 5/500", internal.B10("5"), internal.B10("500"), 0},
{"negative: 100/100 digit", internal.B10("100"), hundoDigit, 0},
{"negative: -1204/-5", internal.B10("-1204"), internal.B10("-15"), 0},
{"negative: 0/-5 digits", internal.B10("0"), internal.B10("-5"), 0},
}
// Run all the tests!
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := ConstantTimeEqByte(test.a, test.b)
require.Equal(t, test.expected, actual)
})
}
}
func TestConstantTimeEqSound(t *testing.T) {
hundoDigit := internal.B10("3593421565679030456559622742114065111786271367498220644136232358421457354322411370928949366452183472")
tests := []struct {
name string
a, b *big.Int
expected bool
}{
{"positive: 5", internal.B10("5"), internal.B10("5"), true},
{"positive: 100", internal.B10("100"), internal.B10("100"), true},
{"positive: -1204", internal.B10("-1204"), internal.B10("-1204"), true},
{"positive: 100 digits", hundoDigit, hundoDigit, true},
{"positive: 0", internal.B10("0"), internal.B10("0"), true},
{"positive: 0/-0", internal.B10("0"), internal.B10("-0"), true},
{"positive: -0/-0", internal.B10("-0"), internal.B10("-0"), true},
{"negative: 5/-5", internal.B10("5"), internal.B10("-5"), false},
{"negative: 5/500", internal.B10("5"), internal.B10("500"), false},
{"negative: 100/100 digit", internal.B10("100"), hundoDigit, false},
{"negative: -1204/-5", internal.B10("-1204"), internal.B10("-15"), false},
{"negative: 0/-5 digits", internal.B10("0"), internal.B10("-5"), false},
}
// Run all the tests!
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := ConstantTimeEq(test.a, test.b)
require.Equal(t, test.expected, actual)
})
}
}
// Ring membership tests
func TestIn(t *testing.T) {
// Some large numbers for testing
x, _ := new(big.Int).SetString("21888242871839275222246405745257275088696311157297823662689037894645226208583", 10)
y, _ := new(big.Int).SetString("32168432167132168106409840321684604654063138460840123871234181628904319728058", 10)
N := new(big.Int).Mul(x, y) // N = xy
NN := new(big.Int).Mul(N, N) // N^2 = N*N = x^2y^2
errMember := internal.ErrZmMembership
tests := []struct {
x *big.Int
m *big.Int
expected error
}{
//
// Completist test for: Z_4
//
// Too small: -x ∉ Z_4, \forall \x \in \N
{big.NewInt(-4), four, errMember},
{big.NewInt(-3), four, errMember},
{big.NewInt(-2), four, errMember},
{big.NewInt(-1), four, errMember},
// Just right: {0,1,2,3} = Z_4
{big.NewInt(0), four, nil},
{big.NewInt(1), four, nil},
{big.NewInt(2), four, nil},
{big.NewInt(3), four, nil},
// Too big: {4,5,6,7} ∉ Z_4
{big.NewInt(4), four, errMember},
{big.NewInt(5), four, errMember},
{big.NewInt(6), four, errMember},
{big.NewInt(7), four, errMember},
//
// Large numbers
//
// x,y,N < N^2
{x, NN, nil},
{y, NN, nil},
{N, NN, nil},
// N+x,N+y,2N < N^2 ⇒ x ∈ Z_N^2
{big.NewInt(0).Add(N, x), NN, nil},
{big.NewInt(0).Add(N, y), NN, nil},
{big.NewInt(0).Add(N, N), NN, nil},
// Nx,Ny < N^2 ⇒ x ∈ Z_N^2
{big.NewInt(0).Mul(N, x), NN, nil},
{big.NewInt(0).Mul(N, y), NN, nil},
// -x,-y,-N ∉ Z_N^2
{big.NewInt(0).Neg(x), NN, errMember},
{big.NewInt(0).Neg(y), NN, errMember},
{big.NewInt(0).Neg(N), NN, errMember},
// N^2 ∉ Z_N^2
{NN, NN, errMember},
}
// All the tests!
for _, test := range tests {
actual := In(test.x, test.m)
require.Equal(t, test.expected, actual)
}
}
// Tests for modular addition with known answers
func TestAdd(t *testing.T) {
// Pre-compute some values
sumXyModn, err := Add(x, y, n)
require.Nil(t, err)
tests := []struct {
x, y, m, expected *big.Int // inputs: x,y,m
}{
// Small number tests
{big.NewInt(-1), big.NewInt(1), four, big.NewInt(0)},
{big.NewInt(2), big.NewInt(1), four, big.NewInt(3)},
{big.NewInt(0), big.NewInt(2), four, big.NewInt(2)},
{big.NewInt(2), big.NewInt(4), four, big.NewInt(2)},
{big.NewInt(15), big.NewInt(15), four, big.NewInt(2)},
// Large number tests
{x, y, m, sumxy},
{y, x, m, sumxy},
// Large number Zero tests
{Zero, x, m, x},
{x, Zero, m, x},
{Zero, y, m, y},
{y, Zero, m, y},
// Commutative
{x, y, m, sumxy},
{y, x, m, sumxy},
{x, y, n, sumXyModn},
{y, x, n, sumXyModn},
{sumXyModn, Zero, n, sumXyModn},
{Zero, sumXyModn, n, sumXyModn},
}
// All the tests!
for _, test := range tests {
actual, err := Add(test.x, test.y, test.m)
require.NoError(t, err)
require.Zero(t, actual.Cmp(test.expected))
}
}
// Tests for modular addition according to known invariants
func TestAddInvariants(t *testing.T) {
inputs := []*big.Int{x, y, Zero, One, new(big.Int).Neg(x), new(big.Int).Neg(y)}
moduli := []*big.Int{m, n, big.NewInt(10001)}
// Run all combinations of the inputs/moduli
for _, x := range inputs {
for _, y := range inputs {
for _, m := range moduli {
// Addition is commutative
z0, err := Add(x, y, m)
require.NoError(t, err)
z1, err := Add(y, x, m)
require.NoError(t, err)
require.Equal(t, z0, z1)
// Addition is transitive: x+x+y == y+x+x == x+y+x
a0, _ := Add(x, x, m)
a0, _ = Add(a0, y, m)
a1, _ := Add(y, x, m)
a1, _ = Add(a1, x, m)
a2, _ := Add(x, y, m)
a2, _ = Add(a2, x, m)
require.Equal(t, a0, a1)
require.Equal(t, a1, a2)
}
}
}
}
// Tests modular multiplication with known answers
func TestMul(t *testing.T) {
// Pre-compute some values
xyModm := new(big.Int).Mod(xy, m)
tests := []struct {
x, y, m, expected *big.Int // inputs: x,y,m
}{
// Small number tests
{big.NewInt(-1), big.NewInt(1), four, big.NewInt(3)},
{big.NewInt(2), big.NewInt(1), four, big.NewInt(2)},
{big.NewInt(0), big.NewInt(2), four, big.NewInt(0)},
{big.NewInt(2), big.NewInt(4), four, big.NewInt(0)},
{big.NewInt(15), big.NewInt(15), four, big.NewInt(1)},
// Large number tests
{x, y, m, xyModm},
{y, x, m, xyModm},
// Large number Zero tests
{Zero, x, m, Zero},
{x, Zero, m, Zero},
{Zero, y, n, Zero},
}
// All the tests!
for _, test := range tests {
z, err := Mul(test.x, test.y, test.m)
require.NoError(t, err)
require.Zero(t, z.Cmp(test.expected))
}
}
// Tests for modular multiplication according to known invariants
func TestMulInvariants(t *testing.T) {
inputs := []*big.Int{x, y, Zero, One, new(big.Int).Neg(x), new(big.Int).Neg(y)}
moduli := []*big.Int{m, n, big.NewInt(10001)}
// Run all combinations of the inputs/moduli
for _, x := range inputs {
for _, y := range inputs {
for _, m := range moduli {
// Mul is commutative
a, err := Mul(x, y, m)
require.NoError(t, err)
aʹ, err := Mul(y, x, m)
require.NoError(t, err)
require.Equal(t, a, aʹ)
// Mul is transitive: (xx)y == (xy)x
z, _ := Mul(x, x, m)
z, _ = Mul(z, y, m)
zʹ, _ := Mul(x, y, m)
zʹ, _ = Mul(zʹ, x, m)
require.Equal(t, z, zʹ)
}
}
}
}
// Tests modular negation with known answers
func TestNeg(t *testing.T) {
tests := []struct {
x, m, e *big.Int
}{
{big.NewInt(1), big.NewInt(7), big.NewInt(6)},
{big.NewInt(2), big.NewInt(7), big.NewInt(5)},
{big.NewInt(3), big.NewInt(7), big.NewInt(4)},
{big.NewInt(4), big.NewInt(7), big.NewInt(3)},
{big.NewInt(5), big.NewInt(7), big.NewInt(2)},
{big.NewInt(6), big.NewInt(7), big.NewInt(1)},
{big.NewInt(-1), big.NewInt(7), big.NewInt(1)},
{big.NewInt(-2), big.NewInt(7), big.NewInt(2)},
{big.NewInt(-3), big.NewInt(7), big.NewInt(3)},
{big.NewInt(-4), big.NewInt(7), big.NewInt(4)},
{big.NewInt(-5), big.NewInt(7), big.NewInt(5)},
{big.NewInt(-6), big.NewInt(7), big.NewInt(6)},
{big.NewInt(8), big.NewInt(7), big.NewInt(6)},
{big.NewInt(9), big.NewInt(7), big.NewInt(5)},
{big.NewInt(10), big.NewInt(7), big.NewInt(4)},
{big.NewInt(11), big.NewInt(7), big.NewInt(3)},
{big.NewInt(12), big.NewInt(7), big.NewInt(2)},
{big.NewInt(13), big.NewInt(7), big.NewInt(1)},
}
for _, test := range tests {
r, err := Neg(test.x, test.m)
require.NoError(t, err)
if r.Cmp(test.e) != 0 {
t.Errorf("TestNeg failed. Expected %v, got: %v ", test.e, r)
}
}
}
func TestNegInvariants(t *testing.T) {
tests := []struct {
x, m, e *big.Int
}{
{big.NewInt(0), big.NewInt(7), big.NewInt(0)},
{big.NewInt(7), big.NewInt(7), big.NewInt(0)},
{big.NewInt(-7), big.NewInt(7), big.NewInt(0)},
}
for _, test := range tests {
r, err := Neg(test.x, test.m)
require.NoError(t, err)
if r.Cmp(test.e) != 0 {
t.Errorf("TestNeg failed. Expected %v, got: %v ", test.e, r)
}
}
}
// Simple test for distinct Rand output
func TestRandDistinct(t *testing.T) {
// Each value should be distinct
a, _ := Rand(n)
b, _ := Rand(n)
c, _ := Rand(n)
// ❄️❄️❄️
require.NotEqual(t, a, b)
require.NotEqual(t, a, c)
require.NotEqual(t, b, c)
}
// Rand values should be O(log2(m)) bits
func TestRandIsExpectedLength(t *testing.T) {
trials := 1000
max := big.NewInt(-1)
// Generate many nonces, keep the max
for i := 0; i < trials; i++ {
r, err := Rand(m)
require.NoError(t, err)
// Nonces should be < m
if r.Cmp(m) != -1 {
t.Errorf("nonce too large, require %v < %v", r, m)
}
if r.Cmp(max) == 1 {
max = r
}
}
// With high probability, the max nonce should be very close N
lowerBound := new(big.Int).Rsh(m, 1)
if max.Cmp(lowerBound) == -1 {
t.Errorf("Expected max nonce: %v > %v", max, lowerBound)
}
}
// Randomly selected nonces with a large modulus will be unique with overwhelming probability
func TestRandDistinctWithLargeModulus(t *testing.T) {
const iterations = 1000
testUnique(t, iterations, func() *big.Int {
r, _ := Rand(m)
return r
})
}
// Calls sampleFunc() n times and asserts that the lower 64B of each output are unique.
func testUnique(t *testing.T, iterations int, sampleFunc func() *big.Int) {
// For simplicity, we test only the lower 64B of each nonce. This is sufficient
// to prove uniqueness and go-lang doesn't hash slices (no slices in maps)
const size = 256 / 8
seen := make(map[[size]byte]bool)
var x [size]byte
// Check the pre-computed commitments for uniquness
for i := 0; i < iterations; i++ {
// Retrieve a sample
sample := sampleFunc()
require.NotNil(t, sample)
// Copy the bytes from slice>array
copy(x[:], sample.Bytes())
// Ensure each sample is unique
if seen[x] {
t.Errorf("duplicate sample found: %v", x)
}
seen[x] = true
}
}
// Ensure Rand never returns 0 or 1.
func TestRandNotZeroNotOne(t *testing.T) {
// Test for non-zero only useful when iterations >> |Z_m|
const iterations = 1000
m := big.NewInt(5)
for i := 0; i < iterations; i++ {
r, err := Rand(m)
require.NoError(t, err)
// Not 0 or 1
require.NotEqual(t, r, Zero)
require.NotEqual(t, r, One)
}
}
func TestRand_NilModulusErrors(t *testing.T) {
r, err := Rand(nil)
require.Nil(t, r)
require.Contains(t, err.Error(), internal.ErrNilArguments.Error())
}
// Double-inverse is the identity function in fields
func TestInvRoundTrip(t *testing.T) {
m := internal.B10("1031") // Prime-order modulus
for _, a := range []*big.Int{
internal.B10("500"),
internal.B10("-500"),
internal.B10("1"),
internal.B10("1030"),
} {
// Our expected value is the modular reduction of the test value
expected := a.Mod(a, m)
// Invert and check
aInv, err := Inv(a, m)
require.NoError(t, err, "a=%v", a)
require.NotNil(t, aInv)
// Invert again and check
a_, err := Inv(aInv, m)
if err != nil {
require.Equal(t, expected, a_)
}
}
}
// Tests values for which there is no inverse in the given field
func TestInvNotFound(t *testing.T) {
m := internal.B10("1024") // m = 2^10
// 0 and even numbers will not have inverse in this ring
for _, a := range []*big.Int{
internal.B10("500"),
internal.B10("-500"),
internal.B10("0"),
internal.B10("1024"),
internal.B10("512"),
internal.B10("300000000"),
} {
// Invert and check
aInv, err := Inv(a, m)
require.Error(t, err, "a=%v", a)
require.Nil(t, aInv)
}
}
func TestExpKnownAnswer(t *testing.T) {
p := internal.B10("1031") // prime-order field
pMinus1 := internal.B10("1030")
tests := []struct {
name string
x, e, m *big.Int
expected *big.Int
}{
{"fermat's little thm: 500", internal.B10("500"), p, p, internal.B10("500")},
{"fermat's little thm (p-1): 500", internal.B10("500"), pMinus1, p, One},
{"fermat's little thm (p-1): 5000", internal.B10("5000"), pMinus1, p, One},
{"399^0 = 1", internal.B10("399"), Zero, p, One},
{"673^1 = 673", internal.B10("673"), One, p, internal.B10("673")},
}
// Run all the tests!
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual, err := Exp(test.x, test.e, test.m)
if err != nil {
require.Equal(t, test.expected, actual)
}
})
}
}
// A product of two 1024b safe primes
var N1024 = internal.B10("22657252520748253292205422817162431301953923432914829530688424232913850279325496327198502914522231560238552529734156383924448818535517634061008476071362010781638360092704508943571866960229942049437914690556866055765377519627454975682400206932320319743805083072214857842762721537739950074695623974079312071498296625705376593890814889314744719469735809152488403143751157723139035869185892099006348653635981206799193781030834368833947197930944812082594326193527332208252230115672713914945889734620959932802893197325106135662762752470236627025599443912886530954179753873735786171937758916890000958846322096261981191349917")
// A product of two 256b safe primes
var N256 = internal.B10("10815068324662993508164204692909269429257853772524581783499643160896147777579932560873002543907262462663453338979819981987639157192530671167315407970757417")
func Benchmark_rand1024(b *testing.B) {
if testing.Short() {
b.Skip("skipping test in short mode.")
}
for i := 0; i < b.N; i++ {
Rand(N1024) // nolint
}
}
func BenchmarkRand1024(b *testing.B) {
if testing.Short() {
b.Skip("skipping test in short mode.")
}
for i := 0; i < b.N; i++ {
Rand(N1024) // nolint
}
}
func Benchmark_rand256(b *testing.B) {
if testing.Short() {
b.Skip("skipping test in short mode.")
}
for i := 0; i < b.N; i++ {
Rand(N256) // nolint
}
}
func BenchmarkRandStar256(b *testing.B) {
if testing.Short() {
b.Skip("skipping test in short mode.")
}
for i := 0; i < b.N; i++ {
Rand(N256) // nolint
}
}
+42
View File
@@ -0,0 +1,42 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package core
import (
"crypto/rand"
"fmt"
"math"
"math/big"
)
// GenerateSafePrime creates a prime number `p`
// where (`p`-1)/2 is also prime with at least `bits`
func GenerateSafePrime(bits uint) (*big.Int, error) {
if bits < 3 {
return nil, fmt.Errorf("safe prime size must be at least 3-bits")
}
var p *big.Int
var err error
checks := int(math.Max(float64(bits)/16, 8))
for {
// rand.Prime throws an error if bits < 2
// -1 so the Sophie-Germain prime is 1023 bits
// and the Safe prime is 1024
p, err = rand.Prime(rand.Reader, int(bits)-1)
if err != nil {
return nil, err
}
p.Add(p.Lsh(p, 1), One)
if p.ProbablyPrime(checks) {
break
}
}
return p, nil
}
+114
View File
@@ -0,0 +1,114 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package protocol
import (
"encoding/base64"
"encoding/json"
"fmt"
)
var (
ErrNotInitialized = fmt.Errorf("object has not been initialized")
ErrProtocolFinished = fmt.Errorf("the protocol has finished")
)
const (
// Dkls18Dkg specifies the DKG protocol of the DKLs18 potocol.
Dkls18Dkg = "DKLs18-DKG"
// Dkls18Sign specifies the DKG protocol of the DKLs18 potocol.
Dkls18Sign = "DKLs18-Sign"
// Dkls18Refresh specifies the DKG protocol of the DKLs18 potocol.
Dkls18Refresh = "DKLs18-Refresh"
// versions will increment in 100 intervals, to leave room for adding other versions in between them if it is
// ever needed in the future.
// Version0 is version 0!
Version0 = 100
// Version1 is version 2!
Version1 = 200
)
// Message provides serializers and deserializer for the inputs and outputs of each step of the protocol.
// Moreover, it adds some metadata and versioning around the serialized data.
type Message struct {
Payloads map[string][]byte `json:"payloads"`
Metadata map[string]string `json:"metadata"`
Protocol string `json:"protocol"`
Version uint `json:"version"`
}
// EncodeMessage encodes the message to a string.
func EncodeMessage(m *Message) (string, error) {
bz, err := json.Marshal(m)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(bz), nil
}
// DecodeMessage decodes the message from a string.
func DecodeMessage(s string) (*Message, error) {
bz, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return nil, err
}
var m Message
if err := json.Unmarshal(bz, &m); err != nil {
return nil, err
}
return &m, nil
}
// MarshalJSON marshals the message to JSON.
func (m *Message) MarshalJSON() ([]byte, error) {
type Alias Message // Use type alias to avoid infinite recursion
return json.Marshal(&struct {
*Alias
}{
Alias: (*Alias)(m),
})
}
// UnmarshalJSON unmarshals the message from JSON.
func (m *Message) UnmarshalJSON(data []byte) error {
var obj map[string]interface{}
if err := json.Unmarshal(data, &obj); err != nil {
return err
}
m.Payloads = make(map[string][]byte)
m.Metadata = make(map[string]string)
for k, v := range obj {
switch k {
case "payloads":
m.Payloads = v.(map[string][]byte)
case "metadata":
m.Metadata = v.(map[string]string)
case "protocol":
m.Protocol = v.(string)
case "version":
m.Version = uint(v.(float64))
}
}
return nil
}
// Iterator an interface for the DKLs18 protocols that follows the iterator pattern.
type Iterator interface {
// Next runs the next round of the protocol.
// Returns `ErrProtocolFinished` when protocol has completed.
Next(input *Message) (*Message, error)
// Result returns the final result, if any, of the completed protocol.
// Returns nil if the protocol has not yet terminated.
// Returns an error if an error was encountered during protocol execution.
Result(version uint) (*Message, error)
}