Skip to content

Commit 6e15005

Browse files
committed
CMS: Better exception when no ukm for MQV, with test
1 parent a7d9325 commit 6e15005

4 files changed

Lines changed: 110 additions & 0 deletions

File tree

‎pkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipient.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,12 @@ private SecretKey calculateAgreedWrapKey(AlgorithmIdentifier keyEncAlg, Algorith
207207

208208
if (isMQV(agreeAlgOID))
209209
{
210+
// RFC 5753 sec. 3.2.1: for 1-Pass ECMQV the ukm MUST be present
211+
if (userKeyingMaterial == null)
212+
{
213+
throw new CMSException("User keying material must be present for MQV.");
214+
}
215+
210216
MQVuserKeyingMaterial ukm = MQVuserKeyingMaterial.getInstance(userKeyingMaterial.getOctets());
211217

212218
SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(

‎pkix/src/main/jdk1.1/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipient.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ private SecretKey calculateAgreedWrapKey(AlgorithmIdentifier keyEncAlg, Algorith
147147

148148
if (CMSUtils.isMQV(agreeAlgOID))
149149
{
150+
// RFC 5753 sec. 3.2.1: for 1-Pass ECMQV the ukm MUST be present
151+
if (userKeyingMaterial == null)
152+
{
153+
throw new CMSException("User keying material must be present for MQV.");
154+
}
155+
150156
MQVuserKeyingMaterial ukm = MQVuserKeyingMaterial.getInstance(userKeyingMaterial.getOctets());
151157

152158
SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3539,6 +3539,55 @@ public void testECMQVFreshEphemeralKeyPerMessage()
35393539
confirmDataReceived(ed2.getRecipientInfos(), data, _reciEcCert2, _reciEcKP2.getPrivate(), BC);
35403540
}
35413541

3542+
/*
3543+
* RFC 5753 sec. 3.2.1: for 1-pass ECMQV the ukm (carrying the MQVuserKeyingMaterial) MUST be present, so a
3544+
* message without one is rejected with a specific error rather than a generic one.
3545+
*/
3546+
public void testECMQVKeyAgreeWithoutUkm()
3547+
throws Exception
3548+
{
3549+
byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
3550+
3551+
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
3552+
3553+
edGen.addRecipientInfoGenerator(new JceKeyAgreeRecipientInfoGenerator(CMSAlgorithm.ECMQV_SHA1KDF,
3554+
_origEcKP.getPrivate(), _origEcKP.getPublic(),
3555+
CMSAlgorithm.AES128_WRAP).addRecipient(_reciEcCert).setProvider(BC));
3556+
3557+
CMSEnvelopedData ed = edGen.generate(
3558+
new CMSProcessableByteArray(data),
3559+
new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build());
3560+
3561+
// Rebuild the message with the (mandatory) ukm removed from the KeyAgreeRecipientInfo
3562+
ContentInfo contentInfo = ed.toASN1Structure();
3563+
EnvelopedData envelopedData = EnvelopedData.getInstance(contentInfo.getContent());
3564+
KeyAgreeRecipientInfo kari = KeyAgreeRecipientInfo.getInstance(
3565+
RecipientInfo.getInstance(envelopedData.getRecipientInfos().getObjectAt(0)).getInfo());
3566+
assertNotNull(kari.getUserKeyingMaterial());
3567+
3568+
KeyAgreeRecipientInfo strippedKari = new KeyAgreeRecipientInfo(kari.getOriginator(), null,
3569+
kari.getKeyEncryptionAlgorithm(), kari.getRecipientEncryptedKeys());
3570+
EnvelopedData strippedEnvelopedData = new EnvelopedData(envelopedData.getOriginatorInfo(),
3571+
new DERSet(new RecipientInfo(strippedKari)), envelopedData.getEncryptedContentInfo(),
3572+
envelopedData.getUnprotectedAttrs());
3573+
CMSEnvelopedData stripped = new CMSEnvelopedData(
3574+
new ContentInfo(contentInfo.getContentType(), strippedEnvelopedData));
3575+
3576+
RecipientInformation recipient =
3577+
(RecipientInformation)stripped.getRecipientInfos().getRecipients().iterator().next();
3578+
3579+
try
3580+
{
3581+
recipient.getContent(new JceKeyAgreeEnvelopedRecipient(_reciEcKP.getPrivate()).setProvider(BC));
3582+
fail("no exception");
3583+
}
3584+
catch (CMSException e)
3585+
{
3586+
assertEquals("User keying material must be present for MQV.", e.getMessage());
3587+
assertNull(e.getCause());
3588+
}
3589+
}
3590+
35423591
private static byte[] getMQVEphemeralPublicKey(CMSEnvelopedData ed)
35433592
throws IOException
35443593
{

‎pkix/src/test/jdk1.4/org/bouncycastle/cms/test/NewEnvelopedDataTest.java‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,55 @@ public void testECMQVFreshEphemeralKeyPerMessage()
10721072
confirmDataReceived(ed2.getRecipientInfos(), data, _reciEcCert2, _reciEcKP2.getPrivate(), BC);
10731073
}
10741074

1075+
/*
1076+
* RFC 5753 sec. 3.2.1: for 1-pass ECMQV the ukm (carrying the MQVuserKeyingMaterial) MUST be present, so a
1077+
* message without one is rejected with a specific error rather than a generic one.
1078+
*/
1079+
public void testECMQVKeyAgreeWithoutUkm()
1080+
throws Exception
1081+
{
1082+
byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
1083+
1084+
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
1085+
1086+
edGen.addRecipientInfoGenerator(new JceKeyAgreeRecipientInfoGenerator(CMSAlgorithm.ECMQV_SHA1KDF,
1087+
_origEcKP.getPrivate(), _origEcKP.getPublic(),
1088+
CMSAlgorithm.AES128_WRAP).addRecipient(_reciEcCert).setProvider(BC));
1089+
1090+
CMSEnvelopedData ed = edGen.generate(
1091+
new CMSProcessableByteArray(data),
1092+
new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build());
1093+
1094+
// Rebuild the message with the (mandatory) ukm removed from the KeyAgreeRecipientInfo
1095+
ContentInfo contentInfo = ed.toASN1Structure();
1096+
EnvelopedData envelopedData = EnvelopedData.getInstance(contentInfo.getContent());
1097+
KeyAgreeRecipientInfo kari = KeyAgreeRecipientInfo.getInstance(
1098+
RecipientInfo.getInstance(envelopedData.getRecipientInfos().getObjectAt(0)).getInfo());
1099+
assertNotNull(kari.getUserKeyingMaterial());
1100+
1101+
KeyAgreeRecipientInfo strippedKari = new KeyAgreeRecipientInfo(kari.getOriginator(), null,
1102+
kari.getKeyEncryptionAlgorithm(), kari.getRecipientEncryptedKeys());
1103+
EnvelopedData strippedEnvelopedData = new EnvelopedData(envelopedData.getOriginatorInfo(),
1104+
new DERSet(new RecipientInfo(strippedKari)), envelopedData.getEncryptedContentInfo(),
1105+
envelopedData.getUnprotectedAttrs());
1106+
CMSEnvelopedData stripped = new CMSEnvelopedData(
1107+
new ContentInfo(contentInfo.getContentType(), strippedEnvelopedData));
1108+
1109+
RecipientInformation recipient =
1110+
(RecipientInformation)stripped.getRecipientInfos().getRecipients().iterator().next();
1111+
1112+
try
1113+
{
1114+
recipient.getContent(new JceKeyAgreeEnvelopedRecipient(_reciEcKP.getPrivate()).setProvider(BC));
1115+
fail("no exception");
1116+
}
1117+
catch (CMSException e)
1118+
{
1119+
assertEquals("User keying material must be present for MQV.", e.getMessage());
1120+
assertNull(e.getCause());
1121+
}
1122+
}
1123+
10751124
private static byte[] getMQVEphemeralPublicKey(CMSEnvelopedData ed)
10761125
throws IOException
10771126
{

0 commit comments

Comments
 (0)