mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
131 lines
4.5 KiB
Rust
131 lines
4.5 KiB
Rust
use std::collections::BTreeMap;
|
|
use std::time::SystemTime;
|
|
|
|
use imbl_value::InternedString;
|
|
use openssl::pkey::{PKey, Private};
|
|
use openssl::x509::X509;
|
|
|
|
use crate::db::model::DatabaseModel;
|
|
use crate::hostname::{Hostname, generate_hostname, generate_id};
|
|
use crate::net::ssl::{gen_nistp256, make_root_cert};
|
|
use crate::prelude::*;
|
|
use crate::util::serde::Pem;
|
|
|
|
fn hash_password(password: &str) -> Result<String, Error> {
|
|
argon2::hash_encoded(
|
|
password.as_bytes(),
|
|
&rand::random::<[u8; 16]>()[..],
|
|
&argon2::Config::rfc9106_low_mem(),
|
|
)
|
|
.with_kind(crate::ErrorKind::PasswordHashGeneration)
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct AccountInfo {
|
|
pub server_id: String,
|
|
pub hostname: Hostname,
|
|
pub password: String,
|
|
pub root_ca_key: PKey<Private>,
|
|
pub root_ca_cert: X509,
|
|
pub ssh_key: ssh_key::PrivateKey,
|
|
pub developer_key: ed25519_dalek::SigningKey,
|
|
}
|
|
impl AccountInfo {
|
|
pub fn new(
|
|
password: &str,
|
|
start_time: SystemTime,
|
|
hostname: Option<InternedString>,
|
|
) -> Result<Self, Error> {
|
|
let server_id = generate_id();
|
|
let hostname = if let Some(h) = hostname {
|
|
Hostname::validate(h)?
|
|
} else {
|
|
generate_hostname()
|
|
};
|
|
let root_ca_key = gen_nistp256()?;
|
|
let root_ca_cert = make_root_cert(&root_ca_key, &hostname, start_time)?;
|
|
let ssh_key = ssh_key::PrivateKey::from(ssh_key::private::Ed25519Keypair::random(
|
|
&mut ssh_key::rand_core::OsRng::default(),
|
|
));
|
|
let developer_key =
|
|
ed25519_dalek::SigningKey::generate(&mut ssh_key::rand_core::OsRng::default());
|
|
Ok(Self {
|
|
server_id,
|
|
hostname,
|
|
password: hash_password(password)?,
|
|
root_ca_key,
|
|
root_ca_cert,
|
|
ssh_key,
|
|
developer_key,
|
|
})
|
|
}
|
|
|
|
pub fn load(db: &DatabaseModel) -> Result<Self, Error> {
|
|
let server_id = db.as_public().as_server_info().as_id().de()?;
|
|
let hostname = Hostname(db.as_public().as_server_info().as_hostname().de()?);
|
|
let password = db.as_private().as_password().de()?;
|
|
let key_store = db.as_private().as_key_store();
|
|
let cert_store = key_store.as_local_certs();
|
|
let root_ca_key = cert_store.as_root_key().de()?.0;
|
|
let root_ca_cert = cert_store.as_root_cert().de()?.0;
|
|
let ssh_key = db.as_private().as_ssh_privkey().de()?.0;
|
|
let compat_s9pk_key = db.as_private().as_developer_key().de()?.0;
|
|
|
|
Ok(Self {
|
|
server_id,
|
|
hostname,
|
|
password,
|
|
root_ca_key,
|
|
root_ca_cert,
|
|
ssh_key,
|
|
developer_key: compat_s9pk_key,
|
|
})
|
|
}
|
|
|
|
pub fn save(&self, db: &mut DatabaseModel) -> Result<(), Error> {
|
|
let server_info = db.as_public_mut().as_server_info_mut();
|
|
server_info.as_id_mut().ser(&self.server_id)?;
|
|
server_info.as_hostname_mut().ser(&self.hostname.0)?;
|
|
server_info
|
|
.as_pubkey_mut()
|
|
.ser(&self.ssh_key.public_key().to_openssh()?)?;
|
|
server_info.as_password_hash_mut().ser(&self.password)?;
|
|
db.as_private_mut().as_password_mut().ser(&self.password)?;
|
|
db.as_private_mut()
|
|
.as_ssh_privkey_mut()
|
|
.ser(Pem::new_ref(&self.ssh_key))?;
|
|
db.as_private_mut()
|
|
.as_developer_key_mut()
|
|
.ser(Pem::new_ref(&self.developer_key))?;
|
|
let key_store = db.as_private_mut().as_key_store_mut();
|
|
let cert_store = key_store.as_local_certs_mut();
|
|
if cert_store.as_root_cert().de()?.0 != self.root_ca_cert {
|
|
cert_store
|
|
.as_root_key_mut()
|
|
.ser(Pem::new_ref(&self.root_ca_key))?;
|
|
cert_store
|
|
.as_root_cert_mut()
|
|
.ser(Pem::new_ref(&self.root_ca_cert))?;
|
|
let int_key = crate::net::ssl::gen_nistp256()?;
|
|
let int_cert =
|
|
crate::net::ssl::make_int_cert((&self.root_ca_key, &self.root_ca_cert), &int_key)?;
|
|
cert_store.as_int_key_mut().ser(&Pem(int_key))?;
|
|
cert_store.as_int_cert_mut().ser(&Pem(int_cert))?;
|
|
cert_store.as_leaves_mut().ser(&BTreeMap::new())?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn set_password(&mut self, password: &str) -> Result<(), Error> {
|
|
self.password = hash_password(password)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn hostnames(&self) -> impl IntoIterator<Item = InternedString> + Send + '_ {
|
|
[
|
|
self.hostname.no_dot_host_name(),
|
|
self.hostname.local_domain_name(),
|
|
]
|
|
}
|
|
}
|