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

oss-fuzz based fuzzers #1

Open
wants to merge 4 commits into
base: draft-irtf-cfrg-vrf-03
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ test/default/stream2
test/default/stream3
test/default/stream4
test/default/verify1
test/default/vrf
test/default/xchacha20
test/js.done
testing
23 changes: 23 additions & 0 deletions fuzz/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2019 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################

FROM gcr.io/oss-fuzz-base/base-builder
MAINTAINER [email protected]
RUN apt-get update && apt-get install -y make autoconf automake libtool
RUN git clone --depth 1 https://github.com/algorand/libsodium vrf
WORKDIR vrf
COPY build.sh $SRC/
COPY *.cc $SRC/
41 changes: 41 additions & 0 deletions fuzz/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Building

To build this you can do the following in the current directory:

* docker build -t vrf-fuzzer-image .
* docker run --rm -i --privileged -e FUZZING_ENGINE=libfuzzer -e SANITIZER=address -t vrf-fuzzer-image /bin/bash

Once you're in the shell you can simply type "compile" and it will build the fuzzers
and write them into /out.

These fuzzers are statically linked so you can copy the binary wherever or just run
them with /out/vrf_fuzzer or /out/roundtrip_fuzzer

If you specify a directory (e.g. /out/vrf_fuzzer ~/corpus) it will write unique
inputs that result in improved coverage to the directory. You can also specify a
directory with files already in it to bootstrap the initial set of known paths to
improve the speed with which it finds new paths.

The round trip fuzzer is really just abusing the fuzzer permutation engine to get
quickcheck style property testing, while the vrf fuzzer actually feeds bad data
into the various functions, so the vrf fuzzer is going to be more useful.

# Alternate Modes

If you'd like to run the fuzzer under an alternate sanitizer (e.g. MSAN or UBSAN)
simply alter the SANITIZER value:

docker run --rm -i --privileged -e FUZZING_ENGINE=libfuzzer -e SANITIZER=memory -t vrf-fuzzer-image /bin/bash
docker run --rm -i --privileged -e FUZZING_ENGINE=libfuzzer -e SANITIZER=undefined -t vrf-fuzzer-image /bin/bash

This image also supports alternate fuzzing engines, but I recommend sticking with
libfuzzer for now.

# Upstreaming

