Add fuzz targets, custom mutator and fuzzing dictionary

This commit is contained in:
daniel.zatovic
2022-05-11 15:08:16 +02:00
committed by Milan Broz
parent de8a27ae02
commit f58aff21a9
14 changed files with 1771 additions and 0 deletions

1
.gitignore vendored
View File

@@ -58,3 +58,4 @@ tests/unit-utils-io
tests/vectors-test tests/vectors-test
tests/test-symbols-list.h tests/test-symbols-list.h
tests/all-symbols-test tests/all-symbols-test
tests/fuzz/LUKS2.pb*

View File

@@ -14,8 +14,17 @@ AM_CPPFLAGS = \
-DVERSION=\""$(VERSION)"\" \ -DVERSION=\""$(VERSION)"\" \
-DEXTERNAL_LUKS2_TOKENS_PATH=\"${EXTERNAL_LUKS2_TOKENS_PATH}\" -DEXTERNAL_LUKS2_TOKENS_PATH=\"${EXTERNAL_LUKS2_TOKENS_PATH}\"
AM_CFLAGS = -Wall AM_CFLAGS = -Wall
AM_CXXFLAGS = -Wall
AM_LDFLAGS = AM_LDFLAGS =
if ENABLE_FUZZ_TARGETS
SUBDIRS += tests/fuzz
AM_CFLAGS += -fsanitize=fuzzer-no-link
AM_CXXFLAGS += -fsanitize=fuzzer-no-link
AM_LDFLAGS += -fsanitize=fuzzer-no-link
endif
LDADD = $(LTLIBINTL) LDADD = $(LTLIBINTL)
tmpfilesddir = @DEFAULT_TMPFILESDIR@ tmpfilesddir = @DEFAULT_TMPFILESDIR@
@@ -64,3 +73,8 @@ uninstall-local:
check-programs: libcryptsetup.la check-programs: libcryptsetup.la
$(MAKE) -C tests $@ $(MAKE) -C tests $@
if ENABLE_FUZZ_TARGETS
fuzz-targets: libcryptsetup.la libcrypto_backend.la
$(MAKE) -C tests/fuzz $@
endif

View File

