Encryption in Crystal lang

#1

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.

#2

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

#3

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

#4

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

#5

@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.

#6

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.

#7

@redcodefinal 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.

#8

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

#9

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.