Once the VRF code makes it into libsodium proper you can upstream this work by
submitting the vrf_fuzzer.cc (and the roundtrip_fuzzer.cc if you want that sort
of testing) to oss-fuzz as a PR adding them to the current libsodium project dir
(https://github.com/google/oss-fuzz/tree/master/projects/libsodium). The existing
code already searches for every file named *_fuzzer.cc so it will automatically
build the new fuzzers and Google's infrastructure will run it all.
25 changes: 25 additions & 0 deletions fuzz/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash -eu
# Copyright 2019 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################

./autogen.sh
./configure --enable-static LDFLAGS="$CXXFLAGS"
make -j$(nproc) all

for f in $SRC/*_fuzzer.cc; do
fuzzer=$(basename "$f" _fuzzer.cc)
$CXX $CXXFLAGS -std=c++11 -I"$SRC/vrf/src/libsodium/include" "$f" -o "$OUT/${fuzzer}_fuzzer" "$SRC/vrf/src/libsodium/.libs/libsodium.a" -lFuzzingEngine
done
41 changes: 41 additions & 0 deletions fuzz/roundtrip_fuzzer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <string.h>
#include <sodium/crypto_vrf.h>
#include <sodium.h>
#include <assert.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
int ret;

ret = sodium_init();
assert(ret >= 0);
unsigned char pk[crypto_vrf_PUBLICKEYBYTES];
unsigned char sk[crypto_vrf_SECRETKEYBYTES];
unsigned char seed[crypto_vrf_SEEDBYTES] = {};
// copy as many bytes into the seed as we can
int seedsize = crypto_vrf_SEEDBYTES;
if (size < crypto_vrf_SEEDBYTES) {
seedsize = size;
}
memcpy(seed, data, seedsize);
crypto_vrf_keypair_from_seed(pk, sk, seed);
ret = crypto_vrf_is_valid_key(pk);
if (ret != 0) {
// invalid key.
return 0;
}

unsigned char proof[crypto_vrf_PROOFBYTES];
ret = crypto_vrf_prove(proof, sk, data, size);
assert(ret == 0);

unsigned char output[crypto_vrf_OUTPUTBYTES];
ret = crypto_vrf_proof_to_hash(output, proof);
assert(ret == 0);

unsigned char voutput[crypto_vrf_OUTPUTBYTES];
ret = crypto_vrf_verify(voutput, pk, proof, data, size);
assert(ret == 0);

assert(memcmp(voutput, output, crypto_vrf_OUTPUTBYTES) == 0);
return 0;
}
37 changes: 37 additions & 0 deletions fuzz/vrf_fuzzer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include <string.h>
#include <sodium/crypto_vrf.h>
#include <sodium.h>
#include <stdio.h>
#include <assert.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
int ret;

ret = sodium_init();
assert(ret >= 0);
unsigned char pk[crypto_vrf_PUBLICKEYBYTES];
unsigned char sk[crypto_vrf_SECRETKEYBYTES];
unsigned char seed[crypto_vrf_SEEDBYTES] = {};
// copy as many bytes into the seed as we can
int seedsize = crypto_vrf_SEEDBYTES;
if (size < crypto_vrf_SEEDBYTES) {
seedsize = size;
}
memcpy(seed, data, seedsize);
crypto_vrf_keypair_from_seed(pk, sk, seed);
ret = crypto_vrf_is_valid_key(pk);

unsigned char proof[crypto_vrf_PROOFBYTES];
int proofsize = crypto_vrf_PROOFBYTES;
if (size < crypto_vrf_PROOFBYTES) {
proofsize = size;
}
memcpy(proof, data, proofsize);

unsigned char output[crypto_vrf_OUTPUTBYTES];
ret = crypto_vrf_proof_to_hash(output, proof);

unsigned char voutput[crypto_vrf_OUTPUTBYTES];
ret = crypto_vrf_verify(voutput, pk, proof, data, size);
return 0;
}
7 changes: 7 additions & 0 deletions src/libsodium/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ libsodium_la_SOURCES = \
crypto_stream/salsa20/stream_salsa20.h \
crypto_stream/xsalsa20/stream_xsalsa20.c \
crypto_verify/sodium/verify.c \
crypto_vrf/crypto_vrf.c \
crypto_vrf/ietfdraft03/keypair.c \
crypto_vrf/ietfdraft03/convert.c \
crypto_vrf/ietfdraft03/prove.c \
crypto_vrf/ietfdraft03/verify.c \
crypto_vrf/ietfdraft03/vrf.c \
crypto_vrf/ietfdraft03/vrf_ietfdraft03.h \
include/sodium/private/chacha20_ietf_ext.h \
include/sodium/private/common.h \
include/sodium/private/ed25519_ref10.h \
Expand Down
90 changes: 90 additions & 0 deletions src/libsodium/crypto_vrf/crypto_vrf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@

#include "crypto_vrf.h"

size_t
crypto_vrf_publickeybytes(void)
{
return crypto_vrf_PUBLICKEYBYTES;
}

size_t
crypto_vrf_secretkeybytes(void)
{
return crypto_vrf_SECRETKEYBYTES;
}

size_t
crypto_vrf_seedbytes(void)
{
return crypto_vrf_SEEDBYTES;
}

size_t
crypto_vrf_proofbytes(void)
{
return crypto_vrf_PROOFBYTES;
}

size_t
crypto_vrf_outputbytes(void)
{
return crypto_vrf_OUTPUTBYTES;
}

const char *
crypto_vrf_primitive(void)
{
return crypto_vrf_PRIMITIVE;
}

int
crypto_vrf_keypair(unsigned char *pk, unsigned char *sk)
{
return crypto_vrf_ietfdraft03_keypair(pk, sk);
}

int
crypto_vrf_keypair_from_seed(unsigned char *pk, unsigned char *sk,
const unsigned char *seed)
{
return crypto_vrf_ietfdraft03_keypair_from_seed(pk, sk, seed);
}

int
crypto_vrf_is_valid_key(const unsigned char *pk)
{
return crypto_vrf_ietfdraft03_is_valid_key(pk);
}

int
crypto_vrf_prove(unsigned char *proof, const unsigned char *skpk,
const unsigned char *m, const unsigned long long mlen)
{
return crypto_vrf_ietfdraft03_prove(proof, skpk, m, mlen);
}

int
crypto_vrf_verify(unsigned char *output, const unsigned char *pk,
const unsigned char *proof, const unsigned char *m,
const unsigned long long mlen)
{
return crypto_vrf_ietfdraft03_verify(output, pk, proof, m, mlen);
}

int
crypto_vrf_proof_to_hash(unsigned char *hash, const unsigned char *proof)
{
return crypto_vrf_ietfdraft03_proof_to_hash(hash, proof);
}

void
crypto_vrf_sk_to_pk(unsigned char *pk, const unsigned char *skpk)
{
crypto_vrf_ietfdraft03_sk_to_pk(pk, skpk);
}

void
crypto_vrf_sk_to_seed(unsigned char *seed, const unsigned char *skpk)
{
crypto_vrf_ietfdraft03_sk_to_seed(seed, skpk);
}
14 changes: 14 additions & 0 deletions src/libsodium/crypto_vrf/ietfdraft03/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
This is an implementation of an elliptic curve-based verifiable random function construction designed by Goldberg et al. and specified in an IETF internet draft, draft-irtf-cfrg-vrf-03 (https://www.ietf.org/id/draft-irtf-cfrg-vrf-03.txt). The specification is still a draft and may change before it becomes standardized. In particular, this implements the ECVRF-ED25519-SHA512-Elligator2 suite.
The code is structured to closely follow the pseudocode given in the spec, with a few notable exceptions described below, and we point to the relevant section of the draft spec in comments before each function.

For readability, the code is separated into a few files: keygen.c (key generation), prove.c (constructing vrf proofs), verify.c (verifying VRF proofs and turning them into output hashes), convert.c (helper functions used in prove and verify), and vrf.c (boilerplate). vrf_ietfdraft03.h is used internally so that prove.c and verify.c can use the helper functions defined in convert.c.

keygen.c contains key generation functions. In this VRF scheme, key generation is identical to standard ed25519 key generation from RFC8032, so these functions are essentially copied directly from libsodium/crypto_sign/ed25519/ref10/keypair.c.
In libsodium's ed25519 signature implementation, the "secret key" returned by keygen is not the 32-byte string RFC8032 calls the secret key -- libsodium calls that 32-byte string the "seed" -- but is instead the seed with the public key appended. This precomputation saves a scalar multiplication during signing. We do this same optimization in the VRF implementation, saving a scalar multiplication during proving, and we use the same terminology (32-byte seed / 64-byte secret key). keygen.c defines functions for converting back and forth between 32-byte seeds and 64-byte secret keys. Our terminology here differs from the VRF draft spec, where "secret key" refers to the 32-byte seed.

convert.c has some internal utility functions: point_to_string, string_to_point, hash_to_curve_elligator2_25519, hash_points, and decode_proof. These correspond to the similarly-named subroutines in the VRF draft spec.

verify.c contains verification-related code: verify, proof_to_hash, validate_key, and helper functions. Verification runtime depends only on the message length (assuming verification succeeds).

prove.c contains the prove function. As mentioned above, to save a scalar multiplication, prove() takes in a 64-byte secret key where the first half is the secret seed and the second half is the public key. If the second half doesn't encode a valid curve point, prove() will fail early; otherwise prove's runtime depends only on the length of the message. (If the second half of the secret key encodes a valid curve point that but not the correct public key, the proof returned may fail to verify.)
prove.c also contains the helper function vrf_nonce_generation (specificed in section 5.4.2.2. of the draft spec). In the draft spec, the nonce generation function takes in the seed and computes a truncated hash of it; we instead compute this same truncated hash earlier in a helper function (vrf_expand_sk, which already hashes the seed to derive x) and pass that to vrf_nonce_generation directly, giving the same result.
Loading