-
Notifications
You must be signed in to change notification settings - Fork 178
Filippo version of Ed25519 #462
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
25448ab
6fe4f1e
da9fb34
451c68d
9cc00ea
28dd3b4
14e7da3
b41db2d
c1fe1cd
b0f9796
f746f8b
c7e292c
c7253db
211afe7
b9c3f4d
bb9eac2
0d1c3cd
bd72d1d
251bafc
0c93a23
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,9 +2,11 @@ | |
| *.o | ||
| *.pyc | ||
| *.swp | ||
| *.deb | ||
| *~ | ||
| moc_* | ||
| ext/ | ||
| .idea/ | ||
| dissent | ||
| docs/html | ||
| test.log | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 = [8]byte{'e', 'd', '.', 'p', 'o', 'i', 'n', 't'} | ||
|
parinayc20 marked this conversation as resolved.
Outdated
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I am correct then zero would also be a valid output for what we are trying to do.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @si-co do you agree ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function seems to have different problems:
IMHO the best way to implement this function would be to use |
||
| // to generate the key. | ||
| func (c *Curve) NewKeyAndSeedWithInput(buffer []byte) (kyber.Scalar, []byte, []byte) { | ||
| digest := sha512.Sum512(buffer[:]) | ||
| digest[0] &= 0xf8 | ||
| digest[31] &= 0xf | ||
|
|
||
| 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 | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| package filippo_ed25519 | ||
|
|
||
| import ( | ||
| "crypto/cipher" | ||
| "encoding/hex" | ||
| "errors" | ||
| filippo_ed25519 "filippo.io/edwards25519" | ||
| "go.dedis.ch/kyber/v3" | ||
| "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 { | ||
|
parinayc20 marked this conversation as resolved.
|
||
| 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) | ||
|
|
||
| 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it seems this would produce an infinite loop with a maliciously crafted
|
||
| } | ||
|
|
||
| if data == nil { | ||
| p.Mul(filippoCofactorScalar, p) | ||
| if p.Equal(&filippoNullPoint) { | ||
| continue | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume there's an expectation here that we're just "unlucky", could you document that? |
||
| } | ||
| return p | ||
| } | ||
|
parinayc20 marked this conversation as resolved.
|
||
|
|
||
| // 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 | ||
| } | ||
| // setCannonicalBytes() | ||
|
parinayc20 marked this conversation as resolved.
Outdated
|
||
| // 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) { | ||
| buf, err := p.MarshalBinary() | ||
| if err != nil { | ||
| return 0, err | ||
| } | ||
| return w.Write(buf) | ||
|
parinayc20 marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| func (p *Point) UnmarshalFrom(r io.Reader) (int, error) { | ||
| if strm, ok := r.(cipher.Stream); ok { | ||
| p.Pick(strm) | ||
| return -1, nil // no byte-count when picking randomly | ||
| } | ||
| buf := make([]byte, p.MarshalSize()) | ||
| n, err := io.ReadFull(r, buf) | ||
| if err != nil { | ||
| return n, err | ||
| } | ||
| return n, p.UnmarshalBinary(buf) | ||
|
parinayc20 marked this conversation as resolved.
Outdated
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.