1 /*
2 * GordianKnot: Security Suite
3 * Copyright 2012-2026. Tony Washer
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 * use this file except in compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17 package io.github.tonywasher.joceanus.gordianknot.api.cipher;
18
19 import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestSpec;
20
21 import java.util.Objects;
22
23 /**
24 * PBE Specification.
25 */
26 public abstract class GordianPBESpec {
27 /**
28 * The Separator.
29 */
30 private static final String SEP = "-";
31
32 /**
33 * The PBEType.
34 */
35 private final GordianPBEType theType;
36
37 /**
38 * is the Spec valid?
39 */
40 private boolean isValid;
41
42 /**
43 * Constructor.
44 *
45 * @param pPBEType the PBEType.
46 */
47 GordianPBESpec(final GordianPBEType pPBEType) {
48 theType = pPBEType;
49 }
50
51 /**
52 * Obtain the PBEType.
53 *
54 * @return the PBEType
55 */
56 public GordianPBEType getPBEType() {
57 return theType;
58 }
59
60 /**
61 * Is the Spec valid?
62 *
63 * @return true/false
64 */
65 public boolean isValid() {
66 return isValid;
67 }
68
69 /**
70 * Set as valid.
71 */
72 void setValid() {
73 isValid = true;
74 }
75
76 /**
77 * DigestAndCountSpec.
78 */
79 public static class GordianPBEDigestAndCountSpec
80 extends GordianPBESpec {
81 /**
82 * The DigestSpec.
83 */
84 private final GordianDigestSpec theDigestSpec;
85
86 /**
87 * The count.
88 */
89 private final int theCount;
90
91 /**
92 * Constructor.
93 *
94 * @param pPBEType the PBEType.
95 * @param pDigestSpec the digestSpec.
96 * @param pCount the iteration count
97 */
98 GordianPBEDigestAndCountSpec(final GordianPBEType pPBEType,
99 final GordianDigestSpec pDigestSpec,
100 final int pCount) {
101 /* Init underlying class and store params */
102 super(pPBEType);
103 theDigestSpec = pDigestSpec;
104 theCount = pCount;
105
106 /* Check validity */
107 checkValidity();
108 }
109
110 /**
111 * Obtain the digestSpec.
112 *
113 * @return the digestSpec
114 */
115 public GordianDigestSpec getDigestSpec() {
116 return theDigestSpec;
117 }
118
119 /**
120 * Obtain the iteration count.
121 *
122 * @return the count
123 */
124 public int getIterationCount() {
125 return theCount;
126 }
127
128 /**
129 * Check validity.
130 */
131 private void checkValidity() {
132 /* Check PBEType */
133 if (getPBEType() != GordianPBEType.PBKDF2
134 && getPBEType() != GordianPBEType.PKCS12) {
135 return;
136 }
137
138 /* Check DigestSpec and Count > 0 */
139 if (theDigestSpec != null
140 && theDigestSpec.isValid()
141 && theCount > 0) {
142 setValid();
143 }
144 }
145
146 @Override
147 public boolean equals(final Object pThat) {
148 /* Handle trivial cases */
149 if (this == pThat) {
150 return true;
151 }
152 if (pThat == null) {
153 return false;
154 }
155
156 /* Check count, digestSpec and PBEType */
157 return pThat instanceof GordianPBEDigestAndCountSpec myThat
158 && theCount == myThat.getIterationCount()
159 && theDigestSpec.equals(myThat.getDigestSpec())
160 && getPBEType() == myThat.getPBEType();
161 }
162
163 @Override
164 public int hashCode() {
165 return Objects.hash(theDigestSpec, theCount, getPBEType());
166 }
167
168 @Override
169 public String toString() {
170 return getPBEType().toString() + SEP + theDigestSpec.toString() + SEP + theCount;
171 }
172 }
173
174 /**
175 * SCryptSpec.
176 */
177 public static class GordianPBESCryptSpec
178 extends GordianPBESpec {
179 /**
180 * Max Small Block Cost.
181 */
182 private static final int MAX_SMALL_COST = 0xFFFF;
183
184 /**
185 * Parallel limit.
186 */
187 private static final int PARALLEL_LIMIT = 128;
188
189 /**
190 * The BlockSize.
191 */
192 private final int theBlockSize;
193
194 /**
195 * The cost.
196 */
197 private final int theCost;
198
199 /**
200 * The Parallelism.
201 */
202 private final int theParallel;
203
204 /**
205 * Constructor.
206 *
207 * @param pCost the cost
208 * @param pBlockSize the blockSize
209 * @param pParallel the parallelism
210 */
211 GordianPBESCryptSpec(final int pCost,
212 final int pBlockSize,
213 final int pParallel) {
214 /* Init underlying class and store params */
215 super(GordianPBEType.SCRYPT);
216 theCost = pCost;
217 theBlockSize = pBlockSize;
218 theParallel = pParallel;
219
220 /* Check validity */
221 checkValidity();
222 }
223
224 /**
225 * Obtain the blockSize.
226 *
227 * @return the blockSize
228 */
229 public int getBlockSize() {
230 return theBlockSize;
231 }
232
233 /**
234 * Obtain the cost.
235 *
236 * @return the cost
237 */
238 public int getCost() {
239 return theCost;
240 }
241
242 /**
243 * Obtain the parallelism.
244 *
245 * @return the parallelism
246 */
247 public int getParallel() {
248 return theParallel;
249 }
250
251 /**
252 * Check validity.
253 */
254 private void checkValidity() {
255 /* Check BlockSize is > 0 */
256 if (theBlockSize <= 0) {
257 return;
258 }
259
260 /* Check Cost is > 1 and power of two */
261 if (theCost <= 1
262 || (theCost & (theCost - 1)) != 0) {
263 return;
264 }
265
266 /* Check Cost restriction for BlockSize of 1 */
267 if (theBlockSize == 1
268 && theCost > MAX_SMALL_COST) {
269 return;
270 }
271
272 /* Check Parallel restriction */
273 final int maxParallel = Integer.MAX_VALUE / (PARALLEL_LIMIT * theBlockSize * Byte.SIZE);
274 if (theParallel >= 1
275 && theParallel <= maxParallel) {
276 setValid();
277 }
278 }
279
280 @Override
281 public boolean equals(final Object pThat) {
282 /* Handle trivial cases */
283 if (this == pThat) {
284 return true;
285 }
286 if (pThat == null) {
287 return false;
288 }
289
290 /* Check cost, blockSize and parallel */
291 return pThat instanceof GordianPBESCryptSpec myThat
292 && theCost == myThat.getCost()
293 && theBlockSize == myThat.getBlockSize()
294 && theParallel == myThat.getParallel()
295 && getPBEType() == myThat.getPBEType();
296 }
297
298 @Override
299 public int hashCode() {
300 return Objects.hash(theBlockSize, theCost, theParallel, getPBEType());
301 }
302
303 @Override
304 public String toString() {
305 return getPBEType().toString() + SEP + theBlockSize + SEP + theCost + SEP + theParallel;
306 }
307 }
308
309 /**
310 * Argon2Spec.
311 */
312 public static class GordianPBEArgon2Spec
313 extends GordianPBESpec {
314 /**
315 * The Memory.
316 */
317 private final int theMemory;
318
319 /**
320 * The lanes.
321 */
322 private final int theLanes;
323
324 /**
325 * The Iterations.
326 */
327 private final int theIterations;
328
329 /**
330 * Constructor.
331 *
332 * @param pLanes the Lanes
333 * @param pMemory the Memory
334 * @param pIterations the iterations
335 */
336 GordianPBEArgon2Spec(final int pLanes,
337 final int pMemory,
338 final int pIterations) {
339 /* Init underlying class and store params */
340 super(GordianPBEType.ARGON2);
341 theLanes = pLanes;
342 theMemory = pMemory;
343 theIterations = pIterations;
344
345 /* Check validity */
346 checkValidity();
347 }
348
349 /**
350 * Obtain the lanes.
351 *
352 * @return the lanes
353 */
354 public int getLanes() {
355 return theLanes;
356 }
357
358 /**
359 * Obtain the memory.
360 *
361 * @return the memory
362 */
363 public int getMemory() {
364 return theMemory;
365 }
366
367 /**
368 * Obtain the iteration count.
369 *
370 * @return the count
371 */
372 public int getIterationCount() {
373 return theIterations;
374 }
375
376 /**
377 * Check validity.
378 */
379 private void checkValidity() {
380 /* Check Iterations and Lanes are > 0 */
381 if (theIterations <= 0
382 || theLanes <= 0) {
383 return;
384 }
385
386 /* Check Memory is >= 2 * lanes */
387 if (theMemory >= theLanes << 1) {
388 setValid();
389 }
390 }
391
392 @Override
393 public boolean equals(final Object pThat) {
394 /* Handle trivial cases */
395 if (this == pThat) {
396 return true;
397 }
398 if (pThat == null) {
399 return false;
400 }
401
402 /* Check lanes, memory and iterations */
403 return pThat instanceof GordianPBEArgon2Spec myThat
404 && theLanes == myThat.getLanes()
405 && theMemory == myThat.getMemory()
406 && theIterations == myThat.getIterationCount()
407 && getPBEType() == myThat.getPBEType();
408 }
409
410 @Override
411 public int hashCode() {
412 return Objects.hash(theLanes, theMemory, theIterations, getPBEType());
413 }
414
415 @Override
416 public String toString() {
417 return getPBEType().toString() + SEP + theLanes + SEP + theMemory + SEP + theIterations;
418 }
419 }
420 }