Skip to content

Commit e776ac2

Browse files
committed
added further lookups for LMS.
added independent setting of DigestAlgorithm for LMS (via ExtendedContentSigner).
1 parent 200b399 commit e776ac2

File tree

9 files changed

+206
-6
lines changed

9 files changed

+206
-6
lines changed

pkix/src/main/java/org/bouncycastle/cms/SignerInfoGeneratorBuilder.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
1111
import org.bouncycastle.operator.DigestCalculator;
1212
import org.bouncycastle.operator.DigestCalculatorProvider;
13+
import org.bouncycastle.operator.ExtendedContentSigner;
1314
import org.bouncycastle.operator.OperatorCreationException;
1415

1516
/**
@@ -146,7 +147,22 @@ private SignerInfoGenerator createGenerator(ContentSigner contentSigner, SignerI
146147
}
147148
else
148149
{
149-
digester = digestProvider.get(digAlgFinder.find(contentSigner.getAlgorithmIdentifier()));
150+
if (contentSigner instanceof ExtendedContentSigner)
151+
{
152+
digester = digestProvider.get(((ExtendedContentSigner)contentSigner).getDigestAlgorithmIdentifier());
153+
}
154+
else
155+
{
156+
AlgorithmIdentifier digAlg = digAlgFinder.find(contentSigner.getAlgorithmIdentifier());
157+
if (digAlg != null)
158+
{
159+
digester = digestProvider.get(digAlg);
160+
}
161+
else
162+
{
163+
throw new OperatorCreationException("no digest algorithm specified for signature algorithm");
164+
}
165+
}
150166
}
151167

152168
if (directSignature)

pkix/src/main/java/org/bouncycastle/operator/DefaultAlgorithmNameFinder.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ private static void addAlgorithm(ASN1ObjectIdentifier algOid, String algorithmNa
5656
addAlgorithm(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512WITHCVC-ECDSA");
5757
addAlgorithm(BCObjectIdentifiers.falcon_512, "FALCON");
5858
addAlgorithm(BCObjectIdentifiers.falcon_1024, "FALCON");
59-
59+
60+
addAlgorithm(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
61+
6062
addAlgorithm(NISTObjectIdentifiers.id_ml_dsa_44, "ML-DSA-44");
6163
addAlgorithm(NISTObjectIdentifiers.id_ml_dsa_65, "ML-DSA-65");
6264
addAlgorithm(NISTObjectIdentifiers.id_ml_dsa_87, "ML-DSA-87");

pkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,6 @@ public class DefaultDigestAlgorithmIdentifierFinder
192192

193193
digestOids.put(EdECObjectIdentifiers.id_Ed25519, NISTObjectIdentifiers.id_sha512);
194194

195-
digestOids.put(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, NISTObjectIdentifiers.id_sha256);
196-
197195
digestNameToOids.put("SHA-1", OIWObjectIdentifiers.idSHA1);
198196
digestNameToOids.put("SHA-224", NISTObjectIdentifiers.id_sha224);
199197
digestNameToOids.put("SHA-256", NISTObjectIdentifiers.id_sha256);
@@ -299,6 +297,8 @@ private static void addDigestAlgId(ASN1ObjectIdentifier oid, boolean withNullPar
299297
digestOidToAlgIds.put(oid, algId);
300298
}
301299

300+
public static DigestAlgorithmIdentifierFinder INSTANCE = new DefaultDigestAlgorithmIdentifierFinder();
301+
302302
public AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId)
303303
{
304304
ASN1ObjectIdentifier sigAlgOid = sigAlgId.getAlgorithm();
@@ -324,6 +324,12 @@ public AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId)
324324
digAlgOid = (ASN1ObjectIdentifier)digestOids.get(sigAlgOid);
325325
}
326326

327+
if (digAlgOid == null)
328+
{
329+
return null;
330+
}
331+
332+
// keep looking!
327333
return find(digAlgOid);
328334
}
329335

pkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ private static void addDigestOid(ASN1ObjectIdentifier signatureOid, ASN1ObjectId
432432
noParams.add(BCObjectIdentifiers.dilithium3_aes);
433433
noParams.add(BCObjectIdentifiers.dilithium5_aes);
434434

435+
noParams.add(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig);
435436
noParams.add(NISTObjectIdentifiers.id_ml_dsa_44);
436437
noParams.add(NISTObjectIdentifiers.id_ml_dsa_65);
437438
noParams.add(NISTObjectIdentifiers.id_ml_dsa_87);

pkix/src/main/java/org/bouncycastle/operator/DefaultSignatureNameFinder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ private static void addSignatureName(ASN1ObjectIdentifier sigOid, String sigName
9494
addSignatureName(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
9595
addSignatureName(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
9696

97+
addSignatureName(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, "LMS");
98+
9799
addSignatureName(NISTObjectIdentifiers.id_ml_dsa_44, "ML-DSA-44");
98100
addSignatureName(NISTObjectIdentifiers.id_ml_dsa_65, "ML-DSA-65");
99101
addSignatureName(NISTObjectIdentifiers.id_ml_dsa_87, "ML-DSA-87");
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.bouncycastle.operator;
2+
3+
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
4+
5+
/**
6+
* A Content Signer which also provides details of the digest algorithm used internally.
7+
*/
8+
public interface ExtendedContentSigner
9+
extends ContentSigner
10+
{
11+
/**
12+
* Return the algorithm identifier describing the signature
13+
* algorithm and parameters this signer generates.
14+
*
15+
* @return algorithm oid and parameters.
16+
*/
17+
AlgorithmIdentifier getDigestAlgorithmIdentifier();
18+
}

pkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
3838
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
3939
import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
40+
import org.bouncycastle.operator.ExtendedContentSigner;
4041
import org.bouncycastle.operator.OperatorCreationException;
4142
import org.bouncycastle.operator.RuntimeOperatorException;
4243
import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
@@ -60,6 +61,7 @@ public class JcaContentSignerBuilder
6061
}
6162

6263
private final String signatureAlgorithm;
64+
private final AlgorithmIdentifier signatureDigestAlgorithm;
6365

6466
private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
6567
private SecureRandom random;
@@ -68,14 +70,26 @@ public class JcaContentSignerBuilder
6870
private AlgorithmParameterSpec sigAlgSpec;
6971

7072
public JcaContentSignerBuilder(String signatureAlgorithm)
73+
{
74+
this(signatureAlgorithm, (AlgorithmIdentifier)null);
75+
}
76+
77+
public JcaContentSignerBuilder(String signatureAlgorithm, AlgorithmIdentifier signatureDigestAlgorithmID)
7178
{
7279
this.signatureAlgorithm = signatureAlgorithm;
80+
this.signatureDigestAlgorithm = signatureDigestAlgorithmID;
7381
}
7482

7583
public JcaContentSignerBuilder(String signatureAlgorithm, AlgorithmParameterSpec sigParamSpec)
7684
{
77-
this.signatureAlgorithm = signatureAlgorithm;
85+
this(signatureAlgorithm, sigParamSpec, null);
86+
}
7887

