2

Im trying to implement the serpent encryption algorithm from scratch as a personal side project, now I am stuck with the key scheduling part of it. according to the only the documentation ,https://www.cl.cam.ac.uk/~rja14/Papers/serpent.pdf, I need to padd the key to 256 so I can start generating the subkeys.

how do I apply padding without relying on a external library.

here is the code that I wrote that I am not a 100 percent sure that will lead me to what I am trying to achieve

void prekeyexpasion(unsigned char * key,unsigned char * expandedkeys){
    //padd the key to 256
    int originalkeylen = strlen((const char*)key);
    int lenOfPaddedkey = originalkeylen;
    if(lenOfPaddedkey % 32 !=0){
        lenOfPaddedkey = (lenOfPaddedkey / 32 + 1) * 32;
    }
    unsigned char * paddedkey = new unsigned char[lenOfPaddedkey];

    for (int i = 0; i < lenOfPaddedkey; i++){
        if ( i >= originalkeylen){
            paddedkey[i] = 1;
        }
        else{
            paddedkey[i] = key[i];
        }
        
    }
    //expand to 33 128bit subkeys
    //split per 128bit subkeys to 8 32 bit subsubkey

    
    
}
Arthur
  • 25
  • 5
  • Padding key is not a good idea, reject the small key input or use PBKDF2 to derive full key size. – kelalaka Jul 05 '21 at 13:34
  • @kelalaka No, this is about internal padding performed on the key. – Maarten Bodewes Jul 06 '21 at 16:51
  • @MaartenBodewes I'm aware of that. Why do we want to pad a key? Does anybody perform a security analysis of padding a key? – kelalaka Jul 06 '21 at 17:04
  • 1
    @kelalaka I meant internal as specified in the Serpent paper. The reason why they perform padding is specified in there. A 128 bit key should not be the same as a 192 bit key by accident because the final key word is set to zero instead of one. This goes for any key size except for 256 bit keys I suppose, if you zoom in on the bytes. I'm not defending that design decision, by the way, that's just how it is defined. – Maarten Bodewes Jul 06 '21 at 17:30

1 Answers1

2

Let's have a look at the standard, assuming little endian representation as indicated at the start of the paper:

The user key length is variable, but for the purposes of this submission we fix it at 128, 192 or 256 bits; short keys with less than 256 bits are mapped to full-length keys of 256 bits by appending one “1” bit to the MSB end, followed by as many “0” bits as required to make up 256 bits.


And then have a look at your code:

int originalkeylen = strlen((const char*)key);

First of all, a key is not a password. It's a binary string, not a textual string. That means that strlen cannot be used on keys. Instead the size of the key should be included with the key when supplied (or you should use a higher level construct to deliver the key, this is C++ after all, not C).

int lenOfPaddedkey = originalkeylen;

Generally I would never assign an incorrect value to a variable, even not temporarily.

if(lenOfPaddedkey % 32 !=0){
    lenOfPaddedkey = (lenOfPaddedkey / 32 + 1) * 32;
}

You'd first check if the key size is correct using guards. In that case % 32 isn't needed, you might as well use lenOfPaddedkey != 32. Besides that, the lenOfPaddedkey / 32 will always be zero, thus the entire line might as well be int lenOfPaddedkey = 32, and this should be correct. However, if it is always 32, why would you need a variable?

unsigned char * paddedkey = new unsigned char[lenOfPaddedkey];

Hmm, I don't like to use char * here, but OK, we've got a new buffer consisting of bytes. Beware that this call doesn't initialize to zero, see here for more information.

for (int i = 0; i < lenOfPaddedkey; i++){

OK, fine, although just putting in a 32 literal (as constant / #define will probably speed things up).

    if ( i >= originalkeylen){
        paddedkey[i] = 1;
    }

This is incorrect, only one bit / byte should be added, then zero bits / bytes.

    else{
        paddedkey[i] = key[i];
    }

Yeah, just using a library call to copy bytes could be faster.


Instead:

  1. check sizes of key, should be 16, 24 or 32 bytes;

  2. create padded_key buffer of 4 int32_t values;

  3. copy the bytes into the padded_key buffer (look at this answer, beware that C++ integers are an undefined mess);

  4. perform the padding if needed:

    a. if 2 int32_t are used for a 128 bit key, set the 3rd int32_t to value 1 and set the final int32_t to zero;

    b. if 3 int32_t are used for a 192 bit key, set the 4th int32_t to value 1.

Now you can continue with the key expansion (did you maybe need a larger buffer in the first place?) based on the padded key in 32 bit words.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263