Skip to content

Commit c855976

Browse files
committed
added parameter setting to allow for pre-hash calculation - relates to github #2162
1 parent 40ed011 commit c855976

File tree

3 files changed

+263
-5
lines changed

3 files changed

+263
-5
lines changed

prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/compositesignatures/SignatureSpi.java

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@
3434
import org.bouncycastle.jcajce.CompositePrivateKey;
3535
import org.bouncycastle.jcajce.CompositePublicKey;
3636
import org.bouncycastle.jcajce.interfaces.BCKey;
37+
import org.bouncycastle.jcajce.spec.CompositeSignatureSpec;
3738
import org.bouncycastle.jcajce.spec.ContextParameterSpec;
3839
import org.bouncycastle.jcajce.util.BCJcaJceHelper;
3940
import org.bouncycastle.jcajce.util.JcaJceHelper;
41+
import org.bouncycastle.util.Arrays;
4042
import org.bouncycastle.util.Exceptions;
4143
import org.bouncycastle.util.encoders.Hex;
4244

@@ -102,9 +104,10 @@ public class SignatureSpi
102104
private final String[] algs;
103105
private final Signature[] componentSignatures;
104106
private final byte[] domain;
105-
private final Digest preHashDigest;
107+
private final Digest baseDigest;
106108
private final JcaJceHelper helper = new BCJcaJceHelper();
107109

110+
private Digest preHashDigest;
108111
private ContextParameterSpec contextSpec;
109112
private AlgorithmParameters engineParams = null;
110113

