Skip to content

Commit

Permalink
integrate generated keys with profiles
Browse files Browse the repository at this point in the history
  • Loading branch information
aprxi committed Aug 12, 2024
1 parent 94c65a2 commit 52e1c2b
Show file tree
Hide file tree
Showing 6 changed files with 460 additions and 223 deletions.
9 changes: 4 additions & 5 deletions lumni/src/apps/builtin/llm/prompt/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,11 @@ pub async fn run_cli(
env.get_config_dir().expect("Config directory not defined");
let sqlite_file = config_dir.join("chat.db");

// TODO: None uses default ssh key path, add support for custom ssh key path
let encryption_handler =
EncryptionHandler::new_from_path(None)?.map(Arc::new);
// TODO: add support for custom encryption keys in conversations
//let encryption_handler =
// EncryptionHandler::new_from_path(custom_key_path)?.map(Arc::new);

let db_conn =
Arc::new(ConversationDatabase::new(&sqlite_file, encryption_handler)?);
let db_conn = Arc::new(ConversationDatabase::new(&sqlite_file, None)?);

let mut profile_handler = db_conn.get_profile_handler(None);
if let Some(ref matches) = matches {
Expand Down
105 changes: 52 additions & 53 deletions lumni/src/apps/builtin/llm/prompt/src/chat/db/encryption/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,58 +40,49 @@ impl EncryptionHandler {
}

pub fn new_from_path(
private_key_path: Option<&PathBuf>,
private_key_path: &PathBuf,
) -> Result<Option<Self>, ApplicationError> {
match private_key_path {
Some(path) => {
if !path.exists() {
return Err(ApplicationError::NotFound(format!(
"Private key file not found: {:?}",
path
)));
}

let private_key_pem = fs::read_to_string(path)
.map_err(|e| ApplicationError::IOError(e))?;

let private_key = if Self::is_encrypted_key(&private_key_pem) {
Self::parse_encrypted_private_key(
path.to_str().unwrap(),
&private_key_pem,
)?
} else {
Self::parse_private_key(path.to_str().ok_or_else(
|| {
ApplicationError::InvalidInput(
"Invalid path".to_string(),
)
},
)?)?
};

let public_key = RsaPublicKey::from(&private_key);
let public_key_pem = public_key
.to_public_key_pem(LineEnding::LF)
.map_err(|e| EncryptionError::Other(Box::new(e)))?;
let private_key_pem = private_key
.to_pkcs8_pem(LineEnding::LF)
.map_err(|e| EncryptionError::Pkcs8Error(e))?;

Ok(Some(Self::new(&public_key_pem, &private_key_pem)?))
}
None => {
if let Some(home_dir) = dirs::home_dir() {
let default_path = home_dir.join(".ssh").join("id_rsa");
if default_path.exists() {
Self::new_from_path(Some(&default_path))
} else {
Ok(None)
}
} else {
Ok(None)
}
}
if !private_key_path.exists() {
return Err(ApplicationError::NotFound(format!(
"Private key file not found: {:?}",
private_key_path
)));
}

let private_key_pem = fs::read_to_string(private_key_path)
.map_err(|e| ApplicationError::IOError(e))?;

let private_key = if Self::is_encrypted_key(&private_key_pem) {
Self::parse_encrypted_private_key(
private_key_path.to_str().unwrap(),
&private_key_pem,
)?
} else {
Self::parse_private_key(private_key_path.to_str().ok_or_else(
|| ApplicationError::InvalidInput("Invalid path".to_string()),
)?)?
};

let public_key = RsaPublicKey::from(&private_key);
let public_key_pem = public_key
.to_public_key_pem(LineEnding::LF)
.map_err(|e| EncryptionError::Other(Box::new(e)))?;
let private_key_pem = private_key
.to_pkcs8_pem(LineEnding::LF)
.map_err(|e| EncryptionError::Pkcs8Error(e))?;

Ok(Some(Self::new(&public_key_pem, &private_key_pem)?))
}

pub fn get_private_key_pem(&self) -> Result<String, ApplicationError> {
self.private_key
.to_pkcs8_pem(LineEnding::LF)
.map(|pem| pem.to_string())
.map_err(|e| {
ApplicationError::EncryptionError(EncryptionError::Pkcs8Error(
e.into(),
))
})
}

fn is_encrypted_key(key_pem: &str) -> bool {
Expand Down Expand Up @@ -393,16 +384,19 @@ impl EncryptionHandler {

impl EncryptionHandler {
pub fn generate_private_key(
key_path: PathBuf,
key_path: &PathBuf,
bits: usize,
password: Option<&str>,
) -> Result<(), ApplicationError> {
) -> Result<Self, ApplicationError> {
// Generate a new RSA private key
let private_key = RsaPrivateKey::new(&mut rsa::rand_core::OsRng, bits)
.map_err(|e| {
ApplicationError::EncryptionError(EncryptionError::RsaError(e))
})?;

// Derive the public key
let public_key = RsaPublicKey::from(&private_key);

// Convert private key to PEM format, with optional encryption
let private_key_pem = match password {
Some(pass) => private_key
Expand Down Expand Up @@ -438,6 +432,11 @@ impl EncryptionHandler {
if password.is_some() {
println!("The private key is password-protected.");
}
Ok(())

// Create and return a new EncryptionHandler instance
Ok(Self {
public_key,
private_key,
})
}
}
9 changes: 4 additions & 5 deletions lumni/src/apps/builtin/llm/prompt/src/chat/db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@ CREATE TABLE metadata (
CREATE TABLE user_profiles (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
ssh_key_hash TEXT, -- sha256 hash of the ssh private key
options TEXT NOT NULL, -- JSON string
is_default INTEGER DEFAULT 0
is_default INTEGER DEFAULT 0,
encryption_key_id INTEGER NOT NULL,
FOREIGN KEY (encryption_key_id) REFERENCES encryption_keys(id)
);

CREATE TABLE encryption_keys (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
file_path TEXT NOT NULL,
sha256_hash TEXT NOT NULL,
key_type TEXT NOT NULL
sha256_hash TEXT NOT NULL UNIQUE
);

CREATE TABLE models (
Expand Down
Loading

0 comments on commit 52e1c2b

Please sign in to comment.