Giter Club home page Giter Club logo

aws-encryption-sdk-cli's Introduction

aws-encryption-sdk-cli

Latest Version

Supported Python Versions

Code style: black

Documentation Status

tests

static analysis

This command line tool can be used to encrypt and decrypt files and directories using the AWS Encryption SDK.

The latest full documentation can be found at Read the Docs.

Find us on GitHub.

Security issue notifications

See Support Policy for details on the current support status of all major versions of this library.

Getting Started

Required Prerequisites

  • Python 3.8+
  • aws-encryption-sdk >= 3.1.0

Installation

Note

If you have not already installed cryptography, you might need to install additional prerequisites as detailed in the cryptography installation guide for your operating system.

As a system package:

$ pip install aws-encryption-sdk-cli

Using a virtual environment:

Installation using a python virtual environment is recommended to avoid conflicts between system packages and user installed packages.

For the latest information on Python virtual environments, refer to the Python.org Virtual Environment Documentation

MacOS/Unix:

$ cd my_project
$ python3 -m venv env
$ source env/bin/activate
$ python -m pip install aws-encryption-sdk-cli

Windows (PowerShell):

> cd my_project
> python3 -m venv env
> .\env\Scripts\Activate.ps1
(env) > pip install aws-encryption-sdk-cli

Usage

Input and Output

For the most part, the behavior of aws-encryption-cli in handling files is based on that of GNU CLIs such as cp. A qualifier to this is that when encrypting a file, if a directory is provided as the destination, rather than creating the source filename in the destination directory, a suffix is appended to the destination filename. By default the suffix is .encrypted when encrypting and .decrypted when decrypting, but a custom suffix can be provided by the caller if desired.

If a destination file already exists, the contents will be overwritten.

Allowed input/output pairings +

output

------------+----------+---------------+

stdout | file | directory

input | stdin

single file

pattern match

directory

Y | Y |

------------+----------+---------------+

Y | Y | Y

------------+----------+---------------+
         | Y
------------+----------+---------------+
         | Y

If the source includes a directory and the --recursive flag is set, the entire tree of the source directory is replicated in the target directory.

Parameter Values

Some arguments accept additional parameter values. These values must be provided in the form of key=value as demonstrated below.

--encryption-context key1=value1 key2=value2 "key 3=value with spaces"
--master-keys provider=aws-kms key=$KEY_ID_1 key=$KEY_ID_2
--caching capacity=3 max_age=80.0

Encryption Context

Encrypt

The encryption context is an optional, but recommended, set of key-value pairs that contain arbitrary nonsecret data. The encryption context can contain any data you choose, but it typically consists of data that is useful in logging and tracking, such as data about the file type, purpose, or ownership.

Parameters may be provided using Parameter Values.

--encryption-context key1=value1 key2=value2 "key 3=value with spaces"
Decrypt

If an encryption context is provided on decrypt, it is instead used to require that the message being decrypted was encrypted using an encryption context that matches the specified requirements.

If key=value elements are provided, the decryption will only continue if the encryption context found in the encrypted message contains matching pairs.

--encryption-context required_key=required_value classification=secret

If bare key elements are provided, the decryption will continue if those keys are found, regardless of the values. key and key=value elements can be mixed.

--encryption-context required_key classification=secret

Warning

If encryption context requirements are not satisfied by the ciphertext message, the message will not be decrypted. One side effect of this is that if you chose to write the plaintext output to a file and that file already exists, it will be deleted when we stop the decryption.

Output Metadata

In addition to the actual output of the operation, there is metadata about the operation that can be useful. This metadata includes some information about the operation as well as the complete header data from the ciphertext message.

The metadata for each operation is written to the specified file as a single line containing formatted JSON, so if a single command performs multiple file operations, a separate line will be written for each operation. There are three operating modes:

  • --metadata-output FILE : Writes the metadata output to FILE (can be - for stdout as long as main output is not stdout). Default behavior is to append the metadata entry to the end of FILE.
  • --overwrite-metadata : Force overwriting the contents of FILE with the new metadata.
  • -S/--suppress-metadata : Output metadata is suppressed.
Metadata Contents

The metadata JSON contains the following fields:

  • "mode" : "encrypt"/"decrypt"/"decrypt-unsigned"
  • "input" : Full path to input file (or "<stdin>" if stdin)
  • "output" : Full path to output file (or "<stdout>" if stdout)
  • "header" : JSON representation of message header data
  • "header_auth" : JSON representation of message header authentication data (only on decrypt)
Skipped Files

If encryption context checks fail when attempting to decrypt a file, the metadata contains additional fields:

  • skipped : true
  • reason : "Missing encryption context key or value"
  • missing_encryption_context_keys : List of required encryption context keys that were missing from the message.
  • missing_encryption_context_pairs : List of required encryption context key-value pairs missing from the message.

Master Key Provider

Information for configuring a master key provider must be provided.

Parameters may be provided using Parameter Values.

These parameters are common to all master key providers:

  • provider (default: aws-encryption-sdk-cli::aws-kms) : Indicator of the master key provider to use.

  • key (on encrypt: at least one required, many allowed; on decrypt: one of key or discovery is required) : Identifier for a wrapping key to be used in the operation. Must be an identifier understood by the specified master key provider. The discovery attribute is only available if you are using an aws-kms provider.

Any additional parameters supplied are collected into lists by parameter name and passed to the master key provider class when it is instantiated. Custom master key providers must accept all arguments as prepared. See Advanced Configuration for more information.

Multiple master keys can be defined using multiple instances of the key argument.

Multiple master key providers can be defined using multiple --wrapping-keys groups.

If multiple master key providers are defined, the first one is treated as the primary.

If multiple master keys are defined in the primary master key provider, the first one is treated as the primary. The primary master key is used to generate the data key.

The following logic is used to construct all master key providers. We use StrictAwsKmsMasterKeyProvider as an example.

# With parameters:
--wrapping-keys provider=aws-kms key=$KEY_1 key=$KEY_2

# KMSMasterKeyProvider is called as:
key_provider = StrictAwsKmsMasterKeyProvider(key_ids=[$KEY_1, $KEY_2])
# Single KMS CMK
--wrapping-keys provider=aws-kms key=$KEY_ARN_1

