cryptography
is a Ruby library to provide abstractions for common use-cases of cryptography.
Cryptography is hard. Really hard, and deceptively hard. There are a million and one ways for a developer to screw up when implementing cryptography, and non-cryptographers are exceedingly unlikely to get it right.
Unfortunately, there's a common mentality in the developer community that getting tiny details wrong doesn't matter, that it's still "good enough" to defeat hackers, if not the NSA. This mentality is, frankly, stupid and dangerous. Tiny flaws in implementation details almost inevitably result in a complete breach of security: revealing the contents of ciphertexts, allowing manipulation of the contents of ciphertexts, forging signed messages, or discovering users' passwords. Even weaknesses that seem tolerable can often be trivially combined with non-cryptographic security weaknesses to accomplish the same goals.
Despite this, there are relatively few high-level interfaces to hide the details of cryptographic primitives and to automatically use safe and secure defaults. This library strongly adopts the mentality that if you're typing the letters A-E-S into your code, you're doing it wrong.
This library does not attempt to implement any cryptography on its own. It simply provides a sane, high-level, secure frontend to existing libraries developed and maintained by respected cryptographers with proven track records in security. In particular, cryptographic primitives are implemented with DJB's venerable NaCl library and passwords are processed with bcrypt.
This library was not designed with interoperability as a goal. While it uses standardized, publicly-available algorithms, its data formats incorporate library-specific header information that won't be understood by other systems.
Additionally, this library does not easily allow for settings to be tweaked or algorithms to be selected or changed. This is for the sake of protecting the user from him/herself, but comes at the cost of compatibility with third-party systems.
$ gem install cryptography
You can load either the entire library or only the components you wish to use.
require 'cryptography' # load the entire library
require 'cryptography/vault' # only load Cryptography::Vault
require 'cryptography/lockbox' # only load Cryptography::Lockbox
Each supported use-case is broken down into its own class.
The Cryptography::Vault API should be used to hide sensitive data from unauthorized parties. Access to the vault's contents is granted only to parties who know the key which was used to lock the box.
Cryptography::Vault is implemented using an authenticated symmetric cipher. It is a combination of the Salsa20 stream cipher and the Poly1035 message authentication code.
A key is required to lock and unlock the vault. Keys may be reused for multiple vaults, but any party who knows the key can open any Vault locked with the key.
key = Cryptography::Vault.key
Vaults must be locked with a key and a string containing data to be kept secret. The string is interpreted merely as a sequence of bytes, and details like the string's encoding are ignored.
The locked vault can safely be stored on an insecure medium, or transmitted over an insecure channel.
Note, though, that a locked vault is a sequence of bytes, and not an encoded string. If you need to transmit a vault over a plaintext protocol, you may wish to encode it with something like Base64 or Base32.
vault = Cryptography::Vault.lock(key, "secret data")
vault # => "\xAB\xD8\xD3\x1E..."
contents = Cryptography::Vault.unlock(key, vault)
contents # => "secret data"
If you wish to serialize a Vault to a format other than a byte string, you can instantiate a Vault with the key and plaintext, and convert it to whatever supported format you find most convenient.
# serialize to bytes, string (equivalent to bytes), an array, or a hash
vault = Cryptography::Vault.new(key, "secret data")
vault.to_bytes # => "\xAB\xD8\xD3\x1E..." (equivalent to Vault::lock)
vault.to_s # => "\xAB\xD8\xD3\x1E..." (equivalent to Vault::lock)
vault.to_a # => [ "\xAB\xD8\xD3\x1E...", "\x9A\xEB\x19\x97..." ]
vault.to_h # => { :iv => "\xAB\xD8\xD3\x1E...",
# :ciphertext => "\x9A\xEB\x19\x97...", }
# serialize to plaintext-friendly formats
vault.to_base64 # => "q9jTHg...\n"
vault.to_base32 # => "VPMNGHQ..." (requires the 'base32' gem)
# serialize through the native Ruby marshaller
vault.dump # => \x04\bU:\nVault..." (uses native Ruby [marshalling])
Marshal.dump(vault) # => \x04\bU:\nVault..." (equivalent to the above)
You can later restore the Vault from the above formats, and use the
#unlock
method to access the protected data inside.
# restore a vault from bytes, string, array, or hash
vault = Cryptography::Vault.from_bytes(bytes)
vault = Cryptography::Vault.from_s(string)
vault = Cryptography::Vault.from_a(array)
vault = Cryptography::Vault.from_h(hash)
# restore a vault from plaintext-friendly formats
vault = Cryptography::Vault.from_base64(base64)
vault = Cryptography::Vault.from_base32(base32)
# restore a vault from a marshalled string
vault = Cryptography::Vault.load(dump)
vault = Marshal.load(dump)
# retrieve the data inside the vault
vault.unlock(key) # => "secret data"
If the underlying cipher of the Vault is ever compromised, an update will be released that prevents encrypting new data with the flawed algorithm (this can be disabled, but warnings will still be logged). Existing data can still be decrypted using existing keys. This is possible because all keys encode the algorithm they're to be used for.
You can use this feature to easily replace the keys for a vault. The
#rekey
method takes the old key, a new key, the existing vault, and
returns a new vault containing the data inside the old vault, but
locked with the new key.
new_key = Cryptography::Vault.key
old_key = "..."
vault = "\xAB\xD8\xD3\x1E..."
Cryptography::Vault.rekey(old_key, new_key, vault) # => "\xB3\x88\x9C\x7C"