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

Mutated Chain with Trust Parameter #73

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Mutated Chain with Trust Parameter #73

wants to merge 5 commits into from

Conversation

dannyallover
Copy link
Contributor

@dannyallover dannyallover commented Sep 2, 2020

Continuation off of #71.

Added a parameter for each certificate that indicates if it's trusted or not.

- GeneralizedTime and UTCTime were flipped for encoding.
- Version is EXPLICIT tagged. The encoder was incorrectly
using IMPLICIT tagging.
- Make the coment more clear for encoding the VersionNumber
in X.509 cert to der.
- Add Val0 back in ASN.1 PDU proto to be able to generate
context-specific 0, amongst other things.
- Create a certificate chain and mutate it to create structural
relationships between the certs.
- Add a parameter to indicate if a cert is trusted or not.
@dannyallover
Copy link
Contributor Author

PTAL @sleevi

Copy link

@sleevi sleevi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Danny,

Sorry for the delays! I think I mentioned I was going to be OOO, but now I'm back and picking these up. As I mention in the review comments below, I'm working forward (in terms of what needs to land first), but also trying to "think backwards" (in terms of how stuff will be used).

So this round of comments is mostly my unvarnished take based on the protobuf and the headers, but I haven't paged in the full picture. As such, it's not a direct call for changes in some of these areas (you'll see which), but definitely wanting to make sure we're on the same page and to get your thoughts about some of the design elements, so we can figure out how to document them.

message MutatedChain {
// An X.509 Certificate Chain comprises a list of certificates.
repeated x509_certificate.X509Certificate chain = 1;
repeated TrustParameter trust_parameters = 2;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I was out of the office on vacation, which means I've definitely forgotten more than I remember, but to try and recap:

  • We talked about how 'trusted' isn't really a property of the DER, it's more a property of the target API being fuzzed
    • That is, the target API has a notion of "trusted" or not, as well as things like purpose, hostname to be verified, etc
  • This is why it doesn't belong as a property of the X509Certificate; it's not really an encoding option
  • Because it's not a property of the X509Certificate, it also doesn't lend itself to being in mutations`
    • This is because when we apply a Mutation, what we're doing is structurally modifying one (or more) X509Certificates in chain to expressing something in the proto schema that is intended to match the ASN.1

Does this match your memory as well?

What I'm wondering here is whether we should split further, based on separating out the "encoding" (which may fail to decode on the target-under-fuzzing side) and the API parameters. I think we talked a little about this in your final week, about the tension between target-specific API parameters (e.g. BoringSSL vs NSS API parameters) and those generic to RFC 5280, but that are still not encoding parameters (such as the set of trust anchors, or trusted policy OIDs, etc).

Concretely, I'm curious for your thoughts on

message MutatedChain {
  repeated x509_certificate.X509Certificate chain = 1;
  repeated Mutation mutations = 2;
}

message TrustedChain {
  MutatedChain chain = 1;
  repeated TrustedCertificate certs = 2;
}

message TrustedCertificate {
  required CertIndex index = 1;
}

I'm not 100% sold on this; it might be coupling the Protobuf layout too much to how the code would be structured, so I'm curious your thoughts. But the reason for that proposal above is:

  1. MutatedChain only handles "things expressed via DER"
  2. TrustedChain (for lack of a better naming) contains the set of generic-to-all-libraries API parameters, like those talked about in RFC 5280, Section 6.1.1 (handwave handwave)
  3. We let TrustedCertificate act as all the trust parameter-specific configuration (6.1.1 (d) from RFC 5280)

The reason I'm not entirely sold is I'm trying to work backwards, from a "here's specifically how a target will use this" (e.g. BoringSSL, NSS), and then trying to make sure we have an API that makes sense for that, and if we have an API that makes sense, that we have the Protobufs that make sense for it. That is, from a design perspective, we "work backwards" to work out the design, but since I'm reviewing it, I'm having to "work forward" (first the protobuf, then the API, then the target integration). So there's a lot of state to keep in mind, and I haven't paged it all in, but wanted to share my initial thoughts.

Note: Not asking you to make changes yet, especially as I page in all the reviews, but wanted to share initial thoughts. If we do keep it as-is, which could be perfectly fine, I think we want to add documentation here that talks about a "Here's how you use this" that also reflects how we'll extend it, so we've got a bit of a coherent design.


message TrustParameter {
// Allow certificate to be trusted or not-trusted.
required bool trusted = 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the design I mentioned above, I think we can omit trusted here, since its mere preference implies a certificate is trusted.

That said, with your current API, I think it's not unreasonable to keep, so that the fuzzer won't just make every cert trusted :)

CERT_1 = 1;
CERT_2 = 2;
CERT_3 = 3;
CERT_4 = 4;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
CERT_4 = 4;
CERT_4 = 4;
CERT_5 = 5;
CERT_6 = 6;
CERT_7 = 7;
CERT_8 = 8;
CERT_9 = 9;

"10 certs should be enough for anyone" - Bill Gates


namespace x509_certificate {

struct X509 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
struct X509 {
struct FuzzedCertificate {

I don't recall if we used that name yet? Just thought it might be a better name? Definitely one of the hard problems of computer science, so take what you will.

My concern with "X509" is that it isn't that descriptive, especially with the numbers, and so trying to find something easier for folks to comprehend. Of course, this may change in time.

#include <utility>

namespace x509_certificate {

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
namespace {

We should move all the 'utility' functions (those not part of the header) within an unnamed namespace, to prevent the symbols from triggering the One Definition Rule

That is, it's a nested unnamed namespace within the x509_certificate namespace.

This is discussed in the Google style guide at https://google.github.io/styleguide/cppguide.html#Unnamed_Namespaces_and_Static_Variables

}
}

void Mutate(const Mutation& mutation,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void Mutate(const Mutation& mutation,
void ApplyMutation(const Mutation& mutation,

}
}

void Mutate(const MutateSignature& mutation,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void Mutate(const MutateSignature& mutation,
void MutateSignature(const MutateSignature& mutation,

Combined with my suggestion below, trying to make it explicitly clear in the code that these are different mutations that are happening; that is, trying not to use overloads.

return der;
}

void SetTrust(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void SetTrust(
void ApplyTrustParameters(

Handwavy naming, but "SetTrust" felt a little less descriptive.

return;
}
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} // namespace

This is where the unnamed namespace would end, since only lines 84 - 104 are relevant.

Comment on lines +85 to +101
auto chain = mutated_chain.chain();
auto trust_parameters = mutated_chain.trust_parameters();
auto mutations = mutated_chain.mutations();

if (chain.empty()) {
return {{}};
}

for (const auto& mutation : mutations) {
Mutate(mutation, chain);
}

std::vector<X509> encoded_mutated_chain;
for (const auto& encoded_cert : EncodeChain(chain)) {
encoded_mutated_chain.push_back({encoded_cert, false});
}
SetTrust(encoded_mutated_chain, trust_parameters);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto chain = mutated_chain.chain();
auto trust_parameters = mutated_chain.trust_parameters();
auto mutations = mutated_chain.mutations();
if (chain.empty()) {
return {{}};
}
for (const auto& mutation : mutations) {
Mutate(mutation, chain);
}
std::vector<X509> encoded_mutated_chain;
for (const auto& encoded_cert : EncodeChain(chain)) {
encoded_mutated_chain.push_back({encoded_cert, false});
}
SetTrust(encoded_mutated_chain, trust_parameters);
std::vector<X509> encoded_chain;
// Copy the chain, to allow for local modification
auto chain = mutated_chain.chain();
if (chain.empty()) {
return encoded_chain;
}
// Apply all pending mutations to the local copy.
for (const auto& mutation : mutated_chain.mutations()) {
ApplyMutation(mutation, chain);
}
// Encode the local copy to DER.
for (const auto& encoded_cert : EncodeChain(chain)) {
encoded_chain.push_back({encoded_cert, false});
}
// Update trust settings.
ApplyTrustParameters(encoded_chain, mutated_chain.trust_parameters());

Mostly, I'm restructuring according to https://google.github.io/styleguide/cppguide.html#Local_Variables , to try to declare it before it's used, as well as avoid unnecessary locals, especially when they may end up copying fields (which I don't believe is what you intended, except for chain).

@jvoisin
Copy link
Collaborator

jvoisin commented Oct 25, 2022

@dannyallover any plans to keep on working on this, or shall we close this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants