| /* |
| * Copyright (c) 2015 Samsung Electronics Co., Ltd. |
| * |
| * Sensitive Data Protection |
| * |
| * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| #include <linux/err.h> |
| #include <sdp/dek_common.h> |
| #include <sdp/dek_aes.h> |
| #include <crypto/skcipher.h> |
| |
| #ifdef CONFIG_SDP_ENHANCED |
| #include "../../fs/ext4/sdp/sdp_crypto.h" |
| |
| inline int __dek_aes_encrypt_key32(struct crypto_aead *tfm, |
| unsigned char *key, unsigned int key_len, |
| unsigned char *out); |
| inline int __dek_aes_encrypt_key64(struct crypto_aead *tfm, |
| unsigned char *key, unsigned int key_len, |
| unsigned char *out); |
| inline int __dek_aes_encrypt_key(struct crypto_aead *tfm, |
| unsigned char *key, unsigned int key_len, |
| gcm_pack *pack); |
| inline int __dek_aes_decrypt_key32(struct crypto_aead *tfm, |
| unsigned char *key, unsigned int key_len, |
| unsigned char *out); |
| inline int __dek_aes_decrypt_key64(struct crypto_aead *tfm, |
| unsigned char *key, unsigned int key_len, |
| unsigned char *out); |
| inline int __dek_aes_decrypt_key(struct crypto_aead *tfm, |
| gcm_pack *pack); |
| |
| inline int __dek_aes_encrypt_key_raw(unsigned char *kek, unsigned int kek_len, |
| unsigned char *key, unsigned int key_len, |
| unsigned char *out, unsigned int *out_len); |
| inline int __dek_aes_decrypt_key_raw(unsigned char *kek, unsigned int kek_len, |
| unsigned char *ekey, unsigned int ekey_len, |
| unsigned char *out, unsigned int *out_len); |
| #endif |
| |
| static struct crypto_skcipher *dek_aes_key_setup(kek_t *kek) |
| { |
| struct crypto_skcipher *tfm = NULL; |
| |
| tfm = crypto_alloc_skcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); |
| if (!IS_ERR(tfm)) { |
| crypto_skcipher_setkey(tfm, kek->buf, kek->len); |
| } else { |
| printk("dek: failed to alloc blkcipher\n"); |
| tfm = NULL; |
| } |
| return tfm; |
| } |
| |
| static void dek_aes_key_free(struct crypto_skcipher *tfm) |
| { |
| crypto_free_skcipher(tfm); |
| } |
| |
| #define DEK_AES_CBC_IV_SIZE 16 |
| |
| static int __dek_aes_do_crypt(struct crypto_skcipher *tfm, |
| unsigned char *src, unsigned char *dst, int len, bool encrypt) { |
| int rc = 0; |
| struct skcipher_request *req = NULL; |
| DECLARE_CRYPTO_WAIT(wait); |
| struct scatterlist src_sg, dst_sg; |
| u8 iv[DEK_AES_CBC_IV_SIZE] = {0,}; |
| |
| req = skcipher_request_alloc(tfm, GFP_NOFS); |
| if (!req) { |
| return -ENOMEM; |
| } |
| |
| skcipher_request_set_callback(req, |
| CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, |
| crypto_req_done, &wait); |
| |
| sg_init_one(&src_sg, src, len); |
| sg_init_one(&dst_sg, dst, len); |
| |
| skcipher_request_set_crypt(req, &src_sg, &dst_sg, len, iv); |
| |
| if (encrypt) |
| rc = crypto_wait_req(crypto_skcipher_encrypt(req), &wait); |
| else |
| rc = crypto_wait_req(crypto_skcipher_decrypt(req), &wait); |
| |
| skcipher_request_free(req); |
| return rc; |
| } |
| |
| int dek_aes_encrypt(kek_t *kek, unsigned char *src, unsigned char *dst, int len) { |
| int rc; |
| struct crypto_skcipher *tfm; |
| |
| if(kek == NULL) return -EINVAL; |
| |
| tfm = dek_aes_key_setup(kek); |
| |
| if(tfm) { |
| rc = __dek_aes_do_crypt(tfm, src, dst, len, true); |
| dek_aes_key_free(tfm); |
| return rc; |
| } else |
| return -ENOMEM; |
| } |
| |
| int dek_aes_decrypt(kek_t *kek, unsigned char *src, unsigned char *dst, int len) { |
| int rc; |
| struct crypto_skcipher *tfm; |
| |
| if(kek == NULL) return -EINVAL; |
| |
| tfm = dek_aes_key_setup(kek); |
| |
| if(tfm) { |
| rc = __dek_aes_do_crypt(tfm, src, dst, len, false); |
| dek_aes_key_free(tfm); |
| return rc; |
| } else |
| return -ENOMEM; |
| } |
| |
| #ifdef CONFIG_SDP_ENHANCED |
| int dek_aes_encrypt_key(kek_t *kek, unsigned char *key, unsigned int key_len, |
| unsigned char *out, unsigned int *out_len) |
| { |
| if (kek == NULL) |
| return -EINVAL; |
| return __dek_aes_encrypt_key_raw(kek->buf, kek->len, key, key_len, out, out_len); |
| } |
| |
| int dek_aes_encrypt_key_raw(unsigned char *kek, unsigned int kek_len, |
| unsigned char *key, unsigned int key_len, |
| unsigned char *out, unsigned int *out_len) |
| { |
| if (kek == NULL || kek_len == 0) |
| return -EINVAL; |
| return __dek_aes_encrypt_key_raw(kek, kek_len, key, key_len, out, out_len); |
| } |
| |
| inline int __dek_aes_encrypt_key_raw(unsigned char *kek, unsigned int kek_len, |
| unsigned char *key, unsigned int key_len, |
| unsigned char *out, unsigned int *out_len) |
| { |
| int rc; |
| int type; |
| int ekey_len = 0; |
| struct crypto_aead *tfm; |
| |
| tfm = sdp_crypto_aes_gcm_key_setup(kek, kek_len); |
| if (unlikely(IS_ERR(tfm))) { |
| rc = PTR_ERR(tfm); |
| goto end; |
| } |
| |
| type = CONV_DLEN_TO_TYPE(key_len); |
| ekey_len = CONV_TYPE_TO_PLEN(type); |
| |
| switch(type) { |
| case SDP_CRYPTO_GCM_PACK32: |
| rc = __dek_aes_encrypt_key32(tfm, key, key_len, out); |
| break; |
| case SDP_CRYPTO_GCM_PACK64: |
| rc = __dek_aes_encrypt_key64(tfm, key, key_len, out); |
| break; |
| default: |
| printk(KERN_ERR |
| "dek_aes_encrypt_key: not supported key length(%d)\n", key_len); |
| rc = -EINVAL; |
| break; |
| } |
| sdp_crypto_aes_gcm_key_free(tfm); |
| |
| end: |
| if (rc) |
| *out_len = 0; |
| else |
| *out_len = ekey_len; |
| return rc; |
| } |
| |
| inline int __dek_aes_encrypt_key32(struct crypto_aead *tfm, |
| unsigned char *key, unsigned int key_len, |
| unsigned char *out) |
| { |
| int rc; |
| gcm_pack32 pack; |
| gcm_pack __pack; |
| |
| memset(&pack, 0, sizeof(pack)); |
| __pack.type = SDP_CRYPTO_GCM_PACK32; |
| __pack.iv = pack.iv; |
| __pack.data = pack.data; |
| __pack.auth = pack.auth; |
| |
| rc = __dek_aes_encrypt_key(tfm, key, key_len, &__pack); |
| if (!rc) |
| memcpy(out, &pack, sizeof(pack)); |
| memzero_explicit(&pack, sizeof(gcm_pack32)); |
| return rc; |
| } |
| |
| inline int __dek_aes_encrypt_key64(struct crypto_aead *tfm, |
| unsigned char *key, unsigned int key_len, |
| unsigned char *out) |
| { |
| int rc; |
| gcm_pack64 pack; |
| gcm_pack __pack; |
| |
| memset(&pack, 0, sizeof(gcm_pack64)); |
| __pack.type = SDP_CRYPTO_GCM_PACK64; |
| __pack.iv = pack.iv; |
| __pack.data = pack.data; |
| __pack.auth = pack.auth; |
| |
| rc = __dek_aes_encrypt_key(tfm, key, key_len, &__pack); |
| if (!rc) |
| memcpy(out, &pack, sizeof(pack)); |
| memzero_explicit(&pack, sizeof(pack)); |
| return rc; |
| } |
| |
| inline int __dek_aes_encrypt_key(struct crypto_aead *tfm, |
| unsigned char *key, unsigned int key_len, |
| gcm_pack *pack) |
| { |
| int rc = sdp_crypto_generate_key(pack->iv, SDP_CRYPTO_GCM_IV_LEN); |
| if (rc) |
| memset(pack->iv, 0, SDP_CRYPTO_GCM_IV_LEN); |
| |
| memcpy(pack->data, key, key_len); |
| rc = sdp_crypto_aes_gcm_encrypt_pack(tfm, pack); |
| return rc; |
| } |
| |
| int dek_aes_decrypt_key(kek_t *kek, unsigned char *ekey, unsigned int ekey_len, |
| unsigned char *out, unsigned int *out_len) |
| { |
| if (kek == NULL) |
| return -EINVAL; |
| return __dek_aes_decrypt_key_raw(kek->buf, kek->len, ekey, ekey_len, out, out_len); |
| } |
| |
| int dek_aes_decrypt_key_raw(unsigned char *kek, unsigned int kek_len, |
| unsigned char *ekey, unsigned int ekey_len, |
| unsigned char *out, unsigned int *out_len) |
| { |
| if (kek == NULL || kek_len == 0) |
| return -EINVAL; |
| return __dek_aes_decrypt_key_raw(kek, kek_len, ekey, ekey_len, out, out_len); |
| } |
| |
| inline int __dek_aes_decrypt_key_raw(unsigned char *kek, unsigned int kek_len, |
| unsigned char *ekey, unsigned int ekey_len, |
| unsigned char *out, unsigned int *out_len) |
| { |
| int rc; |
| int type; |
| int key_len = 0; |
| struct crypto_aead *tfm; |
| |
| tfm = sdp_crypto_aes_gcm_key_setup(kek, kek_len); |
| if (unlikely(IS_ERR(tfm))) { |
| rc = PTR_ERR(tfm); |
| goto end; |
| } |
| |
| type = CONV_PLEN_TO_TYPE(ekey_len); |
| key_len = CONV_TYPE_TO_DLEN(type); |
| |
| switch(type) { |
| case SDP_CRYPTO_GCM_PACK32: |
| rc = __dek_aes_decrypt_key32(tfm, ekey, key_len, out); |
| break; |
| case SDP_CRYPTO_GCM_PACK64: |
| rc = __dek_aes_decrypt_key64(tfm, ekey, key_len, out); |
| break; |
| default: |
| printk(KERN_ERR |
| "dek_aes_decrypt_key: not supported ekey length(%d)\n", ekey_len); |
| rc = -EINVAL; |
| break; |
| } |
| sdp_crypto_aes_gcm_key_free(tfm); |
| |
| end: |
| if (rc) |
| *out_len = 0; |
| else |
| *out_len = key_len; |
| return rc; |
| } |
| |
| inline int __dek_aes_decrypt_key32(struct crypto_aead *tfm, |
| unsigned char *ekey, unsigned int key_len, |
| unsigned char *out) |
| { |
| int rc; |
| gcm_pack32 pack; |
| gcm_pack __pack; |
| |
| memcpy(&pack, ekey, sizeof(pack)); |
| __pack.type = SDP_CRYPTO_GCM_PACK32; |
| __pack.iv = pack.iv; |
| __pack.data = pack.data; |
| __pack.auth = pack.auth; |
| |
| rc = __dek_aes_decrypt_key(tfm, &__pack); |
| if (!rc) |
| memcpy(out, pack.data, key_len); |
| memzero_explicit(&pack, sizeof(pack)); |
| return rc; |
| } |
| |
| inline int __dek_aes_decrypt_key64(struct crypto_aead *tfm, |
| unsigned char *ekey, unsigned int key_len, |
| unsigned char *out) |
| { |
| int rc; |
| gcm_pack64 pack; |
| gcm_pack __pack; |
| |
| memcpy(&pack, ekey, sizeof(pack)); |
| __pack.type = SDP_CRYPTO_GCM_PACK64; |
| __pack.iv = pack.iv; |
| __pack.data = pack.data; |
| __pack.auth = pack.auth; |
| |
| rc = __dek_aes_decrypt_key(tfm, &__pack); |
| if (!rc) |
| memcpy(out, pack.data, key_len); |
| memzero_explicit(&pack, sizeof(pack)); |
| return rc; |
| } |
| |
| inline int __dek_aes_decrypt_key(struct crypto_aead *tfm, |
| gcm_pack *pack) |
| { |
| return sdp_crypto_aes_gcm_decrypt_pack(tfm, pack); |
| } |
| #endif |