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
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);
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_;
}
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)