88+
public JcaContentSignerBuilder(String signatureAlgorithm, AlgorithmParameterSpec sigParamSpec, AlgorithmIdentifier signatureDigestAlgorithmID)
89+
{
90+
this.signatureAlgorithm = signatureAlgorithm;
91+
this.signatureDigestAlgorithm = signatureDigestAlgorithmID;
92+
7993
if (sigParamSpec instanceof PSSParameterSpec)
8094
{
8195
PSSParameterSpec pssSpec = (PSSParameterSpec)sigParamSpec;
@@ -160,7 +174,7 @@ public ContentSigner build(PrivateKey privateKey)
160174
sig.initSign(privateKey);
161175
}
162176

163-
return new ContentSigner()
177+
final ContentSigner contentSigner = new ContentSigner()
164178
{
165179
private OutputStream stream = OutputStreamFactory.createStream(sig);
166180

@@ -186,6 +200,39 @@ public byte[] getSignature()
186200
}
187201
}
188202
};
203+
204+
if (signatureDigestAlgorithm != null)
205+
{
206+
return new ExtendedContentSigner()
207+
{
208+
private final AlgorithmIdentifier digestAlgorithm = signatureDigestAlgorithm;
209+
private final ContentSigner signer = contentSigner;
210+
211+
public AlgorithmIdentifier getDigestAlgorithmIdentifier()
212+
{
213+
return digestAlgorithm;
214+
}
215+
216+
public AlgorithmIdentifier getAlgorithmIdentifier()
217+
{
218+
return signer.getAlgorithmIdentifier();
219+
}
220+
221+
public OutputStream getOutputStream()
222+
{
223+
return signer.getOutputStream();
224+
}
225+
226+
public byte[] getSignature()
227+
{
228+
return signer.getSignature();
229+
}
230+
};
231+
}
232+
else
233+
{
234+
return contentSigner;
235+
}
189236
}
190237
catch (GeneralSecurityException e)
191238
{

pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
import org.bouncycastle.asn1.cms.AttributeTable;
2525
import org.bouncycastle.asn1.cms.CMSAttributes;
2626
import org.bouncycastle.asn1.cms.ContentInfo;
27+
import org.bouncycastle.asn1.cms.SignerInfo;
28+
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
29+
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
30+
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
2731
import org.bouncycastle.cert.X509CertificateHolder;
2832
import org.bouncycastle.cert.jcajce.JcaCertStore;
2933
import org.bouncycastle.cms.CMSException;
@@ -55,6 +59,9 @@ public class PQCSignedDataTest
5559
private static String _origDN;
5660
private static KeyPair _origKP;
5761
private static X509Certificate _origCert;
62+
63+
private static KeyPair _origLmsKP;
64+
private static X509Certificate _origLmsCert;
5865
private static KeyPair _origFalconKP;
5966
private static X509Certificate _origFalconCert;
6067
private static KeyPair _origPicnicKP;
@@ -67,6 +74,8 @@ public class PQCSignedDataTest
6774
private static String _signDN;
6875
private static KeyPair _signKP;
6976
private static X509Certificate _signCert;
77+
private static KeyPair _signLmsKP;
78+
private static X509Certificate _signLmsCert;
7079
private static KeyPair _signFalconKP;
7180
private static X509Certificate _signFalconCert;
7281
private static KeyPair _signPicnicKP;
@@ -137,6 +146,12 @@ private static void init()
137146
_signKP = PQCTestUtil.makeKeyPair();
138147
_signCert = PQCTestUtil.makeCertificate(_signKP, _signDN, _origKP, _origDN);
139148

149+
_origLmsKP = PQCTestUtil.makeLmsKeyPair();
150+
_origLmsCert = PQCTestUtil.makeCertificate(_origLmsKP, _origDN, _origLmsKP, _origDN);
151+
152+
_signLmsKP = PQCTestUtil.makeLmsKeyPair();
153+
_signLmsCert = PQCTestUtil.makeCertificate(_signLmsKP, _signDN, _origLmsKP, _origDN);
154+
140155
_origFalconKP = PQCTestUtil.makeFalconKeyPair();
141156
_origFalconCert = PQCTestUtil.makeCertificate(_origFalconKP, _origDN, _origFalconKP, _origDN);
142157

@@ -276,6 +291,81 @@ public void testFalconEncapsulated()
276291
}
277292
}
278293

