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 }