diff --git a/did/key_fuzz_test.go b/did/key_fuzz_test.go new file mode 100644 index 00000000..02713493 --- /dev/null +++ b/did/key_fuzz_test.go @@ -0,0 +1,56 @@ +package did + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var mockPubKeys = []string{ + "b9c5714089478a327f09197987f16f9e5d936e8a", "5f246d7d19aa612d6718d27c1da1ee66859586b0", "7d2d43e63666f45b40316b44212325625dbaeb40", "1c1f02f1640e52b313f2d504b3c0c7ee8ad61108", "69c5888ecd21287fbdac5a43d1558bf73c51e38b", +} + +func FuzzCreateAndDecode(f *testing.F) { + keytypes := GetSupportedDIDKeyTypes() + ktLen := len(keytypes) + + for i, pk := range mockPubKeys { + f.Add(i, []byte(pk)) + } + f.Fuzz(func(t *testing.T, ktSeed int, pubKey []byte) { + kt := keytypes[(ktSeed%ktLen+ktLen)%ktLen] + + didKey, err := CreateDIDKey(kt, pubKey) + assert.NoError(t, err) + + recvPubKey, _, _, err := didKey.Decode() + assert.NoError(t, err) + assert.Equal(t, pubKey, recvPubKey) + }) +} + +func FuzzCreateAndResolve(f *testing.F) { + keytypes := GetSupportedDIDKeyTypes() + ktLen := len(keytypes) + + resolvers := []Resolution{KeyResolver{}, WebResolver{}, PKHResolver{}, PeerResolver{}} + resolver, _ := NewResolver(resolvers...) + + for i, pk := range mockPubKeys { + f.Add(i, []byte(pk)) + } + + f.Fuzz(func(t *testing.T, ktSeed int, pubKey []byte) { + kt := keytypes[(ktSeed%ktLen+ktLen)%ktLen] + + didKey, err := CreateDIDKey(kt, pubKey) + assert.NoError(t, err) + + doc, err := resolver.Resolve(didKey.String()) + if err != nil { + t.Skip() + } + assert.NotEmpty(t, doc) + assert.Equal(t, didKey.String(), doc.DIDDocument.ID) + }) +} diff --git a/did/key_test.go b/did/key_test.go index caf24fd4..95855a4c 100644 --- a/did/key_test.go +++ b/did/key_test.go @@ -196,6 +196,36 @@ func TestExpand(t *testing.T) { }) } +func TestGenerateAndDecode(t *testing.T) { + for _, kt := range GetSupportedDIDKeyTypes() { + privKey, didKey, err := GenerateDIDKey(kt) + assert.NotEmpty(t, privKey) + + expectedLLKeyType, _ := KeyTypeToLDKeyType(kt) + + pubKey, ldKeyType, cryptoKeyType, err := didKey.Decode() + assert.NoError(t, err) + assert.NotEmpty(t, pubKey) + assert.Equal(t, ldKeyType, expectedLLKeyType) + assert.Equal(t, cryptoKeyType, kt) + } +} + +func TestGenerateAndResolve(t *testing.T) { + resolvers := []Resolution{KeyResolver{}, WebResolver{}, PKHResolver{}, PeerResolver{}} + resolver, _ := NewResolver(resolvers...) + + for _, kt := range GetSupportedDIDKeyTypes() { + _, didKey, err := GenerateDIDKey(kt) + assert.NoError(t, err) + + doc, err := resolver.Resolve(didKey.String()) + assert.NoError(t, err) + assert.NotEmpty(t, doc) + assert.Equal(t, didKey.String(), doc.DIDDocument.ID) + } +} + func TestDIDKeySignVerify(t *testing.T) { t.Run("Test Ed25519 did:key", func(t *testing.T) { privKey, didKey, err := GenerateDIDKey(crypto.Ed25519) diff --git a/magefile.go b/magefile.go index 337ef14c..a50d94bd 100644 --- a/magefile.go +++ b/magefile.go @@ -5,6 +5,7 @@ package main import ( "fmt" "io" + "io/ioutil" "log" "os" "os/exec" @@ -43,6 +44,10 @@ func Test() error { return runTests() } +func Fuzz() error { + return runFuzzTests() +} + func runTests(extraTestArgs ...string) error { args := []string{"test"} if mg.Verbose() { @@ -61,6 +66,92 @@ func runTests(extraTestArgs ...string) error { return err } +func runFuzzTests(extraTestArgs ...string) error { + dirs := []string{"./did"} + + for _, dir := range dirs { + functionNames, _ := getFuzzTests(dir) + + for _, testName := range functionNames { + args := []string{"test"} + if mg.Verbose() { + args = append(args, "-v") + } + args = append(args, dir) + args = append(args, fmt.Sprintf("-run=%s", testName)) + args = append(args, fmt.Sprintf("-fuzz=%s", testName)) + args = append(args, "-fuzztime=10s") + testEnv := map[string]string{ + "CGO_ENABLED": "1", + "GO111MODULE": "on", + } + writer := ColorizeTestStdout() + fmt.Printf("%+v\n", args) + _, err := sh.Exec(testEnv, writer, os.Stderr, Go, args...) + if err != nil { + return err + } + } + + } + return nil +} + +var fuzzFuncNameRegex = regexp.MustCompile(`\bFuzz\w+`) + +// Searches the given directory recursively for Go files containing the word "Fuzz" in their function names. +// Returns the names of the functions that match the search criteria. +func getFuzzTests(dir string) ([]string, error) { + var results []string + + // Read the directory contents. + files, err := ioutil.ReadDir(dir) + if err != nil { + return nil, err + } + + // Loop over the files in the directory. + for _, file := range files { + // Get the full path to the file. + filePath := filepath.Join(dir, file.Name()) + + // If the file is a directory, search it recursively. + if file.IsDir() { + subResults, err := getFuzzTests(filePath) + if err != nil { + return nil, err + } + results = append(results, subResults...) + continue + } + + // If the file is not a Go file, skip it. + if filepath.Ext(filePath) != ".go" { + continue + } + + // Open the file. + f, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer f.Close() + + // Read the file contents. + data, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + + // Search the file contents for function names containing the word "Fuzz". + matches := fuzzFuncNameRegex.FindAllString(string(data), -1) + + results = append(results, matches...) + } + + return results, nil +} + func Deps() error { return brewInstall("golangci-lint") }