# Two KMS CMKs
--wrapping-keys provider=aws-kms key=$KEY_ARN_1 key=$KEY_ARN_2

# KMS Alias by name in default region
--wrapping-keys provider=aws-kms key=$ALIAS_NAME

# KMS Alias by name in two specific regions
--wrapping-keys provider=aws-kms key=$ALIAS_NAME region=us-west-2
--wrapping-keys provider=aws-kms key=$ALIAS_NAME region=eu-central-1
AWS KMS

If you want to use the aws-kms master key provider, you can either specify that as the provider or simply not specify a provider and allow the default value to be used.

There are some configuration options which are unique to the aws-kms master key provider:

  • profile : Providing this configuration value will use the specified named profile credentials.
  • discovery (default: false; one of key or discovery with a value of true is required) : Indicates whether this provider should be in "discovery" mode. If true (enabled), the AWS Encryption CLI will attempt to decrypt ciphertexts encrypted with any AWS KMS CMK. If false (disabled), the AWS Encryption CLI will only attempt to decrypt ciphertexts encrypted with the key ARNs specified in the key attribute. Any key specified in the key attribute that is a KMS CMK Identier other than a key ARN will not be used for decryption.
  • discovery-account (optional; available only when discovery=true and discovery-partition is also provided) : If discovery is enabled, limits decryption to AWS KMS CMKs in the specified accounts.
  • discovery-partition (optional; available only when discovery=true and discovery-account is also provided) : If discovery is enabled, limits decryption to AWS KMS CMKs in the specified partition, e.g. "aws" or "aws-gov".
  • region : This allows you to specify the target region.

The logic for determining which region to use is shown in the pseudocode below:

if key ID is an ARN:
   use region identified in ARN
else:
   if region is specified:
      use region
   else if profile is specified and profile has a defined region:
      use region defined in profile
   else:
      use system default region
Advanced Configuration

If you want to use a different master key provider, that provider must register a setuptools entry point. You can find an example of registering this entry point in the setup.py for this package.

When a provider name is specifed in a call to aws-encryption-cli, the appropriate entry point for that name is used.

Handling Multiple Entry Points

If multiple entry points are registered for a given name, you will need to specify the package that registered the entry point you want to use.

In order to specify the package name, use the format: PACKAGE_NAME::ENTRY_POINT.

  • provider=aws-kms
  • provider=aws-encryption-sdk-cli::aws-kms

If you supply only an entry point name and there is only one entry point registered for that name, that entry point will be used.

If you supply only an entry point name and there is more than one entry point registered for that name, an error will be raised showing you all of the packages that have an entry point registered for that name.

If you supply both a package and an entry point name, that exact entry point will be used. If it is not accessible, an error will be raised showing you all of the packages that have an entry point registered for that name.

External Master Key Providers

The entry point name use must not contain the string ::. This is used as a namespace separator as descibed in Handling Multiple Entry Points.

When called, these entry points must return an instance of a master key provider. They must accept the parameters prepared by the CLI as described in Master Key Provider.

These entry points must be registered in the aws_encryption_sdk_cli.master_key_providers group.

If the entry point raises a aws_encryption_sdk_cli.exceptions.BadUserArgumentError, the CLI will present the raised error message to the user to indicate bad user input.

Commitment Policy

The commitment policy controls which algorithms can be used in encryption and decryption. Versions 2.0.x and later of the AWS Encryption CLI use a default commitment policy of require-encrypt-require-decrypt, which ensures that only algorithms which provide key commitment can be used on both encryption and decryption. If you want to use a different commitment policy, you can do so with the --commitment-policy parameter.

For more details, see the Commitment Policy documentation.

# Use a commitment policy that requires an algorithm which provides key commitment
# on both encryption and decryption
--commitment-policy require-encrypt-require-decrypt

Data Key Caching

Data key caching is optional, but if used then the parameters noted as required must be provided. For detailed information about using data key caching with the AWS Encryption SDK, see the data key caching documentation.

Parameters may be provided using Parameter Values.

Allowed parameters:

  • capacity (required) : Number of entries that the cache will hold.
  • max_age (required) : Determines how long each entry can remain in the cache, beginning when it was added.
  • max_messages_encrypted : Determines how long each entry can remain in the cache, beginning when it was added.
  • max_bytes_encrypted : Specifies the maximum number of bytes that a cached data key can encrypt.

Logging and Verbosity

The -v argument allows you to tune the verbosity of the built-in logging to your desired level. In short, the more -v arguments you supply, the more verbose the output gets.

  • unset : aws-encryption-cli logs all warnings, all dependencies only log critical messages
  • -v : aws-encryption-cli performs moderate logging, all dependencies only log critical messages
  • -vv : aws-encryption-cli performs detailed logging, all dependencies only log critical messages
  • -vvv : aws-encryption-cli performs detailed logging, all dependencies perform moderate logging
  • -vvvv : aws-encryption-cli performs detailed logging, all dependencies perform detailed logging

Configuration Files

As with any CLI where the configuration can get rather complex, you might want to use a configuration file to define some or all of your desired behavior.

Configuration files are supported using Python's native argparse file support, which allows you to write configuration files exactly as you would enter arguments in the shell. Configuration file references passed to aws-encryption-cli are identified by the @ prefix and the contents are expanded as if you had included them in line. Configuration files can have any name you desire.

Note

In PowerShell, you will need to escape the @ symbol so that it is sent to aws-encryption-cli rather than interpreted by PowerShell.

For example, if I wanted to use a common master key configuration for all of my calls, I could create a file master-key.conf with contents detailing my master key configuration.

master-key.conf

--master-key key=A_KEY key=ANOTHER_KEY

Then, when calling aws-encryption-cli, I can specify the rest of my arguments and reference my new configuration file, and aws-encryption-cli will use the composite configuration.

aws-encryption-cli -e -i $INPUT_FILE -o $OUTPUT_FILE @master-key.conf

To extend the example, if I wanted a common caching configuration for all of my calls, I could similarly place my caching configuration in a configuration file caching.conf in this example and include both files in my call.

caching.conf

