The patch implements secure booting for the mvebu architecture. This includes: - The addition of secure headers and all needed signatures and keys in mkimage - Commands capable of writing the board's efuses to both write the needed cryptographic data and enable the secure booting mechanism - The creation of convenience text files containing the necessary commands to write the efuses The KAK and CSK keys are expected to reside in the files kwb_kak.key and kwb_csk.key (OpenSSL 2048 bit private keys) in the top-level directory. Signed-off-by: Reinhard Pfau <reinhard.pfau@gdsys.cc> Signed-off-by: Mario Six <mario.six@gdsys.cc> Reviewed-by: Stefan Roese <sr@denx.de> Reviewed-by: Simon Glass <sjg@chromium.org> Signed-off-by: Stefan Roese <sr@denx.de>master
parent
4991b4f7f1
commit
a1b6b0a9c1
@ -0,0 +1,264 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015-2016 Reinhard Pfau <reinhard.pfau@gdsys.cc> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <config.h> |
||||||
|
#include <common.h> |
||||||
|
#include <errno.h> |
||||||
|
#include <asm/io.h> |
||||||
|
#include <asm/arch/cpu.h> |
||||||
|
#include <asm/arch/efuse.h> |
||||||
|
#include <asm/arch/soc.h> |
||||||
|
#include <linux/mbus.h> |
||||||
|
|
||||||
|
#if defined(CONFIG_MVEBU_EFUSE_FAKE) |
||||||
|
#define DRY_RUN |
||||||
|
#else |
||||||
|
#undef DRY_RUN |
||||||
|
#endif |
||||||
|
|
||||||
|
#define MBUS_EFUSE_BASE 0xF6000000 |
||||||
|
#define MBUS_EFUSE_SIZE BIT(20) |
||||||
|
|
||||||
|
#define MVEBU_EFUSE_CONTROL (MVEBU_REGISTER(0xE4008)) |
||||||
|
|
||||||
|
enum { |
||||||
|
MVEBU_EFUSE_CTRL_PROGRAM_ENABLE = (1 << 31), |
||||||
|
}; |
||||||
|
|
||||||
|
struct mvebu_hd_efuse { |
||||||
|
u32 bits_31_0; |
||||||
|
u32 bits_63_32; |
||||||
|
u32 bit64; |
||||||
|
u32 reserved0; |
||||||
|
}; |
||||||
|
|
||||||
|
#ifndef DRY_RUN |
||||||
|
static struct mvebu_hd_efuse *efuses = |
||||||
|
(struct mvebu_hd_efuse *)(MBUS_EFUSE_BASE + 0xF9000); |
||||||
|
#else |
||||||
|
static struct mvebu_hd_efuse efuses[EFUSE_LINE_MAX + 1]; |
||||||
|
#endif |
||||||
|
|
||||||
|
static int efuse_initialised; |
||||||
|
|
||||||
|
static struct mvebu_hd_efuse *get_efuse_line(int nr) |
||||||
|
{ |
||||||
|
if (nr < 0 || nr > 63 || !efuse_initialised) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
return efuses + nr; |
||||||
|
} |
||||||
|
|
||||||
|
static void enable_efuse_program(void) |
||||||
|
{ |
||||||
|
#ifndef DRY_RUN |
||||||
|
setbits_le32(MVEBU_EFUSE_CONTROL, MVEBU_EFUSE_CTRL_PROGRAM_ENABLE); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
static void disable_efuse_program(void) |
||||||
|
{ |
||||||
|
#ifndef DRY_RUN |
||||||
|
clrbits_le32(MVEBU_EFUSE_CONTROL, MVEBU_EFUSE_CTRL_PROGRAM_ENABLE); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
static int do_prog_efuse(struct mvebu_hd_efuse *efuse, |
||||||
|
struct efuse_val *new_val, u32 mask0, u32 mask1) |
||||||
|
{ |
||||||
|
struct efuse_val val; |
||||||
|
|
||||||
|
val.dwords.d[0] = readl(&efuse->bits_31_0); |
||||||
|
val.dwords.d[1] = readl(&efuse->bits_63_32); |
||||||
|
val.lock = readl(&efuse->bit64); |
||||||
|
|
||||||
|
if (val.lock & 1) |
||||||
|
return -EPERM; |
||||||
|
|
||||||
|
val.dwords.d[0] |= (new_val->dwords.d[0] & mask0); |
||||||
|
val.dwords.d[1] |= (new_val->dwords.d[1] & mask1); |
||||||
|
val.lock |= new_val->lock; |
||||||
|
|
||||||
|
writel(val.dwords.d[0], &efuse->bits_31_0); |
||||||
|
mdelay(1); |
||||||
|
writel(val.dwords.d[1], &efuse->bits_63_32); |
||||||
|
mdelay(1); |
||||||
|
writel(val.lock, &efuse->bit64); |
||||||
|
mdelay(5); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int prog_efuse(int nr, struct efuse_val *new_val, u32 mask0, u32 mask1) |
||||||
|
{ |
||||||
|
struct mvebu_hd_efuse *efuse; |
||||||
|
int res = 0; |
||||||
|
|
||||||
|
res = mvebu_efuse_init_hw(); |
||||||
|
if (res) |
||||||
|
return res; |
||||||
|
|
||||||
|
efuse = get_efuse_line(nr); |
||||||
|
if (!efuse) |
||||||
|
return -ENODEV; |
||||||
|
|
||||||
|
if (!new_val) |
||||||
|
return -EINVAL; |
||||||
|
|
||||||
|
/* only write a fuse line with lock bit */ |
||||||
|
if (!new_val->lock) |
||||||
|
return -EINVAL; |
||||||
|
|
||||||
|
/* according to specs ECC protection bits must be 0 on write */ |
||||||
|
if (new_val->bytes.d[7] & 0xFE) |
||||||
|
return -EINVAL; |
||||||
|
|
||||||
|
if (!new_val->dwords.d[0] && !new_val->dwords.d[1] && (mask0 | mask1)) |
||||||
|
return 0; |
||||||
|
|
||||||
|
enable_efuse_program(); |
||||||
|
|
||||||
|
res = do_prog_efuse(efuse, new_val, mask0, mask1); |
||||||
|
|
||||||
|
disable_efuse_program(); |
||||||
|
|
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
int mvebu_efuse_init_hw(void) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
|
||||||
|
if (efuse_initialised) |
||||||
|
return 0; |
||||||
|
|
||||||
|
ret = mvebu_mbus_add_window_by_id( |
||||||
|
CPU_TARGET_SATA23_DFX, 0xA, MBUS_EFUSE_BASE, MBUS_EFUSE_SIZE); |
||||||
|
|
||||||
|
if (ret) |
||||||
|
return ret; |
||||||
|
|
||||||
|
efuse_initialised = 1; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int mvebu_read_efuse(int nr, struct efuse_val *val) |
||||||
|
{ |
||||||
|
struct mvebu_hd_efuse *efuse; |
||||||
|
int res; |
||||||
|
|
||||||
|
res = mvebu_efuse_init_hw(); |
||||||
|
if (res) |
||||||
|
return res; |
||||||
|
|
||||||
|
efuse = get_efuse_line(nr); |
||||||
|
if (!efuse) |
||||||
|
return -ENODEV; |
||||||
|
|
||||||
|
if (!val) |
||||||
|
return -EINVAL; |
||||||
|
|
||||||
|
val->dwords.d[0] = readl(&efuse->bits_31_0); |
||||||
|
val->dwords.d[1] = readl(&efuse->bits_63_32); |
||||||
|
val->lock = readl(&efuse->bit64); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int mvebu_write_efuse(int nr, struct efuse_val *val) |
||||||
|
{ |
||||||
|
return prog_efuse(nr, val, ~0, ~0); |
||||||
|
} |
||||||
|
|
||||||
|
int mvebu_lock_efuse(int nr) |
||||||
|
{ |
||||||
|
struct efuse_val val = { |
||||||
|
.lock = 1, |
||||||
|
}; |
||||||
|
|
||||||
|
return prog_efuse(nr, &val, 0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* wrapper funcs providing the fuse API |
||||||
|
* |
||||||
|
* we use the following mapping: |
||||||
|
* "bank" -> eFuse line |
||||||
|
* "word" -> 0: bits 0-31 |
||||||
|
* 1: bits 32-63 |
||||||
|
* 2: bit 64 (lock) |
||||||
|
*/ |
||||||
|
|
||||||
|
static struct efuse_val prog_val; |
||||||
|
static int valid_prog_words; |
||||||
|
|
||||||
|
int fuse_read(u32 bank, u32 word, u32 *val) |
||||||
|
{ |
||||||
|
struct efuse_val fuse_line; |
||||||
|
int res; |
||||||
|
|
||||||
|
if (bank < EFUSE_LINE_MIN || bank > EFUSE_LINE_MAX || word > 2) |
||||||
|
return -EINVAL; |
||||||
|
|
||||||
|
res = mvebu_read_efuse(bank, &fuse_line); |
||||||
|
if (res) |
||||||
|
return res; |
||||||
|
|
||||||
|
if (word < 2) |
||||||
|
*val = fuse_line.dwords.d[word]; |
||||||
|
else |
||||||
|
*val = fuse_line.lock; |
||||||
|
|
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
int fuse_sense(u32 bank, u32 word, u32 *val) |
||||||
|
{ |
||||||
|
/* not supported */ |
||||||
|
return -ENOSYS; |
||||||
|
} |
||||||
|
|
||||||
|
int fuse_prog(u32 bank, u32 word, u32 val) |
||||||
|
{ |
||||||
|
int res = 0; |
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: Fuse line should be written as whole. |
||||||
|
* So how can we do that with this API? |
||||||
|
* For now: remember values for word == 0 and word == 1 and write the |
||||||
|
* whole line when word == 2. |
||||||
|
* This implies that we always require all 3 fuse prog cmds (one for |
||||||
|
* for each word) to write a single fuse line. |
||||||
|
* Exception is a single write to word 2 which will lock the fuse line. |
||||||
|
* |
||||||
|
* Hope that will be OK. |
||||||
|
*/ |
||||||
|
|
||||||
|
if (bank < EFUSE_LINE_MIN || bank > EFUSE_LINE_MAX || word > 2) |
||||||
|
return -EINVAL; |
||||||
|
|
||||||
|
if (word < 2) { |
||||||
|
prog_val.dwords.d[word] = val; |
||||||
|
valid_prog_words |= (1 << word); |
||||||
|
} else if ((valid_prog_words & 3) == 0 && val) { |
||||||
|
res = mvebu_lock_efuse(bank); |
||||||
|
valid_prog_words = 0; |
||||||
|
} else if ((valid_prog_words & 3) != 3 || !val) { |
||||||
|
res = -EINVAL; |
||||||
|
} else { |
||||||
|
prog_val.lock = val != 0; |
||||||
|
res = mvebu_write_efuse(bank, &prog_val); |
||||||
|
valid_prog_words = 0; |
||||||
|
} |
||||||
|
|
||||||
|
return res; |
||||||
|
} |
||||||
|
|
||||||
|
int fuse_override(u32 bank, u32 word, u32 val) |
||||||
|
{ |
||||||
|
/* not supported */ |
||||||
|
return -ENOSYS; |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Reinhard Pfau <reinhard.pfau@gdsys.cc> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-2.0+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _MVEBU_EFUSE_H |
||||||
|
#define _MVEBU_EFUSE_H |
||||||
|
|
||||||
|
#include <common.h> |
||||||
|
|
||||||
|
struct efuse_val { |
||||||
|
union { |
||||||
|
struct { |
||||||
|
u8 d[8]; |
||||||
|
} bytes; |
||||||
|
struct { |
||||||
|
u16 d[4]; |
||||||
|
} words; |
||||||
|
struct { |
||||||
|
u32 d[2]; |
||||||
|
} dwords; |
||||||
|
}; |
||||||
|
u32 lock; |
||||||
|
}; |
||||||
|
|
||||||
|
#if defined(CONFIG_ARMADA_38X) |
||||||
|
|
||||||
|
enum efuse_line { |
||||||
|
EFUSE_LINE_SECURE_BOOT = 24, |
||||||
|
EFUSE_LINE_PUBKEY_DIGEST_0 = 26, |
||||||
|
EFUSE_LINE_PUBKEY_DIGEST_1 = 27, |
||||||
|
EFUSE_LINE_PUBKEY_DIGEST_2 = 28, |
||||||
|
EFUSE_LINE_PUBKEY_DIGEST_3 = 29, |
||||||
|
EFUSE_LINE_PUBKEY_DIGEST_4 = 30, |
||||||
|
EFUSE_LINE_CSK_0_VALID = 31, |
||||||
|
EFUSE_LINE_CSK_1_VALID = 32, |
||||||
|
EFUSE_LINE_CSK_2_VALID = 33, |
||||||
|
EFUSE_LINE_CSK_3_VALID = 34, |
||||||
|
EFUSE_LINE_CSK_4_VALID = 35, |
||||||
|
EFUSE_LINE_CSK_5_VALID = 36, |
||||||
|
EFUSE_LINE_CSK_6_VALID = 37, |
||||||
|
EFUSE_LINE_CSK_7_VALID = 38, |
||||||
|
EFUSE_LINE_CSK_8_VALID = 39, |
||||||
|
EFUSE_LINE_CSK_9_VALID = 40, |
||||||
|
EFUSE_LINE_CSK_10_VALID = 41, |
||||||
|
EFUSE_LINE_CSK_11_VALID = 42, |
||||||
|
EFUSE_LINE_CSK_12_VALID = 43, |
||||||
|
EFUSE_LINE_CSK_13_VALID = 44, |
||||||
|
EFUSE_LINE_CSK_14_VALID = 45, |
||||||
|
EFUSE_LINE_CSK_15_VALID = 46, |
||||||
|
EFUSE_LINE_FLASH_ID = 47, |
||||||
|
EFUSE_LINE_BOX_ID = 48, |
||||||
|
|
||||||
|
EFUSE_LINE_MIN = 0, |
||||||
|
EFUSE_LINE_MAX = 63, |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
int mvebu_efuse_init_hw(void); |
||||||
|
|
||||||
|
int mvebu_read_efuse(int nr, struct efuse_val *val); |
||||||
|
|
||||||
|
int mvebu_write_efuse(int nr, struct efuse_val *val); |
||||||
|
|
||||||
|
int mvebu_lock_efuse(int nr); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,373 @@ |
|||||||
|
The trusted boot framework on Marvell Armada 38x |
||||||
|
================================================ |
||||||
|
|
||||||
|
Contents: |
||||||
|
|
||||||
|
1. Overview of the trusted boot |
||||||
|
2. Terminology |
||||||
|
3. Boot image layout |
||||||
|
4. The secured header |
||||||
|
5. The secured boot flow |
||||||
|
6. Usage example |
||||||
|
7. Work to be done |
||||||
|
8. Bibliography |
||||||
|
|
||||||
|
1. Overview of the trusted boot |
||||||
|
------------------------------- |
||||||
|
|
||||||
|
The Armada's trusted boot framework enables the SoC to cryptographically verify |
||||||
|
a specially prepared boot image. This can be used to establish a chain of trust |
||||||
|
from the boot firmware all the way to the OS. |
||||||
|
|
||||||
|
To achieve this, the Armada SoC requires a specially prepared boot image, which |
||||||
|
contains the relevant cryptographic data, as well as other information |
||||||
|
pertaining to the boot process. Furthermore, a eFuse structure (a |
||||||
|
one-time-writeable memory) need to be configured in the correct way. |
||||||
|
|
||||||
|
Roughly, the secure boot process works as follows: |
||||||
|
|
||||||
|
* Load the header block of the boot image, extract a special "root" public RSA |
||||||
|
key from it, and verify its SHA-256 hash against a SHA-256 stored in a eFuse |
||||||
|
field. |
||||||
|
* Load an array of code signing public RSA keys from the header block, and |
||||||
|
verify its RSA signature (contained in the header block as well) using the |
||||||
|
"root" RSA key. |
||||||
|
* Choose a code signing key, and use it to verify the header block (excluding |
||||||
|
the key array). |
||||||
|
* Verify the binary image's signature (contained in the header block) using the |
||||||
|
code signing key. |
||||||
|
* If all checks pass successfully, boot the image. |
||||||
|
|
||||||
|
The chain of trust is thus as follows: |
||||||
|
|
||||||
|
* The SHA-256 value in the eFuse field verifies the "root" public key. |
||||||
|
* The "root" public key verifies the code signing key array. |
||||||
|
* The selected code signing key verifies the header block and the binary image. |
||||||
|
|
||||||
|
In the special case of building a boot image containing U-Boot as the binary |
||||||
|
image, which employs this trusted boot framework, the following tasks need to |
||||||
|
be addressed: |
||||||
|
|
||||||
|
1. Creation of the needed cryptographic key material. |
||||||
|
2. Creation of a conforming boot image containing the U-Boot image as binary |
||||||
|
image. |
||||||
|
3. Burning the necessary eFuse values. |
||||||
|
|
||||||
|
(1) will be addressed later, (2) will be taken care of by U-Boot's build |
||||||
|
system (some user configuration is required, though), and for (3) the necessary |
||||||
|
data (essentially a series of U-Boot commands to be entered at the U-Boot |
||||||
|
command prompt) will be created by the build system as well. |
||||||
|
|
||||||
|
The documentation of the trusted boot mode is contained in part 1, chapter |
||||||
|
7.2.5 in the functional specification [1], and in application note [2]. |
||||||
|
|
||||||
|
2. Terminology |
||||||
|
-------------- |
||||||
|
|
||||||
|
CSK - Code Signing Key(s): An array of RSA key pairs, which |
||||||
|
are used to sign and verify the secured header and the |
||||||
|
boot loader image. |
||||||
|
KAK - Key Authentication Key: A RSA key pair, which is used |
||||||
|
to sign and verify the array of CSKs. |
||||||
|
Header block - The first part of the boot image, which contains the |
||||||
|
image's headers (also known as "headers block", "boot |
||||||
|
header", and "image header") |
||||||
|
eFuse - A one-time-writeable memory. |
||||||
|
BootROM - The Armada's built-in boot firmware, which is |
||||||
|
responsible for verifying and starting secure images. |
||||||
|
Boot image - The complete image the SoC's boot firmware loads |
||||||
|
(contains the header block and the binary image) |
||||||
|
Main header - The header in the header block containing information |
||||||
|
and data pertaining to the boot process (used for both |
||||||
|
the regular and secured boot processes) |
||||||
|
Binary image - The binary code payload of the boot image; in this |
||||||
|
case the U-Boot's code (also known as "source image", |
||||||
|
or just "image") |
||||||
|
Secured header - The specialized header in the header block that |
||||||
|
contains information and data pertaining to the |
||||||
|
trusted boot (also known as "security header") |
||||||
|
Secured boot mode - A special boot mode of the Armada SoC in which secured |
||||||
|
images are verified (non-secure images won't boot); |
||||||
|
the mode is activated by setting a eFuse field. |
||||||
|
Trusted debug mode - A special mode for the trusted boot that allows |
||||||
|
debugging of devices employing the trusted boot |
||||||
|
framework in a secure manner (untested in the current |
||||||
|
implementation). |
||||||
|
Trusted boot framework - The ARMADA SoC's implementation of a secure verified |
||||||
|
boot process. |
||||||
|
|
||||||
|
3. Boot image layout |
||||||
|
-------------------- |
||||||
|
|
||||||
|
+-- Boot image --------------------------------------------+ |
||||||
|
| | |
||||||
|
| +-- Header block --------------------------------------+ | |
||||||
|
| | Main header | | |
||||||
|
| +------------------------------------------------------+ | |
||||||
|
| | Secured header | | |
||||||
|
| +------------------------------------------------------+ | |
||||||
|
| | BIN header(s) | | |
||||||
|
| +------------------------------------------------------+ | |
||||||
|
| | REG header(s) | | |
||||||
|
| +------------------------------------------------------+ | |
||||||
|
| | Padding | | |
||||||
|
| +------------------------------------------------------+ | |
||||||
|
| | |
||||||
|
| +------------------------------------------------------+ | |
||||||
|
| | Binary image + checksum | | |
||||||
|
| +------------------------------------------------------+ | |
||||||
|
+----------------------------------------------------------+ |
||||||
|
|
||||||
|
4. The secured header |
||||||
|
--------------------- |
||||||
|
|
||||||
|
For the trusted boot framework, a additional header is added to the boot image. |
||||||
|
The following data are relevant for the secure boot: |
||||||
|
|
||||||
|
KAK: The KAK is contained in the secured header in the form |
||||||
|
of a RSA-2048 public key in DER format with a length of |
||||||
|
524 bytes. |
||||||
|
Header block signature: The RSA signature of the header block (excluding the |
||||||
|
CSK array), created using the selected CSK. |
||||||
|
Binary image signature: The RSA signature of the binary image, created using |
||||||
|
the selected CSK. |
||||||
|
CSK array: The array of the 16 CSKs as RSA-2048 public keys in DER |
||||||
|
format with a length of 8384 = 16 * 524 bytes. |
||||||
|
CSK block signature: The RSA signature of the CSK array, created using the |
||||||
|
KAK. |
||||||
|
|
||||||
|
NOTE: The JTAG delay, Box ID, and Flash ID header fields do play a role in the |
||||||
|
trusted boot process to enable and configure secure debugging, but they were |
||||||
|
not tested in the current implementation of the trusted boot in U-Boot. |
||||||
|
|
||||||
|
5. The secured boot flow |
||||||
|
------------------------ |
||||||
|
|
||||||
|
The steps in the boot flow that are relevant for the trusted boot framework |
||||||
|
proceed as follows: |
||||||
|
|
||||||
|
1) Check if trusted boot is enabled, and perform regular boot if it is not. |
||||||
|
2) Load the secured header, and verify its checksum. |
||||||
|
3) Select the lowest valid CSK from CSK0 to CSK15. |
||||||
|
4) Verify the SHA-256 hash of the KAK embedded in the secured header. |
||||||
|
5) Verify the RSA signature of the CSK block from the secured header with the |
||||||
|
KAK. |
||||||
|
6) Verify the header block signature (which excludes the CSK block) from the |
||||||
|
secured header with the selected CSK. |
||||||
|
7) Load the binary image to the main memory and verify its checksum. |
||||||
|
8) Verify the binary image's RSA signature from the secured header with the |
||||||
|
selected CSK. |
||||||
|
9) Continue the boot process as in the case of the regular boot. |
||||||
|
|
||||||
|
NOTE: All RSA signatures are verified according to the PKCS #1 v2.1 standard |
||||||
|
described in [3]. |
||||||
|
|
||||||
|
NOTE: The Box ID and Flash ID are checked after step 6, and the trusted debug |
||||||
|
mode may be entered there, but since this mode is untested in the current |
||||||
|
implementation, it is not described further. |
||||||
|
|
||||||
|
6. Usage example |
||||||
|
---------------- |
||||||
|
|
||||||
|
### Create key material |
||||||
|
|
||||||
|
To employ the trusted boot framework, cryptographic key material needs to be |
||||||
|
created. In the current implementation, two keys are needed to build a valid |
||||||
|
secured boot image: The KAK private key and a CSK private key (both have to be |
||||||
|
2048 bit RSA keys in PEM format). Note that the usage of more than one CSK is |
||||||
|
currently not supported. |
||||||
|
|
||||||
|
NOTE: Since the public key can be generated from the private key, it is |
||||||
|
sufficient to store the private key for each key pair. |
||||||
|
|
||||||
|
OpenSSL can be used to generate the needed files kwb_kak.key and kwb_csk.key |
||||||
|
(the names of these files have to be configured, see the next section on |
||||||
|
kwbimage.cfg settings): |
||||||
|
|
||||||
|
openssl genrsa -out kwb_kak.key 2048 |
||||||
|
openssl genrsa -out kwb_csk.key 2048 |
||||||
|
|
||||||
|
The generated files have to be placed in the U-Boot root directory. |
||||||
|
|
||||||
|
Alternatively, instead of copying the files, symlinks to the private keys can |
||||||
|
be placed in the U-Boot root directory. |
||||||
|
|
||||||
|
WARNING: Knowledge of the KAK or CSK private key would enable an attacker to |
||||||
|
generate secured boot images containing arbitrary code. Hence, the private keys |
||||||
|
should be carefully guarded. |
||||||
|
|
||||||
|
### Create/Modifiy kwbimage.cfg |
||||||
|
|
||||||
|
The Kirkwook architecture in U-Boot employs a special board-specific |
||||||
|
configuration file (kwbimage.cfg), which controls various boot image settings |
||||||
|
that are interpreted by the BootROM, such as the boot medium. The support the |
||||||
|
trusted boot framework, several new options were added to faciliate |
||||||
|
configuration of the secured boot. |
||||||
|
|
||||||
|
The configuration file's layout has been retained, only the following new |
||||||
|
options were added: |
||||||
|
|
||||||
|
KAK - The name of the KAK RSA private key file in the U-Boot |
||||||
|
root directory, without the trailing extension of ".key". |
||||||
|
CSK - The name of the (active) CSK RSA private key file in the |
||||||
|
U-Boot root directory, without the trailing extension of |
||||||
|
".key". |
||||||
|
BOX_ID - The BoxID to be used for trusted debugging (a integer |
||||||
|
value). |
||||||
|
FLASH_ID - The FlashID to be used for trusted debugging (a integer |
||||||
|
value). |
||||||
|
JTAG_DELAY - The JTAG delay to be used for trusted debugging (a |
||||||
|
integer value). |
||||||
|
CSK_INDEX - The index of the active CSK (a integer value). |
||||||
|
SEC_SPECIALIZED_IMG - Flag to indicate whether to include the BoxID and FlashID |
||||||
|
in the image (that is, whether to use the trusted debug |
||||||
|
mode or not); no parameters. |
||||||
|
SEC_BOOT_DEV - The boot device from which the trusted boot is allowed to |
||||||
|
proceed, identified via a numeric ID. The tested values |
||||||
|
are 0x34 = NOR flash, 0x31 = SDIO/MMC card; for |
||||||
|
additional ID values, consult the documentation in [1]. |
||||||
|
SEC_FUSE_DUMP - Dump the "fuse prog" commands necessary for writing the |
||||||
|
correct eFuse values to a text file in the U-Boot root |
||||||
|
directory. The parameter is the architecture for which to |
||||||
|
dump the commands (currently only "a38x" is supported). |
||||||
|
|
||||||
|
The parameter values may be hardcoded into the file, but it is also possible to |
||||||
|
employ a dynamic approach of creating a Autoconf-like kwbimage.cfg.in, then |
||||||
|
reading configuration values from Kconfig options or from the board config |
||||||
|
file, and generating the actual kwbimage.cfg from this template using Makefile |
||||||
|
mechanisms (see board/gdsys/a38x/Makefile as an example for this approach). |
||||||
|
|
||||||
|
### Set config options |
||||||
|
|
||||||
|
To enable the generation of trusted boot images, the corresponding support |
||||||
|
needs to be activated, and a index for the active CSK needs to be selected as |
||||||
|
well. |
||||||
|
|
||||||
|
Furthermore, eFuse writing support has to be activated in order to burn the |
||||||
|
eFuse structure's values (this option is just needed for programming the eFuse |
||||||
|
structure; production boot images may disable it). |
||||||
|
|
||||||
|
ARM architecture |
||||||
|
-> [*] Build image for trusted boot |
||||||
|
(0) Index of active CSK |
||||||
|
-> [*] Enable eFuse support |
||||||
|
[ ] Fake eFuse access (dry run) |
||||||
|
|
||||||
|
### Build and test boot image |
||||||
|
|
||||||
|
The creation of the boot image is done via the usual invocation of make (with a |
||||||
|
suitably set CROSS_COMPILE environment variable, of course). The resulting boot |
||||||
|
image u-boot-spl.kwb can then be tested, if so desired. The hdrparser from [5] |
||||||
|
can be used for this purpose. To build the tool, invoke make in the |
||||||
|
'tools/marvell/doimage_mv' directory of [5], which builds a stand-alone |
||||||
|
hdrparser executable. A test can be conducted by calling hdrparser with the |
||||||
|
produced boot image and the following (mandatory) parameters: |
||||||
|
|
||||||
|
./hdrparser -k 0 -t u-boot-spl.kwb |
||||||
|
|
||||||
|
Here we assume that the CSK index is 0 and the boot image file resides in the |
||||||
|
same directory (adapt accordingly if needed). The tool should report that all |
||||||
|
checksums are valid ("GOOD"), that all signature verifications succeed |
||||||
|
("PASSED"), and, finally, that the overall test was successful |
||||||
|
("T E S T S U C C E E D E D" in the last line of output). |
||||||
|
|
||||||
|
### Burn eFuse structure |
||||||
|
|
||||||
|
+----------------------------------------------------------+ |
||||||
|
| WARNING: Burning the eFuse structure is a irreversible | |
||||||
|
| operation! Should wrong or corrupted values be used, the | |
||||||
|
| board won't boot anymore, and recovery is likely | |
||||||
|
| impossible! | |
||||||
|
+----------------------------------------------------------+ |
||||||
|
|
||||||
|
After the build process has finished, and the SEC_FUSE_DUMP option was set in |
||||||
|
the kwbimage.cfg was set, a text file kwb_fuses_a38x.txt should be present in |
||||||
|
the U-Boot top-level directory. It contains all the necessary commands to set |
||||||
|
the eFuse structure to the values needed for the used KAK digest, as well as |
||||||
|
the CSK index, Flash ID and Box ID that were selected in kwbimage.cfg. |
||||||
|
|
||||||
|
Sequentially executing the commands in this file at the U-Boot command prompt |
||||||
|
will write these values to the eFuse structure. |
||||||
|
|
||||||
|
If the SEC_FUSE_DUMP option was not set, the commands needed to burn the fuses |
||||||
|
have to be crafted by hand. The needed fuse lines can be looked up in [1]; a |
||||||
|
rough overview of the process is: |
||||||
|
|
||||||
|
* Burn the KAK public key hash. The hash itself can be found in the file |
||||||
|
pub_kak_hash.txt in the U-Boot top-level directory; be careful to account for |
||||||
|
the endianness! |
||||||
|
* Burn the CSK selection, BoxID, and FlashID |
||||||
|
* Enable trusted boot by burning the corresponding fuse (WARNING: this must be |
||||||
|
the last fuse line written!) |
||||||
|
* Lock the unused fuse lines |
||||||
|
|
||||||
|
The command to employ is the "fuse prog" command previously enabled by setting |
||||||
|
the corresponding configuration option. |
||||||
|
|
||||||
|
For the trusted boot, the fuse prog command has a special syntax, since the |
||||||
|
ARMADA SoC demands that whole fuse lines (64 bit values) have to be written as |
||||||
|
a whole. The fuse prog command itself allows lists of 32 bit words to be |
||||||
|
written at a time, but this is translated to a series of single 32 bit write |
||||||
|
operations to the fuse line, where the individual 32 bit words are identified |
||||||
|
by a "word" counter that is increased for each write. |
||||||
|
|
||||||
|
To work around this restriction, we interpret each line to have three "words" |
||||||
|
(0-2): The first and second words are the values to be written to the fuse |
||||||
|
line, and the third is a lock flag, which is supposed to lock the fuse line |
||||||
|
when set to 1. Writes to the first and second words are memoized between |
||||||
|
function calls, and the fuse line is only really written and locked (on writing |
||||||
|
the third word) if both words were previously set, so that "incomplete" writes |
||||||
|
are prevented. An exception to this is a single write to the third word (index |
||||||
|
2) without previously writing neither the first nor the second word, which |
||||||
|
locks the fuse line without setting any value; this is needed to lock the |
||||||
|
unused fuse lines. |
||||||
|
|
||||||
|
As an example, to write the value 0011223344556677 to fuse line 10, we would |
||||||
|
use the following command: |
||||||
|
|
||||||
|
fuse prog -y 10 0 00112233 44556677 1 |
||||||
|
|
||||||
|
Here 10 is the fuse line number, 0 is the index of the first word to be |
||||||
|
written, 00112233 and 44556677 are the values to be written to the fuse line |
||||||
|
(first and second word) and the trailing 1 is the value for the third word |
||||||
|
responsible for locking the line. |
||||||
|
|
||||||
|
A "lock-only" command would look like this: |
||||||
|
|
||||||
|
fuse prog -y 11 2 1 |
||||||
|
|
||||||
|
Here 11 is the fuse number, 2 is the index of the first word to be written |
||||||
|
(notice that we only write to word 2 here; the third word for fuse line |
||||||
|
locking), and the 1 is the value for the word we are writing to. |
||||||
|
|
||||||
|
WARNING: According to application note [4], the VHV pin of the SoC must be |
||||||
|
connected to a 1.8V source during eFuse programming, but *must* be disconnected |
||||||
|
for normal operation. The AN [4] describes a software-controlled circuit (based |
||||||
|
on a N-channel or P-channel FET and a free GPIO pin of the SoC) to achieve |
||||||
|
this, but a jumper-based circuit should suffice as well. Regardless of the |
||||||
|
chosen circuit, the issue needs to be addressed accordingly! |
||||||
|
|
||||||
|
7. Work to be done |
||||||
|
------------------ |
||||||
|
|
||||||
|
* Add the ability to populate more than one CSK |
||||||
|
* Test secure debug |
||||||
|
* Test on Armada XP |
||||||
|
|
||||||
|
8. Bibliography |
||||||
|
--------------- |
||||||
|
|
||||||
|
[1] ARMADA(R) 38x Family High-Performance Single/Dual CPU System on Chip |
||||||
|
Functional Specification; MV-S109094-00, Rev. C; August 2, 2015, |
||||||
|
Preliminary |
||||||
|
[2] AN-383: ARMADA(R) 38x Families Secure Boot Mode Support; MV-S302501-00 |
||||||
|
Rev. A; March 11, 2015, Preliminary |
||||||
|
[3] Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography |
||||||
|
Specifications Version 2.1; February 2003; |
||||||
|
https://www.ietf.org/rfc/rfc3447.txt |
||||||
|
[4] AN-389: ARMADA(R) VHV Power; MV-S302545-00 Rev. B; January 28, 2016, |
||||||
|
Released |
||||||
|
[5] Marvell Armada 38x U-Boot support; November 25, 2015; |
||||||
|
https://github.com/MarvellEmbeddedProcessors/u-boot-marvell |
||||||
|
|
||||||
|
2017-01-05, Mario Six <mario.six@gdsys.cc> |
Loading…
Reference in new issue