defmodule Cryppo.EncryptedData do
@moduledoc """
A struct for encrypted data and encryption artefacts
An `Cryppo.EncryptedData` struct may be marked as belonging to a certain encryption strategy
using field `encryption_strategy_module` containing the module of the encryption strategy.
Can also contain encryption artefacts if they are part of the encryption strategy.
"""
import Cryppo.Base64
import Cryppo.Strategies, only: [find_strategy: 1]
alias Cryppo.{EncryptedData, EncryptionArtefacts, Serialization}
@typedoc """
Struct `Cryppo.EncryptedData`
A `Cryppo.EncryptedData` struct contains
* `encrypted_data`: encrypted data
* `encryption_strategy_module`: module of the encryption strategy to which the key belongs
* `encryption_artefacts`: a map with encryption artefacts
"""
@type t :: %__MODULE__{
encryption_strategy_module: Cryppo.encryption_strategy_module() | nil,
encrypted_data: binary,
encryption_artefacts: EncryptionArtefacts.t()
}
@enforce_keys [:encryption_strategy_module, :encrypted_data, :encryption_artefacts]
defstruct [:encryption_strategy_module, :encrypted_data, :encryption_artefacts]
@doc """
Initialize a struct with the module of an encryption strategy, a
binary with encrypted data, and encryption_artefacts.
"""
@spec new(Cryppo.encryption_strategy_module(), binary, EncryptionArtefacts.t()) :: t()
def new(mod, encrypted_data, %EncryptionArtefacts{} = encryption_artefacts)
when is_atom(mod) and is_binary(encrypted_data) do
%__MODULE__{
encryption_strategy_module: mod,
encrypted_data: encrypted_data,
encryption_artefacts: encryption_artefacts
}
end
@doc false
@spec load(String.t(), String.t(), String.t()) ::
{:ok, t()}
| {:error, :invalid_bson, :invalid_base64, :invalid_encryption_artefacts | String.t()}
| {:unsupported_encryption_strategy, binary}
def load(strategy_name, encrypted_data_base64, encryption_artefacts_base64) do
case find_strategy(strategy_name) do
{:ok, encryption_strategy_mod} ->
with {:ok, encrypted_data} <- decode_base64(encrypted_data_base64),
{:ok, encryption_artefacts} <- EncryptionArtefacts.load(encryption_artefacts_base64) do
{:ok, new(encryption_strategy_mod, encrypted_data, encryption_artefacts)}
end
err ->
err
end
end
defimpl Serialization do
@spec serialize(EncryptedData.t()) :: binary
def serialize(%EncryptedData{
encryption_strategy_module: mod,
encrypted_data: encrypted_data,
encryption_artefacts: encryption_artefacts
}) do
strategy_name = apply(mod, :strategy_name, [])
[
strategy_name,
Base.url_encode64(encrypted_data, padding: true),
Serialization.serialize(encryption_artefacts)
]
|> Enum.join(".")
end
end
end