--caching capacity=10 max_age=60.0 max_messages_encrypted=15
aws-encryption-cli -e -i $INPUT_FILE -o $OUTPUT_FILE @master-key.conf @caching.conf

Configuration files can be referenced anywhere in aws-encryption-cli parameters.

aws-encryption-cli -e -i $INPUT_DIR -o $OUTPUT_DIR @master-key.conf @caching.conf --recursive

Configuration files can have many lines, include comments using #. Escape characters are platform-specific: \ on Linux and MacOS and ` on Windows. Configuration files may also include references to other configuration files.

my-encrypt.config

--encrypt
@master-key.conf # Use existing master key config
@caching.conf
# Always recurse, but require interactive overwrite.
--recursive
--interactive
aws-encryption-cli @my-encrypt -i $INPUT -o $OUTPUT

Encoding

By default, aws-encryption-cli will always output raw binary data and expect raw binary data as input. However, there are some cases where you might not want this to be the case.

Sometimes this might be for convenience:

  • Accepting ciphertext through stdin from a human.
  • Presenting ciphertext through stdout to a human.

Sometimes it might be out of necessity:

  • Saving ciphertext output to a shell variable.

    • Most shells apply a system encoding to any data stored in a variable. As a result, this often results in corrupted data if binary data is stored without additional encoding.
  • Piping ciphertext in PowerShell.

    • Similar to the above, all data passed through a PowerShell pipe is encoded using the system encoding.

In order to address these scenarios, we provide two optional arguments:

  • --decode : Base64-decode input before processing.
  • --encode : Base64-encode output after processing.

These can be used independently or together, on any valid input or output.

Be aware, however, that if you target multiple files either through a path expansion or by targeting a directory, the requested decoding/encoding will be applied to all files.

aws-encryption-sdk-cli's People

Contributors

ajw-aws avatar alex-chew avatar bdonlan avatar dependabot[bot] avatar farleyb-amazon avatar github-actions[bot] avatar josecorella avatar lavaleri avatar lizroth avatar mattsb42-aws avatar phasenoisepon avatar robin-aws avatar salusasecondus avatar scottarc avatar seebees avatar shubhamchaturvedi7 avatar texastony avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aws-encryption-sdk-cli's Issues

extra logging results when logging is enabled

(py36) 186590df9307:aws-encryption-sdk-cli bullocm$ aws-crypto -e @test/integration/integration_tests.conf -i tox.ini -o ~/tmp
(py36) 186590df9307:aws-encryption-sdk-cli bullocm$ aws-crypto -e @test/integration/integration_tests.conf -i tox.ini -o ~/tmp -v
INFO:aws_encryption_sdk_cli:Creating dir if it does not exist /Users/bullocm/tmp
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
(py36) 186590df9307:aws-encryption-sdk-cli bullocm$ aws-crypto -e @test/integration/integration_tests.conf -i tox.ini -o ~/tmp -vv
DEBUG:aws_encryption_sdk_cli:Encryption mode: encrypt
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Encryption source: tox.ini
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Encryption destination: /Users/bullocm/tmp
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Master key provider configuration: [{'key': ['arn:aws:kms:us-east-1:248168362296:key/ce78d3b3-f800-4785-a3b9-63e30bb4b183'], 'provider': 'aws-kms'}]
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Suffix requested: None
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Loading provider: aws-kms
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Requested source: tox.ini
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Expanded source: <generator object _iglob at 0x10aef3d00>
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Duplicating filename tox.ini into /Users/bullocm/tmp
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Overwriting existing target file because no action was specified otherwise: /Users/bullocm/tmp/tox.ini.encrypted
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
INFO:aws_encryption_sdk_cli:Creating dir if it does not exist /Users/bullocm/tmp
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True

PowerShell: Pipe/stream encoding issues with ciphertext to stdout or from stdin

In a PowerShell console, the following command patterns generate 'Unsupported type 63 discovered in data stream' errors:

  • 'hello' | aws-crypto -e -m key=$cmkArn -i - -o - | aws-crypto -d -i - -o -

  • $encrypted = 'hello' | aws-crypto -e -m key=$cmkArn -i - -o -
    $encrypted | aws-crypto -d -i - -o -

Converting the intermediate output to base 64 doesn't help. Neither does changing the $OutputEncoding automatic variable to UTF-8.

enable multithreaded processing

Problem

Especially when dealing with large numbers of files, it would be nice to be able to process them in parallel.

Solution

Parallel threads argument that will process t files in parallel.

ex:

aws-crypto @encrypt.conf -i /var/logs -o /mnt/backup/logs --suffix encypted-archive --threads 10

Trap known unsupported type error

If we can't fix #16 or any other predictable error, we should trap the exception and return a more graceful error, maybe without the stack trace.

Something like: "Piping encrypted output to a decrypt command is not supported in all shells."

Recursive parameter has no effect on file name patterns

When the --input parameter value is a file name pattern, the --recursive parameter seems to have no effect. This might not be expected in Linux, but it's expected in Windows; both cmd and PowerShell.

(py) PS C:\ps-test> dir .\TestCLI\*.xml -Recurse


    Directory: C:\ps-test\TestCLI\TestCLI2\TestCLI3


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        10/4/2017   3:43 PM             60 Goodbye.xml


(py) PS C:\ps-test> aws-crypto -e -m key=$keyid -i .\TestCLI\*.xml -r -o .\TestDirEnc
(py) PS C:\ps-test> dir .\TestDirEnc
(py) PS C:\ps-test> 

(py) PS C:\ps-test> dir .\TestCLI\Goodbye.* -Recurse


    Directory: C:\ps-test\TestCLI\TestCLI2\TestCLI3


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        10/4/2017   3:43 PM             60 Goodbye.xml


    Directory: C:\ps-test\TestCLI\TestCLI2


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        10/4/2017   3:34 PM             13 Goodbye.txt


(py) PS C:\ps-test> aws-crypto -e -m key=$keyid -i .\TestCLI\Goodbye.* --recursive -o .\TestDirEnc
(py) PS C:\ps-test> dir .\TestDirEnc
(py) PS C:\ps-test>

add flake8-print once bug on their end is fixed

