Thread: RFC - Simple Encryption

  1. #1
    ‡ †hë Ö†hÈr sîÐè ‡ Nor's Avatar
    Join Date
    Nov 2001
    Posts
    299

    RFC - Simple Encryption

    Request For Comments - Simple Encryption.

    I've been working on an Stream Cipher for about 2 weeks and I've gotten along way
    I'm going to use this to protect files with my Check Book Program. At the request of a few friends.

    Good, Bad, Ugly. What Do you Think?
    I like ALL feedback.

    Full Source zip is available with VS2003 Solution and project files.
    Sorry but I don't have my 'make' file handy I'll load it onto my web server as soon as I get a chance.

    Full Source
    SimpleCipher_v0.1.zip
    For the Winzip Impaired I've setup a Folder Containing all the files
    SimpleCipher_v0.1/
    If the links stop working then check it out here.
    http://thedivinedude.home.comcast.net
    This code works great on my

    Mandrake Linux 32bit,
    Slackware 12 Linux 32bit,
    Windows Vista 64bit,
    Windows Vista 32,
    Windows XP,
    Windows 98

    I can encode a message on any machine and it be read without problems on the others.


    I've been developing with Visual Studio 2003.net
    And I've had no problems building under GCC g++ version 4.3.3
    Red shows where i've made changes 2-16-09
    SimpleCypher.h
    Code:
    #pragma once
    #define CIPHER_ROUNDS 16
    #include <iostream>
    //#include "MyFileBuffer.h"
    class SimpleCipher
    {
    public:
    	SimpleCipher(void);
    	~SimpleCipher(void);
    	void makeKey(unsigned char* key, size_t length);
    	unsigned char* makeKeyFile(unsigned char* key, size_t keylength, size_t count);
    	void cipher(void* data, size_t length);
    	void encrypt(void* data, size_t length);
    	void decrypt(void* data, size_t length);
    private:
    	unsigned char ring_a[8];
    	unsigned char ring_b[8];
    	unsigned char ring_c[8];
    	unsigned char s_box[256];
    	size_t counter_a, counter_b, counter_c;
    	size_t a,b,c;
    	unsigned char* _key;
    	size_t         _length;
    	bool		   _lock;
    	unsigned char rand();
    
    	void shiftbit(unsigned char* ring);
    	void MakeCheckSum(unsigned char* checksum, const void* data, unsigned int size);
    };
    SimpleCipher.cpp
    Code:
    #include ".\simplecipher.h"
    
    void SimpleCipher::shiftbit(unsigned char* ring){
        unsigned __int32 temp[2];
        unsigned __int32* p = (unsigned __int32*) ring;
        temp[0] =  (*p) >> 1;	
        p++;
        temp[1] =  (*p) >> 1;
        p--;
        temp[1] |= (*p) << 31;	
        p++;
        temp[0] |= (*p) << 31;
        *p = temp[1];
        p--;
        *p = temp[0];
    }
    
    void SimpleCipher::MakeCheckSum(unsigned char* checksum, const void* data, unsigned int size)
    {
        //error check
        if(checksum == NULL) return;
        if(data     == NULL) return;
        if(size     == 0   ) return;
        unsigned char temp, carry;
        unsigned char* d = (unsigned char*)data;
        unsigned char* p;  
        carry = 0;
        do{
            temp = *d;
            temp ^= carry;
            p = checksum;
            p += 7;
            *p += (temp & 0x01);
            p--; 
            temp >>= 1;
            *p += (temp & 0x01);
            p--; 
            temp >>= 1;
            *p += (temp & 0x01);
            p--; 
            temp >>= 1;
            *p += (temp & 0x01);
            p--; 
            temp >>= 1;
            *p += (temp & 0x01);
            p--; 
            temp >>= 1;
            *p += (temp & 0x01);
            p--; 
            temp >>= 1;
            *p += (temp & 0x01);
            p--; 
            temp >>= 1;
            *p += (temp & 0x01);
            d++;
            size--;
            carry ^= ( checksum[0] ^ checksum[1] ^ checksum[2] ^ checksum[3] ^
                             checksum[4] ^ checksum[5] ^ checksum[6] ^ checksum[7]);
            shiftbit(checksum);
        }while(size);
    }
    
    SimpleCipher::SimpleCipher(void)
    {
        _lock = false;
        _key    = 0;
        _length = 0;
    }
    
    SimpleCipher::~SimpleCipher(void)
    {
        if(_key)
            delete _key;
    }
    
    void SimpleCipher::makeKey(unsigned char* key, size_t length){	
        unsigned char temp;
        if(_key)
            delete _key;
        _key = new unsigned char[length];
        if(!_key) return;
        _length = length;
        this->counter_a = 64;
        this->counter_b = 64;
        this->counter_c = 64;
        this->a = 0;
        this->b = 0;
        this->c = 0;
        memset(&ring_a[0], 0, 8);
        memset(&ring_b[0], 0, 8);
        memset(&ring_c[0], 0, 8);
        //Make key from user input.
        for(int x = 0; x != 255; x++){
            MakeCheckSum(&ring_a[0], key, length);
            MakeCheckSum(&ring_b[0], &ring_a[0], 8);
            MakeCheckSum(&ring_c[0], &ring_b[0], 8);
            MakeCheckSum(&ring_a[0], &ring_c[0], 8);
        }
    
        //Prep s_boxes
        for(int x = 0; x != 256; x++)
            s_box[x] = x;
    
            _lock = true;
            for(int x = 0; x != 256; x++){		   
                    temp = this->rand();
                    for(int y = 0; y != 256; y++){
                        s_box[y] ^= temp;
                        rand();
                    }
            }
            this->encrypt(_key, _length);
            _lock = false;
    }
    
    unsigned char* SimpleCipher::makeKeyFile(unsigned char* key, size_t keylength, size_t count){
    	return 0;
    }
    
    void SimpleCipher::cipher(void* data, size_t length){
        unsigned char* p = (unsigned char*)data;
        for(;length != 0;length--,p++)
            *p ^= this->rand();
    }
    	
    void SimpleCipher::encrypt(void* data, size_t length){
        unsigned char* p = (unsigned char*)data;
        for( ;length;length--){
            for(int x=0; x != CIPHER_ROUNDS; x++){
                *p += this->rand();
                *p = s_box[(*p)];
                *p -= s_box[this->rand()];
                *p ^= this->rand();
            }
        p++;
        }
    }
    	
    void SimpleCipher::decrypt(void* data, size_t length){
        unsigned char temp[CIPHER_ROUNDS*3], *p;
        unsigned char s[256];
        p = (unsigned char*)data;
        for(int x = 0; x != 256; x++)
            s[s_box[x]] = x;
         for(;length;length--){   
            for(int x = 0; x != CIPHER_ROUNDS*3; x++)
                temp[x] = this->rand();
            for(int x = CIPHER_ROUNDS*3-1; x >= 0; x-=3){
                *p ^= temp[x];
                *p += s_box[temp[x-1]];
                *p = s[(*p)];
                *p -= temp[x-2];
            }
            p++;
        }
    }			
    
    unsigned char SimpleCipher::rand(){
        unsigned char result;
        a = (ring_a[c] & 0x07);
        b = (ring_b[a] & 0x07);
        c = (ring_c[b] & 0x07);
    
        result = ring_a[a] ^ ring_b[b] ^ ring_c[c];
    
        shiftbit(&ring_c[0]);    counter_c--;
        if(counter_c == 0){
            counter_c = 64;
            shiftbit(&ring_b[0]);
            counter_b--;
            if(counter_b == 0){
                counter_b = 64;
                shiftbit(&ring_a[0]);
                counter_a++;
                if(counter_a == 0){
                    counter_a = 64;
                    if(!_lock){
                        _lock = true;
                        for(int x = CIPHER_ROUNDS; x; x--){
                            this->encrypt(_key, _length);
                            MakeCheckSum(&ring_a[0], _key, _length);
                            MakeCheckSum(&ring_b[0], &ring_a[0], 8);
                            MakeCheckSum(&ring_c[0], &ring_b[0], 8);
                            MakeCheckSum(&ring_a[0], &ring_c[0], 8);                    }
                        _lock = false;
                    }
                }
            }
        }
        return result;
    }
    Last edited by Nor; 02-16-2009 at 06:35 PM.
    Try to help all less knowledgeable than yourself, within
    the limits provided by time, complexity and tolerance.
    - Nor

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    Which algorithm are you implementing?

    You might want to move those helper functions into an unnamed namespace instead of using an underscore prefix.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    ‡ †hë Ö†hÈr sîÐè ‡ Nor's Avatar
    Join Date
    Nov 2001
    Posts
    299
    Its my own creation.
    I'm Calling It - SimpleCypher -

    Its basicly a shift regester which is weak by it's self.
    But by re-processing the registers(rings) after a full cycle I've been able to increase the

    ---the the ummm ummm--- can't think of the word
    Number of output bytes before it starts repeating itself

    by a factor of 262144 to the power of 53773.
    Took 63 hours for my 2.6x4 processor to get the cypher to start repeating.
    Running mimual load, 4 threads, and a key of '123abc'

    But using the s_box mixed in with addition and subtraction I beleave obscures any information from shift registers.

    The helper functions I just cut and pasted from my check book program. I was just using them
    to verify my database files. basicly like a one way hash or crc checksum.
    Try to help all less knowledgeable than yourself, within
    the limits provided by time, complexity and tolerance.
    - Nor

  4. #4
    Registered User
    Join Date
    Dec 2006
    Location
    Canada
    Posts
    3,229
    IIRC names with an underscore prefix are reserved (for implementation).

  5. #5
    ‡ †hë Ö†hÈr sîÐè ‡ Nor's Avatar
    Join Date
    Nov 2001
    Posts
    299
    P.S.
    Code:
     
    Using A ram drive and one 2.6gig processer AMD 64--bit
    and 1 gig file
    
    cypher    mode     16.6 meg/per sec
    encrypt   mode     6.4  meg/per sec
    decrypt   mode     6.2  meg/per sec
    EDIT: with a debug build

    EDIT 2:
    With a release build optimized for speed. vs.net
    Code:
     
    cypher    mode     59.3 meg/per sec
    encrypt   mode    22.7  meg/per sec
    decrypt   mode    21.2  meg/per sec
    And Please note that loadKey() is for debuging use only.
    It's not as secure as MakeKey() because of the 24 byte key limit.
    Last edited by Nor; 02-15-2009 at 10:42 PM.
    Try to help all less knowledgeable than yourself, within
    the limits provided by time, complexity and tolerance.
    - Nor

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    Its my own creation.
    Well, I am not qualified to judge the strength of a cipher on my own, and the mere belief of the designer in the strength of the algorithm is not enough to have any trust that the cipher is not snake oil. abachler apparently has some expert knowledge of cryptanalysis, so perhaps you should try and persuade him to analyse and attempt to break your algorith, otherwise you should implement a well known algorithm that is regarded as strong if you want to put this to real use.

    The helper functions I just cut and pasted from my check book program. I was just using them
    to verify my database files. basicly like a one way hash or crc checksum.
    Yes, but my point is that since those functions are supposed to be implementation detail, you should make it so. The way you tried is presumably to use an underscore prefix, but names beginning with underscores are reserved to the implementation for use in the global namespace, or for any use if an upper case letter immediately follows the underscore (e.g., _MakeCheckSum).

    Instead of using reserved names, wrap the definitions of those functions in an unnamed namespace, or make them private member functions. If they do not need access to the internals of the class, I prefer the former.

    A few other things to note:
    • It is safe to use delete on a null pointer, so you do not need to check that _key is not a null pointer before using delete on it.
    • There is no need to check if new returned a null pointer since a failure to allocate memory would result in a std::bad_alloc exception being thrown.
    • You should use the constructor initialisation list instead of assigning from within the constructor body. Speaking of which, it looks like you assigned to a member variable named lock when you declared it as _lock.
    • Perhaps CIPHER_ROUNDS should be a const static member variable of the SimpleCipher class instead of a macro
    • Remember the rule of three: if you need to implement the destructor, copy constructor, or copy assignment operator, you probably need to implement all three, or at least disable the latter two by declaring them private.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    It looks like the algorithm is susceptible to "bad" passwords.

    It also looks like your implementation may be susceptible to overflow with the right key.

    I may be wrong though; I put my work into known algorithms for my cryptography needs.

    Soma

  8. #8
    ‡ †hë Ö†hÈr sîÐè ‡ Nor's Avatar
    Join Date
    Nov 2001
    Posts
    299
    Quote Originally Posted by laserlight View Post
    Well, I am not qualified to judge the strength of a cipher on my own, and the mere belief of
    the designer in the strength of the algorithm is not enough to have any trust that the cipher is not snake oil.
    Never heard of snake oil but I understand what you mean.

    Quote Originally Posted by laserlight View Post
    otherwise you should implement a well known algorithm that is regarded as strong if you
    want to put this to real use.
    Well known algorithms have been studied for years and are (as much as possible)proven
    secure. But I just wanted to make something of my own.
    2 weeks I think I've done good for the base code of the algorithm.

    Quote Originally Posted by laserlight View Post
    but names beginning with underscores are reserved to the implementation for use in the
    global namespace, or for any use if an upper case letter immediately follows the underscore
    (e.g., _MakeCheckSum).
    I'm a self taught coder, most common standards and practices such as naming I don't know.
    See all the Edits: in Red.Looks A lot better to.

    Quote Originally Posted by laserlight View Post
    A few other things to note:
    • It is safe to use delete on a null pointer, so you do not need to check that _key is not a
      null pointer before using delete on it.
    In Visual studio .net 2003 debug if you delete something twice it throws and assert dialog up with
    VERY LITTLE information about the error. Took me hours to figure out why when I first started
    using it MSVS.net


    Quote Originally Posted by laserlight View Post
    • There is no need to check if new returned a null pointer since a failure to allocate
      memory would result in a std::bad_alloc exception being thrown.
    I've taken heat for this in most the code I've posted.
    Checking new for null pointers is just a force of habit,
    sorry ya ll but I do program on machines that will return a null pointer when memory
    can not be associated.
    TLS, fuel monitors and other PLC type stuff that is Usually not applied to normal computers.

    Quote Originally Posted by laser light View Post
    • You should use the constructor initialisation list instead of assigning from within the
      constructor body. Speaking of which, it looks like you assigned to a member variable named
      lock when you declared it as _lock.
    I use constructors initialization just because its easier to write. but your right.
    The Proper way would be like you said.

    lock = false is suppose to be _lock. not sure what happened there.
    cut in paste in vista is not what it use to be.


    Quote Originally Posted by phantomotap View Post
    It looks like the algorithm is susceptible to "bad" passwords.
    And short passwords.
    When designing the 'MakeCheckSum()' function I was able to have 1 bit alter more
    than 50% or the output, but only after 15 input bytes where processed.
    Thats the reason I loop 255 times in the 'makeKey()' method.
    Anyone have a better suggestion for the checksum method?

    Quote Originally Posted by phantomotap View Post
    It also looks like your implementation may be susceptible to overflow with the right key.
    Are you talking about a buffer overflow?
    I've studied this code in debug mode with 'buffer overrun' checks both in my implementation
    and by using the linker option,
    Could you please be more specific.

    Please Everyone Read. This is not a secure way to encrypt your data.
    All that is needed is 1 plane text byte within the first 262144 bytes of cypher text to crack the key.
    Simple Brute Force of 'unsigned char ring_a' until the known byte is decrypted
    After 262144 the cypher cycles itself then ring_a must be cracked again.
    After cracking ring_a twice the exact '_key' and length can be found.
    This is due to the way I use ring_a to generate ring_b, and ring_b to generate ring_c.

    I've just started this project.
    I'd love to see it evolve into something grand.
    Please, keep the feedback coming.
    Last edited by Nor; 02-16-2009 at 03:12 PM. Reason: Spell Check
    Try to help all less knowledgeable than yourself, within
    the limits provided by time, complexity and tolerance.
    - Nor

  9. #9
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Snake-oil is a term used to describe something that pretends to be something that it really isn't ("It isn't what the label says"). In the old days "doctors" used to sell products that was based on "Snake-oil" that would cure everything and anything. Bear in mind that snake-meat is one of the leanest forms of meat you can find (and they also have very little in way of other forms of oil/fat), so getting oil in any quantity would of course require a HUGE amount of snakes. Obviously, if someone has several bottles for sale at $x each, then most likely it isn't "oil from snakes", no matter what the label says.

    As everyone else says, there are two types of encryption:
    • Proven good
    • Not proven good

    For anything that NEEDS proper encryption, proven good algorithms are a must. For hiding the content of a file from your little brother, mum or dad (who have no great incentive to find out what the file contains ANYWAY), the weak "home-brew" alternative is perhaps OK.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  10. #10
    ‡ †hë Ö†hÈr sîÐè ‡ Nor's Avatar
    Join Date
    Nov 2001
    Posts
    299
    Thinks for the detailed description about snake oil.
    Quote Originally Posted by Nor View Post
    Please Everyone Read. This is not a secure way to encrypt your data.
    Try to help all less knowledgeable than yourself, within
    the limits provided by time, complexity and tolerance.
    - Nor

  11. #11
    ‡ †hë Ö†hÈr sîÐè ‡ Nor's Avatar
    Join Date
    Nov 2001
    Posts
    299
    I seam to remember reading that Leonard Max Adleman was told to
    "Use existing algorithms and quit waisting your professors time".
    And look what happened to him.

    Edit :
    Sorry. the above is wrong. The Quote from his biography states
    "Quit making up new mathematics and use the ones in your book, your wasting my time".
    Last edited by Nor; 02-16-2009 at 06:54 PM.
    Try to help all less knowledgeable than yourself, within
    the limits provided by time, complexity and tolerance.
    - Nor

  12. #12
    Registered User
    Join Date
    Dec 2006
    Location
    Canada
    Posts
    3,229
    In Visual studio .net 2003 debug if you delete something twice it throws and assert dialog up with
    VERY LITTLE information about the error. Took me hours to figure out why when I first started
    using it MSVS.net
    It's illegal to free a pointer twice, and that is not the same as freeing a null pointer, which is legal. "delete" does NOT set a pointer to null. You will have to do that yourself.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 16
    Last Post: 11-23-2007, 01:48 PM
  2. Simple Shift errors...
    By Junior89 in forum C++ Programming
    Replies: 5
    Last Post: 06-26-2007, 05:55 AM
  3. Simple message encryption
    By Vicious in forum C++ Programming
    Replies: 10
    Last Post: 11-07-2004, 11:48 PM
  4. Binary Search Trees Part III
    By Prelude in forum A Brief History of Cprogramming.com
    Replies: 16
    Last Post: 10-02-2004, 03:00 PM
  5. Request for comments
    By Prelude in forum A Brief History of Cprogramming.com
    Replies: 15
    Last Post: 01-02-2004, 10:33 AM