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:
committed by
Norman Feske
parent
a7e6bf698d
commit
065f58af22
75
run/blk_shred.run
Normal file
75
run/blk_shred.run
Normal 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
277
src/app/blk_shred/main.cc
Normal 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);
|
||||||
|
}
|
||||||
3
src/app/blk_shred/target.mk
Normal file
3
src/app/blk_shred/target.mk
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
TARGET = blk_shred
|
||||||
|
LIBS = base jitterentropy libpcg_random
|
||||||
|
SRC_CC = main.cc
|
||||||
Reference in New Issue
Block a user