diff --git a/aws/sdk/examples/kms-helloworld/Cargo.toml b/aws/sdk/examples/kms/Cargo.toml similarity index 60% rename from aws/sdk/examples/kms-helloworld/Cargo.toml rename to aws/sdk/examples/kms/Cargo.toml index 7189a85a2f..513c6ffbf9 100644 --- a/aws/sdk/examples/kms-helloworld/Cargo.toml +++ b/aws/sdk/examples/kms/Cargo.toml @@ -1,14 +1,17 @@ [package] -name = "kms-helloworld" +name = "kms-code-examples" version = "0.1.0" -authors = ["Russell Cohen "] +authors = ["Russell Cohen ", "Doug Schwartz , + + /// Activate verbose mode + #[structopt(short, long)] + verbose: bool, +} +/// Creates an AWS KMS key. +/// # Arguments +/// +/// * `[-d DEFAULT-REGION]` - The region in which the client is created. +/// If not supplied, uses the value of the **AWS_DEFAULT_REGION** environment variable. +/// If the environment variable is not set, defaults to **us-west-2**. +/// * `[-v]` - Whether to display additional information. +#[tokio::main] +async fn main() { + let Opt { + default_region, + verbose, + } = Opt::from_args(); + + let region = default_region + .as_ref() + .map(|region| Region::new(region.clone())) + .or_else(|| aws_types::region::default_provider().region()) + .unwrap_or_else(|| Region::new("us-west-2")); + + if verbose { + println!("KMS client version: {}\n", kms::PKG_VERSION); + println!("Region: {:?}", ®ion); + + SubscriberBuilder::default() + .with_env_filter("info") + .with_span_events(FmtSpan::CLOSE) + .init(); + } + + let conf = Config::builder().region(region).build(); + let client = Client::from_conf(conf); + + match client.create_key().send().await { + Ok(resp) => { + let id = resp + .key_metadata + .unwrap() + .key_id + .unwrap_or_else(|| String::from("No ID!")); + println!("Key: {}", id); + } + Err(e) => { + println!("Got error creating key:"); + println!("{}", e); + process::exit(1); + } + }; +} diff --git a/aws/sdk/examples/kms/src/bin/decrypt.rs b/aws/sdk/examples/kms/src/bin/decrypt.rs new file mode 100644 index 0000000000..00d249e42a --- /dev/null +++ b/aws/sdk/examples/kms/src/bin/decrypt.rs @@ -0,0 +1,108 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +use std::fs; +use std::process; + +use kms::{Blob, Client, Config, Region}; + +use aws_types::region::ProvideRegion; + +use structopt::StructOpt; +use tracing_subscriber::fmt::format::FmtSpan; +use tracing_subscriber::fmt::SubscriberBuilder; + +#[derive(Debug, StructOpt)] +struct Opt { + /// The region. Overrides environment variable AWS_DEFAULT_REGION. + #[structopt(short, long)] + default_region: Option, + + /// Specifies the encryption key + #[structopt(short, long)] + key: String, + + /// The name of the input file with encrypted text to decrypt + #[structopt(short, long)] + input: String, + + /// Specifies whether to display additonal runtime informmation + #[structopt(short, long)] + verbose: bool, +} + +/// Decrypts a string encrypted by AWS KMS. +/// # Arguments +/// +/// * `-k KEY` - The encryption key. +/// * `-i INPUT` - The encrypted string. +/// * `[-d DEFAULT-REGION]` - The region in which the client is created. +/// If not supplied, uses the value of the **AWS_DEFAULT_REGION** environment variable. +/// If the environment variable is not set, defaults to **us-west-2**. +/// * `[-v]` - Whether to display additional information. +#[tokio::main] +async fn main() { + let Opt { + key, + input, + default_region, + verbose, + } = Opt::from_args(); + + let region = default_region + .as_ref() + .map(|region| Region::new(region.clone())) + .or_else(|| aws_types::region::default_provider().region()) + .unwrap_or_else(|| Region::new("us-west-2")); + + if verbose { + println!("KMS client version: {}\n", kms::PKG_VERSION); + println!("Region: {:?}", ®ion); + println!("Key: {}", key); + println!("Input: {}", input); + + SubscriberBuilder::default() + .with_env_filter("info") + .with_span_events(FmtSpan::CLOSE) + .init(); + } + + let conf = Config::builder().region(region).build(); + let client = Client::from_conf(conf); + + // Open input text file and get contents as a string + // input is a base-64 encoded string, so decode it: + let data = fs::read_to_string(input) + .map(|input| { + base64::decode(input).expect("Input file does not contain valid base 64 characters.") + }) + .map(Blob::new); + + let resp = match client + .decrypt() + .key_id(key) + .ciphertext_blob(data.unwrap()) + .send() + .await + { + Ok(output) => output, + Err(e) => { + eprintln!("Encryption failure: {}", e); + process::exit(1); + } + }; + + let inner = resp.plaintext.unwrap(); + let bytes = inner.as_ref(); + + let s = match String::from_utf8(bytes.to_vec()) { + Ok(v) => v, + Err(e) => panic!("Invalid UTF-8 sequence: {}", e), + }; + + println!(); + println!("Decoded string:"); + println!("{}", s); +} diff --git a/aws/sdk/examples/kms/src/bin/encrypt.rs b/aws/sdk/examples/kms/src/bin/encrypt.rs new file mode 100644 index 0000000000..3a94c536b5 --- /dev/null +++ b/aws/sdk/examples/kms/src/bin/encrypt.rs @@ -0,0 +1,106 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +use std::fs::File; +use std::io::Write; +use std::process; + +use kms::{Blob, Client, Config, Region}; + +use aws_types::region::ProvideRegion; + +use structopt::StructOpt; +use tracing_subscriber::fmt::format::FmtSpan; +use tracing_subscriber::fmt::SubscriberBuilder; + +#[derive(Debug, StructOpt)] +struct Opt { + /// The region. Overrides environment variable AWS_DEFAULT_REGION. + #[structopt(short, long)] + default_region: Option, + + /// Specifies the encryption key + #[structopt(short, long)] + key: String, + + /// Specifies the text to encrypt + #[structopt(short, long)] + text: String, + + /// Specifies the name of the file to store the encrypted text in + #[structopt(short, long)] + out: String, + + /// Whether to display additional runtime information + #[structopt(short, long)] + verbose: bool, +} + +/// Encrypts a string using an AWS KMS key. +/// # Arguments +/// +/// * `-k KEY` - The KMS key. +/// * `-o OUT` - The name of the file to store the encryped key in. +/// * `-t TEXT` - The string to encrypt. +/// * `[-d DEFAULT-REGION]` - The region in which the client is created. +/// If not supplied, uses the value of the **AWS_DEFAULT_REGION** environment variable. +/// If the environment variable is not set, defaults to **us-west-2**. +/// * `[-v]` - Whether to display additional information. +#[tokio::main] +async fn main() { + let Opt { + key, + out, + default_region, + text, + verbose, + } = Opt::from_args(); + + let region = default_region + .as_ref() + .map(|region| Region::new(region.clone())) + .or_else(|| aws_types::region::default_provider().region()) + .unwrap_or_else(|| Region::new("us-west-2")); + + if verbose { + println!("KMS client version: {}\n", kms::PKG_VERSION); + println!("Region: {:?}", ®ion); + println!("Key: {}", key); + println!("Text: {}", text); + println!("Out: {}", out); + + SubscriberBuilder::default() + .with_env_filter("info") + .with_span_events(FmtSpan::CLOSE) + .init(); + } + + let conf = Config::builder().region(region).build(); + let client = Client::from_conf(conf); + + let blob = Blob::new(text.as_bytes()); + + let resp = match client.encrypt().key_id(key).plaintext(blob).send().await { + Ok(output) => output, + Err(e) => { + eprintln!("Encryption failure: {}", e); + process::exit(1); + } + }; + + // Did we get an encrypted blob? + let blob = resp.ciphertext_blob.expect("Could not get encrypted text"); + let bytes = blob.as_ref(); + + let s = base64::encode(&bytes); + + let mut ofile = File::create(&out).expect("unable to create file"); + ofile.write_all(s.as_bytes()).expect("unable to write"); + + if verbose { + println!("Wrote the following to {}", &out); + println!("{}", s); + } +} diff --git a/aws/sdk/examples/kms/src/bin/generate-data-key-without-plaintext.rs b/aws/sdk/examples/kms/src/bin/generate-data-key-without-plaintext.rs new file mode 100644 index 0000000000..8a2ec24726 --- /dev/null +++ b/aws/sdk/examples/kms/src/bin/generate-data-key-without-plaintext.rs @@ -0,0 +1,90 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +use std::process; + +use kms::model::DataKeySpec; + +use kms::{Client, Config, Region}; + +use aws_types::region::ProvideRegion; + +use structopt::StructOpt; +use tracing_subscriber::fmt::format::FmtSpan; +use tracing_subscriber::fmt::SubscriberBuilder; + +#[derive(Debug, StructOpt)] +struct Opt { + /// The region. Overrides environment variable AWS_DEFAULT_REGION. + #[structopt(short, long)] + default_region: Option, + + /// Specifies the encryption key + #[structopt(short, long)] + key: String, + + /// Specifies whether to display additonal runtime information + #[structopt(short, long)] + verbose: bool, +} + +/// Creates an AWS KMS data key without plaintext. +/// # Arguments +/// +/// * `[-k KEY]` - The name of the data key. +/// * `[-d DEFAULT-REGION]` - The region in which the client is created. +/// If not supplied, uses the value of the **AWS_DEFAULT_REGION** environment variable. +/// If the environment variable is not set, defaults to **us-west-2**. +/// * `[-v]` - Whether to display additional information. +#[tokio::main] +async fn main() { + let Opt { + key, + default_region, + verbose, + } = Opt::from_args(); + + let region = default_region + .as_ref() + .map(|region| Region::new(region.clone())) + .or_else(|| aws_types::region::default_provider().region()) + .unwrap_or_else(|| Region::new("us-west-2")); + + if verbose { + println!("GenerateDataKeyWithoutPlaintext called with options:"); + println!("Region: {:?}", ®ion); + println!("KMS key: {}", key); + + SubscriberBuilder::default() + .with_env_filter("info") + .with_span_events(FmtSpan::CLOSE) + .init(); + } + + let conf = Config::builder().region(region).build(); + let client = Client::from_conf(conf); + + let resp = match client + .generate_data_key_without_plaintext() + .key_id(key) + .key_spec(DataKeySpec::Aes256) + .send() + .await + { + Ok(output) => output, + Err(e) => { + eprintln!("Encryption failure: {}", e); + process::exit(1); + } + }; + + // Did we get an encrypted blob? + let blob = resp.ciphertext_blob.expect("Could not get encrypted text"); + let bytes = blob.as_ref(); + + let s = base64::encode(&bytes); + + println!("\nData key:"); + println!("{}", s); +} diff --git a/aws/sdk/examples/kms/src/bin/generate-data-key.rs b/aws/sdk/examples/kms/src/bin/generate-data-key.rs new file mode 100644 index 0000000000..438300f8ff --- /dev/null +++ b/aws/sdk/examples/kms/src/bin/generate-data-key.rs @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +use std::process; + +use kms::model::DataKeySpec; +use kms::{Client, Config, Region}; + +use aws_types::region::ProvideRegion; + +use structopt::StructOpt; +use tracing_subscriber::fmt::format::FmtSpan; +use tracing_subscriber::fmt::SubscriberBuilder; + +#[derive(Debug, StructOpt)] +struct Opt { + /// The region. Overrides environment variable AWS_DEFAULT_REGION. + #[structopt(short, long)] + default_region: Option, + + /// Specifies the encryption key + #[structopt(short, long)] + key: String, + + /// Specifies whether additonal runtime informmation is displayed + #[structopt(short, long)] + verbose: bool, +} + +/// Creates an AWS KMS data key. +/// # Arguments +/// +/// * `[-k KEY]` - The name of the key. +/// * `[-d DEFAULT-REGION]` - The region in which the client is created. +/// If not supplied, uses the value of the **AWS_DEFAULT_REGION** environment variable. +/// If the environment variable is not set, defaults to **us-west-2**. +/// * `[-v]` - Whether to display additional information. +#[tokio::main] +async fn main() { + let Opt { + key, + default_region, + verbose, + } = Opt::from_args(); + + let region = default_region + .as_ref() + .map(|region| Region::new(region.clone())) + .or_else(|| aws_types::region::default_provider().region()) + .unwrap_or_else(|| Region::new("us-west-2")); + + if verbose { + println!("KMS client version: {}\n", kms::PKG_VERSION); + println!("Region: {:?}", ®ion); + println!("Key: {}", key); + + SubscriberBuilder::default() + .with_env_filter("info") + .with_span_events(FmtSpan::CLOSE) + .init(); + } + + let conf = Config::builder().region(region).build(); + let client = Client::from_conf(conf); + + let resp = match client + .generate_data_key() + .key_id(key) + .key_spec(DataKeySpec::Aes256) + .send() + .await + { + Ok(output) => output, + Err(e) => { + eprintln!("Encryption failure: {}", e); + process::exit(1); + } + }; + + // Did we get an encrypted blob? + let blob = resp.ciphertext_blob.expect("Could not get encrypted text"); + let bytes = blob.as_ref(); + + let s = base64::encode(&bytes); + + println!("\nData key:"); + println!("{}", s); +} diff --git a/aws/sdk/examples/kms/src/bin/generate-random.rs b/aws/sdk/examples/kms/src/bin/generate-random.rs new file mode 100644 index 0000000000..56c7d89636 --- /dev/null +++ b/aws/sdk/examples/kms/src/bin/generate-random.rs @@ -0,0 +1,100 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +use std::process; + +use kms::{Client, Config, Region}; + +use aws_types::region::ProvideRegion; + +use structopt::StructOpt; +use tracing_subscriber::fmt::format::FmtSpan; +use tracing_subscriber::fmt::SubscriberBuilder; + +#[derive(Debug, StructOpt)] +struct Opt { + /// The region. Overrides environment variable AWS_DEFAULT_REGION. + #[structopt(short, long)] + default_region: Option, + + /// The # of bytes. Must be less than 1024. + #[structopt(short, long)] + length: i32, + + /// Specifies whether additonal runtime informmation is displayed + #[structopt(short, long)] + verbose: bool, +} + +/// Creates a random byte string that is cryptographically secure. +/// # Arguments +/// +/// * `[-l LENGTH]` - The number of bytes to generate. Must be less than 1024. +/// * `[-d DEFAULT-REGION]` - The region in which the client is created. +/// If not supplied, uses the value of the **AWS_DEFAULT_REGION** environment variable. +/// If the environment variable is not set, defaults to **us-west-2**. +/// * `[-v]` - Whether to display additional information. +#[tokio::main] +async fn main() { + let Opt { + length, + default_region, + verbose, + } = Opt::from_args(); + + let region = default_region + .as_ref() + .map(|region| Region::new(region.clone())) + .or_else(|| aws_types::region::default_provider().region()) + .unwrap_or_else(|| Region::new("us-west-2")); + + // Trap out-of-range-values: + match length { + 1...1024 => { + println!("Generating a {} byte random string", length); + } + _ => { + println!("Length {} is not within range 1-1024", length); + process::exit(1); + } + } + + if verbose { + println!("KMS client version: {}\n", kms::PKG_VERSION); + println!("Region: {:?}", ®ion); + println!("Length: {}", length); + + SubscriberBuilder::default() + .with_env_filter("info") + .with_span_events(FmtSpan::CLOSE) + .init(); + } + + let conf = Config::builder().region(region).build(); + let client = Client::from_conf(conf); + + let resp = match client + .generate_random() + .number_of_bytes(length) + .send() + .await + { + Ok(output) => output, + Err(e) => { + println!("Got an error calling GenerateRandom:"); + println!("{}", e); + process::exit(1); + } + }; + + // Did we get an encrypted blob? + let blob = resp.plaintext.expect("Could not get encrypted text"); + let bytes = blob.as_ref(); + + let s = base64::encode(&bytes); + + println!("Data key:"); + println!("{}", s); +} diff --git a/aws/sdk/examples/kms-helloworld/src/main.rs b/aws/sdk/examples/kms/src/bin/kms-helloworld.rs similarity index 94% rename from aws/sdk/examples/kms-helloworld/src/main.rs rename to aws/sdk/examples/kms/src/bin/kms-helloworld.rs index f6c0afd5f5..b5fd6c80a9 100644 --- a/aws/sdk/examples/kms-helloworld/src/main.rs +++ b/aws/sdk/examples/kms/src/bin/kms-helloworld.rs @@ -9,6 +9,7 @@ use kms::Region; use tracing_subscriber::fmt::format::FmtSpan; use tracing_subscriber::fmt::SubscriberBuilder; +/// Creates a random byte string that is cryptographically secure in __us-east-1__. #[tokio::main] async fn main() { SubscriberBuilder::default() diff --git a/aws/sdk/examples/kms/src/bin/reencrypt-data.rs b/aws/sdk/examples/kms/src/bin/reencrypt-data.rs new file mode 100644 index 0000000000..48515ba043 --- /dev/null +++ b/aws/sdk/examples/kms/src/bin/reencrypt-data.rs @@ -0,0 +1,125 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +use std::fs; +use std::fs::File; +use std::io::Write; +use std::process; + +use kms::{Blob, Client, Config, Region}; + +use aws_types::region::ProvideRegion; + +use structopt::StructOpt; +use tracing_subscriber::fmt::format::FmtSpan; +use tracing_subscriber::fmt::SubscriberBuilder; + +#[derive(Debug, StructOpt)] +struct Opt { + /// The region. Overrides environment variable AWS_DEFAULT_REGION. + #[structopt(short, long)] + default_region: Option, + + /// The original encryption key + #[structopt(short, long)] + first_key: String, + /// The new encryption key + #[structopt(short, long)] + new_key: String, + /// The name of the input file containing the text to reencrypt + #[structopt(short, long)] + input_file: String, + /// The name of the output file containing the reencrypted text + #[structopt(short, long)] + output_file: String, + /// Whether to display additonal runtime information + #[structopt(short, long)] + verbose: bool, +} + +/// Re-encrypts a string with an AWS KMS key. +/// # Arguments +/// +/// * `[-f FIRST-KEY]` - The first key used to originally encrypt the string. +/// * `[-n NEW-KEY]` - The new key used to re-encrypt the string. +/// * `[-i INPUT-FILE]` - The file containing the encrypted string. +/// * `[-o OUTPUT-FILE]` - The file containing the re-encrypted string. +/// * `[-d DEFAULT-REGION]` - The region in which the client is created. +/// If not supplied, uses the value of the **AWS_DEFAULT_REGION** environment variable. +/// If the environment variable is not set, defaults to **us-west-2**. +/// * `[-v]` - Whether to display additional information. +#[tokio::main] +async fn main() { + let Opt { + first_key, + new_key, + input_file, + output_file, + default_region, + verbose, + } = Opt::from_args(); + + let region = default_region + .as_ref() + .map(|region| Region::new(region.clone())) + .or_else(|| aws_types::region::default_provider().region()) + .unwrap_or_else(|| Region::new("us-west-2")); + + if verbose { + println!("Running ReEncryptData with args:"); + println!("Region: {:?}", ®ion); + println!("Input key: {}", first_key); + println!("Output key: {}", new_key); + println!("Input filename: {}", input_file); + println!("Output filename: {}", output_file); + + SubscriberBuilder::default() + .with_env_filter("info") + .with_span_events(FmtSpan::CLOSE) + .init(); + } + + let conf = Config::builder().region(region).build(); + let client = Client::from_conf(conf); + + // Get blob from input file + // Open input text file and get contents as a string + // input is a base-64 encoded string, so decode it: + let data = fs::read_to_string(input_file) + .map(|input_file| base64::decode(input_file).expect("invalid base 64")) + .map(Blob::new); + + let resp = match client + .re_encrypt() + .ciphertext_blob(data.unwrap()) + .source_key_id(first_key) + .destination_key_id(new_key) + .send() + .await + { + Ok(output) => output, + Err(e) => { + eprintln!("Encryption failure: {}", e); + process::exit(1); + } + }; + + // Did we get an encrypted blob? + let blob = resp.ciphertext_blob.expect("Could not get encrypted text"); + let bytes = blob.as_ref(); + + let s = base64::encode(&bytes); + let o = &output_file; + + let mut ofile = File::create(o).expect("unable to create file"); + ofile.write_all(s.as_bytes()).expect("unable to write"); + + if verbose { + println!("Wrote the following to {}:", output_file); + println!("{}", s); + } else { + println!("Wrote base64-encoded output to {}", output_file); + } +}