Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use bouncy castle 1.77 #145

Merged
merged 4 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ next (snapshot) release, e.g. `1.1-SNAPSHOT` after releasing `1.0`.
## Changelog

## 2024-xx-yy 1.37
* Use bouncy castle 1.77 (and update API usage accordingly)
* **fix**: Do not encode redundant maxlength in ROAs
* Use a more modern version of the jing (RelaxNG) library
* Add support for router certificates to the time parsing in `SignedObjectUtil`.
Expand Down
13 changes: 3 additions & 10 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,10 @@
<java.runtime.version>11</java.runtime.version>

<net.ripe.ipresource.version>1.52</net.ripe.ipresource.version>
<bouncycastle.version>1.74</bouncycastle.version>
<guava.version>32.1.2-jre</guava.version>
<joda-time.version>2.10.13</joda-time.version>
<bouncycastle.version>1.77</bouncycastle.version>
<guava.version>33.0.0-jre</guava.version>
<joda-time.version>2.12.7</joda-time.version>
<xstream.version>1.4.20</xstream.version>
<commons-io.version>2.11.0</commons-io.version>
<lombok.version>1.18.30</lombok.version>
</properties>

Expand Down Expand Up @@ -100,12 +99,6 @@
<version>${net.ripe.ipresource.version}</version>
</dependency>

<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.ripe.rpki.commons.crypto;

