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