@@ -28,6 +28,7 @@ AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC AC_PROG_CC
AM_PROG_CC_C_O AM_PROG_CC_C_O
AC_PROG_CPP AC_PROG_CPP
AC_PROG_CXX
AC_PROG_INSTALL AC_PROG_INSTALL
AC_PROG_MAKE_SET AC_PROG_MAKE_SET
AC_PROG_MKDIR_P AC_PROG_MKDIR_P
@@ -213,6 +214,25 @@ if test "x$enable_pwquality" = "xyes"; then
PWQUALITY_STATIC_LIBS="$PWQUALITY_LIBS -lcrack -lz" PWQUALITY_STATIC_LIBS="$PWQUALITY_LIBS -lcrack -lz"
fi fi
dnl ==========================================================================
dnl libprotobuf-fuzzer library
AC_ARG_ENABLE([fuzz-targets],
AS_HELP_STRING([--enable-fuzz-targets], [enable building fuzz targets]))
AM_CONDITIONAL(ENABLE_FUZZ_TARGETS, test "x$enable_fuzz_targets" = "xyes")
if test "x$enable_fuzz_targets" = "xyes"; then
PKG_CHECK_MODULES([PROTOBUF], [protobuf],,
AC_MSG_ERROR([You need protobuf library to build fuzz targets.]))
AC_SUBST([PROTOBUF_LIBS])
PKG_CHECK_MODULES([LIBPROTOBUF_MUTATOR], [ libprotobuf-mutator ],,
AC_MSG_ERROR([You need libprotobuf-mutator library to build fuzz targets.]))
AC_SUBST([LIBPROTOBUF_MUTATOR_LIBS])
AC_SUBST([LIBPROTOBUF_MUTATOR_CFLAGS])
AC_CONFIG_FILES([tests/fuzz/Makefile])
fi
dnl ========================================================================== dnl ==========================================================================
dnl passwdqc library (cryptsetup CLI only) dnl passwdqc library (cryptsetup CLI only)
AC_ARG_ENABLE([passwdqc], AC_ARG_ENABLE([passwdqc],

379
tests/fuzz/LUKS2.proto Normal file
View File

@@ -0,0 +1,379 @@
/*
* cryptsetup LUKS2 custom mutator
*
* Copyright (C) 2022 Daniel Zatovic <daniel.zatovic@gmail.com>
* Copyright (C) 2022 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
syntax = "proto2";
package LUKS2_proto;
// ---------------------------------------------------------------------------
// ----------------------------- GENERIC OBJECTS -----------------------------
// ---------------------------------------------------------------------------
message object_id {
oneof id {
// int_id will be mapped to range -16 to 16 (mod 33)
// this way iy should be easier to generate valid
// object cross-references
uint32 int_id = 1;
string string_id = 2;
}
}
message string_uint64 {
required bool negative = 1;
oneof number {
uint32 uint_num = 2;
string string_num = 3;
}
}
enum hash_algorithm {
HASH_ALG_SHA1 = 1;
HASH_ALG_SHA256 = 2;
}
// ---------------------------------------------------------------------------
// ----------------------------- BINARY HEADER -------------------------------
// ---------------------------------------------------------------------------
enum luks2_magic {
INVALID = 0;
FIRST = 1;
SECOND = 2;
}
enum luks_version {
ONE = 1;
TWO = 2;
THREE = 3;
}
// we limit the size to 64KiB to make the fuzzing faster
// because the checksum needs to be calculated for the whole image
enum hdr_size {
size_16_KB = 16384;
size_32_KB = 32768;
size_64_KB = 65536;
// size_128_KB = 131072;
// size_256_KB = 262144;
// size_512_KB = 524288;
// size_1_MB = 1048576;
// size_2_MB = 2097152;
// size_4_MB = 4194304;
}
enum seqid_description {
PRIMARY_GREATER = 0;
SECONDARY_GREATER = 1;
EQUAL = 2;
}
// message luks2_hdr_disk {
// char magic[LUKS2_MAGIC_L];
// //uint16_t version; /* Version 2 */
// uint64_t hdr_size; /* in bytes, including JSON area */
// uint64_t seqid; /* increased on every update */
// char label[LUKS2_LABEL_L];
// char checksum_alg[LUKS2_CHECKSUM_ALG_L];
// uint8_t salt[LUKS2_SALT_L]; /* unique for every header/offset */
// char uuid[LUKS2_UUID_L];
// char subsystem[LUKS2_LABEL_L]; /* owner subsystem label */
// uint64_t hdr_offset; /* offset from device start in bytes */
// char _padding[184];
// uint8_t csum[LUKS2_CHECKSUM_L];
// }
message LUKS2_header {
required luks_version version = 1;
required luks2_magic magic = 2;
required hdr_size hdr_size = 3;
required bool use_correct_checksum = 4;
optional uint64 selected_offset = 5;
}
message LUKS2_both_headers {
required LUKS2_header primary_header = 1;
required LUKS2_header secondary_header = 2;
required seqid_description seqid = 3;
required json_area_description json_area = 4;
}
message json_area_description {
optional config_description config = 1;
repeated keyslot_description keyslots = 2;
repeated digest_description digests = 3;
repeated segment_description segments = 4;
repeated token_description tokens = 5;
}
// ---------------------------------------------------------------------------
// ----------------------------- KEYSLOT OBJECT ------------------------------
// ---------------------------------------------------------------------------
enum keyslot_type {
KEYSLOT_TYPE_LUKS2 = 1;
KEYSLOT_TYPE_REENCRYPT = 2;
KEYSLOT_TYPE_PLACEHOLDER = 3;
}
enum reencrypt_keyslot_mode {
MODE_REENCRYPT = 1;
MODE_ENCRYPT = 2;
MODE_DECRYPT = 3;
}
enum reencrypt_keyslot_direction {
DIRECTION_FORWARD = 1;
DIRECTION_BACKWARD = 2;
}
// The area object contains these mandatory fields:
// - type [string] the area type.
// - offset [string-uint64] the offset from the device start to the beginning of the binary area (in bytes).
// - size [string-uint64] the area size (in bytes).
//
// Area type raw contains these additional fields:
// - encryption [string] the area encryption algorithm, in dm-crypt notation (for example aes-xts-plain64).
// - key_size [integer] the area encryption key size.
//
// Area type none and journal (used only for reencryption optional extension) contain only mandatory fields.
//
// Area type checksum (used only for reencryption optional extension) contains these additional fields:
// - hash [string] The hash algorithm for the checksum resilience mode.
// - sector_size [integer] The data unit size for digest checksum calculated with the hash algorithm.
//
// Area type datashift (used only for reencryption optional extension) contains this additional field:
// - shift_size [string-uint64] The data shift (in bytes) performed during reencryption (shift direction is according to direction field).
enum keyslot_area_type {
KEYSLOT_AREA_TYPE_RAW = 1;
KEYSLOT_AREA_TYPE_NONE = 2;
KEYSLOT_AREA_TYPE_JOURNAL = 3;
KEYSLOT_AREA_TYPE_CHECKSUM = 4;
KEYSLOT_AREA_TYPE_DATASHIFT = 5;
}
message keyslot_area_description {
// mandatory fields
optional keyslot_area_type type = 1;
optional string_uint64 offset = 2;
optional string_uint64 size = 3;
// raw type fields
optional string encryption = 4;
optional int32 key_size = 5;
// checksum type field
optional hash_algorithm hash = 6;
optional int32 sector_size = 7;
// datashift type fields
optional string_uint64 shift_size = 8;
}
// The object describes PBKDF attributes used for the keyslot.
// The kdf object mandatory fields are:
// - type [string] the PBKDF type.
// - salt [base64] the salt for PBKDF (binary data).
//
// The pbkdf2 type (compatible with LUKS1) contains these additional fields:
// - hash [string] the hash algorithm for the PBKDF2 (SHA-256).
// - iterations [integer] the PBKDF2 iterations count.
//
// The argon2i and argon2id type contains these additional fields:
// - time [integer] the time cost (in fact the iterations count for Argon2).
// - memory [integer] the memory cost, in kilobytes. If not available, the keyslot cannot be unlocked.
// - cpus [integer] the required number of threads (CPU cores number cost). If not available, unlocking will be slower.
enum keyslot_kdf_type {
KEYSLOT_KDF_TYPE_PBKDF2 = 1;
KEYSLOT_KDF_TYPE_ARGON2I = 2;
KEYSLOT_KDF_TYPE_ARGON2ID = 3;
}
message keyslot_kdf_description {
optional keyslot_kdf_type type = 1;
optional string salt = 2;
// pbkdf2 type
optional hash_algorithm hash = 3;
optional int32 iterations = 4;
// argon2i and argon2id types
optional int32 time = 5;
optional int32 memory = 6;
optional int32 cpus = 7;
}
enum keyslot_af_type {
KEYSLOT_AF_TYPE_LUKS1 = 1;
}
// The af (anti-forensic splitter) object contains this madatory field:
// - type [string] the anti-forensic function type.
// AF type luks1 (compatible with LUKS1 [1]) contains these additional fields:
// - stripes [integer] the number of stripes, for historical reasons only the 4000 value is supported.
// - hash [string] the hash algorithm used.
message keyslot_af_description {
optional keyslot_af_type type = 1;
optional int32 stripes = 2;
optional hash_algorithm hash = 3;
}
// - type [string] the keyslot type.
// - key_size [integer] the key size (in bytes) stored in keyslot.
// - priority [integer,optional] the keyslot priority. Here 0 means ignore (the slot should be used only if explicitly stated), 1 means normal priority and 2 means high priority (tried before normal priority).
// REENCRYPT
// The key size field must be set to 1. The area type must be none, checksum,
// journal or datashift.
// The reencrypt object must contain these additional fields:
// - mode [string] the reencryption mode. reencrypt, encrypt and decrypt
// - direction [string] the reencryption direction. forward backward
// - area [object] the allocated area in the binary keyslots area.
// LUKS2 object must contain these additional fields:
// - kdf [object] the PBKDF type and parameters used.
// - af [object] the anti-forensic splitter [1] (only the luks1 type is currently
// used).
message keyslot_description {
// type
required object_id oid = 1;
optional keyslot_type type = 2;
optional int32 key_size = 3;
optional int32 priority = 4;
// reencrypt extension
optional reencrypt_keyslot_mode mode = 5;
optional reencrypt_keyslot_direction direction = 6;
// objects
optional keyslot_area_description area = 7;
optional keyslot_kdf_description kdf = 8;
optional keyslot_af_description af = 9;
}
// ---------------------------------------------------------------------------
// ------------------------------ DIGEST OBJECT ------------------------------
// ---------------------------------------------------------------------------
message digest_description {
required object_id oid = 1;
optional keyslot_kdf_type type = 2;
repeated object_id keyslots = 3;
repeated object_id segments = 4;
optional string salt = 5;
optional string digest = 6;
// pbkdf2 digest fields
optional hash_algorithm hash = 7;
optional int32 iterations = 8;
}
// ---------------------------------------------------------------------------
// ----------------------------- SEGMENT OBJECT ------------------------------
// ---------------------------------------------------------------------------
enum segment_type {
SEGMENT_TYPE_LINEAR = 1;
SEGMENT_TYPE_CRYPT = 2;
}
enum segment_flag {
IN_REENCRYPTION = 1;
BACKUP_FINAL = 2;
BACKUP_PREVIOUS = 3;
BACKUP_MOVED_SEGMENT = 4;
}
message segment_integrity_description {
optional string type = 1;
optional string journal_encryption = 2;
optional string journal_integrity = 3;
}
message segment_description {
required object_id oid = 1;
optional segment_type type = 2;
optional string_uint64 offset = 3;
optional string_uint64 size = 4;
repeated segment_flag flags = 5;
// segment type crypt
optional string_uint64 iv_tweak = 6;
optional string encryption = 7;
optional int32 sector_size = 8;
optional segment_integrity_description integrity = 9;
}
// ---------------------------------------------------------------------------
// ------------------------------ TOKEN OBJECT -------------------------------
// ---------------------------------------------------------------------------
message token_description {
required object_id oid = 1;
optional string type = 2;
repeated object_id keyslots = 3;
optional string key_description = 4;
}
// ---------------------------------------------------------------------------
// ------------------------------ CONFIG OBJECT ------------------------------
// ---------------------------------------------------------------------------
// - allow-discards allows TRIM (discards) on the active device.
// - same-cpu-crypt compatibility performance flag for dm-crypt [3] to per- form encryption using the same CPU that originated the request.
// - submit-from-crypt-cpus compatibility performance flag for dm-crypt [3] to disable offloading write requests to a separate thread after encryption.
// - no-journal disable data journalling for dm-integrity [10].
// - no-read-workqueue compatibility performance flag for dm-crypt [3] to bypass dm-crypt read workqueue and process read requests synchronously.
// - no-write-workqueue compatibility performance flag for dm-crypt [3] to bypass dm-crypt write workqueue and process write requests synchronously.
enum config_flag {
CONFIG_FLAG_ALLOW_DISCARDS = 1;
CONFIG_FLAG_SAME_CPU_CRYPT = 2;
CONFIG_FLAG_SUBMIT_FROM_CRYPT_CPUS = 3;
CONFIG_FLAG_NO_JOURNAL = 4;
CONFIG_FLAG_NO_READ_WORKQUEUE = 5;
CONFIG_FLAG_NO_WRITE_WORKQUEUE = 6;
}
enum config_requirement {
CONFIG_REQUIREMENT_OFFLINE_REENCRYPT = 1;
CONFIG_REQUIREMENT_ONLINE_REENCRYPT_V2 = 2;
}
// - json_size [string-uint64] the JSON area size (in bytes). Must match the binary header.
// - keyslots_size [string-uint64] the binary keyslot area size (in bytes). Must be aligned to 4096 bytes.
// - flags [array, optional] the array of string objects with persistent flags for the device.
// - requirements [array, optional] the array of string objects with additional required features for the LUKS device.
message config_description {
required bool use_primary_hdr_size = 2;
repeated config_flag config_flags = 3;
repeated config_requirement requirements = 4;
}

46
tests/fuzz/Makefile.am Normal file
View File

@@ -0,0 +1,46 @@
CLEANFILES = LUKS2.pb.h LUKS2.pb.cc
clean-local:
-rm -rf LUKS2.pb.h LUKS2.pb.cc
TESTS = crypt2_load_fuzz crypt_load_fuzz
LIB_FUZZING_ENGINE := $(if $(LIB_FUZZING_ENGINE),$(LIB_FUZZING_ENGINE),"-fsanitize=fuzzer")
crypt2_load_fuzz_SOURCES = crypt2_load_fuzz.cc common.c
crypt2_load_fuzz_LDADD = $(LDADD) ../../libcryptsetup.la
crypt2_load_fuzz_LDFLAGS = $(AM_LDFLAGS) $(LIB_FUZZING_ENGINE) -static
crypt2_load_fuzz_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/lib -I$(top_srcdir)/tests/fuzz -fsanitize=fuzzer-no-link
crypt2_load_fuzz_CXXFLAGS = $(AM_CXXFLAGS) -I$(top_srcdir)/lib -I$(top_srcdir)/tests/fuzz -fsanitize=fuzzer-no-link
generate_proto: LUKS2.proto
@protoc LUKS2.proto --cpp_out=.
LUKS2.pb.h: generate_proto
LUKS2.pb.cc: generate_proto
BUILT_SOURCES = LUKS2.pb.h LUKS2.pb.cc
crypt2_load_proto_fuzz_SOURCES = crypt2_load_proto_fuzz.cc common.c LUKS2.pb.cc proto_to_luks2_converter.cc
crypt2_load_proto_fuzz_LDADD = \
../../libcryptsetup.la \
../../libcrypto_backend.la \
@LIBPROTOBUF_MUTATOR_LIBS@ \
@PROTOBUF_LIBS@
crypt2_load_proto_fuzz_LDFLAGS = $(AM_LDFLAGS) $(LIB_FUZZING_ENGINE) -static
crypt2_load_proto_fuzz_CFLAGS = -fsanitize=fuzzer-no-link
crypt2_load_proto_fuzz_CXXFLAGS = \
$(AM_CXXFLAGS) \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/tests/fuzz \
-fsanitize=fuzzer-no-link \
@LIBPROTOBUF_MUTATOR_CFLAGS@
proto_to_luks2_SOURCES = proto_to_luks2.cc common.c LUKS2.pb.cc proto_to_luks2_converter.cc
proto_to_luks2_LDADD = ../../libcryptsetup.la ../../libcrypto_backend.la @PROTOBUF_LIBS@
proto_to_luks2_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer-no-link
proto_to_luks2_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/lib -I$(top_srcdir)/tests/fuzz -fsanitize=fuzzer-no-link
proto_to_luks2_CXXFLAGS = $(AM_CXXFLAGS) -I$(top_srcdir)/lib -I$(top_srcdir)/tests/fuzz -fsanitize=fuzzer-no-link
check_PROGRAMS = crypt2_load_fuzz crypt2_load_proto_fuzz proto_to_luks2
fuzz-targets: $(check_PROGRAMS)

44
tests/fuzz/common.c Normal file
View File

@@ -0,0 +1,44 @@
/*
* No copyright is claimed. This code is in the public domain; do with
* it what you wish.
*
* Written by Karel Zak <kzak@redhat.com>
* Petr Uzel <petr.uzel@suse.cz>
*/
#include <unistd.h>
#include <time.h>
#include <errno.h>
/*
* The usleep function was marked obsolete in POSIX.1-2001 and was removed
* in POSIX.1-2008. It was replaced with nanosleep() that provides more
* advantages (like no interaction with signals and other timer functions).
*/
static inline int xusleep(useconds_t usec)
{
struct timespec waittime = {
.tv_sec = usec / 1000000L,
.tv_nsec = (usec % 1000000L) * 1000
};
return nanosleep(&waittime, NULL);
}
int write_all(int fd, const void *buf, size_t count)
{
while (count) {
ssize_t tmp;
errno = 0;
tmp = write(fd, buf, count);
if (tmp > 0) {
count -= tmp;
if (count)
buf = (const void *) ((const char *) buf + tmp);
} else if (errno != EINTR && errno != EAGAIN)
return -1;
if (errno == EAGAIN) /* Try later, *sigh* */
xusleep(250000);
}
return 0;
}

1
tests/fuzz/common.h Normal file
View File

@@ -0,0 +1 @@
extern int write_all(int fd, const void *buf, size_t count);

View File

@@ -0,0 +1,155 @@
/*
* cryptsetup LUKS2 fuzz target
*
* Copyright (C) 2022 Daniel Zatovic <daniel.zatovic@gmail.com>
* Copyright (C) 2022 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
extern "C" {
#define FILESIZE (16777216)
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/types.h>
#include <err.h>
#include "crypto_backend/crypto_backend.h"
#include <luks2/luks2.h>
#include <libcryptsetup.h>
#include <src/cryptsetup.h>
#include "common.h"
int calculate_checksum(const uint8_t* data, size_t size) {
struct crypt_hash *hd = NULL;
struct luks2_hdr_disk *hdr = NULL;
int hash_size;
uint64_t hdr_size1, hdr_size2;
int r = 0;
// primary header
if (sizeof(struct luks2_hdr_disk) > size)
return 0;
hdr = (struct luks2_hdr_disk *) data;
hdr_size1 = be64_to_cpu(hdr->hdr_size);
if (hdr_size1 > size)
return 0;
memset(&hdr->csum, 0, LUKS2_CHECKSUM_L);
if ((r = crypt_hash_init(&hd, "sha256")))
goto out;
if ((r = crypt_hash_write(hd, (char*) data, hdr_size1)))
goto out;
hash_size = crypt_hash_size("sha256");
if (hash_size <= 0) {
r = 1;
goto out;
}
if ((r = crypt_hash_final(hd, (char*)&hdr->csum, (size_t)hash_size)))
goto out;
crypt_hash_destroy(hd);
// secondary header
if (hdr_size1 < sizeof(struct luks2_hdr_disk))
hdr_size1 = sizeof(struct luks2_hdr_disk);
if (hdr_size1 + sizeof(struct luks2_hdr_disk) > size)
return 0;
hdr = (struct luks2_hdr_disk *) (data + hdr_size1);
hdr_size2 = be64_to_cpu(hdr->hdr_size);
if (hdr_size1 + hdr_size2 > size)
return 0;
memset(&hdr->csum, 0, LUKS2_CHECKSUM_L);
if ((r = crypt_hash_init(&hd, "sha256")))
goto out;
if ((r = crypt_hash_write(hd, (char*) hdr, hdr_size2)))
goto out;
if ((r = crypt_hash_final(hd, (char*)&hdr->csum, (size_t)hash_size)))
goto out;
out:
if (hd)
crypt_hash_destroy(hd);
return r;
}
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
int fd;
struct crypt_device *cd;
int result;
uint8_t *map;
int r = 0;
char name[] = "/tmp/test-script-fuzz.XXXXXX";
if ((r = calculate_checksum(data, size)))
return r;
fd = mkostemp(name, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
if (fd < 0)
err(EXIT_FAILURE, "mkostemp() failed");
result = lseek(fd, FILESIZE-1, SEEK_SET);
if (result == -1) {
r = 1;
goto out;
}
result = write(fd, "\0", 1);
if (result != 1) {
r = 1;
goto out;
}
result = lseek(fd, 0, SEEK_SET);
if (result == -1) {
r = 1;
goto out;
}
if (write_all(fd, data, size) != 0) {
r = 1;
goto out;
}
// crypt_set_debug_level(CRYPT_DEBUG_ALL);
r = crypt_init(&cd, name);
if (r < 0 ) {
r = 0;
goto out;
}
r = crypt_load(cd, CRYPT_LUKS2, NULL);
crypt_free(cd);
if (r < 0) {
r = 0;
goto out;
}
out:
close(fd);
unlink(name);
return r;
}
}

View File

@@ -0,0 +1,130 @@
# LUKS2 dictionary based on AFL dictionary for JSON
# -------------------------------------------------
# JSON dictionary from https://github.com/google/AFL/blob/master/dictionaries/json.dict
# Inspired by a dictionary by Jakub Wilk <jwilk@jwilk.net>
#
# LUKS2 keywords by Daniel Zatovic
"0"
",0"
":0"
"0:"
"-1.2e+3"
"true"
"false"
"null"
"\"\""
",\"\""
":\"\""
"\"\":"
"{}"
",{}"
":{}"
"{\"\":0}"
"{{}}"
"[]"
",[]"
":[]"
"[0]"
"[[]]"
"''"
"\\"
"\\b"
"\\f"
"\\n"
"\\r"
"\\t"
"\\u0000"
"\\x00"
"\\0"
"\\uD800\\uDC00"
"\\uDBFF\\uDFFF"
"\"\":0"
"//"
"/**/"
"$ref"
"type"
"coordinates"
"@context"
"@id"
","
":"
"1024"
"2048"
"4096"
"512"
"aegis128-random"
"aes-cbc:essiv:sha256"
"aes-xts-plain64"
"af"
"allow-discards"
"area"
"argon2i"
"argon2id"
"backup-final"
"backup-moved-segment"
"backup-previous"
"checksum"
"config"
"cpus"
"crypt"
"datashift"
"digest"
"digests"
"direction"
"encryption"
"flags"
"hash"
"in-reencryption"
"integrity"
"iterations"
"iv_tweak"
"journal"
"journal_encryption"
"journal_integrity"
"json_size"
"kdf"
"key_description"
"key_size"
"keyslots"
"keyslots_size"
"linear"
"luks2"
"luks2-keyring"
"LUKS\xBA\xBE"
"memory"
"mode"
"no-journal"
"none"
"no-read-workqueue"
"no-write-workqueue"
"offline-reencrypt"
"offset"
"online-reencrypt-v2"
"pbkdf2"
"priority"
"raw"
"reencrypt"
"requirements"
"salt"
"same-cpu-crypt"
"sector_size"
"segments"
"serpent-xts-plain64"
"shift_size"
"size"
"SKUL\xBA\xBE"
"stripes"
"submit-from-crypt-cpus"
"time"
"tokens"
"twofish-xts-plain64"

View File

@@ -0,0 +1,71 @@
/*
* cryptsetup LUKS2 custom mutator fuzz target
*
* Copyright (C) 2022 Daniel Zatovic <daniel.zatovic@gmail.com>
* Copyright (C) 2022 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <cstdint>
#include <memory>
#include <string>
#include <iostream>
#include "LUKS2.pb.h"
#include "proto_to_luks2_converter.h"
#include "libfuzzer/libfuzzer_macro.h"
extern "C" {
#include <libcryptsetup.h>
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
#include "common.h"
}
DEFINE_PROTO_FUZZER(const LUKS2_proto::LUKS2_both_headers &headers) {
int result;
struct crypt_device *cd;
uint8_t *map;
int r = 0;
char name[] = "/tmp/test-proto-fuzz.XXXXXX";
int fd = mkostemp(name, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
if (fd < 0)
err(EXIT_FAILURE, "mkostemp() failed");
LUKS2_proto::LUKS2ProtoConverter converter;
converter.convert(headers, fd);
r = crypt_init(&cd, name);
if (r < 0 ) {
r = 0;
goto out;
}
r = crypt_load(cd, CRYPT_LUKS2, NULL);
crypt_free(cd);
if (r < 0) {
r = 0;
goto out;
}
out:
if (fd >= 0)
close(fd);
unlink(name);
}

125
tests/fuzz/oss-fuzz-build.sh Executable file
View File

@@ -0,0 +1,125 @@
#!/usr/bin/env bash
echo "Running cryptsetup OSS-Fuzz build script."
env
set -ex
apt-get update && apt-get install -y make autoconf automake libtool sharutils \
dmsetup \
pkg-config \
autopoint \
gettext \
expect \
keyutils \
ninja-build \
po4a
# libprotobuf mutator
git clone --depth 1 https://github.com/madler/zlib.git
# no shallow support
git clone http://git.tukaani.org/xz.git
git clone --depth 1 https://github.com/json-c/json-c.git
git clone --depth 1 git://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
git clone --depth 1 git://sourceware.org/git/lvm2.git
git clone --depth 1 https://github.com/rpm-software-management/popt.git
git clone --depth 1 https://github.com/protocolbuffers/protobuf.git
git clone --depth 1 https://github.com/google/libprotobuf-mutator.git
git clone --depth 1 git://git.openssl.org/openssl.git
export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig/
export LC_CTYPE=C.UTF-8
export CC=${CC:-clang}
export CXX=${CXX:-clang++}
export LIB_FUZZING_ENGINE="${LIB_FUZZING_ENGINE:--fsanitize=fuzzer}"
SANITIZER="${SANITIZER:-address -fsanitize-address-use-after-scope}"
flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER -fsanitize=fuzzer-no-link"
export CFLAGS="${CFLAGS:-$flags}"
export CXXFLAGS="${CXXFLAGS:-$flags}"
export OUT="${OUT:-$(pwd)/out}"
export LDFLAGS="$CXXFLAGS"
cd openssl
./Configure linux-x86_64 no-shared --static "$CFLAGS"
make build_generated
make -j libcrypto.a
make install_dev
cd ..
cd e2fsprogs
mkdir build
cd build
../configure --enable-libuuid --enable-libblkid
make -j V=1
make install-libs-recursive
cd ../..
cd zlib
./configure --static
make -j
make install
cd ..
cd xz
./autogen.sh
./configure --enable-static --disable-shared
make -j
make install
cd ..
cd json-c
mkdir build
cd build
cmake .. -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON
make -j
make install
cd ../..
cd lvm2
./configure --enable-static_link
make -j libdm.device-mapper
mv ./libdm/ioctl/libdevmapper.a /usr/lib/libdevmapper.a
mv ./libdm/libdevmapper.h /usr/include/
cd ..
cd popt
./autogen.sh
./configure --disable-shared --enable-static
make -j
make install
cd ..
cd protobuf
git submodule update --init --recursive
./autogen.sh
./configure --prefix=/usr --enable-static --disable-shared
make -j
make install
# rebuild protoc without sanitiser
CFLAGS="" CXXFLAGS="" LDFLAGS="" ./configure --prefix=/usr --enable-static --disable-shared
make -j
mv ./src/protoc /usr/bin/
cd ..
mkdir libprotobuf-mutator-build
cd libprotobuf-mutator-build
cmake ../libprotobuf-mutator -DCMAKE_INSTALL_PREFIX=/usr -DPKG_CONFIG_PATH=/usr/lib/pkgconfig -GNinja -DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release
ninja
ninja install
cd ..
cd cryptsetup
./autogen.sh
./configure --enable-static --disable-ssh-token --disable-blkid --disable-udev --disable-selinux --disable-pwquality --with-crypto_backend=openssl --disable-shared --enable-fuzz-targets
#./configure --enable-static --disable-ssh-token --disable-blkid --disable-udev --disable-selinux --disable-pwquality --with-crypto_backend=openssl --disable-cryptsetup --disable-veritysetup --disable-integritysetup --enable-shared=0
make clean
make -j fuzz-targets
cp ../*_fuzz_seed_corpus.zip $OUT
cp tests/fuzz/*_fuzz $OUT
cp tests/fuzz/*_fuzz.dict $OUT
cp tests/fuzz/proto_to_luks2 $OUT

View File

@@ -0,0 +1,77 @@
/*
* cryptsetup LUKS2 protobuf to image converter
*
* Copyright (C) 2022 Daniel Zatovic <daniel.zatovic@gmail.com>
* Copyright (C) 2022 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <iostream>
#include <string>
#include <type_traits>
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include "proto_to_luks2_converter.h"
using namespace LUKS2_proto;
int main(int argc, char *argv[]) {
LUKS2_both_headers headers;
LUKS2ProtoConverter converter;
std::string out_img_name;
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <LUKS2 proto>\n";
return 1;
}
int fd = open(argv[1], O_RDONLY);
if (fd < 0) {
std::cerr << "Failed to open " << argv[1] << std::endl;
return 1;
}
google::protobuf::io::FileInputStream fileInput(fd);
if (!google::protobuf::TextFormat::Parse(&fileInput, &headers)) {
std::cerr << "Failed to parse protobuf " << argv[1] << std::endl;
goto out;
}
close(fd);
out_img_name = argv[1];
out_img_name += ".img";
fd = open(out_img_name.c_str(), O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC|O_TRUNC, 0644);
if (fd < 0) {
std::cerr << "Failed to open output file " << out_img_name << std::endl;
goto out;
}
converter.set_write_headers_only(false);
converter.convert(headers, fd);
out:
if (fd >= 0)
close(fd);
return 0;
}

View File

@@ -0,0 +1,617 @@
/*
* cryptsetup LUKS2 custom mutator fuzz target
*
* Copyright (C) 2022 Daniel Zatovic <daniel.zatovic@gmail.com>
* Copyright (C) 2022 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "proto_to_luks2_converter.h"
#include <exception>
#include <iostream>
extern "C" {
#include <json-c/json.h>
#include <src/cryptsetup.h>
#include <luks2/luks2.h>
#include <libcryptsetup.h>
#include <err.h>
#include "common.h"
}
#define OFFSET_OF(strct, field) (((char*)&((struct strct*)0)->field) - (char*)0)
namespace LUKS2_proto {
std::string LUKS2ProtoConverter::string_uint64_to_string(const string_uint64 &str_u64) {
std::ostringstream os;
if (str_u64.negative())
os << "-";
if (str_u64.has_uint_num())
os << str_u64.uint_num();
else if (str_u64.has_string_num())
os << str_u64.string_num();
return os.str();
}
std::string LUKS2ProtoConverter::object_id_to_string(const object_id &oid) {
std::ostringstream os;
if (oid.has_int_id()) {
os << (oid.int_id() % 33) - 16;
} else if (oid.has_string_id()) {
os << oid.string_id();
}
return os.str();
}
std::string LUKS2ProtoConverter::hash_algorithm_to_string(const hash_algorithm type) {
switch (type) {
case HASH_ALG_SHA1:
return "sha1";
case HASH_ALG_SHA256:
return "sha256";
}
}
std::string LUKS2ProtoConverter::keyslot_area_type_to_string(const keyslot_area_type type) {
switch (type) {
case KEYSLOT_AREA_TYPE_RAW:
return "raw";
case KEYSLOT_AREA_TYPE_NONE:
return "none";
case KEYSLOT_AREA_TYPE_JOURNAL:
return "journal";
case KEYSLOT_AREA_TYPE_CHECKSUM:
return "checksum";
case KEYSLOT_AREA_TYPE_DATASHIFT:
return "datashift";
}
}
void LUKS2ProtoConverter::generate_keyslot_area(struct json_object *jobj_area, const keyslot_area_description &keyslot_area_desc) {
// mandatory fields
if (keyslot_area_desc.has_type())
json_object_object_add(jobj_area, "type", json_object_new_string(keyslot_area_type_to_string(keyslot_area_desc.type()).c_str()));
if (keyslot_area_desc.has_offset())
json_object_object_add(jobj_area, "offset", json_object_new_string(string_uint64_to_string(keyslot_area_desc.offset()).c_str()));
if (keyslot_area_desc.has_size())
json_object_object_add(jobj_area, "size", json_object_new_string(string_uint64_to_string(keyslot_area_desc.size()).c_str()));
// raw type fields
if (keyslot_area_desc.has_encryption())
json_object_object_add(jobj_area, "encryption", json_object_new_string(keyslot_area_desc.encryption().c_str()));
if (keyslot_area_desc.has_key_size())
json_object_object_add(jobj_area, "key_size", json_object_new_int(keyslot_area_desc.key_size()));
// checksum type fields
if (keyslot_area_desc.has_hash())
json_object_object_add(jobj_area, "hash", json_object_new_string(hash_algorithm_to_string(keyslot_area_desc.hash()).c_str()));
if (keyslot_area_desc.has_sector_size())
json_object_object_add(jobj_area, "sector_size", json_object_new_int(keyslot_area_desc.sector_size()));
// datashift type fields
if (keyslot_area_desc.has_shift_size())
json_object_object_add(jobj_area, "shift_size", json_object_new_string(string_uint64_to_string(keyslot_area_desc.shift_size()).c_str()));
}
std::string LUKS2ProtoConverter::keyslot_kdf_type_to_string(const keyslot_kdf_type type) {
switch (type) {
case KEYSLOT_KDF_TYPE_PBKDF2:
return "pbkdf2";
case KEYSLOT_KDF_TYPE_ARGON2I:
return "argon2i";
case KEYSLOT_KDF_TYPE_ARGON2ID:
return "argon2id";
}
}
void LUKS2ProtoConverter::generate_keyslot_kdf(struct json_object *jobj_kdf, const keyslot_kdf_description &keyslot_kdf_desc) {
// mandatory fields
if (keyslot_kdf_desc.has_type())
json_object_object_add(jobj_kdf, "type", json_object_new_string(keyslot_kdf_type_to_string(keyslot_kdf_desc.type()).c_str()));
if (keyslot_kdf_desc.has_salt())
json_object_object_add(jobj_kdf, "salt", json_object_new_string(keyslot_kdf_desc.salt().c_str()));
else
json_object_object_add(jobj_kdf, "salt", json_object_new_string("6vz4xK7cjan92rDA5JF8O6Jk2HouV0O8DMB6GlztVk="));
// pbkdf2 type
if (keyslot_kdf_desc.has_hash())
json_object_object_add(jobj_kdf, "hash", json_object_new_string(hash_algorithm_to_string(keyslot_kdf_desc.hash()).c_str()));
if (keyslot_kdf_desc.has_iterations())
json_object_object_add(jobj_kdf, "iterations", json_object_new_int(keyslot_kdf_desc.iterations()));
// argon2i and argon2id types
if (keyslot_kdf_desc.has_time())
json_object_object_add(jobj_kdf, "time", json_object_new_int(keyslot_kdf_desc.time()));
if (keyslot_kdf_desc.has_memory())
json_object_object_add(jobj_kdf, "memory", json_object_new_int(keyslot_kdf_desc.memory()));
if (keyslot_kdf_desc.has_cpus())
json_object_object_add(jobj_kdf, "cpus", json_object_new_int(keyslot_kdf_desc.cpus()));
}
std::string LUKS2ProtoConverter::keyslot_af_type_to_string(const keyslot_af_type type) {
switch (type) {
case KEYSLOT_AF_TYPE_LUKS1:
return "luks1";
}
}
void LUKS2ProtoConverter::generate_keyslot_af(struct json_object *jobj_af, const keyslot_af_description &keyslot_af_desc) {
if (keyslot_af_desc.has_type())
json_object_object_add(jobj_af, "type", json_object_new_string(keyslot_af_type_to_string(keyslot_af_desc.type()).c_str()));
if (keyslot_af_desc.has_stripes())
json_object_object_add(jobj_af, "stripes", json_object_new_int(keyslot_af_desc.stripes()));
if (keyslot_af_desc.has_hash())
json_object_object_add(jobj_af, "hash", json_object_new_string(hash_algorithm_to_string(keyslot_af_desc.hash()).c_str()));
}
std::string LUKS2ProtoConverter::keyslot_type_to_string(const keyslot_type type) {
switch (type) {
case KEYSLOT_TYPE_LUKS2:
return "luks2";
case KEYSLOT_TYPE_REENCRYPT:
return "reencrypt";
case KEYSLOT_TYPE_PLACEHOLDER:
return "placeholder";
}
}
std::string LUKS2ProtoConverter::reencrypt_keyslot_mode_to_string(const reencrypt_keyslot_mode mode) {
switch (mode) {
case MODE_REENCRYPT:
return "reencrypt";
case MODE_ENCRYPT:
return "encrypt";
case MODE_DECRYPT:
return "decrypt";
}
}
std::string LUKS2ProtoConverter::reencrypt_keyslot_direction_to_string(const reencrypt_keyslot_direction direction) {
switch (direction) {
case DIRECTION_FORWARD:
return "forward";
case DIRECTION_BACKWARD:
return "backward";
}
}
void LUKS2ProtoConverter::generate_keyslot(struct json_object *jobj_keyslots, const keyslot_description &keyslot_desc) {
struct json_object *jobj_keyslot, *jobj_area, *jobj_kdf, *jobj_af;
jobj_keyslot = json_object_new_object();
if (keyslot_desc.has_type())
json_object_object_add(jobj_keyslot, "type", json_object_new_string(keyslot_type_to_string(keyslot_desc.type()).c_str()));
if (keyslot_desc.has_key_size())
json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(keyslot_desc.key_size()));
if (keyslot_desc.has_priority())
json_object_object_add(jobj_keyslot, "priority", json_object_new_int(keyslot_desc.priority()));
if (keyslot_desc.has_mode())
json_object_object_add(jobj_keyslot, "mode", json_object_new_int(keyslot_desc.mode()));
if (keyslot_desc.has_direction())
json_object_object_add(jobj_keyslot, "direction", json_object_new_int(keyslot_desc.direction()));
/* Area object */
if (keyslot_desc.has_area()) {
jobj_area = json_object_new_object();
generate_keyslot_area(jobj_area, keyslot_desc.area());
json_object_object_add(jobj_keyslot, "area", jobj_area);
}
/* KDF object */
if (keyslot_desc.has_kdf()) {
jobj_kdf = json_object_new_object();
generate_keyslot_kdf(jobj_kdf, keyslot_desc.kdf());
json_object_object_add(jobj_keyslot, "kdf", jobj_kdf);
}
/* AF object */
if (keyslot_desc.has_af()) {
jobj_af = json_object_new_object();
generate_keyslot_af(jobj_af, keyslot_desc.af());
json_object_object_add(jobj_keyslot, "af", jobj_af);
}
json_object_object_add(jobj_keyslots, object_id_to_string(keyslot_desc.oid()).c_str(), jobj_keyslot);
}
void LUKS2ProtoConverter::generate_token(struct json_object *jobj_tokens, const token_description &token_desc) {
struct json_object *jobj_token, *jobj_keyslots;
jobj_token = json_object_new_object();
if (token_desc.has_type())
json_object_object_add(jobj_token, "type", json_object_new_string(token_desc.type().c_str()));
if (token_desc.has_key_description())
json_object_object_add(jobj_token, "key_description", json_object_new_string(token_desc.key_description().c_str()));
if (!token_desc.keyslots().empty()) {
jobj_keyslots = json_object_new_array();
for (const object_id oid : token_desc.keyslots()) {
json_object_array_add(jobj_keyslots,
json_object_new_string(object_id_to_string(oid).c_str()));
}
/* Replace or add new keyslots array */
json_object_object_add(jobj_token, "keyslots", jobj_keyslots);
}
json_object_object_add(jobj_tokens, object_id_to_string(token_desc.oid()).c_str(), jobj_token);
}
void LUKS2ProtoConverter::generate_digest(struct json_object *jobj_digests, const digest_description &digest_desc) {
struct json_object *jobj_digest, *jobj_keyslots, *jobj_segments;
jobj_digest = json_object_new_object();
if (digest_desc.has_type())
json_object_object_add(jobj_digest, "type", json_object_new_string(keyslot_kdf_type_to_string(digest_desc.type()).c_str()));
if (!digest_desc.keyslots().empty()) {
jobj_keyslots = json_object_new_array();
for (const object_id oid : digest_desc.keyslots()) {
json_object_array_add(jobj_keyslots,
json_object_new_string(object_id_to_string(oid).c_str()));
}
/* Replace or add new keyslots array */
json_object_object_add(jobj_digest, "keyslots", jobj_keyslots);
}
if (!digest_desc.segments().empty()) {
jobj_segments = json_object_new_array();
for (const object_id oid : digest_desc.segments()) {
json_object_array_add(jobj_segments,
json_object_new_string(object_id_to_string(oid).c_str()));
}
/* Replace or add new segments array */
json_object_object_add(jobj_digest, "segments", jobj_segments);
}
if (digest_desc.has_salt())
json_object_object_add(jobj_digest, "salt", json_object_new_string(digest_desc.salt().c_str()));
if (digest_desc.has_digest())
json_object_object_add(jobj_digest, "digest", json_object_new_string(digest_desc.digest().c_str()));
if (digest_desc.has_hash())
json_object_object_add(jobj_digest, "hash", json_object_new_string(hash_algorithm_to_string(digest_desc.hash()).c_str()));
if (digest_desc.has_iterations())
json_object_object_add(jobj_digest, "iterations", json_object_new_int(digest_desc.iterations()));
json_object_object_add(jobj_digests, object_id_to_string(digest_desc.oid()).c_str(), jobj_digest);
}
std::string LUKS2ProtoConverter::segment_type_to_string(segment_type type) {
switch (type) {
case SEGMENT_TYPE_LINEAR:
return "linear";
case SEGMENT_TYPE_CRYPT:
return "crypt";
}
}
std::string LUKS2ProtoConverter::segment_flag_to_string(segment_flag flag) {
switch (flag) {
case IN_REENCRYPTION:
return "in-reencryption";
case BACKUP_FINAL:
return "backup-final";
case BACKUP_PREVIOUS:
return "backup-previous";
case BACKUP_MOVED_SEGMENT:
return "backup-moved-segment";
}
}
void LUKS2ProtoConverter::generate_segment_integrity(struct json_object *jobj_integrity, const segment_integrity_description &segment_integrity_desc) {
if (segment_integrity_desc.has_type())
json_object_object_add(jobj_integrity, "type", json_object_new_string(segment_integrity_desc.type().c_str()));
if (segment_integrity_desc.has_journal_encryption())
json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string(segment_integrity_desc.journal_encryption().c_str()));
if (segment_integrity_desc.has_journal_integrity())
json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string(segment_integrity_desc.journal_integrity().c_str()));
}
void LUKS2ProtoConverter::generate_segment(struct json_object *jobj_segments, const segment_description &segment_desc) {
json_object *jobj_flags, *jobj_integrity;
json_object *jobj_segment = json_object_new_object();
if (segment_desc.has_type())
json_object_object_add(jobj_segment, "type", json_object_new_string(segment_type_to_string(segment_desc.type()).c_str()));
if (segment_desc.has_offset())
json_object_object_add(jobj_segment, "offset", json_object_new_string(string_uint64_to_string(segment_desc.offset()).c_str()));
if (segment_desc.has_size())
json_object_object_add(jobj_segment, "size", json_object_new_string(string_uint64_to_string(segment_desc.size()).c_str()));
if (!segment_desc.flags().empty()) {
jobj_flags = json_object_new_array();
for (const int flag : segment_desc.flags()) {
json_object_array_add(jobj_flags,
json_object_new_string(segment_flag_to_string(segment_flag(flag)).c_str()));
}
/* Replace or add new flags array */
json_object_object_add(jobj_segment, "flags", jobj_flags);
}
if (segment_desc.has_iv_tweak())
json_object_object_add(jobj_segment, "iv_tweak", json_object_new_string(string_uint64_to_string(segment_desc.iv_tweak()).c_str()));
if (segment_desc.has_encryption())
json_object_object_add(jobj_segment, "encryption", json_object_new_string(segment_desc.encryption().c_str()));
if (segment_desc.has_sector_size())
json_object_object_add(jobj_segment, "sector_size", json_object_new_int(segment_desc.sector_size()));
if (segment_desc.has_integrity()) {
jobj_integrity = json_object_new_object();
generate_segment_integrity(jobj_integrity, segment_desc.integrity());
json_object_object_add(jobj_segment, "integrity", jobj_integrity);
}
json_object_object_add(jobj_segments, object_id_to_string(segment_desc.oid()).c_str(), jobj_segment);
}
void LUKS2ProtoConverter::create_jobj(const LUKS2_both_headers &headers) {
json_object *jobj_keyslots = NULL;
json_object *jobj_digests = NULL;
json_object *jobj_segments = NULL;
json_object *jobj_tokens = NULL;
const json_area_description &json_desc = headers.json_area();
jobj = json_object_new_object();
if (!jobj)
return;
jobj_keyslots = json_object_new_object();
for (const keyslot_description &keyslot_desc : json_desc.keyslots()) {
generate_keyslot(jobj_keyslots, keyslot_desc);
}
json_object_object_add(jobj, "keyslots", jobj_keyslots);
jobj_digests = json_object_new_object();
for (const digest_description &digest_desc : json_desc.digests()) {
generate_digest(jobj_digests, digest_desc);
}
json_object_object_add(jobj, "digests", jobj_digests);
jobj_segments = json_object_new_object();
for (const segment_description &segment_desc : json_desc.segments()) {
generate_segment(jobj_segments, segment_desc);
}
json_object_object_add(jobj, "segments", jobj_segments);
jobj_tokens = json_object_new_object();
for (const token_description &token_desc : json_desc.tokens()) {
generate_token(jobj_tokens, token_desc);
}
json_object_object_add(jobj, "tokens", jobj_tokens);
if (json_desc.has_config()) {
uint64_t hdr_size = json_desc.config().use_primary_hdr_size() ? headers.primary_header().hdr_size() : headers.secondary_header().hdr_size();
generate_config(json_desc.config(), hdr_size - LUKS2_HDR_BIN_LEN, KEYSLOTS_SIZE);
}
}
void LUKS2ProtoConverter::emit_luks2_binary_header(const LUKS2_header &header_proto, int fd, uint64_t offset, uint64_t seqid) {
struct luks2_hdr_disk hdr = {};
char *json_area = NULL;
int r;
if (hd)
crypt_hash_destroy(hd);
if (crypt_hash_init(&hd, "sha256"))
err(EXIT_FAILURE, "crypt_hash_init failed");
r = lseek(fd, offset, SEEK_SET);
if (r == -1)
err(EXIT_FAILURE, "lseek failed");
switch (header_proto.magic()) {
case INVALID:
memset(&hdr.magic, 0, LUKS2_MAGIC_L);
break;
case FIRST:
memcpy(&hdr.magic, LUKS2_MAGIC_1ST, LUKS2_MAGIC_L);
break;
case SECOND:
memcpy(&hdr.magic, LUKS2_MAGIC_2ND, LUKS2_MAGIC_L);
break;
}
hdr.version = cpu_to_be16(header_proto.version());
hdr.hdr_size = cpu_to_be64(header_proto.hdr_size());
hdr.seqid = cpu_to_be64(seqid);
strncpy(hdr.checksum_alg, "sha256", LUKS2_CHECKSUM_ALG_L);
hdr.checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
strncpy(hdr.uuid, "af7f64ea-3233-4581-946b-6187d812841e", LUKS2_UUID_L);
memset(hdr.salt, 1, LUKS2_SALT_L);
if (header_proto.has_selected_offset())
hdr.hdr_offset = cpu_to_be64(header_proto.selected_offset());
else
hdr.hdr_offset = cpu_to_be64(offset);
if (write_all(fd, &hdr, LUKS2_HDR_BIN_LEN) != 0)
err(EXIT_FAILURE, "write_all failed");
if (crypt_hash_write(hd, (char*)&hdr, LUKS2_HDR_BIN_LEN))
err(EXIT_FAILURE, "crypt_hash_write failed");
size_t hdr_json_area_len = header_proto.hdr_size() - LUKS2_HDR_BIN_LEN;
size_t json_text_len;
const char *json_text;
uint8_t csum[LUKS2_CHECKSUM_L];
if (jobj) {
json_text = json_object_to_json_string_ext((struct json_object *)jobj, JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE);
if (!json_text || !*json_text)
err(EXIT_FAILURE, "json_object_to_json_string_ext failed");
json_text_len = strlen(json_text);
size_t write_size = json_text_len > hdr_json_area_len - 1 ? hdr_json_area_len - 1 : json_text_len;
if (write_all(fd, json_text, write_size) != 0)
err(EXIT_FAILURE, "write_all failed");
if (crypt_hash_write(hd, json_text, write_size))
err(EXIT_FAILURE, "crypt_hash_write failed");
for (size_t i = 0; i < (hdr_json_area_len - json_text_len); i++) {
if (crypt_hash_write(hd, "\0", 1))
err(EXIT_FAILURE, "crypt_hash_write failed");
}
}
if (header_proto.use_correct_checksum()) {
if (lseek(fd, offset + OFFSET_OF(luks2_hdr_disk, csum), SEEK_SET) == -1)
err(EXIT_FAILURE, "lseek failed");
int hash_size = crypt_hash_size("sha256");
if (hash_size <= 0)
err(EXIT_FAILURE, "crypt_hash_size failed");
if (crypt_hash_final(hd, (char*)csum, (size_t)hash_size))
err(EXIT_FAILURE, "crypt_hash_final failed");
if (write_all(fd, csum, hash_size) != 0)
err(EXIT_FAILURE, "write_all failed");
}
}
void LUKS2ProtoConverter::set_write_headers_only(bool headers_only) {
write_headers_only = headers_only;
}
void LUKS2ProtoConverter::convert(const LUKS2_both_headers &headers, int fd) {
uint64_t primary_seqid, secondary_seqid;
const char name_pattern[] = "/tmp/test-proto-fuzz.XXXXXX";
int result;
size_t out_size = headers.primary_header().hdr_size() + headers.secondary_header().hdr_size();
if (!write_headers_only)
out_size += KEYSLOTS_SIZE + DATA_SIZE;
result = lseek(fd, out_size - 1, SEEK_SET);
if (result == -1)
err(EXIT_FAILURE, "lseek failed");
result = write(fd, "\0", 1);
if (result != 1)
err(EXIT_FAILURE, "write failed");
result = lseek(fd, 0, SEEK_SET);
if (result == -1)
err(EXIT_FAILURE, "lseek failed");
switch (headers.seqid()) {
case EQUAL:
primary_seqid = 1;
secondary_seqid = 1;
break;
case PRIMARY_GREATER:
primary_seqid = 2;
secondary_seqid = 1;
break;
case SECONDARY_GREATER:
primary_seqid = 1;
secondary_seqid = 2;
break;
}
create_jobj(headers);
emit_luks2_binary_header(headers.primary_header(), fd, 0, primary_seqid);
emit_luks2_binary_header(headers.secondary_header(), fd, headers.primary_header().hdr_size(), secondary_seqid);
}
std::string LUKS2ProtoConverter::config_flag_to_string(config_flag flag) {
switch (flag) {
case CONFIG_FLAG_ALLOW_DISCARDS:
return "allow-discards";
case CONFIG_FLAG_SAME_CPU_CRYPT:
return "same-cpu-crypt";
case CONFIG_FLAG_SUBMIT_FROM_CRYPT_CPUS:
return "submit-from-crypt-cpus";
case CONFIG_FLAG_NO_JOURNAL:
return "no-journal";
case CONFIG_FLAG_NO_READ_WORKQUEUE:
return "no-read-workqueue";
case CONFIG_FLAG_NO_WRITE_WORKQUEUE:
return "no-write-workqueue";
}
}
std::string LUKS2ProtoConverter::config_requirement_to_string(config_requirement requirement) {
switch (requirement) {
case CONFIG_REQUIREMENT_OFFLINE_REENCRYPT:
return "offline-reencrypt";
case CONFIG_REQUIREMENT_ONLINE_REENCRYPT_V2:
return "online-reencrypt-v2";
}
}
void LUKS2ProtoConverter::generate_config(const config_description &config_desc, uint64_t json_size, uint64_t keyslots_size) {
json_object *jobj_config, *jobj_flags, *jobj_requirements, *jobj_mandatory;
jobj_config = json_object_new_object();
json_object_object_add(jobj_config, "json_size", json_object_new_string(std::to_string(json_size).c_str()));
json_object_object_add(jobj_config, "keyslots_size", json_object_new_string(std::to_string(keyslots_size).c_str()));
if (!config_desc.config_flags().empty()) {
jobj_flags = json_object_new_array();
for (const int flag : config_desc.config_flags()) {
json_object_array_add(jobj_flags,
json_object_new_string(config_flag_to_string(config_flag(flag)).c_str()));
}
/* Replace or add new flags array */
json_object_object_add(jobj_config, "flags", jobj_flags);
}
if (!config_desc.requirements().empty()) {
jobj_requirements = json_object_new_object();
jobj_mandatory = json_object_new_array();
for (const int requirement : config_desc.requirements()) {
json_object_array_add(jobj_mandatory,
json_object_new_string(config_requirement_to_string(config_requirement(requirement)).c_str()));
}
/* Replace or add new requirements array */
json_object_object_add(jobj_requirements, "mandatory", jobj_mandatory);
json_object_object_add(jobj_config, "requirements", jobj_requirements);
}
json_object_object_add(jobj, "config", jobj_config);
}
LUKS2ProtoConverter::~LUKS2ProtoConverter() {
json_object_put(jobj);
if (hd)
crypt_hash_destroy(hd);
}
} // namespace LUKS2_proto

