crypto/fsl: Add command for encapsulating/decapsulating blobs
authorRuchika Gupta <ruchika.gupta@freescale.com>
Tue, 7 Oct 2014 10:16:20 +0000 (15:46 +0530)
committerYork Sun <yorksun@freescale.com>
Thu, 16 Oct 2014 21:20:40 +0000 (14:20 -0700)
Freescale's SEC block has built-in Blob Protocol which provides
a method for protecting user-defined data across system power
cycles. SEC block protects data in a data structure called a Blob,
which provides both confidentiality and integrity protection.

Encapsulating data as a blob
Each time that the Blob Protocol is used to protect data, a
different randomly generated key is used to encrypt the data.
This random key is itself encrypted using a key which is derived
from SoC's non volatile secret key and a 16 bit Key identifier.
The resulting encrypted key along with encrypted data is called a blob.
The non volatile secure key is available for use only during secure boot.

During decapsulation, the reverse process is performed to get back
the original data.

Commands added
--------------
    blob enc - encapsulating data as a cryptgraphic blob
    blob dec - decapsulating cryptgraphic blob to get the data

Commands Syntax
---------------
blob enc src dst len km

Encapsulate and create blob of data $len bytes long
at address $src and store the result at address $dst.
$km is the 16 byte key modifier is also required for
generation/use as key for cryptographic operation. Key
modifier should be 16 byte long.

blob dec src dst len km

Decapsulate the  blob of data at address $src and
store result of $len byte at addr $dst.
$km is the 16 byte key modifier is also required for
generation/use as key for cryptographic operation. Key
modifier should be 16 byte long.

Signed-off-by: Ruchika Gupta <ruchika.gupta@freescale.com>
Reviewed-by: York Sun <yorksun@freescale.com>
common/Makefile
common/cmd_blob.c [new file with mode: 0644]
drivers/crypto/fsl/Makefile
drivers/crypto/fsl/fsl_blob.c [new file with mode: 0644]
drivers/crypto/fsl/jobdesc.c
drivers/crypto/fsl/jobdesc.h
drivers/crypto/fsl/jr.c
include/fsl_sec.h

index b19d3793b467b0e8d88ffb6ae287a1ebf1b9a833..c84b3bc102a418d3f62bd68934c6f7aa53286c11 100644 (file)
@@ -265,4 +265,6 @@ obj-y += aboot.o
 obj-y += fb_mmc.o
 endif
 
+obj-$(CONFIG_CMD_BLOB) += cmd_blob.o
+
 CFLAGS_env_embedded.o := -Wa,--no-warn -DENV_CRC=$(shell tools/envcrc 2>/dev/null)
