Skip to content
This repository has been archived by the owner on Jun 6, 2024. It is now read-only.

Commit

Permalink
keytree: add serialization for xpub, xprv (#228)
Browse files Browse the repository at this point in the history
  • Loading branch information
tessr committed Mar 19, 2019
1 parent 490391e commit 55f3340
Showing 1 changed file with 140 additions and 0 deletions.
140 changes: 140 additions & 0 deletions keytree/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,38 @@ impl Xprv {
precompressed_pubkey: self.precompressed_pubkey,
}
}

/// Serializes this Xprv to a sequence of bytes.
pub fn to_bytes(&self) -> [u8; 64] {
let mut buf = [0u8; 64];
buf[..32].copy_from_slice(&self.scalar.to_bytes());
buf[32..].copy_from_slice(&self.dk);
buf
}

/// Decodes an Xprv from a 64-byte array, and fails if the provided array is not
/// exactly 64 bytes.
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() != 64 {
return None;
}

let mut scalar_bytes = [0u8; 32];
scalar_bytes.copy_from_slice(&bytes[..32]);
let scalar = match Scalar::from_canonical_bytes(scalar_bytes) {
Some(x) => x,
None => return None,
};
let mut dk = [0u8; 32];
dk.copy_from_slice(&bytes[32..]);
let precompressed_pubkey = (scalar * &constants::RISTRETTO_BASEPOINT_POINT).compress();

return Some(Xprv {
scalar,
dk,
precompressed_pubkey,
});
}
}

impl Xpub {
Expand Down Expand Up @@ -78,6 +110,36 @@ impl Xpub {
precompressed_pubkey: child_point.compress(),
}
}

/// Serializes this Xpub to a sequence of bytes.
pub fn to_bytes(&self) -> [u8; 64] {
let mut buf = [0u8; 64];
buf[..32].copy_from_slice(self.precompressed_pubkey.as_bytes());
buf[32..].copy_from_slice(&self.dk);
buf
}

/// Decodes an Xpub from a 64-byte array, and fails if the provided array is not
/// exactly 64 bytes, or if the compressed point fails to decompress.
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() != 64 {
return None;
}

let precompressed_pubkey = CompressedRistretto::from_slice(&bytes[..32]);
let mut dk = [0u8; 32];
dk.copy_from_slice(&bytes[32..]);

let point = match precompressed_pubkey.decompress() {
Some(p) => p,
None => return None,
};
Some(Xpub {
point,
dk,
precompressed_pubkey,
})
}
}

#[cfg(test)]
Expand Down Expand Up @@ -106,6 +168,42 @@ mod tests {
assert_eq!(expected_scalar, xprv.scalar);
}

#[test]
fn serialize_xprv_test() {
let seed = [0u8; 32];
let mut rng = ChaChaRng::from_seed(seed);
let xprv = Xprv::random(&mut rng);
let xprv_bytes = xprv.to_bytes();

// hardcoded, but happens to be expected_scalar concatenated with expected_dk
let expected_bytes = [
74, 83, 195, 251, 188, 89, 151, 14, 229, 248, 90, 248, 19, 135, 93, 255, 193, 58, 144,
74, 46, 83, 174, 126, 101, 250, 13, 234, 110, 98, 201, 1, 159, 7, 231, 190, 85, 81, 56,
122, 152, 186, 151, 124, 115, 45, 8, 13, 203, 15, 41, 160, 72, 227, 101, 105, 18, 198,
83, 62, 50, 238, 122, 237,
];
assert_eq!(&xprv_bytes[..], &expected_bytes[..]);
}

#[test]
fn deserialize_xprv_test() {
let xprv_bytes = [
74, 83, 195, 251, 188, 89, 151, 14, 229, 248, 90, 248, 19, 135, 93, 255, 193, 58, 144,
74, 46, 83, 174, 126, 101, 250, 13, 234, 110, 98, 201, 1, 159, 7, 231, 190, 85, 81, 56,
122, 152, 186, 151, 124, 115, 45, 8, 13, 203, 15, 41, 160, 72, 227, 101, 105, 18, 198,
83, 62, 50, 238, 122, 237,
];

let xprv = Xprv::from_bytes(&xprv_bytes).unwrap();

let seed = [0u8; 32];
let mut rng = ChaChaRng::from_seed(seed);
let expected_xprv = Xprv::random(&mut rng);

assert_eq!(xprv.dk, expected_xprv.dk);
assert_eq!(xprv.scalar, expected_xprv.scalar);
}

#[test]
fn random_xpub_test() {
let seed = [0u8; 32];
Expand All @@ -129,6 +227,48 @@ mod tests {
assert_eq!(xpub.precompressed_pubkey, expected_compressed_point);
}

#[test]
fn serialize_xpub_test() {
let seed = [0u8; 32];
let mut rng = ChaChaRng::from_seed(seed);
let xprv = Xprv::random(&mut rng);
let xpub = xprv.to_xpub();

let xpub_bytes = xpub.to_bytes();

// hardcoded, but happens to be expected_scalar concatenated with expected_compressed_point
let expected_bytes = [
156, 102, 163, 57, 200, 52, 79, 146, 47, 195, 32, 108, 181, 218, 232, 20, 165, 148,
192, 23, 125, 211, 35, 92, 37, 77, 156, 64, 154, 101, 184, 8, 159, 7, 231, 190, 85, 81,
56, 122, 152, 186, 151, 124, 115, 45, 8, 13, 203, 15, 41, 160, 72, 227, 101, 105, 18,
198, 83, 62, 50, 238, 122, 237,
];
assert_eq!(&xpub_bytes[..], &expected_bytes[..]);
}

#[test]
fn deserialize_xpub_test() {
let xpub_bytes = [
156, 102, 163, 57, 200, 52, 79, 146, 47, 195, 32, 108, 181, 218, 232, 20, 165, 148,
192, 23, 125, 211, 35, 92, 37, 77, 156, 64, 154, 101, 184, 8, 159, 7, 231, 190, 85, 81,
56, 122, 152, 186, 151, 124, 115, 45, 8, 13, 203, 15, 41, 160, 72, 227, 101, 105, 18,
198, 83, 62, 50, 238, 122, 237,
];
let xpub = Xpub::from_bytes(&xpub_bytes).unwrap();

let seed = [0u8; 32];
let mut rng = ChaChaRng::from_seed(seed);
let expected_xprv = Xprv::random(&mut rng);
let expected_xpub = expected_xprv.to_xpub();

assert_eq!(xpub.dk, expected_xpub.dk);
assert_eq!(xpub.point, expected_xpub.point);
assert_eq!(
xpub.precompressed_pubkey,
expected_xpub.precompressed_pubkey
);
}

#[test]
fn random_xpub_derivation_test() {
let seed = [0u8; 32];
Expand Down

0 comments on commit 55f3340

Please sign in to comment.