1use std::sync::Arc;
4
5use anyhow::{Context, Result};
6use serde::{Deserialize, Serialize};
7
8use crate::{cert_x509, util::cryptoutil};
9
10pub trait CertManager: Send + Sync {
11 fn get_ca_cert(&self) -> Result<Vec<u8>>;
12 fn get_ca_key(&self) -> Result<p256::SecretKey>;
13 fn get_ca_public_key(&self) -> Result<Vec<u8>>;
14 fn get_user_cert(&self, id: u64) -> Result<Vec<u8>>;
15 fn get_user_key(&self, id: u64) -> Result<p256::SecretKey>;
16 fn get_fabric_id(&self) -> u64;
17 fn get_ipk_epoch_key(&self) -> Vec<u8>;
18}
19
20pub struct FileCertManager {
23 fabric_id: u64,
24 ipk_epoch_key: Vec<u8>,
25 path: String,
26}
27
28#[derive(Serialize, Deserialize)]
29struct Metadata {
30 fabric_id: String,
31 ipk_epoch_key: String,
32}
33
34impl FileCertManager {
35 pub fn new(fabric_id: u64, path: &str) -> Arc<Self> {
36 let ipk_epoch_key: [u8; 16] = rand::random();
37 Arc::new(Self {
38 fabric_id,
39 ipk_epoch_key: ipk_epoch_key.to_vec(),
40 path: path.to_owned(),
41 })
42 }
43
44 pub fn load(path: &str) -> Result<Arc<Self>> {
50 let json_path = format!("{}/metadata.json", path);
51 let pem_path = format!("{}/metadata.pem", path);
52
53 let (fabric_id, ipk_epoch_key) = if std::path::Path::new(&json_path).exists() {
54 let s = std::fs::read_to_string(&json_path)
55 .context(format!("can't read from {}", json_path))?;
56 let m: Metadata = serde_json::from_str(&s)
57 .context(format!("invalid JSON in {}", json_path))?;
58 let fid = m.fabric_id.parse::<u64>()
59 .context("invalid fabric_id in metadata.json")?;
60 let ipk = hex::decode(&m.ipk_epoch_key)
61 .context("invalid ipk_epoch_key hex in metadata.json")?;
62 (fid, ipk)
63 } else {
64 let s = std::fs::read_to_string(&pem_path)
65 .context(format!("can't read from {}", pem_path))?;
66 let fid = s.trim().parse::<u64>()?;
67 (fid, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf])
68 };
69
70 Ok(Arc::new(Self {
71 fabric_id,
72 ipk_epoch_key,
73 path: path.to_owned(),
74 }))
75 }
76
77 fn user_key_fname(&self, id: u64) -> String {
78 format!("{}/{}-private.pem", self.path, id)
79 }
80 fn ca_key_fname(&self) -> String {
81 format!("{}/ca-private.pem", self.path)
82 }
83 fn user_cert_fname(&self, id: u64) -> String {
84 format!("{}/{}-cert.pem", self.path, id)
85 }
86 fn ca_cert_fname(&self) -> String {
87 format!("{}/ca-cert.pem", self.path)
88 }
89 fn metadata_json_fname(&self) -> String {
90 format!("{}/metadata.json", self.path)
91 }
92}
93
94const CA_NODE_ID: u64 = 1;
95
96impl FileCertManager {
112 pub fn bootstrap(&self) -> Result<()> {
116 std::fs::create_dir(&self.path)?;
117
118 let secret_key = p256::SecretKey::random(&mut rand::thread_rng());
119 let data = cryptoutil::secret_key_to_rfc5915(&secret_key)?;
120 let pem = pem::Pem::new("EC PRIVATE KEY", data);
121 std::fs::write(self.ca_key_fname(), pem::encode(&pem).as_bytes())?;
122 let node_public_key = secret_key.public_key().to_sec1_bytes();
123
124 let x509 = cert_x509::encode_x509(
125 &node_public_key,
126 CA_NODE_ID,
127 self.fabric_id,
128 CA_NODE_ID,
129 &secret_key,
130 true,
131 )?;
132 cryptoutil::write_pem("CERTIFICATE", &x509, &self.ca_cert_fname())?;
133 let metadata = Metadata {
134 fabric_id: format!("{}", self.fabric_id),
135 ipk_epoch_key: hex::encode(&self.ipk_epoch_key),
136 };
137 std::fs::write(
138 self.metadata_json_fname(),
139 serde_json::to_string_pretty(&metadata)?,
140 )?;
141 Ok(())
142 }
143
144 pub fn create_user(&self, id: u64) -> Result<()> {
147 let ca_private = self.get_ca_key()?;
148 let secret_key = p256::SecretKey::random(&mut rand::thread_rng());
149 let data = cryptoutil::secret_key_to_rfc5915(&secret_key)?;
150 let pem = pem::Pem::new("EC PRIVATE KEY", data);
151 std::fs::write(self.user_key_fname(id), pem::encode(&pem).as_bytes())?;
152 let node_public_key = secret_key.public_key().to_sec1_bytes();
153
154 let x509 = cert_x509::encode_x509(
155 &node_public_key,
156 id,
157 self.fabric_id,
158 CA_NODE_ID,
159 &ca_private,
160 false,
161 )?;
162 cryptoutil::write_pem("CERTIFICATE", &x509, &self.user_cert_fname(id))?;
163 Ok(())
164 }
165}
166
167impl CertManager for FileCertManager {
168 fn get_ca_cert(&self) -> Result<Vec<u8>> {
169 cryptoutil::read_data_from_pem(&self.ca_cert_fname())
170 }
171
172 fn get_ca_key(&self) -> Result<p256::SecretKey> {
173 cryptoutil::read_private_key_from_pem(&self.ca_key_fname())
174 }
175
176 fn get_user_cert(&self, id: u64) -> Result<Vec<u8>> {
177 cryptoutil::read_data_from_pem(&self.user_cert_fname(id))
178 }
179
180 fn get_user_key(&self, id: u64) -> Result<p256::SecretKey> {
181 cryptoutil::read_private_key_from_pem(&self.user_key_fname(id))
182 }
183
184 fn get_ca_public_key(&self) -> Result<Vec<u8>> {
185 Ok(self.get_ca_key()?.public_key().to_sec1_bytes().to_vec())
186 }
187
188 fn get_fabric_id(&self) -> u64 {
189 self.fabric_id
190 }
191
192 fn get_ipk_epoch_key(&self) -> Vec<u8> {
193 self.ipk_epoch_key.clone()
194 }
195}