No commit suggestions generated

This commit is contained in:
Prad Nukala
2025-10-09 15:10:39 -04:00
commit a934caa7d3
323 changed files with 98121 additions and 0 deletions
+283
View File
@@ -0,0 +1,283 @@
# Crypto Module Makefile
# Provides targets for testing all cryptographic packages individually
.PHONY: all test test-verbose clean help
.PHONY: test-accumulator test-bulletproof test-core test-daed test-dkg test-ecies
.PHONY: test-internal test-keys test-mpc test-ot test-paillier test-sharing
.PHONY: test-signatures test-subtle test-tecdsa test-ted25519 test-zkp
.PHONY: test-core-curves test-core-native test-dkg-frost test-dkg-gennaro test-dkg-gennaro2p
.PHONY: test-ot-base test-ot-extension test-sharing-v1 test-signatures-bbs test-signatures-bls
.PHONY: test-signatures-common test-signatures-schnorr test-tecdsa-dklsv1 test-ted25519-frost
.PHONY: test-ted25519-ted25519 test-zkp-schnorr
.PHONY: bench bench-verbose coverage coverage-html
# Default target
all: test
tidy:
@go mod tidy
@go mod download
# Test all packages
test: tidy
@gum log --level info "Running tests for all crypto packages..."
@go test ./... -v
# Test all packages with verbose output
test-verbose: tidy
@gum log --level info "Running verbose tests for all crypto packages..."
@go test ./... -v -count=1
# Help target
help:
@gum log --level info "Crypto Module Makefile"
@gum log --level info ""
@gum log --level info "Available targets:"
@gum log --level info " all - Run all tests (default)"
@gum log --level info " test - Run all tests"
@gum log --level info " test-verbose - Run all tests with verbose output"
@gum log --level info ""
@gum log --level info "Individual package tests:"
@gum log --level info " test-accumulator - Test accumulator package"
@gum log --level info " test-bulletproof - Test bulletproof package"
@gum log --level info " test-core - Test core package"
@gum log --level info " test-daed - Test daed package"
@gum log --level info " test-dkg - Test dkg package"
@gum log --level info " test-ecies - Test ecies package"
@gum log --level info " test-internal - Test internal package"
@gum log --level info " test-keys - Test keys package"
@gum log --level info " test-mpc - Test mpc package"
@gum log --level info " test-ot - Test ot package"
@gum log --level info " test-paillier - Test paillier package"
@gum log --level info " test-sharing - Test sharing package"
@gum log --level info " test-signatures - Test signatures package"
@gum log --level info " test-subtle - Test subtle package"
@gum log --level info " test-tecdsa - Test tecdsa package"
@gum log --level info " test-ted25519 - Test ted25519 package"
@gum log --level info " test-zkp - Test zkp package"
@gum log --level info ""
@gum log --level info "Subpackage tests:"
@gum log --level info " test-core-curves - Test core/curves package"
@gum log --level info " test-core-native - Test core/curves/native package"
@gum log --level info " test-dkg-frost - Test dkg/frost package"
@gum log --level info " test-dkg-gennaro - Test dkg/gennaro package"
@gum log --level info " test-dkg-gennaro2p - Test dkg/gennaro2p package"
@gum log --level info " test-ot-base - Test ot/base package"
@gum log --level info " test-ot-extension - Test ot/extension package"
@gum log --level info " test-sharing-v1 - Test sharing/v1 package"
@gum log --level info " test-signatures-bbs - Test signatures/bbs package"
@gum log --level info " test-signatures-bls - Test signatures/bls package"
@gum log --level info " test-signatures-common - Test signatures/common package"
@gum log --level info " test-signatures-schnorr - Test signatures/schnorr package"
@gum log --level info " test-tecdsa-dklsv1 - Test tecdsa/dklsv1 package"
@gum log --level info " test-ted25519-frost - Test ted25519/frost package"
@gum log --level info " test-ted25519-ted25519 - Test ted25519/ted25519 package"
@gum log --level info " test-zkp-schnorr - Test zkp/schnorr package"
@gum log --level info ""
@gum log --level info "Performance and coverage:"
@gum log --level info " bench - Run benchmarks"
@gum log --level info " bench-verbose - Run benchmarks with verbose output"
@gum log --level info " coverage - Generate test coverage report"
@gum log --level info " coverage-html - Generate HTML test coverage report"
# Top-level package tests
test-accumulator:
@gum log --level info "Testing accumulator package..."
@go test ./accumulator -v
test-bulletproof:
@gum log --level info "Testing bulletproof package..."
@go test ./bulletproof -v
test-core:
@gum log --level info "Testing core package..."
@go test ./core -v
test-daed:
@gum log --level info "Testing daed package..."
@go test ./daed -v
test-dkg:
@gum log --level info "Testing dkg package..."
@go test ./dkg/... -v
test-ecies:
@gum log --level info "Testing ecies package..."
@go test ./ecies -v
test-internal:
@gum log --level info "Testing internal package..."
@go test ./internal -v
test-keys:
@gum log --level info "Testing keys package..."
@go test ./keys -v
test-mpc:
@gum log --level info "Testing mpc package..."
@go test ./mpc -v
test-ot:
@gum log --level info "Testing ot package..."
@go test ./ot/... -v
test-paillier:
@gum log --level info "Testing paillier package..."
@go test ./paillier -v
test-sharing:
@gum log --level info "Testing sharing package..."
@go test ./sharing/... -v
test-signatures:
@gum log --level info "Testing signatures package..."
@go test ./signatures/... -v
test-subtle:
@gum log --level info "Testing subtle package..."
@go test ./subtle -v
test-tecdsa:
@gum log --level info "Testing tecdsa package..."
@go test ./tecdsa/... -v
test-ted25519:
@gum log --level info "Testing ted25519 package..."
@go test ./ted25519/... -v
test-zkp:
@gum log --level info "Testing zkp package..."
@go test ./zkp/... -v
# Subpackage tests
test-core-curves:
@gum log --level info "Testing core/curves package..."
@go test ./core/curves -v
test-core-native:
@gum log --level info "Testing core/curves/native package..."
@go test ./core/curves/native/... -v
test-dkg-frost:
@gum log --level info "Testing dkg/frost package..."
@go test ./dkg/frost -v
test-dkg-gennaro:
@gum log --level info "Testing dkg/gennaro package..."
@go test ./dkg/gennaro -v
test-dkg-gennaro2p:
@gum log --level info "Testing dkg/gennaro2p package..."
@go test ./dkg/gennaro2p -v
test-ot-base:
@gum log --level info "Testing ot/base package..."
@go test ./ot/base/... -v
test-ot-extension:
@gum log --level info "Testing ot/extension package..."
@go test ./ot/extension/... -v
test-sharing-v1:
@gum log --level info "Testing sharing/v1 package..."
@go test ./sharing/v1 -v
test-signatures-bbs:
@gum log --level info "Testing signatures/bbs package..."
@go test ./signatures/bbs -v
test-signatures-bls:
@gum log --level info "Testing signatures/bls package..."
@go test ./signatures/bls/... -v
test-signatures-common:
@gum log --level info "Testing signatures/common package..."
@go test ./signatures/common -v
test-signatures-schnorr:
@gum log --level info "Testing signatures/schnorr package..."
@go test ./signatures/schnorr/... -v
test-tecdsa-dklsv1:
@gum log --level info "Testing tecdsa/dklsv1 package..."
@go test ./tecdsa/dklsv1/... -v
test-ted25519-frost:
@gum log --level info "Testing ted25519/frost package..."
@go test ./ted25519/frost -v
test-ted25519-ted25519:
@gum log --level info "Testing ted25519/ted25519 package..."
@go test ./ted25519/ted25519 -v
test-zkp-schnorr:
@gum log --level info "Testing zkp/schnorr package..."
@go test ./zkp/schnorr -v
# Performance testing
bench:
@gum log --level info "Running benchmarks for all packages..."
@go test ./... -bench=. -run=^$
bench-verbose:
@gum log --level info "Running verbose benchmarks for all packages..."
@go test ./... -bench=. -benchmem -run=^$ -v
# Coverage testing
coverage:
@gum log --level info "Generating test coverage report..."
@go test ./... -coverprofile=coverage.out
@go tool cover -func=coverage.out
coverage-html:
@gum log --level info "Generating HTML test coverage report..."
@go test ./... -coverprofile=coverage.out
@go tool cover -html=coverage.out -o coverage.html
@gum log --level info "Coverage report generated: coverage.html"
# Build verification
build:
@gum log --level info "Building all packages to verify compilation..."
@go build ./...
# Module maintenance
mod-tidy:
@gum log --level info "Tidying go.mod..."
@go mod tidy
mod-verify:
@gum log --level info "Verifying go.mod..."
@go mod verify
mod-download:
@gum log --level info "Downloading dependencies..."
@go mod download
# Security scanning (if staticcheck is available)
lint:
@gum log --level info "Running static analysis..."
@if command -v staticcheck >/dev/null 2>&1; then \
staticcheck ./...; \
else \
gum log --level warn "staticcheck not installed. Install with: go install honnef.co/go/tools/cmd/staticcheck@latest"; \
go vet ./...; \
fi
# Format code
fmt:
@gum log --level info "Formatting code..."
@go fmt ./...
# Check for formatting issues
fmt-check:
@gum log --level info "Checking code formatting..."
@test -z "$$(go fmt ./...)"
# Run all quality checks
check: fmt-check lint test
@gum log --level info "✅ All quality checks passed!"
# Development workflow
dev: clean mod-tidy fmt lint test
@gum log --level info "✅ Development workflow completed successfully!"
+27
View File
@@ -0,0 +1,27 @@
# Sonr Crypto
Sonr Crypto is a collection of cryptographic primitives that are used by Sonr.
## Packages
- [Accumulator](./accumulator): Accumulator is a cryptographic accumulator that allows for efficient verification of large sets of data.
- [Bulletproof](./bulletproof): Bulletproof is a zero-knowledge proof system that allows for efficient verification of large sets of data.
- [Core](./core): Core is a collection of cryptographic primitives that are used by Sonr.
- [Daed](./daed): Daed is a distributed key generation protocol that allows for efficient verification of large sets of data.
- [Dkg](./dkg): Dkg is a distributed key generation protocol that allows for efficient verification of large sets of data.
- [Ecies](./ecies): Ecies is a symmetric encryption algorithm that allows for efficient verification of large sets of data.
- [Keys](./keys): Keys is a collection of cryptographic primitives that are used by Sonr.
- [Mpc](./mpc): Mpc is a collection of cryptographic primitives that are used by Sonr.
- [Ot](./ot): Ot is a collection of cryptographic primitives that are used by Sonr.
- [Paillier](./paillier): Paillier is a cryptographic algorithm that allows for efficient verification of large sets of data.
- [Sharing](./sharing): Sharing is a collection of cryptographic primitives that are used by Sonr.
- [Signatures](./signatures): Signatures is a collection of cryptographic primitives that are used by Sonr.
- [Subtle](./subtle): Subtle is a collection of cryptographic primitives that are used by Sonr.
- [Tecdsa](./tecdsa): Tecdsa is a collection of cryptographic primitives that are used by Sonr.
- [Ted25519](./ted25519): Ted25519 is a collection of cryptographic primitives that are used by Sonr.
- [Ucan](./ucan): Ucan is a collection of cryptographic primitives that are used by Sonr.
- [Zkp](./zkp): Zkp is a collection of cryptographic primitives that are used by Sonr.
## License
This project is licensed under the [Apache 2.0 License](./LICENSE).
+179
View File
@@ -0,0 +1,179 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// Package accumulator implements the cryptographic accumulator as described in https://eprint.iacr.org/2020/777.pdf
// It also implements the zero knowledge proof of knowledge protocol
// described in section 7 of the paper.
// Note: the paper only describes for non-membership witness case, but we don't
// use non-membership witness. We only implement the membership witness case.
package accumulator
import (
"fmt"
"git.sr.ht/~sircmpwn/go-bare"
"github.com/sonr-io/sonr/crypto/core/curves"
)
type structMarshal struct {
Curve string `bare:"curve"`
Value []byte `bare:"value"`
}
type Element curves.Scalar
// Coefficient is a point
type Coefficient curves.Point
// Accumulator is a point
type Accumulator struct {
value curves.Point
}
// New creates a new accumulator.
func (acc *Accumulator) New(curve *curves.PairingCurve) (*Accumulator, error) {
// If we need to support non-membership witness, we need to implement Accumulator Initialization
// as described in section 6 of <https://eprint.iacr.org/2020/777.pdf>
// for now we don't need non-membership witness
// i.e., it computes V0 = prod(y + α) * P, y ∈ Y_V0, P is a generator of G1. Since we do not use non-membership witness
// we just set the initial accumulator a G1 generator.
acc.value = curve.Scalar.Point().Generator()
return acc, nil
}
// WithElements initializes a new accumulator prefilled with entries
// Each member is assumed to be hashed
// V = prod(y + α) * V0, for all y∈ Y_V
func (acc *Accumulator) WithElements(
curve *curves.PairingCurve,
key *SecretKey,
m []Element,
) (*Accumulator, error) {
_, err := acc.New(curve)
if err != nil {
return nil, err
}
y, err := key.BatchAdditions(m)
if err != nil {
return nil, err
}
acc.value = acc.value.Mul(y)
return acc, nil
}
// AddElements accumulates a set of elements into the accumulator.
func (acc *Accumulator) AddElements(key *SecretKey, m []Element) (*Accumulator, error) {
if acc.value == nil || key.value == nil {
return nil, fmt.Errorf("accumulator and secret key should not be nil")
}
y, err := key.BatchAdditions(m)
if err != nil {
return nil, err
}
acc.value = acc.value.Mul(y)
return acc, nil
}
// Add accumulates a single element into the accumulator
// V' = (y + alpha) * V
func (acc *Accumulator) Add(key *SecretKey, e Element) (*Accumulator, error) {
if acc.value == nil || acc.value.IsIdentity() || key.value == nil || e == nil {
return nil, fmt.Errorf("accumulator, secret key and element should not be nil")
}
y := e.Add(key.value) // y + alpha
acc.value = acc.value.Mul(y)
return acc, nil
}
// Remove removes a single element from accumulator if it exists
// V' = 1/(y+alpha) * V
func (acc *Accumulator) Remove(key *SecretKey, e Element) (*Accumulator, error) {
if acc.value == nil || acc.value.IsIdentity() || key.value == nil || e == nil {
return nil, fmt.Errorf("accumulator, secret key and element should not be nil")
}
y := e.Add(key.value) // y + alpha
y, err := y.Invert() // 1/(y+alpha)
if err != nil {
return nil, err
}
acc.value = acc.value.Mul(y)
return acc, nil
}
// Update performs a batch addition and deletion as described on page 7, section 3 in
// https://eprint.iacr.org/2020/777.pdf
func (acc *Accumulator) Update(
key *SecretKey,
additions []Element,
deletions []Element,
) (*Accumulator, []Coefficient, error) {
if acc.value == nil || acc.value.IsIdentity() || key.value == nil {
return nil, nil, fmt.Errorf("accumulator and secret key should not be nil")
}
// Compute dA(-alpha) = prod(y + alpha), y in the set of A ⊆ ACC-Y_V
a, err := key.BatchAdditions(additions)
if err != nil {
return nil, nil, err
}
// Compute dD(-alpha) = 1/prod(y + alpha), y in the set of D ⊆ Y_V
d, err := key.BatchDeletions(deletions)
if err != nil {
return nil, nil, err
}
// dA(-alpha)/dD(-alpha)
div := a.Mul(d)
newAcc := acc.value.Mul(div)
// build an array of coefficients
elements, err := key.CreateCoefficients(additions, deletions)
if err != nil {
return nil, nil, err
}
coefficients := make([]Coefficient, len(elements))
for i := range elements {
coefficients[i] = acc.value.Mul(elements[i])
}
acc.value = newAcc
return acc, coefficients, nil
}
// MarshalBinary converts Accumulator to bytes
func (acc Accumulator) MarshalBinary() ([]byte, error) {
if acc.value == nil {
return nil, fmt.Errorf("accumulator cannot be nil")
}
tv := &structMarshal{
Value: acc.value.ToAffineCompressed(),
Curve: acc.value.CurveName(),
}
return bare.Marshal(tv)
}
// UnmarshalBinary sets Accumulator from bytes
func (acc *Accumulator) UnmarshalBinary(data []byte) error {
tv := new(structMarshal)
err := bare.Unmarshal(data, tv)
if err != nil {
return err
}
curve := curves.GetCurveByName(tv.Curve)
if curve == nil {
return fmt.Errorf("invalid curve")
}
value, err := curve.NewIdentityPoint().FromAffineCompressed(tv.Value)
if err != nil {
return err
}
acc.value = value
return nil
}
+221
View File
@@ -0,0 +1,221 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package accumulator
import (
"encoding/hex"
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestNewAccumulator100(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
var seed [32]byte
key, err := new(SecretKey).New(curve, seed[:])
require.NoError(t, err)
require.NotNil(t, key)
acc, err := new(Accumulator).New(curve)
require.NoError(t, err)
accBz, err := acc.MarshalBinary()
require.NoError(t, err)
fmt.Println(accBz)
fmt.Println(len(accBz))
fmt.Println(hex.EncodeToString(accBz))
fmt.Println(len(hex.EncodeToString(accBz)))
require.Equal(t, 60, len(accBz), "Marshalled accumulator should be 60 bytes")
require.Equal(
t,
120,
len(hex.EncodeToString(accBz)),
"Hex-encoded accumulator should be 120 characters",
)
require.NotNil(t, acc)
require.Equal(t, acc.value.ToAffineCompressed(), curve.PointG1.Generator().ToAffineCompressed())
}
func TestNewAccumulator10K(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
var seed [32]byte
key, err := new(SecretKey).New(curve, seed[:])
require.NoError(t, err)
require.NotNil(t, key)
acc, err := new(Accumulator).New(curve)
require.NoError(t, err)
require.NotNil(t, acc)
require.Equal(t, acc.value.ToAffineCompressed(), curve.PointG1.Generator().ToAffineCompressed())
}
func TestNewAccumulator10M(t *testing.T) {
// Initiating 10M values takes time
if testing.Short() {
t.Skip("skipping test in short mode.")
}
curve := curves.BLS12381(&curves.PointBls12381G1{})
var seed [32]byte
key, err := new(SecretKey).New(curve, seed[:])
require.NoError(t, err)
require.NotNil(t, key)
acc, err := new(Accumulator).New(curve)
require.NoError(t, err)
require.NotNil(t, acc)
require.Equal(t, acc.value.ToAffineCompressed(), curve.PointG1.Generator().ToAffineCompressed())
}
func TestWithElements(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
var seed [32]byte
key, _ := new(SecretKey).New(curve, seed[:])
element1 := curve.Scalar.Hash([]byte("value1"))
element2 := curve.Scalar.Hash([]byte("value2"))
elements := []Element{element1, element2}
newAcc, err := new(Accumulator).WithElements(curve, key, elements)
require.NoError(t, err)
require.NotNil(t, newAcc)
require.NotEqual(
t,
newAcc.value.ToAffineCompressed(),
curve.PointG1.Identity().ToAffineCompressed(),
)
require.NotEqual(
t,
newAcc.value.ToAffineCompressed(),
curve.PointG1.Generator().ToAffineCompressed(),
)
_, _ = newAcc.Remove(key, element1)
_, _ = newAcc.Remove(key, element2)
require.Equal(
t,
newAcc.value.ToAffineCompressed(),
curve.PointG1.Generator().ToAffineCompressed(),
)
}
func TestAdd(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
var seed [32]byte
key, err := new(SecretKey).New(curve, seed[:])
require.NoError(t, err)
require.NotNil(t, key)
acc := &Accumulator{curve.PointG1.Generator()}
_, _ = acc.New(curve)
require.NoError(t, err)
require.NotNil(t, acc)
element := curve.Scalar.Hash([]byte("value1"))
require.NoError(t, err)
require.NotNil(t, element)
_, _ = acc.Add(key, element)
require.NotEqual(
t,
acc.value.ToAffineCompressed(),
curve.PointG1.Generator().ToAffineCompressed(),
)
}
func TestRemove(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
var seed [32]byte
key, err := new(SecretKey).New(curve, seed[:])
require.NoError(t, err)
require.NotNil(t, key)
acc, err := new(Accumulator).New(curve)
require.NoError(t, err)
require.NotNil(t, acc)
require.Equal(t, acc.value.ToAffineCompressed(), curve.PointG1.Generator().ToAffineCompressed())
element := curve.Scalar.Hash([]byte("value1"))
require.NoError(t, err)
require.NotNil(t, element)
// add element
_, _ = acc.Add(key, element)
require.NotEqual(
t,
acc.value.ToAffineCompressed(),
curve.PointG1.Generator().ToAffineCompressed(),
)
// remove element
acc, err = acc.Remove(key, element)
require.NoError(t, err)
require.Equal(t, acc.value.ToAffineCompressed(), curve.PointG1.Generator().ToAffineCompressed())
}
func TestAddElements(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
var seed [32]byte
key, err := new(SecretKey).New(curve, seed[:])
require.NoError(t, err)
require.NotNil(t, key)
acc := &Accumulator{curve.PointG1.Generator()}
_, _ = acc.New(curve)
require.NoError(t, err)
require.NotNil(t, acc)
require.Equal(t, acc.value.ToAffineCompressed(), curve.PointG1.Generator().ToAffineCompressed())
element1 := curve.Scalar.Hash([]byte("value1"))
element2 := curve.Scalar.Hash([]byte("value2"))
element3 := curve.Scalar.Hash([]byte("value3"))
elements := []Element{element1, element2, element3}
acc, err = acc.AddElements(key, elements)
require.NoError(t, err)
require.NotEqual(
t,
acc.value.ToAffineCompressed(),
curve.PointG1.Generator().ToAffineCompressed(),
)
}
func TestAccumulatorMarshal(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
point := curve.PointG1.Generator().Mul(curve.Scalar.New(2))
data, err := Accumulator{point}.MarshalBinary()
require.NoError(t, err)
require.NotNil(t, data)
// element cannot be empty
_, err = Accumulator{}.MarshalBinary()
require.Error(t, err)
e := &Accumulator{curve.PointG1.Generator()}
_ = e.UnmarshalBinary(data)
require.True(t, e.value.Equal(point))
}
func TestUpdate(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
var seed [32]byte
key, err := new(SecretKey).New(curve, seed[:])
require.NoError(t, err)
require.NotNil(t, key)
acc, err := new(Accumulator).New(curve)
require.NoError(t, err)
require.NotNil(t, acc)
require.Equal(t, acc.value.ToAffineCompressed(), curve.PointG1.Generator().ToAffineCompressed())
element1 := curve.Scalar.Hash([]byte("value1"))
element2 := curve.Scalar.Hash([]byte("value2"))
element3 := curve.Scalar.Hash([]byte("value3"))
elements := []Element{element1, element2, element3}
acc, _, err = acc.Update(key, elements, nil)
require.NoError(t, err)
require.NotEqual(
t,
acc.value.ToAffineCompressed(),
curve.PointG1.Generator().ToAffineCompressed(),
)
acc, _, err = acc.Update(key, nil, elements)
require.NoError(t, err)
require.Equal(t, acc.value.ToAffineCompressed(), curve.PointG1.Generator().ToAffineCompressed())
}
+247
View File
@@ -0,0 +1,247 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package accumulator
import (
"errors"
"fmt"
"git.sr.ht/~sircmpwn/go-bare"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// SecretKey is the secret alpha only held by the accumulator manager.
type SecretKey struct {
value curves.Scalar
}
// New creates a new secret key from the seed.
func (sk *SecretKey) New(curve *curves.PairingCurve, seed []byte) (*SecretKey, error) {
sk.value = curve.Scalar.Hash(seed)
return sk, nil
}
// GetPublicKey creates a public key from SecretKey sk
func (sk SecretKey) GetPublicKey(curve *curves.PairingCurve) (*PublicKey, error) {
if sk.value == nil || curve == nil {
return nil, fmt.Errorf("curve and sk value cannot be nil")
}
value := curve.Scalar.Point().(curves.PairingPoint).OtherGroup().Generator().Mul(sk.value)
return &PublicKey{value.(curves.PairingPoint)}, nil
}
// MarshalBinary converts SecretKey to bytes
func (sk SecretKey) MarshalBinary() ([]byte, error) {
if sk.value == nil {
return nil, fmt.Errorf("sk cannot be empty")
}
tv := &structMarshal{
Value: sk.value.Bytes(),
Curve: sk.value.Point().CurveName(),
}
return bare.Marshal(tv)
}
// UnmarshalBinary sets SecretKey from bytes
func (sk *SecretKey) UnmarshalBinary(data []byte) error {
tv := new(structMarshal)
err := bare.Unmarshal(data, tv)
if err != nil {
return err
}
curve := curves.GetCurveByName(tv.Curve)
if curve == nil {
return fmt.Errorf("invalid curve")
}
value, err := curve.NewScalar().SetBytes(tv.Value)
if err != nil {
return err
}
sk.value = value
return nil
}
// BatchAdditions computes product(y + sk) for y in additions and output the product
func (sk SecretKey) BatchAdditions(additions []Element) (Element, error) {
if sk.value == nil {
return nil, fmt.Errorf("secret key cannot be empty")
}
mul := sk.value.One()
for i := 0; i < len(additions); i++ {
if additions[i] == nil {
return nil, fmt.Errorf("some element in additions is nil")
}
// y + alpha
temp := additions[i].Add(sk.value)
// prod(y + alpha)
mul = mul.Mul(temp)
}
return mul, nil
}
// BatchDeletions computes 1/product(y + sk) for y in deletions and output it
func (sk SecretKey) BatchDeletions(deletions []Element) (Element, error) {
v, err := sk.BatchAdditions(deletions)
if err != nil {
return nil, err
}
y, err := v.Invert()
if err != nil {
return nil, err
}
return y, nil
}
// CreateCoefficients creates the Batch Polynomial coefficients
// See page 7 of https://eprint.iacr.org/2020/777.pdf
func (sk SecretKey) CreateCoefficients(
additions []Element,
deletions []Element,
) ([]Element, error) {
if sk.value == nil {
return nil, fmt.Errorf("secret key should not be nil")
}
// vD(x) = ∑^{m}_{s=1}{ ∏ 1..s {yD_i + alpha}^-1 ∏ 1 ..s-1 {yD_j - x}
one := sk.value.One()
m1 := one.Neg() // m1 is -1
vD := make(polynomial, 0, len(deletions))
for s := 0; s < len(deletions); s++ {
// ∏ 1..s (yD_i + alpha)^-1
c, err := sk.BatchDeletions(deletions[0 : s+1])
if err != nil {
return nil, fmt.Errorf("error in sk batchDeletions")
}
poly := make(polynomial, 1, s+2)
poly[0] = one
// ∏ 1..(s-1) (yD_j - x)
for j := 0; j < s; j++ {
t := make(polynomial, 2)
// yD_j
t[0] = deletions[j]
// -x
t[1] = m1
// polynomial multiplication (yD_1-x) * (yD_2 - x) ...
poly, err = poly.Mul(t)
if err != nil {
return nil, err
}
}
poly, err = poly.MulScalar(c)
if err != nil {
return nil, err
}
vD, err = vD.Add(poly)
if err != nil {
return nil, err
}
}
// vD(x) * ∏ 1..n (yA_i + alpha)
bAdd, err := sk.BatchAdditions(additions)
if err != nil {
return nil, fmt.Errorf("error in sk batchAdditions")
}
vD, err = vD.MulScalar(bAdd)
if err != nil {
return nil, err
}
// vA(x) = ∑^n_{s=1}{ ∏ 1..s-1 {yA_i + alpha} ∏ s+1..n {yA_j - x} }
vA := make(polynomial, 0, len(additions))
for s := range additions {
// ∏ 1..s-1 {yA_i + alpha}
var c Element
if s == 0 {
c = one
} else {
c, err = sk.BatchAdditions(additions[0:s])
if err != nil {
return nil, err
}
}
poly := make(polynomial, 1, s+2)
poly[0] = one
// ∏ s+1..n {yA_j - x}
for j := s + 1; j < len(additions); j++ {
t := make(polynomial, 2)
t[0] = additions[j]
t[1] = m1
// polynomial multiplication (yA_1-x) * (yA_2 - x) ...
poly, err = poly.Mul(t)
if err != nil {
return nil, err
}
}
poly, err = poly.MulScalar(c)
if err != nil {
return nil, err
}
vA, err = vA.Add(poly)
if err != nil {
return nil, err
}
}
// vA - vD
vA, err = vA.Sub(vD)
if err != nil {
return nil, err
}
result := make([]Element, len(vA))
for i := 0; i < len(vA); i++ {
result[i] = vA[i]
}
return result, nil
}
// PublicKey is the public key of accumulator, it should be sk * generator of G2
type PublicKey struct {
value curves.PairingPoint
}
// MarshalBinary converts PublicKey to bytes
func (pk PublicKey) MarshalBinary() ([]byte, error) {
if pk.value == nil {
return nil, fmt.Errorf("public key cannot be nil")
}
tv := &structMarshal{
Value: pk.value.ToAffineCompressed(),
Curve: pk.value.CurveName(),
}
return bare.Marshal(tv)
}
// UnmarshalBinary sets PublicKey from bytes
func (pk *PublicKey) UnmarshalBinary(data []byte) error {
tv := new(structMarshal)
err := bare.Unmarshal(data, tv)
if err != nil {
return err
}
curve := curves.GetPairingCurveByName(tv.Curve)
if curve == nil {
return fmt.Errorf("invalid curve")
}
value, err := curve.NewScalar().Point().FromAffineCompressed(tv.Value)
if err != nil {
return err
}
var ok bool
pk.value, ok = value.(curves.PairingPoint)
if !ok {
return errors.New("can't convert to PairingPoint")
}
return nil
}
+88
View File
@@ -0,0 +1,88 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package accumulator
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestSecretKeyMarshal(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
data, err := SecretKey{curve.Scalar.One()}.MarshalBinary()
require.NoError(t, err)
require.NotNil(t, data)
e := &SecretKey{curve.Scalar.New(2)}
err = e.UnmarshalBinary(data)
require.NoError(t, err)
require.Equal(t, e.value.Bytes(), curve.Scalar.One().Bytes())
// element cannot be empty
_, err = SecretKey{}.MarshalBinary()
require.Error(t, err)
}
func TestPublicKeyMarshal(t *testing.T) {
// Actually test both toBytes() and from()
curve := curves.BLS12381(&curves.PointBls12381G1{})
sk := &SecretKey{curve.Scalar.New(3)}
pk, _ := sk.GetPublicKey(curve)
pkBytes, err := pk.MarshalBinary()
require.NoError(t, err)
require.NotNil(t, pkBytes)
pk2 := &PublicKey{}
err = pk2.UnmarshalBinary(pkBytes)
require.NoError(t, err)
require.True(t, pk.value.Equal(pk2.value))
}
func TestBatch(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
var seed [32]byte
sk, _ := new(SecretKey).New(curve, seed[:])
element1 := curve.Scalar.Hash([]byte("value1"))
element2 := curve.Scalar.Hash([]byte("value2"))
elements := []Element{element1, element2}
add, err := sk.BatchAdditions(elements)
require.NoError(t, err)
require.NotNil(t, add)
del, err := sk.BatchDeletions(elements)
require.NoError(t, err)
require.NotNil(t, del)
result := add.Mul(del)
require.Equal(t, result, curve.Scalar.One())
g1 := curve.PointG1.Generator()
acc := g1.Mul(add)
require.NotEqual(t, acc, g1)
acc = acc.Mul(del)
require.Equal(t, acc.ToAffineCompressed(), g1.ToAffineCompressed())
acc2 := g1.Mul(result)
require.True(t, acc2.Equal(g1))
}
func TestCoefficient(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
sk, _ := new(SecretKey).New(curve, []byte("1234567890"))
element1 := curve.Scalar.Hash([]byte("value1"))
element2 := curve.Scalar.Hash([]byte("value2"))
element3 := curve.Scalar.Hash([]byte("value3"))
element4 := curve.Scalar.Hash([]byte("value4"))
element5 := curve.Scalar.Hash([]byte("value5"))
elements := []Element{element1, element2, element3, element4, element5}
coefficients, err := sk.CreateCoefficients(elements[0:2], elements[2:5])
require.NoError(t, err)
require.Equal(t, len(coefficients), 3)
}
+204
View File
@@ -0,0 +1,204 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package accumulator
import (
"fmt"
"math"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// dad constructs two polynomials - dA(x) and dD(x)
// dA(y) = prod(y_A,t - y), t = 1...n
// dD(y) = prod(y_D,t - y), t = 1...n
func dad(values []Element, y Element) (Element, error) {
if values == nil || y == nil {
return nil, fmt.Errorf("curve, values or y should not be nil")
}
for _, value := range values {
if value == nil {
return nil, fmt.Errorf("some element is nil")
}
}
result := y.One()
if len(values) == 1 {
a := values[0]
result = a.Sub(y)
} else {
for i := range values {
temp := values[i].Sub(y)
result = result.Mul(temp)
}
}
return result, nil
}
type polynomialPoint []curves.Point
// evaluate evaluates a PolynomialG1 on input x.
func (p polynomialPoint) evaluate(x curves.Scalar) (curves.Point, error) {
if p == nil {
return nil, fmt.Errorf("p cannot be empty")
}
for i := 0; i < len(p); i++ {
if p[i] == nil {
return nil, fmt.Errorf("some coefficient in p is nil")
}
}
pp := x
res := p[0]
for i := 1; i < len(p); i++ {
r := p[i].Mul(pp)
res = res.Add(r)
pp = pp.Mul(x)
}
return res, nil
}
// Add adds two PolynomialG1
func (p polynomialPoint) Add(rhs polynomialPoint) (polynomialPoint, error) {
maxLen := int(math.Max(float64(len(p)), float64(len(rhs))))
result := make(polynomialPoint, maxLen)
for i, c := range p {
if c == nil {
return nil, fmt.Errorf("invalid coefficient at %d", i)
}
result[i] = c.Add(c.Identity())
}
for i, c := range rhs {
if c == nil {
return nil, fmt.Errorf("invalid coefficient at %d", i)
}
if result[i] == nil {
result[i] = c.Add(c.Identity())
} else {
result[i] = result[i].Add(c)
}
}
return result, nil
}
// Mul for PolynomialG1 computes rhs * p, p is a polynomial, rhs is a value
func (p polynomialPoint) Mul(rhs curves.Scalar) (polynomialPoint, error) {
result := make(polynomialPoint, len(p))
for i, c := range p {
if c == nil {
return nil, fmt.Errorf("invalid coefficient at %d", i)
}
result[i] = c.Mul(rhs)
}
return result, nil
}
type polynomial []curves.Scalar
// Add adds two polynomials
func (p polynomial) Add(rhs polynomial) (polynomial, error) {
maxLen := int(math.Max(float64(len(p)), float64(len(rhs))))
result := make([]curves.Scalar, maxLen)
for i, c := range p {
if c == nil {
return nil, fmt.Errorf("invalid coefficient at %d", i)
}
result[i] = c.Clone()
}
for i, c := range rhs {
if c == nil {
return nil, fmt.Errorf("invalid coefficient at %d", i)
}
if result[i] == nil {
result[i] = c.Clone()
} else {
result[i] = result[i].Add(c)
}
}
return result, nil
}
// Sub computes p-rhs and returns
func (p polynomial) Sub(rhs polynomial) (polynomial, error) {
maxLen := int(math.Max(float64(len(p)), float64(len(rhs))))
result := make([]curves.Scalar, maxLen)
for i, c := range p {
if c == nil {
return nil, fmt.Errorf("invalid coefficient at %d", i)
}
result[i] = c.Clone()
}
for i, c := range rhs {
if c == nil {
return nil, fmt.Errorf("invalid coefficient at %d", i)
}
if result[i] == nil {
result[i] = c.Neg()
} else {
result[i] = result[i].Sub(c)
}
}
return result, nil
}
// Mul multiplies two polynomials - p * rhs
func (p polynomial) Mul(rhs polynomial) (polynomial, error) {
// Check for each coefficient that should not be nil
for i, c := range p {
if c == nil {
return nil, fmt.Errorf("coefficient in p at %d is nil", i)
}
}
for i, c := range rhs {
if c == nil {
return nil, fmt.Errorf("coefficient in rhs at %d is nil", i)
}
}
m := len(p)
n := len(rhs)
// Initialize the product polynomial
prod := make(polynomial, m+n-1)
for i := 0; i < len(prod); i++ {
prod[i] = p[0].Zero()
}
// Multiply two polynomials term by term
for i, cp := range p {
for j, cr := range rhs {
temp := cp.Mul(cr)
prod[i+j] = prod[i+j].Add(temp)
}
}
return prod, nil
}
// MulScalar computes p * rhs, where rhs is a scalar value
func (p polynomial) MulScalar(rhs curves.Scalar) (polynomial, error) {
result := make(polynomial, len(p))
for i, c := range p {
if c == nil {
return nil, fmt.Errorf("coefficient at %d is nil", i)
}
result[i] = c.Mul(rhs)
}
return result, nil
}
+404
View File
@@ -0,0 +1,404 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package accumulator
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestEvaluatePolyG1(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
poly := polynomialPoint{
curve.PointG1.Generator().Mul(curve.Scalar.New(3)),
curve.PointG1.Generator().Mul(curve.Scalar.New(2)),
curve.PointG1.Generator().Mul(curve.Scalar.New(1)),
}
output1, err := poly.evaluate(curve.Scalar.New(1))
require.NoError(t, err)
require.NotNil(t, output1)
result1 := curve.PointG1.Generator().Mul(curve.Scalar.New(6))
require.Equal(t, output1.ToAffineCompressed(), result1.ToAffineCompressed())
output2, err := poly.evaluate(curve.Scalar.New(2))
require.NoError(t, err)
require.NotNil(t, output2)
result2 := curve.PointG1.Generator().Mul(curve.Scalar.New(11))
require.Equal(t, output2.ToAffineCompressed(), result2.ToAffineCompressed())
}
func TestEvaluatePolyG1Error(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
poly := polynomialPoint{
nil,
curve.PointG1.Generator().Mul(curve.Scalar.New(2)),
curve.PointG1.Generator().Mul(curve.Scalar.New(1)),
}
_, err := poly.evaluate(curve.Scalar.New(1))
require.Error(t, err)
}
func TestAddAssignPolyG1(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
// Test polynomial with equal length
poly1 := polynomialPoint{
curve.PointG1.Generator().Mul(curve.Scalar.New(3)),
curve.PointG1.Generator().Mul(curve.Scalar.New(2)),
curve.PointG1.Generator().Mul(curve.Scalar.New(1)),
}
poly2 := polynomialPoint{
curve.PointG1.Generator().Mul(curve.Scalar.New(1)),
curve.PointG1.Generator().Mul(curve.Scalar.New(2)),
curve.PointG1.Generator().Mul(curve.Scalar.New(3)),
}
output, err := poly1.Add(poly2)
require.NoError(t, err)
require.NotNil(t, output)
result := polynomialPoint{
curve.PointG1.Generator().Mul(curve.Scalar.New(4)),
curve.PointG1.Generator().Mul(curve.Scalar.New(4)),
curve.PointG1.Generator().Mul(curve.Scalar.New(4)),
}
for i := 0; i < len(output); i++ {
require.Equal(t, output[i].ToAffineCompressed(), result[i].ToAffineCompressed())
}
// Test polynomials with unequal length
poly3 := polynomialPoint{
curve.PointG1.Generator().Mul(curve.Scalar.New(1)),
curve.PointG1.Generator().Mul(curve.Scalar.New(2)),
}
output2, err := poly1.Add(poly3)
require.NoError(t, err)
require.NotNil(t, output2)
result2 := polynomialPoint{
curve.PointG1.Generator().Mul(curve.Scalar.New(4)),
curve.PointG1.Generator().Mul(curve.Scalar.New(4)),
curve.PointG1.Generator().Mul(curve.Scalar.New(1)),
}
require.Equal(t, len(output2), len(result2))
for i := 0; i < len(output2); i++ {
require.Equal(t, output2[i].ToAffineCompressed(), result2[i].ToAffineCompressed())
}
// Test polynomial with Capacity
poly4 := make(polynomialPoint, 0, 3)
poly5, err := poly4.Add(poly1)
require.NoError(t, err)
require.Equal(t, len(poly5), len(poly1))
for i := 0; i < len(poly5); i++ {
require.Equal(t, poly5[i].ToAffineCompressed(), poly1[i].ToAffineCompressed())
}
}
func TestAddAssignPolyG1Error(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
poly1 := polynomialPoint{
nil,
curve.PointG1.Generator().Mul(curve.Scalar.New(2)),
curve.PointG1.Generator().Mul(curve.Scalar.New(1)),
}
poly2 := polynomialPoint{
curve.PointG1.Generator().Mul(curve.Scalar.New(1)),
curve.PointG1.Generator().Mul(curve.Scalar.New(2)),
curve.PointG1.Generator().Mul(curve.Scalar.New(3)),
}
output, err := poly1.Add(poly2)
require.Error(t, err)
require.Nil(t, output)
}
func TestMulAssignPolyG1(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
poly := polynomialPoint{
curve.PointG1.Generator().Mul(curve.Scalar.New(3)),
curve.PointG1.Generator().Mul(curve.Scalar.New(2)),
curve.PointG1.Generator().Mul(curve.Scalar.New(1)),
}
rhs := curve.Scalar.New(3)
output, err := poly.Mul(rhs)
require.NoError(t, err)
require.NotNil(t, output)
poly2 := polynomialPoint{
curve.PointG1.Generator().Mul(curve.Scalar.New(9)),
curve.PointG1.Generator().Mul(curve.Scalar.New(6)),
curve.PointG1.Generator().Mul(curve.Scalar.New(3)),
}
for i := 0; i < len(poly2); i++ {
require.Equal(t, output[i].ToAffineCompressed(), poly2[i].ToAffineCompressed())
}
}
func TestMulAssignPolyG1Error(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
poly := polynomialPoint{
nil,
curve.PointG1.Generator().Mul(curve.Scalar.New(2)),
curve.PointG1.Generator().Mul(curve.Scalar.New(1)),
}
rhs := curve.Scalar.New(3)
output, err := poly.Mul(rhs)
require.Error(t, err)
require.Nil(t, output)
}
func TestPushPoly(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
poly := polynomial{
curve.Scalar.New(3),
curve.Scalar.New(2),
curve.Scalar.New(1),
}
scalar := curve.Scalar.New(4)
result := append(poly, scalar)
require.Equal(t, result[3], scalar)
// Push one more
scalar2 := curve.Scalar.New(5)
result2 := append(result, scalar2)
require.Equal(t, result2[4], scalar2)
// Push to a new polynomial
newPoly := polynomial{}
newPoly = append(newPoly, scalar)
require.Equal(t, newPoly[0], scalar)
newPoly = append(newPoly, scalar2)
require.Equal(t, newPoly[1], scalar2)
}
func TestAddAssignPoly(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
// Test polynomial with equal length
poly1 := polynomial{
curve.Scalar.New(3),
curve.Scalar.New(2),
curve.Scalar.New(1),
}
poly2 := polynomial{
curve.Scalar.New(1),
curve.Scalar.New(2),
curve.Scalar.New(3),
}
output, err := poly1.Add(poly2)
require.NoError(t, err)
require.NotNil(t, output)
result := []curves.Scalar{
curve.Scalar.New(4),
curve.Scalar.New(4),
curve.Scalar.New(4),
}
for i := 0; i < len(output); i++ {
require.Equal(t, output[i], result[i])
}
// Test polynomials with unequal length
poly3 := polynomial{
curve.Scalar.New(1),
curve.Scalar.New(2),
}
output2, err := poly1.Add(poly3)
require.NoError(t, err)
require.NotNil(t, output2)
result2 := []curves.Scalar{
curve.Scalar.New(4),
curve.Scalar.New(4),
curve.Scalar.New(1),
}
require.Equal(t, len(output2), len(result2))
for i := 0; i < len(output2); i++ {
require.Equal(t, output2[i], result2[i])
}
}
func TestAddAssignPolyError(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
// Test polynomial with equal length
poly1 := polynomial{
nil,
curve.Scalar.New(2),
curve.Scalar.New(1),
}
poly2 := polynomial{
curve.Scalar.New(1),
curve.Scalar.New(2),
curve.Scalar.New(3),
}
output, err := poly1.Add(poly2)
require.Error(t, err)
require.Nil(t, output)
}
func TestSubAssignPoly(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
// Test polynomial with equal length
poly1 := polynomial{
curve.Scalar.New(3),
curve.Scalar.New(2),
curve.Scalar.New(1),
}
poly2 := polynomial{
curve.Scalar.New(1),
curve.Scalar.New(2),
curve.Scalar.New(3),
}
output, err := poly1.Sub(poly2)
require.NoError(t, err)
require.NotNil(t, output)
result := []curves.Scalar{
curve.Scalar.New(2),
curve.Scalar.New(0),
curve.Scalar.New(-2),
}
for i := 0; i < len(output); i++ {
require.Equal(t, output[i].Bytes(), result[i].Bytes())
}
// Test polynomials with unequal length
poly3 := polynomial{
curve.Scalar.New(1),
curve.Scalar.New(2),
curve.Scalar.New(3),
curve.Scalar.New(4),
}
output2, err := poly1.Sub(poly3)
require.NoError(t, err)
require.NotNil(t, output2)
result2 := []curves.Scalar{
curve.Scalar.New(2),
curve.Scalar.New(0),
curve.Scalar.New(-2),
curve.Scalar.New(-4),
}
require.Equal(t, len(output2), len(result2))
for i := 0; i < len(output2); i++ {
require.Equal(t, output2[i].Bytes(), result2[i].Bytes())
}
}
func TestSubAssignPolyError(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
poly1 := polynomial{
nil,
curve.Scalar.New(2),
curve.Scalar.New(1),
}
poly2 := polynomial{
curve.Scalar.New(1),
curve.Scalar.New(2),
curve.Scalar.New(3),
}
output, err := poly1.Sub(poly2)
require.Error(t, err)
require.Nil(t, output)
}
func TestMulAssignPoly(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
// Test polynomial with equal length
poly1 := polynomial{
curve.Scalar.New(3),
curve.Scalar.New(2),
curve.Scalar.New(1),
}
poly2 := polynomial{
curve.Scalar.New(1),
curve.Scalar.New(2),
curve.Scalar.New(3),
}
output, err := poly1.Mul(poly2)
require.NoError(t, err)
require.NotNil(t, output)
result := []curves.Scalar{
curve.Scalar.New(3),
curve.Scalar.New(8),
curve.Scalar.New(14),
curve.Scalar.New(8),
curve.Scalar.New(3),
}
for i := 0; i < len(result); i++ {
require.Equal(t, output[i].Bytes(), result[i].Bytes())
}
// Test polynomials with unequal length
poly3 := polynomial{
curve.Scalar.New(1),
curve.Scalar.New(2),
}
output2, err := poly1.Mul(poly3)
require.NoError(t, err)
require.NotNil(t, output2)
result2 := []curves.Scalar{
curve.Scalar.New(3),
curve.Scalar.New(8),
curve.Scalar.New(5),
curve.Scalar.New(2),
}
require.Equal(t, len(output2), 4)
for i := 0; i < len(output2); i++ {
require.Equal(t, output2[i].Bytes(), result2[i].Bytes())
}
}
func TestMulAssignPolyError(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
poly1 := polynomial{
nil,
curve.Scalar.New(2),
curve.Scalar.New(1),
}
poly2 := polynomial{
curve.Scalar.New(1),
curve.Scalar.New(2),
curve.Scalar.New(3),
}
output, err := poly1.Mul(poly2)
require.Error(t, err)
require.Nil(t, output)
}
func TestMulValueAssignPoly(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
poly := polynomial{
curve.Scalar.New(3),
curve.Scalar.New(2),
curve.Scalar.New(1),
}
rhs := curve.Scalar.New(3)
output, err := poly.MulScalar(rhs)
require.NoError(t, err)
require.NotNil(t, output)
coefficients2 := []curves.Scalar{
curve.Scalar.New(9),
curve.Scalar.New(6),
curve.Scalar.New(3),
}
for i := 0; i < len(coefficients2); i++ {
require.Equal(t, output[i].Bytes(), coefficients2[i].Bytes())
}
}
func TestMulValueAssignPolyError(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
poly := polynomial{
nil,
curve.Scalar.New(2),
curve.Scalar.New(1),
}
rhs := curve.Scalar.New(3)
output, err := poly.MulScalar(rhs)
require.Error(t, err)
require.Nil(t, output)
}
+527
View File
@@ -0,0 +1,527 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package accumulator
import (
"bytes"
crand "crypto/rand"
"errors"
"fmt"
"git.sr.ht/~sircmpwn/go-bare"
"github.com/sonr-io/sonr/crypto/core/curves"
)
type proofParamsMarshal struct {
X []byte `bare:"x"`
Y []byte `bare:"y"`
Z []byte `bare:"z"`
Curve string `bare:"curve"`
}
// ProofParams contains four distinct public generators of G1 - X, Y, Z
type ProofParams struct {
x, y, z curves.Point
}
// New samples X, Y, Z, K
func (p *ProofParams) New(
curve *curves.PairingCurve,
pk *PublicKey,
entropy []byte,
) (*ProofParams, error) {
pkBytes, err := pk.MarshalBinary()
if err != nil {
return nil, err
}
prefix := bytes.Repeat([]byte{0xFF}, 32)
data := append(prefix, entropy...)
data = append(data, pkBytes...)
p.z = curve.Scalar.Point().Hash(data)
data[0] = 0xFE
p.y = curve.Scalar.Point().Hash(data)
data[0] = 0xFD
p.x = curve.Scalar.Point().Hash(data)
return p, nil
}
// MarshalBinary converts ProofParams to bytes
func (p *ProofParams) MarshalBinary() ([]byte, error) {
if p.x == nil || p.y == nil || p.z == nil {
return nil, fmt.Errorf("some value x, y, or z is nil")
}
tv := &proofParamsMarshal{
X: p.x.ToAffineCompressed(),
Y: p.y.ToAffineCompressed(),
Z: p.z.ToAffineCompressed(),
Curve: p.x.CurveName(),
}
return bare.Marshal(tv)
}
// UnmarshalBinary converts bytes to ProofParams
func (p *ProofParams) UnmarshalBinary(data []byte) error {
if data == nil {
return fmt.Errorf("expected non-zero byte sequence")
}
tv := new(proofParamsMarshal)
err := bare.Unmarshal(data, tv)
if err != nil {
return err
}
curve := curves.GetCurveByName(tv.Curve)
if curve == nil {
return fmt.Errorf("invalid curve")
}
x, err := curve.NewIdentityPoint().FromAffineCompressed(tv.X)
if err != nil {
return err
}
y, err := curve.NewIdentityPoint().FromAffineCompressed(tv.Y)
if err != nil {
return err
}
z, err := curve.NewIdentityPoint().FromAffineCompressed(tv.Z)
if err != nil {
return err
}
p.x = x
p.y = y
p.z = z
return nil
}
// MembershipProofCommitting contains value computed in Proof of knowledge and
// Blinding phases as described in section 7 of https://eprint.iacr.org/2020/777.pdf
type MembershipProofCommitting struct {
eC curves.Point
tSigma curves.Point
tRho curves.Point
deltaSigma curves.Scalar
deltaRho curves.Scalar
blindingFactor curves.Scalar
rSigma curves.Scalar
rRho curves.Scalar
rDeltaSigma curves.Scalar
rDeltaRho curves.Scalar
sigma curves.Scalar
rho curves.Scalar
capRSigma curves.Point
capRRho curves.Point
capRDeltaSigma curves.Point
capRDeltaRho curves.Point
capRE curves.Scalar
accumulator curves.Point
witnessValue curves.Scalar
xG1 curves.Point
yG1 curves.Point
zG1 curves.Point
}
// New initiates values of MembershipProofCommitting
func (mpc *MembershipProofCommitting) New(
witness *MembershipWitness,
acc *Accumulator,
pp *ProofParams,
pk *PublicKey,
) (*MembershipProofCommitting, error) {
// Randomly select σ, ρ
sigma := witness.y.Random(crand.Reader)
rho := witness.y.Random(crand.Reader)
// E_C = C + (σ + ρ)Z
t := sigma
t = t.Add(rho)
eC := pp.z
eC = eC.Mul(t)
eC = eC.Add(witness.c)
// T_σ = σX
tSigma := pp.x
tSigma = tSigma.Mul(sigma)
// T_ρ = ρY
tRho := pp.y
tRho = tRho.Mul(rho)
// δ_σ = yσ
deltaSigma := witness.y
deltaSigma = deltaSigma.Mul(sigma)
// δ_ρ = yρ
deltaRho := witness.y
deltaRho = deltaRho.Mul(rho)
// Randomly pick r_σ,r_ρ,r_δσ,r_δρ
rY := witness.y.Random(crand.Reader)
rSigma := witness.y.Random(crand.Reader)
rRho := witness.y.Random(crand.Reader)
rDeltaSigma := witness.y.Random(crand.Reader)
rDeltaRho := witness.y.Random(crand.Reader)
// R_σ = r_σ X
capRSigma := pp.x
capRSigma = capRSigma.Mul(rSigma)
// R_ρ = ρY
capRRho := pp.y
capRRho = capRRho.Mul(rRho)
// R_δσ = r_y T_σ - r_δσ X
negX := pp.x
negX = negX.Neg()
capRDeltaSigma := tSigma.Mul(rY)
capRDeltaSigma = capRDeltaSigma.Add(negX.Mul(rDeltaSigma))
// R_δρ = r_y T_ρ - r_δρ Y
negY := pp.y
negY = negY.Neg()
capRDeltaRho := tRho.Mul(rY)
capRDeltaRho = capRDeltaRho.Add(negY.Mul(rDeltaRho))
// P~
g2 := pk.value.Generator()
// -r_δσ - r_δρ
exp := rDeltaSigma
exp = exp.Add(rDeltaRho)
exp = exp.Neg()
// -r_σ - r_ρ
exp2 := rSigma
exp2 = exp2.Add(rRho)
exp2 = exp2.Neg()
// rY * eC
rYeC := eC.Mul(rY)
// (-r_δσ - r_δρ)*Z
expZ := pp.z.Mul(exp)
// (-r_σ - r_ρ)*Z
exp2Z := pp.z.Mul(exp2)
// Prepare
rYeCPrep, ok := rYeC.(curves.PairingPoint)
if !ok {
return nil, errors.New("incorrect type conversion")
}
g2Prep, ok := g2.(curves.PairingPoint)
if !ok {
return nil, errors.New("incorrect type conversion")
}
expZPrep, ok := expZ.(curves.PairingPoint)
if !ok {
return nil, errors.New("incorrect type conversion")
}
exp2ZPrep, ok := exp2Z.(curves.PairingPoint)
if !ok {
return nil, errors.New("incorrect type conversion")
}
pkPrep := pk.value
// Pairing
capRE := g2Prep.MultiPairing(rYeCPrep, g2Prep, expZPrep, g2Prep, exp2ZPrep, pkPrep)
return &MembershipProofCommitting{
eC,
tSigma,
tRho,
deltaSigma,
deltaRho,
rY,
rSigma,
rRho,
rDeltaSigma,
rDeltaRho,
sigma,
rho,
capRSigma,
capRRho,
capRDeltaSigma,
capRDeltaRho,
capRE,
acc.value,
witness.y,
pp.x,
pp.y,
pp.z,
}, nil
}
// GetChallengeBytes returns bytes that need to be hashed for generating challenge.
// V || Ec || T_sigma || T_rho || R_E || R_sigma || R_rho || R_delta_sigma || R_delta_rho
func (mpc MembershipProofCommitting) GetChallengeBytes() []byte {
res := mpc.accumulator.ToAffineCompressed()
res = append(res, mpc.eC.ToAffineCompressed()...)
res = append(res, mpc.tSigma.ToAffineCompressed()...)
res = append(res, mpc.tRho.ToAffineCompressed()...)
res = append(res, mpc.capRE.Bytes()...)
res = append(res, mpc.capRSigma.ToAffineCompressed()...)
res = append(res, mpc.capRRho.ToAffineCompressed()...)
res = append(res, mpc.capRDeltaSigma.ToAffineCompressed()...)
res = append(res, mpc.capRDeltaRho.ToAffineCompressed()...)
return res
}
// GenProof computes the s values for Fiat-Shamir and return the actual
// proof to be sent to the verifier given the challenge c.
func (mpc *MembershipProofCommitting) GenProof(c curves.Scalar) *MembershipProof {
// s_y = r_y + c*y
sY := schnorr(mpc.blindingFactor, mpc.witnessValue, c)
// s_σ = r_σ + c*σ
sSigma := schnorr(mpc.rSigma, mpc.sigma, c)
// s_ρ = r_ρ + c*ρ
sRho := schnorr(mpc.rRho, mpc.rho, c)
// s_δσ = rδσ + c*δ_σ
sDeltaSigma := schnorr(mpc.rDeltaSigma, mpc.deltaSigma, c)
// s_δρ = rδρ + c*δ_ρ
sDeltaRho := schnorr(mpc.rDeltaRho, mpc.deltaRho, c)
return &MembershipProof{
mpc.eC,
mpc.tSigma,
mpc.tRho,
sSigma,
sRho,
sDeltaSigma,
sDeltaRho,
sY,
}
}
func schnorr(r, v, challenge curves.Scalar) curves.Scalar {
res := v
res = res.Mul(challenge)
res = res.Add(r)
return res
}
type membershipProofMarshal struct {
EC []byte `bare:"e_c"`
TSigma []byte `bare:"t_sigma"`
TRho []byte `bare:"t_rho"`
SSigma []byte `bare:"s_sigma"`
SRho []byte `bare:"s_rho"`
SDeltaSigma []byte `bare:"s_delta_sigma"`
SDeltaRho []byte `bare:"s_delta_rho"`
SY []byte `bare:"s_y"`
Curve string `bare:"curve"`
}
// MembershipProof contains values in the proof to be verified
type MembershipProof struct {
eC curves.Point
tSigma curves.Point
tRho curves.Point
sSigma curves.Scalar
sRho curves.Scalar
sDeltaSigma curves.Scalar
sDeltaRho curves.Scalar
sY curves.Scalar
}
// Finalize computes values in the proof to be verified.
func (mp *MembershipProof) Finalize(
acc *Accumulator,
pp *ProofParams,
pk *PublicKey,
challenge curves.Scalar,
) (*MembershipProofFinal, error) {
// R_σ = s_δ X + c T_σ
negTSigma := mp.tSigma
negTSigma = negTSigma.Neg()
capRSigma := pp.x.Mul(mp.sSigma)
capRSigma = capRSigma.Add(negTSigma.Mul(challenge))
// R_ρ = s_ρ Y + c T_ρ
negTRho := mp.tRho
negTRho = negTRho.Neg()
capRRho := pp.y.Mul(mp.sRho)
capRRho = capRRho.Add(negTRho.Mul(challenge))
// R_δσ = s_y T_σ - s_δσ X
negX := pp.x
negX = negX.Neg()
capRDeltaSigma := mp.tSigma.Mul(mp.sY)
capRDeltaSigma = capRDeltaSigma.Add(negX.Mul(mp.sDeltaSigma))
// R_δρ = s_y T_ρ - s_δρ Y
negY := pp.y
negY = negY.Neg()
capRDeltaRho := mp.tRho.Mul(mp.sY)
capRDeltaRho = capRDeltaRho.Add(negY.Mul(mp.sDeltaRho))
// tildeP
g2 := pk.value.Generator()
// Compute capRE, the pairing
// E_c * s_y
eCsY := mp.eC.Mul(mp.sY)
// (-s_delta_sigma - s_delta_rho) * Z
exp := mp.sDeltaSigma
exp = exp.Add(mp.sDeltaRho)
exp = exp.Neg()
expZ := pp.z.Mul(exp)
// (-c) * V
exp = challenge.Neg()
expV := acc.value.Mul(exp)
// E_c * s_y + (-s_delta_sigma - s_delta_rho) * Z + (-c) * V
lhs := eCsY.Add(expZ).Add(expV)
// (-s_sigma - s_rho) * Z
exp = mp.sSigma
exp = exp.Add(mp.sRho)
exp = exp.Neg()
expZ2 := pp.z.Mul(exp)
// E_c * c
cEc := mp.eC.Mul(challenge)
// (-s_sigma - s_rho) * Z + E_c * c
rhs := cEc.Add(expZ2)
// Prepare
lhsPrep, ok := lhs.(curves.PairingPoint)
if !ok {
return nil, errors.New("incorrect type conversion")
}
g2Prep, ok := g2.(curves.PairingPoint)
if !ok {
return nil, errors.New("incorrect type conversion")
}
rhsPrep, ok := rhs.(curves.PairingPoint)
if !ok {
return nil, errors.New("incorrect type conversion")
}
pkPrep := pk.value
// capRE
capRE := g2Prep.MultiPairing(lhsPrep, g2Prep, rhsPrep, pkPrep)
return &MembershipProofFinal{
acc.value,
mp.eC,
mp.tSigma,
mp.tRho,
capRE,
capRSigma,
capRRho,
capRDeltaSigma,
capRDeltaRho,
}, nil
}
// MarshalBinary converts MembershipProof to bytes
func (mp MembershipProof) MarshalBinary() ([]byte, error) {
tv := &membershipProofMarshal{
EC: mp.eC.ToAffineCompressed(),
TSigma: mp.tSigma.ToAffineCompressed(),
TRho: mp.tRho.ToAffineCompressed(),
SSigma: mp.sSigma.Bytes(),
SRho: mp.sRho.Bytes(),
SDeltaSigma: mp.sDeltaSigma.Bytes(),
SDeltaRho: mp.sDeltaRho.Bytes(),
SY: mp.sY.Bytes(),
Curve: mp.eC.CurveName(),
}
return bare.Marshal(tv)
}
// UnmarshalBinary converts bytes to MembershipProof
func (mp *MembershipProof) UnmarshalBinary(data []byte) error {
if data == nil {
return fmt.Errorf("expected non-zero byte sequence")
}
tv := new(membershipProofMarshal)
err := bare.Unmarshal(data, tv)
if err != nil {
return err
}
curve := curves.GetCurveByName(tv.Curve)
if curve == nil {
return fmt.Errorf("invalid curve")
}
eC, err := curve.NewIdentityPoint().FromAffineCompressed(tv.EC)
if err != nil {
return err
}
tSigma, err := curve.NewIdentityPoint().FromAffineCompressed(tv.TSigma)
if err != nil {
return err
}
tRho, err := curve.NewIdentityPoint().FromAffineCompressed(tv.TRho)
if err != nil {
return err
}
sSigma, err := curve.NewScalar().SetBytes(tv.SSigma)
if err != nil {
return err
}
sRho, err := curve.NewScalar().SetBytes(tv.SRho)
if err != nil {
return err
}
sDeltaSigma, err := curve.NewScalar().SetBytes(tv.SDeltaSigma)
if err != nil {
return err
}
sDeltaRho, err := curve.NewScalar().SetBytes(tv.SDeltaRho)
if err != nil {
return err
}
sY, err := curve.NewScalar().SetBytes(tv.SY)
if err != nil {
return err
}
mp.eC = eC
mp.tSigma = tSigma
mp.tRho = tRho
mp.sSigma = sSigma
mp.sRho = sRho
mp.sDeltaSigma = sDeltaSigma
mp.sDeltaRho = sDeltaRho
mp.sY = sY
return nil
}
// MembershipProofFinal contains values that are input to Fiat-Shamir Heuristic
type MembershipProofFinal struct {
accumulator curves.Point
eC curves.Point
tSigma curves.Point
tRho curves.Point
capRE curves.Scalar
capRSigma curves.Point
capRRho curves.Point
capRDeltaSigma curves.Point
capRDeltaRho curves.Point
}
// GetChallenge computes Fiat-Shamir Heuristic taking input values of MembershipProofFinal
func (m MembershipProofFinal) GetChallenge(curve *curves.PairingCurve) curves.Scalar {
res := m.accumulator.ToAffineCompressed()
res = append(res, m.eC.ToAffineCompressed()...)
res = append(res, m.tSigma.ToAffineCompressed()...)
res = append(res, m.tRho.ToAffineCompressed()...)
res = append(res, m.capRE.Bytes()...)
res = append(res, m.capRSigma.ToAffineCompressed()...)
res = append(res, m.capRRho.ToAffineCompressed()...)
res = append(res, m.capRDeltaSigma.ToAffineCompressed()...)
res = append(res, m.capRDeltaRho.ToAffineCompressed()...)
challenge := curve.Scalar.Hash(res)
return challenge
}
+182
View File
@@ -0,0 +1,182 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package accumulator
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestProofParamsMarshal(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
sk, _ := new(SecretKey).New(curve, []byte("1234567890"))
pk, _ := sk.GetPublicKey(curve)
params, err := new(ProofParams).New(curve, pk, []byte("entropy"))
require.NoError(t, err)
require.NotNil(t, params.x)
require.NotNil(t, params.y)
require.NotNil(t, params.z)
bytes, err := params.MarshalBinary()
require.NoError(t, err)
require.NotNil(t, bytes)
params2 := &ProofParams{
curve.PointG1.Generator(),
curve.PointG1.Generator(),
curve.PointG1.Generator(),
}
err = params2.UnmarshalBinary(bytes)
require.NoError(t, err)
require.True(t, params.x.Equal(params2.x))
require.True(t, params.y.Equal(params2.y))
require.True(t, params.z.Equal(params2.z))
}
func TestMembershipProof(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
sk, _ := new(SecretKey).New(curve, []byte("1234567890"))
pk, _ := sk.GetPublicKey(curve)
element1 := curve.Scalar.Hash([]byte("3"))
element2 := curve.Scalar.Hash([]byte("4"))
element3 := curve.Scalar.Hash([]byte("5"))
element4 := curve.Scalar.Hash([]byte("6"))
element5 := curve.Scalar.Hash([]byte("7"))
element6 := curve.Scalar.Hash([]byte("8"))
element7 := curve.Scalar.Hash([]byte("9"))
elements := []Element{element1, element2, element3, element4, element5, element6, element7}
// Initiate a new accumulator
acc, err := new(Accumulator).WithElements(curve, sk, elements)
require.NoError(t, err)
require.NotNil(t, acc.value)
// Initiate a new membership witness for value elements[3]
wit, err := new(MembershipWitness).New(elements[3], acc, sk)
require.NoError(t, err)
require.Equal(t, wit.y, elements[3])
// Create proof parameters, which contains randomly sampled G1 points X, Y, Z, K
params, err := new(ProofParams).New(curve, pk, []byte("entropy"))
require.NoError(t, err)
require.NotNil(t, params.x)
require.NotNil(t, params.y)
require.NotNil(t, params.z)
mpc, err := new(MembershipProofCommitting).New(wit, acc, params, pk)
require.NoError(t, err)
testMPC(t, mpc)
challenge := curve.Scalar.Hash(mpc.GetChallengeBytes())
require.NotNil(t, challenge)
proof := mpc.GenProof(challenge)
require.NotNil(t, proof)
testProof(t, proof)
finalProof, err := proof.Finalize(acc, params, pk, challenge)
require.NoError(t, err)
require.NotNil(t, finalProof)
testFinalProof(t, finalProof)
challenge2 := finalProof.GetChallenge(curve)
require.Equal(t, challenge, challenge2)
// Check we can still have a valid proof even if accumulator and witness are updated
data1 := curve.Scalar.Hash([]byte("1"))
data2 := curve.Scalar.Hash([]byte("2"))
data3 := curve.Scalar.Hash([]byte("3"))
data4 := curve.Scalar.Hash([]byte("4"))
data5 := curve.Scalar.Hash([]byte("5"))
data := []Element{data1, data2, data3, data4, data5}
additions := data[0:2]
deletions := data[2:5]
_, coefficients, err := acc.Update(sk, additions, deletions)
require.NoError(t, err)
require.NotNil(t, coefficients)
_, err = wit.BatchUpdate(additions, deletions, coefficients)
require.NoError(t, err)
newParams, err := new(ProofParams).New(curve, pk, []byte("entropy"))
require.NoError(t, err)
require.NotNil(t, newParams.x)
require.NotNil(t, newParams.y)
require.NotNil(t, newParams.z)
newMPC, err := new(MembershipProofCommitting).New(wit, acc, newParams, pk)
require.NoError(t, err)
testMPC(t, newMPC)
challenge3 := curve.Scalar.Hash(newMPC.GetChallengeBytes())
require.NotNil(t, challenge3)
newProof := newMPC.GenProof(challenge3)
require.NotNil(t, newProof)
testProof(t, newProof)
newFinalProof, err := newProof.Finalize(acc, newParams, pk, challenge3)
require.NoError(t, err)
require.NotNil(t, newFinalProof)
testFinalProof(t, newFinalProof)
challenge4 := newFinalProof.GetChallenge(curve)
require.Equal(t, challenge3, challenge4)
}
func testMPC(t *testing.T, mpc *MembershipProofCommitting) {
require.NotNil(t, mpc.eC)
require.NotNil(t, mpc.tSigma)
require.NotNil(t, mpc.tRho)
require.NotNil(t, mpc.deltaSigma)
require.NotNil(t, mpc.deltaRho)
require.NotNil(t, mpc.blindingFactor)
require.NotNil(t, mpc.rSigma)
require.NotNil(t, mpc.rRho)
require.NotNil(t, mpc.rDeltaSigma)
require.NotNil(t, mpc.rDeltaRho)
require.NotNil(t, mpc.sigma)
require.NotNil(t, mpc.rho)
require.NotNil(t, mpc.capRSigma)
require.NotNil(t, mpc.capRRho)
require.NotNil(t, mpc.capRDeltaSigma)
require.NotNil(t, mpc.capRDeltaRho)
require.NotNil(t, mpc.capRE)
require.NotNil(t, mpc.accumulator)
require.NotNil(t, mpc.witnessValue)
require.NotNil(t, mpc.xG1)
require.NotNil(t, mpc.yG1)
require.NotNil(t, mpc.zG1)
}
func testProof(t *testing.T, proof *MembershipProof) {
require.NotNil(t, proof.eC)
require.NotNil(t, proof.tSigma)
require.NotNil(t, proof.tRho)
require.NotNil(t, proof.sSigma)
require.NotNil(t, proof.sRho)
require.NotNil(t, proof.sDeltaSigma)
require.NotNil(t, proof.sDeltaRho)
require.NotNil(t, proof.sY)
}
func testFinalProof(t *testing.T, finalProof *MembershipProofFinal) {
require.NotNil(t, finalProof.accumulator)
require.NotNil(t, finalProof.eC)
require.NotNil(t, finalProof.tSigma)
require.NotNil(t, finalProof.tRho)
require.NotNil(t, finalProof.capRE)
require.NotNil(t, finalProof.capRSigma)
require.NotNil(t, finalProof.capRRho)
require.NotNil(t, finalProof.capRDeltaSigma)
require.NotNil(t, finalProof.capRDeltaRho)
}
+392
View File
@@ -0,0 +1,392 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package accumulator
import (
"errors"
"fmt"
"git.sr.ht/~sircmpwn/go-bare"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// MembershipWitness contains the witness c and the value y respect to the accumulator state.
type MembershipWitness struct {
c curves.Point
y curves.Scalar
}
// New creates a new membership witness
func (mw *MembershipWitness) New(
y Element,
acc *Accumulator,
sk *SecretKey,
) (*MembershipWitness, error) {
if acc.value == nil || acc.value.IsIdentity() {
return nil, fmt.Errorf("value of accumulator should not be nil")
}
if sk.value == nil || sk.value.IsZero() {
return nil, fmt.Errorf("secret key should not be nil")
}
if y == nil || y.IsZero() {
return nil, fmt.Errorf("y should not be nil")
}
newAcc := &Accumulator{acc.value}
_, err := newAcc.Remove(sk, y)
if err != nil {
return nil, err
}
mw.c = newAcc.value
mw.y = y.Add(y.Zero())
return mw, nil
}
// Verify the MembershipWitness mw is a valid witness as per section 4 in
// <https://eprint.iacr.org/2020/777>
func (mw MembershipWitness) Verify(pk *PublicKey, acc *Accumulator) error {
if mw.c == nil || mw.y == nil || mw.c.IsIdentity() || mw.y.IsZero() {
return fmt.Errorf("c and y should not be nil")
}
if pk.value == nil || pk.value.IsIdentity() {
return fmt.Errorf("invalid public key")
}
if acc.value == nil || acc.value.IsIdentity() {
return fmt.Errorf("accumulator value should not be nil")
}
// Set -tildeP
g2, ok := pk.value.Generator().(curves.PairingPoint)
if !ok {
return errors.New("incorrect type conversion")
}
// y*tildeP + tildeQ, tildeP is a G2 generator.
p, ok := g2.Mul(mw.y).Add(pk.value).(curves.PairingPoint)
if !ok {
return errors.New("incorrect type conversion")
}
// Prepare
witness, ok := mw.c.(curves.PairingPoint)
if !ok {
return errors.New("incorrect type conversion")
}
v, ok := acc.value.Neg().(curves.PairingPoint)
if !ok {
return errors.New("incorrect type conversion")
}
// Check e(witness, y*tildeP + tildeQ) * e(-acc, tildeP) == Identity
result := p.MultiPairing(witness, p, v, g2)
if !result.IsOne() {
return fmt.Errorf("invalid result")
}
return nil
}
// ApplyDelta returns C' = dA(y)/dD(y)*C + 1/dD(y) * <Gamma_y, Omega>
// according to the witness update protocol described in section 4 of
// https://eprint.iacr.org/2020/777.pdf
func (mw *MembershipWitness) ApplyDelta(delta *Delta) (*MembershipWitness, error) {
if mw.c == nil || mw.y == nil || delta == nil {
return nil, fmt.Errorf("y, c or delta should not be nil")
}
// C' = dA(y)/dD(y)*C + 1/dD(y) * <Gamma_y, Omega>
mw.c = mw.c.Mul(delta.d).Add(delta.p)
return mw, nil
}
// BatchUpdate performs batch update as described in section 4
func (mw *MembershipWitness) BatchUpdate(
additions []Element,
deletions []Element,
coefficients []Coefficient,
) (*MembershipWitness, error) {
delta, err := evaluateDelta(mw.y, additions, deletions, coefficients)
if err != nil {
return nil, err
}
mw, err = mw.ApplyDelta(delta)
if err != nil {
return nil, fmt.Errorf("applyDelta fails")
}
return mw, nil
}
// MultiBatchUpdate performs multi-batch update using epoch as described in section 4.2
func (mw *MembershipWitness) MultiBatchUpdate(
A [][]Element,
D [][]Element,
C [][]Coefficient,
) (*MembershipWitness, error) {
delta, err := evaluateDeltas(mw.y, A, D, C)
if err != nil {
return nil, fmt.Errorf("evaluateDeltas fails")
}
mw, err = mw.ApplyDelta(delta)
if err != nil {
return nil, err
}
return mw, nil
}
// MarshalBinary converts a membership witness to bytes
func (mw MembershipWitness) MarshalBinary() ([]byte, error) {
if mw.c == nil || mw.y == nil {
return nil, fmt.Errorf("c and y value should not be nil")
}
result := append(mw.c.ToAffineCompressed(), mw.y.Bytes()...)
tv := &structMarshal{
Value: result,
Curve: mw.c.CurveName(),
}
return bare.Marshal(tv)
}
// UnmarshalBinary converts bytes into MembershipWitness
func (mw *MembershipWitness) UnmarshalBinary(data []byte) error {
if data == nil {
return fmt.Errorf("input data should not be nil")
}
tv := new(structMarshal)
err := bare.Unmarshal(data, tv)
if err != nil {
return err
}
curve := curves.GetCurveByName(tv.Curve)
if curve == nil {
return fmt.Errorf("invalid curve")
}
ptLength := len(curve.Point.ToAffineCompressed())
scLength := len(curve.Scalar.Bytes())
expectedLength := ptLength + scLength
if len(tv.Value) != expectedLength {
return fmt.Errorf("invalid byte sequence")
}
cValue, err := curve.Point.FromAffineCompressed(tv.Value[:ptLength])
if err != nil {
return err
}
yValue, err := curve.Scalar.SetBytes(tv.Value[ptLength:])
if err != nil {
return err
}
mw.c = cValue
mw.y = yValue
return nil
}
// Delta contains values d and p, where d should be the division dA(y)/dD(y) on some value y
// p should be equal to 1/dD * <Gamma_y, Omega>
type Delta struct {
d curves.Scalar
p curves.Point
}
// MarshalBinary converts Delta into bytes
func (d *Delta) MarshalBinary() ([]byte, error) {
if d.d == nil || d.p == nil {
return nil, fmt.Errorf("d and p should not be nil")
}
var result []byte
result = append(result, d.p.ToAffineCompressed()...)
result = append(result, d.d.Bytes()...)
tv := &structMarshal{
Value: result,
Curve: d.p.CurveName(),
}
return bare.Marshal(tv)
}
// UnmarshalBinary converts data into Delta
func (d *Delta) UnmarshalBinary(data []byte) error {
if data == nil {
return fmt.Errorf("expected non-zero byte sequence")
}
tv := new(structMarshal)
err := bare.Unmarshal(data, tv)
if err != nil {
return err
}
curve := curves.GetCurveByName(tv.Curve)
if curve == nil {
return fmt.Errorf("invalid curve")
}
ptLength := len(curve.Point.ToAffineCompressed())
scLength := len(curve.Scalar.Bytes())
expectedLength := ptLength + scLength
if len(tv.Value) != expectedLength {
return fmt.Errorf("invalid byte sequence")
}
pValue, err := curve.NewIdentityPoint().FromAffineCompressed(tv.Value[:ptLength])
if err != nil {
return err
}
dValue, err := curve.NewScalar().SetBytes(tv.Value[ptLength:])
if err != nil {
return err
}
d.d = dValue
d.p = pValue
return nil
}
// evaluateDeltas compute values used for membership witness batch update with epoch
// as described in section 4.2, page 11 of https://eprint.iacr.org/2020/777.pdf
func evaluateDeltas(y Element, A [][]Element, D [][]Element, C [][]Coefficient) (*Delta, error) {
if len(A) != len(D) || len(A) != len(C) {
return nil, fmt.Errorf("a, d, c should have same length")
}
one := y.One()
size := len(A)
// dA(x) = ∏ 1..n (yA_i - x)
aa := make([]curves.Scalar, 0)
// dD(x) = ∏ 1..m (yD_i - x)
dd := make([]curves.Scalar, 0)
a := one
d := one
// dA_{a->b}(y) = ∏ a..b dAs(y)
// dD_{a->b}(y) = ∏ a..b dDs(y)
for i := range size {
adds := A[i]
dels := D[i]
// ta = dAs(y)
ta, err := dad(adds, y)
if err != nil {
return nil, fmt.Errorf("dad on additions fails")
}
// td = dDs(y)
td, err := dad(dels, y)
if err != nil {
return nil, fmt.Errorf("dad on deletions fails")
}
// ∏ a..b dAs(y)
a = a.Mul(ta)
// ∏ a..b dDs(y)
d = d.Mul(td)
aa = append(aa, ta)
dd = append(dd, td)
}
// If this fails, then this value was removed.
d, err := d.Invert()
if err != nil {
return nil, fmt.Errorf("no inverse exists")
}
// <Gamma_y, Omega>
p := make(polynomialPoint, 0, size)
// Ωi->j+1 = ∑ 1..t (dAt * dDt-1) · Ω
for i := 0; i < size; i++ {
// t = i+1
// ∏^(t-1)_(h=i+1)
ddh := one
// dDi→t1 (y)
for h := 0; h < i; h++ {
ddh = ddh.Mul(dd[h])
}
// ∏^(j+1)_(k=t+1)
dak := one
// dAt->j(y)
for k := i + 1; k < size; k++ {
dak = dak.Mul(aa[k])
}
// dDi->t-1(y) * dAt->j(y)
dak = dak.Mul(ddh)
pp := make(polynomialPoint, len(C[i]))
for j := 0; j < len(pp); j++ {
pp[j] = C[i][j]
}
// dDi->t-1(y) * dAt->j(y) · Ω
pp, err := pp.Mul(dak)
if err != nil {
return nil, fmt.Errorf("pp.Mul fails")
}
p, err = p.Add(pp)
if err != nil {
return nil, fmt.Errorf("pp.Add fails")
}
}
// dAi->j(y)/dDi->j(y)
a = a.Mul(d)
// Ωi->j(y)
v, err := p.evaluate(y)
if err != nil {
return nil, fmt.Errorf("p.evaluate fails")
}
// (1/dDi->j(y)) * Ωi->j(y)
v = v.Mul(d)
// return
return &Delta{d: a, p: v}, nil
}
// evaluateDelta computes values used for membership witness batch update
// as described in section 4.1 of https://eprint.iacr.org/2020/777.pdf
func evaluateDelta(
y Element,
additions []Element,
deletions []Element,
coefficients []Coefficient,
) (*Delta, error) {
// dD(y) = ∏ 1..m (yD_i - y), d = 1/dD(y)
var err error
d, err := dad(deletions, y)
if err != nil {
return nil, fmt.Errorf("dad fails on deletions")
}
d, err = d.Invert()
if err != nil {
return nil, fmt.Errorf("no inverse exists")
}
// dA(y) = ∏ 1..n (yA_i - y)
a, err := dad(additions, y)
if err != nil {
return nil, fmt.Errorf("dad fails on additions")
}
// dA(y)/dD(y)
a = a.Mul(d)
// Create a PolynomialG1 from coefficients
p := make(polynomialPoint, len(coefficients))
for i := range coefficients {
p[i] = coefficients[i]
}
// <Gamma_y, Omega>
v, err := p.evaluate(y)
if err != nil {
return nil, fmt.Errorf("p.evaluate fails")
}
// 1/dD * <Gamma_y, Omega>
v = v.Mul(d)
return &Delta{d: a, p: v}, nil
}
+229
View File
@@ -0,0 +1,229 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package accumulator
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func Test_Membership_Witness_New(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
var seed [32]byte
key, _ := new(SecretKey).New(curve, seed[:])
acc, _ := new(Accumulator).New(curve)
e := curve.Scalar.New(2)
mw, err := new(MembershipWitness).New(e, acc, key)
require.NoError(t, err)
require.NotNil(t, mw.c)
require.NotNil(t, mw.y)
}
func Test_Membership_Witness_Marshal(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
mw := &MembershipWitness{
curve.PointG1.Generator().Mul(curve.Scalar.New(10)),
curve.Scalar.New(15),
}
data, err := mw.MarshalBinary()
require.NoError(t, err)
require.NotNil(t, data)
newMW := &MembershipWitness{}
err = newMW.UnmarshalBinary(data)
require.NoError(t, err)
require.True(t, mw.c.Equal(newMW.c))
require.Equal(t, 0, mw.y.Cmp(newMW.y))
}
func Test_Membership(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
sk, _ := new(SecretKey).New(curve, []byte("1234567890"))
pk, _ := sk.GetPublicKey(curve)
element1 := curve.Scalar.Hash([]byte("3"))
element2 := curve.Scalar.Hash([]byte("4"))
element3 := curve.Scalar.Hash([]byte("5"))
element4 := curve.Scalar.Hash([]byte("6"))
element5 := curve.Scalar.Hash([]byte("7"))
element6 := curve.Scalar.Hash([]byte("8"))
element7 := curve.Scalar.Hash([]byte("9"))
elements := []Element{element1, element2, element3, element4, element5, element6, element7}
// nm_witness_max works as well if set to value larger than 0 for this test.x
acc, err := new(Accumulator).WithElements(curve, sk, elements)
require.NoError(t, err)
require.NotNil(t, acc.value)
require.False(t, acc.value.IsIdentity())
require.True(t, acc.value.IsOnCurve())
require.NotEqual(t, acc.value, curve.NewG1GeneratorPoint())
wit, err := new(MembershipWitness).New(elements[3], acc, sk)
require.NoError(t, err)
require.Equal(t, wit.y, elements[3])
err = wit.Verify(pk, acc)
require.NoError(t, err)
// Test wrong cases, forge a wrong witness
wrongWit := MembershipWitness{
curve.PointG1.Identity(),
curve.Scalar.One(),
}
err = wrongWit.Verify(pk, acc)
require.Error(t, err)
// Test wrong cases, forge a wrong accumulator
wrongAcc := &Accumulator{
curve.PointG1.Generator(),
}
err = wit.Verify(pk, wrongAcc)
require.Error(t, err)
}
func Test_Membership_Batch_Update(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
sk, _ := new(SecretKey).New(curve, []byte("1234567890"))
pk, _ := sk.GetPublicKey(curve)
element1 := curve.Scalar.Hash([]byte("3"))
element2 := curve.Scalar.Hash([]byte("4"))
element3 := curve.Scalar.Hash([]byte("5"))
element4 := curve.Scalar.Hash([]byte("6"))
element5 := curve.Scalar.Hash([]byte("7"))
element6 := curve.Scalar.Hash([]byte("8"))
element7 := curve.Scalar.Hash([]byte("9"))
elements := []Element{element1, element2, element3, element4, element5, element6, element7}
// nm_witness_max works as well if set to value larger than 0 for this test.
acc, err := new(Accumulator).WithElements(curve, sk, elements)
require.NoError(t, err)
require.NotNil(t, acc.value)
wit, err := new(MembershipWitness).New(elements[3], acc, sk)
require.NoError(t, err)
require.Equal(t, wit.y, elements[3])
err = wit.Verify(pk, acc)
require.Nil(t, err)
data1 := curve.Scalar.Hash([]byte("1"))
data2 := curve.Scalar.Hash([]byte("2"))
data3 := curve.Scalar.Hash([]byte("3"))
data4 := curve.Scalar.Hash([]byte("4"))
data5 := curve.Scalar.Hash([]byte("5"))
data := []Element{data1, data2, data3, data4, data5}
additions := data[0:2]
deletions := data[2:5]
_, coefficients, err := acc.Update(sk, additions, deletions)
require.NoError(t, err)
require.NotNil(t, coefficients)
_, err = wit.BatchUpdate(additions, deletions, coefficients)
require.NoError(t, err)
err = wit.Verify(pk, acc)
require.Nil(t, err)
}
func Test_Membership_Multi_Batch_Update(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G1{})
sk, _ := new(SecretKey).New(curve, []byte("1234567890"))
pk, _ := sk.GetPublicKey(curve)
element1 := curve.Scalar.Hash([]byte("3"))
element2 := curve.Scalar.Hash([]byte("4"))
element3 := curve.Scalar.Hash([]byte("5"))
element4 := curve.Scalar.Hash([]byte("6"))
element5 := curve.Scalar.Hash([]byte("7"))
element6 := curve.Scalar.Hash([]byte("8"))
element7 := curve.Scalar.Hash([]byte("9"))
element8 := curve.Scalar.Hash([]byte("10"))
element9 := curve.Scalar.Hash([]byte("11"))
element10 := curve.Scalar.Hash([]byte("12"))
element11 := curve.Scalar.Hash([]byte("13"))
element12 := curve.Scalar.Hash([]byte("14"))
element13 := curve.Scalar.Hash([]byte("15"))
element14 := curve.Scalar.Hash([]byte("16"))
element15 := curve.Scalar.Hash([]byte("17"))
element16 := curve.Scalar.Hash([]byte("18"))
element17 := curve.Scalar.Hash([]byte("19"))
element18 := curve.Scalar.Hash([]byte("20"))
elements := []Element{
element1,
element2,
element3,
element4,
element5,
element6,
element7,
element8,
element9,
element10,
element11,
element12,
element13,
element14,
element15,
element16,
element17,
element18,
}
acc, err := new(Accumulator).WithElements(curve, sk, elements)
require.NoError(t, err)
require.NotNil(t, acc.value)
wit, err := new(MembershipWitness).New(elements[3], acc, sk)
require.NoError(t, err)
err = wit.Verify(pk, acc)
require.Nil(t, err)
data1 := curve.Scalar.Hash([]byte("1"))
data2 := curve.Scalar.Hash([]byte("2"))
data3 := curve.Scalar.Hash([]byte("3"))
data4 := curve.Scalar.Hash([]byte("4"))
data5 := curve.Scalar.Hash([]byte("5"))
data := []Element{data1, data2, data3, data4, data5}
adds1 := data[0:2]
dels1 := data[2:5]
_, coeffs1, err := acc.Update(sk, adds1, dels1)
require.NoError(t, err)
require.NotNil(t, coeffs1)
dels2 := elements[8:10]
_, coeffs2, err := acc.Update(sk, []Element{}, dels2)
require.NoError(t, err)
require.NotNil(t, coeffs2)
dels3 := elements[11:14]
_, coeffs3, err := acc.Update(sk, []Element{}, dels3)
require.NoError(t, err)
require.NotNil(t, coeffs3)
a := make([][]Element, 3)
a[0] = adds1
a[1] = []Element{}
a[2] = []Element{}
d := make([][]Element, 3)
d[0] = dels1
d[1] = dels2
d[2] = dels3
c := make([][]Coefficient, 3)
c[0] = coeffs1
c[1] = coeffs2
c[2] = coeffs3
_, err = wit.MultiBatchUpdate(a, d, c)
require.NoError(t, err)
err = wit.Verify(pk, acc)
require.Nil(t, err)
}
+121
View File
@@ -0,0 +1,121 @@
// Package aead provides authenticated encryption with associated data (AEAD) implementations
// following NIST SP 800-38D standards for secure data encryption and integrity verification.
package aead
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
)
const (
// NonceSize defines the standard 96-bit nonce size for optimal GCM performance
NonceSize = 12
// TagSize defines the 128-bit authentication tag size for GCM
TagSize = 16
// KeySize defines the AES-256 key size
KeySize = 32
)
// AESGCMCipher wraps AES-GCM operations with secure defaults
type AESGCMCipher struct {
gcm cipher.AEAD
}
// NewAESGCM creates a new AES-GCM cipher with the provided 256-bit key
func NewAESGCM(key []byte) (*AESGCMCipher, error) {
if len(key) != KeySize {
return nil, fmt.Errorf("invalid key size: expected %d bytes, got %d", KeySize, len(key))
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("failed to create GCM mode: %w", err)
}
return &AESGCMCipher{gcm: gcm}, nil
}
// Encrypt encrypts plaintext with additional authenticated data (AAD) using AES-GCM
// Returns nonce + ciphertext + tag concatenated for easy storage
func (a *AESGCMCipher) Encrypt(plaintext, aad []byte) ([]byte, error) {
// Generate cryptographically secure random nonce
nonce, err := generateNonce()
if err != nil {
return nil, fmt.Errorf("failed to generate nonce: %w", err)
}
// Encrypt with GCM (includes authentication tag)
ciphertext := a.gcm.Seal(nil, nonce, plaintext, aad)
// Prepend nonce to ciphertext for transport
result := make([]byte, NonceSize+len(ciphertext))
copy(result[:NonceSize], nonce)
copy(result[NonceSize:], ciphertext)
return result, nil
}
// Decrypt decrypts and authenticates ciphertext with AAD using AES-GCM
// Expects input format: nonce + ciphertext + tag
func (a *AESGCMCipher) Decrypt(data, aad []byte) ([]byte, error) {
if len(data) < NonceSize+TagSize {
return nil, fmt.Errorf("invalid ciphertext length: minimum %d bytes required", NonceSize+TagSize)
}
// Extract nonce and ciphertext
nonce := data[:NonceSize]
ciphertext := data[NonceSize:]
// Decrypt and verify authentication tag
plaintext, err := a.gcm.Open(nil, nonce, ciphertext, aad)
if err != nil {
return nil, fmt.Errorf("decryption and authentication failed: %w", err)
}
return plaintext, nil
}
// EncryptWithNonce encrypts plaintext using a provided nonce (for testing purposes)
// WARNING: Nonce reuse can compromise security. Use only for testing.
func (a *AESGCMCipher) EncryptWithNonce(plaintext, aad, nonce []byte) ([]byte, error) {
if len(nonce) != NonceSize {
return nil, fmt.Errorf("invalid nonce size: expected %d bytes, got %d", NonceSize, len(nonce))
}
// Encrypt with provided nonce
ciphertext := a.gcm.Seal(nil, nonce, plaintext, aad)
// Prepend nonce to ciphertext
result := make([]byte, NonceSize+len(ciphertext))
copy(result[:NonceSize], nonce)
copy(result[NonceSize:], ciphertext)
return result, nil
}
// generateNonce creates a cryptographically secure 96-bit nonce
func generateNonce() ([]byte, error) {
nonce := make([]byte, NonceSize)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, fmt.Errorf("failed to read random bytes: %w", err)
}
return nonce, nil
}
// GetNonceSize returns the nonce size used by this cipher
func (a *AESGCMCipher) GetNonceSize() int {
return NonceSize
}
// GetTagSize returns the authentication tag size
func (a *AESGCMCipher) GetTagSize() int {
return TagSize
}
+349
View File
@@ -0,0 +1,349 @@
package aead
import (
"crypto/rand"
"fmt"
"testing"
)
func TestNewAESGCM(t *testing.T) {
tests := []struct {
name string
keySize int
wantErr bool
}{
{"valid 256-bit key", 32, false},
{"invalid 128-bit key", 16, true},
{"invalid 192-bit key", 24, true},
{"empty key", 0, true},
{"oversized key", 64, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
key := make([]byte, tt.keySize)
_, err := rand.Read(key)
if err != nil {
t.Fatalf("Failed to generate test key: %v", err)
}
cipher, err := NewAESGCM(key)
if (err != nil) != tt.wantErr {
t.Errorf("NewAESGCM() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && cipher == nil {
t.Error("NewAESGCM() returned nil cipher without error")
}
})
}
}
func TestAESGCMEncryptDecrypt(t *testing.T) {
// Generate random 256-bit key
key := make([]byte, KeySize)
_, err := rand.Read(key)
if err != nil {
t.Fatalf("Failed to generate test key: %v", err)
}
cipher, err := NewAESGCM(key)
if err != nil {
t.Fatalf("Failed to create AES-GCM cipher: %v", err)
}
tests := []struct {
name string
plaintext []byte
aad []byte
}{
{"empty plaintext", []byte{}, nil},
{"small plaintext", []byte("hello"), nil},
{"large plaintext", make([]byte, 1024), nil},
{"with AAD", []byte("secret data"), []byte("additional auth data")},
{"unicode plaintext", []byte("Hello, 世界! 🔐"), []byte("metadata")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Fill large plaintext with test data
if len(tt.plaintext) == 1024 {
for i := range tt.plaintext {
tt.plaintext[i] = byte(i % 256)
}
}
// Encrypt
ciphertext, err := cipher.Encrypt(tt.plaintext, tt.aad)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
// Verify ciphertext structure
expectedLen := NonceSize + len(tt.plaintext) + TagSize
if len(ciphertext) != expectedLen {
t.Errorf("Unexpected ciphertext length: got %d, want %d", len(ciphertext), expectedLen)
}
// Decrypt
decrypted, err := cipher.Decrypt(ciphertext, tt.aad)
if err != nil {
t.Fatalf("Decrypt() error = %v", err)
}
// Verify plaintext matches
if string(decrypted) != string(tt.plaintext) {
t.Errorf("Decrypted text doesn't match original: got %q, want %q", string(decrypted), string(tt.plaintext))
}
})
}
}
func TestAESGCMAuthenticationFailure(t *testing.T) {
key := make([]byte, KeySize)
_, err := rand.Read(key)
if err != nil {
t.Fatalf("Failed to generate test key: %v", err)
}
cipher, err := NewAESGCM(key)
if err != nil {
t.Fatalf("Failed to create AES-GCM cipher: %v", err)
}
plaintext := []byte("authenticated data")
aad := []byte("additional data")
ciphertext, err := cipher.Encrypt(plaintext, aad)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
tests := []struct {
name string
modifyFunc func([]byte) []byte
modifyAAD func([]byte) []byte
expectFailure bool
}{
{
"tampered ciphertext",
func(data []byte) []byte {
if len(data) > NonceSize+5 {
data[NonceSize+5] ^= 0x01 // Flip one bit in ciphertext
}
return data
},
nil,
true,
},
{
"tampered nonce",
func(data []byte) []byte {
if len(data) > 5 {
data[5] ^= 0x01 // Flip one bit in nonce
}
return data
},
nil,
true,
},
{
"tampered AAD",
nil,
func(aad []byte) []byte {
modified := make([]byte, len(aad))
copy(modified, aad)
if len(modified) > 0 {
modified[0] ^= 0x01
}
return modified
},
true,
},
{
"valid decryption",
nil,
nil,
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testCiphertext := make([]byte, len(ciphertext))
copy(testCiphertext, ciphertext)
testAAD := make([]byte, len(aad))
copy(testAAD, aad)
if tt.modifyFunc != nil {
testCiphertext = tt.modifyFunc(testCiphertext)
}
if tt.modifyAAD != nil {
testAAD = tt.modifyAAD(testAAD)
}
_, err := cipher.Decrypt(testCiphertext, testAAD)
if tt.expectFailure && err == nil {
t.Error("Expected decryption to fail due to tampering, but it succeeded")
}
if !tt.expectFailure && err != nil {
t.Errorf("Expected decryption to succeed, but got error: %v", err)
}
})
}
}
func TestAESGCMWithNonce(t *testing.T) {
key := make([]byte, KeySize)
_, err := rand.Read(key)
if err != nil {
t.Fatalf("Failed to generate test key: %v", err)
}
cipher, err := NewAESGCM(key)
if err != nil {
t.Fatalf("Failed to create AES-GCM cipher: %v", err)
}
plaintext := []byte("test message")
nonce := make([]byte, NonceSize)
_, err = rand.Read(nonce)
if err != nil {
t.Fatalf("Failed to generate nonce: %v", err)
}
// Test encryption with provided nonce
ciphertext, err := cipher.EncryptWithNonce(plaintext, nil, nonce)
if err != nil {
t.Fatalf("EncryptWithNonce() error = %v", err)
}
// Verify nonce is properly prepended
if string(ciphertext[:NonceSize]) != string(nonce) {
t.Error("Nonce not properly prepended to ciphertext")
}
// Test decryption works
decrypted, err := cipher.Decrypt(ciphertext, nil)
if err != nil {
t.Fatalf("Decrypt() error = %v", err)
}
if string(decrypted) != string(plaintext) {
t.Errorf("Decrypted text doesn't match: got %q, want %q", string(decrypted), string(plaintext))
}
// Test invalid nonce size
invalidNonce := make([]byte, 8)
_, err = cipher.EncryptWithNonce(plaintext, nil, invalidNonce)
if err == nil {
t.Error("Expected error for invalid nonce size")
}
}
func TestAESGCMInvalidCiphertext(t *testing.T) {
key := make([]byte, KeySize)
_, err := rand.Read(key)
if err != nil {
t.Fatalf("Failed to generate test key: %v", err)
}
cipher, err := NewAESGCM(key)
if err != nil {
t.Fatalf("Failed to create AES-GCM cipher: %v", err)
}
tests := []struct {
name string
ciphertext []byte
}{
{"empty ciphertext", []byte{}},
{"too short ciphertext", make([]byte, NonceSize)},
{"minimal invalid", make([]byte, NonceSize+TagSize-1)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := cipher.Decrypt(tt.ciphertext, nil)
if err == nil {
t.Error("Expected error for invalid ciphertext length")
}
})
}
}
func BenchmarkAESGCMEncrypt(b *testing.B) {
key := make([]byte, KeySize)
_, err := rand.Read(key)
if err != nil {
b.Fatalf("Failed to generate test key: %v", err)
}
cipher, err := NewAESGCM(key)
if err != nil {
b.Fatalf("Failed to create AES-GCM cipher: %v", err)
}
sizes := []int{64, 512, 1024, 4096}
for _, size := range sizes {
b.Run(fmt.Sprintf("%dB", size), func(b *testing.B) {
plaintext := make([]byte, size)
_, err := rand.Read(plaintext)
if err != nil {
b.Fatalf("Failed to generate test data: %v", err)
}
b.ResetTimer()
b.SetBytes(int64(size))
for i := 0; i < b.N; i++ {
_, err := cipher.Encrypt(plaintext, nil)
if err != nil {
b.Fatalf("Encrypt error: %v", err)
}
}
})
}
}
func BenchmarkAESGCMDecrypt(b *testing.B) {
key := make([]byte, KeySize)
_, err := rand.Read(key)
if err != nil {
b.Fatalf("Failed to generate test key: %v", err)
}
cipher, err := NewAESGCM(key)
if err != nil {
b.Fatalf("Failed to create AES-GCM cipher: %v", err)
}
sizes := []int{64, 512, 1024, 4096}
for _, size := range sizes {
b.Run(fmt.Sprintf("%dB", size), func(b *testing.B) {
plaintext := make([]byte, size)
_, err := rand.Read(plaintext)
if err != nil {
b.Fatalf("Failed to generate test data: %v", err)
}
ciphertext, err := cipher.Encrypt(plaintext, nil)
if err != nil {
b.Fatalf("Failed to encrypt test data: %v", err)
}
b.ResetTimer()
b.SetBytes(int64(size))
for i := 0; i < b.N; i++ {
_, err := cipher.Decrypt(ciphertext, nil)
if err != nil {
b.Fatalf("Decrypt error: %v", err)
}
}
})
}
}
+213
View File
@@ -0,0 +1,213 @@
// Package argon2 provides secure key derivation using Argon2id
package argon2
import (
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"fmt"
"strings"
"golang.org/x/crypto/argon2"
)
// Config defines Argon2id parameters
type Config struct {
Time uint32 // Number of iterations
Memory uint32 // Memory in KB
Parallelism uint8 // Number of threads
SaltLength uint32 // Salt length in bytes
KeyLength uint32 // Output key length in bytes
}
// DefaultConfig returns secure default parameters
func DefaultConfig() *Config {
return &Config{
Time: 1,
Memory: 64 * 1024, // 64MB
Parallelism: 4,
SaltLength: 32,
KeyLength: 32,
}
}
// LightConfig returns lighter parameters for testing
func LightConfig() *Config {
return &Config{
Time: 1,
Memory: 16 * 1024, // 16MB
Parallelism: 2,
SaltLength: 16,
KeyLength: 32,
}
}
// HighSecurityConfig returns high-security parameters
func HighSecurityConfig() *Config {
return &Config{
Time: 3,
Memory: 128 * 1024, // 128MB
Parallelism: 4,
SaltLength: 32,
KeyLength: 32,
}
}
// KDF implements Argon2id key derivation
type KDF struct {
config *Config
}
// New creates a new Argon2id KDF with the given configuration
func New(config *Config) *KDF {
if config == nil {
config = DefaultConfig()
}
return &KDF{config: config}
}
// DeriveKey derives a key from password and salt
func (k *KDF) DeriveKey(password []byte, salt []byte) []byte {
return argon2.IDKey(
password,
salt,
k.config.Time,
k.config.Memory,
k.config.Parallelism,
k.config.KeyLength,
)
}
// GenerateSalt generates a cryptographically secure salt
func (k *KDF) GenerateSalt() ([]byte, error) {
salt := make([]byte, k.config.SaltLength)
if _, err := rand.Read(salt); err != nil {
return nil, fmt.Errorf("failed to generate salt: %w", err)
}
return salt, nil
}
// HashPassword generates a hash with embedded salt and parameters
func (k *KDF) HashPassword(password []byte) (string, error) {
salt, err := k.GenerateSalt()
if err != nil {
return "", err
}
hash := k.DeriveKey(password, salt)
// Encode in PHC format: $argon2id$v=19$m=65536,t=1,p=4$salt$hash
encodedSalt := base64.RawStdEncoding.EncodeToString(salt)
encodedHash := base64.RawStdEncoding.EncodeToString(hash)
return fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
argon2.Version,
k.config.Memory,
k.config.Time,
k.config.Parallelism,
encodedSalt,
encodedHash,
), nil
}
// VerifyPassword verifies a password against a PHC-formatted hash
func VerifyPassword(password []byte, encodedHash string) (bool, error) {
params, salt, hash, err := decodeHash(encodedHash)
if err != nil {
return false, err
}
kdf := &KDF{config: params}
derivedHash := kdf.DeriveKey(password, salt)
// Constant-time comparison
return subtle.ConstantTimeCompare(hash, derivedHash) == 1, nil
}
// decodeHash parses PHC-formatted Argon2id hash
func decodeHash(encodedHash string) (*Config, []byte, []byte, error) {
parts := strings.Split(encodedHash, "$")
if len(parts) != 6 {
return nil, nil, nil, fmt.Errorf("invalid hash format")
}
if parts[1] != "argon2id" {
return nil, nil, nil, fmt.Errorf("unsupported algorithm: %s", parts[1])
}
var version int
_, err := fmt.Sscanf(parts[2], "v=%d", &version)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to parse version: %w", err)
}
if version != argon2.Version {
return nil, nil, nil, fmt.Errorf("unsupported Argon2 version: %d", version)
}
var memory, time uint32
var parallelism uint8
_, err = fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &memory, &time, &parallelism)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to parse parameters: %w", err)
}
salt, err := base64.RawStdEncoding.DecodeString(parts[4])
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to decode salt: %w", err)
}
hash, err := base64.RawStdEncoding.DecodeString(parts[5])
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to decode hash: %w", err)
}
config := &Config{
Time: time,
Memory: memory,
Parallelism: parallelism,
SaltLength: uint32(len(salt)),
KeyLength: uint32(len(hash)),
}
return config, salt, hash, nil
}
// CompareHashes performs constant-time comparison of two hashes
func CompareHashes(hash1, hash2 []byte) bool {
return subtle.ConstantTimeCompare(hash1, hash2) == 1
}
// ValidateConfig validates Argon2id parameters
func ValidateConfig(config *Config) error {
if config.Time < 1 {
return fmt.Errorf("time must be at least 1")
}
if config.Memory < 8*1024 {
return fmt.Errorf("memory must be at least 8MB")
}
if config.Parallelism < 1 {
return fmt.Errorf("parallelism must be at least 1")
}
if config.SaltLength < 8 {
return fmt.Errorf("salt length must be at least 8 bytes")
}
if config.KeyLength < 16 {
return fmt.Errorf("key length must be at least 16 bytes")
}
return nil
}
// EstimateTime estimates the time required for key derivation
func EstimateTime(config *Config, iterations int) string {
// This is a rough estimate - actual time depends on hardware
baseTime := float64(config.Time) * float64(config.Memory) / (64 * 1024)
totalTime := baseTime * float64(iterations)
if totalTime < 1 {
return fmt.Sprintf("%.2f ms", totalTime*1000)
} else if totalTime < 60 {
return fmt.Sprintf("%.2f s", totalTime)
}
return fmt.Sprintf("%.2f min", totalTime/60)
}
+396
View File
@@ -0,0 +1,396 @@
package argon2
import (
"bytes"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestKDF_DeriveKey(t *testing.T) {
kdf := New(DefaultConfig())
password := []byte("test-password")
salt := []byte("salt-must-be-at-least-16-bytes!!")
// Derive key
key := kdf.DeriveKey(password, salt)
assert.Len(t, key, int(kdf.config.KeyLength))
// Same inputs should produce same key
key2 := kdf.DeriveKey(password, salt)
assert.Equal(t, key, key2)
// Different password should produce different key
key3 := kdf.DeriveKey([]byte("different"), salt)
assert.NotEqual(t, key, key3)
// Different salt should produce different key
salt2 := []byte("different-salt-at-least-16-bytes")
key4 := kdf.DeriveKey(password, salt2)
assert.NotEqual(t, key, key4)
}
func TestKDF_GenerateSalt(t *testing.T) {
kdf := New(DefaultConfig())
salt1, err := kdf.GenerateSalt()
require.NoError(t, err)
assert.Len(t, salt1, int(kdf.config.SaltLength))
// Should generate different salt each time
salt2, err := kdf.GenerateSalt()
require.NoError(t, err)
assert.NotEqual(t, salt1, salt2)
}
func TestKDF_HashPassword(t *testing.T) {
kdf := New(DefaultConfig())
password := []byte("MySecureP@ssw0rd")
hash, err := kdf.HashPassword(password)
require.NoError(t, err)
// Check format
assert.True(t, strings.HasPrefix(hash, "$argon2id$"))
parts := strings.Split(hash, "$")
assert.Len(t, parts, 6)
// Verify password
valid, err := VerifyPassword(password, hash)
require.NoError(t, err)
assert.True(t, valid)
// Wrong password should fail
valid, err = VerifyPassword([]byte("wrong"), hash)
require.NoError(t, err)
assert.False(t, valid)
}
func TestVerifyPassword(t *testing.T) {
testCases := []struct {
name string
password string
hash string
valid bool
wantErr bool
}{
{
name: "valid password",
password: "password123",
hash: "$argon2id$v=19$m=65536,t=1,p=4$c2FsdC1tdXN0LWJlLWF0LWxlYXN0LTE2LWJ5dGVzISE$+4smaTt/N7ivKLrqsPIbTplUxDBRMxTKCYOcXWTJOEI",
valid: true,
},
{
name: "invalid password",
password: "wrongpassword",
hash: "$argon2id$v=19$m=65536,t=1,p=4$c2FsdC1tdXN0LWJlLWF0LWxlYXN0LTE2LWJ5dGVzISE$+4smaTt/N7ivKLrqsPIbTplUxDBRMxTKCYOcXWTJOEI",
valid: false,
},
{
name: "invalid format",
password: "password",
hash: "invalid-hash-format",
wantErr: true,
},
{
name: "wrong algorithm",
password: "password",
hash: "$bcrypt$v=19$m=65536,t=1,p=4$salt$hash",
wantErr: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
valid, err := VerifyPassword([]byte(tc.password), tc.hash)
if tc.wantErr {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, tc.valid, valid)
}
})
}
}
func TestConfigurations(t *testing.T) {
configs := map[string]*Config{
"default": DefaultConfig(),
"light": LightConfig(),
"high": HighSecurityConfig(),
}
for name, config := range configs {
t.Run(name, func(t *testing.T) {
err := ValidateConfig(config)
assert.NoError(t, err)
kdf := New(config)
password := []byte("test-password")
hash, err := kdf.HashPassword(password)
require.NoError(t, err)
valid, err := VerifyPassword(password, hash)
require.NoError(t, err)
assert.True(t, valid)
})
}
}
func TestValidateConfig(t *testing.T) {
testCases := []struct {
name string
config *Config
wantErr bool
errMsg string
}{
{
name: "valid config",
config: DefaultConfig(),
wantErr: false,
},
{
name: "time too low",
config: &Config{
Time: 0,
Memory: 64 * 1024,
Parallelism: 4,
SaltLength: 32,
KeyLength: 32,
},
wantErr: true,
errMsg: "time must be at least 1",
},
{
name: "memory too low",
config: &Config{
Time: 1,
Memory: 4 * 1024,
Parallelism: 4,
SaltLength: 32,
KeyLength: 32,
},
wantErr: true,
errMsg: "memory must be at least 8MB",
},
{
name: "parallelism too low",
config: &Config{
Time: 1,
Memory: 64 * 1024,
Parallelism: 0,
SaltLength: 32,
KeyLength: 32,
},
wantErr: true,
errMsg: "parallelism must be at least 1",
},
{
name: "salt too short",
config: &Config{
Time: 1,
Memory: 64 * 1024,
Parallelism: 4,
SaltLength: 4,
KeyLength: 32,
},
wantErr: true,
errMsg: "salt length must be at least 8 bytes",
},
{
name: "key too short",
config: &Config{
Time: 1,
Memory: 64 * 1024,
Parallelism: 4,
SaltLength: 32,
KeyLength: 8,
},
wantErr: true,
errMsg: "key length must be at least 16 bytes",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := ValidateConfig(tc.config)
if tc.wantErr {
assert.Error(t, err)
assert.Contains(t, err.Error(), tc.errMsg)
} else {
assert.NoError(t, err)
}
})
}
}
func TestCompareHashes(t *testing.T) {
hash1 := []byte("hash1")
hash2 := []byte("hash1")
hash3 := []byte("hash2")
assert.True(t, CompareHashes(hash1, hash2))
assert.False(t, CompareHashes(hash1, hash3))
assert.False(t, CompareHashes([]byte("short"), []byte("longer")))
}
func TestEstimateTime(t *testing.T) {
config := DefaultConfig()
estimate := EstimateTime(config, 1)
assert.NotEmpty(t, estimate)
assert.True(t, strings.HasSuffix(estimate, "ms") ||
strings.HasSuffix(estimate, "s") ||
strings.HasSuffix(estimate, "min"))
// Test different scales
estimate = EstimateTime(config, 100)
assert.NotEmpty(t, estimate)
// High security config should take longer
highConfig := HighSecurityConfig()
highEstimate := EstimateTime(highConfig, 1)
assert.NotEmpty(t, highEstimate)
}
func TestDecodeHash(t *testing.T) {
validHash := "$argon2id$v=19$m=65536,t=1,p=4$c2FsdA$aGFzaA"
config, salt, hash, err := decodeHash(validHash)
require.NoError(t, err)
assert.Equal(t, uint32(65536), config.Memory)
assert.Equal(t, uint32(1), config.Time)
assert.Equal(t, uint8(4), config.Parallelism)
assert.Equal(t, []byte("salt"), salt)
assert.Equal(t, []byte("hash"), hash)
// Test invalid formats
invalidHashes := []string{
"invalid",
"$bcrypt$v=19$m=65536,t=1,p=4$salt$hash",
"$argon2id$v=18$m=65536,t=1,p=4$salt$hash", // wrong version
"$argon2id$v=19$invalid$salt$hash",
"$argon2id$v=19$m=65536,t=1,p=4$!invalid!$hash",
}
for _, h := range invalidHashes {
_, _, _, err := decodeHash(h)
assert.Error(t, err)
}
}
func BenchmarkDeriveKey(b *testing.B) {
configs := map[string]*Config{
"light": LightConfig(),
"default": DefaultConfig(),
"high": HighSecurityConfig(),
}
password := []byte("benchmark-password")
salt := []byte("benchmark-salt-at-least-16-bytes")
for name, config := range configs {
b.Run(name, func(b *testing.B) {
kdf := New(config)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = kdf.DeriveKey(password, salt)
}
})
}
}
func BenchmarkHashPassword(b *testing.B) {
kdf := New(LightConfig()) // Use light config for benchmarks
password := []byte("benchmark-password")
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = kdf.HashPassword(password)
}
}
func BenchmarkVerifyPassword(b *testing.B) {
kdf := New(LightConfig())
password := []byte("benchmark-password")
hash, _ := kdf.HashPassword(password)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = VerifyPassword(password, hash)
}
}
func TestConcurrentDerivation(t *testing.T) {
kdf := New(DefaultConfig())
password := []byte("concurrent-test")
// Generate multiple salts
salts := make([][]byte, 10)
for i := range salts {
salt, err := kdf.GenerateSalt()
require.NoError(t, err)
salts[i] = salt
}
// Derive keys concurrently
results := make([][]byte, len(salts))
done := make(chan int, len(salts))
for i, salt := range salts {
go func(idx int, s []byte) {
results[idx] = kdf.DeriveKey(password, s)
done <- idx
}(i, salt)
}
// Wait for all goroutines
for i := 0; i < len(salts); i++ {
<-done
}
// Verify all keys are different (different salts)
for i := 0; i < len(results)-1; i++ {
for j := i + 1; j < len(results); j++ {
assert.False(t, bytes.Equal(results[i], results[j]))
}
}
}
func TestPerformanceBenchmark(t *testing.T) {
if testing.Short() {
t.Skip("Skipping performance benchmark in short mode")
}
configs := []struct {
name string
config *Config
maxMs int64
}{
{"light", LightConfig(), 100},
{"default", DefaultConfig(), 500},
}
password := []byte("perf-test")
for _, tc := range configs {
t.Run(tc.name, func(t *testing.T) {
kdf := New(tc.config)
salt, _ := kdf.GenerateSalt()
start := time.Now()
_ = kdf.DeriveKey(password, salt)
elapsed := time.Since(start).Milliseconds()
t.Logf("%s config took %dms", tc.name, elapsed)
assert.Less(t, elapsed, tc.maxMs,
"derivation took too long: %dms > %dms", elapsed, tc.maxMs)
})
}
}
+57
View File
@@ -0,0 +1,57 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bulletproof
import (
"github.com/pkg/errors"
"golang.org/x/crypto/sha3"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// generators contains a list of points to be used as generators for bulletproofs.
type generators []curves.Point
// ippGenerators holds generators necessary for an Inner Product Proof
// It includes a single u generator, and a list of generators divided in half to G and H
// See lines 10 on pg 16 of https://eprint.iacr.org/2017/1066.pdf
type ippGenerators struct {
G generators
H generators
}
// getGeneratorPoints generates generators using HashToCurve with Shake256(domain) as input
// lenVector is the length of the scalars used for the Inner Product Proof
// getGeneratorPoints will return 2*lenVector + 1 total points, split between a single u generator
// and G and H lists of vectors per the IPP specification
// See lines 10 on pg 16 of https://eprint.iacr.org/2017/1066.pdf
func getGeneratorPoints(lenVector int, domain []byte, curve curves.Curve) (*ippGenerators, error) {
shake := sha3.NewShake256()
_, err := shake.Write(domain)
if err != nil {
return nil, errors.Wrap(err, "getGeneratorPoints shake.Write")
}
numPoints := lenVector * 2
points := make([]curves.Point, numPoints)
for i := 0; i < numPoints; i++ {
bytes := [64]byte{}
_, err := shake.Read(bytes[:])
if err != nil {
return nil, errors.Wrap(err, "getGeneratorPoints shake.Read")
}
nextPoint := curve.Point.Hash(bytes[:])
points[i] = nextPoint
}
// Get G and H by splitting points in half
G, H, err := splitPointVector(points)
if err != nil {
return nil, errors.Wrap(err, "getGeneratorPoints splitPointVector")
}
out := ippGenerators{G: G, H: H}
return &out, nil
}
+61
View File
@@ -0,0 +1,61 @@
package bulletproof
import (
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/sha3"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestGeneratorsHappyPath(t *testing.T) {
curve := curves.ED25519()
gs, err := getGeneratorPoints(10, []byte("test"), *curve)
gsConcatenated := concatIPPGenerators(*gs)
require.NoError(t, err)
require.Len(t, gs.G, 10)
require.Len(t, gs.H, 10)
require.True(t, noDuplicates(gsConcatenated))
}
func TestGeneratorsUniquePerDomain(t *testing.T) {
curve := curves.ED25519()
gs1, err := getGeneratorPoints(10, []byte("test"), *curve)
gs1Concatenated := concatIPPGenerators(*gs1)
require.NoError(t, err)
gs2, err := getGeneratorPoints(10, []byte("test2"), *curve)
gs2Concatenated := concatIPPGenerators(*gs2)
require.NoError(t, err)
require.True(t, areDisjoint(gs1Concatenated, gs2Concatenated))
}
func noDuplicates(gs generators) bool {
seen := map[[32]byte]bool{}
for _, G := range gs {
value := sha3.Sum256(G.ToAffineCompressed())
if seen[value] {
return false
}
seen[value] = true
}
return true
}
func areDisjoint(gs1, gs2 generators) bool {
for _, g1 := range gs1 {
for _, g2 := range gs2 {
if g1.Equal(g2) {
return false
}
}
}
return true
}
func concatIPPGenerators(ippGens ippGenerators) generators {
var out generators
out = append(out, ippGens.G...)
out = append(out, ippGens.H...)
return out
}
+181
View File
@@ -0,0 +1,181 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bulletproof
import (
"github.com/pkg/errors"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// innerProduct takes two lists of scalars (a, b) and performs the dot product returning a single scalar.
func innerProduct(a, b []curves.Scalar) (curves.Scalar, error) {
if len(a) != len(b) {
return nil, errors.New("length of scalar vectors must be the same")
}
if len(a) < 1 {
return nil, errors.New("length of vectors must be at least one")
}
// Get a new scalar of value zero of the same curve as input arguments
innerProduct := a[0].Zero()
for i, aElem := range a {
bElem := b[i]
// innerProduct = aElem*bElem + innerProduct
innerProduct = aElem.MulAdd(bElem, innerProduct)
}
return innerProduct, nil
}
// splitPointVector takes a vector of points, splits it in half returning each half.
func splitPointVector(points []curves.Point) ([]curves.Point, []curves.Point, error) {
if len(points) < 1 {
return nil, nil, errors.New("length of points must be at least one")
}
if len(points)&0x01 != 0 {
return nil, nil, errors.New("length of points must be even")
}
nPrime := len(points) >> 1
firstHalf := points[:nPrime]
secondHalf := points[nPrime:]
return firstHalf, secondHalf, nil
}
// splitScalarVector takes a vector of scalars, splits it in half returning each half.
func splitScalarVector(scalars []curves.Scalar) ([]curves.Scalar, []curves.Scalar, error) {
if len(scalars) < 1 {
return nil, nil, errors.New("length of scalars must be at least one")
}
if len(scalars)&0x01 != 0 {
return nil, nil, errors.New("length of scalars must be even")
}
nPrime := len(scalars) >> 1
firstHalf := scalars[:nPrime]
secondHalf := scalars[nPrime:]
return firstHalf, secondHalf, nil
}
// multiplyScalarToPointVector takes a single scalar and a list of points, multiplies each point by scalar.
func multiplyScalarToPointVector(x curves.Scalar, g []curves.Point) []curves.Point {
products := make([]curves.Point, len(g))
for i, gElem := range g {
product := gElem.Mul(x)
products[i] = product
}
return products
}
// multiplyScalarToScalarVector takes a single scalar (x) and a list of scalars (a), multiplies each scalar in the vector by the scalar.
func multiplyScalarToScalarVector(x curves.Scalar, a []curves.Scalar) []curves.Scalar {
products := make([]curves.Scalar, len(a))
for i, aElem := range a {
product := aElem.Mul(x)
products[i] = product
}
return products
}
// multiplyPairwisePointVectors takes two lists of points (g, h) and performs a pairwise multiplication returning a list of points.
func multiplyPairwisePointVectors(g, h []curves.Point) ([]curves.Point, error) {
if len(g) != len(h) {
return nil, errors.New("length of point vectors must be the same")
}
product := make([]curves.Point, len(g))
for i, gElem := range g {
product[i] = gElem.Add(h[i])
}
return product, nil
}
// multiplyPairwiseScalarVectors takes two lists of points (a, b) and performs a pairwise multiplication returning a list of scalars.
func multiplyPairwiseScalarVectors(a, b []curves.Scalar) ([]curves.Scalar, error) {
if len(a) != len(b) {
return nil, errors.New("length of point vectors must be the same")
}
product := make([]curves.Scalar, len(a))
for i, aElem := range a {
product[i] = aElem.Mul(b[i])
}
return product, nil
}
// addPairwiseScalarVectors takes two lists of scalars (a, b) and performs a pairwise addition returning a list of scalars.
func addPairwiseScalarVectors(a, b []curves.Scalar) ([]curves.Scalar, error) {
if len(a) != len(b) {
return nil, errors.New("length of scalar vectors must be the same")
}
sum := make([]curves.Scalar, len(a))
for i, aElem := range a {
sum[i] = aElem.Add(b[i])
}
return sum, nil
}
// subtractPairwiseScalarVectors takes two lists of scalars (a, b) and performs a pairwise subtraction returning a list of scalars.
func subtractPairwiseScalarVectors(a, b []curves.Scalar) ([]curves.Scalar, error) {
if len(a) != len(b) {
return nil, errors.New("length of scalar vectors must be the same")
}
diff := make([]curves.Scalar, len(a))
for i, aElem := range a {
diff[i] = aElem.Sub(b[i])
}
return diff, nil
}
// invertScalars takes a list of scalars then returns a list with each element inverted.
func invertScalars(xs []curves.Scalar) ([]curves.Scalar, error) {
xinvs := make([]curves.Scalar, len(xs))
for i, x := range xs {
xinv, err := x.Invert()
if err != nil {
return nil, errors.Wrap(err, "bulletproof helpers invertx")
}
xinvs[i] = xinv
}
return xinvs, nil
}
// isPowerOfTwo returns whether a number i is a power of two or not.
func isPowerOfTwo(i int) bool {
return i&(i-1) == 0
}
// get2nVector returns a scalar vector 2^n such that [1, 2, 4, ... 2^(n-1)]
// See k^n and 2^n definitions on pg 12 of https://eprint.iacr.org/2017/1066.pdf
func get2nVector(length int, curve curves.Curve) []curves.Scalar {
vector2n := make([]curves.Scalar, length)
vector2n[0] = curve.Scalar.One()
for i := 1; i < length; i++ {
vector2n[i] = vector2n[i-1].Double()
}
return vector2n
}
func get1nVector(length int, curve curves.Curve) []curves.Scalar {
vector1n := make([]curves.Scalar, length)
for i := 0; i < length; i++ {
vector1n[i] = curve.Scalar.One()
}
return vector1n
}
func getknVector(k curves.Scalar, length int, curve curves.Curve) []curves.Scalar {
vectorkn := make([]curves.Scalar, length)
vectorkn[0] = curve.Scalar.One()
vectorkn[1] = k
for i := 2; i < length; i++ {
vectorkn[i] = vectorkn[i-1].Mul(k)
}
return vectorkn
}
+85
View File
@@ -0,0 +1,85 @@
package bulletproof
import (
crand "crypto/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestInnerProductHappyPath(t *testing.T) {
curve := curves.ED25519()
a := randScalarVec(3, *curve)
b := randScalarVec(3, *curve)
_, err := innerProduct(a, b)
require.NoError(t, err)
}
func TestInnerProductMismatchedLengths(t *testing.T) {
curve := curves.ED25519()
a := randScalarVec(3, *curve)
b := randScalarVec(4, *curve)
_, err := innerProduct(a, b)
require.Error(t, err)
}
func TestInnerProductEmptyVector(t *testing.T) {
curve := curves.ED25519()
a := randScalarVec(0, *curve)
b := randScalarVec(0, *curve)
_, err := innerProduct(a, b)
require.Error(t, err)
}
func TestInnerProductOut(t *testing.T) {
curve := curves.ED25519()
a := randScalarVec(2, *curve)
b := randScalarVec(2, *curve)
c, err := innerProduct(a, b)
require.NoError(t, err)
// Calculate manually a0*b0 + a1*b1
cPrime := a[0].Mul(b[0]).Add(a[1].Mul(b[1]))
require.Equal(t, c, cPrime)
}
func TestSplitListofPointsHappyPath(t *testing.T) {
curve := curves.ED25519()
points := randPointVec(10, *curve)
firstHalf, secondHalf, err := splitPointVector(points)
require.NoError(t, err)
require.Len(t, firstHalf, 5)
require.Len(t, secondHalf, 5)
}
func TestSplitListofPointsOddLength(t *testing.T) {
curve := curves.ED25519()
points := randPointVec(11, *curve)
_, _, err := splitPointVector(points)
require.Error(t, err)
}
func TestSplitListofPointsZeroLength(t *testing.T) {
curve := curves.ED25519()
points := randPointVec(0, *curve)
_, _, err := splitPointVector(points)
require.Error(t, err)
}
func randScalarVec(length int, curve curves.Curve) []curves.Scalar {
out := make([]curves.Scalar, length)
for i := 0; i < length; i++ {
out[i] = curve.Scalar.Random(crand.Reader)
}
return out
}
func randPointVec(length int, curve curves.Curve) []curves.Point {
out := make([]curves.Point, length)
for i := 0; i < length; i++ {
out[i] = curve.Point.Random(crand.Reader)
}
return out
}
+415
View File
@@ -0,0 +1,415 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// Package bulletproof implements the zero knowledge protocol bulletproofs as defined in https://eprint.iacr.org/2017/1066.pdf
package bulletproof
import (
"github.com/gtank/merlin"
"github.com/pkg/errors"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// InnerProductProver is the struct used to create InnerProductProofs
// It specifies which curve to use and holds precomputed generators
// See NewInnerProductProver() for prover initialization.
type InnerProductProver struct {
curve curves.Curve
generators ippGenerators
}
// InnerProductProof contains necessary output for the inner product proof
// a and b are the final input vectors of scalars, they should be of length 1
// Ls and Rs are calculated per recursion of the IPP and are necessary for verification
// See section 3.1 on pg 15 of https://eprint.iacr.org/2017/1066.pdf
type InnerProductProof struct {
a, b curves.Scalar
capLs, capRs []curves.Point
curve *curves.Curve
}
// ippRecursion is the same as IPP but tracks recursive a', b', g', h' and Ls and Rs
// It should only be used internally by InnerProductProver.Prove()
// See L35 on pg 16 of https://eprint.iacr.org/2017/1066.pdf
type ippRecursion struct {
a, b []curves.Scalar
c curves.Scalar
capLs, capRs []curves.Point
g, h []curves.Point
u, capP curves.Point
transcript *merlin.Transcript
}
// NewInnerProductProver initializes a new prover
// It uses the specified domain to generate generators for vectors of at most maxVectorLength
// A prover can be used to construct inner product proofs for vectors of length less than or equal to maxVectorLength
// A prover is defined by an explicit curve.
func NewInnerProductProver(
maxVectorLength int,
domain []byte,
curve curves.Curve,
) (*InnerProductProver, error) {
generators, err := getGeneratorPoints(maxVectorLength, domain, curve)
if err != nil {
return nil, errors.Wrap(err, "ipp getGenerators")
}
return &InnerProductProver{curve: curve, generators: *generators}, nil
}
// NewInnerProductProof initializes a new InnerProductProof for a specified curve
// This should be used in tandem with UnmarshalBinary() to convert a marshaled proof into the struct.
func NewInnerProductProof(curve *curves.Curve) *InnerProductProof {
var capLs, capRs []curves.Point
newProof := InnerProductProof{
a: curve.NewScalar(),
b: curve.NewScalar(),
capLs: capLs,
capRs: capRs,
curve: curve,
}
return &newProof
}
// rangeToIPP takes the output of a range proof and converts it into an inner product proof
// See section 4.2 on pg 20
// The conversion specifies generators to use (g and hPrime), as well as the two vectors l, r of which the inner product is tHat
// Additionally, note that the P used for the IPP is in fact P*h^-mu from the range proof.
func (prover *InnerProductProver) rangeToIPP(
proofG, proofH []curves.Point,
l, r []curves.Scalar,
tHat curves.Scalar,
capPhmuinv, u curves.Point,
transcript *merlin.Transcript,
) (*InnerProductProof, error) {
// Note that P as a witness is only g^l * h^r
// P needs to be in the form of g^l * h^r * u^<l,r>
// Calculate the final P including the u^<l,r> term
utHat := u.Mul(tHat)
capP := capPhmuinv.Add(utHat)
// Use params to prove inner product
recursionParams := &ippRecursion{
a: l,
b: r,
capLs: []curves.Point{},
capRs: []curves.Point{},
c: tHat,
g: proofG,
h: proofH,
capP: capP,
u: u,
transcript: transcript,
}
return prover.proveRecursive(recursionParams)
}
// getP returns the initial P value given two scalars a,b and point u
// This method should only be used for testing
// See (3) on page 13 of https://eprint.iacr.org/2017/1066.pdf
func (prover *InnerProductProver) getP(a, b []curves.Scalar, u curves.Point) (curves.Point, error) {
// Vectors must have length power of two
if !isPowerOfTwo(len(a)) {
return nil, errors.New("ipp vector length must be power of two")
}
// Generator vectors must be same length
if len(prover.generators.G) != len(prover.generators.H) {
return nil, errors.New("ipp generator lengths of g and h must be equal")
}
// Inner product requires len(a) == len(b) else error is returned
c, err := innerProduct(a, b)
if err != nil {
return nil, errors.Wrap(err, "ipp getInnerProduct")
}
// In case where len(a) is less than number of generators precomputed by prover, trim to length
proofG := prover.generators.G[0:len(a)]
proofH := prover.generators.H[0:len(b)]
// initial P = g^a * h^b * u^(a dot b) (See (3) on page 13 of https://eprint.iacr.org/2017/1066.pdf)
ga := prover.curve.NewGeneratorPoint().SumOfProducts(proofG, a)
hb := prover.curve.NewGeneratorPoint().SumOfProducts(proofH, b)
uadotb := u.Mul(c)
capP := ga.Add(hb).Add(uadotb)
return capP, nil
}
// Prove executes the prover protocol on pg 16 of https://eprint.iacr.org/2017/1066.pdf
// It generates an inner product proof for vectors a and b, using u to blind the inner product in P
// A transcript is used for the Fiat Shamir heuristic.
func (prover *InnerProductProver) Prove(
a, b []curves.Scalar,
u curves.Point,
transcript *merlin.Transcript,
) (*InnerProductProof, error) {
// Vectors must have length power of two
if !isPowerOfTwo(len(a)) {
return nil, errors.New("ipp vector length must be power of two")
}
// Generator vectors must be same length
if len(prover.generators.G) != len(prover.generators.H) {
return nil, errors.New("ipp generator lengths of g and h must be equal")
}
// Inner product requires len(a) == len(b) else error is returned
c, err := innerProduct(a, b)
if err != nil {
return nil, errors.Wrap(err, "ipp getInnerProduct")
}
// Length of vectors must be less than the number of generators generated
if len(a) > len(prover.generators.G) {
return nil, errors.New("ipp vector length must be less than maxVectorLength")
}
// In case where len(a) is less than number of generators precomputed by prover, trim to length
proofG := prover.generators.G[0:len(a)]
proofH := prover.generators.H[0:len(b)]
// initial P = g^a * h^b * u^(a dot b) (See (3) on page 13 of https://eprint.iacr.org/2017/1066.pdf)
ga := prover.curve.NewGeneratorPoint().SumOfProducts(proofG, a)
hb := prover.curve.NewGeneratorPoint().SumOfProducts(proofH, b)
uadotb := u.Mul(c)
capP := ga.Add(hb).Add(uadotb)
recursionParams := &ippRecursion{
a: a,
b: b,
capLs: []curves.Point{},
capRs: []curves.Point{},
c: c,
g: proofG,
h: proofH,
capP: capP,
u: u,
transcript: transcript,
}
return prover.proveRecursive(recursionParams)
}
// proveRecursive executes the recursion on pg 16 of https://eprint.iacr.org/2017/1066.pdf
func (prover *InnerProductProver) proveRecursive(
recursionParams *ippRecursion,
) (*InnerProductProof, error) {
// length checks
if len(recursionParams.a) != len(recursionParams.b) {
return nil, errors.New("ipp proveRecursive a and b different lengths")
}
if len(recursionParams.g) != len(recursionParams.h) {
return nil, errors.New("ipp proveRecursive g and h different lengths")
}
if len(recursionParams.a) != len(recursionParams.g) {
return nil, errors.New("ipp proveRecursive scalar and point vectors different lengths")
}
// Base case (L14, pg16 of https://eprint.iacr.org/2017/1066.pdf)
if len(recursionParams.a) == 1 {
proof := &InnerProductProof{
a: recursionParams.a[0],
b: recursionParams.b[0],
capLs: recursionParams.capLs,
capRs: recursionParams.capRs,
curve: &prover.curve,
}
return proof, nil
}
// Split current state into low (first half) vs high (second half) vectors
aLo, aHi, err := splitScalarVector(recursionParams.a)
if err != nil {
return nil, errors.Wrap(err, "recursionParams splitScalarVector")
}
bLo, bHi, err := splitScalarVector(recursionParams.b)
if err != nil {
return nil, errors.Wrap(err, "recursionParams splitScalarVector")
}
gLo, gHi, err := splitPointVector(recursionParams.g)
if err != nil {
return nil, errors.Wrap(err, "recursionParams splitPointVector")
}
hLo, hHi, err := splitPointVector(recursionParams.h)
if err != nil {
return nil, errors.Wrap(err, "recursionParams splitPointVector")
}
// c_l, c_r (L21,22, pg16 of https://eprint.iacr.org/2017/1066.pdf)
cL, err := innerProduct(aLo, bHi)
if err != nil {
return nil, errors.Wrap(err, "recursionParams innerProduct")
}
cR, err := innerProduct(aHi, bLo)
if err != nil {
return nil, errors.Wrap(err, "recursionParams innerProduct")
}
// L, R (L23,24, pg16 of https://eprint.iacr.org/2017/1066.pdf)
lga := prover.curve.Point.SumOfProducts(gHi, aLo)
lhb := prover.curve.Point.SumOfProducts(hLo, bHi)
ucL := recursionParams.u.Mul(cL)
capL := lga.Add(lhb).Add(ucL)
rga := prover.curve.Point.SumOfProducts(gLo, aHi)
rhb := prover.curve.Point.SumOfProducts(hHi, bLo)
ucR := recursionParams.u.Mul(cR)
capR := rga.Add(rhb).Add(ucR)
// Add L,R for verifier to use to calculate final g, h
newL := recursionParams.capLs
newL = append(newL, capL)
newR := recursionParams.capRs
newR = append(newR, capR)
// Get x from L, R for non-interactive (See section 4.4 pg22 of https://eprint.iacr.org/2017/1066.pdf)
// Note this replaces the interactive model, i.e. L36-28 of pg16 of https://eprint.iacr.org/2017/1066.pdf
x, err := prover.calcx(capL, capR, recursionParams.transcript)
if err != nil {
return nil, errors.Wrap(err, "recursionParams calcx")
}
// Calculate recursive inputs
xInv, err := x.Invert()
if err != nil {
return nil, errors.Wrap(err, "recursionParams x.Invert")
}
// g', h' (L29,30, pg16 of https://eprint.iacr.org/2017/1066.pdf)
gLoxInverse := multiplyScalarToPointVector(xInv, gLo)
gHix := multiplyScalarToPointVector(x, gHi)
gPrime, err := multiplyPairwisePointVectors(gLoxInverse, gHix)
if err != nil {
return nil, errors.Wrap(err, "recursionParams multiplyPairwisePointVectors")
}
hLox := multiplyScalarToPointVector(x, hLo)
hHixInv := multiplyScalarToPointVector(xInv, hHi)
hPrime, err := multiplyPairwisePointVectors(hLox, hHixInv)
if err != nil {
return nil, errors.Wrap(err, "recursionParams multiplyPairwisePointVectors")
}
// P' (L31, pg16 of https://eprint.iacr.org/2017/1066.pdf)
xSquare := x.Square()
xInvSquare := xInv.Square()
LxSquare := capL.Mul(xSquare)
RxInvSquare := capR.Mul(xInvSquare)
PPrime := LxSquare.Add(recursionParams.capP).Add(RxInvSquare)
// a', b' (L33, 34, pg16 of https://eprint.iacr.org/2017/1066.pdf)
aLox := multiplyScalarToScalarVector(x, aLo)
aHixIn := multiplyScalarToScalarVector(xInv, aHi)
aPrime, err := addPairwiseScalarVectors(aLox, aHixIn)
if err != nil {
return nil, errors.Wrap(err, "recursionParams addPairwiseScalarVectors")
}
bLoxInv := multiplyScalarToScalarVector(xInv, bLo)
bHix := multiplyScalarToScalarVector(x, bHi)
bPrime, err := addPairwiseScalarVectors(bLoxInv, bHix)
if err != nil {
return nil, errors.Wrap(err, "recursionParams addPairwiseScalarVectors")
}
// c'
cPrime, err := innerProduct(aPrime, bPrime)
if err != nil {
return nil, errors.Wrap(err, "recursionParams innerProduct")
}
// Make recursive call (L35, pg16 of https://eprint.iacr.org/2017/1066.pdf)
recursiveIPP := &ippRecursion{
a: aPrime,
b: bPrime,
capLs: newL,
capRs: newR,
c: cPrime,
g: gPrime,
h: hPrime,
capP: PPrime,
u: recursionParams.u,
transcript: recursionParams.transcript,
}
out, err := prover.proveRecursive(recursiveIPP)
if err != nil {
return nil, errors.Wrap(err, "recursionParams proveRecursive")
}
return out, nil
}
// calcx uses a merlin transcript for Fiat Shamir
// For each recursion, it takes the current state of the transcript and appends the newly calculated L and R values
// A new scalar is then read from the transcript
// See section 4.4 pg22 of https://eprint.iacr.org/2017/1066.pdf
func (prover *InnerProductProver) calcx(
capL, capR curves.Point,
transcript *merlin.Transcript,
) (curves.Scalar, error) {
// Add the newest capL and capR values to transcript
transcript.AppendMessage([]byte("addRecursiveL"), capL.ToAffineUncompressed())
transcript.AppendMessage([]byte("addRecursiveR"), capR.ToAffineUncompressed())
// Read 64 bytes from, set to scalar
outBytes := transcript.ExtractBytes([]byte("getx"), 64)
x, err := prover.curve.NewScalar().SetBytesWide(outBytes)
if err != nil {
return nil, errors.Wrap(err, "calcx NewScalar SetBytesWide")
}
return x, nil
}
// MarshalBinary takes an inner product proof and marshals into bytes.
func (proof *InnerProductProof) MarshalBinary() []byte {
var out []byte
out = append(out, proof.a.Bytes()...)
out = append(out, proof.b.Bytes()...)
for i, capLElem := range proof.capLs {
capRElem := proof.capRs[i]
out = append(out, capLElem.ToAffineCompressed()...)
out = append(out, capRElem.ToAffineCompressed()...)
}
return out
}
// UnmarshalBinary takes bytes of a marshaled proof and writes them into an inner product proof
// The inner product proof used should be from the output of NewInnerProductProof().
func (proof *InnerProductProof) UnmarshalBinary(data []byte) error {
scalarLen := len(proof.curve.NewScalar().Bytes())
pointLen := len(proof.curve.NewGeneratorPoint().ToAffineCompressed())
ptr := 0
// Get scalars
a, err := proof.curve.NewScalar().SetBytes(data[ptr : ptr+scalarLen])
if err != nil {
return errors.New("innerProductProof UnmarshalBinary SetBytes")
}
proof.a = a
ptr += scalarLen
b, err := proof.curve.NewScalar().SetBytes(data[ptr : ptr+scalarLen])
if err != nil {
return errors.New("innerProductProof UnmarshalBinary SetBytes")
}
proof.b = b
ptr += scalarLen
// Get points
var capLs, capRs []curves.Point //nolint:prealloc // pointer arithmetic makes it too unreadable.
for ptr < len(data) {
capLElem, err := proof.curve.Point.FromAffineCompressed(data[ptr : ptr+pointLen])
if err != nil {
return errors.New("innerProductProof UnmarshalBinary FromAffineCompressed")
}
capLs = append(capLs, capLElem)
ptr += pointLen
capRElem, err := proof.curve.Point.FromAffineCompressed(data[ptr : ptr+pointLen])
if err != nil {
return errors.New("innerProductProof UnmarshalBinary FromAffineCompressed")
}
capRs = append(capRs, capRElem)
ptr += pointLen
}
proof.capLs = capLs
proof.capRs = capRs
return nil
}
+99
View File
@@ -0,0 +1,99 @@
package bulletproof
import (
crand "crypto/rand"
"testing"
"github.com/gtank/merlin"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestIPPHappyPath(t *testing.T) {
curve := curves.ED25519()
prover, err := NewInnerProductProver(8, []byte("test"), *curve)
require.NoError(t, err)
a := randScalarVec(8, *curve)
b := randScalarVec(8, *curve)
u := curve.Point.Random(crand.Reader)
transcript := merlin.NewTranscript("test")
proof, err := prover.Prove(a, b, u, transcript)
require.NoError(t, err)
require.Equal(t, 3, len(proof.capLs))
require.Equal(t, 3, len(proof.capRs))
}
func TestIPPMismatchedVectors(t *testing.T) {
curve := curves.ED25519()
prover, err := NewInnerProductProver(8, []byte("test"), *curve)
require.NoError(t, err)
a := randScalarVec(4, *curve)
b := randScalarVec(8, *curve)
u := curve.Point.Random(crand.Reader)
transcript := merlin.NewTranscript("test")
_, err = prover.Prove(a, b, u, transcript)
require.Error(t, err)
}
func TestIPPNonPowerOfTwoLengthVectors(t *testing.T) {
curve := curves.ED25519()
prover, err := NewInnerProductProver(8, []byte("test"), *curve)
require.NoError(t, err)
a := randScalarVec(3, *curve)
b := randScalarVec(3, *curve)
u := curve.Point.Random(crand.Reader)
transcript := merlin.NewTranscript("test")
_, err = prover.Prove(a, b, u, transcript)
require.Error(t, err)
}
func TestIPPZeroLengthVectors(t *testing.T) {
curve := curves.ED25519()
prover, err := NewInnerProductProver(8, []byte("test"), *curve)
require.NoError(t, err)
a := randScalarVec(0, *curve)
b := randScalarVec(0, *curve)
u := curve.Point.Random(crand.Reader)
transcript := merlin.NewTranscript("test")
_, err = prover.Prove(a, b, u, transcript)
require.Error(t, err)
}
func TestIPPGreaterThanMaxLengthVectors(t *testing.T) {
curve := curves.ED25519()
prover, err := NewInnerProductProver(8, []byte("test"), *curve)
require.NoError(t, err)
a := randScalarVec(16, *curve)
b := randScalarVec(16, *curve)
u := curve.Point.Random(crand.Reader)
transcript := merlin.NewTranscript("test")
_, err = prover.Prove(a, b, u, transcript)
require.Error(t, err)
}
func TestIPPMarshal(t *testing.T) {
curve := curves.ED25519()
prover, err := NewInnerProductProver(8, []byte("test"), *curve)
require.NoError(t, err)
a := randScalarVec(8, *curve)
b := randScalarVec(8, *curve)
u := curve.Point.Random(crand.Reader)
transcript := merlin.NewTranscript("test")
proof, err := prover.Prove(a, b, u, transcript)
require.NoError(t, err)
proofMarshaled := proof.MarshalBinary()
proofPrime := NewInnerProductProof(curve)
err = proofPrime.UnmarshalBinary(proofMarshaled)
require.NoError(t, err)
require.Zero(t, proof.a.Cmp(proofPrime.a))
require.Zero(t, proof.b.Cmp(proofPrime.b))
for i, proofCapLElem := range proof.capLs {
proofPrimeCapLElem := proofPrime.capLs[i]
require.True(t, proofCapLElem.Equal(proofPrimeCapLElem))
proofCapRElem := proof.capRs[i]
proofPrimeCapRElem := proofPrime.capRs[i]
require.True(t, proofCapRElem.Equal(proofPrimeCapRElem))
}
}
+236
View File
@@ -0,0 +1,236 @@
package bulletproof
import (
"github.com/gtank/merlin"
"github.com/pkg/errors"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// InnerProductVerifier is the struct used to verify inner product proofs
// It specifies which curve to use and holds precomputed generators
// See NewInnerProductProver() for prover initialization.
type InnerProductVerifier struct {
curve curves.Curve
generators ippGenerators
}
// NewInnerProductVerifier initializes a new verifier
// It uses the specified domain to generate generators for vectors of at most maxVectorLength
// A verifier can be used to verify inner product proofs for vectors of length less than or equal to maxVectorLength
// A verifier is defined by an explicit curve.
func NewInnerProductVerifier(
maxVectorLength int,
domain []byte,
curve curves.Curve,
) (*InnerProductVerifier, error) {
generators, err := getGeneratorPoints(maxVectorLength, domain, curve)
if err != nil {
return nil, errors.Wrap(err, "ipp getGenerators")
}
return &InnerProductVerifier{curve: curve, generators: *generators}, nil
}
// Verify verifies the given proof inputs
// It implements the final comparison of section 3.1 on pg17 of https://eprint.iacr.org/2017/1066.pdf
func (verifier *InnerProductVerifier) Verify(
capP, u curves.Point,
proof *InnerProductProof,
transcript *merlin.Transcript,
) (bool, error) {
if len(proof.capLs) != len(proof.capRs) {
return false, errors.New("ipp capLs and capRs must be same length")
}
// Generator vectors must be same length
if len(verifier.generators.G) != len(verifier.generators.H) {
return false, errors.New("ipp generator lengths of g and h must be equal")
}
// Get generators for each elem in a, b and one more for u
// len(Ls) = log n, therefore can just exponentiate
n := 1 << len(proof.capLs)
// Length of vectors must be less than the number of generators generated
if n > len(verifier.generators.G) {
return false, errors.New("ipp vector length must be less than maxVectorLength")
}
// In case where len(a) is less than number of generators precomputed by prover, trim to length
proofG := verifier.generators.G[0:n]
proofH := verifier.generators.H[0:n]
xs, err := getxs(transcript, proof.capLs, proof.capRs, verifier.curve)
if err != nil {
return false, errors.Wrap(err, "verifier getxs")
}
s, err := verifier.getsNew(xs, n)
if err != nil {
return false, errors.Wrap(err, "verifier getss")
}
lhs, err := verifier.getLHS(u, proof, proofG, proofH, s)
if err != nil {
return false, errors.Wrap(err, "verify getLHS")
}
rhs, err := verifier.getRHS(capP, proof, xs)
if err != nil {
return false, errors.Wrap(err, "verify getRHS")
}
return lhs.Equal(rhs), nil
}
// Verify verifies the given proof inputs
// It implements the final comparison of section 3.1 on pg17 of https://eprint.iacr.org/2017/1066.pdf
func (verifier *InnerProductVerifier) VerifyFromRangeProof(
proofG, proofH []curves.Point,
capPhmuinv, u curves.Point,
tHat curves.Scalar,
proof *InnerProductProof,
transcript *merlin.Transcript,
) (bool, error) {
// Get generators for each elem in a, b and one more for u
// len(Ls) = log n, therefore can just exponentiate
n := 1 << len(proof.capLs)
xs, err := getxs(transcript, proof.capLs, proof.capRs, verifier.curve)
if err != nil {
return false, errors.Wrap(err, "verifier getxs")
}
s, err := verifier.gets(xs, n)
if err != nil {
return false, errors.Wrap(err, "verifier getss")
}
lhs, err := verifier.getLHS(u, proof, proofG, proofH, s)
if err != nil {
return false, errors.Wrap(err, "verify getLHS")
}
utHat := u.Mul(tHat)
capP := capPhmuinv.Add(utHat)
rhs, err := verifier.getRHS(capP, proof, xs)
if err != nil {
return false, errors.Wrap(err, "verify getRHS")
}
return lhs.Equal(rhs), nil
}
// getRHS gets the right hand side of the final comparison of section 3.1 on pg17.
func (*InnerProductVerifier) getRHS(
capP curves.Point,
proof *InnerProductProof,
xs []curves.Scalar,
) (curves.Point, error) {
product := capP
for j, Lj := range proof.capLs {
Rj := proof.capRs[j]
xj := xs[j]
xjSquare := xj.Square()
xjSquareInv, err := xjSquare.Invert()
if err != nil {
return nil, errors.Wrap(err, "verify invert")
}
LjxjSquare := Lj.Mul(xjSquare)
RjxjSquareInv := Rj.Mul(xjSquareInv)
product = product.Add(LjxjSquare).Add(RjxjSquareInv)
}
return product, nil
}
// getLHS gets the left hand side of the final comparison of section 3.1 on pg17.
func (verifier *InnerProductVerifier) getLHS(
u curves.Point,
proof *InnerProductProof,
g, h []curves.Point,
s []curves.Scalar,
) (curves.Point, error) {
sInv, err := invertScalars(s)
if err != nil {
return nil, errors.Wrap(err, "verify invertScalars")
}
// g^(a*s)
as := multiplyScalarToScalarVector(proof.a, s)
gas := verifier.curve.Point.SumOfProducts(g, as)
// h^(b*s^-1)
bsInv := multiplyScalarToScalarVector(proof.b, sInv)
hbsInv := verifier.curve.Point.SumOfProducts(h, bsInv)
// u^a*b
ab := proof.a.Mul(proof.b)
uab := u.Mul(ab)
// g^(a*s) * h^(b*s^-1) * u^a*b
out := gas.Add(hbsInv).Add(uab)
return out, nil
}
// getxs calculates the x values from Ls and Rs
// Note that each x is read from the transcript, then the L and R at a certain index are written to the transcript
// This mirrors the reading of xs and writing of Ls and Rs in the prover.
func getxs(
transcript *merlin.Transcript,
capLs, capRs []curves.Point,
curve curves.Curve,
) ([]curves.Scalar, error) {
xs := make([]curves.Scalar, len(capLs))
for i, capLi := range capLs {
capRi := capRs[i]
// Add the newest L and R values to transcript
transcript.AppendMessage([]byte("addRecursiveL"), capLi.ToAffineUncompressed())
transcript.AppendMessage([]byte("addRecursiveR"), capRi.ToAffineUncompressed())
// Read 64 bytes from, set to scalar
outBytes := transcript.ExtractBytes([]byte("getx"), 64)
x, err := curve.NewScalar().SetBytesWide(outBytes)
if err != nil {
return nil, errors.Wrap(err, "calcx NewScalar SetBytesWide")
}
xs[i] = x
}
return xs, nil
}
// gets calculates the vector s of values used for verification
// See the second expression of section 3.1 on pg15
// nolint
func (verifier *InnerProductVerifier) gets(xs []curves.Scalar, n int) ([]curves.Scalar, error) {
ss := make([]curves.Scalar, n)
for i := 0; i < n; i++ {
si := verifier.curve.Scalar.One()
for j, xj := range xs {
if i>>(len(xs)-j-1)&0x01 == 1 {
si = si.Mul(xj)
} else {
xjInverse, err := xj.Invert()
if err != nil {
return nil, errors.Wrap(err, "getss invert")
}
si = si.Mul(xjInverse)
}
}
ss[i] = si
}
return ss, nil
}
// getsNew calculates the vector s of values used for verification
// It provides analogous functionality as gets(), but uses a O(n) algorithm vs O(nlogn)
// The algorithm inverts all xs, then begins multiplying the inversion by the square of x elements to
// calculate all s values thus minimizing necessary inversions/ computation.
func (verifier *InnerProductVerifier) getsNew(xs []curves.Scalar, n int) ([]curves.Scalar, error) {
var err error
ss := make([]curves.Scalar, n)
// First element is all xs inverted mul'd
ss[0] = verifier.curve.Scalar.One()
for _, xj := range xs {
ss[0] = ss[0].Mul(xj)
}
ss[0], err = ss[0].Invert()
if err != nil {
return nil, errors.Wrap(err, "ipp gets inv ss0")
}
for j, xj := range xs {
xjSquared := xj.Square()
for i := 0; i < n; i += 1 << (len(xs) - j) {
ss[i+1<<(len(xs)-j-1)] = ss[i].Mul(xjSquared)
}
}
return ss, nil
}
+79
View File
@@ -0,0 +1,79 @@
package bulletproof
import (
crand "crypto/rand"
"testing"
"github.com/gtank/merlin"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestIPPVerifyHappyPath(t *testing.T) {
curve := curves.ED25519()
vecLength := 256
prover, err := NewInnerProductProver(vecLength, []byte("test"), *curve)
require.NoError(t, err)
a := randScalarVec(vecLength, *curve)
b := randScalarVec(vecLength, *curve)
u := curve.Point.Random(crand.Reader)
transcriptProver := merlin.NewTranscript("test")
proof, err := prover.Prove(a, b, u, transcriptProver)
require.NoError(t, err)
verifier, err := NewInnerProductVerifier(vecLength, []byte("test"), *curve)
require.NoError(t, err)
capP, err := prover.getP(a, b, u)
require.NoError(t, err)
transcriptVerifier := merlin.NewTranscript("test")
verified, err := verifier.Verify(capP, u, proof, transcriptVerifier)
require.NoError(t, err)
require.True(t, verified)
}
func BenchmarkIPPVerification(bench *testing.B) {
curve := curves.ED25519()
vecLength := 1024
prover, _ := NewInnerProductProver(vecLength, []byte("test"), *curve)
a := randScalarVec(vecLength, *curve)
b := randScalarVec(vecLength, *curve)
u := curve.Point.Random(crand.Reader)
transcriptProver := merlin.NewTranscript("test")
proof, _ := prover.Prove(a, b, u, transcriptProver)
verifier, _ := NewInnerProductVerifier(vecLength, []byte("test"), *curve)
capP, _ := prover.getP(a, b, u)
transcriptVerifier := merlin.NewTranscript("test")
verified, _ := verifier.Verify(capP, u, proof, transcriptVerifier)
require.True(bench, verified)
}
func TestIPPVerifyInvalidProof(t *testing.T) {
curve := curves.ED25519()
vecLength := 64
prover, err := NewInnerProductProver(vecLength, []byte("test"), *curve)
require.NoError(t, err)
a := randScalarVec(vecLength, *curve)
b := randScalarVec(vecLength, *curve)
u := curve.Point.Random(crand.Reader)
aPrime := randScalarVec(64, *curve)
bPrime := randScalarVec(64, *curve)
uPrime := curve.Point.Random(crand.Reader)
transcriptProver := merlin.NewTranscript("test")
proofPrime, err := prover.Prove(aPrime, bPrime, uPrime, transcriptProver)
require.NoError(t, err)
verifier, err := NewInnerProductVerifier(vecLength, []byte("test"), *curve)
require.NoError(t, err)
capP, err := prover.getP(a, b, u)
require.NoError(t, err)
transcriptVerifier := merlin.NewTranscript("test")
// Check for different capP, u from proof
verified, err := verifier.Verify(capP, u, proofPrime, transcriptVerifier)
require.NoError(t, err)
require.False(t, verified)
}
+386
View File
@@ -0,0 +1,386 @@
package bulletproof
import (
crand "crypto/rand"
"github.com/gtank/merlin"
"github.com/pkg/errors"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// BatchProve proves that a list of scalars v are in the range n.
// It implements the aggregating logarithmic proofs defined on pg21.
// Instead of taking a single value and a single blinding factor, BatchProve takes in a list of values and list of
// blinding factors.
func (prover *RangeProver) BatchProve(
v, gamma []curves.Scalar,
n int,
proofGenerators RangeProofGenerators,
transcript *merlin.Transcript,
) (*RangeProof, error) {
// Define nm as the total bits required for secrets, calculated as number of secrets * n
m := len(v)
nm := n * m
// nm must be less than or equal to the number of generators generated
if nm > len(prover.generators.G) {
return nil, errors.New("ipp vector length must be less than or equal to maxVectorLength")
}
// In case where nm is less than number of generators precomputed by prover, trim to length
proofG := prover.generators.G[0:nm]
proofH := prover.generators.H[0:nm]
// Check that each elem in v is in range [0, 2^n]
for _, vi := range v {
checkedRange := checkRange(vi, n)
if checkedRange != nil {
return nil, checkedRange
}
}
// L40 on pg19
aL, err := getaLBatched(v, n, prover.curve)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
onenm := get1nVector(nm, prover.curve)
// L41 on pg19
aR, err := subtractPairwiseScalarVectors(aL, onenm)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
alpha := prover.curve.Scalar.Random(crand.Reader)
// Calc A (L44, pg19)
halpha := proofGenerators.h.Mul(alpha)
gaL := prover.curve.Point.SumOfProducts(proofG, aL)
haR := prover.curve.Point.SumOfProducts(proofH, aR)
capA := halpha.Add(gaL).Add(haR)
// L45, 46, pg19
sL := getBlindingVector(nm, prover.curve)
sR := getBlindingVector(nm, prover.curve)
rho := prover.curve.Scalar.Random(crand.Reader)
// Calc S (L47, pg19)
hrho := proofGenerators.h.Mul(rho)
gsL := prover.curve.Point.SumOfProducts(proofG, sL)
hsR := prover.curve.Point.SumOfProducts(proofH, sR)
capS := hrho.Add(gsL).Add(hsR)
// Fiat Shamir for y,z (L49, pg19)
capV := getcapVBatched(v, gamma, proofGenerators.g, proofGenerators.h)
y, z, err := calcyzBatched(capV, capA, capS, transcript, prover.curve)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// Calc t_1, t_2
// See the l(X), r(X), equations on pg 21
// Use l(X)'s and r(X)'s constant and linear terms to derive t_1 and t_2
// (a_l - z*1^n)
zonenm := multiplyScalarToScalarVector(z, onenm)
constantTerml, err := subtractPairwiseScalarVectors(aL, zonenm)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
linearTerml := sL
// zSum term, see equation 71 on pg21
zSum := getSumTermrXBatched(z, n, len(v), prover.curve)
// a_r + z*1^nm
aRPluszonenm, err := addPairwiseScalarVectors(aR, zonenm)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
ynm := getknVector(y, nm, prover.curve)
hadamard, err := multiplyPairwiseScalarVectors(ynm, aRPluszonenm)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
constantTermr, err := addPairwiseScalarVectors(hadamard, zSum)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
linearTermr, err := multiplyPairwiseScalarVectors(ynm, sR)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// t_1 (as the linear coefficient) is the sum of the dot products of l(X)'s linear term dot r(X)'s constant term
// and r(X)'s linear term dot l(X)'s constant term
t1FirstTerm, err := innerProduct(linearTerml, constantTermr)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
t1SecondTerm, err := innerProduct(linearTermr, constantTerml)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
t1 := t1FirstTerm.Add(t1SecondTerm)
// t_2 (as the quadratic coefficient) is the dot product of l(X)'s and r(X)'s linear terms
t2, err := innerProduct(linearTerml, linearTermr)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// L52, pg20
tau1 := prover.curve.Scalar.Random(crand.Reader)
tau2 := prover.curve.Scalar.Random(crand.Reader)
// T_1, T_2 (L53, pg20)
capT1 := proofGenerators.g.Mul(t1).Add(proofGenerators.h.Mul(tau1))
capT2 := proofGenerators.g.Mul(t2).Add(proofGenerators.h.Mul(tau2))
// Fiat shamir for x (L55, pg20)
x, err := calcx(capT1, capT2, transcript, prover.curve)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// Calc l
// Instead of using the expression in the line, evaluate l() at x
sLx := multiplyScalarToScalarVector(x, linearTerml)
l, err := addPairwiseScalarVectors(constantTerml, sLx)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// Calc r
// Instead of using the expression in the line, evaluate r() at x
ynsRx := multiplyScalarToScalarVector(x, linearTermr)
r, err := addPairwiseScalarVectors(constantTermr, ynsRx)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// Calc t hat
// For efficiency, instead of calculating the dot product, evaluate t() at x
zm := getknVector(z, m, prover.curve)
zsquarezm := multiplyScalarToScalarVector(z.Square(), zm)
sumv := prover.curve.Scalar.Zero()
for i := 0; i < m; i++ {
elem := zsquarezm[i].Mul(v[i])
sumv = sumv.Add(elem)
}
deltayzBatched, err := deltayzBatched(y, z, n, m, prover.curve)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
t0 := sumv.Add(deltayzBatched)
tLinear := t1.Mul(x)
tQuadratic := t2.Mul(x.Square())
tHat := t0.Add(tLinear).Add(tQuadratic)
// Calc tau_x (L61, pg20)
tau2xsquare := tau2.Mul(x.Square())
tau1x := tau1.Mul(x)
zsum := prover.curve.Scalar.Zero()
zExp := z.Clone()
for j := 1; j < m+1; j++ {
zExp = zExp.Mul(z)
zsum = zsum.Add(zExp.Mul(gamma[j-1]))
}
taux := tau2xsquare.Add(tau1x).Add(zsum)
// Calc mu (L62, pg20)
mu := alpha.Add(rho.Mul(x))
// Calc IPP (See section 4.2)
hPrime, err := gethPrime(proofH, y, prover.curve)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// P is redefined in batched case, see bottom equation on pg21.
capPhmu := getPhmuBatched(
proofG,
hPrime,
proofGenerators.h,
capA,
capS,
x,
y,
z,
mu,
n,
m,
prover.curve,
)
wBytes := transcript.ExtractBytes([]byte("getw"), 64)
w, err := prover.curve.NewScalar().SetBytesWide(wBytes)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
ipp, err := prover.ippProver.rangeToIPP(
proofG,
hPrime,
l,
r,
tHat,
capPhmu,
proofGenerators.u.Mul(w),
transcript,
)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
out := &RangeProof{
capA: capA,
capS: capS,
capT1: capT1,
capT2: capT2,
taux: taux,
mu: mu,
tHat: tHat,
ipp: ipp,
curve: &prover.curve,
}
return out, nil
}
// See final term of L71 on pg 21
// Sigma_{j=1}^{m} z^{1+j} * (0^{(j-1)*n} || 2^{n} || 0^{(m-j)*n}).
func getSumTermrXBatched(z curves.Scalar, n, m int, curve curves.Curve) []curves.Scalar {
twoN := get2nVector(n, curve)
var out []curves.Scalar
// The final power should be one more than m
zExp := z.Clone()
for j := 0; j < m; j++ {
zExp = zExp.Mul(z)
elem := multiplyScalarToScalarVector(zExp, twoN)
out = append(out, elem...)
}
return out
}
func getcapVBatched(v, gamma []curves.Scalar, g, h curves.Point) []curves.Point {
out := make([]curves.Point, len(v))
for i, vi := range v {
out[i] = getcapV(vi, gamma[i], g, h)
}
return out
}
func getaLBatched(v []curves.Scalar, n int, curve curves.Curve) ([]curves.Scalar, error) {
var aL []curves.Scalar
for _, vi := range v {
aLi, err := getaL(vi, n, curve)
if err != nil {
return nil, err
}
aL = append(aL, aLi...)
}
return aL, nil
}
func calcyzBatched(
capV []curves.Point,
capA, capS curves.Point,
transcript *merlin.Transcript,
curve curves.Curve,
) (curves.Scalar, curves.Scalar, error) {
// Add the A,S values to transcript
for _, capVi := range capV {
transcript.AppendMessage([]byte("addV"), capVi.ToAffineUncompressed())
}
transcript.AppendMessage([]byte("addcapA"), capA.ToAffineUncompressed())
transcript.AppendMessage([]byte("addcapS"), capS.ToAffineUncompressed())
// Read 64 bytes twice from, set to scalar for y and z
yBytes := transcript.ExtractBytes([]byte("gety"), 64)
y, err := curve.NewScalar().SetBytesWide(yBytes)
if err != nil {
return nil, nil, errors.Wrap(err, "calcyz NewScalar SetBytesWide")
}
zBytes := transcript.ExtractBytes([]byte("getz"), 64)
z, err := curve.NewScalar().SetBytesWide(zBytes)
if err != nil {
return nil, nil, errors.Wrap(err, "calcyz NewScalar SetBytesWide")
}
return y, z, nil
}
func deltayzBatched(y, z curves.Scalar, n, m int, curve curves.Curve) (curves.Scalar, error) {
// z - z^2
zMinuszsquare := z.Sub(z.Square())
// 1^(n*m)
onenm := get1nVector(n*m, curve)
// <1^nm, y^nm>
onenmdotynm, err := innerProduct(onenm, getknVector(y, n*m, curve))
if err != nil {
return nil, errors.Wrap(err, "deltayz")
}
// (z - z^2)*<1^n, y^n>
termFirst := zMinuszsquare.Mul(onenmdotynm)
// <1^n, 2^n>
onendottwon, err := innerProduct(get1nVector(n, curve), get2nVector(n, curve))
if err != nil {
return nil, errors.Wrap(err, "deltayz")
}
termSecond := curve.Scalar.Zero()
zExp := z.Square()
for j := 1; j < m+1; j++ {
zExp = zExp.Mul(z)
elem := zExp.Mul(onendottwon)
termSecond = termSecond.Add(elem)
}
// (z - z^2)*<1^n, y^n> - z^3*<1^n, 2^n>
out := termFirst.Sub(termSecond)
return out, nil
}
// Bottom equation on pg21.
func getPhmuBatched(
proofG, proofHPrime []curves.Point,
h, capA, capS curves.Point,
x, y, z, mu curves.Scalar,
n, m int,
curve curves.Curve,
) curves.Point {
twoN := get2nVector(n, curve)
// h'^(z*y^n + z^2*2^n)
lastElem := curve.NewIdentityPoint()
zExp := z.Clone()
for j := 1; j < m+1; j++ {
// Get subvector of h
hSubvector := proofHPrime[(j-1)*n : j*n]
// z^(j+1)
zExp = zExp.Mul(z)
exp := multiplyScalarToScalarVector(zExp, twoN)
// Final elem
elem := curve.Point.SumOfProducts(hSubvector, exp)
lastElem = lastElem.Add(elem)
}
zynm := multiplyScalarToScalarVector(z, getknVector(y, n*m, curve))
hPrimezynm := curve.Point.SumOfProducts(proofHPrime, zynm)
lastElem = lastElem.Add(hPrimezynm)
// S^x
capSx := capS.Mul(x)
// g^-z --> -z*<1,g>
onenm := get1nVector(n*m, curve)
zNeg := z.Neg()
zinvonen := multiplyScalarToScalarVector(zNeg, onenm)
zgdotonen := curve.Point.SumOfProducts(proofG, zinvonen)
// L66 on pg20
P := capA.Add(capSx).Add(zgdotonen).Add(lastElem)
hmu := h.Mul(mu)
Phmu := P.Sub(hmu)
return Phmu
}
+102
View File
@@ -0,0 +1,102 @@
package bulletproof
import (
crand "crypto/rand"
"testing"
"github.com/gtank/merlin"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestRangeBatchProverHappyPath(t *testing.T) {
curve := curves.ED25519()
n := 256
prover, err := NewRangeProver(n*4, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
v1 := curve.Scalar.Random(crand.Reader)
v2 := curve.Scalar.Random(crand.Reader)
v3 := curve.Scalar.Random(crand.Reader)
v4 := curve.Scalar.Random(crand.Reader)
v := []curves.Scalar{v1, v2, v3, v4}
gamma1 := curve.Scalar.Random(crand.Reader)
gamma2 := curve.Scalar.Random(crand.Reader)
gamma3 := curve.Scalar.Random(crand.Reader)
gamma4 := curve.Scalar.Random(crand.Reader)
gamma := []curves.Scalar{gamma1, gamma2, gamma3, gamma4}
g := curve.Point.Random(crand.Reader)
h := curve.Point.Random(crand.Reader)
u := curve.Point.Random(crand.Reader)
proofGenerators := RangeProofGenerators{
g: g,
h: h,
u: u,
}
transcript := merlin.NewTranscript("test")
proof, err := prover.BatchProve(v, gamma, n, proofGenerators, transcript)
require.NoError(t, err)
require.NotNil(t, proof)
require.Equal(t, 10, len(proof.ipp.capLs))
require.Equal(t, 10, len(proof.ipp.capRs))
}
func TestGetaLBatched(t *testing.T) {
curve := curves.ED25519()
v1 := curve.Scalar.Random(crand.Reader)
v2 := curve.Scalar.Random(crand.Reader)
v3 := curve.Scalar.Random(crand.Reader)
v4 := curve.Scalar.Random(crand.Reader)
v := []curves.Scalar{v1, v2, v3, v4}
aL, err := getaLBatched(v, 256, *curve)
require.NoError(t, err)
twoN := get2nVector(256, *curve)
for i := 1; i < len(v)+1; i++ {
vec := aL[(i-1)*256 : i*256]
product, err := innerProduct(vec, twoN)
require.NoError(t, err)
require.Zero(t, product.Cmp(v[i-1]))
}
}
func TestRangeBatchProverMarshal(t *testing.T) {
curve := curves.ED25519()
n := 256
prover, err := NewRangeProver(n*4, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
v1 := curve.Scalar.Random(crand.Reader)
v2 := curve.Scalar.Random(crand.Reader)
v3 := curve.Scalar.Random(crand.Reader)
v4 := curve.Scalar.Random(crand.Reader)
v := []curves.Scalar{v1, v2, v3, v4}
gamma1 := curve.Scalar.Random(crand.Reader)
gamma2 := curve.Scalar.Random(crand.Reader)
gamma3 := curve.Scalar.Random(crand.Reader)
gamma4 := curve.Scalar.Random(crand.Reader)
gamma := []curves.Scalar{gamma1, gamma2, gamma3, gamma4}
g := curve.Point.Random(crand.Reader)
h := curve.Point.Random(crand.Reader)
u := curve.Point.Random(crand.Reader)
proofGenerators := RangeProofGenerators{
g: g,
h: h,
u: u,
}
transcript := merlin.NewTranscript("test")
proof, err := prover.BatchProve(v, gamma, n, proofGenerators, transcript)
require.NoError(t, err)
proofMarshaled := proof.MarshalBinary()
proofPrime := NewRangeProof(curve)
err = proofPrime.UnmarshalBinary(proofMarshaled)
require.NoError(t, err)
require.True(t, proof.capA.Equal(proofPrime.capA))
require.True(t, proof.capS.Equal(proofPrime.capS))
require.True(t, proof.capT1.Equal(proofPrime.capT1))
require.True(t, proof.capT2.Equal(proofPrime.capT2))
require.Zero(t, proof.taux.Cmp(proofPrime.taux))
require.Zero(t, proof.mu.Cmp(proofPrime.mu))
require.Zero(t, proof.tHat.Cmp(proofPrime.tHat))
}
+133
View File
@@ -0,0 +1,133 @@
package bulletproof
import (
"github.com/gtank/merlin"
"github.com/pkg/errors"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// VerifyBatched verifies a given batched range proof.
// It takes in a list of commitments to the secret values as capV instead of a single commitment to a single point
// when compared to the unbatched single range proof case.
func (verifier *RangeVerifier) VerifyBatched(
proof *RangeProof,
capV []curves.Point,
proofGenerators RangeProofGenerators,
n int,
transcript *merlin.Transcript,
) (bool, error) {
// Define nm as the total bits required for secrets, calculated as number of secrets * n
m := len(capV)
nm := n * m
// nm must be less than the number of generators generated
if nm > len(verifier.generators.G) {
return false, errors.New("ipp vector length must be less than maxVectorLength")
}
// In case where len(a) is less than number of generators precomputed by prover, trim to length
proofG := verifier.generators.G[0:nm]
proofH := verifier.generators.H[0:nm]
// Calc y,z,x from Fiat Shamir heuristic
y, z, err := calcyzBatched(capV, proof.capA, proof.capS, transcript, verifier.curve)
if err != nil {
return false, errors.Wrap(err, "rangeproof verify")
}
x, err := calcx(proof.capT1, proof.capT2, transcript, verifier.curve)
if err != nil {
return false, errors.Wrap(err, "rangeproof verify")
}
wBytes := transcript.ExtractBytes([]byte("getw"), 64)
w, err := verifier.curve.NewScalar().SetBytesWide(wBytes)
if err != nil {
return false, errors.Wrap(err, "rangeproof prove")
}
// Calc delta(y,z), redefined for batched case on pg21
deltayzBatched, err := deltayzBatched(y, z, n, m, verifier.curve)
if err != nil {
return false, errors.Wrap(err, "rangeproof verify")
}
// Check tHat: L65, pg20
// See equation 72 on pg21
tHatIsValid := verifier.checktHatBatched(
proof,
capV,
proofGenerators.g,
proofGenerators.h,
deltayzBatched,
x,
z,
m,
)
if !tHatIsValid {
return false, errors.New("rangeproof verify tHat is invalid")
}
// Verify IPP
hPrime, err := gethPrime(proofH, y, verifier.curve)
if err != nil {
return false, errors.Wrap(err, "rangeproof verify")
}
capPhmu := getPhmuBatched(
proofG,
hPrime,
proofGenerators.h,
proof.capA,
proof.capS,
x,
y,
z,
proof.mu,
n,
m,
verifier.curve,
)
ippVerified, err := verifier.ippVerifier.VerifyFromRangeProof(
proofG,
hPrime,
capPhmu,
proofGenerators.u.Mul(w),
proof.tHat,
proof.ipp,
transcript,
)
if err != nil {
return false, errors.Wrap(err, "rangeproof verify")
}
return ippVerified, nil
}
// L65, pg20.
func (verifier *RangeVerifier) checktHatBatched(
proof *RangeProof,
capV []curves.Point,
g, h curves.Point,
deltayz, x, z curves.Scalar,
m int,
) bool {
// g^tHat * h^tau_x
gtHat := g.Mul(proof.tHat)
htaux := h.Mul(proof.taux)
lhs := gtHat.Add(htaux)
// V^z^2 * g^delta(y,z) * Tau_1^x * Tau_2^x^2
// g^delta(y,z) * V^(z^2*z^m) * Tau_1^x * Tau_2^x^2
zm := getknVector(z, m, verifier.curve)
zsquarezm := multiplyScalarToScalarVector(z.Square(), zm)
capVzsquaretwom := verifier.curve.Point.SumOfProducts(capV, zsquarezm)
gdeltayz := g.Mul(deltayz)
capTau1x := proof.capT1.Mul(x)
capTau2xsquare := proof.capT2.Mul(x.Square())
rhs := capVzsquaretwom.Add(gdeltayz).Add(capTau1x).Add(capTau2xsquare)
// Compare lhs =? rhs
return lhs.Equal(rhs)
}
+148
View File
@@ -0,0 +1,148 @@
package bulletproof
import (
crand "crypto/rand"
"testing"
"github.com/gtank/merlin"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestRangeBatchVerifyHappyPath(t *testing.T) {
curve := curves.ED25519()
n := 256
prover, err := NewRangeProver(n*4, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
v1 := curve.Scalar.Random(crand.Reader)
v2 := curve.Scalar.Random(crand.Reader)
v3 := curve.Scalar.Random(crand.Reader)
v4 := curve.Scalar.Random(crand.Reader)
v := []curves.Scalar{v1, v2, v3, v4}
gamma1 := curve.Scalar.Random(crand.Reader)
gamma2 := curve.Scalar.Random(crand.Reader)
gamma3 := curve.Scalar.Random(crand.Reader)
gamma4 := curve.Scalar.Random(crand.Reader)
gamma := []curves.Scalar{gamma1, gamma2, gamma3, gamma4}
g := curve.Point.Random(crand.Reader)
h := curve.Point.Random(crand.Reader)
u := curve.Point.Random(crand.Reader)
proofGenerators := RangeProofGenerators{
g: g,
h: h,
u: u,
}
transcript := merlin.NewTranscript("test")
proof, err := prover.BatchProve(v, gamma, n, proofGenerators, transcript)
require.NoError(t, err)
verifier, err := NewRangeVerifier(n*4, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
transcriptVerifier := merlin.NewTranscript("test")
capV := getcapVBatched(v, gamma, g, h)
verified, err := verifier.VerifyBatched(proof, capV, proofGenerators, n, transcriptVerifier)
require.NoError(t, err)
require.True(t, verified)
}
func TestRangeBatchVerifyNotInRange(t *testing.T) {
curve := curves.ED25519()
n := 2
prover, err := NewRangeProver(n*4, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
v1 := curve.Scalar.One()
v2 := curve.Scalar.Random(crand.Reader)
v3 := curve.Scalar.Random(crand.Reader)
v4 := curve.Scalar.Random(crand.Reader)
v := []curves.Scalar{v1, v2, v3, v4}
gamma1 := curve.Scalar.Random(crand.Reader)
gamma2 := curve.Scalar.Random(crand.Reader)
gamma3 := curve.Scalar.Random(crand.Reader)
gamma4 := curve.Scalar.Random(crand.Reader)
gamma := []curves.Scalar{gamma1, gamma2, gamma3, gamma4}
g := curve.Point.Random(crand.Reader)
h := curve.Point.Random(crand.Reader)
u := curve.Point.Random(crand.Reader)
proofGenerators := RangeProofGenerators{
g: g,
h: h,
u: u,
}
transcript := merlin.NewTranscript("test")
_, err = prover.BatchProve(v, gamma, n, proofGenerators, transcript)
require.Error(t, err)
}
func TestRangeBatchVerifyNonRandom(t *testing.T) {
curve := curves.ED25519()
n := 2
prover, err := NewRangeProver(n*4, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
v1 := curve.Scalar.One()
v2 := curve.Scalar.One()
v3 := curve.Scalar.One()
v4 := curve.Scalar.One()
v := []curves.Scalar{v1, v2, v3, v4}
gamma1 := curve.Scalar.Random(crand.Reader)
gamma2 := curve.Scalar.Random(crand.Reader)
gamma3 := curve.Scalar.Random(crand.Reader)
gamma4 := curve.Scalar.Random(crand.Reader)
gamma := []curves.Scalar{gamma1, gamma2, gamma3, gamma4}
g := curve.Point.Random(crand.Reader)
h := curve.Point.Random(crand.Reader)
u := curve.Point.Random(crand.Reader)
proofGenerators := RangeProofGenerators{
g: g,
h: h,
u: u,
}
transcript := merlin.NewTranscript("test")
proof, err := prover.BatchProve(v, gamma, n, proofGenerators, transcript)
require.NoError(t, err)
verifier, err := NewRangeVerifier(n*4, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
transcriptVerifier := merlin.NewTranscript("test")
capV := getcapVBatched(v, gamma, g, h)
verified, err := verifier.VerifyBatched(proof, capV, proofGenerators, n, transcriptVerifier)
require.NoError(t, err)
require.True(t, verified)
}
func TestRangeBatchVerifyInvalid(t *testing.T) {
curve := curves.ED25519()
n := 2
prover, err := NewRangeProver(n*4, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
v1 := curve.Scalar.One()
v2 := curve.Scalar.One()
v3 := curve.Scalar.One()
v4 := curve.Scalar.One()
v := []curves.Scalar{v1, v2, v3, v4}
gamma1 := curve.Scalar.Random(crand.Reader)
gamma2 := curve.Scalar.Random(crand.Reader)
gamma3 := curve.Scalar.Random(crand.Reader)
gamma4 := curve.Scalar.Random(crand.Reader)
gamma := []curves.Scalar{gamma1, gamma2, gamma3, gamma4}
g := curve.Point.Random(crand.Reader)
h := curve.Point.Random(crand.Reader)
u := curve.Point.Random(crand.Reader)
proofGenerators := RangeProofGenerators{
g: g,
h: h,
u: u,
}
transcript := merlin.NewTranscript("test")
proof, err := prover.BatchProve(v, gamma, n, proofGenerators, transcript)
require.NoError(t, err)
verifier, err := NewRangeVerifier(n*4, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
transcriptVerifier := merlin.NewTranscript("test")
capV := getcapVBatched(v, gamma, g, h)
capV[0] = curve.Point.Random(crand.Reader)
verified, err := verifier.VerifyBatched(proof, capV, proofGenerators, n, transcriptVerifier)
require.Error(t, err)
require.False(t, verified)
}
+514
View File
@@ -0,0 +1,514 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// Package bulletproof implements the zero knowledge protocol bulletproofs as defined in https://eprint.iacr.org/2017/1066.pdf
package bulletproof
import (
crand "crypto/rand"
"math/big"
"github.com/gtank/merlin"
"github.com/pkg/errors"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// RangeProver is the struct used to create RangeProofs
// It specifies which curve to use and holds precomputed generators
// See NewRangeProver() for prover initialization.
type RangeProver struct {
curve curves.Curve
generators *ippGenerators
ippProver *InnerProductProver
}
// RangeProof is the struct used to hold a range proof
// capA is a commitment to a_L and a_R using randomness alpha
// capS is a commitment to s_L and s_R using randomness rho
// capTau1,2 are commitments to t1,t2 respectively using randomness tau_1,2
// tHat represents t(X) as defined on page 19
// taux is the blinding factor for tHat
// ipp is the inner product proof used for compacting the transfer of l,r (See 4.2 on pg20).
type RangeProof struct {
capA, capS, capT1, capT2 curves.Point
taux, mu, tHat curves.Scalar
ipp *InnerProductProof
curve *curves.Curve
}
type RangeProofGenerators struct {
g, h, u curves.Point
}
// NewRangeProver initializes a new prover
// It uses the specified domain to generate generators for vectors of at most maxVectorLength
// A prover can be used to construct range proofs for vectors of length less than or equal to maxVectorLength
// A prover is defined by an explicit curve.
func NewRangeProver(
maxVectorLength int,
rangeDomain, ippDomain []byte,
curve curves.Curve,
) (*RangeProver, error) {
generators, err := getGeneratorPoints(maxVectorLength, rangeDomain, curve)
if err != nil {
return nil, errors.Wrap(err, "range NewRangeProver")
}
ippProver, err := NewInnerProductProver(maxVectorLength, ippDomain, curve)
if err != nil {
return nil, errors.Wrap(err, "range NewRangeProver")
}
return &RangeProver{curve: curve, generators: generators, ippProver: ippProver}, nil
}
// NewRangeProof initializes a new RangeProof for a specified curve
// This should be used in tandem with UnmarshalBinary() to convert a marshaled proof into the struct.
func NewRangeProof(curve *curves.Curve) *RangeProof {
out := RangeProof{
capA: nil,
capS: nil,
capT1: nil,
capT2: nil,
taux: nil,
mu: nil,
tHat: nil,
ipp: NewInnerProductProof(curve),
curve: curve,
}
return &out
}
// Prove uses the range prover to prove that some value v is within the range [0, 2^n]
// It implements the protocol defined on pgs 19,20 in https://eprint.iacr.org/2017/1066.pdf
// v is the value of which to prove the range
// n is the power that specifies the upper bound of the range, ie. 2^n
// gamma is a scalar used for as a blinding factor
// g, h, u are unique points used as generators for the blinding factor
// transcript is a merlin transcript to be used for the fiat shamir heuristic.
func (prover *RangeProver) Prove(
v, gamma curves.Scalar,
n int,
proofGenerators RangeProofGenerators,
transcript *merlin.Transcript,
) (*RangeProof, error) {
// n must be less than or equal to the number of generators generated
if n > len(prover.generators.G) {
return nil, errors.New("ipp vector length must be less than or equal to maxVectorLength")
}
// In case where len(a) is less than number of generators precomputed by prover, trim to length
proofG := prover.generators.G[0:n]
proofH := prover.generators.H[0:n]
// Check that v is in range [0, 2^n]
if bigZero := big.NewInt(0); v.BigInt().Cmp(bigZero) == -1 {
return nil, errors.New("v is less than 0")
}
bigTwo := big.NewInt(2)
if n < 0 {
return nil, errors.New("n cannot be less than 0")
}
bigN := big.NewInt(int64(n))
var bigTwoToN big.Int
bigTwoToN.Exp(bigTwo, bigN, nil)
if v.BigInt().Cmp(&bigTwoToN) == 1 {
return nil, errors.New("v is greater than 2^n")
}
// L40 on pg19
aL, err := getaL(v, n, prover.curve)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
onen := get1nVector(n, prover.curve)
// L41 on pg19
aR, err := subtractPairwiseScalarVectors(aL, onen)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
alpha := prover.curve.Scalar.Random(crand.Reader)
// Calc A (L44, pg19)
halpha := proofGenerators.h.Mul(alpha)
gaL := prover.curve.Point.SumOfProducts(proofG, aL)
haR := prover.curve.Point.SumOfProducts(proofH, aR)
capA := halpha.Add(gaL).Add(haR)
// L45, 46, pg19
sL := getBlindingVector(n, prover.curve)
sR := getBlindingVector(n, prover.curve)
rho := prover.curve.Scalar.Random(crand.Reader)
// Calc S (L47, pg19)
hrho := proofGenerators.h.Mul(rho)
gsL := prover.curve.Point.SumOfProducts(proofG, sL)
hsR := prover.curve.Point.SumOfProducts(proofH, sR)
capS := hrho.Add(gsL).Add(hsR)
// Fiat Shamir for y,z (L49, pg19)
capV := getcapV(v, gamma, proofGenerators.g, proofGenerators.h)
y, z, err := calcyz(capV, capA, capS, transcript, prover.curve)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// Calc t_1, t_2
// See the l(X), r(X), t(X) equations on pg 19
// Use l(X)'s and r(X)'s constant and linear terms to derive t_1 and t_2
// (a_l - z*1^n)
zonen := multiplyScalarToScalarVector(z, onen)
constantTerml, err := subtractPairwiseScalarVectors(aL, zonen)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
linearTerml := sL
// z^2 * 2^N
twoN := get2nVector(n, prover.curve)
zSquareTwon := multiplyScalarToScalarVector(z.Square(), twoN)
// a_r + z*1^n
aRPluszonen, err := addPairwiseScalarVectors(aR, zonen)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
yn := getknVector(y, n, prover.curve)
hadamard, err := multiplyPairwiseScalarVectors(yn, aRPluszonen)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
constantTermr, err := addPairwiseScalarVectors(hadamard, zSquareTwon)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
linearTermr, err := multiplyPairwiseScalarVectors(yn, sR)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// t_1 (as the linear coefficient) is the sum of the dot products of l(X)'s linear term dot r(X)'s constant term
// and r(X)'s linear term dot l(X)'s constant term
t1FirstTerm, err := innerProduct(linearTerml, constantTermr)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
t1SecondTerm, err := innerProduct(linearTermr, constantTerml)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
t1 := t1FirstTerm.Add(t1SecondTerm)
// t_2 (as the quadratic coefficient) is the dot product of l(X)'s and r(X)'s linear terms
t2, err := innerProduct(linearTerml, linearTermr)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// L52, pg20
tau1 := prover.curve.Scalar.Random(crand.Reader)
tau2 := prover.curve.Scalar.Random(crand.Reader)
// T_1, T_2 (L53, pg20)
capT1 := proofGenerators.g.Mul(t1).Add(proofGenerators.h.Mul(tau1))
capT2 := proofGenerators.g.Mul(t2).Add(proofGenerators.h.Mul(tau2))
// Fiat shamir for x (L55, pg20)
x, err := calcx(capT1, capT2, transcript, prover.curve)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// Calc l (L58, pg20)
// Instead of using the expression in the line, evaluate l() at x
sLx := multiplyScalarToScalarVector(x, linearTerml)
l, err := addPairwiseScalarVectors(constantTerml, sLx)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// Calc r (L59, pg20)
// Instead of using the expression in the line, evaluate r() at x
ynsRx := multiplyScalarToScalarVector(x, linearTermr)
r, err := addPairwiseScalarVectors(constantTermr, ynsRx)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
// Calc t hat (L60, pg20)
// For efficiency, instead of calculating the dot product, evaluate t() at x
deltayz, err := deltayz(y, z, n, prover.curve)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
t0 := v.Mul(z.Square()).Add(deltayz)
tLinear := t1.Mul(x)
tQuadratic := t2.Mul(x.Square())
tHat := t0.Add(tLinear).Add(tQuadratic)
// Calc tau_x (L61, pg20)
tau2xsquare := tau2.Mul(x.Square())
tau1x := tau1.Mul(x)
zsquaregamma := z.Square().Mul(gamma)
taux := tau2xsquare.Add(tau1x).Add(zsquaregamma)
// Calc mu (L62, pg20)
mu := alpha.Add(rho.Mul(x))
// Calc IPP (See section 4.2)
hPrime, err := gethPrime(proofH, y, prover.curve)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
capPhmu, err := getPhmu(
proofG,
hPrime,
proofGenerators.h,
capA,
capS,
x,
y,
z,
mu,
n,
prover.curve,
)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
wBytes := transcript.ExtractBytes([]byte("getw"), 64)
w, err := prover.curve.NewScalar().SetBytesWide(wBytes)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
ipp, err := prover.ippProver.rangeToIPP(
proofG,
hPrime,
l,
r,
tHat,
capPhmu,
proofGenerators.u.Mul(w),
transcript,
)
if err != nil {
return nil, errors.Wrap(err, "rangeproof prove")
}
out := &RangeProof{
capA: capA,
capS: capS,
capT1: capT1,
capT2: capT2,
taux: taux,
mu: mu,
tHat: tHat,
ipp: ipp,
curve: &prover.curve,
}
return out, nil
}
// MarshalBinary takes a range proof and marshals into bytes.
func (proof *RangeProof) MarshalBinary() []byte {
var out []byte
out = append(out, proof.capA.ToAffineCompressed()...)
out = append(out, proof.capS.ToAffineCompressed()...)
out = append(out, proof.capT1.ToAffineCompressed()...)
out = append(out, proof.capT2.ToAffineCompressed()...)
out = append(out, proof.taux.Bytes()...)
out = append(out, proof.mu.Bytes()...)
out = append(out, proof.tHat.Bytes()...)
out = append(out, proof.ipp.MarshalBinary()...)
return out
}
// UnmarshalBinary takes bytes of a marshaled proof and writes them into a range proof
// The range proof used should be from the output of NewRangeProof().
func (proof *RangeProof) UnmarshalBinary(data []byte) error {
scalarLen := len(proof.curve.NewScalar().Bytes())
pointLen := len(proof.curve.NewGeneratorPoint().ToAffineCompressed())
ptr := 0
// Get points
capA, err := proof.curve.Point.FromAffineCompressed(data[ptr : ptr+pointLen])
if err != nil {
return errors.New("rangeProof UnmarshalBinary FromAffineCompressed")
}
proof.capA = capA
ptr += pointLen
capS, err := proof.curve.Point.FromAffineCompressed(data[ptr : ptr+pointLen])
if err != nil {
return errors.New("rangeProof UnmarshalBinary FromAffineCompressed")
}
proof.capS = capS
ptr += pointLen
capT1, err := proof.curve.Point.FromAffineCompressed(data[ptr : ptr+pointLen])
if err != nil {
return errors.New("rangeProof UnmarshalBinary FromAffineCompressed")
}
proof.capT1 = capT1
ptr += pointLen
capT2, err := proof.curve.Point.FromAffineCompressed(data[ptr : ptr+pointLen])
if err != nil {
return errors.New("rangeProof UnmarshalBinary FromAffineCompressed")
}
proof.capT2 = capT2
ptr += pointLen
// Get scalars
taux, err := proof.curve.NewScalar().SetBytes(data[ptr : ptr+scalarLen])
if err != nil {
return errors.New("rangeProof UnmarshalBinary SetBytes")
}
proof.taux = taux
ptr += scalarLen
mu, err := proof.curve.NewScalar().SetBytes(data[ptr : ptr+scalarLen])
if err != nil {
return errors.New("rangeProof UnmarshalBinary SetBytes")
}
proof.mu = mu
ptr += scalarLen
tHat, err := proof.curve.NewScalar().SetBytes(data[ptr : ptr+scalarLen])
if err != nil {
return errors.New("rangeProof UnmarshalBinary SetBytes")
}
proof.tHat = tHat
ptr += scalarLen
// Get IPP
err = proof.ipp.UnmarshalBinary(data[ptr:])
if err != nil {
return errors.New("rangeProof UnmarshalBinary")
}
return nil
}
// checkRange validates whether some scalar v is within the range [0, 2^n - 1]
// It will return an error if v is less than 0 or greater than 2^n - 1
// Otherwise it will return nil.
func checkRange(v curves.Scalar, n int) error {
bigOne := big.NewInt(1)
if n < 0 {
return errors.New("n cannot be less than 0")
}
var bigTwoToN big.Int
bigTwoToN.Lsh(bigOne, uint(n))
if v.BigInt().Cmp(&bigTwoToN) == 1 {
return errors.New("v is greater than 2^n")
}
return nil
}
// getBlindingVector returns a vector of scalars used as blinding factors for commitments.
func getBlindingVector(length int, curve curves.Curve) []curves.Scalar {
vec := make([]curves.Scalar, length)
for i := 0; i < length; i++ {
vec[i] = curve.Scalar.Random(crand.Reader)
}
return vec
}
// getcapV returns a commitment to v using blinding factor gamma.
func getcapV(v, gamma curves.Scalar, g, h curves.Point) curves.Point {
return h.Mul(gamma).Add(g.Mul(v))
}
// getaL obtains the bit vector representation of v
// See the a_L definition towards the bottom of pg 17 of https://eprint.iacr.org/2017/1066.pdf
func getaL(v curves.Scalar, n int, curve curves.Curve) ([]curves.Scalar, error) {
var err error
vBytes := v.Bytes()
zero := curve.Scalar.Zero()
one := curve.Scalar.One()
aL := make([]curves.Scalar, n)
for j := 0; j < len(aL); j++ {
aL[j] = zero
}
for i := 0; i < n; i++ {
ithBit := vBytes[i>>3] >> (i & 0x07) & 0x01
aL[i], err = cmoveScalar(zero, one, int(ithBit), curve)
if err != nil {
return nil, errors.Wrap(err, "getaL")
}
}
return aL, nil
}
// cmoveScalar provides a constant time operation that returns x if which is 0 and returns y if which is 1.
func cmoveScalar(x, y curves.Scalar, which int, curve curves.Curve) (curves.Scalar, error) {
if which != 0 && which != 1 {
return nil, errors.New("cmoveScalar which must be 0 or 1")
}
mask := -byte(which)
xBytes := x.Bytes()
yBytes := y.Bytes()
for i, xByte := range xBytes {
xBytes[i] ^= (xByte ^ yBytes[i]) & mask
}
out, err := curve.NewScalar().SetBytes(xBytes)
if err != nil {
return nil, errors.Wrap(err, "cmoveScalar SetBytes")
}
return out, nil
}
// calcyz uses a merlin transcript for Fiat Shamir
// It takes the current state of the transcript and appends the newly calculated capA and capS values
// Two new scalars are then read from the transcript
// See section 4.4 pg22 of https://eprint.iacr.org/2017/1066.pdf
func calcyz(
capV, capA, capS curves.Point,
transcript *merlin.Transcript,
curve curves.Curve,
) (curves.Scalar, curves.Scalar, error) {
// Add the A,S values to transcript
transcript.AppendMessage([]byte("addV"), capV.ToAffineUncompressed())
transcript.AppendMessage([]byte("addcapA"), capA.ToAffineUncompressed())
transcript.AppendMessage([]byte("addcapS"), capS.ToAffineUncompressed())
// Read 64 bytes twice from, set to scalar for y and z
yBytes := transcript.ExtractBytes([]byte("gety"), 64)
y, err := curve.NewScalar().SetBytesWide(yBytes)
if err != nil {
return nil, nil, errors.Wrap(err, "calcyz NewScalar SetBytesWide")
}
zBytes := transcript.ExtractBytes([]byte("getz"), 64)
z, err := curve.NewScalar().SetBytesWide(zBytes)
if err != nil {
return nil, nil, errors.Wrap(err, "calcyz NewScalar SetBytesWide")
}
return y, z, nil
}
// calcx uses a merlin transcript for Fiat Shamir
// It takes the current state of the transcript and appends the newly calculated capT1 and capT2 values
// A new scalar is then read from the transcript
// See section 4.4 pg22 of https://eprint.iacr.org/2017/1066.pdf
func calcx(
capT1, capT2 curves.Point,
transcript *merlin.Transcript,
curve curves.Curve,
) (curves.Scalar, error) {
// Add the Tau1,2 values to transcript
transcript.AppendMessage([]byte("addcapT1"), capT1.ToAffineUncompressed())
transcript.AppendMessage([]byte("addcapT2"), capT2.ToAffineUncompressed())
// Read 64 bytes from, set to scalar
outBytes := transcript.ExtractBytes([]byte("getx"), 64)
x, err := curve.NewScalar().SetBytesWide(outBytes)
if err != nil {
return nil, errors.Wrap(err, "calcx NewScalar SetBytesWide")
}
return x, nil
}
+86
View File
@@ -0,0 +1,86 @@
package bulletproof
import (
crand "crypto/rand"
"testing"
"github.com/gtank/merlin"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestRangeProverHappyPath(t *testing.T) {
curve := curves.ED25519()
n := 256
prover, err := NewRangeProver(n, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
v := curve.Scalar.Random(crand.Reader)
gamma := curve.Scalar.Random(crand.Reader)
g := curve.Point.Random(crand.Reader)
h := curve.Point.Random(crand.Reader)
u := curve.Point.Random(crand.Reader)
proofGenerators := RangeProofGenerators{
g: g,
h: h,
u: u,
}
transcript := merlin.NewTranscript("test")
proof, err := prover.Prove(v, gamma, n, proofGenerators, transcript)
require.NoError(t, err)
require.NotNil(t, proof)
require.Equal(t, 8, len(proof.ipp.capLs))
require.Equal(t, 8, len(proof.ipp.capRs))
}
func TestGetaL(t *testing.T) {
curve := curves.ED25519()
v := curve.Scalar.Random(crand.Reader)
aL, err := getaL(v, 256, *curve)
require.NoError(t, err)
twoN := get2nVector(256, *curve)
product, err := innerProduct(aL, twoN)
require.NoError(t, err)
require.Zero(t, product.Cmp(v))
}
func TestCmove(t *testing.T) {
curve := curves.ED25519()
two := curve.Scalar.One().Double()
four := two.Double()
out, err := cmoveScalar(two, four, 1, *curve)
require.NoError(t, err)
require.Zero(t, out.Cmp(four))
}
func TestRangeProverMarshal(t *testing.T) {
curve := curves.ED25519()
n := 256
prover, err := NewRangeProver(n, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
v := curve.Scalar.Random(crand.Reader)
gamma := curve.Scalar.Random(crand.Reader)
g := curve.Point.Random(crand.Reader)
h := curve.Point.Random(crand.Reader)
u := curve.Point.Random(crand.Reader)
proofGenerators := RangeProofGenerators{
g: g,
h: h,
u: u,
}
transcript := merlin.NewTranscript("test")
proof, err := prover.Prove(v, gamma, n, proofGenerators, transcript)
require.NoError(t, err)
proofMarshaled := proof.MarshalBinary()
proofPrime := NewRangeProof(curve)
err = proofPrime.UnmarshalBinary(proofMarshaled)
require.NoError(t, err)
require.True(t, proof.capA.Equal(proofPrime.capA))
require.True(t, proof.capS.Equal(proofPrime.capS))
require.True(t, proof.capT1.Equal(proofPrime.capT1))
require.True(t, proof.capT2.Equal(proofPrime.capT2))
require.Zero(t, proof.taux.Cmp(proofPrime.taux))
require.Zero(t, proof.mu.Cmp(proofPrime.mu))
require.Zero(t, proof.tHat.Cmp(proofPrime.tHat))
}
+235
View File
@@ -0,0 +1,235 @@
package bulletproof
import (
"github.com/gtank/merlin"
"github.com/pkg/errors"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// RangeVerifier is the struct used to verify RangeProofs
// It specifies which curve to use and holds precomputed generators
// See NewRangeVerifier() for verifier initialization.
type RangeVerifier struct {
curve curves.Curve
generators *ippGenerators
ippVerifier *InnerProductVerifier
}
// NewRangeVerifier initializes a new verifier
// It uses the specified domain to generate generators for vectors of at most maxVectorLength
// A verifier can be used to verify range proofs for vectors of length less than or equal to maxVectorLength
// A verifier is defined by an explicit curve.
func NewRangeVerifier(
maxVectorLength int,
rangeDomain, ippDomain []byte,
curve curves.Curve,
) (*RangeVerifier, error) {
generators, err := getGeneratorPoints(maxVectorLength, rangeDomain, curve)
if err != nil {
return nil, errors.Wrap(err, "range NewRangeProver")
}
ippVerifier, err := NewInnerProductVerifier(maxVectorLength, ippDomain, curve)
if err != nil {
return nil, errors.Wrap(err, "range NewRangeProver")
}
return &RangeVerifier{curve: curve, generators: generators, ippVerifier: ippVerifier}, nil
}
// Verify verifies the given range proof inputs
// It implements the checking of L65 on pg 20
// It also verifies the dot product of <l,r> using the inner product proof\
// capV is a commitment to v using blinding factor gamma
// n is the power that specifies the upper bound of the range, ie. 2^n
// g, h, u are unique points used as generators for the blinding factor
// transcript is a merlin transcript to be used for the fiat shamir heuristic.
func (verifier *RangeVerifier) Verify(
proof *RangeProof,
capV curves.Point,
proofGenerators RangeProofGenerators,
n int,
transcript *merlin.Transcript,
) (bool, error) {
// Length of vectors must be less than the number of generators generated
if n > len(verifier.generators.G) {
return false, errors.New("ipp vector length must be less than maxVectorLength")
}
// In case where len(a) is less than number of generators precomputed by prover, trim to length
proofG := verifier.generators.G[0:n]
proofH := verifier.generators.H[0:n]
// Calc y,z,x from Fiat Shamir heuristic
y, z, err := calcyz(capV, proof.capA, proof.capS, transcript, verifier.curve)
if err != nil {
return false, errors.Wrap(err, "rangeproof verify")
}
x, err := calcx(proof.capT1, proof.capT2, transcript, verifier.curve)
if err != nil {
return false, errors.Wrap(err, "rangeproof verify")
}
wBytes := transcript.ExtractBytes([]byte("getw"), 64)
w, err := verifier.curve.NewScalar().SetBytesWide(wBytes)
if err != nil {
return false, errors.Wrap(err, "rangeproof prove")
}
// Calc delta(y,z)
deltayz, err := deltayz(y, z, n, verifier.curve)
if err != nil {
return false, errors.Wrap(err, "rangeproof verify")
}
// Check tHat: L65, pg20
tHatIsValid := verifier.checktHat(
proof,
capV,
proofGenerators.g,
proofGenerators.h,
deltayz,
x,
z,
)
if !tHatIsValid {
return false, errors.New("rangeproof verify tHat is invalid")
}
// Verify IPP
hPrime, err := gethPrime(proofH, y, verifier.curve)
if err != nil {
return false, errors.Wrap(err, "rangeproof verify")
}
capPhmu, err := getPhmu(
proofG,
hPrime,
proofGenerators.h,
proof.capA,
proof.capS,
x,
y,
z,
proof.mu,
n,
verifier.curve,
)
if err != nil {
return false, errors.Wrap(err, "rangeproof verify")
}
ippVerified, err := verifier.ippVerifier.VerifyFromRangeProof(
proofG,
hPrime,
capPhmu,
proofGenerators.u.Mul(w),
proof.tHat,
proof.ipp,
transcript,
)
if err != nil {
return false, errors.Wrap(err, "rangeproof verify")
}
return ippVerified, nil
}
// L65, pg20.
func (*RangeVerifier) checktHat(
proof *RangeProof,
capV, g, h curves.Point,
deltayz, x, z curves.Scalar,
) bool {
// g^tHat * h^tau_x
gtHat := g.Mul(proof.tHat)
htaux := h.Mul(proof.taux)
lhs := gtHat.Add(htaux)
// V^z^2 * g^delta(y,z) * Tau_1^x * Tau_2^x^2
capVzsquare := capV.Mul(z.Square())
gdeltayz := g.Mul(deltayz)
capTau1x := proof.capT1.Mul(x)
capTau2xsquare := proof.capT2.Mul(x.Square())
rhs := capVzsquare.Add(gdeltayz).Add(capTau1x).Add(capTau2xsquare)
// Compare lhs =? rhs
return lhs.Equal(rhs)
}
// gethPrime calculates new h prime generators as defined in L64 on pg20.
func gethPrime(h []curves.Point, y curves.Scalar, curve curves.Curve) ([]curves.Point, error) {
hPrime := make([]curves.Point, len(h))
yInv, err := y.Invert()
yInvn := getknVector(yInv, len(h), curve)
if err != nil {
return nil, errors.Wrap(err, "gethPrime")
}
for i, hElem := range h {
hPrime[i] = hElem.Mul(yInvn[i])
}
return hPrime, nil
}
// Obtain P used for IPP verification
// See L67 on pg20
// Note P on L66 includes blinding factor hmu, this method removes that factor.
func getPhmu(
proofG, proofHPrime []curves.Point,
h, capA, capS curves.Point,
x, y, z, mu curves.Scalar,
n int,
curve curves.Curve,
) (curves.Point, error) {
// h'^(z*y^n + z^2*2^n)
zyn := multiplyScalarToScalarVector(z, getknVector(y, n, curve))
zsquaretwon := multiplyScalarToScalarVector(z.Square(), get2nVector(n, curve))
elemLastExponent, err := addPairwiseScalarVectors(zyn, zsquaretwon)
if err != nil {
return nil, errors.Wrap(err, "getPhmu")
}
lastElem := curve.Point.SumOfProducts(proofHPrime, elemLastExponent)
// S^x
capSx := capS.Mul(x)
// g^-z --> -z*<1,g>
onen := get1nVector(n, curve)
zNeg := z.Neg()
zinvonen := multiplyScalarToScalarVector(zNeg, onen)
zgdotonen := curve.Point.SumOfProducts(proofG, zinvonen)
// L66 on pg20
P := capA.Add(capSx).Add(zgdotonen).Add(lastElem)
hmu := h.Mul(mu)
Phmu := P.Sub(hmu)
return Phmu, nil
}
// Delta function for delta(y,z), See (39) on pg18.
func deltayz(y, z curves.Scalar, n int, curve curves.Curve) (curves.Scalar, error) {
// z - z^2
zMinuszsquare := z.Sub(z.Square())
// 1^n
onen := get1nVector(n, curve)
// <1^n, y^n>
onendotyn, err := innerProduct(onen, getknVector(y, n, curve))
if err != nil {
return nil, errors.Wrap(err, "deltayz")
}
// (z - z^2)*<1^n, y^n>
termFirst := zMinuszsquare.Mul(onendotyn)
// <1^n, 2^n>
onendottwon, err := innerProduct(onen, get2nVector(n, curve))
if err != nil {
return nil, errors.Wrap(err, "deltayz")
}
// z^3*<1^n, 2^n>
termSecond := z.Cube().Mul(onendottwon)
// (z - z^2)*<1^n, y^n> - z^3*<1^n, 2^n>
out := termFirst.Sub(termSecond)
return out, nil
}
+87
View File
@@ -0,0 +1,87 @@
package bulletproof
import (
crand "crypto/rand"
"testing"
"github.com/gtank/merlin"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestRangeVerifyHappyPath(t *testing.T) {
curve := curves.ED25519()
n := 256
prover, err := NewRangeProver(n, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
v := curve.Scalar.Random(crand.Reader)
gamma := curve.Scalar.Random(crand.Reader)
g := curve.Point.Random(crand.Reader)
h := curve.Point.Random(crand.Reader)
u := curve.Point.Random(crand.Reader)
proofGenerators := RangeProofGenerators{
g: g,
h: h,
u: u,
}
transcript := merlin.NewTranscript("test")
proof, err := prover.Prove(v, gamma, n, proofGenerators, transcript)
require.NoError(t, err)
verifier, err := NewRangeVerifier(n, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
transcriptVerifier := merlin.NewTranscript("test")
capV := getcapV(v, gamma, g, h)
verified, err := verifier.Verify(proof, capV, proofGenerators, n, transcriptVerifier)
require.NoError(t, err)
require.True(t, verified)
}
func TestRangeVerifyNotInRange(t *testing.T) {
curve := curves.ED25519()
n := 2
prover, err := NewRangeProver(n, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
v := curve.Scalar.Random(crand.Reader)
gamma := curve.Scalar.Random(crand.Reader)
g := curve.Point.Random(crand.Reader)
h := curve.Point.Random(crand.Reader)
u := curve.Point.Random(crand.Reader)
proofGenerators := RangeProofGenerators{
g: g,
h: h,
u: u,
}
transcript := merlin.NewTranscript("test")
_, err = prover.Prove(v, gamma, n, proofGenerators, transcript)
require.Error(t, err)
}
func TestRangeVerifyNonRandom(t *testing.T) {
curve := curves.ED25519()
n := 2
prover, err := NewRangeProver(n, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
v := curve.Scalar.One()
gamma := curve.Scalar.Random(crand.Reader)
g := curve.Point.Random(crand.Reader)
h := curve.Point.Random(crand.Reader)
u := curve.Point.Random(crand.Reader)
proofGenerators := RangeProofGenerators{
g: g,
h: h,
u: u,
}
transcript := merlin.NewTranscript("test")
proof, err := prover.Prove(v, gamma, n, proofGenerators, transcript)
require.NoError(t, err)
verifier, err := NewRangeVerifier(n, []byte("rangeDomain"), []byte("ippDomain"), *curve)
require.NoError(t, err)
transcriptVerifier := merlin.NewTranscript("test")
capV := getcapV(v, gamma, g, h)
verified, err := verifier.Verify(proof, capV, proofGenerators, n, transcriptVerifier)
require.NoError(t, err)
require.True(t, verified)
}
Executable
+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.
Executable
+123
View File
@@ -0,0 +1,123 @@
//
// 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
}
+396
View File
@@ -0,0 +1,396 @@
//
// 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))
}
File diff suppressed because it is too large Load Diff
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/sonr-io/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 x, y
}
func expandMsgXmd(h hash.Hash, msg, domain []byte, outLen int) ([]byte, error) {
if len(domain) > 255 {
return nil, fmt.Errorf("invalid domain length")
}
domainLen := uint8(len(domain))
// 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
}
+93
View File
@@ -0,0 +1,93 @@
// Package curves provides integration tests for elliptic curve support
package curves
import (
"crypto/elliptic"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core"
)
// TestAdditionalCurveSupport tests the new curve support in hash.go
func TestAdditionalCurveSupport(t *testing.T) {
tests := []struct {
name string
curve elliptic.Curve
expectedSecurity int
expectedL int
}{
{
name: "P-256",
curve: elliptic.P256(),
expectedSecurity: 128,
expectedL: 48,
},
{
name: "P-384",
curve: elliptic.P384(),
expectedSecurity: 192,
expectedL: 72,
},
{
name: "P-521",
curve: elliptic.P521(),
expectedSecurity: 256,
expectedL: 98,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test Hash function with the curve
msg := []byte("test message for curve support")
result, err := core.Hash(msg, tt.curve)
require.NoError(t, err, "Hash should succeed for %s", tt.name)
require.NotNil(t, result, "Hash result should not be nil for %s", tt.name)
// Verify the result is a valid big integer
require.Greater(t, result.BitLen(), 0, "Hash result should have non-zero bit length")
})
}
}
// BenchmarkHashWithCurves benchmarks the hash function with different curves
func BenchmarkHashWithCurves(b *testing.B) {
curves := []struct {
name string
curve elliptic.Curve
}{
{"P256", elliptic.P256()},
{"P384", elliptic.P384()},
{"P521", elliptic.P521()},
}
msg := []byte("benchmark message for hash function")
for _, c := range curves {
b.Run(c.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = core.Hash(msg, c.curve)
}
})
}
}
// TestBackwardCompatibility ensures old curves still work
func TestBackwardCompatibility(t *testing.T) {
// Test that P-256 still works as before
msg := []byte("backward compatibility test")
result256, err := core.Hash(msg, elliptic.P256())
require.NoError(t, err)
require.NotNil(t, result256)
// Test with same message multiple times for consistency
for i := 0; i < 10; i++ {
result, err := core.Hash(msg, elliptic.P256())
require.NoError(t, err)
require.Equal(t, result256.Cmp(result), 0, "Hash should be consistent")
}
}
+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/sonr-io/sonr/crypto/core"
"github.com/dustinxie/ecc"
"github.com/sonr-io/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/sonr-io/sonr/crypto/core"
tt "github.com/sonr-io/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)
})
}
}
+353
View File
@@ -0,0 +1,353 @@
//
// 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/sonr-io/sonr/crypto/core"
"github.com/sonr-io/sonr/crypto/core/curves/native/bls12381"
"github.com/sonr-io/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)
}
+864
View File
@@ -0,0 +1,864 @@
//
// 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/sonr-io/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
}
+621
View File
@@ -0,0 +1,621 @@
//
// 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/sonr-io/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
}
+282
View File
@@ -0,0 +1,282 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// Package curves: Field implementation IS NOT constant time as it leverages math/big for big number operations.
package curves
import (
"crypto/rand"
"encoding/json"
"fmt"
"io"
"math/big"
"sync"
)
var (
ed25519SubGroupOrderOnce sync.Once
ed25519SubGroupOrder *big.Int
)
// Field is a finite field.
type Field struct {
*big.Int
}
// Element is a group element within a finite field.
type Element struct {
Modulus *Field `json:"modulus"`
Value *big.Int `json:"value"`
}
// ElementJSON is used in JSON<>Element conversions.
// For years, big.Int hasn't properly supported JSON unmarshaling
// https://github.com/golang/go/issues/28154
type ElementJSON struct {
Modulus string `json:"modulus"`
Value string `json:"value"`
}
// Marshal Element to JSON
func (x *Element) MarshalJSON() ([]byte, error) {
return json.Marshal(ElementJSON{
Modulus: x.Modulus.String(),
Value: x.Value.String(),
})
}
func (x *Element) UnmarshalJSON(bytes []byte) error {
var e ElementJSON
err := json.Unmarshal(bytes, &e)
if err != nil {
return err
}
// Convert the strings to big.Ints
modulus, ok := new(big.Int).SetString(e.Modulus, 10)
if !ok {
return fmt.Errorf("failed to unmarshal modulus string '%v' to big.Int", e.Modulus)
}
x.Modulus = &Field{modulus}
x.Value, ok = new(big.Int).SetString(e.Value, 10)
if !ok {
return fmt.Errorf("failed to unmarshal value string '%v' to big.Int", e.Value)
}
return nil
}
// The probability of returning true for a randomly chosen
// non-prime is at most ¼ⁿ. 64 is a widely used standard
// that is more than sufficient.
const millerRabinRounds = 64
// New is a constructor for a Field.
func NewField(modulus *big.Int) *Field {
// For our purposes we never expect to be dealing with a non-prime field. This provides some protection against
// accidentally doing that.
if !modulus.ProbablyPrime(millerRabinRounds) {
panic(fmt.Sprintf("modulus: %x is not a prime", modulus))
}
return &Field{modulus}
}
func newElement(field *Field, value *big.Int) *Element {
if !field.IsValid(value) {
panic(fmt.Sprintf("value: %x is not within field: %x", value, field))
}
return &Element{field, value}
}
// IsValid returns whether or not the value is within [0, modulus)
func (f Field) IsValid(value *big.Int) bool {
// value < modulus && value >= 0
return value.Cmp(f.Int) < 0 && value.Sign() >= 0
}
func (f Field) NewElement(value *big.Int) *Element {
return newElement(&f, value)
}
func (f Field) Zero() *Element {
return newElement(&f, big.NewInt(0))
}
func (f Field) One() *Element {
return newElement(&f, big.NewInt(1))
}
func (f Field) RandomElement(r io.Reader) (*Element, error) {
if r == nil {
r = rand.Reader
}
var randInt *big.Int
var err error
// Ed25519 needs to do special handling
// in case the value is used in
// Scalar multiplications with points
if f.Int.Cmp(Ed25519Order()) == 0 {
scalar := NewEd25519Scalar()
randInt, err = scalar.RandomWithReader(r)
} else {
// Read a random integer within the field. This is defined as [0, max) so we don't need to
// explicitly check it is within the field. If it is not, NewElement will panic anyways.
randInt, err = rand.Int(r, f.Int)
}
if err != nil {
return nil, err
}
return newElement(&f, randInt), nil
}
// ElementFromBytes initializes a new field element from big-endian bytes
func (f Field) ElementFromBytes(bytes []byte) *Element {
return newElement(&f, new(big.Int).SetBytes(bytes))
}
// ReducedElementFromBytes initializes a new field element from big-endian bytes and reduces it by
// the modulus of the field.
//
// WARNING: If this is used with cryptographic constructions which rely on a uniform distribution of
// values, this may introduce a bias to the value of the returned field element. This happens when
// the integer range of the provided bytes is not an integer multiple of the field order.
//
// Assume we are working in field which a modulus of 3 and the range of the uniform random bytes we
// provide as input is 5. Thus, the set of field elements is {0, 1, 2} and the set of integer values
// for the input bytes is: {0, 1, 2, 3, 4}. What is the distribution of the output values produced
// by this function?
//
// ReducedElementFromBytes(0) => 0
// ReducedElementFromBytes(1) => 1
// ReducedElementFromBytes(2) => 2
// ReducedElementFromBytes(3) => 0
// ReducedElementFromBytes(4) => 1
//
// For a value space V and random value v, a uniform distribution is defined as P[V = v] = 1/|V|
// where |V| is to the order of the field. Using the results from above, we see that P[v = 0] = 2/5,
// P[v = 1] = 2/5, and P[v = 2] = 1/5. For a uniform distribution we would expect these to each be
// equal to 1/3. As they do not, this does not return uniform output for that example.
//
// To see why this is okay if the range is a multiple of the field order, change the input range to
// 6 and notice that now each output has a probability of 2/6 = 1/3, and the output is uniform.
func (f Field) ReducedElementFromBytes(bytes []byte) *Element {
value := new(big.Int).SetBytes(bytes)
value.Mod(value, f.Int)
return newElement(&f, value)
}
func (x Element) Field() *Field {
return x.Modulus
}
// Add returns the sum x+y
func (x Element) Add(y *Element) *Element {
x.validateFields(y)
sum := new(big.Int).Add(x.Value, y.Value)
sum.Mod(sum, x.Modulus.Int)
return newElement(x.Modulus, sum)
}
// Sub returns the difference x-y
func (x Element) Sub(y *Element) *Element {
x.validateFields(y)
difference := new(big.Int).Sub(x.Value, y.Value)
difference.Mod(difference, x.Modulus.Int)
return newElement(x.Modulus, difference)
}
// Neg returns the field negation
func (x Element) Neg() *Element {
z := new(big.Int).Neg(x.Value)
z.Mod(z, x.Modulus.Int)
return newElement(x.Modulus, z)
}
// Mul returns the product x*y
func (x Element) Mul(y *Element) *Element {
x.validateFields(y)
product := new(big.Int).Mul(x.Value, y.Value)
product.Mod(product, x.Modulus.Int)
return newElement(x.Modulus, product)
}
// Div returns the quotient x/y
func (x Element) Div(y *Element) *Element {
x.validateFields(y)
yInv := new(big.Int).ModInverse(y.Value, x.Modulus.Int)
quotient := new(big.Int).Mul(x.Value, yInv)
quotient.Mod(quotient, x.Modulus.Int)
return newElement(x.Modulus, quotient)
}
// Pow computes x^y reduced by the modulus
func (x Element) Pow(y *Element) *Element {
x.validateFields(y)
return newElement(x.Modulus, new(big.Int).Exp(x.Value, y.Value, x.Modulus.Int))
}
func (x Element) Invert() *Element {
return newElement(x.Modulus, new(big.Int).ModInverse(x.Value, x.Modulus.Int))
}
func (x Element) Sqrt() *Element {
return newElement(x.Modulus, new(big.Int).ModSqrt(x.Value, x.Modulus.Int))
}
// BigInt returns value as a big.Int
func (x Element) BigInt() *big.Int {
return x.Value
}
// Bytes returns the value as bytes
func (x Element) Bytes() []byte {
return x.BigInt().Bytes()
}
// IsEqual returns x == y
func (x Element) IsEqual(y *Element) bool {
if !x.isEqualFields(y) {
return false
}
return x.Value.Cmp(y.Value) == 0
}
// Clone returns a new copy of the element
func (x Element) Clone() *Element {
return x.Modulus.ElementFromBytes(x.Bytes())
}
func (x Element) isEqualFields(y *Element) bool {
return x.Modulus.Int.Cmp(y.Modulus.Int) == 0
}
func (x Element) validateFields(y *Element) {
if !x.isEqualFields(y) {
panic("fields must match for valid binary operation")
}
}
// SubgroupOrder returns the order of the Ed25519 base Point.
func Ed25519Order() *big.Int {
ed25519SubGroupOrderOnce.Do(func() {
order, ok := new(big.Int).SetString(
"1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED",
16,
)
if !ok {
panic("invalid hex string provided. This should never happen as it is constant.")
}
ed25519SubGroupOrder = order
})
return ed25519SubGroupOrder
}
+305
View File
@@ -0,0 +1,305 @@
//
// 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/sonr-io/sonr/crypto/core"
"github.com/sonr-io/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)
//}
+672
View File
@@ -0,0 +1,672 @@
//
// 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/sonr-io/sonr/crypto/core/curves/native"
secp256k1 "github.com/sonr-io/sonr/crypto/core/curves/native/k256"
"github.com/sonr-io/sonr/crypto/core/curves/native/k256/fp"
"github.com/sonr-io/sonr/crypto/core/curves/native/k256/fq"
"github.com/sonr-io/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())
// Note: Interface constraint prevents returning error directly
// Returning nil on error for now, caller should check for nil
if err != nil {
// Log error for debugging if logger is available
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
}
+620
View File
@@ -0,0 +1,620 @@
//
// 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 n, err
}
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))
}
}
+81
View File
@@ -0,0 +1,81 @@
package bls12381
import (
"math/bits"
"github.com/sonr-io/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/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/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)))
}
+346
View File
@@ -0,0 +1,346 @@
//
// 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/sonr-io/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())
}
+455
View File
@@ -0,0 +1,455 @@
package bls12381
import (
"encoding/binary"
"math/big"
"sync"
"github.com/sonr-io/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)
}
+353
View File
@@ -0,0 +1,353 @@
//
// 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/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/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
+431
View File
@@ -0,0 +1,431 @@
package bls12381
import (
crand "crypto/rand"
"encoding/hex"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/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
+569
View File
@@ -0,0 +1,569 @@
package bls12381
import (
crand "crypto/rand"
"encoding/hex"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/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/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/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,
}
}
@@ -0,0 +1,64 @@
package bls12381
import (
crand "crypto/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/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/sonr-io/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 x, y
}
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)
}
}
+207
View File
@@ -0,0 +1,207 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
"math/big"
"sync"
"github.com/sonr-io/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)
}
+330
View File
@@ -0,0 +1,330 @@
//
// 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/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/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
+493
View File
@@ -0,0 +1,493 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
"math/big"
"sync"
"github.com/sonr-io/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
+330
View File
@@ -0,0 +1,330 @@
//
// 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/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/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
+475
View File
@@ -0,0 +1,475 @@
package k256
import (
"sync"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/core/curves/native/k256/fp"
"github.com/sonr-io/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/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/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 x, y
}
+206
View File
@@ -0,0 +1,206 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
"math/big"
"sync"
"github.com/sonr-io/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)
}
+330
View File
@@ -0,0 +1,330 @@
//
// 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/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/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()))
}
}
+1463
View File
File diff suppressed because it is too large Load Diff
+510
View File
@@ -0,0 +1,510 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
"math/big"
"sync"
"github.com/sonr-io/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,
}
+330
View File
@@ -0,0 +1,330 @@
//
// 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/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/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()))
}
}
+1651
View File
File diff suppressed because it is too large Load Diff
+350
View File
@@ -0,0 +1,350 @@
package p256
import (
"sync"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/core/curves/native/p256/fp"
"github.com/sonr-io/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/sonr-io/sonr/crypto/core/curves"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/core/curves/native/p256"
"github.com/sonr-io/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
+375
View File
@@ -0,0 +1,375 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
"encoding/binary"
"fmt"
"math/big"
"github.com/sonr-io/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)
}
+273
View File
@@ -0,0 +1,273 @@
//
// 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
+369
View File
@@ -0,0 +1,369 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
"encoding/binary"
"fmt"
"math/big"
"github.com/sonr-io/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)
}
+273
View File
@@ -0,0 +1,273 @@
//
// 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
+471
View File
@@ -0,0 +1,471 @@
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 x, y
}
// 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/sonr-io/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
}
+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/sonr-io/sonr/crypto/core/curves/native"
p256n "github.com/sonr-io/sonr/crypto/core/curves/native/p256"
"github.com/sonr-io/sonr/crypto/core/curves/native/p256/fp"
"github.com/sonr-io/sonr/crypto/core/curves/native/p256/fq"
"github.com/sonr-io/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())
// Note: Interface constraint prevents returning error directly
// Returning nil on error for now, caller should check for nil
if err != nil {
// Log error for debugging if logger is available
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
}
+556
View File
@@ -0,0 +1,556 @@
//
// 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))
}

Some files were not shown because too many files have changed in this diff Show More