diff --git a/.gitignore b/.gitignore index 48a3c84bb..68adb8b25 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *~ moc_* ext/ +.idea/ dissent docs/html test.log diff --git a/docs/README.md b/docs/README.md index 7456ea44e..10a7e6f08 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,2 +1 @@ - To manually browse the doc start from [sidebar.md](sidebar.md). Otherwise visit [https://dedis.github.io/kyber/](https://dedis.github.io/kyber/) diff --git a/docs/introduction.md b/docs/introduction.md index 9dfbcaa44..c9af18039 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -4,5 +4,5 @@ The kyber go library posses several functions for cryptographic operations on different cureves like ed25519 and the functions can be used to perform various -operations on points, scalars and groups in linear or varaible time and can be +operations on points, scalars and groups in linear or variable time and can be used to implement the same. \ No newline at end of file diff --git a/docs/point.md b/docs/point.md index 26328f094..6bbabfe49 100644 --- a/docs/point.md +++ b/docs/point.md @@ -58,7 +58,7 @@ group. It returns a `true/false` indicating wether the Points are equal or not. | Output | - `Point` : Caller Point with value equal to the neutral identity element | This function sets the receiver object to a neutral identity element, depending -upon the suite taken into consideration. +upon the suite taken into consideration. ### Base() diff --git a/docs/sidebar.md b/docs/sidebar.md index b8339053a..3090efddb 100644 --- a/docs/sidebar.md +++ b/docs/sidebar.md @@ -6,4 +6,4 @@ - [Point](point.md) - [Scalar](scalar.md) - [Marshaling](marshalling.md) -- [XOF](xof.md) \ No newline at end of file +- [XOF](xof.md) diff --git a/go.mod b/go.mod index 7ab3cdeba..139a61a3d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,7 @@ module go.dedis.ch/kyber/v3 require ( + filippo.io/edwards25519 v1.0.0 github.com/stretchr/testify v1.3.0 go.dedis.ch/fixbuf v1.0.3 go.dedis.ch/protobuf v1.0.11 diff --git a/go.sum b/go.sum index c9ea88086..5e1a37228 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= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/group/filippo_ed25519/const.go b/group/filippo_ed25519/const.go new file mode 100644 index 000000000..7d63cef59 --- /dev/null +++ b/group/filippo_ed25519/const.go @@ -0,0 +1,16 @@ +package filippo_ed25519 + +import ( + filippo_ed25519 "filippo.io/edwards25519" + "math/big" +) + +var primeOrder, _ = new(big.Int).SetString("7237005577332262213973186563042994240857116359379907606001950938285454250989", 10) +var cofactor = new(big.Int).SetInt64(8) +var fullOrder = new(big.Int).Mul(primeOrder, cofactor) + +var filippoPrimeOrderScalar = setBigInt(primeOrder) +var filippoCofactorScalar = setBigInt(cofactor) +var filippoNullPoint = Point{filippo_ed25519.NewIdentityPoint()} + +var marshalPointID = [16]byte{'f', 'i', 'l', 'i', 'p', 'p', 'o', '_', 'e', 'd', '.', 'p', 'o', 'i', 'n', 't'} diff --git a/group/filippo_ed25519/curve.go b/group/filippo_ed25519/curve.go new file mode 100644 index 000000000..058c5aaf8 --- /dev/null +++ b/group/filippo_ed25519/curve.go @@ -0,0 +1,80 @@ +package filippo_ed25519 + +import ( + "crypto/cipher" + "crypto/sha512" + filippo_ed25519 "filippo.io/edwards25519" + "go.dedis.ch/kyber/v3" + "go.dedis.ch/kyber/v3/util/random" +) + +// Curve represents the Ed25519 group. +// There are no parameters and no initialization is required +// because it supports only this one specific curve. +type Curve struct { +} + +// Return the name of the curve, "Ed25519". +func (c *Curve) String() string { + return "Ed25519" +} + +// ScalarLen returns 32, the size in bytes of an encoded Scalar +// for the Ed25519 curve. +func (c *Curve) ScalarLen() int { + return 32 +} + +// Scalar creates a new Scalar for the prime-order subgroup of the Ed25519 curve. +// The scalars in this package implement kyber.Scalar's SetBytes +// method, interpreting the bytes as a little-endian integer, in order to remain +// compatible with other Ed25519 implementations, and with the standard implementation +// of the EdDSA signature. +func (c *Curve) Scalar() kyber.Scalar { + return &Scalar{new(filippo_ed25519.Scalar)} +} + +// PointLen returns 32, the size in bytes of an encoded Point on the Ed25519 curve. +func (c *Curve) PointLen() int { + return 32 +} + +// Point creates a new Point on the Ed25519 curve. +func (c *Curve) Point() kyber.Point { + P := Point{new(filippo_ed25519.Point)} + return &P +} + +// NewKeyAndSeedWithInput returns a formatted Ed25519 key (avoid subgroup attack by +// requiring it to be a multiple of 8). It also returns the input and the digest used +// to generate the key. +func (c *Curve) NewKeyAndSeedWithInput(buffer []byte) (kyber.Scalar, []byte, []byte) { + digest := sha512.Sum512(buffer[:]) + digest[0] &= 0xf8 + digest[31] &= 0xf + + // In here the 31st byte has been done AND 16 just to bring the secret key chosen below the group order i.e. l + // in which the highest priority byte is 16. Doing the normal procedure of unsetting the highest priority bit and + // setting the highest priority bit leads to errors when the secret is passed to the filippo library which expects + // all the scalars to be below l. + + secret := c.Scalar().(*Scalar) + secret.SetBytes(digest[:32]) + return secret, buffer, digest[32:] +} + +// NewKeyAndSeed returns a formatted Ed25519 key (avoid subgroup attack by requiring +// it to be a multiple of 8). It also returns the seed and the input used to generate +// the key. +func (c *Curve) NewKeyAndSeed(stream cipher.Stream) (kyber.Scalar, []byte, []byte) { + var buffer [32]byte + random.Bytes(buffer[:], stream) + return c.NewKeyAndSeedWithInput(buffer[:]) +} + +// NewKey returns a formatted Ed25519 key (avoiding subgroup attack by requiring +// it to be a multiple of 8). NewKey implements the kyber/util/key.Generator interface. +func (c *Curve) NewKey(stream cipher.Stream) kyber.Scalar { + secret, _, _ := c.NewKeyAndSeed(stream) + return secret +} diff --git a/group/filippo_ed25519/curve_test.go b/group/filippo_ed25519/curve_test.go new file mode 100644 index 000000000..e54e1dafd --- /dev/null +++ b/group/filippo_ed25519/curve_test.go @@ -0,0 +1,45 @@ +package filippo_ed25519 + +import ( + "github.com/stretchr/testify/assert" + "go.dedis.ch/kyber/v3/util/test" + "math" + "testing" +) + +var tSuite = NewBlakeSHA256FilippoEd25519() +var groupBench = test.NewGroupBench(tSuite) + +func TestSuite(t *testing.T) { test.SuiteTest(t, tSuite) } + +func TestCurve_NewKey(t *testing.T) { + group := Curve{} + stream := tSuite.RandomStream() + + for i := 0.0; i < math.Pow(10, 6); i++ { + s := group.NewKey(stream).(*Scalar) + + // little-endian check of a multiple of 8 + b, _ := s.MarshalBinary() + assert.Equal(t, uint8(0), b[0]&7) + } +} + +func BenchmarkScalarAdd(b *testing.B) { groupBench.ScalarAdd(b.N) } +func BenchmarkScalarSub(b *testing.B) { groupBench.ScalarSub(b.N) } +func BenchmarkScalarNeg(b *testing.B) { groupBench.ScalarNeg(b.N) } +func BenchmarkScalarMul(b *testing.B) { groupBench.ScalarMul(b.N) } +func BenchmarkScalarDiv(b *testing.B) { groupBench.ScalarDiv(b.N) } +func BenchmarkScalarInv(b *testing.B) { groupBench.ScalarInv(b.N) } +func BenchmarkScalarPick(b *testing.B) { groupBench.ScalarPick(b.N) } +func BenchmarkScalarEncode(b *testing.B) { groupBench.ScalarEncode(b.N) } +func BenchmarkScalarDecode(b *testing.B) { groupBench.ScalarDecode(b.N) } + +func BenchmarkPointAdd(b *testing.B) { groupBench.PointAdd(b.N) } +func BenchmarkPointSub(b *testing.B) { groupBench.PointSub(b.N) } +func BenchmarkPointNeg(b *testing.B) { groupBench.PointNeg(b.N) } +func BenchmarkPointMul(b *testing.B) { groupBench.PointMul(b.N) } +func BenchmarkPointBaseMul(b *testing.B) { groupBench.PointBaseMul(b.N) } +func BenchmarkPointPick(b *testing.B) { groupBench.PointPick(b.N) } +func BenchmarkPointEncode(b *testing.B) { groupBench.PointEncode(b.N) } +func BenchmarkPointDecode(b *testing.B) { groupBench.PointDecode(b.N) } diff --git a/group/filippo_ed25519/point.go b/group/filippo_ed25519/point.go new file mode 100644 index 000000000..7e288b9a7 --- /dev/null +++ b/group/filippo_ed25519/point.go @@ -0,0 +1,198 @@ +package filippo_ed25519 + +import ( + "crypto/cipher" + "encoding/hex" + "errors" + filippo_ed25519 "filippo.io/edwards25519" + "go.dedis.ch/kyber/v3" + "go.dedis.ch/kyber/v3/group/internal/marshalling" + "io" +) + +type Point struct { + point *filippo_ed25519.Point +} + +func (p *Point) Equal(s2 kyber.Point) bool { + return p.point.Equal(s2.(*Point).point) == 1 +} + +func (p *Point) Null() kyber.Point { + p.point = filippo_ed25519.NewIdentityPoint() + return p +} + +func (p *Point) Base() kyber.Point { + p.point = filippo_ed25519.NewGeneratorPoint() + return p +} + +func (p *Point) Pick(rand cipher.Stream) kyber.Point { + return p.Embed(nil, rand) +} + +func (p *Point) Set(a kyber.Point) kyber.Point { + if p.point == nil { + p.point = new(filippo_ed25519.Point) + } + p.point.Set(a.(*Point).point) + return p +} + +func (p *Point) Clone() kyber.Point { + p2 := new(Point) + p2.point = new(filippo_ed25519.Point) + p2.point.Set(p.point) + return p2 +} + +func (p *Point) Add(a, b kyber.Point) kyber.Point { + if p.point == nil { + p.point = new(filippo_ed25519.Point) + } + p.point.Add(a.(*Point).point, b.(*Point).point) + return p +} + +func (p *Point) Sub(a, b kyber.Point) kyber.Point { + if p.point == nil { + p.point = new(filippo_ed25519.Point) + } + p.point.Subtract(a.(*Point).point, b.(*Point).point) + return p +} + +func (p *Point) Neg(a kyber.Point) kyber.Point { + if p.point == nil { + p.point = new(filippo_ed25519.Point) + } + p.point.Negate(a.(*Point).point) + return p +} + +func (p *Point) Mul(a kyber.Scalar, b kyber.Point) kyber.Point { + if p == nil || p.point == nil { + p.point = new(filippo_ed25519.Point) + } + if b == nil || b.(*Point).point == nil { + p.point = p.point.ScalarBaseMult(a.(*Scalar).scalar) + } else { + p.point.ScalarMult(a.(*Scalar).scalar, b.(*Point).point) + } + return p +} + +func (p *Point) EmbedLen() int { + // Reserve the most-significant 8 bits for pseudo-randomness. + // Reserve the least-significant 8 bits for embedded data length. + // (Hopefully it's unlikely we'll need >=2048-bit curves soon.) + return (255 - 8 - 8) / 8 +} + +func (p *Point) Embed(data []byte, rand cipher.Stream) kyber.Point { + + // How many bytes to embed? + dl := p.EmbedLen() + if dl > len(data) { + dl = len(data) + } + + p.point = new(filippo_ed25519.Point) + + // Github issue raised for the problem of the infinite loop + + for { + // Pick a random point, with optional embedded data + var b [32]byte + rand.XORKeyStream(b[:], b[:]) + if data != nil { + b[0] = byte(dl) // Encode length in low 8 bits + copy(b[1:1+dl], data) // Copy in data to embed + } + + _, err := p.point.SetBytes(b[:]) + if err != nil { + continue + } + + // If we're using the full group, + // we just need any point on the curve, so we're done. + // if c.full { + // return P,data[dl:] + // } + + // We're using the prime-order subgroup, + // so we need to make sure the point is in that subencoding. + // If we're not trying to embed data, + // we can convert our point into one in the subgroup + // simply by multiplying it by the cofactor. + + if data == nil { + p.Mul(filippoCofactorScalar, p) // multiply by cofactor + if p.Equal(&filippoNullPoint) { + continue // unlucky; try again + } + return p // success + } + + // Since we need the point's y-coordinate to hold our data, + // we must simply check if the point is in the subgroup + // and retry point generation until it is. + var Q Point + Q.Mul(filippoPrimeOrderScalar, p) + if Q.Equal(&filippoNullPoint) { + return p // success + } + // Keep trying... + } +} + +func (p *Point) Data() ([]byte, error) { + if p.point == nil { + return nil, errors.New("point not initialized") + } + + b := p.point.Bytes() + dl := int(b[0]) + if dl > p.EmbedLen() { + return nil, errors.New("invalid embedded data length") + } + return b[1 : 1+dl], nil +} + +func (p *Point) MarshalSize() int { + return 32 +} + +func (p *Point) String() string { + b := p.point.Bytes() + return hex.EncodeToString(b) +} + +func (p *Point) MarshalBinary() ([]byte, error) { + if p.point == nil { + return nil, errors.New("point not initialized") + } + return p.point.Bytes(), nil +} + +func (p *Point) MarshalID() []byte { + return marshalPointID[:] +} + +func (p *Point) UnmarshalBinary(b []byte) error { + if p.point == nil { + p.point = new(filippo_ed25519.Point) + } + _, err := p.point.SetBytes(b) + return err +} + +func (p *Point) MarshalTo(w io.Writer) (int, error) { + return marshalling.PointMarshalTo(p, w) +} + +func (p *Point) UnmarshalFrom(r io.Reader) (int, error) { + return marshalling.PointUnmarshalFrom(p, r) +} diff --git a/group/filippo_ed25519/scalar.go b/group/filippo_ed25519/scalar.go new file mode 100644 index 000000000..cf33bf207 --- /dev/null +++ b/group/filippo_ed25519/scalar.go @@ -0,0 +1,184 @@ +package filippo_ed25519 + +import ( + "crypto/cipher" + "encoding/hex" + "errors" + filippo_ed25519 "filippo.io/edwards25519" + "fmt" + "go.dedis.ch/kyber/v3" + "go.dedis.ch/kyber/v3/group/internal/marshalling" + "go.dedis.ch/kyber/v3/group/mod" + "go.dedis.ch/kyber/v3/util/random" + "io" + "math/big" +) + +type Scalar struct { + scalar *filippo_ed25519.Scalar +} + +func (s *Scalar) Equal(a kyber.Scalar) bool { + return s.scalar.Equal(a.(*Scalar).scalar) == 1 +} + +func (s *Scalar) Set(a kyber.Scalar) kyber.Scalar { + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + _ = s.scalar.Set(a.(*Scalar).scalar) + return s +} + +func (s *Scalar) Clone() kyber.Scalar { + s2 := new(Scalar) + s2.scalar = new(filippo_ed25519.Scalar) + s2.scalar.Set(s.scalar) + return s2 +} + +func (s *Scalar) SetInt64(v int64) kyber.Scalar { + return s.setInt(mod.NewInt64(v, primeOrder)) +} + +func (s *Scalar) Zero() kyber.Scalar { + b := [32]byte{0} + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + _, err := s.scalar.SetCanonicalBytes(b[:]) + if err != nil { + fmt.Println(err) + } + return s +} + +func (s *Scalar) One() kyber.Scalar { + b := [32]byte{1} + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + _, err := s.scalar.SetCanonicalBytes(b[:]) + if err != nil { + fmt.Println(err) + } + return s +} + +func (s *Scalar) Add(a, b kyber.Scalar) kyber.Scalar { + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + s.scalar.Add(a.(*Scalar).scalar, b.(*Scalar).scalar) + return s +} + +func (s *Scalar) Sub(a, b kyber.Scalar) kyber.Scalar { + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + s.scalar.Subtract(a.(*Scalar).scalar, b.(*Scalar).scalar) + return s +} + +func (s *Scalar) Mul(a, b kyber.Scalar) kyber.Scalar { + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + s.scalar.Multiply(a.(*Scalar).scalar, b.(*Scalar).scalar) + return s +} + +func (s *Scalar) Div(a, b kyber.Scalar) kyber.Scalar { + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + b1 := b.(*Scalar) + b1.scalar.Invert(b1.scalar) + s.scalar.Multiply(a.(*Scalar).scalar, b1.scalar) + return s +} + +func (s *Scalar) Inv(a kyber.Scalar) kyber.Scalar { + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + s.scalar.Invert(a.(*Scalar).scalar) + return s +} + +func (s *Scalar) Neg(a kyber.Scalar) kyber.Scalar { + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + s.scalar.Negate(a.(*Scalar).scalar) + return s +} + +func (s *Scalar) Pick(rand cipher.Stream) kyber.Scalar { + i := mod.NewInt(random.Int(primeOrder, rand), primeOrder) + return s.setInt(i) +} + +func (s *Scalar) SetBytes(b []byte) kyber.Scalar { + // This function requires bytes in little-endian + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + _, err := s.scalar.SetCanonicalBytes(b) + if err != nil { + fmt.Println(err) + } + return s +} + +func (s *Scalar) setInt(i *mod.Int) kyber.Scalar { + b := i.LittleEndian(32, 32) + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + _, err := s.scalar.SetCanonicalBytes(b) + if err != nil { + fmt.Println(err) + } + return s +} + +func setBigInt(i *big.Int) *Scalar { + s := Scalar{} + s.setInt(mod.NewInt(i, fullOrder)) + return &s +} + +func (s *Scalar) MarshalSize() int { + return 32 +} + +func (s *Scalar) MarshalBinary() ([]byte, error) { + if s.scalar == nil { + return nil, errors.New("point not initialized") + } + b := s.scalar.Bytes() + return b, nil +} + +func (s *Scalar) UnmarshalBinary(b []byte) error { + if s.scalar == nil { + s.scalar = new(filippo_ed25519.Scalar) + } + _, err := s.scalar.SetCanonicalBytes(b) + return err +} + +func (s *Scalar) String() string { + b, _ := s.MarshalBinary() + return hex.EncodeToString(b) +} + +func (s *Scalar) MarshalTo(w io.Writer) (int, error) { + return marshalling.ScalarMarshalTo(s, w) +} + +func (s *Scalar) UnmarshalFrom(r io.Reader) (int, error) { + return marshalling.ScalarUnmarshalFrom(s, r) +} diff --git a/group/filippo_ed25519/suite.go b/group/filippo_ed25519/suite.go new file mode 100644 index 000000000..94c5e8560 --- /dev/null +++ b/group/filippo_ed25519/suite.go @@ -0,0 +1,71 @@ +package filippo_ed25519 + +import ( + "crypto/cipher" + "crypto/sha256" + "hash" + "io" + "reflect" + + "go.dedis.ch/fixbuf" + "go.dedis.ch/kyber/v3" + "go.dedis.ch/kyber/v3/group/internal/marshalling" + "go.dedis.ch/kyber/v3/util/random" + "go.dedis.ch/kyber/v3/xof/blake2xb" +) + +// SuiteFilippoEd25519 implements some basic functionalities such as Group, HashFactory, +// and XOFFactory. +type SuiteFilippoEd25519 struct { + Curve + r cipher.Stream +} + +// Hash returns a newly instanciated sha256 hash function. +func (s *SuiteFilippoEd25519) Hash() hash.Hash { + return sha256.New() +} + +// XOF returns an XOF which is implemented via the Blake2b hash. +func (s *SuiteFilippoEd25519) XOF(seed []byte) kyber.XOF { + return blake2xb.New(seed) +} + +func (s *SuiteFilippoEd25519) Read(r io.Reader, objs ...interface{}) error { + return fixbuf.Read(r, s, objs...) +} + +func (s *SuiteFilippoEd25519) Write(w io.Writer, objs ...interface{}) error { + return fixbuf.Write(w, objs) +} + +// New implements the kyber.Encoding interface +func (s *SuiteFilippoEd25519) New(t reflect.Type) interface{} { + return marshalling.GroupNew(s, t) +} + +// RandomStream returns a cipher.Stream that returns a key stream +// from crypto/rand. +func (s *SuiteFilippoEd25519) RandomStream() cipher.Stream { + if s.r != nil { + return s.r + } + return random.New() +} + +// NewBlakeSHA256Ed25519 returns a cipher suite based on package +// go.dedis.ch/kyber/v3/xof/blake2xb, SHA-256, and the Ed25519 curve. +// It produces cryptographically random numbers via package crypto/rand. +func NewBlakeSHA256FilippoEd25519() *SuiteFilippoEd25519 { + suite := new(SuiteFilippoEd25519) + return suite +} + +// NewBlakeSHA256Ed25519WithRand returns a cipher suite based on package +// go.dedis.ch/kyber/v3/xof/blake2xb, SHA-256, and the Ed25519 curve. +// It produces cryptographically random numbers via the provided stream r. +func NewBlakeSHA256FilippoEd25519WithRand(r cipher.Stream) *SuiteFilippoEd25519 { + suite := new(SuiteFilippoEd25519) + suite.r = r + return suite +}