Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move from ring et al to openssl #9

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,9 @@ path = "src/utility.rs"
dbus = "0.8.4"
dbus-crossroads = "0.2.1"
parsec-client = "0.11.0"
ring = { version = "0.16.15", features = ["std"] }
anyhow = "1.0.32"
rsa = "0.5.0"
pkcs1 = "0.2.4"
sha2 = "0.9.1"
openssl = "0.10"
hex = "0.4.0"
rand = "0.8"

[build-dependencies]
dbus-codegen = "0.5.0"
13 changes: 6 additions & 7 deletions src/agent/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,19 @@ impl dbus_parsec_control_server::ComGithubPuiterwijkDBusPARSECControl for Agent
}
Some(key_id) => key_id,
};
let (wrapkey_path, contents_path) =
self.get_secret_file_paths(&secret_type, secret_group, secret_name)?;
let (wrapkey_path, contents_path) = self
.get_secret_file_paths(&secret_type, secret_group, secret_name)
.map_err(|e| MethodErr::failed(&format!("{}", e)))?;

// Try to decrypt the secret to ensure we're not being passed nonsense
match self.decrypt_secret(
self.decrypt_secret(
&secret_type,
secret_group,
secret_name,
&wrapper_key,
&secret_value,
) {
Some(_) => {}
None => return Err(MethodErr::failed("Unable to decrypt secret")),
};
)
.map_err(|_| MethodErr::failed("Unable to decrypt secret"))?;

let err_failed = MethodErr::failed("Unable to write secret");

Expand Down
94 changes: 24 additions & 70 deletions src/agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,13 @@
use std::fs;
use std::path;

use anyhow::{bail, Context, Result};
use parsec_client::core::interface::operations::psa_algorithm::{AsymmetricEncryption, Hash};
use parsec_client::core::interface::operations::psa_key_attributes::{
Attributes, Lifetime, Policy, Type, UsageFlags,
};
use parsec_client::BasicClient;

use crate::utils;

use ring::aead::Aad;
use ring::aead::BoundKey;
use ring::aead::OpeningKey;
use ring::aead::UnboundKey;
use ring::aead::AES_256_GCM;

use dbus::tree::MethodErr;

mod control;
mod networkmanager;

Expand Down Expand Up @@ -108,9 +99,9 @@ impl Agent {
secret_type: &KeyType,
secret_group: &str,
secret_name: &str,
) -> Result<(path::PathBuf, path::PathBuf), MethodErr> {
) -> Result<(path::PathBuf, path::PathBuf)> {
if !is_valid_identifier(secret_group) || !is_valid_identifier(secret_name) {
Err(MethodErr::failed("Invalid secret identifiers"))
bail!("Invalid secret identifiers");
} else {
let file_base = format!(
"secret{}{}{}{}{}{}",
Expand Down Expand Up @@ -169,27 +160,14 @@ impl Agent {
secret_type: &KeyType,
secret_group: &str,
secret_name: &str,
) -> Option<Vec<u8>> {
let (wrapkey_path, contents_path) =
match self.get_secret_file_paths(secret_type, secret_group, secret_name) {
Ok(res) => res,
Err(_) => return None,
};
let wrapkey = match fs::read(&wrapkey_path) {
Ok(res) => res,
Err(err) => {
eprintln!("Error reading wrapkey {:?}: {}", wrapkey_path, err);
return None;
}
};
let contents = match fs::read(&contents_path) {
Ok(res) => res,
Err(err) => {
eprintln!("Error reading contents {:?}: {}", contents_path, err);
return None;
}
};
) -> Result<Vec<u8>> {
let (wrapkey_path, contents_path) = self
.get_secret_file_paths(secret_type, secret_group, secret_name)
.context("Error determining secret file path")?;
let wrapkey = fs::read(&wrapkey_path).context("Error reading wrapkey")?;
let contents = fs::read(&contents_path).context("Error reading secret contents")?;
self.decrypt_secret(secret_type, secret_group, secret_name, &wrapkey, &contents)
.context("Error decrypting secret")
}

fn decrypt_secret(
Expand All @@ -199,48 +177,24 @@ impl Agent {
secret_name: &str,
wrapkey: &[u8],
value: &[u8],
) -> Option<Vec<u8>> {
) -> Result<Vec<u8>> {
let key_name = self.key_name(secret_type, secret_group);
let asym_enc_algo = AsymmetricEncryption::RsaOaep {
hash_alg: Hash::Sha256,
};

let wrapkey =
match self
.parsec_client
.psa_asymmetric_decrypt(key_name, asym_enc_algo, wrapkey, None)
{
Ok(key) => key,
Err(err) => {
eprintln!("Error decrypting wrapper key: {}", err);
return None;
}
};

let aad = format!("{};{};{}", secret_type.to_type(), secret_group, secret_name);
let aad = Aad::from(aad);

let wrapkey = match UnboundKey::new(&AES_256_GCM, &wrapkey) {
Ok(key) => key,
Err(err) => {
eprintln!("Wrapkey invalid: {}", err);
return None;
}
};

let mut in_out = value.to_vec();

let mut wrapkey: OpeningKey<utils::CounterNonce> =
BoundKey::new(wrapkey, utils::CounterNonce::new());

let plaintext = match wrapkey.open_in_place(aad, &mut in_out) {
Ok(pt) => pt,
Err(err) => {
eprintln!("Error decrypting inner contents: {}", err);
return None;
}
};

Some(plaintext.to_vec())
let wrapkey = self
.parsec_client
.psa_asymmetric_decrypt(key_name, asym_enc_algo, wrapkey, None)
.context("Error decrypting wrapkey")?;

crate::crypto::decrypt_secret(
wrapkey,
&secret_type.to_type(),
secret_group,
secret_name,
value,
)
.context("Error decrypting secret")
}
}
13 changes: 10 additions & 3 deletions src/agent/networkmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,16 @@ impl<'a> NMConnectionInfo<'a> for &NMConnection<'a> {
self.get("vpn")?.get("service-type")?.0.as_str()?,
)),
"wireguard" => Some(NetworkManagerConnectionType::Wireguard),
"802-11-wireless" => match self.get("802-11-wireless-security")?.get("key-mgmt")?.0.as_str()? {
"802-11-wireless" => match self
.get("802-11-wireless-security")?
.get("key-mgmt")?
.0
.as_str()?
{
"wpa-psk" => Some(NetworkManagerConnectionType::Wireless(NMWirelessType::PSK)),
_ => Some(NetworkManagerConnectionType::Wireless(NMWirelessType::Enterprise)),
_ => Some(NetworkManagerConnectionType::Wireless(
NMWirelessType::Enterprise,
)),
},
_ => None,
}
Expand Down Expand Up @@ -201,7 +208,7 @@ impl nm_secretagent::OrgFreedesktopNetworkManagerSecretAgent for Agent {
self.retrieve_secret(&KeyType::NetworkManager, conn_name, secret_name),
)
})
.filter(|x| x.1.is_some())
.filter(|x| x.1.is_ok())
.map(|x| (x.0.to_string(), String::from_utf8(x.1.unwrap())))
.filter(|x| x.1.is_ok())
.map(|x| (x.0, str_refarg(x.1.unwrap())))
Expand Down
134 changes: 134 additions & 0 deletions src/crypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use anyhow::{Context, Result};
use openssl::{
rsa::RsaRef,
symm::{decrypt_aead, encrypt_aead, Cipher},
};