diff --git a/common/cmd_blob.c b/common/cmd_blob.c
new file mode 100644 (file)
index 0000000..82ecaf0
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *
+ * Command for encapsulating/decapsulating blob of memory.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <malloc.h>
+#include <asm/byteorder.h>
+#include <linux/compiler.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * blob_decap() - Decapsulate the data as a blob
+ * @key_mod:   - Pointer to key modifier/key
+ * @src:       - Address of data to be decapsulated
+ * @dst:       - Address of data to be decapsulated
+ * @len:       - Size of data to be decapsulated
+ *
+ * Returns zero on success,and negative on error.
+ */
+__weak int blob_decap(u8 *key_mod, u8 *src, u8 *dst, u32 len)
+{
+       return 0;
+}
+
+/**
+ * blob_encap() - Encapsulate the data as a blob
+ * @key_mod:   - Pointer to key modifier/key
+ * @src:       - Address of data to be encapsulated
+ * @dst:       - Address of data to be encapsulated
+ * @len:       - Size of data to be encapsulated
+ *
+ * Returns zero on success,and negative on error.
+ */
+__weak int blob_encap(u8 *key_mod, u8 *src, u8 *dst, u32 len)
+{
+       return 0;
+}
+
+/**
+ * do_blob() - Handle the "blob" command-line command
+ * @cmdtp:     Command data struct pointer
+ * @flag:      Command flag
+ * @argc:      Command-line argument count
+ * @argv:      Array of command-line arguments
+ *
+ * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
+ * on error.
+ */
+static int do_blob(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
+{
+       uint32_t key_addr, src_addr, dst_addr, len;
+       uint8_t *km_ptr, *src_ptr, *dst_ptr;
+       int enc, ret = 0;
+
+       if (argc != 6)
+               return CMD_RET_USAGE;
+
+       if (!strncmp(argv[1], "enc", 3))
+               enc = 1;
+       else if (!strncmp(argv[1], "dec", 3))
+               enc = 0;
+       else
+               return CMD_RET_USAGE;
+
+       src_addr = simple_strtoul(argv[2], NULL, 16);
+       dst_addr = simple_strtoul(argv[3], NULL, 16);
+       len = simple_strtoul(argv[4], NULL, 16);
+       key_addr = simple_strtoul(argv[5], NULL, 16);
+
+       km_ptr = (uint8_t *)key_addr;
+       src_ptr = (uint8_t *)src_addr;
+       dst_ptr = (uint8_t *)dst_addr;
+
+       if (enc)
+               ret = blob_encap(km_ptr, src_ptr, dst_ptr, len);
+       else
+               ret = blob_decap(km_ptr, src_ptr, dst_ptr, len);
+
+       return ret;
+}
+
+/***************************************************/
+static char blob_help_text[] =
+       "enc src dst len km - Encapsulate and create blob of data\n"
+       "                          $len bytes long at address $src and\n"
+       "                          store the result at address $dst.\n"
+       "                          $km is the 16 byte key modifier\n"
+       "                          is also required for generation/use as\n"
+       "                          key for cryptographic operation. Key\n"
+       "                          modifier should be 16 byte long.\n"
+       "blob dec src dst len km - Decapsulate the  blob of data at address\n"
+       "                          $src and store result of $len byte at\n"
+       "                          addr $dst.\n"
+       "                          $km is the 16 byte key modifier\n"
+       "                          is also required for generation/use as\n"
+       "                          key for cryptographic operation. Key\n"
+       "                          modifier should be 16 byte long.\n";
+
+U_BOOT_CMD(
+       blob, 6, 1, do_blob,
+       "Blob encapsulation/decryption",
+       blob_help_text
+);
index 59d96519e77e677c8813b98e3a127de8cf4497c5..cb13d2e0ae65c6c80009730890448a12681e14eb 100644 (file)
@@ -7,3 +7,4 @@
 #
 
 obj-$(CONFIG_FSL_CAAM) += jr.o fsl_hash.o jobdesc.o error.o
+obj-$(CONFIG_CMD_BLOB) += fsl_blob.o
diff --git a/drivers/crypto/fsl/fsl_blob.c b/drivers/crypto/fsl/fsl_blob.c
new file mode 100644 (file)
index 0000000..bc01075
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include "jobdesc.h"
+#include "desc.h"
+#include "jr.h"
+
+int blob_decrypt(u8 *key_mod, u8 *src, u8 *dst, u8 len)
+{
+       int ret, i = 0;
+       u32 *desc;
+
+       printf("\nDecapsulating data to form blob\n");
+       desc = malloc(sizeof(int) * MAX_CAAM_DESCSIZE);
+       if (!desc) {
+               debug("Not enough memory for descriptor allocation\n");
+               return -1;
+       }
+
+       inline_cnstr_jobdesc_blob_decap(desc, key_mod, src, dst, len);
+
+       for (i = 0; i < 14; i++)
+               printf("%x\n", *(desc + i));
+       ret = run_descriptor_jr(desc);
+
+       if (ret)
+               printf("Error in Decapsulation %d\n", ret);
+
+       free(desc);
+       return ret;
+}
+
+int blob_encrypt(u8 *key_mod, u8 *src, u8 *dst, u8 len)
+{
+       int ret, i = 0;
+       u32 *desc;
+
+       printf("\nEncapsulating data to form blob\n");
+       desc = malloc(sizeof(int) * MAX_CAAM_DESCSIZE);
+       if (!desc) {
+               debug("Not enough memory for descriptor allocation\n");
+               return -1;
+       }
+
+       inline_cnstr_jobdesc_blob_encap(desc, key_mod, src, dst, len);
+       for (i = 0; i < 14; i++)
+               printf("%x\n", *(desc + i));
+       ret = run_descriptor_jr(desc);
+
+       if (ret)
+               printf("Error in Encapsulation %d\n", ret);
+
+       free(desc);
+       return ret;
+}
index cbe5c3020e0d81f4a53016d5bccb71a76d859243..1386baec0fda45a54e7d9664a92cef90d3c6e150 100644 (file)
@@ -12,6 +12,9 @@
 #include "desc_constr.h"
 #include "jobdesc.h"
 
+#define KEY_BLOB_SIZE                  32
+#define MAC_SIZE                       16
+
 void inline_cnstr_jobdesc_hash(uint32_t *desc,
                          const uint8_t *msg, uint32_t msgsz, uint8_t *digest,
                          u32 alg_type, uint32_t alg_size, int sg_tbl)
@@ -43,3 +46,80 @@ void inline_cnstr_jobdesc_hash(uint32_t *desc,
        append_store(desc, dma_addr_out, storelen,
                     LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_CONTEXT);
 }
+
+void inline_cnstr_jobdesc_blob_encap(uint32_t *desc, uint8_t *key_idnfr,
+                                    uint8_t *plain_txt, uint8_t *enc_blob,
+                                    uint32_t in_sz)
+{
+       dma_addr_t dma_addr_key_idnfr, dma_addr_in, dma_addr_out;
+       uint32_t key_sz = KEY_IDNFR_SZ_BYTES;
+       /* output blob will have 32 bytes key blob in beginning and
+        * 16 byte HMAC identifier at end of data blob */
+       uint32_t out_sz = in_sz + KEY_BLOB_SIZE + MAC_SIZE;
+
+       dma_addr_key_idnfr = virt_to_phys((void *)key_idnfr);
+       dma_addr_in     = virt_to_phys((void *)plain_txt);
+       dma_addr_out    = virt_to_phys((void *)enc_blob);
+
+       init_job_desc(desc, 0);
+
+       append_key(desc, dma_addr_key_idnfr, key_sz, CLASS_2);
+
+       append_seq_in_ptr(desc, dma_addr_in, in_sz, 0);
+
+       append_seq_out_ptr(desc, dma_addr_out, out_sz, 0);
+
+       append_operation(desc, OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB);
+}
+
+void inline_cnstr_jobdesc_blob_decap(uint32_t *desc, uint8_t *key_idnfr,
+                                    uint8_t *enc_blob, uint8_t *plain_txt,
+                                    uint32_t out_sz)
+{
+       dma_addr_t dma_addr_key_idnfr, dma_addr_in, dma_addr_out;
+       uint32_t key_sz = KEY_IDNFR_SZ_BYTES;
+       uint32_t in_sz = out_sz + KEY_BLOB_SIZE + MAC_SIZE;
+
+       dma_addr_key_idnfr = virt_to_phys((void *)key_idnfr);
+       dma_addr_in     = virt_to_phys((void *)enc_blob);
+       dma_addr_out    = virt_to_phys((void *)plain_txt);
+
+       init_job_desc(desc, 0);
+
+       append_key(desc, dma_addr_key_idnfr, key_sz, CLASS_2);
+
+       append_seq_in_ptr(desc, dma_addr_in, in_sz, 0);
+
+       append_seq_out_ptr(desc, dma_addr_out, out_sz, 0);
+
+       append_operation(desc, OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB);
+}
+
+/*
+ * Descriptor to instantiate RNG State Handle 0 in normal mode and
+ * load the JDKEK, TDKEK and TDSK registers
+ */
+void inline_cnstr_jobdesc_rng_instantiation(uint32_t *desc)
+{
+       u32 *jump_cmd;
+
+       init_job_desc(desc, 0);
+
+       /* INIT RNG in non-test mode */
+       append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
+                        OP_ALG_AS_INIT);
+
+       /* wait for done */
+       jump_cmd = append_jump(desc, JUMP_CLASS_CLASS1);
+       set_jump_tgt_here(desc, jump_cmd);
+
+       /*
+        * load 1 to clear written reg:
+        * resets the done interrrupt and returns the RNG to idle.
+        */
+       append_load_imm_u32(desc, 1, LDST_SRCDST_WORD_CLRW);
+
+       /* generate secure keys (non-test) */
+       append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG |
+                        OP_ALG_RNG4_SK);
+}
index ed61579ac06aacf2d22c46f26b3049426db1a5a5..3cf7226de23b7d47ebfa8d7f4aad2a4af9eb40e5 100644 (file)
 #include <common.h>
 #include <asm/io.h>
 
