How good an encryption algorithm is this?

B

Bonj

I was in need of an encryption algorithm to the following requirements:
1) Must be capable of encrypting strings to a byte array, and decyrpting
back again to the same string
2) Must have the same algorithm work with strings that may or may not be
unicode
3) Number of bytes back must either be <= number of _TCHARs in *
sizeof(_TCHAR), or the relation between output size and input size can be
calculated simply. Has to take into account the null terminator on the end
of the string.
4) Encryption algorithm must also return the exact number of bytes of the
encrypted data
I was struggling to get the CryptoAPI to work, with CALG_RC4 it didn't
encrypt at all, and with CALG_RC2 it didn't decrypt at all (both claimed to
have succeeded, but with the former, the encrypted string was exactly the
same as the input, and with the latter, the decrypted string was exactly the
same as the encrypted string) - and I thought I'd done everything right (at
the bottom if you reckon you can spot where I havent...)

So I decided to invent my own algorithm, and I just wanted anybody's opinion
on how secure this could be compared to the Win32 API version.
First, a C# program generates an array of 256 bytes, as such:
using System;
using System.Security.Cryptography;
class Class1
{
[STAThread]
static void Main()
{
byte[] b_raw = new byte[256];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(b_raw);
Console.Write("const BYTE b[] = {");
for(int i = 0; i < 256; i += 8)
{
Console.WriteLine("0x{0:x}, 0x{1:x}, 0x{2:x}, 0x{3:x}, 0x{4:x}, 0x{5:x},
0x{6:x}, 0x{7:x},",
b_raw, b_raw[i+1], b_raw[i+2], b_raw[i+3], b_raw[i+4], b_raw[i+5],
b_raw[i+6], b_raw[i+7]);
}
}
}
This is run twice, so I have two arrays of 256 bytes, say key1 and key2.
These are then hardcoded into the C++ encryption algorithm.
Then, the C++ encryption algorithm goes as such:
It memcpys the _TCHAR array to a byte array, then loops round each byte of
this array.
For each byte, it gets the value of key1[n] (where n is the byte number),
and calls this 'b_current_indir' (the starting 'indirection level').
Then, it gets the value of key2[n] and calls this 'levels' - the number of
indirection levels.
Then, an inner loop runs 'levels' times - and on each loop the following
happens: the current byte of the data to be encrypted (dictated by the outer
loop) is XORed with key2[b_current_indir], and THEN, b_current_indir is
reassigned to take on the value of key2[b_current_indir].

The whole C++ algorithm is defined as such:
void Decrypt(BYTE* orig, long bytelen, _TCHAR* dataout)
{
BYTE* b_temp = new BYTE[bytelen];
#ifdef _DEBUG
ZeroMemory(b_temp, bytelen);
#endif
for(long bytenum = 0; bytenum < bytelen; bytenum++)
{
BYTE levels = b[bytenum % 256],
b_current_indir = b2[bytenum % 256]; //always starts the same
b_temp[(long)bytenum] = orig[(long)bytenum];
for(long level = 0; level < levels; level++)
{
b_temp[(long)bytenum] ^= b_current_indir;
b_current_indir = b2[(long)b_current_indir];
}
}
memcpy(dataout, b_temp, bytelen);
delete[] b_temp;
}

void Encrypt(_TCHAR* orig, long textlen, BYTE* dataout)
{
long bytelen = textlen * sizeof(_TCHAR);
BYTE* b_temp = new BYTE[bytelen];
#ifdef _DEBUG
ZeroMemory(b_temp, bytelen);
#endif
memcpy(b_temp, orig, bytelen);
for(long bytenum = 0; bytenum < bytelen; bytenum++)
{
BYTE levels = b[(long)(bytenum % 256)],
b_current_indir = b2[(long)(bytenum % 256)];
for(long level = 0; level < levels; level++)
{
b_temp[(long)bytenum] ^= b_current_indir;
b_current_indir = b2[(long)b_current_indir];
}
}
memcpy(dataout, b_temp, bytelen);
delete[] b_temp;
}

int main()
{
LPTSTR testtext = _T("TheMagicBonj");
long textlen = _tcslen(testtext) + 1;
BYTE* b_enc = new BYTE[textlen * sizeof(_TCHAR)];
#ifdef _DEBUG
ZeroMemory(b_enc, textlen * sizeof(_TCHAR));
#endif
Encrypt(testtext, textlen, b_enc);
_TCHAR* t_enc = new _TCHAR[textlen];
ZeroMemory(t_enc, textlen * sizeof(_TCHAR));
memcpy(t_enc, b_enc, textlen * sizeof(_TCHAR));
_tprintf(_T("The encrypted text is \"%s\"\n"), t_enc);
delete[] t_enc;

_TCHAR* t_dec = new _TCHAR[textlen];
Decrypt(b_enc, textlen * sizeof(_TCHAR), t_dec);
_tprintf(_T("The decrypted text is \"%s\"\n"), t_dec);
delete[] t_dec;

}

It seems to work to my eyes, but is it a pile of rubbish?

My initial thoughts were that somebody could crack it by disassembling the
machine code into assembly language, discovering where the global namespace
section of the DLL file was and deriving key1 and key2 from that, and then
just plugging them back through the algorithm, without necessarily
understanding the algorithm from the assembly language. But even simpler
than that, if they discovered that "it was probably *this* DLL that created
*that* encrypted data, so if I find out how to call the DLL, I can decrypt
it". To counter that, I could put the key in the application very easily.
But does this help? Could someone with a knowledge of assembly language
guess with a reasonable degree of accuracy which bit of data in a PE file
was likely to be a key to an encryption algorithm?
Wouldn't it be just as easy for someone wanting to crack it to still do that
if I had written a DLL that used the Win32 API Crypto functions? If not, why
not? Is the effectiveness of private key cryptography only as good as how
well you can hide the key?

Please give as many thoughts as possible...

My (unsuccessful) attempt to use the Win32 API is as follows:
BOOL GetData(_TCHAR* datain, long lendatain)
{
HCRYPTPROV hCryptProv;
HCRYPTHASH hCryptHash;
HCRYPTKEY hCryptKey;
DWORD bytelen = (lendatain + 10) * sizeof(_TCHAR), databack = 0, bytesback
= 0;
BYTE* bData = new BYTE[bytelen];
_TCHAR* cryptdata = new _TCHAR[lendatain],
* decryptdata = new _TCHAR[lendatain];

BOOL bSuccess = CryptAcquireContext(&hCryptProv, keysetname, NULL,
PROV_RSA_FULL, CRYPT_NEWKEYSET);
if(!bSuccess) bSuccess |= CryptAcquireContext(&hCryptProv, keysetname,
NULL, PROV_RSA_FULL, 0);
memcpy(bData, datain, bytelen);
bSuccess &= CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hCryptHash);
bSuccess &= CryptHashData(hCryptHash, (BYTE*)textkey, _tcslen(textkey), 0);
bSuccess &= CryptDeriveKey(hCryptProv, CALG_RC2, hCryptHash, 0,
&hCryptKey);
bSuccess &= CryptEncrypt(hCryptKey, hCryptHash, TRUE, 0, bData, &bytesback,
bytelen);
CryptDestroyKey(hCryptKey);
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProv, 0);
memcpy(cryptdata, bData, bytesback);
_tprintf(_T("The encrypted data is \"%s\"\n"), cryptdata);


bSuccess = CryptAcquireContext(&hCryptProv, keysetname, NULL,
PROV_RSA_FULL, CRYPT_NEWKEYSET);
if(!bSuccess) bSuccess |= CryptAcquireContext(&hCryptProv, keysetname,
NULL, PROV_RSA_FULL, 0);
bSuccess &= CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hCryptHash);
bSuccess &= CryptHashData(hCryptHash, (BYTE*)textkey, _tcslen(textkey), 0);
bSuccess &= CryptDeriveKey(hCryptProv, CALG_RC2, hCryptHash, 0,
&hCryptKey);
bSuccess &= CryptDecrypt(hCryptKey, hCryptHash, TRUE, 0, bData,
&bytesback);
memcpy(decryptdata, bData, bytesback);
databack = (DWORD)(bytelen / sizeof(_TCHAR));
_tprintf(_T("The decrypted data is \"%s\"\n"), decryptdata);

CryptDestroyKey(hCryptKey);
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProv, 0);

delete[] bData;
delete[] cryptdata;
delete[] decryptdata;
CryptDestroyKey(hCryptKey);
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProv, 0);

return bSuccess;
}

void test(_TCHAR* string)
{
GetData(string, _tcslen(string));
}

int _tmain()
{
test(_T("TheMagicBonj"));
return 0;
}
The output is:

The encrypted data is "^ðnÍ&lÕeMagicBo"
The decrypted data is "@2"

which is disappointing.
 
N

Nicholas Paldino [.NET/C# MVP]

Bonj,

See inline:
1) Must be capable of encrypting strings to a byte array, and decyrpting
back again to the same string

All encryption algorithms do this. If you couldn't decrypt back, then
it would be a hash.
2) Must have the same algorithm work with strings that may or may not be
unicode

This should not be an issue. Encryption works on byte streams, not
strings. As long as the string can be converted to/from a byte stream, you
won't have a problem.
3) Number of bytes back must either be <= number of _TCHARs in *
sizeof(_TCHAR), or the relation between output size and input size can be
calculated simply. Has to take into account the null terminator on the end
of the string.

The null terminator at the end of a string is just another byte. If you
encrypt it, it's going to take up space. If you don't need it, don't use it
then. As for the output size, I believe that it is the same as the input
size.
4) Encryption algorithm must also return the exact number of bytes of the
encrypted data

Do you mean that it will return the number of bytes it would take to
encrypt the data? Since most algorithms return the same size, this won't be
a problem.

