zerotier_api/
address.rs

1use crate::{InternalError, VerifyingKey, PUBLIC_KEY_LENGTH};
2
3use arrayref::{array_mut_ref, array_ref};
4use failure::Error;
5use serde::*;
6use std::{convert::TryFrom, mem};
7
8use salsa20::stream_cipher::generic_array::GenericArray;
9use salsa20::stream_cipher::{NewStreamCipher, SyncStreamCipher};
10use salsa20::Salsa20;
11
12use sha2::{Digest, Sha512};
13
14/// [`Address`](struct.Address.html) length in bytes.
15pub const ADDRESS_LENGTH: usize = 5;
16
17const BLOCK_SIZE: usize = 64;
18const MEMORY_SIZE: usize = 1 << 21; // 2 MB
19const U64_SIZE: usize = mem::size_of::<u64>();
20
21/// 40-bit node ID derived from [`VerifyingKey`](struct.VerifyingKey.html).
22///
23/// Address is derived by taking last five bytes of memory-hard hash.
24/// Address is valid unless:
25///
26/// - first byte of memory-hard hash is greater than `0x10`
27/// - first byte of address is `0xFF`
28/// - every byte of address is `0x00`
29#[derive(Clone, Debug, PartialEq)]
30pub struct Address([u8; ADDRESS_LENGTH]);
31
32impl Serialize for Address {
33    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
34    where
35        S: Serializer,
36    {
37        serializer.serialize_str(&hex::encode(self.0))
38    }
39}
40
41/// Ad-hoc memory-hard hash function used to derive address from ZeroTier public key.
42fn memory_hard_hash(public_key: &VerifyingKey) -> Result<[u8; BLOCK_SIZE], Error> {
43    let mut buf = [0u8; BLOCK_SIZE];
44    let mut mem = [0u8; MEMORY_SIZE];
45
46    let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = public_key.into();
47    buf.copy_from_slice(&Sha512::digest(&public_key_bytes));
48
49    let mut cipher = Salsa20::new(
50        GenericArray::from_slice(&buf[0..32]),
51        GenericArray::from_slice(&buf[32..40]),
52    );
53
54    cipher.apply_keystream(&mut mem[..BLOCK_SIZE]);
55
56    for i in (BLOCK_SIZE..MEMORY_SIZE).step_by(BLOCK_SIZE) {
57        let (src, dst) = mem.split_at_mut(i);
58
59        dst[..BLOCK_SIZE].copy_from_slice(&src[i - BLOCK_SIZE..]);
60        cipher.apply_keystream(&mut dst[..BLOCK_SIZE]);
61    }
62
63    for i in (0..MEMORY_SIZE).step_by(2 * U64_SIZE) {
64        let n1 = u64::from_be_bytes(*array_ref!(mem, i, U64_SIZE));
65        let n2 = u64::from_be_bytes(*array_ref!(mem, i + U64_SIZE, U64_SIZE));
66
67        let i1 = usize::try_from(n1)? % (BLOCK_SIZE / U64_SIZE) * U64_SIZE;
68        let i2 = usize::try_from(n2)? % (MEMORY_SIZE / U64_SIZE) * U64_SIZE;
69
70        mem::swap(
71            array_mut_ref!(buf, i1, U64_SIZE),
72            array_mut_ref!(mem, i2, U64_SIZE),
73        );
74
75        cipher.apply_keystream(&mut buf[..]);
76    }
77
78    if buf[0] >= 17 {
79        Err(InternalError::InvalidHashcash.into())
80    } else {
81        Ok(buf)
82    }
83}
84
85/// Tries to derive address from [`VerifyingKey`](struct.VerifyingKey.html). Throws
86/// [`InternalError`](enum.InternalError.html) for invalid addresses.
87impl TryFrom<&VerifyingKey> for Address {
88    type Error = Error;
89
90    fn try_from(public_key: &VerifyingKey) -> Result<Self, Error> {
91        let hash = memory_hard_hash(public_key)?;
92        let addr = array_ref!(hash, BLOCK_SIZE - ADDRESS_LENGTH, ADDRESS_LENGTH).clone();
93
94        if addr[0] == 0xff || addr[..] == [0, 0, 0, 0, 0] {
95            Err(InternalError::ReservedAddress.into())
96        } else {
97            Ok(Address(addr))
98        }
99    }
100}
101
102/// Tries to construct an address from a slice of bytes. Fails if `len(bytes) != 5`.
103impl TryFrom<&[u8]> for Address {
104    type Error = Error;
105
106    fn try_from(bytes: &[u8]) -> Result<Self, Error> {
107        if bytes.len() != ADDRESS_LENGTH {
108            Err(InternalError::BytesLengthError.into())
109        } else {
110            Ok(Self(*array_ref!(bytes, 0, ADDRESS_LENGTH)))
111        }
112    }
113}