diff --git a/goldilocks.go b/goldilocks.go new file mode 100644 index 0000000..1d9f294 --- /dev/null +++ b/goldilocks.go @@ -0,0 +1,96 @@ +package ed448 + +import ( + "fmt" + "io" + + "golang.org/x/crypto/sha3" +) + +type PublicKey [57]byte +type PrivateKey [57]byte + +func PointByPrivate(p PrivateKey) Point { + + digest := [57]byte{} + sha3.ShakeSum256(digest[:], p[:]) + clamp(digest[:]) + + r := NewScalar(digest[:]) + r.Halve(r) + r.Halve(r) + h := PrecomputedScalarMul(r) + + return h +} + +func EdPrivateKeyToX448(edKey PrivateKey) [56]byte { + x := [56]byte{} + sha3.ShakeSum256(x[:], edKey[:]) + return x +} + +func EdPublicKeyToX448(edKey PublicKey) [56]byte { + return fromEdDSATox448(edKey[:]) +} + +func Ed448DeriveSecret(pubkey PublicKey, privkey PrivateKey) [56]byte { + xpriv := EdPrivateKeyToX448(privkey) + xpub := EdPublicKeyToX448(pubkey) + a, b := x448ScalarMul(xpub[:], xpriv[:]) + if !b { + panic("Diffie-Hellman: result must not be zero") + } + return a +} + +func Ed448DerivePublicKey(privkey PrivateKey) PublicKey { + var pub PublicKey + p := PointByPrivate(privkey).EdDSAEncode() + copy(pub[:], p[:]) + return pub +} + +func Ed448Sign(privkey PrivateKey, pubkey PublicKey, message, context []byte, prehashed bool) [114]byte { + if len(context) != 0 { + panic("Context is not supported!") + } + if prehashed { + panic("Prehashing is not supported!") + } + p := NewPoint([16]uint32{}, [16]uint32{}, [16]uint32{}, [16]uint32{}) + + if !p.EdDSADecode(pubkey[:]) { + panic("Point is not on the curve!") + } + return DSASign(privkey, p, message) +} + +func Ed448Verify(pubkey PublicKey, signature, message, context []byte, prehashed bool) bool { + if len(context) != 0 { + panic("Context is not supported!") + } + if prehashed { + panic("Prehashing is not supported!") + } + p := NewPoint([16]uint32{}, [16]uint32{}, [16]uint32{}, [16]uint32{}) + + if !p.EdDSADecode(pubkey[:]) { + panic("Point is not on the curve!") + } + var sig [114]byte + copy(sig[:], signature[:]) + + return DSAVerify(sig, p, message) +} + +func Ed448GenerateKey(reader io.Reader) (PrivateKey, error) { + key := new(PrivateKey) + n, err := io.ReadFull(reader, key[:]) + if err != nil { + return PrivateKey{}, err + } else if n != 57 { + return PrivateKey{}, fmt.Errorf("not 57 random bytes") + } + return *key, nil +} diff --git a/goldilocks_test.go b/goldilocks_test.go new file mode 100644 index 0000000..de7c91a --- /dev/null +++ b/goldilocks_test.go @@ -0,0 +1,94 @@ +package ed448 + +import ( + "bytes" + "testing" +) + +func TestEdPublicKeyToX448(t *testing.T) { + // b93a28627cfa29fedb03c21aac0faa1ea0ba84c10cefa07c938f2e0adbf996f02c8d00e39695dfb6a0636c8bcb21645b06a869dfbbb489ef00 + edKey := PublicKey{ + 0xb9, 0x3a, 0x28, 0x62, 0x7c, 0xfa, 0x29, 0xfe, + 0xdb, 0x03, 0xc2, 0x1a, 0xac, 0x0f, 0xaa, 0x1e, + 0xa0, 0xba, 0x84, 0xc1, 0x0c, 0xef, 0xa0, 0x7c, + 0x93, 0x8f, 0x2e, 0x0a, 0xdb, 0xf9, 0x96, 0xf0, + 0x2c, 0x8d, 0x00, 0xe3, 0x96, 0x95, 0xdf, 0xb6, + 0xa0, 0x63, 0x6c, 0x8b, 0xcb, 0x21, 0x64, 0x5b, + 0x06, 0xa8, 0x69, 0xdf, 0xbb, 0xb4, 0x89, 0xef, + 0x00, + } + + // 163af30230e62cbf36fd8f4713f2204d78fa8f94f79adfe4f49ed1075d12b3a725a5e5c0564faa6445900b4d166b89b76f2db5c374411129 + x448Key := [56]byte{ + 0x16, 0x3a, 0xf3, 0x02, 0x30, 0xe6, 0x2c, 0xbf, + 0x36, 0xfd, 0x8f, 0x47, 0x13, 0xf2, 0x20, 0x4d, + 0x78, 0xfa, 0x8f, 0x94, 0xf7, 0x9a, 0xdf, 0xe4, + 0xf4, 0x9e, 0xd1, 0x07, 0x5d, 0x12, 0xb3, 0xa7, + 0x25, 0xa5, 0xe5, 0xc0, 0x56, 0x4f, 0xaa, 0x64, + 0x45, 0x90, 0x0b, 0x4d, 0x16, 0x6b, 0x89, 0xb7, + 0x6f, 0x2d, 0xb5, 0xc3, 0x74, 0x41, 0x11, 0x29, + } + generatedKey := EdPublicKeyToX448(edKey) + + if bytes.Compare(x448Key[:], generatedKey[:]) != 0 { + t.Errorf("converted x448 key must be %x, but it is %x", generatedKey, x448Key) + } +} + +func TestEdPrivateKeyToX448(t *testing.T) { + // b93a28627cfa29fedb03c21aac0faa1ea0ba84c10cefa07c938f2e0adbf996f02c8d00e39695dfb6a0636c8bcb21645b06a869dfbbb489ef00 + edKey := PrivateKey{ + 0xb9, 0x3a, 0x28, 0x62, 0x7c, 0xfa, 0x29, 0xfe, + 0xdb, 0x03, 0xc2, 0x1a, 0xac, 0x0f, 0xaa, 0x1e, + 0xa0, 0xba, 0x84, 0xc1, 0x0c, 0xef, 0xa0, 0x7c, + 0x93, 0x8f, 0x2e, 0x0a, 0xdb, 0xf9, 0x96, 0xf0, + 0x2c, 0x8d, 0x00, 0xe3, 0x96, 0x95, 0xdf, 0xb6, + 0xa0, 0x63, 0x6c, 0x8b, 0xcb, 0x21, 0x64, 0x5b, + 0x06, 0xa8, 0x69, 0xdf, 0xbb, 0xb4, 0x89, 0xef, + 0x00, + } + + // 74a4d56b9ca4bb819778d5b089ef89428bbe768825c83264e97cfba7c0a5f3c33d6ac807e3a568d72a605283f89b8afa52b06323704d9278, + x448Key := [56]byte{ + 0x74, 0xa4, 0xd5, 0x6b, 0x9c, 0xa4, 0xbb, 0x81, + 0x97, 0x78, 0xd5, 0xb0, 0x89, 0xef, 0x89, 0x42, + 0x8b, 0xbe, 0x76, 0x88, 0x25, 0xc8, 0x32, 0x64, + 0xe9, 0x7c, 0xfb, 0xa7, 0xc0, 0xa5, 0xf3, 0xc3, + 0x3d, 0x6a, 0xc8, 0x07, 0xe3, 0xa5, 0x68, 0xd7, + 0x2a, 0x60, 0x52, 0x83, 0xf8, 0x9b, 0x8a, 0xfa, + 0x52, 0xb0, 0x63, 0x23, 0x70, 0x4d, 0x92, 0x78, + } + generatedKey := EdPrivateKeyToX448(edKey) + + if bytes.Compare(x448Key[:], generatedKey[:]) != 0 { + t.Errorf("converted x448 key must be %x, but it is %x", generatedKey, x448Key) + } +} + +func TestSignVerify(t *testing.T) { + priv := PrivateKey{ + 0xb9, 0x3a, 0x28, 0x62, 0x7c, 0xfa, 0x29, 0xfe, + 0xdb, 0x03, 0xc2, 0x1a, 0xac, 0x0f, 0xaa, 0x1e, + 0xa0, 0xba, 0x84, 0xc1, 0x0c, 0xef, 0xa0, 0x7c, + 0x93, 0x8f, 0x2e, 0x0a, 0xdb, 0xf9, 0x96, 0xf0, + 0x2c, 0x8d, 0x00, 0xe3, 0x96, 0x95, 0xdf, 0xb6, + 0xa0, 0x63, 0x6c, 0x8b, 0xcb, 0x21, 0x64, 0x5b, + 0x06, 0xa8, 0x69, 0xdf, 0xbb, 0xb4, 0x89, 0xef, + 0x00, + } + + pub := Ed448DerivePublicKey(priv) + + sig := Ed448Sign(priv, pub, []byte{1}, []byte{}, false) + ver := Ed448Verify(pub, sig[:], []byte{1}, []byte{}, false) + if ver != true { + t.Errorf("Signature doesn't verify") + } + + sig = Ed448Sign(priv, pub, []byte{1}, []byte{}, false) + if Ed448Verify(pub, sig[:], []byte{2}, []byte{}, false) { + t.Errorf("wrong signature verification") + } + + +} diff --git a/scalar_test.go b/scalar_test.go index 7c107ac..83d1f9c 100644 --- a/scalar_test.go +++ b/scalar_test.go @@ -300,7 +300,6 @@ func (s *Ed448Suite) Test_ScalarDecodeLong(c *C) { 0x4b75d258, 0x34a8bc39, } - x = scalarZero out = decodeLong(x, buf) c.Assert(out, DeepEquals, exp) @@ -322,7 +321,6 @@ func (s *Ed448Suite) Test_ScalarDecodeLong(c *C) { 0xac5eb1a1, 0x32a3e4eb, } - x = scalarZero out = decodeLong(x, buf) c.Assert(out, DeepEquals, exp) }