From d6950e214c73cfd1bde2c9802163950b5cc59cee Mon Sep 17 00:00:00 2001 From: Jiamin Ma Date: Mon, 24 Jul 2017 18:39:23 +0800 Subject: [PATCH] unifykey: support both old and new unifykey format PD#148057: for m8bb m200 platform, we have the latest linux kernel and a relative old version uboot running on it. For some historic resons, the unifykey data stored in emmc/nand has totally different format, which means the key stored by old uboot cannot be fetched out by new kernel. To solve this problem, we have to support both the old and new unifykey dataformat in lasted kernel. Change-Id: Ic70df6543466b345a5ff513bfaabfe4cfcf647ed Signed-off-by: Jiamin Ma --- arch/arm/boot/dts/amlogic/meson8b_m200.dts | 73 +-- drivers/amlogic/unifykey/unifykey.c | 75 ++- drivers/amlogic/unifykey/unifykey.h | 16 +- drivers/amlogic/unifykey/unifykey_dts.c | 34 +- .../amlogic/unifykey/v7/key_storage/Makefile | 7 +- .../unifykey/v7/key_storage/crypto_api.c | 65 ++- .../amlogic/unifykey/v7/key_storage/storage.c | 548 ++++++++++++------ .../amlogic/unifykey/v7/key_storage/storage.h | 45 +- .../unifykey/v7/key_storage/storage_data.h | 2 +- .../unifykey/v7/key_storage/storage_util.c | 8 +- .../unifykey/v7/key_storage/storage_util.h | 1 + drivers/amlogic/unifykey/v7/key_storage/tlv.c | 247 -------- 12 files changed, 581 insertions(+), 540 deletions(-) delete mode 100644 drivers/amlogic/unifykey/v7/key_storage/tlv.c diff --git a/arch/arm/boot/dts/amlogic/meson8b_m200.dts b/arch/arm/boot/dts/amlogic/meson8b_m200.dts index 0a9ad1c28d75..8f8e40b9c8f6 100644 --- a/arch/arm/boot/dts/amlogic/meson8b_m200.dts +++ b/arch/arm/boot/dts/amlogic/meson8b_m200.dts @@ -224,93 +224,48 @@ compatible = "amlogic, unifykey"; status = "ok"; - unifykey-num = <14>; + unifykey-num = <6>; unifykey-index-0 = <&keysn_0>; unifykey-index-1 = <&keysn_1>; unifykey-index-2 = <&keysn_2>; unifykey-index-3 = <&keysn_3>; unifykey-index-4 = <&keysn_4>; unifykey-index-5 = <&keysn_5>; - unifykey-index-6 = <&keysn_6>; - unifykey-index-7 = <&keysn_7>; - unifykey-index-8 = <&keysn_8>; - unifykey-index-9 = <&keysn_9>; - unifykey-index-10= <&keysn_10>; - unifykey-index-11= <&keysn_11>; - unifykey-index-12= <&keysn_12>; - unifykey-index-13= <&keysn_13>; keysn_0: key_0{ key-name = "usid"; - key-device = "normal"; + key-device = "nandkey"; + key-dataformat = "allascii"; key-permit = "read","write","del"; }; keysn_1:key_1{ key-name = "mac"; - key-device = "normal"; + key-device = "nandkey"; + key-dataformat = "hexdata"; key-permit = "read","write","del"; }; keysn_2:key_2{ key-name = "hdcp"; - key-device = "secure"; - key-type = "sha1"; + key-device = "nandkey"; + key-dataformat = "hexdata"; key-permit = "read","write","del"; }; keysn_3:key_3{ key-name = "secure_boot_set"; - key-device = "efuse"; - key-permit = "write"; + key-device = "efusekey"; + key-dataformat = "hexdata"; + key-permit = "read","write"; }; keysn_4:key_4{ key-name = "mac_bt"; - key-device = "normal"; + key-device = "nandkey"; + key-dataformat = "hexdata"; key-permit = "read","write","del"; - key-type = "mac"; }; keysn_5:key_5{ key-name = "mac_wifi"; - key-device = "normal"; - key-permit = "read","write","del"; - key-type = "mac"; - }; - keysn_6:key_6{ - key-name = "hdcp2_tx"; - key-device = "normal"; - key-permit = "read","write","del"; - }; - keysn_7:key_7{ - key-name = "hdcp2_rx"; - key-device = "normal"; - key-permit = "read","write","del"; - }; - keysn_8:key_8{ - key-name = "widevinekeybox"; - key-device = "secure"; - key-permit = "read","write","del"; - }; - keysn_9:key_9{ - key-name = "deviceid"; - key-device = "normal"; - key-permit = "read","write","del"; - }; - keysn_10:key_10{ - key-name = "hdcp22_fw_private"; - key-device = "secure"; - key-permit = "read","write","del"; - }; - keysn_11:key_11{ - key-name = "PlayReadykeybox25"; - key-device = "secure"; - key-permit = "read","write","del"; - }; - keysn_12:key_12{ - key-name = "prpubkeybox";// PlayReady - key-device = "secure"; - key-permit = "read","write","del"; - }; - keysn_13:key_13{ - key-name = "prprivkeybox";// PlayReady - key-device = "secure"; + key-device = "nandkey"; + key-dataformat = "hexdata"; key-permit = "read","write","del"; }; };//End unifykey diff --git a/drivers/amlogic/unifykey/unifykey.c b/drivers/amlogic/unifykey/unifykey.c index 787900d75f54..56313491f93c 100644 --- a/drivers/amlogic/unifykey/unifykey.c +++ b/drivers/amlogic/unifykey/unifykey.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "unifykey.h" #include "amlkey_if.h" @@ -61,6 +62,30 @@ static int init_flag; static int module_init_flag; static int lock_flag; +static char hex_to_asc(char para) +{ + if (para >= 0 && para <= 9) + para = para+'0'; + else if (para >= 0xa && para <= 0xf) + para = para+'a'-0xa; + + return para; +} + +static char asc_to_hex(char para) +{ + if (para >= '0' && para <= '9') + para = para-'0'; + else if (para >= 'a' && para <= 'f') + para = para-'a'+0xa; + else if (para >= 'A' && para <= 'F') + para = para-'A'+0xa; + + return para; +} + + + static int key_storage_init(char *buf, unsigned int len) { int encrypt_type; @@ -315,6 +340,8 @@ int key_unify_write(char *keyname, unsigned char *keydata, int err = 0; int attr; struct key_item_t *unifykey; + unsigned char *fmt_data; + int i, j; unifykey = unifykey_find_item_by_name(keyname); if (unifykey == NULL) { @@ -340,8 +367,24 @@ int key_unify_write(char *keyname, unsigned char *keydata, KEY_UNIFY_ATTR_SECURE : 0); attr |= (unifykey->attr & KEY_UNIFY_ATTR_ENCRYPT_MASK) ? KEY_UNIFY_ATTR_ENCRYPT : 0; - err = key_storage_write(keyname, keydata, - datalen, attr); + if (is_meson_m8b_cpu() && + (unifykey->df != KEY_M_HEXASCII)) { + fmt_data = kmalloc(datalen*2+1, GFP_KERNEL); + if (!fmt_data) + return -ENOMEM; + memset(fmt_data, 0x00, datalen*2+1); + for (i = 0, j = 0; i < datalen; i++) { + fmt_data[j++] = hex_to_asc(( + keydata[i]>>4) & 0x0f); + fmt_data[j++] = hex_to_asc(( + keydata[i]) & 0x0f); + } + err = key_storage_write(keyname, fmt_data, + strlen(fmt_data), attr); + kfree(fmt_data); + } else + err = key_storage_write(keyname, keydata, + datalen, attr); break; case KEY_M_UNKNOWN_DEV: default: @@ -367,6 +410,8 @@ int key_unify_read(char *keyname, unsigned char *keydata, { int err = 0; struct key_item_t *unifykey; + unsigned char *fmt_data; + int i, j; if (!keydata) { pr_err("%s:%d, keydata is NULL\n", @@ -398,8 +443,26 @@ int key_unify_read(char *keyname, unsigned char *keydata, datalen, reallen, 1); break; case KEY_M_NORMAL: - err = key_storage_read(keyname, keydata, - datalen, reallen, 0); + if (is_meson_m8b_cpu() && + (unifykey->df != KEY_M_HEXASCII)) { + fmt_data = kmalloc(datalen*2+1, GFP_KERNEL); + if (!fmt_data) + return -ENOMEM; + memset(fmt_data, 0x00, datalen*2+1); + err = key_storage_read(keyname, fmt_data, + datalen*2, reallen, 0); + if (err != 0) { + kfree(fmt_data); + return err; + } + for (i = 0, j = 0; i < *reallen/2; i++, j += 2) + keydata[i] = (((asc_to_hex(fmt_data[j] + ))<<4) | (asc_to_hex(fmt_data[j+1]))); + *reallen = *reallen/2; + kfree(fmt_data); + } else + err = key_storage_read(keyname, keydata, + datalen, reallen, 0); break; case KEY_M_UNKNOWN_DEV: default: @@ -459,6 +522,9 @@ int key_unify_size(char *keyname, unsigned int *reallen) break; case KEY_M_NORMAL: *reallen = key_storage_size(keyname); + if (is_meson_m8b_cpu() && + (unifykey->df != KEY_M_HEXASCII)) + *reallen = *reallen/2; break; case KEY_M_UNKNOWN_DEV: default: @@ -1106,6 +1172,7 @@ static ssize_t write_store(struct class *cla, unsigned char *keydata = NULL; size_t key_len = 0; + if (curkey != NULL) { keydata = kzalloc(count, GFP_KERNEL); diff --git a/drivers/amlogic/unifykey/unifykey.h b/drivers/amlogic/unifykey/unifykey.h index 4d56029c65d4..d0f90537dd7b 100644 --- a/drivers/amlogic/unifykey/unifykey.h +++ b/drivers/amlogic/unifykey/unifykey.h @@ -36,6 +36,14 @@ enum key_manager_dev_e { KEY_M_MAX_DEV, }; +/*key data format*/ +enum key_manager_df_e { + KEY_M_HEXDATA = 0, + KEY_M_HEXASCII = 1, + KEY_M_ALLASCII = 2, + KEY_M_MAX_DF = 0xff, +}; + enum key_manager_permit_e { KEY_M_PERMIT_READ = (1<<0), KEY_M_PERMIT_WRITE = (1<<1), @@ -47,7 +55,13 @@ enum key_manager_flag_e { KEY_M_FLAG_EMPTY = (0<<0), KEY_M_FLAG_EXSIT = (1<<0), }; + +#ifdef CONFIG_MACH_MESON8B +#define KEY_UNIFY_NAME_LEN 16 +#else #define KEY_UNIFY_NAME_LEN 48 +#endif + /* for ioctrl transfer parameters. */ struct key_item_info_t { unsigned int id; @@ -67,7 +81,7 @@ struct key_item_t { char name[KEY_UNIFY_NAME_LEN]; int id; unsigned int dev; /* key save in device //efuse, */ - /* unsigned int df; */ + unsigned int df; unsigned int permit; int attr; int reserve; diff --git a/drivers/amlogic/unifykey/unifykey_dts.c b/drivers/amlogic/unifykey/unifykey_dts.c index 167b405ba726..86e49b925819 100644 --- a/drivers/amlogic/unifykey/unifykey_dts.c +++ b/drivers/amlogic/unifykey/unifykey_dts.c @@ -32,6 +32,11 @@ #define KEY_DEV_NORMAL "normal" #define KEY_DEV_SECURE "secure" +/* compatibility for old names */ +#define KEY_COMP_DEV_EFUSE "efusekey" +#define KEY_COMP_DEV_NORMAL "nandkey" +#define KEY_COMP_DEV_SECURE "secureskey" + /* permision */ #define KEY_PERMIT_READ "read" #define KEY_PERMIT_WRITE "write" @@ -172,11 +177,14 @@ static int unifykey_item_parse_dt(struct device_node *node, int id) goto exit; } if (propname) { - if (strcmp(propname, KEY_DEV_EFUSE) == 0) + if (strcmp(propname, KEY_DEV_EFUSE) == 0 || + strcmp(propname, KEY_COMP_DEV_EFUSE) == 0) temp_item->dev = KEY_M_EFUSE; - else if (strcmp(propname, KEY_DEV_NORMAL) == 0) + else if (strcmp(propname, KEY_DEV_NORMAL) == 0 || + strcmp(propname, KEY_COMP_DEV_NORMAL) == 0) temp_item->dev = KEY_M_NORMAL; - else if (strcmp(propname, KEY_DEV_SECURE) == 0) + else if (strcmp(propname, KEY_DEV_SECURE) == 0 || + strcmp(propname, KEY_COMP_DEV_SECURE)) temp_item->dev = KEY_M_SECURE; else temp_item->dev = KEY_M_UNKNOWN_DEV; @@ -191,6 +199,26 @@ static int unifykey_item_parse_dt(struct device_node *node, int id) temp_item->permit |= KEY_M_PERMIT_DEL; temp_item->id = id; + if (is_meson_m8b_cpu()) { + temp_item->df = KEY_M_MAX_DF; + ret = of_property_read_string(node, "key-dataformat", + &propname); + if (ret < 0) { + ret = -EINVAL; + goto exit; + } + if (propname) { + if (strcmp(propname, "hexdata") == 0) + temp_item->df = KEY_M_HEXDATA; + else if (strcmp(propname, "hexascii") == 0) + temp_item->df = KEY_M_HEXASCII; + else if (strcmp(propname, "allascii") == 0) + temp_item->df = KEY_M_ALLASCII; + else + temp_item->df = KEY_M_MAX_DF; + } + } + temp_item->attr = 0; ret = of_property_read_string(node, "key-encrypt", &propname); if (ret < 0) diff --git a/drivers/amlogic/unifykey/v7/key_storage/Makefile b/drivers/amlogic/unifykey/v7/key_storage/Makefile index 15a06f9e83f0..9e46935acaa3 100644 --- a/drivers/amlogic/unifykey/v7/key_storage/Makefile +++ b/drivers/amlogic/unifykey/v7/key_storage/Makefile @@ -1,4 +1,9 @@ # # Makefile for Meson m8b series unifykey. # -obj-$(CONFIG_AMLOGIC_V7_UNIFYKEY) += crypto_api.o storage.o storage_util.o tlv.o key_service_routine.o storage_apis.o storage_util.o +obj-$(CONFIG_AMLOGIC_V7_UNIFYKEY) += crypto_api.o \ + storage.o \ + storage_util.o \ + key_service_routine.o \ + storage_apis.o \ + storage_util.o diff --git a/drivers/amlogic/unifykey/v7/key_storage/crypto_api.c b/drivers/amlogic/unifykey/v7/key_storage/crypto_api.c index 87de64e21b4f..0a368e2a855c 100644 --- a/drivers/amlogic/unifykey/v7/key_storage/crypto_api.c +++ b/drivers/amlogic/unifykey/v7/key_storage/crypto_api.c @@ -18,53 +18,60 @@ #include #include #include +#include #include #include "crypto_api.h" -static u8 pkey[32] = {0xc1, 0xb2, 0x33, 0x34, 5, 6, 7, 8, - 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x11, - 0xc3, 0xb4, 0x32, 0x34, 5, 6, 7, 8, - 9, 0xa, 0xb, 0xc1, 0xd, 0xe1, 0xf2, 0x12}; + +static u8 const fixed_iv[16] = {0xAD, 0x93, 0x00, 0xC4, + 0x8E, 0x50, 0x20, 0xC5, + 0x3F, 0xBF, 0x23, 0x32, + 0x80, 0x5A, 0xC6, 0xDF}; +static u8 const fixed_key[32] = {0x2F, 0x7D, 0x49, 0xD9, + 0x15, 0x8B, 0x7F, 0x04, + 0x2C, 0x80, 0xB0, 0x62, + 0x78, 0x25, 0x8D, 0x9C, + 0x13, 0x22, 0x02, 0x4A, + 0x55, 0x23, 0xBB, 0xCB, + 0xF1, 0xFB, 0x2A, 0xCC, + 0xBB, 0x95, 0xF4, 0x50}; + + + +// AES-256-CBC int do_aes_internal(unsigned char enc_flag, unsigned char *in, int in_len, unsigned char *out, int *out_len) { - int ret = -1; - int load_len = 0; - unsigned char blk_buf[64]; - struct crypto_cipher *tfm; + int ret = -1; + struct crypto_blkcipher *tfm; + struct blkcipher_desc desc; + struct scatterlist sg_in; + struct scatterlist sg_out; if (!in || !out || !out_len) return ret; - memset(out, 0x00, in_len); - - tfm = crypto_alloc_cipher("aes", 4, CRYPTO_ALG_ASYNC); + tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tfm)) return PTR_ERR(tfm); + desc.tfm = tfm; + desc.flags = 0x00; - crypto_cipher_setkey(tfm, pkey, 32); + sg_init_one(&sg_in, in, in_len); + sg_init_one(&sg_out, out, in_len); + crypto_blkcipher_setkey(tfm, fixed_key, 32); + crypto_blkcipher_set_iv(tfm, fixed_iv, 16); - load_len = AES_BLOCK_SIZE; - *out_len = 0; + if (enc_flag) + crypto_blkcipher_encrypt(&desc, &sg_out, &sg_in, sg_in.length); + else + crypto_blkcipher_decrypt(&desc, &sg_out, &sg_in, sg_in.length); - do { - memset(blk_buf, 0, sizeof(blk_buf)); - memcpy(blk_buf, in + *out_len, load_len); - - if (enc_flag) - crypto_cipher_encrypt_one(tfm, out + *out_len, blk_buf); - else - crypto_cipher_decrypt_one(tfm, out + *out_len, blk_buf); - *out_len += load_len; - - if (*out_len + load_len > in_len) - load_len = in_len - *out_len; - } while (*out_len != in_len); + *out_len = sg_out.length; + crypto_free_blkcipher(tfm); ret = 0; - - crypto_free_cipher(tfm); return ret; } diff --git a/drivers/amlogic/unifykey/v7/key_storage/storage.c b/drivers/amlogic/unifykey/v7/key_storage/storage.c index 2bd231c6b59f..ce6114e10c68 100644 --- a/drivers/amlogic/unifykey/v7/key_storage/storage.c +++ b/drivers/amlogic/unifykey/v7/key_storage/storage.c @@ -17,21 +17,27 @@ #include #include +#include +#include #include "storage.h" #include "storage_util.h" #include "storage_data.h" #include "storage_def.h" #include "crypto_api.h" +static int emmc_key_transfer(u8 *buf, u32 *value, u32 len, u32 direct); +static uint32_t emmckey_checksum(uint8_t *buf, uint32_t length); + static void storage_hash(uint8_t *input, uint32_t insize, uint8_t *output) { sha256(input, insize, output); } - static int32_t check_object_valid(struct storage_object *object) { + /* uboot does not support hash checking */ +#if 0 uint8_t temp[32]; storage_hash(object->dataptr, object->datasize, temp); @@ -39,105 +45,84 @@ static int32_t check_object_valid(struct storage_object *object) return 0; else return -1; +#endif + return 0; } int32_t storage_writeToFlash(void) { - uint8_t *internal_storage = NULL; - uint8_t *penchead, *pcontent, *pcontent_bak; - uint8_t *pns_rawhead, *pns_enchead, *pns_content; - struct storage_block_raw_head *rawhead_t; - struct storage_block_enc_head enchead_t; - struct storage_node *node; - struct storage_object *obj; + uint8_t *pblock = + (uint8_t *)get_share_storage_block_base(); + struct emmc_key_head *pemmc_key_head; + struct key_storage_head *pstorage_key_head; + uint8_t *pkey_data; + struct storage_node *node; + struct storage_object *obj; + uint32_t pos = 0; - uint32_t datasize; - uint32_t outlen; - uint32_t sumsize; - int32_t ret = 0; - - - internal_storage = (uint8_t *)get_internal_storage_base(); - if (!internal_storage) - goto storage_writeflash_err; - - penchead = internal_storage + STORAGE_BLOCK_RAW_HEAD_SIZE; - pcontent = penchead + STORAGE_BLOCK_ENC_HEAD_SIZE; - pcontent_bak = pcontent; - pns_rawhead = (uint8_t *)get_share_storage_block_base(); - pns_enchead = pns_rawhead + STORAGE_BLOCK_RAW_HEAD_SIZE; - pns_content = pns_enchead + STORAGE_BLOCK_ENC_HEAD_SIZE; - - /* internal: raw head */ - memset(pns_rawhead, 0, STORAGE_BLOCK_RAW_HEAD_SIZE); - rawhead_t = (struct storage_block_raw_head *)pns_rawhead; - memcpy(rawhead_t->mark, "AMLSECURITY", 11); - rawhead_t->version = BLOCK_VERSION; - rawhead_t->enctype = storage_chose_enctype(); - /*fixme: no enc support now*/ - //set_encryptkeyfrom(rawhead_t->enctype); - - /* internal: storage */ - datasize = 0; - list_for_each_entry(node, &nodelist, list) - if (datasize < STORAGE_BLOCK_SIZE - - STORAGE_BLOCK_RAW_HEAD_SIZE - - STORAGE_BLOCK_ENC_HEAD_SIZE) { - obj = &(node->object); - if ((node->status & OBJ_STATUS_VALID) - && (obj->dataptr)) { - Tlv_WriteObject(obj, pcontent, &outlen); - datasize += outlen; - pcontent += outlen; - } - } - sumsize = (((datasize+15)/16)*16); - sumsize += STORAGE_BLOCK_RAW_HEAD_SIZE; - sumsize += STORAGE_BLOCK_ENC_HEAD_SIZE; - if (sumsize > flash_storage_size) { - //ERROR("storage size is larger than flash!\n"); - goto storage_writeflash_err; + /* for emmc , there is an extra header to be handled */ + /* the below check is ugly, someone should move the + * related code to emmc key + */ + if (kallsyms_lookup_name("emmc_key_read")) { + pemmc_key_head = (struct emmc_key_head *)pblock; + pstorage_key_head = (struct key_storage_head *) + (pblock + sizeof(struct emmc_key_head)); + } else { + pemmc_key_head = NULL; + pstorage_key_head = (struct key_storage_head *)pblock; } - memset(&enchead_t, 0, sizeof(enchead_t)); - enchead_t.blocksize = datasize; - enchead_t.flashsize = flash_storage_size; - Tlv_WriteHead(&enchead_t, penchead); -#if 1 - ret = do_aes_internal(1, penchead, - STORAGE_BLOCK_ENC_HEAD_SIZE, - pns_enchead, (int *)&outlen); - if (ret) - goto storage_writeflash_err; -#else - ret = 0; - memcpy(pns_enchead, penchead, - STORAGE_BLOCK_ENC_HEAD_SIZE); -#endif -#if 1 - ret = do_aes_internal(1, pcontent_bak, - ((datasize+15)/16)*16, - pns_content, (int *)&outlen); - if (ret) - goto storage_writeflash_err; -#else - ret = 0; - memcpy(pns_content, pcontent_bak, ((datasize+15)/16)*16); -#endif - /* write message*/ - //msg->cmd = MSG_CMD_WRITEKEY; - //msg->state += 1; - ret = 0; - version = BLOCK_VERSION; - aesfrom = storage_chose_enctype(); - goto storage_writeflash_exit; + /* fill storage key header */ + memset(pstorage_key_head, 0x00, sizeof(struct key_storage_head)); + memcpy(pstorage_key_head->mark, "keyexist", 8); + pstorage_key_head->version = 2; + pstorage_key_head->item_cnt = 0; + pstorage_key_head->size = 0; -storage_writeflash_err: - ret = -1; -storage_writeflash_exit: - return ret; + pstorage_key_head->size += sizeof(struct key_storage_head); + pkey_data = (uint8_t *)pstorage_key_head + + sizeof(struct key_storage_head); + + //list_for_each_entry(node, &nodelist, list) + list_for_each_entry_reverse(node, &nodelist, list) + if (node->status & OBJ_STATUS_VALID) { + obj = &(node->object); + pstorage_key_head->item[pstorage_key_head-> + item_cnt].position = pos; + memcpy(&pkey_data[pos], + obj, sizeof(struct storage_object)); + pos += sizeof(struct storage_object); + memcpy(&pkey_data[pos], + obj->storage_data, obj->storage_size); + pos += obj->storage_size; + + + pstorage_key_head->size += + sizeof(struct storage_object); + pstorage_key_head->size += obj->storage_size; + pstorage_key_head->item_cnt++; + } + + if (pemmc_key_head != NULL) { + /* fill the extra emmc key header */ + memset(pemmc_key_head, 0x00, sizeof(struct emmc_key_head)); + memcpy(pemmc_key_head->mark, "emmckeys", 8); + emmc_key_transfer(pemmc_key_head->mark, + &pemmc_key_head->mark_checksum, 8, 1); + /* + * do not blame me for the magic number 128*1024-28, + * only to be compatible for old next-dev + */ + pemmc_key_head->checksum = + emmckey_checksum((uint8_t *)pstorage_key_head, + 128*1024-28); + } + + return RET_OK; } + static void __storage_add(struct storage_node *node) { list_add(&node->list, &nodelist); @@ -148,6 +133,8 @@ static void __storage_del(struct storage_node *node) list_del(&node->list); if (node->object.dataptr != NULL) storage_free(node->object.dataptr); + if (node->object.storage_data != NULL) + storage_free(node->object.storage_data); storage_free(node); } @@ -173,7 +160,7 @@ static struct storage_node *storage_create_empty(void) { struct storage_node *node; - node = storage_malloc(sizeof(*node)); + node = storage_zalloc(sizeof(*node)); if (node == NULL) return NULL; memset(node, 0, sizeof(struct storage_node)); @@ -182,6 +169,7 @@ static struct storage_node *storage_create_empty(void) return node; } + static struct storage_node *storage_create(uint8_t *name, uint32_t namelen) { struct storage_node *node; @@ -198,6 +186,12 @@ static struct storage_node *storage_create(uint8_t *name, uint32_t namelen) return node; } +static void storage_del(struct storage_node *node) +{ + __storage_del(node); +} + + static void storage_reset(void) { struct storage_node *node; @@ -206,6 +200,7 @@ static void storage_reset(void) __storage_del(node); } + static int32_t storage_infocheck(struct storage_node *node, uint32_t attr) { struct storage_object *obj = &(node->object); @@ -216,54 +211,177 @@ static int32_t storage_infocheck(struct storage_node *node, uint32_t attr) return -1; } + +static uint16_t aml_key_checksum(char *data, int length) +{ + uint16_t checksum; + uint8_t *pdata; + int i; + + checksum = 0; + pdata = (uint8_t *)data; + for (i = 0; i < length; i++) + checksum += pdata[i]; + + return checksum; +} + + +static uint32_t emmckey_checksum(uint8_t *buf, uint32_t length) +{ + uint32_t checksum = 0; + uint32_t cnt; + + for (cnt = 0; cnt < length; cnt++) + checksum += buf[cnt]; + + return checksum; +} + + + +static int is_hex_str(uint8_t *buf, uint32_t len) +{ + uint32_t i; + + for (i = 0; i < len; i++) + switch (buf[i]) { + case '0' ... '9': + case 'a' ... 'f': + case 'A' ... 'F': + break; + default: + return 0; + } + return 1; +} + +static char asc_to_hex(char para) +{ + if (para >= '0' && para <= '9') + para = para-'0'; + else if (para >= 'a' && para <= 'f') + para = para - 'a'+0xa; + else if (para >= 'A' && para <= 'F') + para = para-'A'+0xa; + + return para; +} + + +uint32_t to_storage_data(uint8_t *data, uint32_t valid_size, + uint8_t *storage_data, uint16_t storage_size, + uint16_t *checksum) +{ + uint8_t *tmp_buf; + uint8_t *data_clone; + uint8_t chr; + int i; + uint32_t valid_comp_size = 0; + int enc_data_len = 0; + + + if (!is_hex_str(data, valid_size)) + return RET_EINVAL; + + data_clone = storage_zalloc(valid_size); + if (!data_clone) + return RET_EMEM; + memcpy(data_clone, data, valid_size); + + for (i = 0; i < valid_size; i++) { + chr = data_clone[i]; + if (i%2 == 0) + data_clone[i/2] = 0x00; + data_clone[i/2] |= (i%2 == 0) ? + (asc_to_hex(chr) << 4):(asc_to_hex(chr) & 0xf); + } + + + valid_comp_size = (valid_size+1)>>1; + *checksum = aml_key_checksum(data_clone, valid_comp_size); + + tmp_buf = storage_zalloc(storage_size); + if (!tmp_buf) { + storage_free(data_clone); + return RET_EMEM; + } + memcpy(tmp_buf, &valid_comp_size, 4); + memcpy(tmp_buf+4, data_clone, valid_comp_size); + + do_aes_internal(1, tmp_buf, storage_size, storage_data, &enc_data_len); + + storage_free(tmp_buf); + storage_free(data_clone); + + return RET_OK; +} + + uint32_t storage_write(uint8_t *name, uint32_t namelen, uint8_t *writebuf, uint32_t bufsize, uint32_t attr) { - struct storage_node *node; + struct storage_node *node; struct storage_object *object; + int is_new = 0; - if (namelen >= MAX_OBJ_NAME_LEN) + + if ((namelen >= MAX_OBJ_NAME_LEN) || + !is_hex_str(writebuf, bufsize)) return RET_EINVAL; + + /* update key node */ node = storage_find(name, namelen); if (node == NULL) { /*it's new object*/ + is_new = 1; node = storage_create(name, namelen); if (node == NULL) return RET_EMEM; object = &(node->object); - object->dataptr = storage_malloc((int32_t)bufsize); - if (!object->dataptr) - return RET_EMEM; node->status |= OBJ_STATUS_VALID; object->attribute = attr; + object->namesize = namelen; object->type = OBJ_TYPE_GENERIC; - memcpy(object->dataptr, writebuf, bufsize); - object->datasize = bufsize; - storage_hash(object->dataptr, - object->datasize, object->hashptr); + object->type1 = 0x41c; /* be compatible with old unifykey */ } else { /* the object is existed*/ + /* TODO: check whether attr is updated correctly */ if (storage_infocheck(node, attr) < 0) return RET_EINVAL; object = &(node->object); - if (object->datasize < bufsize) { - storage_free(object->dataptr); - object->dataptr = storage_malloc(bufsize); - if (!object->dataptr) - return RET_EMEM; - memcpy(object->dataptr, writebuf, bufsize); - object->datasize = bufsize; - storage_hash(object->dataptr, - object->datasize, object->hashptr); - } else { - memcpy(object->dataptr, writebuf, bufsize); - object->datasize = bufsize; - storage_hash(object->dataptr, - object->datasize, object->hashptr); - } + storage_free(object->dataptr); + storage_free(object->storage_data); } - if (!storage_writeToFlash()) - return RET_OK; - else + object->datasize = bufsize; + object->valid_size = bufsize; + object->storage_size = (((((object->valid_size+1)>>1)+4)+15)>>4)<<4; + object->dataptr = storage_zalloc(object->valid_size); + if (!object->dataptr) { + if (is_new) + storage_del(node); + return RET_EMEM; + } + object->storage_data = storage_zalloc(object->storage_size); + if (!object->storage_data) { + if (is_new) + storage_del(node); + return RET_EMEM; + } + memcpy(object->dataptr, writebuf, object->valid_size); + + if (to_storage_data(object->dataptr, object->valid_size, + object->storage_data, object->storage_size, + &object->checksum) != RET_OK) { + if (is_new) + storage_del(node); return RET_EFAIL; + } + + storage_hash(object->dataptr, + object->datasize, object->hashptr); + + storage_writeToFlash(); + + return RET_OK; } static int32_t storage_read_permit(struct storage_node *node) @@ -385,101 +503,151 @@ uint32_t storage_status(uint8_t *name, uint32_t namelen, uint32_t *retval) return RET_OK; } +static int emmc_key_transfer(u8 *buf, u32 *value, u32 len, u32 direct) +{ + u8 checksum = 0; + u32 i; + + if (direct) { + for (i = 0; i < len; i++) + checksum += buf[i]; + for (i = 0; i < len; i++) + buf[i] ^= checksum; + *value = checksum; + } else { + checksum = *value; + for (i = 0; i < len; i++) + buf[i] ^= checksum; + checksum = 0; + for (i = 0; i < len; i++) + checksum += buf[i]; + if (checksum == *value) + return 0; + return -1; + } + + return 0; +} + + /* initialize nodelist from internal storage block*/ void storage_init(uint32_t flashsize) { - uint8_t *internal_storage; - struct storage_block_raw_head *prawhead; - struct storage_block_enc_head enchead; - uint8_t *pcontent, *prawcontent; - uint8_t *pblock = (uint8_t *)get_share_storage_block_base(); + uint8_t *pblock = + (uint8_t *)get_share_storage_block_base(); + struct emmc_key_head *pemmc_key_head; + struct key_storage_head *pstorage_key_head; + int key_count; + int i, j, n; + struct storage_node *node; + struct storage_object *tmp_obj; + char *tmp_content; + uint8_t *tmp; + int out_len; + int key_hex_len; - int32_t ret; - uint32_t outlen, sum; - struct storage_node *node; + /* we should handle an extra header for emmc key */ + if (kallsyms_lookup_name("emmc_key_read")) { + pemmc_key_head = (struct emmc_key_head *)pblock; + pstorage_key_head = (struct key_storage_head *) + (pblock + sizeof(struct emmc_key_head)); + } else { + pemmc_key_head = NULL; + pstorage_key_head = (struct key_storage_head *)pblock; + } + if (pemmc_key_head != NULL) { + if (!emmc_key_transfer(pemmc_key_head->mark, + &pemmc_key_head->mark_checksum, 8, 0)) { + if (memcmp(pemmc_key_head->mark, + "emmckeys", 8) != 0) { + flash_storage_size = flashsize; + goto storage_init_err; + } + } else { + flash_storage_size = flashsize; + goto storage_init_err; + } + } - /* reset */ - storage_reset(); - internal_storage = (uint8_t *)get_internal_storage_base(); - if (!internal_storage) - goto storage_init_err; - - aesfrom = -1; - version = -1; - prawhead = (struct storage_block_raw_head *)pblock; - if (strcmp((const char *)(prawhead->mark), "AMLSECURITY") != 0) { + if (memcmp(pstorage_key_head->mark, "keyexist", 8) != 0) { flash_storage_size = flashsize; goto storage_init_err; } - aesfrom = prawhead->enctype; - /*fixme: no enc support now*/ -#if 0 - set_encryptkeyfrom(aesfrom); -#endif - version = prawhead->version; -#if 1 - ret = do_aes_internal(0, - pblock+STORAGE_BLOCK_RAW_HEAD_SIZE, - STORAGE_BLOCK_ENC_HEAD_SIZE, - internal_storage, (int *)&outlen); - if (ret) { - flash_storage_size = flashsize; - goto storage_init_err; - } -#else - memcpy(internal_storage, pblock + STORAGE_BLOCK_RAW_HEAD_SIZE, - STORAGE_BLOCK_ENC_HEAD_SIZE); -#endif - - memset(&enchead, 0, sizeof(enchead)); - ret = Tlv_ReadHead(internal_storage, &enchead); - if (ret || (enchead.blocksize == 0)) { - flash_storage_size = flashsize; - goto storage_init_err; - } - if (enchead.flashsize != 0) - flash_storage_size = enchead.flashsize; - if (flash_storage_size != flashsize) - goto storage_init_err; - - pcontent = pblock+STORAGE_BLOCK_RAW_HEAD_SIZE - + STORAGE_BLOCK_ENC_HEAD_SIZE; - prawcontent = internal_storage + STORAGE_BLOCK_ENC_HEAD_SIZE; -#if 1 - ret = do_aes_internal(0, pcontent, ((enchead.blocksize+15)/16)*16, - prawcontent, (int *)&outlen); - if (ret) - goto storage_init_err; -#else - memcpy(prawcontent, pcontent, ((enchead.blocksize+15)/16)*16); -#endif - sum = enchead.blocksize; - while (sum > 0) { + /* parse each key */ + tmp_content = (char *)&pstorage_key_head[1]; // skip storage key header + key_count = pstorage_key_head->item_cnt; + for (i = 0; i < key_count; i++) { node = storage_create_empty(); if (node == NULL) goto storage_init_err; - ret = Tlv_ReadObject(prawcontent, node, &outlen); - if (ret) + + tmp_obj = (struct storage_object *) + &tmp_content[pstorage_key_head->item[i].position]; + node->object.slot = tmp_obj->slot; + node->object.state = tmp_obj->state; + node->object.storage_size = tmp_obj->storage_size; + node->object.valid_size = tmp_obj->valid_size; + node->object.type1 = tmp_obj->type1; + node->object.checksum = tmp_obj->checksum; + node->object.type = tmp_obj->type; + node->object.attribute = tmp_obj->attribute; + node->object.datasize = tmp_obj->valid_size; + strcpy(node->object.name, tmp_obj->name); + node->object.namesize = strlen(node->object.name); + + node->object.storage_data = + storage_malloc(node->object.storage_size); + if (node->object.storage_data == NULL) goto storage_init_err; - ret = check_object_valid(&(node->object)); - if (!ret) + node->object.dataptr = storage_malloc(node->object.valid_size); + if (node->object.dataptr == NULL) + goto storage_init_err; + memcpy(node->object.storage_data, + &tmp_content[pstorage_key_head->item[i].position+ + sizeof(struct storage_object)], + node->object.storage_size); + memcpy(node->object.hashptr, tmp_obj->hashptr, + sizeof(tmp_obj->hashptr)); + + /* decrypt each key */ + tmp = storage_malloc(node->object.storage_size); + if (tmp == NULL) + goto storage_init_err; + + do_aes_internal(0, node->object.storage_data, + node->object.storage_size, tmp, &out_len); + memcpy(&key_hex_len, tmp, 4); + tmp = tmp + 4; + + /* switch to ascii form */ + /* TODO: add checksum validation(refer to key_core_show) */ + memset(node->object.dataptr, 0x00, node->object.valid_size); + n = 0; + for (j = 0; j < node->object.valid_size>>1; j++) + n += sprintf(&node->object.dataptr[n], "%02x", tmp[j]); + if (node->object.valid_size % 2) + n += sprintf(&node->object.dataptr[n], "%x", + (tmp[j]&0xf0)>>4); + tmp = tmp - 4; + storage_free(tmp); + + if (check_object_valid(&(node->object)) == 0) node->status = OBJ_STATUS_VALID; else node->status = OBJ_STATUS_INVALID; - sum -= outlen; - prawcontent += outlen; } + goto storage_init_exit; storage_init_err: - storage_reset(); - free_share_storage(); - free_internal_storage(); + storage_reset(); + free_share_storage(); + free_internal_storage(); storage_init_exit: - return; + return; } uint32_t get_share_storage_block_base(void) diff --git a/drivers/amlogic/unifykey/v7/key_storage/storage.h b/drivers/amlogic/unifykey/v7/key_storage/storage.h index 33a1d109fb01..6f802c95b376 100644 --- a/drivers/amlogic/unifykey/v7/key_storage/storage.h +++ b/drivers/amlogic/unifykey/v7/key_storage/storage.h @@ -22,7 +22,11 @@ #include #include -#define MAX_OBJ_NAME_LEN 80 +#define KEY_MAX_COUNT 32 + + +#define MAX_OBJ_NAME_LEN 16 +#define MAX_MAGIC_STR_LEN 16 /*Attribute*/ #define OBJ_ATTR_SECURE (1<<0) @@ -33,13 +37,21 @@ #define OBJ_TYPE_GENERIC 0xA00000BF struct storage_object { - char name[MAX_OBJ_NAME_LEN]; + char name[MAX_OBJ_NAME_LEN]; + uint16_t slot; + uint16_t type1; + uint16_t valid_size; + uint16_t storage_size; + uint16_t state; + uint16_t checksum; + char hashptr[36]; uint32_t namesize; uint32_t attribute; /*secure, OTP*/ uint32_t type; /*AES, RSA, GENERIC, ...*/ uint32_t datasize; - uint8_t *dataptr; - uint8_t hashptr[32]; + char reserve[40]; + uint8_t *storage_data; + uint8_t *dataptr; }; #define OBJ_STATUS_FREE 0 @@ -57,6 +69,31 @@ struct storage_node { #define ENC_TYPE_FIXED 2 #define STORAGE_BLOCK_RAW_HEAD_SIZE 512 #define BLOCK_VERSION 0 + +struct emmc_key_head { + uint8_t mark[MAX_MAGIC_STR_LEN]; + uint32_t mark_checksum; + uint32_t checksum; + uint32_t reserve; +}; + +struct key_head_item { + uint32_t position; + uint32_t state; + uint32_t reserve; +}; +struct key_storage_head { + char mark[MAX_MAGIC_STR_LEN]; + uint32_t version; + uint32_t inver; //inver = ~version + 1 + uint32_t tag; + uint32_t size; //tatol size + uint32_t item_cnt; + struct key_head_item item[KEY_MAX_COUNT]; + char reserve[92]; +}; + + struct storage_block_raw_head { uint8_t mark[16]; /* AMLSECURITY*/ uint32_t version; diff --git a/drivers/amlogic/unifykey/v7/key_storage/storage_data.h b/drivers/amlogic/unifykey/v7/key_storage/storage_data.h index 4fda93496d4c..2a428759f80f 100644 --- a/drivers/amlogic/unifykey/v7/key_storage/storage_data.h +++ b/drivers/amlogic/unifykey/v7/key_storage/storage_data.h @@ -25,7 +25,7 @@ static LIST_HEAD(nodelist); static int32_t aesfrom = -1; static int32_t aesfrom_outer = -1; -static int32_t version; +//static int32_t version; static uint8_t *storage_shar_in_block; static uint8_t *storage_shar_out_block; static uint8_t *storage_share_block; diff --git a/drivers/amlogic/unifykey/v7/key_storage/storage_util.c b/drivers/amlogic/unifykey/v7/key_storage/storage_util.c index 658628895957..d82fe967b268 100644 --- a/drivers/amlogic/unifykey/v7/key_storage/storage_util.c +++ b/drivers/amlogic/unifykey/v7/key_storage/storage_util.c @@ -25,7 +25,13 @@ void *storage_malloc(int32_t size) return kmalloc(size, GFP_KERNEL); } +void *storage_zalloc(int32_t size) +{ + return kzalloc(size, GFP_KERNEL); +} + void storage_free(void *ptr) { - kfree(ptr); + if (ptr != NULL) + kfree(ptr); } diff --git a/drivers/amlogic/unifykey/v7/key_storage/storage_util.h b/drivers/amlogic/unifykey/v7/key_storage/storage_util.h index 9f46b023d31b..db6a28a16853 100644 --- a/drivers/amlogic/unifykey/v7/key_storage/storage_util.h +++ b/drivers/amlogic/unifykey/v7/key_storage/storage_util.h @@ -21,6 +21,7 @@ #include void *storage_malloc(int32_t size); +void *storage_zalloc(int32_t size); void storage_free(void *ptr); #endif diff --git a/drivers/amlogic/unifykey/v7/key_storage/tlv.c b/drivers/amlogic/unifykey/v7/key_storage/tlv.c deleted file mode 100644 index eee036d5dc18..000000000000 --- a/drivers/amlogic/unifykey/v7/key_storage/tlv.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * drivers/amlogic/unifykey/v7/key_storage/tlv.c - * - * Copyright (C) 2017 Amlogic, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - */ - -#include -#include "storage.h" -#include "storage_util.h" - -static uint32_t Tlv_WriteUint32(uint8_t *output, uint32_t tag, uint32_t value) -{ - uint8_t *out; - - out = output; - *((uint32_t *)out) = tag; - out += 4; - *((uint32_t *)out) = 4; - out += 4; - *((uint32_t *)out) = value; - return 12; -} - -static uint32_t Tlv_WriteBuf(uint8_t *output, uint32_t tag, - uint32_t length, uint8_t *input) -{ - uint8_t *out = output; - *((uint32_t *)out) = tag; - out += 4; - *((uint32_t *)out) = (((length+3)/4)*4); - out += 4; - memset(out, 0, ((length+3)/4)*4); - memcpy(out, input, length); - return 8+(((length+3)/4)*4); -} - -int32_t Tlv_ReadHead(uint8_t *buf, struct storage_block_enc_head *pblockhead) -{ - uint32_t tag; - uint32_t sum; - uint32_t length; - uint8_t *input; - - input = buf; - tag = *((uint32_t *)input); - input += 4; - sum = *((uint32_t *)input); - input += 4; - if (tag != emTlvHead) - return -1; - - while (sum > 0) { - tag = *((unsigned int *)input); - input += 4; - length = *((unsigned int *)input); - input += 4; - sum -= 8; - switch (tag) { - case emTlvHeadSize: - pblockhead->blocksize = *((uint32_t *)input); - sum -= length; - input += length; - break; - case emTlvHeadFlashSize: - pblockhead->flashsize = *((uint32_t *)input); - sum -= length; - input += length; - break; - default: - sum -= length; - input += length; - break; - } - } - return 0; -} - -int32_t Tlv_ReadObject(uint8_t *input, - struct storage_node *node, uint32_t *allsize) -{ - uint8_t *buf; - uint32_t tag; - uint32_t length; - uint32_t isum; - struct storage_object *pcontent = &(node->object); - - buf = input; - tag = *(unsigned int *)buf; - buf += 4; - isum = *(unsigned int *)buf; - buf += 4; - if (tag != emTlvObject) - return -1; - *allsize = isum+8; - - while (isum > 0) { - tag = *(uint32_t *)buf; - buf += 4; - length = *(uint32_t *)buf; - buf += 4; - isum -= 8; - switch (tag) { - case emTlvObjNameSize: - pcontent->namesize = *((uint32_t *)buf); - buf += 4; - isum -= 4; - break; - case emTlvObjName: - memset(pcontent->name, 0, MAX_OBJ_NAME_LEN); - memcpy(pcontent->name, buf, pcontent->namesize); - buf += length; - isum -= length; - break; - case emTlvObjType: - pcontent->type = *((uint32_t *)buf); - buf += 4; - isum -= 4; - break; - case emTlvObjAttr: - pcontent->attribute = *((uint32_t *)buf); - buf += 4; - isum -= 4; - break; - case emTlvObjDataSize: - pcontent->datasize = *((uint32_t *)buf); - buf += 4; - isum -= 4; - break; - case emTlvObjHashBuf: - if (length != 32) - goto Tlv_ReadKeyContent_err; - memcpy(pcontent->hashptr, buf, length); - buf += length; - isum -= length; - break; - case emTlvObjDataBuf: - pcontent->dataptr = - storage_malloc(pcontent->datasize); - if (!pcontent->dataptr) - goto Tlv_ReadKeyContent_err; - memcpy(pcontent->dataptr, - buf, pcontent->datasize); - buf += length; - isum -= length; - break; - default: - buf += length; - isum -= length; - break; - } - } - return 0; - -Tlv_ReadKeyContent_err: - node->status = OBJ_STATUS_INVALID; - return -1; -} - -void Tlv_WriteHead(struct storage_block_enc_head *enchead, uint8_t *output) -{ - uint8_t *buffer; - uint32_t *sum; - uint32_t length; - - buffer = output; - *(uint32_t *)buffer = emTlvHead; - buffer += 4; - sum = (uint32_t *)buffer; - buffer += 4; - - *sum = 0; - - length = Tlv_WriteUint32(buffer, emTlvHeadSize, enchead->blocksize); - *sum += length; - buffer += length; - - length = Tlv_WriteUint32(buffer, emTlvHeadFlashSize, - enchead->flashsize); - *sum += length; - buffer += length; -} - -void Tlv_WriteObject(struct storage_object *object, - uint8_t *output, uint32_t *outlen) -{ - uint32_t length; - uint32_t *sum; - uint8_t *buffer; - uint32_t temp; - - buffer = output; - *outlen = 0; - *(uint32_t *)buffer = emTlvObject; - buffer += 4; - sum = (uint32_t *)buffer; - buffer += 4; - *outlen += 8; - - temp = strlen(object->name); - if (temp != 0) { - length = Tlv_WriteUint32(buffer, emTlvObjNameSize, temp); - buffer += length; - *outlen += length; - - length = Tlv_WriteBuf(buffer, emTlvObjName, - temp, (uint8_t *)(object->name)); - buffer += length; - *outlen += length; - } - - if (object->dataptr && (object->datasize != 0)) { - length = Tlv_WriteUint32(buffer, - emTlvObjDataSize, object->datasize); - buffer += length; - *outlen += length; - - length = Tlv_WriteBuf(buffer, emTlvObjDataBuf, - object->datasize, object->dataptr); - buffer += length; - *outlen += length; - } - - length = Tlv_WriteBuf(buffer, emTlvObjHashBuf, 32, object->hashptr); - buffer += length; - *outlen += length; - - length = Tlv_WriteUint32(buffer, emTlvObjType, object->type); - buffer += length; - *outlen += length; - - length = Tlv_WriteUint32(buffer, emTlvObjAttr, object->attribute); - buffer += length; - *outlen += length; - - *sum = (*outlen-8); -}