Author Topic: Crypto pentesting >> Break my cipher! [Open Source Release]  (Read 1577 times)

0 Members and 1 Guest are viewing this topic.

Offline x0nic

  • Peasant
  • *
  • Posts: 51
  • Cookies: 5
    • View Profile
INTRODUCTION

Being 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 ARGS

Alright, 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 Info
The 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.
Code: (C) [Select]
/*  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +  +
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 Box
The 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>
Code: (C) [Select]
/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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



EXAMPLES

Let'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:
Quote
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 alike

Now let's fire up the actual cipher for comparison. The de7crypted code for a plain text of „AAA...A“[32] is:
Quote
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:
Quote
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).
Quote
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.
Quote
ZG6dyjHNcav87EG2sd8R8CLHpybqJ/D
Hints: Plain text is a short, english sentence. The passphrase has 14 characters that can all be found on a generic US-keyboard



THE SOURCE CODE

Below 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 EDITS
June 6th - added crypt.cpp and sbox.cpp for sneak-peeking the source
« Last Edit: June 06, 2015, 08:20:23 pm by x0nic »

Offline Polyphony

  • VIP
  • Knight
  • *
  • Posts: 178
  • Cookies: 23
    • View Profile
Re: Crypto pentesting >> Break my cipher! [Open Source Release]
« Reply #1 on: May 28, 2015, 06:17:23 am »
you should include a makefile so that instead of trying to figure out why it doesn't compile, we can just run:

Code: [Select]
make all

a simple makefile could be written in 2 lines:

Code: [Select]
all:
    g++ <flags> <sources> <headers>
« Last Edit: May 28, 2015, 06:17:52 am by Polyphony »
Code: [Select]
<Spacecow_> for that matter I have trouble believing bitches are made out of ribs
<Gundilido> we are the revolutionary vanguard fighting for the peoples right to display sombrero dawning poultry
<Spacecow> did they see your doodle?
<~phage> Maybe
<+Unresolved> its just not creative enough for me
<+Unresolved> my imagination is to big to something so simple

Offline x0nic

  • Peasant
  • *
  • Posts: 51
  • Cookies: 5
    • View Profile
Re: Crypto pentesting >> Break my cipher! [Open Source Release]
« Reply #2 on: May 28, 2015, 01:43:45 pm »
eh yeah, or maybe just use
Code: [Select]
g++ -Wall -O3 -std=c++11instead. It should work then; found it to be quite obvious.

Whatever, I'ma add your ./make this evening or so, chillin