Packages
mob_dev
0.6.2
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.37
0.3.35
0.3.34
0.3.33
0.3.28
0.3.26
0.3.23
0.3.21
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.18
0.2.17
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
Development tooling for the Mob mobile framework
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/mob.plugin.keygen.ex
defmodule Mix.Tasks.Mob.Plugin.Keygen do
use Mix.Task
@shortdoc "Generate an Ed25519 keypair for signing a mob plugin"
@moduledoc """
Generates a per-plugin Ed25519 keypair and writes:
- `~/.mob/keys/<plugin_name>.priv` — base64-encoded raw 32-byte private
key, mode 0600.
- `priv/mob_plugin.pub` in the plugin directory — base64-encoded raw
32-byte public key.
The public key file ships with the plugin (committed to source
control); the private key never leaves the author's machine.
mix mob.plugin.keygen [--plugin <dir>] [--force]
## Options
* `--plugin <dir>` — plugin directory; defaults to the current
working directory.
* `--force` — overwrite an existing `~/.mob/keys/<name>.priv`.
Refused by default to prevent an accidental key rotation.
"""
alias MobDev.Plugin.{Crypto, Manifest, PrivateKeyStore}
@switches [plugin: :string, force: :boolean]
@impl Mix.Task
def run(args) do
{opts, _, _} = OptionParser.parse(args, strict: @switches)
plugin_dir = opts[:plugin] || File.cwd!()
force? = Keyword.get(opts, :force, false)
name = plugin_name!(plugin_dir)
refuse_if_exists!(name, force?)
{priv, pub} = Crypto.generate_keypair()
:ok = PrivateKeyStore.write_key(name, priv)
pub_path = write_pubkey!(plugin_dir, pub)
Mix.shell().info([
:green,
" generated ",
:reset,
"ed25519 keypair for #{name}\n",
" private key: #{PrivateKeyStore.key_path(name)} (mode 0600)\n",
" public key: #{pub_path}\n",
" fingerprint: ",
:cyan,
Crypto.fingerprint(pub),
:reset,
"\n\nNext: run `mix mob.plugin.sign` to produce priv/mob_plugin.sig.\n"
])
end
defp plugin_name!(plugin_dir) do
case Manifest.load(plugin_dir) do
{:ok, %{name: name}} when is_atom(name) and not is_nil(name) ->
name
{:ok, _} ->
Mix.raise(
"#{plugin_dir}/priv/mob_plugin.exs is missing a :name field — " <>
"a plugin needs a manifest before it can be signed"
)
{:error, reason} ->
Mix.raise("could not load plugin manifest at #{plugin_dir}: #{reason}")
end
end
defp refuse_if_exists!(name, false) do
path = PrivateKeyStore.key_path(name)
if File.exists?(path) do
Mix.raise(
"private key already exists at #{path} — pass --force to overwrite " <>
"(this is a key rotation; existing hosts will need to re-run " <>
"mix mob.plugin.trust)"
)
end
end
defp refuse_if_exists!(_name, true), do: :ok
defp write_pubkey!(plugin_dir, pub) do
path = Path.join(plugin_dir, "priv/mob_plugin.pub")
File.mkdir_p!(Path.dirname(path))
File.write!(path, Base.encode64(pub) <> "\n")
path
end
end