Skip to content

Instantly share code, notes, and snippets.

@Dav1dde Dav1dde/gist:3900517
Created Oct 16, 2012

Embed
What would you like to do?

Minecraft Encryption with OpenSSL

Definitions

pk = Public Key, sent with the EncryptionKeyRequest

vt = Verify Token, sent unecrypted with the EncryptionKeyRequest

ss = Shared Secret, generated by the client, 16 random bytes

ess = encrypted Shared Secret, encrypted with the servers pk

EncryptionKeyRequest, 0xfd

Packet:

[server_id : string,
 short : pk_length,
 byte[] : pk,
 short : vt_length,
 byte[] : vt
]

The server expects an EncryptionKeyResponse as answer.

To encrypt the generated ss you need to "decode" the sent pk somehow, this is done with the d2i_RSA_PUBKEY function:

#include <openssl/rsa.h>
#include <openssl/x509.h>

unsigned char* pk;
int pk_length;
// pk = packet.public_key;
// pk_length = packet.public_key_length;
RSA* rsa = d2i_RSA_PUBKEY(NULL, pk, pk_length);

An Implementation in D:

import deimos.openssl.rsa;
import deimos.openssl.x509;

RSA* decode_public(const(ubyte)[] public_key) {
    auto mem_ptr = public_key.ptr;
    return d2i_RSA_PUBKEY(null, &mem_ptr, public_key.length);
}

auto rsa = decode_public(packet.public_key[0..packet.public_key_length]);

Next step, encrypting the ss:

int ss_length = 16;
unsigned char* ss = get_random(ss_length);
unsigned char* out_buf = (unsigned char*)malloc(RSA_size(rsa))
unsigned char* ess = RSA_public_encrypt(ss_length, ss, out_buf, RSA_PKCS1_PADDING);

In D:

ubyte[] encrypt(RSA* rsa, ubyte[] data) {
    size_t s = RSA_size(rsa);
    ubyte[] buf = new ubyte[s];
    RSA_public_encrypt(cast(uint)data.length, data.ptr, buf.ptr, rsa, RSA_PKCS1_PADDING);
    return buf;
}

ubyte[] ss = get_random(16);
auto ess = rsa.encrypt(ss); // UFCS = encrypt(rsa, ss);

When you're done, free the RSA Object with RSA_free:

RSA_free(rsa);

EncryptionKeyResponse, 0xfc (S→C)

After this packet the AES (AES 128 CFB8 to be precise) encryption starts, you need the ss (from above) to initialize the AES encryption.

How to initialize the AES context in D (the function names are the same in C):

ubyte[] key = ss;
ubyte[] iv = ss;

EVP_CIPHER_CTX ctx_encrypt;
EVP_CIPHER_CTX ctx_decrypt;

EVP_CIPHER_CTX_init(&ctx_encrypt);
EVP_EncryptInit_ex(&ctx_encrypt, EVP_aes_128_cfb8(), engine, key.ptr, iv.ptr);
       
EVP_CIPHER_CTX_init(&ctx_decrypt);
EVP_DecryptInit_ex(&ctx_decrypt, EVP_aes_128_cfb8(), engine, key.ptr, iv.ptr);

uint _block_size = EVP_CIPHER_block_size(EVP_aes_128_cfb8());

ubyte[] encrypt(T)(T data) if(isArray!T) {
    return encrypt(data.ptr, (ElementEncodingType!T).sizeof*data.length);
}

ubyte[] encrypt(T)(T data, size_t size) if(isPointer!T) {
    ubyte[] out_ = new ubyte[size + _block_size-1];
    int outlen;
    if(!EVP_EncryptUpdate(&ctx_encrypt, out_.ptr, &outlen, cast(const(ubyte)*)data, cast(int)size)) {
        throw new OpenSSLException();
    }
    out_.length = outlen;

    return out_;
}

ubyte[] decrypt(T)(T data) if(isArray!T) {
    return decrypt(data.ptr, (ElementEncodingType!T).sizeof*data.length);
}

ubyte[] decrypt(T)(T data, size_t size) if(isPointer!T) {
    ubyte[] out_ = new ubyte[size + _block_size];
    int outlen;
    if(!EVP_DecryptUpdate(&ctx_decrypt, out_.ptr, &outlen, cast(const(ubyte)*)data, cast(int)size)) {
        throw new OpenSSLException();
    }
    out_.length = outlen;

    return out_;
}
@martijn-heil

This comment has been minimized.

Copy link

commented Nov 1, 2016

EncryptionKeyResponse, 0xfc is not bound to the client as you indicate with EncryptionKeyResponse, 0xfc (S→C),
it is bound to the server. So that would be EncryptionKeyResponse, 0xfc (C→S)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.