+#define KEY_IDNFR_SZ_BYTES             16
+
 void inline_cnstr_jobdesc_hash(uint32_t *desc,
                          const uint8_t *msg, uint32_t msgsz, uint8_t *digest,
                          u32 alg_type, uint32_t alg_size, int sg_tbl);
 
+void inline_cnstr_jobdesc_blob_encap(uint32_t *desc, uint8_t *key_idnfr,
+                                    uint8_t *plain_txt, uint8_t *enc_blob,
+                                    uint32_t in_sz);
+
+void inline_cnstr_jobdesc_blob_decap(uint32_t *desc, uint8_t *key_idnfr,
+                                    uint8_t *enc_blob, uint8_t *plain_txt,
+                                    uint32_t out_sz);
+
+void inline_cnstr_jobdesc_rng_instantiation(uint32_t *desc);
 #endif
index a107e6afcbb8eec10ef4ae0f5ac74df1f2e30c11..29681e18a6d0398558382e3944d063a0912db3d8 100644 (file)
@@ -10,6 +10,7 @@
 #include <malloc.h>
 #include "fsl_sec.h"
 #include "jr.h"
+#include "jobdesc.h"
 
 #define CIRC_CNT(head, tail, size)     (((head) - (tail)) & (size - 1))
 #define CIRC_SPACE(head, tail, size)   CIRC_CNT((tail), (head) + 1, (size))