View File

@@ -0,0 +1,91 @@
/*
* cryptsetup LUKS2 custom mutator fuzz target
*
* Copyright (C) 2022 Daniel Zatovic <daniel.zatovic@gmail.com>
* Copyright (C) 2022 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef LUKS2_PROTO_CONVERTER_H_
#define LUKS2_PROTO_CONVERTER_H_
#include <sstream>
#include <string>
#include <json-c/json.h>
#include "LUKS2.pb.h"
extern "C" {
#include "crypto_backend/crypto_backend.h"
}
namespace LUKS2_proto {
class LUKS2ProtoConverter {
public:
~LUKS2ProtoConverter();
std::string string_uint64_to_string(const string_uint64 &str_u64);
std::string hash_algorithm_to_string(const hash_algorithm type);
std::string object_id_to_string(const object_id &oid);
std::string keyslot_area_type_to_string(const keyslot_area_type type);
std::string keyslot_kdf_type_to_string(const keyslot_kdf_type type);
std::string reencrypt_keyslot_mode_to_string(const reencrypt_keyslot_mode mode);
std::string keyslot_type_to_string(const keyslot_type type);
std::string reencrypt_keyslot_direction_to_string(const reencrypt_keyslot_direction direction);
std::string keyslot_af_type_to_string(const keyslot_af_type type);
std::string config_flag_to_string(config_flag flag);
std::string config_requirement_to_string(config_requirement requirements);
std::string segment_type_to_string(segment_type type);
std::string segment_flag_to_string(segment_flag flag);
void generate_keyslot(struct json_object *jobj_keyslots, const keyslot_description &keyslot_desc);
void generate_keyslot_area(struct json_object *jobj_area, const keyslot_area_description &keyslot_area_desc);
void generate_keyslot_kdf(struct json_object *jobj_kdf, const keyslot_kdf_description &keyslot_kdf_desc);
void generate_keyslot_af(struct json_object *jobj_af, const keyslot_af_description &keyslot_af_desc);
void generate_token(struct json_object *jobj_tokens, const token_description &token_desc);
void generate_digest(struct json_object *jobj_digests, const digest_description &digest_desc);
void generate_segment_integrity(struct json_object *jobj_integrity, const segment_integrity_description &segment_integrity_desc);
void generate_segment(struct json_object *jobj_segments, const segment_description &segment_desc);
void generate_config(const config_description &config_desc, uint64_t json_size, uint64_t keyslots_size);
void create_jobj(const LUKS2_both_headers &headers, uint64_t hdr_size);
void emit_luks2_binary_header(uint64_t offset, uint64_t seqid, bool is_primary, uint64_t hdr_size);
void convert(const LUKS2_both_headers &headers, int fd);
void create_jobj(const LUKS2_both_headers &headers);
void emit_luks2_binary_header(const LUKS2_header &header_proto, int fd, uint64_t offset, uint64_t seqid);
void set_write_headers_only(bool headers_only);
const uint8_t *get_out_buffer();
const size_t get_out_size();
static const uint64_t KEYSLOTS_SIZE = 3 * 1024 * 1024;
static const uint64_t DATA_SIZE = 16 * 1024 * 1024;
private:
bool write_headers_only = false;
struct crypt_hash *hd = NULL;
struct ::json_object *jobj = NULL;
};
} // namespace LUKS2_proto
#endif // LUKS2_PROTO_CONVERTER_H_