Skip to content

server.internal.encryption

fn new_context #

fn new_context(key []u8) !&Context

new_context builds a Context from the 32-byte AES-256 key derived during the handshake. It returns an error on a wrong-sized key rather than panicking.

fn new_server_key_pair #

fn new_server_key_pair() !&ServerKeyPair

new_server_key_pair generates a fresh secp384r1 keypair.

fn prepare_handshake #

fn prepare_handshake(client_public_key_b64 string) !HandshakeResult

prepare_handshake runs the full server side of the Bedrock encryption handshake for one client: generate the server keypair, ECDH against the client public key, derive the AES key from a fresh random salt, and build a signed ServerToClientHandshake JWT. The keypair is freed before returning.

struct Context #

@[heap]
struct Context {
mut:
	key             []u8
	encrypt_stream  CtrStream
	decrypt_stream  CtrStream
	encrypt_counter u64
	decrypt_counter u64
}

Context is the per-session Bedrock cipher. MCPE uses "GCM without the auth tag", which is just AES-256-CTR with the GCM initial counter block as IV: the first 12 bytes of the key followed by 00 00 00 02. Each direction keeps its own persistent CTR keystream and a monotonic counter that feeds a per-packet SHA-256 checksum.

fn (Context) encrypt #

fn (mut c Context) encrypt(payload []u8) []u8

encrypt appends the per-packet checksum to the plaintext, then encrypts the combined buffer with the send-direction CTR keystream.

fn (Context) decrypt #

fn (mut c Context) decrypt(encrypted []u8) ![]u8

decrypt reverses encrypt: it decrypts with the receive-direction keystream, splits off the trailing checksum, and verifies it against the monotonic receive counter. A mismatch returns an error - the caller must disconnect.

struct HandshakeResult #

struct HandshakeResult {
pub:
	key           []u8
	handshake_jwt string
}

HandshakeResult carries the two outputs of the handshake preparation: the derived AES-256 key (kept server-side to build the Context) and the signed ServerToClientHandshake JWT sent to the client.

struct ServerKeyPair #

struct ServerKeyPair {
mut:
	private_key ecdsa.PrivateKey
	public_key  ecdsa.PublicKey
	freed       bool
}

ServerKeyPair holds the ephemeral P-384 ECDH/signing keypair the server generates once per session for the encryption handshake. It owns two OpenSSL key handles and must be freed when the handshake is done.

fn (ServerKeyPair) free #

fn (mut k ServerKeyPair) free()

free releases both key handles. Idempotent: crypto.ecdsa's own free is not, so the guard keeps a second call from double freeing.

fn (ServerKeyPair) public_key_der #

fn (k &ServerKeyPair) public_key_der() ![]u8

public_key_der returns the server's SubjectPublicKeyInfo (SPKI) DER bytes, used as the base64 x5u header of the ServerToClientHandshake JWT.

fn (ServerKeyPair) derive_shared_secret #

fn (k &ServerKeyPair) derive_shared_secret(client_public_key_b64 string) ![]u8

derive_shared_secret runs ECDH against the client public key (a base64 SPKI DER string from the login chain) and returns the 48-byte raw shared secret. A client key on the wrong curve is rejected while parsing, before the derive.

fn (ServerKeyPair) sign_es384 #

fn (k &ServerKeyPair) sign_es384(message []u8) ![]u8

sign_es384 signs the message with the server private key using ECDSA-SHA384 and returns the JWS-style fixed width raw R||S signature (96 bytes).