I am trying to replicate the below code in php to have a web based account creation script but I do not really understand what is happening before the md5 hash.
This is the code:
reg_seconds = (unsigned) regtime / 3600L;
ch = strlen (&password[0]);
_itoa (reg_seconds, &config_data[0], 10 );
//Throw some salt in the game ;)
sprintf (&password[ch], "_%s_salt", &config_data[0] );
//printf ("New password = %s\n", password );
MDString (&password[0], &MDBuffer[0] );
for (ch=0;ch<16;ch++)
sprintf (&md5password[ch*2], "%02x", (unsigned char) MDBuffer[ch]);
md5password[32] = 0;
When using the password "password" and a regtime of "399969" I get a hashed password of "9a5c041c5b37febc90ad3dc66ec62c83"
Could anyone explain what exactly is happening and what the final string is that gets hashed?
Ok let's look line by line, assuming password = "password" and regtime = 399969
reg_seconds = (unsigned) regtime / 3600L;
=> reg_seconds = 111 NB incoherent with the name, isn't is regtime % 3600L
ch = strlen (&password[0]);
=> ch = 8
_itoa (reg_seconds, &config_data[0], 10 );
=> config_data = "111"
//Throw some salt in the game ;)
sprintf (&password[ch], "_%s_salt", &config_data[0] );
=> password = "password_111_salt"
//printf ("New password = %s\n", password ); Why did not you uncomment this ?
MDString (&password[0], &MDBuffer[0] );
MDBuffer receives the binary hash
for (ch=0;ch<16;ch++)
sprintf (&md5password[ch*2], "%02x", (unsigned char) MDBuffer[ch]);
md5password[32] = 0;
md5password receives the hex encoded hash : "033f7d591eda915e708571edd255b511"
Oups it is not the expected hash !
Because 399969 is not regtime but is reg_seconds in above code... So
password = "password_399969_salt"
md5password = "9a5c041c5b37febc90ad3dc66ec62c83"
Related
Something in here is not allowing '\n' to be interpreted as a newline control character. Can someone help?
The output, when printed, seems to print
"I want this statement\nto be in newlines,\nbecause I want it\n that way.",
AND NOT
"I want this statement
to be in newlines,
because I want it
that way."
static void
append_object_to_array(json_t* my_json_array)
{
const char* KEY = "KEY";
const char* VALUE = "I want this statement\nto be in newlines,\nbecause I want it\n that way.";
json_t* my_json_obj = json_object();
json_object_set_new(my_json_obj , KEY, json_string(VALUE));
json_array_append_new(my_json_array, my_json_obj);
}
int main()
{
json_t* json_array = json_array();
append_object_to_array(json_array);
json_decref(my_json_array);
char* output = NULL;
output = json_dumps(my_json_array, JSON_ENCODE_ANY);
/* My other failed attempts at getting this to work
* output = json_dumps(my_json_array, JSON_ESCAPE_SLASH);
* output = json_dumps(my_json_array, JSON_INDENT(n));
*/
char* expected_output = "[{\"KEY\": \"I want this statement\nto be in newlines,\nbecause I want it\n that way.\"}]";
if (strcmp(expected_output, output) != 0) {
printf("Expected: %s", expected_output);
printf("\n");
printf("Actual: %s", output);
} else {
printf("Success");
}
return 0;
}
P.S. I have checked the following possible duplicates (I may have missed something), but they don't seem to fit what I am looking for.
Are multi-line strings allowed in JSON?
Can a JSON value contain a multiline string
I want to do an encryption / decryption program in C based on something I did in perl. The compiled perl program is 2MB so I figure if I write it in C it will be a smaller executable size.
My problem is, while I get it to encrypt, I can't get it to decrypt. It's been literally ages since I last used C so I forgot a lot of stuff. Someone please enlighten me to what I'm doing wrong here? Thanks.
/*
============================================================================
Name : test-c.c
Description : Testing Project
Trying to do a C version of this perl code:
my $cipher = Crypt::CBC->new( -key => $salt_key, -cipher => 'DES' -header => 'none');
my $enc_text = $cipher->encrypt_hex($text);
Requires : -lcrypt
References :
Function: cbc_crypt (char *key, char *blocks, unsigned len, unsigned mode, char *ivec)
GNU C Library: DES Encryption (http://www.gnu.org/software/libc/manual/html_node/DES-Encryption.html#DES-Encryption)
cbc_crypt (http://unix.derkeiler.com/Newsgroups/comp.unix.programmer/2012-10/msg00023.html)
============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rpc/des_crypt.h>
int main(void) {
char key[] = "aBcDeFg1";
char pass[] = "mypass1234test";
char encbuff[] = "87654321";
char decbuff[] = "87645321";
int buffsize;
int result;
des_setparity(key);
/* Encrypt pass, result is in encbuff */
buffsize = sizeof(pass);
/* Add to pass to ensure size is divisable by 8. */
while (buffsize % 8) {
pass[buffsize++] = '\0';
}
printf("Encrypted: ");
result = cbc_crypt(key, pass, buffsize, DES_ENCRYPT | DES_SW, encbuff);
if (DES_FAILED(result) || strcmp(encbuff, "") == 0) {
if(strcmp(encbuff, "") == 0) {
printf("*** Null Output ***\n");
} else {
printf("*** Encryption Error ***\n");
}
} else {
printf("%s\n", encbuff);
}
/* Decrypt encbuff, result is in decbuff */
/* FIXME: Decryption doesn't work:
Encrypted: ,�7&���8
Decrypted: *** Decryption Error ***
*/
buffsize = sizeof(encbuff);
/* Add to pass to ensure size is divisable by 8. */
while (buffsize % 8) {
encbuff[buffsize++] = '\0';
}
printf("Decrypted: ");
result = cbc_crypt(key, encbuff, buffsize, DES_DECRYPT | DES_SW, decbuff);
if(DES_FAILED(result) || strcmp(decbuff, "") == 0) {
if(strcmp(encbuff, "") == 0) {
printf("*** Null Output ***\n");
} else {
printf("*** Decryption Error ***\n");
}
} else {
printf("%s\n", decbuff);
}
return 0;
}
As noticed by #Brian_Sidebotham , using char str[10];buffsize=sizeof(str);...str[buffsize++]='\0' is not a good thing in c. Writing beyond the end of an array may trigger undefined behavior. It could work or deliver wrong results or stop due to a segmentation fault. So the first thing to do is to work with larger buffers. For instance :
char key[9];
sprintf(key,"12345678");
char password[420];
sprintf(password,"i am not going to write my password here.");
Notice that it is 9, not 8, to leave some place for the null terminating character \0 that is automatically added by sprintf() to build a valid c string.
Moreover, the last parameter of cbc_crypt() is not the output. It is not the encrypted string but an 8-byte initialization vector for the chaining. That way, if you need to encrypt multiple strings, it prevents regularities in the clear text from appearing in the cipher text. As the cipher text is to be decrypted, the same initialization vector must be provided. Encryption is performed in place : the input string pass will be overwritten by the encrypted text.
Finally, make sure that your strings are large enough because the encrypted text seems to be slightly larger that the clear text.
The following code is expected to work. It is compiled by gcc main.c -o main
/*
============================================================================
Name : test-c.c
Description : Testing Project
Trying to do a C version of this perl code:
my $cipher = Crypt::CBC->new( -key => $salt_key, -cipher => 'DES' -header => 'none');
my $enc_text = $cipher->encrypt_hex($text);
Requires : -lcrypt
References :
Function: cbc_crypt (char *key, char *blocks, unsigned len, unsigned mode, char *ivec)
GNU C Library: DES Encryption (http://www.gnu.org/software/libc/manual/html_node/DES-Encryption.html#DES-Encryption)
cbc_crypt (http://unix.derkeiler.com/Newsgroups/comp.unix.programmer/2012-10/msg00023.html)
============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rpc/des_crypt.h>
#define BUFFSIZE 420
int main(void) {
// char key[] = "aBcDeFg1";
char key[9];
sprintf(key,"aBcDeFg1");
//char pass[] = "mypass1234test";
char pass[BUFFSIZE];
sprintf(pass,"mypass1234test");
// char encbuff[] = "87654321";
char ivec[9];
sprintf(ivec,"87654321");
// char decbuff[] = "87645321";
char ivectemp[9];
strcpy(ivectemp,ivec);
int buffsize;
int result;
des_setparity(key);
/* Encrypt pass, result is in encbuff */
buffsize = strlen(pass);
printf("buffsize is %d\n",buffsize);
/* Add to pass to ensure size is divisable by 8. */
while (buffsize % 8 && buffsize<BUFFSIZE) {
pass[buffsize++] = '\0';
}
printf("pass is %s\n",pass);
printf("buffsize is %d\n",buffsize);
printf("Encrypted: ");
result = cbc_crypt(key, pass, buffsize, DES_ENCRYPT | DES_SW, ivectemp);
if (DES_FAILED(result) || strcmp(pass, "") == 0) {
if(strcmp(pass, "") == 0) {
printf("*** Null Output ***\n");
} else {
printf("*** Encryption Error ***\n");
}
} else {
printf("%s\n", pass);
}
/* Decrypt encbuff, result is in decbuff */
/* FIXME: Decryption doesn't work:
Encrypted: ,�7&���8
Decrypted: *** Decryption Error ***
*/
buffsize = strlen(pass);
printf("buffsize is %d\n",buffsize);
/* Add to pass to ensure size is divisable by 8. */
while (buffsize % 8 && buffsize<BUFFSIZE) {
pass[buffsize++] = '\0';
}
printf("buffsize is %d\n",buffsize);
printf("Decrypted: ");
//keeping the same initialization vector for decrypting, encrypt has altered ivectemp
strcpy(ivectemp,ivec);
result = cbc_crypt(key, pass, buffsize, DES_DECRYPT | DES_SW, ivectemp);
if(DES_FAILED(result) || strcmp(pass, "") == 0) {
if(strcmp(pass, "") == 0) {
printf("*** Null Output ***\n");
} else {
printf("*** Decryption Error ***\n");
}
} else {
printf("%s\n",pass);
}
return 0;
}
Now... A few words about security... According to the wikipedia page about the Data Encryption Standard NBS DES :
DES is now considered to be insecure for many applications. This is mainly due to the 56-bit key size being too small; in January, 1999, distributed.net and the Electronic Frontier Foundation collaborated to publicly break a DES key in 22 hours and 15 minutes
You may switch to the implementation of AES by the openssl library for instance. See How to do encryption using AES in Openssl or Simple AES encryption decryption with openssl library in C or AES (aes-cbc-128, aes-cbc-192, aes-cbc-256) encryption/decryption with openssl C for instance.
I have this function to convert html in put to xhtml:
char* cleanhtml(char* localhtml)
{
char *buffer_;
static char *cleansed_buffer;
// uses Libtidy to convert the buffer to XML
TidyBuffer output = {0};
TidyBuffer errbuf = {0};
int rc = -1;
bool ok;
TidyDoc tdoc;
tdoc = tidyCreate(); // Initialize "document"
ok = tidyOptSetBool( tdoc, TidyXhtmlOut, yes ); // Convert to XHTML
rc = tidySetErrorBuffer( tdoc, &errbuf ); // Capture diagnostics
rc = tidyParseString(tdoc, localhtml); // Parse the input
rc = tidyCleanAndRepair( tdoc ); // Tidy it up!
rc = tidyRunDiagnostics( tdoc ); // Kvetch// If error, force output.
rc = ( tidyOptSetBool(tdoc, TidyForceOutput, yes) ? rc : -1 );
rc = tidySaveBuffer( tdoc, &output ); // Pretty Print
cleansed_buffer = (char*) malloc(output.size + 1);
memcpy (cleansed_buffer, (char*)output.bp,output.size);
tidyBufFree( &output );
tidyBufFree( &errbuf );
tidyRelease( tdoc );
return cleansed_buffer;
}
But the problem is it is not working correctly...for some reason the xhtml being given out is not being able to be parsed and it is wrong format. So how can libtidy be used to convert a given string(huge string) to xhtml in right format.
And can somebody please give me link to the latest libtidy (dlls,includes) for C.I can't find it anywhere...not even on there website..
Some very strange things happen in my source code.
The following function works well and it prints 'y' when the password is correct and prints 'n' when it is incorrect. But if i add some UART1_Write and Delay functions to the else statement the bug comes out and even if the password is "zxc" (correct) it ALWAYS enters the else statement.
I'm using MikroC PRO for PIC v6.0.0, the robot system is made of PIC18F452 and RN-42 bluetooth module connected to it. I am testing with a laptop with a bluetooth and TeraTerm.
For more info: http://instagram.com/p/pLnU9eDL8z/#
Here it is the well working routine:
void authenticate() {
char *input = "";
char *password = "zxc\0";
unsigned char ready = 0;
while (connected && !ready) {
if (UART1_Data_Ready()) {
UART1_Read_Text(input, "|", 17);
strcat(input, "\0");
if (strcmp(input, password) == 0) {
UART1_Write('y');
ready = 1;
} else {
UART1_Write('n');
ready = 1;
}
}
}
}
This version of the routine ALWAYS goes in the ELSE statement of the strcmp(input, password) == 0 part:
void authenticate() {
char *input = "";
char *password = "zxc\0";
unsigned char ready = 0;
while (connected && !ready) {
if (UART1_Data_Ready()) {
UART1_Read_Text(input, "|", 17);
strcat(input, "\0");
if (strcmp(input, password) == 0) {
UART1_Write('y');
ready = 1;
} else {
UART1_Write('n');
Delay_ms(100);
UART1_Write('$');
Delay_ms(100);
UART1_Write('$');
Delay_ms(100);
UART1_Write('$');
Delay_ms(100);
UART1_Write('K');
Delay_ms(100);
UART1_Write(',');
Delay_ms(100);
UART1_Write('-');
Delay_ms(100);
UART1_Write('-');
Delay_ms(100);
UART1_Write('-');
Delay_ms(100);
UART1_Write('\n');
ready = 1;
}
}
}
}
It is important to send all these addition symbols in order to get RN-42 into command mode and disconnect the user if the password is wrong.
Please help me solve the problem. Any ideas appreciated!
As others have pointed out in the comments section a major issue with your code is that you are trying to store the UART data to memory that does not belong to you.
When you declare char *input = "";, you haven't actually allocated any space except for a single byte that stores '\0'. Then, when you use UART1_Read_Text(), you tell that function you may have up to 17 characters that will be read before finding the delimiter - all of which should be stored at the location pointed to by input.
The description of that library function can be found here. Also, based on the library description it looks like UART1_Read_Text() already adds the null-termination to the UART data. I base this assumption off the description of UARTx_Write_Text and the example that they provide on their website. However, I would recommend that you verify that is indeed the case.
Also, your initialization of password is redundant and char *password = "zxc\0" should be changed to char *password = "zxc". When you declare a string literal using double quotation marks it is automatically null-terminated. This excerpt is from "C in a Nutshell":
A string literal consists of a sequence of characters (and/or escape sequences) enclosed in double quotation marks... A string literal is a static array of char that contains character codes followed by a string terminator, the null character \0... The empty string "" occupies exactly one byte in memory, which holds the terminating null character.
Based on the above, I would go about it a little more like this:
#define MAX_NUM_UART_RX_CHARACTERS 17
void authenticate()
{
char input[MAX_NUM_UART_RX_CHARACTERS + 1];
char *password = "zxc";
unsigned char ready = 0;
while (connected && !ready)
{
if (UART1_Data_Ready())
{
UART1_Read_Text(input, "|", MAX_NUM_UART_RX_CHARACTERS);
if (strcmp(input, password) == 0)
{
UART1_Write('y');
ready = 1;
}
else
{
UART1_Write('n');
ready = 1;
}
}
}
}
For example, the command:
openssl enc -aes-256-cbc -a -in test.txt -k pinkrhino -nosalt -p -out openssl_output.txt
outputs something like:
key = 33D890D33F91D52FC9B405A0DDA65336C3C4B557A3D79FE69AB674BE82C5C3D2
iv = 677C95C475C0E057B739750748608A49
How is that key generated? (C code as an answer would be too awesome to ask for :) )
Also, how is the iv generated?
Looks like some kind of hex to me.
OpenSSL uses the function EVP_BytesToKey. You can find the call to it in apps/enc.c. The enc utility used to use the MD5 digest by default in the Key Derivation Algorithm (KDF) if you didn't specify a different digest with the -md argument. Now it uses SHA-256 by default. Here's a working example using MD5:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>
int main(int argc, char *argv[])
{
const EVP_CIPHER *cipher;
const EVP_MD *dgst = NULL;
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
const char *password = "password";
const unsigned char *salt = NULL;
int i;
OpenSSL_add_all_algorithms();
cipher = EVP_get_cipherbyname("aes-256-cbc");
if(!cipher) { fprintf(stderr, "no such cipher\n"); return 1; }
dgst=EVP_get_digestbyname("md5");
if(!dgst) { fprintf(stderr, "no such digest\n"); return 1; }
if(!EVP_BytesToKey(cipher, dgst, salt,
(unsigned char *) password,
strlen(password), 1, key, iv))
{
fprintf(stderr, "EVP_BytesToKey failed\n");
return 1;
}
printf("Key: "); for(i=0; i<cipher->key_len; ++i) { printf("%02x", key[i]); } printf("\n");
printf("IV: "); for(i=0; i<cipher->iv_len; ++i) { printf("%02x", iv[i]); } printf("\n");
return 0;
}
Example usage:
gcc b2k.c -o b2k -lcrypto -g
./b2k
Key: 5f4dcc3b5aa765d61d8327deb882cf992b95990a9151374abd8ff8c5a7a0fe08
IV: b7b4372cdfbcb3d16a2631b59b509e94
Which generates the same key as this OpenSSL command line:
openssl enc -aes-256-cbc -k password -nosalt -p < /dev/null
key=5F4DCC3B5AA765D61D8327DEB882CF992B95990A9151374ABD8FF8C5A7A0FE08
iv =B7B4372CDFBCB3D16A2631B59B509E94
OpenSSL 1.1.0c changed the digest algorithm used in some internal components. Formerly, MD5 was used, and 1.1.0 switched to SHA256. Be careful the change is not affecting you in both EVP_BytesToKey and commands like openssl enc.
If anyone is looking for implementing the same in SWIFT
I converted the EVP_BytesToKey in swift
/*
- parameter keyLen: keyLen
- parameter ivLen: ivLen
- parameter digest: digest e.g "md5" or "sha1"
- parameter salt: salt
- parameter data: data
- parameter count: count
- returns: key and IV respectively
*/
open static func evpBytesToKey(_ keyLen:Int, ivLen:Int, digest:String, salt:[UInt8], data:Data, count:Int)-> [[UInt8]] {
let saltData = Data(bytes: UnsafePointer<UInt8>(salt), count: Int(salt.count))
var both = [[UInt8]](repeating: [UInt8](), count: 2)
var key = [UInt8](repeating: 0,count: keyLen)
var key_ix = 0
var iv = [UInt8](repeating: 0,count: ivLen)
var iv_ix = 0
var nkey = keyLen;
var niv = ivLen;
var i = 0
var addmd = 0
var md:Data = Data()
var md_buf:[UInt8]
while true {
addmd = addmd + 1
md.append(data)
md.append(saltData)
if(digest=="md5"){
md = NSData(data:md.md5()) as Data
}else if (digest == "sha1"){
md = NSData(data:md.sha1()) as Data
}
for _ in 1...(count-1){
if(digest=="md5"){
md = NSData(data:md.md5()) as Data
}else if (digest == "sha1"){
md = NSData(data:md.sha1()) as Data
}
}
md_buf = Array (UnsafeBufferPointer(start: md.bytes, count: md.count))
// md_buf = Array(UnsafeBufferPointer(start: md.bytes.bindMemory(to: UInt8.self, capacity: md.count), count: md.length))
i = 0
if (nkey > 0) {
while(true) {
if (nkey == 0){
break
}
if (i == md.count){
break
}
key[key_ix] = md_buf[i];
key_ix = key_ix + 1
nkey = nkey - 1
i = i + 1
}
}
if (niv > 0 && i != md_buf.count) {
while(true) {
if (niv == 0){
break
}
if (i == md_buf.count){
break
}
iv[iv_ix] = md_buf[i]
iv_ix = iv_ix + 1
niv = niv - 1
i = i + 1
}
}
if (nkey == 0 && niv == 0) {
break
}
}
both[0] = key
both[1] = iv
return both
}
I use CryptoSwift for the hash.
This is a much cleaner way as apples does not recommend OpenSSL in iOS
UPDATE : Swift 3
Here is a version for mbedTLS / Polar SSL - tested and working.
typedef int bool;
#define false 0
#define true (!false)
//------------------------------------------------------------------------------
static bool EVP_BytesToKey( const unsigned int nDesiredKeyLen, const unsigned char* salt,
const unsigned char* password, const unsigned int nPwdLen,
unsigned char* pOutKey, unsigned char* pOutIV )
{
// This is a re-implemntation of openssl's password to key & IV routine for mbedtls.
// (See openssl apps/enc.c and /crypto/evp/evp_key.c) It is not any kind of
// standard (e.g. PBKDF2), and it only uses an interation count of 1, so it's
// pretty crappy. MD5 is used as the digest in Openssl 1.0.2, 1.1 and late
// use SHA256. Since this is for embedded system, I figure you know what you've
// got, so I made it compile-time configurable.
//
// The signature has been re-jiggered to make it less general.
//
// See: https://wiki.openssl.org/index.php/Manual:EVP_BytesToKey(3)
// And: https://www.cryptopp.com/wiki/OPENSSL_EVP_BytesToKey
#define IV_BYTE_COUNT 16
#if BTK_USE_MD5
# define DIGEST_BYTE_COUNT 16 // MD5
#else
# define DIGEST_BYTE_COUNT 32 // SHA
#endif
bool bRet;
unsigned char md_buf[ DIGEST_BYTE_COUNT ];
mbedtls_md_context_t md_ctx;
bool bAddLastMD = false;
unsigned int nKeyToGo = nDesiredKeyLen; // 32, typical
unsigned int nIVToGo = IV_BYTE_COUNT;
mbedtls_md_init( &md_ctx );
#if BTK_USE_MD5
int rc = mbedtls_md_setup( &md_ctx, mbedtls_md_info_from_type( MBEDTLS_MD_MD5 ), 0 );
#else
int rc = mbedtls_md_setup( &md_ctx, mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ), 0 );
#endif
if (rc != 0 )
{
fprintf( stderr, "mbedutils_md_setup() failed -0x%04x\n", -rc );
bRet = false;
goto exit;
}
while( 1 )
{
mbedtls_md_starts( &md_ctx ); // start digest
if ( bAddLastMD == false ) // first time
{
bAddLastMD = true; // do it next time
}
else
{
mbedtls_md_update( &md_ctx, &md_buf[0], DIGEST_BYTE_COUNT );
}
mbedtls_md_update( &md_ctx, &password[0], nPwdLen );
mbedtls_md_update( &md_ctx, &salt[0], 8 );
mbedtls_md_finish( &md_ctx, &md_buf[0] );
//
// Iteration loop here in original removed as unused by "openssl enc"
//
// Following code treats the output key and iv as one long, concatentated buffer
// and smears as much digest across it as is available. If not enough, it takes the
// big, enclosing loop, makes more digest, and continues where it left off on
// the last iteration.
unsigned int ii = 0; // index into mb_buf
if ( nKeyToGo != 0 ) // still have key to fill in?
{
while( 1 )
{
if ( nKeyToGo == 0 ) // key part is full/done
break;
if ( ii == DIGEST_BYTE_COUNT ) // ran out of digest, so loop
break;
*pOutKey++ = md_buf[ ii ]; // stick byte in output key
nKeyToGo--;
ii++;
}
}
if ( nIVToGo != 0 // still have fill up IV
&& // and
ii != DIGEST_BYTE_COUNT // have some digest available
)
{
while( 1 )
{
if ( nIVToGo == 0 ) // iv is full/done
break;
if ( ii == DIGEST_BYTE_COUNT ) // ran out of digest, so loop
break;
*pOutIV++ = md_buf[ ii ]; // stick byte in output IV
nIVToGo--;
ii++;
}
}
if ( nKeyToGo == 0 && nIVToGo == 0 ) // output full, break main loop and exit
break;
} // outermost while loop
bRet = true;
exit:
mbedtls_md_free( &md_ctx );
return bRet;
}
If anyone passing through here is looking for a working, performant reference implementation in Haskell, here it is:
import Crypto.Hash
import qualified Data.ByteString as B
import Data.ByteArray (convert)
import Data.Monoid ((<>))
evpBytesToKey :: HashAlgorithm alg =>
Int -> Int -> alg -> Maybe B.ByteString -> B.ByteString -> (B.ByteString, B.ByteString)
evpBytesToKey keyLen ivLen alg mSalt password =
let bytes = B.concat . take required . iterate go $ hash' passAndSalt
(key, rest) = B.splitAt keyLen bytes
in (key, B.take ivLen rest)
where
hash' = convert . hashWith alg
required = 1 + ((keyLen + ivLen - 1) `div` hashDigestSize alg)
passAndSalt = maybe password (password <>) mSalt
go = hash' . (<> passAndSalt)
It uses hash algorithms provided by the cryptonite package. The arguments are desired key and IV size in bytes, the hash algorithm to use (like e.g. (undefined :: MD5)), optional salt and the password. The result is a tuple of key and IV.