1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package com.jsql.util.bruter;
19
20 import org.apache.commons.codec.binary.Base32;
21
22 import java.util.Base64;
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public class Base16 extends BaseNCodec {
47
48
49
50
51
52
53 private static final int BITS_PER_ENCODED_BYTE = 4;
54 private static final int BYTES_PER_ENCODED_BLOCK = 2;
55 private static final int BYTES_PER_UNENCODED_BLOCK = 1;
56
57
58
59
60
61
62 private static final byte[] UPPER_CASE_DECODE_TABLE = {
63
64 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
65 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
66 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
67 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
68 -1, 10, 11, 12, 13, 14, 15
69 };
70
71
72
73
74
75 private static final byte[] UPPER_CASE_ENCODE_TABLE = {
76 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
77 'A', 'B', 'C', 'D', 'E', 'F'
78 };
79
80
81
82
83
84
85 private static final byte[] LOWER_CASE_DECODE_TABLE = {
86
87 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
88 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
89 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
90 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
91 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
92 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
93 -1, 10, 11, 12, 13, 14, 15
94 };
95
96
97
98
99
100 private static final byte[] LOWER_CASE_ENCODE_TABLE = {
101 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
102 'a', 'b', 'c', 'd', 'e', 'f'
103 };
104
105
106 private static final int MASK_4BITS = 0x0f;
107
108
109
110
111 private final byte[] decodeTable;
112
113
114
115
116 private final byte[] encodeTable;
117
118
119
120
121 public Base16() {
122 this(false);
123 }
124
125
126
127
128
129
130 public Base16(final boolean lowerCase) {
131 this(lowerCase, DECODING_POLICY_DEFAULT);
132 }
133
134
135
136
137
138
139
140 public Base16(final boolean lowerCase, final CodecPolicy decodingPolicy) {
141
142 super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, 0, 0, PAD_DEFAULT, decodingPolicy);
143
144 if (lowerCase) {
145
146 this.encodeTable = LOWER_CASE_ENCODE_TABLE;
147 this.decodeTable = LOWER_CASE_DECODE_TABLE;
148
149 } else {
150
151 this.encodeTable = UPPER_CASE_ENCODE_TABLE;
152 this.decodeTable = UPPER_CASE_DECODE_TABLE;
153 }
154 }
155
156 @Override
157 public void decode(final byte[] data, int offsetInput, final int length, final Context context) {
158
159 int offset = offsetInput;
160
161 if (context.eof || length < 0) {
162
163 context.eof = true;
164 if (context.ibitWorkArea != 0) {
165 this.validateTrailingCharacter();
166 }
167
168 return;
169 }
170
171 final int dataLen = Math.min(data.length - offset, length);
172 final int availableChars = (context.ibitWorkArea != 0 ? 1 : 0) + dataLen;
173
174
175 if (availableChars == 1 && availableChars == dataLen) {
176
177 context.ibitWorkArea = this.decodeOctet(data[offset]) + 1;
178 return;
179 }
180
181
182 final int charsToProcess = availableChars % BYTES_PER_ENCODED_BLOCK == 0 ? availableChars : availableChars - 1;
183
184 final byte[] buffer = this.ensureBufferSize(charsToProcess / BYTES_PER_ENCODED_BLOCK, context);
185
186 int result;
187 var i = 0;
188 if (dataLen < availableChars) {
189
190
191 result = (context.ibitWorkArea - 1) << BITS_PER_ENCODED_BYTE;
192 result |= this.decodeOctet(data[offset++]);
193 i = 2;
194
195 buffer[context.pos++] = (byte)result;
196
197
198 context.ibitWorkArea = 0;
199 }
200
201 while (i < charsToProcess) {
202
203 result = this.decodeOctet(data[offset++]) << BITS_PER_ENCODED_BYTE;
204 result |= this.decodeOctet(data[offset++]);
205 i += 2;
206 buffer[context.pos++] = (byte)result;
207 }
208
209
210 if (i < dataLen) {
211 context.ibitWorkArea = this.decodeOctet(data[i]) + 1;
212 }
213 }
214
215 private int decodeOctet(final byte octet) {
216
217 int decoded = -1;
218 if ((octet & 0xff) < this.decodeTable.length) {
219 decoded = this.decodeTable[octet];
220 }
221
222 if (decoded == -1) {
223 throw new IllegalArgumentException("Invalid octet in encoded value: " + (int)octet);
224 }
225
226 return decoded;
227 }
228
229 @Override
230 public void encode(final byte[] data, final int offset, final int length, final Context context) {
231
232 if (context.eof) {
233 return;
234 }
235
236 if (length < 0) {
237
238 context.eof = true;
239 return;
240 }
241
242 final int size = length * BYTES_PER_ENCODED_BLOCK;
243 if (size < 0) {
244 throw new IllegalArgumentException("Input length exceeds maximum size for encoded data: " + length);
245 }
246
247 final byte[] buffer = this.ensureBufferSize(size, context);
248
249 final int end = offset + length;
250 for (int i = offset; i < end; i++) {
251
252 final int value = data[i];
253 final int high = (value >> BITS_PER_ENCODED_BYTE) & MASK_4BITS;
254 final int low = value & MASK_4BITS;
255 buffer[context.pos++] = this.encodeTable[high];
256 buffer[context.pos++] = this.encodeTable[low];
257 }
258 }
259
260
261
262
263
264
265
266
267 @Override
268 public boolean isInAlphabet(final byte octet) {
269 return (octet & 0xff) < this.decodeTable.length && this.decodeTable[octet] != -1;
270 }
271
272
273
274
275
276
277
278 private void validateTrailingCharacter() {
279 if (this.isStrictDecoding()) {
280 throw new IllegalArgumentException(
281 "Strict decoding: Last encoded character is a valid base 16 alphabet" +
282 "character but not a possible encoding. " +
283 "Decoding requires at least two characters to create one byte."
284 );
285 }
286 }
287 }
288