eCryptfs: Enable GCM support in eCryptfs
PATCH 3/3 eCryptfs: Enable GCM support in eCryptfs
This patch adds support for GCM as a cipher mode in eCryptfs. If a file is encrypted in this mode, the layout of the lower file will change to accomodate it. This is due to the need to store auth tags and IVs as well as encrypted data.
The file layout being used is as follows. After the header, the first extent will hold auth tags and IVs. After this metadata extent, data extents are written until the first metadata extent becomes full, at which point another metadata extent is created, and subsequent data extents have auth tags and IVs stored in that new extent until it fills up, etc. The last extent in the file should always be a data extent, except in the case of a zero length upper file, in which case it is the header. This makes it so that the lower file will never have a case where it needs to move extents around or have large gaps in the file.
--- fs/ecryptfs/Kconfig | 1 + fs/ecryptfs/crypto.c | 441 +++++++++++++++++++++++++++++++++++++---- fs/ecryptfs/ecryptfs_kernel.h | 9 +- fs/ecryptfs/inode.c | 18 +- fs/ecryptfs/main.c | 2 +- fs/ecryptfs/super.c | 3 + include/linux/ecryptfs.h | 1 + 7 files changed, 432 insertions(+), 43 deletions(-)
fs/ecryptfs/Kconfig +1 -0
@@ -4,6 +4,7 @@ config ECRYPT_FS select CRYPTO_ECB select CRYPTO_CBC select CRYPTO_MD5 + select CRYPTO_GCM help Encrypted filesystem that operates on the VFS layer. See <file:Documentation/filesystems/ecryptfs.txt> to learn more about
fs/ecryptfs/crypto.c +401 -40
598 lines not on this page · raw message
fs/ecryptfs/ecryptfs_kernel.h +8 -1
@@ -129,6 +129,7 @@ ecryptfs_get_key_payload_data(struct key *key)#define ECRYPTFS_MAX_NUM_ENC_KEYS 64#define ECRYPTFS_MAX_IV_BYTES 16/* 128 bits */ #define ECRYPTFS_SALT_BYTES 2 +#define ECRYPTFS_GCM_TAG_SIZE 16#define MAGIC_ECRYPTFS_MARKER 0x3c81b7f5#define MAGIC_ECRYPTFS_MARKER_SIZE_BYTES 8/* 4*2 */ #define ECRYPTFS_FILE_SIZE_BYTES (sizeof(u64))
@@ -236,7 +237,7 @@ struct ecryptfs_crypt_stat {size_t extent_shift;unsigned int extent_mask;struct ecryptfs_mount_crypt_stat *mount_crypt_stat; -struct crypto_ablkcipher *tfm; +struct crypto_tfm *tfm;struct crypto_hash *hash_tfm;/* Crypto context for generating * the initialization vectors */ unsigned char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE ];
@@ -359,6 +360,12 @@ struct ecryptfs_sb_info {struct backing_dev_info bdi; }; +/* extent metadata (for GCM) */ +struct ecryptfs_extent_metadata { +u8 iv_bytes[ECRYPTFS_MAX_IV_BYTES ]; +u8 auth_tag_bytes[ECRYPTFS_GCM_TAG_SIZE ]; +}; +/* file private data. */ struct ecryptfs_file_info {struct file *wfi_file;
fs/ecryptfs/inode.c +17 -1
@@ -720,15 +720,31 @@ upper_size_to_lower_size(struct ecryptfs_crypt_stat *crypt_stat, loff_t upper_size) {loff_t lower_size; +int mode_code =ecryptfs_code_for_cipher_mode_string ( + crypt_stat->cipher_mode); lower_size =ecryptfs_lower_header_size (crypt_stat);if (upper_size !=0 ) {loff_t num_extents; +int metadata_per_extent; +struct ecryptfs_extent_metadata extent_metadata; num_extents = upper_size >> crypt_stat->extent_shift; + metadata_per_extent = crypt_stat->extent_size + /sizeof (extent_metadata); +if (upper_size & ~crypt_stat->extent_mask) num_extents++; - lower_size += (num_extents * crypt_stat->extent_size); + +if (mode_code ==ECRYPTFS_CIPHER_MODE_GCM ) { + lower_size += + (num_extents + + ((num_extents + metadata_per_extent -1 ) + / metadata_per_extent)) * + crypt_stat->extent_size; + }else { + lower_size += (num_extents * crypt_stat->extent_size); + } }return lower_size; }
fs/ecryptfs/main.c +1 -1
@@ -260,7 +260,7 @@ static void ecryptfs_init_mount_crypt_stat(static int ecryptfs_parse_options (struct ecryptfs_sb_info *sbi,char *options,uid_t *check_ruid) { -const char *mode_white_list[] = {"cbc" }; +const char *mode_white_list[] = {"cbc" ,"gcm" };char *p;int i =0 ;int rc =0 ;
fs/ecryptfs/super.c +3 -0
@@ -164,6 +164,9 @@ static int ecryptfs_show_options(struct seq_file *m, struct dentry *root)seq_printf (m,",ecryptfs_cipher=%s" , mount_crypt_stat->global_default_cipher_name); +seq_printf (m,",ecryptfs_cipher_mode=%s" , + mount_crypt_stat->global_default_cipher_mode_name); +if (mount_crypt_stat->global_default_cipher_key_size)seq_printf (m,",ecryptfs_key_bytes=%zd" , mount_crypt_stat->global_default_cipher_key_size);
include/linux/ecryptfs.h +1 -0
@@ -45,6 +45,7 @@#define RFC2440_CIPHER_RSA 0x01#define ECRYPTFS_CIPHER_MODE_CBC 0x01 +#define ECRYPTFS_CIPHER_MODE_GCM 0x02/** * For convenience, we may need to pass around the encrypted session
Signature · 1 line
1.7.10.4
We got some more feedback on our recent patch from Taylor Hornby, who pointed out the previous issue with IVs. It turns out our solution to that introduced another issue. In CBC mode, the generation of IVs includes the extent offset, which has the effect that if someone reorders the extents in a file, they will fail to decrypt properly. With GCM and the new IV behaviour, there was no such protection from reordering of extents within a file.
Our solution to this is to set associated data to include the extent number, which will cause integrity checks to fail and errors to be returned if extents are reordered within a file. The following patch should replace 3/3 in this patchset.
This patch adds support for GCM as a cipher mode in eCryptfs. If a file is encrypted in this mode, the layout of the lower file will change to accomodate it. This is due to the need to store auth tags and IVs as well as encrypted data.
The file layout being used is as follows. After the header, the first extent will hold IVs and auth tags. After this metadata extent, data extents are written until the first metadata extent becomes full, at which point another metadata extent is created, and subsequent data extents have auth tags and IVs stored in that new extent until it fills up, etc. The last extent in the file should always be a data extent, except in the case of a zero length upper file, in which case it is the header. This makes it so that the lower file will never have a case where it needs to move extents around or have large gaps in the file.
--- fs/ecryptfs/Kconfig | 1 + fs/ecryptfs/crypto.c | 483 +++++++++++++++++++++++++++++++++++++++--- fs/ecryptfs/ecryptfs_kernel.h | 9 +- fs/ecryptfs/inode.c | 18 +- fs/ecryptfs/main.c | 2 +- fs/ecryptfs/super.c | 3 + include/linux/ecryptfs.h | 1 + 7 files changed, 479 insertions(+), 38 deletions(-)
fs/ecryptfs/Kconfig +1 -0
@@ -4,6 +4,7 @@ config ECRYPT_FS select CRYPTO_ECB select CRYPTO_CBC select CRYPTO_MD5 + select CRYPTO_GCM help Encrypted filesystem that operates on the VFS layer. See <file:Documentation/filesystems/ecryptfs.txt> to learn more about
fs/ecryptfs/crypto.c +448 -35
631 lines not on this page · raw message
fs/ecryptfs/ecryptfs_kernel.h +8 -1
@@ -129,6 +129,7 @@ ecryptfs_get_key_payload_data(struct key *key)#define ECRYPTFS_MAX_NUM_ENC_KEYS 64#define ECRYPTFS_MAX_IV_BYTES 16/* 128 bits */ #define ECRYPTFS_SALT_BYTES 2 +#define ECRYPTFS_GCM_TAG_SIZE 16#define MAGIC_ECRYPTFS_MARKER 0x3c81b7f5#define MAGIC_ECRYPTFS_MARKER_SIZE_BYTES 8/* 4*2 */ #define ECRYPTFS_FILE_SIZE_BYTES (sizeof(u64))
@@ -236,7 +237,7 @@ struct ecryptfs_crypt_stat {size_t extent_shift;unsigned int extent_mask;struct ecryptfs_mount_crypt_stat *mount_crypt_stat; -struct crypto_ablkcipher *tfm; +struct crypto_tfm *tfm;struct crypto_hash *hash_tfm;/* Crypto context for generating * the initialization vectors */ unsigned char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE ];
@@ -359,6 +360,12 @@ struct ecryptfs_sb_info {struct backing_dev_info bdi; }; +/* extent metadata (for GCM) */ +struct ecryptfs_extent_metadata { +u8 iv_bytes[ECRYPTFS_MAX_IV_BYTES ]; +u8 auth_tag_bytes[ECRYPTFS_GCM_TAG_SIZE ]; +}; +/* file private data. */ struct ecryptfs_file_info {struct file *wfi_file;
fs/ecryptfs/inode.c +17 -1
@@ -720,15 +720,31 @@ upper_size_to_lower_size(struct ecryptfs_crypt_stat *crypt_stat, loff_t upper_size) {loff_t lower_size; +int mode_code =ecryptfs_code_for_cipher_mode_string ( + crypt_stat->cipher_mode); lower_size =ecryptfs_lower_header_size (crypt_stat);if (upper_size !=0 ) {loff_t num_extents; +int metadata_per_extent; +struct ecryptfs_extent_metadata extent_metadata; num_extents = upper_size >> crypt_stat->extent_shift; + metadata_per_extent = crypt_stat->extent_size + /sizeof (extent_metadata); +if (upper_size & ~crypt_stat->extent_mask) num_extents++; - lower_size += (num_extents * crypt_stat->extent_size); + +if (mode_code ==ECRYPTFS_CIPHER_MODE_GCM ) { + lower_size += + (num_extents + + ((num_extents + metadata_per_extent -1 ) + / metadata_per_extent)) * + crypt_stat->extent_size; + }else { + lower_size += (num_extents * crypt_stat->extent_size); + } }return lower_size; }
fs/ecryptfs/main.c +1 -1
@@ -260,7 +260,7 @@ static void ecryptfs_init_mount_crypt_stat(static int ecryptfs_parse_options (struct ecryptfs_sb_info *sbi,char *options,uid_t *check_ruid) { -const char *mode_white_list[] = {"cbc" }; +const char *mode_white_list[] = {"cbc" ,"gcm" };char *p;int i =0 ;int rc =0 ;
fs/ecryptfs/super.c +3 -0
@@ -164,6 +164,9 @@ static int ecryptfs_show_options(struct seq_file *m, struct dentry *root)seq_printf (m,",ecryptfs_cipher=%s" , mount_crypt_stat->global_default_cipher_name); +seq_printf (m,",ecryptfs_cipher_mode=%s" , + mount_crypt_stat->global_default_cipher_mode_name); +if (mount_crypt_stat->global_default_cipher_key_size)seq_printf (m,",ecryptfs_key_bytes=%zd" , mount_crypt_stat->global_default_cipher_key_size);
include/linux/ecryptfs.h +1 -0
@@ -45,6 +45,7 @@#define RFC2440_CIPHER_RSA 0x01#define ECRYPTFS_CIPHER_MODE_CBC 0x01 +#define ECRYPTFS_CIPHER_MODE_GCM 0x02/** * For convenience, we may need to pass around the encrypted session
Signature · 1 line
1.8.3.2