Implement PKCS#7 padding

Stefan Alfbo - May 16 - - Dev Community

This is the first crypto challenge in set 2 from cryptopals, which is a set on block cipher cryptography. This is bread-and-butter crypto, the kind you'll see implemented in most web software that does crypto.

The difficulty level is relatively easy, to solve this problem I have used the programming language Go.

Problem description

A block cipher transforms a fixed-sized block (usually 8 or 16 bytes) of plaintext into ciphertext. But we almost never want to transform a single block; we encrypt irregularly-sized messages.

One way we account for irregularly-sized messages is by padding, creating a plaintext that is an even multiple of the blocksize. The most popular padding scheme is called PKCS#7.

So: pad any block to a specific block length, by appending the number of bytes of padding to the end of the block. For instance,

"YELLOW SUBMARINE"

... padded to 20 bytes would be:

"YELLOW SUBMARINE\x04\x04\x04\x04"

Solution

We begin to create a unit test based on the input and output from the problem description above.

func TestPKCS7Padding(t *testing.T) {
    data := []byte("YELLOW SUBMARINE")

    t.Run("Padding needed", func(t *testing.T) {
        expected := []byte("YELLOW SUBMARINE\x04\x04\x04\x04")
        blockSize := 20

        actual, err := block.PKCS7Padding(data, blockSize)
        if err != nil {
            t.Errorf("Unexpected error: %v", err)
        }

        if string(actual) != string(expected) {
            t.Errorf("Expected %v, was %v", expected, actual)
        }
    })

    t.Run("No padding needed", func(t *testing.T) {
        blockSize := 16
        actual, err := block.PKCS7Padding(data, blockSize)
        if err != nil {
            t.Errorf("Unexpected error: %v", err)
        }

        if string(actual) != string(data) {
            t.Errorf("Expected %v, was %v", data, actual)
        }
    })
}
Enter fullscreen mode Exit fullscreen mode

We are using YELLOW SUBMARINE and the block size 20 as input parameters to the function. The actual output is then confirmed against the expected variable which is, YELLOW SUBMARINE\x04\x04\x04\x04. There is also a test for when the data has the size so it can be evenly divided by the block size.

The implementation of the PKCS7Padding function could look like this.

func PKCS7Padding(data []byte, blockSize int) ([]byte, error) {
    rest := (len(data) % blockSize)
    if rest == 0 {
        return data, nil
    }

    paddingSize := blockSize - rest
    padded := make([]byte, len(data)+paddingSize)

    copy(padded, data)

    for i := len(data); i < len(padded); i++ {
        padded[i] = byte(paddingSize)
    }

    return padded, nil
}
Enter fullscreen mode Exit fullscreen mode

First we calculate the padding size by using the modulus operator to see if the data can be evenly divided by the block size. If there is no rest, then we don't have to pad the data; otherwise we calculate how many bytes we need to pad to complete the last block.

The for loop is doing the padding, where each byte added as padding is set to the number of bytes that are added. This means the value of each padding byte is the same as the paddingSize variable.

The complete solution can be found on GitHub.

Conclusion

The padding scheme for PKCS#7 is simple to understand and straightforward to implement.

References

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .