[cryptography] understand the Padding mode

Cryptography - Padding mode

When encrypting data, some encryption algorithms need plaintext to meet certain length requirements, such as DES and AES. For grouping encryption, plaintext is a multiple of packets, but in most cases, the probability of plaintext meeting the requirements is very low. In previous implementations, my implementations did not consider the case that the encryption length is not met, That is, I have ensured that the content I have passed in meets the needs. This article will talk about how to pad when I am not satisfied.

NoPadding

As the name suggests, this is not filled, that is, the mode I adopted before. This requires that the original data must meet the grouping requirements. When it is not met, this mode cannot be used.

PKCS5/PKCS7

Fill to an integer multiple of the block size. The fill value is the number of fills. The block size of PKCS5Padding should be 8 bytes, while the block size of PKCS7Padding can be in the range of 1 ~ 255. Because the size of AES block is exactly 8 bytes, the two methods are the same for AES Padding.

X923Padding

Fill to an integer multiple of the block size. The last byte of the filling value is the number of filling, and other bytes are filled with 0

ISO10126Padding

Fill to an integer multiple of the block size. The last byte of the filling value is the filled quantity, and other bytes are filled with random bytes.

ISO7816-4Padding

Fill to an integer multiple of the block size. The first byte of the filling value is 0x80, and other bytes are filled with 0x00.

TBCPadding(Trailling-Bit-Compliment)

When the last bit of the original text is 1, 0x00 is filled, and when the last bit is 0, 0xFF is filled.

PKCS1Padding

The filling format is as follows:

Padding = 00 + BT + PS + 00 + D
  • 00 is a fixed byte
  • BT is the processing mode
  • PS is the padding byte, and the padding quantity is k - 3 - D. K represents the length of the key and D represents the length of the original text. The minimum length of PS is 8 bytes. The filled value is determined according to the BT value:
    • When BT = 00, full 00 is filled
    • When BT = 01, full FF is filled
    • When BT = 02, it is filled randomly, but it cannot be 00.

summary

I just introduced some common Padding schemes above. Let's make a summary to compare the differences between these Padding schemes.

Padding schemeRecord lengthAre random bytes involved
PKCS5/PKCS7
X923Padding
ISO10126Padding
ISO7816-4Padding
TBCPadding
PKCS1Padding

code implementation

This code introduces a package because some padding requires the participation of random numbers.

// add package: rand = "0.6.3"

use rand::Rng;
use std::iter::repeat;

pub struct Padding {}

impl Padding {
    pub fn no_padding(bytes: &[u8], _block_size: u8) -> Vec<u8> {
        return bytes.to_owned();
    }

    pub fn zero_padding(bytes: &[u8], block_size: u8) -> Vec<u8> {
        let mut padding_length = block_size as usize - bytes.len() % block_size as usize;

        let mut result = bytes.to_owned();

        while padding_length > 0 {
            padding_length -= 1;
            result.push(0x0);
        }
        return result;
    }

    pub fn iso10126_padding(bytes: &[u8], block_size: u8) -> Vec<u8> {
        let mut result = bytes.to_owned();
        let mut rng = rand::thread_rng();

        while (result.len() + 1) % block_size as usize != 0 {
            result.push(rng.gen())
        }

        result.push((result.len() - bytes.len() + 1) as u8);

        return result;
    }

    pub fn iso7816_4padding(bytes: &[u8], block_size: u8) -> Vec<u8> {
        let mut result = bytes.to_owned();
        result.push(0x80);
        while result.len() % block_size as usize != 0 {
            result.push(0x00);
        }
        result
    }

    pub fn pkcs5_padding(bytes: &[u8], block_size: u8) -> Vec<u8> {
        let mut result = bytes.to_owned();
        let padding_length = block_size as usize - bytes.len() % block_size as usize;
        result.extend(repeat(padding_length as u8).take(padding_length));
        return result;
    }

    pub fn x923_padding(bytes: &[u8], block_size: u8) -> Vec<u8> {
        let mut result = bytes.to_owned();

        while (result.len() + 1) % block_size as usize != 0 {
            result.push(0x00);
        }
        result.push((result.len() - bytes.len() + 1) as u8);

        result
    }

    pub fn tbc_padding(bytes: &[u8], block_size: u8) -> Vec<u8> {
        let last_bit = bytes[bytes.len() - 1] & 0x1;
        let mut result = bytes.to_owned();

        let padding_byte = match last_bit != 0 {
            true => 0x00u8,
            false => 0xFFu8,
        };

        while result.len() % block_size as usize != 0 {
            result.push(padding_byte);
        }

        result
    }

    pub fn pkcs1_padding(bytes: &[u8], block_size: u8) -> Vec<u8> {
        // 00 + BT + PS + 00 + D
        let mut result = bytes.to_owned();
        result.push(0x00);
        result.push(0x02);
        let mut padding_length = block_size as usize - 3 - bytes.len();
        let mut rng = rand::thread_rng();

        while padding_length > 0 {
            padding_length -= 1;
            result.push(rng.gen());
        }

        result.push(0x00);
        result.push(bytes.len() as u8);
        return result;
    }
}

#[cfg(test)]
mod test {
    use crate::padding::{Padding};

    #[test]
    fn test_no_padding() {
        let result = Padding::no_padding(&[1, 2, 3, 4], 0);
        assert_eq!(result, vec![1, 2, 3, 4]);
    }

    #[test]
    fn test_zero_padding() {
        let result = Padding::zero_padding(&[1, 2, 3, 4], 16);
        assert_eq!(result, vec![1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    }

    #[test]
    fn test_iso10126_padding() {
        let result = Padding::iso10126_padding(&[1, 2, 3, 4], 16);
        assert_eq!(result[0..4], [1, 2, 3, 4]);
        assert_eq!(result.last(), Option::Some(&12u8));
    }

    #[test]
    fn test_iso7816_4padding() {
        let result = Padding::iso7816_4padding(&[1, 2, 3, 4], 16);
        assert_eq!(result, [1, 2, 3, 4, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    }

    #[test]
    fn test_pkcs5_padding() {
        let result = Padding::pkcs5_padding(&[1, 2, 3, 4], 16);
        assert_eq!(result, vec![1, 2, 3, 4, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12])
    }

    #[test]
    fn test_pkcs1_padding() {
        let result = Padding::pkcs1_padding(&[1, 2, 3, 4], 16);
        assert_eq!(result[0..6], vec![1, 2, 3, 4, 0, 2]);
    }

    #[test]
    fn test_x923_padding() {
        let result = Padding::x923_padding(&[1, 2, 3, 4], 16);
        assert_eq!(result, [1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12])
    }

    #[test]
    fn test_tbc_padding() {
        let result = Padding::tbc_padding(&[1, 2, 3, 4], 16);
        assert_eq!(result, [1, 2, 3, 4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
        let result = Padding::tbc_padding(&[1, 2, 3, 5], 16);
        assert_eq!(result, [1, 2, 3, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    }
}

reference material

Tags: cryptology

Posted on Sun, 10 Oct 2021 07:33:13 -0400 by new_programmer