Skip to content

Commit eb83ae5

Browse files
committed
Merge branch 'rfc-8103-1-jcajce' into 'main'
RFC 8103 ChaCha20-Poly1305 See merge request root/bc-java!95
2 parents e1aec63 + e3fa081 commit eb83ae5

File tree

9 files changed

+331
-23
lines changed

9 files changed

+331
-23
lines changed

core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,7 @@ public interface KISAObjectIdentifiers
3131

3232
/** RFC 9708 MTS-HashSig-2013; OID 1.2.840.113549.1.9.16.0.64 */
3333
static final ASN1ObjectIdentifier id_mod_mts_hashsig_2013 = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.64");
34+
35+
/** RFC 8103 id-mod-CMS-AEADChaCha20Poly1305; OID 1.2.840.113549.1.9.16.0.66 */
36+
static final ASN1ObjectIdentifier id_mod_CMS_AEADChaCha20Poly1305 = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.66");
3437
}

mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,43 @@ public void testCapEncrypt()
348348
SMIMETestUtil.verifyMessageBytes(msg, res);
349349
}
350350

351+
public void testChacha20Poly1305Encrypt()
352+
throws Exception
353+
{
354+
MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington");
355+
356+
SMIMEAuthEnvelopedGenerator gen = new SMIMEAuthEnvelopedGenerator();
357+
358+
//
359+
// create a subject key id - this has to be done the same way as
360+
// it is done in the certificate associated with the private key
361+
//
362+
MessageDigest dig = MessageDigest.getInstance("SHA256", BC);
363+
364+
dig.update(_reciCert.getPublicKey().getEncoded());
365+
366+
gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(dig.digest(), _reciCert.getPublicKey()).setProvider(BC));
367+
368+
//
369+
// generate a MimeBodyPart object which encapsulates the content
370+
// we want encrypted.
371+
//
372+
MimeBodyPart mp = gen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.ChaCha20Poly1305).setProvider(BC).build());
373+
374+
SMIMEAuthEnveloped m = new SMIMEAuthEnveloped(mp);
375+
376+
dig.update(_reciCert.getPublicKey().getEncoded());
377+
378+
RecipientId recId = new KeyTransRecipientId(dig.digest());
379+
380+
RecipientInformationStore recipients = m.getRecipientInfos();
381+
RecipientInformation recipient = recipients.get(recId);
382+
383+
MimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)));
384+
385+
SMIMETestUtil.verifyMessageBytes(msg, res);
386+
}
387+
351388
public void testTwoRecipients()
352389
throws Exception
353390
{

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,5 @@ public class CMSAlgorithm
105105

106106
public static final ASN1ObjectIdentifier SHAKE128_LEN = NISTObjectIdentifiers.id_shake128_len.intern();
107107
public static final ASN1ObjectIdentifier SHAKE256_LEN = NISTObjectIdentifiers.id_shake256_len.intern();
108+
public static final ASN1ObjectIdentifier ChaCha20Poly1305 = PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305.intern();
108109
}

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
1313
import org.bouncycastle.asn1.cms.ContentInfo;
1414
import org.bouncycastle.asn1.cms.EncryptedContentInfo;
15+
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
1516
import org.bouncycastle.operator.OutputAEADEncryptor;
1617

1718
public class CMSAuthEnvelopedDataGenerator
@@ -36,10 +37,17 @@ private CMSAuthEnvelopedData doGenerate(
3637
try
3738
{
3839
OutputStream cOut = contentEncryptor.getOutputStream(bOut);
39-
40-
content.write(cOut);
41-
42-
authenticatedAttrSet = CMSUtils.processAuthAttrSet(authAttrsGenerator, contentEncryptor);
40+
if (contentEncryptor.getAlgorithmIdentifier().getAlgorithm().equals(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305))
41+
{
42+
// AEAD Ciphers process AAD at first
43+
authenticatedAttrSet = CMSUtils.processAuthAttrSet(authAttrsGenerator, contentEncryptor);
44+
content.write(cOut);
45+
}
46+
else
47+
{
48+
content.write(cOut);
49+
authenticatedAttrSet = CMSUtils.processAuthAttrSet(authAttrsGenerator, contentEncryptor);
50+
}
4351

4452
cOut.close();
4553
}
@@ -66,7 +74,7 @@ private CMSAuthEnvelopedData doGenerate(
6674
* generate an auth-enveloped object that contains an CMS Enveloped Data
6775
* object using the given provider.
6876
*
69-
* @param content the content to be encrypted
77+
* @param content the content to be encrypted
7078
* @param contentEncryptor the symmetric key based encryptor to encrypt the content with.
7179
*/
7280
public CMSAuthEnvelopedData generate(

pkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public class EnvelopedDataHelper
110110
MAC_ALG_NAMES.put(CMSAlgorithm.AES192_CBC, "AESMac");
111111
MAC_ALG_NAMES.put(CMSAlgorithm.AES256_CBC, "AESMac");
112112
MAC_ALG_NAMES.put(CMSAlgorithm.RC2_CBC, "RC2Mac");
113+
MAC_ALG_NAMES.put(CMSAlgorithm.ChaCha20Poly1305, "ChaCha20Poly1305Mac");
113114

114115
PBKDF2_ALG_NAMES.put(PasswordRecipient.PRF.HMacSHA1.getAlgorithmID(), "PBKDF2WITHHMACSHA1");
115116
PBKDF2_ALG_NAMES.put(PasswordRecipient.PRF.HMacSHA224.getAlgorithmID(), "PBKDF2WITHHMACSHA224");
@@ -123,6 +124,7 @@ public class EnvelopedDataHelper
123124
authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes128_CCM);
124125
authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes192_CCM);
125126
authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes256_CCM);
127+
authEnvelopedAlgorithms.add(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305);
126128
}
127129