@@ -319,6 +320,120 @@ int sec_reset(void)
        return 0;
 }
 
+static int instantiate_rng(void)
+{
+       struct result op;
+       u32 *desc;
+       u32 rdsta_val;
+       int ret = 0;
+       ccsr_sec_t __iomem *sec =
+                       (ccsr_sec_t __iomem *)CONFIG_SYS_FSL_SEC_ADDR;
+       struct rng4tst __iomem *rng =
+                       (struct rng4tst __iomem *)&sec->rng;
+
+       memset(&op, 0, sizeof(struct result));
+
+       desc = malloc(sizeof(int) * 6);
+       if (!desc) {
+               printf("cannot allocate RNG init descriptor memory\n");
+               return -1;
+       }
+
+       inline_cnstr_jobdesc_rng_instantiation(desc);
+       ret = run_descriptor_jr(desc);
+
+       if (ret)
+               printf("RNG: Instantiation failed with error %x\n", ret);
+
+       rdsta_val = sec_in32(&rng->rdsta);
+       if (op.status || !(rdsta_val & RNG_STATE0_HANDLE_INSTANTIATED))
+               return -1;
+
+       return ret;
+}
+
+static u8 get_rng_vid(void)
+{
+       ccsr_sec_t *sec = (void *)CONFIG_SYS_FSL_SEC_ADDR;
+       u32 cha_vid = sec_in32(&sec->chavid_ls);
+
+       return (cha_vid & SEC_CHAVID_RNG_LS_MASK) >> SEC_CHAVID_LS_RNG_SHIFT;
+}
+
+/*
+ * By default, the TRNG runs for 200 clocks per sample;
+ * 1200 clocks per sample generates better entropy.
+ */
+static void kick_trng(int ent_delay)
+{
+       ccsr_sec_t __iomem *sec =
+                       (ccsr_sec_t __iomem *)CONFIG_SYS_FSL_SEC_ADDR;
+       struct rng4tst __iomem *rng =
+                       (struct rng4tst __iomem *)&sec->rng;
+       u32 val;
+
+       /* put RNG4 into program mode */
+       sec_setbits32(&rng->rtmctl, RTMCTL_PRGM);
+       /* rtsdctl bits 0-15 contain "Entropy Delay, which defines the
+        * length (in system clocks) of each Entropy sample taken
+        * */
+       val = sec_in32(&rng->rtsdctl);
+       val = (val & ~RTSDCTL_ENT_DLY_MASK) |
+             (ent_delay << RTSDCTL_ENT_DLY_SHIFT);
+       sec_out32(&rng->rtsdctl, val);
+       /* min. freq. count, equal to 1/4 of the entropy sample length */
+       sec_out32(&rng->rtfreqmin, ent_delay >> 2);
+       /* max. freq. count, equal to 8 times the entropy sample length */
+       sec_out32(&rng->rtfreqmax, ent_delay << 3);
+       /* put RNG4 into run mode */
+       sec_clrbits32(&rng->rtmctl, RTMCTL_PRGM);
+}
+
+static int rng_init(void)
+{
+       int ret, ent_delay = RTSDCTL_ENT_DLY_MIN;
+       ccsr_sec_t __iomem *sec =
+                       (ccsr_sec_t __iomem *)CONFIG_SYS_FSL_SEC_ADDR;
+       struct rng4tst __iomem *rng =
+                       (struct rng4tst __iomem *)&sec->rng;
+
+       u32 rdsta = sec_in32(&rng->rdsta);
+
+       /* Check if RNG state 0 handler is already instantiated */
+       if (rdsta & RNG_STATE0_HANDLE_INSTANTIATED)
+               return 0;
+
+       do {
+               /*
+                * If either of the SH's were instantiated by somebody else
+                * then it is assumed that the entropy
+                * parameters are properly set and thus the function
+                * setting these (kick_trng(...)) is skipped.
+                * Also, if a handle was instantiated, do not change
+                * the TRNG parameters.
+                */
+               kick_trng(ent_delay);
+               ent_delay += 400;
+               /*
+                * if instantiate_rng(...) fails, the loop will rerun
+                * and the kick_trng(...) function will modfiy the
+                * upper and lower limits of the entropy sampling
+                * interval, leading to a sucessful initialization of
+                * the RNG.
+                */
+               ret = instantiate_rng();
+       } while ((ret == -1) && (ent_delay < RTSDCTL_ENT_DLY_MAX));
+       if (ret) {
+               printf("RNG: Failed to instantiate RNG\n");
+               return ret;
+       }
+
+        /* Enable RDB bit so that RNG works faster */
+       sec_setbits32(&sec->scfgr, SEC_SCFGR_RDBENABLE);
+
+       return ret;
+}
+
 int sec_init(void)
 {
        int ret = 0;
@@ -330,8 +445,18 @@ int sec_init(void)
        sec_out32(&sec->mcfgr, mcr | 1 << MCFGR_PS_SHIFT);
 #endif
        ret = jr_init();
-       if (ret < 0)
+       if (ret < 0) {
+               printf("SEC initialization failed\n");
                return -1;
+       }
+
+       if (get_rng_vid() >= 4) {
+               if (rng_init() < 0) {
+                       printf("RNG instantiation failed\n");
+                       return -1;
+               }
+               printf("SEC: RNG instantiated\n");
+       }
 
        return ret;
 }
