Skip to content

Instantly share code, notes, and snippets.

@esnya
Created June 26, 2016 04:46
Show Gist options
  • Save esnya/ad1d417223e8305ed769b379c439acf6 to your computer and use it in GitHub Desktop.
Save esnya/ad1d417223e8305ed769b379c439acf6 to your computer and use it in GitHub Desktop.
Wrapper classes of OpenSSL RSA/DES-GCM encrypt/decrypt for D Language
import std.algorithm;
import std.conv;
import std.datetime;
import std.exception;
import std.stdio;
extern(C) int SSL_library_init();
static this() {
SSL_library_init();
}
extern(C) {
void ERR_load_crypto_strings();
void ERR_free_strings();
}
static this() {
ERR_load_crypto_strings();
}
static ~this() {
ERR_free_strings();
}
extern(C) {
uint ERR_get_error();
char* ERR_error_string(uint code, void*);
}
class OpenSSLError : Error {
this(string msg, string file = __FILE__, size_t line = __LINE__) {
import std.string;
char[] str;
while (1) {
auto code = ERR_get_error();
if (!code) break;
str ~= ERR_error_string(code, null).fromStringz();
}
if (!str) str = "No Error".dup;
super((msg ~ str).idup, file, line);
}
}
extern(C) {
void* BN_new();
void BN_free(void* bn);
int BN_set_word(void* bn, uint w);
}
class BigNum {
void* ptr;
alias ptr this;
this() {
ptr = enforceEx!OpenSSLError(BN_new());
}
this(uint w) {
this();
opAssign(w);
}
~this() {
BN_free(ptr);
}
void opAssign(T : uint)(T w) {
enforceEx!OpenSSLError(BN_set_word(ptr, w));
}
}
unittest {
scope a = new BigNum();
}
extern(C) {
void* RSA_new();
void RSA_free(void* rsa);
int RSA_size(in void* rsa);
int RSA_generate_key_ex(void* rsa, int bits, void* e, void* cb);
int RSA_private_encrypt(int flen, ubyte *from,
ubyte *to, void *rsa, int padding);
int RSA_public_encrypt(int flen, ubyte *from,
ubyte *to, void *rsa, int padding);
int RSA_private_decrypt(int flen, ubyte *from,
ubyte *to, void *rsa, int padding);
int RSA_public_decrypt(int flen, ubyte *from,
ubyte *to, void *rsa, int padding);
}
class RSA {
static auto RSA_F4() {
return new BigNum(65537);
}
enum Padding : int {
PKCS1 = 1,
SSLV23 = 2,
NO = 3,
PKCS1_OEWAP = 4,
X931 = 5,
PKCS1_PSS =6,
}
void* ptr;
alias ptr this;
this() {
ptr = enforceEx!OpenSSLError(RSA_new());
}
this(int bits, BigNum e) {
this();
generateKey(bits, e);
}
~this() {
RSA_free(ptr);
}
@property size() {
return RSA_size(ptr);
}
void generateKey(int bits, BigNum e) {
enforceEx!OpenSSLError(RSA_generate_key_ex(ptr, bits, e, null));
}
private T[] encrypt(alias func, T = ubyte)(ubyte[] from) in {
assert(from.length <= size - 11);
} body {
return crypt!(func, T)(from);
}
private T[] decrypt(alias func, T = ubyte)(ubyte[] from) in {
assert(from.length == size);
} body {
return crypt!(func, T)(from);
}
private T[] crypt(alias func, T = ubyte)(ubyte[] from) {
auto to = new ubyte[size];
auto length = enforceEx!OpenSSLError(func(cast(int)from.length, from.ptr, to.ptr, ptr, Padding.PKCS1));
return cast(T[])(cast(void[])to[0 .. length]);
}
auto privateEncrypt(T = ubyte)(void[] from) {
return encrypt!(RSA_private_encrypt, T)(cast(ubyte[])from);
}
auto publicEncrypt(T = ubyte)(void[] from) {
return encrypt!(RSA_public_encrypt, T)(cast(ubyte[])from);
}
auto privateDecrypt(T = ubyte)(void[] from) {
return decrypt!(RSA_private_decrypt, T)(cast(ubyte[])from);
}
auto publicDecrypt(T = ubyte)(void[] from) {
return decrypt!(RSA_public_decrypt, T)(cast(ubyte[])from);
}
}
///
unittest {
scope a = new RSA(1024, RSA.RSA_F4);
assert(a.size == 1024 / 8);
auto m1 = "foobar".dup;
auto c1 = a.privateEncrypt(m1);
assert(c1.length == a.size);
assert(equal(a.publicDecrypt!char(c1), m1));
auto m2 = [1, 2, 3, 4];
auto c2 = a.privateEncrypt(m2);
assert(c2.length == a.size);
assert(equal(a.publicDecrypt!int(c2), m2));
}
extern(C) {
void* EVP_aes_256_gcm();
void* EVP_CIPHER_CTX_new();
void EVP_CIPHER_CTX_free(void* ctx);
int EVP_EncryptInit_ex(void* ctx, in void* type, void* impl, ubyte* key, ubyte* iv);
int EVP_EncryptUpdate(void *ctx, ubyte *out_,
int *outl, ubyte *in_, int inl);
int EVP_EncryptFinal_ex(void *ctx, ubyte *out_,
int *outl);
int EVP_DecryptInit_ex(void* ctx, in void* type, void* impl, ubyte* key, ubyte* iv);
int EVP_DecryptUpdate(void *ctx, ubyte *out_,
int *outl, ubyte *in_, int inl);
int EVP_DecryptFinal_ex(void *ctx, ubyte *out_,
int *outl);
}
class EvpCipherCtx {
static void* aes_256_gcm() {
return EVP_aes_256_gcm();
}
void* ptr;
alias ptr this;
this() {
ptr = enforceEx!OpenSSLError(EVP_CIPHER_CTX_new());
}
~this() {
EVP_CIPHER_CTX_free(ptr);
}
private void cryptInit(alias func)(void* type, ubyte[] key, ubyte[] iv) in {
assert(iv.length == 12);
} body {
enforceEx!OpenSSLError(func(ptr, type, null, key.ptr, iv.ptr));
}
private int cryptUpdate(alias func)(ubyte[] from, ubyte[] to) {
int length;
enforceEx!OpenSSLError(func(ptr, to.ptr, &length, from.ptr, cast(int)from.length));
enforce(length <= to.length);
return length;
}
private int cryptFinal(alias func)(ubyte[] to) {
int length;
//enforceEx!OpenSSLError(func(ptr, to.ptr, &length)); // Bug: Error on decrypt
func(ptr, to.ptr, &length);
enforce(length <= to.length);
return length;
}
private T[] crypt(alias upd, alias fin, T)(ubyte[] from, ubyte[] to) {
auto len1 = cryptUpdate!upd(from, to);
auto len2 = cryptFinal!fin(to[len1 .. $]);
return cast(T[])cast(void[])to[0 .. (len1 + len2)];
}
void encryptInit(void* type, ubyte[] key, ubyte[] iv) {
cryptInit!(EVP_EncryptInit_ex)(type, key, iv);
}
int encryptUpdate(void[] from, ubyte[] to) {
return cryptUpdate!(EVP_EncryptUpdate)(cast(ubyte[])from, to);
}
int encryptFinal(ubyte[] to) {
return cryptFinal!(EVP_EncryptFinal_ex)(to);
}
ubyte[] encrypt(void[] from, ubyte[] to) {
return crypt!(EVP_EncryptUpdate, EVP_EncryptFinal_ex, ubyte)(cast(ubyte[])from, to);
}
ubyte[] encrypt(void* type, ubyte[] key, ubyte[] iv, void[] from, ubyte[] to) {
encryptInit(type, key, iv);
return encrypt(from, to);
}
void decryptInit(void* type, ubyte[] key, ubyte[] iv) {
cryptInit!(EVP_DecryptInit_ex)(type, key, iv);
}
int decryptUpdate(ubyte[] from, ubyte[] to) {
return cryptUpdate!(EVP_DecryptUpdate)(from, to);
}
int decryptFinal(ubyte[] to) {
return cryptFinal!(EVP_DecryptFinal_ex)(to);
}
T[] decrypt(T)(ubyte[] from, T[] to) {
return crypt!(EVP_DecryptUpdate, EVP_DecryptFinal_ex, T)(from, cast(ubyte[])cast(void[])to);
}
T[] decrypt(T)(void* type, ubyte[] key, ubyte[] iv, ubyte[] from, T[] to) {
decryptInit(type, key, iv);
return decrypt(from, to);
}
}
unittest {
ubyte[] key = [
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
];
ubyte[] iv = [
1, 2, 3, 4, 5, 6,
1, 2, 3, 4, 5, 6,
];
scope ec1 = new EvpCipherCtx();
ec1.encryptInit(EvpCipherCtx.aes_256_gcm, key, iv);
auto m1 = "foobarbazhogehogehuga".dup;
auto c1buf = new ubyte[m1.length * 2];
auto c1 = ec1.encrypt(m1, c1buf);
scope dc1 = new EvpCipherCtx();
dc1.encryptInit(EvpCipherCtx.aes_256_gcm, key, iv);
auto m1buf = new char[m1.length * 2];
assert(equal(dc1.decrypt(c1, m1buf), m1));
int[] m2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
scope ctx2 = new EvpCipherCtx();
auto c2buf = new ubyte[m2.length * 4 * 2];
auto c2 = ctx2.encrypt(EvpCipherCtx.aes_256_gcm, key, iv, m2, c2buf);
auto m2buf = new int[m2.length * 2];
assert(equal(ctx2.decrypt(EvpCipherCtx.aes_256_gcm, key, iv, c2, m2buf), m2));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment