INTRODUCTIONBeing kinda fascinated by cryptography lately, I decided to push myself into it. The result of this happened to be a new crypto algorithm, which I've written completely from scratch. It's not that I'd imagine its security properties to be anywhere near AES, Twofish or any other crypto endboss, but regarding the times we live in, I guess we just can't have enough different crypto systems out there, haha
However, the main purpose of my share is asking the evilzone community to crack it.
Because although I somehow managed to en-/decrypt some crap, I was uber-unable to handle actual crypto analysis. I tried to learn this stuff but ended up in some insane condition of raging agony, repeatedly banging my dong against the keyboard. So maybe anyone of you with a bit more knowledge in the field could tell me how secure my cipher actually is.
CLI & INPUT ARGSAlright, let's roll. So the program we're talking about is called "7Crypt", and wow this badboy shows up with some super fancy bitch-moistening interface, I can tell ya. And watch dem functions, hell yes
I came.
HOW IT WORKS(Not wanting to go into TOO much detail, some remaining questions may only be answered by your code comprehension. Maybe I upgrade the description later on, we'll see)General InfoThe whole damn thing is about a ridiculously large and polymorphic substitution table. Its individual appearance is passphrase-dependent (more on that later), but in any case it is counting 128 columns and 128 rows (+one special-purpose row, so precicely 128x129), with table-cells being exactly 7 bits in size.
Currently, those cells are simply stored as chars(8bit) whose 2^7 bit is nulled. The S-Box itself is displayed by using a two-dimensional vector.
While en-/decrypting, the said s-box moves its fat ass over the plaintext, successively ciphering 7 plain bits at once. Meanwhile, the box continually morphs its content by swapping columns, rotating rows (using modular addition) and by pseudo-randomly picking a column to "use next"(more on that later). Which columns are swapped, how many rows shifted etc. is determined by the plain 7bit values on the one hand, and their appropriate substitution value on the other. These two main informations are mashed up within in a continuously running morphing void.
Like that, the SBoxe's arsecheeks scrub all over the poor plain text, twice - Firstly going from the first byte to the last, and then reversing back to the front where it started (that's for encryption; the DEcryption scheme is inversed).
Within the source, each crypting-direction is referred to as ONE round, therefore summing up to (currently) two cipher rounds in total.
7c_Crypt.cpp
Note: The bitArray class is a simple vector<bool>/bitset mashup to simplify low-level binary ops. Its source is included in the 7z archive. /* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Written by x0nic/xnc aka CF // Germany, 2014-2015
Unlicensed open-source software - Provided "as is", no rights reserved.
:::Feel free to use it for any non-monetary purpose. You may also alter
:::the code, but please be kind and credit the original author; thanks.
__________________________________________________________________________
*/
#include <iostream>
#include <string>
#include "7Crypt.h"
using namespace std;
int8_t bloat = 0,
OFFSET;
int32_t BEG,
END;
size_t bitSz;
void _7CRYPT (char* BYTES, const size_t SIZE, string* PASS, const bool MODE)
{
bitArray BIN(BYTES, SIZE);
SBoxContainer boxeS;
createSBoxes(PASS, &boxeS);
bitSz = BIN.size();
if(bitSz%7) {bloat = bitSz%7; bitSz -= bloat;}
for(uint8_t R=0; R<NumOfRounds; R++)
{
/*EN!*/ if(MODE) {
initRound(R);
_en7crypt(BIN,boxeS[R]);
}
/*DE!*/ else {
initRound(NumOfRounds-R-1);
_de7crypt(BIN,boxeS[NumOfRounds-R-1]);
}
}
memcpy(BYTES, BIN.char_data(), SIZE);
}
void initRound(uint8_t sboxNr)
{
if(sboxNr%2){ //change cipher direction
OFFSET = -7;
BEG = bitSz-7;
END = -7;
}
else { OFFSET = 7;
BEG = 0;
END = bitSz;
}
}
//////////////////////////////////////////////////////////////////////////////////
/* ENCRYPTION ENCRYPTION ENCRYPTION */
void _en7crypt(bitArray& BIN, SBox& SBox)
{
char cipher=0x00, plain=0x00;
uint16_t col=0, row=0, rotateRows=0;
for(int32_t pln=BEG; pln!=END; pln+=OFFSET)
{
plain = BIN.bits2char(pln,7);
row = (plain + rotateRows)%128;
cipher = SBox[col][row] ^ SBox[col][128];
BIN.overwrite(pln, cipher, 7);
//redef for next iteration
SBox[ plain ].swap(SBox[ col ]);
col = (col + SBox[col][row]) % NumOfColumns;
rotateRows = ++row;
}
if(bloat) {
plain = BIN.bits2char(bitSz, bloat);
cipher = plain ^ SBox[col][128];
BIN.overwrite(bitSz, cipher, bloat);
}
}
//////////////////////////////////////////////////////////////////////////////////
/* DECRYPTION DECRYPTION DECRYPTION */
void _de7crypt(bitArray& BIN, SBox& SBox)
{
char cipher=0x00, plain=0x00;
uint16_t col=0, row=0, rotateRows=0;
for(int32_t cph=BEG; cph!=END; cph+=OFFSET)
{
cipher = BIN.bits2char(cph,7) ^ SBox[col][128];
for(row=0; row<=127; row++)
if(cipher == SBox[col][row])
{
plain = ((row - rotateRows)+128)%128;
break;
}
BIN.overwrite (cph,plain,7);
//redef for next iteration
SBox[ plain ].swap(SBox[ col ]);
col = (col + SBox[col][row]) % NumOfColumns;
rotateRows = ++row;
}
if(bloat) {
cipher = BIN.bits2char(bitSz, bloat);
plain = cipher ^ SBox[col][128];
BIN.overwrite(bitSz, plain, bloat);
}
}
// EOF
The Substitution BoxThe user provides a passphrase with a size of something between 14 and 112 characters, which is then converted into binary code. If necessary, a password expanding function enlarges the passphrase, until (size>=14 && size%7==0) returns true.
So we got those bits now, which will serve as a 'logical source' for table generation. It goes like this: The program applies a static shuffle-routine to them, after which they pass various logic gates that make the bit array shrink down to a size of seven. (The cipher name starts to make some sense, huh?) Afterwards, the achieved 7 bits have the chance of being added to the currently constructed column, and thus becoming a table cell. To be approved, their value shall NOT be existent in the column already...
Now some brainiacs might already presume something. The 7 cipher bits that substitute the plain ones are chosen by using the plain 7bit decimal value as index for the currently active column. Afterwards, the very same value (= 7 plain bits) is added to the row-rotating offset (initialised as 0), right before applying modulo 128 to the rota-equation.
Continually, the next active column is chosen by some XOR stuff and then suddenly all those crazy swapping things happen, woa dude. But that shall be enough for now; I guess the basics are explained.
7c_CreateSubBox.cpp
Note: 'bVector' and 'cVector' are only typedefs for <char> and <bool> /* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Written by x0nic/xnc aka CF // Germany, 2014-2015
Unlicensed open-source software - Provided "as is", no rights reserved.
:::Feel free to use it for any non-monetary purpose. You may also alter
:::the code, but please be kind and credit the original author; thanks.
__________________________________________________________________________
*/
#include <iostream>
#include <string>
#include <bitset>
#include <vector>
#include "7Crypt.h"
using namespace std;
void createSBoxes(string* passwd, SBoxContainer* boxeS)
{
if(passwd->size()<14 || passwd->size()%7)
expandPass(passwd);
bVector passwd_Bits = str2bit(passwd);
cVector COLUMN;
SBox SBOX;
while(SBOX.size() < NumOfColumns)
{
while(COLUMN.size() < 128)
{
shuffleBits(&passwd_Bits);
char newCell = createNewCell(&passwd_Bits);
/* * * */check:
bool cellRepeat = false;
for(uint8_t c: COLUMN)
if (c == newCell) {
cellRepeat = true;
break;
}
if(!cellRepeat)
COLUMN.push_back(newCell);
else {
newCell = (newCell+1) % 128;
goto check;
}
}
char clmSTAMP = COLUMN[0]; //security add-on (will be XORed with chosen
for(uint8_t s=32; s<=128; s+=32)//SBox-cell to reduce lucky brute force hits)
clmSTAMP ^= COLUMN[s-1];
if(clmSTAMP != 0x00 && clmSTAMP != 0x7F) {
COLUMN.push_back(clmSTAMP);
SBOX.push_back(COLUMN);
}
COLUMN.clear();
}
while(boxeS->size() < NumOfRounds) //one fresh box for each cipher round
boxeS->push_back(SBOX);
}
void shuffleBits (bVector* vBl)
{
auto vR = vBl->rbegin(); //starts right, moves <<<
auto vL = vBl->begin(); //starts left, moves >>>
size_t middle = vBl->size() / 2;
bVector shfl;
int8_t wordSz = 7;
while(wordSz > 0) //1. shuffle 7bit-words (ABCD -> DACB)
{
for(uint16_t s=0; s<middle; s+=wordSz) { //itors meet @ "middle"
for(uint8_t v=0; v<wordSz; v++) shfl.push_back(*vR++);
for(uint8_t v=0; v<wordSz; v++) shfl.push_back(*vL++);
}
*vBl = shfl;
shfl.clear();
wordSz -= 5; //2. shuffle 2bit-words
}
//3. quit
}
char createNewCell (bVector* vBl)
{
bVector _56bit;
create56bitBlock(&_56bit, vBl);
// fill EIGHT 7bit-words
char A = vec2char(&_56bit, 0, 7);
char B = vec2char(&_56bit, 7, 7);
char C = vec2char(&_56bit, 14, 7);
char D = vec2char(&_56bit, 21, 7);
char E = vec2char(&_56bit, 28, 7);
char F = vec2char(&_56bit, 35, 7);
char G = vec2char(&_56bit, 42, 7);
char H = vec2char(&_56bit, 49, 7);
return ( (A&B) | (C&D) ) ^ ( (E&F) | (G&H) );
}
void create56bitBlock(bVector* DEST, bVector* SRC)
{
uint8_t max = 56;
bVector _56bit, xorBuf;
_56bit.assign(SRC->begin(), SRC->begin()+56);
for(auto pos = SRC->begin()+56; pos < SRC->end(); pos+=56)
{
if(SRC->end() - pos < 56)
max = SRC->end() - pos;
xorBuf.assign(pos, pos+max);
XOR(&_56bit, &xorBuf);
}
*DEST = _56bit;
}
void XOR (bVector* bin1, bVector* bin2)
{
for(uint16_t pos = 0; pos < bin2->size(); pos++)
if(bin1->at(pos) != bin2->at(pos)) (*bin1)[pos] = 1;
else (*bin1)[pos] = 0;
}
/* PASSWORD EXPANSION */ // if necessary
void expandPass(string* passwd)
{
bVector passwd_Bits;
while(passwd->size()<14 || passwd->size()%7) {
passwd_Bits = str2bit(passwd);
shuffleBits(&passwd_Bits);
*passwd += createPassChar(&passwd_Bits);
}
}
char createPassChar (bVector* vBl)
{
bVector _56bit;
create56bitBlock(&_56bit, vBl);
// fill SEVEN 8bit-words
char A = vec2char(&_56bit, 0, 8);
char B = vec2char(&_56bit, 8, 8);
char C = vec2char(&_56bit, 16, 8);
char D = vec2char(&_56bit, 24, 8);
char E = vec2char(&_56bit, 32, 8);
char F = vec2char(&_56bit, 40, 8);
char G = vec2char(&_56bit, 48, 8);
return ((A&B)|(C&D)) ^ ((E&F)|(G&(!A)));
}
/* TYPE CONVERSIONS */
char vec2char (bVector* vBl, size_t POS, uint8_t BIT_SIZE)
{
bitset<8> byte = 0x00;
for(uint8_t i=0; i<BIT_SIZE; i++)
if( (*vBl)[POS+i] ) byte[i]=true;
return byte.to_ulong();
}
bVector str2bit (string* str)
{
bitset<8> byte;
bVector vBl;
for(uint8_t i=0; i<str->size(); i++) {
byte = str->at(i); //char to 8bit-bitset
for(uint8_t bit=0; bit<8; bit++)
if(byte[bit]) vBl.push_back(1); //bitset-bit to vector-bit
else vBl.push_back(0);
}
return vBl;
}
// EOF
EXAMPLESLet's suggest we are using „0123456789abcd“ as passphrase, and – for simplicities sake – a string with 32 times „A“ as plain text. (Note that I have to use parameter -b* here, for obvious reasons.) So the given plain text in base64 (without any 7crypt-ciphering) would look like this:
QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUE
Naturally, we see stuff repeating - „QUFB“, in this case. (The „E“ at the end is due to padding)
* 7crypt doesn't use a native base64 implementation, but the basic concept & alphabet letters are just alikeNow let's fire up the actual cipher for comparison. The de7crypted code for a plain text of „AAA...A“[32] is:
nKVWXXIPFvPTpAdWLesrE024iJ4QVGRX8lWk2UqztxH
Okay, looks better. At least there seems to be no real repeating pattern.
Next, we retry by changing a single bit within the plaintext (the passphrase stays the same). This is done by simply exchanging the last „A“ with a „C“, so now our plain text equals „AAA....A“[31] + „C“. This is 7crypts output:
sE30tOuOA/uSs+XeojZHf5hGbRjF2DiBvwpQEv8hmZL
What a mess. And remember: We used the same password; only ONE plain text bit has been changed. This behaviour refers to
Shannon's law.
For the last example, we change one single bit within the passphrase/password/key, or whatever you want to call it. So instead of „0123456789abcd“ we will now use „
1123456789abcd“ and see what happens. For the plain text we simply re-use the one from last example (= 31 x “A“ and 1 x “C“ at the end).
q40fjAkFllopMaR0Ln7mRw8RRZM2If86XaUYvqJHJCD
Shit seems to work.
EHHH YEAH, AND WHAT NOW?Download the source, compile it, try stuff out, and then tell me where the cryptographical weak-points are.... If you want. This is a free world. gl&hf
→ Of course, feedback of any other kind is also appreciated. This explicitly includes bug-reports and/or thoughts on the code itself (note that I am no IT student or anything, only an autodidact).
Ah and by the way, I was also thinking about a little challenge where I offer something like 10$ in BTC to whoever breaks a certain cipher text, but I guess those badass admins just bitchslap the fuck outta me when it comes to money or anything similar. I dunno the exact rules yet, tbh.
Anyway, here's some 7crypt cipher you can test your skills on. No official BTC reward right now. Just send me a PM with the matching plaintext and/or used password, and we might work something out.
ZG6dyjHNcav87EG2sd8R8CLHpybqJ/D
Hints: Plain text is a short, english sentence. The passphrase has 14 characters that can all be found on a generic US-keyboardTHE SOURCE CODEBelow you find a 7z-package that contains the whole cipher source. No compiled binaries inside, and no project for faggot-IDE XY or anything, just the plain source code.
NOTE: If you want to encrypt files above 1mb, I would recommend your compiler to use its speed optimization setting.
The password for unpacking is „7crypt_xnc“.
And it is written in C++, btw
OP EDITSJune 6th - added crypt.cpp and sbox.cpp for sneak-peeking the source