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 }