Skip to content

Commit f32cde4

Browse files
haowqsjyao1
authored andcommitted
ld to generate the final binary and doc
Signed-off-by: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Liu Jiang <gerry@linux.alibaba.com> Signed-off-by: haowei <WeiX.Hao@intel.com>
1 parent c3055c8 commit f32cde4

File tree

7 files changed

+588
-0
lines changed

7 files changed

+588
-0
lines changed

doc/secure_boot_guide.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Secure boot guide for td-shim
2+
3+
This guide follows the [Secure Boot Specification](secure_boot.md) for td-shim.
4+
5+
## Build td-shim with secure boot feature enabled
6+
7+
```
8+
cargo xbuild -p td-shim --features "secure-boot" --target x86_64-unknown-uefi --release
9+
```
10+
11+
## Build payload
12+
13+
Refer to [README](../README.md), using ELF as example:
14+
15+
```
16+
cargo xbuild -p td-payload --target devtools/rustc-targets/x86_64-unknown-none.json --release
17+
```
18+
19+
## Generate Key
20+
21+
1. ECDSA NIST P384
22+
23+
Run below command to generate a DER encoded PKCS8 formate EC private key file (on Linux):
24+
```
25+
openssl genpkey -algorithm EC \
26+
-pkeyopt ec_paramgen_curve:P-384 \
27+
-pkeyopt ec_param_enc:named_curve \
28+
-outform der -out ecdsa-p384-private.der
29+
30+
openssl pkcs8 -topk8 -nocrypt -inform der -in ecdsa-p384-private.der -outform der -out ecdsa-p384-private.pk8
31+
```
32+
33+
Generate public key file with private key:
34+
```
35+
openssl ec -inform der -in ecdsa-p384-private.der -pubout -outform der -out ecdsa-p384-public.der
36+
```
37+
38+
2. RSA 3072
39+
40+
Run below command to generate a DER encoded PKCS8 formate RSA private key file (on Linux).
41+
```
42+
openssl genpkey -algorithm RSA \
43+
-pkeyopt rsa_keygen_bits:3072 \
44+
-pkeyopt rsa_keygen_pubexp:65537 | \
45+
openssl pkcs8 -topk8 -nocrypt -outform der > rsa-3072-private.pk8
46+
```
47+
48+
Generate public key file with private key:
49+
```
50+
openssl rsa -inform der -in rsa-3072-private.pk8 -pubout -outform der -out rsa-3072-public.der
51+
```
52+
53+
## Sign payload with [rust-tdpayload-signing](../td-shim-sign-payload)
54+
Using ECDSA NIST P384 as example:
55+
56+
Clear environment varibles CC and AR at first:
57+
```
58+
set CC_x86_64_unknown_uefi=
59+
set AR_x86_64_unknown_uefi=
60+
```
61+
62+
Run the signing tool:
63+
```
64+
cargo run -p td-shim-tools --bin td-shim-sign-payload -- -A ECDSA_NIST_P384_SHA384 data/sample-keys/ecdsa-p384-private.pk8 target/x86_64-unknown-none/release/td-payload 1 1
65+
```
66+
The signed payload file **td-payload-signed** is located in the same folder with input `td-payload`.
67+
68+
## Enroll public key into CFV with [rust-tdshim-key-enroll](../td-shim-tools)
69+
Build final.bin:
70+
```
71+
cargo run -p td-shim-tools --bin td-shim-ld -- target/x86_64-unknown-uefi/release/ResetVector.bin target/x86_64-unknown-uefi/release/rust-tdshim.efi target/x86_64-unknown-none/release/td-payload-signed -o target/x86_64-unknown-uefi/release/final.bin
72+
```
73+
74+
Enroll public key:
75+
```
76+
cargo run -p td-shim-tools --bin td-shim-enroll-key -- -H SHA384 target/x86_64-unknown-uefi/release/final.bin data/sample-keys/ecdsa-p384-public.der
77+
```
78+
79+
The output file **final.sb.bin** with secure boot enabled is located in the same folder with input final.bin.
80+

td-shim-tools/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ homepage = "https://github.com/confidential-containers"
77
license = "BSD-2-Clause-Patent"
88
edition = "2018"
99

