Project

porky_lib

0.0
No release in over a year
A library for cryptographic services using AWS KMS and RbNaCl
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Project Readme

CircleCI codecov Gem Version Depfu

PorkyLib

This gem is a cryptographic services library. PorkyLib uses AWS Key Management Service (KMS) for key management and RbNaCl for performing cryptographic operations.

Installation

Add this line to your application's Gemfile:

gem 'porky_lib'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install porky_lib

Inside of your Ruby program do:

require 'porky_lib'

... to pull it in as a dependency.

Usage

Initialization

Something like the following should be included in an initializer in your Rails project:

# Use PorkyLib's AWS KMS mock client except in production, for example
use_mock_client = !Rails.env.production?
max_file_size = 0 # max file size allowed, in bytes - defaults to 0
presign_url_expires_in = 300 # expiry time for presigned urls, in seconds - defaults to 300 (5 minutes)
PorkyLib::Config.configure(aws_region: ENV[AWS_REGION],
                           aws_key_id: ENV[AWS_KEY_ID],
                           aws_key_secret: ENV[AWS_KEY_SECRET],
                           aws_client_mock: use_mock_client,
                           max_file_size: max_file_size,
                           presign_url_expires_in: presign_url_expires_in)
PorkyLib::Config.initialize_aws

Creating a New CMK

To create a new customer master key (CMK) within AWS:

# Where tags is a list of key/value pairs (i.e [{ key1: 'value1' }])
# key_alias is an optional parameter, and if provided will create an alias with the provided value for the newly created key
# key_rotation_enabled is an optional parameter, and if true will enable automatic key rotation for the new created key. Default is true.
key_id = PorkyLib::Symmetric.instance.create_key(tags, key_alias, key_rotation_enabled)

Creating an Alias for an Existing CMK

To create a new alias for an existing customer master key (CMK) within AWS:

# Where key_id is the AWS key ID or Amazon Resource Name (ARN)
# key_alias is the value of the alias to create
PorkyLib::Symmetric.instance.create_alias(key_id, key_alias)

Enabling Key Rotation for an Existing CMK

To create a new alias for an existing customer master key (CMK) within AWS:

# Where key_id is the AWS key ID or Amazon Resource Name (ARN)
PorkyLib::Symmetric.instance.enable_key_rotation(key_id)

Encrypting Data

To encrypt data:

# Where data is the data to encrypt
# cmk_key_id is the AWS key ID, Amazon Resource Name (ARN) or alias for the CMK to use to generate the data encryption key (DEK)
# ciphertext_dek is an optional parameter to specify the data encryption key to use to encrypt the data. If not provided, a new data encryption key will be generated. Default is nil.
# encryption_context is an optional parameter to provide additional authentication data for encrypting the DEK. Default is nil.
[ciphertext_dek, ciphertext, nonce] = PorkyLib::Symmetric.instance.encrypt(data, cmk_key_id, ciphertext_dek, encryption_context)

To encrypt data with a known plaintext key:

# Where plaintext is the data to encrypt
# plaintext_key is the encryption key to use
# encryption_info is the structure returned that contains:
#   ciphertext: plaintext encrypted under plaintext_key
#   nonce: The generated nonce
encryption_info = PorkyLib::Symmetric.instance.encrypt_with_key(plaintext, plaintext_key)

Decrypting Data

To decrypt data:

# Where ciphertext_dek is the encrypted data encryption key (DEK)
# ciphertext is the encrypted data to be decrypted
# nonce is the nonce value associated with ciphertext
# encryption_context is an optional parameter to provide additional authentication data for decrypting the DEK. Default is nil. Note, this must match the value that was used to encrypt.
plaintext_data = PorkyLib::Symmetric.instance.decrypt(ciphertext_dek, ciphertext, nonce, encryption_context)

To decrypt data with a known plaintext key:

# Where ciphertext is the encrypted data to be decrypted
# plaintext_key is the decryption key to use
# nonce is the nonce to use
# decryption_info is the structured returned that contains:
#   plaintext: ciphertext decrypted under plaintext_key
decryption_info = PorkyLib::Symmetric.instance.decrypt_with_key(ciphertext, plaintext_key, nonce)

Generating Data Encryption Keys

To generate a new data encryption key:

# Where cmk_key_id is the AWS key ID, Amazon Resource Name (ARN) or alias for the CMK to use to generate the data encryption key (DEK)
# encryption_context is an optional parameter to provide additional authentication data for encrypting the DEK. Default is nil.
plaintext_key, ciphertext_key = PorkyLib::Symmetric.instance.generate_data_encryption_key(cmk_key_id, encryption_context)

Decrypting Data Encryption Keys

To decrypt an existing ciphertext data encryption key:

# Where ciphertext_key is the data encryption key, encrypted by a CMK within your AWS environment.
# encryption_context is an optional parameter to provide additional authentication data for encrypting the DEK. Default is nil.
plaintext_key = PorkyLib::Symmetric.instance.generate_data_encryption_key(ciphertext_key, encryption_context)

Securely Deleting Plaintext Key From Memory

To securely delete the plaintext key from memory:

# Where length is the number of bytes of the plaintext key (i.e. plaintext_key.bytesize)
plaintext_key.replace(PorkyLib::Symmetric.instance.secure_delete_plaintext_key(plaintext_key.bytesize))

Check If An Alias Exists

To verify whether an alias exists or not:

# Where key_alias is the alias name to verify
alias_exists = PorkyLib::Symmetric.instance.cmk_alias_exists?(key_alias)

To Read From AWS S3

# Where bucket_name is the name of the S3 bucket to read from
# file_key is file identifier of the file/data that was written to S3.
file_data = PorkyLib::FileService.read(bucket_name, file_key)

