uuid_v1_variant/
lib.rs

1#![feature(libc)]
2
3extern crate libc;
4extern crate rustc_serialize;
5extern crate rand;
6extern crate eui48;
7
8use libc::{c_char};
9use std::path::Path;
10use std::fs::File;
11use std::io::prelude::*;
12use std::io::BufReader;
13use std::ops::Add;
14use std::time::{SystemTime, Duration, UNIX_EPOCH};
15use std::ffi::CStr;
16use rand::{OsRng, Rng};
17use rustc_serialize::hex::FromHex;
18use eui48::{MacAddress, Eui48};
19
20//------------------------------------------------------------------------------
21
22#[no_mangle]
23pub extern fn uuid_gen_new(ptr: *const c_char) -> *mut UUIDGen {
24    let eui_cstr = unsafe {
25        assert!(!ptr.is_null());
26        CStr::from_ptr(ptr)
27    };
28    let mut eui: [u8; 6] = Default::default();
29    eui.copy_from_slice(&eui_cstr.to_bytes()[0..6]);
30    Box::into_raw(Box::new(UUIDGen::new(eui)))
31}
32
33#[no_mangle]
34pub extern fn uuid_gen_free(ptr: *mut UUIDGen) {
35    if ptr.is_null() { return }
36    unsafe {
37        Box::from_raw(ptr);
38    }
39}
40
41#[no_mangle]
42pub extern fn uuid_gen_nonce64(gen_ptr: *mut UUIDGen, nonce_ptr: *mut u8) {
43    let gen = unsafe {
44        assert!(!gen_ptr.is_null());
45        &mut *gen_ptr
46    };
47    let nonce64: [u8; 8] = gen.nonce64();
48    let nonce64: &[u8] = &nonce64[0..8];
49    unsafe { std::ptr::copy(&(nonce64)[0], nonce_ptr, 8) }
50}
51
52#[no_mangle]
53pub extern fn uuid_gen_uuid128(gen_ptr: *mut UUIDGen, uuid_ptr: *mut u8) {
54    let gen = unsafe {
55        assert!(!gen_ptr.is_null());
56        &mut *gen_ptr
57    };
58    let uuid128: [u8; 16] = gen.uuid128();
59    let uuid128: &[u8] = &uuid128[0..16];
60    unsafe { std::ptr::copy(&(uuid128)[0], uuid_ptr, 16) }
61}
62
63//------------------------------------------------------------------------------
64
65#[derive(Debug)]
66pub struct UUIDGen(MacAddress);
67
68impl UUIDGen {
69
70    pub fn new(eui: Eui48) -> UUIDGen {
71        UUIDGen(MacAddress::new(eui))
72    }
73
74    // Generates a 64-bit nonce. This should not be used as a UUID.
75    pub fn nonce64(&self) -> [u8; 8] {
76        let nanosec_bytes = nanosecs_since_epoch56();
77
78        let mut rng = OsRng::new()
79            .expect("Failed to initialise RNG.");
80        let r = rng.gen::<[u8; 1]>();
81
82        let mut bytes = [0; 8];
83        bytes[0] = nanosec_bytes[0];
84        bytes[1] = nanosec_bytes[1];
85        bytes[2] = nanosec_bytes[2];
86        bytes[3] = nanosec_bytes[3];
87        bytes[4] = nanosec_bytes[4];
88        bytes[5] = nanosec_bytes[5];
89        bytes[6] = nanosec_bytes[6];
90        bytes[7] = r[0];
91        bytes
92    }
93
94    // A variant of the v1 UUID (128-bit).
95    pub fn uuid128(&self) -> [u8; 16] {
96        let nanosec_bytes = nanosecs_since_epoch();
97
98        let mut rng = OsRng::new()
99            .expect("Failed to initialise RNG.");
100        let r = rng.gen::<[u8; 2]>();
101
102        let mac_bytes = self.0.as_bytes();
103        let mut bytes = [0; 16];
104        bytes[0] = nanosec_bytes[0];
105        bytes[1] = nanosec_bytes[1];
106        bytes[2] = nanosec_bytes[2];
107        bytes[3] = nanosec_bytes[3];
108        bytes[4] = nanosec_bytes[4];
109        bytes[5] = nanosec_bytes[5];
110        bytes[6] = nanosec_bytes[6];
111        bytes[7] = nanosec_bytes[7];
112        bytes[8] = mac_bytes[0];
113        bytes[9] = mac_bytes[1];
114        bytes[10] = mac_bytes[2];
115        bytes[11] = mac_bytes[3];
116        bytes[12] = mac_bytes[4];
117        bytes[13] = mac_bytes[5];
118        bytes[14] = r[0];
119        bytes[15] = r[1];
120        bytes
121    }
122}
123
124//------------------------------------------------------------------------------
125// Internal
126//------------------------------------------------------------------------------
127
128fn epoch_min() -> SystemTime {
129    let secs_years_47: u64 = 60 * 60 * 24 * 365 * 47;
130    UNIX_EPOCH.add(Duration::new(secs_years_47, 0))
131}
132
133// Takes the inverse of the little_endian function on a 64-bit integer
134// in order to obtain 7 bytes.
135fn little_endian_inv56(x: u64) -> [u8; 7] {
136    let mut bytes: [u8; 7] = [0; 7];
137    bytes[0] = (x & 0xFF) as u8;
138    for i in 1..7 {
139        bytes[i] = ((x & (0xFF << i * 8)) >> i * 8) as u8;
140    }
141    bytes
142}
143
144// Takes the inverse of the little_endian function on a 64-bit integer
145// in order to obtain 8 bytes.
146fn little_endian_inv64(x: u64) -> [u8; 8] {
147    let mut bytes: [u8; 8] = [0; 8];
148    bytes[0] = (x & 0xFF) as u8;
149    for i in 1..8 {
150        bytes[i] = ((x & (0xFF << i * 8)) >> i * 8) as u8;
151    }
152    bytes
153}
154
155// 100-nanosecond intervals since 2017 as 56-bits.
156fn nanosecs_since_epoch56() -> [u8; 7] {
157    let now = SystemTime::now();
158    let duration = now.duration_since(epoch_min())
159        .expect("Failed to get duration since unix epoch.");
160
161    // seconds to 100-nanosecond interval bytes
162    let secs: u64 = duration.as_secs() as u64 * 1_000_000_0;
163    let nanosecs: u64 = secs + (duration.subsec_nanos() / 1_00) as u64;
164
165    little_endian_inv56(nanosecs)
166}
167
168// 100-nanosecond intervals since 2017 as 64-bits.
169fn nanosecs_since_epoch() -> [u8; 8] {
170    let now = SystemTime::now();
171    let duration = now.duration_since(epoch_min())
172        .expect("Failed to get duration since unix epoch.");
173
174    // seconds to 100-nanosecond interval bytes
175    let secs: u64 = duration.as_secs() as u64 * 1_000_000_0;
176    let nanosecs: u64 = secs + (duration.subsec_nanos() / 1_00) as u64;
177
178    little_endian_inv64(nanosecs)
179}
180
181// Linux only interface Eui48 read.
182pub fn read_interface_eui(iface: &str) -> Eui48 {
183    let path = Path::new("/sys/class/net").join(Path::new(iface)).join("address");
184
185    let f = File::open(path)
186        .expect("Network interface not found.");
187
188    let mut reader = BufReader::new(f);
189    let mut line = String::new();
190
191    reader.read_line(&mut line)
192        .expect("Unable to read iface.");
193
194    let mut eui: Eui48 = [0; 6];
195    for i in 0..6 {
196        let byte: String = line.drain(0..2).collect();
197        line.drain(0..1);
198        let byte_v = byte.from_hex()
199            .expect(format!("Failed to decode mac_address byte {}", i).as_str());
200        eui[i] = byte_v[0];
201    }
202
203    eui
204}
205
206//------------------------------------------------------------------------------
207// Tests
208//------------------------------------------------------------------------------
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use std::collections::HashMap;
214    use rustc_serialize::hex::ToHex;
215
216    #[test]
217    fn uuid_gen() {
218
219        let eui = read_interface_eui("new0");
220        let gen = UUIDGen::new(eui);
221
222        // birthday paradox B ^ (1/2 * n) of 64 bits => 2 ^ 32
223        let mut entries = HashMap::new();
224
225        for i in 1..4_294_967_296 as u64 {
226            let key = gen.nonce64().to_hex();
227            if !entries.contains_key(&key) {
228                entries.insert(key, ());
229            } else {
230                println!("Found collision at {} where key = {}", i, key);
231            }
232        }
233    }
234}