1 module ddata.jwt.sign; 2 3 import std.format; 4 5 import deimos.openssl.pem; 6 import deimos.openssl.rsa; 7 import deimos.openssl.hmac; 8 import deimos.openssl.sha; 9 import deimos.openssl.err; 10 11 import ddata.common; 12 import ddata.jwt.algorithm; 13 14 // These functions are added to the bindings post version 2.0, but vibe-d seems to 15 // import version ~>1.0 of the bindings. So you get compile errors when including 16 // in a vibe project. This is for that. 17 static if (!__traits(compiles, { 18 auto ctx = HMAC_CTX_new; 19 HMAC_CTX_reset(ctx); 20 HMAC_CTX_free(ctx); 21 })) { 22 extern(C) nothrow { 23 HMAC_CTX * HMAC_CTX_new(); 24 void HMAC_CTX_free(HMAC_CTX *ctx); 25 void HMAC_CTX_reset(HMAC_CTX * ctx); 26 } 27 } 28 29 private string signHs(string message, string key, uint digestLength, const(EVP_MD)* evp) @trusted { 30 auto signedData = new ubyte[digestLength]; 31 32 auto ctx = HMAC_CTX_new(); 33 if (ctx is null) { 34 throw new Exception("Failed to create HMAC context"); 35 } 36 scope(exit) HMAC_CTX_free(ctx); 37 38 if (!HMAC_Init_ex(ctx, key.ptr, cast(int)key.length, evp, null)) { 39 throw new Exception("Failed to initialize HMAC context - %s".format(getLastError)); 40 } 41 if (!HMAC_Update(ctx, cast(const(ubyte)*)message.ptr, cast(ulong)message.length)) { 42 throw new Exception("Failed to update HMAC - %s".format(getLastError)); 43 } 44 if (!HMAC_Final(ctx, cast(ubyte*)signedData.ptr, &digestLength)) { 45 throw new Exception("Failed to finalize HMAC - %s".format(getLastError)); 46 } 47 48 return cast(string)signedData; 49 } 50 51 private string signRs(string message, string key, uint digestLength, int type) @trusted { 52 auto sha256 = new ubyte[digestLength]; 53 SHA256(cast(const(ubyte)*)message.ptr, message.length, sha256.ptr); 54 55 auto signedData = new ubyte[digestLength * 8]; 56 57 RSA* ctx = RSA_new(); 58 if (ctx is null) { 59 throw new Exception("Failed to create RSA context - %s".format(getLastError)); 60 } 61 scope(exit) RSA_free(ctx); 62 63 BIO* bio = BIO_new_mem_buf(cast(char*)key.ptr, cast(int)key.length); 64 if (bio is null) { 65 throw new Exception("Failed to load key in memory bio - %s".format(getLastError)); 66 } 67 scope(exit) BIO_free(bio); 68 69 RSA* rsaPrivate = PEM_read_bio_RSAPrivateKey(bio, &ctx, null, null); 70 if(rsaPrivate is null) { 71 throw new Exception("Failed to create RSA private key - %s".format(getLastError)); 72 } 73 if (!RSA_sign(type, cast(const(ubyte)*)sha256.ptr, digestLength, signedData.ptr, &digestLength, rsaPrivate)) { 74 throw new Exception("Failed to sign RSA message digest - %s".format(getLastError)); 75 } 76 77 return cast(string)signedData; 78 } 79 80 package string sign(string message, string key, Algorithm algorithm = Algorithm.hs256) @safe { 81 final switch (algorithm) { 82 case Algorithm.hs256: 83 return signHs(message, key, SHA256_DIGEST_LENGTH, () @trusted { return EVP_sha256(); } ()); 84 case Algorithm.rs256: { 85 return signRs(message, key, SHA256_DIGEST_LENGTH, NID_sha256); 86 } 87 } 88 }