Skip to content

FR: add (optional) support for zerocopy? #821

@danderson

Description

@danderson

TL;DR: zerocopy is neat, and I think some of the key types in the curve25519-dalek expanded universe should optionally derive its traits. I'm willing to send the patches to make it happen, WDYT?

Background

The zerocopy crate (github) provides safe zero-copy transmutation facilities. It effectively lets you transmute values to byte sequences and back, with compile-time enforcement that the conversion is safe (e.g. all bit patterns are valid if you derive FromBytes, sizes and alignments are correct, ...).

This crate is used a lot in Fuchsia, but it's not tied to it, and is quite handy for things like pulling apart network packets. For example, here's a WireGuard handshake initiation message:

#[repr(C, packed)]
#[derive(TryFromBytes, IntoBytes, Immutable, KnownLayout)]
struct HandshakeInitiation {
    msg_type: MessageType, // a repr(u8) enum
    _reserved: [u8; 3],
    sender_id: zerocopy::byteorder::little_endian::U32,
    ephemeral_pub: [u8; 32],
    static_pub_sealed: [u8; 32 + 16],
    timestamp_sealed: [u8; 12 + 16],
    mac1: [u8; 16],
    mac2: [u8; 16],
}

With those derives, I can do things like:

let mut pkt: Vec<u8> = obtain_packet();
// fails if slice is the wrong size/alignment or msg_type is invalid, otherwise transmutes without copying
let mut pkt = HandshakeInitiation::try_mut_from_bytes(&mut pkt)?;

// Twiddle pkt here...

// Transmute back to &[u8], again with no copying
write_to_network(pkt.as_bytes())

The derive macros know which primitive types can be safely transmuted in this way, and has a comprehensive ruleset for proving that composite types meet the trait requirements (e.g. a struct containing an enum field can derive TryFromBytes but not FromBytes, since not all tag bit patterns are valid).

Proposal

For the code I'm writing, it would be quite handy for some of the curve25519-dalek key types to implement zerocopy traits. In the example struct above, ephemeral_pub should ideally be an x25519_dalek::PublicKey, so that I can remove the the extra noise of PublicKey::from() and key.to_bytes() when touching that field.

So, I propose adding a zerocopy crate feature (off by default), and when enabled derive the appropriate zerocopy traits on types that currently support serde serialization. The serde traits already pinpoint those types that are "acceptable to store", so to speak, and zerocopy enables approximately the same thing. So, that seems like a reasonable set of types to make zerocopy-able too.

Taking x25519_dalek as an example, that would mean deriving the zerocopy traits on PublicKey and StaticSecret only. In both cases, I believe (but the derive macros will verify!) that those types bottom out as [u8; 32]s with no invalid bit patterns, so they should be able to derive FromBytes, TryFromBytes and IntoBytes to get a complete set of safe byte conversions.

If this sounds like something you'd be okay with, I can do the writing of code and sending of PRs, but I wanted to first see if this is something you'd even want. I believe the maintenance burden would be small since it's not creating new feature surface, but ofc that's easy for me to say as not-the-maintainer :). I could also write up a draft PR to see what the actual change would look like, if that would help decide (and obviously with no hard feelings if the outcome is "no thanks" and throwing the draft away!).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions