ed25519_hd_key/
lib.rs

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}