1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT OR Apache-2.0

mod client;
pub mod error;

use snafu::{OptionExt, ResultExt};
use tough::async_trait;
use tough::key_source::KeySource;
use tough::sign::{parse_keypair, Sign};

/// Implements the KeySource trait for keys that live in AWS SSM.
#[derive(Debug)]
pub struct SsmKeySource {
    pub profile: Option<String>,
    pub parameter_name: String,
    pub key_id: Option<String>,
}

/// Implements the KeySource trait.
#[async_trait]
impl KeySource for SsmKeySource {
    async fn as_sign(
        &self,
    ) -> std::result::Result<Box<dyn Sign>, Box<dyn std::error::Error + Send + Sync + 'static>>
    {
        let ssm_client = client::build_client(self.profile.as_deref())?;
        let response = ssm_client
            .get_parameter()
            .name(self.parameter_name.to_owned())
            .with_decryption(true)
            .send()
            .await
            .context(error::SsmGetParameterSnafu {
                profile: self.profile.clone(),
                parameter_name: &self.parameter_name,
            })?;
        let data = response
            .parameter
            .context(error::SsmMissingFieldSnafu {
                parameter_name: &self.parameter_name,
                field: "parameter",
            })?
            .value
            .context(error::SsmMissingFieldSnafu {
                parameter_name: &self.parameter_name,
                field: "parameter.value",
            })?
            .as_bytes()
            .to_vec();
        let sign = Box::new(parse_keypair(&data).context(error::KeyPairParseSnafu)?);
        Ok(sign)
    }

    async fn write(
        &self,
        value: &str,
        key_id_hex: &str,
    ) -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
        let ssm_client = client::build_client(self.profile.as_deref())?;

        ssm_client
            .put_parameter()
            .name(self.parameter_name.to_owned())
            .description(key_id_hex.to_owned())
            .set_key_id(self.key_id.as_ref().cloned())
            .overwrite(true)
            .set_type(Some(aws_sdk_ssm::types::ParameterType::SecureString))
            .value(value.to_owned())
            .send()
            .await
            .context(error::SsmPutParameterSnafu {
                profile: self.profile.clone(),
                parameter_name: &self.parameter_name,
            })?;

        Ok(())
    }
}