crypto-secret.cr

Crystal CI GitHub release GitHub commits since latest release (by date) for a branch Docs

Secrets hold sensitive information

The Secret interface manages limited time access to a secret and securely erases the secret when no longer needed.

Multiple Secret classes exist. Most of the time you shouldn't need to change the Secret type - the cryptographic library should have sane defaults. If you have a high security or high performance application see which secret type should I use?

Secret providers may implement additional protections via:

Installation

  1. Add the dependency to your shard.yml:

`yaml dependencies:

 crypto-secret:
   github: didactic-drunk/crypto-secret

`

  1. Run shards install

Usage

Rules:

  1. Secrets should be erased (wiped) ASAP
  2. Secrets are only available within a readonly or readwrite block
  3. Secrets are not thread safe except for the provided Slice (only when reading) within a single readonly or readwrite block
require "crypto-secret/bidet"

# Bidet is a minimal but fast secret implementation
secret = Crypto::Secret::Bidet.new 32
# Don't forget to wipe!
secret.wipe do
  secret.readonly do |slice|
    # May only read slice
  end
  secret.readwrite do |slice|
    # May read or write to slice
  end
end # secret is erased

Breaking the rules:

If you need thread safety :

  1. Switch to a Stateless Secret
  2. Or switch the Secret's state to readonly or readwrite after construction and never switch it again. sodium.cr makes use of this technique to provide thread safe encryption/decryption
  3. Or wrap all access in a Mutex (compatible with all Secret classes)

If you need more better performance:

If you need compatibility with any Secret:

Converting Bytes to a Secret

slice = method_that_returns_bytes()
secret = Crypto::Secret::Bidet.move_from slice # erases slice
# or
secret = Crypto::Secret::Bidet.copy_from slice
# or
secret = Crypto::Secret::Bidet size_in_bytes
secret.move_from slice

What is a Secret?

<strike>Secrets are Keys</strike> That's complicated and specific to the application. Some examples:

Not secrets:

Why?

The Secret interface is designed to handle varied levels of confidentiality with a unified API for cryptography libraries.

There is no one size fits all solution. Different applications have different security requirements. Sometimes for the same algorithm.

A master key (kgk) may reside on a HSM and generate subkeys on demand. Or for most applications the master key may use a best effort approach using a combination of [guard pages, mlock, mprotect]. Other keys in the same application may handle a high volume of messages where [guard pages, mlock, mprotect] overhead is too high. A key verifying a public key signature may not be Secret (but is a Secret::Not).

How do I use a Secret returned by a shard?

Accessing as a Slice(UInt8) | Bytes

secret = method_that_returns_a_secret()
secret.wipe do
  secret.readonly do |slice|
    ...
  end
  secret.readwrite do |slice|
    ...
  end
end

Using a Secret to hold decrypted file contents:

key = ...another Secret...
encrypted_str = File.read("filename")
decrypted_size = encrypted_str.bytesize - mac_size
file_secret = Crypto::Secret::Large.new decrypted_size
file_secret.wipe do
  file_secrets.readwrite do |slice|
    decrypt(key: key, src: encrypted_str, dst: slice)

    # Do something with file contents in slice
  end
end # Decrypted data is erased

Reusing a Secret

# May be used to generate new keys
secret.random

# Copy to secret and wipe `slice`
secret.move_from slice

When should I use a Secret?

When implementing an encryption class return Secret keys with a sane default implementation that suits the average use for your class. Several default implementations are provided. Allow overriding the default returned key and/or allow users of your class to provide their own Secret for cases where they need more or less protection.

Example:

class SimplifiedEncryption
  # Allow users of your library to provide their own Secret key.  Also provide a sane default.
  def encrypt(data : Bytes | String, key : Secret? = nil) : {Secret, Bytes}
    key ||= Crypto::Secret::Default.random
    ...
    {key, encrypted_slice}
  end
end

What attacks does a Secret protect against?

Each implementation may add additional protections

Other languages/libraries

Implementing a new Secret holding class

Only intended for use by crypto library authors

class MySecret
  # Choose one
  include Crypto::Secret::Stateless
  include Crypto::Secret::Stateful

  def initialize(size)
    # allocate or reference storage
    # optionally mlock
  end

  protected def to_slice(& : Bytes -> Nil)
    # The yielded Slice only needs to be valid within the block
    # yield Slice.new(pointer, size)
  ensure
    # optionally reencrypt or signal HSM
  end

  def bytesize : Int32
    # return the size
  end

  # optionally override [noaccess, readonly, readwrite]
  # optionally override (almost) any other method with an implementation specific version
end

Contributing

Open a discussion or issue before creating PR's

  1. Fork it (<https://github.com/your-github-user/crypto-secret/fork>)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors