Hello i'm not expert with cryptographic modules, but i played sometime with the "authenc" module, which is AEAD, and i applied it to the filesystem "ecryptfs". In the forwarded message i added a brief description of what i do and i hope this can be a useful example for you.
--- Begin Message ---
- Subject: Authenc support for ecryptfs
- From: Roberto Sassu <roberto.sassu@xxxxxxxxx>
- Date: Fri, 12 Mar 2010 11:42:37 +0100
- User-agent: KMail/1.13.0 (Linux/2.6.32.9-70.fc12.x86_64; KDE/4.4.0; x86_64; ; )
Hello all i developed a patch for ecryptfs in order to take advantage of the authenc module. This permits to use encryption or encryption with authentication with one single tfm. In order to use authentication i made the following changes: - Generation of an authentication key (for now only 16-bit keys are supported); - Authentication algorithm tested: "hmac(sha1)" and "digest_null"; - modified the file format, adding 20 bytes after each extent; - modified the tag 3 packet format in order to specify if authentication on data must be checked; - added parameter "ecryptfs_auth" and "ecryptfs_auth_key_bytes" parameters to mount to specify the algorithm and the size of the key will be used to perform authentication on data; - backward compatibility with the current ecryptfs code; - no integrity verification on the header for now. Just a remark: the patch must be applied to the latest kernel (2.6.34-rc1) because the 2.6.33 has a bug in the authenc module.diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 7cb0a59..d42a59d 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -33,19 +33,68 @@ #include <linux/crypto.h> #include <linux/file.h> #include <linux/scatterlist.h> +#include <crypto/authenc.h> +#include <linux/rtnetlink.h> #include <asm/unaligned.h> #include "ecryptfs_kernel.h" static int ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, + struct page *src_page, int src_offset, int size, unsigned char *auth, unsigned char *iv); static int ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, + struct page *src_page, int src_offset, int size, unsigned char *auth, unsigned char *iv); +/* + * struct for async completion + */ +struct crypto_async_completion { + struct completion completion; + int err; +}; + +/* + * callback for handling the completion of an async request + */ +void crypto_async_request_cb_complete(struct crypto_async_request *req, int err) +{ + struct crypto_async_completion *res = req->data; + + if (err == -EINPROGRESS) + return; + + res->err = err; + complete(&res->completion); +} + +/* + * wait completion of async request + */ +int crypto_async_wait_for_completion(struct crypto_async_completion *ahash_result, int cur_ret) +{ + int ret = cur_ret; + + switch (ret) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + ret = wait_for_completion_interruptible(&ahash_result->completion); + if (!ret && !(ret = ahash_result->err)) { + INIT_COMPLETION(ahash_result->completion); + break; + } + /* fall through */ + default: + /* error */ + break; + } + + return ret; +} /** * ecryptfs_to_hex @@ -144,21 +193,29 @@ out: static int ecryptfs_crypto_api_algify_cipher_name(char **algified_name, char *cipher_name, - char *chaining_modifier) + char *chaining_modifier, char *auth_name) { int cipher_name_len = strlen(cipher_name); int chaining_modifier_len = strlen(chaining_modifier); + int auth_name_len = auth_name ? strlen(auth_name) : 0; int algified_name_len; int rc; - algified_name_len = (chaining_modifier_len + cipher_name_len + 3); + if(auth_name_len) + algified_name_len = (7 + 3 + auth_name_len + chaining_modifier_len + cipher_name_len + 3); + else + algified_name_len = (chaining_modifier_len + cipher_name_len + 3); (*algified_name) = kmalloc(algified_name_len, GFP_KERNEL); if (!(*algified_name)) { rc = -ENOMEM; goto out; } - snprintf((*algified_name), algified_name_len, "%s(%s)", - chaining_modifier, cipher_name); + if(auth_name_len) + snprintf((*algified_name), algified_name_len, "authenc(%s,%s(%s))", auth_name, + chaining_modifier, cipher_name); + else + snprintf((*algified_name), algified_name_len, "%s(%s)", + chaining_modifier, cipher_name); rc = 0; out: return rc; @@ -242,7 +299,7 @@ void ecryptfs_destroy_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat) struct ecryptfs_key_sig *key_sig, *key_sig_tmp; if (crypt_stat->tfm) - crypto_free_blkcipher(crypt_stat->tfm); + crypto_free_aead(crypt_stat->tfm); if (crypt_stat->hash_tfm) crypto_free_hash(crypt_stat->hash_tfm); list_for_each_entry_safe(key_sig, key_sig_tmp, @@ -337,11 +394,15 @@ static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, struct scatterlist *src_sg, int size, unsigned char *iv) { - struct blkcipher_desc desc = { - .tfm = crypt_stat->tfm, - .info = iv, - .flags = CRYPTO_TFM_REQ_MAY_SLEEP - }; + struct aead_request *req = NULL; + struct crypto_async_completion result; + unsigned char *assoc = NULL; + struct scatterlist asg; + struct rtattr *rta; + unsigned char *p; + int keylen = 0; + unsigned char *key = NULL; + struct crypto_authenc_key_param *param; int rc = 0; BUG_ON(!crypt_stat || !crypt_stat->tfm @@ -352,24 +413,102 @@ static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, ecryptfs_dump_hex(crypt_stat->key, crypt_stat->key_size); } + /* Consider doing this once, when the file is opened */ mutex_lock(&crypt_stat->cs_tfm_mutex); + + init_completion(&result.completion); + + req = aead_request_alloc(crypt_stat->tfm, GFP_KERNEL); + if (!req) { + ecryptfs_printk(KERN_ERR, "Failed to allocate request; [%d]\n", + rc); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + rc = -EINVAL; + return rc; + } + + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_async_request_cb_complete, &result); + if (!(crypt_stat->flags & ECRYPTFS_KEY_SET)) { - rc = crypto_blkcipher_setkey(crypt_stat->tfm, crypt_stat->key, - crypt_stat->key_size); + keylen = crypt_stat->key_size + crypt_stat->auth_key_size + RTA_SPACE(sizeof(*param)); + key = kzalloc(keylen, GFP_KERNEL); + if(!key) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Out of memory\n"); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + goto out; + } + + p = key; + rta = (void *)p; + rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM; + rta->rta_len = RTA_LENGTH(sizeof(*param)); + param = RTA_DATA(rta); + p += RTA_SPACE(sizeof(*param)); + + if(crypt_stat->auth_key_size) { + memcpy(p, crypt_stat->key, crypt_stat->auth_key_size); + p += crypt_stat->auth_key_size; + } + + param->enckeylen = cpu_to_be32(crypt_stat->key_size); + memcpy(p, crypt_stat->key + crypt_stat->auth_key_size, crypt_stat->key_size); + + rc = crypto_aead_setkey(crypt_stat->tfm, key, keylen); + if (rc) { + ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n", + rc); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + rc = -EINVAL; + goto out; + } crypt_stat->flags |= ECRYPTFS_KEY_SET; } - if (rc) { - ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n", - rc); + + if(crypt_stat->authsize) { + rc = crypto_aead_setauthsize(crypt_stat->tfm, crypt_stat->authsize); + if (rc) { + ecryptfs_printk(KERN_ERR, "alg: aead: Failed to set " + "authsize to %u\n", crypt_stat->authsize); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + rc = -EINVAL; + goto out; + } + } + + ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes.\n", size); + + assoc = kstrdup(ECRYPTFS_ASSOC_STRING, GFP_KERNEL); + if(!assoc) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Out of memory\n"); mutex_unlock(&crypt_stat->cs_tfm_mutex); - rc = -EINVAL; goto out; } - ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes.\n", size); - crypto_blkcipher_encrypt_iv(&desc, dest_sg, src_sg, size); + + sg_init_one(&asg, assoc, ECRYPTFS_ASSOC_LEN); + + aead_request_set_crypt(req, src_sg, dest_sg, size, iv); + aead_request_set_assoc(req, &asg, ECRYPTFS_ASSOC_LEN); + + rc = crypto_aead_encrypt(req); + if(rc == -EBUSY || rc == -EINPROGRESS) { + rc = wait_for_completion_interruptible(&result.completion); + if (!rc && !(rc = result.err)) { + INIT_COMPLETION(result.completion); + } + } + mutex_unlock(&crypt_stat->cs_tfm_mutex); out: + if(assoc) + kfree(assoc); + if(key) + kfree(key); + + aead_request_free(req); return rc; } @@ -381,8 +520,8 @@ out: static void ecryptfs_lower_offset_for_extent(loff_t *offset, loff_t extent_num, struct ecryptfs_crypt_stat *crypt_stat) { - (*offset) = (crypt_stat->num_header_bytes_at_front - + (crypt_stat->extent_size * extent_num)); + (*offset) = (crypt_stat->num_header_bytes_at_front + crypt_stat->authsize + + ((crypt_stat->extent_size + crypt_stat->authsize) * extent_num)); } /** @@ -400,7 +539,7 @@ static void ecryptfs_lower_offset_for_extent(loff_t *offset, loff_t extent_num, */ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, struct ecryptfs_crypt_stat *crypt_stat, - struct page *page, + struct page *page, unsigned char *auth, unsigned long extent_offset) { loff_t extent_base; @@ -432,7 +571,7 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, rc = ecryptfs_encrypt_page_offset(crypt_stat, enc_extent_page, 0, page, (extent_offset * crypt_stat->extent_size), - crypt_stat->extent_size, extent_iv); + crypt_stat->extent_size, auth, extent_iv); if (rc < 0) { printk(KERN_ERR "%s: Error attempting to encrypt page with " "page->index = [%ld], extent_offset = [%ld]; " @@ -448,6 +587,8 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " "encryption:\n"); ecryptfs_dump_hex((char *)(page_address(enc_extent_page)), 8); + ecryptfs_printk(KERN_DEBUG, "Auth: \n"); + ecryptfs_dump_hex(auth, crypt_stat->authsize); } out: return rc; @@ -477,7 +618,8 @@ int ecryptfs_encrypt_page(struct page *page) struct page *enc_extent_page = NULL; loff_t extent_offset; int rc = 0; - + unsigned char *auth = NULL; + ecryptfs_inode = page->mapping->host; crypt_stat = &(ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat); @@ -490,34 +632,57 @@ int ecryptfs_encrypt_page(struct page *page) goto out; } enc_extent_virt = kmap(enc_extent_page); + if(crypt_stat->authsize) { + auth = kzalloc(crypt_stat->authsize, GFP_KERNEL); + if(!auth) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Error allocating memory for " + "auth\n"); + goto out; + } + } for (extent_offset = 0; extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size); extent_offset++) { loff_t offset; - rc = ecryptfs_encrypt_extent(enc_extent_page, crypt_stat, page, + rc = ecryptfs_encrypt_extent(enc_extent_page, crypt_stat, page, auth, extent_offset); if (rc) { printk(KERN_ERR "%s: Error encrypting extent; " "rc = [%d]\n", __func__, rc); goto out; } + ecryptfs_lower_offset_for_extent( &offset, ((((loff_t)page->index) * (PAGE_CACHE_SIZE / crypt_stat->extent_size)) + extent_offset), crypt_stat); + rc = ecryptfs_write_lower(ecryptfs_inode, enc_extent_virt, - offset, crypt_stat->extent_size); + offset, crypt_stat->extent_size); if (rc < 0) { ecryptfs_printk(KERN_ERR, "Error attempting " "to write lower page; rc = [%d]" "\n", rc); goto out; } + if(crypt_stat->authsize) { + rc = ecryptfs_write_lower(ecryptfs_inode, auth, + offset + crypt_stat->extent_size, crypt_stat->authsize); + if (rc < 0) { + ecryptfs_printk(KERN_ERR, "Error attempting " + "to write auth; rc = [%d]" + "\n", rc); + goto out; + } + } } rc = 0; out: + if (auth) + kfree(auth); if (enc_extent_page) { kunmap(enc_extent_page); __free_page(enc_extent_page); @@ -527,7 +692,7 @@ out: static int ecryptfs_decrypt_extent(struct page *page, struct ecryptfs_crypt_stat *crypt_stat, - struct page *enc_extent_page, + struct page *enc_extent_page, unsigned char *auth, unsigned long extent_offset) { loff_t extent_base; @@ -555,12 +720,14 @@ static int ecryptfs_decrypt_extent(struct page *page, (page_address(enc_extent_page) + (extent_offset * crypt_stat->extent_size)), 8); + ecryptfs_printk(KERN_DEBUG, "Auth: \n"); + ecryptfs_dump_hex(auth, crypt_stat->authsize); } rc = ecryptfs_decrypt_page_offset(crypt_stat, page, (extent_offset * crypt_stat->extent_size), enc_extent_page, 0, - crypt_stat->extent_size, extent_iv); + crypt_stat->extent_size, auth, extent_iv); if (rc < 0) { printk(KERN_ERR "%s: Error attempting to decrypt to page with " "page->index = [%ld], extent_offset = [%ld]; " @@ -607,6 +774,7 @@ int ecryptfs_decrypt_page(struct page *page) struct page *enc_extent_page = NULL; unsigned long extent_offset; int rc = 0; + unsigned char *auth = NULL; ecryptfs_inode = page->mapping->host; crypt_stat = @@ -619,6 +787,15 @@ int ecryptfs_decrypt_page(struct page *page) "encrypted extent\n"); goto out; } + if(crypt_stat->authsize) { + auth = kzalloc(crypt_stat->authsize, GFP_KERNEL); + if(!auth) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Error allocating memory for " + "auth\n"); + goto out; + } + } enc_extent_virt = kmap(enc_extent_page); for (extent_offset = 0; extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size); @@ -629,6 +806,7 @@ int ecryptfs_decrypt_page(struct page *page) &offset, ((page->index * (PAGE_CACHE_SIZE / crypt_stat->extent_size)) + extent_offset), crypt_stat); + rc = ecryptfs_read_lower(enc_extent_virt, offset, crypt_stat->extent_size, ecryptfs_inode); @@ -638,7 +816,22 @@ int ecryptfs_decrypt_page(struct page *page) "\n", rc); goto out; } - rc = ecryptfs_decrypt_extent(page, crypt_stat, enc_extent_page, + if(!rc) /* No need to decrypt extent if there's nothing to read from the lower file */ + break; + + if(crypt_stat->authsize) { + rc = ecryptfs_read_lower(auth, offset + + crypt_stat->extent_size, crypt_stat->authsize, + ecryptfs_inode); + if (rc < 0) { + ecryptfs_printk(KERN_ERR, "Error attempting " + "to read lower page auth; rc = [%d]" + "\n", rc); + goto out; + } + } + + rc = ecryptfs_decrypt_extent(page, crypt_stat, enc_extent_page, auth, extent_offset); if (rc) { printk(KERN_ERR "%s: Error encrypting extent; " @@ -647,6 +840,8 @@ int ecryptfs_decrypt_page(struct page *page) } } out: + if (auth) + kfree(auth); if (enc_extent_page) { kunmap(enc_extent_page); __free_page(enc_extent_page); @@ -669,27 +864,101 @@ static int decrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, struct scatterlist *src_sg, int size, unsigned char *iv) { - struct blkcipher_desc desc = { - .tfm = crypt_stat->tfm, - .info = iv, - .flags = CRYPTO_TFM_REQ_MAY_SLEEP - }; + struct aead_request *req = NULL; + struct crypto_async_completion result; + unsigned char *assoc = NULL; + struct scatterlist asg; + struct rtattr *rta; + unsigned char *p; + int keylen = 0; + unsigned char *key = NULL; + struct crypto_authenc_key_param *param; int rc = 0; /* Consider doing this once, when the file is opened */ mutex_lock(&crypt_stat->cs_tfm_mutex); - rc = crypto_blkcipher_setkey(crypt_stat->tfm, crypt_stat->key, - crypt_stat->key_size); + + init_completion(&result.completion); + + req = aead_request_alloc(crypt_stat->tfm, GFP_KERNEL); + if (!req) { + rc = PTR_ERR(req); + ecryptfs_printk(KERN_ERR, "Failed to allocate request; [%d]\n", rc); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + return rc; + } + + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_async_request_cb_complete, &result); + + keylen = crypt_stat->key_size + crypt_stat->auth_key_size + RTA_SPACE(sizeof(*param)); + + key = kzalloc(keylen, GFP_KERNEL); + if(!key) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Out of memory\n"); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + goto out; + } + + p = key; + rta = (void *)p; + rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM; + rta->rta_len = RTA_LENGTH(sizeof(*param)); + param = RTA_DATA(rta); + p += RTA_SPACE(sizeof(*param)); + + if(crypt_stat->auth_key_size) { + memcpy(p, crypt_stat->key, crypt_stat->auth_key_size); + p += crypt_stat->auth_key_size; + } + + param->enckeylen = cpu_to_be32(crypt_stat->key_size); + memcpy(p, crypt_stat->key + crypt_stat->auth_key_size, crypt_stat->key_size); + + rc = crypto_aead_setkey(crypt_stat->tfm, key, keylen); if (rc) { ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n", rc); mutex_unlock(&crypt_stat->cs_tfm_mutex); - rc = -EINVAL; goto out; } + + if(crypt_stat->authsize) { + rc = crypto_aead_setauthsize(crypt_stat->tfm, crypt_stat->authsize); + if (rc) { + ecryptfs_printk(KERN_ERR, "alg: aead: Failed to set " + "authsize to %u\n", crypt_stat->authsize); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + goto out; + } + } + ecryptfs_printk(KERN_DEBUG, "Decrypting [%d] bytes.\n", size); - rc = crypto_blkcipher_decrypt_iv(&desc, dest_sg, src_sg, size); + + assoc = kstrdup(ECRYPTFS_ASSOC_STRING, GFP_KERNEL); + if(!assoc) { + rc = -ENOMEM; + ecryptfs_printk(KERN_ERR, "Out of memory\n"); + mutex_unlock(&crypt_stat->cs_tfm_mutex); + goto out; + } + + sg_init_one(&asg, assoc, ECRYPTFS_ASSOC_LEN); + + aead_request_set_crypt(req, src_sg, dest_sg, size + crypt_stat->authsize, iv); + aead_request_set_assoc(req, &asg, ECRYPTFS_ASSOC_LEN); + + rc = crypto_aead_decrypt(req); + if(rc == -EBUSY || rc == -EINPROGRESS) { + rc = wait_for_completion_interruptible(&result.completion); + if (!rc && !(rc = result.err)) { + INIT_COMPLETION(result.completion); + } + } + mutex_unlock(&crypt_stat->cs_tfm_mutex); + if (rc) { ecryptfs_printk(KERN_ERR, "Error decrypting; rc = [%d]\n", rc); @@ -697,6 +966,12 @@ static int decrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, } rc = size; out: + if(assoc) + kfree(assoc); + if(key) + kfree(key); + + aead_request_free(req); return rc; } @@ -715,17 +990,22 @@ out: static int ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, + struct page *src_page, int src_offset, int size, unsigned char *auth, unsigned char *iv) { - struct scatterlist src_sg, dst_sg; - - sg_init_table(&src_sg, 1); - sg_init_table(&dst_sg, 1); - - sg_set_page(&src_sg, src_page, size, src_offset); - sg_set_page(&dst_sg, dst_page, size, dst_offset); - return encrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); + struct scatterlist src_sg[2], dst_sg[2]; + + sg_init_table(src_sg, 2); + sg_init_table(dst_sg, 2); + + sg_set_page(&src_sg[0], src_page, size, src_offset); + if(auth) + sg_set_buf(&src_sg[1], auth, crypt_stat->authsize); + sg_set_page(&dst_sg[0], dst_page, size, dst_offset); + if(auth) + sg_set_buf(&dst_sg[1], auth, crypt_stat->authsize); + + return encrypt_scatterlist(crypt_stat, dst_sg, src_sg, size, iv); } /** @@ -743,18 +1023,22 @@ ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, static int ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, + struct page *src_page, int src_offset, int size, unsigned char *auth, unsigned char *iv) { - struct scatterlist src_sg, dst_sg; - - sg_init_table(&src_sg, 1); - sg_set_page(&src_sg, src_page, size, src_offset); - - sg_init_table(&dst_sg, 1); - sg_set_page(&dst_sg, dst_page, size, dst_offset); - - return decrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); + struct scatterlist src_sg[2], dst_sg[2]; + + sg_init_table(src_sg, 2); + sg_set_page(&src_sg[0], src_page, size, src_offset); + if(auth) + sg_set_buf(&src_sg[1], auth, crypt_stat->authsize); + + sg_init_table(dst_sg, 2); + sg_set_page(&dst_sg[0], dst_page, size, dst_offset); + if(auth) + sg_set_buf(&dst_sg[1], auth, crypt_stat->authsize); + + return decrypt_scatterlist(crypt_stat, dst_sg, src_sg, size, iv); } #define ECRYPTFS_MAX_SCATTERLIST_LEN 4 @@ -772,7 +1056,7 @@ int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat) { char *full_alg_name; int rc = -EINVAL; - + if (!crypt_stat->cipher) { ecryptfs_printk(KERN_ERR, "No cipher specified\n"); goto out; @@ -782,17 +1066,28 @@ int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat) "key_size_bits = [%d]\n", crypt_stat->cipher, (int)strlen(crypt_stat->cipher), crypt_stat->key_size << 3); + + ecryptfs_printk(KERN_DEBUG, + "Initializing auth [%s]; strlen = [%d]; " + "key_size_bits = [%d]\n", + crypt_stat->auth, (int)strlen(crypt_stat->auth), + crypt_stat->auth_key_size << 3 + ); if (crypt_stat->tfm) { rc = 0; goto out; } mutex_lock(&crypt_stat->cs_tfm_mutex); - rc = ecryptfs_crypto_api_algify_cipher_name(&full_alg_name, - crypt_stat->cipher, "cbc"); + if(strcmp(crypt_stat->cipher, "cipher_null") == 0) + rc = ecryptfs_crypto_api_algify_cipher_name(&full_alg_name, + crypt_stat->cipher, "ecb", crypt_stat->auth); + else + rc = ecryptfs_crypto_api_algify_cipher_name(&full_alg_name, + crypt_stat->cipher, "cbc", crypt_stat->auth); if (rc) goto out_unlock; - crypt_stat->tfm = crypto_alloc_blkcipher(full_alg_name, 0, - CRYPTO_ALG_ASYNC); + crypt_stat->tfm = crypto_alloc_aead(full_alg_name, 0, 0); + kfree(full_alg_name); if (IS_ERR(crypt_stat->tfm)) { rc = PTR_ERR(crypt_stat->tfm); @@ -802,7 +1097,10 @@ int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat) crypt_stat->cipher); goto out_unlock; } - crypto_blkcipher_set_flags(crypt_stat->tfm, CRYPTO_TFM_REQ_WEAK_KEY); + crypto_aead_clear_flags(crypt_stat->tfm, ~0); + crypto_aead_set_flags(crypt_stat->tfm, CRYPTO_TFM_REQ_WEAK_KEY); + + crypt_stat->authsize = crypto_aead_authsize(crypt_stat->tfm); rc = 0; out_unlock: mutex_unlock(&crypt_stat->cs_tfm_mutex); @@ -881,12 +1179,15 @@ out: static void ecryptfs_generate_new_key(struct ecryptfs_crypt_stat *crypt_stat) { - get_random_bytes(crypt_stat->key, crypt_stat->key_size); + get_random_bytes(crypt_stat->key, crypt_stat->auth_key_size + crypt_stat->key_size); crypt_stat->flags |= ECRYPTFS_KEY_VALID; ecryptfs_compute_root_iv(crypt_stat); if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Generated new session key:\n"); + ecryptfs_printk(KERN_DEBUG, "Generated new auth key:\n"); ecryptfs_dump_hex(crypt_stat->key, + crypt_stat->auth_key_size); + ecryptfs_printk(KERN_DEBUG, "Generated new session key:\n"); + ecryptfs_dump_hex(crypt_stat->key + crypt_stat->auth_key_size, crypt_stat->key_size); } } @@ -994,6 +1295,7 @@ int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry) &ecryptfs_superblock_to_private( ecryptfs_dentry->d_sb)->mount_crypt_stat; int cipher_name_len; + int auth_name_len; int rc = 0; ecryptfs_set_default_crypt_stat_vals(crypt_stat, mount_crypt_stat); @@ -1013,8 +1315,19 @@ int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry) mount_crypt_stat->global_default_cipher_name, cipher_name_len); crypt_stat->cipher[cipher_name_len] = '\0'; + memcpy(crypt_stat->cipher, + mount_crypt_stat->global_default_cipher_name, + cipher_name_len); crypt_stat->key_size = mount_crypt_stat->global_default_cipher_key_size; + auth_name_len = + strlen(mount_crypt_stat->global_default_auth_name); + memcpy(crypt_stat->auth, + mount_crypt_stat->global_default_auth_name, + auth_name_len); + crypt_stat->auth[auth_name_len] = '\0'; + crypt_stat->auth_key_size = + mount_crypt_stat->global_default_auth_key_size; ecryptfs_generate_new_key(crypt_stat); rc = ecryptfs_init_crypt_ctx(crypt_stat); if (rc) @@ -1141,7 +1454,8 @@ ecryptfs_cipher_code_str_map[] = { {"twofish", RFC2440_CIPHER_TWOFISH}, {"cast6", RFC2440_CIPHER_CAST_6}, {"aes", RFC2440_CIPHER_AES_192}, - {"aes", RFC2440_CIPHER_AES_256} + {"aes", RFC2440_CIPHER_AES_256}, + {"cipher_null", ECRYPTFS_CRYPTO_NULL_CODE} }; /** @@ -1314,9 +1628,14 @@ ecryptfs_write_metadata_to_contents(struct dentry *ecryptfs_dentry, char *virt, size_t virt_len) { int rc; - + char auth[ECRYPTFS_AUTHSIZE]; + rc = ecryptfs_write_lower(ecryptfs_dentry->d_inode, virt, 0, virt_len); + + rc = ecryptfs_write_lower(ecryptfs_dentry->d_inode, auth, + virt_len, ECRYPTFS_AUTHSIZE); + if (rc < 0) printk(KERN_ERR "%s: Error attempting to write header " "information to lower file; rc = [%d]\n", __func__, rc); @@ -1582,6 +1901,8 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) { int rc = 0; char *page_virt = NULL; + char auth[ECRYPTFS_AUTHSIZE]; + struct inode *ecryptfs_inode = ecryptfs_dentry->d_inode; struct ecryptfs_crypt_stat *crypt_stat = &ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat; @@ -1601,6 +1922,10 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry) } rc = ecryptfs_read_lower(page_virt, 0, crypt_stat->extent_size, ecryptfs_inode); + + rc = ecryptfs_read_lower(auth, crypt_stat->extent_size, ECRYPTFS_AUTHSIZE, + ecryptfs_inode); + if (rc >= 0) rc = ecryptfs_read_headers_virt(page_virt, crypt_stat, ecryptfs_dentry, @@ -1759,7 +2084,7 @@ ecryptfs_process_key_cipher(struct crypto_blkcipher **key_tfm, goto out; } rc = ecryptfs_crypto_api_algify_cipher_name(&full_alg_name, cipher_name, - "ecb"); + "ecb", NULL); if (rc) goto out; *key_tfm = crypto_alloc_blkcipher(full_alg_name, 0, CRYPTO_ALG_ASYNC); diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index 542f625..73b2ab3 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -96,6 +96,9 @@ #define RFC2440_CIPHER_RSA 0x01 +#define ECRYPTFS_HMAC_AUTH_MASK 0x10 +#define ECRYPTFS_CRYPTO_NULL_CODE 0x0c + /** * For convenience, we may need to pass around the encrypted session * key between kernel and userspace because the authentication token @@ -194,6 +197,7 @@ ecryptfs_get_key_payload_data(struct key *key) #define ECRYPTFS_SUPER_MAGIC 0xf15f #define ECRYPTFS_MAX_KEYSET_SIZE 1024 #define ECRYPTFS_MAX_CIPHER_NAME_SIZE 32 +#define ECRYPTFS_MAX_AUTH_NAME_SIZE 32 #define ECRYPTFS_MAX_NUM_ENC_KEYS 64 #define ECRYPTFS_MAX_IV_BYTES 16 /* 128 bits */ #define ECRYPTFS_SALT_BYTES 2 @@ -203,6 +207,8 @@ ecryptfs_get_key_payload_data(struct key *key) #define ECRYPTFS_DEFAULT_CIPHER "aes" #define ECRYPTFS_DEFAULT_KEY_BYTES 16 #define ECRYPTFS_DEFAULT_HASH "md5" +#define ECRYPTFS_DEFAULT_AUTH "digest_null" +#define ECRYPTFS_DEFAULT_AUTH_KEY_BYTES 0 #define ECRYPTFS_TAG_70_DIGEST ECRYPTFS_DEFAULT_HASH #define ECRYPTFS_TAG_1_PACKET_TYPE 0x01 #define ECRYPTFS_TAG_3_PACKET_TYPE 0x8C @@ -230,6 +236,14 @@ ecryptfs_get_key_payload_data(struct key *key) #define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX "ECRYPTFS_FNEK_ENCRYPTED." #define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE 24 #define ECRYPTFS_ENCRYPTED_DENTRY_NAME_LEN (18 + 1 + 4 + 1 + 32) +#define ECRYPTFS_ASSOC_STRING "\x49\x5c\x50\x1f\x1d\x94\xcc\x81 \ + \xba\xb7\xb6\x03\xaf\xa5\xc1\xa1 \ + \xd8\x5c\x42\x68\xe0\x6c\xda\x89 \ + \x05\xac\x56\xac\x1b\x2a\xd3\x86" +#define ECRYPTFS_ASSOC_LEN 32 +#define ECRYPTFS_AUTHSIZE 20 +#define ECRYPTFS_FIXED_AUTH_KEY_SIZE 16 +#define ECRYPTFS_FIXED_AUTH_NAME "hmac(sha1)" struct ecryptfs_key_sig { struct list_head crypt_stat_list; @@ -276,13 +290,16 @@ struct ecryptfs_crypt_stat { size_t num_header_bytes_at_front; size_t extent_size; /* Data extent size; default is 4096 */ size_t key_size; + size_t auth_key_size; + size_t authsize; size_t extent_shift; unsigned int extent_mask; struct ecryptfs_mount_crypt_stat *mount_crypt_stat; - struct crypto_blkcipher *tfm; + struct crypto_aead *tfm; struct crypto_hash *hash_tfm; /* Crypto context for generating * the initialization vectors */ unsigned char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE]; + unsigned char auth[ECRYPTFS_MAX_AUTH_NAME_SIZE]; unsigned char key[ECRYPTFS_MAX_KEY_BYTES]; unsigned char root_iv[ECRYPTFS_MAX_IV_BYTES]; struct list_head keysig_list; @@ -381,11 +398,14 @@ struct ecryptfs_mount_crypt_stat { struct mutex global_auth_tok_list_mutex; size_t num_global_auth_toks; size_t global_default_cipher_key_size; + size_t global_default_auth_key_size; size_t global_default_fn_cipher_key_bytes; unsigned char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1]; unsigned char global_default_fn_cipher_name[ ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1]; + unsigned char global_default_auth_name[ECRYPTFS_MAX_AUTH_NAME_SIZE + + 1]; char global_default_fnek_sig[ECRYPTFS_SIG_SIZE_HEX + 1]; }; diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 678172b..8ba50d8 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -296,6 +296,7 @@ static int ecryptfs_fasync(int fd, struct file *file, int flag) static int ecryptfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); + const struct file_operations ecryptfs_dir_fops = { .readdir = ecryptfs_readdir, .ioctl = ecryptfs_ioctl, diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index a0a7847..adc0835 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -1328,6 +1328,16 @@ parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat, rc = -EINVAL; goto out_free; } + if((u16)data[(*packet_size)] & ECRYPTFS_HMAC_AUTH_MASK) { + crypt_stat->auth_key_size = ECRYPTFS_FIXED_AUTH_KEY_SIZE; + memcpy(crypt_stat->auth, ECRYPTFS_FIXED_AUTH_NAME, strlen(ECRYPTFS_FIXED_AUTH_NAME)); + crypt_stat->auth[strlen(ECRYPTFS_FIXED_AUTH_NAME)]='\0'; + data[(*packet_size)] &= ~ECRYPTFS_HMAC_AUTH_MASK; + } else { + memcpy(crypt_stat->auth, ECRYPTFS_DEFAULT_AUTH, strlen(ECRYPTFS_DEFAULT_AUTH)); + crypt_stat->auth[strlen(ECRYPTFS_DEFAULT_AUTH)]='\0'; + } + rc = ecryptfs_cipher_code_to_string(crypt_stat->cipher, (u16)data[(*packet_size)]); if (rc) @@ -1340,7 +1350,7 @@ parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat, break; default: crypt_stat->key_size = - (*new_auth_tok)->session_key.encrypted_key_size; + (*new_auth_tok)->session_key.encrypted_key_size - crypt_stat->auth_key_size; } rc = ecryptfs_init_crypt_ctx(crypt_stat); if (rc) @@ -2118,13 +2128,13 @@ write_tag_3_packet(char *dest, size_t *remaining_bytes, mount_crypt_stat->global_default_cipher_key_size; if (auth_tok->session_key.encrypted_key_size == 0) auth_tok->session_key.encrypted_key_size = - crypt_stat->key_size; + crypt_stat->auth_key_size + crypt_stat->key_size; if (crypt_stat->key_size == 24 && strcmp("aes", crypt_stat->cipher) == 0) { memset((crypt_stat->key + 24), 0, 8); - auth_tok->session_key.encrypted_key_size = 32; + auth_tok->session_key.encrypted_key_size = crypt_stat->auth_key_size + 32; } else - auth_tok->session_key.encrypted_key_size = crypt_stat->key_size; + auth_tok->session_key.encrypted_key_size = crypt_stat->auth_key_size + crypt_stat->key_size; key_rec->enc_key_size = auth_tok->session_key.encrypted_key_size; encrypted_session_key_valid = 0; @@ -2149,7 +2159,7 @@ write_tag_3_packet(char *dest, size_t *remaining_bytes, session_key_encryption_key_bytes); memcpy(session_key_encryption_key, auth_tok->token.password.session_key_encryption_key, - crypt_stat->key_size); + crypt_stat->key_size); ecryptfs_printk(KERN_DEBUG, "Cached session key " "encryption key: \n"); if (ecryptfs_verbosity > 0) @@ -2217,7 +2227,7 @@ encrypted_session_key_set: + 1 /* Hash identifier */ + ECRYPTFS_SALT_SIZE /* Salt */ + 1 /* Hash iterations */ - + key_rec->enc_key_size); /* Encrypted key size */ + + key_rec->enc_key_size); /* Encrypted key size (cipher) */ if (max_packet_size > (*remaining_bytes)) { printk(KERN_ERR "Packet too large; need up to [%td] bytes, but " "there are only [%td] available\n", max_packet_size, @@ -2241,13 +2251,16 @@ encrypted_session_key_set: /* TODO: Break from RFC2440 so that arbitrary ciphers can be * specified with strings */ cipher_code = ecryptfs_code_for_cipher_string(crypt_stat->cipher, - crypt_stat->key_size); + crypt_stat->key_size); if (cipher_code == 0) { ecryptfs_printk(KERN_WARNING, "Unable to generate code for " "cipher [%s]\n", crypt_stat->cipher); rc = -EINVAL; goto out; } + /* WARNING: the following code is not standard */ + if(crypt_stat->auth_key_size) + cipher_code |= ECRYPTFS_HMAC_AUTH_MASK; dest[(*packet_size)++] = cipher_code; dest[(*packet_size)++] = 0x03; /* S2K */ dest[(*packet_size)++] = 0x01; /* MD5 (TODO: parameterize) */ diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index ea2f921..51dc3cd 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -207,7 +207,9 @@ enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, ecryptfs_opt_passthrough, ecryptfs_opt_xattr_metadata, ecryptfs_opt_encrypted_view, ecryptfs_opt_fnek_sig, ecryptfs_opt_fn_cipher, ecryptfs_opt_fn_cipher_key_bytes, - ecryptfs_opt_unlink_sigs, ecryptfs_opt_err }; + ecryptfs_opt_unlink_sigs, ecryptfs_opt_auth, + ecryptfs_opt_auth_key_bytes,ecryptfs_opt_err + }; static const match_table_t tokens = { {ecryptfs_opt_sig, "sig=%s"}, @@ -222,7 +224,9 @@ static const match_table_t tokens = { {ecryptfs_opt_fn_cipher, "ecryptfs_fn_cipher=%s"}, {ecryptfs_opt_fn_cipher_key_bytes, "ecryptfs_fn_key_bytes=%u"}, {ecryptfs_opt_unlink_sigs, "ecryptfs_unlink_sigs"}, - {ecryptfs_opt_err, NULL} + {ecryptfs_opt_auth, "ecryptfs_auth=%s"}, + {ecryptfs_opt_auth_key_bytes, "ecryptfs_auth_key_bytes=%u"}, + {ecryptfs_opt_err, NULL}, }; static int ecryptfs_init_global_auth_toks( @@ -304,7 +308,13 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options) char *fnek_src; char *cipher_key_bytes_src; char *fn_cipher_key_bytes_src; - + char *auth_key_bytes_src; + char *auth_name_dst; + char *auth_name_src; + int auth_key_bytes; + int auth_name_set = 0; + int auth_key_bytes_set = 0; + if (!options) { rc = -EINVAL; goto out; @@ -405,6 +415,31 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options) case ecryptfs_opt_unlink_sigs: mount_crypt_stat->flags |= ECRYPTFS_UNLINK_SIGS; break; + case ecryptfs_opt_auth: + auth_name_src = args[0].from; + auth_name_dst = + mount_crypt_stat-> + global_default_auth_name; + strncpy(auth_name_dst, auth_name_src, + ECRYPTFS_MAX_AUTH_NAME_SIZE); + auth_name_dst[ECRYPTFS_MAX_AUTH_NAME_SIZE] = '\0'; + auth_name_set = 1; + break; + case ecryptfs_opt_auth_key_bytes: + auth_key_bytes_src = args[0].from; + auth_key_bytes = + (int)simple_strtol(auth_key_bytes_src, + &auth_key_bytes_src, 0); + if(auth_key_bytes != ECRYPTFS_FIXED_AUTH_KEY_SIZE) { + ecryptfs_printk(KERN_ERR, "Only %d-bit long hmac keys are" + "currently supported; auth_key_size" + "will be set to %d\n", ECRYPTFS_FIXED_AUTH_KEY_SIZE, + ECRYPTFS_FIXED_AUTH_KEY_SIZE); + } + mount_crypt_stat->global_default_auth_key_size = + auth_key_bytes; + auth_key_bytes_set = 1; + break; case ecryptfs_opt_err: default: printk(KERN_WARNING @@ -426,12 +461,25 @@ static int ecryptfs_parse_options(struct super_block *sb, char *options) strcpy(mount_crypt_stat->global_default_cipher_name, ECRYPTFS_DEFAULT_CIPHER); } + if (!auth_name_set) { + int auth_name_len = strlen(ECRYPTFS_DEFAULT_AUTH); + + BUG_ON(auth_name_len >= ECRYPTFS_MAX_AUTH_NAME_SIZE); + strcpy(mount_crypt_stat->global_default_auth_name, + ECRYPTFS_DEFAULT_AUTH); + } if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) && !fn_cipher_name_set) strcpy(mount_crypt_stat->global_default_fn_cipher_name, mount_crypt_stat->global_default_cipher_name); if (!cipher_key_bytes_set) mount_crypt_stat->global_default_cipher_key_size = 0; + if (!auth_key_bytes_set) { + if(auth_name_set && strcmp(mount_crypt_stat-> global_default_auth_name, ECRYPTFS_DEFAULT_AUTH)) + mount_crypt_stat->global_default_auth_key_size = ECRYPTFS_FIXED_AUTH_KEY_SIZE; + else + mount_crypt_stat->global_default_auth_key_size = 0; + } if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) && !fn_cipher_key_bytes_set) mount_crypt_stat->global_default_fn_cipher_key_bytes = diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c index df4ce99..48306fa 100644 --- a/fs/ecryptfs/mmap.c +++ b/fs/ecryptfs/mmap.c @@ -521,6 +521,7 @@ static int ecryptfs_write_end(struct file *file, ecryptfs_printk(KERN_DEBUG, "Expanded file size to " "[0x%.16x]\n", i_size_read(ecryptfs_inode)); } + rc = ecryptfs_write_inode_size_to_metadata(ecryptfs_inode); if (rc) printk(KERN_ERR "Error writing inode size to metadata; "Attachment: smime.p7s
Description: S/MIME cryptographic signature
--- End Message ---
Attachment:
smime.p7s
Description: S/MIME cryptographic signature