#[allow(unused)]
pub(crate) struct EncryptResult {
pub wrapped_wrapkey: Vec<u8>,
pub secret: Vec<u8>,
}

#[allow(unused)]
pub(crate) fn encrypt_secret<T>(
public_key: &RsaRef<T>,
secret_type: &str,
secret_group: &str,
secret_name: &str,
secret_value: Vec<u8>,
) -> Result<EncryptResult>
where
T: openssl::pkey::HasPublic,
{
// Generate a wrapper key
let mut wrapkey: [u8; 32] = [0; 32];
openssl::rand::rand_bytes(&mut wrapkey).context("Unable to generate random wrapper key")?;
let wrapkey = wrapkey;

// Encrypt the wrapper key
let mut wrapped_wrapkey = Vec::with_capacity(public_key.size() as usize);
wrapped_wrapkey.resize(public_key.size() as usize, 0);
public_key
.public_encrypt(
&wrapkey,
&mut wrapped_wrapkey,
openssl::rsa::Padding::PKCS1_OAEP,
)
.context("Unable to encrypt wrapper key")?;

// Encrypt the secret
let mut tag = vec![0; 16];
let mut nonce = [0; 12];
nonce[0] = 1;
let aad = format!("{};{};{}", &secret_type, &secret_group, &secret_name);
let mut secret = encrypt_aead(
Cipher::aes_256_gcm(),
&wrapkey,
Some(&nonce),
&aad.as_bytes(),
&secret_value,
&mut tag,
)
.context("Unable to encrypt secret")?;
secret.extend_from_slice(&tag);

Ok(EncryptResult {
wrapped_wrapkey,
secret,
})
}

#[allow(unused)]
pub(crate) fn decrypt_secret(
wrapkey: Vec<u8>,
secret_type: &str,
secret_group: &str,
secret_name: &str,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
let aad = format!("{};{};{}", secret_type, secret_group, secret_name);

let (value, tag) = ciphertext.split_at(ciphertext.len() - 16);
let mut nonce = [0; 12];
nonce[0] = 1;

decrypt_aead(
Cipher::aes_256_gcm(),
&wrapkey,
Some(&nonce),
aad.as_bytes(),
value,
&tag,
)
.context("Error decrypting secret")
.map(|res| res.to_vec())
}

#[cfg(test)]
mod test {
use openssl::{
rand::rand_bytes,
rsa::{Padding, Rsa},
};

#[test]
fn test_encrypt_decrypt() {
let privkey = Rsa::generate(2048).unwrap();
let mut test_contents = vec![0; 128];

// Generate random bytes to test with
rand_bytes(&mut test_contents).unwrap();

let encrypt_result = super::encrypt_secret(
&privkey,
"test_type",
"test_group",
"test_name",
test_contents.clone(),
)
.unwrap();

let mut decrypted_wrapkey = Vec::with_capacity(privkey.size() as usize);
decrypted_wrapkey.resize(privkey.size() as usize, 0);
let decrypted_wrapkey_size = privkey
.private_decrypt(
&encrypt_result.wrapped_wrapkey,
&mut decrypted_wrapkey,
Padding::PKCS1_OAEP,
)
.unwrap();
decrypted_wrapkey.truncate(decrypted_wrapkey_size);

let decrypted_contents = super::decrypt_secret(
decrypted_wrapkey,
"test_type",
"test_group",
"test_name",
&encrypt_result.secret,
)
.unwrap();

assert_eq!(test_contents, decrypted_contents);
}
}
3 changes: 1 addition & 2 deletions src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ use dbus::tree;
use dbus::tree::{Factory, Interface, MTFn};

use parsec_client::core::interface::requests::Opcode;
use parsec_client::core::secrecy::Secret;
use parsec_client::BasicClient;

mod agent;
mod utils;
mod crypto;

mod nm_secretagent {
include!(concat!(env!("OUT_DIR"), "/nm_secretagent.rs"));
Expand Down
Loading