linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch] Adding Secure Deletion to UBIFS
@ 2012-02-09 15:24 Joel Reardon
  2012-02-13 16:54 ` Artem Bityutskiy
                   ` (2 more replies)
  0 siblings, 3 replies; 55+ messages in thread
From: Joel Reardon @ 2012-02-09 15:24 UTC (permalink / raw)
  To: linux-mtd; +Cc: linux-kernel, linux-fsdevel

[-- Attachment #1: Type: TEXT/PLAIN, Size: 88435 bytes --]

This patch provides efficient secure deletion for UBIFS.

The general problem of secure deletion on flash memory is the disparity between 
I/O size and erase block size---the inability to perform in-place updates means 
that simply overwriting data with a new value is insufficient. Moreover, 
forcing the garbage collection of every erase block that contains some deleted 
entry is woefully inefficient and reduces the lifetime of the flash memory.
Multiply overwriting deleted pages with zeros (but not erasing it) technically 
works but it is outside the specification, can cause data errors in other 
pages, and does not work with modern, larger MLC types of memory.

The solution here is to encrypt every data node with a different encryption 
key, and colocate the keys in a small (0.5-1 %) key storage area (KSA). The 
erase blocks of the KSA are frequently purged to remove deleted keys---each 
block of the old KSA is replaced with a new block, where all the used keys 
remain in the same position in the block, but all the unused and deleted keys 
are replaced with fresh, random data. This is done with UBI's atomic update, so 
it is not possible to lose any needed keys during this update, and the actual 
addition wear caused by more frequent erasure is evenly levelled over the 
device.

Each data nodes includes a reference to a key in the KSA. This key is read and 
used to decrypt the data. When a new data node is written, an unused key is 
selected from the KSA and used to encrypt the data node. The reference to the 
key is then included with the node. The keys in the KSA are written before 
actually being used to encrypt data. To securely delete a data node, we simply 
mark the corresponding key position as deleted, and during the next purging 
operation the KSA erase block that contains the key is then updated to a 
version that does not contain the key.

The data of data node is encrypted/decrypted in the compression/decompression 
functions respectively. A key parameter is passed in---if this is NULL, then no 
cryption is performed. If it points to a buffer, then it is interpreted as a 
128bit AES key. Counter mode is used with an IV of 0. Since each data block is 
encrypted with a unique key---each key only encrypts one item only once and 
sequentially---we do not need to worry about storing and generating IVs.

Key state is managed entirely in the TNC. When nodes are added to the TNC, they 
include a reference to a logical position in the key state map (KSA logical 
block number, offset). The TNC adding nodes will mark the key as used, removing 
a node will mark it as deleted, updating a node marks old as deleted and new as 
used. After purging, all deleted keys are replaced with new data and their key 
positions are marked as unused. Additionally, a checkpoint of the key state map 
is commited. When mounting the most recent checkpoint is loaded and then, if 
the journal is non-empty, the uncommited entries are replayed---thus updating 
the TNC and therefore correcting the key state map. The znode now includes the 
position in the KSA.

If power is lost during a commiting/purging of the key state map, then some KSA 
erase blocks will be updated and others will not be. However, in all possible 
failures, remounting the device guarantees the following three properties 
(which refer to the correctness of the key state map and are critical to the 
security of the system:
- every unused key must not decrypt any data node---either valid or invalid
- every used key must have exactly one data node it can decrypt and this data 
node must be valid according to the index
- every deleted key must not decrypt any data node that is valid according to 
the index.

I have a paper with lots more details and a proper treatment on power failure 
are examined.

Summary of changes to UBIFS

Mounting
mount the file system
  - allocate and initialize the keymap
  - deallocate keymap if an error occurs
  - read the size of the KSA from the master node
unmount the file system
- deallocate the keymap
create default file system
- use storage medium's geometry to compute the
   required KSA size
- store this information in the master node
- call keymap's initialize KSA routine

Commit
- call the keymap's purging routine

Input/Output
write data
- obtain an unused key position from the keymap - store the key's position in 
the data node's header - use the keymap and key position to look up the actual 
key - provide the key to the compress function recompute data after truncation
- obtain the original key, decrypt the data
- obtain a new key, encrypt the data with it after truncating
read data
- use the keymap and data node's key position to look up the actual key - 
provide the key to the decompress function

Tree Node Cache
add/update the TNC
- provide a key position when adding data nodes
- store the key position inside TNC entries
- mark key position as used
- if also updating, mark the old key position as deleted before storing the new 
position
delete/truncate the TNC
- when removing a data node from the TNC, mark the stored key position as 
deleted committing the TNC
- read and write key position to stored tree nodes

Testing was done using integck in mtd-utils for both drive mounted with the 
enhancement and without, where nandsim was used as the test flash device. Power 
cut testing was also performed. Manual testing of mounting and use was 
performed for both secure deletion and without. A suit of unit tests for the 
new keymap component exist but are not included. (What should I do with them?)

Things still to address:
- a consistency check for the three properties on the correctness of the key 
state map, along with the resulting ability to recover from a missing 
checkpoint by recomputing the key state map
- a change to an on-flash data structure renders it incompatible with older 
version, so this needs to be fixed

Joel Reardon

--------------------------------------------------
Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/commit.c linux-3.2.1-ubifsec/fs/ubifs/commit.c
--- linux-3.2.1-vanilla/fs/ubifs/commit.c	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/commit.c	2012-02-08 16:54:49.097506842 
+0100
@@ -114,7 +114,11 @@ static int do_commit(struct ubifs_info *

  	dbg_cmt("start");
  	ubifs_assert(!c->ro_media && !c->ro_mount);
-
+	if (c->use_ubifsec) {
+		struct timeval tv;
+		do_gettimeofday(&tv);
+		c->next_commit = tv.tv_sec + c->commit_interval;
+	}
  	if (c->ro_error) {
  		err = -EROFS;
  		goto out_up;
@@ -134,6 +138,11 @@ static int do_commit(struct ubifs_info *
  	}

  	c->cmt_no += 1;
+	if (c->use_ubifsec) {
+		err = keymap_purge(c);
+		if (err)
+			goto out_up;
+	}
  	err = ubifs_gc_start_commit(c);
  	if (err)
  		goto out_up;
@@ -304,6 +313,16 @@ int ubifs_bg_thread(void *info)
  		if (try_to_freeze())
  			continue;

+		if (UBIFSEC_COMMIT_INTERVAL(c)) {
+			struct timeval tv;
+			do_gettimeofday(&tv);
+			if (c->next_commit < tv.tv_sec) {
+				c->next_commit = tv.tv_sec + 
c->commit_interval;
+				c->cmt_state = COMMIT_REQUIRED;
+				c->need_bgt = 1;
+			}
+		}
+
  		set_current_state(TASK_INTERRUPTIBLE);
  		/* Check if there is something to do */
  		if (!c->need_bgt) {
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/compress.c linux-3.2.1-ubifsec/fs/ubifs/compress.c
--- linux-3.2.1-vanilla/fs/ubifs/compress.c	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/compress.c	2012-02-08 16:54:49.097506842 
+0100
@@ -27,9 +27,11 @@
   * decompression.
   */

-#include <linux/crypto.h>
  #include "ubifs.h"

+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+
  /* Fake description object for the "none" compressor */
  static struct ubifs_compressor none_compr = {
  	.compr_type = UBIFS_COMPR_NONE,
@@ -74,6 +76,40 @@ static struct ubifs_compressor zlib_comp
  /* All UBIFS compressors */
  struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];

+int ubifs_aes_crypt(u8 *str, int len, u8 *key, int keylen, u8 *iv, int ivlen)
+{
+	struct crypto_blkcipher *tfm;
+	struct blkcipher_desc desc;
+	struct scatterlist sg;
+	int err = 0;
+	tfm = crypto_alloc_blkcipher(UBIFSEC_CRYPTO_ALGORITHM, 0, 0);
+
+	if (IS_ERR(tfm)) {
+		ubifs_err("failed to load transform for aes: %ld",
+			  PTR_ERR(tfm));
+		return err;
+	}
+
+	err = crypto_blkcipher_setkey(tfm, key, keylen);
+	desc.tfm = tfm;
+	desc.flags = 0;
+	if (err) {
+		ubifs_err("setkey() failed  flags=%x",
+			  crypto_blkcipher_get_flags(tfm));
+		return err;
+	}
+	memset(&sg, 0, sizeof(struct scatterlist));
+
+	sg_set_buf(&sg, str, len);
+	desc.info = iv;
+	err = crypto_blkcipher_encrypt(&desc, &sg, &sg, len);
+	crypto_free_blkcipher(tfm);
+	if (err)
+		return err;
+	return 0;
+}
+
+
  /**
   * ubifs_compress - compress data.
   * @in_buf: data to compress
@@ -93,7 +129,7 @@ struct ubifs_compressor *ubifs_compresso
   * buffer and %UBIFS_COMPR_NONE is returned in @compr_type.
   */
  void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int 
*out_len,
-		    int *compr_type)
+		    int *compr_type, u8* key)
  {
  	int err;
  	struct ubifs_compressor *compr = ubifs_compressors[*compr_type];
@@ -115,7 +151,7 @@ void ubifs_compress(const void *in_buf,
  		ubifs_warn("cannot compress %d bytes, compressor %s, "
  			   "error %d, leave data uncompressed",
  			   in_len, compr->name, err);
-		 goto no_compr;
+		goto no_compr;
  	}

  	/*
@@ -124,13 +160,20 @@ void ubifs_compress(const void *in_buf,
  	 */
  	if (in_len - *out_len < UBIFS_MIN_COMPRESS_DIFF)
  		goto no_compr;
-
-	return;
+	goto encrypt;

  no_compr:
  	memcpy(out_buf, in_buf, in_len);
  	*out_len = in_len;
  	*compr_type = UBIFS_COMPR_NONE;
+	goto encrypt;
+encrypt:
+	if (key) {
+		u8 iv[AES_KEYSIZE_128];
+		memset(iv, 0, AES_KEYSIZE_128);
+		ubifs_aes_crypt(out_buf, *out_len, key, AES_KEYSIZE_128,
+				iv, AES_KEYSIZE_128);
+	}
  }

  /**
@@ -145,8 +188,9 @@ no_compr:
   * The length of the uncompressed data is returned in @out_len. This functions
   * returns %0 on success or a negative error code on failure.
   */
-int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
-		     int *out_len, int compr_type)
+int ubifs_decompress(void *in_buf, int in_len, void *out_buf,
+		     int *out_len, int compr_type, u8* key)
+
  {
  	int err;
  	struct ubifs_compressor *compr;
@@ -163,6 +207,12 @@ int ubifs_decompress(const void *in_buf,
  		return -EINVAL;
  	}

+	if (key) {
+		u8 iv[AES_KEYSIZE_128];
+		memset(iv, 0, AES_KEYSIZE_128);
+		ubifs_aes_crypt(in_buf, in_len, key, AES_KEYSIZE_128,
+				iv, AES_KEYSIZE_128);
+	}
  	if (compr_type == UBIFS_COMPR_NONE) {
  		memcpy(out_buf, in_buf, in_len);
  		*out_len = in_len;
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/file.c linux-3.2.1-ubifsec/fs/ubifs/file.c
--- linux-3.2.1-vanilla/fs/ubifs/file.c	2012-01-12 20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/file.c	2012-02-08 16:54:49.101506843 +0100
@@ -61,6 +61,8 @@ static int read_block(struct inode *inod
  	int err, len, out_len;
  	union ubifs_key key;
  	unsigned int dlen;
+	u8 crypto_key[UBIFSEC_KEYSIZE];
+	u8 *pkey = NULL;

  	data_key_init(c, &key, inode->i_ino, block);
  	err = ubifs_tnc_lookup(c, &key, dn);
@@ -79,8 +81,14 @@ static int read_block(struct inode *inod

  	dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
  	out_len = UBIFS_BLOCK_SIZE;
+	if (c->use_ubifsec) {
+		keymap_read_key(c, le64_to_cpu(dn->crypto_lookup), crypto_key);
+		pkey = crypto_key;
+	}
  	err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
-			       le16_to_cpu(dn->compr_type));
+			       le16_to_cpu(dn->compr_type), pkey);
+	POISON_KEY(crypto_key);
+
  	if (err || len != out_len)
  		goto dump;

@@ -636,6 +644,8 @@ static int populate_page(struct ubifs_in
  			memset(addr, 0, UBIFS_BLOCK_SIZE);
  		} else if (key_block(c, &bu->zbranch[nn].key) == page_block) {
  			struct ubifs_data_node *dn;
+			u8 crypto_key[UBIFSEC_KEYSIZE];
+			u8 *pkey = NULL;

  			dn = bu->buf + (bu->zbranch[nn].offs - offs);

@@ -648,8 +658,17 @@ static int populate_page(struct ubifs_in

  			dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
  			out_len = UBIFS_BLOCK_SIZE;
+			if (c->use_ubifsec) {
+				keymap_read_key(c,
+ le64_to_cpu(dn->crypto_lookup),
+						crypto_key);
+				pkey = crypto_key;
+			}
  			err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
- le16_to_cpu(dn->compr_type));
+ le16_to_cpu(dn->compr_type),
+					       pkey);
+			POISON_KEY(crypto_key);
+
  			if (err || len != out_len)
  				goto out_err;

diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/gc.c linux-3.2.1-ubifsec/fs/ubifs/gc.c
--- linux-3.2.1-vanilla/fs/ubifs/gc.c	2012-01-12 20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/gc.c	2012-02-08 16:54:49.105506843 +0100
@@ -322,15 +322,30 @@ static int move_node(struct ubifs_info *
  		     struct ubifs_scan_node *snod, struct ubifs_wbuf *wbuf)
  {
  	int err, new_lnum = wbuf->lnum, new_offs = wbuf->offs + wbuf->used;
+	u64 crypto_lookup = 0;

  	cond_resched();
+	/* When moving a node, inform the keymap and determine if it wants
+     * to promote the key to a longer-term key storage area.
+     */
+	if (c->use_ubifsec && key_type(c, &snod->key) == UBIFS_DATA_KEY) {
+		struct ubifs_data_node *dn =
+			(struct ubifs_data_node *) snod->node;
+		crypto_lookup = le64_to_cpu(dn->crypto_lookup);
+		if (keymap_gc_should_promote(c, crypto_lookup)) {
+			u64 new_pos;
+			err = keymap_swap_encryption_key(c, dn);
+			new_pos = le64_to_cpu(dn->crypto_lookup);
+			crypto_lookup = new_pos;
+		}
+	}
+
  	err = ubifs_wbuf_write_nolock(wbuf, snod->node, snod->len);
  	if (err)
  		return err;
-
  	err = ubifs_tnc_replace(c, &snod->key, sleb->lnum,
  				snod->offs, new_lnum, new_offs,
-				snod->len);
+				snod->len, crypto_lookup);
  	list_del(&snod->list);
  	kfree(snod);
  	return err;
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/io.c linux-3.2.1-ubifsec/fs/ubifs/io.c
--- linux-3.2.1-vanilla/fs/ubifs/io.c	2012-01-12 20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/io.c	2012-02-08 16:54:49.105506843 +0100
@@ -367,6 +367,19 @@ static unsigned long long next_sqnum(str
  }

  /**
+ * ubifs_set_datanode_crc - writes the crc for a data node to the common
+ * header.
+ * @node: the data node
+ * @len: the length of data
+ */
+void ubifs_set_datanode_crc(void *node)
+{
+	struct ubifs_ch *ch = (struct ubifs_ch *) node;
+	int len = le32_to_cpu(ch->len);
+	ch->crc = cpu_to_le32(crc32(UBIFS_CRC32_INIT, node + 8, len - 8));
+}
+
+/**
   * ubifs_prepare_node - prepare node to be written to flash.
   * @c: UBIFS file-system description object
   * @node: the node to pad
@@ -379,7 +392,6 @@ static unsigned long long next_sqnum(str
   */
  void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
  {
-	uint32_t crc;
  	struct ubifs_ch *ch = node;
  	unsigned long long sqnum = next_sqnum(c);

@@ -390,8 +402,7 @@ void ubifs_prepare_node(struct ubifs_inf
  	ch->group_type = UBIFS_NO_NODE_GROUP;
  	ch->sqnum = cpu_to_le64(sqnum);
  	ch->padding[0] = ch->padding[1] = 0;
-	crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
-	ch->crc = cpu_to_le32(crc);
+	ubifs_set_datanode_crc(node);

  	if (pad) {
  		len = ALIGN(len, 8);
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/journal.c linux-3.2.1-ubifsec/fs/ubifs/journal.c
--- linux-3.2.1-vanilla/fs/ubifs/journal.c	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/journal.c	2012-02-08 16:54:49.105506843 
+0100
@@ -696,6 +696,9 @@ int ubifs_jnl_write_data(struct ubifs_in
  	int err, lnum, offs, compr_type, out_len;
  	int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
  	struct ubifs_inode *ui = ubifs_inode(inode);
+	u8 crypto_key[UBIFSEC_KEYSIZE];
+	u8 *pkey = NULL;
+	u64 pos = 0;

  	dbg_jnl("ino %lu, blk %u, len %d, key %s",
  		(unsigned long)key_inum(c, key), key_block(c, key), len,
@@ -718,6 +721,15 @@ int ubifs_jnl_write_data(struct ubifs_in

  	data->ch.node_type = UBIFS_DATA_NODE;
  	key_write(c, key, &data->key);
+
+	if (c->use_ubifsec) {
+		err = keymap_free_key(c, &pos, crypto_key);
+		data->crypto_lookup = cpu_to_le64(pos);
+		if (err)
+			return -ENOMEM;
+		pkey = crypto_key;
+	}
+
  	data->size = cpu_to_le32(len);
  	zero_data_node_unused(data);

@@ -728,7 +740,8 @@ int ubifs_jnl_write_data(struct ubifs_in
  		compr_type = ui->compr_type;

  	out_len = dlen - UBIFS_DATA_NODE_SZ;
-	ubifs_compress(buf, len, &data->data, &out_len, &compr_type);
+	ubifs_compress(buf, len, &data->data, &out_len, &compr_type, pkey);
+	POISON_KEY(crypto_key);
  	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);

  	dlen = UBIFS_DATA_NODE_SZ + out_len;
@@ -745,7 +758,7 @@ int ubifs_jnl_write_data(struct ubifs_in
  	ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, key_inum(c, key));
  	release_head(c, DATAHD);

-	err = ubifs_tnc_add(c, key, lnum, offs, dlen);
+	err = ubifs_tnc_add_with_crypto_lookup(c, key, lnum, offs, dlen, pos);
  	if (err)
  		goto out_ro;

@@ -1099,10 +1112,14 @@ out_free:
   * This function is used when an inode is truncated and the last data node of
   * the inode has to be re-compressed and re-written.
   */
-static int recomp_data_node(struct ubifs_data_node *dn, int *new_len)
+static int recomp_data_node(struct ubifs_info *c, struct ubifs_data_node *dn,
+			    int *new_len)
  {
  	void *buf;
+	u64 old_pos;
  	int err, len, compr_type, out_len;
+	u8 crypto_key[UBIFSEC_KEYSIZE];
+	u8 *pkey = NULL;

  	out_len = le32_to_cpu(dn->size);
  	buf = kmalloc(out_len * WORST_COMPR_FACTOR, GFP_NOFS);
@@ -1111,11 +1128,31 @@ static int recomp_data_node(struct ubifs

  	len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
  	compr_type = le16_to_cpu(dn->compr_type);
-	err = ubifs_decompress(&dn->data, len, buf, &out_len, compr_type);
+	if (c->use_ubifsec) {
+		err = keymap_read_key(c, le64_to_cpu(dn->crypto_lookup),
+				      crypto_key);
+		if (err)
+			goto out;
+		pkey = crypto_key;
+	}
+	err = ubifs_decompress(&dn->data, len, buf, &out_len, compr_type, 
pkey);
+
  	if (err)
  		goto out;
+	if (c->use_ubifsec) {
+		u64 new_pos;
+		old_pos = le64_to_cpu(dn->crypto_lookup);
+
+		err = keymap_free_key(c, &new_pos, crypto_key);
+		dn->crypto_lookup = cpu_to_le64(new_pos);
+		ubifs_assert(pkey == crypto_key);
+		if (err)
+			goto out;
+		keymap_mark_deleted(c, old_pos);
+	}

-	ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type);
+	ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type, pkey);
+	POISON_KEY(crypto_key);
  	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);
  	dn->compr_type = cpu_to_le16(compr_type);
  	dn->size = cpu_to_le32(*new_len);
@@ -1190,7 +1227,7 @@ int ubifs_jnl_truncate(struct ubifs_info
  				int compr_type = le16_to_cpu(dn->compr_type);

  				if (compr_type != UBIFS_COMPR_NONE) {
-					err = recomp_data_node(dn, &dlen);
+					err = recomp_data_node(c, dn, &dlen);
  					if (err)
  						goto out_free;
  				} else {
@@ -1224,7 +1261,9 @@ int ubifs_jnl_truncate(struct ubifs_info

  	if (dlen) {
  		sz = offs + UBIFS_INO_NODE_SZ + UBIFS_TRUN_NODE_SZ;
-		err = ubifs_tnc_add(c, &key, lnum, sz, dlen);
+		err = ubifs_tnc_add_with_crypto_lookup(
+			c, &key, lnum, sz, dlen,
+			le64_to_cpu(dn->crypto_lookup));
  		if (err)
  			goto out_ro;
  	}
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/keymap.c linux-3.2.1-ubifsec/fs/ubifs/keymap.c
--- linux-3.2.1-vanilla/fs/ubifs/keymap.c	1970-01-01 01:00:00.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/keymap.c	2012-02-08 16:57:56.945515934 
+0100
@@ -0,0 +1,1314 @@
+/*
+ * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ * Copyright (C) 2006, 2007 University of Szeged, Hungary
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Adrian Hunter
+ *	  Artem Bityutskiy (Битюцкий Артём)
+ *	  Zoltan Sogor
+ */
+
+/*
+ * keymap.c
+ * Author: Joel Reardon
+ *
+ * The keymap implements the key storage, assignment, and deletion for UBIFSec
+ * to ensure secure deletion of data.
+ */
+
+#include "ubifs.h"
+#include <linux/random.h>
+
+#define KEYMAP_STATE_FREE 0
+#define KEYMAP_STATE_USED 1
+#define KEYMAP_STATE_DELETED 2
+#define KEYMAP_STATES_PER_BYTE 4
+#define KEYMAP_STATES_PER_BYTE_SHIFT 2
+#define KEYMAP_CHECKPOINT_MAGIC 0x00602e23
+
+#define RETURN_VAL_IF(v, e) do { if ((e)) return (v); } while (0)
+#define RETURN_ON_ERR(e) do { if ((e)) return (e); } while (0)
+
+/* TODO(reardon): ubifs allows updates to the file system during a commit,
+ * adding such changes to empty journal that becomes the new journal after
+ * commiting; so we must allow ubifsec to assign keys out during a purge.
+ * It can be implemented as follows: when trying to get a key, check if
+ * purge_ongoing is set true. If not, then its the standard path. Otherwise,
+ * Keys are assigned from a block that does not contain deleted data---i.e.,
+ * one whose purging will be skipped and whose keys will otherwise be
+ * marked as non-assignable. During this update, the key state map is not
+ * changed  but instead an entry is  added to a queue of changes made during
+ * purging. After writing the new KSA, the key state map is updated with the
+ * change queue. Purging is then completed and  the key state map is again
+ * correct.
+ */
+
+/* TODO(reardon): add a consistency check / full scan to rebuild checkpoint.
+ * This can have two flavours: to recovery from a missing checkpoint,
+ * simply do a full scan of all the data nodes in the TNC, and then mark their
+ * crypto positions as 'used' and the rest as 'unused'. To perform a
+ * consistency check, then read all data nodes and mark all the valid nodes's
+ * crypto positions as used, the invalid nodes (i.e., not contained in the 
TNC)
+ * as deleted, and all other posistions as unused. Then check for accord with
+ * regards to the checkpointed value and the TNC. This can be internally or as
+ * a standalone tool. However, currently, if a valid checkpoint cannot be
+ * recovered then UBIFSec reports an error and fails to mount.
+ */
+
+/* The crypto key positions are stored in zbraches in the TNC. It may be best
+ * to refactor it so that the data in each node is part of a data structure
+ * that is allocated separately, or has a variable allocation: e.g.:
+ * struct ubifs_zbranch * zbr = make_zbr(c), where make_zbr() uses c's
+ * use_ubifsec to decide whether to allocate a full zbr or a reduced one.
+ * Zbrs themselves may use a substructure of lnum,offs,len,crypto_lookup to
+ * reduce the parameter load of ubifs_tnc_add() and in the same way remove the
+ * need for ubifs_tnc_add_with_crypto_lookup.
+ */
+
+/* Currently, the KSA ranges are fixed into a short-term and long-term area,
+ * with immediate promotion if a node is garbage collected. If this is made
+ * more user-configurable, than the following define will need to be changed,
+ * along with an entire interface to be added to control it during the
+ * formatting of the partition. This is only the max size of a KSA used for
+ * sizing the array.
+ */
+#define KEYMAP_MAX_KSA_RANGES 2
+
+/* The key cache is a way of optimizing read performance by reading an entire
+ * page worth of keys into memory and keeping it locally. This way, if a
+ * key is requested on the same page (i.e., being assigned out sequentally)
+ * then it will be returned without requiring another read of the device.
+ * However, this is a trade-off with security: keeping keys in memory is
+ * always less desirable than having no keys in memory against some kinds of
+ * attacks; if the attacker is only able to read the system's memory at only
+ * one moment in time, then having as few keys as possible available is
+ * obviously a benefit.
+ */
+struct key_cache {
+	u8 *page;	/* the page of data that has been read from flash */
+	int page_num;	/* the page number (rel. to the beginning of the KSA,
+			 * that is stored in page. If page_num is -1 then the
+			 * page is not storing any valid data. */
+};
+
+/* A KSA range is a set of erase blocks that are devoted to storing keys. The
+ * compete set is called the KSA, and a KSA range is a subset of the KSA where
+ * all the keys in the range have a similar expected lifetime. The expected
+ * lifetimes are determined heuristically through generation garbage
+ * collection: each time a data node is garbage collected, it may be promoted
+ * to the next (longer-lived) KSA range.  The number of garbage collections
+ * until promotion is stored in gcs_until_promotion. If this value is 0, then
+ * it is the final KSA range (i.e., longest lived). If it is 1, then it is
+ * automatically promoted. For any other value, a random number is generated
+ * to determine if it should be promoted. (As this is all anyhow heuristical
+ * and to obtain simply fewer KSA erasures for purging, detailed accounting of
+ * garbage collections is both unnessessary and inefficient).
+ *
+ * There are two caches for the KSA. The read cache stores the last page of
+ * keys used to read a key for reading a data node. The write cache stores the
+ * last page of keys used to read a key for writing a new data node. Whether
+ * it is read or write is decided based on whether the key being read is equal
+ * to the cur_pos value. If either cache has the valid page for our read, then
+ * the cached value is used instead of reading it from the flash. Otherwise
+ * the key is read and tehe cahced is updated.
+ */
+struct ksa_range {
+	u32 ksa_first;  /* first LEB in this KSA range, relative to the KSA */
+	u32 ksa_size;   /* last LEB in this KSA range, relative to the KSA */
+	u32 gcs_until_promotion;  /* the number of garbage collections until
+				   * promotion to the next KSA. */
+	u64 cur_pos;  /* A pointer to a key position in this KSA range used for
+		       * key assignment. Finding an unused key begins from this
+		       * value. Its value is most often the last key assigned
+		       * from this KSA. The exception is after purging when
+		       * it is reset to the first key position. */
+	u32 erases;  /* The number of erasures in this KSA. This is only used 
to
+		      * determine the effectiveness of the KSA partitioning. */
+	struct key_cache rd_cache;  /* A reference to the read cache. */
+	struct key_cache wr_cache;  /* A reference to the write cache. */
+};
+
+/* This is the main keymaps data structure for UBIFSec. There is only instance
+ * of the keymap for the UBIFS main context. Its main purpose is to be aware
+ * of which LEBs belong to the KSA, how the KSA is divided into ranges, the
+ * state for each key, and mutexes to ensure thread safety.
+ */
+struct ubifs_keymap {
+	u32 key_size;  /* geometry: size of a key in bytes */
+	u32 keys_per_eb;  /* geometry: number of keys per KSA LEB. */
+	u32 ksa_size;  /* geometry: number of LEBS in the KSA */
+	u32 ksa_first;  /* the first LEB belonging to the KSA */
+	u32 ckpt_leb;  /* the LEB that is used to store keypoints of the KSA. 
*/
+
+	/* a double-array [ksa_size]x[keys_per_eb] that stores the
+	 * state of the key at that position. The state consumes two bits and
+	 * is tightly packed in the bytes. The first index is from 0
+	 * to the number of KSA LEBs - 1, and the second is from 0 to
+	 * the number of keys per (KSA LEB - 1) >> 2.
+	 */
+	u8 **state;
+	u64 free;  /* Number of unused keys in the system. */
+	/* A LEB-sized buffer used during atomic updates to write new
+	 * versions of the KSA and checkpoint. */
+	u8 *kbuf;
+	u64 deleted;  /* Number of deleted keys in the system. */
+	u32 ckpt_sz;  /* The size of an uncompressed checkpoint */
+	u8 *ckpt_buf; /* Buffer used to store the uncompressed checkpoint while
+		       * mounting and commiting. */
+	u32 next_checkpoint_off;  /* Offset in the checkpoint block where the
+				   * next checkpoint will be written. */
+
+	/* This mutex must be locked when update the state of a key. */
+	struct mutex state_mutex;
+	/* This mutex must be locked when using kbuf. */
+	struct mutex kbuf_mutex;
+	/* This mutex must be locked when doing a purge. */
+	struct mutex purge_mutex;
+	/* True if there a purge happening. Must be read under the purge mutex
+	 * lock  */
+	int purge_ongoing;
+
+	u32 erasures;  /* Count of KSA LEB erasures due to purging. */
+	int read_only;  /* Is the file-system mounted read only? */
+	/* References to the KSA range data structure for each KSA range. */
+	struct ksa_range ksa[KEYMAP_MAX_KSA_RANGES];
+	int ksa_ranges; /* The actual number of KSA ranges. */
+};
+
+/* This function sets a key position's state. */
+static inline
+void set_state(struct ubifs_keymap *km, int eb, int offset, int value)
+{
+	static const int minor_mask = (1 << KEYMAP_STATES_PER_BYTE_SHIFT) - 1;
+	int major = offset >> KEYMAP_STATES_PER_BYTE_SHIFT;
+	int shift = 2 * (offset & minor_mask);
+	int mask  = minor_mask << shift;
+	int old = km->state[eb][major];
+	km->state[eb][major] = old - (old & mask) + (value << shift);
+}
+
+/* This function gets a key position's state. */
+static inline
+int get_state(struct ubifs_keymap *km, int eb, int offset)
+{
+	static const int minor_mask = (1 << KEYMAP_STATES_PER_BYTE_SHIFT) - 1;
+	int major = offset >> KEYMAP_STATES_PER_BYTE_SHIFT;
+	int shift = 2 * (offset & minor_mask);
+	return (km->state[eb][major] >> shift) & minor_mask;
+}
+
+/* TODO(reardon): to optimize the common event of reading all states
+ * sequentially (e.g., purge, checkpoint), make a caching reading function.
+ */
+
+/* This function invalidates the current page in the key_cache that is passed.
+ * It also poisons the memory. */
+static void key_cache_invalidate(const struct ubifs_info *c,
+				 struct key_cache *cache)
+{
+	memset(cache->page, 0xff, c->min_io_size);
+	cache->page_num = -1;
+}
+
+/* This function frees the data for a keymap ksa range. It simply poisons the
+ * memory for the read and write caches, then frees them.
+ */
+static void keymap_ksa_free(const struct ubifs_info *c, struct ksa_range *ksa)
+{
+	key_cache_invalidate(c, &(ksa->wr_cache));
+	key_cache_invalidate(c, &(ksa->rd_cache));
+	kfree(ksa->wr_cache.page);
+	kfree(ksa->rd_cache.page);
+}
+
+/* This function invalidates both the read and write cache for the passed KSA
+ * range */
+static void ksa_key_cache_invalidate(const struct ubifs_info *c,
+				     struct ksa_range *ksa)
+{
+	key_cache_invalidate(c, &ksa->rd_cache);
+	key_cache_invalidate(c, &ksa->wr_cache);
+}
+
+static int keymap_eb_to_ksa_range(const struct ubifs_keymap *km, const u32 
eb);
+
+/* This function writes some KSA range information to the kernel log. */
+static void keymap_ksa_trace(struct ubifs_keymap *km, const int ksa_num)
+{
+	struct ksa_range *ksa = km->ksa + ksa_num;
+	int i;
+	int state_cnt[3];
+	memset(state_cnt, 0, 3 * sizeof(int));
+	for (i = 0; i < ksa->ksa_size; ++i) {
+		int j;
+		for (j = 0; j < km->keys_per_eb; ++j)
+			state_cnt[get_state(km, i + ksa->ksa_first, j)]++;
+	}
+	ubifs_msg("(keymap) ksa=%d free=%d used=%d delete=%d erases=%d",
+		  ksa_num, state_cnt[0], state_cnt[1], state_cnt[2],
+		  (int) ksa->erases);
+}
+
+/* This function calls keymap_ksa_trace for each KSA in the keymap. */
+static void keymap_trace(struct ubifs_keymap *km)
+{
+	int i;
+	for (i = 0; i < km->ksa_ranges; ++i)
+		keymap_ksa_trace(km, i);
+}
+
+/* This function converts a 64-bit key position into the 32-bit LEB number
+ * and 32-bit offset in the LEB where the key can be found.
+ */
+static void pos_to_eb_offset(const u64 pos, u32 *eb, u32 *offset)
+{
+	*offset = 0xffffffff & pos;
+	*eb = 0xffffffff & (pos >> 32);
+}
+
+/* This function converts the LEB number and offset for a key into the 64-bit
+ * key position value.
+ */
+static u64 eb_offset_to_pos(u32 eb, u32 offset)
+{
+	return (((u64)eb) << 32) + offset;
+}
+
+/* This function allows external components of UBIFS become aware of the
+ * number of keys per KSA LEB without knowing the internals of the keymap.
+ */
+int keymap_keys_per_eb(struct ubifs_info *c)
+{
+	return c->leb_size / UBIFSEC_KEYSIZE;
+}
+
+/* This function initializes an already-allocated key_cache data structure. */
+static int key_cache_init(struct ubifs_info *c, struct key_cache *cache)
+{
+	cache->page = kmalloc(c->min_io_size, GFP_NOFS);
+	RETURN_VAL_IF(-ENOMEM, !cache->page);
+	cache->page_num = -1;
+	return 0;
+}
+
+/* This function adds a new KSA range to the keymap, which it inializes with
+ * the following parameters:
+ *   pos: the array index for ksa where the new ksa range should be added.
+ *   ebs: the number of LEBS in the range
+ *   promotion: the value to set as gcs_until_promotion.
+ *
+ * The function sets the first KSA LEB for this range as follows:
+ *   0, if pos == 0
+ *   one more than the last KSA LEB for the previous KSA range, if pos > 0.
+ *
+ * If ebs is set to 0, then the size of this range is set to difference
+ * between the total number of KSA LEBs and the range's starting LEB.
+ */
+static int add_ksa_range(struct ubifs_info *c, int pos, u32 ebs, int 
promotion)
+{
+	struct ubifs_keymap *km;
+	int err;
+	km = c->km;
+	if (pos < 0 || pos >= km->ksa_ranges) {
+		ubifs_err("error adding KSA range: pos %d is invalid.", pos);
+		return -EINVAL;
+	}
+	if (pos == 0) {
+		km->ksa[0].ksa_first = 0;
+	} else {
+		km->ksa[pos].ksa_first =
+			km->ksa[pos - 1].ksa_first + km->ksa[pos - 1].ksa_size;
+	}
+	if (ebs < 0 || ebs > km->ksa_size - km->ksa[pos].ksa_first) {
+		ubifs_err("error adding KSA range: cannot allocate %d ebs "
+			  "to pos %d", (int) ebs, pos);
+		return -EINVAL;
+	}
+	if (ebs == 0) {
+		km->ksa[pos].ksa_size = km->ksa_size - km->ksa[pos].ksa_first;
+		ubifs_assert(promotion == 0);
+	} else {
+		km->ksa[pos].ksa_size = ebs;
+	}
+	km->ksa[pos].gcs_until_promotion = promotion;
+	km->ksa[pos].erases = 0;
+	km->ksa[pos].cur_pos = eb_offset_to_pos(km->ksa[pos].ksa_first, 0);
+	err = key_cache_init(c, &km->ksa[pos].rd_cache);
+	err = key_cache_init(c, &km->ksa[pos].wr_cache);
+	RETURN_ON_ERR(err);
+	ubifs_msg("(keymap) added KSA range %d: %d->%d, protomotion at %d",
+		  pos, (int) km->ksa[pos].ksa_first,
+		  (int) km->ksa[pos].ksa_first + (int) km->ksa[pos].ksa_size,
+		  promotion);
+	return 0;
+}
+
+/* This function initializes the KSA range to a default value. We use two KSA
+ * ranges, each half the size of the KSA, and automatically promote any key
+ * whose data node is once garbage collected. This creates a short-term and
+ * long-term storage area. If there are less than 6 LEBs in the KSA, then only
+ * a single KSA range is used.
+ */
+static int keymap_init_ksa_ranges(struct ubifs_info *c)
+{
+	int err = 0;
+	u32 blocks = c->km->ksa_size >> 1;
+	if (c->km->ksa_size < 6) {
+		c->km->ksa_ranges = 1;
+		err = add_ksa_range(c, 0, 0, 0);
+	} else {
+		c->km->ksa_ranges = 2;
+		err = add_ksa_range(c, 0, blocks, 1);
+		err = add_ksa_range(c, 1, 0, 0);
+	}
+	RETURN_ON_ERR(err);
+	return 0;
+}
+
+static void keymap_mark_used_internal(struct ubifs_keymap *c, u64 pos);
+static int keymap_checkpoint_pack(struct ubifs_info *c, u8* buf, int len);
+static int keymap_checkpoint_unpack(struct ubifs_info *c, u8* buf, int len);
+
+/* This function initializes the keymap data structure contained in the
+ * ubifs_info structure FOR A FRESHLY FORMATED PARTITION. This means that it
+ * erases all the KSA blocks, as it assumes it does not contain valid keys.
+ * This should only be called when formatting UBIFS or mounting it when the
+ * flash memory is all 0xFF.  It marks all the keys as deleted, then purges,
+ * which effectively fills the entire KSA with fresh data and sets all the
+ * variables correctly.
+ */
+int keymap_initialize_fresh(struct ubifs_info *c)
+{
+	int i;
+	int j;
+	int err;
+	struct ubifs_keymap *km = c->km;
+	ubifs_assert(c->empty);
+	km->free = 0;
+	km->next_checkpoint_off = 0;
+	mutex_lock(&km->kbuf_mutex);
+	for (i = 0; i < km->ksa_size; ++i) {
+		for (j = 0; j < km->keys_per_eb; ++j) {
+			set_state(km, i, j, KEYMAP_STATE_DELETED);
+			km->deleted++;
+		}
+	}
+	mutex_unlock(&km->kbuf_mutex);
+	RETURN_VAL_IF(0, km->read_only);
+	err = keymap_purge(c);
+	return err;
+}
+
+/* This function writes a checkpoint of the current key state map to the LEB
+ * reserved for key state map checkpoints. It first packs the current state
+ * map to the ckpt_buf, then compresses using UBIFS's existing LZO compressor.
+ * The result is then prefixed with the length of the compressed checkpoint,
+ * and appended with a magic number (i.e., not all 0xFF) to signal that it has
+ * been successfully written. This is all written onto the beginning of kbuf.
+ *
+ * The size of the checkpoint is then page-aligned, and then the first free
+ * page in the checkpoint LEB is checked to see if it will fit. If it fits,
+ * then it is written onto those pages. If it does not fit, then kbuf is
+ * instead written using atomic update to the checkpoint LEB, such that the
+ * successful result is a checkpoint LEB with only one checkpoint, and
+ * otherwise the old LEB remains.
+ */
+
+/* TODO(reardon): currently, the compressed checkpoint cannot exceed a LEB.
+ * The solution is of course to span it over multiple LEBs---but this needs to
+ * be implemented. */
+int keymap_write_checkpoint(struct ubifs_info *c)
+{
+	int err = 0;
+	int compr = UBIFS_COMPR_LZO;
+	int len = c->leb_size - 8;
+	int len_al;
+	struct ubifs_keymap *km = c->km;
+
+	RETURN_VAL_IF(0, !km);
+	RETURN_VAL_IF(-EROFS, km->read_only);
+
+	mutex_lock(&km->kbuf_mutex);
+	memset(km->kbuf, 0xff, c->leb_size);
+	keymap_checkpoint_pack(c, km->ckpt_buf, km->ckpt_sz);
+	ubifs_compress(km->ckpt_buf, km->ckpt_sz, km->kbuf + sizeof(u32),
+		       &len, &compr,  NULL);
+
+	*(u32 *)(km->kbuf) = cpu_to_le32(len);
+	len += sizeof(u32);
+	*(u32 *)(km->kbuf + len) = cpu_to_le32(KEYMAP_CHECKPOINT_MAGIC);
+	len += sizeof(u32);
+	len_al = ALIGN(len,  c->min_io_size);
+
+	if (km->next_checkpoint_off + len_al > c->leb_size) {
+		km->next_checkpoint_off = len_al;
+		km->erasures++;
+		err = ubifs_leb_change(c, km->ckpt_leb, km->kbuf,
+				       len_al, UBI_SHORTTERM);
+	} else {
+		err = ubifs_leb_write(c, km->ckpt_leb, km->kbuf,
+				      km->next_checkpoint_off,
+				      len_al, UBI_SHORTTERM);
+		km->next_checkpoint_off += len_al;
+	}
+
+	mutex_unlock(&km->kbuf_mutex);
+	return err;
+}
+
+/* This function finds the next checkpoint on the checkpoint block and returns
+ * the length and whether it was correctly formatted. Purported checkpoint
+ * lengths MUST be range-checked with regards to the buffer length.
+ */
+static int find_next_checkpoint(char *buf, int buf_len, int offset,
+				int *valid, int *length)
+{
+	int len;
+	int su32 = sizeof(u32);
+	if (offset + su32 >= buf_len)
+		goto failure;
+
+	len = le32_to_cpu(*(u32 *)(buf+offset));
+	if (len >= 0 && len + offset + su32 < buf_len) {
+		*length = len;
+	} else {
+		ubifs_err("checkpoint reported an invalid length (%d)", len);
+		goto failure;
+	}
+
+	*valid = 0;
+	if (KEYMAP_CHECKPOINT_MAGIC ==
+	    le32_to_cpu(*(u32 *)(buf + su32 + len + offset))) {
+		*valid = 1;
+	}
+	return 0;
+
+failure:
+	*valid = 0;
+	*length = 0;
+	return -EINVAL;
+}
+
+/* This function reads the most recent checkpoint from the checkpoint LEB,
+ * decompresses it, and then reads the state of each key into the key state
+ * map. It does this by sequentially readin checkpoints from the checkpoint
+ * block until no more are available. The first four bytes of a checkpoint
+ * stores the length, which is used to jump ahead to the end and verify the
+ * magic number.
+ */
+int keymap_read_checkpoint(struct ubifs_info *c)
+{
+	int err = 0;
+	int offset = 0;
+	int last_valid;
+	int ckpt_len, full_ckpt_len, ckpt_offset;
+	int i;
+	int empty_after = c->leb_size;
+	char *buf;
+	struct ubifs_keymap *km = c->km;
+
+	RETURN_VAL_IF(0, !km);
+	buf = km->kbuf;
+
+	mutex_lock(&km->kbuf_mutex);
+	err = ubi_read(c->ubi, c->km->ckpt_leb, buf, 0, c->leb_size);
+	if (err)
+		goto out;
+	/* Find the length of data on the erase block. */
+	for (i = c->leb_size - 1; i >= 0; --i) {
+		if ((u8)(buf[i]) != 0xff)
+			break;
+		empty_after = i;
+	}
+
+	/* If this is a freshly-formatted UBIFSec partition, then format the
+	 * key storage area.
+	 */
+	if (c->empty) {
+		ubifs_assert(empty_after == 0);
+		goto format_keyspace;
+	}
+
+	/* If this is not freshly formatted yet no checkpoints are written,
+	 * then return an error.
+	 */
+	if (empty_after == 0) {
+		ubifs_err("checkpoint block is empty, but this is not an "\
+			  "empty partition.  Perform a full scan of data "\
+			  "nodes.");
+		err =  -EINVAL;
+		goto out;
+	}
+
+	offset = 0;
+	ckpt_offset = -1;
+	ckpt_len = 0;
+	last_valid = 0;
+	while (offset < empty_after) {
+		int len, valid;
+		err = find_next_checkpoint(buf, c->leb_size, offset,
+					   &valid, &len);
+		if (err)
+			goto out;
+		if (valid) {
+			ckpt_offset = offset + sizeof(u32);
+			ckpt_len = len;
+			last_valid = 1;
+		} else {
+			last_valid = 0;
+		}
+		offset += ALIGN(len + sizeof(u32) * 2, c->min_io_size);
+	}
+	if (ckpt_offset == -1) {
+		ubifs_err("no valid checkpoint found");
+		err = -EINVAL;
+		goto out;
+	}
+	if (!last_valid && !c->need_recovery) {
+		ubifs_err("last checkpoint is invalid but file system "\
+			  "should have been safely unmounted---perform "\
+			  "a full scan.");
+		err = -EINVAL;
+		goto out;
+	}
+
+	km->next_checkpoint_off = offset;
+	full_ckpt_len = km->ckpt_sz;
+	err = ubifs_decompress(km->kbuf + ckpt_offset, ckpt_len, km->ckpt_buf,
+			       &full_ckpt_len, UBIFS_COMPR_LZO, NULL);
+	if (err)
+		goto out;
+	err = keymap_checkpoint_unpack(c, km->ckpt_buf, full_ckpt_len);
+	keymap_trace(c->km);
+out:
+	mutex_unlock(&km->kbuf_mutex);
+	return err;
+
+format_keyspace:
+	/* This is a fresh file system. */
+	mutex_unlock(&km->kbuf_mutex);
+	err = keymap_initialize_fresh(c);
+	return err;
+}
+
+/* This function packs the key state map into an uncompressed checkpoint,
+ * storing it on the buffer buf. The len varaible indicates the size of the
+ * buffer, and all writes to the buffer are checked by assertion against this
+ * length before they are performed.
+ */
+static int keymap_checkpoint_pack(struct ubifs_info *c, u8 *buf, int len)
+{
+	int i;
+	int j;
+	int k = 0;
+	int err = 0;
+	struct ubifs_keymap *km = c->km;
+
+	mutex_lock(&km->state_mutex);
+	for (i = 0; i < km->ksa_size; ++i) {
+		for (j = 0; j < km->keys_per_eb; j += 8) {
+			u8 val = 0;
+			int l = 0;
+			if (k >= len) {
+				err = -EINVAL;
+				goto out;
+			}
+			for (l = 0; l < 8; ++l) {
+				int state = get_state(km, i, j+l);
+				if (state == KEYMAP_STATE_USED)
+					val += (1 << l);
+			}
+			ubifs_assert(k < len);
+			buf[k++] = val;
+		}
+	}
+out:
+	mutex_unlock(&km->state_mutex);
+	return err;
+}
+
+/* This function unpacks a checkpoint (in its uncompressed form, as stored in
+ * buf) and sets the key state map accordingly. It performs the reverse as
+ * keymap_checkpoint_unpack.
+ */
+static int keymap_checkpoint_unpack(struct ubifs_info *c, u8 *buf, int len)
+{
+	int i;
+	int j;
+	int k = 0;
+	int last = -1;
+	int err = 0;
+	struct ubifs_keymap *km = c->km;
+	mutex_lock(&km->state_mutex);
+	for (i = 0; i < km->ksa_size; ++i) {
+		for (j = 0; j < km->keys_per_eb; j += 8) {
+			u8 val;
+			int l;
+			if (k >= len) {
+				err = -EINVAL;
+				goto out;
+			}
+			val = buf[k++];
+			last = val;
+			for (l = 0; l < 8; ++l) {
+				int state;
+				set_state(km, i, j + l, 1 & val);
+				val = val >> 1;
+				state = get_state(km, i, j + l);
+				if (state == KEYMAP_STATE_FREE)
+					km->free++;
+			}
+		}
+	}
+out:
+	mutex_unlock(&km->state_mutex);
+	return err;
+}
+
+/* This function allows external access to the number of free keys in the
+ * keymap.
+ */
+u64 keymap_freekeys(struct ubifs_info *c)
+{
+	RETURN_VAL_IF(0, !c->km);
+	return c->km->free;
+}
+
+/* This function generates a new random key and places it in the to field. The
+ * keymap stores the size of the key used as a field. We assume that
+ * get_random_bytes() will always yield cryptographically-suitable random
+ * data. If that changes, this will need to be updated.
+ */
+static void generate_key(struct ubifs_keymap *km, u8 *to)
+{
+	get_random_bytes(to, km->key_size);
+}
+
+/* This function performs the purging operation on a single key storage area
+ * LEB. All unused and deleted keys on that erase block are replaced with
+ * fresh random data. It reads the entire erase block currently stored on the
+ * storage medium, and then updates its in memory with the new version, which
+ * is then written using atomic update.
+ */
+
+/* TODO(reardon): currently, blocks containing no deleted keys are not
+ * updated. This is okay, but an attacker who is able to see these keys can
+ * then decrypt data that will be written at an unknown future time. It is
+ * useful to mark such erase blocks as potentially exposed, so that the keys
+ * are only assigned from then after repurging. Each KSA range would then
+ * maintain its exposed LEBs, which would be purged if needed. Exposed LEBs
+ * would be empty, so they can be indepently and immediately purged as a
+ * one-off, then used to assign keys.
+ *
+ * This optimization prevents purging many KSA LEBs at each purging when the
+ * storage medium is usually empty, but also limits the utility of this
+ * exposure attack.
+ */
+static int do_purge(struct ubifs_info *c, int eb)
+{
+	int i;
+	u64 old_free = c->km->free;
+	struct ubifs_keymap *km = c->km;
+	int err = 0;
+	ubifs_assert(km);
+	ubifs_assert(!km->read_only);
+	mutex_lock(&km->kbuf_mutex);
+
+	/* Read the entire key leb. */
+	err = ubi_read(c->ubi, eb + km->ksa_first, km->kbuf, 0, c->leb_size);
+
+	if (err) {
+		ubifs_err("(keymap) cannot read LEB %d, error %d",
+			  eb + (int) km->ksa_first, err);
+		goto out;
+	}
+
+	mutex_lock(&km->state_mutex);
+	/* Update unused and deleted keys. */
+	for (i = 0; i < km->keys_per_eb; ++i) {
+		if (get_state(km, eb, i) == KEYMAP_STATE_FREE ||
+				get_state(km, eb, i) == KEYMAP_STATE_DELETED) {
+			/* If the key is deleted, we need to update the
+			 * accounting information.
+			 */
+			if (get_state(km, eb, i) == KEYMAP_STATE_DELETED) {
+				set_state(km, eb, i, KEYMAP_STATE_FREE);
+				km->free++;
+				km->deleted--;
+			}
+			/* Replace the stale key with a fresh key */
+			generate_key(km, km->kbuf + (i * c->km->key_size));
+		}
+	}
+	mutex_unlock(&km->state_mutex);
+
+	if (old_free != km->free) {
+		km->erasures++;
+		km->ksa[keymap_eb_to_ksa_range(km, eb)].erases++;
+		ubifs_leb_change(c, eb + km->ksa_first, c->km->kbuf,
+				 c->leb_size,  UBI_SHORTTERM);
+	}
+
+out:
+	mutex_unlock(&km->kbuf_mutex);
+	return err;
+}
+
+/* This functions performs a purge on the KSA. It proceeds iteratively over
+ * all the LEBs in the KSA, calling do_purge on each one. It also invalides
+ * the caches for each KSA range.
+ */
+int keymap_purge(struct ubifs_info *c)
+{
+	int err;
+	int i;
+	struct ubifs_keymap *km = c->km;
+	RETURN_VAL_IF(0, !km);
+	RETURN_VAL_IF(-EROFS, km->read_only);
+
+	mutex_lock(&km->purge_mutex);
+	if (km->purge_ongoing) {
+		mutex_unlock(&km->purge_mutex);
+		return -EAGAIN;
+	}
+	km->purge_ongoing = 1;
+	mutex_unlock(&km->purge_mutex);
+
+	for (i = 0; i < km->ksa_ranges; ++i) {
+		ksa_key_cache_invalidate(c, km->ksa + i);
+		km->ksa[i].cur_pos = eb_offset_to_pos(km->ksa[i].ksa_first, 0);
+	}
+
+	for (i = 0; i < km->ksa_size; ++i) {
+		err = do_purge(c, i);
+		if (err)
+			goto out;
+	}
+	err = keymap_write_checkpoint(c);
+
+out:
+	mutex_lock(&km->purge_mutex);
+	km->purge_ongoing = 0;
+	mutex_unlock(&km->purge_mutex);
+
+	return err;
+}
+
+/* This function allocates a keymap data structure and initializes its fields.
+ * The ubifs_info context is passed as a parameter and its km field is set to
+ * the keymap that is preprared by this function. If memory cannot be
+ * allocated, it returns -ENOMEM. Otherwise it returns 0.
+ */
+int keymap_init(struct ubifs_info *c, int read_only, int use_ubifsec)
+{
+	struct ubifs_keymap *km;
+	c->km = NULL;
+
+	RETURN_VAL_IF(0, !use_ubifsec);
+
+	km = kmalloc(sizeof(struct ubifs_keymap), GFP_NOFS);
+	RETURN_VAL_IF(-ENOMEM, !km);
+
+	km->ksa_size = 0;
+	km->ksa_first = 0;
+	km->key_size = UBIFSEC_KEYSIZE;
+	km->keys_per_eb = c->leb_size / km->key_size;
+	km->deleted = 0;
+	km->erasures = 0;
+	km->free = 0;
+	km->state = NULL;
+	km->ckpt_buf = NULL;
+	km->purge_ongoing = 0;
+	km->read_only = read_only;
+	km->kbuf = vmalloc(sizeof(u8) * c->leb_size);
+	if (!km->kbuf) {
+		kfree(km);
+		return -ENOMEM;
+	}
+
+	mutex_init(&km->state_mutex);
+	mutex_init(&km->kbuf_mutex);
+	mutex_init(&km->purge_mutex);
+
+	c->km = km;
+	return 0;
+}
+
+/* This function tells the keymap that the ubifs_info context has its range of
+ * LEBs for the keymap reserved: [c->crypto_first, c->crypto_first +
+ * c->crypto_lebs - 1]. Initialization of the keymap is completed in this
+ * function, where the geometry of the keymap is computed and the checkpoint
+ * buffer and key state map data structures are allocated. All keys are given
+ * an initial state of unused, and the function completes by calling
+ * keymap_read_checkpoint to load the current checkpoint to the keystate map.
+ */
+int keymap_assign_lebs(struct ubifs_info *c)
+{
+	struct ubifs_keymap *km;
+	int i;
+	int err;
+
+	km = c->km;
+	RETURN_VAL_IF(0, !km);
+
+	km->ksa_size = c->crypto_lebs - 1;
+	km->ksa_first = c->crypto_first + 1;
+	km->ckpt_leb = c->crypto_first;
+	km->ckpt_sz = ((km->ksa_size * km->keys_per_eb) >> 3);
+	km->ckpt_buf = kmalloc(km->ckpt_sz, GFP_NOFS);
+
+	RETURN_VAL_IF(-ENOMEM, !km->ckpt_buf);
+	/* the state is a double array: first index for each KSA LEB, second
+	 * index for each key on a KSA LEB.
+	 */
+	km->state = kmalloc(sizeof(u8 *) * km->ksa_size, GFP_NOFS);
+	RETURN_VAL_IF(-ENOMEM, !km->state);
+
+	for (i = 0; i < km->ksa_size; ++i) {
+		int len;
+
+		ubifs_assert(km->keys_per_eb % 4 == 0);
+		len = (sizeof(u8) * km->keys_per_eb)
+			>> KEYMAP_STATES_PER_BYTE_SHIFT;
+		km->state[i] = kmalloc(len, GFP_NOFS);
+		RETURN_VAL_IF(-ENOMEM, !(km->state[i]));
+		memset(km->state[i], 0, len);
+		km->free += km->keys_per_eb;
+	}
+	err = keymap_init_ksa_ranges(c);
+	if (err)
+		return err;
+
+	return keymap_read_checkpoint(c);
+}
+
+/* This function frees the memory being used by the keymap.
+ */
+void keymap_free(struct ubifs_info *c)
+{
+	int i;
+	struct ubifs_keymap *km = c->km;
+
+	if (!km)
+		return;
+	mutex_destroy(&km->state_mutex);
+	mutex_destroy(&km->kbuf_mutex);
+	mutex_destroy(&km->purge_mutex);
+	kfree(km->ckpt_buf);
+	for (i = 0; i < km->ksa_ranges; ++i)
+		keymap_ksa_free(c, &(km->ksa[i]));
+	if (km->state) {
+		for (i = 0; i < km->ksa_size; ++i)
+			kfree(km->state[i]);
+		kfree(km->state);
+	}
+	vfree(km->kbuf);
+	kfree(km);
+	c->km = NULL;
+}
+
+/* This function increments the key position refernce for the ksa range
+ * parameter to the next one based on a cyclical ordering with two levels of
+ * granularity: the major is the LEB number, the minor is the key position in
+ * the LEB.
+ */
+static void ksa_range_increment_pos(struct ubifs_keymap *km,
+				    struct ksa_range *ksa)
+{
+	u32 offset;
+	u32 eb;
+
+	ubifs_assert(km);
+	ubifs_assert(ksa);
+
+	pos_to_eb_offset(ksa->cur_pos, &eb, &offset);
+	offset++;
+	if (offset == km->keys_per_eb) {
+		offset = 0;
+		eb++;
+	}
+	if (eb == ksa->ksa_first + ksa->ksa_size)
+		eb = ksa->ksa_first;
+	ksa->cur_pos = eb_offset_to_pos(eb, offset);
+}
+
+/* This function returns true if the key position passed in corresponds to a
+ * unused key. It must be accessed when the key state map is locked.
+ */
+/* TODO(reardon): there's a decent optimizaiton that can be done here:
+ * advancing the KSA position to the next LEB may not be efficient if that
+ * LEB is mostly used, and only has a few unused keys. Here we assume that
+ * either data is deleted soon, or it is deleted later. In this case, its
+ * not efficient to put new data on a KSA block that is otherwise used, as
+ * deleting this new value may trigger an purge on this block only to delete
+ * this one value. More efficient to fill a completely empty block with new
+ * data then such blocks. This may be implemented as a two-pass technique
+ * for advancement, where first, any block with >50% used keys will be
+ * simply ignored. Afterwards, such blocks are indeed considered as space
+ * becomes more constrained.
+ */
+static
+int keymap_is_free(struct ubifs_keymap *km, u64 pos)
+{
+	u32 eb, offset;
+	pos_to_eb_offset(pos, &eb, &offset);
+	return get_state(km, eb, offset) == KEYMAP_STATE_FREE;
+}
+
+/* This function fills pos and key with an unused key in the ksa range as
+ * indicated by the array index ksa_pos. pos must not be null. If key is null,
+ * then the key is not read from the flash storage medium but still assigned.
+ * The key pos is marked as used. If no unused keys are available, then ENOMEM
+ * is returned.
+ */
+int keymap_free_key_in_range(
+	struct ubifs_info *c, int ksa_pos, u64 *pos, u8 *key)
+{
+	struct ubifs_keymap *km = c->km;
+	struct ksa_range *ksa = km->ksa + ksa_pos;
+	int err = 0;
+	u64 start;
+
+	RETURN_VAL_IF(0, !km);
+	RETURN_VAL_IF(-EROFS, km->read_only);
+
+	ubifs_assert(ksa_pos >= 0 && ksa_pos < km->ksa_ranges);
+	ubifs_assert(pos);
+
+	start = ksa->cur_pos;
+
+	if (km->free == 0)
+		RETURN_VAL_IF(-ENOMEM, km->deleted > 0);
+
+	mutex_lock(&km->state_mutex);
+	while (!keymap_is_free(km, ksa->cur_pos)) {
+		ksa_range_increment_pos(km, ksa);
+		if (start == ksa->cur_pos) {
+			err = -ENOMEM;
+			goto out;
+		}
+	}
+	*pos = ksa->cur_pos;
+
+	if (key)
+		keymap_read_key(c, *pos, key);
+
+	keymap_mark_used_internal(km, ksa->cur_pos);
+
+out:
+	mutex_unlock(&km->state_mutex);
+	return err;
+}
+
+/* This function performs the same as keymap_free_key_in_range, except that it
+ * will search for a key in any KSA range greater than or equal to the ksa_pos
+ * array index value. The search begins at the smallest KSA range index, and
+ * returns the first found unused key.
+ */
+int keymap_free_key_in_min_range(
+	struct ubifs_info *c, int ksa_pos, u64 *pos, u8 *key)
+{
+	struct ubifs_keymap *km = c->km;
+	int i;
+	int err;
+
+	RETURN_VAL_IF(0, !km);
+	RETURN_VAL_IF(-EROFS, km->read_only);
+
+	if (km->free == 0)
+		RETURN_VAL_IF(-ENOMEM, km->deleted > 0);
+
+	for (i = ksa_pos; i < km->ksa_ranges; ++i) {
+		err = keymap_free_key_in_range(c, i, pos, key);
+		if (!err)
+			return 0;
+		RETURN_VAL_IF(err, err != -ENOMEM);
+	}
+	return -ENOMEM;
+}
+
+/* This function performs the same as keymap_free_key_in_range, except that it
+ * will search for a key in all the KSA ranges sequentially, starting with the
+ * smallest. The first found unused key is returned.
+ */
+int keymap_free_key(struct ubifs_info *c, u64 *pos, u8 *key)
+{
+	return keymap_free_key_in_min_range(c, 0, pos, key);
+}
+
+int keymap_eb_to_ksa_range(const struct ubifs_keymap *km, const u32 eb)
+{
+	int i;
+	for (i = 1; i < km->ksa_ranges; ++i)
+		if (eb < km->ksa[i].ksa_first)
+			return i - 1;
+	return km->ksa_ranges - 1;
+}
+
+int keymap_pos_to_ksa_range(struct ubifs_keymap *km, u64 pos)
+{
+	u32 eb, offset;
+
+	pos_to_eb_offset(pos, &eb, &offset);
+	return keymap_eb_to_ksa_range(km, eb);
+}
+
+/* This function swaps the encryption key for a data node. The idea for this
+ * is that the data node is being garbage collected, so we can re-encrypt it
+ * at effectiveness no addition cost (safe for the cipher operations). We do
+ * this to 'promote' a datanode to a higher area of the KSA (that is to say,
+ * longer-lived). Each time a datanode survives UBIFS's garbage collection, we
+ * take that as a clue that this data is long-term achieval data. The ultimate
+ * goal is to have all the keys that will never be deleted sitting on the same
+ * KSA LEBs, so that it is never purged.
+ */
+int keymap_swap_encryption_key(struct ubifs_info *c,
+			       struct ubifs_data_node *dn)
+{
+	u8 old_key[UBIFSEC_KEYSIZE];
+	u8 new_key[UBIFSEC_KEYSIZE];
+	char iv[UBIFSEC_KEYSIZE];
+	char *pbuf;
+	u64 old_pos = 0;
+	u64 new_pos = 0;
+	int dlen;
+	int err = 0;
+
+	RETURN_VAL_IF(0, !c->km);
+
+	memset(old_key, 0, UBIFSEC_KEYSIZE);
+	memset(new_key, 0, UBIFSEC_KEYSIZE);
+
+	old_pos = le64_to_cpu(dn->crypto_lookup);
+	err = keymap_read_key(c, old_pos, old_key);
+	RETURN_ON_ERR(err);
+	err = keymap_promote_key(c, old_pos, &new_pos, new_key);
+
+	if (err == -ENOMEM)
+		return err;
+	if (err) {
+		ubifs_err("(keymap) promote key failed: %d", err);
+		return err;
+	}
+	pbuf = kmalloc(4096, GFP_NOFS);
+	RETURN_VAL_IF(-ENOMEM, !pbuf);
+
+	dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
+	memcpy(pbuf, dn->data, dlen);
+
+	memset(iv, 0, UBIFSEC_KEYSIZE);
+	ubifs_aes_crypt(
+		pbuf, dlen, old_key, UBIFSEC_KEYSIZE, iv, UBIFSEC_KEYSIZE);
+	POISON_KEY(old_key);
+
+	memset(iv, 0, UBIFSEC_KEYSIZE);
+	ubifs_aes_crypt(
+		pbuf, dlen, new_key, UBIFSEC_KEYSIZE, iv, UBIFSEC_KEYSIZE);
+	POISON_KEY(new_key);
+
+	memcpy(dn->data, pbuf, dlen);
+	kfree(pbuf);
+	dn->crypto_lookup = cpu_to_le64(new_pos);
+	ubifs_set_datanode_crc(dn);
+
+	return 0;
+}
+
+/* This function promotes a key to a higher (longer-term) KSA block. If no
+ * space is available at a higher block, then it returns -ENOMEM, and no
+ * action should be taken. Otherwise, the caller should decrypt the data with
+ * the old key, encrypt it with the new key, mark the old position
+ * as deleted, and the new position as used.
+ */
+int keymap_promote_key(struct ubifs_info *c, u64 old_pos, u64 *new_pos, u8 
*key)
+{
+	struct ubifs_keymap *km = c->km;
+	int ksa_range;
+	RETURN_VAL_IF(0, !km);
+	ubifs_assert(!km->read_only);
+
+	ksa_range = keymap_pos_to_ksa_range(km, old_pos);
+	ubifs_assert(ksa_range >= 0 && ksa_range < km->ksa_ranges - 1);
+	return keymap_free_key_in_min_range(c, ksa_range + 1, new_pos, key);
+}
+
+/* This function determines if a key position should be promoted to a
+ * higher KSA range. The criteria is the key's current KSA range, and that
+ * KSA range's promotion policy: (expected) number of garbage collections
+ * between promotions. If it is 0, it will not be protomoted. If it is 1, it
+ * will be promoted. Otherwise, it is promoted with probability
+ * 1/2**gcs_until_promotion.
+ */
+int keymap_gc_should_promote(struct ubifs_info *c, u64 pos)
+{
+	int value;
+	int shift;
+	int ksa_range;
+	struct ubifs_keymap *km = c->km;
+
+	RETURN_VAL_IF(0, !km);
+	RETURN_VAL_IF(0, km->read_only);
+
+	ksa_range = keymap_pos_to_ksa_range(km, pos);
+	RETURN_VAL_IF(0, km->ksa[ksa_range].gcs_until_promotion == 0);
+	RETURN_VAL_IF(1, km->ksa[ksa_range].gcs_until_promotion == 1);
+
+	shift = sizeof(int) << 3;
+	get_random_bytes((u8 *) &value, sizeof(int));
+	shift -= km->ksa[ksa_range].gcs_until_promotion;
+	if (shift < 0)
+		shift = 0;
+	if ((value << shift) == 0)
+		return 1;
+
+	return 0;
+}
+
+/* This function sets pos to an unused key position by calling
+ * keymap_free_key with a NULL key.
+ */
+int keymap_free_pos(struct ubifs_info *c, u64 *pos)
+{
+	return keymap_free_key(c, pos, NULL);
+}
+
+/* This function reads a key as defined by the LEB number and offset, and puts
+ * the read key in the buffer buf. It tries to use the appropriate KSA range
+ * cache if it is valid for the position, otherwise it reads it from flash and
+ * updates the corresponding read/write cache. It assumes it is a write
+ * operation if the key being read is the same as the KSA range's current
+ * key assignment position.
+ */
+static int keymap_read_key_cache(struct ubifs_info *c, u32 eb,
+				 u32 offset, u8 *buf)
+{
+	struct ubifs_keymap *km = c->km;
+	struct ksa_range *ksa;
+	struct key_cache *cache;
+	int err;
+	int byte_offset;
+	int page;
+	int page_offset;
+	int page_num;
+
+	RETURN_VAL_IF(0, !km);
+	ksa = &(km->ksa[keymap_eb_to_ksa_range(km, eb)]);
+	cache = NULL;
+	byte_offset = offset * c->km->key_size;
+	page = byte_offset >> c->min_io_shift;
+	page_offset = byte_offset - (page << c->min_io_shift);
+	page_num = page + eb * (c->leb_size >> c->min_io_shift);
+
+	/* Determine if its a write based on cur pos, and set the correct 
cache.
+	 * Unless the other cache is actually on the same page, in which case
+	 * use that instead.
+	 */
+	if (eb_offset_to_pos(eb, offset) == ksa->cur_pos &&
+			keymap_is_free(km, ksa->cur_pos)) {
+		cache = &ksa->wr_cache;
+		if (ksa->rd_cache.page_num == page_num)
+			cache = &ksa->rd_cache;
+	} else {
+		cache = &ksa->rd_cache;
+		if (ksa->wr_cache.page_num == page_num)
+			cache = &ksa->wr_cache;
+	}
+
+	if (cache->page_num != page_num) {
+		err = ubi_read(c->ubi, eb + c->km->ksa_first, cache->page,
+			       page << c->min_io_shift, c->min_io_size);
+		RETURN_ON_ERR(err);
+		cache->page_num = page_num;
+	}
+	memcpy(buf, cache->page + page_offset, c->km->key_size);
+	return 0;
+}
+
+/* This function reads a key and stores the result in buf. It converts the key
+ * position to an LEB number and offset, then calls keymap_read_key_cache.
+ */
+int keymap_read_key(struct ubifs_info *c, u64 pos, u8* buf)
+{
+	u32 eb;
+	u32 offset;
+	RETURN_VAL_IF(0, !c->km);
+	pos_to_eb_offset(pos, &eb, &offset);
+	RETURN_VAL_IF(-EINVAL, eb > c->km->ksa_size ||
+			       offset > c->km->keys_per_eb);
+	return keymap_read_key_cache(c, eb, offset, buf);
+}
+
+/* This function marks a key position as being used in the key state map. It
+ * assumes that the key state map is already locked.
+ */
+static void keymap_mark_used_internal(struct ubifs_keymap *km, u64 pos)
+{
+	u32 eb, offset;
+	pos_to_eb_offset(pos, &eb, &offset);
+	if (offset >= km->keys_per_eb || eb > km->ksa_size) {
+		ubifs_err("(keymap) [%d:%d] out of range to mark used.",
+			  (int) eb, (int) offset);
+		return;
+	}
+	if (get_state(km, eb, offset) == KEYMAP_STATE_FREE)
+		km->free--;
+	set_state(km, eb, offset, KEYMAP_STATE_USED);
+}
+
+/* This function marks a key position as used, and it can be called from
+ * outside keymap.c.  It simply locks the key state map and calls the internal
+ * version.
+ */
+void keymap_mark_used(struct ubifs_info *c, u64 pos)
+{
+	struct ubifs_keymap *km = c->km;
+	if (!km)
+		return;
+	ubifs_assert(!km->read_only);
+	mutex_lock(&km->state_mutex);
+	keymap_mark_used_internal(km, pos);
+	mutex_unlock(&km->state_mutex);
+}
+
+/* This function marks a key position as deleted.
+ */
+void keymap_mark_deleted(struct ubifs_info *c, u64 pos)
+{
+	u32 eb, offset;
+	struct ubifs_keymap *km = c->km;
+	if (!km)
+		return;
+	ubifs_assert(!km->read_only);
+
+	mutex_lock(&km->state_mutex);
+	pos_to_eb_offset(pos, &eb, &offset);
+
+	if (offset >= km->keys_per_eb || eb > km->ksa_size) {
+		ubifs_err("(keymap) [%d:%d] out of range to mark deleted.",
+			  (int) eb, (int) offset);
+		goto unlock;
+	}
+
+	if (get_state(km, eb, offset) != KEYMAP_STATE_DELETED)
+		km->deleted++;
+
+	set_state(km, eb, offset, KEYMAP_STATE_DELETED);
+
+unlock:
+	mutex_unlock(&km->state_mutex);
+}
+
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/Makefile linux-3.2.1-ubifsec/fs/ubifs/Makefile
--- linux-3.2.1-vanilla/fs/ubifs/Makefile	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/Makefile	2012-02-08 16:58:48.913518449 
+0100
@@ -1,6 +1,6 @@
  obj-$(CONFIG_UBIFS_FS) += ubifs.o

-ubifs-y += shrinker.o journal.o file.o dir.o super.o sb.o io.o
+ubifs-y += shrinker.o journal.o file.o dir.o super.o sb.o io.o keymap.o
  ubifs-y += tnc.o master.o scan.o replay.o log.o commit.o gc.o orphan.o
  ubifs-y += budget.o find.o tnc_commit.o compress.o lpt.o lprops.o
  ubifs-y += recovery.o ioctl.o lpt_commit.o tnc_misc.o
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/replay.c linux-3.2.1-ubifsec/fs/ubifs/replay.c
--- linux-3.2.1-vanilla/fs/ubifs/replay.c	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/replay.c	2012-02-08 16:54:49.145506844 
+0100
@@ -67,6 +67,7 @@ struct replay_entry {
  			loff_t new_size;
  		};
  	};
+	unsigned long long crypto_lookup;
  };

  /**
@@ -249,10 +250,12 @@ static int apply_replay_entry(struct ubi
  			default:
  				err = ubifs_tnc_remove(c, &r->key);
  				break;
-			}
-		else
-			err = ubifs_tnc_add(c, &r->key, r->lnum, r->offs,
-					    r->len);
+		} else {
+			err = ubifs_tnc_add_with_crypto_lookup(
+				c, &r->key, r->lnum, r->offs, r->len,
+				r->crypto_lookup);
+
+		}
  		if (err)
  			return err;

@@ -354,10 +357,11 @@ static void destroy_replay_list(struct u
   * order, the older modifications are applied first. This function returns 
zero
   * in case of success and a negative error code in case of failure.
   */
-static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
-		       union ubifs_key *key, unsigned long long sqnum,
-		       int deletion, int *used, loff_t old_size,
-		       loff_t new_size)
+static int insert_node(
+	struct ubifs_info *c, int lnum, int offs, int len,
+	union ubifs_key *key, unsigned long long sqnum,
+	int deletion, int *used, loff_t old_size, loff_t new_size,
+	unsigned long long crypto_lookup)
  {
  	struct replay_entry *r;

@@ -372,6 +376,7 @@ static int insert_node(struct ubifs_info

  	if (!deletion)
  		*used += ALIGN(len, 8);
+	r->crypto_lookup = crypto_lookup;
  	r->lnum = lnum;
  	r->offs = offs;
  	r->len = len;
@@ -607,7 +612,7 @@ static int replay_bud(struct ubifs_info
  				deletion = 1;
  			err = insert_node(c, lnum, snod->offs, snod->len,
  					  &snod->key, snod->sqnum, deletion,
-					  &used, 0, new_size);
+					  &used, 0, new_size, 0);
  			break;
  		}
  		case UBIFS_DATA_NODE:
@@ -616,10 +621,11 @@ static int replay_bud(struct ubifs_info
  			loff_t new_size = le32_to_cpu(dn->size) +
  					  key_block(c, &snod->key) *
  					  UBIFS_BLOCK_SIZE;
-
-			err = insert_node(c, lnum, snod->offs, snod->len,
-					  &snod->key, snod->sqnum, deletion,
-					  &used, 0, new_size);
+			err = insert_node(
+				c, lnum, snod->offs, snod->len,
+				&snod->key, snod->sqnum, deletion,
+				&used, 0, new_size,
+				le64_to_cpu(dn->crypto_lookup));
  			break;
  		}
  		case UBIFS_DENT_NODE:
@@ -659,7 +665,7 @@ static int replay_bud(struct ubifs_info
  			trun_key_init(c, &key, le32_to_cpu(trun->inum));
  			err = insert_node(c, lnum, snod->offs, snod->len,
  					  &key, snod->sqnum, 1, &used,
-					  old_size, new_size);
+					  old_size, new_size, 0);
  			break;
  		}
  		default:
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/sb.c linux-3.2.1-ubifsec/fs/ubifs/sb.c
--- linux-3.2.1-vanilla/fs/ubifs/sb.c	2012-01-12 20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/sb.c	2012-02-08 16:54:49.145506844 +0100
@@ -82,6 +82,7 @@ static int create_default_filesystem(str
  	int err, tmp, jnl_lebs, log_lebs, max_buds, main_lebs, main_first;
  	int lpt_lebs, lpt_first, orph_lebs, big_lpt, ino_waste, sup_flags = 0;
  	int min_leb_cnt = UBIFS_MIN_LEB_CNT;
+	int crypto_lebs, crypto_first;
  	long long tmp64, main_bytes;
  	__le64 tmp_le64;

@@ -139,8 +140,22 @@ static int create_default_filesystem(str
  		 */
  		orph_lebs += 1;
  #endif
+	if (c->use_ubifsec) {
+		/* Compute the size of the key space area based on partition
+		 * geometry.
+		 */
+		unsigned long long partition_bytes = c->leb_cnt * c->leb_size;
+		unsigned long long data_nodes =
+			partition_bytes >> UBIFS_BLOCK_SHIFT;
+		do_div(data_nodes, keymap_keys_per_eb(c));
+		crypto_lebs = data_nodes + UBIFSEC_CRYPTO_LEBS_MIN +
+			(data_nodes >> UBIFSEC_CRYPTO_LEBS_SCALE_SHIFT);
+	} else {
+		crypto_lebs = 0;
+	}

-	main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS - log_lebs;
+	main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS -
+		    log_lebs - crypto_lebs;
  	main_lebs -= orph_lebs;

  	lpt_first = UBIFS_LOG_LNUM + log_lebs;
@@ -155,6 +170,7 @@ static int create_default_filesystem(str
  		lpt_first + lpt_lebs - 1);

  	main_first = c->leb_cnt - main_lebs;
+	crypto_first = c->leb_cnt - main_lebs - crypto_lebs;

  	/* Create default superblock */
  	tmp = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);
@@ -175,6 +191,7 @@ static int create_default_filesystem(str
  	sup->max_leb_cnt   = cpu_to_le32(c->max_leb_cnt);
  	sup->max_bud_bytes = cpu_to_le64(tmp64);
  	sup->log_lebs      = cpu_to_le32(log_lebs);
+	sup->crypto_lebs   = cpu_to_le32(crypto_lebs);
  	sup->lpt_lebs      = cpu_to_le32(lpt_lebs);
  	sup->orph_lebs     = cpu_to_le32(orph_lebs);
  	sup->jhead_cnt     = cpu_to_le32(DEFAULT_JHEADS_CNT);
@@ -182,6 +199,7 @@ static int create_default_filesystem(str
  	sup->lsave_cnt     = cpu_to_le32(c->lsave_cnt);
  	sup->fmt_version   = cpu_to_le32(UBIFS_FORMAT_VERSION);
  	sup->time_gran     = cpu_to_le32(DEFAULT_TIME_GRAN);
+	sup->use_ubifsec   = cpu_to_le32(c->use_ubifsec);
  	if (c->mount_opts.override_compr)
  		sup->default_compr = cpu_to_le16(c->mount_opts.compr_type);
  	else
@@ -440,7 +458,7 @@ static int validate_sb(struct ubifs_info
  	}

  	if (UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs +
-	    c->orph_lebs + c->main_lebs != c->leb_cnt) {
+	    c->orph_lebs + c->main_lebs + c->crypto_lebs != c->leb_cnt) {
  		err = 12;
  		goto failed;
  	}
@@ -603,6 +621,7 @@ int ubifs_read_superblock(struct ubifs_i
  	c->max_bud_bytes = le64_to_cpu(sup->max_bud_bytes);
  	c->log_lebs      = le32_to_cpu(sup->log_lebs);
  	c->lpt_lebs      = le32_to_cpu(sup->lpt_lebs);
+	c->crypto_lebs   = le32_to_cpu(sup->crypto_lebs);
  	c->orph_lebs     = le32_to_cpu(sup->orph_lebs);
  	c->jhead_cnt     = le32_to_cpu(sup->jhead_cnt) + NONDATA_JHEADS_CNT;
  	c->fanout        = le32_to_cpu(sup->fanout);
@@ -610,6 +629,7 @@ int ubifs_read_superblock(struct ubifs_i
  	c->rp_size       = le64_to_cpu(sup->rp_size);
  	c->rp_uid        = le32_to_cpu(sup->rp_uid);
  	c->rp_gid        = le32_to_cpu(sup->rp_gid);
+	c->use_ubifsec   = le32_to_cpu(sup->use_ubifsec);
  	sup_flags        = le32_to_cpu(sup->flags);
  	if (!c->mount_opts.override_compr)
  		c->default_compr = le16_to_cpu(sup->default_compr);
@@ -643,8 +663,10 @@ int ubifs_read_superblock(struct ubifs_i
  	c->lpt_last = c->lpt_first + c->lpt_lebs - 1;
  	c->orph_first = c->lpt_last + 1;
  	c->orph_last = c->orph_first + c->orph_lebs - 1;
-	c->main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS;
+	c->main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS -
+		       c->crypto_lebs;
  	c->main_lebs -= c->log_lebs + c->lpt_lebs + c->orph_lebs;
+	c->crypto_first = c->leb_cnt - c->main_lebs - c->crypto_lebs;
  	c->main_first = c->leb_cnt - c->main_lebs;

  	err = validate_sb(c, sup);
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/super.c linux-3.2.1-ubifsec/fs/ubifs/super.c
--- linux-3.2.1-vanilla/fs/ubifs/super.c	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/super.c	2012-02-08 16:54:49.149506844 
+0100
@@ -438,7 +438,8 @@ static int ubifs_show_options(struct seq
  		seq_printf(s, ",chk_data_crc");
  	else if (c->mount_opts.chk_data_crc == 1)
  		seq_printf(s, ",no_chk_data_crc");
-
+	if (c->mount_opts.use_ubifsec)
+		seq_printf(s, ",use_ubifsec");
  	if (c->mount_opts.override_compr) {
  		seq_printf(s, ",compr=%s",
  			   ubifs_compr_name(c->mount_opts.compr_type));
@@ -938,6 +939,8 @@ static int check_volume_empty(struct ubi
   * Opt_chk_data_crc: check CRCs when reading data nodes
   * Opt_no_chk_data_crc: do not check CRCs when reading data nodes
   * Opt_override_compr: override default compressor
+ * Opt_commit_interval: seconds between a forced commit
+ * Opt_use_ubifsec: use ubifsec secure deletion feature
   * Opt_err: just end of array marker
   */
  enum {
@@ -948,6 +951,8 @@ enum {
  	Opt_chk_data_crc,
  	Opt_no_chk_data_crc,
  	Opt_override_compr,
+	Opt_commit_interval,
+	Opt_use_ubifsec,
  	Opt_err,
  };

@@ -959,6 +964,8 @@ static const match_table_t tokens = {
  	{Opt_chk_data_crc, "chk_data_crc"},
  	{Opt_no_chk_data_crc, "no_chk_data_crc"},
  	{Opt_override_compr, "compr=%s"},
+	{Opt_commit_interval, "commit_interval=%s"},
+	{Opt_use_ubifsec, "use_ubifsec"},
  	{Opt_err, NULL},
  };

@@ -1036,6 +1043,18 @@ static int ubifs_parse_options(struct ub
  			c->mount_opts.chk_data_crc = 1;
  			c->no_chk_data_crc = 1;
  			break;
+		case Opt_commit_interval:
+		{
+			struct timeval tv;
+			unsigned long seconds;
+			char *name = match_strdup(&args[0]);
+			kstrtoul(name, 10, &seconds);
+			kfree(name);
+			c->commit_interval = seconds;
+			do_gettimeofday(&tv);
+			c->next_commit = tv.tv_sec + c->commit_interval;
+			break;
+		}
  		case Opt_override_compr:
  		{
  			char *name = match_strdup(&args[0]);
@@ -1058,6 +1077,12 @@ static int ubifs_parse_options(struct ub
  			c->default_compr = c->mount_opts.compr_type;
  			break;
  		}
+		case Opt_use_ubifsec:
+		{
+			c->mount_opts.use_ubifsec = 1;
+			c->use_ubifsec = 1;
+			break;
+		}
  		default:
  		{
  			unsigned long flag;
@@ -1236,6 +1261,12 @@ static int mount_ubifs(struct ubifs_info
  	err = ubifs_read_superblock(c);
  	if (err)
  		goto out_free;
+	keymap_init(c, c->ro_media, c->use_ubifsec);
+	if (c->use_ubifsec && !c->km)
+		goto out_free;
+
+	/* After reading super block, give the keymap its geometry. */
+	keymap_assign_lebs(c);

  	/*
  	 * Make sure the compressor which is set as default in the superblock
@@ -1487,6 +1518,7 @@ static int mount_ubifs(struct ubifs_info
  		c->bud_bytes, c->bud_bytes >> 10, c->bud_bytes >> 20);
  	dbg_msg("max. seq. number:    %llu", c->max_sqnum);
  	dbg_msg("commit number:       %llu", c->cmt_no);
+	dbg_msg("use ubifsec:         %d", c->use_ubifsec);

  	return 0;

@@ -1514,6 +1546,7 @@ out_free:
  	kfree(c->bu.buf);
  	vfree(c->ileb_buf);
  	vfree(c->sbuf);
+	keymap_free(c);
  	kfree(c->bottom_up_buf);
  	ubifs_debugging_exit(c);
  	return err;
@@ -1553,6 +1586,7 @@ static void ubifs_umount(struct ubifs_in
  	kfree(c->bu.buf);
  	vfree(c->ileb_buf);
  	vfree(c->sbuf);
+	keymap_free(c);
  	kfree(c->bottom_up_buf);
  	ubifs_debugging_exit(c);
  }
@@ -2010,6 +2044,8 @@ static struct ubifs_info *alloc_ubifs_in

  		c->highest_inum = UBIFS_FIRST_INO;
  		c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM;
+		c->commit_interval = (unsigned long) -1;
+		c->use_ubifsec = 0;

  		ubi_get_volume_info(ubi, &c->vi);
  		ubi_get_device_info(c->vi.ubi_num, &c->di);
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/tnc.c linux-3.2.1-ubifsec/fs/ubifs/tnc.c
--- linux-3.2.1-vanilla/fs/ubifs/tnc.c	2012-01-12 20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/tnc.c	2012-02-08 16:54:49.149506844 +0100
@@ -2154,19 +2154,22 @@ do_split:
  }

  /**
- * ubifs_tnc_add - add a node to TNC.
+ * ubifs_tnc_add_with_crypto_lookup - add a node to TNC.
   * @c: UBIFS file-system description object
   * @key: key to add
   * @lnum: LEB number of node
   * @offs: node offset
   * @len: node length
+ * @crypto_lookup: the node's key position in the KSA
   *
   * This function adds a node with key @key to TNC. The node may be new or it 
may
   * obsolete some existing one. Returns %0 on success or negative error code on
   * failure.
   */
-int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
-		  int offs, int len)
+int ubifs_tnc_add_with_crypto_lookup(struct ubifs_info *c,
+				     const union ubifs_key *key, int lnum,
+				     int offs, int len,
+				     unsigned long long crypto_lookup)
  {
  	int found, n, err = 0;
  	struct ubifs_znode *znode;
@@ -2181,11 +2184,20 @@ int ubifs_tnc_add(struct ubifs_info *c,
  		zbr.lnum = lnum;
  		zbr.offs = offs;
  		zbr.len = len;
+		if (c->use_ubifsec && key_type(c, key) == UBIFS_DATA_KEY) {
+			zbr.crypto_lookup = crypto_lookup;
+			keymap_mark_used(c, zbr.crypto_lookup);
+		}
  		key_copy(c, key, &zbr.key);
  		err = tnc_insert(c, znode, &zbr, n + 1);
  	} else if (found == 1) {
  		struct ubifs_zbranch *zbr = &znode->zbranch[n];

+		if (c->use_ubifsec && key_type(c, key) == UBIFS_DATA_KEY) {
+			keymap_mark_deleted(c, zbr->crypto_lookup);
+			zbr->crypto_lookup = crypto_lookup;
+			keymap_mark_used(c, zbr->crypto_lookup);
+		}
  		lnc_free(zbr);
  		err = ubifs_add_dirt(c, zbr->lnum, zbr->len);
  		zbr->lnum = lnum;
@@ -2200,6 +2212,13 @@ int ubifs_tnc_add(struct ubifs_info *c,
  	return err;
  }

+int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
+		  int offs, int len) {
+	if (c->use_ubifsec && key_type(c, key) == UBIFS_DATA_KEY)
+		ubifs_assert(c->use_ubifsec == 0);
+	return ubifs_tnc_add_with_crypto_lookup(c, key, lnum, offs, len, 0);
+}
+
  /**
   * ubifs_tnc_replace - replace a node in the TNC only if the old node is 
found.
   * @c: UBIFS file-system description object
@@ -2215,7 +2234,8 @@ int ubifs_tnc_add(struct ubifs_info *c,
   * Returns %0 on success or negative error code on failure.
   */
  int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key,
-		      int old_lnum, int old_offs, int lnum, int offs, int len)
+		      int old_lnum, int old_offs, int lnum, int offs, int len,
+		      u64 crypto_lookup)
  {
  	int found, n, err = 0;
  	struct ubifs_znode *znode;
@@ -2234,6 +2254,15 @@ int ubifs_tnc_replace(struct ubifs_info

  		found = 0;
  		if (zbr->lnum == old_lnum && zbr->offs == old_offs) {
+			if (c->use_ubifsec &&
+			    key_type(c, key) == UBIFS_DATA_KEY) {
+				if (zbr->crypto_lookup != crypto_lookup) {
+					keymap_mark_deleted(
+						c, zbr->crypto_lookup);
+					zbr->crypto_lookup = crypto_lookup;
+					keymap_mark_used(c, 
zbr->crypto_lookup);
+				}
+			}
  			lnc_free(zbr);
  			err = ubifs_add_dirt(c, zbr->lnum, zbr->len);
  			if (err)
@@ -2401,6 +2430,8 @@ static int tnc_delete(struct ubifs_info
  	dbg_tnc("deleting %s", DBGKEY(&znode->zbranch[n].key));

  	zbr = &znode->zbranch[n];
+	if (c->use_ubifsec && key_type(c, &(zbr->key)) == UBIFS_DATA_KEY)
+		keymap_mark_deleted(c, zbr->crypto_lookup);
  	lnc_free(zbr);

  	err = ubifs_add_dirt(c, zbr->lnum, zbr->len);
@@ -2478,6 +2509,7 @@ static int tnc_delete(struct ubifs_info
  			c->zroot.lnum = zbr->lnum;
  			c->zroot.offs = zbr->offs;
  			c->zroot.len = zbr->len;
+			c->zroot.crypto_lookup = zbr->crypto_lookup;
  			c->zroot.znode = znode;
  			ubifs_assert(!ubifs_zn_obsolete(zp));
  			ubifs_assert(ubifs_zn_dirty(zp));
@@ -2647,6 +2679,7 @@ int ubifs_tnc_remove_range(struct ubifs_
  			key = &znode->zbranch[i].key;
  			if (!key_in_range(c, key, from_key, to_key))
  				break;
+			keymap_mark_deleted(c, 
znode->zbranch[i].crypto_lookup);
  			lnc_free(&znode->zbranch[i]);
  			err = ubifs_add_dirt(c, znode->zbranch[i].lnum,
  					     znode->zbranch[i].len);
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/tnc_commit.c 
linux-3.2.1-ubifsec/fs/ubifs/tnc_commit.c
--- linux-3.2.1-vanilla/fs/ubifs/tnc_commit.c	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/tnc_commit.c	2012-02-08 16:54:49.149506844 
+0100
@@ -51,6 +51,8 @@ static int make_idx_node(struct ubifs_in
  		key_write_idx(c, &zbr->key, &br->key);
  		br->lnum = cpu_to_le32(zbr->lnum);
  		br->offs = cpu_to_le32(zbr->offs);
+		if (key_type(c, &zbr->key) == UBIFS_DATA_KEY)
+			br->crypto_lookup = cpu_to_le64(zbr->crypto_lookup);
  		br->len = cpu_to_le32(zbr->len);
  		if (!zbr->lnum || !zbr->len) {
  			ubifs_err("bad ref in znode");
@@ -859,6 +861,10 @@ static int write_index(struct ubifs_info
  			struct ubifs_zbranch *zbr = &znode->zbranch[i];

  			key_write_idx(c, &zbr->key, &br->key);
+			if (key_type(c, &zbr->key) == UBIFS_DATA_KEY) {
+				br->crypto_lookup =
+					cpu_to_le64(zbr->crypto_lookup);
+			}
  			br->lnum = cpu_to_le32(zbr->lnum);
  			br->offs = cpu_to_le32(zbr->offs);
  			br->len = cpu_to_le32(zbr->len);
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/tnc_misc.c linux-3.2.1-ubifsec/fs/ubifs/tnc_misc.c
--- linux-3.2.1-vanilla/fs/ubifs/tnc_misc.c	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/tnc_misc.c	2012-02-08 16:54:49.149506844 
+0100
@@ -309,6 +309,7 @@ static int read_znode(struct ubifs_info
  		zbr->lnum = le32_to_cpu(br->lnum);
  		zbr->offs = le32_to_cpu(br->offs);
  		zbr->len  = le32_to_cpu(br->len);
+		zbr->crypto_lookup = 0;
  		zbr->znode = NULL;

  		/* Validate branch */
@@ -323,10 +324,12 @@ static int read_znode(struct ubifs_info

  		switch (key_type(c, &zbr->key)) {
  		case UBIFS_INO_KEY:
-		case UBIFS_DATA_KEY:
  		case UBIFS_DENT_KEY:
  		case UBIFS_XENT_KEY:
  			break;
+		case UBIFS_DATA_KEY:
+			zbr->crypto_lookup = le64_to_cpu(br->crypto_lookup);
+			break;
  		default:
  			dbg_msg("bad key type at slot %d: %s", i,
  				DBGKEY(&zbr->key));
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/ubifs.h linux-3.2.1-ubifsec/fs/ubifs/ubifs.h
--- linux-3.2.1-vanilla/fs/ubifs/ubifs.h	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/ubifs.h	2012-02-08 16:54:51.969506980 
+0100
@@ -163,6 +163,19 @@
  /* Maximum number of data nodes to bulk-read */
  #define UBIFS_MAX_BULK_READ 32

+/* Size of 128 bits in bytes */
+#define AES_KEYSIZE_128 16
+
+/* Key size in bytes for UBIFSEC */
+#define UBIFSEC_KEYSIZE AES_KEYSIZE_128
+#define UBIFSEC_CRYPTO_ALGORITHM "ctr(aes)"
+#define UBIFSEC_CRYPTO_LEBS_SCALE_SHIFT 4
+#define UBIFSEC_CRYPTO_LEBS_MIN 3
+#define UBIFSEC_COMMIT_INTERVAL(c)					\
+	((c)->use_ubifsec && (c)->commit_interval != -1)
+
+#define POISON_KEY(p) memset(p, 0xff, UBIFSEC_KEYSIZE)
+
  /*
   * Lockdep classes for UBIFS inode @ui_mutex.
   */
@@ -749,6 +762,7 @@ struct ubifs_zbranch {
  	int lnum;
  	int offs;
  	int len;
+	unsigned long long crypto_lookup;
  };

  /**
@@ -930,6 +944,7 @@ struct ubifs_orphan {
   *                  specified in @compr_type)
   * @compr_type: compressor type to override the superblock compressor with
   *              (%UBIFS_COMPR_NONE, etc)
+ * @use_ubifsec: use ubifsec secure deletion feature
   */
  struct ubifs_mount_opts {
  	unsigned int unmount_mode:2;
@@ -937,6 +952,7 @@ struct ubifs_mount_opts {
  	unsigned int chk_data_crc:2;
  	unsigned int override_compr:1;
  	unsigned int compr_type:2;
+	unsigned int use_ubifsec:1;
  };

  /**
@@ -974,6 +990,7 @@ struct ubifs_budg_info {
  };

  struct ubifs_debug_info;
+struct ubifs_keymap;

  /**
   * struct ubifs_info - UBIFS file-system description data structure
@@ -1218,6 +1235,13 @@ struct ubifs_debug_info;
   *                  FS to R/W mode
   * @size_tree: inode size information for recovery
   * @mount_opts: UBIFS-specific mount options
+ * @km: the keymap data structure for ubifsec
+ * @crypto_lebs: the number of LEBS assigned to store keys for the keymap
+ * @crypto_first: the number of the first LEB assigned to store keys
+ * @use_ubifsec: the switch to enable/disable UBIFSec
+ * @commit_interval: the number of seconds between forced commiting to purge
+ *                   the key storage area of deleted keys
+ * @next_commit: the time at which the next commit will occur
   *
   * @dbg: debugging-related information
   */
@@ -1450,6 +1474,13 @@ struct ubifs_info {
  #ifdef CONFIG_UBIFS_FS_DEBUG
  	struct ubifs_debug_info *dbg;
  #endif
+
+	struct ubifs_keymap *km;
+	int crypto_lebs;
+	int crypto_first;
+	unsigned int use_ubifsec;
+	long commit_interval;
+	unsigned long long next_commit;
  };

  extern struct list_head ubifs_infos;
@@ -1489,6 +1520,7 @@ int ubifs_write_node(struct ubifs_info *
  		     int offs, int dtype);
  int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
  		     int offs, int quiet, int must_chk_crc);
+void ubifs_set_datanode_crc(void *node);
  void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad);
  void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last);
  int ubifs_io_init(struct ubifs_info *c);
@@ -1579,8 +1611,12 @@ int ubifs_tnc_locate(struct ubifs_info *
  		     void *node, int *lnum, int *offs);
  int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
  		  int offs, int len);
+int ubifs_tnc_add_with_crypto_lookup(
+	struct ubifs_info *c, const union ubifs_key *key,
+	int lnum, int offs, int len, unsigned long long crypto_lookup);
  int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key,
-		      int old_lnum, int old_offs, int lnum, int offs, int len);
+		      int old_lnum, int old_offs, int lnum, int offs,
+		      int len, u64 crypto_lookup);
  int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
  		     int lnum, int offs, int len, const struct qstr *nm);
  int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key);
@@ -1775,9 +1811,26 @@ long ubifs_compat_ioctl(struct file *fil
  int __init ubifs_compressors_init(void);
  void ubifs_compressors_exit(void);
  void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int 
*out_len,
-		    int *compr_type);
-int ubifs_decompress(const void *buf, int len, void *out, int *out_len,
-		     int compr_type);
+		    int *compr_type, u8* key);
+int ubifs_decompress(void *buf, int len, void *out, int *out_len,
+		     int compr_type, u8 *key);
+int ubifs_aes_crypt(u8 *str, int len, u8 *key, int keylen, u8 *iv, int ivlen);
+
+/* keymap.c */
+int keymap_purge(struct ubifs_info *c);
+int keymap_init(struct ubifs_info *c, int read_only, int use_ubifsec);
+void keymap_free(struct ubifs_info *c);
+int keymap_free_key(struct ubifs_info *c, u64 *pos, u8 *key);
+int keymap_read_key(struct ubifs_info *c, u64 pos, u8 *buf);
+void keymap_mark_used(struct ubifs_info *c, u64 pos);
+void keymap_mark_deleted(struct ubifs_info *c, u64 pos);
+int keymap_assign_lebs(struct ubifs_info *c);
+int keymap_keys_per_eb(struct ubifs_info *c);
+int keymap_promote_key(struct ubifs_info *c, u64 old_pos, u64 *new_pos,
+		       u8 *key);
+int keymap_gc_should_promote(struct ubifs_info *c, u64 pos);
+int keymap_swap_encryption_key(struct ubifs_info *c,
+			       struct ubifs_data_node *dn);

  #include "debug.h"
  #include "misc.h"
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/ubifs-media.h 
linux-3.2.1-ubifsec/fs/ubifs/ubifs-media.h
--- linux-3.2.1-vanilla/fs/ubifs/ubifs-media.h	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/ubifs-media.h	2012-02-08 16:54:51.969506980 
+0100
@@ -550,9 +550,14 @@ struct ubifs_dent_node {
   * Note, do not forget to amend 'zero_data_node_unused()' function when
   * changing the padding fields.
   */
+/* TODO(reardon): sorry about lifting these 8 unused bytes from @key to
+ * use for @crypto_lookup. Can someone reviewing this give me a more elegant
+ * and maintable way of preserving the size of data in a data node?
+ */
  struct ubifs_data_node {
  	struct ubifs_ch ch;
-	__u8 key[UBIFS_MAX_KEY_LEN];
+	__u8 key[UBIFS_MAX_KEY_LEN/2];
+	__u64 crypto_lookup;
  	__le32 size;
  	__le16 compr_type;
  	__u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */
@@ -614,10 +619,12 @@ struct ubifs_pad_node {
   * @rp_uid: reserve pool UID
   * @rp_gid: reserve pool GID
   * @rp_size: size of the reserved pool in bytes
- * @padding2: reserved for future, zeroes
   * @time_gran: time granularity in nanoseconds
   * @uuid: UUID generated when the file system image was created
   * @ro_compat_version: UBIFS R/O compatibility version
+ * @crypto_lebs: number of LEBS being used to store keys
+ * @use_ubifsec: whether the file system should use ubifsec secure deletio
+ * @padding2: reserved for future, zeroes
   */
  struct ubifs_sb_node {
  	struct ubifs_ch ch;
@@ -645,7 +652,9 @@ struct ubifs_sb_node {
  	__le32 time_gran;
  	__u8 uuid[16];
  	__le32 ro_compat_version;
-	__u8 padding2[3968];
+	__le32 crypto_lebs;
+	__u8 use_ubifsec;
+	__u8 padding2[3963];
  } __packed;

  /**
@@ -742,6 +751,7 @@ struct ubifs_branch {
  	__le32 lnum;
  	__le32 offs;
  	__le32 len;
+	__le64 crypto_lookup;
  	__u8 key[];
  } __packed;

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-02-09 15:24 [patch] Adding Secure Deletion to UBIFS Joel Reardon
@ 2012-02-13 16:54 ` Artem Bityutskiy
  2012-02-23 14:59   ` Joel Reardon
  2012-02-20 20:15 ` [patch] Move CRC computation to separate function Joel Reardon
  2012-02-29 17:25 ` [patch] Adding Secure Deletion to UBIFS Artem Bityutskiy
  2 siblings, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-02-13 16:54 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 671 bytes --]

Hi, I will try to get into more details soon, but to keep you busy - the
first thing to start is to introduce new on-flash format version -
because you need more space in 'struct ubifs_branch'

On Thu, 2012-02-09 at 16:24 +0100, Joel Reardon wrote:
> @@ -742,6 +751,7 @@ struct ubifs_branch {
>   	__le32 lnum;
>   	__le32 offs;
>   	__le32 len;
> +	__le64 crypto_lookup;
>   	__u8 key[];
>   } __packed;

Could you make a separate patch which adds new on-flash format and make
sure that new ubifs binaries can mount the old formats and work with
them. And that old ubifs binaries gracefully reject the new format.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [patch] Move CRC computation to separate function
  2012-02-09 15:24 [patch] Adding Secure Deletion to UBIFS Joel Reardon
  2012-02-13 16:54 ` Artem Bityutskiy
@ 2012-02-20 20:15 ` Joel Reardon
  2012-02-29 16:10   ` Artem Bityutskiy
  2012-02-29 17:25 ` [patch] Adding Secure Deletion to UBIFS Artem Bityutskiy
  2 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-02-20 20:15 UTC (permalink / raw)
  To: linux-mtd; +Cc: linux-kernel, linux-fsdevel

This patch moves the computation of CRCs for data nodes from 
within ubifs_prepare_node to a separate function ubifs_set_datanode_crc, 
which takes a data node, and computes and sets the CRC. This is to avoid 
duplication of the CRC computation code in other places where it may be 
needed.

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/io.c linux-3.2.1-ubifsec/fs/ubifs/io.c
--- linux-3.2.1-vanilla/fs/ubifs/io.c   2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/io.c   2012-02-20 20:17:48.796684293 
+0100
@@ -367,6 +367,18 @@ static unsigned long long next_sqnum(str
  }

  /**
+ * ubifs_set_datanode_crc - writes the crc for a data node to the common
+ * header.
+ * @node: the data node
+ */
+void ubifs_set_datanode_crc(void *node)
+{
+       struct ubifs_ch *ch = (struct ubifs_ch *) node;
+       int len = le32_to_cpu(ch->len);
+       ch->crc = cpu_to_le32(crc32(UBIFS_CRC32_INIT, node + 8, len - 8));
+}
+
+/**
   * ubifs_prepare_node - prepare node to be written to flash.
   * @c: UBIFS file-system description object
   * @node: the node to pad
@@ -379,7 +391,6 @@ static unsigned long long next_sqnum(str
   */
  void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int 
pad)
  {
-       uint32_t crc;
         struct ubifs_ch *ch = node;
         unsigned long long sqnum = next_sqnum(c);

@@ -390,8 +401,7 @@ void ubifs_prepare_node(struct ubifs_inf
         ch->group_type = UBIFS_NO_NODE_GROUP;
         ch->sqnum = cpu_to_le64(sqnum);
         ch->padding[0] = ch->padding[1] = 0;
-       crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
-       ch->crc = cpu_to_le32(crc);
+       ubifs_set_datanode_crc(node);

         if (pad) {
                 len = ALIGN(len, 8);
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/ubifs.h linux-3.2.1-ubifsec/fs/ubifs/ubifs.h
--- linux-3.2.1-vanilla/fs/ubifs/ubifs.h        2012-01-12 
20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/ubifs.h        2012-02-20 
20:18:17.368685674 +0100
@@ -1489,6 +1489,7 @@ int ubifs_write_node(struct ubifs_info *
                      int offs, int dtype);
  int ubifs_check_node(const struct ubifs_info *c, const void *buf, int 
lnum,
                      int offs, int quiet, int must_chk_crc);
+void ubifs_set_datanode_crc(void *node);
  void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int 
pad);
  void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int 
last);
  int ubifs_io_init(struct ubifs_info *c);



^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-02-13 16:54 ` Artem Bityutskiy
@ 2012-02-23 14:59   ` Joel Reardon
  2012-02-23 15:29     ` [patch] Add encryption key parameter to compress/decompress functions Joel Reardon
                       ` (2 more replies)
  0 siblings, 3 replies; 55+ messages in thread
From: Joel Reardon @ 2012-02-23 14:59 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel

>
> Could you make a separate patch which adds new on-flash format and make
> sure that new ubifs binaries can mount the old formats and work with
> them. And that old ubifs binaries gracefully reject the new format.
>

This patch provides a new version of two on-flash data structures for 
UBIFS:
ubifs_branch and ubifs_data_node have been grown by 8 bytes to include a 
64-bit reference
to a key position, which is needed by a proposed ubifs secure deletion 
addition. This patch is generated
against linux-3.2.1.

Because both data structures use C99 flexible arrays, it is not possible 
to add the new field as
the last member of the structure. Thus, it was added one before last, the 
last was renamed, and in a
handful of places where it was used, a macro accessor is used to get the 
version-correct position.

The new/modified macros are the following:
FMT_VERSION_DATA_NODE_HAS_CRYPTO_LOOKUP(c)
FMT_VERSION_BRANCH_HAS_CRYPTO_LOOKUP(c)
These take the ubifs_context and return true if the version defined in 
fmt_version has a crypto_lookup field.

FMT_VERSION_BRANCH_POS_KEY(c, br)
FMT_VERSION_DATA_NODE_POS_DATA(c, dn)
These take the ubifs_context and the data_node / branch and return the 
address of the key / data field
that is correct for the version defined in fmt_version. key and data are 
renamed to __key / __data to
prevent any accidental version incapible usage.

UBIFS_DATA_NODE_SZ(c)
UBIFS_BRANCH_SZ(c)
These now take the ubifs_context and return the data structure's size 
according to the format version.

In create_default_filesystem, the fmt_version is set earlier so these 
macros behave correctly.


This was tested as follows:
1) A vanilla and modified module were both created. An empty drive was 
mounted under the vanilla
module to create a version 4 drive. Data was written to it
and integck was run (always a hundred times followed by powercut tests).
2) The module was switched to modified and it was mounted again. The data 
was read correctly and the
integck was run.
3) New data was written and it was mounted again under the vanilla 
modules. Both data was read again
and integck was run.
4) Finally, it was remounted under version 5 and the data correctly read 
and integck was run.

Additionally, an empty drive was mounted under the modified modules to 
create a version 5 drive.
1) Data was written to it and integck was run.
2) It was attempted to be mounted under the vanilla driver, but it did not 
succeed. Dmesg reports
version mismatch as the reason. A mounting attempted as read-only 
continued, but it failed when trying to read
the superblock.
3) It was remounted under the modified module where the data was correctly 
read back.


----------

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/commit.c 
linux-3.2.1-ubifsec/fs/ubifs/commit.c
--- linux-3.2.1-vanilla/fs/ubifs/commit.c	2012-01-12 
20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/commit.c	2012-02-23 
15:06:49.107957279 +0100
@@ -650,7 +650,7 @@ int dbg_check_old_index(struct ubifs_inf
  		/* Check key range */
  		key_read(c, ubifs_idx_key(c, idx), &l_key);
  		br = ubifs_idx_branch(c, idx, child_cnt - 1);
-		key_read(c, &br->key, &u_key);
+		key_read(c, FMT_VERSION_BRANCH_POS_KEY(c, br), &u_key);
  		if (keys_cmp(c, &lower_key, &l_key) > 0) {
  			err = 5;
  			goto out_dump;
@@ -699,10 +699,11 @@ int dbg_check_old_index(struct ubifs_inf
  		lnum = le32_to_cpu(br->lnum);
  		offs = le32_to_cpu(br->offs);
  		len = le32_to_cpu(br->len);
-		key_read(c, &br->key, &lower_key);
+		key_read(c, FMT_VERSION_BRANCH_POS_KEY(c, br), 
&lower_key);
  		if (iip + 1 < le16_to_cpu(idx->child_cnt)) {
  			br = ubifs_idx_branch(c, idx, iip + 1);
-			key_read(c, &br->key, &upper_key);
+			key_read(c, FMT_VERSION_BRANCH_POS_KEY(c, br),
+				 &upper_key);
  		} else
  			key_copy(c, &i->upper_key, &upper_key);
  	}
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/debug.c linux-3.2.1-ubifsec/fs/ubifs/debug.c
--- linux-3.2.1-vanilla/fs/ubifs/debug.c	2012-01-12 
20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/debug.c	2012-02-21 
22:27:01.222462655 +0100
@@ -538,7 +538,7 @@ void dbg_dump_node(const struct ubifs_in
  	case UBIFS_DATA_NODE:
  	{
  		const struct ubifs_data_node *dn = node;
-		int dlen = le32_to_cpu(ch->len) - UBIFS_DATA_NODE_SZ;
+		int dlen = le32_to_cpu(ch->len) - UBIFS_DATA_NODE_SZ(c);

  		key_read(c, &dn->key, &key);
  		printk(KERN_DEBUG "\tkey            %s\n", DBGKEY(&key));
@@ -550,7 +550,7 @@ void dbg_dump_node(const struct ubifs_in
  		       dlen);
  		printk(KERN_DEBUG "\tdata:\n");
  		print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_OFFSET, 32, 
1,
-			       (void *)&dn->data, dlen, 0);
+			       FMT_VERSION_DATA_NODE_POS_DATA(c, dn), 
dlen, 0);
  		break;
  	}
  	case UBIFS_TRUN_NODE:
@@ -579,7 +579,7 @@ void dbg_dump_node(const struct ubifs_in
  			const struct ubifs_branch *br;

  			br = ubifs_idx_branch(c, idx, i);
-			key_read(c, &br->key, &key);
+			key_read(c, FMT_VERSION_BRANCH_POS_KEY(c, br), 
&key);
  			printk(KERN_DEBUG "\t%d: LEB %d:%d len %d key 
%s\n",
  			       i, le32_to_cpu(br->lnum), 
le32_to_cpu(br->offs),
  			       le32_to_cpu(br->len), DBGKEY(&key));
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/file.c linux-3.2.1-ubifsec/fs/ubifs/file.c
--- linux-3.2.1-vanilla/fs/ubifs/file.c	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/file.c	2012-02-23 15:07:01.695957886 
+0100
@@ -77,10 +77,12 @@ static int read_block(struct inode *inod
  	if (len <= 0 || len > UBIFS_BLOCK_SIZE)
  		goto dump;

-	dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
+	dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ(c);
  	out_len = UBIFS_BLOCK_SIZE;
-	err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
-			       le16_to_cpu(dn->compr_type));
+	err = ubifs_decompress(FMT_VERSION_DATA_NODE_POS_DATA(c, dn), 
dlen,
+			       addr, &out_len, 
le16_to_cpu(dn->compr_type));
+	if (FMT_VERSION_DATA_NODE_HAS_CRYPTO_LOOKUP(c))
+		ubifs_assert(le64_to_cpu(dn->crypto_lookup) == 0);
  	if (err || len != out_len)
  		goto dump;

@@ -646,10 +648,13 @@ static int populate_page(struct ubifs_in
  			if (len <= 0 || len > UBIFS_BLOCK_SIZE)
  				goto out_err;

-			dlen = le32_to_cpu(dn->ch.len) - 
UBIFS_DATA_NODE_SZ;
+			dlen = le32_to_cpu(dn->ch.len) - 
UBIFS_DATA_NODE_SZ(c);
  			out_len = UBIFS_BLOCK_SIZE;
-			err = ubifs_decompress(&dn->data, dlen, addr, 
&out_len,
- 
le16_to_cpu(dn->compr_type));
+			err =
+			ubifs_decompress(
+				FMT_VERSION_DATA_NODE_POS_DATA(c, dn),
+				dlen, addr, &out_len,
+				le16_to_cpu(dn->compr_type));
  			if (err || len != out_len)
  				goto out_err;

diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/journal.c 
linux-3.2.1-ubifsec/fs/ubifs/journal.c
--- linux-3.2.1-vanilla/fs/ubifs/journal.c	2012-01-12 
20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/journal.c	2012-02-23 
15:07:31.835959345 +0100
@@ -720,6 +720,8 @@ int ubifs_jnl_write_data(struct ubifs_in
  	key_write(c, key, &data->key);
  	data->size = cpu_to_le32(len);
  	zero_data_node_unused(data);
+	if (FMT_VERSION_DATA_NODE_HAS_CRYPTO_LOOKUP(c))
+		data->crypto_lookup = cpu_to_le64(0);

  	if (!(ui->flags & UBIFS_COMPR_FL))
  		/* Compression is disabled for this inode */
@@ -727,11 +729,12 @@ int ubifs_jnl_write_data(struct ubifs_in
  	else
  		compr_type = ui->compr_type;

-	out_len = dlen - UBIFS_DATA_NODE_SZ;
-	ubifs_compress(buf, len, &data->data, &out_len, &compr_type);
+	out_len = dlen - UBIFS_DATA_NODE_SZ(c);
+	ubifs_compress(buf, len, FMT_VERSION_DATA_NODE_POS_DATA(c, data),
+		       &out_len, &compr_type);
  	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);

-	dlen = UBIFS_DATA_NODE_SZ + out_len;
+	dlen = UBIFS_DATA_NODE_SZ(c) + out_len;
  	data->compr_type = cpu_to_le16(compr_type);

  	/* Make reservation before allocating sequence numbers */
@@ -1093,13 +1096,16 @@ out_free:

  /**
   * recomp_data_node - re-compress a truncated data node.
+ * @c: UBIFS file-system description object
   * @dn: data node to re-compress
   * @new_len: new length
   *
   * This function is used when an inode is truncated and the last data 
node of
   * the inode has to be re-compressed and re-written.
   */
-static int recomp_data_node(struct ubifs_data_node *dn, int *new_len)
+static int recomp_data_node(struct ubifs_info *c,
+			    struct ubifs_data_node *dn,
+			    int *new_len)
  {
  	void *buf;
  	int err, len, compr_type, out_len;
@@ -1109,17 +1115,20 @@ static int recomp_data_node(struct ubifs
  	if (!buf)
  		return -ENOMEM;

-	len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
+	len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ(c);
  	compr_type = le16_to_cpu(dn->compr_type);
-	err = ubifs_decompress(&dn->data, len, buf, &out_len, compr_type);
+	err = ubifs_decompress(FMT_VERSION_DATA_NODE_POS_DATA(c, dn),
+			       len, buf, &out_len, compr_type);
  	if (err)
  		goto out;

-	ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type);
+	ubifs_compress(buf, *new_len, FMT_VERSION_DATA_NODE_POS_DATA(c, 
dn),
+		       &out_len, &compr_type);
+
  	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);
  	dn->compr_type = cpu_to_le16(compr_type);
  	dn->size = cpu_to_le32(*new_len);
-	*new_len = UBIFS_DATA_NODE_SZ + out_len;
+	*new_len = UBIFS_DATA_NODE_SZ(c) + out_len;
  out:
  	kfree(buf);
  	return err;
@@ -1190,12 +1199,12 @@ int ubifs_jnl_truncate(struct ubifs_info
  				int compr_type = 
le16_to_cpu(dn->compr_type);

  				if (compr_type != UBIFS_COMPR_NONE) {
-					err = recomp_data_node(dn, &dlen);
+					err = recomp_data_node(c, dn, 
&dlen);
  					if (err)
  						goto out_free;
  				} else {
  					dn->size = cpu_to_le32(dlen);
-					dlen += UBIFS_DATA_NODE_SZ;
+					dlen += UBIFS_DATA_NODE_SZ(c);
  				}
  				zero_data_node_unused(dn);
  			}
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/misc.h linux-3.2.1-ubifsec/fs/ubifs/misc.h
--- linux-3.2.1-vanilla/fs/ubifs/misc.h	2012-02-20 19:36:00.670753551 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/misc.h	2012-02-23 15:09:47.899965933 
+0100
@@ -200,7 +200,8 @@ static inline int ubifs_return_leb(struc
   */
  static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int 
child_cnt)
  {
-	return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * 
child_cnt;
+	return UBIFS_IDX_NODE_SZ
+	       + (UBIFS_BRANCH_SZ(c) + c->key_len) * child_cnt;
  }

  /**
@@ -214,8 +215,9 @@ struct ubifs_branch *ubifs_idx_branch(co
  				      const struct ubifs_idx_node *idx,
  				      int bnum)
  {
-	return (struct ubifs_branch *)((void *)idx->branches +
-				       (UBIFS_BRANCH_SZ + c->key_len) * 
bnum);
+	return (struct ubifs_branch *)
+		((void *)idx->branches + (UBIFS_BRANCH_SZ(c) + c->key_len)
+		* bnum);
  }

  /**
@@ -226,7 +228,8 @@ struct ubifs_branch *ubifs_idx_branch(co
  static inline void *ubifs_idx_key(const struct ubifs_info *c,
  				  const struct ubifs_idx_node *idx)
  {
-	return (void *)((struct ubifs_branch *)idx->branches)->key;
+	return (void *) FMT_VERSION_BRANCH_POS_KEY(
+		c, (struct ubifs_branch *)idx->branches);
  }

  /**
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/sb.c linux-3.2.1-ubifsec/fs/ubifs/sb.c
--- linux-3.2.1-vanilla/fs/ubifs/sb.c	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/sb.c	2012-02-22 11:39:51.480718000 
+0100
@@ -84,7 +84,7 @@ static int create_default_filesystem(str
  	int min_leb_cnt = UBIFS_MIN_LEB_CNT;
  	long long tmp64, main_bytes;
  	__le64 tmp_le64;
-
+	c->fmt_version = UBIFS_FORMAT_VERSION;
  	/* Some functions called from here depend on the @c->key_len filed 
*/
  	c->key_len = UBIFS_SK_LEN;

@@ -175,6 +175,7 @@ static int create_default_filesystem(str
  	sup->max_leb_cnt   = cpu_to_le32(c->max_leb_cnt);
  	sup->max_bud_bytes = cpu_to_le64(tmp64);
  	sup->log_lebs      = cpu_to_le32(log_lebs);
+	sup->crypto_lebs   = cpu_to_le32(0);
  	sup->lpt_lebs      = cpu_to_le32(lpt_lebs);
  	sup->orph_lebs     = cpu_to_le32(orph_lebs);
  	sup->jhead_cnt     = cpu_to_le32(DEFAULT_JHEADS_CNT);
@@ -182,6 +183,7 @@ static int create_default_filesystem(str
  	sup->lsave_cnt     = cpu_to_le32(c->lsave_cnt);
  	sup->fmt_version   = cpu_to_le32(UBIFS_FORMAT_VERSION);
  	sup->time_gran     = cpu_to_le32(DEFAULT_TIME_GRAN);
+	sup->use_ubifsec   = cpu_to_le32(0);
  	if (c->mount_opts.override_compr)
  		sup->default_compr = 
cpu_to_le16(c->mount_opts.compr_type);
  	else
@@ -279,7 +281,7 @@ static int create_default_filesystem(str
  	idx->child_cnt = cpu_to_le16(1);
  	ino_key_init(c, &key, UBIFS_ROOT_INO);
  	br = ubifs_idx_branch(c, idx, 0);
-	key_write_idx(c, &key, &br->key);
+	key_write_idx(c, &key, FMT_VERSION_BRANCH_POS_KEY(c, br));
  	br->lnum = cpu_to_le32(main_first + DEFAULT_DATA_LEB);
  	br->len  = cpu_to_le32(UBIFS_INO_NODE_SZ);
  	err = ubifs_write_node(c, idx, tmp, main_first + DEFAULT_IDX_LEB, 
0,
@@ -610,6 +612,10 @@ int ubifs_read_superblock(struct ubifs_i
  	c->rp_size       = le64_to_cpu(sup->rp_size);
  	c->rp_uid        = le32_to_cpu(sup->rp_uid);
  	c->rp_gid        = le32_to_cpu(sup->rp_gid);
+	if (c->fmt_version > 4) {
+		ubifs_assert(le32_to_cpu(sup->crypto_lebs) == 0);
+		ubifs_assert(le32_to_cpu(sup->use_ubifsec) == 0);
+	}
  	sup_flags        = le32_to_cpu(sup->flags);
  	if (!c->mount_opts.override_compr)
  		c->default_compr = le16_to_cpu(sup->default_compr);
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/super.c linux-3.2.1-ubifsec/fs/ubifs/super.c
--- linux-3.2.1-vanilla/fs/ubifs/super.c	2012-02-20 
19:36:03.478753687 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/super.c	2012-02-21 
22:20:04.478442495 +0100
@@ -585,13 +585,14 @@ static int init_constants_early(struct u
  	c->ranges[UBIFS_DENT_NODE].max_len = UBIFS_MAX_DENT_NODE_SZ;
  	c->ranges[UBIFS_XENT_NODE].min_len = UBIFS_XENT_NODE_SZ;
  	c->ranges[UBIFS_XENT_NODE].max_len = UBIFS_MAX_XENT_NODE_SZ;
-	c->ranges[UBIFS_DATA_NODE].min_len = UBIFS_DATA_NODE_SZ;
+	c->ranges[UBIFS_DATA_NODE].min_len = UBIFS_DATA_NODE_VMIN_SZ;
  	c->ranges[UBIFS_DATA_NODE].max_len = UBIFS_MAX_DATA_NODE_SZ;
  	/*
  	 * Minimum indexing node size is amended later when superblock is
  	 * read and the key length is known.
  	 */
-	c->ranges[UBIFS_IDX_NODE].min_len = UBIFS_IDX_NODE_SZ + 
UBIFS_BRANCH_SZ;
+	c->ranges[UBIFS_IDX_NODE].min_len = UBIFS_IDX_NODE_SZ +
+					    UBIFS_BRANCH_SZ(c);
  	/*
  	 * Maximum indexing node size is amended later when superblock is
  	 * read and the fanout is known.
@@ -1463,7 +1464,7 @@ static int mount_ubifs(struct ubifs_info
  	dbg_msg("max. znode size      %d", c->max_znode_sz);
  	dbg_msg("max. index node size %d", c->max_idx_node_sz);
  	dbg_msg("node sizes:          data %zu, inode %zu, dentry %zu",
-		UBIFS_DATA_NODE_SZ, UBIFS_INO_NODE_SZ, 
UBIFS_DENT_NODE_SZ);
+		UBIFS_DATA_NODE_SZ(c), UBIFS_INO_NODE_SZ, 
UBIFS_DENT_NODE_SZ);
  	dbg_msg("node sizes:          trun %zu, sb %zu, master %zu",
  		UBIFS_TRUN_NODE_SZ, UBIFS_SB_NODE_SZ, UBIFS_MST_NODE_SZ);
  	dbg_msg("node sizes:          ref %zu, cmt. start %zu, orph %zu",
@@ -2213,7 +2214,8 @@ static int __init ubifs_init(void)
  	BUILD_BUG_ON(UBIFS_INO_NODE_SZ  & 7);
  	BUILD_BUG_ON(UBIFS_DENT_NODE_SZ & 7);
  	BUILD_BUG_ON(UBIFS_XENT_NODE_SZ & 7);
-	BUILD_BUG_ON(UBIFS_DATA_NODE_SZ & 7);
+	BUILD_BUG_ON(UBIFS_DATA_NODE_V4_SZ & 7);
+	BUILD_BUG_ON(UBIFS_DATA_NODE_V5_SZ & 7);
  	BUILD_BUG_ON(UBIFS_TRUN_NODE_SZ & 7);
  	BUILD_BUG_ON(UBIFS_SB_NODE_SZ   & 7);
  	BUILD_BUG_ON(UBIFS_MST_NODE_SZ  & 7);
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/tnc_commit.c 
linux-3.2.1-ubifsec/fs/ubifs/tnc_commit.c
--- linux-3.2.1-vanilla/fs/ubifs/tnc_commit.c	2012-01-12 
20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/tnc_commit.c	2012-02-23 
15:09:27.295964938 +0100
@@ -48,9 +48,13 @@ static int make_idx_node(struct ubifs_in
  		struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
  		struct ubifs_zbranch *zbr = &znode->zbranch[i];

-		key_write_idx(c, &zbr->key, &br->key);
+		key_write_idx(c, &zbr->key, FMT_VERSION_BRANCH_POS_KEY(c, 
br));
  		br->lnum = cpu_to_le32(zbr->lnum);
  		br->offs = cpu_to_le32(zbr->offs);
+		if (key_type(c, &zbr->key) == UBIFS_DATA_KEY) {
+			if (FMT_VERSION_BRANCH_HAS_CRYPTO_LOOKUP(c))
+				br->crypto_lookup = cpu_to_le64(0);
+		}
  		br->len = cpu_to_le32(zbr->len);
  		if (!zbr->lnum || !zbr->len) {
  			ubifs_err("bad ref in znode");
@@ -858,7 +862,12 @@ static int write_index(struct ubifs_info
  			struct ubifs_branch *br = ubifs_idx_branch(c, idx, 
i);
  			struct ubifs_zbranch *zbr = &znode->zbranch[i];

-			key_write_idx(c, &zbr->key, &br->key);
+			key_write_idx(c, &zbr->key,
+				      FMT_VERSION_BRANCH_POS_KEY(c, br));
+			if (key_type(c, &zbr->key) == UBIFS_DATA_KEY) {
+				if 
(FMT_VERSION_BRANCH_HAS_CRYPTO_LOOKUP(c))
+					br->crypto_lookup = 
cpu_to_le64(0);
+			}
  			br->lnum = cpu_to_le32(zbr->lnum);
  			br->offs = cpu_to_le32(zbr->offs);
  			br->len = cpu_to_le32(zbr->len);
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/tnc_misc.c 
linux-3.2.1-ubifsec/fs/ubifs/tnc_misc.c
--- linux-3.2.1-vanilla/fs/ubifs/tnc_misc.c	2012-01-12 
20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/tnc_misc.c	2012-02-21 
13:13:01.775280827 +0100
@@ -305,7 +305,7 @@ static int read_znode(struct ubifs_info
  		const struct ubifs_branch *br = ubifs_idx_branch(c, idx, 
i);
  		struct ubifs_zbranch *zbr = &znode->zbranch[i];

-		key_read(c, &br->key, &zbr->key);
+		key_read(c, FMT_VERSION_BRANCH_POS_KEY(c, br), &zbr->key);
  		zbr->lnum = le32_to_cpu(br->lnum);
  		zbr->offs = le32_to_cpu(br->offs);
  		zbr->len  = le32_to_cpu(br->len);
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/ubifs.h linux-3.2.1-ubifsec/fs/ubifs/ubifs.h
--- linux-3.2.1-vanilla/fs/ubifs/ubifs.h	2012-01-12 
20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/ubifs.h	2012-02-21 
22:07:55.582407209 +0100
@@ -73,7 +73,7 @@
  #define MIN_INDEX_LEBS 2

  /* Minimum amount of data UBIFS writes to the flash */
-#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8)
+#define MIN_WRITE_SZ (UBIFS_DATA_NODE_VMIN_SZ + 8)

  /*
   * Currently we do not support inode number overlapping and re-using, so 
this
@@ -155,7 +155,7 @@
   * How much memory is needed for a buffer where we comress a data node.
   */
  #define COMPRESSED_DATA_NODE_BUF_SZ \
-	(UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR)
+	(UBIFS_DATA_NODE_VMAX_SZ + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR)

  /* Maximum expected tree height for use by bottom_up_buf */
  #define BOTTOM_UP_HEIGHT 64
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/ubifs-media.h 
linux-3.2.1-ubifsec/fs/ubifs/ubifs-media.h
--- linux-3.2.1-vanilla/fs/ubifs/ubifs-media.h	2012-01-12 
20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/ubifs-media.h	2012-02-23 
15:12:45.971974550 +0100
@@ -44,9 +44,23 @@
   * a new feature.
   *
   * UBIFS went into mainline kernel with format version 4. The older 
formats
- * were development formats.
- */
-#define UBIFS_FORMAT_VERSION 4
+ * were development formats.  Version 5 is then used for secure deletion
+ * enhancement. N.B.: when adding new versions that also have this field,
+ * include the version in the data_struct_has_field defines below.
+ */
+#define UBIFS_FORMAT_VERSION 5
+#define FMT_VERSION_BRANCH_HAS_CRYPTO_LOOKUP(c) ((c)->fmt_version == 5)
+#define FMT_VERSION_DATA_NODE_HAS_CRYPTO_LOOKUP(c) ((c)->fmt_version == 
5)
+#define FMT_VERSION_DATA_NODE_POS_DATA(c, dn)				\
+	(FMT_VERSION_DATA_NODE_HAS_CRYPTO_LOOKUP(c) ?			\
+	((void *) (&((dn)->__data))) :					\
+	((void *) (&((dn)->crypto_lookup))))
+
+#define FMT_VERSION_BRANCH_POS_KEY(c, br)				\
+	(FMT_VERSION_BRANCH_HAS_CRYPTO_LOOKUP(c) ?			\
+	((void *) (&((br)->__key))) :					\
+	((void *) (&((br)->crypto_lookup))))
+

  /*
   * Read-only compatibility version. If the UBIFS format is changed, older 
UBIFS
@@ -274,25 +288,33 @@ enum {
  			   UBIFS_MIN_ORPH_LEBS + UBIFS_MIN_MAIN_LEBS)

  /* Node sizes (N.B. these are guaranteed to be multiples of 8) */
-#define UBIFS_CH_SZ        sizeof(struct ubifs_ch)
-#define UBIFS_INO_NODE_SZ  sizeof(struct ubifs_ino_node)
-#define UBIFS_DATA_NODE_SZ sizeof(struct ubifs_data_node)
-#define UBIFS_DENT_NODE_SZ sizeof(struct ubifs_dent_node)
-#define UBIFS_TRUN_NODE_SZ sizeof(struct ubifs_trun_node)
-#define UBIFS_PAD_NODE_SZ  sizeof(struct ubifs_pad_node)
-#define UBIFS_SB_NODE_SZ   sizeof(struct ubifs_sb_node)
-#define UBIFS_MST_NODE_SZ  sizeof(struct ubifs_mst_node)
-#define UBIFS_REF_NODE_SZ  sizeof(struct ubifs_ref_node)
-#define UBIFS_IDX_NODE_SZ  sizeof(struct ubifs_idx_node)
-#define UBIFS_CS_NODE_SZ   sizeof(struct ubifs_cs_node)
-#define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node)
+#define UBIFS_CH_SZ            sizeof(struct ubifs_ch)
+#define UBIFS_INO_NODE_SZ      sizeof(struct ubifs_ino_node)
+#define UBIFS_DATA_NODE_SZ(c)   (sizeof(struct ubifs_data_node) \
+	- (FMT_VERSION_BRANCH_HAS_CRYPTO_LOOKUP(c) ? 0 : sizeof(__u64)))
+/* The min and max data node header size across all version */
+#define UBIFS_DATA_NODE_V4_SZ (sizeof(struct ubifs_data_node) - 
sizeof(__u64))
+#define UBIFS_DATA_NODE_V5_SZ (sizeof(struct ubifs_data_node))
+#define UBIFS_DATA_NODE_VMIN_SZ UBIFS_DATA_NODE_V4_SZ
+#define UBIFS_DATA_NODE_VMAX_SZ UBIFS_DATA_NODE_V5_SZ
+
+#define UBIFS_DENT_NODE_SZ     sizeof(struct ubifs_dent_node)
+#define UBIFS_TRUN_NODE_SZ     sizeof(struct ubifs_trun_node)
+#define UBIFS_PAD_NODE_SZ      sizeof(struct ubifs_pad_node)
+#define UBIFS_SB_NODE_SZ       sizeof(struct ubifs_sb_node)
+#define UBIFS_MST_NODE_SZ      sizeof(struct ubifs_mst_node)
+#define UBIFS_REF_NODE_SZ      sizeof(struct ubifs_ref_node)
+#define UBIFS_IDX_NODE_SZ      sizeof(struct ubifs_idx_node)
+#define UBIFS_CS_NODE_SZ       sizeof(struct ubifs_cs_node)
+#define UBIFS_ORPH_NODE_SZ     sizeof(struct ubifs_orph_node)
  /* Extended attribute entry nodes are identical to directory entry nodes 
*/
  #define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ
  /* Only this does not have to be multiple of 8 bytes */
-#define UBIFS_BRANCH_SZ    sizeof(struct ubifs_branch)
+#define UBIFS_BRANCH_SZ(c)    (sizeof(struct ubifs_branch)  \
+	- (FMT_VERSION_BRANCH_HAS_CRYPTO_LOOKUP(c) ? 0 : sizeof(__u64)))

  /* Maximum node sizes (N.B. these are guaranteed to be multiples of 8) */
-#define UBIFS_MAX_DATA_NODE_SZ  (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE)
+#define UBIFS_MAX_DATA_NODE_SZ  (UBIFS_DATA_NODE_VMAX_SZ + 
UBIFS_BLOCK_SIZE)
  #define UBIFS_MAX_INO_NODE_SZ   (UBIFS_INO_NODE_SZ + UBIFS_MAX_INO_DATA)
  #define UBIFS_MAX_DENT_NODE_SZ  (UBIFS_DENT_NODE_SZ + UBIFS_MAX_NLEN + 1)
  #define UBIFS_MAX_XENT_NODE_SZ  UBIFS_MAX_DENT_NODE_SZ
@@ -549,6 +571,18 @@ struct ubifs_dent_node {
   *
   * Note, do not forget to amend 'zero_data_node_unused()' function when
   * changing the padding fields.
+ *
+ * This data structure format is version 5, which includes the 
@crypto_lookup
+ * field. Since @data is unbounded, the new field must be before it.
+ * Compatibility macros:
+ * FMT_VERSION_DATA_NODE_POS_DATA: returns a pointer to the appropriate 
place
+ *				   where the key begins for the current
+ *				   mounted version.
+ * FMT_VERSION_DATA_NODE_HAS_CRYPTO_LOOKUP: true if the current mounted
+ *					    version contains the
+ *					    @crypto_lookup field.
+ * UBIFS_DATA_NODE_SZ: now returns the appropriate size for this data 
structure
+ *		       depending on the version.
   */
  struct ubifs_data_node {
  	struct ubifs_ch ch;
@@ -556,7 +590,8 @@ struct ubifs_data_node {
  	__le32 size;
  	__le16 compr_type;
  	__u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! 
*/
-	__u8 data[];
+	__u64 crypto_lookup;
+	__u8 __data[];
  } __packed;

  /**
@@ -614,10 +649,12 @@ struct ubifs_pad_node {
   * @rp_uid: reserve pool UID
   * @rp_gid: reserve pool GID
   * @rp_size: size of the reserved pool in bytes
- * @padding2: reserved for future, zeroes
   * @time_gran: time granularity in nanoseconds
   * @uuid: UUID generated when the file system image was created
   * @ro_compat_version: UBIFS R/O compatibility version
+ * @crypto_lebs: number of LEBS being used to store keys
+ * @use_ubifsec: whether the file system should use ubifsec secure 
deletion
+ * @padding2: reserved for future, zeroes
   */
  struct ubifs_sb_node {
  	struct ubifs_ch ch;
@@ -645,7 +682,9 @@ struct ubifs_sb_node {
  	__le32 time_gran;
  	__u8 uuid[16];
  	__le32 ro_compat_version;
-	__u8 padding2[3968];
+	__le32 crypto_lebs;
+	__u8 use_ubifsec;
+	__u8 padding2[3963];
  } __packed;

  /**
@@ -736,13 +775,25 @@ struct ubifs_ref_node {
   * @lnum: LEB number of the target node
   * @offs: offset within @lnum
   * @len: target node length
- * @key: key
+ * @__key: key.
+ *
+ * This data structure format is version 5, which includes the 
crypto_lookup
+ * field. Since key is unbounded, the new field must be before it.
+ * Compatibility macros:
+ * FMT_VERSION_BRANCH_POS_KEY: returns a pointer to the appropriate place
+ *			       where the key begins for the current 
mounted
+ *			       version.
+ * FMT_VERSION_BRANCH_HAS_CRYPTO_LOOKUP: true if the current mounted 
version
+ *					 contains the crypto_lookup field.
+ * UBIFS_BRANCH_SZ: now returns the appropriate size for this data 
structure
+ *		    depending on the version.
   */
  struct ubifs_branch {
  	__le32 lnum;
  	__le32 offs;
  	__le32 len;
-	__u8 key[];
+	__le64 crypto_lookup;
+	__u8 __key[];
  } __packed;

  /**
@@ -750,7 +801,7 @@ struct ubifs_branch {
   * @ch: common header
   * @child_cnt: number of child index nodes
   * @level: tree level
- * @branches: LEB number / offset / length / key branches
+ * @branches: LEB number / offset / length / crypto_lookup / key branches
   */
  struct ubifs_idx_node {
  	struct ubifs_ch ch;


^ permalink raw reply	[flat|nested] 55+ messages in thread

* [patch] Add encryption key parameter to compress/decompress functions
  2012-02-23 14:59   ` Joel Reardon
@ 2012-02-23 15:29     ` Joel Reardon
  2012-03-09  7:17       ` Artem Bityutskiy
  2012-02-29 17:09     ` [patch] Adding Secure Deletion to UBIFS Artem Bityutskiy
  2012-03-15 14:48     ` [patch] Remove notion of key schemes Joel Reardon
  2 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-02-23 15:29 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel

This patch extends to interface for compression and decompression of data 
nodes to take a cryptographic key
as a parameter. If it is set to NULL, then the current behaviour persists. 
If it is non-null, then it is taken
as a pointer to an array of length UBIFSEC_KEYSIZE (=16 bytes) which 
contains the key to use to encrypt
the data node. Each data node should be encrypted with a separate key as 
it uses the same initialization vector  (of 0).

In all places where the compress/decompress are called, a NULL parameter 
is used, so it will have no effect on
deployed systems. It will be needed when adding secure deletion to UBIFS 
using individually encrypted data nodes.

It was tested by with hundred runs of integck along with power cut tests.

------

Signed-off-by: Joel Reardon<reardonj@inf.ethz.ch>
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/compress.c 
linux-3.2.1-ubifsec/fs/ubifs/compress.c
--- linux-3.2.1-vanilla/fs/ubifs/compress.c	2012-01-12 
20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/compress.c	2012-02-23 
16:07:44.287023169 +0100
@@ -27,9 +27,11 @@
   * decompression.
   */

-#include <linux/crypto.h>
  #include "ubifs.h"

+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+
  /* Fake description object for the "none" compressor */
  static struct ubifs_compressor none_compr = {
  	.compr_type = UBIFS_COMPR_NONE,
@@ -74,6 +76,40 @@ static struct ubifs_compressor zlib_comp
  /* All UBIFS compressors */
  struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];

+int ubifs_aes_crypt(u8 *str, int len, u8 *key, int keylen, u8 *iv, int 
ivlen)
+{
+	struct crypto_blkcipher *tfm;
+	struct blkcipher_desc desc;
+	struct scatterlist sg;
+	int err = 0;
+	tfm = crypto_alloc_blkcipher(UBIFSEC_CRYPTO_ALGORITHM, 0, 0);
+
+	if (IS_ERR(tfm)) {
+		ubifs_err("failed to load transform for aes: %ld",
+			  PTR_ERR(tfm));
+		return err;
+	}
+
+	err = crypto_blkcipher_setkey(tfm, key, keylen);
+	desc.tfm = tfm;
+	desc.flags = 0;
+	if (err) {
+		ubifs_err("setkey() failed  flags=%x",
+			  crypto_blkcipher_get_flags(tfm));
+		return err;
+	}
+	memset(&sg, 0, sizeof(struct scatterlist));
+
+	sg_set_buf(&sg, str, len);
+	desc.info = iv;
+	err = crypto_blkcipher_encrypt(&desc, &sg, &sg, len);
+	crypto_free_blkcipher(tfm);
+	if (err)
+		return err;
+	return 0;
+}
+
+
  /**
   * ubifs_compress - compress data.
   * @in_buf: data to compress
@@ -93,7 +129,7 @@ struct ubifs_compressor *ubifs_compresso
   * buffer and %UBIFS_COMPR_NONE is returned in @compr_type.
   */
  void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int 
*out_len,
-		    int *compr_type)
+		    int *compr_type, u8* key)
  {
  	int err;
  	struct ubifs_compressor *compr = ubifs_compressors[*compr_type];
@@ -115,7 +151,7 @@ void ubifs_compress(const void *in_buf,
  		ubifs_warn("cannot compress %d bytes, compressor %s, "
  			   "error %d, leave data uncompressed",
  			   in_len, compr->name, err);
-		 goto no_compr;
+		goto no_compr;
  	}

  	/*
@@ -124,13 +160,20 @@ void ubifs_compress(const void *in_buf,
  	 */
  	if (in_len - *out_len < UBIFS_MIN_COMPRESS_DIFF)
  		goto no_compr;
-
-	return;
+	goto encrypt;

  no_compr:
  	memcpy(out_buf, in_buf, in_len);
  	*out_len = in_len;
  	*compr_type = UBIFS_COMPR_NONE;
+	goto encrypt;
+encrypt:
+	if (key) {
+		u8 iv[AES_KEYSIZE_128];
+		memset(iv, 0, AES_KEYSIZE_128);
+		ubifs_aes_crypt(out_buf, *out_len, key, AES_KEYSIZE_128,
+				iv, AES_KEYSIZE_128);
+	}
  }

  /**
@@ -145,8 +188,9 @@ no_compr:
   * The length of the uncompressed data is returned in @out_len. This 
functions
   * returns %0 on success or a negative error code on failure.
   */
-int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
-		     int *out_len, int compr_type)
+int ubifs_decompress(void *in_buf, int in_len, void *out_buf,
+		     int *out_len, int compr_type, u8* key)
+
  {
  	int err;
  	struct ubifs_compressor *compr;
@@ -163,6 +207,12 @@ int ubifs_decompress(const void *in_buf,
  		return -EINVAL;
  	}

+	if (key) {
+		u8 iv[AES_KEYSIZE_128];
+		memset(iv, 0, AES_KEYSIZE_128);
+		ubifs_aes_crypt(in_buf, in_len, key, AES_KEYSIZE_128,
+				iv, AES_KEYSIZE_128);
+	}
  	if (compr_type == UBIFS_COMPR_NONE) {
  		memcpy(out_buf, in_buf, in_len);
  		*out_len = in_len;
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/file.c linux-3.2.1-ubifsec/fs/ubifs/file.c
--- linux-3.2.1-vanilla/fs/ubifs/file.c	2012-01-12 20:42:45.000000000 
+0100
+++ linux-3.2.1-ubifsec/fs/ubifs/file.c	2012-02-23 16:15:48.607046613 
+0100
@@ -80,7 +80,7 @@ static int read_block(struct inode *inod
  	dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
  	out_len = UBIFS_BLOCK_SIZE;
  	err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
-			       le16_to_cpu(dn->compr_type));
+			       le16_to_cpu(dn->compr_type), NULL);
  	if (err || len != out_len)
  		goto dump;

@@ -649,7 +649,8 @@ static int populate_page(struct ubifs_in
  			dlen = le32_to_cpu(dn->ch.len) - 
UBIFS_DATA_NODE_SZ;
  			out_len = UBIFS_BLOCK_SIZE;
  			err = ubifs_decompress(&dn->data, dlen, addr, 
&out_len,
- 
le16_to_cpu(dn->compr_type));
+ 
le16_to_cpu(dn->compr_type),
+					       NULL);
  			if (err || len != out_len)
  				goto out_err;

diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/journal.c 
linux-3.2.1-ubifsec/fs/ubifs/journal.c
--- linux-3.2.1-vanilla/fs/ubifs/journal.c	2012-01-12 
20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/journal.c	2012-02-23 
16:15:05.415044519 +0100
@@ -728,7 +728,7 @@ int ubifs_jnl_write_data(struct ubifs_in
  		compr_type = ui->compr_type;

  	out_len = dlen - UBIFS_DATA_NODE_SZ;
-	ubifs_compress(buf, len, &data->data, &out_len, &compr_type);
+	ubifs_compress(buf, len, &data->data, &out_len, &compr_type, 
NULL);
  	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);

  	dlen = UBIFS_DATA_NODE_SZ + out_len;
@@ -1111,11 +1111,12 @@ static int recomp_data_node(struct ubifs

  	len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
  	compr_type = le16_to_cpu(dn->compr_type);
-	err = ubifs_decompress(&dn->data, len, buf, &out_len, compr_type);
+	err = ubifs_decompress(
+		&dn->data, len, buf, &out_len, compr_type, NULL);
  	if (err)
  		goto out;

-	ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type);
+	ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type, 
NULL);
  	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);
  	dn->compr_type = cpu_to_le16(compr_type);
  	dn->size = cpu_to_le32(*new_len);
diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
linux-3.2.1-vanilla/fs/ubifs/ubifs.h linux-3.2.1-ubifsec/fs/ubifs/ubifs.h
--- linux-3.2.1-vanilla/fs/ubifs/ubifs.h	2012-01-12 
20:42:45.000000000 +0100
+++ linux-3.2.1-ubifsec/fs/ubifs/ubifs.h	2012-02-23 
16:09:03.071026982 +0100
@@ -163,6 +163,14 @@
  /* Maximum number of data nodes to bulk-read */
  #define UBIFS_MAX_BULK_READ 32

+/* Size of 128 bits in bytes */
+#define AES_KEYSIZE_128 16
+
+/* Key size in bytes for UBIFSEC */
+#define UBIFSEC_KEYSIZE AES_KEYSIZE_128
+#define UBIFSEC_CRYPTO_ALGORITHM "ctr(aes)"
+#define POISON_KEY(p) memset(p, 0xff, UBIFSEC_KEYSIZE)
+
  /*
   * Lockdep classes for UBIFS inode @ui_mutex.
   */
@@ -1775,9 +1783,10 @@ long ubifs_compat_ioctl(struct file *fil
  int __init ubifs_compressors_init(void);
  void ubifs_compressors_exit(void);
  void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int 
*out_len,
-		    int *compr_type);
-int ubifs_decompress(const void *buf, int len, void *out, int *out_len,
-		     int compr_type);
+		    int *compr_type, u8* key);
+int ubifs_decompress(void *buf, int len, void *out, int *out_len,
+		     int compr_type, u8 *key);
+int ubifs_aes_crypt(u8 *str, int len, u8 *key, int keylen, u8 *iv, int 
ivlen);

  #include "debug.h"
  #include "misc.h"



^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Move CRC computation to separate function
  2012-02-20 20:15 ` [patch] Move CRC computation to separate function Joel Reardon
@ 2012-02-29 16:10   ` Artem Bityutskiy
  2012-03-19 22:46     ` Joel Reardon
  0 siblings, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-02-29 16:10 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-kernel, linux-fsdevel

[-- Attachment #1: Type: text/plain, Size: 1635 bytes --]

On Mon, 2012-02-20 at 21:15 +0100, Joel Reardon wrote:
> This patch moves the computation of CRCs for data nodes from 
> within ubifs_prepare_node to a separate function ubifs_set_datanode_crc, 
> which takes a data node, and computes and sets the CRC. This is to avoid 
> duplication of the CRC computation code in other places where it may be 
> needed.
> 
> Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
> diff -uprN -X linux-3.2.1-vanilla/Documentation/dontdiff 
> linux-3.2.1-vanilla/fs/ubifs/io.c linux-3.2.1-ubifsec/fs/ubifs/io.c
> --- linux-3.2.1-vanilla/fs/ubifs/io.c   2012-01-12 20:42:45.000000000 
> +0100
> +++ linux-3.2.1-ubifsec/fs/ubifs/io.c   2012-02-20 20:17:48.796684293 
> +0100
> @@ -367,6 +367,18 @@ static unsigned long long next_sqnum(str
>   }

This patch is line-wrapped and cannot be applied. Would you please send
a non-wrapped version. You may experiment by sending to yourself, then
saving, and then applying using "git am".

> 
>   /**
> + * ubifs_set_datanode_crc - writes the crc for a data node to the common
> + * header.
> + * @node: the data node
> + */
> +void ubifs_set_datanode_crc(void *node)
> +{
> +       struct ubifs_ch *ch = (struct ubifs_ch *) node;
> +       int len = le32_to_cpu(ch->len);
> +       ch->crc = cpu_to_le32(crc32(UBIFS_CRC32_INIT, node + 8, len - 8));
> +}

This changes is not needed unless there is other code which needs this
function. So once I get a non-wrapped patch I can create a branch for
you in the UBIFS tree and collect your patches. Once they are ready,
they can be merged.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-02-23 14:59   ` Joel Reardon
  2012-02-23 15:29     ` [patch] Add encryption key parameter to compress/decompress functions Joel Reardon
@ 2012-02-29 17:09     ` Artem Bityutskiy
  2012-03-15 14:48     ` [patch] Remove notion of key schemes Joel Reardon
  2 siblings, 0 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-02-29 17:09 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1221 bytes --]

On Thu, 2012-02-23 at 15:59 +0100, Joel Reardon wrote:
> >
> > Could you make a separate patch which adds new on-flash format and make
> > sure that new ubifs binaries can mount the old formats and work with
> > them. And that old ubifs binaries gracefully reject the new format.
> >
> 
> This patch provides a new version of two on-flash data structures for 
> UBIFS:

Similarly - wrapped patch and git am wont' apply it, sorry.

> ubifs_branch and ubifs_data_node have been grown by 8 bytes to include a 
> 64-bit reference
> to a key position, which is needed by a proposed ubifs secure deletion 
> addition. This patch is generated
> against linux-3.2.1.

Please, use linux-ubifs tree instead:
git://git.infradead.org/linux-ubifs.git

It has the newest stuff.

> Because both data structures use C99 flexible arrays, it is not possible 
> to add the new field as
> the last member of the structure.

>  Thus, it was added one before last, the 
> last was renamed, and in a
> handful of places where it was used, a macro accessor is used to get the 
> version-correct position.

Hmm, we can make it to be key[UBIFS_SK_LEN] instead, if it helps.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-02-09 15:24 [patch] Adding Secure Deletion to UBIFS Joel Reardon
  2012-02-13 16:54 ` Artem Bityutskiy
  2012-02-20 20:15 ` [patch] Move CRC computation to separate function Joel Reardon
@ 2012-02-29 17:25 ` Artem Bityutskiy
  2012-03-01 13:41   ` Joel Reardon
  2 siblings, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-02-29 17:25 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 951 bytes --]

On Thu, 2012-02-09 at 16:24 +0100, Joel Reardon wrote:
> 
> Each data nodes includes a reference to a key in the KSA. This key is read and 
> used to decrypt the data. When a new data node is written, an unused key is 
> selected from the KSA and used to encrypt the data node. The reference to the 
> key is then included with the node. The keys in the KSA are written before 
> actually being used to encrypt data. To securely delete a data node, we simply 
> mark the corresponding key position as deleted, and during the next purging 
> operation the KSA erase block that contains the key is then updated to a 
> version that does not contain the key.

Why do you need to have your '__u64 crypto_lookup' both in the data node
and the index? Isn't it enough to have them only inside the data nodes?
ubifs_branch anyway points to the data node and you can read your
'crypto_lookup' from there.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-02-29 17:25 ` [patch] Adding Secure Deletion to UBIFS Artem Bityutskiy
@ 2012-03-01 13:41   ` Joel Reardon
  2012-03-09  7:36     ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-01 13:41 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel

This is true. The reason its done is as an optimization for the 
following reason.

When deleting a data node, the key position is marked as deleted. The key 
positions then requires a datanode read to find. By keeping it in memory (thus the TNC), 
marking a key pos as deleted doesn't require a flash read. This improves 
e.g., truncations and full file deletion speed, which would otherwise read 
each data node from flash to get the positions. If it is not stored in 
ubifs_branch (but still stored in the tree), then it would have to be 
loaded once 'on-demand' after mounting. This is also an option, of course. 
Currently, deleting a file performs a walk on all the TNC's data nodes 
that are removed, so the extra mark-delete incurs no observable cost.

Cheers,
Joel Reardon

On Wed, 29 Feb 2012, Artem Bityutskiy wrote:

> On Thu, 2012-02-09 at 16:24 +0100, Joel Reardon wrote:
>>
>> Each data nodes includes a reference to a key in the KSA. This key is read and
>> used to decrypt the data. When a new data node is written, an unused key is
>> selected from the KSA and used to encrypt the data node. The reference to the
>> key is then included with the node. The keys in the KSA are written before
>> actually being used to encrypt data. To securely delete a data node, we simply
>> mark the corresponding key position as deleted, and during the next purging
>> operation the KSA erase block that contains the key is then updated to a
>> version that does not contain the key.
>
> Why do you need to have your '__u64 crypto_lookup' both in the data node
> and the index? Isn't it enough to have them only inside the data nodes?
> ubifs_branch anyway points to the data node and you can read your
> 'crypto_lookup' from there.
>
> -- 
> Best Regards,
> Artem Bityutskiy
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Add encryption key parameter to compress/decompress functions
  2012-02-23 15:29     ` [patch] Add encryption key parameter to compress/decompress functions Joel Reardon
@ 2012-03-09  7:17       ` Artem Bityutskiy
  2012-03-19 16:54         ` [patch] Add design document for UBIFS secure deletion Joel Reardon
  0 siblings, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-09  7:17 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

On Thu, 2012-02-23 at 16:29 +0100, Joel Reardon wrote:
> This patch extends to interface for compression and decompression of data 
> nodes to take a cryptographic key
> as a parameter. If it is set to NULL, then the current behaviour persists. 
> If it is non-null, then it is taken
> as a pointer to an array of length UBIFSEC_KEYSIZE (=16 bytes) which 
> contains the key to use to encrypt
> the data node. Each data node should be encrypted with a separate key as 
> it uses the same initialization vector  (of 0).

This patch is also wrapped... Would you consider using git - commit your
patches, then git format-patch, then git send-email them - git tools
will take everything right. Use --dry-run with git send-email first to
check everything is right.

-- 
Best Regards,
Artem Bityutskiy


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-03-01 13:41   ` Joel Reardon
@ 2012-03-09  7:36     ` Artem Bityutskiy
  2012-03-09 19:29       ` Joel Reardon
  0 siblings, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-09  7:36 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

On Thu, 2012-03-01 at 14:41 +0100, Joel Reardon wrote:
> This is true. The reason its done is as an optimization for the 
> following reason.
> 
> When deleting a data node, the key position is marked as deleted.

Would you please explain again how the key position is being marked as
deleted please. Would be nice to have a wiki or a public document with
design reference somewhere, so you could just point "see page 5" as an
answer, and update it if needed.

>  The key 
> positions then requires a datanode read to find.

Sorry, would you please provide a bit more detailed information. I am
sure you explained it before, but the explanation is buried somewhere.

I am ready to create an ubifs branch for you and keep a text file with
design/FAQ there, and update the branch as UBIFS moves forward, and
apply your small incremental patches - as soon as we have an
applicable / nice patch.

>  By keeping it in memory (thus the TNC), 
> marking a key pos as deleted doesn't require a flash read.


Well, I'd say that this should be solved with another layer of caching.
E.g., we have so called "LNC" (Leaf node cache) - we keep direntries at
the leaf level to avoid extra reads. You could extend LNC and keep keys
there.

Consider this approach:

1. You throw out this optimization so far
2. Keep working on your stuff - you have enough issues to deal with.
You'll have less compatibility issues when you throw that out. The
design will be simpler as well.
3. When / if other things are done, you try to extend LNC to always
cache the keys.

>  This improves 
> e.g., truncations and full file deletion speed, which would otherwise read 
> each data node from flash to get the positions. If it is not stored in 
> ubifs_branch (but still stored in the tree), then it would have to be 
> loaded once 'on-demand' after mounting. This is also an option, of course. 
> Currently, deleting a file performs a walk on all the TNC's data nodes 
> that are removed, so the extra mark-delete incurs no observable cost.

I wonder also if it is wise to enable secure deletion globally. Yes, we
pay the cost of maintaining the keys anyways, but we could avoid paying
the costs at deletion time when deleting non-securely.

Isn't it wiser to have a special interface for secure deletion which
would be slower than normal deletion? I believe this is the right
approach. And I believe block-based file-systems would go this way. Just
think about MMC which has secure erase trim which is so slow - I do not
think anyone would use it for everything by default - people would have
a separate interface.

Did you explore, by the way, if something like this is being worked on
for other FSes in Linux?


-- 
Best Regards,
Artem Bityutskiy


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-03-09  7:36     ` Artem Bityutskiy
@ 2012-03-09 19:29       ` Joel Reardon
  2012-03-12 13:30         ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-09 19:29 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel


>
> I wonder also if it is wise to enable secure deletion globally. Yes, we
> pay the cost of maintaining the keys anyways, but we could avoid paying
> the costs at deletion time when deleting non-securely.
>
> Isn't it wiser to have a special interface for secure deletion which
> would be slower than normal deletion? I believe this is the right
> approach. And I believe block-based file-systems would go this way. Just
> think about MMC which has secure erase trim which is so slow - I do not
> think anyone would use it for everything by default - people would have
> a separate interface.

Here there is a wide space of solutions. One approach used in block-based
systems is to use a +s attribute, and then progate this whenever files are
copied etc. However, it presents the major useability problem in that a
user must remember to set this attribute for this files. And no
application can be reliably expected to set this value correctly since it
is not a standard action. Sensitive data not marked as such will thus not
be securely deleted. It suffers the same way as requiring users to use srm
when appropriate---after the first time they forget they'll probably never
again remember.

In this solution, keys are marked to be deleted when the data node
encrypted with that key is removed from the TNC. So a really simple
change:
1. node is added - key marked as used;
2. node is removed - key marked as deleted;
3. node is modified to a new value; old key marked deleted, new key marked
as used.

The keys themselves are not immediately removed, this occurs during
UBIFS commit (although it can also happen independently, but also as a
periodic background action). Thus, deletion has a low cost, just erasing
the small set of key storing blocks in the background, and the cost will
likely be
the same regardless if the entire partition is purged instead of
just some data marked as sensitive: it only takes one key on an
LEB to warrant erasure of the LEB, and there are (LEB_SIZE >> 16)
keys per LEB.

However, without caching the keys in the TNC, then deletion takes as long
as reading a file. Thankfully, it will be nowhere near as bad as MMC
secure erase trim, which (I presume) actually GCs and erases all the EBs that
store file data. In this solution, it only needs to read the data node's
header to get the key position, and mark it as deleted. Deletion happens
in the backgroun.

To me, being able to set secure erase or not as a partition level setting
makes alot of sense. The OS partition does not need it, but the user file data
partition will have it, and UBI makes sense that wear levelling is done
among all the partitions properly. Then taint status for files'
sensitivity doesn't need to be maintained. The deployer simply chooses for
this part of the FS, do they want secure deletion over the data or not,
one time at the beginning. (Compare this to the decision on whether or
not to encrypt their entire partition or later encrypting each file they
make.)


>
> Did you explore, by the way, if something like this is being worked on
> for other FSes in Linux?
>

In my related works research I found nothing like this. Some advocated
putting a key in the inode for a file then securely deleting things at a
file-level granularity by GC and erasing the inode. Another suggestion
multiple overwrites with zeros but that doesn't seem like it works with
modern denser flash memories.

I'll remove the change to tree branch, and repost correctly the
version change where only data node is effected. Btw, when I was
developing it I used the last 8 bytes from the key as the key position,
because the key was 16 bytes but only 8 were used. Could you comment on
the last 8 bytes of ubifs keys?


Joel

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-03-09 19:29       ` Joel Reardon
@ 2012-03-12 13:30         ` Artem Bityutskiy
  2012-03-12 13:34           ` Joel Reardon
  2012-03-12 13:36           ` Artem Bityutskiy
  0 siblings, 2 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-12 13:30 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

On Fri, 2012-03-09 at 20:29 +0100, Joel Reardon wrote:
> To me, being able to set secure erase or not as a partition level setting
> makes alot of sense. The OS partition does not need it, but the user file data
> partition will have it, and UBI makes sense that wear levelling is done
> among all the partitions properly. Then taint status for files'
> sensitivity doesn't need to be maintained. The deployer simply chooses for
> this part of the FS, do they want secure deletion over the data or not,
> one time at the beginning. (Compare this to the decision on whether or
> not to encrypt their entire partition or later encrypting each file they
> make.)

Well, it makes sense. I do not insist on having a separate interface for
secure deletion. But duplication of the same information in the B-tree
slots and the data nodes the slots point to does not make much sense to
me.

I mostly look at this from the maintainability and upstreamability POW
and I see that despite on the clever ideas the changes broke backward
compatibility, patches are big and intrusive. From my point of view -
contributors come and go but I keep maintaining this beast :-)

> I'll remove the change to tree branch, and repost correctly the
> version change where only data node is effected.

But please, keep in mind my motivations and ensure that what I suggest
makes sense from your POW!

>  Btw, when I was
> developing it I used the last 8 bytes from the key as the key position,
> because the key was 16 bytes but only 8 were used. Could you comment on
> the last 8 bytes of ubifs keys?

I think you can use them. But is it possible to kill these things from
the data nodes themselves? We can always find it by looking up the index
by the data node key, right?

-- 
Best Regards,
Artem Bityutskiy


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-03-12 13:30         ` Artem Bityutskiy
@ 2012-03-12 13:34           ` Joel Reardon
  2012-03-12 13:36           ` Artem Bityutskiy
  1 sibling, 0 replies; 55+ messages in thread
From: Joel Reardon @ 2012-03-12 13:34 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel

>
> I think you can use them. But is it possible to kill these things from
> the data nodes themselves? We can always find it by looking up the index
> by the data node key, right?
>

Yep, thats exactly right. Originally it was just inside the data node;
I put it into the TNC as a optimization later during development. If I use
the last eight bytes from the data node's key, and I remove the position
from ubifs_branch, then there is no longer any change to the on-disk
format, so thats should be useful. Yeah, storing the same value twice
means also needing a consistency check. What can be done is that when the
value is loaded once after mounting, it is stored in in the z-node in
memory.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-03-12 13:30         ` Artem Bityutskiy
  2012-03-12 13:34           ` Joel Reardon
@ 2012-03-12 13:36           ` Artem Bityutskiy
  2012-03-12 13:37             ` Joel Reardon
  2012-03-14 10:20             ` Joel Reardon
  1 sibling, 2 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-12 13:36 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

On Mon, 2012-03-12 at 15:30 +0200, Artem Bityutskiy wrote:
> >  Btw, when I was
> > developing it I used the last 8 bytes from the key as the key position,
> > because the key was 16 bytes but only 8 were used. Could you comment on
> > the last 8 bytes of ubifs keys?
> 
> I think you can use them. But is it possible to kill these things from
> the data nodes themselves? We can always find it by looking up the index
> by the data node key, right?

Joel, but please, send small patches, preparation patches, etc. Do not
disappear for several months and do not come back with another big
patch. Tell about your intentions in advance.

E.g., if you decide to start using those unused 8 bytes - first do small
preparations like removing the notion of "key scheme" which we
provisioned but never used (from comments as well). Clean-up the "space"
for yourself. Then start using them for your purposes. As I said, as
soon as I get the first patch I like, I'll create a separate branch in
the UBIFS git tree for your work.

Thanks!

-- 
Best Regards,
Artem Bityutskiy


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-03-12 13:36           ` Artem Bityutskiy
@ 2012-03-12 13:37             ` Joel Reardon
  2012-03-14 10:20             ` Joel Reardon
  1 sibling, 0 replies; 55+ messages in thread
From: Joel Reardon @ 2012-03-12 13:37 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel

Sounds good, I'll use the main git repo and as the first patch I'll claim
those 8 bytes and make key.h reflect whats going on.

Cheers,
Joel

On Mon, 12 Mar 2012, Artem Bityutskiy wrote:

> On Mon, 2012-03-12 at 15:30 +0200, Artem Bityutskiy wrote:
> > >  Btw, when I was
> > > developing it I used the last 8 bytes from the key as the key position,
> > > because the key was 16 bytes but only 8 were used. Could you comment on
> > > the last 8 bytes of ubifs keys?
> >
> > I think you can use them. But is it possible to kill these things from
> > the data nodes themselves? We can always find it by looking up the index
> > by the data node key, right?
>
> Joel, but please, send small patches, preparation patches, etc. Do not
> disappear for several months and do not come back with another big
> patch. Tell about your intentions in advance.
>
> E.g., if you decide to start using those unused 8 bytes - first do small
> preparations like removing the notion of "key scheme" which we
> provisioned but never used (from comments as well). Clean-up the "space"
> for yourself. Then start using them for your purposes. As I said, as
> soon as I get the first patch I like, I'll create a separate branch in
> the UBIFS git tree for your work.
>
> Thanks!
>
> --
> Best Regards,
> Artem Bityutskiy
>
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-03-12 13:36           ` Artem Bityutskiy
  2012-03-12 13:37             ` Joel Reardon
@ 2012-03-14 10:20             ` Joel Reardon
  2012-03-14 10:27               ` Artem Bityutskiy
  1 sibling, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-14 10:20 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel

For removing the key scheme notion, is it correct to remove:
UBIFS_KEY_MAX_LEN and UBIFS_SK_, UBIFS_S_KEY_BLOCK_BITS, ... and replace
it with a fixed UBIFS_KEY_LEN (and other values), thus also ignoring
key_fmt in key_max_inode_size and simply use a fixed key scheme? Or should
I simply reduce MAX_LEN to 8 but still allow multiple bit assignments
within those 8 bytes via selecting a different key_fmt.

Cheers,
Joel Reardon



On Mon, 12 Mar 2012, Artem Bityutskiy wrote:

> On Mon, 2012-03-12 at 15:30 +0200, Artem Bityutskiy wrote:
> > >  Btw, when I was
> > > developing it I used the last 8 bytes from the key as the key position,
> > > because the key was 16 bytes but only 8 were used. Could you comment on
> > > the last 8 bytes of ubifs keys?
> >
> > I think you can use them. But is it possible to kill these things from
> > the data nodes themselves? We can always find it by looking up the index
> > by the data node key, right?
>
> Joel, but please, send small patches, preparation patches, etc. Do not
> disappear for several months and do not come back with another big
> patch. Tell about your intentions in advance.
>
> E.g., if you decide to start using those unused 8 bytes - first do small
> preparations like removing the notion of "key scheme" which we
> provisioned but never used (from comments as well). Clean-up the "space"
> for yourself. Then start using them for your purposes. As I said, as
> soon as I get the first patch I like, I'll create a separate branch in
> the UBIFS git tree for your work.
>
> Thanks!
>
> --
> Best Regards,
> Artem Bityutskiy
>
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Adding Secure Deletion to UBIFS
  2012-03-14 10:20             ` Joel Reardon
@ 2012-03-14 10:27               ` Artem Bityutskiy
  0 siblings, 0 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-14 10:27 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 545 bytes --]

On Wed, 2012-03-14 at 11:20 +0100, Joel Reardon wrote:
> For removing the key scheme notion, is it correct to remove:
> UBIFS_KEY_MAX_LEN and UBIFS_SK_, UBIFS_S_KEY_BLOCK_BITS, ... and replace
> it with a fixed UBIFS_KEY_LEN (and other values), thus also ignoring
> key_fmt in key_max_inode_size and simply use a fixed key scheme? Or should
> I simply reduce MAX_LEN to 8 but still allow multiple bit assignments
> within those 8 bytes via selecting a different key_fmt.

Yes, sounds reasonable.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [patch] Remove notion of key schemes
  2012-02-23 14:59   ` Joel Reardon
  2012-02-23 15:29     ` [patch] Add encryption key parameter to compress/decompress functions Joel Reardon
  2012-02-29 17:09     ` [patch] Adding Secure Deletion to UBIFS Artem Bityutskiy
@ 2012-03-15 14:48     ` Joel Reardon
  2012-03-16 12:43       ` Artem Bityutskiy
  2012-03-16 12:51       ` Artem Bityutskiy
  2 siblings, 2 replies; 55+ messages in thread
From: Joel Reardon @ 2012-03-15 14:48 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel

Removed the notion of different  key schemes for ubifs with a single 64
bit key scheme. Reduced the size of UBIFS keys to 8 bytes
globally. Fill in the void left by the shorter key with a cryptolookup
field (8 byte) for data node and padding for the other nodes.
Changed zero_* functions to operate on the new padding. Replaced key scheme
enum with one entry into a define.

This was tested using integck and writing files on a nandsimmed partition
using both the vanilla version of ubifs and the modified version. Data
was written using both ubifs drivers and successfully read when later
mounting with either driver.

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
---
 fs/ubifs/dir.c         |    2 +-
 fs/ubifs/journal.c     |    2 +
 fs/ubifs/key.h         |   78 ++++++++++++++++++++---------------------------
 fs/ubifs/sb.c          |    4 +-
 fs/ubifs/ubifs-media.h |   38 ++++++++++-------------
 fs/ubifs/ubifs.h       |   12 ++++----
 6 files changed, 61 insertions(+), 75 deletions(-)

diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index ec9f187..7e87f92 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -365,7 +365,7 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir)

 	dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos);

-	if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2)
+	if (file->f_pos > UBIFS_KEY_HASH_MASK || file->f_pos == 2)
 		/*
 		 * The directory was seek'ed to a senseless position or there
 		 * are no more entries.
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 2f438ab..d7b8f10 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -66,6 +66,7 @@
  */
 static inline void zero_ino_node_unused(struct ubifs_ino_node *ino)
 {
+	memset(ino->padding0, 0, 8);
 	memset(ino->padding1, 0, 4);
 	memset(ino->padding2, 0, 26);
 }
@@ -77,6 +78,7 @@ static inline void zero_ino_node_unused(struct ubifs_ino_node *ino)
  */
 static inline void zero_dent_node_unused(struct ubifs_dent_node *dent)
 {
+	memset(dent->padding0, 0, 8);
 	dent->padding1 = 0;
 	memset(dent->padding2, 0, 4);
 }
diff --git a/fs/ubifs/key.h b/fs/ubifs/key.h
index 92a8491..5dc1e2b 100644
--- a/fs/ubifs/key.h
+++ b/fs/ubifs/key.h
@@ -22,16 +22,13 @@

 /*
  * This header contains various key-related definitions and helper function.
- * UBIFS allows several key schemes, so we access key fields only via these
- * helpers. At the moment only one key scheme is supported.
+ * All access to key fields must be done only via the helper functions
+ * defined in this file.
  *
- * Simple key scheme
- * ~~~~~~~~~~~~~~~~~
- *
- * Keys are 64-bits long. First 32-bits are inode number (parent inode number
- * in case of direntry key). Next 3 bits are node type. The last 29 bits are
- * 4KiB offset in case of inode node, and direntry hash in case of a direntry
- * node. We use "r5" hash borrowed from reiserfs.
+ * Keys are 64-bits long. The first 32-bits are inode number (parent inode
+ * number in case of direntry key). The next 3 bits are node type. The next
+ * 29 bits are 4KiB offset in case of inode node, and direntry hash in case
+ * of a direntry node. We use "r5" hash borrowed from reiserfs.
  */

 #ifndef __UBIFS_KEY_H__
@@ -47,7 +44,7 @@
  */
 static inline uint32_t key_mask_hash(uint32_t hash)
 {
-	hash &= UBIFS_S_KEY_HASH_MASK;
+	hash &= UBIFS_KEY_HASH_MASK;
 	if (unlikely(hash <= 2))
 		hash += 3;
 	return hash;
@@ -97,7 +94,7 @@ static inline void ino_key_init(const struct ubifs_info *c,
 				union ubifs_key *key, ino_t inum)
 {
 	key->u32[0] = inum;
-	key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS;
+	key->u32[1] = UBIFS_INO_KEY << UBIFS_KEY_BLOCK_BITS;
 }

 /**
@@ -112,8 +109,7 @@ static inline void ino_key_init_flash(const struct ubifs_info *c, void *k,
 	union ubifs_key *key = k;

 	key->j32[0] = cpu_to_le32(inum);
-	key->j32[1] = cpu_to_le32(UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS);
-	memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+	key->j32[1] = cpu_to_le32(UBIFS_INO_KEY << UBIFS_KEY_BLOCK_BITS);
 }

 /**
@@ -155,9 +151,9 @@ static inline void dent_key_init(const struct ubifs_info *c,
 {
 	uint32_t hash = c->key_hash(nm->name, nm->len);

-	ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+	ubifs_assert(!(hash & ~UBIFS_KEY_HASH_MASK));
 	key->u32[0] = inum;
-	key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS);
+	key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_KEY_HASH_BITS);
 }

 /**
@@ -172,9 +168,9 @@ static inline void dent_key_init_hash(const struct ubifs_info *c,
 				      union ubifs_key *key, ino_t inum,
 				      uint32_t hash)
 {
-	ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+	ubifs_assert(!(hash & ~UBIFS_KEY_HASH_MASK));
 	key->u32[0] = inum;
-	key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS);
+	key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_KEY_HASH_BITS);
 }

 /**
@@ -190,11 +186,10 @@ static inline void dent_key_init_flash(const struct ubifs_info *c, void *k,
 	union ubifs_key *key = k;
 	uint32_t hash = c->key_hash(nm->name, nm->len);

-	ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+	ubifs_assert(!(hash & ~UBIFS_KEY_HASH_MASK));
 	key->j32[0] = cpu_to_le32(inum);
 	key->j32[1] = cpu_to_le32(hash |
-				  (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS));
-	memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+				  (UBIFS_DENT_KEY << UBIFS_KEY_HASH_BITS));
 }

 /**
@@ -207,7 +202,7 @@ static inline void lowest_dent_key(const struct ubifs_info *c,
 				   union ubifs_key *key, ino_t inum)
 {
 	key->u32[0] = inum;
-	key->u32[1] = UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS;
+	key->u32[1] = UBIFS_DENT_KEY << UBIFS_KEY_HASH_BITS;
 }

 /**
@@ -223,9 +218,9 @@ static inline void xent_key_init(const struct ubifs_info *c,
 {
 	uint32_t hash = c->key_hash(nm->name, nm->len);

-	ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+	ubifs_assert(!(hash & ~UBIFS_KEY_HASH_MASK));
 	key->u32[0] = inum;
-	key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS);
+	key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_KEY_HASH_BITS);
 }

 /**
@@ -241,11 +236,10 @@ static inline void xent_key_init_flash(const struct ubifs_info *c, void *k,
 	union ubifs_key *key = k;
 	uint32_t hash = c->key_hash(nm->name, nm->len);

-	ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+	ubifs_assert(!(hash & ~UBIFS_KEY_HASH_MASK));
 	key->j32[0] = cpu_to_le32(inum);
 	key->j32[1] = cpu_to_le32(hash |
-				  (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS));
-	memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+				  (UBIFS_XENT_KEY << UBIFS_KEY_HASH_BITS));
 }

 /**
@@ -258,7 +252,7 @@ static inline void lowest_xent_key(const struct ubifs_info *c,
 				   union ubifs_key *key, ino_t inum)
 {
 	key->u32[0] = inum;
-	key->u32[1] = UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS;
+	key->u32[1] = UBIFS_XENT_KEY << UBIFS_KEY_HASH_BITS;
 }

 /**
@@ -272,9 +266,9 @@ static inline void data_key_init(const struct ubifs_info *c,
 				 union ubifs_key *key, ino_t inum,
 				 unsigned int block)
 {
-	ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK));
+	ubifs_assert(!(block & ~UBIFS_KEY_BLOCK_MASK));
 	key->u32[0] = inum;
-	key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS);
+	key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_KEY_BLOCK_BITS);
 }

 /**
@@ -286,7 +280,7 @@ static inline void data_key_init(const struct ubifs_info *c,
 static inline void highest_data_key(const struct ubifs_info *c,
 				   union ubifs_key *key, ino_t inum)
 {
-	data_key_init(c, key, inum, UBIFS_S_KEY_BLOCK_MASK);
+	data_key_init(c, key, inum, UBIFS_KEY_BLOCK_MASK);
 }

 /**
@@ -302,7 +296,7 @@ static inline void trun_key_init(const struct ubifs_info *c,
 				 union ubifs_key *key, ino_t inum)
 {
 	key->u32[0] = inum;
-	key->u32[1] = UBIFS_TRUN_KEY << UBIFS_S_KEY_BLOCK_BITS;
+	key->u32[1] = UBIFS_TRUN_KEY << UBIFS_KEY_BLOCK_BITS;
 }

 /**
@@ -327,7 +321,7 @@ static inline void invalid_key_init(const struct ubifs_info *c,
 static inline int key_type(const struct ubifs_info *c,
 			   const union ubifs_key *key)
 {
-	return key->u32[1] >> UBIFS_S_KEY_BLOCK_BITS;
+	return key->u32[1] >> UBIFS_KEY_BLOCK_BITS;
 }

 /**
@@ -339,7 +333,7 @@ static inline int key_type_flash(const struct ubifs_info *c, const void *k)
 {
 	const union ubifs_key *key = k;

-	return le32_to_cpu(key->j32[1]) >> UBIFS_S_KEY_BLOCK_BITS;
+	return le32_to_cpu(key->j32[1]) >> UBIFS_KEY_BLOCK_BITS;
 }

 /**
@@ -374,7 +368,7 @@ static inline ino_t key_inum_flash(const struct ubifs_info *c, const void *k)
 static inline uint32_t key_hash(const struct ubifs_info *c,
 				const union ubifs_key *key)
 {
-	return key->u32[1] & UBIFS_S_KEY_HASH_MASK;
+	return key->u32[1] & UBIFS_KEY_HASH_MASK;
 }

 /**
@@ -386,7 +380,7 @@ static inline uint32_t key_hash_flash(const struct ubifs_info *c, const void *k)
 {
 	const union ubifs_key *key = k;

-	return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_HASH_MASK;
+	return le32_to_cpu(key->j32[1]) & UBIFS_KEY_HASH_MASK;
 }

 /**
@@ -397,7 +391,7 @@ static inline uint32_t key_hash_flash(const struct ubifs_info *c, const void *k)
 static inline unsigned int key_block(const struct ubifs_info *c,
 				     const union ubifs_key *key)
 {
-	return key->u32[1] & UBIFS_S_KEY_BLOCK_MASK;
+	return key->u32[1] & UBIFS_KEY_BLOCK_MASK;
 }

 /**
@@ -410,7 +404,7 @@ static inline unsigned int key_block_flash(const struct ubifs_info *c,
 {
 	const union ubifs_key *key = k;

-	return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_BLOCK_MASK;
+	return le32_to_cpu(key->j32[1]) & UBIFS_KEY_BLOCK_MASK;
 }

 /**
@@ -441,7 +435,6 @@ static inline void key_write(const struct ubifs_info *c,

 	t->j32[0] = cpu_to_le32(from->u32[0]);
 	t->j32[1] = cpu_to_le32(from->u32[1]);
-	memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8);
 }

 /**
@@ -532,17 +525,12 @@ static inline int is_hash_key(const struct ubifs_info *c,
 }

 /**
- * key_max_inode_size - get maximum file size allowed by current key format.
+ * key_max_inode_size - get maximum file size allowed.
  * @c: UBIFS file-system description object
  */
 static inline unsigned long long key_max_inode_size(const struct ubifs_info *c)
 {
-	switch (c->key_fmt) {
-	case UBIFS_SIMPLE_KEY_FMT:
-		return (1ULL << UBIFS_S_KEY_BLOCK_BITS) * UBIFS_BLOCK_SIZE;
-	default:
-		return 0;
-	}
+	return (1ULL << UBIFS_KEY_BLOCK_BITS) * UBIFS_BLOCK_SIZE;
 }

 #endif /* !__UBIFS_KEY_H__ */
diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 771f7fb..3fc9071 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -86,7 +86,7 @@ static int create_default_filesystem(struct ubifs_info *c)
 	__le64 tmp_le64;

 	/* Some functions called from here depend on the @c->key_len filed */
-	c->key_len = UBIFS_SK_LEN;
+	c->key_len = UBIFS_KEY_LEN;

 	/*
 	 * First of all, we have to calculate default file-system geometry -
@@ -599,7 +599,7 @@ int ubifs_read_superblock(struct ubifs_info *c)

 	switch (c->key_fmt) {
 	case UBIFS_SIMPLE_KEY_FMT:
-		c->key_len = UBIFS_SK_LEN;
+		c->key_len = UBIFS_KEY_LEN;
 		break;
 	default:
 		ubifs_err("unsupported key format");
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index e24380c..0b769da 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -108,11 +108,11 @@
 /* UBIFS padding byte pattern (must not be first or last byte of node magic) */
 #define UBIFS_PADDING_BYTE 0xCE

-/* Maximum possible key length */
-#define UBIFS_MAX_KEY_LEN 16
+/* UBIFS key length */
+#define UBIFS_KEY_LEN 8

-/* Key length ("simple" format) */
-#define UBIFS_SK_LEN 8
+/* UBIFS key format */
+#define UBIFS_SIMPLE_KEY_FMT 0

 /* Minimum index tree fanout */
 #define UBIFS_MIN_FANOUT 3
@@ -196,22 +196,13 @@ enum {
 };

 /*
- * Supported key formats.
- *
- * UBIFS_SIMPLE_KEY_FMT: simple key format
- */
-enum {
-	UBIFS_SIMPLE_KEY_FMT,
-};
-
-/*
  * The simple key format uses 29 bits for storing UBIFS block number and hash
  * value.
  */
-#define UBIFS_S_KEY_BLOCK_BITS 29
-#define UBIFS_S_KEY_BLOCK_MASK 0x1FFFFFFF
-#define UBIFS_S_KEY_HASH_BITS  UBIFS_S_KEY_BLOCK_BITS
-#define UBIFS_S_KEY_HASH_MASK  UBIFS_S_KEY_BLOCK_MASK
+#define UBIFS_KEY_BLOCK_BITS 29
+#define UBIFS_KEY_BLOCK_MASK 0x1FFFFFFF
+#define UBIFS_KEY_HASH_BITS  UBIFS_KEY_BLOCK_BITS
+#define UBIFS_KEY_HASH_MASK  UBIFS_KEY_BLOCK_MASK

 /*
  * Key types.
@@ -456,6 +447,7 @@ union ubifs_dev_desc {
  * struct ubifs_ino_node - inode node.
  * @ch: common header
  * @key: node key
+ * @padding0: reserved for future, zeroes
  * @creat_sqnum: sequence number at time of creation
  * @size: inode size in bytes (amount of uncompressed data)
  * @atime_sec: access time seconds
@@ -489,7 +481,8 @@ union ubifs_dev_desc {
  */
 struct ubifs_ino_node {
 	struct ubifs_ch ch;
-	__u8 key[UBIFS_MAX_KEY_LEN];
+	__u8 key[UBIFS_KEY_LEN];
+	__u8 padding0[8]; /* Watch 'zero_ino_node_unused()' if changing! */
 	__le64 creat_sqnum;
 	__le64 size;
 	__le64 atime_sec;
@@ -517,6 +510,7 @@ struct ubifs_ino_node {
  * struct ubifs_dent_node - directory entry node.
  * @ch: common header
  * @key: node key
+ * @padding0: reserved for future, zeroes
  * @inum: target inode number
  * @padding1: reserved for future, zeroes
  * @type: type of the target inode (%UBIFS_ITYPE_REG, %UBIFS_ITYPE_DIR, etc)
@@ -529,9 +523,10 @@ struct ubifs_ino_node {
  */
 struct ubifs_dent_node {
 	struct ubifs_ch ch;
-	__u8 key[UBIFS_MAX_KEY_LEN];
+	__u8 key[UBIFS_KEY_LEN];
+	__u8 padding0[8]; /* Watch 'zero_dent_node_unused()' if changing! */
 	__le64 inum;
-	__u8 padding1;
+	__u8 padding1; /* Watch 'zero_dent_node_unused()' if changing! */
 	__u8 type;
 	__le16 nlen;
 	__u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */
@@ -552,7 +547,8 @@ struct ubifs_dent_node {
  */
 struct ubifs_data_node {
 	struct ubifs_ch ch;
-	__u8 key[UBIFS_MAX_KEY_LEN];
+	__u8 key[UBIFS_KEY_LEN];
+	__le64 crypto_lookup;
 	__le32 size;
 	__le16 compr_type;
 	__u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 93d59ac..7c343e1 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -274,10 +274,10 @@ struct ubifs_old_idx {

 /* The below union makes it easier to deal with keys */
 union ubifs_key {
-	uint8_t u8[UBIFS_SK_LEN];
-	uint32_t u32[UBIFS_SK_LEN/4];
-	uint64_t u64[UBIFS_SK_LEN/8];
-	__le32 j32[UBIFS_SK_LEN/4];
+	uint8_t u8[UBIFS_KEY_LEN];
+	uint32_t u32[UBIFS_KEY_LEN/4];
+	uint64_t u64[UBIFS_KEY_LEN/8];
+	__le32 j32[UBIFS_KEY_LEN/4];
 };

 /**
@@ -1065,8 +1065,8 @@ struct ubifs_debug_info;
  *
  * @key_hash_type: type of the key hash
  * @key_hash: direntry key hash function
- * @key_fmt: key format
- * @key_len: key length
+ * @key_fmt: (obsolete, set to 0) key format
+ * @key_len: key length in bytes
  * @fanout: fanout of the index tree (number of links per indexing node)
  *
  * @min_io_size: minimal input/output unit size
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [patch] Remove notion of key schemes
  2012-03-15 14:48     ` [patch] Remove notion of key schemes Joel Reardon
@ 2012-03-16 12:43       ` Artem Bityutskiy
  2012-03-16 12:51       ` Artem Bityutskiy
  1 sibling, 0 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-16 12:43 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2230 bytes --]

On Thu, 2012-03-15 at 15:48 +0100, Joel Reardon wrote:
> @@ -112,8 +109,7 @@ static inline void ino_key_init_flash(const struct ubifs_info *c, void *k,
>  	union ubifs_key *key = k;
> 
>  	key->j32[0] = cpu_to_le32(inum);
> -	key->j32[1] = cpu_to_le32(UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS);
> -	memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
> +	key->j32[1] = cpu_to_le32(UBIFS_INO_KEY << UBIFS_KEY_BLOCK_BITS);
>  }

So current UBIFS driver will always zero out unused parts of the key.
Looks like a flaw in UBIFS, but it is too late to do anything about
this. Could you please also think about the situation when a
security-enabled image is mounted in an older kernel which will start
zeroing unused bytes. What will happen when it is mounted by newer UBIFS
with the security stuff? Would be great to make sure this is handled
nicely.

>  /**
> - * key_max_inode_size - get maximum file size allowed by current key format.
> + * key_max_inode_size - get maximum file size allowed.
>   * @c: UBIFS file-system description object
>   */
>  static inline unsigned long long key_max_inode_size(const struct ubifs_info *c)
>  {
> -	switch (c->key_fmt) {
> -	case UBIFS_SIMPLE_KEY_FMT:
> -		return (1ULL << UBIFS_S_KEY_BLOCK_BITS) * UBIFS_BLOCK_SIZE;
> -	default:
> -		return 0;
> -	}
> +	return (1ULL << UBIFS_KEY_BLOCK_BITS) * UBIFS_BLOCK_SIZE;
>  }

I think this function should also be removed and turned into a macro. 
>  struct ubifs_dent_node {
>  	struct ubifs_ch ch;
> -	__u8 key[UBIFS_MAX_KEY_LEN];
> +	__u8 key[UBIFS_KEY_LEN];
> +	__u8 padding0[8]; /* Watch 'zero_dent_node_unused()' if changing! */
>  	__le64 inum;
> -	__u8 padding1;
> +	__u8 padding1; /* Watch 'zero_dent_node_unused()' if changing! */
>  	__u8 type;
>  	__le16 nlen;
>  	__u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */
> @@ -552,7 +547,8 @@ struct ubifs_dent_node {
>   */
>  struct ubifs_data_node {
>  	struct ubifs_ch ch;
> -	__u8 key[UBIFS_MAX_KEY_LEN];
> +	__u8 key[UBIFS_KEY_LEN];
> +	__le64 crypto_lookup;

Err, no, this patch should be _pure_ key schemes removal. All the crypto
stuff should be separate.

Otherwise looks good!

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Remove notion of key schemes
  2012-03-15 14:48     ` [patch] Remove notion of key schemes Joel Reardon
  2012-03-16 12:43       ` Artem Bityutskiy
@ 2012-03-16 12:51       ` Artem Bityutskiy
  2012-03-16 13:34         ` Joel Reardon
  1 sibling, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-16 12:51 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 318 bytes --]

On Thu, 2012-03-15 at 15:48 +0100, Joel Reardon wrote:
> +       __u8 padding0[8]; /* Watch 'zero_dent_node_unused()' if changing! */

Also please, be consistent with overall UBIFS coding style and comment
fields at the structure header comment, not near the field itself.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Remove notion of key schemes
  2012-03-16 12:51       ` Artem Bityutskiy
@ 2012-03-16 13:34         ` Joel Reardon
  2012-03-16 13:41           ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-16 13:34 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel


On Fri, 16 Mar 2012, Artem Bityutskiy wrote:

> On Thu, 2012-03-15 at 15:48 +0100, Joel Reardon wrote:
> > +       __u8 padding0[8]; /* Watch 'zero_dent_node_unused()' if changing! */
>
> Also please, be consistent with overall UBIFS coding style and comment
> fields at the structure header comment, not near the field itself.
>


I actually just copied this comment from the comment following padding1
and padding2; should they all just be omitted?


As for the ubifs being mounted with the old, it may be best to increase
the version format number. The old version won't be able to 'read' (i.e.,
decrypt) the data, while the new version has a switch to enable both
modes. If new data is written by the old version then the new version will
also have trouble to read it (unless we set crypto_lookup==0 to mean no
key). But its probably for the best to just let older version mount the
security enhanced one as read-only using the version format as the data
will be anyhow unreadable. Non-security-enhanced ubifs (but
aware) partitions can set the version format to the older value as they
will be compatible.

cheers,
Joel Reardon

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Remove notion of key schemes
  2012-03-16 13:34         ` Joel Reardon
@ 2012-03-16 13:41           ` Artem Bityutskiy
  2012-03-16 15:02             ` Joel Reardon
  0 siblings, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-16 13:41 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1118 bytes --]

On Fri, 2012-03-16 at 14:34 +0100, Joel Reardon wrote:
> I actually just copied this comment from the comment following padding1
> and padding2; should they all just be omitted?

Ah, ok, ignore this comment then please.

> As for the ubifs being mounted with the old, it may be best to increase
> the version format number. The old version won't be able to 'read' (i.e.,
> decrypt) the data, while the new version has a switch to enable both
> modes. If new data is written by the old version then the new version will
> also have trouble to read it (unless we set crypto_lookup==0 to mean no
> key). But its probably for the best to just let older version mount the
> security enhanced one as read-only using the version format as the data
> will be anyhow unreadable. Non-security-enhanced ubifs (but
> aware) partitions can set the version format to the older value as they
> will be compatible.

OK. Then I guess the version increment patch should also be separate. In
general, try to make logically independent things separately and keep
patches small.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Remove notion of key schemes
  2012-03-16 13:41           ` Artem Bityutskiy
@ 2012-03-16 15:02             ` Joel Reardon
  2012-03-19 14:56               ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-16 15:02 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel

Removed the notion of different  key schemes for ubifs with a single 64
bit key scheme. Reduced the size of UBIFS keys to 8 bytes
globally. Fill in the void left behind with padding for the effected
nodes, so previous version will not be effected.
Changed zero_* functions to operate on the new padding. Replaced key
scheme enum with one entry into a define.

This was tested using integck and writing files on a nandsimmed partition
using both the vanilla version of ubifs and the modified version. Data
was written using both ubifs drivers and successfully read when later
mounting with either driver.

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>


diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index ec9f187..7e87f92 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -365,7 +365,7 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir)

 	dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos);

-	if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2)
+	if (file->f_pos > UBIFS_KEY_HASH_MASK || file->f_pos == 2)
 		/*
 		 * The directory was seek'ed to a senseless position or there
 		 * are no more entries.
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 2f438ab..137415e 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -66,6 +66,7 @@
  */
 static inline void zero_ino_node_unused(struct ubifs_ino_node *ino)
 {
+	memset(ino->padding0, 0, 8);
 	memset(ino->padding1, 0, 4);
 	memset(ino->padding2, 0, 26);
 }
@@ -77,6 +78,7 @@ static inline void zero_ino_node_unused(struct ubifs_ino_node *ino)
  */
 static inline void zero_dent_node_unused(struct ubifs_dent_node *dent)
 {
+	memset(dent->padding0, 0, 8);
 	dent->padding1 = 0;
 	memset(dent->padding2, 0, 4);
 }
@@ -87,7 +89,8 @@ static inline void zero_dent_node_unused(struct ubifs_dent_node *dent)
  */
 static inline void zero_data_node_unused(struct ubifs_data_node *data)
 {
-	memset(data->padding, 0, 2);
+	data->padding0 = 0;
+	memset(data->padding1, 0, 2);
 }

 /**
diff --git a/fs/ubifs/key.h b/fs/ubifs/key.h
index 92a8491..f67f24a 100644
--- a/fs/ubifs/key.h
+++ b/fs/ubifs/key.h
@@ -22,16 +22,13 @@

 /*
  * This header contains various key-related definitions and helper function.
- * UBIFS allows several key schemes, so we access key fields only via these
- * helpers. At the moment only one key scheme is supported.
+ * All access to key fields must be done only via the helper functions
+ * defined in this file.
  *
- * Simple key scheme
- * ~~~~~~~~~~~~~~~~~
- *
- * Keys are 64-bits long. First 32-bits are inode number (parent inode number
- * in case of direntry key). Next 3 bits are node type. The last 29 bits are
- * 4KiB offset in case of inode node, and direntry hash in case of a direntry
- * node. We use "r5" hash borrowed from reiserfs.
+ * Keys are 64-bits long. The first 32-bits are inode number (parent inode
+ * number in case of direntry key). The next 3 bits are node type. The next
+ * 29 bits are 4KiB offset in case of inode node, and direntry hash in case
+ * of a direntry node. We use "r5" hash borrowed from reiserfs.
  */

 #ifndef __UBIFS_KEY_H__
@@ -47,7 +44,7 @@
  */
 static inline uint32_t key_mask_hash(uint32_t hash)
 {
-	hash &= UBIFS_S_KEY_HASH_MASK;
+	hash &= UBIFS_KEY_HASH_MASK;
 	if (unlikely(hash <= 2))
 		hash += 3;
 	return hash;
@@ -97,7 +94,7 @@ static inline void ino_key_init(const struct ubifs_info *c,
 				union ubifs_key *key, ino_t inum)
 {
 	key->u32[0] = inum;
-	key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS;
+	key->u32[1] = UBIFS_INO_KEY << UBIFS_KEY_BLOCK_BITS;
 }

 /**
@@ -112,8 +109,7 @@ static inline void ino_key_init_flash(const struct ubifs_info *c, void *k,
 	union ubifs_key *key = k;

 	key->j32[0] = cpu_to_le32(inum);
-	key->j32[1] = cpu_to_le32(UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS);
-	memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+	key->j32[1] = cpu_to_le32(UBIFS_INO_KEY << UBIFS_KEY_BLOCK_BITS);
 }

 /**
@@ -155,9 +151,9 @@ static inline void dent_key_init(const struct ubifs_info *c,
 {
 	uint32_t hash = c->key_hash(nm->name, nm->len);

-	ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+	ubifs_assert(!(hash & ~UBIFS_KEY_HASH_MASK));
 	key->u32[0] = inum;
-	key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS);
+	key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_KEY_HASH_BITS);
 }

 /**
@@ -172,9 +168,9 @@ static inline void dent_key_init_hash(const struct ubifs_info *c,
 				      union ubifs_key *key, ino_t inum,
 				      uint32_t hash)
 {
-	ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+	ubifs_assert(!(hash & ~UBIFS_KEY_HASH_MASK));
 	key->u32[0] = inum;
-	key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS);
+	key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_KEY_HASH_BITS);
 }

 /**
@@ -190,11 +186,10 @@ static inline void dent_key_init_flash(const struct ubifs_info *c, void *k,
 	union ubifs_key *key = k;
 	uint32_t hash = c->key_hash(nm->name, nm->len);

-	ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+	ubifs_assert(!(hash & ~UBIFS_KEY_HASH_MASK));
 	key->j32[0] = cpu_to_le32(inum);
 	key->j32[1] = cpu_to_le32(hash |
-				  (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS));
-	memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+				  (UBIFS_DENT_KEY << UBIFS_KEY_HASH_BITS));
 }

 /**
@@ -207,7 +202,7 @@ static inline void lowest_dent_key(const struct ubifs_info *c,
 				   union ubifs_key *key, ino_t inum)
 {
 	key->u32[0] = inum;
-	key->u32[1] = UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS;
+	key->u32[1] = UBIFS_DENT_KEY << UBIFS_KEY_HASH_BITS;
 }

 /**
@@ -223,9 +218,9 @@ static inline void xent_key_init(const struct ubifs_info *c,
 {
 	uint32_t hash = c->key_hash(nm->name, nm->len);

-	ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+	ubifs_assert(!(hash & ~UBIFS_KEY_HASH_MASK));
 	key->u32[0] = inum;
-	key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS);
+	key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_KEY_HASH_BITS);
 }

 /**
@@ -241,11 +236,10 @@ static inline void xent_key_init_flash(const struct ubifs_info *c, void *k,
 	union ubifs_key *key = k;
 	uint32_t hash = c->key_hash(nm->name, nm->len);

-	ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+	ubifs_assert(!(hash & ~UBIFS_KEY_HASH_MASK));
 	key->j32[0] = cpu_to_le32(inum);
 	key->j32[1] = cpu_to_le32(hash |
-				  (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS));
-	memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8);
+				  (UBIFS_XENT_KEY << UBIFS_KEY_HASH_BITS));
 }

 /**
@@ -258,7 +252,7 @@ static inline void lowest_xent_key(const struct ubifs_info *c,
 				   union ubifs_key *key, ino_t inum)
 {
 	key->u32[0] = inum;
-	key->u32[1] = UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS;
+	key->u32[1] = UBIFS_XENT_KEY << UBIFS_KEY_HASH_BITS;
 }

 /**
@@ -272,9 +266,9 @@ static inline void data_key_init(const struct ubifs_info *c,
 				 union ubifs_key *key, ino_t inum,
 				 unsigned int block)
 {
-	ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK));
+	ubifs_assert(!(block & ~UBIFS_KEY_BLOCK_MASK));
 	key->u32[0] = inum;
-	key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS);
+	key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_KEY_BLOCK_BITS);
 }

 /**
@@ -286,7 +280,7 @@ static inline void data_key_init(const struct ubifs_info *c,
 static inline void highest_data_key(const struct ubifs_info *c,
 				   union ubifs_key *key, ino_t inum)
 {
-	data_key_init(c, key, inum, UBIFS_S_KEY_BLOCK_MASK);
+	data_key_init(c, key, inum, UBIFS_KEY_BLOCK_MASK);
 }

 /**
@@ -302,7 +296,7 @@ static inline void trun_key_init(const struct ubifs_info *c,
 				 union ubifs_key *key, ino_t inum)
 {
 	key->u32[0] = inum;
-	key->u32[1] = UBIFS_TRUN_KEY << UBIFS_S_KEY_BLOCK_BITS;
+	key->u32[1] = UBIFS_TRUN_KEY << UBIFS_KEY_BLOCK_BITS;
 }

 /**
@@ -327,7 +321,7 @@ static inline void invalid_key_init(const struct ubifs_info *c,
 static inline int key_type(const struct ubifs_info *c,
 			   const union ubifs_key *key)
 {
-	return key->u32[1] >> UBIFS_S_KEY_BLOCK_BITS;
+	return key->u32[1] >> UBIFS_KEY_BLOCK_BITS;
 }

 /**
@@ -339,7 +333,7 @@ static inline int key_type_flash(const struct ubifs_info *c, const void *k)
 {
 	const union ubifs_key *key = k;

-	return le32_to_cpu(key->j32[1]) >> UBIFS_S_KEY_BLOCK_BITS;
+	return le32_to_cpu(key->j32[1]) >> UBIFS_KEY_BLOCK_BITS;
 }

 /**
@@ -374,7 +368,7 @@ static inline ino_t key_inum_flash(const struct ubifs_info *c, const void *k)
 static inline uint32_t key_hash(const struct ubifs_info *c,
 				const union ubifs_key *key)
 {
-	return key->u32[1] & UBIFS_S_KEY_HASH_MASK;
+	return key->u32[1] & UBIFS_KEY_HASH_MASK;
 }

 /**
@@ -386,7 +380,7 @@ static inline uint32_t key_hash_flash(const struct ubifs_info *c, const void *k)
 {
 	const union ubifs_key *key = k;

-	return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_HASH_MASK;
+	return le32_to_cpu(key->j32[1]) & UBIFS_KEY_HASH_MASK;
 }

 /**
@@ -397,7 +391,7 @@ static inline uint32_t key_hash_flash(const struct ubifs_info *c, const void *k)
 static inline unsigned int key_block(const struct ubifs_info *c,
 				     const union ubifs_key *key)
 {
-	return key->u32[1] & UBIFS_S_KEY_BLOCK_MASK;
+	return key->u32[1] & UBIFS_KEY_BLOCK_MASK;
 }

 /**
@@ -410,7 +404,7 @@ static inline unsigned int key_block_flash(const struct ubifs_info *c,
 {
 	const union ubifs_key *key = k;

-	return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_BLOCK_MASK;
+	return le32_to_cpu(key->j32[1]) & UBIFS_KEY_BLOCK_MASK;
 }

 /**
@@ -441,7 +435,6 @@ static inline void key_write(const struct ubifs_info *c,

 	t->j32[0] = cpu_to_le32(from->u32[0]);
 	t->j32[1] = cpu_to_le32(from->u32[1]);
-	memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8);
 }

 /**
@@ -531,18 +524,4 @@ static inline int is_hash_key(const struct ubifs_info *c,
 	return type == UBIFS_DENT_KEY || type == UBIFS_XENT_KEY;
 }

-/**
- * key_max_inode_size - get maximum file size allowed by current key format.
- * @c: UBIFS file-system description object
- */
-static inline unsigned long long key_max_inode_size(const struct ubifs_info *c)
-{
-	switch (c->key_fmt) {
-	case UBIFS_SIMPLE_KEY_FMT:
-		return (1ULL << UBIFS_S_KEY_BLOCK_BITS) * UBIFS_BLOCK_SIZE;
-	default:
-		return 0;
-	}
-}
-
 #endif /* !__UBIFS_KEY_H__ */
diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 771f7fb..3fc9071 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -86,7 +86,7 @@ static int create_default_filesystem(struct ubifs_info *c)
 	__le64 tmp_le64;

 	/* Some functions called from here depend on the @c->key_len filed */
-	c->key_len = UBIFS_SK_LEN;
+	c->key_len = UBIFS_KEY_LEN;

 	/*
 	 * First of all, we have to calculate default file-system geometry -
@@ -599,7 +599,7 @@ int ubifs_read_superblock(struct ubifs_info *c)

 	switch (c->key_fmt) {
 	case UBIFS_SIMPLE_KEY_FMT:
-		c->key_len = UBIFS_SK_LEN;
+		c->key_len = UBIFS_KEY_LEN;
 		break;
 	default:
 		ubifs_err("unsupported key format");
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 63765d5..312175d 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -2057,7 +2057,7 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
 	sb->s_magic = UBIFS_SUPER_MAGIC;
 	sb->s_blocksize = UBIFS_BLOCK_SIZE;
 	sb->s_blocksize_bits = UBIFS_BLOCK_SHIFT;
-	sb->s_maxbytes = c->max_inode_sz = key_max_inode_size(c);
+	sb->s_maxbytes = c->max_inode_sz = UBIFS_KEY_MAX_INODE_SIZE;
 	if (c->max_inode_sz > MAX_LFS_FILESIZE)
 		sb->s_maxbytes = c->max_inode_sz = MAX_LFS_FILESIZE;
 	sb->s_op = &ubifs_super_operations;
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index e24380c..9ecbd37 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -108,11 +108,11 @@
 /* UBIFS padding byte pattern (must not be first or last byte of node magic) */
 #define UBIFS_PADDING_BYTE 0xCE

-/* Maximum possible key length */
-#define UBIFS_MAX_KEY_LEN 16
+/* UBIFS key length */
+#define UBIFS_KEY_LEN 8

-/* Key length ("simple" format) */
-#define UBIFS_SK_LEN 8
+/* UBIFS key format */
+#define UBIFS_SIMPLE_KEY_FMT 0

 /* Minimum index tree fanout */
 #define UBIFS_MIN_FANOUT 3
@@ -196,22 +196,15 @@ enum {
 };

 /*
- * Supported key formats.
- *
- * UBIFS_SIMPLE_KEY_FMT: simple key format
- */
-enum {
-	UBIFS_SIMPLE_KEY_FMT,
-};
-
-/*
  * The simple key format uses 29 bits for storing UBIFS block number and hash
  * value.
  */
-#define UBIFS_S_KEY_BLOCK_BITS 29
-#define UBIFS_S_KEY_BLOCK_MASK 0x1FFFFFFF
-#define UBIFS_S_KEY_HASH_BITS  UBIFS_S_KEY_BLOCK_BITS
-#define UBIFS_S_KEY_HASH_MASK  UBIFS_S_KEY_BLOCK_MASK
+#define UBIFS_KEY_BLOCK_BITS 29
+#define UBIFS_KEY_BLOCK_MASK 0x1FFFFFFF
+#define UBIFS_KEY_HASH_BITS  UBIFS_KEY_BLOCK_BITS
+#define UBIFS_KEY_HASH_MASK  UBIFS_KEY_BLOCK_MASK
+#define UBIFS_KEY_MAX_INODE_SIZE \
+	((1ULL << UBIFS_KEY_BLOCK_BITS) * UBIFS_BLOCK_SIZE)

 /*
  * Key types.
@@ -456,6 +449,7 @@ union ubifs_dev_desc {
  * struct ubifs_ino_node - inode node.
  * @ch: common header
  * @key: node key
+ * @padding0: reserved for future, zeroes
  * @creat_sqnum: sequence number at time of creation
  * @size: inode size in bytes (amount of uncompressed data)
  * @atime_sec: access time seconds
@@ -489,7 +483,8 @@ union ubifs_dev_desc {
  */
 struct ubifs_ino_node {
 	struct ubifs_ch ch;
-	__u8 key[UBIFS_MAX_KEY_LEN];
+	__u8 key[UBIFS_KEY_LEN];
+	__u8 padding0[8]; /* Watch 'zero_ino_node_unused()' if changing! */
 	__le64 creat_sqnum;
 	__le64 size;
 	__le64 atime_sec;
@@ -517,6 +512,7 @@ struct ubifs_ino_node {
  * struct ubifs_dent_node - directory entry node.
  * @ch: common header
  * @key: node key
+ * @padding0: reserved for future, zeroes
  * @inum: target inode number
  * @padding1: reserved for future, zeroes
  * @type: type of the target inode (%UBIFS_ITYPE_REG, %UBIFS_ITYPE_DIR, etc)
@@ -529,9 +525,10 @@ struct ubifs_ino_node {
  */
 struct ubifs_dent_node {
 	struct ubifs_ch ch;
-	__u8 key[UBIFS_MAX_KEY_LEN];
+	__u8 key[UBIFS_KEY_LEN];
+	__u8 padding0[8]; /* Watch 'zero_dent_node_unused()' if changing! */
 	__le64 inum;
-	__u8 padding1;
+	__u8 padding1; /* Watch 'zero_dent_node_unused()' if changing! */
 	__u8 type;
 	__le16 nlen;
 	__u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */
@@ -552,10 +549,11 @@ struct ubifs_dent_node {
  */
 struct ubifs_data_node {
 	struct ubifs_ch ch;
-	__u8 key[UBIFS_MAX_KEY_LEN];
+	__u8 key[UBIFS_KEY_LEN];
+	__le64 padding0; /* Watch 'zero_data_node_unused()' if changing! */
 	__le32 size;
 	__le16 compr_type;
-	__u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */
+	__u8 padding1[2]; /* Watch 'zero_data_node_unused()' if changing! */
 	__u8 data[];
 } __packed;

diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 93d59ac..7c343e1 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -274,10 +274,10 @@ struct ubifs_old_idx {

 /* The below union makes it easier to deal with keys */
 union ubifs_key {
-	uint8_t u8[UBIFS_SK_LEN];
-	uint32_t u32[UBIFS_SK_LEN/4];
-	uint64_t u64[UBIFS_SK_LEN/8];
-	__le32 j32[UBIFS_SK_LEN/4];
+	uint8_t u8[UBIFS_KEY_LEN];
+	uint32_t u32[UBIFS_KEY_LEN/4];
+	uint64_t u64[UBIFS_KEY_LEN/8];
+	__le32 j32[UBIFS_KEY_LEN/4];
 };

 /**
@@ -1065,8 +1065,8 @@ struct ubifs_debug_info;
  *
  * @key_hash_type: type of the key hash
  * @key_hash: direntry key hash function
- * @key_fmt: key format
- * @key_len: key length
+ * @key_fmt: (obsolete, set to 0) key format
+ * @key_len: key length in bytes
  * @fanout: fanout of the index tree (number of links per indexing node)
  *
  * @min_io_size: minimal input/output unit size


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [patch] Remove notion of key schemes
  2012-03-16 15:02             ` Joel Reardon
@ 2012-03-19 14:56               ` Artem Bityutskiy
  0 siblings, 0 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-19 14:56 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1201 bytes --]

On Fri, 2012-03-16 at 16:02 +0100, Joel Reardon wrote:
> Removed the notion of different  key schemes for ubifs with a single 64
> bit key scheme. Reduced the size of UBIFS keys to 8 bytes
> globally. Fill in the void left behind with padding for the effected
> nodes, so previous version will not be effected.
> Changed zero_* functions to operate on the new padding. Replaced key
> scheme enum with one entry into a define.
> 
> This was tested using integck and writing files on a nandsimmed partition
> using both the vanilla version of ubifs and the modified version. Data
> was written using both ubifs drivers and successfully read when later
> mounting with either driver.
> 
> Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>

Pushed to linux-ubifs.git, branch "joel", thanks.

But please, note, that this patch was also a bit big for review. The
better way would be make things like re-naming to be separate, etc. In
general, all non-brainers (everything you can do with a simple regexp,
like re-naming) should be separate. Then reviewer knows that he should
not spend to much time on that noise. And "brainers" go separate.


-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [patch] Add design document for UBIFS secure deletion
  2012-03-09  7:17       ` Artem Bityutskiy
@ 2012-03-19 16:54         ` Joel Reardon
  2012-03-20 20:10           ` Randy Dunlap
  2012-03-21 16:10           ` Artem Bityutskiy
  0 siblings, 2 replies; 55+ messages in thread
From: Joel Reardon @ 2012-03-19 16:54 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel

Design document should be self explanatory.

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>

---
 Documentation/filesystems/ubifsec.txt |  358 +++++++++++++++++++++++++++++++++
 1 files changed, 358 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/filesystems/ubifsec.txt

diff --git a/Documentation/filesystems/ubifsec.txt b/Documentation/filesystems/ubifsec.txt
new file mode 100644
index 0000000..4eb41fb
--- /dev/null
+++ b/Documentation/filesystems/ubifsec.txt
@@ -0,0 +1,357 @@
+UBIFS Secure Deletion Enhancement
+
+Written by Joel Reardon <reardonj@inf.ethz.ch>
+Last revised: 19.3.2012
+
+Introduction
+============
+UBIFSec provides efficient secure deletion for the flash file system UBIFS.
+Trivial secure deletion by overwriting the deleted data does not work for
+flash memory, as there is a large difference between the size of the I/O unit
+(page) and the erasure unit (erase block). UBIFSec encrypts each data node
+with a distinct key and stores the keys colocated in a key storage area (KSA).
+Secure deletion is achieved by atomically updating the (small) set of erase
+blocks that constitute the KSA to remove keys corresponding to deleted data,
+thereby deleting the data nodes they encrypted.
+
+Key Storage Area (KSA)
+======================
+UBIFSec uses a small migrating set of erase blocks to store all the data
+node's keys---this set is called the Key Storage Area (KSA). The KSA is
+managed separately from the rest of the file system. In particular, it does
+not behave like a log-structured file system: when a KSA erase block is
+updated, its contents are written to a new erase block, the logical reference
+to the KSA block is updated, and the previous version of the KSA erase block
+is then  erased. Thus, except while updating, only one copy of the data in the
+KSA is available on the storage medium.  When the file system is created,
+cryptographically-suitable random data is written from random_bytes() to each
+of the KSA's LEBs and all the keys are marked as unused.  Purging writes new
+versions of the KSA LEBs using UBI's atomic update feature.
+
+Each data node's header stores the logical KSA position that contains its
+decryption key. The erase blocks in the KSA are periodically erased to
+securely delete any keys that decrypt deleted data. When the file system no
+longer needs a data node---i.e, it is removed or updated---we mark the data
+node's corresponding key in the KSA as deleted.  This is independent of the
+notion of files; keys are marked as deleted whenever a data node is discarded.
+A key remains marked as deleted until it is removed from the storage medium
+and its location is replaced with fresh, unused random data, which is then
+marked as unused.
+
+When a new data node is written to the storage medium, an unused key is
+selected from the KSA and its position is written to the data node's header.
+The keys are in a protected area of the file system, so only users with root
+access to the storage medium are capable of reading the keys that encrypt
+data.
+
+Purging
+=======
+Purging is a periodic procedure that securely deletes keys from the KSA.
+Purging proceeds iteratively over each of the KSA's erase blocks: a new
+version of the erase block is prepared where the used keys remain in the same
+position and all other keys (i.e., unused and deleted keys) are replaced with
+fresh, unused, cryptographically-appropriate random data from a source of
+hardware randomness. This fresh random data is then assigned to new keys as
+needed. We keep used keys logically-fixed because their corresponding data
+node has already written its logical position. The new version of the block is
+then written to an arbitrary empty erase block on the storage medium.  After
+completion, the erase block containing the old version is erased, thus
+securely deleting the unused and deleted keys along with the data nodes they
+encrypt.
+
+If a KSA erase block becomes a bad block while erasing it, it is possible that
+its contents will remain readable on the storage medium without the ability to
+remove them. In this case, it is necessary to re-encrypt any data node whose
+encryption key remains available and force the garbage collection of those
+erase blocks on which the data nodes reside.
+
+Key State Map
+=============
+The key state map is an in-memory map that maps key positions to key states
+{unused, used, deleted}. Unused keys can be assigned and then marked used.
+Used keys are keys that encrypt some valid data node, so they must be
+preserved to ensure availability of the file system's data. Deleted keys are
+keys used to encrypt deleted data---i.e., data nodes that are no longer
+referenced by the index---and should be purged from the system to achieve
+secure deletion.
+
+A correct key state map is one that has the following three properties:
+1. every unused key must not decrypt any data node---either valid or invalid
+2. every used key must have exactly one data node it can decrypt and this data
+node must be valid according to the index
+3. every deleted key must not decrypt any data node that is valid according to
+the index.
+
+The operation of purging performed on a correct key state map guarantees
+DNEFS's soundness: purging securely deletes any key in the KSA marked as
+deleted---afterwards, every key either decrypts one valid data node or nothing
+at all and every valid data node can be decrypted.  A correct key state map
+also guarantees the integrity of our data during purging, because no key that
+is used to decrypt valid data will be removed.
+
+The key state map is stored, used, and updated in volatile memory. Initially,
+the key state map of a freshly-formatted UBIFSec file system is correct as it
+consists of no data nodes, and every key is fresh random data that is marked
+as unused. While mounted, UBIFSec performs appropriate key management to
+ensure that the key state map is always correct when new data is written,
+deleted, etc. We now show that we can always create a correct key state map
+when mounting an arbitrary UBIFSec file system.
+
+The key state map is built from a periodic checkpoint combined with a replay
+of the most recent changes while mounting.  We checkpoint the current key
+state map to the storage medium whenever the KSA is purged. After a purge,
+every key is either unused or used, and so a checkpoint of this map can be
+stored using one bit per key---less than 1\% of the KSA's size---which is then
+compressed.  A special LEB is used to store checkpoints, where each new
+checkpoint is appended; when the erase block is full then the next checkpoint
+is written at the beginning using an atomic update.
+
+Correctness of the Key State Map
+================================
+As an invariant, we require that UBIFSec's key state map is always correct
+before and after executing a purge. This restriction---instead of requiring
+correctness at all times after mounting---is to allow writing new data during
+a purging operation, and to account for the time between marking a key as used
+and writing the data it encrypts onto the storage medium.
+
+The checkpoint is correct when it is written to the storage medium, and
+therefore it is correct when it is loaded during mounting if no other changes
+occurred to the file system. If the file system changed after committing and
+before unmounting, then UBIFS's replay mechanism is used to generate the
+correct key state map: first the checkpoint is loaded, then the replay entries
+are simulated. Therefore, we always perform purging during regular UBIFS
+commits; the nodes that are replayed for UBIFS are exactly the ones that must
+be replayed for UBIFSec. If the stored checkpoint gets corrupted, then a full
+scan of the valid data nodes can rebuild the correct key state map.
+
+As it is possible for the storage medium to fail during the commit operation
+(e.g., due to a  loss of power), we now show that our invariant holds
+regardless of the condition of unmounting.  Purging consists of atomically
+updating each LEB containing deleted keys and afterwards writing a new
+checkpoint. UBI's atomic update feature ensures that any failure before
+completing the update is equivalent to failing immediately before beginning.
+Therefore, the following is the complete list of possible failure points:
+1. before the first purge.
+2. between some purges.
+3. after all the purges but before the checkpoint.
+4. during the checkpoint.
+5. after the checkpoint but before finishing other UBIFS commit actions.
+
+We now show that we can construct a correct key state map in all these
+situations.
+
+First, failure can occur before purging the first LEB, which means the KSA is
+unchanged.  When remounting the device, the loaded checkpoint is updated  with
+the replay data, thereby constructing the exact key state map before
+purging---taken as correct by assumption.
+
+Second, failure can occur after purging one, several, or indeed  all of the
+KSA's LEBs. When remounting the device, the loaded checkpoint merged with the
+replay data  reflects the state before the first purge, so some purged LEBs
+contain new unused data while the key state map claims it is a deleted key. As
+these are cryptographically-suitable random values, with high probability they
+cannot successfully decrypt any existing valid data node.
+
+Third, failure can occur while writing to the checkpoint LEB.  When the
+checkpoint is written using atomic updates, then failing during the operation
+is equivalent to failing before it begins (cf. 2nd case).  Incomplete
+checkpoints are detected and so the previous valid checkpoint is loaded
+instead.  After replaying all the nodes, the key state map is equal to its
+state immediately before purging the KSA. This means that all entries marked
+as deleted are actually unused entries, so the invariant holds.
+
+Finally, failure can occur after successfully purging the KSA and
+checkpointing the key state map, but before completing the regular UBIFS
+commit.  In this case, the current key state map correctly reflects the
+contents of the KSA. When mounting, the replay mechanism incorrectly updates
+it with the journal entries of the previous iteration.  Table 1 shows the full
+space of possibilities when replaying old changes on the post-purged
+checkpoint. It shows that it is only possible for an unused key to be
+erroneously marked as deleted, which still results in a correct key state map.
+
++--------+--------------+--------+---------+-----------------------+---------+
+|old ckpt|   replay's   | ckpt   | value   |         cause         |  key's  |
+| value  |    effect    | value  | after   |                       |  state  |
+|        |              |        | recvry  |                       |         |
++--------+--------------+--------+---------+-----------------------+---------+
+| unused |    nothing   | unused | unused  |        no event       | correct |
+| unused |   mark used  |  used  |  used   |      key assigned     | correct |
+| unused | mark deleted | unused | deleted | key assigned, deleted | correct |
+|  used  |    nothing   |  used  |  used   |        no event       | correct |
+|  used  |  mark used   |  used  |  used   |      cannot occur     | correct |
+|  used  | mark deleted | unused | deleted |      key deleted      | correct |
++--------+--------------+--------+---------+-----------------------+---------+
+Table 1: Consequences of replaying false information during committing.
+
+Adding Encryption in Compression
+================================
+Currently, compression (respectively decompression) is applied to all data
+before (respectively after) storage medium operations.  We modify the compress
+and decompress functions to take an optional key parameter. If no key is
+provided, then the functions behave normally. If a key is provided, then the
+data is encrypted before compression (respectively decrypted after
+compression) using the provided key as an AES key in counter mode.
+
+Implementing the Keymap
+=======================
+The keymap data structure and its algorithms constitute the majority of
+UBIFSec's changes. The keymap  maintains KSA information and caches, along
+with the key state map, the current position for assigning fresh keys, and a
+buffer for atomic updates and checkpoint preparation.
+
+The KSA is divided into discrete KSA ranges, which are ranked in terms of
+expected data lifetime. Generational garbage collection is heuristically used
+to promote longer-lived data to long-term ranges of the KSA. The goal is to
+reduce the number of LEBs that need to be erased during purging by having data
+that remains on the device reside elsewhere, which reduces the time required
+to purge large storage media.  KSA LEBs that are skipped during a purge have
+their unused keys marked as nonassignable until the LEB is finally replaced
+with fresh, random data.
+
+Each time UBIFS garbage collects a data node, it may be promoted to a
+longer-term area of the KSA. This requires decrypting the stored data,
+encrypting with a new key, and updating the data node's key location and
+checksum. However, it does not incur additional cost to read or write the
+data, as it is only optionally  performed during regular UBIFS garbage
+collection when the data node is copied to a new location.
+
+The key state map is a two-dimensional array indexed first by the relative LEB
+number and then by the offset in the LEB. Key positions, stored in
+_crypto_lookup_ variables, are stored as 64-bit numbers where the first 32
+bits encode the relative LEB number and the next 32 bits encode the offset.  A
+key position's state can be changed to used or deleted by external functions,
+but only the keymap's purging function marks the state as unused. A lock
+synchronizes all access to the key state map. The function to get a free key
+position atomically searches for  an unused key position, marks the entry as
+used, and optionally returns the key.
+
+The keymap structure maintains an LEB-sized buffer for atomic updates.
+Purging the keymap proceeds iteratively for each of the KSA's LEBs. It reads
+the old version of a block into that buffer, performs the update, and provides
+it to UBI's atomic update function. Random data is taken from Linux kernel's
+hardware randomness source that is cryptographically suitable.  Purging is
+synchronized by a lock to prevent a second purge before the first has
+completed.
+
+The keymap structure also maintains a buffer for checkpoint preparation. After
+committing, the checkpoint is created  using one bit for each entry indicating
+if the key position is unused or used.  The checkpoint is then compressed,
+prefixed with the compressed size, and suffixed with the magic number.
+
+The external interface of the keymap consists of the following:
+keymap_purge() - purge the deleted keys, write a new checkpoint
+keymap_init() - initialization
+keymap_free() - deallocate memory
+keymap_free_key() - find and return an unused key
+keymap_read_key() - convert a key location to a key
+keymap_mark_used() - mark a key location as used
+keymap_mark_deleted() - mark a key location as deleted
+keymap_assign_lebs() - used during initialization to tell the keymap which
+		       LEBS are used for keys
+keymap_keys_per_eb() - returns the number of keys that fit on an erase block
+keymap_gc_should_promote() - returns true if we should reencrypt the data node
+			     during garbage collection
+keymap_swap_encryption_key() - performs the re-encryption of a data node during
+			       garbage collection
+
+Summary of Changes to UBIFS Components
+======================================
+
+Mounting.
+
+mounting the file system:
+- allocate and initialize the keymap
+- deallocate keymap if an error occurs
+- read the size of the KSA from the master node
+
+unmounting the file system:
+- deallocate the keymap
+
+creating default file system:
+- use storage medium's geometry to compute the required KSA size
+- store the size in the master node
+- call keymap's initialize KSA routine
+
+Commit.
+
+committing the journal:
+- call the keymap's purging routine
+
+Input/Output.
+
+writing data:
+- obtain an unused key position from the keymap
+- store the key's position in the data node's header
+- use the keymap and key position to look up the actual key
+- provide the key to the compress function
+
+recomputing last data node after truncation:
+- obtain the original key, decrypt the data
+- obtain a new key, encrypt the data with it after truncating
+
+reading data:
+- use the keymap and data node's key position to look up the actual key
+- provide the key to the decompress function
+
+Tree Node Cache.
+
+adding/updating the TNC:
+- provide a key position when adding data nodes
+- store the key position inside TNC entries
+- mark key position as used
+- if also updating, mark the old key position as deleted before storing the
+  new position.
+
+deleting/truncating the TNC:
+- when removing a data node from the TNC, mark the stored key position as
+  deleted
+
+committing the TNC:
+- read and write key position to stored tree nodes
+
+Garbage Collection.
+
+copying a data node to a new location:
+- decide whether to promote data nodes
+- re-encrypt promoted data node
+- mark old key is deleted, new key as used
+
+Future Extensions
+=================
+
+Encrypting metadata.
+
+UBIFSec securely deletes file content, but not a file's name and other
+metadata.  Some encrypted file systems, such as ecryptfs, encrypt this
+metadata along with the file data. Our implementation leverages the
+compression functionality of UBIFS to seamlessly add cryptographic operations.
+However, there is no technical reason that prohibits assigning an encryption
+key to non-data nodes (such as the index) and storing them encrypted on the
+storage medium.
+
+Purging Policies.
+
+Purging is currently performed after a user-controlled period of time and
+before unmounting the device. More elaborate policies could exist, where
+purging occurs once a threshold of deleted keys is passed, ensuring that the
+amount of exposable data is limited, so the deletion of many files would thus
+act as a trigger for purging.  An ioctl can be offered to allow user-level
+applications to trigger a purge, such as an email application that purges the
+file system after clearing the cache. We can alternatively use a new extended
+attribute to act a trigger: whenever any data node belonging to a sensitive
+file is deleted, then UBIFSec triggers an immediate purge. This allows users
+to have confidence that most files are periodically deleted, while sensitive
+files are promptly deleted.
+
+Password-protected Encrypted File System.
+
+UBIFSec can be trivially extended to offer a passphrase-protected encrypted
+file system: we simply encrypt the KSA whenever we write random data, and
+derive the decryption key from a provided passphrase when mounting. Since,
+with high probability, each randomly-generated key in the KSA is unique, we
+can use a block cipher in electronic codebook mode to allow rapid decryption
+of randomly accessed  offsets without storing additional initialization
+vectors. Such a scheme provides all the benefits of UBIFSec along with the
+additional guarantee that a non-coercive attacker (i.e., theft or repurposing)
+is unable to access any data stored on the device---provided the master secret
+does not reside in volatile memory at the time of the attack.
-- 
1.7.0.4




^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [patch] Move CRC computation to separate function
  2012-02-29 16:10   ` Artem Bityutskiy
@ 2012-03-19 22:46     ` Joel Reardon
  2012-03-23 14:09       ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-19 22:46 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-kernel, linux-fsdevel

This patch moves the computation of CRCs for data nodes from
within ubifs_prepare_node to a separate function ubifs_set_datanode_crc,
which takes a data node, and computes and sets the CRC. This is to avoid
duplication of the CRC computation code in other places where it may be
needed.


Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>

---
 fs/ubifs/io.c    |   16 +++++++++++++---
 fs/ubifs/ubifs.h |    1 +
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/fs/ubifs/io.c b/fs/ubifs/io.c
index 9228950..9d39b27 100644
--- a/fs/ubifs/io.c
+++ b/fs/ubifs/io.c
@@ -367,6 +367,18 @@ static unsigned long long next_sqnum(struct ubifs_info *c)
 }

 /**
+ * ubifs_set_datanode_crc - writes the crc for a data node to the common
+ * header.
+ * @node: the data node
+ */
+void ubifs_set_datanode_crc(void *node)
+{
+	struct ubifs_ch *ch = (struct ubifs_ch *) node;
+	int len = le32_to_cpu(ch->len);
+	ch->crc = cpu_to_le32(crc32(UBIFS_CRC32_INIT, node + 8, len - 8));
+}
+
+/**
  * ubifs_prepare_node - prepare node to be written to flash.
  * @c: UBIFS file-system description object
  * @node: the node to pad
@@ -379,7 +391,6 @@ static unsigned long long next_sqnum(struct ubifs_info *c)
  */
 void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
 {
-	uint32_t crc;
 	struct ubifs_ch *ch = node;
 	unsigned long long sqnum = next_sqnum(c);

@@ -390,8 +401,7 @@ void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
 	ch->group_type = UBIFS_NO_NODE_GROUP;
 	ch->sqnum = cpu_to_le64(sqnum);
 	ch->padding[0] = ch->padding[1] = 0;
-	crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
-	ch->crc = cpu_to_le32(crc);
+	ubifs_set_datanode_crc(node);

 	if (pad) {
 		len = ALIGN(len, 8);
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 7c343e1..bfd0798 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1486,6 +1486,7 @@ int ubifs_write_node(struct ubifs_info *c, void *node, int len, int lnum,
 		     int offs, int dtype);
 int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
 		     int offs, int quiet, int must_chk_crc);
+void ubifs_set_datanode_crc(void *node);
 void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad);
 void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last);
 int ubifs_io_init(struct ubifs_info *c);
-- 
1.7.0.4




^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [patch] Add design document for UBIFS secure deletion
  2012-03-19 16:54         ` [patch] Add design document for UBIFS secure deletion Joel Reardon
@ 2012-03-20 20:10           ` Randy Dunlap
  2012-03-21 13:26             ` Joel Reardon
  2012-03-21 16:10           ` Artem Bityutskiy
  1 sibling, 1 reply; 55+ messages in thread
From: Randy Dunlap @ 2012-03-20 20:10 UTC (permalink / raw)
  To: Joel Reardon; +Cc: Artem Bityutskiy, linux-mtd, linux-fsdevel, linux-kernel

On 03/19/2012 09:54 AM, Joel Reardon wrote:


Nice job overall.  I have just a few comments below.


> Design document should be self explanatory.
> 
> Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
> 
> ---
>  Documentation/filesystems/ubifsec.txt |  358 +++++++++++++++++++++++++++++++++
>  1 files changed, 358 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/filesystems/ubifsec.txt
> 
> diff --git a/Documentation/filesystems/ubifsec.txt b/Documentation/filesystems/ubifsec.txt
> new file mode 100644
> index 0000000..4eb41fb
> --- /dev/null
> +++ b/Documentation/filesystems/ubifsec.txt
> @@ -0,0 +1,357 @@
> +UBIFS Secure Deletion Enhancement
> +
> +Written by Joel Reardon <reardonj@inf.ethz.ch>
> +Last revised: 19.3.2012
> +
> +Introduction
> +============





...

> +Key State Map
> +=============



...

> +The operation of purging performed on a correct key state map guarantees
> +DNEFS's soundness: purging securely deletes any key in the KSA marked as




What is DNEFS?

> +deleted---afterwards, every key either decrypts one valid data node or nothing
> +at all and every valid data node can be decrypted.  A correct key state map
> +also guarantees the integrity of our data during purging, because no key that
> +is used to decrypt valid data will be removed.
> +




...

> +
> +The key state map is built from a periodic checkpoint combined with a replay
> +of the most recent changes while mounting.  We checkpoint the current key
> +state map to the storage medium whenever the KSA is purged. After a purge,
> +every key is either unused or used, and so a checkpoint of this map can be
> +stored using one bit per key---less than 1\% of the KSA's size---which is then




drop '\' ?

> +compressed.  A special LEB is used to store checkpoints, where each new




What is LEB?

> +checkpoint is appended; when the erase block is full then the next checkpoint
> +is written at the beginning using an atomic update.
> +
> +Correctness of the Key State Map
> +================================




...

> +Second, failure can occur after purging one, several, or indeed  all of the
> +KSA's LEBs. When remounting the device, the loaded checkpoint merged with the
> +replay data  reflects the state before the first purge, so some purged LEBs
> +contain new unused data while the key state map claims it is a deleted key. As
> +these are cryptographically-suitable random values, with high probability they
> +cannot successfully decrypt any existing valid data node.




Last sentence seems to be incomplete or just odd.

> +
> +Third, failure can occur while writing to the checkpoint LEB.  When the
> +checkpoint is written using atomic updates, then failing during the operation
> +is equivalent to failing before it begins (cf. 2nd case).  Incomplete




s/cf./compare/
No need to save the space and lots of people probably won't know what
cf. is.

> +checkpoints are detected and so the previous valid checkpoint is loaded
> +instead.  After replaying all the nodes, the key state map is equal to its
> +state immediately before purging the KSA. This means that all entries marked
> +as deleted are actually unused entries, so the invariant holds.



-- 
~Randy

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Add design document for UBIFS secure deletion
  2012-03-20 20:10           ` Randy Dunlap
@ 2012-03-21 13:26             ` Joel Reardon
  2012-03-21 16:20               ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-21 13:26 UTC (permalink / raw)
  To: Randy Dunlap; +Cc: Artem Bityutskiy, linux-mtd, linux-fsdevel, linux-kernel

Thanks for giving it a readover and finding a few errant remnants of the
academic version of this design doc. With your suggestions, the update
follows.

cheers,
Joel Reardon

p.s., I'm new to this git; git format-patch gives me one patch per commit,
which is a pity for a commit that e.g, fixes a typo. Format-patch
doesn't obviously report how to produce unified patches. Creating a new
branch, applying the patches, committing, and generating a new patch seems
too much overkill but I've seen it proposed. Simpler way?

---
 Documentation/filesystems/ubifsec.txt |   23 ++++++++++++++---------
 1 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/Documentation/filesystems/ubifsec.txt b/Documentation/filesystems/ubifsec.txt
index 4eb41fb..175f13d 100644
--- a/Documentation/filesystems/ubifsec.txt
+++ b/Documentation/filesystems/ubifsec.txt
@@ -10,9 +10,16 @@ Trivial secure deletion by overwriting the deleted data does not work for
 flash memory, as there is a large difference between the size of the I/O unit
 (page) and the erasure unit (erase block). UBIFSec encrypts each data node
 with a distinct key and stores the keys colocated in a key storage area (KSA).
-Secure deletion is achieved by atomically updating the (small) set of erase
-blocks that constitute the KSA to remove keys corresponding to deleted data,
-thereby deleting the data nodes they encrypted.
+Secure deletion is achieved by updating the (small) set of erase blocks that
+constitute the KSA to remove keys corresponding to deleted data, thereby
+deleting the data nodes they encrypted. The additional wear due to flash
+erasure is small, only the erase blocks containing the keys, and the operation
+of removing old keys---called purging---is done periodically so the actual
+increase in wear is controled by the user. Moreover, the use of UBI's logical
+erase block (LEB) interface means that the additional wear is evenly spread
+over the flash memory and the new version of a LEB containing the keys can be
+written using UBI's atomic update proceedure to ensure no keys are lost during
+an update.

 Key Storage Area (KSA)
 ======================
@@ -83,7 +90,7 @@ node must be valid according to the index
 the index.

 The operation of purging performed on a correct key state map guarantees
-DNEFS's soundness: purging securely deletes any key in the KSA marked as
+soundness: purging securely deletes any key in the KSA marked as
 deleted---afterwards, every key either decrypts one valid data node or nothing
 at all and every valid data node can be decrypted.  A correct key state map
 also guarantees the integrity of our data during purging, because no key that
@@ -101,7 +108,7 @@ The key state map is built from a periodic checkpoint combined with a replay
 of the most recent changes while mounting.  We checkpoint the current key
 state map to the storage medium whenever the KSA is purged. After a purge,
 every key is either unused or used, and so a checkpoint of this map can be
-stored using one bit per key---less than 1\% of the KSA's size---which is then
+stored using one bit per key---less than 1% of the KSA's size---which is then
 compressed.  A special LEB is used to store checkpoints, where each new
 checkpoint is appended; when the erase block is full then the next checkpoint
 is written at the beginning using an atomic update.
@@ -148,13 +155,11 @@ purging---taken as correct by assumption.
 Second, failure can occur after purging one, several, or indeed  all of the
 KSA's LEBs. When remounting the device, the loaded checkpoint merged with the
 replay data  reflects the state before the first purge, so some purged LEBs
-contain new unused data while the key state map claims it is a deleted key. As
-these are cryptographically-suitable random values, with high probability they
-cannot successfully decrypt any existing valid data node.
+contain new unused data while the key state map claims it is a deleted key.

 Third, failure can occur while writing to the checkpoint LEB.  When the
 checkpoint is written using atomic updates, then failing during the operation
-is equivalent to failing before it begins (cf. 2nd case).  Incomplete
+is equivalent to failing before it begins (compare the 2nd case).  Incomplete
 checkpoints are detected and so the previous valid checkpoint is loaded
 instead.  After replaying all the nodes, the key state map is equal to its
 state immediately before purging the KSA. This means that all entries marked
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [patch] Add design document for UBIFS secure deletion
  2012-03-19 16:54         ` [patch] Add design document for UBIFS secure deletion Joel Reardon
  2012-03-20 20:10           ` Randy Dunlap
@ 2012-03-21 16:10           ` Artem Bityutskiy
  2012-03-23 13:50             ` Joel Reardon
  1 sibling, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-21 16:10 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2386 bytes --]

On Mon, 2012-03-19 at 17:54 +0100, Joel Reardon wrote:
> Design document should be self explanatory.
> 
> Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
> 
> ---
>  Documentation/filesystems/ubifsec.txt |  358 +++++++++++++++++++++++++++++++++
>  1 files changed, 358 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/filesystems/ubifsec.txt
> 
> diff --git a/Documentation/filesystems/ubifsec.txt b/Documentation/filesystems/ubifsec.txt
> new file mode 100644
> index 0000000..4eb41fb
> --- /dev/null
> +++ b/Documentation/filesystems/ubifsec.txt
> @@ -0,0 +1,357 @@
> +UBIFS Secure Deletion Enhancement
> +
> +Written by Joel Reardon <reardonj@inf.ethz.ch>
> +Last revised: 19.3.2012
> +
> +Introduction
> +============
> +UBIFSec provides efficient secure deletion for the flash file system UBIFS.
> +Trivial secure deletion by overwriting the deleted data does not work for
> +flash memory, as there is a large difference between the size of the I/O unit
> +(page) and the erasure unit (erase block).

I think for correctness you should use term "LEB" everywhere, not
"eraseblock".

>  UBIFSec encrypts each data node
> +with a distinct key and stores the keys colocated in a key storage area (KSA).
> +Secure deletion is achieved by atomically updating the (small) set of erase
> +blocks that constitute the KSA to remove keys corresponding to deleted data,
> +thereby deleting the data nodes they encrypted.
> +
> +Key Storage Area (KSA)
> +======================
> +UBIFSec uses a small migrating set of erase blocks to store all the data

"Migrating" set? To me it sounds like the KSA area changes the position
withing the UBI volume. I'd suggest to remove word "migrating".

> +node's keys---this set is called the Key Storage Area (KSA). The KSA is
> +managed separately from the rest of the file system. In particular, it does
> +not behave like a log-structured file system: when a KSA erase block is
> +updated, its contents are written to a new erase block

s/to a new erase block/to a new KSA LEB/ ?

> , the logical reference
> +to the KSA block is updated, and the previous version of the KSA erase block

s/KSA block/KSA LEB/ ?

Also, it is not clear what is the "logical reference" - would be nice to
probably introduce this notion before using it.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Add design document for UBIFS secure deletion
  2012-03-21 13:26             ` Joel Reardon
@ 2012-03-21 16:20               ` Artem Bityutskiy
  0 siblings, 0 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-21 16:20 UTC (permalink / raw)
  To: Joel Reardon; +Cc: Randy Dunlap, linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 659 bytes --]

On Wed, 2012-03-21 at 14:26 +0100, Joel Reardon wrote:
> p.s., I'm new to this git; git format-patch gives me one patch per commit,
> which is a pity for a commit that e.g, fixes a typo. Format-patch
> doesn't obviously report how to produce unified patches. Creating a new
> branch, applying the patches, committing, and generating a new patch seems
> too much overkill but I've seen it proposed. Simpler way?

The --amend option of git commit allows you to update the current HEAD
commit. I could not really understand which other questions you ask, but
if you have a specific question - I may try to help.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [patch] Add design document for UBIFS secure deletion
  2012-03-21 16:10           ` Artem Bityutskiy
@ 2012-03-23 13:50             ` Joel Reardon
  2012-03-23 15:38               ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-23 13:50 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel

Updated design doc with comments.

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
---
 Documentation/filesystems/ubifsec.txt |  362 +++++++++++++++++++++++++++++++++
 1 files changed, 362 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/filesystems/ubifsec.txt

diff --git a/Documentation/filesystems/ubifsec.txt b/Documentation/filesystems/ubifsec.txt
new file mode 100644
index 0000000..ae53791
--- /dev/null
+++ b/Documentation/filesystems/ubifsec.txt
@@ -0,0 +1,361 @@
+UBIFS Secure Deletion Enhancement
+
+Written by Joel Reardon <reardonj@inf.ethz.ch>
+Last revised: 19.3.2012
+
+Introduction
+============
+UBIFSec provides efficient secure deletion for the flash file system UBIFS.
+Trivial secure deletion by overwriting the deleted data does not work for
+UBI-accessed flash memory, as there is a large difference between the size of
+the I/O unit (page) and the erasure unit (logical erase block or LEB).
+UBIFSec encrypts each data node with a distinct key and stores the keys
+colocated in a key storage area (KSA).  Secure deletion is achieved by
+updating the (small) set of LEBs that constitute the KSA to remove keys
+corresponding to deleted data, thereby deleting the data nodes they encrypted.
+The additional wear due to flash erasure is small, only the LEBs containing
+the keys, and the operation of removing old keys---called purging---is done
+periodically so the actual increase in wear is controlled by the user.
+Moreover, the use of UBI's logical interface means that the additional wear is
+evenly spread over the flash memory and the new version of a LEB containing
+the keys can be written using UBI's atomic update proceedure to ensure no keys
+are lost during an update.
+
+Key Storage Area (KSA)
+======================
+UBIFSec uses a small set of LEBs to store all the data node's keys---this set
+is called the Key Storage Area (KSA). The KSA is managed separately from the
+rest of the file system. In particular, it does not behave like a
+log-structured file system: when a KSA LEB is updated, its contents are
+written to a new physical location on the flash memory, UBI's logical map is
+then updated to this new physical address and the previous version of the KSA
+LEB is then erased.  Thus, except while updating the KSA, only one copy of the
+data in the KSA is available on the storage medium.  When the file system is
+created, cryptographically-suitable random data is written from random_bytes()
+to each of the KSA's LEBs and all the keys are marked as unused.  Purging
+writes new versions of the KSA LEBs using UBI's atomic update feature.
+
+Each data node's header stores the logical KSA position that contains its
+decryption key. The LEBs in the KSA are periodically erased to securely delete
+any keys that decrypt deleted data. When the file system no longer needs a
+data node---i.e, it is removed or updated---we mark the data node's
+corresponding key in the KSA as deleted.  This is independent of the notion of
+files; keys are marked as deleted whenever a data node is discarded.  A key
+remains marked as deleted until it is removed from the storage medium and its
+location is replaced with fresh, unused random data, which is then marked as
+unused.
+
+When a new data node is written to the storage medium, an unused key is
+selected from the KSA and its position is written to the data node's header.
+The keys are in a protected area of the file system, so only users with root
+access to the storage medium are capable of reading the keys that encrypt
+data.
+
+Purging
+=======
+Purging is a periodic procedure that securely deletes keys from the KSA.
+Purging proceeds iteratively over each of the KSA's LEBs: a new version of the
+LEB is prepared where the used keys remain in the same position and all other
+keys (i.e., unused and deleted keys) are replaced with fresh, unused,
+cryptographically-appropriate random data from a source of hardware
+randomness. This fresh random data is then assigned to new keys as needed. We
+keep used keys logically-fixed because their corresponding data node has
+already written its logical position. The new version of the LEB is then
+written to an arbitrary empty LEB on the storage medium.  After completion,
+the LEB containing the old version is erased, thus securely deleting the
+unused and deleted keys along with the data nodes they encrypt.
+
+If a KSA LEB becomes a bad block while erasing it, it is possible that its
+contents will remain readable on the storage medium without the ability to
+remove them. In this case, it is necessary to re-encrypt any data node whose
+encryption key remains available and force the garbage collection of those
+LEBs on which the data nodes reside.
+
+Key State Map
+=============
+The key state map is an in-memory map that maps key positions to key states
+{unused, used, deleted}. Unused keys can be assigned and then marked used.
+Used keys are keys that encrypt some valid data node, so they must be
+preserved to ensure availability of the file system's data. Deleted keys are
+keys used to encrypt deleted data---i.e., data nodes that are no longer
+referenced by the index---and should be purged from the system to achieve
+secure deletion.
+
+A correct key state map is one that has the following three properties:
+1. every unused key must not decrypt any data node---either valid or invalid
+2. every used key must have exactly one data node it can decrypt and this data
+node must be valid according to the index
+3. every deleted key must not decrypt any data node that is valid according to
+the index.
+
+The operation of purging performed on a correct key state map guarantees
+soundness: purging securely deletes any key in the KSA marked as
+deleted---afterwards, every key either decrypts one valid data node or nothing
+at all and every valid data node can be decrypted.  A correct key state map
+also guarantees the integrity of our data during purging, because no key that
+is used to decrypt valid data will be removed.
+
+The key state map is stored, used, and updated in volatile memory. Initially,
+the key state map of a freshly-formatted UBIFSec file system is correct as it
+consists of no data nodes, and every key is fresh random data that is marked
+as unused. While mounted, UBIFSec performs appropriate key management to
+ensure that the key state map is always correct when new data is written,
+deleted, etc. We now show that we can always create a correct key state map
+when mounting an arbitrary UBIFSec file system.
+
+The key state map is built from a periodic checkpoint combined with a replay
+of the most recent changes while mounting.  We checkpoint the current key
+state map to the storage medium whenever the KSA is purged. After a purge,
+every key is either unused or used, and so a checkpoint of this map can be
+stored using one bit per key---less than 1% of the KSA's size---which is then
+compressed.  A special LEB is used to store checkpoints, where each new
+checkpoint is appended; when the LEB is full then the next checkpoint is
+written at the beginning using an atomic update.
+
+Correctness of the Key State Map
+================================
+As an invariant, we require that UBIFSec's key state map is always correct
+before and after executing a purge. This restriction---instead of requiring
+correctness at all times after mounting---is to allow writing new data during
+a purging operation, and to account for the time between marking a key as used
+and writing the data it encrypts onto the storage medium.
+
+The checkpoint is correct when it is written to the storage medium, and
+therefore it is correct when it is loaded during mounting if no other changes
+occurred to the file system. If the file system changed after committing and
+before unmounting, then UBIFS's replay mechanism is used to generate the
+correct key state map: first the checkpoint is loaded, then the replay entries
+are simulated. Therefore, we always perform purging during regular UBIFS
+commits; the nodes that are replayed for UBIFS are exactly the ones that must
+be replayed for UBIFSec. If the stored checkpoint gets corrupted, then a full
+scan of the valid data nodes can rebuild the correct key state map.
+
+As it is possible for the storage medium to fail during the commit operation
+(e.g., due to a  loss of power), we now show that our invariant holds
+regardless of the condition of unmounting.  Purging consists of atomically
+updating each LEB containing deleted keys and afterwards writing a new
+checkpoint. UBI's atomic update feature ensures that any failure before
+completing the update is equivalent to failing immediately before beginning.
+Therefore, the following is the complete list of possible failure points:
+1. before the first purge.
+2. between some purges.
+3. after all the purges but before the checkpoint.
+4. during the checkpoint.
+5. after the checkpoint but before finishing other UBIFS commit actions.
+
+We now show that we can construct a correct key state map in all these
+situations.
+
+First, failure can occur before purging the first LEB, which means the KSA is
+unchanged.  When remounting the device, the loaded checkpoint is updated  with
+the replay data, thereby constructing the exact key state map before
+purging---taken as correct by assumption.
+
+Second, failure can occur after purging one, several, or indeed  all of the
+KSA's LEBs. When remounting the device, the loaded checkpoint merged with the
+replay data  reflects the state before the first purge, so some purged LEBs
+contain new unused data while the key state map claims it is a deleted key.
+
+Third, failure can occur while writing to the checkpoint LEB.  When the
+checkpoint is written using atomic updates, then failing during the operation
+is equivalent to failing before it begins (compare the 2nd case).  Incomplete
+checkpoints are detected and so the previous valid checkpoint is loaded
+instead.  After replaying all the nodes, the key state map is equal to its
+state immediately before purging the KSA. This means that all entries marked
+as deleted are actually unused entries, so the invariant holds.
+
+Finally, failure can occur after successfully purging the KSA and
+checkpointing the key state map, but before completing the regular UBIFS
+commit.  In this case, the current key state map correctly reflects the
+contents of the KSA. When mounting, the replay mechanism incorrectly updates
+it with the journal entries of the previous iteration.  Table 1 shows the full
+space of possibilities when replaying old changes on the post-purged
+checkpoint. It shows that it is only possible for an unused key to be
+erroneously marked as deleted, which still results in a correct key state map.
+
++--------+--------------+--------+---------+-----------------------+---------+
+|old ckpt|   replay's   | ckpt   | value   |         cause         |  key's  |
+| value  |    effect    | value  | after   |                       |  state  |
+|        |              |        | recvry  |                       |         |
++--------+--------------+--------+---------+-----------------------+---------+
+| unused |    nothing   | unused | unused  |        no event       | correct |
+| unused |   mark used  |  used  |  used   |      key assigned     | correct |
+| unused | mark deleted | unused | deleted | key assigned, deleted | correct |
+|  used  |    nothing   |  used  |  used   |        no event       | correct |
+|  used  |  mark used   |  used  |  used   |      cannot occur     | correct |
+|  used  | mark deleted | unused | deleted |      key deleted      | correct |
++--------+--------------+--------+---------+-----------------------+---------+
+Table 1: Consequences of replaying false information during committing.
+
+Adding Encryption in Compression
+================================
+Currently, compression (respectively decompression) is applied to all data
+before (respectively after) storage medium operations.  We modify the compress
+and decompress functions to take an optional key parameter. If no key is
+provided, then the functions behave normally. If a key is provided, then the
+data is encrypted before compression (respectively decrypted after
+compression) using the provided key as an AES key in counter mode.
+
+Implementing the Keymap
+=======================
+The keymap data structure and its algorithms constitute the majority of
+UBIFSec's changes. The keymap  maintains KSA information and caches, along
+with the key state map, the current position for assigning fresh keys, and a
+buffer for atomic updates and checkpoint preparation.
+
+The KSA is divided into discrete KSA ranges, which are ranked in terms of
+expected data lifetime. Generational garbage collection is heuristically used
+to promote longer-lived data to long-term ranges of the KSA. The goal is to
+reduce the number of LEBs that need to be erased during purging by having data
+that remains on the device reside elsewhere, which reduces the time required
+to purge large storage media.  KSA LEBs that are skipped during a purge have
+their unused keys marked as nonassignable until the LEB is finally replaced
+with fresh, random data.
+
+Each time UBIFS garbage collects a data node, it may be promoted to a
+longer-term area of the KSA. This requires decrypting the stored data,
+encrypting with a new key, and updating the data node's key location and
+checksum. However, it does not incur additional cost to read or write the
+data, as it is only optionally  performed during regular UBIFS garbage
+collection when the data node is copied to a new location.
+
+The key state map is a two-dimensional array indexed first by the relative LEB
+number and then by the offset in the LEB. Key positions, stored in
+_crypto_lookup_ variables, are stored as 64-bit numbers where the first 32
+bits encode the relative LEB number and the next 32 bits encode the offset.  A
+key position's state can be changed to used or deleted by external functions,
+but only the keymap's purging function marks the state as unused. A lock
+synchronizes all access to the key state map. The function to get a free key
+position atomically searches for  an unused key position, marks the entry as
+used, and optionally returns the key.
+
+The keymap structure maintains an LEB-sized buffer for atomic updates.
+Purging the keymap proceeds iteratively for each of the KSA's LEBs. It reads
+the old version of the LEB into that buffer, performs the update, and provides
+it to UBI's atomic update function. Random data is taken from Linux kernel's
+hardware randomness source that is cryptographically suitable.  Purging is
+synchronized by a lock to prevent a second purge before the first has
+completed.
+
+The keymap structure also maintains a buffer for checkpoint preparation. After
+committing, the checkpoint is created  using one bit for each entry indicating
+if the key position is unused or used.  The checkpoint is then compressed,
+prefixed with the compressed size, and suffixed with the magic number.
+
+The external interface of the keymap consists of the following:
+keymap_purge() - purge the deleted keys, write a new checkpoint
+keymap_init() - initialization
+keymap_free() - deallocate memory
+keymap_free_key() - find and return an unused key
+keymap_read_key() - convert a key location to a key
+keymap_mark_used() - mark a key location as used
+keymap_mark_deleted() - mark a key location as deleted
+keymap_assign_lebs() - used during initialization to tell the keymap which
+		       LEBS are used for keys
+keymap_keys_per_eb() - returns the number of keys that fit on an LEB
+keymap_gc_should_promote() - returns true if we should reencrypt the data node
+			     during garbage collection
+keymap_swap_encryption_key() - performs the re-encryption of a data node during
+			       garbage collection
+
+Summary of Changes to UBIFS Components
+======================================
+
+Mounting.
+
+mounting the file system:
+- allocate and initialize the keymap
+- deallocate keymap if an error occurs
+- read the size of the KSA from the master node
+
+unmounting the file system:
+- deallocate the keymap
+
+creating default file system:
+- use storage medium's geometry to compute the required KSA size
+- store the size in the master node
+- call keymap's initialize KSA routine
+
+Commit.
+
+committing the journal:
+- call the keymap's purging routine
+
+Input/Output.
+
+writing data:
+- obtain an unused key position from the keymap
+- store the key's position in the data node's header
+- use the keymap and key position to look up the actual key
+- provide the key to the compress function
+
+recomputing last data node after truncation:
+- obtain the original key, decrypt the data
+- obtain a new key, encrypt the data with it after truncating
+
+reading data:
+- use the keymap and data node's key position to look up the actual key
+- provide the key to the decompress function
+
+Tree Node Cache.
+
+adding/updating the TNC:
+- provide a key position when adding data nodes
+- store the key position inside TNC entries
+- mark key position as used
+- if also updating, mark the old key position as deleted before storing the
+  new position.
+
+deleting/truncating the TNC:
+- when removing a data node from the TNC, mark the stored key position as
+  deleted
+
+committing the TNC:
+- read and write key position to stored tree nodes
+
+Garbage Collection.
+
+copying a data node to a new location:
+- decide whether to promote data nodes
+- re-encrypt promoted data node
+- mark old key is deleted, new key as used
+
+Future Extensions
+=================
+
+Encrypting metadata.
+
+UBIFSec securely deletes file content, but not a file's name and other
+metadata.  Some encrypted file systems, such as ecryptfs, encrypt this
+metadata along with the file data. Our implementation leverages the
+compression functionality of UBIFS to seamlessly add cryptographic operations.
+However, there is no technical reason that prohibits assigning an encryption
+key to non-data nodes (such as the index) and storing them encrypted on the
+storage medium.
+
+Purging Policies.
+
+Purging is currently performed after a user-controlled period of time and
+before unmounting the device. More elaborate policies could exist, where
+purging occurs once a threshold of deleted keys is passed, ensuring that the
+amount of exposable data is limited, so the deletion of many files would thus
+act as a trigger for purging.  An ioctl can be offered to allow user-level
+applications to trigger a purge, such as an email application that purges the
+file system after clearing the cache. We can alternatively use a new extended
+attribute to act a trigger: whenever any data node belonging to a sensitive
+file is deleted, then UBIFSec triggers an immediate purge. This allows users
+to have confidence that most files are periodically deleted, while sensitive
+files are promptly deleted.
+
+Password-protected Encrypted File System.
+
+UBIFSec can be trivially extended to offer a passphrase-protected encrypted
+file system: we simply encrypt the KSA whenever we write random data, and
+derive the decryption key from a provided passphrase when mounting. Since,
+with high probability, each randomly-generated key in the KSA is unique, we
+can use a block cipher in electronic codebook mode to allow rapid decryption
+of randomly accessed  offsets without storing additional initialization
+vectors. Such a scheme provides all the benefits of UBIFSec along with the
+additional guarantee that a non-coercive attacker (i.e., theft or repurposing)
+is unable to access any data stored on the device---provided the master secret
+does not reside in volatile memory at the time of the attack.
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [patch] Move CRC computation to separate function
  2012-03-19 22:46     ` Joel Reardon
@ 2012-03-23 14:09       ` Artem Bityutskiy
  2012-03-23 16:45         ` Joel Reardon
  0 siblings, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-23 14:09 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-kernel, linux-fsdevel

[-- Attachment #1: Type: text/plain, Size: 1437 bytes --]

On Mon, 2012-03-19 at 23:46 +0100, Joel Reardon wrote:
>  /**
> + * ubifs_set_datanode_crc - writes the crc for a data node to the common
> + * header.
> + * @node: the data node
> + */
> +void ubifs_set_datanode_crc(void *node)
> +{
> +	struct ubifs_ch *ch = (struct ubifs_ch *) node;
> +	int len = le32_to_cpu(ch->len);
> +	ch->crc = cpu_to_le32(crc32(UBIFS_CRC32_INIT, node + 8, len - 8));
> +}

Will this be used by other code, outside of io.c? Because currently
there is only one user in the same file, which does not justify
introducing it and making non-static. If answers are "yes", then it is
better to make it static inline and put to misc.h instead, because it is
very small.

>  void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
>  {
> -	uint32_t crc;
>  	struct ubifs_ch *ch = node;
>  	unsigned long long sqnum = next_sqnum(c);
> 
> @@ -390,8 +401,7 @@ void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
>  	ch->group_type = UBIFS_NO_NODE_GROUP;
>  	ch->sqnum = cpu_to_le64(sqnum);
>  	ch->padding[0] = ch->padding[1] = 0;
> -	crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
> -	ch->crc = cpu_to_le32(crc);
> +	ubifs_set_datanode_crc(node);

But ubifs_prepare_node() is generic and works for any node type, not
just data nodes, which means that using 'datanode' in the name is not a
good idea.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Add design document for UBIFS secure deletion
  2012-03-23 13:50             ` Joel Reardon
@ 2012-03-23 15:38               ` Artem Bityutskiy
  2012-03-23 16:38                 ` Joel Reardon
  0 siblings, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-23 15:38 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 8540 bytes --]

I've pushed this patch to the joel branch, but still have comments. Feel
free to send incremental changes - I'll just squash them in.

On Fri, 2012-03-23 at 14:50 +0100, Joel Reardon wrote:
> +Introduction
> +============
> +UBIFSec provides efficient secure deletion for the flash file system UBIFS.
> +Trivial secure deletion by overwriting the deleted data does not work for
> +UBI-accessed flash memory, as there is a large difference between the size of
> +the I/O unit (page) and the erasure unit (logical erase block or LEB).
> +UBIFSec encrypts each data node with a distinct key and stores the keys
> +colocated in a key storage area (KSA).  Secure deletion is achieved by
> +updating the (small) set of LEBs that constitute the KSA to remove keys
> +corresponding to deleted data, thereby deleting the data nodes they encrypted.
> +The additional wear due to flash erasure is small, only the LEBs containing
> +the keys, and the operation of removing old keys---called purging---is done
> +periodically so the actual increase in wear is controlled by the user.
> +Moreover, the use of UBI's logical interface means that the additional wear is
> +evenly spread over the flash memory and the new version of a LEB containing
> +the keys can be written using UBI's atomic update proceedure to ensure no keys
> +are lost during an update.

How about: s/during an update/in case of a power cut/

> +Key Storage Area (KSA)
> +======================
> +UBIFSec uses a small set of LEBs to store all the data node's keys---this set
> +is called the Key Storage Area (KSA). The KSA is managed separately from the
> +rest of the file system.

> In particular, it does not behave like a
> +log-structured file system: when a KSA LEB is updated, its contents are
> +written to a new physical location on the flash memory, UBI's logical map is
> +then updated to this new physical address and the previous version of the KSA
> +LEB is then erased.  

Am I right that you basically wanted to say that when you update a KSA
LEB, you make sure that the physical flash does not contain the old
contents of this LEB? Would be nice to re-phrase.

Side question - how do you do this?

> Thus, except while updating the KSA, only one copy of the
> +data in the KSA is available on the storage medium.  When the file system is
> +created, cryptographically-suitable random data is written from random_bytes()
> +to each of the KSA's LEBs and all the keys are marked as unused. Purging
> +writes new versions of the KSA LEBs using UBI's atomic update feature.

Just a general question about: I guess you have to call ubi_sync() then
to make sure the old version is actually erased, right?

> +Each data node's header stores the logical KSA position that contains its
> +decryption key. The LEBs in the KSA are periodically erased to securely delete
> +any keys that decrypt deleted data. When the file system no longer needs a
> +data node---i.e, it is removed or updated---we mark the data node's
> +corresponding key in the KSA as deleted.  This is independent of the notion of
> +files; keys are marked as deleted whenever a data node is discarded.  A key
> +remains marked as deleted until it is removed from the storage medium and its
> +location is replaced with fresh, unused random data, which is then marked as
> +unused.

Read this far, and have 2 big questions:

1. How keys are marked as deleted (I guess in some in-memory data
structure)
2. When deleted keys are removed from the medium (probably on commit?)

I guess I'll find the answers below.

> +When a new data node is written to the storage medium, an unused key is
> +selected from the KSA and its position is written to the data node's header.

Questions arises - how the key is selected?

> +The keys are in a protected area of the file system, so only users with root
> +access to the storage medium are capable of reading the keys that encrypt
> +data.

Hmm, what is the protected area? What prevents anyone from reading them
by finding them in /dev/ubiX_Y or /dev/mtdZ ?

> +Purging
> +=======
> +Purging is a periodic procedure that securely deletes keys from the KSA.

I guess you mean deleted keys?

> +Purging proceeds iteratively over each of the KSA's LEBs: a new version of the
> +LEB is prepared where the used keys remain in the same position and all other
> +keys (i.e., unused and deleted keys) are replaced with fresh, unused,
> +cryptographically-appropriate random data from a source of hardware
> +randomness.

Hmm, why is it necessary to re-initialize unused keys?

>  This fresh random data is then assigned to new keys as needed. We
> +keep used keys logically-fixed because their corresponding data node has
> +already written its logical position. The new version of the LEB is then
> +written to an arbitrary empty LEB on the storage medium.  After completion,
> +the LEB containing the old version is erased, thus securely deleting the
> +unused and deleted keys along with the data nodes they encrypt.
> +
> +If a KSA LEB becomes a bad block while erasing it, it is possible that its
> +contents will remain readable on the storage medium without the ability to
> +remove them. In this case, it is necessary to re-encrypt any data node whose
> +encryption key remains available and force the garbage collection of those
> +LEBs on which the data nodes reside.

Good point. UBI will always try to erase and re-write a PEB several
times before marking it as bad, so hopefully the keys will disappear,
but there is no guarantee.

> +Key State Map
> +=============
> +The key state map is an in-memory map that maps key positions to key states
> +{unused, used, deleted}. 

Is it 2 bits per key?

> Unused keys can be assigned and then marked used.
> +Used keys are keys that encrypt some valid data node, so they must be
> +preserved to ensure availability of the file system's data. Deleted keys are
> +keys used to encrypt deleted data---i.e., data nodes that are no longer
> +referenced by the index---and should be purged from the system to achieve
> +secure deletion.
> +
> +A correct key state map is one that has the following three properties:
> +1. every unused key must not decrypt any data node---either valid or invalid
> +2. every used key must have exactly one data node it can decrypt and this data
> +node must be valid according to the index
> +3. every deleted key must not decrypt any data node that is valid according to
> +the index.

I guess you do not enforce these rules, you just rely on the randomness?

> +
> +The operation of purging performed on a correct key state map guarantees
> +soundness: purging securely deletes any key in the KSA marked as
> +deleted---afterwards, every key either decrypts one valid data node or nothing
> +at all and every valid data node can be decrypted.  A correct key state map
> +also guarantees the integrity of our data during purging, because no key that
> +is used to decrypt valid data will be removed.
> +
> +The key state map is stored, used, and updated in volatile memory. Initially,
> +the key state map of a freshly-formatted UBIFSec file system is correct as it
> +consists of no data nodes, and every key is fresh random data that is marked
> +as unused. While mounted, UBIFSec performs appropriate key management to
> +ensure that the key state map is always correct when new data is written,
> +deleted, etc. We now show that we can always create a correct key state map
> +when mounting an arbitrary UBIFSec file system.

You'd also need to teach mkfs.ubifs to write correct KSA.

> +The key state map is built from a periodic checkpoint combined with a replay
> +of the most recent changes while mounting.  We checkpoint the current key
> +state map to the storage medium whenever the KSA is purged.

, which happens when UBIFS commits?

>  After a purge,
> +every key is either unused or used, and so a checkpoint of this map can be
> +stored using one bit per key---less than 1% of the KSA's size---which is then
> +compressed.  A special LEB is used to store checkpoints, where each new
> +checkpoint is appended; when the LEB is full then the next checkpoint is
> +written at the beginning using an atomic update.

Hmm, interesting how you manage checkpoints. I guess the easiest would
be to have a special inode (not visible for users) and store checkpoints
there?

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Add design document for UBIFS secure deletion
  2012-03-23 15:38               ` Artem Bityutskiy
@ 2012-03-23 16:38                 ` Joel Reardon
  2012-03-26 15:03                   ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-23 16:38 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-fsdevel, linux-kernel

I'll answer some quickly here now while they're fresh in mind and add them
to the doc later.

>
> > In particular, it does not behave like a
> > +log-structured file system: when a KSA LEB is updated, its contents are
> > +written to a new physical location on the flash memory, UBI's logical map is
> > +then updated to this new physical address and the previous version of the KSA
> > +LEB is then erased.
>
> Am I right that you basically wanted to say that when you update a KSA
> LEB, you make sure that the physical flash does not contain the old
> contents of this LEB? Would be nice to re-phrase.
>
> Side question - how do you do this?

Here I'm just spelling out atomic update, so nothing fancy except
leb_change() to replace the keys.

>
> > Thus, except while updating the KSA, only one copy of the
> > +data in the KSA is available on the storage medium.  When the file system is
> > +created, cryptographically-suitable random data is written from random_bytes()
> > +to each of the KSA's LEBs and all the keys are marked as unused. Purging
> > +writes new versions of the KSA LEBs using UBI's atomic update feature.
>
> Just a general question about: I guess you have to call ubi_sync() then
> to make sure the old version is actually erased, right?

There was an ioctl I think I was using, but I suspect ubi_sync is
more elegant. Is it already performed after UBIFS commit?

>
> Read this far, and have 2 big questions:
>
> 1. How keys are marked as deleted (I guess in some in-memory data
> structure)
> 2. When deleted keys are removed from the medium (probably on commit?)

1. Yeah, 2bit for key state
2. During commit, the KSA LEBs are rewritten.

>
> I guess I'll find the answers below.
>
> > +When a new data node is written to the storage medium, an unused key is
> > +selected from the KSA and its position is written to the data node's header.
>
> Questions arises - how the key is selected?

Assigns the next unused key and keeps track of where it was to search
onwards.

>
> > +The keys are in a protected area of the file system, so only users with root
> > +access to the storage medium are capable of reading the keys that encrypt
> > +data.
>
> Hmm, what is the protected area? What prevents anyone from reading them
> by finding them in /dev/ubiX_Y or /dev/mtdZ ?

Protected as in the kernel stops joe user from reading raw from /dev/mtd,
no? Of course anyone with physical access can get the keys, so the
security relys on these keys being securely deleted, but anyhow keeping
live keys out of the filesystem also helps.

>
> Hmm, why is it necessary to re-initialize unused keys?
>

This can be a mount option actually, it was a response to  attack that was
proposed to me about the design. The idea here is that, suppose the
attacker reads the drive at time X with sufficient privilages to read the
keys. Then the attacker will know for certain that those keys will
eventually be used to encrypt data. So they can compromise not only the
current data, but also future data. By replacing unused keys, the attacker
is limited only to compromizing current data.

>
> Is it 2 bits per key?
>

In development it was 32bits :). But its now properly 2bits per with
bitwise ops for accessing.
>
> I guess you do not enforce these rules, you just rely on the randomness?

Yep, so the conditions need only hold with cryptographically high
probability.

>
> You'd also need to teach mkfs.ubifs to write correct KSA.
>

Ah, yes! Any other tool?

>
> Hmm, interesting how you manage checkpoints. I guess the easiest would
> be to have a special inode (not visible for users) and store checkpoints
> there?
>

The first block of the KSA stores only checkpoints, so KSA size is 1 +
minrequired + some slack. So this may need the multiple LEB
approach for one of the other UBIFS areas when its size is too big, but I
havn't done that since its generally so highly compressable that it
wasn't ever a concern.

Cheers,
Joel Reardon

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Move CRC computation to separate function
  2012-03-23 14:09       ` Artem Bityutskiy
@ 2012-03-23 16:45         ` Joel Reardon
  2012-03-23 16:51           ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-23 16:45 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-kernel, linux-fsdevel

When GCing a data node, it may be reencrypted as a means of organizing the
KSA and reducing the number of LEBs that need to be erased. A Long term
keys in one half for stable data, short term in the other.  During this
reencryption the CRC is recomputed. Also, after truncating it the last
chunk needs to be reencrypted too, or else the key can be used with the
old version to find the truncated part.

For the misc.h approach, it does not currently include ubifs-media.h, but
that is where UBIFS_CRC32_INIT is defined, so move it also to misc.h



On Fri, 23 Mar 2012, Artem Bityutskiy wrote:

> On Mon, 2012-03-19 at 23:46 +0100, Joel Reardon wrote:
> >  /**
> > + * ubifs_set_datanode_crc - writes the crc for a data node to the common
> > + * header.
> > + * @node: the data node
> > + */
> > +void ubifs_set_datanode_crc(void *node)
> > +{
> > +	struct ubifs_ch *ch = (struct ubifs_ch *) node;
> > +	int len = le32_to_cpu(ch->len);
> > +	ch->crc = cpu_to_le32(crc32(UBIFS_CRC32_INIT, node + 8, len - 8));
> > +}
>
> Will this be used by other code, outside of io.c? Because currently
> there is only one user in the same file, which does not justify
> introducing it and making non-static. If answers are "yes", then it is
> better to make it static inline and put to misc.h instead, because it is
> very small.
>
> >  void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
> >  {
> > -	uint32_t crc;
> >  	struct ubifs_ch *ch = node;
> >  	unsigned long long sqnum = next_sqnum(c);
> >
> > @@ -390,8 +401,7 @@ void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
> >  	ch->group_type = UBIFS_NO_NODE_GROUP;
> >  	ch->sqnum = cpu_to_le64(sqnum);
> >  	ch->padding[0] = ch->padding[1] = 0;
> > -	crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
> > -	ch->crc = cpu_to_le32(crc);
> > +	ubifs_set_datanode_crc(node);
>
> But ubifs_prepare_node() is generic and works for any node type, not
> just data nodes, which means that using 'datanode' in the name is not a
> good idea.
>
> --
> Best Regards,
> Artem Bityutskiy
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Move CRC computation to separate function
  2012-03-23 16:45         ` Joel Reardon
@ 2012-03-23 16:51           ` Artem Bityutskiy
  2012-03-25 20:38             ` Joel Reardon
  2012-03-25 21:11             ` [patch] Add a encryption key parameter to the compress / decompress function Joel Reardon
  0 siblings, 2 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-23 16:51 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-kernel, linux-fsdevel

[-- Attachment #1: Type: text/plain, Size: 765 bytes --]

On Fri, 2012-03-23 at 17:45 +0100, Joel Reardon wrote:
> When GCing a data node, it may be reencrypted as a means of organizing the
> KSA and reducing the number of LEBs that need to be erased. A Long term
> keys in one half for stable data, short term in the other.  During this
> reencryption the CRC is recomputed. Also, after truncating it the last
> chunk needs to be reencrypted too, or else the key can be used with the
> old version to find the truncated part.

I guess this means there will be more users for this function.

> For the misc.h approach, it does not currently include ubifs-media.h, but
> that is where UBIFS_CRC32_INIT is defined, so move it also to misc.h

Just include it from misc.h.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Move CRC computation to separate function
  2012-03-23 16:51           ` Artem Bityutskiy
@ 2012-03-25 20:38             ` Joel Reardon
  2012-03-26 15:34               ` Artem Bityutskiy
  2012-03-25 21:11             ` [patch] Add a encryption key parameter to the compress / decompress function Joel Reardon
  1 sibling, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-25 20:38 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-kernel, linux-fsdevel

CRC computation now an inline header function which will be used in
multiple places in the future.

--

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>

 fs/ubifs/io.c   |    4 +---
 fs/ubifs/misc.h |   15 +++++++++++++++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/fs/ubifs/io.c b/fs/ubifs/io.c
index 9228950..103532e 100644
--- a/fs/ubifs/io.c
+++ b/fs/ubifs/io.c
@@ -379,7 +379,6 @@ static unsigned long long next_sqnum(struct ubifs_info *c)
  */
 void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
 {
-	uint32_t crc;
 	struct ubifs_ch *ch = node;
 	unsigned long long sqnum = next_sqnum(c);

@@ -390,8 +389,7 @@ void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
 	ch->group_type = UBIFS_NO_NODE_GROUP;
 	ch->sqnum = cpu_to_le64(sqnum);
 	ch->padding[0] = ch->padding[1] = 0;
-	crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
-	ch->crc = cpu_to_le32(crc);
+	ubifs_set_node_crc(node);

 	if (pad) {
 		len = ALIGN(len, 8);
diff --git a/fs/ubifs/misc.h b/fs/ubifs/misc.h
index ee7cb5e..ffda6a5 100644
--- a/fs/ubifs/misc.h
+++ b/fs/ubifs/misc.h
@@ -27,6 +27,9 @@
 #ifndef __UBIFS_MISC_H__
 #define __UBIFS_MISC_H__

+#include <linux/crc32.h>
+#include "ubifs-media.h"
+
 /**
  * ubifs_zn_dirty - check if znode is dirty.
  * @znode: znode to check
@@ -300,4 +303,16 @@ static inline int ubifs_next_log_lnum(const struct ubifs_info *c, int lnum)
 	return lnum;
 }

+/**
+ * ubifs_set_crc - computes and writes the crc for a ubifs node to the common
+ * header.
+ * @node: the data node
+ */
+static inline void ubifs_set_node_crc(void *node)
+{
+	struct ubifs_ch *ch = (struct ubifs_ch *) node;
+	int len = le32_to_cpu(ch->len);
+	ch->crc = cpu_to_le32(crc32(UBIFS_CRC32_INIT, node + 8, len - 8));
+}
+
 #endif /* __UBIFS_MISC_H__ */
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [patch] Add a encryption key parameter to the compress / decompress function.
  2012-03-23 16:51           ` Artem Bityutskiy
  2012-03-25 20:38             ` Joel Reardon
@ 2012-03-25 21:11             ` Joel Reardon
  2012-03-25 21:38               ` [patch] Add cryptographic functionality when a key is passed to the compress / decompress functions Joel Reardon
  2012-03-27  8:27               ` [patch] Add a encryption key parameter to the compress / decompress function Artem Bityutskiy
  1 sibling, 2 replies; 55+ messages in thread
From: Joel Reardon @ 2012-03-25 21:11 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-kernel, linux-fsdevel

Compress and decompress now have an extra u8 *key field, and NULL is
passed wherever it is used. Later this will be used for an encryption key
for the data being compressed/decompressed (and NULL if no cryptographic operations should be done.)

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>

>From 650a52b5301778cf2f0c17ed993a104a4473a8fb Mon Sep 17 00:00:00 2001
From: Joel Reardon <jreardon@fatbottomsworth.(none)>
Date: Sun, 25 Mar 2012 23:06:03 +0200
Subject: [PATCH] Added a key parameter to de/compress.

---
 fs/ubifs/compress.c |    8 ++++++--
 fs/ubifs/file.c     |    5 +++--
 fs/ubifs/journal.c  |    7 ++++---
 fs/ubifs/ubifs.h    |    4 ++--
 4 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
index 11e4132..d1812fa 100644
--- a/fs/ubifs/compress.c
+++ b/fs/ubifs/compress.c
@@ -82,6 +82,8 @@ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
  * @out_len: output buffer length is returned here
  * @compr_type: type of compression to use on enter, actually used compression
  *              type on exit
+ * @key: a pointer to a key-sized buffer to use as the encryption key. if NULL
+ *      then no encryption is performed.
  *
  * This function compresses input buffer @in_buf of length @in_len and stores
  * the result in the output buffer @out_buf and the resulting length in
@@ -93,7 +95,7 @@ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
  * buffer and %UBIFS_COMPR_NONE is returned in @compr_type.
  */
 void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
-		    int *compr_type)
+		    int *compr_type, u8 *key)
 {
 	int err;
 	struct ubifs_compressor *compr = ubifs_compressors[*compr_type];
@@ -140,13 +142,15 @@ no_compr:
  * @out_buf: output buffer where decompressed data should
  * @out_len: output length is returned here
  * @compr_type: type of compression
+ * @key: a pointer to a key-sized buffer to use as the decryption key. if
+ *	 NULL then no decryption is performed.
  *
  * This function decompresses data from buffer @in_buf into buffer @out_buf.
  * The length of the uncompressed data is returned in @out_len. This functions
  * returns %0 on success or a negative error code on failure.
  */
 int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
-		     int *out_len, int compr_type)
+		     int *out_len, int compr_type, u8 *key)
 {
 	int err;
 	struct ubifs_compressor *compr;
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index f9c234b..2660c9f 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -80,7 +80,7 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,
 	dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
 	out_len = UBIFS_BLOCK_SIZE;
 	err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
-			       le16_to_cpu(dn->compr_type));
+			       le16_to_cpu(dn->compr_type), NULL);
 	if (err || len != out_len)
 		goto dump;

@@ -649,7 +649,8 @@ static int populate_page(struct ubifs_info *c, struct page *page,
 			dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
 			out_len = UBIFS_BLOCK_SIZE;
 			err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
-					       le16_to_cpu(dn->compr_type));
+					       le16_to_cpu(dn->compr_type),
+					       NULL);
 			if (err || len != out_len)
 				goto out_err;

diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 2f438ab..9156395 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -727,7 +727,7 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
 		compr_type = ui->compr_type;

 	out_len = dlen - UBIFS_DATA_NODE_SZ;
-	ubifs_compress(buf, len, &data->data, &out_len, &compr_type);
+	ubifs_compress(buf, len, &data->data, &out_len, &compr_type, NULL);
 	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);

 	dlen = UBIFS_DATA_NODE_SZ + out_len;
@@ -1110,11 +1110,12 @@ static int recomp_data_node(struct ubifs_data_node *dn, int *new_len)

 	len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
 	compr_type = le16_to_cpu(dn->compr_type);
-	err = ubifs_decompress(&dn->data, len, buf, &out_len, compr_type);
+	err = ubifs_decompress(
+		&dn->data, len, buf, &out_len, compr_type, NULL);
 	if (err)
 		goto out;

-	ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type);
+	ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type, NULL);
 	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);
 	dn->compr_type = cpu_to_le16(compr_type);
 	dn->size = cpu_to_le32(*new_len);
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 93d59ac..0cc1180 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1772,9 +1772,9 @@ long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 int __init ubifs_compressors_init(void);
 void ubifs_compressors_exit(void);
 void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
-		    int *compr_type);
+		    int *compr_type, u8 *key);
 int ubifs_decompress(const void *buf, int len, void *out, int *out_len,
-		     int compr_type);
+		     int compr_type, u8 *key);

 #include "debug.h"
 #include "misc.h"
-- 
1.7.1



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [patch] Add cryptographic functionality when a key is passed to the compress / decompress functions
  2012-03-25 21:11             ` [patch] Add a encryption key parameter to the compress / decompress function Joel Reardon
@ 2012-03-25 21:38               ` Joel Reardon
  2012-03-27  8:33                 ` Artem Bityutskiy
  2012-03-27  8:27               ` [patch] Add a encryption key parameter to the compress / decompress function Artem Bityutskiy
  1 sibling, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-25 21:38 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-kernel, linux-fsdevel

This patch adds a function to perform AES encryption. The compress and
decompress routines use this function if they are called with a non-NULL
key parameter. It uses AES counter mode (where encryption and decryption
are the same function) and performs the operation in place on the data. It
uses a default IV of 0, since each key is only evey used to encrypt one
data item the IV does not matter.

The const qualifier was removed from the decompress routine for the
following reason. Encrypted data is not compressable, so compression is
first applied then the result is encrypted. In the reverse, decryption is
first applied and the result decompressed. This means that either the
input buffer for decompression is used to perform an in-place decryption
before decompression, or a third buffer is added and data is copied around.

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
index d1812fa..b738ab0 100644
--- a/fs/ubifs/compress.c
+++ b/fs/ubifs/compress.c
@@ -27,9 +27,12 @@
  * decompression.
  */

-#include <linux/crypto.h>
 #include "ubifs.h"

+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+
+
 /* Fake description object for the "none" compressor */
 static struct ubifs_compressor none_compr = {
 	.compr_type = UBIFS_COMPR_NONE,
@@ -75,6 +78,53 @@ static struct ubifs_compressor zlib_compr = {
 struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];

 /**
+ * ubifs_aes_crypt - encrypt / decrypt data.
+ * @str: data to crypt
+ * @len: length of the data
+ * @key: the key to use to crypt the data
+ * @key_len: the length of the key
+ * @iv: the initialization vector to use
+ * @ivlen: the length of the initialization vector
+ *
+ * This function applies aes encryption to the data. It is done in counter
+ * mode, which means that encryption and decryption are the same operation,
+ * i.e., it XORs the same generated bitstream, so it can be used both for
+ * encryption / decryption. The operation is done in-place, so str mutates.
+ */
+int ubifs_aes_crypt(void *str, int len, u8 *key, int keylen, u8 *iv, int ivlen)
+{
+	struct crypto_blkcipher *tfm;
+	struct blkcipher_desc desc;
+	struct scatterlist sg;
+	int err = 0;
+	tfm = crypto_alloc_blkcipher(UBIFS_CRYPTO_ALGORITHM, 0, 0);
+
+	if (IS_ERR(tfm)) {
+		ubifs_err("failed to load transform for aes: %ld",
+			  PTR_ERR(tfm));
+		return err;
+	}
+
+	err = crypto_blkcipher_setkey(tfm, key, keylen);
+	desc.tfm = tfm;
+	desc.flags = 0;
+	if (err) {
+		ubifs_err("setkey() failed  flags=%x",
+			  crypto_blkcipher_get_flags(tfm));
+	return err;
+	}
+	memset(&sg, 0, sizeof(struct scatterlist));
+
+	sg_set_buf(&sg, str, len);
+	desc.info = iv;
+	err = crypto_blkcipher_encrypt(&desc, &sg, &sg, len);
+	crypto_free_blkcipher(tfm);
+	if (err)
+		return err;
+	return 0;
+}
+
+/**
  * ubifs_compress - compress data.
  * @in_buf: data to compress
  * @in_len: length of the data to compress
@@ -127,12 +177,21 @@ void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
 	if (in_len - *out_len < UBIFS_MIN_COMPRESS_DIFF)
 		goto no_compr;

-	return;
+	goto encrypt;

 no_compr:
 	memcpy(out_buf, in_buf, in_len);
 	*out_len = in_len;
 	*compr_type = UBIFS_COMPR_NONE;
+	goto encrypt;
+
+encrypt:
+	if (key) {
+		u8 iv[UBIFS_CRYPTO_KEYSIZE];
+		memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
+		ubifs_aes_crypt(out_buf, *out_len, key, UBIFS_CRYPTO_KEYSIZE,
+				iv, UBIFS_CRYPTO_KEYSIZE);
+	}
 }

 /**
@@ -149,7 +208,7 @@ no_compr:
  * The length of the uncompressed data is returned in @out_len. This functions
  * returns %0 on success or a negative error code on failure.
  */
-int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
+int ubifs_decompress(void *in_buf, int in_len, void *out_buf,
 		     int *out_len, int compr_type, u8 *key)
 {
 	int err;
@@ -167,6 +226,13 @@ int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
 		return -EINVAL;
 	}

+	if (key) {
+		u8 iv[UBIFS_CRYPTO_KEYSIZE];
+		memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
+		ubifs_aes_crypt(in_buf, in_len, key, UBIFS_CRYPTO_KEYSIZE,
+				iv, UBIFS_CRYPTO_KEYSIZE);
+	}
+
 	if (compr_type == UBIFS_COMPR_NONE) {
 		memcpy(out_buf, in_buf, in_len);
 		*out_len = in_len;
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 0cc1180..923babe 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -160,6 +160,14 @@
 /* Maximum number of data nodes to bulk-read */
 #define UBIFS_MAX_BULK_READ 32

+/* Size of 128 bits in bytes */
+#define AES_KEYSIZE_128 16
+
+/* Key size in bytes for UBIFS */
+#define UBIFS_CRYPTO_KEYSIZE AES_KEYSIZE_128
+/* AES in counter mode is the encryption algorithm. */
+#define UBIFS_CRYPTO_ALGORITHM "ctr(aes)"
+
 /*
  * Lockdep classes for UBIFS inode @ui_mutex.
  */
@@ -1771,9 +1779,11 @@ long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 /* compressor.c */
 int __init ubifs_compressors_init(void);
 void ubifs_compressors_exit(void);
+int ubifs_aes_crypt(void *str, int len, u8 *key, int keylen,
+		    u8 *iv, int ivlen);
 void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
 		    int *compr_type, u8 *key);
-int ubifs_decompress(const void *buf, int len, void *out, int *out_len,
+int ubifs_decompress(void *buf, int len, void *out, int *out_len,
 		     int compr_type, u8 *key);

 #include "debug.h"


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [patch] Add design document for UBIFS secure deletion
  2012-03-23 16:38                 ` Joel Reardon
@ 2012-03-26 15:03                   ` Artem Bityutskiy
  0 siblings, 0 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-26 15:03 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-fsdevel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3419 bytes --]

On Fri, 2012-03-23 at 17:38 +0100, Joel Reardon wrote:
> I'll answer some quickly here now while they're fresh in mind and add them
> to the doc later.
> 
> >
> > > In particular, it does not behave like a
> > > +log-structured file system: when a KSA LEB is updated, its contents are
> > > +written to a new physical location on the flash memory, UBI's logical map is
> > > +then updated to this new physical address and the previous version of the KSA
> > > +LEB is then erased.
> >
> > Am I right that you basically wanted to say that when you update a KSA
> > LEB, you make sure that the physical flash does not contain the old
> > contents of this LEB? Would be nice to re-phrase.
> >
> > Side question - how do you do this?
> 
> Here I'm just spelling out atomic update, so nothing fancy except
> leb_change() to replace the keys.

This will write the new data to a different PEB, and schedule the old
PEB for erasure in background. If the UBI thread is alive, it will
schedule the erasure soon. But if it is stopped or dead, the erasure may
happen only when there is a need in an empty PEB.

To make sure the old contents are really erased, you'd have to call
'ubi_sync()'. But the problem with this is that it will force erasure of
_all_ the scheduled PEBs, so it may block for long time, especially on
NOR.

For the security things you'd probably need to extend 'ubi_leb_change()'
and add a 'sync' parameter which would make sure the previous PEB is
synchronously erased before 'ubi_leb_change()' returns. I think it is
very easy to do.

> Protected as in the kernel stops joe user from reading raw from /dev/mtd,
> no?

No, I think both UBI volumes and MTD devices are world-readable.


> > Hmm, why is it necessary to re-initialize unused keys?
> >
> 
> This can be a mount option actually, it was a response to  attack that was
> proposed to me about the design. The idea here is that, suppose the
> attacker reads the drive at time X with sufficient privilages to read the
> keys. Then the attacker will know for certain that those keys will
> eventually be used to encrypt data. So they can compromise not only the
> current data, but also future data. By replacing unused keys, the attacker
> is limited only to compromizing current data.

Would be nice to put this to the doc, and I do not think we need a mount
option for this.

> Ah, yes! Any other tool?

No, just this one.

> > Hmm, interesting how you manage checkpoints. I guess the easiest would
> > be to have a special inode (not visible for users) and store checkpoints
> > there?
> >
> 
> The first block of the KSA stores only checkpoints, so KSA size is 1 +
> minrequired + some slack. So this may need the multiple LEB
> approach for one of the other UBIFS areas when its size is too big, but I
> havn't done that since its generally so highly compressable that it
> wasn't ever a concern.

But I guess the larger is the volume the larger the checkpoint is? Also,
would you please rename it to something else because checkpoint is a
very common word with more or less commonly understood meaning, and
using it for something else in context of file-systems is asking for
troubles. Even if you call it 'backpack' (random word from my head) -
I'll be happier :-) But something more sensible is more appreciated of
course :-)

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Move CRC computation to separate function
  2012-03-25 20:38             ` Joel Reardon
@ 2012-03-26 15:34               ` Artem Bityutskiy
  0 siblings, 0 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-26 15:34 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-kernel, linux-fsdevel

[-- Attachment #1: Type: text/plain, Size: 1060 bytes --]

On Sun, 2012-03-25 at 22:38 +0200, Joel Reardon wrote:
> CRC computation now an inline header function which will be used in
> multiple places in the future.
> 
> --
This will make git-am ignore Signed-off-by. Also, please, subject UBIFS
patches with an "UBIFS:" prefix. The good way to send a patch is to use
git format-patch and then git send-email or just insert it to the
message body as-is.

The best way to make sure you send patches correctly is to send to
yourself, then save, then try to apply with 'git am' and look at the
results.

> Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
> 
>  fs/ubifs/io.c   |    4 +---
>  fs/ubifs/misc.h |   15 +++++++++++++++
>  2 files changed, 16 insertions(+), 3 deletions(-)

> +/**
> + * ubifs_set_crc - computes and writes the crc for a ubifs node to the common
> + * header.
> + * @node: the data node
> + */
The comment is not good. I've amended it and pushed the patch, thanks!

> +static inline void ubifs_set_node_crc(void *node)
...

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Add a encryption key parameter to the compress / decompress function.
  2012-03-25 21:11             ` [patch] Add a encryption key parameter to the compress / decompress function Joel Reardon
  2012-03-25 21:38               ` [patch] Add cryptographic functionality when a key is passed to the compress / decompress functions Joel Reardon
@ 2012-03-27  8:27               ` Artem Bityutskiy
  2012-03-29 14:11                 ` [patch] UBIFS: " Joel Reardon
  1 sibling, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-27  8:27 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-kernel, linux-fsdevel

[-- Attachment #1: Type: text/plain, Size: 2040 bytes --]

On Sun, 2012-03-25 at 23:11 +0200, Joel Reardon wrote:
> Compress and decompress now have an extra u8 *key field, and NULL is
> passed wherever it is used. Later this will be used for an encryption key
> for the data being compressed/decompressed (and NULL if no cryptographic operations should be done.)
> 
> Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
> 
> From 650a52b5301778cf2f0c17ed993a104a4473a8fb Mon Sep 17 00:00:00 2001
> From: Joel Reardon <jreardon@fatbottomsworth.(none)>
> Date: Sun, 25 Mar 2012 23:06:03 +0200
> Subject: [PATCH] Added a key parameter to de/compress.

If I use git-am I will get the above "From" etc stuff in the commit
message, which is not what was intended. Just send the 'git
format-patch' output "as-is".

> 
> ---
>  fs/ubifs/compress.c |    8 ++++++--
>  fs/ubifs/file.c     |    5 +++--
>  fs/ubifs/journal.c  |    7 ++++---
>  fs/ubifs/ubifs.h    |    4 ++--
>  4 files changed, 15 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
> index 11e4132..d1812fa 100644
> --- a/fs/ubifs/compress.c
> +++ b/fs/ubifs/compress.c
> @@ -82,6 +82,8 @@ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
>   * @out_len: output buffer length is returned here
>   * @compr_type: type of compression to use on enter, actually used compression
>   *              type on exit
> + * @key: a pointer to a key-sized buffer to use as the encryption key. if NULL
> + *      then no encryption is performed.

This is confusing. In UBIFS we already have notion of "key" with
established meaning, defined in fs/ubifs/key.h. Would you please use a
different term, e.g., "encryption key", "crypto-key", for better
readability. The same for the variable name - to avoid ambiguity, use
something like 'cryptokey' or at least 'ckey' or something else.

Also, the comment says "key-sized" buffer, which is also confusing.
Either do not say anything about the size or define it better.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] Add cryptographic functionality when a key is passed to the compress / decompress functions
  2012-03-25 21:38               ` [patch] Add cryptographic functionality when a key is passed to the compress / decompress functions Joel Reardon
@ 2012-03-27  8:33                 ` Artem Bityutskiy
  2012-03-29 14:39                   ` [patch] UBIFS: " Joel Reardon
  0 siblings, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-03-27  8:33 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-kernel, linux-fsdevel

[-- Attachment #1: Type: text/plain, Size: 591 bytes --]

I guess you'll need to amend this patch if you change the previous one.
Also 2 nit-picks.

On Sun, 2012-03-25 at 23:38 +0200, Joel Reardon wrote:
> +	if (err) {
> +		ubifs_err("setkey() failed  flags=%x",
Please, use %#x instead.
> +			  crypto_blkcipher_get_flags(tfm));
> +	return err;
> +	}

Indentation.


> +	if (key) {
> +		u8 iv[UBIFS_CRYPTO_KEYSIZE];
> +		memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);

Please to the same convention we use globally in UBIFS - put a blank
like between "iv" declaration and the "memset" invocation..

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [patch] UBIFS: Add a encryption key parameter to the compress / decompress function.
  2012-03-27  8:27               ` [patch] Add a encryption key parameter to the compress / decompress function Artem Bityutskiy
@ 2012-03-29 14:11                 ` Joel Reardon
  2012-04-02 14:02                   ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-29 14:11 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-kernel, linux-fsdevel

Added a crypto_key parameter to ubifs compress and decompress. Will be used
later if non-NULL to encrypt / decrypt data nodes.

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
---
 fs/ubifs/compress.c |    8 ++++++--
 fs/ubifs/file.c     |    5 +++--
 fs/ubifs/journal.c  |    7 ++++---
 fs/ubifs/ubifs.h    |    4 ++--
 4 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
index 11e4132..c91974a 100644
--- a/fs/ubifs/compress.c
+++ b/fs/ubifs/compress.c
@@ -82,6 +82,8 @@ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
  * @out_len: output buffer length is returned here
  * @compr_type: type of compression to use on enter, actually used compression
  *              type on exit
+ * @crypto_key: a pointer to bytes to use as the encryption key,
+ *              if NULL then no encryption is performed.
  *
  * This function compresses input buffer @in_buf of length @in_len and stores
  * the result in the output buffer @out_buf and the resulting length in
@@ -93,7 +95,7 @@ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
  * buffer and %UBIFS_COMPR_NONE is returned in @compr_type.
  */
 void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
-		    int *compr_type)
+		    int *compr_type, u8 *crypto_key)
 {
 	int err;
 	struct ubifs_compressor *compr = ubifs_compressors[*compr_type];
@@ -140,13 +142,15 @@ no_compr:
  * @out_buf: output buffer where decompressed data should
  * @out_len: output length is returned here
  * @compr_type: type of compression
+ * @crypto_key: a pointer to bytes to use as the decryption key,
+ *		if NULL then no decryption is performed.
  *
  * This function decompresses data from buffer @in_buf into buffer @out_buf.
  * The length of the uncompressed data is returned in @out_len. This functions
  * returns %0 on success or a negative error code on failure.
  */
 int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
-		     int *out_len, int compr_type)
+		     int *out_len, int compr_type, u8 *crypto_key)
 {
 	int err;
 	struct ubifs_compressor *compr;
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index f9c234b..2660c9f 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -80,7 +80,7 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,
 	dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
 	out_len = UBIFS_BLOCK_SIZE;
 	err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
-			       le16_to_cpu(dn->compr_type));
+			       le16_to_cpu(dn->compr_type), NULL);
 	if (err || len != out_len)
 		goto dump;

@@ -649,7 +649,8 @@ static int populate_page(struct ubifs_info *c, struct page *page,
 			dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
 			out_len = UBIFS_BLOCK_SIZE;
 			err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
-					       le16_to_cpu(dn->compr_type));
+					       le16_to_cpu(dn->compr_type),
+					       NULL);
 			if (err || len != out_len)
 				goto out_err;

diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 2f438ab..9156395 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -727,7 +727,7 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
 		compr_type = ui->compr_type;

 	out_len = dlen - UBIFS_DATA_NODE_SZ;
-	ubifs_compress(buf, len, &data->data, &out_len, &compr_type);
+	ubifs_compress(buf, len, &data->data, &out_len, &compr_type, NULL);
 	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);

 	dlen = UBIFS_DATA_NODE_SZ + out_len;
@@ -1110,11 +1110,12 @@ static int recomp_data_node(struct ubifs_data_node *dn, int *new_len)

 	len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
 	compr_type = le16_to_cpu(dn->compr_type);
-	err = ubifs_decompress(&dn->data, len, buf, &out_len, compr_type);
+	err = ubifs_decompress(
+		&dn->data, len, buf, &out_len, compr_type, NULL);
 	if (err)
 		goto out;

-	ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type);
+	ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type, NULL);
 	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);
 	dn->compr_type = cpu_to_le16(compr_type);
 	dn->size = cpu_to_le32(*new_len);
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 93d59ac..0cc1180 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1772,9 +1772,9 @@ long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 int __init ubifs_compressors_init(void);
 void ubifs_compressors_exit(void);
 void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
-		    int *compr_type);
+		    int *compr_type, u8 *crypto_key);
 int ubifs_decompress(const void *buf, int len, void *out, int *out_len,
-		     int compr_type);
+		     int compr_type, u8 *crypto_key);

 #include "debug.h"
 #include "misc.h"
-- 
1.7.5.4



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [patch] UBIFS: Add cryptographic functionality when a key is passed to the compress / decompress functions
  2012-03-27  8:33                 ` Artem Bityutskiy
@ 2012-03-29 14:39                   ` Joel Reardon
  2012-04-02 14:36                     ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-03-29 14:39 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-kernel, linux-fsdevel

This patch adds a function to perform AES encryption. The compress and
decompress routines use this function if they are called with a non-NULL
key parameter. It uses AES counter mode (where encryption and decryption
are the same function) and performs the operation in place on the data. It
uses a default IV of 0, since each key is only evey used to encrypt one
data item the IV does not matter.

The const qualifier was removed from the decompress routine for the
following reason. Encrypted data is not compressable, so compression is
first applied then the result is encrypted. In the reverse, decryption is
first applied and the result decompressed. This means that either the
input buffer for decompression is used to perform an in-place decryption
before decompression, or a third buffer is added and data is copied around.


Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
---
 fs/ubifs/compress.c |   77 +++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/ubifs/ubifs.h    |   12 +++++++-
 2 files changed, 85 insertions(+), 4 deletions(-)

diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
index c91974a..f94cf21 100644
--- a/fs/ubifs/compress.c
+++ b/fs/ubifs/compress.c
@@ -27,9 +27,12 @@
  * decompression.
  */

-#include <linux/crypto.h>
 #include "ubifs.h"

+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+
+
 /* Fake description object for the "none" compressor */
 static struct ubifs_compressor none_compr = {
 	.compr_type = UBIFS_COMPR_NONE,
@@ -75,6 +78,55 @@ static struct ubifs_compressor zlib_compr = {
 struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];

 /**
+ * ubifs_aes_crypt - encrypt / decrypt data.
+ * @str: data to crypt
+ * @len: length of the data
+ * @crypto_key: the cryptographic key to use to crypt the data
+ * @crypto_key_len: the length of the crypto_key
+ * @iv: the initialization vector to use
+ * @ivlen: the length of the initialization vector
+ *
+ * This function applies aes encryption to the data. It is done in counter
+ * mode, which means that encryption and decryption are the same operation,
+ * i.e., it XORs the same generated bitstream, so it can be used both for
+ * encryption / decryption. The operation is done in-place, so str mutates.
+ */
+int ubifs_aes_crypt(void *str, int len, u8 *crypto_key,
+		    int crypto_key_len, u8 *iv, int ivlen)
+{
+	struct crypto_blkcipher *tfm;
+	struct blkcipher_desc desc;
+	struct scatterlist sg;
+	int err = 0;
+
+	tfm = crypto_alloc_blkcipher(UBIFS_CRYPTO_ALGORITHM, 0, 0);
+
+	if (IS_ERR(tfm)) {
+		ubifs_err("failed to load transform for aes: %ld",
+			  PTR_ERR(tfm));
+		return err;
+	}
+
+	err = crypto_blkcipher_setkey(tfm, crypto_key, crypto_key_len);
+	desc.tfm = tfm;
+	desc.flags = 0;
+	if (err) {
+		ubifs_err("crypto_blkcipher_setkey() failed  flags=%#x",
+			  crypto_blkcipher_get_flags(tfm));
+		return err;
+	}
+	memset(&sg, 0, sizeof(struct scatterlist));
+
+	sg_set_buf(&sg, str, len);
+	desc.info = iv;
+	err = crypto_blkcipher_encrypt(&desc, &sg, &sg, len);
+	crypto_free_blkcipher(tfm);
+	if (err)
+		return err;
+	return 0;
+}
+
+/**
  * ubifs_compress - compress data.
  * @in_buf: data to compress
  * @in_len: length of the data to compress
@@ -127,12 +179,22 @@ void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
 	if (in_len - *out_len < UBIFS_MIN_COMPRESS_DIFF)
 		goto no_compr;

-	return;
+	goto encrypt;

 no_compr:
 	memcpy(out_buf, in_buf, in_len);
 	*out_len = in_len;
 	*compr_type = UBIFS_COMPR_NONE;
+	goto encrypt;
+
+encrypt:
+	if (crypto_key) {
+		u8 iv[UBIFS_CRYPTO_KEYSIZE];
+
+		memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
+		ubifs_aes_crypt(out_buf, *out_len, crypto_key,
+				UBIFS_CRYPTO_KEYSIZE, iv, UBIFS_CRYPTO_KEYSIZE);
+	}
 }

 /**
@@ -149,7 +211,7 @@ no_compr:
  * The length of the uncompressed data is returned in @out_len. This functions
  * returns %0 on success or a negative error code on failure.
  */
-int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
+int ubifs_decompress(void *in_buf, int in_len, void *out_buf,
 		     int *out_len, int compr_type, u8 *crypto_key)
 {
 	int err;
@@ -167,6 +229,15 @@ int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
 		return -EINVAL;
 	}

+	if (crypto_key) {
+		u8 iv[UBIFS_CRYPTO_KEYSIZE];
+
+		memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
+		ubifs_aes_crypt(in_buf, in_len, crypto_key,
+				UBIFS_CRYPTO_KEYSIZE, iv,
+				UBIFS_CRYPTO_KEYSIZE);
+	}
+
 	if (compr_type == UBIFS_COMPR_NONE) {
 		memcpy(out_buf, in_buf, in_len);
 		*out_len = in_len;
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index b425264..dbfa508 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -160,6 +160,14 @@
 /* Maximum number of data nodes to bulk-read */
 #define UBIFS_MAX_BULK_READ 32

+/* Size of 128 bits in bytes */
+#define AES_KEYSIZE_128 16
+
+/* Key size in bytes for UBIFS */
+#define UBIFS_CRYPTO_KEYSIZE AES_KEYSIZE_128
+/* AES in counter mode is the encryption algorithm. */
+#define UBIFS_CRYPTO_ALGORITHM "ctr(aes)"
+
 /*
  * Lockdep classes for UBIFS inode @ui_mutex.
  */
@@ -1771,9 +1779,11 @@ long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 /* compressor.c */
 int __init ubifs_compressors_init(void);
 void ubifs_compressors_exit(void);
+int ubifs_aes_crypt(void *str, int len, u8 *crypto_key, int crypto_key_len,
+		    u8 *iv, int ivlen);
 void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
 		    int *compr_type, u8 *crypto_key);
-int ubifs_decompress(const void *buf, int len, void *out, int *out_len,
+int ubifs_decompress(void *buf, int len, void *out, int *out_len,
 		     int compr_type, u8 *crypto_key);

 #include "debug.h"
-- 
1.7.5.4



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [patch] UBIFS: Add a encryption key parameter to the compress / decompress function.
  2012-03-29 14:11                 ` [patch] UBIFS: " Joel Reardon
@ 2012-04-02 14:02                   ` Artem Bityutskiy
  0 siblings, 0 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-04-02 14:02 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-kernel, linux-fsdevel

[-- Attachment #1: Type: text/plain, Size: 2310 bytes --]

On Thu, 2012-03-29 at 16:11 +0200, Joel Reardon wrote:
> Added a crypto_key parameter to ubifs compress and decompress. Will be used
> later if non-NULL to encrypt / decrypt data nodes.
> 
> Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>

Hi,

thanks, pushed with the following amendments:

diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
index c91974a..b796b8d 100644
--- a/fs/ubifs/compress.c
+++ b/fs/ubifs/compress.c
@@ -82,8 +82,8 @@ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
  * @out_len: output buffer length is returned here
  * @compr_type: type of compression to use on enter, actually used compression
  *              type on exit
- * @crypto_key: a pointer to bytes to use as the encryption key,
- *              if NULL then no encryption is performed.
+ * @crypto_key: a pointer to bytes to use as the encryption key, if NULL then
+ *              no encryption is performed
  *
  * This function compresses input buffer @in_buf of length @in_len and stores
  * the result in the output buffer @out_buf and the resulting length in
@@ -142,8 +142,8 @@ no_compr:
  * @out_buf: output buffer where decompressed data should
  * @out_len: output length is returned here
  * @compr_type: type of compression
- * @crypto_key: a pointer to bytes to use as the decryption key,
- *             if NULL then no decryption is performed.
+ * @crypto_key: a pointer to bytes to use as the decryption key, if NULL then
+ *              no decryption is performed.
  *
  * This function decompresses data from buffer @in_buf into buffer @out_buf.
  * The length of the uncompressed data is returned in @out_len. This functions
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 9ece275..57c4d2f 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -1113,8 +1113,7 @@ static int recomp_data_node(struct ubifs_data_node *dn, int *new_len)
 
        len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
        compr_type = le16_to_cpu(dn->compr_type);
-       err = ubifs_decompress(
-               &dn->data, len, buf, &out_len, compr_type, NULL);
+       err = ubifs_decompress(&dn->data, len, buf, &out_len, compr_type, NULL);
        if (err)
                goto out;

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [patch] UBIFS: Add cryptographic functionality when a key is passed to the compress / decompress functions
  2012-03-29 14:39                   ` [patch] UBIFS: " Joel Reardon
@ 2012-04-02 14:36                     ` Artem Bityutskiy
  2012-04-02 14:48                       ` Joel Reardon
  0 siblings, 1 reply; 55+ messages in thread
From: Artem Bityutskiy @ 2012-04-02 14:36 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-kernel, linux-fsdevel

[-- Attachment #1: Type: text/plain, Size: 3361 bytes --]

On Thu, 2012-03-29 at 16:39 +0200, Joel Reardon wrote:
>  /* Fake description object for the "none" compressor */
>  static struct ubifs_compressor none_compr = {
>  	.compr_type = UBIFS_COMPR_NONE,
> @@ -75,6 +78,55 @@ static struct ubifs_compressor zlib_compr = {
>  struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
> 
>  /**
> + * ubifs_aes_crypt - encrypt / decrypt data.
> + * @str: data to crypt
> + * @len: length of the data
> + * @crypto_key: the cryptographic key to use to crypt the data
> + * @crypto_key_len: the length of the crypto_key
> + * @iv: the initialization vector to use
> + * @ivlen: the length of the initialization vector
> + *
> + * This function applies aes encryption to the data. It is done in counter
> + * mode, which means that encryption and decryption are the same operation,
> + * i.e., it XORs the same generated bitstream, so it can be used both for
> + * encryption / decryption. The operation is done in-place, so str mutates.
> + */
> +int ubifs_aes_crypt(void *str, int len, u8 *crypto_key,
> +		    int crypto_key_len, u8 *iv, int ivlen)

You support only one length - please, kill ivlen parameter.

Also, should ubifs_aes_crypt be static? I do not see any users outside
of compress.c. In this case remove the "ubifs_" prefix. But a
non-written convention, in UBIFS we _tend_ to prefix only non-static
functions with "ubifs_" and avoid having it for static functions.

> +{
> +	struct crypto_blkcipher *tfm;
> +	struct blkcipher_desc desc;
> +	struct scatterlist sg;
> +	int err = 0;
> +
> +	tfm = crypto_alloc_blkcipher(UBIFS_CRYPTO_ALGORITHM, 0, 0);
> +

Unnecessary empty line.

> +	if (IS_ERR(tfm)) {
> +		ubifs_err("failed to load transform for aes: %ld",
> +			  PTR_ERR(tfm));
> +		return err;
> +	}
> +
> +	err = crypto_blkcipher_setkey(tfm, crypto_key, crypto_key_len);
> +	desc.tfm = tfm;
> +	desc.flags = 0;
> +	if (err) {
> +		ubifs_err("crypto_blkcipher_setkey() failed  flags=%#x",
> +			  crypto_blkcipher_get_flags(tfm));
> +		return err;
> +	}
> +	memset(&sg, 0, sizeof(struct scatterlist));
> +

Empty lines mean grouping, and I think this memeset should be grouped
with sg_set_buf instead.


>  no_compr:
>  	memcpy(out_buf, in_buf, in_len);
>  	*out_len = in_len;
>  	*compr_type = UBIFS_COMPR_NONE;
> +	goto encrypt;
> +
> +encrypt:

I guess the above goto is redundant?

> +	if (crypto_key) {
> +		u8 iv[UBIFS_CRYPTO_KEYSIZE];
> +
> +		memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
> +		ubifs_aes_crypt(out_buf, *out_len, crypto_key,
> +				UBIFS_CRYPTO_KEYSIZE, iv, UBIFS_CRYPTO_KEYSIZE);
> +	}
>  }
> 
>  /**
> @@ -149,7 +211,7 @@ no_compr:
>   * The length of the uncompressed data is returned in @out_len. This functions
>   * returns %0 on success or a negative error code on failure.
>   */
> -int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
> +int ubifs_decompress(void *in_buf, int in_len, void *out_buf,
>  		     int *out_len, int compr_type, u8 *crypto_key)

Please, write a fat "WARNING" note in the comment and tell that this
function modifies the input buffer.

> +/* Size of 128 bits in bytes */
> +#define AES_KEYSIZE_128 16

If you have no plans to support keys larger than 128 just kill this
constant please.

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] UBIFS: Add cryptographic functionality when a key is passed to the compress / decompress functions
  2012-04-02 14:36                     ` Artem Bityutskiy
@ 2012-04-02 14:48                       ` Joel Reardon
  2012-04-02 14:57                         ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-04-02 14:48 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-kernel, linux-fsdevel


>
> You support only one length - please, kill ivlen parameter.
>
> Also, should ubifs_aes_crypt be static? I do not see any users outside
> of compress.c. In this case remove the "ubifs_" prefix. But a
> non-written convention, in UBIFS we _tend_ to prefix only non-static
> functions with "ubifs_" and avoid having it for static functions.
>

Should length for key remain, and the IV is just the same? Or should the
global #define just be used inside the aes function.

There is another use where the data is decrypted and reencrypted with a
different key. (during GC and if an erase block becomes bad.) In this
case, the data is not decompressed and recompressed, only the encryption
changes. However, for simplicity, and because its not frequent, we can
make it static and use the compress functions to handle this.

>
> I guess the above goto is redundant?
>

It is, but I put it in for future developers who may add a new control
case there after without expecting the above to 'fall through'.




^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] UBIFS: Add cryptographic functionality when a key is passed to the compress / decompress functions
  2012-04-02 14:48                       ` Joel Reardon
@ 2012-04-02 14:57                         ` Artem Bityutskiy
  2012-04-02 14:58                           ` Joel Reardon
  2012-04-03 10:29                           ` Joel Reardon
  0 siblings, 2 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-04-02 14:57 UTC (permalink / raw)
  To: Joel Reardon; +Cc: linux-mtd, linux-kernel, linux-fsdevel

[-- Attachment #1: Type: text/plain, Size: 817 bytes --]

On Mon, 2012-04-02 at 16:48 +0200, Joel Reardon wrote:
> >
> > You support only one length - please, kill ivlen parameter.
> >
> > Also, should ubifs_aes_crypt be static? I do not see any users outside
> > of compress.c. In this case remove the "ubifs_" prefix. But a
> > non-written convention, in UBIFS we _tend_ to prefix only non-static
> > functions with "ubifs_" and avoid having it for static functions.
> >
> 
> Should length for key remain, and the IV is just the same? Or should the
> global #define just be used inside the aes function.

I guess I was a little confused WRT iv vs cryptokey. Anyway, I thought
you just use UBIFS_CRYPTO_KEYSIZE for all of this, why not to just use
the constant in this function? Why passing the length as an argument?

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] UBIFS: Add cryptographic functionality when a key is passed to the compress / decompress functions
  2012-04-02 14:57                         ` Artem Bityutskiy
@ 2012-04-02 14:58                           ` Joel Reardon
  2012-04-03 10:29                           ` Joel Reardon
  1 sibling, 0 replies; 55+ messages in thread
From: Joel Reardon @ 2012-04-02 14:58 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-kernel, linux-fsdevel

>
> I guess I was a little confused WRT iv vs cryptokey. Anyway, I thought
> you just use UBIFS_CRYPTO_KEYSIZE for all of this, why not to just use
> the constant in this function? Why passing the length as an argument?
>

There's no actually reason... in my head, if I have a char buffer then the
next parameter is its length. I'll change it so the comments discuss the
length of the buffer.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [patch] UBIFS: Add cryptographic functionality when a key is passed to the compress / decompress functions
  2012-04-02 14:57                         ` Artem Bityutskiy
  2012-04-02 14:58                           ` Joel Reardon
@ 2012-04-03 10:29                           ` Joel Reardon
  2012-04-03 10:41                             ` Guillaume LECERF
  1 sibling, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-04-03 10:29 UTC (permalink / raw)
  To: Artem Bityutskiy; +Cc: linux-mtd, linux-kernel, linux-fsdevel

This patch adds a function to perform AES encryption. The compress and
decompress routines use this function if they are called with a non-NULL
key parameter. It uses AES counter mode (where encryption and decryption
are the same function) and performs the operation in place on the data. It
uses a default IV of 0, since each key is only evey used to encrypt one
data item the IV does not matter.

The const qualifier was removed from the decompress routine for the
following reason. Encrypted data is not compressable, so compression is
first applied then the result is encrypted. In the reverse, decryption is
first applied and the result decompressed. This means that either the
input buffer for decompression is used to perform an in-place decryption
before decompression, or a third buffer is added and data is copied around.

This was tested by using a static key as the key parameter where both compress
and decompress were called. Data was written and the file system was unmounted
and the mtd dev was scanned with hexdump to make sure that no plaintext data
was available. The drive was remounted and the data successful read back. The
static key was then removed and replaced with NULL, and the same test was done
except that now the data appeared in plaintext on the raw device.

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
---
 fs/ubifs/compress.c |   75 ++++++++++++++++++++++++++++++++++++++++++++++++---
 fs/ubifs/ubifs.h    |    8 +++++-
 2 files changed, 78 insertions(+), 5 deletions(-)

diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
index b796b8d..e6dc77d 100644
--- a/fs/ubifs/compress.c
+++ b/fs/ubifs/compress.c
@@ -27,9 +27,12 @@
  * decompression.
  */

-#include <linux/crypto.h>
 #include "ubifs.h"

+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+
+
 /* Fake description object for the "none" compressor */
 static struct ubifs_compressor none_compr = {
 	.compr_type = UBIFS_COMPR_NONE,
@@ -75,6 +78,53 @@ static struct ubifs_compressor zlib_compr = {
 struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];

 /**
+ * ubifs_aes_crypt - encrypt / decrypt data.
+ * @str: data to crypt
+ * @len: length of the data
+ * @crypto_key: the cryptographic key to use to crypt the data
+ * @iv: the initialization vector to use
+ *
+ * This function applies aes encryption to the data. It is done in counter
+ * mode, which means that encryption and decryption are the same operation,
+ * i.e., it XORs the same generated bitstream, so it can be used both for
+ * encryption / decryption. The operation is done in-place, so str mutates.
+ * Both crypto_key and iv are valid pointers to a buffer of length
+ * UBIFS_CRYPTO_KEYSIZE.
+ */
+int ubifs_aes_crypt(void *str, int len, u8 *crypto_key, u8 *iv)
+{
+	struct crypto_blkcipher *tfm;
+	struct blkcipher_desc desc;
+	struct scatterlist sg;
+	int err = 0;
+
+	tfm = crypto_alloc_blkcipher(UBIFS_CRYPTO_ALGORITHM, 0, 0);
+	if (IS_ERR(tfm)) {
+		ubifs_err("failed to load transform for aes: %ld",
+			  PTR_ERR(tfm));
+		return err;
+	}
+
+	err = crypto_blkcipher_setkey(tfm, crypto_key, UBIFS_CRYPTO_KEYSIZE);
+	desc.tfm = tfm;
+	desc.flags = 0;
+	if (err) {
+		ubifs_err("crypto_blkcipher_setkey() failed  flags=%#x",
+			  crypto_blkcipher_get_flags(tfm));
+		return err;
+	}
+
+	memset(&sg, 0, sizeof(struct scatterlist));
+	sg_set_buf(&sg, str, len);
+	desc.info = iv;
+	err = crypto_blkcipher_encrypt(&desc, &sg, &sg, len);
+	crypto_free_blkcipher(tfm);
+	if (err)
+		return err;
+	return 0;
+}
+
+/**
  * ubifs_compress - compress data.
  * @in_buf: data to compress
  * @in_len: length of the data to compress
@@ -126,13 +176,21 @@ void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
 	 */
 	if (in_len - *out_len < UBIFS_MIN_COMPRESS_DIFF)
 		goto no_compr;
-
-	return;
+	goto encrypt;

 no_compr:
 	memcpy(out_buf, in_buf, in_len);
 	*out_len = in_len;
 	*compr_type = UBIFS_COMPR_NONE;
+	goto encrypt;
+
+encrypt:
+	if (crypto_key) {
+		u8 iv[UBIFS_CRYPTO_KEYSIZE];
+
+		memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
+		ubifs_aes_crypt(out_buf, *out_len, crypto_key, iv);
+	}
 }

 /**
@@ -148,8 +206,10 @@ no_compr:
  * This function decompresses data from buffer @in_buf into buffer @out_buf.
  * The length of the uncompressed data is returned in @out_len. This functions
  * returns %0 on success or a negative error code on failure.
+ *
+ * WARNING: this function may modify the contents of in_buf when executing.
  */
-int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
+int ubifs_decompress(void *in_buf, int in_len, void *out_buf,
 		     int *out_len, int compr_type, u8 *crypto_key)
 {
 	int err;
@@ -167,6 +227,13 @@ int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
 		return -EINVAL;
 	}

+	if (crypto_key) {
+		u8 iv[UBIFS_CRYPTO_KEYSIZE];
+
+		memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
+		ubifs_aes_crypt(in_buf, in_len, crypto_key, iv);
+	}
+
 	if (compr_type == UBIFS_COMPR_NONE) {
 		memcpy(out_buf, in_buf, in_len);
 		*out_len = in_len;
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 3ed12be..84d2c49 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -160,6 +160,11 @@
 /* Maximum number of data nodes to bulk-read */
 #define UBIFS_MAX_BULK_READ 32

+/* 128 bit key size in bytes for UBIFS */
+#define UBIFS_CRYPTO_KEYSIZE 16
+/* AES in counter mode is the encryption algorithm. */
+#define UBIFS_CRYPTO_ALGORITHM "ctr(aes)"
+
 /*
  * Lockdep classes for UBIFS inode @ui_mutex.
  */
@@ -1771,9 +1776,10 @@ long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 /* compressor.c */
 int __init ubifs_compressors_init(void);
 void ubifs_compressors_exit(void);
+int ubifs_aes_crypt(void *str, int len, u8 *crypto_key, u8 *iv);
 void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
 		    int *compr_type, u8 *crypto_key);
-int ubifs_decompress(const void *buf, int len, void *out, int *out_len,
+int ubifs_decompress(void *buf, int len, void *out, int *out_len,
 		     int compr_type, u8 *crypto_key);

 #include "debug.h"
-- 
1.7.5.4



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [patch] UBIFS: Add cryptographic functionality when a key is passed to the compress / decompress functions
  2012-04-03 10:29                           ` Joel Reardon
@ 2012-04-03 10:41                             ` Guillaume LECERF
  2012-04-03 11:35                               ` Joel Reardon
  0 siblings, 1 reply; 55+ messages in thread
From: Guillaume LECERF @ 2012-04-03 10:41 UTC (permalink / raw)
  To: Joel Reardon; +Cc: Artem Bityutskiy, linux-fsdevel, linux-mtd, linux-kernel

Hi,

2012/4/3 Joel Reardon <joel@clambassador.com>:
>  no_compr:
>        memcpy(out_buf, in_buf, in_len);
>        *out_len = in_len;
>        *compr_type = UBIFS_COMPR_NONE;
> +       goto encrypt;

This goto seems unneeded here.

> +
> +encrypt:
> +       if (crypto_key) {
> +               u8 iv[UBIFS_CRYPTO_KEYSIZE];
> +
> +               memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
> +               ubifs_aes_crypt(out_buf, *out_len, crypto_key, iv);
> +       }




-- 
Guillaume LECERF
OpenBricks developer - www.openbricks.org

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [patch] UBIFS: Add cryptographic functionality when a key is passed to the compress / decompress functions
  2012-04-03 10:41                             ` Guillaume LECERF
@ 2012-04-03 11:35                               ` Joel Reardon
  2012-04-12 14:05                                 ` Artem Bityutskiy
  0 siblings, 1 reply; 55+ messages in thread
From: Joel Reardon @ 2012-04-03 11:35 UTC (permalink / raw)
  To: Guillaume LECERF; +Cc: Artem Bityutskiy, linux-fsdevel, linux-mtd, linux-kernel

Without the goto:

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
---
 fs/ubifs/compress.c |   74 ++++++++++++++++++++++++++++++++++++++++++++++++---
 fs/ubifs/ubifs.h    |    8 +++++-
 2 files changed, 77 insertions(+), 5 deletions(-)

diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
index b796b8d..61fe584 100644
--- a/fs/ubifs/compress.c
+++ b/fs/ubifs/compress.c
@@ -27,9 +27,12 @@
  * decompression.
  */

-#include <linux/crypto.h>
 #include "ubifs.h"

+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+
+
 /* Fake description object for the "none" compressor */
 static struct ubifs_compressor none_compr = {
 	.compr_type = UBIFS_COMPR_NONE,
@@ -75,6 +78,53 @@ static struct ubifs_compressor zlib_compr = {
 struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];

 /**
+ * ubifs_aes_crypt - encrypt / decrypt data.
+ * @str: data to crypt
+ * @len: length of the data
+ * @crypto_key: the cryptographic key to use to crypt the data
+ * @iv: the initialization vector to use
+ *
+ * This function applies aes encryption to the data. It is done in counter
+ * mode, which means that encryption and decryption are the same operation,
+ * i.e., it XORs the same generated bitstream, so it can be used both for
+ * encryption / decryption. The operation is done in-place, so str mutates.
+ * Both crypto_key and iv are valid pointers to a buffer of length
+ * UBIFS_CRYPTO_KEYSIZE.
+ */
+int ubifs_aes_crypt(void *str, int len, u8 *crypto_key, u8 *iv)
+{
+	struct crypto_blkcipher *tfm;
+	struct blkcipher_desc desc;
+	struct scatterlist sg;
+	int err = 0;
+
+	tfm = crypto_alloc_blkcipher(UBIFS_CRYPTO_ALGORITHM, 0, 0);
+	if (IS_ERR(tfm)) {
+		ubifs_err("failed to load transform for aes: %ld",
+			  PTR_ERR(tfm));
+		return err;
+	}
+
+	err = crypto_blkcipher_setkey(tfm, crypto_key, UBIFS_CRYPTO_KEYSIZE);
+	desc.tfm = tfm;
+	desc.flags = 0;
+	if (err) {
+		ubifs_err("crypto_blkcipher_setkey() failed  flags=%#x",
+			  crypto_blkcipher_get_flags(tfm));
+		return err;
+	}
+
+	memset(&sg, 0, sizeof(struct scatterlist));
+	sg_set_buf(&sg, str, len);
+	desc.info = iv;
+	err = crypto_blkcipher_encrypt(&desc, &sg, &sg, len);
+	crypto_free_blkcipher(tfm);
+	if (err)
+		return err;
+	return 0;
+}
+
+/**
  * ubifs_compress - compress data.
  * @in_buf: data to compress
  * @in_len: length of the data to compress
@@ -126,13 +176,20 @@ void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
 	 */
 	if (in_len - *out_len < UBIFS_MIN_COMPRESS_DIFF)
 		goto no_compr;
-
-	return;
+	goto encrypt;

 no_compr:
 	memcpy(out_buf, in_buf, in_len);
 	*out_len = in_len;
 	*compr_type = UBIFS_COMPR_NONE;
+
+encrypt:
+	if (crypto_key) {
+		u8 iv[UBIFS_CRYPTO_KEYSIZE];
+
+		memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
+		ubifs_aes_crypt(out_buf, *out_len, crypto_key, iv);
+	}
 }

 /**
@@ -148,8 +205,10 @@ no_compr:
  * This function decompresses data from buffer @in_buf into buffer @out_buf.
  * The length of the uncompressed data is returned in @out_len. This functions
  * returns %0 on success or a negative error code on failure.
+ *
+ * WARNING: this function may modify the contents of in_buf when executing.
  */
-int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
+int ubifs_decompress(void *in_buf, int in_len, void *out_buf,
 		     int *out_len, int compr_type, u8 *crypto_key)
 {
 	int err;
@@ -167,6 +226,13 @@ int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
 		return -EINVAL;
 	}

+	if (crypto_key) {
+		u8 iv[UBIFS_CRYPTO_KEYSIZE];
+
+		memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
+		ubifs_aes_crypt(in_buf, in_len, crypto_key, iv);
+	}
+
 	if (compr_type == UBIFS_COMPR_NONE) {
 		memcpy(out_buf, in_buf, in_len);
 		*out_len = in_len;
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 3ed12be..84d2c49 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -160,6 +160,11 @@
 /* Maximum number of data nodes to bulk-read */
 #define UBIFS_MAX_BULK_READ 32

+/* 128 bit key size in bytes for UBIFS */
+#define UBIFS_CRYPTO_KEYSIZE 16
+/* AES in counter mode is the encryption algorithm. */
+#define UBIFS_CRYPTO_ALGORITHM "ctr(aes)"
+
 /*
  * Lockdep classes for UBIFS inode @ui_mutex.
  */
@@ -1771,9 +1776,10 @@ long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 /* compressor.c */
 int __init ubifs_compressors_init(void);
 void ubifs_compressors_exit(void);
+int ubifs_aes_crypt(void *str, int len, u8 *crypto_key, u8 *iv);
 void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
 		    int *compr_type, u8 *crypto_key);
-int ubifs_decompress(const void *buf, int len, void *out, int *out_len,
+int ubifs_decompress(void *buf, int len, void *out, int *out_len,
 		     int compr_type, u8 *crypto_key);

 #include "debug.h"
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [patch] UBIFS: Add cryptographic functionality when a key is passed to the compress / decompress functions
  2012-04-03 11:35                               ` Joel Reardon
@ 2012-04-12 14:05                                 ` Artem Bityutskiy
  0 siblings, 0 replies; 55+ messages in thread
From: Artem Bityutskiy @ 2012-04-12 14:05 UTC (permalink / raw)
  To: Joel Reardon; +Cc: Guillaume LECERF, linux-mtd, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 15834 bytes --]

I've pushed this patch (actually folded into the previous one), but
massaged it a bit.

Please, verify.

 Please, see below and
take into account for the future submissions.

1. Keep the subject line short - it should give only rough idea what
patch does.

2. When you re-send, add [PATCH v2] prefix.

3. When you re-send, send full patch with the commit message, not just
a reply with new diff. E.g., if I apply this patch using git am I will
get the following:

----
UBIFS: Add cryptographic functionality when a key is passed to the compress / decompress functions

Without the goto:

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
----

Does the commit message make sense? No. I have to manually copy-paste
you commit message from the previous patch...


On Tue, 2012-04-03 at 13:35 +0200, Joel Reardon wrote:
> Without the goto:
> 
> Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
> ---
>  fs/ubifs/compress.c |   74 ++++++++++++++++++++++++++++++++++++++++++++++++---
>  fs/ubifs/ubifs.h    |    8 +++++-
>  2 files changed, 77 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
> index b796b8d..61fe584 100644
> --- a/fs/ubifs/compress.c
> +++ b/fs/ubifs/compress.c
> @@ -27,9 +27,12 @@
>   * decompression.
>   */
> 
> -#include <linux/crypto.h>
>  #include "ubifs.h"
> 
> +#include <linux/crypto.h>
> +#include <linux/scatterlist.h>

No need to re-arrange includes unnecessarily. All .c files in UBIFS
first include linux headers and then UBIFS headers. I do not know if
this is good or bad, but at least consistent. If you want to change
this, change globally.

> +
> +

No need to add extra new lines without a need. In general, do not
make unneeded changes, and look at the style of the current code
and try to follow it. If you want to change it - fine, but change for
entire UBIFS.

>  /* Fake description object for the "none" compressor */
>  static struct ubifs_compressor none_compr = {
>  	.compr_type = UBIFS_COMPR_NONE,
> @@ -75,6 +78,53 @@ static struct ubifs_compressor zlib_compr = {
>  struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
> 
>  /**
> + * ubifs_aes_crypt - encrypt / decrypt data.
> + * @str: data to crypt
> + * @len: length of the data
> + * @crypto_key: the cryptographic key to use to crypt the data
> + * @iv: the initialization vector to use
> + *
> + * This function applies aes encryption to the data. It is done in counter
> + * mode, which means that encryption and decryption are the same operation,
> + * i.e., it XORs the same generated bitstream, so it can be used both for
> + * encryption / decryption. The operation is done in-place, so str mutates.
> + * Both crypto_key and iv are valid pointers to a buffer of length
> + * UBIFS_CRYPTO_KEYSIZE.
> + */

I believe this was stupid, but I used kernel-doc @ and % prefixes for
argument and costants even in the comments. So to be consistent, use
@str and %UBIFS_CRYPTO_KEYSIZE.

> +int ubifs_aes_crypt(void *str, int len, u8 *crypto_key, u8 *iv)

Surely you can add 'const' qualifier to 'crypto_key' ?
And it can be static - no users outside compress.c.

> +{
> +	struct crypto_blkcipher *tfm;
> +	struct blkcipher_desc desc;
> +	struct scatterlist sg;
> +	int err = 0;

Unneeded initialization.

> +
> +	tfm = crypto_alloc_blkcipher(UBIFS_CRYPTO_ALGORITHM, 0, 0);
> +	if (IS_ERR(tfm)) {
> +		ubifs_err("failed to load transform for aes: %ld",
> +			  PTR_ERR(tfm));
> +		return err;

You return 0 here, which is wrong. Should be "return PTR_ERR(tfm)".

> +	}
> +
> +	err = crypto_blkcipher_setkey(tfm, crypto_key, UBIFS_CRYPTO_KEYSIZE);
> +	desc.tfm = tfm;
> +	desc.flags = 0;
Why you ingected the above 2 lines here? Initialize 'desc' in one place,
do not spread it all over the function.
> +	if (err) {
> +		ubifs_err("crypto_blkcipher_setkey() failed  flags=%#x",
> +			  crypto_blkcipher_get_flags(tfm));

I tried to not include function names in error messages.

> +		return err;
> +	}
> +
> +	memset(&sg, 0, sizeof(struct scatterlist));
> +	sg_set_buf(&sg, str, len);
> +	desc.info = iv;
> +	err = crypto_blkcipher_encrypt(&desc, &sg, &sg, len);
> +	crypto_free_blkcipher(tfm);
> +	if (err)
> +		return err;
> +	return 0;

Can be just 'return err' instead of the 3 above lines.

>  /**
> @@ -148,8 +205,10 @@ no_compr:
>   * This function decompresses data from buffer @in_buf into buffer @out_buf.
>   * The length of the uncompressed data is returned in @out_len. This functions
>   * returns %0 on success or a negative error code on failure.
> + *
> + * WARNING: this function may modify the contents of in_buf when executing.
>   */
> -int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
> +int ubifs_decompress(void *in_buf, int in_len, void *out_buf,
>  		     int *out_len, int compr_type, u8 *crypto_key)
>  {
>  	int err;
> @@ -167,6 +226,13 @@ int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
>  		return -EINVAL;
>  	}
> 
> +	if (crypto_key) {
> +		u8 iv[UBIFS_CRYPTO_KEYSIZE];
> +
> +		memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
> +		ubifs_aes_crypt(in_buf, in_len, crypto_key, iv);
> +	}

So what's the point of defining and initializing 'iv' outside of
'ubifs_aes_crypt()', 2 times? Why not to move it inside?


> +/* 128 bit key size in bytes for UBIFS */
> +#define UBIFS_CRYPTO_KEYSIZE 16
> +/* AES in counter mode is the encryption algorithm. */

Nit-pick: no dot at the end of one-line comment. I've removed it myself,
just keep in mind.

Anyway, too many things. I've modified your patch and pushed, here is
the diff against this patch - please, take a look and take into account.
There are few changes in the comments which I made to make the shorter
as well, not so important.

I've done the following 2 things as well:

1. Move iv to ubifs_aes_crypt()
2. make crypto_key void. In general, if we do not look inside the
pointer, we make it void, because we do not really care about the type,
since we do not look inside.

Please, verify. Below is the resulting patch. Also in the 'joel' branch.

From 8bd3e978c5034f2e4a4017f2453eaa8271487cfa Mon Sep 17 00:00:00 2001
From: Joel Reardon <joel@clambassador.com>
Date: Thu, 12 Apr 2012 16:29:59 +0300
Subject: [PATCH] UBIFS: impliment encryption in the (de)compress functions

This patch adds a function to perform AES encryption. The compress and
decompress routines use this function if they are called with a non-NULL
key parameter. It uses AES counter mode (where encryption and decryption
are the same function) and performs the operation in place on the data. It
uses a default IV of 0, since each key is only evey used to encrypt one
data item the IV does not matter.

The const qualifier was removed from the decompress routine for the
following reason. Encrypted data is not compressable, so compression is
first applied then the result is encrypted. In the reverse, decryption is
first applied and the result decompressed. This means that either the
input buffer for decompression is used to perform an in-place decryption
before decompression, or a third buffer is added and data is copied around.

This was tested by using a static key as the key parameter where both compress
and decompress were called. Data was written and the file system was unmounted
and the mtd dev was scanned with hexdump to make sure that no plaintext data
was available. The drive was remounted and the data successful read back. The
static key was then removed and replaced with NULL, and the same test was done
except that now the data appeared in plaintext on the raw device.

Signed-off-by: Joel Reardon <reardonj@inf.ethz.ch>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
---
 fs/ubifs/compress.c |   67 ++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/ubifs/file.c     |    5 ++-
 fs/ubifs/journal.c  |    6 ++--
 fs/ubifs/ubifs.h    |   11 ++++++--
 4 files changed, 78 insertions(+), 11 deletions(-)

diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c
index 11e4132..7b57457 100644
--- a/fs/ubifs/compress.c
+++ b/fs/ubifs/compress.c
@@ -28,6 +28,7 @@
  */
 
 #include <linux/crypto.h>
+#include <linux/scatterlist.h>
 #include "ubifs.h"
 
 /* Fake description object for the "none" compressor */
@@ -75,6 +76,53 @@ static struct ubifs_compressor zlib_compr = {
 struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
 
 /**
+ * aes_crypt - encrypt / decrypt data.
+ * @str: the data to crypt
+ * @len: length of the data
+ * @crypto_key: the cryptographic key to use to crypt the data
+ *
+ * This function applies AES encryption to the data. It is done in counter
+ * mode, which means that encryption and decryption are the same operation,
+ * i.e., it XORs the same generated bitstream, so it can be used both for
+ * encryption / decryption. Returns zero in case of success and a negative
+ * error code in case of failure.
+ *
+ * WARNING: The operation is done in-place, so @str mutates!
+ */
+static int aes_crypt(void *str, int len, const void *crypto_key)
+{
+	struct crypto_blkcipher *tfm;
+	struct blkcipher_desc desc;
+	struct scatterlist sg;
+	uint8_t iv[UBIFS_CRYPTO_KEYSIZE];
+	int err;
+
+	tfm = crypto_alloc_blkcipher(UBIFS_CRYPTO_ALGORITHM, 0, 0);
+	if (IS_ERR(tfm)) {
+		err = PTR_ERR(tfm);
+		ubifs_err("failed to load transform for aes, error %d", err);
+		return err;
+	}
+
+	err = crypto_blkcipher_setkey(tfm, crypto_key, UBIFS_CRYPTO_KEYSIZE);
+	if (err) {
+		ubifs_err("cannot set the AES key, flags %#x, error %d",
+			  crypto_blkcipher_get_flags(tfm), err);
+		return err;
+	}
+
+	memset(&sg, 0, sizeof(struct scatterlist));
+	sg_set_buf(&sg, str, len);
+	memset(iv, 0, UBIFS_CRYPTO_KEYSIZE);
+	desc.info = iv;
+	desc.tfm = tfm;
+	desc.flags = 0;
+	err = crypto_blkcipher_encrypt(&desc, &sg, &sg, len);
+	crypto_free_blkcipher(tfm);
+	return err;
+}
+
+/**
  * ubifs_compress - compress data.
  * @in_buf: data to compress
  * @in_len: length of the data to compress
@@ -82,6 +130,7 @@ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
  * @out_len: output buffer length is returned here
  * @compr_type: type of compression to use on enter, actually used compression
  *              type on exit
+ * @crypto_key: the encryption key or NULL if no encryption needed
  *
  * This function compresses input buffer @in_buf of length @in_len and stores
  * the result in the output buffer @out_buf and the resulting length in
@@ -93,7 +142,7 @@ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
  * buffer and %UBIFS_COMPR_NONE is returned in @compr_type.
  */
 void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
-		    int *compr_type)
+		    int *compr_type, void *crypto_key)
 {
 	int err;
 	struct ubifs_compressor *compr = ubifs_compressors[*compr_type];
@@ -125,12 +174,18 @@ void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
 	if (in_len - *out_len < UBIFS_MIN_COMPRESS_DIFF)
 		goto no_compr;
 
+	if (crypto_key)
+		aes_crypt(out_buf, *out_len, crypto_key);
+
 	return;
 
 no_compr:
 	memcpy(out_buf, in_buf, in_len);
 	*out_len = in_len;
 	*compr_type = UBIFS_COMPR_NONE;
+
+	if (crypto_key)
+		aes_crypt(out_buf, *out_len, crypto_key);
 }
 
 /**
@@ -140,13 +195,16 @@ no_compr:
  * @out_buf: output buffer where decompressed data should
  * @out_len: output length is returned here
  * @compr_type: type of compression
+ * @crypto_key: the encryption key or %NULL if no encryption needed
  *
  * This function decompresses data from buffer @in_buf into buffer @out_buf.
  * The length of the uncompressed data is returned in @out_len. This functions
  * returns %0 on success or a negative error code on failure.
+ *
+ * WARNING: this function may modify the contents of @in_buf!
  */
-int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
-		     int *out_len, int compr_type)
+int ubifs_decompress(void *in_buf, int in_len, void *out_buf,
+		     int *out_len, int compr_type, void *crypto_key)
 {
 	int err;
 	struct ubifs_compressor *compr;
@@ -163,6 +221,9 @@ int ubifs_decompress(const void *in_buf, int in_len, void *out_buf,
 		return -EINVAL;
 	}
 
+	if (crypto_key)
+		aes_crypt(in_buf, in_len, crypto_key);
+
 	if (compr_type == UBIFS_COMPR_NONE) {
 		memcpy(out_buf, in_buf, in_len);
 		*out_len = in_len;
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 5c8f6dc..e8fa837 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -80,7 +80,7 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,
 	dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
 	out_len = UBIFS_BLOCK_SIZE;
 	err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
-			       le16_to_cpu(dn->compr_type));
+			       le16_to_cpu(dn->compr_type), NULL);
 	if (err || len != out_len)
 		goto dump;
 
@@ -649,7 +649,8 @@ static int populate_page(struct ubifs_info *c, struct page *page,
 			dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
 			out_len = UBIFS_BLOCK_SIZE;
 			err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
-					       le16_to_cpu(dn->compr_type));
+					       le16_to_cpu(dn->compr_type),
+					       NULL);
 			if (err || len != out_len)
 				goto out_err;
 
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 137415e..57c4d2f 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -730,7 +730,7 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
 		compr_type = ui->compr_type;
 
 	out_len = dlen - UBIFS_DATA_NODE_SZ;
-	ubifs_compress(buf, len, &data->data, &out_len, &compr_type);
+	ubifs_compress(buf, len, &data->data, &out_len, &compr_type, NULL);
 	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);
 
 	dlen = UBIFS_DATA_NODE_SZ + out_len;
@@ -1113,11 +1113,11 @@ static int recomp_data_node(struct ubifs_data_node *dn, int *new_len)
 
 	len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
 	compr_type = le16_to_cpu(dn->compr_type);
-	err = ubifs_decompress(&dn->data, len, buf, &out_len, compr_type);
+	err = ubifs_decompress(&dn->data, len, buf, &out_len, compr_type, NULL);
 	if (err)
 		goto out;
 
-	ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type);
+	ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type, NULL);
 	ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);
 	dn->compr_type = cpu_to_le16(compr_type);
 	dn->size = cpu_to_le32(*new_len);
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 7c343e1..ce6d8c2 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -160,6 +160,11 @@
 /* Maximum number of data nodes to bulk-read */
 #define UBIFS_MAX_BULK_READ 32
 
+/* 128 bit key size in bytes for UBIFS */
+#define UBIFS_CRYPTO_KEYSIZE 16
+/* AES in counter mode is the encryption algorithm */
+#define UBIFS_CRYPTO_ALGORITHM "ctr(aes)"
+
 /*
  * Lockdep classes for UBIFS inode @ui_mutex.
  */
@@ -1772,9 +1777,9 @@ long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 int __init ubifs_compressors_init(void);
 void ubifs_compressors_exit(void);
 void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
-		    int *compr_type);
-int ubifs_decompress(const void *buf, int len, void *out, int *out_len,
-		     int compr_type);
+		    int *compr_type, void *crypto_key);
+int ubifs_decompress(void *buf, int len, void *out, int *out_len,
+		     int compr_type, void *crypto_key);
 
 #include "debug.h"
 #include "misc.h"
-- 
1.7.9.1

-- 
Best Regards,
Artem Bityutskiy

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply related	[flat|nested] 55+ messages in thread

end of thread, other threads:[~2012-04-12 14:02 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-09 15:24 [patch] Adding Secure Deletion to UBIFS Joel Reardon
2012-02-13 16:54 ` Artem Bityutskiy
2012-02-23 14:59   ` Joel Reardon
2012-02-23 15:29     ` [patch] Add encryption key parameter to compress/decompress functions Joel Reardon
2012-03-09  7:17       ` Artem Bityutskiy
2012-03-19 16:54         ` [patch] Add design document for UBIFS secure deletion Joel Reardon
2012-03-20 20:10           ` Randy Dunlap
2012-03-21 13:26             ` Joel Reardon
2012-03-21 16:20               ` Artem Bityutskiy
2012-03-21 16:10           ` Artem Bityutskiy
2012-03-23 13:50             ` Joel Reardon
2012-03-23 15:38               ` Artem Bityutskiy
2012-03-23 16:38                 ` Joel Reardon
2012-03-26 15:03                   ` Artem Bityutskiy
2012-02-29 17:09     ` [patch] Adding Secure Deletion to UBIFS Artem Bityutskiy
2012-03-15 14:48     ` [patch] Remove notion of key schemes Joel Reardon
2012-03-16 12:43       ` Artem Bityutskiy
2012-03-16 12:51       ` Artem Bityutskiy
2012-03-16 13:34         ` Joel Reardon
2012-03-16 13:41           ` Artem Bityutskiy
2012-03-16 15:02             ` Joel Reardon
2012-03-19 14:56               ` Artem Bityutskiy
2012-02-20 20:15 ` [patch] Move CRC computation to separate function Joel Reardon
2012-02-29 16:10   ` Artem Bityutskiy
2012-03-19 22:46     ` Joel Reardon
2012-03-23 14:09       ` Artem Bityutskiy
2012-03-23 16:45         ` Joel Reardon
2012-03-23 16:51           ` Artem Bityutskiy
2012-03-25 20:38             ` Joel Reardon
2012-03-26 15:34               ` Artem Bityutskiy
2012-03-25 21:11             ` [patch] Add a encryption key parameter to the compress / decompress function Joel Reardon
2012-03-25 21:38               ` [patch] Add cryptographic functionality when a key is passed to the compress / decompress functions Joel Reardon
2012-03-27  8:33                 ` Artem Bityutskiy
2012-03-29 14:39                   ` [patch] UBIFS: " Joel Reardon
2012-04-02 14:36                     ` Artem Bityutskiy
2012-04-02 14:48                       ` Joel Reardon
2012-04-02 14:57                         ` Artem Bityutskiy
2012-04-02 14:58                           ` Joel Reardon
2012-04-03 10:29                           ` Joel Reardon
2012-04-03 10:41                             ` Guillaume LECERF
2012-04-03 11:35                               ` Joel Reardon
2012-04-12 14:05                                 ` Artem Bityutskiy
2012-03-27  8:27               ` [patch] Add a encryption key parameter to the compress / decompress function Artem Bityutskiy
2012-03-29 14:11                 ` [patch] UBIFS: " Joel Reardon
2012-04-02 14:02                   ` Artem Bityutskiy
2012-02-29 17:25 ` [patch] Adding Secure Deletion to UBIFS Artem Bityutskiy
2012-03-01 13:41   ` Joel Reardon
2012-03-09  7:36     ` Artem Bityutskiy
2012-03-09 19:29       ` Joel Reardon
2012-03-12 13:30         ` Artem Bityutskiy
2012-03-12 13:34           ` Joel Reardon
2012-03-12 13:36           ` Artem Bityutskiy
2012-03-12 13:37             ` Joel Reardon
2012-03-14 10:20             ` Joel Reardon
2012-03-14 10:27               ` Artem Bityutskiy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).