I would recommend against rolling your encryption. Rather, you should
use one of the classes in the System.Security.Cryptography namespace. If
you need compression as well, then you can apply that after the encryption.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)
I was struggling to get the CryptoAPI to work, with CALG_RC4 it didn't
encrypt at all, and with CALG_RC2 it didn't decrypt at all (both claimed
to have succeeded, but with the former, the encrypted string was exactly
the same as the input, and with the latter, the decrypted string was
exactly the same as the encrypted string) - and I thought I'd done
everything right (at the bottom if you reckon you can spot where I
havent...)

So I decided to invent my own algorithm, and I just wanted anybody's
opinion on how secure this could be compared to the Win32 API version.
First, a C# program generates an array of 256 bytes, as such:
using System;
using System.Security.Cryptography;
class Class1
{
[STAThread]
static void Main()
{
byte[] b_raw = new byte[256];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(b_raw);
Console.Write("const BYTE b[] = {");
for(int i = 0; i < 256; i += 8)
{
Console.WriteLine("0x{0:x}, 0x{1:x}, 0x{2:x}, 0x{3:x}, 0x{4:x}, 0x{5:x},
0x{6:x}, 0x{7:x},",
b_raw, b_raw[i+1], b_raw[i+2], b_raw[i+3], b_raw[i+4], b_raw[i+5],
b_raw[i+6], b_raw[i+7]);
}
}
}
This is run twice, so I have two arrays of 256 bytes, say key1 and key2.
These are then hardcoded into the C++ encryption algorithm.
Then, the C++ encryption algorithm goes as such:
It memcpys the _TCHAR array to a byte array, then loops round each byte of
this array.
For each byte, it gets the value of key1[n] (where n is the byte number),
and calls this 'b_current_indir' (the starting 'indirection level').
Then, it gets the value of key2[n] and calls this 'levels' - the number of
indirection levels.
Then, an inner loop runs 'levels' times - and on each loop the following
happens: the current byte of the data to be encrypted (dictated by the
outer loop) is XORed with key2[b_current_indir], and THEN, b_current_indir
is reassigned to take on the value of key2[b_current_indir].

The whole C++ algorithm is defined as such:
void Decrypt(BYTE* orig, long bytelen, _TCHAR* dataout)
{
BYTE* b_temp = new BYTE[bytelen];
#ifdef _DEBUG
ZeroMemory(b_temp, bytelen);
#endif
for(long bytenum = 0; bytenum < bytelen; bytenum++)
{
BYTE levels = b[bytenum % 256],
b_current_indir = b2[bytenum % 256]; //always starts the same
b_temp[(long)bytenum] = orig[(long)bytenum];
for(long level = 0; level < levels; level++)
{
b_temp[(long)bytenum] ^= b_current_indir;
b_current_indir = b2[(long)b_current_indir];
}
}
memcpy(dataout, b_temp, bytelen);
delete[] b_temp;
}

void Encrypt(_TCHAR* orig, long textlen, BYTE* dataout)
{
long bytelen = textlen * sizeof(_TCHAR);
BYTE* b_temp = new BYTE[bytelen];
#ifdef _DEBUG
ZeroMemory(b_temp, bytelen);
#endif
memcpy(b_temp, orig, bytelen);
for(long bytenum = 0; bytenum < bytelen; bytenum++)
{
BYTE levels = b[(long)(bytenum % 256)],
b_current_indir = b2[(long)(bytenum % 256)];
for(long level = 0; level < levels; level++)
{
b_temp[(long)bytenum] ^= b_current_indir;
b_current_indir = b2[(long)b_current_indir];
}
}
memcpy(dataout, b_temp, bytelen);
delete[] b_temp;
}

int main()
{
LPTSTR testtext = _T("TheMagicBonj");
long textlen = _tcslen(testtext) + 1;
BYTE* b_enc = new BYTE[textlen * sizeof(_TCHAR)];
#ifdef _DEBUG
ZeroMemory(b_enc, textlen * sizeof(_TCHAR));
#endif
Encrypt(testtext, textlen, b_enc);
_TCHAR* t_enc = new _TCHAR[textlen];
ZeroMemory(t_enc, textlen * sizeof(_TCHAR));
memcpy(t_enc, b_enc, textlen * sizeof(_TCHAR));
_tprintf(_T("The encrypted text is \"%s\"\n"), t_enc);
delete[] t_enc;

_TCHAR* t_dec = new _TCHAR[textlen];
Decrypt(b_enc, textlen * sizeof(_TCHAR), t_dec);
_tprintf(_T("The decrypted text is \"%s\"\n"), t_dec);
delete[] t_dec;

}

It seems to work to my eyes, but is it a pile of rubbish?