index 2a26d85ee8dd1765e720097ba1b2c2da0f97051a..aa850a3bf137dc4015840eb93e0afc86ad1fd072 100644 (file)
 
 /* Security Engine Block (MS = Most Sig., LS = Least Sig.) */
 #if CONFIG_SYS_FSL_SEC_COMPAT >= 4
+/* RNG4 TRNG test registers */
+struct rng4tst {
+#define RTMCTL_PRGM 0x00010000 /* 1 -> program mode, 0 -> run mode */
+       u32 rtmctl;             /* misc. control register */
+       u32 rtscmisc;           /* statistical check misc. register */
+       u32 rtpkrrng;           /* poker range register */
+#define RTSDCTL_ENT_DLY_MIN    1200
+#define RTSDCTL_ENT_DLY_MAX    12800
+       union {
+               u32 rtpkrmax;   /* PRGM=1: poker max. limit register */
+               u32 rtpkrsq;    /* PRGM=0: poker square calc. result register */
+       };
+#define RTSDCTL_ENT_DLY_SHIFT 16
+#define RTSDCTL_ENT_DLY_MASK (0xffff << RTSDCTL_ENT_DLY_SHIFT)
+       u32 rtsdctl;            /* seed control register */
+       union {
+               u32 rtsblim;    /* PRGM=1: sparse bit limit register */
+               u32 rttotsam;   /* PRGM=0: total samples register */
+       };
+       u32 rtfreqmin;          /* frequency count min. limit register */
+       union {
+               u32 rtfreqmax;  /* PRGM=1: freq. count max. limit register */
+               u32 rtfreqcnt;  /* PRGM=0: freq. count register */
+       };
+       u32 rsvd1[40];
+#define RNG_STATE0_HANDLE_INSTANTIATED 0x00000001
+       u32 rdsta;              /*RNG DRNG Status Register*/
+       u32 rsvd2[15];
+};
+
 typedef struct ccsr_sec {
        u32     res0;
        u32     mcfgr;          /* Master CFG Register */
@@ -53,7 +83,9 @@ typedef struct ccsr_sec {
        u8      res4[0x40];
        u32     dar;            /* DECO Avail Register */
        u32     drr;            /* DECO Reset Register */
-       u8      res5[0xe78];
+       u8      res5[0x4d8];
+       struct rng4tst rng;     /* RNG Registers */
+       u8      res11[0x8a0];
        u32     crnr_ms;        /* CHA Revision Number Register, MS */
        u32     crnr_ls;        /* CHA Revision Number Register, LS */
        u32     ctpr_ms;        /* Compile Time Parameters Register, MS */