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
crypto_box: Add optional serde support #27
Conversation
I'd definitely prefer for |
@tarcieri updated! |
It'd be nice to avoid the We've generally used handwritten |
True. Might not be relevant for server applications (where serde_derive will most probably already be part of the dependency tree), but for minimal implementations (e.g. no_std). I'll update the PR. |
Here's an example from another crate of ours if this helps: https://github.com/RustCrypto/traits/blob/master/elliptic-curve/src/public_key.rs#L356-L392 |
ddaba29
to
ac5332c
Compare
Updated. I implemented two variants, first I used Note that my familiarity with serde internals and manual ser/de impls is rather superficial, so I hope this does the right thing 🙂 Round-trip serialization works, and the bincode looks reasonable (32 plain bytes without a length prefix or tag). |
My understanding is this approach works well for We should probably try to solve these concerns in a single place within the RustCrypto project, but my best understanding is a byte slice is the most flexible option. |
Opened an issue about common serde support: RustCrypto/traits#880 |
Alright, I added another commit that switches to a slice based approach. The bincode overhead for this is 8 bytes, plus we need to do length checking (but otherwise that would be done by serde, so that's fine). |
With the `serde` feature, serialization and deserialization is implemented for `PublicKey`.
The CBOR overhead of a tuple is something ridiculous like turning each byte into 4 or 8-bytes, so the unnecessary length prefix in |
Here's the error message when deserializing a public key from a 4-byte bincode array:
Code: let deserialized: PublicKey = bincode::deserialize(&[4, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4]).unwrap(); |
Yeah, serde_json does something similar (I think it's |
One of the things I mentioned on RustCrypto/traits#880 is the ability to generate serializers/deserializers which use a different representation for human readable formats, e.g. hex. You can see some examples of that here: https://github.com/RustCrypto/traits/blob/559eb9e349884ff53db032db9bff7044b9d73dad/elliptic-curve/src/scalar/core.rs#L405-L464 |
Ah, yeah, I saw you mentioned the human readable format thing. I wasn't aware that it's that easy to implement, I'll add this tomorrow 🙂 |
Actually, it's not as easy, because currently there's no method of hex encoding/decoding a slice in the library. And I'm not sure if it should be the responsibility of this library (as a user, you can access the bytes and encode it yourself). In the case of If we do that, then a few questions arise:
I'd either go with "standardize on a non-optional hex encoding dependency" or "don't do any specialization for human readable serializers at all". (By the way, the |
Really before we proliferate this too much, I'd prefer to make a
I think it would make sense for it to always be included when
That's just a deficiency of the current implementation. It could be corrected. The serializer is based on All that said, my main suggestion would be to just stick with binary serializers for now and circle back on human readable format detection in some common solution like RustCrypto/traits#880. |
Is it needed though? I think all hex encoder/decoder implementations use code based on simple subtraction/addition/shifts without branches on processed data. |
Nope! The ...and heavy branching: https://github.com/KokaKiwi/rust-hex/blob/e8b40e42/src/lib.rs#L176-L184 |
Yeah, for secret keys you'd need a constant-time implementation. It probably makes sense to not let a LUT-based hex encoding crate into the dependency list at all (even if it's currently only used for public keys).
I agree. In that case, this PR can be merged, right? |
Hm, don't merge this yet, it seems that msgpack roundtrip-serialization with rmp-serde seems to fail... I first need to find out why.
|
I think I found the reason. I initially implemented deserialization like this. // Deserialize slice
let slice = <&[u8]>::deserialize(deserializer)?;
// Convert to array (with length check)
let array: [u8; KEY_SIZE] = slice
.try_into()
.map_err(|_| Error::invalid_length(slice.len(), &"a 32-byte public key"))?;
Ok(PublicKey::from(array)) Since we deserialize into a slice, that means that this will only work for deserializers that can borrow the serialized data directly. That's the case for bincode, and I think for CBOR as well. The msgpack deserializer doesn't seem to support this. I changed the deserializer to use a I pushed a commit with the |
This allows serializers to use a more efficient byte array representation. Otherwise sequence based serialization would be used.
That's unfortunate. I wonder if there's an actual limitation of the msgpack format that requires that, or if it's just a missing feature of the decoder. (Also: issues like this are all the more reason why it'd be good to solve serialization centrally in one place) |
The
serde
feature is optional, but enabled by default. (Could be made off-by-default if desired, not sure if we have a batteries-included or a minimal-by-default policy here.)I used the serde-crate-alias trick, inspired by RustCrypto/RSA#41
For a round-trip serialization test, I added "bincode" and "rand" dev-dependencies.