-
Notifications
You must be signed in to change notification settings - Fork 576
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
Add IPAddrBlock and ASIdentifiers extensions (RFC 3779) #4443
base: master
Are you sure you want to change the base?
Conversation
@arckoor There are some CI failures which are relevant. Stylistically for calls to |
@arckoor Remaining CI issues look related to the visibility annotations (eg |
@randombit We're currently chipping away at the fuzzers, but the visibility is also an issue. Previously linux/clang was failing, which I resolved by moving the definition of |
@arckoor ok thanks. I’ll try to have a look over the weekend |
@randombit Have you had the chance to take a look yet, and can I still contribute something to make your life easier? |
@arckoor The main issue here is CI failing. In general this is a change that's appropriate to the library and the code from a quick look seems fine. But we certainly can't merge if it will break the build. Unfortunately I don't know the exact issue; I guess symbol visibility behaves differently on macOS/Windows vs Linux. My only suggestion would be to try adding |
@randombit The clang build should now succeed. After much debugging we realized the compiler was optimizing away the |
cec640d
to
4986bf0
Compare
Co-authored-by: butteronarchbtw <[email protected]>
@randombit we've now resolved the symbol issues by instantiating the template classes in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@arckoor @butteronarchbtw Not a full review and I still need to check the tests but enough for you to get started on. Overall things seem fine with two exceptions
-
The various types have setters that seem to me quite easy to misuse. I would rather see values set using constructors, at least whenever possible.
-
The decoding functions seem really complicated considering the types involved.
BER_Decoder
is designed to make this easy, so either there is improvements possible in the decoding or there are improvements possible inBER_Decoder
. I left some suggestions which are untested but should help get you going.
template <Version V> | ||
class BOTAN_PUBLIC_API(3, 6) IPAddress final { | ||
public: | ||
void set_from_bytes(const std::vector<uint8_t>& v); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Input values should be span
, here std::span<const uint8_t> v
* RFC 3779 X.509 Extensions for IP Addr | ||
* | ||
*/ | ||
class BOTAN_PUBLIC_API(3, 6) IPAddressBlocks final : public Certificate_Extension { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The BOTAN_PUBLIC_API
macros should read 3, 7 since that's the next release
|
||
void set_min(const IPAddress<V>& min) { m_min = min; } | ||
|
||
void set_max(const IPAddress<V>& max) { m_max = max; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General comment: I'd prefer if possible (and it seems like it is from my current understanding) that values are set once in a constructor and we don't have any setters.
uint16_t afi = (m_addr_family[0] << 8) | m_addr_family[1]; | ||
if(1 > afi || afi > 2) { | ||
throw Decoding_Error("Only AFI IPv4 and IPv6 are supported."); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here combine the check on afi
and the decoding
if(afi == 1) {
...
} else if(afi == 2) {
...
} else {
throw Decoding_Error("Only AFI IPv4 and IPv6 are supported.");
}
BER_Decoder obj_ber = BER_Decoder(obj); | ||
BER_Object as_min_obj = obj_ber.get_next_object(); | ||
std::vector<uint8_t> bytes; | ||
bytes.assign(as_min_obj.data().begin(), as_min_obj.data().end()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ASId
is just an INTEGER
, BER_Decoder
supports this already
|
||
void ASBlocks::ASIdOrRange::decode_from(BER_Decoder& from) { | ||
BER_Object obj = from.get_next_object(); | ||
ASN1_Type type_tag = obj.type_tag(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this function can be quite a bit simpler overall, something like (untested)
ASN1_Tag next_tag = from.peek_next_object().type_tag();
if(next_tag == ASN1_Type::Integer) {
from.decode(m_min);
m_max = m_min;
} else if(next_tag == ASN11_Type::Sequence) {
from.start_sequence().decode(m_min).decode(m_max).end_cons();
} else {
throw ...
}
|
||
void ASBlocks::ASIdOrRange::encode_into(Botan::DER_Encoder& into) const { | ||
if(m_min == m_max) { | ||
into.add_object(ASN1_Type::Integer, ASN1_Class::Universal, encode_asnum(m_min)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DER_Encoder
already knows to zero prefix, all that should be required here is
into.encode(m_min)
(likewise wherever encode_asnum
is called, I don't believe this function is necessary at all)
ASN1_Type type_tag = obj.type_tag(); | ||
|
||
// this can either be a prefix or a single address | ||
if(type_tag == ASN1_Type::BitString) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function similar to ASBlocks::ASIdOrRange::decode_from
can be made much simpler by using peek_next_object
to decide if you are in the IPAddress
or IPAddressRange
case. Then you can use the builtin functionality for decoding bit strings, parsing sequences etc without having to replicate logic here.
m_addr_family = addr_family; | ||
size_t addr_family_length = addr_family.size(); | ||
if(2 > addr_family_length || addr_family_length > 3) { | ||
throw Decoding_Error("AFI/SAFI too long / too short."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this surpsisingly hard to parse. Maybe just
if(addr_family.size() != 2 && addr_family.size() != 3)
?
} | ||
|
||
void ASBlocks::ASIdentifiers::decode_from(Botan::BER_Decoder& from) { | ||
BER_Object obj = from.get_next_object(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be done as (untested)
from.start_sequence()
.decode_optional(m_asnum, ASN1_Type(0), ASN1_Class::ExplicitContextSpecific);
.decode_optional(m_rdi, ASN1_Type(1), ASN1_Class::ExplicitContextSpecific);
.end_cons()
If it's something much harder than that it suggests BER_Decoder
needs a helper
This PR implements two extensions required for working with secure routing. These extensions contain IP Addresses or AS numbers, as specified by RFC 3779.
Both encoding and decoding of AS numbers, single addresses, address prefixes and min-max ranges have been tested.