1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, ErrorKind};
use std::path::Path;

use app_dirs::*;
use libp2p::identity::Keypair;

use crate::crypto::ec::{PrivateKey, PublicKey};
use crate::graph::persist::*;
use log::*;
use ring::{self, rand};

mod graphwalker;
pub use self::graphwalker::*;

const APP_INFO: AppInfo = AppInfo {
    name: "glycos",
    author: "glycos",
};

#[derive(Debug)]
pub enum KeyLoadError {
    AppDirsError,
    CorruptFileError,
    CryptographicError,
    IoError(ErrorKind),
}

impl From<AppDirsError> for KeyLoadError {
    fn from(_: AppDirsError) -> KeyLoadError {
        KeyLoadError::AppDirsError
    }
}

impl From<io::Error> for KeyLoadError {
    fn from(e: io::Error) -> KeyLoadError {
        KeyLoadError::IoError(e.kind())
    }
}

impl From<ring::error::Unspecified> for KeyLoadError {
    fn from(_: ring::error::Unspecified) -> KeyLoadError {
        KeyLoadError::CryptographicError
    }
}

pub fn load_key<P: AsRef<Path>>(path: P, create: bool) -> Result<PrivateKey, KeyLoadError> {
    let f = File::open(&path);
    let sk = match f {
        Ok(mut f) => {
            debug!("Key found on disk -- loading");
            let mut sk = [0u8; 32];
            let len = f.read(&mut sk)?;
            if len != 32 {
                return Err(KeyLoadError::CorruptFileError);
            }
            PrivateKey::from_bytes(sk)
        }
        Err(ref e) if create && e.kind() == ErrorKind::NotFound => {
            debug!("Key not found on disk -- creating");
            // File does not exist, but we are allowed to create it.
            let mut f = File::create(path)?;
            let rng = rand::SystemRandom::new();
            let sk = PrivateKey::generate(&rng)?;

            f.write_all(&sk.bytes())?;

            sk
        }
        Err(e) => return Err(e.into()),
    };

    Ok(sk)
}

/// The peer key is the key used for the DHT
pub fn load_default_peer_key_pair(create: bool) -> Result<Keypair, KeyLoadError> {
    let config_path = app_root(AppDataType::UserConfig, &APP_INFO)?;
    let path = config_path.join("peer.key");
    match File::open(&path) {
        Ok(mut f) => {
            let mut buf = Vec::new();
            f.read_to_end(&mut buf)?;
            Ok(
                Keypair::from_protobuf_encoding(&buf)
                    .map_err(|_| KeyLoadError::CorruptFileError)?,
            )
        }
        Err(e) if create && e.kind() == ErrorKind::NotFound => {
            let keypair = Keypair::generate_ed25519();
            let mut f = File::create(path)?;
            f.write_all(
                &keypair
                    .to_protobuf_encoding()
                    .map_err(|_| KeyLoadError::CryptographicError)?,
            )?;
            Ok(keypair)
        }
        Err(e) => Err(e.into()),
    }
}

/// The profile key is the secret key of the users profile
pub fn load_default_profile_key(create: bool) -> Result<PrivateKey, KeyLoadError> {
    let config_path = app_root(AppDataType::UserConfig, &APP_INFO)?;
    load_key(config_path.join("profile.key"), create)
}

pub fn read_default_profile() -> Result<Option<PublicKey>, KeyLoadError> {
    let config_path = app_root(AppDataType::UserConfig, &APP_INFO)?;
    let path = config_path.join("profile-identity.key");
    let f = File::open(&path);
    match f {
        Ok(mut f) => {
            debug!("Profile found on disk -- loading");
            let mut sk = [0u8; 32];
            let len = f.read(&mut sk)?;
            if len != 32 {
                return Err(KeyLoadError::CorruptFileError);
            }
            Ok(Some(PublicKey::from_bytes(&sk)?))
        }
        Err(ref e) if e.kind() == ErrorKind::NotFound => Ok(None),
        Err(e) => Err(e.into()),
    }
}

// XXX: Maybe rename KeyLoadError to CoreKeyError?
pub fn write_default_profile(profile: &PublicKey) -> Result<(), KeyLoadError> {
    let config_path = app_root(AppDataType::UserConfig, &APP_INFO)?;
    let path = config_path.join("profile-identity.key");
    let mut f = File::create(path)?;
    f.write_all(&profile.bytes())?;
    Ok(())
}

#[derive(Debug)]
pub enum GraphLoadError {
    PersistError(PersistError),
    AppDirsError(AppDirsError),
}

impl From<PersistError> for GraphLoadError {
    fn from(e: PersistError) -> GraphLoadError {
        GraphLoadError::PersistError(e)
    }
}

impl From<AppDirsError> for GraphLoadError {
    fn from(e: AppDirsError) -> GraphLoadError {
        GraphLoadError::AppDirsError(e)
    }
}

pub fn load_default_graph() -> Result<PersistentGraph, GraphLoadError> {
    let cache_path = app_dir(AppDataType::UserCache, &APP_INFO, "graph")?;
    let cache_path = cache_path.join("graph");
    let config = sled::Config::new()
        .path(cache_path)
        .cache_capacity(2_000_000_000)
        .use_compression(true)
        .flush_every_ms(Some(1000));
    Ok(PersistentGraph::open(config)?)
}