matc/
certmanager.rs

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
//! Certificate manager trait and default file based implementation

use std::sync::Arc;

use anyhow::{Context, Result};

use crate::{cert_x509, util::cryptoutil};

pub trait CertManager: Send + Sync {
    fn get_ca_cert(&self) -> Result<Vec<u8>>;
    fn get_ca_key(&self) -> Result<p256::SecretKey>;
    fn get_ca_public_key(&self) -> Result<Vec<u8>>;
    fn get_user_cert(&self, id: u64) -> Result<Vec<u8>>;
    fn get_user_key(&self, id: u64) -> Result<p256::SecretKey>;
    fn get_fabric_id(&self) -> u64;
}

/// Example implementation of [CertManager] trait.
/// It stores keys and certificates in PEM files in specified directory.
pub struct FileCertManager {
    fabric_id: u64,
    path: String,
}

impl FileCertManager {
    pub fn new(fabric_id: u64, path: &str) -> Arc<Self> {
        Arc::new(Self {
            fabric_id,
            path: path.to_owned(),
        })
    }
    pub fn load(path: &str) -> Result<Arc<Self>> {
        let fname = format!("{}/metadata.pem", path);
        let fabric_str = std::fs::read_to_string(&fname)
            .context(format!("can't read from {}", fname))?;
        let fabric_id = fabric_str.parse::<u64>()?;
        Ok(Arc::new(Self {
            fabric_id,
            path: path.to_owned(),
        }))
    }
    fn user_key_fname(&self, id: u64) -> String {
        format!("{}/{}-private.pem", self.path, id)
    }
    fn ca_key_fname(&self) -> String {
        format!("{}/ca-private.pem", self.path)
    }
    fn user_cert_fname(&self, id: u64) -> String {
        format!("{}/{}-cert.pem", self.path, id)
    }
    fn ca_cert_fname(&self) -> String {
        format!("{}/ca-cert.pem", self.path)
    }
    fn metadata_fname(&self) -> String {
        format!("{}/metadata.pem", self.path)
    }
}

const CA_NODE_ID: u64 = 1;

/*fn extract_fabric_id(fname: &str) -> Result<u64> {
    let x509_raw = cryptoutil::read_data_from_pem(fname)?;
    let x509 = x509_cert::Certificate::from_der(&x509_raw)?;
    let subject = x509.tbs_certificate.subject;
    for rdn in subject.0 {
        for av in rdn.0.as_slice() {
            if av.oid == const_oid::ObjectIdentifier::new_unwrap("1.3.6.1.4.1.37244.1.5") {
                let valstr = av.value.decode_as::<String>()?;
                return Ok(u64::from_str_radix(&valstr, 16)?)
            }
        }
    };
    Err(anyhow::anyhow!("can't extract fabric id"))
}*/

impl FileCertManager {
    /// Initialize CA. Create directory, generate CA key and certificate and store them in specified directory.
    /// Directory must not exist before calling this function. If it exists function will fail.
    pub fn bootstrap(&self) -> Result<()> {
        std::fs::create_dir(&self.path)?;

        let secret_key = p256::SecretKey::random(&mut rand::thread_rng());
        let data = cryptoutil::secret_key_to_rfc5915(&secret_key)?;
        let pem = pem::Pem::new("EC PRIVATE KEY", data);
        std::fs::write(self.ca_key_fname(), pem::encode(&pem).as_bytes())?;
        let node_public_key = secret_key.public_key().to_sec1_bytes();

        let x509 = cert_x509::encode_x509(
            &node_public_key,
            CA_NODE_ID,
            self.fabric_id,
            CA_NODE_ID,
            &secret_key,
            true,
        )?;
        cryptoutil::write_pem("CERTIFICATE", &x509, &self.ca_cert_fname())?;
        std::fs::write(self.metadata_fname(), format!("{}", self.fabric_id))?;
        Ok(())
    }

    /// Create key and certificate for specified node identifier.
    /// This can be used as credentials for admin(and any additional) user controlling devices.
    pub fn create_user(&self, id: u64) -> Result<()> {
        let ca_private = self.get_ca_key()?;
        let secret_key = p256::SecretKey::random(&mut rand::thread_rng());
        let data = cryptoutil::secret_key_to_rfc5915(&secret_key)?;
        let pem = pem::Pem::new("EC PRIVATE KEY", data);
        std::fs::write(self.user_key_fname(id), pem::encode(&pem).as_bytes())?;
        let node_public_key = secret_key.public_key().to_sec1_bytes();

        let x509 = cert_x509::encode_x509(
            &node_public_key,
            id,
            self.fabric_id,
            CA_NODE_ID,
            &ca_private,
            false,
        )?;
        cryptoutil::write_pem("CERTIFICATE", &x509, &self.user_cert_fname(id))?;
        Ok(())
    }
}

impl CertManager for FileCertManager {
    fn get_ca_cert(&self) -> Result<Vec<u8>> {
        cryptoutil::read_data_from_pem(&self.ca_cert_fname())
    }

    fn get_ca_key(&self) -> Result<p256::SecretKey> {
        cryptoutil::read_private_key_from_pem(&self.ca_key_fname())
    }

    fn get_user_cert(&self, id: u64) -> Result<Vec<u8>> {
        cryptoutil::read_data_from_pem(&self.user_cert_fname(id))
    }

    fn get_user_key(&self, id: u64) -> Result<p256::SecretKey> {
        cryptoutil::read_private_key_from_pem(&self.user_key_fname(id))
    }

    fn get_ca_public_key(&self) -> Result<Vec<u8>> {
        Ok(self.get_ca_key()?.public_key().to_sec1_bytes().to_vec())
    }

    fn get_fabric_id(&self) -> u64 {
        self.fabric_id
    }
}