@@ -113,6 +116,7 @@ public class SignatureSpi
113116
SignatureSpi(ASN1ObjectIdentifier algorithm, Digest preHashDigest)
114117
{
115118
this.algorithm = algorithm;
119+
this.baseDigest = preHashDigest;
116120
this.preHashDigest = preHashDigest;
117121
this.domain = domainSeparators.get(algorithm);
118122

@@ -329,9 +333,16 @@ protected byte[] engineSign()
329333
private void processPreHashedMessage(byte[] r)
330334
throws SignatureException
331335
{
332-
byte[] dig = new byte[preHashDigest.getDigestSize()];
336+
byte[] dig = new byte[baseDigest.getDigestSize()];
333337

334-
preHashDigest.doFinal(dig, 0);
338+
try
339+
{
340+
preHashDigest.doFinal(dig, 0);
341+
}
342+
catch (IllegalStateException e)
343+
{
344+
throw new SignatureException(e.getMessage());
345+
}
335346

336347
for (int i = 0; i < this.componentSignatures.length; i++)
337348
{
@@ -443,6 +454,20 @@ protected void engineSetParameter(AlgorithmParameterSpec algorithmParameterSpec)
443454
throw new InvalidAlgorithmParameterException("keys invalid on reset: " + e.getMessage(), e);
444455
}
445456
}
457+
else if (algorithmParameterSpec instanceof CompositeSignatureSpec)
458+
{
459+
CompositeSignatureSpec compositeSignatureSpec = (CompositeSignatureSpec)algorithmParameterSpec;
460+
461+
if (compositeSignatureSpec.isPrehashMode())
462+
{
463+
this.preHashDigest = new NullDigest(baseDigest.getDigestSize());
464+
}
465+
else
466+
{
467+
this.preHashDigest = this.baseDigest;
468+
}
469+
this.contextSpec = (ContextParameterSpec)compositeSignatureSpec.getSecondarySpec();
470+
}
446471
else
447472
{
448473
throw new InvalidAlgorithmParameterException("unknown parameterSpec passed to composite signature");
@@ -494,6 +519,74 @@ protected final AlgorithmParameters engineGetParameters()
494519
return engineParams;
495520
}
496521

522+
private static class NullDigest
523+
implements Digest
524+
{
525+
private final int expectedSize;
526+
private final OpenByteArrayOutputStream bOut = new OpenByteArrayOutputStream();
527+
528+
NullDigest(int expectedSize)
529+
{
530+
this.expectedSize = expectedSize;
531+
}
532+
533+
public String getAlgorithmName()
534+
{
535+
return "NULL";
536+
}
537+
538+
public int getDigestSize()
539+
{
540+
return bOut.size();
541+
}
542+
543+
public void update(byte in)
544+
{
545+
bOut.write(in);
546+
}
547+
548+
public void update(byte[] in, int inOff, int len)
549+
{
550+
bOut.write(in, inOff, len);
551+
}
552+
553+
public int doFinal(byte[] out, int outOff)
554+
{
555+
int size = bOut.size();
556+
if (size != expectedSize)
557+
{
558+
throw new IllegalStateException("provided pre-hash digest is the wrong length");
559+
}
560+
561+
bOut.copy(out, outOff);
562+
563+
reset();
564+
565+
return size;
566+
}
567+
568+
public void reset()
569+
{
570+
bOut.reset();
571+
}
572+
573+
private class OpenByteArrayOutputStream
574+
extends ByteArrayOutputStream
575+
{
576+
public void reset()
577+
{
578+
super.reset();
579+
580+
Arrays.clear(buf);
581+
}
582+
583+
void copy(byte[] out, int outOff)
584+
{
585+
System.arraycopy(buf, 0, out, outOff, this.size());
586+
}
587+
}
588+
}
589+
497590
public static final class HashMLDSA44_ECDSA_P256_SHA256
498591
extends SignatureSpi
499592
{
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.bouncycastle.jcajce.spec;
2+
3+
import java.security.spec.AlgorithmParameterSpec;
4+
5+
/**
6+
* Parameters for the CompositeSignature algorithm.
7+
*/
8+
public class CompositeSignatureSpec
9+
implements AlgorithmParameterSpec
10+
{
11+
private final boolean isPrehashMode;
12+
private final AlgorithmParameterSpec secondaryParameterSpec;
13+
14+
/**
15+
* Base Constructor.
16+
*
17+
* @param isPrehashMode if true, msg passed in will be the precalculated pre-hash.
18+
*/
19+
public CompositeSignatureSpec(boolean isPrehashMode)
20+
{
21+
this(isPrehashMode, null);
22+
}
23+
24+
/**
25+
* Constructor which allows for another parameter spec (usually ContextParameterSpec).
26+
*
27+
* @param isPrehashMode if true, msg passed in will be the precalculated pre-hash.
28+
* @param secondaryParameterSpec the other spec, in addition to pre-hash mode, which needs to be applied.
29+
*/
30+
public CompositeSignatureSpec(boolean isPrehashMode, AlgorithmParameterSpec secondaryParameterSpec)
31+
{
32+
this.isPrehashMode = isPrehashMode;
33+
this.secondaryParameterSpec = secondaryParameterSpec;
34+
}
35+
36+
public boolean isPrehashMode()
37+
{
38+
return isPrehashMode;
39+
}
40+
41+
public AlgorithmParameterSpec getSecondarySpec()
42+
{
43+
return secondaryParameterSpec;
44+
}
45+
}

prov/src/test/java/org/bouncycastle/jcajce/provider/test/CompositeSignaturesTest.java

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
import java.security.KeyFactory;
88
import java.security.KeyPair;
99
import java.security.KeyPairGenerator;
10+
import java.security.MessageDigest;
1011
import java.security.PrivateKey;
1112
import java.security.PublicKey;
1213
import java.security.Security;
1314
import java.security.Signature;
15+
import java.security.SignatureException;
1416
import java.security.cert.CertificateFactory;
1517
import java.security.cert.X509Certificate;
1618
import java.security.interfaces.RSAPrivateKey;
@@ -37,6 +39,7 @@
3739
import org.bouncycastle.jcajce.interfaces.MLDSAPrivateKey;
3840
import org.bouncycastle.jcajce.provider.asymmetric.compositesignatures.CompositeIndex;
3941
import org.bouncycastle.jcajce.provider.asymmetric.mldsa.BCMLDSAPublicKey;
42+
import org.bouncycastle.jcajce.spec.CompositeSignatureSpec;
4043
import org.bouncycastle.jcajce.spec.ContextParameterSpec;
4144
import org.bouncycastle.jcajce.spec.MLDSAParameterSpec;
4245
import org.bouncycastle.jcajce.spec.MLDSAPrivateKeySpec;
@@ -45,6 +48,7 @@
4548
import org.bouncycastle.test.TestResourceFinder;
4649
import org.bouncycastle.util.Arrays;
4750
import org.bouncycastle.util.Strings;
51+
import org.bouncycastle.util.encoders.Hex;
4852

4953
public class CompositeSignaturesTest
5054
extends TestCase
@@ -410,20 +414,136 @@ public void testMixedCompositionWithNull()
410414

411415
}
412416

417+
public void testPrehash()
418+
throws Exception
419+
{
420+
doTestPrehash("MLDSA44-ECDSA-P256-SHA256", "SHA256");
421+
doTestPrehash("MLDSA65-ECDSA-P256-SHA512", "SHA512");
422+
}
423+
424+
public void testPrehashWithContext()
425+
throws Exception
426+
{
427+
doTestPrehash("MLDSA44-ECDSA-P256-SHA256", "SHA256", new ContextParameterSpec(Hex.decode("deadbeef")));
428+
doTestPrehash("MLDSA65-ECDSA-P256-SHA512", "SHA512", new ContextParameterSpec(Hex.decode("deadbeef")));
429+
}
430+
431+
private void doTestPrehash(String sigName, String digestName)
432+
throws Exception
433+
{
434+
byte[] msg = Strings.toUTF8ByteArray(messageToBeSigned);
435+
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(sigName, "BC");
436+
KeyPair keyPair = keyPairGenerator.generateKeyPair();
437+
438+
// full msg sign, verify hash
439+
Signature signature = Signature.getInstance(sigName, "BC");
440+
signature.initSign(keyPair.getPrivate());
441+
signature.update(msg);
442+
443+
byte[] signatureValue = signature.sign();
444+
445+
signature.initVerify(keyPair.getPublic());
446+
signature.setParameter(new CompositeSignatureSpec(true));
447+
signature.update(MessageDigest.getInstance(digestName, "BC").digest(msg));
448+
assertTrue(signature.verify(signatureValue));
449+
450+
// full msg sign, verify hash
451+
signature = Signature.getInstance(sigName, "BC");
452+
signature.initSign(keyPair.getPrivate());
453+
signature.setParameter(new CompositeSignatureSpec(true));
454+
signature.update(MessageDigest.getInstance(digestName, "BC").digest(msg));
455+
456+
signatureValue = signature.sign();
457+
458+
signature.initVerify(keyPair.getPublic());
459+
signature.setParameter(new CompositeSignatureSpec(false));
460+
signature.update(msg);
461+
assertTrue(signature.verify(signatureValue));
462+
463+
// exceptions
464+
signature.initSign(keyPair.getPrivate());
465+
try
466+
{
467+
signature.setParameter(new CompositeSignatureSpec(true));
468+
signature.update(Hex.decode("beef"));
469+
signature.sign();
470+
fail("sign");
471+
}
472+
catch (SignatureException e)
473+
{
474+
assertEquals("provided pre-hash digest is the wrong length", e.getMessage());
475+
}
476+
477+
// exceptions
478+
signature.initVerify(keyPair.getPublic());
479+
try
480+
{
481+
signature.setParameter(new CompositeSignatureSpec(true));
482+
signature.update(Hex.decode("beef"));
483+
signature.verify(signatureValue);
484+
fail("verify");
485+
}
486+
catch (SignatureException e)
487+
{
488+
assertEquals("provided pre-hash digest is the wrong length", e.getMessage());
489+
}
490+
}
491+
492+
private void doTestPrehash(String sigName, String digestName, ContextParameterSpec contextSpec)
493+
throws Exception
494+
{
495+
byte[] msg = Strings.toUTF8ByteArray(messageToBeSigned);
496+
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(sigName, "BC");
497+
KeyPair keyPair = keyPairGenerator.generateKeyPair();
498+
499+
// full msg sign, verify hash
500+
Signature signature = Signature.getInstance(sigName, "BC");
501+
signature.initSign(keyPair.getPrivate());
502+
signature.setParameter(contextSpec);
503+
signature.update(msg);
504+
505+
byte[] signatureValue = signature.sign();
506+
507+
signature.initVerify(keyPair.getPublic());
508+
signature.setParameter(new CompositeSignatureSpec(true, contextSpec));
509+
signature.update(MessageDigest.getInstance(digestName, "BC").digest(msg));
510+
assertTrue(signature.verify(signatureValue));
511+
512+
// full msg sign, verify hash
513+
signature = Signature.getInstance(sigName, "BC");
514+
signature.initSign(keyPair.getPrivate());
515+
signature.setParameter(new CompositeSignatureSpec(true, contextSpec));
516+
signature.update(MessageDigest.getInstance(digestName, "BC").digest(msg));
517+
518+
signatureValue = signature.sign();
519+
520+
signature.initVerify(keyPair.getPublic());
521+
signature.setParameter(new CompositeSignatureSpec(false, contextSpec));
522+
signature.update(msg);
523+
assertTrue(signature.verify(signatureValue));
524+
525+
signature.initVerify(keyPair.getPublic());
526+
signature.setParameter(new CompositeSignatureSpec(false));
527+
signature.update(msg);
528+
assertFalse(signature.verify(signatureValue));
529+
}
530+
413531
public void testSigningAndVerificationInternal()
414532
throws Exception
415533
{
534+
byte[] msg = Strings.toUTF8ByteArray(messageToBeSigned);
535+
416536
for (String oid : compositeSignaturesOIDs)
417537
{
418538
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(oid, "BC");
419539
KeyPair keyPair = keyPairGenerator.generateKeyPair();
420540
Signature signature = Signature.getInstance(oid, "BC");
421541
signature.initSign(keyPair.getPrivate());
422-
signature.update(Strings.toUTF8ByteArray(messageToBeSigned));
542+
signature.update(msg);
423543
byte[] signatureValue = signature.sign();
424544

425545
signature.initVerify(keyPair.getPublic());
426-
signature.update(Strings.toUTF8ByteArray(messageToBeSigned));
546+
signature.update(msg);
427547
TestCase.assertTrue(signature.verify(signatureValue));
428548
}
429549
}

0 commit comments

Comments
 (0)