My initial thoughts were that somebody could crack it by disassembling the
machine code into assembly language, discovering where the global
namespace section of the DLL file was and deriving key1 and key2 from
that, and then just plugging them back through the algorithm, without
necessarily understanding the algorithm from the assembly language. But
even simpler than that, if they discovered that "it was probably *this*
DLL that created *that* encrypted data, so if I find out how to call the
DLL, I can decrypt it". To counter that, I could put the key in the
application very easily. But does this help? Could someone with a
knowledge of assembly language guess with a reasonable degree of accuracy
which bit of data in a PE file was likely to be a key to an encryption
algorithm?
Wouldn't it be just as easy for someone wanting to crack it to still do
that if I had written a DLL that used the Win32 API Crypto functions? If
not, why not? Is the effectiveness of private key cryptography only as
good as how well you can hide the key?

Please give as many thoughts as possible...

My (unsuccessful) attempt to use the Win32 API is as follows:
BOOL GetData(_TCHAR* datain, long lendatain)
{
HCRYPTPROV hCryptProv;
HCRYPTHASH hCryptHash;
HCRYPTKEY hCryptKey;
DWORD bytelen = (lendatain + 10) * sizeof(_TCHAR), databack = 0, bytesback
= 0;
BYTE* bData = new BYTE[bytelen];
_TCHAR* cryptdata = new _TCHAR[lendatain],
* decryptdata = new _TCHAR[lendatain];

BOOL bSuccess = CryptAcquireContext(&hCryptProv, keysetname, NULL,
PROV_RSA_FULL, CRYPT_NEWKEYSET);
if(!bSuccess) bSuccess |= CryptAcquireContext(&hCryptProv, keysetname,
NULL, PROV_RSA_FULL, 0);
memcpy(bData, datain, bytelen);
bSuccess &= CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hCryptHash);
bSuccess &= CryptHashData(hCryptHash, (BYTE*)textkey, _tcslen(textkey),
0);
bSuccess &= CryptDeriveKey(hCryptProv, CALG_RC2, hCryptHash, 0,
&hCryptKey);
bSuccess &= CryptEncrypt(hCryptKey, hCryptHash, TRUE, 0, bData,
&bytesback, bytelen);
CryptDestroyKey(hCryptKey);
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProv, 0);
memcpy(cryptdata, bData, bytesback);
_tprintf(_T("The encrypted data is \"%s\"\n"), cryptdata);


bSuccess = CryptAcquireContext(&hCryptProv, keysetname, NULL,
PROV_RSA_FULL, CRYPT_NEWKEYSET);
if(!bSuccess) bSuccess |= CryptAcquireContext(&hCryptProv, keysetname,
NULL, PROV_RSA_FULL, 0);
bSuccess &= CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hCryptHash);
bSuccess &= CryptHashData(hCryptHash, (BYTE*)textkey, _tcslen(textkey),
0);
bSuccess &= CryptDeriveKey(hCryptProv, CALG_RC2, hCryptHash, 0,
&hCryptKey);
bSuccess &= CryptDecrypt(hCryptKey, hCryptHash, TRUE, 0, bData,
&bytesback);
memcpy(decryptdata, bData, bytesback);
databack = (DWORD)(bytelen / sizeof(_TCHAR));
_tprintf(_T("The decrypted data is \"%s\"\n"), decryptdata);

CryptDestroyKey(hCryptKey);
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProv, 0);

delete[] bData;
delete[] cryptdata;
delete[] decryptdata;
CryptDestroyKey(hCryptKey);
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProv, 0);

return bSuccess;
}

void test(_TCHAR* string)
{
GetData(string, _tcslen(string));
}

int _tmain()
{
test(_T("TheMagicBonj"));
return 0;
}
The output is:

The encrypted data is "^ðnÍ&lÕeMagicBo"
The decrypted data is "@2"

which is disappointing.
 
B

Bonj

Do you mean that it will return the number of bytes it would take to
encrypt the data?

The total length in bytes of the encrypted data, yes.

Since most algorithms return the same size, this won't be
a problem.

That's fine them, this is what I was hoping.
I would recommend against rolling your encryption.

Yes, but why? Why would somebody be able to crack my own home-grown
encryption, whereas they wouldn't be able to crack one that used a
Cryptographic API?
My main line of curiosity is this:
Rather, you should use one of the classes in the
System.Security.Cryptography namespace. If you need compression as well,
then you can apply that after the encryption.

I am wanting to do it in an unmanaged way...sorry, I should have pointed
that out.
I don't need compression.
Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)
I was struggling to get the CryptoAPI to work, with CALG_RC4 it didn't
encrypt at all, and with CALG_RC2 it didn't decrypt at all (both claimed
to have succeeded, but with the former, the encrypted string was exactly
the same as the input, and with the latter, the decrypted string was
exactly the same as the encrypted string) - and I thought I'd done
everything right (at the bottom if you reckon you can spot where I
havent...)

