app/blk_shred: fill a block device with noise

The blk_shred utitity write over an entire Block session region with
noise suitable as a ciphertext background. See the 'sequence' utility
for making multiple write passes.

Fix #81
This commit is contained in:
Emery Hemingway
2017-08-26 10:44:13 -05:00
committed by Norman Feske
parent a7e6bf698d
commit 065f58af22
3 changed files with 355 additions and 0 deletions

75
run/blk_shred.run Normal file
View File

@@ -0,0 +1,75 @@
#
# \brief Scenario to wipe hard drive in target machine
# \author Emery Hemingway
# \date 2017-08-26
#
#
# Build
#
set build_components { app/blk_shred }
source ${genode_dir}/repos/base/run/platform_drv.inc
append_platform_drv_build_components
build $build_components
create_boot_directory
import_from_depot \
genodelabs/src/[base_src] \
genodelabs/src/init \
genodelabs/src/ahci_drv \
#
# Generate config
#
set config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="RAM" quantum="1M" />
<provides><service name="Timer" /></provides>
</start>}
append_platform_drv_config
append config {
<start name="ahci_drv">
<resource name="RAM" quantum="2M" />
<provides><service name="Block" /></provides>
<config>
<policy label_prefix="blk_shred" device="0" writeable="yes"/>
</config>
</start>
<start name="blk_shred">
<resource name="RAM" quantum="4M" />
</start>
</config> }
install_config $config
#
# Boot modules
#
set boot_modules { blk_shred }
append_platform_drv_boot_modules
build_boot_image $boot_modules
run_genode_until forever

277
src/app/blk_shred/main.cc Normal file
View File