10+
[[bin]]
11+
name = "td-shim-enroll"
12+
required-features = ["enroller"]
13+
14+
[[bin]]
15+
name = "td-shim-ld"
16+
required-features = ["linker"]
17+
18+
[[bin]]
19+
name = "td-shim-sign-payload"
20+
required-features = ["signer"]
21+
1022
[dependencies]
1123
r-efi = "3.2.0"
1224
scroll = { version = "0.10", default-features = false, features = ["derive"]}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
## Public key enrollment
2+
3+
This tool accepts **DER encoded** public key file as input and enrolls the **public key hash** into CFV.
4+
Public keys are a SubjectPublicKeyInfo as specified in [IETF RFC 3280](https://datatracker.ietf.org/doc/html/rfc3280).
5+
6+
### Configuration Firmware Volume (CFV)
7+
8+
Quotation from [Intel TDX Virtual Firmware Design Guide](https://www.intel.com/content/dam/develop/external/us/en/documents/tdx-virtual-firmware-design-guide-rev-1.pdf),
9+
section 3.2:
10+
```
11+
TDVF/TD-SHIM may also include a configuration firmware volume (CFV) that is seperated from the boot firmware volume (BFV).
12+
The reason to do so is because the CFV is measured in the `RTMR`, while the BFV is measured in `TDMR`.
13+
14+
Configuration Firmware Volume includes all the provisoned data and this region is read-only. One possible usage is to
15+
provide UEFI Secure Boot Variable content in this region, such as PK, KEK, db, dbx.
16+
17+
The filesystem GUID must be `EFI_SYSTEM_NV_DATA_FV_GUID`, defined in
18+
[https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Guid/SystemNvDataGuid.h](https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Guid/SystemNvDataGuid.h)
19+
```
20+
21+
### Generate public key
22+
23+
1. ECDSA NIST P384
24+
25+
Generate DER encoded public key from DER encoded EC private key file.
26+
```
27+
openssl ec -inform der -in ecdsa-p384-private.der -pubout -outform der -out ecdsa-p384-public.der
28+
```
29+
30+
2. RSA 3072
31+
32+
Generate DER encoded public key from DER encoded pkcs8 formated private key:
33+
```
34+
openssl rsa -inform der -in rsa-3072-private.pk8 -pubout -outform der -out rsa-3072-public.der
35+
```
36+
37+
### Enrollment
38+
39+
Run the tool:
40+
```
41+
cargo run -p td-shim-tools --bin td-shim-enroll -- [-H {hash_algorithm}] [-o {output_file}] [-k {public_key_file}] [-f {Firmware_file}] {tdshim_file}
42+
```
43+
44+
For example:
45+
```
46+
cargo run -p td-shim-tools --bin td-shim-enroll -- -H SHA384 -o final.sb.bin target/x86_64-unknown-uefi/release/final.bin -k data/sample-keys/ecdsa-p384-public.der
47+
```
48+
49+
To enroll raw files into CFV:
50+
```
51+
cargo run -p td-shim-tools --bin td-shim-enroll -- -o final.sb.bin target/x86_64-unknown-uefi/release/final.bin -f AB122746-2735-4013-A5C4-90F739CA29BD data/sample-keys/ecdsa-p384-public.der 4EF32D2C-7DD1-44BD-A4C9-E0F8FCC5372A data/sample-keys/rsa-3072-public.der
52+
```
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// Copyright (c) 2021 Intel Corporation
2+
// Copyright (c) 2022 Alibaba Cloud
3+
//
4+
// SPDX-License-Identifier: BSD-2-Clause-Patent
5+
6+
#[macro_use]
7+
extern crate clap;
8+
use log::{error, LevelFilter};
9+
use std::path::PathBuf;
10+
use std::str::FromStr;
11+
use std::vec::Vec;
12+
use std::{env, io, path::Path};
13+
use td_shim_tools::enroller::{create_key_file, enroll_files, FirmwareRawFile};
14+
use td_shim_tools::InputData;
15+
use td_uefi_pi::pi::guid;
16+
const TDSHIM_SB_NAME: &str = "final.sb.bin";
17+
18+
struct Config {
19+
// Input file path to be read
20+
pub input: String,
21+
// Output file path to be written
22+
pub output: PathBuf,
23+
// Public key file path
24+
pub key: Option<String>,
25+
// Hash algorithm "SHA384" by default
26+
pub hash_alg: String,
27+
// Firmware file information to be enrolled into CFV,
28+
// consists of (Guid, FilePath)
29+
pub firmware_files: Vec<(guid::Guid, String)>,
30+
// Log level "SHA384" by default
31+
pub log_level: String,
32+
}
33+
34+
#[derive(Debug)]
35+
pub enum ConfigParseError {
36+
InvlidGuid,
37+
InvalidLogLevel,
38+
InvalidInputFilePath,
39+
}
40+
41+
impl Config {
42+
pub fn new() -> Result<Self, ConfigParseError> {
43+
let matches = command!()
44+
.arg(
45+
arg!([tdshim] "shim binary file")
46+
.required(true)
47+
.allow_invalid_utf8(false),
48+
)
49+
.arg(
50+
arg!(-k --key "public key file for enrollment")
51+
.required(false)
52+
.takes_value(true)
53+
.allow_invalid_utf8(false),
54+
)
55+
.arg(
56+
arg!(-H --hash "hash algorithm to compute digest")
57+
.required(false)
58+
.takes_value(true)
59+
.default_value("SHA384"),
60+
)
61+
.arg(
62+
arg!(-f --file "<Guid> <FilePath> Firmware file to be enrolled into CFV")
63+
.required(false)
64+
.multiple_values(true)
65+
.multiple_occurrences(true)
66+
.takes_value(true)
67+
.allow_invalid_utf8(false),
68+
)
69+
.arg(
70+
arg!(-l --"log-level" "logging level: [off, error, warn, info, debug, trace]")
71+
.required(false)
72+
.default_value("info"),
73+
)
74+
.arg(
75+
arg!(-o --output "output of the enrolled shim binary file")
76+
.required(false)
77+
.takes_value(true)
78+
.allow_invalid_utf8(false),
79+
)
80+
.get_matches();
81+
82+
// Safe to unwrap() because they are mandatory or have default values.
83+
//
84+
// rust-td binary file
85+
let input = matches.value_of("tdshim").unwrap().to_string();
86+
let output = match matches.value_of("output") {
87+
Some(v) => Path::new(v).to_path_buf(),
88+
None => {
89+
let p = Path::new(input.as_str())
90+
.canonicalize()
91+
.map_err(|_| ConfigParseError::InvalidInputFilePath)?;
92+
p.parent().unwrap_or(Path::new("/")).join(TDSHIM_SB_NAME)
93+
}
94+
};
95+
let hash_alg = String::from_str(matches.value_of("hash").unwrap()).unwrap();
96+
let key = match matches.value_of("key") {
97+
Some(v) => Some(v.to_string()),
98+
None => None,
99+
};
100+
101+
let firmware_files = match matches.values_of("file") {
102+
Some(inputs) => {
103+
let inputs = inputs.collect::<Vec<&str>>();
104+
let mut firmware_files: Vec<(guid::Guid, String)> = Vec::new();
105+
for i in 0..(inputs.len() / 2) {
106+
firmware_files.push((
107+
// Guid
108+
guid::Guid::from_str(inputs[i * 2])
109+
.map_err(|_| ConfigParseError::InvlidGuid)?,
110+
// File path
111+
inputs[i * 2 + 1].to_string(),
112+
));
113+
}
114+
firmware_files
115+
}
116+
None => Vec::new(),
117+
};
118+
119+
// Safe to unwrap() because they are mandatory or have default values.
120+
let log_level = String::from_str(matches.value_of("log-level").unwrap())
121+
.map_err(|_| ConfigParseError::InvalidLogLevel)?;
122+
123+
Ok(Self {
124+
input,
125+
output,
126+
hash_alg,
127+
key,
128+
firmware_files,
129+
log_level,
130+
})
131+
}
132+
}
133+
134+
fn main() -> io::Result<()> {
135+
use env_logger::Env;
136+
let env = Env::default()
137+
.filter_or("MY_LOG_LEVEL", "info")
138+
.write_style_or("MY_LOG_STYLE", "always");
139+
env_logger::init_from_env(env);
140+
let config = Config::new().map_err(|e| {
141+
error!("Parse command line error: {:?}", e);
142+
io::Error::new(io::ErrorKind::Other, "Invalid command line parameter")
143+
})?;
144+
145+
if let Ok(lvl) = LevelFilter::from_str(config.log_level.as_str()) {
146+
log::set_max_level(lvl);
147+
}
148+
149+
// Convert input files as firmware file format
150+
let ffs = create_firmware_files(&config)?;
151+
// Enroll the files into CFV
152+
enroll_files(config.input.as_str(), config.output, ffs)?;
153+
154+
Ok(())
155+
}
156+
157+
// Build firmware files according to command line input
158+
// 0 / 1 public key file to be enrolled
159+
// 0 ~ n raw file read from system path to be enrolled
160+
fn create_firmware_files(config: &Config) -> io::Result<Vec<FirmwareRawFile>> {
161+
let mut files: Vec<FirmwareRawFile> = Vec::new();
162+
163+
if let Some(key) = &config.key {
164+
let ff_sb = create_key_file(key.as_str(), config.hash_alg.as_str())?;
165+
files.push(ff_sb);
166+
}
167+
168+
for (guid, path) in &config.firmware_files {
169+
// Create a firmware file
170+
let mut f = FirmwareRawFile::new(guid.as_bytes());
171+
let data = InputData::new(path, 1..=1024 * 1024, "firmware file")?;
172+
f.append(data.as_bytes());
173+
files.push(f)
174+
}
175+
176+
Ok(files)
177+
}

0 commit comments

Comments
 (0)