So I decided to invent my own algorithm, and I just wanted anybody's
opinion on how secure this could be compared to the Win32 API version.
First, a C# program generates an array of 256 bytes, as such:
using System;
using System.Security.Cryptography;
class Class1
{
[STAThread]
static void Main()
{
byte[] b_raw = new byte[256];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(b_raw);
Console.Write("const BYTE b[] = {");
for(int i = 0; i < 256; i += 8)
{
Console.WriteLine("0x{0:x}, 0x{1:x}, 0x{2:x}, 0x{3:x}, 0x{4:x},
0x{5:x}, 0x{6:x}, 0x{7:x},",
b_raw, b_raw[i+1], b_raw[i+2], b_raw[i+3], b_raw[i+4], b_raw[i+5],
b_raw[i+6], b_raw[i+7]);
}
}
}
This is run twice, so I have two arrays of 256 bytes, say key1 and key2.
These are then hardcoded into the C++ encryption algorithm.
Then, the C++ encryption algorithm goes as such:
It memcpys the _TCHAR array to a byte array, then loops round each byte
of this array.
For each byte, it gets the value of key1[n] (where n is the byte number),
and calls this 'b_current_indir' (the starting 'indirection level').
Then, it gets the value of key2[n] and calls this 'levels' - the number
of indirection levels.
Then, an inner loop runs 'levels' times - and on each loop the following
happens: the current byte of the data to be encrypted (dictated by the
outer loop) is XORed with key2[b_current_indir], and THEN,
b_current_indir is reassigned to take on the value of
key2[b_current_indir].

The whole C++ algorithm is defined as such:
void Decrypt(BYTE* orig, long bytelen, _TCHAR* dataout)
{
BYTE* b_temp = new BYTE[bytelen];
#ifdef _DEBUG
ZeroMemory(b_temp, bytelen);
#endif
for(long bytenum = 0; bytenum < bytelen; bytenum++)
{
BYTE levels = b[bytenum % 256],
b_current_indir = b2[bytenum % 256]; //always starts the same
b_temp[(long)bytenum] = orig[(long)bytenum];
for(long level = 0; level < levels; level++)
{
b_temp[(long)bytenum] ^= b_current_indir;
b_current_indir = b2[(long)b_current_indir];
}
}
memcpy(dataout, b_temp, bytelen);
delete[] b_temp;
}

void Encrypt(_TCHAR* orig, long textlen, BYTE* dataout)
{
long bytelen = textlen * sizeof(_TCHAR);
BYTE* b_temp = new BYTE[bytelen];
#ifdef _DEBUG
ZeroMemory(b_temp, bytelen);
#endif
memcpy(b_temp, orig, bytelen);
for(long bytenum = 0; bytenum < bytelen; bytenum++)
{
BYTE levels = b[(long)(bytenum % 256)],
b_current_indir = b2[(long)(bytenum % 256)];
for(long level = 0; level < levels; level++)
{
b_temp[(long)bytenum] ^= b_current_indir;
b_current_indir = b2[(long)b_current_indir];
}
}
memcpy(dataout, b_temp, bytelen);
delete[] b_temp;
}

int main()
{
LPTSTR testtext = _T("TheMagicBonj");
long textlen = _tcslen(testtext) + 1;
BYTE* b_enc = new BYTE[textlen * sizeof(_TCHAR)];
#ifdef _DEBUG
ZeroMemory(b_enc, textlen * sizeof(_TCHAR));
#endif
Encrypt(testtext, textlen, b_enc);
_TCHAR* t_enc = new _TCHAR[textlen];
ZeroMemory(t_enc, textlen * sizeof(_TCHAR));
memcpy(t_enc, b_enc, textlen * sizeof(_TCHAR));
_tprintf(_T("The encrypted text is \"%s\"\n"), t_enc);
delete[] t_enc;

_TCHAR* t_dec = new _TCHAR[textlen];
Decrypt(b_enc, textlen * sizeof(_TCHAR), t_dec);
_tprintf(_T("The decrypted text is \"%s\"\n"), t_dec);
delete[] t_dec;

}

It seems to work to my eyes, but is it a pile of rubbish?

My initial thoughts were that somebody could crack it by disassembling
the machine code into assembly language, discovering where the global
namespace section of the DLL file was and deriving key1 and key2 from
that, and then just plugging them back through the algorithm, without
necessarily understanding the algorithm from the assembly language. But
even simpler than that, if they discovered that "it was probably *this*
DLL that created *that* encrypted data, so if I find out how to call the
DLL, I can decrypt it". To counter that, I could put the key in the
application very easily. But does this help? Could someone with a
knowledge of assembly language guess with a reasonable degree of accuracy
which bit of data in a PE file was likely to be a key to an encryption
algorithm?
Wouldn't it be just as easy for someone wanting to crack it to still do
that if I had written a DLL that used the Win32 API Crypto functions? If
not, why not? Is the effectiveness of private key cryptography only as
good as how well you can hide the key?

Please give as many thoughts as possible...

