Commit c2cf33af authored by Sami Tolvanen's avatar Sami Tolvanen Committed by Milan Broz

WIP: Add support for verity FEC.

parent eb593111
......@@ -456,6 +456,7 @@ CS_STR_WITH([verity-hash], [hash function for verity mode], [sha256])
CS_NUM_WITH([verity-data-block], [data block size for verity mode], [4096])
CS_NUM_WITH([verity-hash-block], [hash block size for verity mode], [4096])
CS_NUM_WITH([verity-salt-size], [salt size for verity mode], [32])
CS_NUM_WITH([verity-fec-roots], [parity bytes for verity FEC], [2])
dnl ==========================================================================
......
......@@ -304,7 +304,9 @@ struct crypt_params_verity {
const char *hash_name; /**< hash function */
const char *data_device; /**< data_device (CRYPT_VERITY_CREATE_HASH) */
const char *hash_device; /**< hash_device (output only) */
const char *fec_device; /**< fec_device (output only) */
const char *salt; /**< salt */
int fec_roots; /**< fec_roots */
uint32_t salt_size; /**< salt size (in bytes) */
uint32_t hash_type; /**< in-kernel hashing type */
uint32_t data_block_size; /**< data block size (in bytes) */
......
......@@ -33,6 +33,7 @@
#include "luks.h"
#include "loopaes.h"
#include "verity.h"
#include "fec.h"
#include "tcrypt.h"
#include "internal.h"
......@@ -1054,6 +1055,8 @@ static int _crypt_format_verity(struct crypt_device *cd,
if (!(cd->u.verity.hdr.hash_name = strdup(params->hash_name)))
return -ENOMEM;
cd->u.verity.hdr.data_device = NULL;
cd->u.verity.hdr.fec_device = params->fec_device;
cd->u.verity.hdr.fec_roots = params->fec_roots;
cd->u.verity.hdr.data_block_size = params->data_block_size;
cd->u.verity.hdr.hash_block_size = params->hash_block_size;
cd->u.verity.hdr.hash_area_offset = params->hash_area_offset;
......@@ -1092,6 +1095,13 @@ static int _crypt_format_verity(struct crypt_device *cd,
cd->u.verity.uuid,
&cd->u.verity.hdr);
}
if (params->fec_device) {
r = VERITY_FEC_create(cd, &cd->u.verity.hdr);
if (r)
return r;
}
return r;
}
......
......@@ -7,7 +7,16 @@ libverity_la_CFLAGS = -Wall $(AM_CFLAGS) @CRYPTO_CFLAGS@
libverity_la_SOURCES = \
verity_hash.c \
verity.c \
verity.h
verity.h \
fec.c \
fec.h \
libfec/encode_rs_char.c \
libfec/encode_rs.h \
libfec/init_rs_char.c \
libfec/init_rs.h \
libfec/rs-common.h \
libfec/char.h \
libfec/fec.h
AM_CPPFLAGS = -include config.h \
-I$(top_srcdir)/lib \
......
/*
* dm-verity Forward Error Correction (FEC) support
*
* Copyright (C) 2015, Google, Inc. All rights reserved.
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This file 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "verity.h"
#include "internal.h"
#include "fec.h"
#include "libfec/fec.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) \
sizeof(array) / sizeof(array[0])
#endif
#define FEC_SIGNATURE "fec...\0\0"
#define FEC_VERSION 0
struct fec_sb {
uint8_t signature[8]; /* "fec...\0\0" */
uint32_t version; /* superblock version */
uint8_t _pad1[4];
uint32_t roots; /* parity bytes */
uint64_t blocks; /* number of data blocks */
uint8_t _pad2[484];
} __attribute__((packed));
struct fec_input_device {
struct device *device;
int fd;
uint64_t start;
uint64_t count;
};
struct fec_context {
int rsn;
int roots;
uint64_t size;
uint64_t blocks;
uint64_t rounds;
uint32_t block_size;
struct fec_input_device *inputs;
size_t ninputs;
};
/* computes ceil(x / y) */
static inline uint64_t FEC_div_round_up(uint64_t x, uint64_t y)
{
return (x / y) + (x % y > 0 ? 1 : 0);
}
/* writes the entire data buffer to fd */
int FEC_write(int fd, const void *p, size_t count)
{
const uint8_t *data = (const uint8_t *)p;
size_t left = count;
while (left > 0) {
ssize_t n = TEMP_FAILURE_RETRY(write(fd, data, left));
if (n == -1)
return -errno;
data += n;
left -= n;
}
return 0;
}
/* reads count bytes to data from fd at offset */
int FEC_pread(int fd, uint8_t *data, size_t count, uint64_t offset)
{
size_t left = count;
while (left > 0) {
ssize_t n = TEMP_FAILURE_RETRY(pread64(fd, data, left,
offset));
if (n <= 0)
return -errno;
data += n;
left -= n;
offset += n;
}
return 0;
}
/* returns a physical offset for the given RS offset */
static inline uint64_t FEC_interleave(struct fec_context *ctx, uint64_t offset)
{
return (offset / ctx->rsn) +
(offset % ctx->rsn) * ctx->rounds * ctx->block_size;
}
/* returns data for a byte at the specified RS offset */
int FEC_read_interleaved(struct fec_context *ctx, uint64_t i, uint8_t *output,
size_t count)
{
size_t n;
uint64_t offset = FEC_interleave(ctx, i);
/* offsets outside input area are assumed to contain zeros */
if (offset >= ctx->size) {
memset(output, 0, count);
return 0;
}
/* find the correct input device and read from it */
for (n = 0; n < ctx->ninputs; ++n) {
if (offset >= ctx->inputs[n].count) {
offset -= ctx->inputs[n].count;
continue;
}
return FEC_pread(ctx->inputs[n].fd, output, count,
ctx->inputs[n].start + offset);
}
/* should never be reached */
return -1;
}
static int FEC_write_sb(struct fec_context *ctx, int fd)
{
struct fec_sb sb;
memset(&sb, 0, sizeof(sb));
memcpy(&sb.signature, FEC_SIGNATURE, sizeof(sb.signature));
sb.version = FEC_VERSION;
sb.roots = ctx->roots;
sb.blocks = ctx->size / ctx->block_size;
return FEC_write(fd, &sb, sizeof(sb));
}
/* encodes inputs to fd */
static int FEC_encode_inputs(struct crypt_device *cd,
struct crypt_params_verity *params,
struct fec_input_device *inputs,
size_t ninputs, int fd)
{
int r;
int i;
struct fec_context ctx;
uint32_t b;
uint64_t n;
uint8_t parity[params->fec_roots];
uint8_t rs_block[FEC_RSM];
uint8_t *buf = NULL;
void *rs;
/* initialize parameters */
ctx.roots = params->fec_roots;
ctx.rsn = FEC_RSM - ctx.roots;
ctx.block_size = params->data_block_size;
ctx.inputs = inputs;
ctx.ninputs = ninputs;
rs = init_rs_char(FEC_PARAMS(ctx.roots));
if (!rs) {
log_err(cd, _("Failed to allocate RS context.\n"));
return -ENOMEM;
}
/* calculate the total area covered by error correction codes */
ctx.size = 0;
for (n = 0; n < ctx.ninputs; ++n)
ctx.size += ctx.inputs[n].count;
/* each byte in a data block is covered by a different code */
ctx.blocks = FEC_div_round_up(ctx.size, ctx.block_size);
ctx.rounds = FEC_div_round_up(ctx.blocks, ctx.rsn);
buf = malloc(ctx.rounds * ctx.block_size * ctx.roots);
if (!buf) {
log_err(cd, _("Failed to allocate buffer.\n"));
return -ENOMEM;
}
/* write superblock */
r = FEC_write_sb(&ctx, fd);
if (r) {
log_err(cd, _("Failed to write FEC superblock.\n"));
goto out;
}
/* encode input */
for (n = 0; n < ctx.rounds; ++n) {
for (i = 0; i < ctx.rsn; ++i) {
r = FEC_read_interleaved(&ctx,
n * ctx.rsn * ctx.block_size + i,
&buf[i * ctx.block_size],
ctx.block_size);
if (r) {
log_err(cd, _("Failed to read RS block %"
PRIu64 " byte %d.\n"), n, i);
goto out;
}
}
for (b = 0; b < ctx.block_size; ++b) {
for (i = 0; i < ctx.rsn; ++i)
rs_block[i] = buf[i * ctx.block_size + b];
encode_rs_char(rs, rs_block, parity);
r = FEC_write(fd, parity, sizeof(parity));
if (r) {
log_err(cd, _("Failed to write parity for RS "
"block %" PRIu64 ".\n"), n);
goto out;
}
}
}
out:
if (rs)
free_rs_char(rs);
free(buf);
return r;
}
static int FEC_open_inputs(struct crypt_device *cd,
struct fec_input_device *inputs,
size_t ninputs)
{
size_t n;
for (n = 0; n < ninputs; ++n)
inputs[n].fd = -1;
for (n = 0; n < ninputs; ++n) {
inputs[n].fd =
TEMP_FAILURE_RETRY(open(device_path(inputs[n].device),
O_RDWR));
if (inputs[n].fd == -1) {
log_err(cd, _("Failed to open %s.\n"),
device_path(inputs[n].device));
return -errno;
}
}
return 0;
}
int VERITY_FEC_create(struct crypt_device *cd,
struct crypt_params_verity *params)
{
int r;
int fd = -1;
struct fec_input_device inputs[2];
/* validate parameters */
if (params->data_block_size != params->hash_block_size) {
log_err(cd, _("Block sizes must match for FEC.\n"));
return -EINVAL;
}
if (params->fec_roots > FEC_RSM - FEC_MIN_RSN ||
params->fec_roots < FEC_RSM - FEC_MAX_RSN) {
log_err(cd, _("Invalid number of parity bytes.\n"));
return -EINVAL;
}
/* open the output device */
fd = TEMP_FAILURE_RETRY(open(params->fec_device, O_RDWR | O_CLOEXEC));
if (fd == -1) {
log_err(cd, _("Cannot open device %s.\n"), params->fec_device);
return -errno;
}
/* input devices */
memset(inputs, 0, sizeof(inputs));
inputs[0].device = crypt_data_device(cd);
inputs[0].count = params->data_size * params->data_block_size;
/* cover the entire hash device starting from hash_offset */
inputs[1].device = crypt_metadata_device(cd);
inputs[1].start = VERITY_hash_offset_block(params) *
params->data_block_size;
r = device_size(crypt_metadata_device(cd), &inputs[1].count);
if (r) {
log_err(cd, _("Failed to determine size for device %s.\n"),
device_path(crypt_metadata_device(cd)));
goto out;
}
inputs[1].count -= inputs[1].start;
r = FEC_open_inputs(cd, inputs, ARRAY_SIZE(inputs));
if (r)
goto out;
r = FEC_encode_inputs(cd, params, inputs, ARRAY_SIZE(inputs), fd);
out:
if (inputs[0].fd != -1)
TEMP_FAILURE_RETRY(close(inputs[0].fd));
if (inputs[1].fd != -1)
TEMP_FAILURE_RETRY(close(inputs[1].fd));
if (fd != -1)
TEMP_FAILURE_RETRY(close(fd));
return r;
}
/*
* dm-verity Forward Error Correction (FEC) support
*
* Copyright (C) 2015, Google, Inc. All rights reserved.
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This file 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _FEC_H
#define _FEC_H
#include <unistd.h>
/* ecc parameters */
#define FEC_RSM 255
#define FEC_MIN_RSN 231
#define FEC_MAX_RSN 253
/* parameters to init_rs_char */
#define FEC_PARAMS(roots) \
8, /* symbol size in bits */ \
0x11d, /* field generator polynomial coefficients */ \
0, /* first root of the generator */ \
1, /* primitive element to generate polynomial roots */ \
(roots), /* polynomial degree (number of roots) */ \
0 /* padding bytes at the front of shortened block */
struct crypt_device;
struct crypt_params_verity;
int VERITY_FEC_create(struct crypt_device *cd,
struct crypt_params_verity *params);
#endif
/* Stuff specific to the 8-bit symbol version of the general purpose RS codecs
*
* Copyright 2003, Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
typedef unsigned char data_t;
#define MODNN(x) modnn(rs,x)
#define MM (rs->mm)
#define NN (rs->nn)
#define ALPHA_TO (rs->alpha_to)
#define INDEX_OF (rs->index_of)
#define GENPOLY (rs->genpoly)
#define NROOTS (rs->nroots)
#define FCR (rs->fcr)
#define PRIM (rs->prim)
#define IPRIM (rs->iprim)
#define PAD (rs->pad)
#define A0 (NN)
/* The guts of the Reed-Solomon encoder, meant to be #included
* into a function body with the following typedefs, macros and variables supplied
* according to the code parameters:
* data_t - a typedef for the data symbol
* data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded
* data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols
* NROOTS - the number of roots in the RS code generator polynomial,
* which is the same as the number of parity symbols in a block.
Integer variable or literal.
*
* NN - the total number of symbols in a RS block. Integer variable or literal.
* PAD - the number of pad symbols in a block. Integer variable or literal.
* ALPHA_TO - The address of an array of NN elements to convert Galois field
* elements in index (log) form to polynomial form. Read only.
* INDEX_OF - The address of an array of NN elements to convert Galois field
* elements in polynomial form to index (log) form. Read only.
* MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
* GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form
* The memset() and memmove() functions are used. The appropriate header
* file declaring these functions (usually <string.h>) must be included by the calling
* program.
* Copyright 2004, Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#undef A0
#define A0 (NN) /* Special reserved value encoding zero in index form */
{
int i, j;
data_t feedback;
memset(parity,0,NROOTS*sizeof(data_t));
for(i=0;i<NN-NROOTS-PAD;i++){
feedback = INDEX_OF[data[i] ^ parity[0]];
if(feedback != A0){ /* feedback term is non-zero */
#ifdef UNNORMALIZED
/* This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
* always be for the polynomials constructed by init_rs()
*/
feedback = MODNN(NN - GENPOLY[NROOTS] + feedback);
#endif
for(j=1;j<NROOTS;j++)
parity[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS-j])];
}
/* Shift */
memmove(&parity[0],&parity[1],sizeof(data_t)*(NROOTS-1));
if(feedback != A0)
parity[NROOTS-1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])];
else
parity[NROOTS-1] = 0;
}
}
/* Reed-Solomon encoder
* Copyright 2002, Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#include <string.h>
#include "char.h"
#include "rs-common.h"
void encode_rs_char(void *p,data_t *data, data_t *parity){
struct rs *rs = (struct rs *)p;
#include "encode_rs.h"
}
This diff is collapsed.
/* Common code for intializing a Reed-Solomon control block (char or int symbols)
* Copyright 2004 Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#undef NULL
#define NULL ((void *)0)
{
int i, j, sr,root,iprim;
rs = NULL;
/* Check parameter ranges */
if(symsize < 0 || symsize > 8*(int)sizeof(data_t)){
goto done;
}
if(fcr < 0 || fcr >= (1<<symsize))
goto done;
if(prim <= 0 || prim >= (1<<symsize))
goto done;
if(nroots < 0 || nroots >= (1<<symsize))
goto done; /* Can't have more roots than symbol values! */
if(pad < 0 || pad >= ((1<<symsize) -1 - nroots))
goto done; /* Too much padding */
rs = (struct rs *)calloc(1,sizeof(struct rs));
if(rs == NULL)
goto done;
rs->mm = symsize;
rs->nn = (1<<symsize)-1;
rs->pad = pad;
rs->alpha_to = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
if(rs->alpha_to == NULL){
free(rs);
rs = NULL;
goto done;
}
rs->index_of = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
if(rs->index_of == NULL){
free(rs->alpha_to);
free(rs);
rs = NULL;
goto done;
}
/* Generate Galois field lookup tables */
rs->index_of[0] = A0; /* log(zero) = -inf */
rs->alpha_to[A0] = 0; /* alpha**-inf = 0 */
sr = 1;
for(i=0;i<rs->nn;i++){
rs->index_of[sr] = i;
rs->alpha_to[i] = sr;
sr <<= 1;
if(sr & (1<<symsize))
sr ^= gfpoly;
sr &= rs->nn;
}
if(sr != 1){
/* field generator polynomial is not primitive! */
free(rs->alpha_to);
free(rs->index_of);
free(rs);
rs = NULL;
goto done;
}
/* Form RS code generator polynomial from its roots */
rs->genpoly = (data_t *)malloc(sizeof(data_t)*(nroots+1));
if(rs->genpoly == NULL){
free(rs->alpha_to);
free(rs->index_of);
free(rs);
rs = NULL;
goto done;
}
rs->fcr = fcr;
rs->prim = prim;
rs->nroots = nroots;
/* Find prim-th root of 1, used in decoding */
for(iprim=1;(iprim % prim) != 0;iprim += rs->nn)
;
rs->iprim = iprim / prim;
rs->genpoly[0] = 1;
for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) {
rs->genpoly[i+1] = 1;
/* Multiply rs->genpoly[] by @**(root + x) */
for (j = i; j > 0; j--){
if (rs->genpoly[j] != 0)
rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)];
else
rs->genpoly[j] = rs->genpoly[j-1];
}
/* rs->genpoly[0] can never be zero */
rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)];
}
/* convert rs->genpoly[] to index form for quicker encoding */
for (i = 0; i <= nroots; i++)
rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
done:;
}
/* Initialize a RS codec
*
* Copyright 2002 Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#include <stdlib.h>
#include "char.h"
#include "rs-common.h"
void free_rs_char(void *p){
struct rs *rs = (struct rs *)p;
free(rs->alpha_to);
free(rs->index_of);
free(rs->genpoly);
free(rs);
}
/* Initialize a Reed-Solomon codec
* symsize = symbol size, bits
* gfpoly = Field generator polynomial coefficients
* fcr = first root of RS code generator polynomial, index form
* prim = primitive element to generate polynomial roots
* nroots = RS code generator polynomial degree (number of roots)
* pad = padding bytes at front of shortened block
*/
void *init_rs_char(int symsize,int gfpoly,int fcr,int prim,
int nroots,int pad){
struct rs *rs;
#include "init_rs.h"
return rs;
}
/* Stuff common to all the general-purpose Reed-Solomon codecs
* Copyright 2004 Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
/* Reed-Solomon codec control block */
struct rs {
int mm; /* Bits per symbol */
int nn; /* Symbols per block (= (1<<mm)-1) */
data_t *alpha_to; /* log lookup table */
data_t *index_of; /* Antilog lookup table */
data_t *genpoly; /* Generator polynomial */
int nroots; /* Number of generator roots = number of parity symbols */
int fcr; /* First consecutive root, index form */
int prim; /* Primitive element, index form */
int iprim; /* prim-th root of 1, index form */
int pad; /* Padding bytes in shortened block */
};
static inline int modnn(struct rs *rs,int x){
while (x >= rs->nn) {
x -= rs->nn;
x = (x >> rs->mm) + (x & rs->nn);
}
return x;
}
......@@ -25,6 +25,8 @@
static int use_superblock = 1;
static const char *fec_device = NULL;
static int fec_roots = DEFAULT_VERITY_FEC_ROOTS;
static const char *hash_algorithm = NULL;
static int hash_type = 1;