@@ -0,0 +1,277 @@
/*
* \brief Block shredder
* \author Emery Hemingway
* \date 2017-08-26
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/**
* TODO:
* - Progress report
*/
/* Genode includes */
#include <block_session/connection.h>
#include <timer_session/connection.h>
#include <base/heap.h>
#include <base/component.h>
#include <base/sleep.h>
/* Jitterentropy includes */
#include <jitterentropy.h>
/* PCG includes */
#include <pcg_variants.h>
namespace Blk_shred {
using namespace Genode;
using namespace Block;
struct Main;
enum {
/* XXX: part_blk has a fixed backend buffer that limits our packet size */
PKT_SIZE = 1 << 20,
PKT_BUF_SIZE = PKT_SIZE*2 + (32<<10)
};
uint64_t pcg_init[2] = PCG32_INITIALIZER;
}
struct Blk_shred::Main
{
Genode::Env &env;
Timer::Connection timer { env };
Heap heap { env.pd(), env.rm() };
Allocator_avl packet_alloc { &heap };
Block::Connection blk {
env, &packet_alloc, PKT_BUF_SIZE };
Block::Session::Tx::Source &pkt_source = *blk.tx();
size_t blk_size = 0;
Block::sector_t blk_total= 0;
rand_data *jent;
pcg32_random_t pcg;
template <typename... ARGS>
void die(ARGS &&... args)
{
/* wipe noise state */
seed_noise();
error(args...);
env.parent().exit(~0);
sleep_forever();
}
void seed_noise()
{
/* read entropy into initialization variables */
jent_read_entropy(jent, (char*)&pcg_init, sizeof(pcg_init));
pcg_init[1] |= 1;
pcg32_srandom_r(&pcg, pcg_init[0], pcg_init[1]);
}
Main(Genode::Env &env) : env(env)
{
/***********************
** Seed noise source **
***********************/
jitterentropy_init(heap);
if (jent_entropy_init() != 0)
die("jitterentropy library could not be initialized!");
jent = jent_entropy_collector_alloc(0, 0);
if (!jent)
die("jitterentropy could not allocate entropy collector!");
seed_noise();
/****************************************
** Initialize block device parameters **
****************************************/
{
Block::Session::Operations ops;
blk.info(&blk_total, &blk_size, &ops);
if (!ops.supported(Block::Packet_descriptor::WRITE))
die("block device not writeable!");
}
}
~Main()
{
/* wipe noise state */
seed_noise();
jent_entropy_collector_free(jent);
}
void submit_noise(Block::Packet_descriptor const &pkt)
{
uint32_t *buffer = (uint32_t*)pkt_source.packet_content(pkt);
for (size_t i = 0; i < ((pkt.block_count()*blk_size) / sizeof(uint32_t)); ++i)
buffer[i] = pcg32_random_r(&pcg);
pkt_source.submit_packet(pkt);
}
void shred()
{
float const mbytes = (float(blk_total) * float(blk_size)) / (1<<20);
log("shredding ", mbytes/(1<<10), " GiB...");
auto start_ms = timer.elapsed_ms();
size_t const blk_per_pkt = PKT_SIZE / blk_size;
size_t const bytes_per_pkt = blk_per_pkt * blk_size;
/*
* send two packets in succession, the first write
* aligns those that follow with end of the device
*/
Block::sector_t blk_offset = blk_total % blk_per_pkt;
if (blk_offset == 0)
blk_offset = blk_per_pkt;
{
Block::Packet_descriptor pkt(
pkt_source.alloc_packet(bytes_per_pkt),
Block::Packet_descriptor::WRITE,
0, blk_offset);
submit_noise(pkt);
}
{
Block::Packet_descriptor pkt(
pkt_source.alloc_packet(bytes_per_pkt),
Block::Packet_descriptor::WRITE,
blk_offset, blk_per_pkt);
submit_noise(pkt);
blk_offset += blk_per_pkt;
}
while (true) {
Block::Packet_descriptor const ack = pkt_source.get_acked_packet();
if (!ack.succeeded())
error("ack indicates failure ", ack.block_number(),"/",blk_total);
pkt_source.release_packet(ack);
if (blk_offset >= blk_total) break;
/* reuse packet buffer region */
Block::Packet_descriptor const pkt(
ack, Block::Packet_descriptor::WRITE,
blk_offset, blk_per_pkt);
submit_noise(pkt);
blk_offset += blk_per_pkt;
}
Block::Packet_descriptor const ack = pkt_source.get_acked_packet();
if (!ack.succeeded())
error("ack indicates failure ", ack.block_number(),"/",blk_total);
float seconds = (timer.elapsed_ms() - start_ms) / 1000;
log("shred complete, ", mbytes / seconds, " MiB/s");
}
/**
* Verify a block, PCG must first be seeked into position
*/
bool verify_block(Block::sector_t sector)
{
Block::Packet_descriptor pkt(
pkt_source.alloc_packet(blk_size),
Block::Packet_descriptor::READ, sector, 1);
pkt_source.submit_packet(pkt);
pkt = pkt_source.get_acked_packet();
pkt_source.release_packet(pkt);
if (!pkt.succeeded())
die("error while reading back sector ", sector);
uint32_t *buffer = (uint32_t*)pkt_source.packet_content(pkt);
for (size_t i = 0; i < (blk_size / sizeof(uint32_t)); ++i) {
if (buffer[i] != pcg32_random_r(&pcg))
die("sector ", sector, " is invalid");
}
return true;
}
/**
* Verify random blocks
*/
void verify()
{
/* a weak RNG to generate skips */
pcg32_random_t skip_gen;
pcg32_srandom_r(&skip_gen, pcg32_random_r(&pcg), pcg32_random_r(&pcg));
/* reset the noise generator */
pcg32_srandom_r(&pcg, pcg_init[0], pcg_init[1]);
/* make jumps of approximately 1 Mib */
int const max_jump = (2<<20) / blk_size;
int const step = blk_size / sizeof(uint32_t);
Block::sector_t sector_offset = 0;
unsigned long count = 0;
/* verify the first block and loop */
if (!verify_block(sector_offset)) return;
++count;
/* random jump loop */
while (true) {
/* make a bounded random jumps */
Block::sector_t skip =
pcg32_boundedrand_r(&skip_gen, max_jump)+1;
if (sector_offset+skip >= blk_total)
break;
/* move the sector offset ahead */
sector_offset += skip;
/* seek the noise source ahead for the next read */
pcg32_advance_r(&pcg, (skip-1)*step);
if (!verify_block(sector_offset)) return;
++count;
}
/* verify the last block */
pcg32_advance_r(&pcg, (blk_total-(sector_offset+2))*step);
if (!verify_block(blk_total-1)) return;
++count;
log(count, " blocks passed random spot check");
}
};
void Component::construct(Genode::Env &env)
{
Blk_shred::Main main(env);
main.shred();
main.verify();
env.parent().exit(0);
}

View File

@@ -0,0 +1,3 @@
TARGET = blk_shred
LIBS = base jitterentropy libpcg_random
SRC_CC = main.cc