Skip to content

Commit 797afd8

Browse files
committed
Added support for ECKA-EG, relates to github #790.
1 parent 6fc14db commit 797afd8

3 files changed

Lines changed: 51 additions & 0 deletions

File tree

docs/releasenotes.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ <h3>2.1.2 Defects Fixed</h3>
4949
<li>JceOpenSSLPKCS8DecryptorProviderBuilder cast the PBES2 key-derivation-function parameters blind to PBKDF2Params, so an EncryptedPrivateKeyInfo whose KDF inside PBES2 was scrypt (RFC 7914, e.g. anything produced by "openssl pkcs8 -topk8 -scrypt") failed to decrypt with "DLSequence cannot be cast to PBKDF2Params". The builder now dispatches on the KDF algorithm OID: id-PBKDF2 takes the existing PBKDF2 path, id-scrypt parses the parameters as ScryptParams and derives the key via SCrypt.generate (the password is encoded as UTF-8 to match OpenSSL's raw-bytes treatment). PBKDF2-based PBES2, PKCS#5 PBES1 and PKCS#12 PBE paths are unchanged (issue #400).</li>
5050
<li>RFC3280CertPathUtilities.processCRLB2 (in both prov and pkix) emitted an opaque AnnotatedException "No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point." when the CRL's issuingDistributionPoint did not match the cert's CRL distribution point names, with no way for an operator to tell which CRL had been returned for which DP. The message now appends both name lists, e.g. ". cert DP names: [6: http://crl3.example/foo.crl, 6: http://crl4.example/foo.crl]; CRL IDP names: [6: http://crl4.example/bar.crl]", letting the cause of the mismatch be diagnosed without re-running with extra logging. Existing assertion sites in NistCertPathTest / NistCertPathTest2 / PKITSTest were updated to use prefix-matching against the original message (issue #800).</li>
5151
<li>JceInputDecryptorProviderBuilder previously assumed the supplied AlgorithmIdentifier parameters were either an ASN1OctetString (raw IV) or GOST28147Parameters, so init() failed with "DLSequence cannot be cast to ASN1ObjectIdentifier" (via the GOST fallback) on an AES-GCM (or AES-CCM) AlgorithmIdentifier carrying GCMParameters / CCMParameters. The builder now dispatches on the algorithm OID: id-aes{128,192,256}-GCM and id-aes{128,192,256}-CCM parse the parameters as GCMParameters / CCMParameters and init the cipher via GCMParameterSpec(icvLen*8, nonce); the existing IV and GOST paths are unchanged (issue #1510).</li>
52+
<li>CMS EnvelopedData with a BSI TR-03111 ECKA-EG-X963KDF key agreement (BSIObjectIdentifiers.ecka_eg_X963kdf_SHA1/SHA224/SHA256/SHA384/SHA512/RIPEMD160) failed both encode and decode: JceKeyAgreeRecipientInfoGenerator threw "Unknown key agreement algorithm" on generate, and JceKeyAgreeRecipient's parallel branch silently fell through to a null UserKeyingMaterialSpec, producing the wrong shared secret and "checksum failed" on the AES key unwrap. The underlying agreement (ECDHBasicAgreement + KDF2BytesGenerator) is structurally identical to dhSinglePass_stdDH_*kdf_scheme, and BSI TR-03109-3 / ICAO 9303-11 — the canonical consumers of ECKA-EG-in-CMS — specify the RFC 5753 ECC-CMS-SharedInfo format for the KDF input. The six BSI ecka_eg_X963kdf_* OIDs have been added to the EC dispatch table in CMSUtils so both encode and decode route through the existing RFC 5753 KeyMaterialGenerator (issue #790).</li>
5253
<li>BLS12_381Aggregation.aggregateVerifyHashed previously fed the per-signer (pk_i, H(msg_i)) pairs straight into the multi-pairing, omitting the per-message public-key aggregation and identity-rejection step required by draft-irtf-cfrg-bls-signature §2.9 lines 12-13. An attacker who controlled pk_a and pk_b = -pk_a (each with a valid PoP, since they hold both secret keys) could publish an aggregate over (pk_a, m), (pk_b, m), (pk_c, m') whose pk_a / pk_b contributions cancelled in the pairing equation, reducing it to a check of pk_c on m' while the aggregate still ostensibly claimed contributions from pk_a and pk_b. BLS12_381BasicScheme.aggregateVerify pre-screened by rejecting duplicate messages and BLS12_381MessageAugmentation folded the public key into the hash input so the spec grouping was trivial, but BLS12_381ProofOfPossession.aggregateVerify took the unmodified helper path. The shared helper now groups signers by hashed message, sums the public keys per group, and rejects the aggregate when any per-group key equals the G1 identity (the spec form for §2.9); all three schemes inherit the check.</li>
5354
</ul>
5455
<h3>2.2.3 Additional Features and Functionality</h3>

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.bouncycastle.asn1.DERSet;
3030
import org.bouncycastle.asn1.DERTaggedObject;
3131
import org.bouncycastle.asn1.DLSet;
32+
import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
3233
import org.bouncycastle.asn1.cms.AttributeTable;
3334
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
3435
import org.bouncycastle.asn1.cms.ContentInfo;
@@ -92,6 +93,18 @@ class CMSUtils
9293
ecAlgs.add(PKCSObjectIdentifiers.dhSinglePass_stdDH_hkdf_sha384_scheme);
9394
ecAlgs.add(PKCSObjectIdentifiers.dhSinglePass_stdDH_hkdf_sha512_scheme);
9495

96+
// BSI TR-03111 ECKA-EG with X9.63 KDF. Structurally identical to
97+
// dhSinglePass_stdDH_*kdf_scheme (ECDH + X9.63 KDF + RFC 5753
98+
// ECC-CMS-SharedInfo per BSI TR-03109-3 / ICAO 9303-11); dispatch
99+
// through the same EC code path so the RFC 5753 KDF material is
100+
// generated consistently for both encode and decode (issue #790).
101+
ecAlgs.add(BSIObjectIdentifiers.ecka_eg_X963kdf_SHA1);
102+
ecAlgs.add(BSIObjectIdentifiers.ecka_eg_X963kdf_SHA224);
103+
ecAlgs.add(BSIObjectIdentifiers.ecka_eg_X963kdf_SHA256);
104+
ecAlgs.add(BSIObjectIdentifiers.ecka_eg_X963kdf_SHA384);
105+
ecAlgs.add(BSIObjectIdentifiers.ecka_eg_X963kdf_SHA512);
106+
ecAlgs.add(BSIObjectIdentifiers.ecka_eg_X963kdf_RIPEMD160);
107+
95108
gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH);
96109
gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001);
97110
gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256);

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2164,6 +2164,43 @@ public void testECKeyAgree()
21642164
assertEquals(X9ObjectIdentifiers.prime239v1, recInfo.getOriginator().getOriginatorKey().getAlgorithm().getParameters());
21652165
}
21662166

