defmodule NostrBasics.Crypto.AES256CBC do @moduledoc """ Algorithm that encrypts and decrypts direct messages """ @doc """ Encrypts a direct message The example below can't test for the result, as it changes on every execution... ## Examples iex> remote_public_key = <<0xEFC83F01C8FB309DF2C8866B8C7924CC8B6F0580AFDDE1D6E16E2B6107C2862C::256>> ...> local_private_key = <<0x4E22DA43418DD934373CBB38A5AB13059191A2B3A51C5E0B67EB1334656943B8::256>> ...> "this message will end up being encrypted" ...> |> NostrBasics.Crypto.AES256CBC.encrypt(local_private_key, remote_public_key) """ @spec encrypt(String.t(), <<_::256>>, <<_::256>>) :: String.t() def encrypt(message, seckey, pubkey) do iv = :crypto.strong_rand_bytes(16) shared_secret = get_shared_secret(seckey, pubkey) cipher_text = :crypto.crypto_one_time(:aes_256_cbc, shared_secret, iv, message, encrypt: true, padding: :pkcs_padding ) b64_cypher_text = Base.encode64(cipher_text) b64_iv = Base.encode64(iv) "#{b64_cypher_text}?iv=#{b64_iv}" end @doc """ Decrypts a direct message ## Examples iex> remote_public_key = <<0xEFC83F01C8FB309DF2C8866B8C7924CC8B6F0580AFDDE1D6E16E2B6107C2862C::256>> ...> local_private_key = <<0x4E22DA43418DD934373CBB38A5AB13059191A2B3A51C5E0B67EB1334656943B8::256>> ...> "sWLtVbabr8fzIugnOXo4og==?iv=nxF/xVqbC4JdMRUEC0Jfyg==" ...> |> NostrBasics.Crypto.AES256CBC.decrypt(local_private_key, remote_public_key) {:ok, "test"} """ @spec decrypt(String.t(), <<_::256>>, <<_::256>>) :: {:ok, String.t()} | {:error, atom() | String.t()} def decrypt(message, seckey, pubkey) do [message, iv] = String.split(message, "?iv=") with {:ok, message} <- Base.decode64(message), {:ok, iv} <- Base.decode64(iv) do shared_secret = get_shared_secret(seckey, pubkey) decrypted = :crypto.crypto_one_time(:aes_256_cbc, shared_secret, iv, message, encrypt: false, padding: :pkcs_padding ) {:ok, decrypted} else {:error, message} -> {:error, message} :error -> {:error, "cannot decode iv, which should be in base64"} end end defp get_shared_secret(<<_::256>> = seckey, <<_::256>> = pubkey) do :crypto.compute_key( :ecdh, <<0x02::8, pubkey::bitstring-256>>, seckey, :secp256k1 ) end end