128130
private static final short[] rc2Table = {

pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,10 +376,17 @@ public OutputStream getOutputStream(OutputStream dOut)
376376
{
377377
algId = algorithmIdentifier;
378378
}
379-
380-
// TODO: works for CCM too, but others will follow.
381-
GCMParameters p = GCMParameters.getInstance(algId.getParameters());
382-
macOut = new MacCaptureStream(dOut, p.getIcvLen());
379+
380+
if (algorithmIdentifier.getAlgorithm().equals(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305))
381+
{
382+
macOut = new MacCaptureStream(dOut, 16);
383+
}
384+
else
385+
{
386+
// TODO: works for CCM too, but others will follow.
387+
GCMParameters p = GCMParameters.getInstance(algId.getParameters());
388+
macOut = new MacCaptureStream(dOut, p.getIcvLen());
389+
}
383390
return new CipherOutputStream(macOut, cipher);
384391
}
385392

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.bouncycastle.cms.jcajce;
2+
3+
import java.security.Key;
4+
5+
import javax.crypto.Cipher;
6+
7+
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
8+
import org.bouncycastle.cms.CMSException;
9+
import org.bouncycastle.cms.RecipientOperator;
10+
11+
public class JcePasswordAuthEnvelopedRecipient
12+
extends JcePasswordRecipient
13+
{
14+
public JcePasswordAuthEnvelopedRecipient(char[] password)
15+
{
16+
super(password);
17+
}
18+
19+
public RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncryptionAlgorithm,
20+
final AlgorithmIdentifier contentMacAlgorithm,
21+
byte[] derivedKey,
22+
byte[] encryptedContentEncryptionKey)
23+
throws CMSException
24+
{
25+
Key secretKey = extractSecretKey(keyEncryptionAlgorithm, contentMacAlgorithm, derivedKey, encryptedContentEncryptionKey);
26+
27+
final Cipher dataCipher = helper.createContentCipher(secretKey, contentMacAlgorithm);
28+
29+
return new RecipientOperator(new CMSInputAEADDecryptor(contentMacAlgorithm, dataCipher));
30+
}
31+
}

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.bouncycastle.asn1.cms.GCMParameters;
2323
import org.bouncycastle.asn1.cms.Time;
2424
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
25+
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
2526
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
2627
import org.bouncycastle.cms.CMSAttributeTableGenerationException;
2728
import org.bouncycastle.cms.CMSAttributeTableGenerator;
@@ -207,6 +208,53 @@ public AttributeTable getAttributes(Map parameters)
207208
assertEquals("Hello, world!", Strings.fromByteArray(recData));
208209
}
209210

211+
public void testChacha20Poly1305()
212+
throws Exception
213+
{
214+
if (!CMSTestUtil.isAeadAvailable())
215+
{
216+
return;
217+
}
218+
byte[] message = Strings.toByteArray("Hello, world!");
219+
OutputEncryptor candidate = new JceCMSContentEncryptorBuilder(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305).setProvider(BC).build();
220+
221+
assertEquals(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305, candidate.getAlgorithmIdentifier().getAlgorithm());
222+
//assertNotNull(GCMParameters.getInstance(candidate.getAlgorithmIdentifier().getParameters()));
223+
224+
assertTrue(candidate instanceof OutputAEADEncryptor);
225+
226+
OutputAEADEncryptor macProvider = (OutputAEADEncryptor)candidate;
227+
228+
CMSAuthEnvelopedDataGenerator authGen = new CMSAuthEnvelopedDataGenerator();
229+
230+
authGen.setAuthenticatedAttributeGenerator(new CMSAttributeTableGenerator()
231+
{
232+
public AttributeTable getAttributes(Map parameters)
233+
throws CMSAttributeTableGenerationException
234+
{
235+
Hashtable<ASN1ObjectIdentifier, Attribute> attrs = new Hashtable<ASN1ObjectIdentifier, Attribute>();
236+
Attribute testAttr = new Attribute(CMSAttributes.signingTime,
237+
new DERSet(new Time(new Date())));
238+
attrs.put(testAttr.getAttrType(), testAttr);
239+
return new AttributeTable(attrs);
240+
}
241+
});
242+
243+
authGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert));
244+
245+
CMSAuthEnvelopedData authData = authGen.generate(new CMSProcessableByteArray(message), macProvider);
246+
247+
CMSAuthEnvelopedData encAuthData = new CMSAuthEnvelopedData(authData.getEncoded());
248+
249+
RecipientInformationStore recipients = encAuthData.getRecipientInfos();
250+
251+
RecipientInformation recipient = (RecipientInformation)recipients.getRecipients().iterator().next();
252+
253+
byte[] recData = recipient.getContent(new JceKeyTransAuthEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC));
254+
255+
assertEquals("Hello, world!", Strings.fromByteArray(recData));
256+
}
257+
210258
public void testGCMwithHKDF()
211259
throws Exception
212260
{

0 commit comments

Comments
 (0)