Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an example fuzzing test #53

Merged
merged 1 commit into from
May 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,41 @@ To keep track of upstream AMCL:
- Test
- Commit

### Executing the test suite

We recomment working within the nimbus build environment described here:
https://github.com/status-im/nim-beacon-chain/

To execute the test suite, just navigate to the root of this repo and execute:

```
nimble test
```

> Please note that within the nimbus build environment, the repository will
be located in `nim-beacon-chain/vendor/nim-blscurve`.

### Executing the fuzzing tests

Before you start, please make sure that the regular test suite executes
successfully (see the instructions above). To start a particular fuzzing
test, navigate to the root of this repo and execute:

```
nim tests/fuzzing/run_fuzzing_test.nims <test-name>
```

You can specify the fuzzing engine being used by passing an additional
`--fuzzer` parameter. The currently supported engines are `libFuzzer`
(used by default) and `afl`.

All fuzzing tests are located in `tests/fuzzing` and use the following
naming convention:

```
fuzz_<test-name>.nim
```

## License

Licensed and distributed under either of
Expand Down
38 changes: 10 additions & 28 deletions tests/eth2_vectors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,29 @@

import
# Standard library
json, strutils, os, streams, unittest,
# Third party
yaml,
json, strutils, os, unittest,
# Status libraries
stew/byteutils,
# Public API
../blscurve
../blscurve,
# Test helpers
./test_locator

const ETH2_DIR = currentSourcePath.rsplit(DirSep, 1)[0] / "eth2.0_v0.10.1_vectors"

proc parseTest(file: string): JsonNode =
var yamlStream = openFileStream(file)
defer: yamlStream.close()
result = yamlStream.loadToJson()[0]

const SkippedTests = [
"small"/"fast_aggregate_verify_e6922a0d196d9869"/"data.yaml", # Buggy upstream vector: https://github.com/ethereum/eth2.0-specs/issues/1618
"small"/"fast_aggregate_verify_62bca7cd61880e26"/"data.yaml",
"small"/"fast_aggregate_verify_3b2b0141e95125f0"/"data.yaml",
]
type InOut = enum
Input
Output

template testGen(name, testJson, body: untyped): untyped =
template testGen*(name, testJson, body: untyped): untyped =
## Generates a test proc
## with identifier "test_name"
## The test file is availaible as JsonNode under the
## the variable passed as `testJson`
proc `test _ name`() =
var count = 0 # Need to fail if walkDir doesn't return anything
var skipped = 0
const testDir = ETH2_DIR / astToStr(name)
for file in walkDirRec(testDir, relative = true):
if file in SkippedTests:
echo "[WARNING] Skipping - ", file
inc skipped
continue
for dir, file in walkTests(astToStr(name), skipped):
echo " ", astToStr(name), " test: ", file
let testJson = parseTest(testDir / file)
let testJson = parseTest(dir / file)

body

Expand All @@ -58,10 +44,6 @@ template testGen(name, testJson, body: untyped): untyped =
if skipped > 0:
echo "[Warning]: ", skipped, " tests skipped."

type InOut = enum
Input
Output

proc getFrom(T: typedesc, test: JsonNode, inout: static InOut): T =
when inout == Output:
when T is bool:
Expand Down
34 changes: 34 additions & 0 deletions tests/fuzzing/collect_corpus.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import
stew/byteutils,
../test_locator,
fuzzing_assumptions

var skipped = 0

let corpusDir = getAppDir() / "corpus"

removeDir corpusDir

template getInputBytes(test: JsonNode, fieldName: string): seq[byte] =
test["input"][fieldName].getStr.hexToSeqByte

var inputIdx = 0
template nextInput: string =
inc inputIdx
"input" & $inputIdx

let verifyCorpusDir = corpusDir / "verify"
createDir verifyCorpusDir

for dir, test in walkTests("verify", skipped):
let t = parseTest(dir / test)
let
message = t.getInputBytes "message"
pubKey = t.getInputBytes "pubkey"
signature = t.getInputBytes "signature"

doAssert pubKey.len == fuzzing_assumptions.pubkeyLen and
signature.len == fuzzing_assumptions.signatureLen

writeFile(verifyCorpusDir / nextInput(), message & pubkey & signature)

23 changes: 23 additions & 0 deletions tests/fuzzing/fuzz_verify.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import
testutils/fuzzing, stew/byteutils,
../../blscurve, fuzzing_assumptions

test:
block:
if payload.len < pubkeyLen + signatureLen:
break

let
signatureStart = payload.len - signatureLen
pubkeyStart = signatureStart - pubkeyLen

var sig: Signature
if not sig.fromBytes(payload[signatureStart ..< (signatureStart + signatureLen)]):
break

var pubKey: PublicKey
if not pubKey.fromBytes(payload[pubkeyStart ..< (pubkeyStart + pubkeyLen)]):
break

discard pubKey.verify(payload[0 ..< pubkeyStart], sig)

4 changes: 4 additions & 0 deletions tests/fuzzing/fuzzing_assumptions.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const
pubkeyLen* = 48
signatureLen* = 96

22 changes: 22 additions & 0 deletions tests/fuzzing/run_fuzzing_test.nims
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import strformat
import os except paramCount, paramStr, fileExists # these are also defined in the system module
import confutils, testutils/fuzzing_engines

cli do (testName {.argument.}: string,
fuzzer = libFuzzer):
let
fuzzingDir = thisDir()
fuzzingFile = fuzzingDir / "fuzz_" & addFileExt(testName, "nim")
corpusDir = fuzzingDir / "corpus" / testName

if not fileExists(fuzzingFile):
echo testName, " is not a recognized fuzzing test"
quit 1

let
collectCorpusNim = fuzzingDir / "collect_corpus.nim"
fuzzNims = fuzzingDir / ".." / ".." / ".." / "nim-testutils" / "testutils" / "fuzzing" / "fuzz.nims"

exec &"""nim c -r "{collectCorpusNim}""""
exec &"""nim "{fuzzNims}" {fuzzer} "{fuzzingFile}" "{corpusDir}" """

33 changes: 33 additions & 0 deletions tests/test_locator.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import
# Standard library
json, strutils, os, streams,
# Third party
yaml

export
os, json

const ETH2_DIR = currentSourcePath.rsplit(DirSep, 1)[0] / "eth2.0_v0.10.1_vectors"

proc parseTest*(file: string): JsonNode =
var yamlStream = openFileStream(file)
defer: yamlStream.close()
result = yamlStream.loadToJson()[0]

const SkippedTests = [
"small"/"fast_aggregate_verify_e6922a0d196d9869"/"data.yaml", # Buggy upstream vector: https://github.com/ethereum/eth2.0-specs/issues/1618
"small"/"fast_aggregate_verify_62bca7cd61880e26"/"data.yaml",
"small"/"fast_aggregate_verify_3b2b0141e95125f0"/"data.yaml",
]

iterator walkTests*(category: string, skipped: var int): (string, string) =
let testDir = ETH2_DIR / category

for file in walkDirRec(testDir, relative = true):
if file in SkippedTests:
echo "[WARNING] Skipping - ", file
inc skipped
continue

yield (testDir, file)