1 module crypto.padding; 2 3 import std.exception; 4 import std.algorithm; 5 import std.bitmanip; 6 7 import crypto.random; 8 9 enum PaddingMode 10 { 11 NoPadding, // None 12 ANSIX923, // 00 00 00 04 (Zero + size) 13 ISO10126, // 0A EB 02 04 (Random + size) 14 PKCS5, // 04 04 04 04 (All size) 15 PKCS7, // 04 04 04 04 (All size) 16 Zeros, // 00 00 00 00 (All zero) 17 Customized // 00 00 00 00 + (00 00 00 04) (Zero + Original size) 18 } 19 20 private enum PaddingStuff 21 { 22 None, Zero, Random, Size, OriginalSize 23 } 24 25 alias PaddingNoPadding = PaddingImpl!(PaddingStuff.None, PaddingStuff.None); 26 alias PaddingANSIX923 = PaddingImpl!(PaddingStuff.Zero, PaddingStuff.Size); 27 alias PaddingISO10126 = PaddingImpl!(PaddingStuff.Random, PaddingStuff.Size); 28 alias PaddingPKCS5 = PaddingImpl!(PaddingStuff.Size, PaddingStuff.Size); 29 alias PaddingPKCS7 = PaddingImpl!(PaddingStuff.Size, PaddingStuff.Size); 30 alias PaddingZeros = PaddingImpl!(PaddingStuff.Zero, PaddingStuff.Zero); 31 alias PaddingCustomized = PaddingImpl!(PaddingStuff.Zero, PaddingStuff.OriginalSize); // For downward compatibility. 32 33 class PaddingImpl(PaddingStuff fill, PaddingStuff suffix) 34 { 35 static ubyte[] padding(in ubyte[] data, size_t blockSize) 36 { 37 enforce(((blockSize > 0) && (blockSize % 8 == 0)), "Invalid block size, which must be a multiple of 8."); 38 static assert(((suffix != PaddingStuff.OriginalSize) || (fill == PaddingStuff.Zero)), "PaddingCustomized require: Zero + OriginalSize."); 39 40 static if ((fill == PaddingStuff.None) || (suffix == PaddingStuff.None)) 41 { 42 enforce(((data.length > 0) && (data.length % blockSize == 0)), "Invalid data size, which must be a multiple of blockSize."); 43 44 return cast(ubyte[])data; 45 } 46 else static if (suffix != PaddingStuff.OriginalSize) 47 { 48 size_t paddingSize = blockSize - data.length % blockSize; 49 int index = cast(int)paddingSize - 1; 50 51 ubyte[] buf = new ubyte[paddingSize]; 52 53 void fillA(PaddingStuff type) 54 { 55 switch (type) 56 { 57 case PaddingStuff.Zero: 58 buf[index] = 0x00; 59 break; 60 case PaddingStuff.Random: 61 buf[index] = rnd.next!ubyte; 62 break; 63 case PaddingStuff.Size: 64 buf[index] = cast(ubyte)paddingSize; 65 break; 66 default: 67 assert(0); 68 } 69 } 70 71 fillA(suffix); 72 73 while (--index >= 0) 74 { 75 fillA(fill); 76 } 77 78 return data ~ buf; 79 } 80 else 81 { 82 ubyte[] buf; 83 84 while ((data.length + buf.length + 4) % 8 != 0) 85 { 86 buf ~= 0x00; 87 } 88 89 ubyte[] len_buf = new ubyte[4]; 90 len_buf.write!int(cast(int)data.length, 0); 91 92 return data ~ buf ~ len_buf; 93 } 94 } 95 96 static ubyte[] unpadding(in ubyte[] data, size_t blockSize) 97 { 98 enforce(((blockSize > 0) && (blockSize % 8 == 0)), "Invalid block size, which must be a multiple of 8."); 99 enforce(((data.length > 0) && (data.length % blockSize == 0)), "Invalid data size, which must be a multiple of blockSize."); 100 static assert(((suffix != PaddingStuff.OriginalSize) || (fill == PaddingStuff.Zero)), "PaddingCustomized require: Zero + OriginalSize."); 101 102 static if ((fill == PaddingStuff.None) || (suffix == PaddingStuff.None)) 103 { 104 return cast(ubyte[])data; 105 } 106 else static if ((fill == PaddingStuff.Zero) && (suffix == PaddingStuff.Size)) 107 { 108 size_t size = data[$ - 1]; 109 enforce(size <= blockSize, "Error Padding Mode."); 110 enforce(data[data.length - size..$ - 1].all!((a) => (a == 0)), "Error Padding Mode."); 111 112 return cast(ubyte[])data[0..data.length - size]; 113 } 114 else static if ((fill == PaddingStuff.Random) && (suffix == PaddingStuff.Size)) 115 { 116 size_t size = data[$ - 1]; 117 enforce(size <= blockSize, "Error Padding Mode."); 118 119 return cast(ubyte[])data[0..data.length - size]; 120 } 121 else static if ((fill == PaddingStuff.Size) && (suffix == PaddingStuff.Size)) 122 { 123 size_t size = data[$ - 1]; 124 enforce(size <= blockSize, "Error Padding Mode."); 125 enforce(data[data.length - size..$ - 1].all!((a) => (a == size)), "Error Padding Mode."); 126 127 return cast(ubyte[])data[0..data.length - size]; 128 } 129 else static if ((fill == PaddingStuff.Zero) && (suffix == PaddingStuff.Zero)) 130 { 131 enforce(data[$ - 1] == 0, "Error Padding Mode."); 132 int index = cast(int)data.length - 1; 133 134 while ((index >= 0) && (data[index] == 0)) 135 { 136 index--; 137 } 138 139 return cast(ubyte[])data[0..index + 1]; 140 } 141 else static if ((fill == PaddingStuff.Zero) && (suffix == PaddingStuff.OriginalSize)) 142 { 143 int orgi_len; 144 orgi_len = data.peek!int(data.length - 4); 145 146 enforce(((orgi_len >= 0) && (orgi_len <= (data.length - 4))), "Invalid parameter: data."); 147 148 for (size_t i = orgi_len; i < (data.length - 4); i++) 149 { 150 enforce((data[i] == 0), "Invalid parameter: data."); 151 } 152 153 return cast(ubyte[])data[0..orgi_len]; 154 } 155 else 156 { 157 assert(0); 158 } 159 } 160 } 161 162 unittest 163 { 164 ubyte[] data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; 165 ubyte[] paddinged = PaddingPKCS5.padding(data, 8); 166 ubyte[] unpaddinged = PaddingPKCS5.unpadding(paddinged, 8); 167 assert(data == unpaddinged); 168 } 169 170 class Padding 171 { 172 static ubyte[] padding(in ubyte[] data, size_t blockSize, PaddingMode paddingMode) 173 { 174 final switch (paddingMode) 175 { 176 case PaddingMode.NoPadding: 177 return PaddingNoPadding.padding(data, blockSize); 178 case PaddingMode.ANSIX923: 179 return PaddingANSIX923.padding(data, blockSize); 180 case PaddingMode.ISO10126: 181 return PaddingISO10126.padding(data, blockSize); 182 case PaddingMode.PKCS5: 183 return PaddingPKCS5.padding(data, blockSize); 184 case PaddingMode.PKCS7: 185 return PaddingPKCS7.padding(data, blockSize); 186 case PaddingMode.Zeros: 187 return PaddingZeros.padding(data, blockSize); 188 case PaddingMode.Customized: 189 return PaddingCustomized.padding(data, blockSize); 190 } 191 } 192 193 static ubyte[] unpadding(in ubyte[] data, size_t blockSize, PaddingMode paddingMode) 194 { 195 final switch (paddingMode) 196 { 197 case PaddingMode.NoPadding: 198 return PaddingNoPadding.unpadding(data, blockSize); 199 case PaddingMode.ANSIX923: 200 return PaddingANSIX923.unpadding(data, blockSize); 201 case PaddingMode.ISO10126: 202 return PaddingISO10126.unpadding(data, blockSize); 203 case PaddingMode.PKCS5: 204 return PaddingPKCS5.unpadding(data, blockSize); 205 case PaddingMode.PKCS7: 206 return PaddingPKCS7.unpadding(data, blockSize); 207 case PaddingMode.Zeros: 208 return PaddingZeros.unpadding(data, blockSize); 209 case PaddingMode.Customized: 210 return PaddingCustomized.unpadding(data, blockSize); 211 } 212 } 213 }