Triggered by #19, I want to add automated checking to make sure that errant print statements don't end up in code again. Towards that end, JBKahn/flake8-print looks like a good solution to this, despite its advertised "Alpha" staging.

Unfortunately, there is a bug with flake8-print that keeps it from working with flake8>=3.4.0 JBKahn/flake8-print#23.

Once that bug is fixed (looks like it should be soon), add this as a dependency in the flake8 tox testenv.

reST :ref: tags are not supported in PyPI rendering

Problem

The README currently uses :ref: tags for in-page linking. Unfortunately, these are not accepted by PyPI and our README is used for the long description for PyPI.

Solutions

There are a few possible solutions to this:

  1. Remove in-page links: I don't really like this, as it weakens the documentation navigation story.
  2. Break monolithic README into multiple files: This could make nagivation easier and lets us sidestep this issue.
  3. Keep monolithic README but switch to standard reST links http://www.sphinx-doc.org/en/stable/markup/inline.html#ref-role

investigate options for encryption context/header output

Problem

It would be nice to be able to provide information from the header, specifically the encryption context, in some form of output.

Options

  • Optional metadata output file
  • Optional metadata output to stdout
    • only works if output is not stdout

In PowerShell, the encryption context key cannot be quoted

This isn't a bug, just reporting the repro for the record.

Cmd.exe: Key can be quoted

(py) C:\ps-test>aws-crypto -e -m key=%keyid% -i TestDIR\Hello.txt -o TestEnc -c 'Dept_Code'='IT'
(py) C:\ps-test>

ZSH: Key can be quoted

(ENV)[ec2-user@ip-172-31-39-147 test]$ ls testenc
(ENV)[ec2-user@ip-172-31-39-147 test]$ aws-crypto -e -m key=$keyid -i testdir/hello.txt -o testenc -c 'Dept_Code'='IT'
(ENV)[ec2-user@ip-172-31-39-147 test]$

PowerShell: Single-quoted key & value fails:

