tough/editor/
keys.rs

1// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::error::{self, Result};
5use crate::key_source::KeySource;
6use crate::schema::decoded::{Decoded, Hex};
7use crate::schema::{Delegations, KeyHolder, RoleId, RoleKeys, Root, Signed, Targets};
8use crate::sign::Sign;
9use snafu::{ensure, OptionExt, ResultExt};
10use std::collections::HashMap;
11
12/// A map of key ID (from root.json or the Delegations field of any Targets) to its corresponding signing key
13pub(crate) type KeyList = HashMap<Decoded<Hex>, Box<dyn Sign>>;
14
15impl KeyHolder {
16    /// Creates a key list for the provided keys
17    pub(crate) async fn get_keys(&self, keys: &[Box<dyn KeySource>]) -> Result<KeyList> {
18        match self {
19            Self::Delegations(delegations) => get_targets_keys(delegations, keys).await,
20            Self::Root(root) => get_root_keys(root, keys).await,
21        }
22    }
23
24    /// Returns role keys for the provided role id
25    pub(crate) fn role_keys(&self, name: RoleId) -> Result<RoleKeys> {
26        match self {
27            Self::Delegations(delegations) => {
28                if let RoleId::DelegatedRole(name) = name.clone() {
29                    for role in &delegations.roles {
30                        if role.name == name.clone() {
31                            return Ok(role.keys());
32                        }
33                    }
34                }
35            }
36            Self::Root(root) => {
37                if let RoleId::StandardRole(roletype) = name {
38                    return Ok(root
39                        .roles
40                        .get(&roletype)
41                        .context(error::NoRoleKeysinRootSnafu {
42                            role: roletype.to_string(),
43                        })?
44                        .clone());
45                }
46            }
47        }
48        let role = match name {
49            RoleId::StandardRole(role) => role.to_string(),
50            RoleId::DelegatedRole(role_name) => role_name,
51        };
52        Err(error::Error::SigningKeysNotFound { role })
53    }
54
55    /// Verifies the role using `KeyHolder`'s keys
56    pub(crate) fn verify_role(&self, targets: &Signed<Targets>, name: &str) -> Result<()> {
57        match self {
58            Self::Delegations(delegations) => {
59                delegations
60                    .verify_role(targets, name)
61                    .context(error::VerifyRoleMetadataSnafu {
62                        role: name.to_string(),
63                    })
64            }
65            Self::Root(root) => root
66                .verify_role(targets)
67                .context(error::VerifyRoleMetadataSnafu {
68                    role: name.to_string(),
69                }),
70        }
71    }
72}
73
74/// Gets the corresponding keys from Root (root.json) for the given `KeySource`s.
75/// This is a convenience function that wraps `Root.key_id()` for multiple
76/// `KeySource`s.
77pub(crate) async fn get_root_keys(root: &Root, keys: &[Box<dyn KeySource>]) -> Result<KeyList> {
78    let mut root_keys = KeyList::new();
79
80    for source in keys {
81        // Get a keypair from the given source
82        let key_pair = source
83            .as_sign()
84            .await
85            .context(error::KeyPairFromKeySourceSnafu)?;
86
87        // If the keypair matches any of the keys in the root.json,
88        // add its ID and corresponding keypair the map to be returned
89        if let Some(key_id) = root.key_id(key_pair.as_ref()) {
90            root_keys.insert(key_id, key_pair);
91        }
92    }
93    ensure!(!root_keys.is_empty(), error::KeysNotFoundInRootSnafu);
94    Ok(root_keys)
95}
96
97/// Gets the corresponding keys from delegations for the given `KeySource`s.
98/// This is a convenience function that wraps `Delegations.key_id()` for multiple
99/// `KeySource`s.
100pub(crate) async fn get_targets_keys(
101    delegations: &Delegations,
102    keys: &[Box<dyn KeySource>],
103) -> Result<KeyList> {
104    let mut delegations_keys = KeyList::new();
105    for source in keys {
106        // Get a keypair from the given source
107        let key_pair = source
108            .as_sign()
109            .await
110            .context(error::KeyPairFromKeySourceSnafu)?;
111        // If the keypair matches any of the keys in the delegations metadata,
112        // add its ID and corresponding keypair the map to be returned
113        if let Some(key_id) = delegations.key_id(key_pair.as_ref()) {
114            delegations_keys.insert(key_id, key_pair);
115        }
116    }
117    Ok(delegations_keys)
118}