-1

When I was a kid I loved to play with Lego blocks, it was magical. I can't say that I've changed completely. Today I was playing with hash functions and decided to build my own ToyCipher, I hope no one has a trademark on it... It produces a 256-bit value that is xored with the plain text. The value is produced by hashing a state and a key.

Now that I've played with it and had fun, it's time to tear it down and start anew, but I don't know how. Would any of you kind strangers please help me find the simplest way to break it?

The header source code:

#ifndef TOY_CIPHER_H
#define TOY_CIPHER_H

typedef struct {
  unsigned char state[32];
  unsigned char key[32];
} ToyCtx;

void toycipher_init(ToyCtx* tc, unsigned char const* key,
                    unsigned char const* iv);

// must be a multiple of 32
void toycipher_encrypt(ToyCtx* tc, unsigned char const* in, int in_size,
                       unsigned char* out);
void toycipher_decrypt(ToyCtx* tc, unsigned char const* in, int in_size,
                       unsigned char* out);

#endif

and the implementation:

#include "toy_cipher.h"
#include <stdint.h>
#include <string.h>
#include <openssl/sha.h>
#include <assert.h>

// Private
static void _xor32(uint64_t* dest, uint64_t const* in, uint64_t const* key)
{
  for(int i = 0; i < 4; ++i)
    *dest++ = *in++ ^ *key++;
}

// Public
void toycipher_init(ToyCtx* tc, unsigned char const* key,
                    unsigned char const* iv)
{
  memcpy(tc->state, iv, 32);
  memcpy(tc->key, key, 32);
}

// must be a multiple of 32
void toycipher_encrypt(ToyCtx* tc, unsigned char const* in, int in_size,
                       unsigned char* out)
{
  assert((in_size % 32) == 0);

  int n = in_size >> 5; // in_size / 32
  while(n){
    uint64_t k[4];
    SHA256((unsigned char const*)tc, 64, (unsigned char*)k);
    _xor32((uint64_t*)out, (uint64_t*)in, k);
    memcpy(tc->state, out, 32);
    in += 32;
    out += 32;
    --n;
  }
}

void toycipher_decrypt(ToyCtx* tc, unsigned char const* in, int in_size,
                       unsigned char* out)
{
  assert((in_size % 32) == 0);

  int n = in_size >> 5; // in_size / 32
  while(n){
    uint64_t k[4];
    SHA256((unsigned char const*)tc, 64, (unsigned char*)k);
    memcpy(tc->state, in, 32);
    _xor32((uint64_t*)out, (uint64_t*)in, k);
    in += 32;
    out += 32;
    --n;
  }
}
  • 3
    Hey! Welcome to the Security Stack. Looks like a fun little toy you've got there, but just to let you know, you're probably not going to get much of a response here. Generally questions framed as "break my code" or, "is this code secure?" get down-voted real quick. More specific questions that show you've looked into how to break your own code, like, "Is this a buffer overflow?", fly a lot farther and faster on this stack. That being said, don't give up hope on trashing your toy! – INV3NT3D Sep 23 '16 at 19:03
  • 1
    @drewbenn, that doesn't look like Vigenere, there's a feedback from the output to the hash on the next round, so it shouldn't repeat that trivially – ilkkachu Sep 23 '16 at 19:27
  • This seems related, even though your construction is obviously not exactly the same: crypto.stackexchange.com/questions/9076/… . The speed issue also applies, pulling some numbers, AES is faster than SHA-256, (139 MB/s vs. 111 MB/s) and you're hashing double the bytes than the output, so you get only half of that. – ilkkachu Sep 23 '16 at 19:39
  • 1
    @drewbenn it's not a duplicate, the actual plaintext being xored here is a hash of the state + key – Douglas Sep 23 '16 at 22:15
0

This looks like an implementation of encryption using cipher feedback mode. It has some pros and cons, but it accomplishes the basic goal of stopping someone from decrypting a ciphertext without a key. (I still would recommend using established and well-reviewed libraries over your own code for encryption algorithms in real codebases.)

Note that CFB is not an authenticated mode of encryption and offers no/little integrity protection. Given a ciphertext, someone can make a valid modified ciphertext which will change the plaintext in predictable ways. This can be unacceptable and lead to vulnerabilities in some scenarios. Here's a situation that I ran into once where unauthenticated encryption created multiple vulnerabilities, coincidentally involving an encryption algorithm very similar to yours but with MD5 instead of SHA256 (MD5 does have some issues but I don't think it's an issue here).

  • So there are no very obvious and ridiculously easy ways of breaking this as far as you know? – Douglas Sep 23 '16 at 22:22
  • 1
    You should read about cipher-feedback mode and double-check for any differences that might be issues. (The choice of a hash function like SHA256 as the "cipher" is fine for CFB and isn't uncommon.) It looks likely that this is a valid implementation of CFB mode encryption. – Macil Sep 24 '16 at 0:39
  • 1
    Note that I still wouldn't recommend using your own encryption code like this in real projects that users trust their data to. Mature cryptography libraries are generally reviewed by many people, tested for correctness and against other implementations, maintained as issues come up, and the good ones contain good APIs that don't let you mess up. See nccgroup.trust/us/about-us/newsroom-and-events/blog/2009/july/… – Macil Sep 24 '16 at 0:53

Not the answer you're looking for? Browse other questions tagged or ask your own question.