Skip to content

Commit

Permalink
Adding support for Ed448 digital signatures.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Feb 4, 2020
1 parent 1b61c06 commit 8fcdbad
Show file tree
Hide file tree
Showing 17 changed files with 2,701 additions and 37 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Version numbers are [Semvers](https://semver.org/). We release a minor version f
| Key Exchange | X25519, X448 | RFC-7748 provides new key exchange mechanisms based on Montgomery elliptic curves. | TLS 1.3. Secure Shell. |
| Key Exchange | FourQ | One of the fastest elliptic curves at 128-bit security level. | Experimental for key agreement and digital signatures. |
| Key Exchange / Digital signatures | P-384 | Our optimizations reduce the burden when moving from P-256 to P-384. | ECDSA and ECDH using Suite B at top secret level. |
| Digital Signatures | Ed25519 | RFC-8032 provides new signature schemes based on Edwards curves. | Digital certificates and authentication. |
| Digital Signatures | Ed25519, Ed448 | RFC-8032 provides new signature schemes based on Edwards curves. | Digital certificates and authentication. |

### Work in Progress

Expand Down
44 changes: 43 additions & 1 deletion internal/conv/conv.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
package conv

import (
"encoding/hex"
"fmt"
"math/big"
"math/bits"
"strings"
)

// Hex2BytesLe returns a little-endian byte slice representing an hexadecimal
// number. The input must not have the '0x' preffix.
func Hex2BytesLe(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return b
}

// BytesLe2Hex returns an hexadecimal string of a number stored in a
// little-endian order slice x.
func BytesLe2Hex(x []byte) string {
b := &strings.Builder{}
b := new(strings.Builder)
b.Grow(2*len(x) + 2)
fmt.Fprint(b, "0x")
if len(x) == 0 {
Expand Down Expand Up @@ -53,6 +65,36 @@ func BigInt2BytesLe(z []byte, x *big.Int) {
}
}

// Uint64Le2Hex returns an hexadecimal string of a number stored in a
// little-endian order slice x.
func Uint64Le2Hex(x []uint64) string {
b := new(strings.Builder)
b.Grow(16*len(x) + 2)
fmt.Fprint(b, "0x")
if len(x) == 0 {
fmt.Fprint(b, "00")
}
for i := len(x) - 1; i >= 0; i-- {
fmt.Fprintf(b, "%016x", x[i])
}
return b.String()
}

// UintLe2Hex returns an hexadecimal string of a number stored in a
// little-endian order slice x.
func UintLe2Hex(x []uint) string {
b := new(strings.Builder)
b.Grow((bits.UintSize/4)*len(x) + 2)
fmt.Fprint(b, "0x")
if len(x) == 0 {
fmt.Fprint(b, "00")
}
for i := len(x) - 1; i >= 0; i-- {
fmt.Fprintf(b, fmt.Sprintf("%%0%vx", bits.UintSize/4), x[i])
}
return b.String()
}

// Uint64Le2BigInt converts a llitle-endian slice x into a big number.
func Uint64Le2BigInt(x []uint64) *big.Int {
n := len(x)
Expand Down
94 changes: 59 additions & 35 deletions math/fp448/fp.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,59 +46,83 @@ func Neg(z, x *Elt) { Sub(z, &p, x) }
// Modp ensures that z is between [0,p-1].
func Modp(z *Elt) { Sub(z, z, &p) }

// InvSqrt calculates z = sqrt(x/y) iff x/y is a quadratic-residue, which is
// indicated by returning isQR = true. Otherwise, when x/y is a quadratic
// non-residue, z will have an undetermined value and isQR = false.
func InvSqrt(z, x, y *Elt) (isQR bool) {
t0, t1 := &Elt{}, &Elt{}
Mul(t0, x, y) // x*y
Sqr(t1, y) // y^2
Mul(t1, t0, t1) // x*y^3
powPminus3div4(z, t1) // (x*y^3)^k
Mul(z, z, t0) // z = x*y*(x*y^3)^k = x^(k+1) * y^(3k+1)

// Check if x/y is a quadratic residue
Sqr(t0, z) // z^2
Mul(t0, t0, y) // y*z^2
Sub(t0, t0, x) // y*z^2-x
return IsZero(t0)
}

// Inv calculates z = 1/x mod p.
func Inv(z, x *Elt) {
x0, x1, x2 := &Elt{}, &Elt{}, &Elt{}
Sqr(x2, x)
Mul(x2, x2, x)
Sqr(x0, x2)
t := &Elt{}
powPminus3div4(t, x) // t = x^k
Sqr(t, t) // t = x^2k
Sqr(t, t) // t = x^4k
Mul(z, t, x) // z = x^(4k+1)
}

// powPminus3div4 calculates z = x^k mod p, where k = (p-3)/4.
func powPminus3div4(z, x *Elt) {
x0, x1 := &Elt{}, &Elt{}
Sqr(z, x)
Mul(z, z, x)
Sqr(x0, z)
Mul(x0, x0, x)
Sqr(x2, x0)
Sqr(x2, x2)
Sqr(x2, x2)
Mul(x2, x2, x0)
Sqr(x1, x2)
Sqr(z, x0)
Sqr(z, z)
Sqr(z, z)
Mul(z, z, x0)
Sqr(x1, z)
for i := 0; i < 5; i++ {
Sqr(x1, x1)
}
Mul(x1, x1, x2)
Sqr(x2, x1)
Mul(x1, x1, z)
Sqr(z, x1)
for i := 0; i < 11; i++ {
Sqr(x2, x2)
Sqr(z, z)
}
Mul(x2, x2, x1)
Sqr(x2, x2)
Sqr(x2, x2)
Sqr(x2, x2)
Mul(x2, x2, x0)
Sqr(x1, x2)
Mul(z, z, x1)
Sqr(z, z)
Sqr(z, z)
Sqr(z, z)
Mul(z, z, x0)
Sqr(x1, z)
for i := 0; i < 26; i++ {
Sqr(x1, x1)
}
Mul(x1, x1, x2)
Sqr(x2, x1)
Mul(x1, x1, z)
Sqr(z, x1)
for i := 0; i < 53; i++ {
Sqr(x2, x2)
Sqr(z, z)
}
Mul(x2, x2, x1)
Sqr(x2, x2)
Sqr(x2, x2)
Sqr(x2, x2)
Mul(x2, x2, x0)
Sqr(x1, x2)
Mul(z, z, x1)
Sqr(z, z)
Sqr(z, z)
Sqr(z, z)
Mul(z, z, x0)
Sqr(x1, z)
for i := 0; i < 110; i++ {
Sqr(x1, x1)
}
Mul(x1, x1, x2)
Sqr(x2, x1)
Mul(x2, x2, x)
Mul(x1, x1, z)
Sqr(z, x1)
Mul(z, z, x)
for i := 0; i < 223; i++ {
Sqr(x2, x2)
Sqr(z, z)
}
Mul(x2, x2, x1)
Sqr(x2, x2)
Sqr(x2, x2)
Mul(z, x2, x)
Mul(z, z, x1)
}

// Cmov assigns y to x if n is 1.
Expand Down
49 changes: 49 additions & 0 deletions math/fp448/fp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,50 @@ func TestInv(t *testing.T) {
}
}

func TestInvSqrt(t *testing.T) {
const numTests = 1 << 9
var x, y, z Elt
prime := P()
p := conv.BytesLe2BigInt(prime[:])
exp := big.NewInt(1)
exp.Add(p, exp).Rsh(exp, 2)
var frac, root, sqRoot big.Int
var wantQR bool
var want *big.Int
for i := 0; i < numTests; i++ {
_, _ = rand.Read(x[:])
_, _ = rand.Read(y[:])

gotQR := InvSqrt(&z, &x, &y)
Modp(&z)
got := conv.BytesLe2BigInt(z[:])

xx := conv.BytesLe2BigInt(x[:])
yy := conv.BytesLe2BigInt(y[:])
frac.ModInverse(yy, p).Mul(&frac, xx).Mod(&frac, p)
root.Exp(&frac, exp, p)
sqRoot.Mul(&root, &root).Mod(&sqRoot, p)

if sqRoot.Cmp(&frac) == 0 {
want = &root
wantQR = true
} else {
want = big.NewInt(0)
wantQR = false
}

if wantQR {
if gotQR != wantQR || got.Cmp(want) != 0 {
test.ReportError(t, got, want, x, y)
}
} else {
if gotQR != wantQR {
test.ReportError(t, gotQR, wantQR, x, y)
}
}
}
}

func TestGeneric(t *testing.T) {
t.Run("Cmov", func(t *testing.T) { testCmov(t, cmovGeneric) })
t.Run("Cswap", func(t *testing.T) { testCswap(t, cswapGeneric) })
Expand Down Expand Up @@ -345,4 +389,9 @@ func BenchmarkFp(b *testing.B) {
Inv(&x, &y)
}
})
b.Run("InvSqrt", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = InvSqrt(&z, &x, &y)
}
})
}
7 changes: 7 additions & 0 deletions sign/ed448/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Package ed448 provides the signature scheme Ed448 as described in RFC-8032.
//
// References:
// - RFC8032 https://rfc-editor.org/rfc/rfc8032.txt
// - EdDSA for more curves https://eprint.iacr.org/2015/677
// - High-speed high-security signatures. https://doi.org/10.1007/s13389-012-0027-1
package ed448
Loading

0 comments on commit 8fcdbad

Please sign in to comment.