diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java b/core/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java index 4c1425a18c..ac9f338b9c 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/CRLDistPoint.java @@ -42,6 +42,11 @@ public static CRLDistPoint fromExtensions(Extensions extensions) private CRLDistPoint( ASN1Sequence seq) { + if (seq.size() < 1) + { + throw new IllegalArgumentException("sequence may not be empty"); + } + this.seq = seq; } diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java b/core/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java index aac282796a..c70332e0de 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java @@ -73,6 +73,11 @@ public CertificatePolicies( private CertificatePolicies( ASN1Sequence seq) { + if (seq.size() < 1) + { + throw new IllegalArgumentException("sequence may not be empty"); + } + this.policyInformation = new PolicyInformation[seq.size()]; for (int i = 0; i != seq.size(); i++) diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java b/core/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java index 82aea5af0d..d733eed65a 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java @@ -87,6 +87,11 @@ public ExtendedKeyUsage( private ExtendedKeyUsage( ASN1Sequence seq) { + if (seq.size() < 1) + { + throw new IllegalArgumentException("sequence may not be empty"); + } + this.seq = seq; Enumeration e = seq.getObjects(); diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/PolicyMappings.java b/core/src/main/java/org/bouncycastle/asn1/x509/PolicyMappings.java index 96ebba3f01..6a7ebebf34 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/PolicyMappings.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/PolicyMappings.java @@ -47,6 +47,11 @@ public static PolicyMappings getInstance(Object obj) */ private PolicyMappings(ASN1Sequence seq) { + if (seq.size() < 1) + { + throw new IllegalArgumentException("sequence may not be empty"); + } + this.seq = seq; } diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java b/core/src/main/java/org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java index a12d8357dc..6224016e76 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/SubjectDirectoryAttributes.java @@ -72,6 +72,11 @@ public static SubjectDirectoryAttributes getInstance( */ private SubjectDirectoryAttributes(ASN1Sequence seq) { + if (seq.size() < 1) + { + throw new IllegalArgumentException("sequence may not be empty"); + } + Enumeration e = seq.getObjects(); while (e.hasMoreElements()) diff --git a/core/src/test/java/org/bouncycastle/asn1/test/EmptyExtensionSequenceTest.java b/core/src/test/java/org/bouncycastle/asn1/test/EmptyExtensionSequenceTest.java new file mode 100644 index 0000000000..b38b192372 --- /dev/null +++ b/core/src/test/java/org/bouncycastle/asn1/test/EmptyExtensionSequenceTest.java @@ -0,0 +1,134 @@ +package org.bouncycastle.asn1.test; + +import java.io.IOException; +import java.util.Vector; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERSet; +import org.bouncycastle.asn1.DERUTF8String; +import org.bouncycastle.asn1.x509.AccessDescription; +import org.bouncycastle.asn1.x509.Attribute; +import org.bouncycastle.asn1.x509.AuthorityInformationAccess; +import org.bouncycastle.asn1.x509.CRLDistPoint; +import org.bouncycastle.asn1.x509.CertPolicyId; +import org.bouncycastle.asn1.x509.CertificatePolicies; +import org.bouncycastle.asn1.x509.DistributionPoint; +import org.bouncycastle.asn1.x509.DistributionPointName; +import org.bouncycastle.asn1.x509.ExtendedKeyUsage; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyPurposeId; +import org.bouncycastle.asn1.x509.PolicyInformation; +import org.bouncycastle.asn1.x509.PolicyMappings; +import org.bouncycastle.asn1.x509.SubjectDirectoryAttributes; +import org.bouncycastle.util.test.SimpleTest; + +/** + * The X.509 extension types whose RFC 5280 syntax is SEQUENCE SIZE (1..MAX) + * must reject an empty SEQUENCE on parse, matching AuthorityInformationAccess. + */ +public class EmptyExtensionSequenceTest + extends SimpleTest +{ + private static final ASN1ObjectIdentifier anyPolicy = new ASN1ObjectIdentifier("2.5.29.32.0"); + + public String getName() + { + return "EmptyExtensionSequence"; + } + + public void performTest() + throws IOException + { + emptyRejected(); + nonEmptyParses(); + } + + private void emptyRejected() + { + DERSequence empty = new DERSequence(); + + rejectEmpty("CertificatePolicies", new Parse() + { + public void parse() { CertificatePolicies.getInstance(empty); } + }); + rejectEmpty("PolicyMappings", new Parse() + { + public void parse() { PolicyMappings.getInstance(empty); } + }); + rejectEmpty("ExtendedKeyUsage", new Parse() + { + public void parse() { ExtendedKeyUsage.getInstance(empty); } + }); + rejectEmpty("CRLDistPoint", new Parse() + { + public void parse() { CRLDistPoint.getInstance(empty); } + }); + rejectEmpty("SubjectDirectoryAttributes", new Parse() + { + public void parse() { SubjectDirectoryAttributes.getInstance(empty); } + }); + // already enforced before this change - guards the family stays consistent + rejectEmpty("AuthorityInformationAccess", new Parse() + { + public void parse() { AuthorityInformationAccess.getInstance(empty); } + }); + } + + private void rejectEmpty(String name, Parse p) + { + try + { + p.parse(); + fail("empty " + name + " accepted"); + } + catch (IllegalArgumentException e) + { + isTrue("unexpected message for " + name + ": " + e.getMessage(), + e.getMessage().indexOf("sequence may not be empty") >= 0); + } + } + + private void nonEmptyParses() + { + // each built non-empty and re-parsed through the ASN1Sequence constructor + CertificatePolicies.getInstance( + new CertificatePolicies(new PolicyInformation(anyPolicy)).toASN1Primitive()); + + PolicyMappings.getInstance( + new PolicyMappings(CertPolicyId.getInstance(anyPolicy), + CertPolicyId.getInstance(anyPolicy)).toASN1Primitive()); + + ExtendedKeyUsage.getInstance( + new ExtendedKeyUsage(KeyPurposeId.id_kp_serverAuth).toASN1Primitive()); + + DistributionPoint dp = new DistributionPoint( + new DistributionPointName(new GeneralNames( + new GeneralName(GeneralName.uniformResourceIdentifier, "http://crl.example/c.crl"))), + null, null); + CRLDistPoint.getInstance( + new CRLDistPoint(new DistributionPoint[]{ dp }).toASN1Primitive()); + + AuthorityInformationAccess.getInstance( + new AuthorityInformationAccess(AccessDescription.id_ad_caIssuers, + new GeneralName(GeneralName.uniformResourceIdentifier, "http://ca.example/ca")).toASN1Primitive()); + + Vector attrs = new Vector(); + attrs.addElement(new Attribute(new ASN1ObjectIdentifier("2.5.4.3"), + new DERSet(new DERUTF8String("name")))); + SubjectDirectoryAttributes.getInstance( + new SubjectDirectoryAttributes(attrs).toASN1Primitive()); + } + + private interface Parse + { + void parse(); + } + + public static void main(String[] args) + { + runTest(new EmptyExtensionSequenceTest()); + } +} diff --git a/core/src/test/java/org/bouncycastle/asn1/test/RegressionTest.java b/core/src/test/java/org/bouncycastle/asn1/test/RegressionTest.java index 7d04559f37..b71d60d62d 100644 --- a/core/src/test/java/org/bouncycastle/asn1/test/RegressionTest.java +++ b/core/src/test/java/org/bouncycastle/asn1/test/RegressionTest.java @@ -47,6 +47,7 @@ public class RegressionTest new TargetInformationTest(), new SubjectKeyIdentifierTest(), new AuthorityKeyIdentifierTest(), + new EmptyExtensionSequenceTest(), new TBSCertListTest(), new TBSCertificateIssuerTest(), new AttributeCertificateInfoIssuerTest(), diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 7d2b9b88ed..47f25d761d 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -42,6 +42,7 @@