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 totrue
) -
AWS_S3_MAX_FILE_SIZE
- Max file size (defaults to1MB
) -
AWS_S3_STORAGE_CLASS
- One of STANDARD, REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE (defaults toSTANDARD
) -
AWS_S3_KEEP_FILE_NAME
- Saves the file in AWS S3 with the original file name (defaults totrue
)
-
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 totrue
) -
AWS_S3_MAX_FILE_SIZE
- Max file size (defaults to1MB
) -
DESTINATION
- Location to save the file (defaults toFILE_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