From 557b5a320e31508408a97b4b466ee1c3ea5653e7 Mon Sep 17 00:00:00 2001 From: Daniel Bourdrez <3641580+bytemare@users.noreply.github.com> Date: Fri, 20 Jan 2023 14:38:03 +0100 Subject: [PATCH] Add Edwards25519 and use always-square-and-multiply for pow, and fix ristretto pow (#26) Signed-off-by: bytemare <3641580+bytemare@users.noreply.github.com> --- .github/.golangci.yml | 2 +- go.mod | 1 + go.sum | 2 + groups.go | 6 + internal/edwards25519/element.go | 173 +++++++++ internal/edwards25519/group.go | 82 +++++ internal/edwards25519/map.go | 192 ++++++++++ internal/edwards25519/scalar.go | 338 ++++++++++++++++++ internal/ristretto/element.go | 28 +- internal/ristretto/ristretto.go | 2 +- internal/ristretto/scalar.go | 142 +++++--- tests/element_test.go | 3 +- tests/groups_test.go | 25 +- tests/h2c_test.go | 36 +- tests/scalar_test.go | 130 ++++++- tests/utils_test.go | 55 ++- .../edwards25519_XMD-SHA-512_ELL2_NU_.json | 90 +++++ .../edwards25519_XMD-SHA-512_ELL2_RO_.json | 115 ++++++ 18 files changed, 1335 insertions(+), 87 deletions(-) create mode 100644 internal/edwards25519/element.go create mode 100644 internal/edwards25519/group.go create mode 100644 internal/edwards25519/map.go create mode 100644 internal/edwards25519/scalar.go create mode 100644 tests/vectors/edwards25519_XMD-SHA-512_ELL2_NU_.json create mode 100644 tests/vectors/edwards25519_XMD-SHA-512_ELL2_RO_.json diff --git a/.github/.golangci.yml b/.github/.golangci.yml index ab13f0d..029a01c 100644 --- a/.github/.golangci.yml +++ b/.github/.golangci.yml @@ -119,7 +119,7 @@ linters-settings: # Default: false custom-order: true gocognit: - min-complexity: 15 + min-complexity: 17 goconst: min-len: 2 min-occurrences: 2 diff --git a/go.mod b/go.mod index b191307..f49153f 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/bytemare/crypto go 1.19 require ( + filippo.io/edwards25519 v1.0.0 filippo.io/nistec v0.0.0-20220513155737-c4b6d02e738c github.com/bytemare/hash2curve v0.1.2 github.com/gtank/ristretto255 v0.1.2 diff --git a/go.sum b/go.sum index def0525..006db1b 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/nistec v0.0.0-20220513155737-c4b6d02e738c h1:x4epP2lA8b5UYoIFjcVpN+MfJQeX5M5Yilmc1VH0YDw= filippo.io/nistec v0.0.0-20220513155737-c4b6d02e738c/go.mod h1:84fxC9mi+MhC2AERXI4LSa8cmSVOzrFikg6hZ4IfCyw= github.com/bytemare/hash v0.1.3 h1:E2v/+gqvLTjaR8W2JdhqaB2L9161yFBlSXDnYEyMt94= diff --git a/groups.go b/groups.go index 697cfe1..eb3a590 100644 --- a/groups.go +++ b/groups.go @@ -18,6 +18,7 @@ import ( "sync" "github.com/bytemare/crypto/internal" + "github.com/bytemare/crypto/internal/edwards25519" "github.com/bytemare/crypto/internal/nist" "github.com/bytemare/crypto/internal/ristretto" ) @@ -41,6 +42,9 @@ const ( // P521Sha512 identifies a group over P521 with SHA2-512 hash-to-group hashing. P521Sha512 + // Edwards25519Sha512 identifies the Edwards25519 group with SHA2-512 hash-to-group hashing. + Edwards25519Sha512 + maxID dstfmt = "%s-V%02d-CS%02d-%s" @@ -157,6 +161,8 @@ func (g Group) init() { g.initGroup(nist.P384) case P521Sha512: g.initGroup(nist.P521) + case Edwards25519Sha512: + g.initGroup(edwards25519.New) case maxID: fallthrough default: diff --git a/internal/edwards25519/element.go b/internal/edwards25519/element.go new file mode 100644 index 0000000..0cd7b2b --- /dev/null +++ b/internal/edwards25519/element.go @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree or at +// https://spdx.org/licenses/MIT.html + +package edwards25519 + +import ( + "fmt" + + ed "filippo.io/edwards25519" + + "github.com/bytemare/crypto/internal" +) + +// Element implements the Element interface for the Ristretto255 group element. +type Element struct { + element ed.Point +} + +func checkElement(element internal.Element) *Element { + if element == nil { + panic(internal.ErrParamNilPoint) + } + + ec, ok := element.(*Element) + if !ok { + panic(internal.ErrCastElement) + } + + return ec +} + +// Base sets the element to the group's base point a.k.a. canonical generator. +func (e *Element) Base() internal.Element { + e.element.Set(ed.NewGeneratorPoint()) + return e +} + +// Identity sets the element to the point at infinity of the Group's underlying curve. +func (e *Element) Identity() internal.Element { + e.element.Set(ed.NewIdentityPoint()) + return e +} + +// Add sets the receiver to the sum of the input and the receiver, and returns the receiver. +func (e *Element) Add(element internal.Element) internal.Element { + ec := checkElement(element) + e.element.Add(&e.element, &ec.element) + + return e +} + +// Double sets the receiver to its double, and returns it. +func (e *Element) Double() internal.Element { + e.element.Add(&e.element, &e.element) + return e +} + +// Negate sets the receiver to its negation, and returns it. +func (e *Element) Negate() internal.Element { + e.element.Negate(&e.element) + return e +} + +// Subtract subtracts the input from the receiver, and returns the receiver. +func (e *Element) Subtract(element internal.Element) internal.Element { + ec := checkElement(element) + e.element.Subtract(&e.element, &ec.element) + + return e +} + +// Multiply sets the receiver to the scalar multiplication of the receiver with the given Scalar, and returns it. +func (e *Element) Multiply(scalar internal.Scalar) internal.Element { + if scalar == nil { + e.Identity() + return e + } + + sc := assert(scalar) + e.element.ScalarMult(&sc.scalar, &e.element) + + return e +} + +// Equal returns 1 if the elements are equivalent, and 0 otherwise. +func (e *Element) Equal(element internal.Element) int { + ec := checkElement(element) + return e.element.Equal(&ec.element) +} + +// IsIdentity returns whether the Element is the point at infinity of the Group's underlying curve. +func (e *Element) IsIdentity() bool { + return e.element.Equal(ed.NewIdentityPoint()) == 1 +} + +func (e *Element) set(element *Element) *Element { + *e = *element + return e +} + +// Set sets the receiver to the value of the argument, and returns the receiver. +func (e *Element) Set(element internal.Element) internal.Element { + if element == nil { + return e.set(nil) + } + + ec, ok := element.(*Element) + if !ok { + panic(internal.ErrCastElement) + } + + return e.set(ec) +} + +// Copy returns a copy of the receiver. +func (e *Element) Copy() internal.Element { + return &Element{*ed.NewIdentityPoint().Set(&e.element)} +} + +// Encode returns the compressed byte encoding of the element. +func (e *Element) Encode() []byte { + return e.element.Bytes() +} + +// XCoordinate returns the encoded x coordinate of the element, which is the same as Encode(). +func (e *Element) XCoordinate() []byte { + return e.element.BytesMontgomery() +} + +func decodeElement(element []byte) (*ed.Point, error) { + if len(element) == 0 { + return nil, internal.ErrParamNilPoint + } + + e := ed.NewIdentityPoint() + if _, err := e.SetBytes(element); err != nil { + return nil, fmt.Errorf("edwards25519 element Decode: %w", err) + } + + return e, nil +} + +// Decode sets the receiver to a decoding of the input data, and returns an error on failure. +func (e *Element) Decode(data []byte) error { + element, err := decodeElement(data) + if err != nil { + return err + } + + // superfluous identity check + if element.Equal(ed.NewIdentityPoint()) == 1 { + return internal.ErrIdentity + } + + e.element = *element + + return nil +} + +// MarshalBinary returns the compressed byte encoding of the element. +func (e *Element) MarshalBinary() (data []byte, err error) { + return e.Encode(), nil +} + +// UnmarshalBinary sets e to the decoding of the byte encoded element. +func (e *Element) UnmarshalBinary(data []byte) error { + return e.Decode(data) +} diff --git a/internal/edwards25519/group.go b/internal/edwards25519/group.go new file mode 100644 index 0000000..01e955b --- /dev/null +++ b/internal/edwards25519/group.go @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree or at +// https://spdx.org/licenses/MIT.html + +// Package edwards25519 allows simple and abstracted operations in the Edwards25519 group. +package edwards25519 + +import ( + ed "filippo.io/edwards25519" + + "github.com/bytemare/crypto/internal" +) + +const ( + canonicalEncodingLength = 32 + orderPrime = "7237005577332262213973186563042994240857116359379907606001950938285454250989" +) + +// Group represents the Edwards25519 group. It exposes a prime-order group API with hash-to-curve operations. +type Group struct{} + +// New returns a new instantiation of the Edwards25519 Group. +func New() internal.Group { + return Group{} +} + +// NewScalar returns a new, empty, scalar. +func (g Group) NewScalar() internal.Scalar { + return &Scalar{*ed.NewScalar()} +} + +// NewElement returns the identity element (point at infinity). +func (g Group) NewElement() internal.Element { + return &Element{*ed.NewIdentityPoint()} +} + +// Base returns group's base point a.k.a. canonical generator. +func (g Group) Base() internal.Element { + return &Element{*ed.NewGeneratorPoint()} +} + +// HashToScalar returns a safe mapping of the arbitrary input to a Scalar. +// The DST must not be empty or nil, and is recommended to be longer than 16 bytes. +func (g Group) HashToScalar(input, dst []byte) internal.Scalar { + return &Scalar{*HashToEdwards25519Field(input, dst)} +} + +// HashToGroup returns a safe mapping of the arbitrary input to an Element in the Group. +// The DST must not be empty or nil, and is recommended to be longer than 16 bytes. +func (g Group) HashToGroup(input, dst []byte) internal.Element { + return &Element{*HashToEdwards25519(input, dst)} +} + +// EncodeToGroup returns a non-uniform mapping of the arbitrary input to an Element in the Group. +// The DST must not be empty or nil, and is recommended to be longer than 16 bytes. +func (g Group) EncodeToGroup(input, dst []byte) internal.Element { + return &Element{*EncodeToEdwards25519(input, dst)} +} + +// Ciphersuite returns the hash-to-curve ciphersuite identifier. +func (g Group) Ciphersuite() string { + return H2C +} + +// ScalarLength returns the byte size of an encoded element. +func (g Group) ScalarLength() uint { + return canonicalEncodingLength +} + +// ElementLength returns the byte size of an encoded element. +func (g Group) ElementLength() uint { + return canonicalEncodingLength +} + +// Order returns the order of the canonical group of scalars. +func (g Group) Order() string { + return orderPrime +} diff --git a/internal/edwards25519/map.go b/internal/edwards25519/map.go new file mode 100644 index 0000000..5513d62 --- /dev/null +++ b/internal/edwards25519/map.go @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree or at +// https://spdx.org/licenses/MIT.html + +package edwards25519 + +import ( + "crypto" + "math/big" + + "filippo.io/edwards25519" + "filippo.io/edwards25519/field" + "github.com/bytemare/hash2curve" +) + +const ( + // H2C represents the hash-to-curve string identifier. + H2C = "edwards25519_XMD:SHA-512_ELL2_RO_" + + // p25519 is the prime 2^255 - 19 for the field. + // = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed. + p25519 = "57896044618658097711785492504343953926634992332820282019728792003956564819949" +) + +var ( + a, _ = fe().SetBytes([]byte{ + 6, 109, 7, 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, + }) + minA = fe().Negate(a) + zero = fe().Zero() + one = fe().One() + minOne = fe().Negate(one) + two = fe().Add(one, one) + invsqrtD, _ = fe().SetBytes([]byte{ + 6, 126, 69, 255, 170, 4, 110, 204, 130, 26, 125, 75, 209, 211, 161, 197, + 126, 79, 252, 3, 220, 8, 123, 210, 187, 6, 160, 96, 244, 237, 38, 15, + }) + + fieldPrime, _ = new(big.Int).SetString(p25519, 10) +) + +func fe() *field.Element { + return new(field.Element) +} + +func element(input []byte) *field.Element { + e, err := new(field.Element).SetBytes(input) + if err != nil { + panic(err) + } + + return e +} + +func adjust(in []byte) []byte { + // If necessary, build a buffer of right size, so it gets correctly interpreted. + if l := canonicalEncodingLength - len(in); l > 0 { + buf := make([]byte, l, canonicalEncodingLength) + buf = append(buf, in...) + in = buf + } + + // Reverse, because filippo.io/edwards25519 works in little-endian + return reverse(in) +} + +func reverse(b []byte) []byte { + l := len(b) - 1 + for i := 0; i < len(b)/2; i++ { + b[i], b[l-i] = b[l-i], b[i] + } + + return b +} + +// HashToEdwards25519Field implements hash-to-scalar mapping modulo the order of Edwards25519 using input with dst. +func HashToEdwards25519Field(input, dst []byte) *edwards25519.Scalar { + sc := hash2curve.HashToFieldXMD(crypto.SHA512, input, dst, 1, 1, 48, &order) + b := adjust(sc[0].Bytes()) + + s, err := edwards25519.NewScalar().SetCanonicalBytes(b) + if err != nil { + panic(err) + } + + return s +} + +// HashToEdwards25519 implements hash-to-curve mapping to Edwards25519 of input with dst. +func HashToEdwards25519(input, dst []byte) *edwards25519.Point { + u := hash2curve.HashToFieldXMD(crypto.SHA512, input, dst, 2, 1, 48, fieldPrime) + q0 := element(adjust(u[0].Bytes())) + q1 := element(adjust(u[1].Bytes())) + p0 := Elligator2Edwards(q0) + p1 := Elligator2Edwards(q1) + p0.Add(p0, p1) + p0.MultByCofactor(p0) + + return p0 +} + +// EncodeToEdwards25519 implements encode-to-curve mapping to Edwards25519 of input with dst. +func EncodeToEdwards25519(input, dst []byte) *edwards25519.Point { + q := hash2curve.HashToFieldXMD(crypto.SHA512, input, dst, 1, 1, 48, fieldPrime) + b := adjust(q[0].Bytes()) + p0 := Elligator2Edwards(element(b)) + p0.MultByCofactor(p0) + + return p0 +} + +// Elligator2Edwards maps the field element to a point on Edwards25519. +func Elligator2Edwards(e *field.Element) *edwards25519.Point { + u, v := Elligator2Montgomery(e) + x, y := MontgomeryToEdwards(u, v) + + return AffineToEdwards(x, y) +} + +// Elligator2Montgomery implements the Elligator2 mapping to Curve25519. +func Elligator2Montgomery(e *field.Element) (x, y *field.Element) { + t1 := fe().Square(e) // u^2 + t1.Multiply(t1, two) // t1 = 2u^2 + e1 := t1.Equal(minOne) // + t1.Swap(zero, e1) // if 2u^2 == -1, t1 = 0 + + x1 := fe().Add(t1, one) // t1 + 1 + x1.Invert(x1) // 1 / (t1 + 1) + x1.Multiply(x1, minA) // x1 = -A / (t1 + 1) + + gx1 := fe().Add(x1, a) // x1 + A + gx1.Multiply(gx1, x1) // x1 * (x1 + A) + gx1.Add(gx1, one) // x1 * (x1 + A) + 1 + gx1.Multiply(gx1, x1) // x1 * (x1 * (x1 + A) + 1) + + x2 := fe().Negate(x1) // -x1 + x2.Subtract(x2, a) // -x2 - A + + gx2 := fe().Multiply(t1, gx1) // t1 * gx1 + + root1, _isSquare := fe().SqrtRatio(gx1, one) // root1 = (+) sqrt(gx1) + negRoot1 := fe().Negate(root1) // negRoot1 = (-) sqrt(gx1) + root2, _ := fe().SqrtRatio(gx2, one) // root2 = (+) sqrt(gx2) + + // if gx1 is square, set the point to (x1, -root1) + // if not, set the point to (x2, +root2) + if _isSquare == 1 { + x = x1 + y = negRoot1 // set sgn0(y) == 1, i.e. negative + } else { + x = x2 + y = root2 // set sgn0(y) == 0, i.e. positive + } + + return x, y +} + +// AffineToEdwards takes the affine coordinates of an Edwards25519 and returns a pointer to Point represented in +// extended projective coordinates. +func AffineToEdwards(x, y *field.Element) *edwards25519.Point { + t := fe().Multiply(x, y) + + p, err := new(edwards25519.Point).SetExtendedCoordinates(x, y, fe().One(), t) + if err != nil { + panic(err) + } + + return p +} + +// MontgomeryToEdwards lifts a Curve25519 point to its Edwards25519 equivalent. +func MontgomeryToEdwards(u, v *field.Element) (x, y *field.Element) { + x = fe().Invert(v) + x.Multiply(x, u) + x.Multiply(x, invsqrtD) + + y = MontgomeryUToEdwardsY(u) + + return +} + +// MontgomeryUToEdwardsY transforms a Curve25519 x (or u) coordinate to an Edwards25519 y coordinate. +func MontgomeryUToEdwardsY(u *field.Element) *field.Element { + u1 := fe().Subtract(u, one) + u2 := fe().Add(u, one) + + return u1.Multiply(u1, u2.Invert(u2)) +} diff --git a/internal/edwards25519/scalar.go b/internal/edwards25519/scalar.go new file mode 100644 index 0000000..c706849 --- /dev/null +++ b/internal/edwards25519/scalar.go @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (C) 2021 Daniel Bourdrez. All Rights Reserved. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree or at +// https://spdx.org/licenses/MIT.html + +package edwards25519 + +import ( + "fmt" + "math/big" + + ed "filippo.io/edwards25519" + + "github.com/bytemare/crypto/internal" +) + +const ( + inputLength = 64 +) + +var ( + scZero Scalar + scOne Scalar + order big.Int +) + +func init() { + scZero = Scalar{*ed.NewScalar()} + if err := scZero.Decode([]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, + }); err != nil { + panic(err) + } + + scOne = Scalar{*ed.NewScalar()} + if err := scOne.Decode([]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, + }); err != nil { + panic(err) + } + + if _, ok := order.SetString(orderPrime, 10); !ok { + panic(internal.ErrBigIntConversion) + } +} + +// Scalar implements the Scalar interface for Ristretto255 group scalars. +type Scalar struct { + scalar ed.Scalar +} + +func assert(scalar internal.Scalar) *Scalar { + sc, ok := scalar.(*Scalar) + if !ok { + panic(internal.ErrCastScalar) + } + + return &Scalar{*ed.NewScalar().Set(&sc.scalar)} +} + +func (s *Scalar) set(scalar *ed.Scalar) *Scalar { + s.scalar = *scalar + return s +} + +// Zero sets the scalar to 0, and returns it. +func (s *Scalar) Zero() internal.Scalar { + s.scalar = *ed.NewScalar() + return s +} + +// One sets the scalar to 1, and returns it. +func (s *Scalar) One() internal.Scalar { + s.set(&scOne.scalar) + return s +} + +// Random sets the current scalar to a new random scalar and returns it. +// The random source is crypto/rand, and this functions is guaranteed to return a non-zero scalar. +func (s *Scalar) Random() internal.Scalar { + for { + random := internal.RandomBytes(inputLength) + if _, err := s.scalar.SetUniformBytes(random); err != nil { + panic(err) + } + + if !s.IsZero() { + return s + } + } +} + +// Add sets the receiver to the sum of the input and the receiver, and returns the receiver. +func (s *Scalar) Add(scalar internal.Scalar) internal.Scalar { + if scalar == nil { + return s + } + + sc := assert(scalar) + s.scalar.Add(&s.scalar, &sc.scalar) + + return s +} + +// Subtract subtracts the input from the receiver, and returns the receiver. +func (s *Scalar) Subtract(scalar internal.Scalar) internal.Scalar { + if scalar == nil { + return s + } + + sc := assert(scalar) + s.scalar.Subtract(&s.scalar, &sc.scalar) + + return s +} + +func (s *Scalar) multiply(scalar *Scalar) { + s.scalar.Multiply(&s.scalar, &scalar.scalar) +} + +// Multiply multiplies the receiver with the input, and returns the receiver. +func (s *Scalar) Multiply(scalar internal.Scalar) internal.Scalar { + if scalar == nil { + return s.Zero() + } + + sc := assert(scalar) + s.multiply(sc) + + return s +} + +func getMSBit(in byte) int { + for i := 7; i >= 0; i-- { + mask := byte(1 << uint(i)) + if in&mask != 0 { + return i + } + } + + return 0 +} + +func getMSByte(in []byte) int { + msb := 0 + + for i, b := range in { + if b != 0 { + msb = i + } + } + + return msb +} + +func (s *Scalar) square() { + s.scalar.Multiply(&s.scalar, &s.scalar) +} + +// Pow sets s to s**scalar modulo the group order, and returns s. If scalar is nil, it returns 1. +func (s *Scalar) Pow(scalar internal.Scalar) internal.Scalar { + sc := assert(scalar) + exponent := sc.scalar.Bytes() + msbyte := getMSByte(exponent) + msbit := getMSBit(exponent[msbyte]) + + result := s.copy() + dummy := s.copy() + + for i := 31; i >= 0; i-- { + exp := exponent[i] + firstByte := i == msbyte + + for j := 7; j >= 0; j-- { + run := i < msbyte || (firstByte && j < msbit) + currentBitValue := exp & byte(1<= 0; i-- { + res = res || (ienc[i] > jenc[i]) + } + + if res { + return 0 + } + + return 1 +} + +// IsZero returns whether the scalar is 0. +func (s *Scalar) IsZero() bool { + return s.scalar.Equal(ed.NewScalar()) == 1 +} + +// Set sets the receiver to the value of the argument scalar, and returns the receiver. +func (s *Scalar) Set(scalar internal.Scalar) internal.Scalar { + if scalar == nil { + return s.set(nil) + } + + ec := assert(scalar) + s.scalar = ec.scalar + + return s +} + +// SetInt sets s to i modulo the field order, and returns an error if one occurs. +func (s *Scalar) SetInt(i *big.Int) error { + a := new(big.Int).Set(i) + + bytes := make([]byte, 32) + bytes = a.Mod(a, &order).FillBytes(bytes) + + for j, k := 0, len(bytes)-1; j < k; j, k = j+1, k-1 { + bytes[j], bytes[k] = bytes[k], bytes[j] + } + + return s.Decode(bytes) +} + +func (s *Scalar) copy() *Scalar { + return &Scalar{*ed.NewScalar().Set(&s.scalar)} +} + +// Copy returns a copy of the receiver. +func (s *Scalar) Copy() internal.Scalar { + return &Scalar{*ed.NewScalar().Set(&s.scalar)} +} + +// Encode returns the compressed byte encoding of the scalar. +func (s *Scalar) Encode() []byte { + return s.scalar.Bytes() +} + +func decodeScalar(scalar []byte) (*ed.Scalar, error) { + if len(scalar) == 0 { + return nil, internal.ErrParamNilScalar + } + + if len(scalar) != canonicalEncodingLength { + return nil, internal.ErrParamScalarLength + } + + s := ed.NewScalar() + if _, err := s.SetCanonicalBytes(scalar); err != nil { + return nil, fmt.Errorf("ristretto scalar Decode: %w", err) + } + + return s, nil +} + +// Decode sets the receiver to a decoding of the input data, and returns an error on failure. +func (s *Scalar) Decode(in []byte) error { + sc, err := decodeScalar(in) + if err != nil { + return err + } + + s.scalar = *sc + + return nil +} + +// MarshalBinary returns the compressed byte encoding of the scalar. +func (s *Scalar) MarshalBinary() (data []byte, err error) { + return s.Encode(), nil +} + +// UnmarshalBinary sets e to the decoding of the byte encoded scalar. +func (s *Scalar) UnmarshalBinary(data []byte) error { + if err := s.Decode(data); err != nil { + return fmt.Errorf("edwards25519: %w", err) + } + + return nil +} diff --git a/internal/ristretto/element.go b/internal/ristretto/element.go index f35a576..88f56d5 100644 --- a/internal/ristretto/element.go +++ b/internal/ristretto/element.go @@ -6,7 +6,7 @@ // LICENSE file in the root directory of this source tree or at // https://spdx.org/licenses/MIT.html -// Package ristretto allows simple and abstracted operations in the Ristretto255 group +// Package ristretto allows simple and abstracted operations in the Ristretto255 group. package ristretto import ( @@ -139,6 +139,19 @@ func (e *Element) XCoordinate() []byte { return e.Encode() } +func decodeElement(element []byte) (*ristretto255.Element, error) { + if len(element) == 0 { + return nil, internal.ErrParamNilPoint + } + + e := ristretto255.NewElement() + if err := e.Decode(element); err != nil { + return nil, fmt.Errorf("ristretto element Decode: %w", err) + } + + return e, nil +} + // Decode sets the receiver to a decoding of the input data, and returns an error on failure. func (e *Element) Decode(data []byte) error { element, err := decodeElement(data) @@ -156,19 +169,6 @@ func (e *Element) Decode(data []byte) error { return nil } -func decodeElement(element []byte) (*ristretto255.Element, error) { - if len(element) == 0 { - return nil, internal.ErrParamNilPoint - } - - e := ristretto255.NewElement() - if err := e.Decode(element); err != nil { - return nil, fmt.Errorf("ristretto element Decode: %w", err) - } - - return e, nil -} - // MarshalBinary returns the compressed byte encoding of the element. func (e *Element) MarshalBinary() ([]byte, error) { return e.Encode(), nil diff --git a/internal/ristretto/ristretto.go b/internal/ristretto/ristretto.go index b9abeaa..7414b2f 100644 --- a/internal/ristretto/ristretto.go +++ b/internal/ristretto/ristretto.go @@ -6,7 +6,7 @@ // LICENSE file in the root directory of this source tree or at // https://spdx.org/licenses/MIT.html -// Package ristretto wraps github.com/gtank/ristretto255 and exposes a simple prime-order group API with hash-to-curve. +// Package ristretto allows simple and abstracted operations in the Ristretto255 group. package ristretto import ( diff --git a/internal/ristretto/scalar.go b/internal/ristretto/scalar.go index 30c2d17..f4a1294 100644 --- a/internal/ristretto/scalar.go +++ b/internal/ristretto/scalar.go @@ -6,7 +6,7 @@ // LICENSE file in the root directory of this source tree or at // https://spdx.org/licenses/MIT.html -// Package ristretto allows simple and abstracted operations in the Ristretto255 group +// Package ristretto allows simple and abstracted operations in the Ristretto255 group. package ristretto import ( @@ -20,7 +20,11 @@ import ( const canonicalEncodingLength = 32 -var scOne Scalar +var ( + scZero = &Scalar{*ristretto255.NewScalar()} + scOne Scalar + order big.Int +) func init() { scOne = Scalar{*ristretto255.NewScalar()} @@ -30,6 +34,10 @@ func init() { }); err != nil { panic(err) } + + if _, ok := order.SetString(orderPrime, 10); !ok { + panic(internal.ErrBigIntConversion) + } } // Scalar implements the Scalar interface for Ristretto255 group scalars. @@ -46,6 +54,10 @@ func assert(scalar internal.Scalar) *Scalar { return sc } +func (s *Scalar) set(scalar *ristretto255.Scalar) { + s.scalar = *ristretto255.NewScalar().Add(&scZero.scalar, scalar) +} + // Zero sets the scalar to 0, and returns it. func (s *Scalar) Zero() internal.Scalar { s.scalar.Zero() @@ -54,7 +66,7 @@ func (s *Scalar) Zero() internal.Scalar { // One sets the scalar to 1, and returns it. func (s *Scalar) One() internal.Scalar { - s.set(&scOne) + s.set(&scOne.scalar) return s } @@ -95,6 +107,10 @@ func (s *Scalar) Subtract(scalar internal.Scalar) internal.Scalar { return s } +func (s *Scalar) multiply(scalar *Scalar) { + s.scalar.Multiply(&s.scalar, &scalar.scalar) +} + // Multiply multiplies the receiver with the input, and returns the receiver. func (s *Scalar) Multiply(scalar internal.Scalar) internal.Scalar { if scalar == nil { @@ -102,27 +118,78 @@ func (s *Scalar) Multiply(scalar internal.Scalar) internal.Scalar { } sc := assert(scalar) - s.scalar.Multiply(&s.scalar, &sc.scalar) + s.multiply(sc) return s } -// Pow sets s to s**scalar modulo the group order, and returns s. If scalar is nil, it returns 1. -func (s *Scalar) Pow(scalar internal.Scalar) internal.Scalar { - if scalar == nil || scalar.IsZero() { - return s.One() +func getMSBit(in byte) int { + for i := 7; i >= 0; i-- { + mask := byte(1 << uint(i)) + if in&mask != 0 { + return i + } } - if scalar.Equal(scalar.Copy().One()) == 1 { - return s + return 0 +} + +func getMSByte(in []byte) int { + msb := 0 + + for i, b := range in { + if b != 0 { + msb = i + } } + return msb +} + +func (s *Scalar) square() { + s.scalar.Multiply(&s.scalar, &s.scalar) +} + +// Pow sets s to s**scalar modulo the group order, and returns s. If scalar is nil, it returns 1. +func (s *Scalar) Pow(scalar internal.Scalar) internal.Scalar { sc := assert(scalar) - sc.Subtract(&scOne) + exponent := sc.scalar.Encode(nil) + msbyte := getMSByte(exponent) + msbit := getMSBit(exponent[msbyte]) + + result := s.copy() + dummy := s.copy() + + for i := 31; i >= 0; i-- { + exp := exponent[i] + firstByte := i == msbyte + + for j := 7; j >= 0; j-- { + run := i < msbyte || (firstByte && j < msbit) + currentBitValue := exp & byte(1< i; i++ { + aBytes[i], aBytes[j] = aBytes[j], aBytes[i] + j-- + } + + u := &field.Element{} + if _, err := u.SetBytes(aBytes); err != nil { + t.Fatal(err) + } + + return u +} + +func vectorToEdwards25519(t *testing.T, x, y string) *edwards25519.Point { + u, v := affineToEdwards(t, x), affineToEdwards(t, y) + return edwards255192.AffineToEdwards(u, v) +} + func (v *vector) run(t *testing.T) { var expected string - if v.group == crypto.P256Sha256 || v.group == crypto.P384Sha384 || v.group == crypto.P521Sha512 { + + switch v.group { + case crypto.P256Sha256, crypto.P384Sha384, crypto.P521Sha512: e := ecFromGroup(v.group) x, y := vectorToNistBig(v.P.X, v.P.Y) expected = hex.EncodeToString(elliptic.MarshalCompressed(e, x, y)) + case crypto.Edwards25519Sha512: + p := vectorToEdwards25519(t, v.P.X, v.P.Y) + expected = hex.EncodeToString(p.Bytes()) } switch v.Ciphersuite[len(v.Ciphersuite)-3:] { diff --git a/tests/scalar_test.go b/tests/scalar_test.go index af6ac24..b3872c7 100644 --- a/tests/scalar_test.go +++ b/tests/scalar_test.go @@ -39,7 +39,8 @@ func TestScalar_WrongInput(t *testing.T) { var wrongGroup crypto.Group switch group.id { - case crypto.Ristretto255Sha512: + // The following is arbitrary, and simply aims at confusing identifiers + case crypto.Ristretto255Sha512, crypto.Edwards25519Sha512: wrongGroup = crypto.P256Sha256 case crypto.P256Sha256, crypto.P384Sha384, crypto.P521Sha512: wrongGroup = crypto.Ristretto255Sha512 @@ -288,19 +289,136 @@ func scalarTestPow(t *testing.T, g crypto.Group) { // s**1 = s s = g.NewScalar().Random() - one := g.NewScalar().One() - if s.Copy().Pow(one).Equal(s) != 1 { + exp := g.NewScalar().One() + if s.Copy().Pow(exp).Equal(s) != 1 { t.Fatal("expected s**1 = s") } // s**2 = s*s - s = g.NewScalar().Random() + s = g.NewScalar().One() + s.Add(s.Copy().One()) s2 := s.Copy().Multiply(s) - two := g.NewScalar().One().Add(g.NewScalar().One()) + if err := exp.SetInt(big.NewInt(2)); err != nil { + t.Fatal(err) + } - if s.Pow(two).Equal(s2) != 1 { + if s.Pow(exp).Equal(s2) != 1 { t.Fatal("expected s**2 = s*s") } + + // s**3 = s*s*s + s = g.NewScalar().Random() + s3 := s.Copy().Multiply(s) + s3.Multiply(s) + _ = exp.SetInt(big.NewInt(3)) + + if s.Pow(exp).Equal(s3) != 1 { + t.Fatal("expected s**3 = s*s*s") + } + + // 5**7 = 78125 = 00000000 00000001 00110001 00101101 = 1 49 45 + iBase := big.NewInt(5) + iExp := big.NewInt(7) + order, ok := new(big.Int).SetString(g.Order(), 0) + if !ok { + t.Fatal(ok) + } + iResult := new(big.Int).Exp(iBase, iExp, order) + result := g.NewScalar() + if err := result.SetInt(iResult); err != nil { + t.Fatal(err) + } + + if err := s.SetInt(iBase); err != nil { + t.Fatal(err) + } + if err := exp.SetInt(iExp); err != nil { + t.Fatal(err) + } + res := s.Pow(exp) + if res.Equal(result) != 1 { + t.Fatal("expected 5**7 = 78125") + } + + // 3**255 = 11F1B08E87EC42C5D83C3218FC83C41DCFD9F4428F4F92AF1AAA80AA46162B1F71E981273601F4AD1DD4709B5ACA650265A6AB + iBase = big.NewInt(3) + iExp = big.NewInt(255) + order, ok = new(big.Int).SetString(g.Order(), 0) + if !ok { + t.Fatal(ok) + } + iResult = new(big.Int).Exp(iBase, iExp, order) + result = g.NewScalar() + if err := result.SetInt(iResult); err != nil { + t.Fatal(err) + } + + if err := s.SetInt(iBase); err != nil { + t.Fatal(err) + } + if err := exp.SetInt(iExp); err != nil { + t.Fatal(err) + } + res = s.Pow(exp) + if res.Equal(result) != 1 { + t.Fatal("expected 3**255 = 11F1B08E87EC42C5D83C3218FC83C41DCFD9F4428F4F92AF1AAA80AA46162B1F71E981273601F4AD1DD4709B5ACA650265A6AB") + } + + // 7945232487465**513 + iBase.SetInt64(7945232487465) + iExp.SetInt64(513) + iResult = iResult.Exp(iBase, iExp, order) + if err := result.SetInt(iResult); err != nil { + t.Fatal(err) + } + + if err := s.SetInt(iBase); err != nil { + t.Fatal(err) + } + + if err := exp.SetInt(iExp); err != nil { + t.Fatal(err) + } + + res = s.Pow(exp) + if res.Equal(result) != 1 { + t.Fatal("expect equality on 7945232487465**513") + } + + // random**random + s.Random() + exp.Random() + + switch g { + case crypto.Ristretto255Sha512, crypto.Edwards25519Sha512: + e := s.Encode() + for i, j := 0, len(e)-1; i < j; i++ { + e[i], e[j] = e[j], e[i] + j-- + } + iBase.SetBytes(e) + + e = exp.Encode() + for i, j := 0, len(e)-1; i < j; i++ { + e[i], e[j] = e[j], e[i] + j-- + } + iExp.SetBytes(e) + + default: + iBase.SetBytes(s.Encode()) + iExp.SetBytes(exp.Encode()) + } + + iResult.Exp(iBase, iExp, order) + + if err := result.SetInt(iResult); err != nil { + t.Fatal(err) + } + + if s.Pow(exp).Equal(result) != 1 { + t.Fatal("expected equality on random numbers") + } } func scalarTestInvert(t *testing.T, g crypto.Group) { diff --git a/tests/utils_test.go b/tests/utils_test.go index d471ceb..dfa13ca 100644 --- a/tests/utils_test.go +++ b/tests/utils_test.go @@ -24,6 +24,7 @@ type testGroup struct { h2c string e2c string basePoint string + identity string elementLength int scalarLength int id crypto.Group @@ -31,10 +32,56 @@ type testGroup struct { func testGroups() []*testGroup { return []*testGroup{ - {"Ristretto255", "ristretto255_XMD:SHA-512_R255MAP_RO_", "ristretto255_XMD:SHA-512_R255MAP_RO_", "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", 32, 32, 1}, - {"P256", "P256_XMD:SHA-256_SSWU_RO_", "P256_XMD:SHA-256_SSWU_NU_", "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 33, 32, 3}, - {"P384", "P384_XMD:SHA-384_SSWU_RO_", "P384_XMD:SHA-384_SSWU_NU_", "03aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", 49, 48, 4}, - {"P521", "P521_XMD:SHA-512_SSWU_RO_", "P521_XMD:SHA-512_SSWU_NU_", "0200c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 67, 66, 5}, + { + "Ristretto255", + "ristretto255_XMD:SHA-512_R255MAP_RO_", + "ristretto255_XMD:SHA-512_R255MAP_RO_", + "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", + "0000000000000000000000000000000000000000000000000000000000000000", + 32, + 32, + 1, + }, + { + "P256", + "P256_XMD:SHA-256_SSWU_RO_", + "P256_XMD:SHA-256_SSWU_NU_", + "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "00", + 33, + 32, + 3, + }, + { + "P384", + "P384_XMD:SHA-384_SSWU_RO_", + "P384_XMD:SHA-384_SSWU_NU_", + "03aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", + "00", + 49, + 48, + 4, + }, + { + "P521", + "P521_XMD:SHA-512_SSWU_RO_", + "P521_XMD:SHA-512_SSWU_NU_", + "0200c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", + "00", + 67, + 66, + 5, + }, + { + "Edwards25519", + "edwards25519_XMD:SHA-512_ELL2_RO_", + "edwards25519_XMD:SHA-512_ELL2_NU_", + "5866666666666666666666666666666666666666666666666666666666666666", + "0100000000000000000000000000000000000000000000000000000000000000", + 32, + 32, + 6, + }, } } diff --git a/tests/vectors/edwards25519_XMD-SHA-512_ELL2_NU_.json b/tests/vectors/edwards25519_XMD-SHA-512_ELL2_NU_.json new file mode 100644 index 0000000..b06a7cc --- /dev/null +++ b/tests/vectors/edwards25519_XMD-SHA-512_ELL2_NU_.json @@ -0,0 +1,90 @@ +{ + "L": "0x30", + "Z": "0x2", + "ciphersuite": "edwards25519_XMD:SHA-512_ELL2_NU_", + "curve": "edwards25519", + "dst": "QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_NU_", + "expand": "XMD", + "field": { + "m": "0x1", + "p": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" + }, + "hash": "sha512", + "k": "0x80", + "map": { + "name": "ELL2" + }, + "randomOracle": false, + "vectors": [ + { + "P": { + "x": "0x1ff2b70ecf862799e11b7ae744e3489aa058ce805dd323a936375a84695e76da", + "y": "0x222e314d04a4d5725e9f2aff9fb2a6b69ef375a1214eb19021ceab2d687f0f9b" + }, + "Q": { + "x": "0x42836f691d05211ebc65ef8fcf01e0fb6328ec9c4737c26050471e50803022eb", + "y": "0x22cb4aaa555e23bd460262d2130d6a3c9207aa8bbb85060928beb263d6d42a95" + }, + "msg": "", + "u": [ + "0x7f3e7fb9428103ad7f52db32f9df32505d7b427d894c5093f7a0f0374a30641d" + ] + }, + { + "P": { + "x": "0x5f13cc69c891d86927eb37bd4afc6672360007c63f68a33ab423a3aa040fd2a8", + "y": "0x67732d50f9a26f73111dd1ed5dba225614e538599db58ba30aaea1f5c827fa42" + }, + "Q": { + "x": "0x333e41b61c6dd43af220c1ac34a3663e1cf537f996bab50ab66e33c4bd8e4e19", + "y": "0x51b6f178eb08c4a782c820e306b82c6e273ab22e258d972cd0c511787b2a3443" + }, + "msg": "abc", + "u": [ + "0x09cfa30ad79bd59456594a0f5d3a76f6b71c6787b04de98be5cd201a556e253b" + ] + }, + { + "P": { + "x": "0x1dd2fefce934ecfd7aae6ec998de088d7dd03316aa1847198aecf699ba6613f1", + "y": "0x2f8a6c24dd1adde73909cada6a4a137577b0f179d336685c4a955a0a8e1a86fb" + }, + "Q": { + "x": "0x55186c242c78e7d0ec5b6c9553f04c6aeef64e69ec2e824472394da32647cfc6", + "y": "0x5b9ea3c265ee42256a8f724f616307ef38496ef7eba391c08f99f3bea6fa88f0" + }, + "msg": "abcdef0123456789", + "u": [ + "0x475ccff99225ef90d78cc9338e9f6a6bb7b17607c0c4428937de75d33edba941" + ] + }, + { + "P": { + "x": "0x35fbdc5143e8a97afd3096f2b843e07df72e15bfca2eaf6879bf97c5d3362f73", + "y": "0x2af6ff6ef5ebba128b0774f4296cb4c2279a074658b083b8dcca91f57a603450" + }, + "Q": { + "x": "0x024b6e1621606dca8071aa97b43dce4040ca78284f2a527dcf5d0fbfac2b07e7", + "y": "0x5102353883d739bdc9f8a3af650342b171217167dcce34f8db57208ec1dfdbf2" + }, + "msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "u": [ + "0x049a1c8bd51bcb2aec339f387d1ff51428b88d0763a91bcdf6929814ac95d03d" + ] + }, + { + "P": { + "x": "0x6e5e1f37e99345887fc12111575fc1c3e36df4b289b8759d23af14d774b66bff", + "y": "0x2c90c3d39eb18ff291d33441b35f3262cdd307162cc97c31bfcc7a4245891a37" + }, + "Q": { + "x": "0x3e6368cff6e88a58e250c54bd27d2c989ae9b3acb6067f2651ad282ab8c21cd9", + "y": "0x38fb39f1566ca118ae6c7af42810c0bb9767ae5960abb5a8ca792530bfb9447d" + }, + "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "u": [ + "0x3cb0178a8137cefa5b79a3a57c858d7eeeaa787b2781be4a362a2f0750d24fa0" + ] + } + ] +} diff --git a/tests/vectors/edwards25519_XMD-SHA-512_ELL2_RO_.json b/tests/vectors/edwards25519_XMD-SHA-512_ELL2_RO_.json new file mode 100644 index 0000000..1d1698c --- /dev/null +++ b/tests/vectors/edwards25519_XMD-SHA-512_ELL2_RO_.json @@ -0,0 +1,115 @@ +{ + "L": "0x30", + "Z": "0x2", + "ciphersuite": "edwards25519_XMD:SHA-512_ELL2_RO_", + "curve": "edwards25519", + "dst": "QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_", + "expand": "XMD", + "field": { + "m": "0x1", + "p": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" + }, + "hash": "sha512", + "k": "0x80", + "map": { + "name": "ELL2" + }, + "randomOracle": true, + "vectors": [ + { + "P": { + "x": "0x3c3da6925a3c3c268448dcabb47ccde5439559d9599646a8260e47b1e4822fc6", + "y": "0x09a6c8561a0b22bef63124c588ce4c62ea83a3c899763af26d795302e115dc21" + }, + "Q0": { + "x": "0x6549118f65bb617b9e8b438decedc73c496eaed496806d3b2eb9ee60b88e09a7", + "y": "0x7315bcc8cf47ed68048d22bad602c6680b3382a08c7c5d3f439a973fb4cf9feb" + }, + "Q1": { + "x": "0x31dcfc5c58aa1bee6e760bf78cbe71c2bead8cebb2e397ece0f37a3da19c9ed2", + "y": "0x7876d81474828d8a5928b50c82420b2bd0898d819e9550c5c82c39fc9bafa196" + }, + "msg": "", + "u": [ + "0x03fef4813c8cb5f98c6eef88fae174e6e7d5380de2b007799ac7ee712d203f3a", + "0x780bdddd137290c8f589dc687795aafae35f6b674668d92bf92ae793e6a60c75" + ] + }, + { + "P": { + "x": "0x608040b42285cc0d72cbb3985c6b04c935370c7361f4b7fbdb1ae7f8c1a8ecad", + "y": "0x1a8395b88338f22e435bbd301183e7f20a5f9de643f11882fb237f88268a5531" + }, + "Q0": { + "x": "0x5c1525bd5d4b4e034512949d187c39d48e8cd84242aa4758956e4adc7d445573", + "y": "0x2bf426cf7122d1a90abc7f2d108befc2ef415ce8c2d09695a7407240faa01f29" + }, + "Q1": { + "x": "0x37b03bba828860c6b459ddad476c83e0f9285787a269df2156219b7e5c86210c", + "y": "0x285ebf5412f84d0ad7bb4e136729a9ffd2195d5b8e73c0dc85110ce06958f432" + }, + "msg": "abc", + "u": [ + "0x5081955c4141e4e7d02ec0e36becffaa1934df4d7a270f70679c78f9bd57c227", + "0x005bdc17a9b378b6272573a31b04361f21c371b256252ae5463119aa0b925b76" + ] + }, + { + "P": { + "x": "0x6d7fabf47a2dc03fe7d47f7dddd21082c5fb8f86743cd020f3fb147d57161472", + "y": "0x53060a3d140e7fbcda641ed3cf42c88a75411e648a1add71217f70ea8ec561a6" + }, + "Q0": { + "x": "0x3ac463dd7fddb773b069c5b2b01c0f6b340638f54ee3bd92d452fcec3015b52d", + "y": "0x7b03ba1e8db9ec0b390d5c90168a6a0b7107156c994c674b61fe696cbeb46baf" + }, + "Q1": { + "x": "0x0757e7e904f5e86d2d2f4acf7e01c63827fde2d363985aa7432106f1b3a444ec", + "y": "0x50026c96930a24961e9d86aa91ea1465398ff8e42015e2ec1fa397d416f6a1c0" + }, + "msg": "abcdef0123456789", + "u": [ + "0x285ebaa3be701b79871bcb6e225ecc9b0b32dff2d60424b4c50642636a78d5b3", + "0x2e253e6a0ef658fedb8e4bd6a62d1544fd6547922acb3598ec6b369760b81b31" + ] + }, + { + "P": { + "x": "0x5fb0b92acedd16f3bcb0ef83f5c7b7a9466b5f1e0d8d217421878ea3686f8524", + "y": "0x2eca15e355fcfa39d2982f67ddb0eea138e2994f5956ed37b7f72eea5e89d2f7" + }, + "Q0": { + "x": "0x703e69787ea7524541933edf41f94010a201cc841c1cce60205ec38513458872", + "y": "0x32bb192c4f89106466f0874f5fd56a0d6b6f101cb714777983336c159a9bec75" + }, + "Q1": { + "x": "0x0c9077c5c31720ed9413abe59bf49ce768506128d810cb882435aa90f713ef6b", + "y": "0x7d5aec5210db638c53f050597964b74d6dda4be5b54fa73041bf909ccb3826cb" + }, + "msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "u": [ + "0x4fedd25431c41f2a606952e2945ef5e3ac905a42cf64b8b4d4a83c533bf321af", + "0x02f20716a5801b843987097a8276b6d869295b2e11253751ca72c109d37485a9" + ] + }, + { + "P": { + "x": "0x0efcfde5898a839b00997fbe40d2ebe950bc81181afbd5cd6b9618aa336c1e8c", + "y": "0x6dc2fc04f266c5c27f236a80b14f92ccd051ef1ff027f26a07f8c0f327d8f995" + }, + "Q0": { + "x": "0x21091b2e3f9258c7dfa075e7ae513325a94a3d8a28e1b1cb3b5b6f5d65675592", + "y": "0x41a33d324c89f570e0682cdf7bdb78852295daf8084c669f2cc9692896ab5026" + }, + "Q1": { + "x": "0x4c07ec48c373e39a23bd7954f9e9b66eeab9e5ee1279b867b3d5315aa815454f", + "y": "0x67ccac7c3cb8d1381242d8d6585c57eabaddbb5dca5243a68a8aeb5477d94b3a" + }, + "msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "u": [ + "0x6e34e04a5106e9bd59f64aba49601bf09d23b27f7b594e56d5de06df4a4ea33b", + "0x1c1c2cb59fc053f44b86c5d5eb8c1954b64976d0302d3729ff66e84068f5fd96" + ] + } + ] +}