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

Fix ASN1 Integer & Timestamp encoding and add test files #110

Open
wants to merge 2 commits into
base: master
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
7 changes: 7 additions & 0 deletions proto/asn1-pdu/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
*.py
*.pyc
__pycache__

*.pb.h
*.pb.cc
*.o
14 changes: 14 additions & 0 deletions proto/asn1-pdu/Makefile
Original file line number Diff line number Diff line change
@@ -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
7 changes: 5 additions & 2 deletions proto/asn1-pdu/asn1_universal_types_to_der.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ void Encode(const Boolean& boolean, std::vector<uint8_t>& der) {
}

void Encode(const Integer& integer, std::vector<uint8_t>& der) {
// Cannot have an empty integer, so make sure the length is at least 1.
EncodeTagAndLength(kAsn1Integer,
std::min<size_t>(0x01u, integer.val().size()), der.size(),
std::max<size_t>(0x01u, integer.val().size()), der.size(),
der);

if (!integer.val().empty()) {
Expand Down Expand Up @@ -139,7 +140,9 @@ void EncodeTimestamp(const google::protobuf::Timestamp& timestamp,
bool use_two_digit_year,
std::vector<uint8_t>& der) {
std::string iso_date = google::protobuf::util::TimeUtil::ToString(timestamp);
if (iso_date.size() < 25) {

// Expected date format: yyyy-MM-ddTHH:mm:ssZ (20 characters).
if (iso_date.size() < 20) {
return;
}

Expand Down
4 changes: 3 additions & 1 deletion proto/asn1-pdu/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#ifndef PROTO_ASN1_PDU_COMMON_H_
#define PROTO_ASN1_PDU_COMMON_H_

#include <stddef.h>

#include <stdint.h>

#include <vector>
Expand Down Expand Up @@ -82,4 +84,4 @@ void EncodeTagAndLength(uint8_t tag_byte,
// that |der| remains a valid DER encoding.
void ReplaceTag(uint8_t tag_byte, size_t pos_of_tag, std::vector<uint8_t>& der);

#endif // PROTO_ASN1_PDU_COMMON_H_
#endif // PROTO_ASN1_PDU_COMMON_H_
5 changes: 5 additions & 0 deletions proto/tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.py
*.pyc
*.pem
*.der
*.proto
16 changes: 16 additions & 0 deletions proto/tests/Makefile
Original file line number Diff line number Diff line change
@@ -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
56 changes: 56 additions & 0 deletions proto/tests/README.md
Original file line number Diff line number Diff line change
@@ -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
```
62 changes: 62 additions & 0 deletions proto/tests/protobuf_to_der.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include <fstream>
#include <openssl/evp.h>
#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<char *>(calloc(expected_size + 1, 1));
const auto actual_size = EVP_EncodeBlock(reinterpret_cast<unsigned char *>(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<char>(file)), std::istreambuf_iterator<char>());
printf("Read %ld bytes\n\n", str.length());
return str;
}

void writeFile(const char* path, std::vector<uint8_t> 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<char>(file));
}

int main(int argc, char **argv) {
if (argc != 3) {
printf("Usage: ./protobuf_to_der.o <in file> <out file>\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<uint8_t> 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;
}
Binary file added proto/tests/protobuf_to_der.o
Binary file not shown.
2 changes: 2 additions & 0 deletions proto/tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
asn1crypto
protobuf