1 //! This library provides bindings for C++ code to comfortably and reasonably safely interface with
2 //! the libhwtrust Rust library.
3
4 use coset::CborSerializable;
5 use hwtrust::dice::ChainForm;
6 use hwtrust::rkp::Csr as InnerCsr;
7 use hwtrust::session::{Options, RkpInstance, Session};
8 use std::str::FromStr;
9
10 #[allow(clippy::needless_maybe_sized)]
11 #[allow(unsafe_op_in_unsafe_fn)]
12 #[cxx::bridge(namespace = "hwtrust::rust")]
13 mod ffi {
14 /// The set of validation rules to apply.
15 enum DiceChainKind {
16 /// The DICE chain specified by VSR 13.
17 Vsr13,
18 /// The DICE chain specified by VSR 14.
19 Vsr14,
20 /// The DICE chain specified by VSR 15.
21 Vsr15,
22 /// The DICE chain specified by VSR 16.
23 Vsr16,
24 }
25
26 /// The result type used by [`verify_dice_chain()`]. The standard [`Result`] is currently only
27 /// converted to exceptions by `cxxbridge` but we can't use exceptions so need to do something
28 /// custom.
29 struct VerifyDiceChainResult {
30 /// If non-empty, the description of the verification error that occurred.
31 error: String,
32 /// If [`error`] is empty, a handle to the verified chain.
33 chain: Box<DiceChain>,
34 /// If [`error`] is empty, the length of the chain.
35 len: usize,
36 }
37
38 /// The result type used by [`validate_csr()`]. The standard [`Result`] is currently only
39 /// converted to exceptions by `cxxbridge` but we can't use exceptions so need to do something
40 /// custom.
41 struct ValidateCsrResult {
42 /// If non-empty, the description of the verification error that occurred.
43 error: String,
44 /// If [`error`] is empty, a handle to the validated Csr.
45 csr: Box<Csr>,
46 }
47
48 extern "Rust" {
49 type DiceChain;
50
51 #[cxx_name = VerifyDiceChain]
verify_dice_chain( chain: &[u8], kind: DiceChainKind, allow_any_mode: bool, instance: &str, ) -> VerifyDiceChainResult52 fn verify_dice_chain(
53 chain: &[u8],
54 kind: DiceChainKind,
55 allow_any_mode: bool,
56 instance: &str,
57 ) -> VerifyDiceChainResult;
58
59 #[cxx_name = GetDiceChainPublicKey]
get_dice_chain_public_key(chain: &DiceChain, n: usize) -> Vec<u8>60 fn get_dice_chain_public_key(chain: &DiceChain, n: usize) -> Vec<u8>;
61
62 #[cxx_name = IsDiceChainProper]
is_dice_chain_proper(chain: &DiceChain) -> bool63 fn is_dice_chain_proper(chain: &DiceChain) -> bool;
64
65 type Csr;
66
67 #[cxx_name = validateCsr]
validate_csr( csr: &[u8], kind: DiceChainKind, allow_any_mode: bool, instance: &str, ) -> ValidateCsrResult68 fn validate_csr(
69 csr: &[u8],
70 kind: DiceChainKind,
71 allow_any_mode: bool,
72 instance: &str,
73 ) -> ValidateCsrResult;
74
75 #[cxx_name = getDiceChainFromCsr]
get_dice_chain_from_csr(csr: &Csr) -> VerifyDiceChainResult76 fn get_dice_chain_from_csr(csr: &Csr) -> VerifyDiceChainResult;
77 }
78 }
79
80 /// A DICE chain as exposed over the cxx bridge.
81 pub struct DiceChain(Option<ChainForm>);
82
verify_dice_chain( chain: &[u8], kind: ffi::DiceChainKind, allow_any_mode: bool, instance: &str, ) -> ffi::VerifyDiceChainResult83 fn verify_dice_chain(
84 chain: &[u8],
85 kind: ffi::DiceChainKind,
86 allow_any_mode: bool,
87 instance: &str,
88 ) -> ffi::VerifyDiceChainResult {
89 let mut session = Session {
90 options: match kind {
91 ffi::DiceChainKind::Vsr13 => Options::vsr13(),
92 ffi::DiceChainKind::Vsr14 => Options::vsr14(),
93 ffi::DiceChainKind::Vsr15 => Options::vsr15(),
94 ffi::DiceChainKind::Vsr16 => Options::vsr16(),
95 _ => {
96 return ffi::VerifyDiceChainResult {
97 error: "invalid chain kind".to_string(),
98 chain: Box::new(DiceChain(None)),
99 len: 0,
100 }
101 }
102 },
103 };
104 let Ok(rkp_instance) = RkpInstance::from_str(instance) else {
105 return ffi::VerifyDiceChainResult {
106 error: format!("invalid RKP instance: {}", instance),
107 chain: Box::new(DiceChain(None)),
108 len: 0,
109 };
110 };
111 session.set_allow_any_mode(allow_any_mode);
112 session.set_rkp_instance(rkp_instance);
113 match ChainForm::from_cbor(&session, chain) {
114 Ok(chain) => {
115 let len = chain.length();
116 let chain = Box::new(DiceChain(Some(chain)));
117 ffi::VerifyDiceChainResult { error: "".to_string(), chain, len }
118 }
119 Err(e) => {
120 let error = format!("{:#}", e);
121 ffi::VerifyDiceChainResult { error, chain: Box::new(DiceChain(None)), len: 0 }
122 }
123 }
124 }
125
get_dice_chain_public_key(chain: &DiceChain, n: usize) -> Vec<u8>126 fn get_dice_chain_public_key(chain: &DiceChain, n: usize) -> Vec<u8> {
127 if let DiceChain(Some(chain)) = chain {
128 let key = match chain {
129 ChainForm::Proper(chain) => chain.payloads()[n].subject_public_key(),
130 ChainForm::Degenerate(chain) => chain.public_key(),
131 };
132 if let Ok(cose_key) = key.to_cose_key() {
133 if let Ok(bytes) = cose_key.to_vec() {
134 return bytes;
135 }
136 }
137 }
138 Vec::new()
139 }
140
is_dice_chain_proper(chain: &DiceChain) -> bool141 fn is_dice_chain_proper(chain: &DiceChain) -> bool {
142 if let DiceChain(Some(chain)) = chain {
143 match chain {
144 ChainForm::Proper(_) => true,
145 ChainForm::Degenerate(_) => false,
146 }
147 } else {
148 false
149 }
150 }
151
152 /// A Csr as exposed over the cxx bridge.
153 pub struct Csr(Option<InnerCsr>);
154
validate_csr( csr: &[u8], kind: ffi::DiceChainKind, allow_any_mode: bool, instance: &str, ) -> ffi::ValidateCsrResult155 fn validate_csr(
156 csr: &[u8],
157 kind: ffi::DiceChainKind,
158 allow_any_mode: bool,
159 instance: &str,
160 ) -> ffi::ValidateCsrResult {
161 let mut session = Session {
162 options: match kind {
163 ffi::DiceChainKind::Vsr13 => Options::vsr13(),
164 ffi::DiceChainKind::Vsr14 => Options::vsr14(),
165 ffi::DiceChainKind::Vsr15 => Options::vsr15(),
166 ffi::DiceChainKind::Vsr16 => Options::vsr16(),
167 _ => {
168 return ffi::ValidateCsrResult {
169 error: "invalid chain kind".to_string(),
170 csr: Box::new(Csr(None)),
171 }
172 }
173 },
174 };
175 let Ok(rkp_instance) = RkpInstance::from_str(instance) else {
176 return ffi::ValidateCsrResult {
177 error: format!("invalid RKP instance: {}", instance),
178 csr: Box::new(Csr(None)),
179 };
180 };
181 session.set_allow_any_mode(allow_any_mode);
182 session.set_rkp_instance(rkp_instance);
183 match InnerCsr::from_cbor(&session, csr) {
184 Ok(csr) => {
185 let csr = Box::new(Csr(Some(csr)));
186 ffi::ValidateCsrResult { error: "".to_string(), csr }
187 }
188 Err(e) => {
189 let error = format!("{:#}", e);
190 ffi::ValidateCsrResult { error, csr: Box::new(Csr(None)) }
191 }
192 }
193 }
194
get_dice_chain_from_csr(csr: &Csr) -> ffi::VerifyDiceChainResult195 fn get_dice_chain_from_csr(csr: &Csr) -> ffi::VerifyDiceChainResult {
196 match csr {
197 Csr(Some(csr)) => {
198 let chain = csr.dice_chain();
199 let len = chain.length();
200 let chain = Box::new(DiceChain(Some(chain)));
201 ffi::VerifyDiceChainResult { error: "".to_string(), chain, len }
202 }
203 _ => ffi::VerifyDiceChainResult {
204 error: "CSR could not be destructured".to_string(),
205 chain: Box::new(DiceChain(None)),
206 len: 0,
207 },
208 }
209 }
210