/**
* Encountered an ASN.1 structure that was not expected (e.g. implicit instead of explicit tags).
*/
public class IllegalAsn1StructureException extends IllegalArgumentException {
public IllegalAsn1StructureException(String message) {
super(message);
}

public IllegalAsn1StructureException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.ripe.rpki.commons.crypto.cms;

import net.ripe.rpki.commons.crypto.IllegalAsn1StructureException;
import net.ripe.rpki.commons.crypto.util.BouncyCastleUtil;
import net.ripe.rpki.commons.crypto.x509cert.AbstractX509CertificateWrapperException;
import net.ripe.rpki.commons.crypto.x509cert.X509ResourceCertificate;
Expand Down Expand Up @@ -373,18 +374,34 @@ private void verifyUnsignedAttributes(SignerInformation signer) {
validationResult.rejectIfFalse(signer.getUnsignedAttributes() == null, UNSIGNED_ATTRS_OMITTED);
}

protected static BigInteger getRpkiObjectVersion(ASN1Sequence seq) {
ASN1Primitive asn1Version = seq.getObjectAt(0).toASN1Primitive();
BigInteger version = null;
if (asn1Version instanceof ASN1Integer) {
version = ((ASN1Integer) asn1Version).getValue();
} else if (asn1Version instanceof DERTaggedObject){
final ASN1Primitive o = ((DERTaggedObject) asn1Version).getObject();
if (o instanceof ASN1Integer) {
version = ((ASN1Integer) o).getValue();
/**
* Parse the version field in a signed object.
* By convention, the version is the first field in the sequence, and it is explicitly tagged.
* @param tagNo tag number
* @param seq sequence of content
* @return version number if present, empty otherwise.
*/
protected static Optional<BigInteger> getTaggedVersion(int tagNo, ASN1Sequence seq) throws IllegalAsn1StructureException {
try {
ASN1Encodable maybeVersion = seq.getObjectAt(0).toASN1Primitive();
if (maybeVersion instanceof DLTaggedObject) {
ASN1TaggedObject tagged = (ASN1TaggedObject) maybeVersion;
if (tagged.getTagNo() == tagNo) {
var integerVersion = tagged.getExplicitBaseObject();
if (!tagged.isExplicit()) {
throw new IllegalAsn1StructureException("Expected explicit tag for version field");
}

if (integerVersion instanceof ASN1Integer) {
return Optional.of(((ASN1Integer) integerVersion).getValue());
}
}
}

return Optional.empty();
} catch (IllegalStateException e) {
// for example: version is implicitly tagged instead of explicitly
throw new IllegalAsn1StructureException("Error parsing version field", e);
}
return version;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
import net.ripe.rpki.commons.validation.ValidationResult;
import net.ripe.rpki.commons.validation.ValidationString;
import net.ripe.rpki.commons.validation.objectvalidators.CertificateRepositoryObjectValidationContext;
import net.ripe.rpki.commons.validation.objectvalidators.ResourceValidatorFactory;
import net.ripe.rpki.commons.validation.objectvalidators.X509ResourceCertificateParentChildValidator;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.math.BigInteger;
import java.text.ParseException;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;

import static net.ripe.rpki.commons.crypto.util.Asn1Util.*;
Expand Down Expand Up @@ -102,8 +103,13 @@ public void decodeAsn1Content(ASN1Encodable encoded) {
final int itemCount = seq.size();
int offset = 0;
if (itemCount == 6) {
BigInteger version = getRpkiObjectVersion(seq);
validationResult.rejectIfFalse(BigInteger.ZERO.equals(version), "mf.version", "manifest version must be 0, but is " + version);
Optional<BigInteger> optionalVersion = getTaggedVersion(0, seq);
optionalVersion.ifPresentOrElse(foundVersion -> {
validationResult.rejectIfFalse(BigInteger.ZERO.equals(foundVersion), MANIFEST_VERSION, optionalVersion.get().toString());
version = 0;
}, () -> {
validationResult.error(MANIFEST_VERSION, "missing/not explicitly tagged");
});
offset++;
} else if (itemCount == 5) {
version = ManifestCms.DEFAULT_VERSION;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.ripe.ipresource.IpRange;
import net.ripe.ipresource.IpResourceSet;
import net.ripe.ipresource.IpResourceType;
import net.ripe.rpki.commons.crypto.IllegalAsn1StructureException;
import net.ripe.rpki.commons.crypto.cms.RpkiSignedObjectInfo;
import net.ripe.rpki.commons.crypto.cms.RpkiSignedObjectParser;
import net.ripe.rpki.commons.crypto.rfc3779.AddressFamily;
Expand All @@ -17,6 +18,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import static net.ripe.rpki.commons.crypto.util.Asn1Util.*;
import static net.ripe.rpki.commons.validation.ValidationString.*;
Expand Down Expand Up @@ -96,11 +98,15 @@ void parseRouteOriginAttestation(ASN1Encodable der) {

final int itemCount = seq.size();
if (itemCount == 3) {
BigInteger version = getRpkiObjectVersion(seq);
if (validationResult.rejectIfFalse(BigInteger.ZERO.equals(version), ROA_ATTESTATION_VERSION, "attestation version must be 0, but is " + version)) {
asn = Asn1Util.parseAsId(seq.getObjectAt(1));
prefixes = parseRoaIpAddressFamilySequence(seq.getObjectAt(2));
}
Optional<BigInteger> maybeVersion = getTaggedVersion(0, seq);
maybeVersion.ifPresentOrElse(version -> {
if (validationResult.rejectIfFalse(BigInteger.ZERO.equals(version), ROA_ATTESTATION_VERSION, "attestation version must be 0, but is " + version)) {
asn = Asn1Util.parseAsId(seq.getObjectAt(1));
prefixes = parseRoaIpAddressFamilySequence(seq.getObjectAt(2));
}
}, () -> {
validationResult.error(ROA_ATTESTATION_VERSION, "missing/not explicitly tagged");
});
} else if (itemCount == 2) {
asn = Asn1Util.parseAsId(seq.getObjectAt(0));
prefixes = parseRoaIpAddressFamilySequence(seq.getObjectAt(1));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
package net.ripe.rpki.commons.crypto.rfc3779;

import com.google.common.base.Preconditions;
import net.ripe.ipresource.*;
import org.apache.commons.lang3.Validate;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Null;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERBitString;
import net.ripe.rpki.commons.crypto.IllegalAsn1StructureException;
import org.bouncycastle.asn1.*;

import java.security.cert.X509Certificate;
import java.util.EnumSet;
Expand Down Expand Up @@ -80,7 +74,7 @@ public SortedMap<AddressFamily, IpResourceSet> parseIpAddressBlocks(byte[] exten
}

for (AddressFamily addressFamily : map.keySet()) {
Validate.isTrue(!addressFamily.hasSubsequentAddressFamilyIdentifier(), "SAFI not supported");
Preconditions.checkArgument(!addressFamily.hasSubsequentAddressFamilyIdentifier(), "SAFI not supported");
}

return map;
Expand All @@ -96,8 +90,8 @@ public IpResourceSet parseAsIdentifiers(byte[] extension) {
expect(octetString, ASN1OctetString.class);
ASN1OctetString o = (ASN1OctetString) octetString;
IpResourceSet[] resources = derToAsIdentifiers(decode(o.getOctets()));
Validate.notNull(resources[1], "inheritance of resources has not been implemented yet");
Validate.isTrue(resources[1].isEmpty(), "routing domain identifiers (RDI) not supported");
Preconditions.checkNotNull(resources[1], "inheritance of resources has not been implemented yet");
Preconditions.checkArgument(resources[1].isEmpty(), "routing domain identifiers (RDI) not supported");
return resources[0];
}

Expand All @@ -120,7 +114,7 @@ SortedMap<AddressFamily, IpResourceSet> derToIpAddressBlocks(ASN1Encodable der)
*/
void derToIpAddressFamily(ASN1Encodable der, SortedMap<AddressFamily, IpResourceSet> map) {
ASN1Sequence seq = expect(der, ASN1Sequence.class);
Validate.isTrue(seq.size() == 2, "IpAddressFamily must have exactly two entries: addressFamily and IpAddressChoice");
Preconditions.checkArgument(seq.size() == 2, "IpAddressFamily must have exactly two entries: addressFamily and IpAddressChoice");

AddressFamily addressFamily = AddressFamily.fromDer(seq.getObjectAt(0));
IpResourceSet resources = derToIpAddressChoice(addressFamily.toIpResourceType(), seq.getObjectAt(1));
Expand All @@ -144,14 +138,14 @@ IpResourceSet derToIpAddressChoice(IpResourceType type, ASN1Encodable der) {
IpResource current = derToIpAddressOrRange(type, seq.getObjectAt(i));
// Check if previous and next are (1) in order and (2) not continuous
if (previous != null) {
Validate.isTrue(!previous.adjacent(current), "IP resources in extension MUST NOT be adjacent");
Preconditions.checkArgument(!previous.adjacent(current), "IP resources in extension MUST NOT be adjacent");
// The addressesOrRanges element is a SEQUENCE OF IPAddressOrRange
// types. The addressPrefix and addressRange elements MUST be sorted
// using the binary representation of:
//
// <lowest IP address in range> | <prefix length>
UniqueIpResource start = current.getStart();
Validate.isTrue(previous.getEnd().compareTo(start) < 0, "addressOrRanges MUST be sorted");
Preconditions.checkArgument(previous.getEnd().compareTo(start) < 0, "addressOrRanges MUST be sorted");
}

result.add(current);
Expand Down Expand Up @@ -182,7 +176,7 @@ IpResource derToIpAddressOrRange(IpResourceType type, ASN1Encodable der) {
*/
IpResource derToIpRange(IpResourceType type, ASN1Encodable der) {
ASN1Sequence sequence = expect(der, ASN1Sequence.class);
Validate.isTrue(sequence.size() == 2, "IPRange MUST consist of two entries (start and end)");
Preconditions.checkArgument(sequence.size() == 2, "IPRange MUST consist of two entries (start and end)");

IpAddress start = parseIpAddress(type, sequence.getObjectAt(0), false);
IpAddress end = parseIpAddress(type, sequence.getObjectAt(1), true);
Expand All @@ -195,7 +189,7 @@ IpResource derToIpRange(IpResourceType type, ASN1Encodable der) {
*/
IpResourceRange derToAsRange(ASN1Encodable der) {
ASN1Sequence seq = expect(der, ASN1Sequence.class);
Validate.isTrue(seq.size() == 2, "ASN1Sequence with two elements expected");
Preconditions.checkArgument(seq.size() == 2, "ASN1Sequence with two elements expected");
return parseAsId(seq.getObjectAt(0)).upTo(parseAsId(seq.getObjectAt(1)));
}

Expand Down Expand Up @@ -232,8 +226,8 @@ IpResourceSet derToAsIdsOrRanges(ASN1Encodable der) {
if (previous != null) {
UniqueIpResource start = current.getStart();

Validate.isTrue(!start.adjacent(previous.getEnd()), "ASIdOrRange entries MUST NOT be adjacent");
Validate.isTrue(start.max(previous.getEnd()).equals(start), "ASIdOrRange entries MUST be sorted by increasing numeric value");
Preconditions.checkArgument(!start.adjacent(previous.getEnd()), "ASIdOrRange entries MUST NOT be adjacent");
Preconditions.checkArgument(start.max(previous.getEnd()).equals(start), "ASIdOrRange entries MUST be sorted by increasing numeric value");
}
result.add(current);
previous = current;
Expand Down Expand Up @@ -267,17 +261,22 @@ IpResourceSet derToAsIdentifierChoice(ASN1Encodable der) {
*/
IpResourceSet[] derToAsIdentifiers(ASN1Encodable der) {
expect(der, ASN1Sequence.class);
ASN1Sequence seq = (ASN1Sequence) der;
Validate.isTrue(seq.size() <= 2, "ASN1Sequence with 2 or fewer elements expected");

IpResourceSet[] result = {new IpResourceSet(), new IpResourceSet()};
for (int i = 0; i < seq.size(); ++i) {
expect(seq.getObjectAt(i), ASN1TaggedObject.class);
ASN1TaggedObject tagged = (ASN1TaggedObject) seq.getObjectAt(i);
Validate.isTrue(tagged.getTagNo() == 0 || tagged.getTagNo() == 1, "unknown tag no: " + tagged.getTagNo());
result[tagged.getTagNo()] = derToAsIdentifierChoice(tagged.getObject());
try {
ASN1Sequence seq = (ASN1Sequence) der;
Preconditions.checkArgument(seq.size() <= 2, "ASN1Sequence with 2 or fewer elements expected");

IpResourceSet[] result = {new IpResourceSet(), new IpResourceSet()};
for (int i = 0; i < seq.size(); ++i) {
expect(seq.getObjectAt(i), ASN1TaggedObject.class);
ASN1TaggedObject tagged = (ASN1TaggedObject) seq.getObjectAt(i);
Preconditions.checkArgument(tagged.getTagNo() == 0 || tagged.getTagNo() == 1, "unknown tag no: " + tagged.getTagNo());
Preconditions.checkArgument((tagged.getTagClass() & BERTags.CONTEXT_SPECIFIC) != 0, "element tag is context specific.");
result[tagged.getTagNo()] = derToAsIdentifierChoice(tagged.getExplicitBaseObject());
}
return result;
} catch (IllegalStateException e) {
throw new IllegalAsn1StructureException("Could not parse AsIdentifiers extension", e);
}
return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import net.ripe.rpki.commons.provisioning.x509.ProvisioningCmsCertificateParser;
import net.ripe.rpki.commons.validation.ValidationLocation;
import net.ripe.rpki.commons.validation.ValidationResult;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
Expand Down Expand Up @@ -123,7 +122,7 @@ private String extractMessages(CMSException e) {
messages.add(t.getMessage());
t = t.getCause();
}
return StringUtils.join(messages, ", reason: ");
return String.join(", reason: ", messages);
}

public ProvisioningCmsObject getProvisioningCmsObject() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import net.ripe.rpki.commons.validation.ValidationResult;
import net.ripe.rpki.commons.validation.ValidationString;
import net.ripe.rpki.commons.xml.XmlSerializer;
import org.apache.commons.lang3.NotImplementedException;

import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -96,7 +95,7 @@ public static String serialize(AbstractProvisioningPayload payload) {
case error_response:
return ERROR_RESPONSE_SERIALIZER.serialize((RequestNotPerformedResponsePayload) payload);
default:
throw new NotImplementedException("Don't have serializer for PayloadMessageType: " + type);
throw new UnsupportedOperationException("Don't have serializer for PayloadMessageType: " + type);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package net.ripe.rpki.commons.provisioning.x509;

import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import net.ripe.rpki.commons.crypto.ValidityPeriod;
import net.ripe.rpki.commons.crypto.x509cert.X509CertificateBuilderHelper;
import net.ripe.rpki.commons.util.UTC;
Expand Down Expand Up @@ -48,9 +50,9 @@ public ProvisioningIdentityCertificateBuilder withSignatureProvider(String signa
}

public ProvisioningIdentityCertificate build() {
Validate.notNull(selfSigningKeyPair, "Self Signing KeyPair is required");
Validate.notNull(selfSigningSubject, "Self Signing DN is required");
Validate.notNull(signatureProvider, "Signature Provider is required");
Preconditions.checkNotNull(selfSigningKeyPair, "Self Signing KeyPair is required");
Preconditions.checkNotNull(selfSigningSubject, "Self Signing DN is required");
Preconditions.checkNotNull(signatureProvider, "Signature Provider is required");
setUpImplicitRequirementsForBuilderHelper();
builderHelper.withPublicKey(selfSigningKeyPair.getPublic());
builderHelper.withSigningKeyPair(selfSigningKeyPair);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ private ValidationString() {
public static final String MANIFEST_THIS_UPDATE_TIME_BEFORE_NEXT_UPDATE_TIME = "mf.this.update.before.next.update";
public static final String MANIFEST_ENTRY_FILE_NAME_IS_RELATIVE = "mf.entry.file.name.is.relative";

public static final String MANIFEST_VERSION = "mf.version";

//ghostbusters
public static final String GHOSTBUSTERS_RECORD_CONTENT_TYPE = "ghostbusters.record.content.type";
public static final String GHOSTBUSTERS_RECORD_SINGLE_VCARD = "ghostbusters.record.single.vcard";
Expand Down
Loading
Loading