To Read Unencrypted Files From AWS S3

# Where bucket_name is the name of the S3 bucket to read from
# file_key is file identifier of the file/data that was written to S3.
file_data = PorkyLib::Unencrypted::FileService.read(bucket_name, file_key)

To Write To AWS S3

# --- DEPRECATED --- Please use write_data or write_file instead of write
# Where file is the data to encrypt and upload to S3 (can be a path or raw data or ruby file object)
# bucket_name is the name of the S3 bucket to write to
# key_id is the ID of the CMK to use to generate a data encryption key to encrypt the file data
# options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
file_key = PorkyLib::FileService.write(file, bucket_name, key_id, options)

To Write Files To AWS S3

# Where file is the data to encrypt and upload to S3 (can be a path or ruby file object)
# bucket_name is the name of the S3 bucket to write to
# key_id is the ID of the CMK to use to generate a data encryption key to encrypt the file data
# options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
file_key = PorkyLib::FileService.write_file(file, bucket_name, key_id, options)

To Write Data To AWS S3

# Where data is the raw data to encrypt and upload to S3
# bucket_name is the name of the S3 bucket to write to
# key_id is the ID of the CMK to use to generate a data encryption key to encrypt the file data
# options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
file_key = PorkyLib::FileService.write_data(data, bucket_name, key_id, options)

To Write Unencrypted To AWS S3

# --- DEPRECATED --- Please use write_data or write_file instead of write
# Where file is the data to upload to S3 (can be a path or raw data or ruby file object)
# bucket_name is the name of the S3 bucket to write to
# options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
file_key = PorkyLib::Unencrypted::FileService.write(file, bucket_name, options)

To Write Unencrypted Files To AWS S3

# Where file is the data to encrypt and upload to S3 (can be a path or ruby file object)
# bucket_name is the name of the S3 bucket to write to
# options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
file_key = PorkyLib::Unencrypted::FileService.write_file(file, bucket_name, options)

To Write Unencrypted Data To AWS S3

# Where data is the raw data to encrypt and upload to S3
# bucket_name is the name of the S3 bucket to write to
# options is an optional parameter for specifying optional metadata about the file and the storage_class of the object
file_key = PorkyLib::Unencrypted::FileService.write_data(data, bucket_name, options)

Generate S3 Presigned POST URL

To generate a new presigned POST url (used to upload files directly to AWS S3):

# Where bucket_name is the name of the S3 bucket to write to
# options is an optional parameter for specifying optional metadata about the file
# file_key is randomly generated, unless it's passed as a parameter in the options hash using 'file_name' as key
url, file_key = PorkyLib::Symmetric.instance.presigned_post_url(bucket_name, options)

Generate S3 Presigned GET URL

To generate a new presigned GET url (used to download files directly from AWS S3):

# Where bucket_name is the name of the S3 bucket to read from
# file_key is the file identifier of the file/data that was written to S3.
url = PorkyLib::Symmetric.instance.presigned_get_url(bucket_name, file_key)

Rake task

If you want to write or read an encrypted file from the command line, there is a Rake write and read task.

Note: the environment variables can be set globally or by prepending them to the rake task command

Write file

Rake task name: file:write

Environment variables:

  • Required
    • FILE_PATH - Absolute or relative file path
    • CMK_KEY_ID - Alias of the CMK key
    • AWS_S3_BUCKET - AWS S3 bucket name
    • AWS_REGION - AWS region name
    • AWS_ACCESS_KEY_ID - AWS access key ID (credentials)
    • AWS_ACCESS_KEY - AWS secret access key (credentials)
  • Optional
    • AWS_S3_MOCK_CLIENT - PorkyLib's AWS KMS mock client (defaults to true)
    • AWS_S3_MAX_FILE_SIZE - Max file size (defaults to 1MB)
    • AWS_S3_STORAGE_CLASS - One of STANDARD, REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE (defaults to STANDARD)
    • AWS_S3_KEEP_FILE_NAME - Saves the file in AWS S3 with the original file name (defaults to true)

Read file

Rake task name: file:read

Environment variables:

  • Required
    • FILE_KEY - AWS S3 object file key
    • AWS_S3_BUCKET - AWS S3 bucket name
    • AWS_REGION - AWS region name
    • AWS_ACCESS_KEY_ID - AWS access key ID (credentials)
    • AWS_ACCESS_KEY - AWS secret access key (credentials)
  • Optional
    • AWS_S3_MOCK_CLIENT - PorkyLib's AWS KMS mock client (defaults to true)
    • AWS_S3_MAX_FILE_SIZE - Max file size (defaults to 1MB)
    • DESTINATION - Location to save the file (defaults to FILE_KEY)

Development

Development on this project should occur on separate feature branches and pull requests should be submitted. When submitting a pull request, the pull request comment template should be filled out as much as possible to ensure a quick review and increase the likelihood of the pull request being accepted.

Ruby

This application requires:

  • Ruby version: 2.7.2

Ruby 2.7.2 and greater requires OpenSSL 1.1+. To link to Homebrew's upgraded version of OpenSSL, add the following to your bash profile

export RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl@1.1)"

If you do not have Ruby installed, it is recommended you use ruby-install and chruby to manage Ruby versions.

brew install ruby-install chruby
ruby-install ruby 2.7.2

Add the following lines to ~/.bash_profile:

source /usr/local/opt/chruby/share/chruby/chruby.sh
source /usr/local/opt/chruby/share/chruby/auto.sh

Set Ruby version to 2.7.2:

source ~/.bash_profile
chruby 2.7.2

Running Tests

rspec # Without code coverage
COVERAGE=true rspec # with code coverage

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/Zetatango/porky_lib