Current section
Files
Jump to
Current section
Files
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
@sep "::"
@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, signature} <- ExPublicKey.sign(str, priv),
{:ok, encryption_tuple} <- ExCrypto.encrypt(aes_256_key, str),
do: {:ok, "#{encrypted_key}#{@sep}#{Base.url_encode64(signature)}#{@sep}#{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, signature, encrypted_payload] <- String.split(str, @sep),
{:ok, decrypted_key} <- ExPublicKey.decrypt_public(encrypted_key, pub),
{iv, cipher} <- decode_payload(encrypted_payload),
{:ok, decrypted} <- ExCrypto.decrypt(decrypted_key, iv, cipher),
{:ok, true} <- ExPublicKey.verify(decrypted, Base.url_decode64!(signature), pub) do
{:ok, decrypted}
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