diff --git a/proto/asn1-pdu/.gitignore b/proto/asn1-pdu/.gitignore new file mode 100644 index 0000000..2c439eb --- /dev/null +++ b/proto/asn1-pdu/.gitignore @@ -0,0 +1,7 @@ +*.py +*.pyc +__pycache__ + +*.pb.h +*.pb.cc +*.o diff --git a/proto/asn1-pdu/Makefile b/proto/asn1-pdu/Makefile new file mode 100644 index 0000000..e531e9f --- /dev/null +++ b/proto/asn1-pdu/Makefile @@ -0,0 +1,14 @@ +LPM_DIR=../../../libprotobuf-mutator +PROTOBUF_DIR=$(LPM_DIR)/build/external.protobuf +FLAGS=-O2 -Wall -Wextra -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fstack-clash-protection -fPIE -fPIC -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -Wl,-z,separate-code + +all: proto cpp + +proto: *.proto + protoc *.proto --python_out=. --cpp_out=. + +cpp: *.h *.cc + $(CXX) -c $(FLAGS) *.cc -I$(LPM_DIR) -I$(PROTOBUF_DIR)/include + +clean: + $(RM) *.o diff --git a/proto/tests/.gitignore b/proto/tests/.gitignore new file mode 100644 index 0000000..f167ab7 --- /dev/null +++ b/proto/tests/.gitignore @@ -0,0 +1,5 @@ +*.py +*.pyc +*.pem +*.der +*.proto diff --git a/proto/tests/Makefile b/proto/tests/Makefile new file mode 100644 index 0000000..4a65059 --- /dev/null +++ b/proto/tests/Makefile @@ -0,0 +1,16 @@ +LPM_DIR=../../../libprotobuf-mutator +PROTOBUF_DIR=$(LPM_DIR)/build/external.protobuf +ASN1_PROTOBUF_DIR=../asn1-pdu +FLAGS=-O2 -Wall -Wextra -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fstack-clash-protection -fPIE -fPIC -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -Wl,-z,separate-code + +all: protobuf tests + +protobuf: + $(MAKE) -C $(ASN1_PROTOBUF_DIR) all + +tests: protobuf_to_der.cpp + $(CXX) $(FLAGS) -o protobuf_to_der.o protobuf_to_der.cpp $(ASN1_PROTOBUF_DIR)/*.o -I$(PROTOBUF_DIR)/include -I$(LPM_DIR) -I$(ASN1_PROTOBUF_DIR) $(LPM_DIR)/build/src/libfuzzer/libprotobuf-mutator-libfuzzer.a $(LPM_DIR)/build/src/libprotobuf-mutator.a $(PROTOBUF_DIR)/lib/libprotobuf.a -lcrypto + +clean: + $(RM) *.o + $(MAKE) -C $(ASN1_PROTOBUF_DIR) clean diff --git a/proto/tests/README.md b/proto/tests/README.md new file mode 100644 index 0000000..6f112ea --- /dev/null +++ b/proto/tests/README.md @@ -0,0 +1,56 @@ +# Proto tests + +## Setup + +Clone and build `libprotobuf-mutator`: + +```sh +$ git clone https://github.com/google/libprotobuf-mutator.git libprotobuf-mutator +$ cd libprotobuf-mutator +$ mkdir build && cd build +$ cmake .. -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON -DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release +$ ninja +``` + +Clone `google-fuzzing` and build test files: + +``` +$ git clone https://github.com/google/fuzzing.git fuzzing +$ cd fuzzing/proto/tests +$ make +``` + +## Run + +Generate python files: + +```sh +$ protoc ../asn1-pdu/*.proto --python_out=../asn1-pdu +``` + +Generate a test certificate and convert it to the DER format: + +```sh +$ openssl req -nodes -new -x509 -keyout key.pem -out cert.pem +$ openssl x509 -in cert.pem -out cert.der -outform DER +``` + +Convert it to protobuf: + +```sh +$ ./x509_to_protobuf.py cert.pem cert.proto +``` + +Convert the protobuf back to DER: + +```sh +$ ./protobuf_to_der.o cert.proto cert-2.der +``` + +Make sure OpenSSL can properly parse the certificate and compare it to the +initial one: + +``` +$ openssl x509 -inform DER -in cert-2.der -noout -text +$ openssl x509 -inform DER -in cert.der -noout -text +``` diff --git a/proto/tests/protobuf_to_der.cpp b/proto/tests/protobuf_to_der.cpp new file mode 100644 index 0000000..8ba782a --- /dev/null +++ b/proto/tests/protobuf_to_der.cpp @@ -0,0 +1,62 @@ +#include +#include +#include "x509_certificate.pb.h" + +#include "asn1_pdu_to_der.h" +#include "x509_certificate_to_der.h" + +std::string base64_encode(const unsigned char *buf, int len) { + const auto expected_size = 4 * ((len + 2) / 3); + auto output = reinterpret_cast(calloc(expected_size + 1, 1)); + const auto actual_size = EVP_EncodeBlock(reinterpret_cast(output), buf, len); + if (expected_size != actual_size) { + std::cerr << "Wrong base64 output length: expected " << expected_size << " but got " << actual_size << ".\n"; + } + return output; +} + +std::string readFile(const char* path) { + printf("Reading file at %s\n", path); + std::ifstream file(path, std::ios::in | std::ios::binary); + std::string str = std::string((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + printf("Read %ld bytes\n\n", str.length()); + return str; +} + +void writeFile(const char* path, std::vector data) { + printf("Writing %ld bytes to %s\n", data.size(), path); + std::ofstream file(path, std::ios::out | std::ios::binary); + std::copy(data.begin(), data.end(), std::ostreambuf_iterator(file)); +} + +int main(int argc, char **argv) { + if (argc != 3) { + printf("Usage: ./protobuf_to_der.o \n"); + return 1; + } + + std::string protobuf = readFile(argv[1]); + std::string b64Protobuf = base64_encode((unsigned char *)protobuf.c_str(), protobuf.length()); + printf("Protobuf: %s\n\n", b64Protobuf.c_str()); + + x509_certificate::X509Certificate input; + bool parse_ok = input.ParseFromArray(protobuf.c_str(), protobuf.length()); + if (!parse_ok) { + printf("ParseFromArray failed!\n"); + return 1; + } + + std::string serialized; + input.SerializeToString(&serialized); + std::string b64Serialized = base64_encode((unsigned char *)serialized.c_str(), serialized.length()); + printf("Re-serialized protobuf: %s\n\n", b64Serialized.c_str()); + + std::vector asn1 = x509_certificate::X509CertificateToDER(input); + + std::string b64asn1 = base64_encode(asn1.data(), asn1.size()); + printf("ASN.1: %s\n", b64asn1.c_str()); + + writeFile(argv[2], asn1); + + return 0; +} diff --git a/proto/tests/protobuf_to_der.o b/proto/tests/protobuf_to_der.o new file mode 100755 index 0000000..c905c92 Binary files /dev/null and b/proto/tests/protobuf_to_der.o differ diff --git a/proto/tests/requirements.txt b/proto/tests/requirements.txt new file mode 100644 index 0000000..d0c4975 --- /dev/null +++ b/proto/tests/requirements.txt @@ -0,0 +1,2 @@ +asn1crypto +protobuf