diff --git a/.circleci/rolling-shutter.yml b/.circleci/rolling-shutter.yml index 5dfcb3b06..2dfb41f89 100644 --- a/.circleci/rolling-shutter.yml +++ b/.circleci/rolling-shutter.yml @@ -7,7 +7,7 @@ jobs: path: ~/src - restore_cache: keys: - - rs-generate-v18-{{ checksum "go.sum" }}-{{checksum "go.mod"}} + - rs-generate-v19-{{ checksum "go.sum" }}-{{checksum "go.mod"}} - install-asdf - run: name: "Install asdf plugins" @@ -33,7 +33,7 @@ jobs: find ./docs -name '*.md' -delete - run: make generate - save_cache: - key: rs-generate-v18-{{ checksum "go.sum" }}-{{checksum "go.mod"}} + key: rs-generate-v19-{{ checksum "go.sum" }}-{{checksum "go.mod"}} paths: - "~/go/pkg/mod" - "~/.cache/go-build" @@ -51,10 +51,10 @@ jobs: path: ~/src - restore_cache: keys: - - go-mod-v7-{{ checksum "go.sum" }}-{{ checksum "go.mod" }} + - go-mod-v8-{{ checksum "go.sum" }}-{{ checksum "go.mod" }} - run: go get -d ./... - save_cache: - key: go-mod-v7-{{ checksum "go.sum" }}-{{ checksum "go.mod" }} + key: go-mod-v8-{{ checksum "go.sum" }}-{{ checksum "go.mod" }} paths: - ~/go/pkg/ - restore_cache: @@ -90,7 +90,7 @@ jobs: - store_test_results: path: report - save_cache: - key: rs-build-<< parameters.go-version >>-v4-{{ checksum "go.sum" }}-{{ checksum "go.mod" }} + key: rs-build-<< parameters.go-version >>-v5-{{ checksum "go.sum" }}-{{ checksum "go.mod" }} paths: - "~/.cache/go-build" @@ -200,7 +200,7 @@ jobs: - run: | make lint-changes - save_cache: - key: rs-lint-v13-{{ checksum "go.sum" }}-{{ checksum "go.mod" }} + key: rs-lint-v14-{{ checksum "go.sum" }}-{{ checksum "go.mod" }} paths: - "~/.cache/go-build" - "~/.cache/golangci-lint" diff --git a/rolling-shutter/cmd/cryptocmd/cryptocmd.go b/rolling-shutter/cmd/cryptocmd/cryptocmd.go index 7d8840b02..4111c7b73 100644 --- a/rolling-shutter/cmd/cryptocmd/cryptocmd.go +++ b/rolling-shutter/cmd/cryptocmd/cryptocmd.go @@ -20,6 +20,7 @@ var ( epochIDFlag string sigmaFlag string threshold uint64 + filename string ) func Cmd() *cobra.Command { @@ -27,12 +28,15 @@ func Cmd() *cobra.Command { Use: "crypto", Short: "CLI tool to access crypto functions", Long: `This command provides utility functions to manually encrypt messages with an eon -key, decrypt them with a decryption key, and check that a decryption key is correct.`, +key, decrypt them with a decryption key, and check that a decryption key is correct. It also hosts +a tool to generate and run crypto tests in a JSON formatted collection.`, } cmd.AddCommand(encryptCmd()) cmd.AddCommand(decryptCmd()) cmd.AddCommand(verifyKeyCmd()) cmd.AddCommand(aggregateCmd()) + cmd.AddCommand(GenerateTestdata()) + cmd.AddCommand(RunJSONTests()) return cmd } diff --git a/rolling-shutter/cmd/cryptocmd/jsontests.go b/rolling-shutter/cmd/cryptocmd/jsontests.go new file mode 100644 index 000000000..732b14487 --- /dev/null +++ b/rolling-shutter/cmd/cryptocmd/jsontests.go @@ -0,0 +1,599 @@ +package cryptocmd + +import ( + "bytes" + "crypto/rand" + "encoding/json" + "errors" + "fmt" + "io" + "os" + + "github.com/ethereum/go-ethereum/common/hexutil" + bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" + "github.com/spf13/cobra" + + "github.com/shutter-network/shutter/shlib/shcrypto" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/testkeygen" +) + +func GenerateTestdata() *cobra.Command { + cmd := &cobra.Command{ + Use: "testdata", + Short: "Generate testdata in json format to test crypto implementations", + Args: cobra.RangeArgs(0, 1), + RunE: func(cmd *cobra.Command, args []string) error { + var w io.Writer + if len(args) == 0 { + w = os.Stdout + } else { + f, err := os.OpenFile(args[0], os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) + if err != nil { + panic(err) + } + defer f.Close() + w = f + } + + enc := &testEncoder{ + w: w, + etCounter: 1, + dtCounter: 1, + vtCounter: 1, + } + enc.start() + createJSONTests(*enc) + + enc.flush() + return nil + }, + } + cmd.PersistentFlags().StringVarP(&filename, "filename", "f", "", "filename to write result") + + return cmd +} + +func RunJSONTests() *cobra.Command { + cmd := &cobra.Command{ + Use: "jsontests", + Short: "Use testdata in json format to test crypto implementations", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + errs := readTestcases(filename) + + return errs[0] + }, + } + + cmd.PersistentFlags().StringVarP(&filename, "filename", "f", "", "filename to write result") + return cmd +} + +type testEncoder struct { + w io.Writer + idCounter int + etCounter int + dtCounter int + vtCounter int +} + +func (enc *testEncoder) addTest(tc *testCase) { + tc.ID = fmt.Sprint(enc.idCounter) + switch tc.TestType { + case encryption: + tc.Name = fmt.Sprintf("%s %d", encryption, enc.etCounter) + enc.etCounter++ + case decryption: + tc.Name = fmt.Sprintf("%s %d", decryption, enc.dtCounter) + enc.dtCounter++ + case verification: + tc.Name = fmt.Sprintf("%s %d", verification, enc.vtCounter) + enc.vtCounter++ + default: + panic(fmt.Errorf("unknown test type")) + } + + const indent = " " + encoded, err := json.MarshalIndent(tc, indent, indent) + if err != nil { + panic(fmt.Errorf("can't encode test case: %v", err)) + } + + var buf bytes.Buffer + if enc.idCounter > 0 { + if _, err := buf.WriteString(",\n"); err != nil { + panic(err) + } + } + if _, err := buf.WriteString(indent); err != nil { + panic(err) + } + if _, err := buf.Write(encoded); err != nil { + panic(err) + } + if _, err := buf.WriteTo(enc.w); err != nil { + panic(err) + } + + enc.idCounter++ +} + +func (enc *testEncoder) start() { + _, err := io.WriteString(enc.w, "[\n") + if err != nil { + panic(err) + } +} + +func (enc *testEncoder) flush() { + _, err := io.WriteString(enc.w, "\n]\n") + if err != nil { + panic(err) + } +} + +type testCase struct { + testCaseMeta + Test testData `json:"test_data"` +} + +type testCaseMeta struct { + Name string `json:"name"` + ID string `json:"id"` + Description string `json:"description"` + TestType string `json:"type"` +} + +type testData interface { + Run() error +} + +func (tc *testCase) UnmarshalJSON(b []byte) error { + if err := json.Unmarshal(b, &tc.testCaseMeta); err != nil { + return err + } + switch tc.TestType { + case encryption: + tc.Test = new(encryptionTest) + case decryption: + tc.Test = new(decryptionTest) + case verification: + tc.Test = new(verificationTest) + default: + return fmt.Errorf("invalid test type %q", tc.Test) + } + + var testRaw struct { + Data json.RawMessage `json:"test_data"` + } + if err := json.Unmarshal(b, &testRaw); err != nil { + return err + } + return json.Unmarshal(testRaw.Data, tc.Test) +} + +const ( + encryption = "encryption" + decryption = "decryption" + verification = "verification" +) + +type encryptionTest struct { + Message hexutil.Bytes `json:"message"` + EonPublicKey *shcrypto.EonPublicKey `json:"eon_public_key"` + EpochID identitypreimage.IdentityPreimage `json:"epoch_id"` + Sigma shcrypto.Block `json:"sigma"` + Expected *shcrypto.EncryptedMessage `json:"expected"` +} + +type decryptionTest struct { + Cipher shcrypto.EncryptedMessage `json:"cipher"` + EpochSecretKey shcrypto.EpochSecretKey `json:"epoch_secret_key"` + Expected hexutil.Bytes `json:"expected"` +} + +type verificationTest struct { + EpochSecretKey shcrypto.EpochSecretKey `json:"epoch_secret_key"` + EonPublicKey shcrypto.EonPublicKey `json:"eon_public_key"` + EpochID identitypreimage.IdentityPreimage `json:"epoch_id"` + Expected bool `json:"expected"` +} + +func readTestcases(filename string) []error { + var testcases []*testCase + + f, err := os.OpenFile(filename, os.O_RDONLY, 0o600) + if err != nil { + panic(err) + } + defer f.Close() + decoder := json.NewDecoder(f) + + err = decoder.Decode(&testcases) + if err != nil { + panic(err) + } + var failed int + errs := make([]error, len(testcases)) + for i, testcase := range testcases { + fmt.Printf( + "[%03d/%03d] '%-14s': %-42s ID: % 3s", + i+1, len(testcases), + testcase.Name, + testcase.Description, + testcase.ID, + ) + err = testcase.Test.Run() + + if err != nil { + failed++ + errs = append(errs, err) + fmt.Printf(" FAIL (%s)\n", err) + } else { + fmt.Printf(" PASS\n") + } + } + fmt.Printf("%03d tests failed.\n", failed) + return errs +} + +const ( + random = "random" + fixed = "fixed" + tampered = "tampered" + verifying = "verifying" + nonVerifying = "nonverifying" +) + +var testSpecs = []struct { + description string + payload []byte + style string +}{ + { + "A zero byte message.", + make([]byte, 0), + random, + }, + { + "A 1 byte message.", + make([]byte, 1), + random, + }, + { + "A 31 byte message.", + make([]byte, 31), + random, + }, + { + "A 32 byte message.", + make([]byte, 32), + random, + }, + { + "A 33 byte message.", + make([]byte, 33), + random, + }, + { + "A 319 byte message.", + make([]byte, 319), + random, + }, + { + "A 320 byte message.", + make([]byte, 320), + random, + }, + { + "A 321 byte message.", + make([]byte, 321), + random, + }, + { + "The message 'A message'", + []byte("A message"), + fixed, + }, + { + "An illegal modification of the encrypted message 'A message'", + []byte("A message"), + tampered, + }, + { + "Verification of a random 32 byte epochID", + make([]byte, 32), + verifying, + }, + { + "A failed verification", + make([]byte, 32), + nonVerifying, + }, +} + +func createJSONTests(enc testEncoder) { + keygen := testkeygen.NewKeyGenerator(12, 10) + var err error + for i := range testSpecs { + testSpec := testSpecs[i] + + switch testSpec.style { + case random, fixed: + + if testSpec.style == random { + _, err = rand.Read(testSpec.payload) + } + if err != nil { + panic(err) + } + et, err := createEncryptionTest(keygen, testSpec.payload) + if err != nil { + panic(err) + } + testcase := testCase{ + testCaseMeta: testCaseMeta{ + Description: testSpec.description, + TestType: encryption, + }, + Test: et, + } + if err := verifyTestCase(&testcase); err != nil { + panic(err) + } + enc.addTest(&testcase) + + dt := createDecryptionTest(keygen, *et) + testcase = testCase{ + testCaseMeta: testCaseMeta{ + Description: testSpec.description, + TestType: decryption, + }, + Test: &dt, + } + + if err = verifyTestCase(&testcase); err != nil { + panic(err) + } + enc.addTest(&testcase) + + case tampered: + et, err := createEncryptionTest(keygen, testSpec.payload) + if err != nil { + panic(err) + } + tamperedEt := tamperEncryptedMessage(keygen, *et) + + dt := createDecryptionTest(keygen, tamperedEt) + dt.Expected, _ = hexutil.Decode("0x") + testcase := testCase{ + testCaseMeta: testCaseMeta{ + Description: testSpec.description, + TestType: decryption, + }, + Test: &dt, + } + if err := verifyTestCase(&testcase); err != nil { + panic(err) + } + enc.addTest(&testcase) + case verifying, nonVerifying: + var err error + var vt verificationTest + if testSpec.style == verifying { + vt, err = createVerificationTest(keygen, testSpec.payload) + } else { + vt, err = createFailedVerificationTest(keygen, testSpec.payload) + } + if err != nil { + panic(err) + } + testcase := testCase{ + testCaseMeta: testCaseMeta{ + Description: testSpec.description, + TestType: verification, + }, + Test: &vt, + } + if err := verifyTestCase(&testcase); err != nil { + panic(err) + } + enc.addTest(&testcase) + + default: + panic("no test style defined") + } + } +} + +func verifyTestCase(tc *testCase) error { + if err := testMarshalingRoundtrip(tc); err != nil { + return err + } + return tc.Test.Run() +} + +func createEncryptionTest(keygen *testkeygen.KeyGenerator, message []byte) (*encryptionTest, error) { + epochID := keygen.RandomEpochID(make([]byte, 32)) + + et := encryptionTest{} + + et.Message = message + + et.EonPublicKey = keygen.EonPublicKey(epochID) + et.EpochID = epochID + sigma, err := keygen.RandomSigma() + if err != nil { + return &et, err + } + et.Sigma = sigma + + epochIDPoint := shcrypto.ComputeEpochID(epochID.Bytes()) + + encryptedMessage := shcrypto.Encrypt( + et.Message, + keygen.EonPublicKey(epochID), + epochIDPoint, + sigma, + ) + + et.Expected = encryptedMessage + + if et.Expected.C1 == nil { + return &et, errors.New("failed to marshal") + } + + err = et.Run() + return &et, err +} + +// tamperEncryptedMessage changes the C1 value of EncryptedMessage, which allows to test for malleability issues. +func tamperEncryptedMessage(keygen *testkeygen.KeyGenerator, et encryptionTest) encryptionTest { + decryptionKey := keygen.EpochSecretKey(et.EpochID) + var c1 *bn256.G2 + var err error + + for i := 1; i <= 10000; i++ { + c1 = et.Expected.C1 + c1.Add(c1, c1) + et.Expected.C1 = c1 + sigma := et.Expected.Sigma(decryptionKey) + decryptedBlocks := shcrypto.DecryptBlocks(et.Expected.C3, sigma) + _, err = shcrypto.UnpadMessage(decryptedBlocks) + + if err == nil { + break + } + } + return et +} + +func createDecryptionTest(keygen *testkeygen.KeyGenerator, et encryptionTest) decryptionTest { + dt := decryptionTest{} + epochSecretKey := keygen.EpochSecretKey(et.EpochID) + dt.EpochSecretKey = *epochSecretKey + + dt.Cipher = *et.Expected + + dt.Expected = et.Message + + return dt +} + +func createVerificationTest(keygen *testkeygen.KeyGenerator, payload []byte) (verificationTest, error) { + var err error + vt := verificationTest{} + epochID := keygen.RandomEpochID(payload) + vt.EpochID = epochID + vt.EpochSecretKey = *keygen.EpochSecretKey(epochID) + vt.EonPublicKey = *keygen.EonPublicKey(epochID) + vt.Expected, err = shcrypto.VerifyEpochSecretKey( + &vt.EpochSecretKey, + &vt.EonPublicKey, + epochID.Bytes(), + ) + return vt, err +} + +func createFailedVerificationTest(keygen *testkeygen.KeyGenerator, _ []byte) (verificationTest, error) { + var err error + vt := verificationTest{} + epochID := keygen.RandomEpochID(make([]byte, 32)) + mismatch := keygen.RandomEpochID(make([]byte, 32)) + vt.EpochID = epochID + vt.EpochSecretKey = *keygen.EpochSecretKey(epochID) + vt.EonPublicKey = *keygen.EonPublicKey(mismatch) + vt.Expected, err = shcrypto.VerifyEpochSecretKey( + &vt.EpochSecretKey, + &vt.EonPublicKey, + epochID.Bytes(), + ) + return vt, err +} + +func (et *encryptionTest) Run() error { + result := shcrypto.Encrypt( + et.Message, + et.EonPublicKey, + shcrypto.ComputeEpochID(et.EpochID.Bytes()), + et.Sigma, + ) + + encoded, err := result.MarshalText() + if err != nil { + return fmt.Errorf("failed encryption test on encoding result: %s", err) + } + expectation, err := et.Expected.MarshalText() + if err != nil { + return fmt.Errorf("failed encryption test on encoding expected: %s", err) + } + if !bytes.Equal(encoded, expectation) { + return fmt.Errorf("failed encryption test on equal results: %s != %s", encoded, et.Expected) + } + return nil +} + +func (dt *decryptionTest) Run() error { + expectation, err := dt.Expected.MarshalText() + if err != nil { + return err + } + + result, err := dt.Cipher.Decrypt( + &dt.EpochSecretKey, + ) + if err != nil { + if !bytes.Equal(expectation, []byte("0x")) { + return fmt.Errorf("failed decryption test with error: %s", err) + } + return nil + } + encoded := hexutil.Encode(result) + + if !bytes.Equal(result, dt.Expected) { + return fmt.Errorf("failed decryption test on equal results: %s != %s", encoded, expectation) + } + return nil +} + +func (vt *verificationTest) Run() error { + result, err := shcrypto.VerifyEpochSecretKey( + &vt.EpochSecretKey, + &vt.EonPublicKey, + vt.EpochID[:], + ) + if err != nil { + return fmt.Errorf("failed verification test with error: %s", err) + } + if result != vt.Expected { + return fmt.Errorf("failed verification test on expected result: expected (%t) != result (%t)", vt.Expected, result) + } + return nil +} + +func testMarshalingRoundtrip(tc *testCase) error { + marshaled, err := json.Marshal(tc) + if err != nil { + return err + } + var unmarshaled testCase + err = json.Unmarshal(marshaled, &unmarshaled) + if err != nil { + return err + } + + roundtrip, err := json.Marshal(&unmarshaled) + if err != nil { + return err + } + if !bytes.Equal(marshaled, roundtrip) { + println(len(marshaled)) + println(len(roundtrip)) + println("before:", string(marshaled)) + println("after:", string(roundtrip)) + return errors.New("roundtrip marshaling failed") + } + return nil +} diff --git a/rolling-shutter/docs/rolling-shutter_crypto.md b/rolling-shutter/docs/rolling-shutter_crypto.md index 4f5eed852..ee4f7a4db 100644 --- a/rolling-shutter/docs/rolling-shutter_crypto.md +++ b/rolling-shutter/docs/rolling-shutter_crypto.md @@ -5,7 +5,8 @@ CLI tool to access crypto functions ### Synopsis This command provides utility functions to manually encrypt messages with an eon -key, decrypt them with a decryption key, and check that a decryption key is correct. +key, decrypt them with a decryption key, and check that a decryption key is correct. It also hosts +a tool to generate and run crypto tests in a JSON formatted collection. ### Options @@ -27,5 +28,7 @@ key, decrypt them with a decryption key, and check that a decryption key is corr * [rolling-shutter crypto aggregate](rolling-shutter_crypto_aggregate.md) - Aggregate key shares to construct a decryption key * [rolling-shutter crypto decrypt](rolling-shutter_crypto_decrypt.md) - Decrypt the message given as positional argument * [rolling-shutter crypto encrypt](rolling-shutter_crypto_encrypt.md) - Encrypt the message given as positional argument +* [rolling-shutter crypto jsontests](rolling-shutter_crypto_jsontests.md) - Use testdata in json format to test crypto implementations +* [rolling-shutter crypto testdata](rolling-shutter_crypto_testdata.md) - Generate testdata in json format to test crypto implementations * [rolling-shutter crypto verify-key](rolling-shutter_crypto_verify-key.md) - Check that the decryption key given as positional argument is correct diff --git a/rolling-shutter/docs/rolling-shutter_crypto_jsontests.md b/rolling-shutter/docs/rolling-shutter_crypto_jsontests.md new file mode 100644 index 000000000..8868ced0d --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_crypto_jsontests.md @@ -0,0 +1,27 @@ +## rolling-shutter crypto jsontests + +Use testdata in json format to test crypto implementations + +``` +rolling-shutter crypto jsontests [flags] +``` + +### Options + +``` + -f, --filename string filename to write result + -h, --help help for jsontests +``` + +### Options inherited from parent commands + +``` + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter crypto](rolling-shutter_crypto.md) - CLI tool to access crypto functions + diff --git a/rolling-shutter/docs/rolling-shutter_crypto_testdata.md b/rolling-shutter/docs/rolling-shutter_crypto_testdata.md new file mode 100644 index 000000000..184001b83 --- /dev/null +++ b/rolling-shutter/docs/rolling-shutter_crypto_testdata.md @@ -0,0 +1,27 @@ +## rolling-shutter crypto testdata + +Generate testdata in json format to test crypto implementations + +``` +rolling-shutter crypto testdata [flags] +``` + +### Options + +``` + -f, --filename string filename to write result + -h, --help help for testdata +``` + +### Options inherited from parent commands + +``` + --logformat string set log format, possible values: min, short, long, max (default "long") + --loglevel string set log level, possible values: warn, info, debug (default "info") + --no-color do not write colored logs +``` + +### SEE ALSO + +* [rolling-shutter crypto](rolling-shutter_crypto.md) - CLI tool to access crypto functions + diff --git a/rolling-shutter/medley/identitypreimage/identitypreimage.go b/rolling-shutter/medley/identitypreimage/identitypreimage.go index 40e411302..5b1e34ecd 100644 --- a/rolling-shutter/medley/identitypreimage/identitypreimage.go +++ b/rolling-shutter/medley/identitypreimage/identitypreimage.go @@ -50,3 +50,13 @@ func (e IdentityPreimage) String() string { func Equal(a, b IdentityPreimage) bool { return bytes.Equal(a.Bytes(), b.Bytes()) } + +func (e IdentityPreimage) MarshalText() ([]byte, error) { //nolint:unparam + return []byte(e.Hex()), nil +} + +func (e *IdentityPreimage) UnmarshalText(input []byte) error { + val, err := HexToIdentityPreimage(string(input)) + *e = val.Bytes() + return err +} diff --git a/rolling-shutter/medley/testkeygen/testgenerator.go b/rolling-shutter/medley/testkeygen/testgenerator.go new file mode 100644 index 000000000..4e127386c --- /dev/null +++ b/rolling-shutter/medley/testkeygen/testgenerator.go @@ -0,0 +1,93 @@ +package testkeygen + +import ( + "crypto/rand" + + "github.com/shutter-network/shutter/shlib/shcrypto" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/identitypreimage" +) + +// KeyGenerator is a helper tool to generate secret and public eon and epoch keys and key +// shares. It will generate a new eon key every eonInterval epochs. +type KeyGenerator struct { + eonInterval uint64 + eonKeyGen map[uint64]*EonKeys + NumKeypers uint64 + Threshold uint64 +} + +func NewKeyGenerator(numKeypers uint64, threshold uint64) *KeyGenerator { + return &KeyGenerator{ + eonInterval: 100, // 0 stands for infinity + eonKeyGen: make(map[uint64]*EonKeys), + NumKeypers: numKeypers, + Threshold: threshold, + } +} + +// getEonIndex computes the index of the EON key to be used for the given epochID. We generate a new +// eon key every eonInterval epochs. +func (kg *KeyGenerator) getEonIndex(epochID identitypreimage.IdentityPreimage) uint64 { + if kg.eonInterval == 0 { + return 0 + } + + return epochID.Big().Uint64() / kg.eonInterval +} + +func (kg *KeyGenerator) EonKeysForEpoch(epochID identitypreimage.IdentityPreimage) *EonKeys { + eonIndex := kg.getEonIndex(epochID) + res, ok := kg.eonKeyGen[eonIndex] + var err error + if !ok { + res, err = NewEonKeys( + rand.Reader, + kg.NumKeypers, + kg.Threshold, + ) + if err != nil { + return nil + } + kg.eonKeyGen[eonIndex] = res + } + return res +} + +func (kg *KeyGenerator) EonPublicKeyShare(epochID identitypreimage.IdentityPreimage, keyperIndex uint64) *shcrypto.EonPublicKeyShare { + return kg.EonKeysForEpoch(epochID).keyperShares[keyperIndex].eonPublicKeyShare +} + +func (kg *KeyGenerator) EonPublicKey(epochID identitypreimage.IdentityPreimage) *shcrypto.EonPublicKey { + return kg.EonKeysForEpoch(epochID).publicKey +} + +func (kg *KeyGenerator) EonSecretKeyShare(epochID identitypreimage.IdentityPreimage, keyperIndex uint64) *shcrypto.EonSecretKeyShare { + return kg.EonKeysForEpoch(epochID).keyperShares[keyperIndex].eonSecretKeyShare +} + +func (kg *KeyGenerator) EpochSecretKeyShare(epochID identitypreimage.IdentityPreimage, keyperIndex uint64) *shcrypto.EpochSecretKeyShare { + return kg.EonKeysForEpoch(epochID).keyperShares[keyperIndex].ComputeEpochSecretKeyShare(epochID) +} + +func (kg *KeyGenerator) EpochSecretKey(epochID identitypreimage.IdentityPreimage) *shcrypto.EpochSecretKey { + epochSecretKey, err := kg.EonKeysForEpoch(epochID).EpochSecretKey(epochID) + if err != nil { + panic(err) + } + return epochSecretKey +} + +func (kg *KeyGenerator) RandomEpochID(epochbytes []byte) identitypreimage.IdentityPreimage { + _, err := rand.Read(epochbytes) + if err != nil { + panic(err) + } + + epochID := identitypreimage.IdentityPreimage(epochbytes) + return epochID +} + +func (kg *KeyGenerator) RandomSigma() (shcrypto.Block, error) { + return shcrypto.RandomSigma(rand.Reader) +}