Skip to content

Commit

Permalink
mayo: add Mode interface
Browse files Browse the repository at this point in the history
  • Loading branch information
ilway25 committed Mar 12, 2024
1 parent 5e88046 commit 65e1819
Show file tree
Hide file tree
Showing 28 changed files with 804 additions and 61 deletions.
15 changes: 0 additions & 15 deletions sign/mayo/doc.go

This file was deleted.

56 changes: 56 additions & 0 deletions sign/mayo/example_test.go
@@ -0,0 +1,56 @@
package mayo_test

import (
"fmt"
"sort"

"github.com/cloudflare/circl/sign/mayo"
)

func Example() {
// Check supported modes
modes := mayo.ModeNames()
sort.Strings(modes)
fmt.Printf("Supported modes: %v\n", modes)

// Pick MAYO mode 3.
mode := mayo.ModeByName("MAYO_3")
if mode == nil {
panic("Mode3 not supported")
}

// Alternatively one could simply write
//
// mode := MAYO.Mode3

// Generates a keypair.
pk, sk, err := mode.GenerateKey(nil)
if err != nil {
panic(err)
}
// (Alternatively one can derive a keypair from a seed,
// see mode.NewKeyFromSeed().)

// Packs public and private key
packedSk := sk.Bytes()
packedPk := pk.Bytes()

// Load it again
sk2 := mode.PrivateKeyFromBytes(packedSk)
pk2 := mode.PublicKeyFromBytes(packedPk)

// Creates a signature on our message with the generated private key.
msg := []byte("Some message")
signature, _ := mode.Sign(sk2, msg, nil)

// Checks whether a signature is correct
if !mode.Verify(pk2, msg, signature) {
panic("incorrect signature")
}

fmt.Printf("O.K.")

// Output:
// Supported modes: [MAYO_1 MAYO_2 MAYO_3 MAYO_5]
// O.K.
}
27 changes: 27 additions & 0 deletions sign/mayo/gen.go
Expand Up @@ -86,6 +86,7 @@ var (

func main() {
generateModePackageFiles()
generateModeToplevelFiles()
generateParamsFiles()
generateSourceFiles()
generateSignApiFiles()
Expand Down Expand Up @@ -151,6 +152,32 @@ func generateModePackageFiles() {
}
}

// Generates modeX.go from templates/mode.templ.go
func generateModeToplevelFiles() {
tl, err := template.ParseFiles("templates/mode.templ.go")
if err != nil {
panic(err)
}

for _, mode := range Modes {
buf := new(bytes.Buffer)
err := tl.Execute(buf, mode)
if err != nil {
panic(err)
}

res := buf.String()
offset := strings.Index(res, TemplateWarning)
if offset == -1 {
panic("Missing template warning in mode.templ.go")
}
err = os.WriteFile(mode.Pkg()+".go", []byte(res[offset:]), 0o644)
if err != nil {
panic(err)
}
}
}

// Generates modeX/signapi.go from templates/signapi.templ.go
func generateSignApiFiles() {
tl, err := template.ParseFiles("templates/signapi.templ.go")
Expand Down
100 changes: 100 additions & 0 deletions sign/mayo/mayo.go
@@ -0,0 +1,100 @@
//go:generate go run gen.go

// Package mayo implements the MAYO signature scheme
// as submitted to round1 of the NIST PQC competition of Additional Signature Scehemes and described in
//
// https://csrc.nist.gov/csrc/media/Projects/pqc-dig-sig/documents/round-1/spec-files/mayo-spec-web.pdf
//
// This implemented the nibble-sliced version as proposed in
//
// https://eprint.iacr.org/2023/1683
//
// and the code is written with heavy reference to
//
// https://github.com/PQCMayo/MAYO-C/tree/nibbling-mayo
package mayo

import (
"crypto"
"io"
)

// PublicKey is a MAYO public key.
//
// The structure contains values precomputed during unpacking/key generation
// and is therefore significantly larger than a packed public key.
type PublicKey interface {
// Packs public key
Bytes() []byte
}

// PrivateKey is a MAYO public key.
//
// The structure contains values precomputed during unpacking/key generation
// and is therefore significantly larger than a packed private key.
type PrivateKey interface {
// Packs private key
Bytes() []byte

crypto.Signer
}

// Mode is a certain configuration of the MAYO signature scheme.
type Mode interface {
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error)

// NewKeyFromSeed derives a public/private key pair using the given seed.
// Panics if len(seed) != SeedSize()
NewKeyFromSeed(seed []byte) (PublicKey, PrivateKey)

// Sign signs the given message using entropy from rand and returns the signature.
// If rand is nil, crypto/rand.Reader will be used.
// It will panic if sk has not been generated for this mode.
Sign(sk PrivateKey, msg []byte, rand io.Reader) ([]byte, error)

// Verify checks whether the given signature by pk on msg is valid.
// It will panic if pk is of the wrong mode.
Verify(pk PublicKey, msg []byte, signature []byte) bool

// Unpacks a public key. Panics if the buffer is not of PublicKeySize()
// length. Precomputes values to speed up subsequent calls to Verify.
PublicKeyFromBytes([]byte) PublicKey

// Unpacks a private key. Panics if the buffer is not
// of PrivateKeySize() length. Precomputes values to speed up subsequent
// calls to Sign(To).
PrivateKeyFromBytes([]byte) PrivateKey

// SeedSize returns the size of the seed for NewKeyFromSeed
SeedSize() int

// PublicKeySize returns the size of a packed PublicKey
PublicKeySize() int

// PrivateKeySize returns the size of a packed PrivateKey
PrivateKeySize() int

// SignatureSize returns the size of a signature
SignatureSize() int

// Name returns the name of this mode
Name() string
}

var modes = make(map[string]Mode)

// ModeNames returns the list of supported modes.
func ModeNames() []string {
names := []string{}
for name := range modes {
names = append(names, name)
}
return names
}

// ModeByName returns the mode with the given name or nil when not supported.
func ModeByName(name string) Mode {
return modes[name]
}
136 changes: 136 additions & 0 deletions sign/mayo/mayo_test.go

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions sign/mayo/mode1.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions sign/mayo/mode1/internal/mayo.go
Expand Up @@ -149,13 +149,13 @@ func GenerateKey(rand io.Reader) (*PublicKey, *PrivateKey, error) {
if err != nil {
return nil, nil, err
}
pk, sk := NewKeyFromSeed(seed)
pk, sk := NewKeyFromSeed(&seed)
return pk, sk, nil
}

func NewKeyFromSeed(seed [KeySeedSize]byte) (*PublicKey, *PrivateKey) {
func NewKeyFromSeed(seed *[KeySeedSize]byte) (*PublicKey, *PrivateKey) {
var sk PrivateKey
sk.Unpack(&seed)
sk.Unpack(seed)

return sk.Public(), &sk
}
Expand Down
12 changes: 6 additions & 6 deletions sign/mayo/mode1/internal/mayo_test.go
Expand Up @@ -28,7 +28,7 @@ func TestNewKey(t *testing.T) {
var seed [KeySeedSize]byte
_, _ = hex.Decode(seed[:], []byte(tc.seed))

pk, sk := NewKeyFromSeed(seed)
pk, sk := NewKeyFromSeed(&seed)

var pk2 [PublicKeySize]byte
var sk2 [PrivateKeySize]byte
Expand Down Expand Up @@ -68,7 +68,7 @@ func TestVerify(t *testing.T) {
m, _ := hex.DecodeString(tc.message)
s, _ := hex.DecodeString(tc.signature)

pk, _ := NewKeyFromSeed(seed)
pk, _ := NewKeyFromSeed(&seed)

if !Verify(pk, m, s) {
t.Fatal("should verify")
Expand Down Expand Up @@ -144,7 +144,7 @@ func TestPQCgenKATSign(t *testing.T) {

g2 := nist.NewDRBG(&seed)
_, _ = g2.Read(eseed[:])
pk, sk := NewKeyFromSeed(eseed)
pk, sk := NewKeyFromSeed(&eseed)

var pk2 [PublicKeySize]byte
var sk2 [PrivateKeySize]byte
Expand Down Expand Up @@ -178,7 +178,7 @@ func BenchmarkKeyGen(b *testing.B) {
var seed [KeySeedSize]byte
for i := 0; i < b.N; i++ {
binary.LittleEndian.PutUint64(seed[:], uint64(i))
_, _ = NewKeyFromSeed(seed)
_, _ = NewKeyFromSeed(&seed)
}
}

Expand All @@ -198,7 +198,7 @@ func BenchmarkMatMul(b *testing.B) {
func BenchmarkVerify(b *testing.B) {
var seed [KeySeedSize]byte
var msg [8]byte
pk, sk := NewKeyFromSeed(seed)
pk, sk := NewKeyFromSeed(&seed)
sig, _ := Sign(msg[:], sk, nil)
b.ResetTimer()
for i := 0; i < b.N; i++ {
Expand All @@ -218,7 +218,7 @@ func (zeroReader) Read(buf []byte) (int, error) {
func BenchmarkSign(b *testing.B) {
var seed [KeySeedSize]byte
var msg [8]byte
_, sk := NewKeyFromSeed(seed)
_, sk := NewKeyFromSeed(&seed)
b.ResetTimer()
for i := 0; i < b.N; i++ {
binary.LittleEndian.PutUint64(msg[:], uint64(i))
Expand Down
2 changes: 1 addition & 1 deletion sign/mayo/mode1/mayo.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sign/mayo/mode1/signapi.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 65e1819

Please sign in to comment.