(py) PS C:\ps-test> aws-crypto -e -m key=$keyid -i .\testdir\Hello.txt -o testenc -c 'Dept_Code'='Test'
usage: aws-crypto [-h] (--version | [-e | -d]
                  [-m MASTER_KEYS [MASTER_KEYS ...]]
                  [--caching CACHING [CACHING ...]] -i INPUT -o OUTPUT
                  [-c ENCRYPTION_CONTEXT [ENCRYPTION_CONTEXT ...]]
                  [--algorithm {AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384,AES_128_GCM_IV12_TAG16,AES_128_GCM_IV12_TA
G16_HKDF_SHA256_ECDSA_P256,AES_192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384,AES_256_GCM_IV12_TAG16,AES_192_GCM_IV12_TAG16_H
KDF_SHA256,AES_256_GCM_IV12_TAG16_HKDF_SHA256,AES_128_GCM_IV12_TAG16_HKDF_SHA256,AES_192_GCM_IV12_TAG16}]
                  [--frame-length FRAME_LENGTH] [--max-length MAX_LENGTH]
                  [--suffix SUFFIX] [--interactive] [--no-overwrite] [-r] [-v]
                  [-q]
aws-crypto: error: Argument parameter must follow the format "key=value"

PowerShell: Double-quoted key, single-quoted value fails

(py) PS C:\ps-test> aws-crypto -e -m key=$keyid -i .\testdir\Hello.txt -o testenc -c "Dept_Code"='Test'
usage: aws-crypto [-h] (--version | [-e | -d]
                  [-m MASTER_KEYS [MASTER_KEYS ...]]
                  [--caching CACHING [CACHING ...]] -i INPUT -o OUTPUT
                  [-c ENCRYPTION_CONTEXT [ENCRYPTION_CONTEXT ...]]
                  [--algorithm {AES_128_GCM_IV12_TAG16_HKDF_SHA256,AES_192_GCM_IV12_TAG16_HKDF_SHA256,AES_128_GCM_IV12_T
AG16_HKDF_SHA256_ECDSA_P256,AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384,AES_128_GCM_IV12_TAG16,AES_256_GCM_IV12_TAG16_
HKDF_SHA256,AES_192_GCM_IV12_TAG16,AES_256_GCM_IV12_TAG16,AES_192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384}]
                  [--frame-length FRAME_LENGTH] [--max-length MAX_LENGTH]
                  [--suffix SUFFIX] [--interactive] [--no-overwrite] [-r] [-v]
                  [-q]
aws-crypto: error: Argument parameter must follow the format "key=value"

PowerShell: Single-quoted key, unquoted value fails

(py) PS C:\ps-test> aws-crypto -e -m key=$keyid -i .\testdir\Hello.txt -o testenc -c 'Dept_Code'=Test
usage: aws-crypto [-h] (--version | [-e | -d]
                  [-m MASTER_KEYS [MASTER_KEYS ...]]
                  [--caching CACHING [CACHING ...]] -i INPUT -o OUTPUT
                  [-c ENCRYPTION_CONTEXT [ENCRYPTION_CONTEXT ...]]
                  [--algorithm {AES_256_GCM_IV12_TAG16_HKDF_SHA256,AES_256_GCM_IV12_TAG16,AES_256_GCM_IV12_TAG16_HKDF_SH
A384_ECDSA_P384,AES_192_GCM_IV12_TAG16_HKDF_SHA256,AES_128_GCM_IV12_TAG16_HKDF_SHA256_ECDSA_P256,AES_192_GCM_IV12_TAG16,
AES_192_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384,AES_128_GCM_IV12_TAG16,AES_128_GCM_IV12_TAG16_HKDF_SHA256}]
                  [--frame-length FRAME_LENGTH] [--max-length MAX_LENGTH]
                  [--suffix SUFFIX] [--interactive] [--no-overwrite] [-r] [-v]
                  [-q]
aws-crypto: error: Argument parameter must follow the format "key=value"

PowerShell: Unquoted key, quoted value works

(py) PS C:\ps-test> aws-crypto -e -m key=$keyid -i .\testdir\Hello.txt -o testenc -c Dept_Code='Test'
(py) PS C:\ps-test> 

PowerShell: Unquoted key, unquoted value works

(py) PS C:\ps-test> aws-crypto -e -m key=$keyid -i .\testdir\Hello.txt -o testenc -c Dept_Code=Test
(py) PS C:\ps-test> 

change verbosity behavior

The verbosity flag controls how the logger is configured.

Current verbosity behavior is:

  • nothing : no logging
  • -v : WARN (global)
  • -vv : INFO (global)
  • -vvv : DEBUG (global)

After going through various scenarios and using the logging output, I decided a more useful approach would be:

  • nothing : WARNING (aws_encryption_sdk_cli), CRITICAL (root)
  • -v : INFO (aws_encryption_sdk_cli), CRITICAL (root)
  • -vv : DEBUG (aws_encryption_sdk_cli), CRITICAL (root)
  • -vvv : DEBUG (aws_encryption_sdk_cli), INFO (root)
  • -vvvv : DEBUG (aws_encryption_sdk_cli), DEBUG (root)

WARN is always on for safety reasons: we want users to always be aware of WARN events.

input and output appear as optional in the argparge help text

usage: aws-crypto [-h] (--version | [-e | -d]
[-m MASTER_KEYS [MASTER_KEYS ...]]
[-C CACHING [CACHING ...]] [-i INPUT] [-o OUTPUT]

IIRC, this is a left-over from before I remembered that argparse has special built-in handling for version outputs. This should be a simple matter of marking these as required and removing the special handling to enforce them outside of the argparse configuration.

add built-in base64 en/de-coding

Because of all the issues around system shell encoding, we need to support base64 en/de-coding of out/in-put natively within the CLI.

Outstanding question: what do we actually want this control interface to look like? Specifically, do we want to allow turning the encoding on/off independently? I think we need to in order to support reading in unencoded data.

CLI is overwriting files without warning

I thought overwriting, which is the default, didn't warn by design, but if the --quiet is designed to suppress these warnings, they must have been designed. :)

No warning for single files:
(py) PS C:\ps-test> aws-crypto -e -m key=$keyid -i .\TestDIR\Hello.txt -o .\TestDirEnc
(py) PS C:\ps-test> aws-crypto -e -m key=$keyid -i .\TestDIR\Hello.txt -o .\TestDirEnc
(py) PS C:\ps-test> aws-crypto -e -m key=$keyid -i .\TestDIR\Hello.txt -o .\TestDirEnc
(py) PS C:\ps-test>

PowerShell

(py) PS C:\ps-test> 1..3 | foreach { aws-crypto -e -m key=$keyid -i .\TestDIR\Hello.txt -o .\TestDirEnc }
(py) PS C:\ps-test>

No warning for multiple files in a directory:
(py) PS C:\ps-test> dir .\TestDirEnc

Directory: C:\ps-test\TestDirEnc

Mode LastWriteTime Length Name


-a---- 10/4/2017 1:34 PM 2713 cool-new-thing.py.encrypted
-a---- 10/4/2017 1:34 PM 620 Employees.csv.encrypted
-a---- 10/4/2017 1:34 PM 585 Hello.txt.encrypted

(py) PS C:\ps-test> 1..3 | foreach { aws-crypto -e -m key=$keyid -i .\TestDIR* -o .\TestDirEnc }
(py) PS C:\ps-test>

add beta flag to readme

Since we will be launching this as a beta tool, the following notice needs to be added to the top of the readme:

Because this is a beta tool, your use is subject to the AWS Beta Service Participation Terms in Section 1.10 of the AWS Service Terms.

Inaccurate mypy return annotation (kms post-processing)

def kms_master_key_provider(kwargs):
# type: (Dict[str, List[Union[Text, str]]]) -> Dict[str, List[Union[Text, str]]]

Return type should be Dict[str, Union[List[Union[Text, str]], botocore.session.Session]].

I'm guessing the mypy parser didn't pick this up because follow_imports is turned off (because boto3/botocore/aws-encryption-sdk don't have typing annotations). I should verify that too; make sure it's not a bug in the parser.

Display versions on separate lines

Totally a nit, but --version returns versions of the CLI and SDK. It would be more readable on two lines. It might avoid some errors when reporting versions.

Please change:
(py) PS C:\ps-test> aws-crypto --version
aws-encryption-sdk-cli/1.0.2 aws-encryption-sdk/1.3.2

To:
(py) PS C:\ps-test> aws-crypto --version
aws-encryption-sdk-cli/1.0.2
aws-encryption-sdk/1.3.2

Add support for required encryption context subset on decrypt

Problem

Due to the nature of CLI interactions, it is relatively difficult for users to inspect the encryption context after decrypting a ciphertext message. Because of this, the CLI needs a feature that has not yet been added to the SDK standard: automatic validation of encryption context on decrypt.

Solution

On decrypt, a user can specify an encryption context in the same manner as on encrypt. Once we load the input, before starting to decrypt a message, we will first read the header and verify that the specified key-value pairs are present in the header encryption context. If any of these pairs are missing, the decryption will stop and no plaintext will be written to the output.

--decrypt --encryption-context key=value test=string

If a parameter provided to the encryption context argument does not contain a =, then we will only check that a key with that name is present in the header encryption context.

--decrypt --encryption-context key=value special

move all possible unit tests from patching to using tmpdir

Problem

Some of the unit test suites rely on patching file-related operations to simulate behavior in specific environments. This fragile, high maintenance, and prone to error.

Solution

Once #48 and #60 are merged, do a pass over the unit tests and make sure that where possible we use tmpdir to actually create the test environment we want rather than patching things to make it look like we are in that environment.

storing ciphertext in variable OSX/Linux results in failed decryption

From an initial overview, it appears that this is caused by encoding issues. Apparently PS is not the only one that wraps encoding.

(py36) 186590df9307:aws-encryption-sdk-cli bullocm$ aa=`echo 'asdf' | aws-crypto -e -i - -o - @test/integration/integration_tests.conf`
(py36) 186590df9307:aws-encryption-sdk-cli bullocm$ echo $aa
?x+??Z??6?nypj(?_aws-crypto-public-keyDAnCD9EN1ToOTTS//IzbmxAC8H99wcJzPWELU2WJjXhIOKnG7fbRpugw/IcGMdLWCFA==aws-kmsKarn:aws:kms:us-east-1:248168362296:key/ce78d3b3-f800-4785-a3b9-63e30bb4b183?x???????+Sn?P?n0 `?He.0?H??0| *?H??
        w??z?&???@??;?????dڃ?^??`<i??lZ?Q?5?????=?!??=?6?ow????<???
                                                                   $?m?`lz??=???W??????-?J??+????L)?Z??|g0e0?/#t2g???l7jfm=F??Z)??(?C?va??-??cQw?-,EKI?1?
                                                                                                                                                         =?S??_U?μP3P????ұʹ??0̤??-?????ujbZ?
(py36) 186590df9307:aws-encryption-sdk-cli bullocm$ echo $aa | aws-crypto -d -i - -o - @test/integration/integration_tests.conf
ERROR:aws_encryption_sdk.streaming_client:Error on closing
Traceback (most recent call last):
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/internal/formatting/encryption_context.py", line 132, in read_string
    codecs.decode(source[offset:end], aws_encryption_sdk.internal.defaults.ENCODING),
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/bin/../lib/python3.6/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa7 in position 172: invalid start byte

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk_cli/internal/io_handling.py", line 90, in _single_io_write
    for chunk in handler:
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 262, in next
    return self.readline()
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 240, in readline
    line = self.read(self.line_length)
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 201, in read
    self._prep_message()
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 687, in _prep_message
    self._header, self.header_auth = self._read_header()
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 700, in _read_header
    header, raw_header = aws_encryption_sdk.internal.formatting.deserialize.deserialize_header(self.source_stream)
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/internal/formatting/deserialize.py", line 103, in deserialize_header
    tee_stream.read(ser_encryption_context_length)
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/internal/formatting/encryption_context.py", line 172, in deserialize_encryption_context
    length=key_size
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/internal/formatting/encryption_context.py", line 136, in read_string
    raise SerializationError('Bad format of serialized context.')
aws_encryption_sdk.exceptions.SerializationError: Bad format of serialized context.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 184, in __exit__
    self.close()
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 890, in close
    raise SerializationError('Footer not read')
aws_encryption_sdk.exceptions.SerializationError: Footer not read
Traceback (most recent call last):
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/internal/formatting/encryption_context.py", line 132, in read_string
    codecs.decode(source[offset:end], aws_encryption_sdk.internal.defaults.ENCODING),
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/bin/../lib/python3.6/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa7 in position 172: invalid start byte

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/bin/aws-crypto", line 11, in <module>
    sys.exit(cli())
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk_cli/__init__.py", line 177, in cli
    no_overwrite=args.no_overwrite
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk_cli/__init__.py", line 78, in process_cli_request
    no_overwrite=no_overwrite
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk_cli/internal/io_handling.py", line 118, in process_single_operation
    destination_writer=destination_writer
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk_cli/internal/io_handling.py", line 90, in _single_io_write
    for chunk in handler:
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 262, in next
    return self.readline()
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 240, in readline
    line = self.read(self.line_length)
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 201, in read
    self._prep_message()
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 687, in _prep_message
    self._header, self.header_auth = self._read_header()
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/streaming_client.py", line 700, in _read_header
    header, raw_header = aws_encryption_sdk.internal.formatting.deserialize.deserialize_header(self.source_stream)
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/internal/formatting/deserialize.py", line 103, in deserialize_header
    tee_stream.read(ser_encryption_context_length)
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/internal/formatting/encryption_context.py", line 172, in deserialize_encryption_context
    length=key_size
  File "/Users/bullocm/git/aws-encryption-sdk-cli/.tox/py36/lib/python3.6/site-packages/aws_encryption_sdk/internal/formatting/encryption_context.py", line 136, in read_string
    raise SerializationError('Bad format of serialized context.')
aws_encryption_sdk.exceptions.SerializationError: Bad format of serialized context.

add pypi-parker entries

Problem

Given my personal experience with the AWS CLI, it occurred to me that we should add a couple more entries based on the CLI name rather than the package name.

Solution

Add names:

aws-crypto
awscrypto

convert master key provider discovery to use setuptools entry points

Rather than passing in a callable path for custom MKPs and using NOP post processing, I think what would be better is to implement a discoverable plugin structure that will guide implementers to define an entry point function that returns a MKP callable and optional post-processing callable.

https://packaging.python.org/guides/creating-and-discovering-plugins/
http://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins

consider options to refine error messaging for non-existant targets

Problem

Currently, if you specify an output target that does not exist, and your input does not require a directory output, we assume that you meant to target a file. This can result in an unexpected error message if the parent of your target also does not exist:

"If destination is a file, the immediate parent directory must already exist."

Since we use the same argument for output target regardless of what you specify, it can sometimes be hard to determine intent from a raw string. I'm not sure what the best solution to this is, but it's worth giving some thought.

add pytest markers and refactor testenvs to separate unit/integ

Problem

Currently, all the tests run as a big batch, with the integration tests skipped unless a specific environment variable is found.

This is a bit clunky and makes it difficult to tell in Travis whether integration tests were run or skipped.

Solution

  1. Add pytest markers to all integration tests.
  2. Update tox to have separate testenvs for unit/integ tests.
  3. Update Travis CI configuration to run the new tox testenvs.

allow user-defined output filename suffix

Default behavior when target is a directory is that the source filename will be replicated in the target directory, with the addition of a .encrypted suffix if encrypting and a .decrypted suffix if decrypting.

Add the ability for the user to specify a custom suffix to use instead.

No error or log entry when input is missing

When the input file(s) do not exist (typo in input file name or directory), the CLI returns the command prompt without error or warning. I agree that the command should not fail, but there is no output (as expected), error, warning, or any other hint to the user about what might have gone wrong. This makes debugging very difficult.

Even with -vvvv, there are no 'file not found' or 'directory not found' messages to help the user figure out why there is no output.

Instead, you get messages like this where the True suggests that the file was found.

DEBUG:aws_encryption_sdk_cli:Requested source: .\TestDIR\NotThere
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True 

This is PowerShell, but I'm getting the same results on all platforms.

(py) PS C:\ps-test> dir .\TestDir\NotThere
dir : Cannot find path 'C:\ps-test\TestDir\NotThere' because it does not exist.
At line:1 char:1
+ dir .\TestDir\NotThere
+ ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\ps-test\TestDir\NotThere:String) [Get-ChildItem], ItemNotFoundExcept
   ion
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
    

(py) PS C:\ps-test> aws-crypto -e -m key=$keyid -i .\TestDIR\NotThere -o TestEnc -vvvv
DEBUG:aws_encryption_sdk_cli:Encryption mode: encrypt
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Encryption source: .\TestDIR\NotThere
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Encryption destination: TestEnc
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Master key provider configuration: [{'key': ['fd4e25a6-1a1c-41a9-86a4-14d6814c0ea6'], 'prov
ider': 'aws-kms'}]
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Suffix requested: None
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Loading provider: aws-kms
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable profile from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable config_file from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable credentials_file from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable profile from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable region from config file with value 'us-west-2'.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Setting config variable for region to 'us-west-2'
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable profile from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable data_path from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable region from instance vars with value 'us-west-2'.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable profile from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable ca_bundle from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable profile from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable api_versions from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable profile from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable credentials_file from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable config_file from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable profile from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable metadata_service_timeout from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable profile from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable metadata_service_num_attempts from defaults.
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable profile from defaults.
botocore.credentials
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.credentials:Looking for credentials via: env
botocore.credentials
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.credentials:Looking for credentials via: assume-role
botocore.credentials
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.credentials:Looking for credentials via: shared-credentials-file
botocore.credentials
('aws_encryption_sdk_cli',)
False
INFO:botocore.credentials:Found credentials in shared credentials file: ~/.aws/credentials
botocore.loaders
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.loaders:Loading JSON file: c:\py\lib\site-packages\botocore\data\endpoints.json
botocore.session
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.session:Loading variable profile from defaults.
botocore.loaders
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.loaders:Loading JSON file: c:\py\lib\site-packages\botocore\data\kms\2014-11-01\service-2.json
botocore.hooks
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.hooks:Event creating-client-class.kms: calling handler <function add_generate_presigned_url at 0x04608F60
>
botocore.args
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.args:The s3 config key is not a dictionary type, ignoring its value of: None
botocore.endpoint
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.endpoint:Setting kms timeout as (60, 60)
botocore.loaders
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.loaders:Loading JSON file: c:\py\lib\site-packages\botocore\data\_retry.json
botocore.client
('aws_encryption_sdk_cli',)
False
DEBUG:botocore.client:Registering retry handlers for service: kms
DEBUG:aws_encryption_sdk_cli:Requested source: .\TestDIR\NotThere
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
DEBUG:aws_encryption_sdk_cli:Expanded source: <generator object _iglob at 0x047A55D0>
aws_encryption_sdk_cli
('aws_encryption_sdk_cli',)
True
(py) PS C:\ps-test>

provide access to logs in the event of failed execution

Problem

Currently, if an unexpected error is encountered and verbose logging is not enabled, the user is presented with a generic message:

Encountered unexpected ERROR_TYPE: increase verbosity to see details

This can be troublesome if the error occurs in the middle of a long-running operation where simply running the same command again might be too expensive. For example, when encrypting the contents of a large directory or if this command is used as part of a large shell script.

Options

Preferred option: Temporary File

Cached Stream

Write DEBUG logs to an in-memory stream that caches the most recent n log records, then print them out in the event of an error unless --quiet.

This would allow efficient use of resources but would serve for a cumbersome UI, as in the event of an unexpected error, the collected records will most likely contain a stacktrace.

Temporary File

Write DEBUG logs to a temporary file. If the command completes successfully, delete the temporary file. If an unexpected error occurs, do not delete the temporary file and report the filename to the user:

Encountered unexpected ERROR_TYPE: Detailed results can be found in TMP_FILE

This would provide a more streamlined UI, but could result in a proliferation of temporary files.

To work around this, perhaps we could use a custom temporary directory and clean up the oldest n files on each run, similar to how pytest.tmpdir works?

omitting master key argument on encrypt results in an undesirable error

(py) PS C:\ps-test> "hello" | aws-crypto -e -i - -o -
Traceback (most recent call last):
File "C:\Users\junebl\AppData\Local\Programs\Python\Python36-32\Lib\runpy.py", line 193, in run_module_as_main
"main", mod_spec)
File "C:\Users\junebl\AppData\Local\Programs\Python\Python36-32\Lib\runpy.py", line 85, in run_code
exec(code, run_globals)
File "C:\py\Scripts\aws-crypto.exe_main
.py", line 9, in
File "c:\py\lib\site-packages\aws_encryption_sdk_cli_init
.py", line 142, in cli
args = parse_args(raw_args)
File "c:\py\lib\site-packages\aws_encryption_sdk_cli\internal\arg_parsing.py", line 292, in parse_args
parsed_args.master_keys = _process_master_key_provider_configs(parsed_args.master_keys, parsed_args.action)
File "c:\py\lib\site-packages\aws_encryption_sdk_cli\internal\arg_parsing.py", line 253, in _process_master_key_provid
er_configs
for pos, key_set in enumerate(all_keys):
TypeError: 'NoneType' object is not iterable

when output is a file, require that parent directory already exists

Problem

Currently, if the output is a file, the entire parent tree is automatically created. This is surprising behavior given that when the output is a directory we require that the directory already exist.

Solution

If the output is a file, require that the immediate parent directory already exist.

conditional directory creation log entry is confusing

Every time a file is processed, we make sure that the immediate parent exists, creating it if it does not. This allows us to easily replicate the internal structure of a source directory into a target directory.

Problem

Currently, when we hit the conditional directory creation function, we log:
Creating dir if it does not exist

This can be confusing if the user did not specify a target directory.

Solution

Change the log record to:
Replicating source child directory structure into destination if it does not already exist

document non-obvious argparse behavior

A user encountered this issue when accidentally mistyping --recursive as -recursive.

Because of argparse's behavior, this was interpreted as equivalent to -r -e -c ursive.

This behavior needs to be documented in the readme, as users of this CLI should not be required to be familiar with argparse's behavior.

keep plaintext data keys in KMS responses out of logs

Problem

At DEBUG level output, botocore currently logs the full response of each API call. In the case of our use, this includes the plaintext of data keys as they are being generated or decrypted by KMS.

By enabling the most verbose level of debug output for this CLI (-vvvv), you do end up with DEBUG level output for botocore.

Options

  1. Work with botocore team to redact KMS responses in botocore.
  2. We are already applying a custom filter to the root handler. We can extend that filter to catch and redact all KMS responses.

globbing + recursive does not behave as expected

Problem

When using globbing to expand an input pattern, if one of the results of the expansion is a directory, the recursive flag is required in order to encrypt the contents of that directory.

However, if some of the expanded input targets are files and they happen to come before the directory entries in the expanded results, they will be processed before the recursive check is made.

This is undesirable because it results in a partially successful operation.

Solution

Run through all source values before starting to process any of them, checking for source and destination directory properties.

quoting in config files is not being handled as expected

Problem

Because the config files are read as raw data, not interpreted by a shell, quoted contents are not being handled correctly.

ex:

  • quotes read as characters
  • quoted strings with whitespace being interpreted as multiple arguments

Possible Solution

I think we should be able to solve this by just using shlex.split rather than str.split in the custom config file handler.

[Linux] If stdout is piped to base64, output results has spaces periodically injected

Attempting to solve the problem of binary encoding when saving data to a shell variable, I tried piping the output through base64. Strangely, this appears to sometimes result in spaces being injected into the output string.

$ a=`echo 'asdf'  | aws-crypto -e -m key=$key region=us-west-2 -i - -o - | base64`

$ echo $a
AYADePACR84EuSH6XplpNHUfscAAXwABABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREFoZUxNdTVn Qm5yUElYVXo2QXdNY2JISUtkSVZtWXFDMFN3V0FNbUpHZUYxZ1FIZGVWMFAxaTJMUTFUeWZCbWQz QT09AAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOnVzLXdlc3QtMjo2NTg5NTY2MDA4MzM6a2V5L2Iz NTM3ZWYxLWQ4ZGMtNDc4MC05ZjVhLTU1Nzc2Y2JiMmY3ZgCnAQEBAHhA84wnXjEJdBbBBylRUFcZ ZK2j7xwh6UyLoL28nQ+0FAAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJ YIZIAWUDBAEuMBEEDN3CelKVH2irf40SdAIBEIA7rFIoQvZBRSbd+C5J+vlbUrXj2H/91KDYtrdh 5afcm13JvjNYvTG3ahvTMect/ztv08rci8V+oqFPIXICAAAAAAwAABAAAAAAAAAAAAAAAAAA74BB y5NkHiI9J5GylULALf////8AAAABAAAAAAAAAAAAAAABAAAABVrJva3u0PPwEP1wm9I9crchG74T bgBnMGUCMQD3QgzpYeWm0dXJpbvS1Swj7A6YpgtxLwChVFXIR6kJVtdOgYhyMxqyiCXzY9e2364C MDKdA3YgM/+79pOdm+YCvFEMBOEzTLfcL2FfO64UuHXN9xHiQWLd8wNoGo0NOSFtLA==

$ echo $a | base64 --decode
?x?G??!?^?i4u??_aws-crypto-public-keyDAheLMu5gbase64: invalid input

$ echo $a | sed 's/ //g' | base64 --decode
?x?G??!?^?i4u??_aws-crypto-public-keyDAheLMu5gBnrPIXUz6AwMcbHIKdIVmYqC0SwWAMmJGeF1gQHdeV0P1i2LQ1TyfBmd3A==aws-kmsKarn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f?x@?'^1	t?)QPW0o0m0hL?`?He.00|	*?H??
              ??zR?h??t?;?R(B?AE&??.I??[R????Ԡض?a?ܛ]ɾ3X?1?j1?-?;o??܋?~??O!r
                                                                           ?A˓d"='???B?-????Zɽ??????p??=r?!ng0e1?B
                                                                                                                  ?a???ɥ???,#???
                                                                                                                                q/?TU?G?	V?N??r3??%?c׶߮02?v 3????????Q
9!m,

$ echo $a | sed 's/ //g' | base64 --decode | aws-crypto -d -i - -o -
asdf

Encryption and decryption procedure do not preserve file permissions

Encryption and decryption procedure does not preserve file permissions of the encrypted/decrypted file on Windows (have not checked on Linux). To be fair it is the default behavior of the Windows explorer when you copy-paste files.

To exemplfiy - A user can encrypt a file and scope down encypted file permissions such that only that user has permissions to read/write the file. When the file is decrypted with aws-crypto the permissions from the encrypted file are not copied over to the decrypted file.

How to reproduce (Windows):

Encrypt file:

aws-crypto -m provider=aws-kms key="arn:aws:kms:us-east-1:123456789123:key/a92bcc71-8a09-434a-b3c9-a482e158f3af" -i "C://temp/pt" -o "C://temp/ct" -e

Using Windows file permissions dialog add yourself as an owner with all permissions and remove all other users

Decrypt file:

aws-crypto -m provider=aws-kms key="arn:aws:kms:us-east-1:123456789123:key/a92bcc71-8a09-434a-b3c9-a482e158f3af" -o "C://temp/pt" -i "C://temp/ct" -d

Inspect Windows file permissions; they won't be preserved

Recommending to preserve file permissions when encrypting/decrypting, or at least adding a file permission preserving flag. Make sure you test this behavior on Linux/Mac too.

encryption context output

Problem

We need some way for the CLI to provide the encryption context as output to the caller.

Solution

The CLI will accept three possible new arguments that will control output. Exactly one of these can be provided on each call.

  • --suppress-metadata : Suppresses metadata output.
  • --write-metadata FILE: Overwrites FILE with metadata output.
  • --append-metadata FILE : Appends metadata output to FILE.

Each line written consists of JSON-formatted metadata about a single operation. Fields in this metadata will include:

  • input : - or full path to input file
  • output : - or full path to output file
  • encryption_context : string-string JSON map of the encryption context

Additional metadata values might be added to this structure in the future.

Requirements:

  • The user must specify the filename.
  • If output is a directory, the filename cannot be within that directory.
  • Filename cannot be in input directory or files.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.