My (unsuccessful) attempt to use the Win32 API is as follows:
BOOL GetData(_TCHAR* datain, long lendatain)
{
HCRYPTPROV hCryptProv;
HCRYPTHASH hCryptHash;
HCRYPTKEY hCryptKey;
DWORD bytelen = (lendatain + 10) * sizeof(_TCHAR), databack = 0,
bytesback = 0;
BYTE* bData = new BYTE[bytelen];
_TCHAR* cryptdata = new _TCHAR[lendatain],
* decryptdata = new _TCHAR[lendatain];

BOOL bSuccess = CryptAcquireContext(&hCryptProv, keysetname, NULL,
PROV_RSA_FULL, CRYPT_NEWKEYSET);
if(!bSuccess) bSuccess |= CryptAcquireContext(&hCryptProv, keysetname,
NULL, PROV_RSA_FULL, 0);
memcpy(bData, datain, bytelen);
bSuccess &= CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hCryptHash);
bSuccess &= CryptHashData(hCryptHash, (BYTE*)textkey, _tcslen(textkey),
0);
bSuccess &= CryptDeriveKey(hCryptProv, CALG_RC2, hCryptHash, 0,
&hCryptKey);
bSuccess &= CryptEncrypt(hCryptKey, hCryptHash, TRUE, 0, bData,
&bytesback, bytelen);
CryptDestroyKey(hCryptKey);
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProv, 0);
memcpy(cryptdata, bData, bytesback);
_tprintf(_T("The encrypted data is \"%s\"\n"), cryptdata);


bSuccess = CryptAcquireContext(&hCryptProv, keysetname, NULL,
PROV_RSA_FULL, CRYPT_NEWKEYSET);
if(!bSuccess) bSuccess |= CryptAcquireContext(&hCryptProv, keysetname,
NULL, PROV_RSA_FULL, 0);
bSuccess &= CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hCryptHash);
bSuccess &= CryptHashData(hCryptHash, (BYTE*)textkey, _tcslen(textkey),
0);
bSuccess &= CryptDeriveKey(hCryptProv, CALG_RC2, hCryptHash, 0,
&hCryptKey);
bSuccess &= CryptDecrypt(hCryptKey, hCryptHash, TRUE, 0, bData,
&bytesback);
memcpy(decryptdata, bData, bytesback);
databack = (DWORD)(bytelen / sizeof(_TCHAR));
_tprintf(_T("The decrypted data is \"%s\"\n"), decryptdata);

CryptDestroyKey(hCryptKey);
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProv, 0);

delete[] bData;
delete[] cryptdata;
delete[] decryptdata;
CryptDestroyKey(hCryptKey);
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProv, 0);

return bSuccess;
}

void test(_TCHAR* string)
{
GetData(string, _tcslen(string));
}

int _tmain()
{
test(_T("TheMagicBonj"));
return 0;
}
The output is:

The encrypted data is "^ðnÍ&lÕeMagicBo"
The decrypted data is "@2"

which is disappointing.

 
G

Guest

Bonj said:
Yes, but why? Why would somebody be able to crack my own home-grown
encryption, whereas they wouldn't be able to crack one that used a
Cryptographic API?

.... because you have no chance - zero chance - of coming up on your own with
a crypto algorithm that will be much more secure than rot13. It's very easy
to create an encryption that you yourself cannot break, but this is a very
low standard. Lots of other people have done this and become laughingstocks
because anybody with even minor amounts of crypto training can crack this
stuff. Some do it for sport.

This is just a foolish, foolish idea: read some of Bruce Schneier's
"Crypto-Grams" to see others that have been in the Doghouse for this.

DO NOT ROLL YOUR OWN. Figure out how to use good algorithms.

Steve
 
S

Severian

Bonj,

See inline:


All encryption algorithms do this. If you couldn't decrypt back, then
it would be a hash.


This should not be an issue. Encryption works on byte streams, not
strings. As long as the string can be converted to/from a byte stream, you
won't have a problem.


The null terminator at the end of a string is just another byte. If you
encrypt it, it's going to take up space. If you don't need it, don't use it
then. As for the output size, I believe that it is the same as the input
size.


Do you mean that it will return the number of bytes it would take to
encrypt the data? Since most algorithms return the same size, this won't be
a problem.

I would recommend against rolling your encryption. Rather, you should
use one of the classes in the System.Security.Cryptography namespace. If
you need compression as well, then you can apply that after the encryption.

I agree with the first two sentences, but compression should be done
before encryption, since encrypted data does not usually compress
well.
 
S

ShaneB

Very well put Steve. To presume that you can even come close to the
encryption level of other popular methods is foolish...especially with a
simple iterative XOR algorithm (a common approach for fast, but not
terribly-secure, encryption). I highly recommend you use proven,
time-tested encryption methods if you want to prevent someone from accessing
your data. Just the knowledge of your software using its own proprietary
encryption method may, in fact, invite trouble.

Also note that any encryption method that _relies_ on hiding the algorithm
(or its keys) is inherently insecure. I mean, you don't store your housekey
in the keyhole of your front door do you?

I have a couple questions:
1) Exactly what are you trying to encrypt?
2) Why do you want to avoid using the framework encryption classes (which
are based on proven methods)?