2167+
public void testEckaEgX963Kdf()
2168+
throws Exception
2169+
{
2170+
// Round-trip a CMS EnvelopedData under the BSI TR-03111 ECKA-EG-X963KDF-SHA256
2171+
// key agreement, wrapping an AES-128 content-encryption key. Closes
2172+
// github #790 — without the dispatch fix in CMSUtils the encode side throws
2173+
// "Unknown key agreement algorithm" and the decode side (in pre-2.85 BC)
2174+
// fell through to a null UserKeyingMaterialSpec, producing the wrong shared
2175+
// secret and a "checksum failed" InvalidKeyException from the AES key unwrap.
2176+
ASN1ObjectIdentifier[] kdfs = new ASN1ObjectIdentifier[]{
2177+
CMSAlgorithm.ECKA_EG_X963KDF_SHA256,
2178+
CMSAlgorithm.ECKA_EG_X963KDF_SHA384,
2179+
CMSAlgorithm.ECKA_EG_X963KDF_SHA512
2180+
};
2181+
byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
2182+
2183+
for (int i = 0; i != kdfs.length; ++i)
2184+
{
2185+
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
2186+
2187+
edGen.addRecipientInfoGenerator(new JceKeyAgreeRecipientInfoGenerator(kdfs[i],
2188+
_origEcKP.getPrivate(), _origEcKP.getPublic(),
2189+
CMSAlgorithm.AES128_WRAP).addRecipient(_reciEcCert).setProvider(BC));
2190+
2191+
CMSEnvelopedData ed = edGen.generate(
2192+
new CMSProcessableByteArray(data),
2193+
new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build());
2194+
2195+
assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.AES128_CBC);
2196+
2197+
RecipientInformationStore recipients = ed.getRecipientInfos();
2198+
2199+
confirmDataReceived(recipients, data, _reciEcCert, _reciEcKP.getPrivate(), BC);
2200+
confirmNumberRecipients(recipients, 1);
2201+
}
2202+
}
2203+
21672204
public void testFaultyAgreementRecipient()
21682205
throws Exception
21692206
{

0 commit comments

Comments
 (0)