1extern crate hmac;
2extern crate sha2;
3extern crate sodiumoxide;
4extern crate regex;
5
6use std::mem::transmute;
7use sha2::Sha512;
8use hmac::{Hmac, Mac};
9use sodiumoxide::crypto::sign;
10use regex::Regex;
11use std::convert::TryInto;
12
13const ED25519_CURVE: &'static str = "ed25519 seed";
14pub const HARDENED_OFFSET: u32 = 0x80000000;
15
16
17pub fn get_master_key(seed: &[u8]) -> ([u8; 32], [u8; 32]) {
18 let mut hmac = Hmac::<Sha512>::new_from_slice(ED25519_CURVE.as_bytes()).unwrap();
19 hmac.update(seed);
20 let i = hmac.finalize().into_bytes();
21 let mut il = [0u8; 32];
22 let mut ir = [0u8; 32];
23 il.copy_from_slice(&i[0..32]);
24 ir.copy_from_slice(&i[32..64]);
25
26 (il, ir)
27}
28
29pub fn get_public_key(private_key: &[u8; 32]) -> [u8; 32] {
30 let seed = sign::Seed::from_slice(private_key).unwrap();
31 let (sign_pk, _) = sign::keypair_from_seed(&seed);
32
33 let mut public_key = [0u8; 32];
34 public_key[0..32].copy_from_slice(&sign_pk[..]);
35 public_key.try_into().unwrap()
36}
37
38pub fn derive(key: &[u8; 32], chain_code: &[u8; 32], index: u32) -> ([u8; 32], [u8; 32]) {
39 let index_buffer: [u8; 4] = unsafe { transmute(index.to_be()) };
40 let mut data = [0u8; 37];
41
42 if index & HARDENED_OFFSET != 0 {
43 data[1..33].copy_from_slice(key);
44 } else {
45 let pkey = get_public_key(&key);
46 data[0..32].copy_from_slice(&pkey);
47 };
48 data[33..37].copy_from_slice(&index_buffer);
49
50 let mut hmac = Hmac::<Sha512>::new_from_slice(&chain_code[..]).unwrap();
51 hmac.update(&data);
52 let i = hmac.finalize().into_bytes();
53 let il = i[0..32].try_into().unwrap();
54 let ir = i[32..].try_into().unwrap();
55
56 (il, ir)
57}
58
59pub fn is_valid_path(path: &str) -> bool {
60 if !Regex::new(r"^m(/[0-9]+')+$").unwrap().is_match(path) {
61 return false
62 }
63
64 let segments: Vec<&str> = path.split('/').collect();
65 segments.iter()
66 .skip(1)
67 .map(|s| s.replace("'", ""))
68 .all(|s| s.parse::<u32>().is_ok())
69}
70
71pub fn derive_from_path(path: &str, seed: &[u8]) -> ([u8; 32], [u8; 32]) {
72 if !is_valid_path(path) {
73 panic!("Invalid derivation path {:?}", path);
74 }
75
76 let (mut private_key, mut chain_code) = get_master_key(&seed);
77 let segments: Vec<&str> = path.split('/').collect();
78 let segments = segments.iter()
79 .skip(1)
80 .map(|s| s.replace("'", ""))
81 .map(|s| s.parse::<u32>().unwrap())
82 .collect::<Vec<_>>();
83
84 for segment in segments {
85 let(dprivate_key, dchain_code) = derive(&private_key, &chain_code, segment + HARDENED_OFFSET);
86 private_key = dprivate_key;
87 chain_code = dchain_code;
88 }
89
90 (private_key, chain_code)
91}