294+
public void testLmsEncapsulated()
295+
throws Exception
296+
{
297+
List certList = new ArrayList();
298+
CMSTypedData msg = new CMSProcessableByteArray("Hello World!".getBytes());
299+
300+
certList.add(_origLmsCert);
301+
certList.add(_signLmsCert);
302+
303+
Store certs = new JcaCertStore(certList);
304+
305+
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
306+
307+
DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().setProvider(BC).build();
308+
309+
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(digCalcProv).build(new JcaContentSignerBuilder("LMS", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256)).setProvider(BC).build(_origLmsKP.getPrivate()), _origLmsCert));
310+
311+
gen.addCertificates(certs);
312+
313+
CMSSignedData s = gen.generate(msg, true);
314+
315+
ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded());
316+
ASN1InputStream aIn = new ASN1InputStream(bIn);
317+
318+
s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject()));
319+
320+
certs = s.getCertificates();
321+
322+
SignerInformationStore signers = s.getSignerInfos();
323+
324+
Collection c = signers.getSigners();
325+
Iterator it = c.iterator();
326+
327+
while (it.hasNext())
328+
{
329+
SignerInformation signer = (SignerInformation)it.next();
330+
Collection certCollection = certs.getMatches(signer.getSID());
331+
332+
Iterator certIt = certCollection.iterator();
333+
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
334+
335+
assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert)));
336+
337+
//
338+
// check content digest
339+
//
340+
341+
byte[] contentDigest = (byte[])gen.getGeneratedDigests().get(signer.getDigestAlgOID());
342+
343+
AttributeTable table = signer.getSignedAttributes();
344+
Attribute hash = table.get(CMSAttributes.messageDigest);
345+
346+
assertTrue(MessageDigest.isEqual(contentDigest, ((ASN1OctetString)hash.getAttrValues().getObjectAt(0)).getOctets()));
347+
}
348+
}
349+
350+
public void testTryLmsSettings()
351+
throws Exception
352+
{
353+
DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().setProvider(BC).build();
354+
355+
try
356+
{
357+
new JcaSignerInfoGeneratorBuilder(digCalcProv).build(new JcaContentSignerBuilder("LMS").setProvider(BC).build(_origLmsKP.getPrivate()), _origLmsCert).generate(PKCSObjectIdentifiers.data);
358+
}
359+
catch (OperatorCreationException e)
360+
{
361+
assertEquals("no digest algorithm specified for signature algorithm", e.getMessage());
362+
}
363+
364+
SignerInfo sigInfo = new JcaSignerInfoGeneratorBuilder(digCalcProv).build(new JcaContentSignerBuilder("LMS", new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256)).setProvider(BC).build(_origLmsKP.getPrivate()), _origLmsCert).generate(PKCSObjectIdentifiers.data);
365+
366+
assertEquals(sigInfo.getDigestAlgorithm(), new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
367+
}
368+
279369
public void testPicnicEncapsulated()
280370
throws Exception
281371
{

pkix/src/test/java/org/bouncycastle/cms/test/PQCTestUtil.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@
2222
import org.bouncycastle.jce.X509KeyUsage;
2323
import org.bouncycastle.operator.ContentSigner;
2424
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
25+
import org.bouncycastle.pqc.crypto.lms.LMOtsParameters;
26+
import org.bouncycastle.pqc.crypto.lms.LMSigParameters;
2527
import org.bouncycastle.pqc.jcajce.interfaces.FalconKey;
28+
import org.bouncycastle.pqc.jcajce.interfaces.LMSKey;
2629
import org.bouncycastle.pqc.jcajce.interfaces.PicnicKey;
2730
import org.bouncycastle.pqc.jcajce.spec.FalconParameterSpec;
31+
import org.bouncycastle.pqc.jcajce.spec.LMSKeyGenParameterSpec;
2832
import org.bouncycastle.pqc.jcajce.spec.PicnicParameterSpec;
2933
import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec;
3034
import org.bouncycastle.pqc.jcajce.spec.SPHINCSPlusParameterSpec;
@@ -51,6 +55,16 @@ public static KeyPair makeSphincsPlusKeyPair()
5155
return kpGen.generateKeyPair();
5256
}
5357

58+
public static KeyPair makeLmsKeyPair()
59+
throws Exception
60+
{
61+
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("LMS", "BCPQC");
62+
63+
kpGen.initialize(new LMSKeyGenParameterSpec(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w1), new SecureRandom());
64+
65+
return kpGen.generateKeyPair();
66+
}
67+
5468
public static KeyPair makeFalconKeyPair()
5569
throws Exception
5670
{
@@ -116,6 +130,10 @@ else if (issPriv instanceof SLHDSAKey)
116130
{
117131
sigGen = new JcaContentSignerBuilder("SLH-DSA").setProvider("BC").build(issPriv);
118132
}
133+
else if (issPriv instanceof LMSKey)
134+
{
135+
sigGen = new JcaContentSignerBuilder("LMS").setProvider("BC").build(issPriv);
136+
}
119137
else
120138
{
121139
sigGen = new JcaContentSignerBuilder("SHA512withSPHINCS256").setProvider("BCPQC").build(issPriv);

0 commit comments

Comments
 (0)