1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! Command line test tool for interacting with Secretkeeper.
18 
19 use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::{
20     ISecretkeeper::ISecretkeeper, SecretId::SecretId,
21 };
22 use anyhow::{anyhow, bail, Context, Result};
23 use authgraph_boringssl::BoringSha256;
24 use authgraph_core::traits::Sha256;
25 use clap::{Args, Parser, Subcommand};
26 use coset::CborSerializable;
27 use dice_policy_builder::{
28     policy_for_dice_chain, ConstraintSpec, ConstraintType, MissingAction, TargetEntry,
29     WILDCARD_FULL_ARRAY,
30 };
31 
32 use secretkeeper_client::{dice::OwnedDiceArtifactsWithExplicitKey, SkSession};
33 use secretkeeper_comm::data_types::{
34     error::SecretkeeperError,
35     packet::{ResponsePacket, ResponseType},
36     request::Request,
37     request_response_impl::{GetSecretRequest, GetSecretResponse, StoreSecretRequest},
38     response::Response,
39     {Id, Secret},
40 };
41 use secretkeeper_test::{
42     dice_sample::make_explicit_owned_dice, AUTHORITY_HASH, CONFIG_DESC, MODE, SECURITY_VERSION,
43     SUBCOMPONENT_AUTHORITY_HASH, SUBCOMPONENT_DESCRIPTORS, SUBCOMPONENT_SECURITY_VERSION,
44 };
45 use std::io::Write;
46 
47 #[derive(Parser, Debug)]
48 #[command(about = "Interact with Secretkeeper HAL")]
49 #[command(version = "0.1")]
50 #[command(propagate_version = true)]
51 struct Cli {
52     #[command(subcommand)]
53     command: Command,
54 
55     /// Secretkeeper instance to connect to.
56     #[arg(long, short)]
57     instance: Option<String>,
58 
59     /// Security version in leaf DICE node.
60     #[clap(default_value_t = 100)]
61     #[arg(long, short = 'v')]
62     dice_version: u64,
63 
64     /// Show hex versions of secrets and their IDs.
65     #[clap(default_value_t = false)]
66     #[arg(long, short = 'v')]
67     hex: bool,
68 }
69 
70 #[derive(Subcommand, Debug)]
71 enum Command {
72     /// Store a secret value.
73     Store(StoreArgs),
74     /// Get a secret value.
75     Get(GetArgs),
76     /// Delete a secret value.
77     Delete(DeleteArgs),
78     /// Delete all secret values.
79     DeleteAll(DeleteAllArgs),
80 }
81 
82 #[derive(Args, Debug)]
83 struct StoreArgs {
84     /// Identifier for the secret, as either a short (< 32 byte) string, or as 32 bytes of hex.
85     id: String,
86     /// Value to use as the secret value. If specified as 32 bytes of hex, the decoded value
87     /// will be used as-is; otherwise, a string (less than 31 bytes in length) will be encoded
88     /// as the secret.
89     value: String,
90 }
91 
92 #[derive(Args, Debug)]
93 struct GetArgs {
94     /// Identifier for the secret, as either a short (< 32 byte) string, or as 32 bytes of hex.
95     id: String,
96 }
97 
98 #[derive(Args, Debug)]
99 struct DeleteArgs {
100     /// Identifier for the secret, as either a short (< 32 byte) string, or as 32 bytes of hex.
101     id: String,
102 }
103 
104 #[derive(Args, Debug)]
105 struct DeleteAllArgs {
106     /// Confirm deletion of all secrets.
107     yes: bool,
108 }
109 
110 const SECRETKEEPER_SERVICE: &str = "android.hardware.security.secretkeeper.ISecretkeeper";
111 
112 /// Secretkeeper client information.
113 struct SkClient {
114     sk: binder::Strong<dyn ISecretkeeper>,
115     session: SkSession,
116     dice_artifacts: OwnedDiceArtifactsWithExplicitKey,
117 }
118 
119 impl SkClient {
new(instance: &str, dice_artifacts: OwnedDiceArtifactsWithExplicitKey) -> Self120     fn new(instance: &str, dice_artifacts: OwnedDiceArtifactsWithExplicitKey) -> Self {
121         let sk: binder::Strong<dyn ISecretkeeper> =
122             binder::get_interface(&format!("{SECRETKEEPER_SERVICE}/{instance}")).unwrap();
123         let session = SkSession::new(sk.clone(), &dice_artifacts, None).unwrap();
124         Self { sk, session, dice_artifacts }
125     }
126 
secret_management_request(&mut self, req_data: &[u8]) -> Result<Vec<u8>>127     fn secret_management_request(&mut self, req_data: &[u8]) -> Result<Vec<u8>> {
128         self.session
129             .secret_management_request(req_data)
130             .map_err(|e| anyhow!("secret management: {e:?}"))
131     }
132 
133     /// Construct a sealing policy on the DICE chain with constraints:
134     /// 1. `ExactMatch` on `AUTHORITY_HASH` (non-optional) on all nodes.
135     /// 2. `ExactMatch` on `MODE` (non-optional) on all nodes.
136     /// 3. `GreaterOrEqual` on `SECURITY_VERSION` (optional) on all nodes.
137     /// 4. The  DiceChainEntry corresponding to "AVB" contains SubcomponentDescriptor, for each of those:
138     ///     a) GreaterOrEqual on SECURITY_VERSION (Required)
139     //      b) ExactMatch on AUTHORITY_HASH (Required).
sealing_policy(&self) -> Result<Vec<u8>>140     fn sealing_policy(&self) -> Result<Vec<u8>> {
141         let dice =
142             self.dice_artifacts.explicit_key_dice_chain().context("extract explicit DICE chain")?;
143 
144         let constraint_spec = vec![
145             ConstraintSpec::new(
146                 ConstraintType::ExactMatch,
147                 vec![AUTHORITY_HASH],
148                 MissingAction::Fail,
149                 TargetEntry::All,
150             ),
151             ConstraintSpec::new(
152                 ConstraintType::ExactMatch,
153                 vec![MODE],
154                 MissingAction::Fail,
155                 TargetEntry::All,
156             ),
157             ConstraintSpec::new(
158                 ConstraintType::GreaterOrEqual,
159                 vec![CONFIG_DESC, SECURITY_VERSION],
160                 MissingAction::Ignore,
161                 TargetEntry::All,
162             ),
163             ConstraintSpec::new(
164                 ConstraintType::GreaterOrEqual,
165                 vec![
166                     CONFIG_DESC,
167                     SUBCOMPONENT_DESCRIPTORS,
168                     WILDCARD_FULL_ARRAY,
169                     SUBCOMPONENT_SECURITY_VERSION,
170                 ],
171                 MissingAction::Fail,
172                 TargetEntry::ByName("AVB".to_string()),
173             ),
174             ConstraintSpec::new(
175                 ConstraintType::ExactMatch,
176                 vec![
177                     CONFIG_DESC,
178                     SUBCOMPONENT_DESCRIPTORS,
179                     WILDCARD_FULL_ARRAY,
180                     SUBCOMPONENT_AUTHORITY_HASH,
181                 ],
182                 MissingAction::Fail,
183                 TargetEntry::ByName("AVB".to_string()),
184             ),
185         ];
186         policy_for_dice_chain(dice, constraint_spec)
187             .unwrap()
188             .to_vec()
189             .context("serialize DICE policy")
190     }
191 
store(&mut self, id: &Id, secret: &Secret) -> Result<()>192     fn store(&mut self, id: &Id, secret: &Secret) -> Result<()> {
193         let store_request = StoreSecretRequest {
194             id: id.clone(),
195             secret: secret.clone(),
196             sealing_policy: self.sealing_policy().context("build sealing policy")?,
197         };
198         let store_request =
199             store_request.serialize_to_packet().to_vec().context("serialize StoreSecretRequest")?;
200 
201         let store_response = self.secret_management_request(&store_request)?;
202         let store_response =
203             ResponsePacket::from_slice(&store_response).context("deserialize ResponsePacket")?;
204         let response_type = store_response.response_type().unwrap();
205         if response_type == ResponseType::Success {
206             Ok(())
207         } else {
208             let err = *SecretkeeperError::deserialize_from_packet(store_response).unwrap();
209             Err(anyhow!("STORE failed: {err:?}"))
210         }
211     }
212 
get(&mut self, id: &Id) -> Result<Option<Secret>>213     fn get(&mut self, id: &Id) -> Result<Option<Secret>> {
214         let get_request = GetSecretRequest { id: id.clone(), updated_sealing_policy: None }
215             .serialize_to_packet()
216             .to_vec()
217             .context("serialize GetSecretRequest")?;
218 
219         let get_response = self.secret_management_request(&get_request).context("secret mgmt")?;
220         let get_response =
221             ResponsePacket::from_slice(&get_response).context("deserialize ResponsePacket")?;
222 
223         if get_response.response_type().unwrap() == ResponseType::Success {
224             let get_response = *GetSecretResponse::deserialize_from_packet(get_response).unwrap();
225             Ok(Some(Secret(get_response.secret.0)))
226         } else {
227             // Only expect a not-found failure.
228             let err = *SecretkeeperError::deserialize_from_packet(get_response).unwrap();
229             if err == SecretkeeperError::EntryNotFound {
230                 Ok(None)
231             } else {
232                 Err(anyhow!("GET failed: {err:?}"))
233             }
234         }
235     }
236 
237     /// Helper method to delete secrets.
delete(&self, ids: &[&Id]) -> Result<()>238     fn delete(&self, ids: &[&Id]) -> Result<()> {
239         let ids: Vec<SecretId> = ids.iter().map(|id| SecretId { id: id.0 }).collect();
240         self.sk.deleteIds(&ids).context("deleteIds")
241     }
242 
243     /// Helper method to delete everything.
delete_all(&self) -> Result<()>244     fn delete_all(&self) -> Result<()> {
245         self.sk.deleteAll().context("deleteAll")
246     }
247 }
248 
249 /// Convert a string input into an `Id`.  Input can be 64 bytes of hex, or a string
250 /// that will be hashed to give the `Id` value. Returns the `Id` and a display string.
string_to_id(s: &str, show_hex: bool) -> (Id, String)251 fn string_to_id(s: &str, show_hex: bool) -> (Id, String) {
252     if let Ok(data) = hex::decode(s) {
253         if data.len() == 64 {
254             // Assume something that parses as 64 bytes of hex is it.
255             return (Id(data.try_into().unwrap()), s.to_string().to_lowercase());
256         }
257     }
258     // Create a secret ID by repeating the SHA-256 hash of the string twice.
259     let hash = BoringSha256.compute_sha256(s.as_bytes()).unwrap();
260     let mut id = Id([0; 64]);
261     id.0[..32].copy_from_slice(&hash);
262     id.0[32..].copy_from_slice(&hash);
263     if show_hex {
264         let hex_id = hex::encode(&id.0);
265         (id, format!("'{s}' (as {hex_id})"))
266     } else {
267         (id, format!("'{s}'"))
268     }
269 }
270 
271 /// Convert a string input into a `Secret`.  Input can be 32 bytes of hex, or a short string
272 /// that will be encoded as the `Secret` value. Returns the `Secret` and a display string.
value_to_secret(s: &str, show_hex: bool) -> Result<(Secret, String)>273 fn value_to_secret(s: &str, show_hex: bool) -> Result<(Secret, String)> {
274     if let Ok(data) = hex::decode(s) {
275         if data.len() == 32 {
276             // Assume something that parses as 32 bytes of hex is it.
277             return Ok((Secret(data.try_into().unwrap()), s.to_string().to_lowercase()));
278         }
279     }
280     let data = s.as_bytes();
281     if data.len() > 31 {
282         return Err(anyhow!("secret too long"));
283     }
284     let mut secret = Secret([0; 32]);
285     secret.0[0] = data.len() as u8;
286     secret.0[1..1 + data.len()].copy_from_slice(data);
287     Ok(if show_hex {
288         let hex_secret = hex::encode(&secret.0);
289         (secret, format!("'{s}' (as {hex_secret})"))
290     } else {
291         (secret, format!("'{s}'"))
292     })
293 }
294 
295 /// Convert a `Secret` into a displayable string. If the secret looks like an encoded
296 /// string, show that, otherwise show the value in hex.
secret_to_value_display(secret: &Secret, show_hex: bool) -> String297 fn secret_to_value_display(secret: &Secret, show_hex: bool) -> String {
298     let hex = hex::encode(&secret.0);
299     secret_to_value(secret)
300         .map(|s| if show_hex { format!("'{s}' (from {hex})") } else { format!("'{s}'") })
301         .unwrap_or_else(|_e| format!("{hex}"))
302 }
303 
304 /// Attempt to convert a `Secret` back to a string.
secret_to_value(secret: &Secret) -> Result<String>305 fn secret_to_value(secret: &Secret) -> Result<String> {
306     let len = secret.0[0] as usize;
307     if len > 31 {
308         return Err(anyhow!("too long"));
309     }
310     std::str::from_utf8(&secret.0[1..1 + len]).map(|s| s.to_string()).context("not UTF-8 string")
311 }
312 
main() -> Result<()>313 fn main() -> Result<()> {
314     let cli = Cli::parse();
315 
316     // Figure out which Secretkeeper instance is desired, and connect to it.
317     let instance = if let Some(instance) = &cli.instance {
318         // Explicitly specified.
319         instance.clone()
320     } else {
321         // If there's only one instance, use that.
322         let instances: Vec<String> = binder::get_declared_instances(SECRETKEEPER_SERVICE)
323             .unwrap_or_default()
324             .into_iter()
325             .collect();
326         match instances.len() {
327             0 => bail!("No Secretkeeper instances available on device!"),
328             1 => instances[0].clone(),
329             _ => {
330                 bail!(
331                     concat!(
332                         "Multiple Secretkeeper instances available on device: {}\n",
333                         "Use --instance <instance> to specify one."
334                     ),
335                     instances.join(", ")
336                 );
337             }
338         }
339     };
340     let dice = make_explicit_owned_dice(cli.dice_version);
341     let mut sk_client = SkClient::new(&instance, dice);
342 
343     match cli.command {
344         Command::Get(args) => {
345             let (id, display_id) = string_to_id(&args.id, cli.hex);
346             print!("GET key {display_id}: ");
347             match sk_client.get(&id).context("GET") {
348                 Ok(None) => println!("not found"),
349                 Ok(Some(s)) => println!("{}", secret_to_value_display(&s, cli.hex)),
350                 Err(e) => {
351                     println!("failed!");
352                     return Err(e);
353                 }
354             }
355         }
356         Command::Store(args) => {
357             let (id, display_id) = string_to_id(&args.id, cli.hex);
358             let (secret, display_secret) = value_to_secret(&args.value, cli.hex)?;
359             println!("STORE key {display_id}: {display_secret}");
360             sk_client.store(&id, &secret).context("STORE")?;
361         }
362         Command::Delete(args) => {
363             let (id, display_id) = string_to_id(&args.id, cli.hex);
364             println!("DELETE key {display_id}");
365             sk_client.delete(&[&id]).context("DELETE")?;
366         }
367         Command::DeleteAll(args) => {
368             if !args.yes {
369                 // Request confirmation.
370                 println!("Confirm delete all secrets: [y/N]");
371                 let _ = std::io::stdout().flush();
372                 let mut input = String::new();
373                 std::io::stdin().read_line(&mut input)?;
374                 let c = input.chars().next();
375                 if c != Some('y') && c != Some('Y') {
376                     bail!("DELETE_ALL not confirmed");
377                 }
378             }
379             println!("DELETE_ALL");
380             sk_client.delete_all().context("DELETE_ALL")?;
381         }
382     }
383     Ok(())
384 }
385