Vittorio Cozzolino
Vittorio Cozzolino

Reputation: 941

Speck Cipher - Decryption Issue in C

I'm trying to extend the following implementation of the Speck cipher (https://github.com/inmcm/Simon_Speck_Ciphers) by adding the decryption module. I implemented the decryption algorithm by following the instructions from the original paper from NSA about Speck and Simon (https://eprint.iacr.org/2013/404).

At the moment I'm kinda stuck because I can't decipher correctly the cipher text generated from the encryption routine. I already had a look at other similar questions posted here on SO to no avail.

By checking my code and the stdout, I've noticed that the value stored in x_word in the decryption phase is not correct, starting with the first iteration. Therefore, there is probably an issue with the following instruction:

x_word = rotate_left( (sub_mod((x_word ^ *(round_key_ptr + 21 - i)), y_word, 65535)), 7);

I'm posting the relevant part of my code here:

#define rotate_left(x,n) (x >> (word_size - n)) | (x << n)
#define rotate_right(x,n) (x << (word_size - n)) | (x >> n) 

void Speck_Encrypt_32(uint8_t *key_schedule, uint8_t *plaintext, uint8_t *ciphertext) {

    const uint8_t word_size = 16;
    uint16_t y_word = *(uint16_t *)plaintext;
    uint16_t x_word = *(((uint16_t *)plaintext) + 1);
    uint16_t *round_key_ptr = (uint16_t *)key_schedule;
    uint16_t * word_ptr = (uint16_t *)ciphertext;

    for(uint8_t i = 0; i < 22; i++) {  // Block size 32 has only one round number option        

        x_word = ((rotate_right(x_word, 7)) + y_word) ^ *(round_key_ptr + i);
        y_word = (rotate_left(y_word, 2)) ^ x_word;

        printf("y_word - (%d) - %u\n", i, (unsigned int)y_word);
        printf("x_word - (%d) - %u\n", i, (unsigned int)x_word);
    }
    // Assemble Ciphertext Output Array   
    *word_ptr = y_word;
    word_ptr += 1;
    *word_ptr = x_word;
    return;
}

static inline uint16_t sub_mod(uint16_t a, uint16_t b, uint16_t m)
{
  if ( a>=b )
    return a - b;
  else
    return m - b + a;
}


void Speck_Decrypt_32(uint8_t *key_schedule, uint8_t *plaintext, uint8_t *ciphertext) {

    const uint8_t word_size = 16;
    // Swapping cipher text words
    uint16_t y_word = *(uint16_t *)ciphertext;
    uint16_t x_word = *(((uint16_t *)ciphertext) + 1);
    uint16_t *round_key_ptr = (uint16_t *)key_schedule;
    uint16_t * word_ptr = (uint16_t *)plaintext;

    for(int i=0; i < 4; i++) {
        printf("Ciphertext Byte %02d: %02x \n",i,ciphertext[i]);
        printf("Plaintext Byte %02d: %02x \n",i,plaintext[i]);
    } 

    // Reading round keys in reverse order
    for(uint8_t i = 0; i < 22; i++) {  // Block size 32 has only one round number option

        //printf("y_word - (%d) - %u\n", i, (unsigned int)y_word);
        //printf("x_word - (%d) - %u\n", i, (unsigned int)x_word);
        // Inverting rotations and using custom modular subtraction
        y_word = rotate_right((x_word ^ y_word), 2);
        x_word = rotate_left( (sub_mod((x_word ^ *(round_key_ptr + 21 - i)), y_word, 65535)), 7);

    }
    // Assemble Plaintext - Swapping plaintext words  
    *word_ptr = y_word;
    word_ptr += 1;
    *word_ptr = x_word;
    return;
}

I haven't been coding in C in awhile, there might be a few mistakes.

Upvotes: 0

Views: 1019

Answers (3)

Vittorio Cozzolino
Vittorio Cozzolino

Reputation: 941

Good old pen and paper checks helped me to find two mistakes:

  1. The modular subtraction function was wrong because I was basically doing a mod n-1 instead of a mod n (I was passing as divisor the wrong value).
  2. The function to generate the key schedule was wrong to start with. I had to rewrite it following the research paper specifications.

After fixing the aforementioned bugs, I'm now able to correctly encrypt/decrypt with different block/bey sizes.

Thanks everyone for the help anyway.

Upvotes: 0

Louis
Louis

Reputation: 21

Just do the encryption in reverse. So if your expanded key is k_0,...,k_{21} then you would do the following in C.

#define ROTR(x,r) (((x > > r) | (x < < (16-r))) & 0xffff)

#define ROTL(x,r) (((x< < r) | (x > > (16-r))) & 0xffff)

#define invR(x,y,k) (y=y^x, y=ROTR(y,2), x=x^k, x=(x-y) & 0xffff, x=ROTL(x,7))

Then you do something like:

for(i=21;i>=0;i--) invR(x,y,k[i])

FYI: SIMON and SPECK are among the most highly analyzed block ciphers in recent years. There are more than 70 "peer reviewed" papers which analyze them (as you may check by doing a search on Google Scholar) and they have a security margin similar to AES-128. On an x86 processor, SPECK128/256 has speed nearly identical to ChaCha20 (a very fast stream cipher). A fast software implementation of SPECK128/256 on an x86 processor is about 50% slower than AES-256 using AES-NI (i.e., the hardware instruction). See https://github.com/iadgov/simon-speck-supercop for fast implementations of SIMON and SPECK compatible with the SUPERCOP benchmarking system.

Upvotes: 1

Seth
Seth

Reputation: 2647

Given that the repo's author didn't get around the writing the C decryption routine, I'd have some concerns as to the validity of the C setup or encryption routine first.

Rather than starting there, why not look over one of the working C implementations:

https://github.com/tomasaazevedo/Speck_8-bit_C

https://github.com/yuehann/Simon_Speck

https://github.com/mysummergit/myssl/blob/master/demos/mycipher/speck.c

Upvotes: 1

Related Questions