Created
June 26, 2016 04:46
-
-
Save esnya/ad1d417223e8305ed769b379c439acf6 to your computer and use it in GitHub Desktop.
Wrapper classes of OpenSSL RSA/DES-GCM encrypt/decrypt for D Language
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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