Search code examples
rustlibsodium

Why do Rust/sodiumoxide PublicKeys get prefixed with a length when serialised?


sodiumoxide defines PublicKey as:

new_type! {
    /// `PublicKey` for signatures
    public PublicKey(PUBLICKEYBYTES);
}

The new_type macro expands to:

pub struct $name(pub [u8; $bytes]);

Thus, PublicKey is defined as a simple wrapper of 32 bytes.

When I define my own wrapper of 32 bytes (MyPubKey) it bincode serialises to 32 bytes.

When I bincode serialise PublicKey, it is 40 bytes - the 32 bytes prefixed with a little-endian u64 containing the length.

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate bincode;
extern crate sodiumoxide;
use sodiumoxide::crypto::{sign, box_};
use bincode::{serialize, deserialize, Infinite};

#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct MyPubKey(pub [u8; 32]);

fn main() {
    let (pk, sk) = sign::gen_keypair();
    let arr: [u8; 32] = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31];
    let mpk = MyPubKey(arr);
    let encoded_pk: Vec<u8> = serialize(&pk, Infinite).unwrap();
    let encoded_arr: Vec<u8> = serialize(&arr, Infinite).unwrap();
    let encoded_mpk: Vec<u8> = serialize(&mpk, Infinite).unwrap();
    println!("encoded_pk len:{:?} {:?}", encoded_pk.len(), encoded_pk);
    println!("encoded_arr len:{:?} {:?}", encoded_arr.len(), encoded_arr);
    println!("encoded_mpk len:{:?} {:?}", encoded_mpk.len(), encoded_mpk);
}

Results:

encoded_pk len:40 [32, 0, 0, 0, 0, 0, 0, 0, 7, 199, 134, 217, 109, 46, 34, 155, 89, 232, 171, 185, 199, 190, 253, 88, 15, 202, 58, 211, 198, 49, 46, 225, 213, 233, 114, 253, 61, 182, 123, 181]
encoded_arr len:32 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
encoded_mpk len:32 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]

What is the difference between the PublicKey type, created with sodiumoxide's new_type! macro and the MyPublicKey type?

How can I get the 32 bytes out of a PublicKey so that I can serialise them efficiently?


Solution

  • It's up to the implementation of the serialization. sodiumoxide has chosen to implement all serialization by converting the types to a slice and then serializing that:

    #[cfg(feature = "serde")]
    impl ::serde::Serialize for $newtype {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where S: ::serde::Serializer
        {
            serializer.serialize_bytes(&self[..])
        }
    }
    

    Since slices do not have a size known at compile time, serialization must include the length so that deserialization can occur.

    You can probably implement your own serialization for a remote type or even just serialize the inner field directly:

    serialize(&pk.0, Infinite)