Progress on implementing encryption (I think)
All checks were successful
Build & Test / nix-build (push) Successful in 1m6s
All checks were successful
Build & Test / nix-build (push) Successful in 1m6s
This commit is contained in:
parent
1d741d785a
commit
0f59aaef59
@ -2,4 +2,5 @@ import Config
|
||||
|
||||
config :amethyst,
|
||||
port: 25599, # Bogus port for testing, avoids unexpected conflicts
|
||||
encryption: false
|
||||
encryption: true, # Whether or not to enable encryption, this should almost always be 'true' for security reasons
|
||||
auth: true # Whether or not users should be authenticated.
|
||||
|
@ -26,6 +26,7 @@ defmodule Amethyst.Application do
|
||||
children = [
|
||||
Supervisor.child_spec({Task, fn -> Amethyst.TCPListener.accept(Application.fetch_env!(:amethyst, :port)) end}, restart: :permanent),
|
||||
{Task.Supervisor, name: Amethyst.ConnectionSupervisor},
|
||||
{Amethyst.Keys, 1024},
|
||||
]
|
||||
|
||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||
|
65
lib/encryption.ex
Normal file
65
lib/encryption.ex
Normal file
@ -0,0 +1,65 @@
|
||||
# Amethyst - An experimental Minecraft server written in Elixir.
|
||||
# Copyright (C) 2024 KodiCraft
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
defmodule Amethyst.Keys do
|
||||
@moduledoc """
|
||||
This module generates and manages the keys used for encryption.
|
||||
|
||||
Minecraft uses RSA encryption, keys are stored in ASN.1 format and should be
|
||||
generated at runtime. The vanilla server uses 1024-bit keys, but we can use
|
||||
a larger key size for added security.
|
||||
"""
|
||||
|
||||
use GenServer
|
||||
require Logger
|
||||
|
||||
def start_link(bits) do
|
||||
GenServer.start_link(__MODULE__, bits, name: __MODULE__)
|
||||
end
|
||||
|
||||
def get_priv do
|
||||
GenServer.call(__MODULE__, :get_priv)
|
||||
end
|
||||
|
||||
def get_pub do
|
||||
GenServer.call(__MODULE__, :get_pub)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(bits) do
|
||||
Logger.info("Generating RSA keys with #{bits} bits")
|
||||
# https://elixirforum.com/t/how-to-generate-rsa-public-key-using-crypto-provided-exponent-and-modulus/38487
|
||||
{:RSAPrivateKey, _, modulus, public_exponent, _, _, _, _exponent1, _, _, _other_prime_infos} =
|
||||
rsa_private_key = :public_key.generate_key({:rsa, bits, 65_537})
|
||||
|
||||
rsa_public_key = {:RSAPublicKey, modulus, public_exponent}
|
||||
privkey = :public_key.der_encode(:RSAPrivateKey, rsa_private_key)
|
||||
pubkey = :public_key.der_encode(:RSAPublicKey, rsa_public_key)
|
||||
|
||||
Logger.info("Generated RSA keys")
|
||||
{:ok, {pubkey, privkey}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(:get_priv, _from, {pubkey, privkey}) do
|
||||
{:reply, privkey, {pubkey, privkey}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(:get_pub, _from, {pubkey, privkey}) do
|
||||
{:reply, pubkey, {pubkey, privkey}}
|
||||
end
|
||||
end
|
@ -71,8 +71,12 @@ defmodule Amethyst.Server.Login do
|
||||
Write.varint(0x00) <> Write.string(reason)
|
||||
end
|
||||
# Encryption Request https://wiki.vg/Protocol#Encryption_Request
|
||||
def serialize({:encryption_request, id, pubkey, verify_token, auth}) do
|
||||
Write.varint(0x01) <> Write.string(id) <> Write.varint(byte_size(pubkey)) <> pubkey <> Write.varint(byte_size(verify_token)) <> verify_token <> Write.bool(auth)
|
||||
def serialize({:encryption_request, server_id, pubkey, verify_token, auth}) do
|
||||
Write.varint(0x01) <>
|
||||
Write.string(server_id) <>
|
||||
Write.varint(byte_size(pubkey)) <> pubkey <>
|
||||
Write.varint(byte_size(verify_token)) <> verify_token <>
|
||||
Write.bool(auth)
|
||||
end
|
||||
# Login Success https://wiki.vg/Protocol#Login_Success
|
||||
def serialize({:login_success, uuid, username, props, strict}) do
|
||||
@ -103,7 +107,10 @@ defmodule Amethyst.Server.Login do
|
||||
def handle({:login_start, name, uuid}, client) do
|
||||
Logger.info("Logging in #{name} (#{uuid})")
|
||||
if Application.fetch_env!(:amethyst, :encryption) do
|
||||
raise RuntimeError, "Encryption is not currently supported!"
|
||||
verify_token = :crypto.strong_rand_bytes(4)
|
||||
pubkey = Amethyst.Keys.get_pub()
|
||||
auth = Application.fetch_env!(:amethyst, :auth)
|
||||
transmit({:encryption_request, "amethyst", pubkey, verify_token, auth}, client)
|
||||
else
|
||||
transmit({:login_success, uuid, name, [], false}, client)
|
||||
end
|
||||
|
@ -12,6 +12,7 @@ defmodule WriteTest do
|
||||
assert Amethyst.Minecraft.Write.varint(127) == <<0x7F>>
|
||||
assert Amethyst.Minecraft.Write.varint(128) == <<0x80, 0x01>>
|
||||
assert Amethyst.Minecraft.Write.varint(255) == <<0xFF, 0x01>>
|
||||
assert Amethyst.Minecraft.Write.varint(25_565) == <<0xDD, 0xC7, 0x01>>
|
||||
assert Amethyst.Minecraft.Write.varint(2_147_483_647) == <<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>
|
||||
assert Amethyst.Minecraft.Write.varint(-1) == <<0xFF, 0xFF, 0xFF, 0xFF, 0x0F>>
|
||||
assert Amethyst.Minecraft.Write.varint(-2_147_483_648) == <<0x80, 0x80, 0x80, 0x80, 0x08>>
|
||||
|
Loading…
Reference in New Issue
Block a user