formats icon indicating copy to clipboard operation
formats copied to clipboard

Understanding `tls_codec` and it's traits

Open imor opened this issue 1 year ago • 4 comments

I have been working on adding support for the the Signed Certificate Timestamp extension in x509-cert crate. Since some data in this extension is TLS encoded I an using the tls_codec crate. But I'm a bit confused about the structure of some of the types and traits in tls_codec. Specifially:

  • What's the difference between TlsVecU8<T> and TlsByteVecU8. To me it looks like TlsByteVecU8 can be just a type alias: type TlsByteVecU8 = TlsVecU8<u8>. Is this true? If yes, why do we have two entirely separate types?
  • What is the difference between Serialize and SerializeBytes traits (same for Deserialize and DeserializeBytes)? Are Serialize and Deserialize meant to be used only in an std environment (because they use std::io::{Read, Write})? If yes, why can't SerializeBytes and DeserializeBytes work for both std and no_std?
  • Why is DeserializeBytes impl'd for TlsVecU8<T> but SerializeBytes is not?

Answers to these would be greatly appreciated. Especially the last one because currently my types implement Serialize and DeserializeBytes which looks a bit asymmetric to me.

cc @tarcieri @franziskuskiefer

imor avatar Jun 17 '23 07:06 imor

  • What's the difference between TlsVecU8<T> and TlsByteVecU8. To me it looks like TlsByteVecU8 can be just a type alias: type TlsByteVecU8 = TlsVecU8<u8>. Is this true? If yes, why do we have two entirely separate types?

It's not a type alias. It's a more efficient version of the same. TlsVecU8<T> has to call T::tls_(de)serialize for each element while TlsByteVecU8 knows T==u8 and can thus skip the extra function call. Unfortunately Rust doesn't allow us to overload functions and this is the quickest way around (even if not the most elegant one).

  • What is the difference between Serialize and SerializeBytes traits (same for Deserialize and DeserializeBytes)? Are Serialize and Deserialize meant to be used only in an std environment (because they use std::io::{Read, Write})? If yes, why can't SerializeBytes and DeserializeBytes work for both std and no_std?

Yes, the way the Serialize trait is defined it uses Read/Write, which requires std. You can use (De)SerializeBytes for both, std and no_std but it's a different API.

  • Why is DeserializeBytes impl'd for TlsVecU8<T> but SerializeBytes is not?

No one needed it yet. But it should certainly be done.

franziskuskiefer avatar Jun 21 '23 06:06 franziskuskiefer

Thanks, I didn't realize TlsByteVecU8 is more efficient. I should definitely use it in my work. I'll stick to the SerializeBytes/DeserializeBytes versions as they work with both std and no_std. As for missing SerializeBytes impl, I'll raise a PR for that.

imor avatar Jun 21 '23 11:06 imor

@franziskuskiefer a follow up question:

I replaced TlsVecU16<u8> with TlsByteVecU16 in my code but there is a difference in decoding. E.g.

        let bytes = [0, 3, 2, 1, 0];//first two bytes are length prefix in big endian format, in this case 3 bytes.
        let result = TlsByteVecU16::tls_deserialize(&bytes).unwrap();
        println!("{result:?}");//prints (TlsByteVecU16 { vec: [0, 3, 2] }, [])

        let result = TlsVecU16::<u8>::tls_deserialize(&bytes).unwrap();
        println!("{result:?}");//prints (TlsVecU16 { vec: [2, 1, 0] }, [])

Looks like TlsByteVecU16 isn't skipping over the first two length prefix bytes. They should decode to identical bytes right?

Edit: BTW the code above deserializes using the DeserializeBytes trait.

imor avatar Jun 21 '23 12:06 imor

Looks like TlsByteVecU16 isn't skipping over the first two length prefix bytes. They should decode to identical bytes right?

That sounds like a bug.

franziskuskiefer avatar Jun 28 '23 04:06 franziskuskiefer

I think we can close this issue now?

tarcieri avatar Aug 18 '24 14:08 tarcieri