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,
|
config :amethyst,
|
||||||
port: 25599, # Bogus port for testing, avoids unexpected conflicts
|
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 = [
|
children = [
|
||||||
Supervisor.child_spec({Task, fn -> Amethyst.TCPListener.accept(Application.fetch_env!(:amethyst, :port)) end}, restart: :permanent),
|
Supervisor.child_spec({Task, fn -> Amethyst.TCPListener.accept(Application.fetch_env!(:amethyst, :port)) end}, restart: :permanent),
|
||||||
{Task.Supervisor, name: Amethyst.ConnectionSupervisor},
|
{Task.Supervisor, name: Amethyst.ConnectionSupervisor},
|
||||||
|
{Amethyst.Keys, 1024},
|
||||||
]
|
]
|
||||||
|
|
||||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
# 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)
|
Write.varint(0x00) <> Write.string(reason)
|
||||||
end
|
end
|
||||||
# Encryption Request https://wiki.vg/Protocol#Encryption_Request
|
# Encryption Request https://wiki.vg/Protocol#Encryption_Request
|
||||||
def serialize({:encryption_request, id, pubkey, verify_token, auth}) do
|
def serialize({:encryption_request, server_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)
|
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
|
end
|
||||||
# Login Success https://wiki.vg/Protocol#Login_Success
|
# Login Success https://wiki.vg/Protocol#Login_Success
|
||||||
def serialize({:login_success, uuid, username, props, strict}) do
|
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
|
def handle({:login_start, name, uuid}, client) do
|
||||||
Logger.info("Logging in #{name} (#{uuid})")
|
Logger.info("Logging in #{name} (#{uuid})")
|
||||||
if Application.fetch_env!(:amethyst, :encryption) do
|
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
|
else
|
||||||
transmit({:login_success, uuid, name, [], false}, client)
|
transmit({:login_success, uuid, name, [], false}, client)
|
||||||
end
|
end
|
||||||
|
@ -12,6 +12,7 @@ defmodule WriteTest do
|
|||||||
assert Amethyst.Minecraft.Write.varint(127) == <<0x7F>>
|
assert Amethyst.Minecraft.Write.varint(127) == <<0x7F>>
|
||||||
assert Amethyst.Minecraft.Write.varint(128) == <<0x80, 0x01>>
|
assert Amethyst.Minecraft.Write.varint(128) == <<0x80, 0x01>>
|
||||||
assert Amethyst.Minecraft.Write.varint(255) == <<0xFF, 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(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(-1) == <<0xFF, 0xFF, 0xFF, 0xFF, 0x0F>>
|
||||||
assert Amethyst.Minecraft.Write.varint(-2_147_483_648) == <<0x80, 0x80, 0x80, 0x80, 0x08>>
|
assert Amethyst.Minecraft.Write.varint(-2_147_483_648) == <<0x80, 0x80, 0x80, 0x80, 0x08>>
|
||||||
|
Loading…
Reference in New Issue
Block a user