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
// Copyright 2020 MaidSafe.net limited.
//
// This SAFE Network Software is licensed to you under the MIT license <LICENSE-MIT
// http://opensource.org/licenses/MIT> or the Modified BSD license <LICENSE-BSD
// https://opensource.org/licenses/BSD-3-Clause>, at your option. This file may not be copied,
// modified, or distributed except according to those terms. Please review the Licences for the
// specific language governing permissions and limitations relating to use of the SAFE Network
// Software.

use super::{constants::SN_AUTHD_CONNECTION_IDLE_TIMEOUT, Error, Result};
use log::info;
use qjsonrpc::ClientEndpoint;
use serde::de::DeserializeOwned;
use tokio::runtime;

pub mod auth_types {
    use crate::api::ipc::req::IpcReq;
    use serde::{Deserialize, Serialize};

    pub type SafeAuthReq = IpcReq;
    pub type SafeAuthReqId = u32;

    #[derive(Debug, Serialize, Deserialize)]
    pub struct AuthedApp {
        /// The App ID. It must be unique.
        pub id: String,
        /// The application friendly-name.
        pub name: String,
        /// The application provider/vendor (e.g. MaidSafe)
        pub vendor: String,
    }

    // Type of the list of authorised applications in a Safe
    pub type AuthedAppsList = Vec<AuthedApp>;
}

pub fn parse_hex(hex_str: &str) -> Vec<u8> {
    let mut hex_bytes = hex_str
        .as_bytes()
        .iter()
        .filter_map(|b| match b {
            b'0'..=b'9' => Some(b - b'0'),
            b'a'..=b'f' => Some(b - b'a' + 10),
            b'A'..=b'F' => Some(b - b'A' + 10),
            _ => None,
        })
        .fuse();

    let mut bytes = Vec::new();
    while let (Some(h), Some(l)) = (hex_bytes.next(), hex_bytes.next()) {
        bytes.push(h << 4 | l)
    }
    bytes
}

pub fn ed_sk_from_hex(hex_str: &str) -> Result<ed25519_dalek::SecretKey> {
    let sk_bytes = parse_hex(&hex_str);
    ed25519_dalek::SecretKey::from_bytes(&sk_bytes).map_err(|_| {
        Error::InvalidInput("Failed to deserialize provided Ed25519 secret key".to_string())
    })
}

// Get hex string of a SecretKey
pub fn sk_to_hex(sk: sn_data_types::SecretKey) -> String {
    match sk {
        sn_data_types::SecretKey::Ed25519(sk) => {
            sk.to_bytes().iter().map(|b| format!("{:02x}", b)).collect()
        }
        //SecretKey::Bls(sk) => sk.inner().reveal(), // FIXME: it includes bls in the text
        sn_data_types::SecretKey::BlsShare(sk) => sk.inner().reveal(), // FIXME: it includes bls in the text
    }
}

// Send a request to authd using JSON-RPC over QUIC
pub async fn send_authd_request<T>(
    dest_endpoint: &str,
    method: &str,
    params: serde_json::Value,
) -> Result<T>
where
    T: DeserializeOwned,
{
    info!(
        "Sending '{}' request to SAFE Authenticator on {} ...",
        method, dest_endpoint
    );

    match dirs_next::home_dir() {
        None => Err(Error::AuthdClientError(
            "Failed to obtain local project directory where to read certificate from".to_string(),
        )),
        Some(mut paths) => {
            paths.push(".safe");
            paths.push("authd");

            let cert_base_path = paths.display().to_string();

            let qjsonrpc_client = ClientEndpoint::new(
                &cert_base_path,
                Some(SN_AUTHD_CONNECTION_IDLE_TIMEOUT),
                false,
            )
            .map_err(|err| {
                Error::AuthdClientError(format!("Failed to create client endpoint: {}", err))
            })?;

            // We try to obtain current runtime or create a new one if there is none
            let runtime = match runtime::Handle::try_current() {
                Ok(r) => r,
                Err(_) => runtime::Runtime::new()
                    .map_err(|err| {
                        Error::AuthdClientError(format!("Failed to create runtime: {}", err))
                    })?
                    .handle()
                    .clone(),
            };

            let mut outgoing_conn = {
                let _ = runtime.enter();
                qjsonrpc_client.bind().map_err(|err| {
                    Error::AuthdClientError(format!("Failed to bind endpoint: {}", err))
                })?
            };

            // Establish a new connection
            outgoing_conn
                .connect(dest_endpoint, None)
                .await
                .map_err(|err| {
                    Error::AuthdClientError(format!(
                        "Failed to establish connection with authd: {}",
                        err
                    ))
                })?
                // Send request and await for response
                .send(method, params)
                .await
                .map_err(|err| match err {
                    qjsonrpc::Error::RemoteEndpointError(msg) => Error::AuthdError(msg),
                    other => Error::AuthdClientError(other.to_string()),
                })
        }
    }
}