ShaneB
 
C

Carl Daniel [VC++ MVP]

Severian said:
I agree with the first two sentences, but compression should be done
before encryption, since encrypted data does not usually compress
well.

Exactly. In fact, compressing the output of an encryptor is a low-grade
test for quality encryption. Well encrypted data is by definition not
compressible (it's indistiguishable from random noise).

More to the point, compressing the data before encryption increases the
entropy of the data. This in turn can only improve the quality of the
encryption.

-cd
 
I

Igor Tandetnik

Bonj said:
My (unsuccessful) attempt to use the Win32 API is as follows:
BOOL GetData(_TCHAR* datain, long lendatain)
{
DWORD bytelen = (lendatain + 10) * sizeof(_TCHAR), databack = 0,
bytesback = 0;
BYTE* bData = new BYTE[bytelen];

memcpy(bData, datain, bytelen);

Note that you are reading 10*sizeof(TCHAR) bytes past the end of datain
buffer here. In the worst case, your program can crash. If it does not,
you will have garbage at the end of bData.
bSuccess &= CryptEncrypt(hCryptKey, hCryptHash, TRUE, 0, bData,
&bytesback, bytelen);

Here's your problem. bytesback is an [in, out] parameter. On input, it's
the number of bytes to encrypt. On output, it's the number of encrypted
bytes successfully written. You are passing 0 on input, asking to
encrypt zero bytes.

Oh, and you don't need to pass hCryptHash. It does not do what you seem
to think it does. It's harmless, but makes the API do the unnecessary
work.
memcpy(cryptdata, bData, bytesback);
_tprintf(_T("The encrypted data is \"%s\"\n"), cryptdata);

Encrypted data may very well contain NUL bytes inside, or it may not
contain any, not even at the end. Your printf output will either be
shortened, or will result in a buffer overrun and possibly a crash.
Encryption produces a binary byte stream, not a nice NUL-terminated
string.
bSuccess &= CryptDecrypt(hCryptKey, hCryptHash, TRUE, 0, bData,
&bytesback);

Again, you don't need to pass hCryptHash.
memcpy(decryptdata, bData, bytesback);
databack = (DWORD)(bytelen / sizeof(_TCHAR));

Note that you never change bytelen after it was initially calculated
from lendatain. You can simply write databack = lendatain + 10 (what is
this +10 doing here anyway? )
_tprintf(_T("The decrypted data is \"%s\"\n"), decryptdata);

When encrypting, you didn't include the terminating NUL character. So
decrypted data is not NUL-terminated either (if only accidentally).
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925
 
J

Jeff Louie

Bonj... Inline.
Yes, but why? Why would somebody be able to crack my own home-grown
encryption, whereas they wouldn't be able to crack one that used a
Cryptographic API?<

The idea is that the best encryption algorithm is one that has been made
public for many years and no cryptographer can break it except by brute
force. The cost of breaking the data encrypted by a public algorithm
should
be greater than the value of the data.
I am wanting to do it in an unmanaged way...sorry, I should have
pointed
that out.<

I am using alleged RC4 source code in C, but RC4 is no longer
unconditionally
secure. You should be able to compile ARC4 for streams or char arrays of
fixed size, just don't use strlen on the encrypted array since it stops
at null.

Check out Applied Cryptography v2.0 by Schneier.

Regards,
Jeff
 
J

Jon Skeet [C# MVP]

I am wanting to do it in an unmanaged way...sorry, I should have pointed
that out.

If you want to do it in an unmanaged way, you shouldn't be looking at
C# to start with - all C# code is managed.
 
G

Guest

Note that you are reading 10*sizeof(TCHAR) bytes past the end of datain
buffer here. In the worst case, your program can crash. If it does not,
you will have garbage at the end of bData.

That is an issue, I meant to only use that to allocate the data. I don't
think it's the root cause of my woes, but it won't help!
bSuccess &= CryptEncrypt(hCryptKey, hCryptHash, TRUE, 0, bData,
&bytesback, bytelen);

Here's your problem. bytesback is an [in, out] parameter. On input, it's
the number of bytes to encrypt. On output, it's the number of encrypted
bytes successfully written. You are passing 0 on input, asking to
encrypt zero bytes.

Oh right....I thought the in parameter was the last parameter, that I'm
passing bytelen to - but it would appear not... thanks for that, I'll try it.
Oh, and you don't need to pass hCryptHash. It does not do what you seem
to think it does. It's harmless, but makes the API do the unnecessary
work.

What, pass NULL to it you mean? What would it not do that it previously
would have done if I'd passed hCryptHash?

Encrypted data may very well contain NUL bytes inside, or it may not
contain any, not even at the end. Your printf output will either be
shortened, or will result in a buffer overrun and possibly a crash.
Encryption produces a binary byte stream, not a nice NUL-terminated
string.

Right, OK. I'll give the whole thing an overhaul taking into account your
suggestions with a view that the NULL terminator is just another character to
be encrypted...

Cheers


Note that you never change bytelen after it was initially calculated
from lendatain. You can simply write databack = lendatain + 10 (what is
this +10 doing here anyway? )

It was to make sure I'd allocated more bytes than I'd need.
 
G

Guest

1) Exactly what are you trying to encrypt?

User name / password combination
2) Why do you want to avoid using the framework encryption classes (which
are based on proven methods)?


Don't want to use managed code for the security of the main algorithm of the
program
 
G

Guest

... because you have no chance - zero chance - of coming up on your own with
a crypto algorithm that will be much more secure than rot13.

You appear to have repeated the assertion, instead of attempting to offer a
*reason* as to why this might be the case, as I requested.
Are you able to explain the *technical* reasons as to why someone could
crack my own algorithm, but couldn't crack "rot13" - i.e. what would they
actually physically have to do, and why they wouldn't be able to do this to a
rot13 algorithm. This is the bit I'm having trouble *understanding* - it's
not that I'm having trouble *believing* you and need convincing.
even minor amounts of crypto training

Such as?
can crack this
stuff. Some do it for sport.

Again, I'll repeat the request - *please* explain, if you are able, what
steps they would take to crack mine. I'm not asking you to crack it yourself
- just outline the steps someone would have to take... and point out to me
what bit would trip them up if I was using a professional algorithm.
DO NOT ROLL YOUR OWN. Figure out how to use good algorithms.

Thanks for replying, however it does appear that you've answered the
question "Should I use my own encryption algorithm?" rather than what was
actually asked, which was "Why shouldn't I use my own encryption algorithm?".
 
G

Guest

If you want to do it in an unmanaged way, you shouldn't be looking at
C# to start with - all C# code is managed.

Well, exactly. I'm not looking at writing it in C#, that was just used to
generate 256 random bytes to start with, but since that and because it was a
fairly algorithmic question in general I thought I'd post it here. Sorry if
that caused offence.
 
G

Guest

Carl -
Is the comparison of the encrypted data with *actual* random noise the
beginning of one of the techniques used for cracking the encryption?
If so, how does it progress?
(see also reply to Steve Friedl)
 
G

Guest

Well obviously, because they know what byte zero encrypts to, so they know
what bytes were originally zero, by just comparing which ones are the same as
the last byte. But whether this matters, I don't know... you could perhaps
rearrange the order of the bytes?
 
J

Jon Skeet [C# MVP]

Bonj said:
Well, exactly. I'm not looking at writing it in C#, that was just used to
generate 256 random bytes to start with, but since that and because it was a
fairly algorithmic question in general I thought I'd post it here. Sorry if
that caused offence.

No offence taken, but it does seem to be a less appropriate newsgroup
than, say, a newsgroup about security. (comp.security.misc?)
 
J

Jon Skeet [C# MVP]

Bonj said:
You appear to have repeated the assertion, instead of attempting to
offer a *reason* as to why this might be the case, as I requested.
Are you able to explain the *technical* reasons as to why someone
could crack my own algorithm, but couldn't crack "rot13" - i.e. what
would they actually physically have to do, and why they wouldn't be
able to do this to a rot13 algorithm. This is the bit I'm having
trouble *understanding* - it's not that I'm having trouble
*believing* you and need convincing.

The reason is that very, very clever people who have a *lot* of
experience in encryption still need to look very carefully at
encryption algorithms before being willing to say how secure they are.
Basically, it's not a field where amateur attempts are likely to be
successful.
 
R

Richard Blewett [DevelopMentor]

Cryptography is a mathematical discipline. New cryptographic algorithms are pretty rare and go through a huge exercise in peer review amongst academics as they try to break the algorithm. Many very well respected mathematicians have come up with new algorithms only to have them broken - sometimes spectacularly quickly by their peers.

You've done the right thing in publishing your algorithm because trying to keep the algorithm secret is no guarantee of cryptographic strength. But I will wager alot of money that your algorithm is very easy to break by a cryptographer (of which I'm not one) unless you are a mathematical genius and have actually come up with a new powerful crypto technique.

This is not meant to trivialise your efforts rather than to point out how tortuously complex it is writing an industrial strength crypto algorithm. If you don't want to use managed code you can use CAPICOM library to use one of the standard algorithms.

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

Bonj said:
You appear to have repeated the assertion, instead of attempting to
offer a *reason* as to why this might be the case, as I requested.
Are you able to explain the *technical* reasons as to why someone
could crack my own algorithm, but couldn't crack "rot13" - i.e. what
would they actually physically have to do, and why they wouldn't be
able to do this to a rot13 algorithm. This is the bit I'm having
trouble *understanding* - it's not that I'm having trouble
*believing* you and need convincing.

The reason is that very, very clever people who have a *lot* of
experience in encryption still need to look very carefully at
encryption algorithms before being willing to say how secure they are.
Basically, it's not a field where amateur attempts are likely to be
successful.
 
Top