server.internal.encryption #
OpenSSL EVP interop for the Bedrock encryption handshake. vlib's crypto.ecdsa keeps its EVP_PKEY handle private, so we re-declare the C bindings we need (ECDH derive, SPKI emit, ES384 sign) in the same C-interop style. See vlib/crypto/ecdsa/ecdsa.c.v for the pattern this mirrors.
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 via OpenSSL EVP.
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 C.BIO #
struct C.BIO {}
struct C.BIO_METHOD #
struct C.BIO_METHOD {}
struct C.EVP_MD #
struct C.EVP_MD {}
struct C.EVP_MD_CTX #
struct C.EVP_MD_CTX {}
struct C.EVP_PKEY #
struct C.EVP_PKEY {}
struct C.EVP_PKEY_CTX #
struct C.EVP_PKEY_CTX {}
struct Context #
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, matching PocketMine's EncryptionContext.
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:
pkey &C.EVP_PKEY = unsafe { nil }
}
ServerKeyPair holds the ephemeral P-384 ECDH/signing keypair the server generates once per session for the encryption handshake. It owns the raw EVP_PKEY handle and must be freed when the handshake is done.
fn (ServerKeyPair) free #
fn (mut k ServerKeyPair) free()
free releases the underlying EVP_PKEY. Safe to call once.
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) 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 for P-384).