Encryption in Crystal lang

How do I do encryption in Crystal? I want to encrypt say a hash type or a named tuple containing some information, and then add it as a cookie value. I need to be able to read this encrypted value back and decrypt it to original hash type. Is this possible?

Thank you so much for all the help.

Check out OpenSSL::Cipher https://crystal-lang.org/api/0.35.1/OpenSSL/Cipher.html

@sol.vin Just a heads up that link is for a quite old version of the Crystal docs.

oops my bad, I just googled OpenSSL Cipher Crystal API and thats what came up.

@Blacksmoke16 Wait I’m missing something here, I went through the API docs on latest and couldn’t find Cipher any more, but I know it’s still there because I recently used it in a program I was writing. It’s still there in the src.

Your question consists of two problems -

  1. serialize your hash to some string or binary stream, (and deserialize back)
  2. encrypt it (and decrypt back)

If I would need to do it, I’ll do something like this:

require "yaml"
require "monocypher"

def encrypt(object, secret)
  plaintext = object.to_yaml.to_slice
  ciphertext = Bytes.new(plaintext.size + Crypto::OVERHEAD_SYMMETRIC)
  Crypto.encrypt(key: secret, input: plaintext, output: ciphertext)
  cookiestring = ciphertext.hexstring
end

def decrypt(text, secret)
  ciphertext = text.hexbytes
  raise "ciphertext too short" if ciphertext.size < Crypto::OVERHEAD_SYMMETRIC
  result = Bytes.new(ciphertext.size - Crypto::OVERHEAD_SYMMETRIC)
  unless Crypto.decrypt(key: secret, input: ciphertext, output: result)
    raise "ciphertext is corrupt"
  end
  hash1 = YAML.parse(String.new(result))
end

secret = Crypto::SymmetricKey.new # this will generate a key from system random source
puts "secret key is #{secret}"

hash = {"1" => [1, 2, 3], 2 => "test"}
cookiestring = encrypt(hash, secret)
puts "encrypted message: #{cookiestring}"
puts "decrypted message: #{decrypt(cookiestring, secret)}"

using my monocypher bindings shard and untyped serialization to YAML (it is limited to simple types, but maybe it’s ok for a cookie).

Maybe you should better use OpenSSL or LibSodium bindings for encryption, and typed serialization (using e.g. YAML::Serializable) for serialization.

@sol.vin Looks like that file isnt’t required in the docs_main file nor the openssl.cr file, thus is missing from the docs. Could prob make an issue for it as it’s prob not intentional it’s not there now.

Posted - https://github.com/crystal-lang/crystal/issues/7770

Thanks everyone. I was aware of OpenSSL::Cipher, however, as has been pointed out that wasn’t available in the new documentation.

I did not know about monocypher. Thank you @konovod. Will look into that. I appreciate the example you gave. That would be very helpful.

Thanks.

What type of information is stored in the cookie? It may be a good idea to store a session reference and then have the actual user data only on the server to minimize the amount of places its temporarily stored.

I was thinking of storing email and a session id (nothing sensitive, such as a password). But thought of encrypting it. But probably that is not necessary. As you said, I am just using the session id now. Probably, there is no need to encrypt that.

Thanks.

1 Like

HTTPS to and from your web server should be enough. Just make sure session IDs are unpredictable.