xaptum / xtt

A C implementation of the Trusted Transit protocol for securing Internet of Things (IoT) network traffic. Created to support the Xaptum Edge Network Fabric, an IoT Network Solution.
https://www.xaptum.com
Apache License 2.0
5 stars 5 forks source link

Root Certificate structure changed to match server certificate #67

Open kathrynfejer opened 6 years ago

kathrynfejer commented 6 years ago

XTT currently has 3 different types of certificates that XTT produces (root, server, and x509). Right now a xtt_root_certificate is the concatenation of a root_id and root_public_key. We could give the root and server certificate the same underlying structure(that of the current server cert) where the root certificate would be signed by it's private key.

It's also possible that "root_certificate" is a misnomer, and should be changed to something like root_public_info. Since it is really just the public information about a root packaged together.

If we want to change the curve again (or support the use of multiple curves), rather than users to having to supply, a possibly incorrect curve, the root_certificate could give us a curve type.

On a slightly different note, the root certificate could be changed to match the TLS root certificate that is already on the TPM. The existing cert is PEM encoded, and we would need to parse the x509 to get the root's id and public key, but this way we don't have multiple types of root certificates

zanebeckwith commented 5 years ago

I agree. Here's my two cents:

I think we should move to all-static-x.509 certificates.

For one thing, this means that the "XTT root certificate" goes away, and a root has a single certificate, which is a PEM-encoded x.509 certificate, but where we have precisely defined what goes into the certificate (to avoid having to actually parse ANS.1). The "root ID" is just the "Common Name" of the self-signed root certificate, and the public key is treated as usual in an x.509 cert (which fixes the above issue with curve-types). The difficulty here is mostly in extending our static-ASN.1 code to also parse, and in encoding/decoding base64 (for PEM).

The more-difficult aspect of this is using a single x.509 certificate for the server. This is because the server's XTT certificate contains a signature (by the root) over the cert; while an x.509 cert has the same thing, the structure of what gets signed for the two different cert types is different, and this signature is what gets checked during an XTT handshake.

Therefore, I propose we use "version 3" x.509 certificates (currently, we implicitly create version 1 certs), which allows us to use "certificate extensions" (Sec. 4.2 in RFC 5280). Specifically, we can create two custom extension fields, one to hold the "reserved" field of the XTT cert, and the other to hold the root's XTT signature. That way, the XTT library can pull out from the x.509 certificate the fields it needs to put together the "XTT server certificate" that it will send to the client during the handshake (the other fields of an XTT server certificate are already present in the typical x.509 cert).

To ensure these fields just get ignored by other, traditional x.509 certificate readers (e.g. TLS libraries), these fields must be "non-critical". We could potentially look into applying for a "Private Enterprise Number" to make our fields official; but, until then, we would probably just select some always-unused OIDs for our new fields (we have to figure out how to do that, to make sure we don't end up colliding with anything else).

Version 3 certs are generally preferred to version 1 certs, anyhow, so this will make us more in-line with the recommendations of the above RFC. Though, Version 3 certs have more fields, particularly for certificates of a certificate authority (which our "root"s are), so we also need to take some time to ensure we're creating those fields correctly (e.g. "Basic Constraints").

Similarly, I think we should also (de)serialize public/private keypairs as the SSL-format ASN.1 private key format that we currently use just for the client. For instance, the genkey command of the tool would output just a single file, not one for the public key and another for the private key.

@drbild and @kathrynfejer I'm interested in your take on this.

kathrynfejer commented 5 years ago

I agree parsing the ASN.1 will be the trickier part of making this change, but I am not sure that we could avoid it altogether if we want to change to an x.509 certificate for everything. What we

This may not be possible or just a large overhaul we don't want to do, but is it possible we could use the signature that the x.509 generates? Adding the xtt signature as a non-critical field solves that nicely, but then we do have the extra bytes hanging out.

Serializing/deserializing the key pair should be a pretty simple change. Since we are getting rid of the XTT root certificate, I can't foresee somewhere that we could only want one key instead of both.

zanebeckwith commented 5 years ago

Yea, moving to the SSL ASN.1 combo "private" key format might be a nice first step on this issue.

Using the signature already present in the x.509 certificate isn't feasible, because the signature is made over the entirety of the body of the x.509 certificate, and so includes a bunch of stuff that just isn't relevant to the XTT handshake (e.g. validity periods, OID numbers). To verify the signature, the client would need to get all of that other data, meaning the server would have to send it in the handshake. x.509 is a convenient format for storing certificates, but we shouldn't change the XTT handshake itself to accommodate x.509.

drbild commented 5 years ago

Sorry for taking so long to respond on this one.

I think we should move to all-static-x.509 certificates. For one thing, this means that the "XTT root certificate" goes away, and a root has a single certificate

Agreed. This would be great.

, which is a PEM-encoded x.509 certificate,

How about DER-encoded if it's on the TPM? Then XTT doesn't need to parse Base64 encodings. And it will use slightly less NVRAM on the TPM.

We can ship a PEM-encoded file for servers that just run enftun, not XTT.

EDIT: I guess that the server-side XTT needs to read a server cert file. And that will be a file on disk and thus should be PEM-encoded. So maybe a Base64 decoder is needed anyway.

but where we have precisely defined what goes into the certificate (to avoid having to actually parse ANS.1). The "root ID" is just the "Common Name" of the self-signed root certificate, and the public key is treated as usual in an x.509 cert (which fixes the above issue with curve-types). The difficulty here is mostly in extending our static-ASN.1 code to also parse,

Consider generating a specific ASN.1 decoder just for this structure. Then perhaps we can be less strict on the certificate structure, and not have to write parsing code.

ASN.1 parsers are most concerning from a security standpoint when operating on a user/network supplied data. A remote client can feed bad data to exploit a vulnerability.

Here, the input is static, local, and provided by us (as a file or TPM). A remote actor can't give xtt a bad cert to parse. (XTT server doesn't send the full X.509 cert to the client; the only X.509 parsed by the client is root cert.)

Generated decoders can also be a mitigation against ANS.1 parsing vulnerabilities. Several are designed to generate parsers free from common flaws like buffer overflows.

and in encoding/decoding base64 (for PEM).

See above about DER-encoding on the TPM.

The more-difficult aspect of this is using a single x.509 certificate for the server. This is because the server's XTT certificate contains a signature (by the root) over the cert; while an x.509 cert has the same thing, the structure of what gets signed for the two different cert types is different, and this signature is what gets checked during an XTT handshake.

Therefore, I propose we use "version 3" x.509 certificates (currently, we implicitly create version 1 certs), which allows us to use "certificate extensions" (Sec. 4.2 in RFC 5280). Specifically, we can create two custom extension fields, one to hold the "reserved" field of the XTT cert, and the other to hold the root's XTT signature. That way, the XTT library can pull out from the x.509 certificate the fields it needs to put together the "XTT server certificate" that it will send to the client during the handshake

Re-assembling the XTT server cert from an X.509 cert tightly couples the XTT library to the X.509. This isn't ideal, since we eventually want to drop TLS for the "record" layer and use an XTT-native protocol.

However, standardizing on ASN.1 structures for on-disk formats is good, since existing tools like openssl asn1parse can read them.

Based on a conversion with @zanebeckwith , I suggest the following:

When we drop TLS in the future, it will be easy to update clients or servers to read the XTT cert directly from disk, rather than extracting it from an X.509 cert.

This does increase the cert size somewhat, since the public key is duplicated.

When defining an ASN.1 structure for the XTT certificate, perhaps we can define a common structure for server and root certs (achieving one of the original goals from this issue).