mirror of
https://github.com/onsonr/sonr.git
synced 2025-03-10 21:09:11 +00:00
* refactor: remove redundant branch trigger for scheduled releases * refactor: simplify process-compose commands and improve logging * refactor: remove redundant command * refactor: remove unused error variables and simplify database configuration * feat: introduce task runner for project automation * refactor: Remove hardcoded action and method from form components * refactor: move server setup to main.go and add prometheus metrics * refactor: move index handlers to render handlers * refactor: improve user identification logic in gateway and vault handlers * refactor: rename TitleDescription to TitleDesc for consistency * feat: integrate go-useragent library for enhanced user agent parsing * feat: enhance initial view rendering based on device type * feat: Add support for PostgreSQL database * fix: Use formatPsqlDSN() to properly set PostgreSQL DSN from command flags * feat: Add PostgreSQL support with fallback to SQLite in NewGormDB * feat: Add PostgreSQL connection validation with SQLite fallback * chore: update golang.org/x/crypto dependency to v0.31.0 * feat: add PKL-based configuration initialization * refactor: improve file naming consistency in cmd/sonrd * refactor: Improve init-pkl command with safer config file generation and error handling * fix: add logging for pkl evaluation results * refactor: Move credential handling to gateway context * refactor: Migrate session models to gateway package * refactor: rename models and update User model * chore: initial commit for address and pubkey functionality * refactor: move pubkey package to keys package * refactor: Rename models and add resolver service * feat: add gRPC clients for bank, DID, DWN, and SVC modules * refactor: Migrate title and description components from text package to hero package * refactor: improve file naming conventions * feat: add user credential validation * refactor: rename registration handlers and routes for clarity * <no value> * refactor: Decouple database and IPFS interactions from server setup * refactor: Migrate configuration from class-based to TOML-based structure * refactor: move network configuration files to sonr.net module * feature/1120-leverage-service-authorization * fix: correct DID identifier creation function name * feat: add compressed and uncompressed public keys to keyset * refactor: move address packages to crypto/address * feat: implement pubkey verification * refactor: remove ECDSA-related functions from keyshare and protocol modules * feat: Implement ECDSA signature serialization * <no value> * feat: add vault service for IPFS token storage * refactor: update ucan codec to use new DID generation method * refactor: refactor key management and move address parsers to keys package * refactor: rename key parsers and move to parsers package * fix: resolved import issues with the new spec * feat: improve user onboarding experience by updating button text and functionality * refactor: update point marshaling and unmarshaling methods to use JSON * refactor: remove unnecessary DID method from PubKey * refactor: Rename and refactor MPC key generation functions * test: Add comprehensive test suite for keyshare generation and validation * test: Fix keyshare role validation and encoding tests * feat: Update key share role tests with enclave initialization validation * test(mpc): refactor tests to focus on public API and remove internal role checks * refactor: Remove unnecessary role check in initKeyEnclave function * fix: Enforce strict order for validator and user keyshares in enclave initialization * fix: Update codec_test to match latest codec implementation * refactor: Update KeyEnclave to use string-based key shares and improve error handling * fix: Refactor MPC enclave to use string-based encoding and simplify key management * refactor: Remove redundant keyshare decoding tests in codec_test.go * fix: Resolve type conversion issues in MPC crypto enclave initialization * fix: Convert CID to byte slice in addEnclaveIPFS function * fix: Resolve type conversion and constant definition errors in MPC crypto utils * refactor: Simplify KeyShare encoding and role handling in MPC codec * fix: Resolve JSON unmarshaling type mismatch in KeyShare.Message() * fix: Refactor KeyEnclave to use struct and Enclave interface * fix: Resolve type and naming conflicts in MPC crypto package * refactor: Update codec_test.go to use new KeyEnclave struct fields * refactor: remove keyshare encoding and decoding logic * refactor: Remove unused JSON marshaling functions for curve points * fix: Improve signature serialization and deserialization in MPC crypto This commit addresses several issues with signature handling: - Fixed signature length to 65 bytes - Added proper padding for R and S values - Added nil and zero value checks - Improved error messages for signature parsing The changes ensure more robust signature encoding and decoding, preventing potential nil pointer and invalid signature issues. * fix: Update signature serialization to match protocol test approach * refactor: Simplify KeyEnclave struct and improve message handling * fix: Improve signature serialization and verification in MPC crypto module * refactor: Simplify enclave validation using IsValid method in test * refactor: Add marshaling and comprehensive tests for KeyEnclave * feat: Add JSON marshaling support for Point in KeyEnclave * refactor: Rename KeyEnclave to Enclave and update related functions * refactor: Update PubKey verification to use SHA3-256 hashing * test: Add comprehensive tests for DID and PubKey implementations * refactor: simplify DID key retrieval * test: refactor CI workflow and remove unused DIDAuth middleware * The changes look good! The updated workflows will now: 1. Run tests on push to master 2. Bump the version if the commit doesn't already start with 'bump:' 3. Trigger a release workflow automatically with the new version tag 4. Create and publish the release A few things to note: - Make sure you have the `peter-evans/repository-dispatch` action installed/available - The `commitizen-tools/commitizen-action` should output the new tag for this to work - Ensure your release workflow can handle the repository dispatch event Would you like me to review or suggest any additional modifications to the workflows? * ci(github actions): add build stage dependency for tests * fix(workflow): update workflow to trigger on PR edits * test: Update unit test dependencies * ci: Add GoReleaser dry-run check for merge group events * test: remove unnecessary dependencies between test jobs * ci: Make race and coverage tests depend on build tests
1177 lines
26 KiB
Go
Executable File
1177 lines
26 KiB
Go
Executable File
//
|
|
// Copyright Coinbase, Inc. All Rights Reserved.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package curves
|
|
|
|
import (
|
|
"crypto/elliptic"
|
|
crand "crypto/rand"
|
|
"crypto/subtle"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"sync"
|
|
|
|
"golang.org/x/crypto/blake2b"
|
|
|
|
"github.com/onsonr/sonr/crypto/core/curves/native/pasta/fp"
|
|
"github.com/onsonr/sonr/crypto/core/curves/native/pasta/fq"
|
|
)
|
|
|
|
var (
|
|
b = new(fp.Fp).SetUint64(5)
|
|
three = &fp.Fp{0x6b0ee5d0fffffff5, 0x86f76d2b99b14bd0, 0xfffffffffffffffe, 0x3fffffffffffffff}
|
|
eight = &fp.Fp{0x7387134cffffffe1, 0xd973797adfadd5a8, 0xfffffffffffffffb, 0x3fffffffffffffff}
|
|
bool2int = map[bool]int{
|
|
true: 1,
|
|
false: 0,
|
|
}
|
|
)
|
|
|
|
var isomapper = [13]*fp.Fp{
|
|
new(fp.Fp).SetRaw(&[4]uint64{0x775f6034aaaaaaab, 0x4081775473d8375b, 0xe38e38e38e38e38e, 0x0e38e38e38e38e38}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0x8cf863b02814fb76, 0x0f93b82ee4b99495, 0x267c7ffa51cf412a, 0x3509afd51872d88e}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0x0eb64faef37ea4f7, 0x380af066cfeb6d69, 0x98c7d7ac3d98fd13, 0x17329b9ec5253753}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0xeebec06955555580, 0x8102eea8e7b06eb6, 0xc71c71c71c71c71c, 0x1c71c71c71c71c71}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0xc47f2ab668bcd71f, 0x9c434ac1c96b6980, 0x5a607fcce0494a79, 0x1d572e7ddc099cff}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0x2aa3af1eae5b6604, 0xb4abf9fb9a1fc81c, 0x1d13bf2a7f22b105, 0x325669becaecd5d1}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0x5ad985b5e38e38e4, 0x7642b01ad461bad2, 0x4bda12f684bda12f, 0x1a12f684bda12f68}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0xc67c31d8140a7dbb, 0x07c9dc17725cca4a, 0x133e3ffd28e7a095, 0x1a84d7ea8c396c47}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0x02e2be87d225b234, 0x1765e924f7459378, 0x303216cce1db9ff1, 0x3fb98ff0d2ddcadd}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0x93e53ab371c71c4f, 0x0ac03e8e134eb3e4, 0x7b425ed097b425ed, 0x025ed097b425ed09}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0x5a28279b1d1b42ae, 0x5941a3a4a97aa1b3, 0x0790bfb3506defb6, 0x0c02c5bcca0e6b7f}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0x4d90ab820b12320a, 0xd976bbfabbc5661d, 0x573b3d7f7d681310, 0x17033d3c60c68173}),
|
|
new(fp.Fp).SetRaw(&[4]uint64{0x992d30ecfffffde5, 0x224698fc094cf91b, 0x0000000000000000, 0x4000000000000000}),
|
|
}
|
|
|
|
var (
|
|
isoa = new(fp.Fp).SetRaw(&[4]uint64{0x92bb4b0b657a014b, 0xb74134581a27a59f, 0x49be2d7258370742, 0x18354a2eb0ea8c9c})
|
|
isob = new(fp.Fp).SetRaw(&[4]uint64{1265, 0, 0, 0})
|
|
z = new(fp.Fp).SetRaw(&[4]uint64{0x992d30ecfffffff4, 0x224698fc094cf91b, 0x0000000000000000, 0x4000000000000000})
|
|
)
|
|
|
|
var (
|
|
oldPallasInitonce sync.Once
|
|
oldPallas PallasCurve
|
|
)
|
|
|
|
type PallasCurve struct {
|
|
*elliptic.CurveParams
|
|
}
|
|
|
|
func oldPallasInitAll() {
|
|
oldPallas.CurveParams = new(elliptic.CurveParams)
|
|
oldPallas.P = 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,
|
|
})
|
|
oldPallas.N = 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,
|
|
})
|
|
g := new(Ep).Generator()
|
|
oldPallas.Gx = g.x.BigInt()
|
|
oldPallas.Gy = g.y.BigInt()
|
|
oldPallas.B = big.NewInt(5)
|
|
oldPallas.BitSize = 255
|
|
pallas.Name = PallasName
|
|
}
|
|
|
|
func Pallas() *PallasCurve {
|
|
oldPallasInitonce.Do(oldPallasInitAll)
|
|
return &oldPallas
|
|
}
|
|
|
|
func (curve *PallasCurve) Params() *elliptic.CurveParams {
|
|
return curve.CurveParams
|
|
}
|
|
|
|
func (curve *PallasCurve) IsOnCurve(x, y *big.Int) bool {
|
|
p := new(Ep)
|
|
p.x = new(fp.Fp).SetBigInt(x)
|
|
p.y = new(fp.Fp).SetBigInt(y)
|
|
p.z = new(fp.Fp).SetOne()
|
|
|
|
return p.IsOnCurve()
|
|
}
|
|
|
|
func (curve *PallasCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
|
p := new(Ep)
|
|
p.x = new(fp.Fp).SetBigInt(x1)
|
|
p.y = new(fp.Fp).SetBigInt(y1)
|
|
p.z = new(fp.Fp).SetOne()
|
|
if p.x.IsZero() && p.y.IsZero() {
|
|
p.z.SetZero()
|
|
}
|
|
|
|
q := new(Ep)
|
|
q.x = new(fp.Fp).SetBigInt(x2)
|
|
q.y = new(fp.Fp).SetBigInt(y2)
|
|
q.z = new(fp.Fp).SetOne()
|
|
if q.x.IsZero() && q.y.IsZero() {
|
|
q.z.SetZero()
|
|
}
|
|
p.Add(p, q)
|
|
p.toAffine()
|
|
return p.x.BigInt(), p.y.BigInt()
|
|
}
|
|
|
|
func (curve *PallasCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
|
p := new(Ep)
|
|
p.x = new(fp.Fp).SetBigInt(x1)
|
|
p.y = new(fp.Fp).SetBigInt(y1)
|
|
p.z = new(fp.Fp).SetOne()
|
|
if p.x.IsZero() && p.y.IsZero() {
|
|
p.z.SetZero()
|
|
}
|
|
p.Double(p)
|
|
p.toAffine()
|
|
return p.x.BigInt(), p.y.BigInt()
|
|
}
|
|
|
|
func (curve *PallasCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
|
|
p := new(Ep)
|
|
p.x = new(fp.Fp).SetBigInt(Bx)
|
|
p.y = new(fp.Fp).SetBigInt(By)
|
|
p.z = new(fp.Fp).SetOne()
|
|
if p.x.IsZero() && p.y.IsZero() {
|
|
p.z.SetZero()
|
|
}
|
|
var t [32]byte
|
|
copy(t[:], k)
|
|
ss := new(big.Int).SetBytes(k)
|
|
sc := new(fq.Fq).SetBigInt(ss)
|
|
p.Mul(p, sc)
|
|
p.toAffine()
|
|
return p.x.BigInt(), p.y.BigInt()
|
|
}
|
|
|
|
func (curve *PallasCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
|
|
p := new(Ep).Generator()
|
|
var t [32]byte
|
|
copy(t[:], k)
|
|
ss := new(big.Int).SetBytes(k)
|
|
sc := new(fq.Fq).SetBigInt(ss)
|
|
p.Mul(p, sc)
|
|
p.toAffine()
|
|
return p.x.BigInt(), p.y.BigInt()
|
|
}
|
|
|
|
// PallasScalar - Old interface
|
|
type PallasScalar struct{}
|
|
|
|
func NewPallasScalar() *PallasScalar {
|
|
return &PallasScalar{}
|
|
}
|
|
|
|
func (k PallasScalar) Add(x, y *big.Int) *big.Int {
|
|
r := new(big.Int).Add(x, y)
|
|
return r.Mod(r, fq.BiModulus)
|
|
}
|
|
|
|
func (k PallasScalar) Sub(x, y *big.Int) *big.Int {
|
|
r := new(big.Int).Sub(x, y)
|
|
return r.Mod(r, fq.BiModulus)
|
|
}
|
|
|
|
func (k PallasScalar) Neg(x *big.Int) *big.Int {
|
|
r := new(big.Int).Neg(x)
|
|
return r.Mod(r, fq.BiModulus)
|
|
}
|
|
|
|
func (k PallasScalar) Mul(x, y *big.Int) *big.Int {
|
|
r := new(big.Int).Mul(x, y)
|
|
return r.Mod(r, fq.BiModulus)
|
|
}
|
|
|
|
func (k PallasScalar) Div(x, y *big.Int) *big.Int {
|
|
r := new(big.Int).ModInverse(y, fq.BiModulus)
|
|
r.Mul(r, x)
|
|
return r.Mod(r, fq.BiModulus)
|
|
}
|
|
|
|
func (k PallasScalar) Hash(input []byte) *big.Int {
|
|
return new(ScalarPallas).Hash(input).(*ScalarPallas).value.BigInt()
|
|
}
|
|
|
|
func (k PallasScalar) Bytes(x *big.Int) []byte {
|
|
return x.Bytes()
|
|
}
|
|
|
|
func (k PallasScalar) Random() (*big.Int, error) {
|
|
s, ok := new(ScalarPallas).Random(crand.Reader).(*ScalarPallas)
|
|
if !ok {
|
|
return nil, errors.New("incorrect type conversion")
|
|
}
|
|
return s.value.BigInt(), nil
|
|
}
|
|
|
|
func (k PallasScalar) IsValid(x *big.Int) bool {
|
|
return x.Cmp(fq.BiModulus) == -1
|
|
}
|
|
|
|
// ScalarPallas - New interface
|
|
type ScalarPallas struct {
|
|
value *fq.Fq
|
|
}
|
|
|
|
func (s *ScalarPallas) Random(reader io.Reader) Scalar {
|
|
if reader == nil {
|
|
return nil
|
|
}
|
|
var seed [64]byte
|
|
_, _ = reader.Read(seed[:])
|
|
return s.Hash(seed[:])
|
|
}
|
|
|
|
func (s *ScalarPallas) Hash(bytes []byte) Scalar {
|
|
h, _ := blake2b.New(64, []byte{})
|
|
xmd, err := expandMsgXmd(h, bytes, []byte("pallas_XMD:BLAKE2b_SSWU_RO_"), 64)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var t [64]byte
|
|
copy(t[:], xmd)
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).SetBytesWide(&t),
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) Zero() Scalar {
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).SetZero(),
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) One() Scalar {
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).SetOne(),
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) IsZero() bool {
|
|
return s.value.IsZero()
|
|
}
|
|
|
|
func (s *ScalarPallas) IsOne() bool {
|
|
return s.value.IsOne()
|
|
}
|
|
|
|
func (s *ScalarPallas) IsOdd() bool {
|
|
return (s.value[0] & 1) == 1
|
|
}
|
|
|
|
func (s *ScalarPallas) IsEven() bool {
|
|
return (s.value[0] & 1) == 0
|
|
}
|
|
|
|
func (s *ScalarPallas) New(value int) Scalar {
|
|
v := big.NewInt(int64(value))
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).SetBigInt(v),
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) Cmp(rhs Scalar) int {
|
|
r, ok := rhs.(*ScalarPallas)
|
|
if ok {
|
|
return s.value.Cmp(r.value)
|
|
} else {
|
|
return -2
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) Square() Scalar {
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).Square(s.value),
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) Double() Scalar {
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).Double(s.value),
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) Invert() (Scalar, error) {
|
|
value, wasInverted := new(fq.Fq).Invert(s.value)
|
|
if !wasInverted {
|
|
return nil, fmt.Errorf("inverse doesn't exist")
|
|
}
|
|
return &ScalarPallas{
|
|
value,
|
|
}, nil
|
|
}
|
|
|
|
func (s *ScalarPallas) Sqrt() (Scalar, error) {
|
|
value, wasSquare := new(fq.Fq).Sqrt(s.value)
|
|
if !wasSquare {
|
|
return nil, fmt.Errorf("not a square")
|
|
}
|
|
return &ScalarPallas{
|
|
value,
|
|
}, nil
|
|
}
|
|
|
|
func (s *ScalarPallas) Cube() Scalar {
|
|
value := new(fq.Fq).Mul(s.value, s.value)
|
|
value.Mul(value, s.value)
|
|
return &ScalarPallas{
|
|
value,
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) Add(rhs Scalar) Scalar {
|
|
r, ok := rhs.(*ScalarPallas)
|
|
if ok {
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).Add(s.value, r.value),
|
|
}
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) Sub(rhs Scalar) Scalar {
|
|
r, ok := rhs.(*ScalarPallas)
|
|
if ok {
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).Sub(s.value, r.value),
|
|
}
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) Mul(rhs Scalar) Scalar {
|
|
r, ok := rhs.(*ScalarPallas)
|
|
if ok {
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).Mul(s.value, r.value),
|
|
}
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) MulAdd(y, z Scalar) Scalar {
|
|
return s.Mul(y).Add(z)
|
|
}
|
|
|
|
func (s *ScalarPallas) Div(rhs Scalar) Scalar {
|
|
r, ok := rhs.(*ScalarPallas)
|
|
if ok {
|
|
v, wasInverted := new(fq.Fq).Invert(r.value)
|
|
if !wasInverted {
|
|
return nil
|
|
}
|
|
v.Mul(v, s.value)
|
|
return &ScalarPallas{value: v}
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) Neg() Scalar {
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).Neg(s.value),
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) SetBigInt(v *big.Int) (Scalar, error) {
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).SetBigInt(v),
|
|
}, nil
|
|
}
|
|
|
|
func (s *ScalarPallas) BigInt() *big.Int {
|
|
return s.value.BigInt()
|
|
}
|
|
|
|
func (s *ScalarPallas) Bytes() []byte {
|
|
t := s.value.Bytes()
|
|
return t[:]
|
|
}
|
|
|
|
func (s *ScalarPallas) SetBytes(bytes []byte) (Scalar, error) {
|
|
if len(bytes) != 32 {
|
|
return nil, fmt.Errorf("invalid length")
|
|
}
|
|
var seq [32]byte
|
|
copy(seq[:], bytes)
|
|
value, err := new(fq.Fq).SetBytes(&seq)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &ScalarPallas{
|
|
value,
|
|
}, nil
|
|
}
|
|
|
|
func (s *ScalarPallas) SetBytesWide(bytes []byte) (Scalar, error) {
|
|
if len(bytes) != 64 {
|
|
return nil, fmt.Errorf("invalid length")
|
|
}
|
|
var seq [64]byte
|
|
copy(seq[:], bytes)
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).SetBytesWide(&seq),
|
|
}, nil
|
|
}
|
|
|
|
func (s *ScalarPallas) Point() Point {
|
|
return new(PointPallas).Identity()
|
|
}
|
|
|
|
func (s *ScalarPallas) Clone() Scalar {
|
|
return &ScalarPallas{
|
|
value: new(fq.Fq).Set(s.value),
|
|
}
|
|
}
|
|
|
|
func (s *ScalarPallas) GetFq() *fq.Fq {
|
|
return new(fq.Fq).Set(s.value)
|
|
}
|
|
|
|
func (s *ScalarPallas) SetFq(fq *fq.Fq) *ScalarPallas {
|
|
s.value = fq
|
|
return s
|
|
}
|
|
|
|
func (s *ScalarPallas) MarshalBinary() ([]byte, error) {
|
|
return scalarMarshalBinary(s)
|
|
}
|
|
|
|
func (s *ScalarPallas) UnmarshalBinary(input []byte) error {
|
|
sc, err := scalarUnmarshalBinary(input)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ss, ok := sc.(*ScalarPallas)
|
|
if !ok {
|
|
return fmt.Errorf("invalid scalar")
|
|
}
|
|
s.value = ss.value
|
|
return nil
|
|
}
|
|
|
|
func (s *ScalarPallas) MarshalText() ([]byte, error) {
|
|
return scalarMarshalText(s)
|
|
}
|
|
|
|
func (s *ScalarPallas) UnmarshalText(input []byte) error {
|
|
sc, err := scalarUnmarshalText(input)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ss, ok := sc.(*ScalarPallas)
|
|
if !ok {
|
|
return fmt.Errorf("invalid scalar")
|
|
}
|
|
s.value = ss.value
|
|
return nil
|
|
}
|
|
|
|
func (s *ScalarPallas) MarshalJSON() ([]byte, error) {
|
|
return scalarMarshalJson(s)
|
|
}
|
|
|
|
func (s *ScalarPallas) UnmarshalJSON(input []byte) error {
|
|
sc, err := scalarUnmarshalJson(input)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
S, ok := sc.(*ScalarPallas)
|
|
if !ok {
|
|
return fmt.Errorf("invalid type")
|
|
}
|
|
s.value = S.value
|
|
return nil
|
|
}
|
|
|
|
type PointPallas struct {
|
|
value *Ep
|
|
}
|
|
|
|
func (p *PointPallas) Random(reader io.Reader) Point {
|
|
return &PointPallas{new(Ep).Random(reader)}
|
|
}
|
|
|
|
func (p *PointPallas) Hash(bytes []byte) Point {
|
|
return &PointPallas{new(Ep).Hash(bytes)}
|
|
}
|
|
|
|
func (p *PointPallas) Identity() Point {
|
|
return &PointPallas{new(Ep).Identity()}
|
|
}
|
|
|
|
func (p *PointPallas) Generator() Point {
|
|
return &PointPallas{new(Ep).Generator()}
|
|
}
|
|
|
|
func (p *PointPallas) IsIdentity() bool {
|
|
return p.value.IsIdentity()
|
|
}
|
|
|
|
func (p *PointPallas) IsNegative() bool {
|
|
return p.value.Y().IsOdd()
|
|
}
|
|
|
|
func (p *PointPallas) IsOnCurve() bool {
|
|
return p.value.IsOnCurve()
|
|
}
|
|
|
|
func (p *PointPallas) Double() Point {
|
|
return &PointPallas{new(Ep).Double(p.value)}
|
|
}
|
|
|
|
func (p *PointPallas) Scalar() Scalar {
|
|
return &ScalarPallas{new(fq.Fq).SetZero()}
|
|
}
|
|
|
|
func (p *PointPallas) Neg() Point {
|
|
return &PointPallas{new(Ep).Neg(p.value)}
|
|
}
|
|
|
|
func (p *PointPallas) Add(rhs Point) Point {
|
|
r, ok := rhs.(*PointPallas)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return &PointPallas{new(Ep).Add(p.value, r.value)}
|
|
}
|
|
|
|
func (p *PointPallas) Sub(rhs Point) Point {
|
|
r, ok := rhs.(*PointPallas)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return &PointPallas{new(Ep).Sub(p.value, r.value)}
|
|
}
|
|
|
|
func (p *PointPallas) Mul(rhs Scalar) Point {
|
|
s, ok := rhs.(*ScalarPallas)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return &PointPallas{new(Ep).Mul(p.value, s.value)}
|
|
}
|
|
|
|
func (p *PointPallas) Equal(rhs Point) bool {
|
|
r, ok := rhs.(*PointPallas)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return p.value.Equal(r.value)
|
|
}
|
|
|
|
func (p *PointPallas) Set(x, y *big.Int) (Point, error) {
|
|
xx := subtle.ConstantTimeCompare(x.Bytes(), []byte{})
|
|
yy := subtle.ConstantTimeCompare(y.Bytes(), []byte{})
|
|
xElem := new(fp.Fp).SetBigInt(x)
|
|
var data [32]byte
|
|
if yy == 1 {
|
|
if xx == 1 {
|
|
return &PointPallas{new(Ep).Identity()}, nil
|
|
}
|
|
data = xElem.Bytes()
|
|
return p.FromAffineCompressed(data[:])
|
|
}
|
|
yElem := new(fp.Fp).SetBigInt(y)
|
|
value := &Ep{xElem, yElem, new(fp.Fp).SetOne()}
|
|
if !value.IsOnCurve() {
|
|
return nil, fmt.Errorf("point is not on the curve")
|
|
}
|
|
return &PointPallas{value}, nil
|
|
}
|
|
|
|
func (p *PointPallas) ToAffineCompressed() []byte {
|
|
return p.value.ToAffineCompressed()
|
|
}
|
|
|
|
func (p *PointPallas) ToAffineUncompressed() []byte {
|
|
return p.value.ToAffineUncompressed()
|
|
}
|
|
|
|
func (p *PointPallas) FromAffineCompressed(bytes []byte) (Point, error) {
|
|
value, err := new(Ep).FromAffineCompressed(bytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &PointPallas{value}, nil
|
|
}
|
|
|
|
func (p *PointPallas) FromAffineUncompressed(bytes []byte) (Point, error) {
|
|
value, err := new(Ep).FromAffineUncompressed(bytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &PointPallas{value}, nil
|
|
}
|
|
|
|
func (p *PointPallas) CurveName() string {
|
|
return PallasName
|
|
}
|
|
|
|
func (p *PointPallas) SumOfProducts(points []Point, scalars []Scalar) Point {
|
|
eps := make([]*Ep, len(points))
|
|
for i, pt := range points {
|
|
ps, ok := pt.(*PointPallas)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
eps[i] = ps.value
|
|
}
|
|
value := p.value.SumOfProducts(eps, scalars)
|
|
return &PointPallas{value}
|
|
}
|
|
|
|
func (p *PointPallas) MarshalBinary() ([]byte, error) {
|
|
return pointMarshalBinary(p)
|
|
}
|
|
|
|
func (p *PointPallas) UnmarshalBinary(input []byte) error {
|
|
pt, err := pointUnmarshalBinary(input)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ppt, ok := pt.(*PointPallas)
|
|
if !ok {
|
|
return fmt.Errorf("invalid point")
|
|
}
|
|
p.value = ppt.value
|
|
return nil
|
|
}
|
|
|
|
func (p *PointPallas) MarshalText() ([]byte, error) {
|
|
return pointMarshalText(p)
|
|
}
|
|
|
|
func (p *PointPallas) UnmarshalText(input []byte) error {
|
|
pt, err := pointUnmarshalText(input)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ppt, ok := pt.(*PointPallas)
|
|
if !ok {
|
|
return fmt.Errorf("invalid point")
|
|
}
|
|
p.value = ppt.value
|
|
return nil
|
|
}
|
|
|
|
func (p *PointPallas) MarshalJSON() ([]byte, error) {
|
|
return pointMarshalJSON(p)
|
|
}
|
|
|
|
func (p *PointPallas) UnmarshalJSON(input []byte) error {
|
|
pt, err := pointUnmarshalJSON(input)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
P, ok := pt.(*PointPallas)
|
|
if !ok {
|
|
return fmt.Errorf("invalid type")
|
|
}
|
|
p.value = P.value
|
|
return nil
|
|
}
|
|
|
|
func (p *PointPallas) X() *fp.Fp {
|
|
return p.value.X()
|
|
}
|
|
|
|
func (p *PointPallas) Y() *fp.Fp {
|
|
return p.value.Y()
|
|
}
|
|
|
|
func (p *PointPallas) GetEp() *Ep {
|
|
return new(Ep).Set(p.value)
|
|
}
|
|
|
|
type Ep struct {
|
|
x *fp.Fp
|
|
y *fp.Fp
|
|
z *fp.Fp
|
|
}
|
|
|
|
func (p *Ep) Random(reader io.Reader) *Ep {
|
|
var seed [64]byte
|
|
_, _ = reader.Read(seed[:])
|
|
return p.Hash(seed[:])
|
|
}
|
|
|
|
func (p *Ep) Hash(bytes []byte) *Ep {
|
|
if bytes == nil {
|
|
bytes = []byte{}
|
|
}
|
|
h, _ := blake2b.New(64, []byte{})
|
|
u, _ := expandMsgXmd(h, bytes, []byte("pallas_XMD:BLAKE2b_SSWU_RO_"), 128)
|
|
var buf [64]byte
|
|
copy(buf[:], u[:64])
|
|
u0 := new(fp.Fp).SetBytesWide(&buf)
|
|
copy(buf[:], u[64:])
|
|
u1 := new(fp.Fp).SetBytesWide(&buf)
|
|
|
|
q0 := mapSswu(u0)
|
|
q1 := mapSswu(u1)
|
|
r1 := isoMap(q0)
|
|
r2 := isoMap(q1)
|
|
return p.Identity().Add(r1, r2)
|
|
}
|
|
|
|
func (p *Ep) Identity() *Ep {
|
|
p.x = new(fp.Fp).SetZero()
|
|
p.y = new(fp.Fp).SetZero()
|
|
p.z = new(fp.Fp).SetZero()
|
|
return p
|
|
}
|
|
|
|
func (p *Ep) Generator() *Ep {
|
|
p.x = new(fp.Fp).SetOne()
|
|
p.y = &fp.Fp{0x2f474795455d409d, 0xb443b9b74b8255d9, 0x270c412f2c9a5d66, 0x8e00f71ba43dd6b}
|
|
p.z = new(fp.Fp).SetOne()
|
|
return p
|
|
}
|
|
|
|
func (p *Ep) IsIdentity() bool {
|
|
return p.z.IsZero()
|
|
}
|
|
|
|
func (p *Ep) Double(other *Ep) *Ep {
|
|
if other.IsIdentity() {
|
|
p.Set(other)
|
|
return p
|
|
}
|
|
r := new(Ep)
|
|
// essentially paraphrased https://github.com/MinaProtocol/c-reference-signer/blob/master/crypto.c#L306-L337
|
|
a := new(fp.Fp).Square(other.x)
|
|
b := new(fp.Fp).Square(other.y)
|
|
c := new(fp.Fp).Square(b)
|
|
r.x = new(fp.Fp).Add(other.x, b)
|
|
r.y = new(fp.Fp).Square(r.x)
|
|
r.z = new(fp.Fp).Sub(r.y, a)
|
|
r.x.Sub(r.z, c)
|
|
d := new(fp.Fp).Double(r.x)
|
|
e := new(fp.Fp).Mul(three, a)
|
|
f := new(fp.Fp).Square(e)
|
|
r.y.Double(d)
|
|
r.x.Sub(f, r.y)
|
|
r.y.Sub(d, r.x)
|
|
f.Mul(eight, c)
|
|
r.z.Mul(e, r.y)
|
|
r.y.Sub(r.z, f)
|
|
f.Mul(other.y, other.z)
|
|
r.z.Double(f)
|
|
p.Set(r)
|
|
return p
|
|
}
|
|
|
|
func (p *Ep) Neg(other *Ep) *Ep {
|
|
p.x = new(fp.Fp).Set(other.x)
|
|
p.y = new(fp.Fp).Neg(other.y)
|
|
p.z = new(fp.Fp).Set(other.z)
|
|
return p
|
|
}
|
|
|
|
func (p *Ep) Add(lhs *Ep, rhs *Ep) *Ep {
|
|
if lhs.IsIdentity() {
|
|
return p.Set(rhs)
|
|
}
|
|
if rhs.IsIdentity() {
|
|
return p.Set(lhs)
|
|
}
|
|
z1z1 := new(fp.Fp).Square(lhs.z)
|
|
z2z2 := new(fp.Fp).Square(rhs.z)
|
|
u1 := new(fp.Fp).Mul(lhs.x, z2z2)
|
|
u2 := new(fp.Fp).Mul(rhs.x, z1z1)
|
|
s1 := new(fp.Fp).Mul(lhs.y, z2z2)
|
|
s1.Mul(s1, rhs.z)
|
|
s2 := new(fp.Fp).Mul(rhs.y, z1z1)
|
|
s2.Mul(s2, lhs.z)
|
|
|
|
if u1.Equal(u2) {
|
|
if s1.Equal(s2) {
|
|
return p.Double(lhs)
|
|
} else {
|
|
return p.Identity()
|
|
}
|
|
} else {
|
|
h := new(fp.Fp).Sub(u2, u1)
|
|
i := new(fp.Fp).Double(h)
|
|
i.Square(i)
|
|
j := new(fp.Fp).Mul(i, h)
|
|
r := new(fp.Fp).Sub(s2, s1)
|
|
r.Double(r)
|
|
v := new(fp.Fp).Mul(u1, i)
|
|
x3 := new(fp.Fp).Square(r)
|
|
x3.Sub(x3, j)
|
|
x3.Sub(x3, new(fp.Fp).Double(v))
|
|
s1.Mul(s1, j)
|
|
s1.Double(s1)
|
|
y3 := new(fp.Fp).Mul(r, new(fp.Fp).Sub(v, x3))
|
|
y3.Sub(y3, s1)
|
|
z3 := new(fp.Fp).Add(lhs.z, rhs.z)
|
|
z3.Square(z3)
|
|
z3.Sub(z3, z1z1)
|
|
z3.Sub(z3, z2z2)
|
|
z3.Mul(z3, h)
|
|
p.x = new(fp.Fp).Set(x3)
|
|
p.y = new(fp.Fp).Set(y3)
|
|
p.z = new(fp.Fp).Set(z3)
|
|
|
|
return p
|
|
}
|
|
}
|
|
|
|
func (p *Ep) Sub(lhs, rhs *Ep) *Ep {
|
|
return p.Add(lhs, new(Ep).Neg(rhs))
|
|
}
|
|
|
|
func (p *Ep) Mul(point *Ep, scalar *fq.Fq) *Ep {
|
|
bytes := scalar.Bytes()
|
|
precomputed := [16]*Ep{}
|
|
precomputed[0] = new(Ep).Identity()
|
|
precomputed[1] = new(Ep).Set(point)
|
|
for i := 2; i < 16; i += 2 {
|
|
precomputed[i] = new(Ep).Double(precomputed[i>>1])
|
|
precomputed[i+1] = new(Ep).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
|
|
}
|
|
|
|
func (p *Ep) Equal(other *Ep) bool {
|
|
// warning: requires converting both to affine
|
|
// could save slightly by modifying one so that its z-value equals the other
|
|
// this would save one inversion and a handful of multiplications
|
|
// but this is more subtle and error-prone, so going to just convert both to affine.
|
|
lhs := new(Ep).Set(p)
|
|
rhs := new(Ep).Set(other)
|
|
lhs.toAffine()
|
|
rhs.toAffine()
|
|
return lhs.x.Equal(rhs.x) && lhs.y.Equal(rhs.y)
|
|
}
|
|
|
|
func (p *Ep) Set(other *Ep) *Ep {
|
|
// check is identity or on curve
|
|
p.x = new(fp.Fp).Set(other.x)
|
|
p.y = new(fp.Fp).Set(other.y)
|
|
p.z = new(fp.Fp).Set(other.z)
|
|
return p
|
|
}
|
|
|
|
func (p *Ep) toAffine() *Ep {
|
|
// mutates `p` in-place to convert it to "affine" form.
|
|
if p.IsIdentity() {
|
|
// warning: control flow / not constant-time
|
|
p.x.SetZero()
|
|
p.y.SetZero()
|
|
p.z.SetOne()
|
|
return p
|
|
}
|
|
zInv3, _ := new(fp.Fp).Invert(p.z) // z is necessarily nonzero
|
|
zInv2 := new(fp.Fp).Square(zInv3)
|
|
zInv3.Mul(zInv3, zInv2)
|
|
p.x.Mul(p.x, zInv2)
|
|
p.y.Mul(p.y, zInv3)
|
|
p.z.SetOne()
|
|
return p
|
|
}
|
|
|
|
func (p *Ep) ToAffineCompressed() []byte {
|
|
// Use ZCash encoding where infinity is all zeros
|
|
// and the top bit represents the sign of y and the
|
|
// remainder represent the x-coordinate
|
|
var inf [32]byte
|
|
p1 := new(Ep).Set(p)
|
|
p1.toAffine()
|
|
x := p1.x.Bytes()
|
|
x[31] |= (p1.y.Bytes()[0] & 1) << 7
|
|
subtle.ConstantTimeCopy(bool2int[p1.IsIdentity()], x[:], inf[:])
|
|
return x[:]
|
|
}
|
|
|
|
func (p *Ep) ToAffineUncompressed() []byte {
|
|
p1 := new(Ep).Set(p)
|
|
p1.toAffine()
|
|
x := p1.x.Bytes()
|
|
y := p1.y.Bytes()
|
|
return append(x[:], y[:]...)
|
|
}
|
|
|
|
func (p *Ep) FromAffineCompressed(bytes []byte) (*Ep, error) {
|
|
if len(bytes) != 32 {
|
|
return nil, fmt.Errorf("invalid byte sequence")
|
|
}
|
|
|
|
var input [32]byte
|
|
copy(input[:], bytes)
|
|
sign := (input[31] >> 7) & 1
|
|
input[31] &= 0x7F
|
|
|
|
x := new(fp.Fp)
|
|
if _, err := x.SetBytes(&input); err != nil {
|
|
return nil, err
|
|
}
|
|
rhs := rhsPallas(x)
|
|
if _, square := rhs.Sqrt(rhs); !square {
|
|
return nil, fmt.Errorf("rhs of given x-coordinate is not a square")
|
|
}
|
|
if rhs.Bytes()[0]&1 != sign {
|
|
rhs.Neg(rhs)
|
|
}
|
|
p.x = x
|
|
p.y = rhs
|
|
p.z = new(fp.Fp).SetOne()
|
|
if !p.IsOnCurve() {
|
|
return nil, fmt.Errorf("invalid point")
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
func (p *Ep) FromAffineUncompressed(bytes []byte) (*Ep, error) {
|
|
if len(bytes) != 64 {
|
|
return nil, fmt.Errorf("invalid length")
|
|
}
|
|
p.z = new(fp.Fp).SetOne()
|
|
p.x = new(fp.Fp)
|
|
p.y = new(fp.Fp)
|
|
var x, y [32]byte
|
|
copy(x[:], bytes[:32])
|
|
copy(y[:], bytes[32:])
|
|
if _, err := p.x.SetBytes(&x); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := p.y.SetBytes(&y); err != nil {
|
|
return nil, err
|
|
}
|
|
if !p.IsOnCurve() {
|
|
return nil, fmt.Errorf("invalid point")
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
// rhs of the curve equation
|
|
func rhsPallas(x *fp.Fp) *fp.Fp {
|
|
x2 := new(fp.Fp).Square(x)
|
|
x3 := new(fp.Fp).Mul(x, x2)
|
|
return new(fp.Fp).Add(x3, b)
|
|
}
|
|
|
|
func (p Ep) CurveName() string {
|
|
return "pallas"
|
|
}
|
|
|
|
func (p Ep) SumOfProducts(points []*Ep, scalars []Scalar) *Ep {
|
|
nScalars := make([]*big.Int, len(scalars))
|
|
for i, s := range scalars {
|
|
sc, ok := s.(*ScalarPallas)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
nScalars[i] = sc.value.BigInt()
|
|
}
|
|
return sumOfProductsPippengerPallas(points, nScalars)
|
|
}
|
|
|
|
func (p *Ep) X() *fp.Fp {
|
|
t := new(Ep).Set(p)
|
|
t.toAffine()
|
|
return new(fp.Fp).Set(t.x)
|
|
}
|
|
|
|
func (p *Ep) Y() *fp.Fp {
|
|
t := new(Ep).Set(p)
|
|
t.toAffine()
|
|
return new(fp.Fp).Set(t.y)
|
|
}
|
|
|
|
func (p *Ep) IsOnCurve() bool {
|
|
// y^2 = x^3 + axz^4 + bz^6
|
|
// a = 0
|
|
// b = 5
|
|
z2 := new(fp.Fp).Square(p.z)
|
|
z4 := new(fp.Fp).Square(z2)
|
|
z6 := new(fp.Fp).Mul(z2, z4)
|
|
x2 := new(fp.Fp).Square(p.x)
|
|
x3 := new(fp.Fp).Mul(x2, p.x)
|
|
|
|
lhs := new(fp.Fp).Square(p.y)
|
|
rhs := new(fp.Fp).SetUint64(5)
|
|
rhs.Mul(rhs, z6)
|
|
rhs.Add(rhs, x3)
|
|
return p.z.IsZero() || lhs.Equal(rhs)
|
|
}
|
|
|
|
func (p *Ep) CMove(lhs, rhs *Ep, condition int) *Ep {
|
|
p.x = new(fp.Fp).CMove(lhs.x, rhs.x, condition)
|
|
p.y = new(fp.Fp).CMove(lhs.y, rhs.y, condition)
|
|
p.z = new(fp.Fp).CMove(lhs.z, rhs.z, condition)
|
|
return p
|
|
}
|
|
|
|
func sumOfProductsPippengerPallas(points []*Ep, scalars []*big.Int) *Ep {
|
|
if len(points) != len(scalars) {
|
|
return nil
|
|
}
|
|
|
|
const w = 6
|
|
|
|
bucketSize := (1 << w) - 1
|
|
windows := make([]*Ep, 255/w+1)
|
|
for i := range windows {
|
|
windows[i] = new(Ep).Identity()
|
|
}
|
|
bucket := make([]*Ep, bucketSize)
|
|
|
|
for j := 0; j < len(windows); j++ {
|
|
for i := 0; i < bucketSize; i++ {
|
|
bucket[i] = new(Ep).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].Add(bucket[index-1], points[i])
|
|
}
|
|
}
|
|
|
|
acc, sum := new(Ep).Identity(), new(Ep).Identity()
|
|
|
|
for i := bucketSize - 1; i >= 0; i-- {
|
|
sum.Add(sum, bucket[i])
|
|
acc.Add(acc, sum)
|
|
}
|
|
windows[j] = acc
|
|
}
|
|
|
|
acc := new(Ep).Identity()
|
|
for i := len(windows) - 1; i >= 0; i-- {
|
|
for j := 0; j < w; j++ {
|
|
acc.Double(acc)
|
|
}
|
|
acc.Add(acc, windows[i])
|
|
}
|
|
return acc
|
|
}
|
|
|
|
// Implements a degree 3 isogeny map.
|
|
// The input and output are in Jacobian coordinates, using the method
|
|
// in "Avoiding inversions" [WB2019, section 4.3].
|
|
func isoMap(p *Ep) *Ep {
|
|
var z [4]*fp.Fp
|
|
z[0] = new(fp.Fp).Square(p.z) // z^2
|
|
z[1] = new(fp.Fp).Mul(z[0], p.z) // z^3
|
|
z[2] = new(fp.Fp).Square(z[0]) // z^4
|
|
z[3] = new(fp.Fp).Square(z[1]) // z^6
|
|
|
|
// ((iso[0] * x + iso[1] * z^2) * x + iso[2] * z^4) * x + iso[3] * z^6
|
|
numX := new(fp.Fp).Set(isomapper[0])
|
|
numX.Mul(numX, p.x)
|
|
numX.Add(numX, new(fp.Fp).Mul(isomapper[1], z[0]))
|
|
numX.Mul(numX, p.x)
|
|
numX.Add(numX, new(fp.Fp).Mul(isomapper[2], z[2]))
|
|
numX.Mul(numX, p.x)
|
|
numX.Add(numX, new(fp.Fp).Mul(isomapper[3], z[3]))
|
|
|
|
// (z^2 * x + iso[4] * z^4) * x + iso[5] * z^6
|
|
divX := new(fp.Fp).Set(z[0])
|
|
divX.Mul(divX, p.x)
|
|
divX.Add(divX, new(fp.Fp).Mul(isomapper[4], z[2]))
|
|
divX.Mul(divX, p.x)
|
|
divX.Add(divX, new(fp.Fp).Mul(isomapper[5], z[3]))
|
|
|
|
// (((iso[6] * x + iso[7] * z2) * x + iso[8] * z4) * x + iso[9] * z6) * y
|
|
numY := new(fp.Fp).Set(isomapper[6])
|
|
numY.Mul(numY, p.x)
|
|
numY.Add(numY, new(fp.Fp).Mul(isomapper[7], z[0]))
|
|
numY.Mul(numY, p.x)
|
|
numY.Add(numY, new(fp.Fp).Mul(isomapper[8], z[2]))
|
|
numY.Mul(numY, p.x)
|
|
numY.Add(numY, new(fp.Fp).Mul(isomapper[9], z[3]))
|
|
numY.Mul(numY, p.y)
|
|
|
|
// (((x + iso[10] * z2) * x + iso[11] * z4) * x + iso[12] * z6) * z3
|
|
divY := new(fp.Fp).Set(p.x)
|
|
divY.Add(divY, new(fp.Fp).Mul(isomapper[10], z[0]))
|
|
divY.Mul(divY, p.x)
|
|
divY.Add(divY, new(fp.Fp).Mul(isomapper[11], z[2]))
|
|
divY.Mul(divY, p.x)
|
|
divY.Add(divY, new(fp.Fp).Mul(isomapper[12], z[3]))
|
|
divY.Mul(divY, z[1])
|
|
|
|
z0 := new(fp.Fp).Mul(divX, divY)
|
|
x := new(fp.Fp).Mul(numX, divY)
|
|
x.Mul(x, z0)
|
|
y := new(fp.Fp).Mul(numY, divX)
|
|
y.Mul(y, new(fp.Fp).Square(z0))
|
|
|
|
return &Ep{
|
|
x, y, z0,
|
|
}
|
|
}
|
|
|
|
func mapSswu(u *fp.Fp) *Ep {
|
|
// c1 := new(fp.Fp).Neg(isoa)
|
|
// c1.Invert(c1)
|
|
// c1.Mul(isob, c1)
|
|
c1 := &fp.Fp{
|
|
0x1ee770ce078456ec,
|
|
0x48cfd64c2ce76be0,
|
|
0x43d5774c0ab79e2f,
|
|
0x23368d2bdce28cf3,
|
|
}
|
|
// c2 := new(fp.Fp).Neg(z)
|
|
// c2.Invert(c2)
|
|
c2 := &fp.Fp{
|
|
0x03df915f89d89d8a,
|
|
0x8f1e8db09ef82653,
|
|
0xd89d89d89d89d89d,
|
|
0x1d89d89d89d89d89,
|
|
}
|
|
|
|
u2 := new(fp.Fp).Square(u)
|
|
tv1 := new(fp.Fp).Mul(z, u2)
|
|
tv2 := new(fp.Fp).Square(tv1)
|
|
x1 := new(fp.Fp).Add(tv1, tv2)
|
|
x1.Invert(x1)
|
|
e1 := bool2int[x1.IsZero()]
|
|
x1.Add(x1, new(fp.Fp).SetOne())
|
|
x1.CMove(x1, c2, e1)
|
|
x1.Mul(x1, c1)
|
|
gx1 := new(fp.Fp).Square(x1)
|
|
gx1.Add(gx1, isoa)
|
|
gx1.Mul(gx1, x1)
|
|
gx1.Add(gx1, isob)
|
|
x2 := new(fp.Fp).Mul(tv1, x1)
|
|
tv2.Mul(tv1, tv2)
|
|
gx2 := new(fp.Fp).Mul(gx1, tv2)
|
|
gx1Sqrt, e2 := new(fp.Fp).Sqrt(gx1)
|
|
x := new(fp.Fp).CMove(x2, x1, bool2int[e2])
|
|
gx2Sqrt, _ := new(fp.Fp).Sqrt(gx2)
|
|
y := new(fp.Fp).CMove(gx2Sqrt, gx1Sqrt, bool2int[e2])
|
|
e3 := u.IsOdd() == y.IsOdd()
|
|
y.CMove(new(fp.Fp).Neg(y), y, bool2int[e3])
|
|
|
|
return &Ep{
|
|
x: x, y: y, z: new(fp.Fp).SetOne(),
|
|
}
|
|
}
|