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