1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.gordianknot.impl.bc;
18
19 import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
20 import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
21 import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPairSpec;
22 import io.github.tonywasher.joceanus.gordianknot.api.sign.GordianSignParams;
23 import io.github.tonywasher.joceanus.gordianknot.api.sign.GordianSignatureSpec;
24 import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyKeyPair.BouncyPrivateKey;
25 import io.github.tonywasher.joceanus.gordianknot.impl.bc.BouncyKeyPair.BouncyPublicKey;
26 import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
27 import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
28 import io.github.tonywasher.joceanus.gordianknot.impl.core.keypair.GordianKeyPairValidity;
29 import io.github.tonywasher.joceanus.gordianknot.impl.core.sign.GordianCoreSignature;
30 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
31 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
32 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
33 import org.bouncycastle.crypto.CipherParameters;
34 import org.bouncycastle.crypto.CryptoException;
35 import org.bouncycastle.crypto.Signer;
36 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
37 import org.bouncycastle.crypto.params.ParametersWithContext;
38 import org.bouncycastle.crypto.params.ParametersWithRandom;
39 import org.bouncycastle.pqc.crypto.mldsa.HashMLDSASigner;
40 import org.bouncycastle.pqc.crypto.mldsa.MLDSAKeyGenerationParameters;
41 import org.bouncycastle.pqc.crypto.mldsa.MLDSAKeyPairGenerator;
42 import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters;
43 import org.bouncycastle.pqc.crypto.mldsa.MLDSAPrivateKeyParameters;
44 import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters;
45 import org.bouncycastle.pqc.crypto.mldsa.MLDSASigner;
46 import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory;
47 import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory;
48 import org.bouncycastle.pqc.crypto.util.PublicKeyFactory;
49 import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory;
50
51 import java.io.IOException;
52 import java.security.spec.PKCS8EncodedKeySpec;
53 import java.security.spec.X509EncodedKeySpec;
54 import java.util.Arrays;
55
56
57
58
59 public final class BouncyMLDSAKeyPair {
60
61
62
63 private BouncyMLDSAKeyPair() {
64 }
65
66
67
68
69 public static class BouncyMLDSAPublicKey
70 extends BouncyPublicKey<MLDSAPublicKeyParameters> {
71
72
73
74
75
76
77 BouncyMLDSAPublicKey(final GordianKeyPairSpec pKeySpec,
78 final MLDSAPublicKeyParameters pPublicKey) {
79 super(pKeySpec, pPublicKey);
80 }
81
82 @Override
83 protected boolean matchKey(final AsymmetricKeyParameter pThat) {
84
85 final MLDSAPublicKeyParameters myThis = getPublicKey();
86 final MLDSAPublicKeyParameters myThat = (MLDSAPublicKeyParameters) pThat;
87
88
89 return compareKeys(myThis, myThat);
90 }
91
92
93
94
95
96
97
98
99 private static boolean compareKeys(final MLDSAPublicKeyParameters pFirst,
100 final MLDSAPublicKeyParameters pSecond) {
101 return Arrays.equals(pFirst.getEncoded(), pSecond.getEncoded());
102 }
103 }
104
105
106
107
108 public static class BouncyMLDSAPrivateKey
109 extends BouncyPrivateKey<MLDSAPrivateKeyParameters> {
110
111
112
113
114
115
116 BouncyMLDSAPrivateKey(final GordianKeyPairSpec pKeySpec,
117 final MLDSAPrivateKeyParameters pPrivateKey) {
118 super(pKeySpec, pPrivateKey);
119 }
120
121
122 @Override
123 protected boolean matchKey(final AsymmetricKeyParameter pThat) {
124
125 final MLDSAPrivateKeyParameters myThis = getPrivateKey();
126 final MLDSAPrivateKeyParameters myThat = (MLDSAPrivateKeyParameters) pThat;
127
128
129 return compareKeys(myThis, myThat);
130 }
131
132
133
134
135
136
137
138
139 private static boolean compareKeys(final MLDSAPrivateKeyParameters pFirst,
140 final MLDSAPrivateKeyParameters pSecond) {
141 return Arrays.equals(pFirst.getEncoded(), pSecond.getEncoded());
142 }
143 }
144
145
146
147
148 public static class BouncyMLDSAKeyPairGenerator
149 extends BouncyKeyPairGenerator {
150
151
152
153 private final MLDSAKeyPairGenerator theGenerator;
154
155
156
157
158
159
160
161 BouncyMLDSAKeyPairGenerator(final GordianBaseFactory pFactory,
162 final GordianKeyPairSpec pKeySpec) {
163
164 super(pFactory, pKeySpec);
165
166
167 final MLDSAParameters myParms = pKeySpec.getMLDSAKeySpec().getParameters();
168
169
170 theGenerator = new MLDSAKeyPairGenerator();
171 final MLDSAKeyGenerationParameters myParams = new MLDSAKeyGenerationParameters(getRandom(), myParms);
172 theGenerator.init(myParams);
173 }
174
175 @Override
176 public BouncyKeyPair generateKeyPair() {
177
178 final AsymmetricCipherKeyPair myPair = theGenerator.generateKeyPair();
179 final BouncyMLDSAPublicKey myPublic = new BouncyMLDSAPublicKey(getKeySpec(), (MLDSAPublicKeyParameters) myPair.getPublic());
180 final BouncyMLDSAPrivateKey myPrivate = new BouncyMLDSAPrivateKey(getKeySpec(), (MLDSAPrivateKeyParameters) myPair.getPrivate());
181 return new BouncyKeyPair(myPublic, myPrivate);
182 }
183
184 @Override
185 public PKCS8EncodedKeySpec getPKCS8Encoding(final GordianKeyPair pKeyPair) throws GordianException {
186
187 try {
188
189 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
190
191
192 final BouncyMLDSAPrivateKey myPrivateKey = (BouncyMLDSAPrivateKey) getPrivateKey(pKeyPair);
193 final MLDSAPrivateKeyParameters myParms = myPrivateKey.getPrivateKey();
194 final PrivateKeyInfo myInfo = PrivateKeyInfoFactory.createPrivateKeyInfo(myParms, null);
195 return new PKCS8EncodedKeySpec(myInfo.getEncoded());
196
197 } catch (IOException e) {
198 throw new GordianCryptoException(ERROR_PARSE, e);
199 }
200 }
201
202 @Override
203 public BouncyKeyPair deriveKeyPair(final X509EncodedKeySpec pPublicKey,
204 final PKCS8EncodedKeySpec pPrivateKey) throws GordianException {
205
206 try {
207
208 checkKeySpec(pPrivateKey);
209
210
211 final BouncyMLDSAPublicKey myPublic = derivePublicKey(pPublicKey);
212 final PrivateKeyInfo myInfo = PrivateKeyInfo.getInstance(pPrivateKey.getEncoded());
213 final MLDSAPrivateKeyParameters myParms = (MLDSAPrivateKeyParameters) PrivateKeyFactory.createKey(myInfo);
214 final BouncyMLDSAPrivateKey myPrivate = new BouncyMLDSAPrivateKey(getKeySpec(), myParms);
215 final BouncyKeyPair myPair = new BouncyKeyPair(myPublic, myPrivate);
216
217
218 GordianKeyPairValidity.checkValidity(getFactory(), myPair);
219
220
221 return myPair;
222
223 } catch (IOException e) {
224 throw new GordianCryptoException(ERROR_PARSE, e);
225 }
226 }
227
228 @Override
229 public X509EncodedKeySpec getX509Encoding(final GordianKeyPair pKeyPair) throws GordianException {
230
231 try {
232
233 BouncyKeyPair.checkKeyPair(pKeyPair, getKeySpec());
234
235
236 final BouncyMLDSAPublicKey myPublicKey = (BouncyMLDSAPublicKey) getPublicKey(pKeyPair);
237 final MLDSAPublicKeyParameters myParms = myPublicKey.getPublicKey();
238 final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(myParms);
239 return new X509EncodedKeySpec(myInfo.getEncoded());
240
241 } catch (IOException e) {
242 throw new GordianCryptoException(ERROR_PARSE, e);
243 }
244 }
245
246 @Override
247 public BouncyKeyPair derivePublicOnlyKeyPair(final X509EncodedKeySpec pEncodedKey) throws GordianException {
248 final BouncyMLDSAPublicKey myPublic = derivePublicKey(pEncodedKey);
249 return new BouncyKeyPair(myPublic);
250 }
251
252
253
254
255
256
257
258
259 private BouncyMLDSAPublicKey derivePublicKey(final X509EncodedKeySpec pEncodedKey) throws GordianException {
260
261 try {
262
263 checkKeySpec(pEncodedKey);
264
265
266 final SubjectPublicKeyInfo myInfo = SubjectPublicKeyInfo.getInstance(pEncodedKey.getEncoded());
267 final MLDSAPublicKeyParameters myParms = (MLDSAPublicKeyParameters) PublicKeyFactory.createKey(myInfo);
268 return new BouncyMLDSAPublicKey(getKeySpec(), myParms);
269
270 } catch (IOException e) {
271 throw new GordianCryptoException(ERROR_PARSE, e);
272 }
273 }
274 }
275
276
277
278
279 public static class BouncyMLDSASignature
280 extends GordianCoreSignature {
281
282
283
284 private Signer theSigner;
285
286
287
288
289
290
291
292 BouncyMLDSASignature(final GordianBaseFactory pFactory,
293 final GordianSignatureSpec pSpec) {
294
295 super(pFactory, pSpec);
296 }
297
298
299
300
301
302
303
304 private static Signer createSigner(final GordianKeyPair pKeyPair) {
305
306 final boolean isHash = pKeyPair.getKeyPairSpec().getMLDSAKeySpec().isHash();
307
308
309 return isHash
310 ? new HashMLDSASigner()
311 : new MLDSASigner();
312 }
313
314 @Override
315 public void initForSigning(final GordianSignParams pParams) throws GordianException {
316
317 super.initForSigning(pParams);
318 final BouncyKeyPair myPair = getKeyPair();
319 final byte[] myContext = getContext();
320 BouncyKeyPair.checkKeyPair(myPair);
321
322
323 theSigner = createSigner(myPair);
324 final BouncyMLDSAPrivateKey myPrivate = (BouncyMLDSAPrivateKey) myPair.getPrivateKey();
325 CipherParameters myParms = new ParametersWithRandom(myPrivate.getPrivateKey(), getRandom());
326 if (myContext != null) {
327 myParms = new ParametersWithContext(myParms, myContext);
328 }
329 theSigner.init(true, myParms);
330 }
331
332 @Override
333 public void initForVerify(final GordianSignParams pParams) throws GordianException {
334
335 super.initForVerify(pParams);
336 final BouncyKeyPair myPair = getKeyPair();
337 final byte[] myContext = getContext();
338 BouncyKeyPair.checkKeyPair(myPair);
339
340
341 theSigner = createSigner(myPair);
342 final BouncyMLDSAPublicKey myPublic = (BouncyMLDSAPublicKey) myPair.getPublicKey();
343 CipherParameters myParms = myPublic.getPublicKey();
344 if (myContext != null) {
345 myParms = new ParametersWithContext(myParms, myContext);
346 }
347 theSigner.init(false, myParms);
348 }
349
350 @Override
351 public void update(final byte[] pBytes,
352 final int pOffset,
353 final int pLength) {
354 theSigner.update(pBytes, pOffset, pLength);
355 }
356
357 @Override
358 public void update(final byte pByte) {
359 theSigner.update(pByte);
360 }
361
362 @Override
363 public void update(final byte[] pBytes) {
364 theSigner.update(pBytes, 0, pBytes.length);
365 }
366
367 @Override
368 public void reset() {
369 theSigner.reset();
370 }
371
372 @Override
373 protected BouncyKeyPair getKeyPair() {
374 return (BouncyKeyPair) super.getKeyPair();
375 }
376
377 @Override
378 public GordianBaseFactory getFactory() {
379 return (GordianBaseFactory) super.getFactory();
380 }
381
382 @Override
383 public byte[] sign() throws GordianException {
384
385 checkMode(GordianSignatureMode.SIGN);
386
387
388 try {
389 return theSigner.generateSignature();
390 } catch (CryptoException e) {
391 throw new GordianCryptoException("Failed to sign message", e);
392 }
393 }
394
395 @Override
396 public boolean verify(final byte[] pSignature) throws GordianException {
397
398 checkMode(GordianSignatureMode.VERIFY);
399
400
401 return theSigner.verifySignature(pSignature);
402 }
403 }
404 }