Current section

Files

Jump to
piazza_core lib crypto rsa.ex
Raw

lib/crypto/rsa.ex

defmodule Piazza.Crypto.RSA do
alias ExPublicKey.{
RSAPrivateKey,
RSAPublicKey
}
@type keypair :: {RSAPrivateKey.t, RSAPublicKey.t}
@spec generate_keypair() :: {:ok, keypair} | {:error, term}
def generate_keypair() do
with {:ok, private} <- ExPublicKey.generate_key(),
{:ok, public} <- ExPublicKey.public_key_from_private_key(private),
do: {:ok, {private, public}}
end
@spec pem_encode(keypair) :: {:ok, {binary, binary}} | {:error, term}
def pem_encode({%RSAPrivateKey{} = pk, %RSAPublicKey{} = pub}) do
with {:ok, pk_pem} <- ExPublicKey.pem_encode(pk),
{:ok, pub_pem} <- ExPublicKey.pem_encode(pub),
do: {:ok, {pk_pem, pub_pem}}
end
@spec encrypt(binary, RSAPrivateKey.t) :: {:ok, binary} | :error
def encrypt(str, %RSAPrivateKey{} = priv) when is_binary(str) do
{:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes)
with {:ok, encrypted_key} <- ExPublicKey.encrypt_private(aes_256_key, priv),
{:ok, encryption_tuple} <- ExCrypto.encrypt(aes_256_key, str),
do: {:ok, "#{encrypted_key}::#{encode_payload(encryption_tuple)}"}
end
@spec decrypt(binary, RSAPublicKey.t) :: {:ok, binary} | :error
def decrypt(str, %RSAPublicKey{} = pub) when is_binary(str) do
with [encrypted_key, encrypted_payload] <- String.split(str, "::"),
{:ok, decrypted_key} <- ExPublicKey.decrypt_public(encrypted_key, pub),
{iv, cipher} <- decode_payload(encrypted_payload) do
ExCrypto.decrypt(decrypted_key, iv, cipher)
else
_ -> :error
end
end
defp encode_payload({iv, txt}) when byte_size(iv) == 16 do
Base.url_encode64(iv <> txt)
end
defp decode_payload(payload) do
decoded = Base.url_decode64!(payload)
len = byte_size(decoded)
{binary_part(decoded, 0, 16), binary_part(decoded, 16, len - 16)}
end
end