diff --git a/Cargo.toml b/Cargo.toml index 4720bd31..c2fe4d5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,9 @@ features = ["gnome_47"] package = "rusqlite" version = "0.32.1" +[dependencies.openssl] +version = "0.10.68" + [patch.crates-io] #ggemini = { path = "ggemini" } #ggemtext = { path = "ggemtext" } diff --git a/src/profile/identity/gemini/certificate.rs b/src/profile/identity/gemini/certificate.rs new file mode 100644 index 00000000..2868fff6 --- /dev/null +++ b/src/profile/identity/gemini/certificate.rs @@ -0,0 +1,61 @@ +use gtk::glib::DateTime; +use openssl::{ + asn1::{Asn1Integer, Asn1Time}, + bn::{BigNum, MsbOption}, + hash::MessageDigest, + pkey::PKey, + rsa::Rsa, + x509::{X509Builder, X509Name}, +}; +use std::error::Error; + +// Defaults + +pub const VERSION: i32 = 0; // 1 https://docs.openssl.org/master/man3/X509_get_version +pub const BITS: u32 = 2048; + +/// Generate new Gemini certificate +/// * return PEM string as `Result` +pub fn generate( + time: (DateTime, DateTime), // valid (from, to) + name: &str, +) -> Result> { + // Generate new RSA key pair + let rsa = Rsa::generate(BITS)?; + let key = PKey::from_rsa(rsa)?; + + // Init X509 name + let mut name_builder = X509Name::builder()?; + name_builder.append_entry_by_text("CN", name)?; + let name = name_builder.build(); + + // Init serial number + let mut number = BigNum::new()?; + number.rand(128, MsbOption::MAYBE_ZERO, true)?; + let serial = Asn1Integer::from_bn(&number)?; + + // Init validity period + let not_before = Asn1Time::from_unix(time.0.to_unix())?; + let not_after = Asn1Time::from_unix(time.1.to_unix())?; + + // Build + let mut builder = X509Builder::new()?; + + builder.set_version(VERSION)?; + builder.set_serial_number(&serial)?; + builder.set_subject_name(&name)?; + builder.set_issuer_name(&name)?; + builder.set_not_before(¬_before)?; + builder.set_not_after(¬_after)?; + builder.set_pubkey(&key)?; + builder.sign(&key, MessageDigest::sha256())?; + + let certificate = builder.build(); + + // Result + Ok(format!( + "{}{}", + String::from_utf8(certificate.to_pem()?)?, + String::from_utf8(key.private_key_to_pem_pkcs8()?)?, + )) +}