Skip to content

Commit

Permalink
Merge pull request #36 from sonndinh/download-certs-rust
Browse files Browse the repository at this point in the history
Add a security docs downloader using Rust
  • Loading branch information
sonndinh authored Jul 31, 2023
2 parents 0be2b9a + cf44107 commit 929074f
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 11 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,17 @@ jobs:
cd opendds-smart-lock
./smart-lock android build-toolchain-x86
./smart-lock android compile
build_certs_downloader:

runs-on: ubuntu-22.04

steps:
- name: checkout opendds-smart-lock
uses: actions/checkout@v3
with:
path: opendds-smart-lock
- name: compile rust certs downloader
run: |
cd opendds-smart-lock/src/certs_downloader
cargo build --release
11 changes: 11 additions & 0 deletions src/certs_downloader/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "certs_downloader"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
reqwest = { version = "0.11.18", features = ["blocking", "cookies"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
151 changes: 151 additions & 0 deletions src/certs_downloader/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use reqwest::blocking::Client;
use serde::{Deserialize, Serialize};
use std::fs;
use std::io::Write;

pub struct Config {
api_url: String,
username: String,
password: String,
nonce: String,
directory: String,
part_subdir: Option<String>,
id_ca_subdir: Option<String>,
perm_ca_subdir: Option<String>,
}

impl Config {
pub fn build(args: &Vec<String>) -> Result<Config, String> {
if args.len() < 6 {
let usage_msg = format!("Usage: cargo run <url> <username> <password> <nonce> <dir> [part_dir] [id_ca_dir] [perm_ca_dir]\n\
With:\n\
{0: <15} API URL of the DDS Permission Manager (DPM)\n\
{1: <15} Username of the application in DPM\n\
{2: <15} Password of the application in DPM\n\
{3: <15} A nonce string\n\
{4: <15} Top level directory to store the docs\n\
{5: <15} Subdirectory to store docs specific to this participant (optional)\n\
{6: <15} Subdirectory to store identity CA doc (optional)\n\
{7: <15} Subdirectory to store permissions CA doc (optional)",
"<url>", "<username>", "<password>", "<nonce>", "<dir>", "[part_dir]", "[id_ca_dir]", "[perm_ca_dir]");
return Err(usage_msg);
}
let part_subdir_arg = if args.len() >= 7 {
Some(args[6].clone())
} else {
None
};
let id_ca_subdir_arg = if args.len() >= 8 {
Some(args[7].clone())
} else {
None
};
let perm_ca_subdir_arg = if args.len() >= 9 {
Some(args[8].clone())
} else {
None
};
Ok(Config {
api_url: args[1].clone(),
username: args[2].clone(),
password: args[3].clone(),
nonce: args[4].clone(),
directory: args[5].clone(),
part_subdir: part_subdir_arg,
id_ca_subdir: id_ca_subdir_arg,
perm_ca_subdir: perm_ca_subdir_arg,
})
}
}

#[derive(Serialize, Deserialize)]
struct KeyPair {
private: String,
public: String,
}

pub fn download_certs(config: &Config) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::builder().cookie_store(true).build().unwrap();
let login_url = format!("{}/login", config.api_url);
let credential = format!(
"{{\"username\":\"{}\", \"password\":\"{}\"}}",
config.username, config.password
);
let _auth_resp = client
.post(login_url)
.header(reqwest::header::CONTENT_TYPE, "application/json")
.body(credential)
.send();

let base_url = format!("{}/applications", config.api_url);
let id_ca_dir = format!(
"{}/{}",
config.directory,
if config.id_ca_subdir.is_some() {
config.id_ca_subdir.clone().unwrap()
} else {
String::from("")
}
);
let perm_ca_dir = format!(
"{}/{}",
config.directory,
if config.perm_ca_subdir.is_some() {
config.perm_ca_subdir.clone().unwrap()
} else {
String::from("")
}
);
let part_dir = format!(
"{}/{}",
config.directory,
if config.part_subdir.is_some() {
config.part_subdir.clone().unwrap()
} else {
String::from("")
}
);
download_file(&client, &base_url, "identity_ca.pem", None, &id_ca_dir)?;
download_file(&client, &base_url, "permissions_ca.pem", None, &perm_ca_dir)?;
download_file(&client, &base_url, "governance.xml.p7s", None, &config.directory)?;
download_file(&client, &base_url, "permissions.xml.p7s", Some(&config.nonce), &part_dir)?;

let kp_str = get_body(&client, &base_url, "key_pair", Some(&config.nonce))?;
let kp: KeyPair = serde_json::from_str(&kp_str)?;
let public_file = format!("{}/identity.pem", part_dir);
let private_file = format!("{}/identity_key.pem", part_dir);
fs::File::create(public_file)?.write_all(kp.public.as_bytes())?;
fs::File::create(private_file)?.write_all(kp.private.as_bytes())?;
Ok(())
}

fn download_file(
client: &Client,
base_url: &str,
filename: &str,
nonce: Option<&str>,
directory: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let body = get_body(client, base_url, filename, nonce)?;
let path = format!("{}/{}", directory, filename);
fs::create_dir_all(directory)?;
let mut file = fs::File::create(path)?;
file.write_all(body.as_bytes())?;
Ok(())
}

fn get_body(
client: &Client,
base_url: &str,
filename: &str,
nonce: Option<&str>,
) -> Result<String, Box<dyn std::error::Error>> {
let url;
if nonce.is_some() {
url = format!("{}/{}?nonce={}", base_url, filename, nonce.unwrap());
} else {
url = format!("{}/{}", base_url, filename);
}
let body = client.get(&url).send()?.text()?;
Ok(body)
}
20 changes: 20 additions & 0 deletions src/certs_downloader/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use std::env;
use std::process;

use certs_downloader::Config;
use certs_downloader::download_certs;

fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::build(&args).unwrap_or_else(|err| {
eprintln!("{err}");
process::exit(1);
});

match download_certs(&config) {
Ok(_) => (),
Err(err) => {
eprintln!("download_certs failed: {:?}", err);
}
}
}
2 changes: 1 addition & 1 deletion src/smartlock.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[smartlock]
topic_prefix = "C.53."
domain_id = 1
username = "54"
username = "47"
api_url = "https://dpm.unityfoundation.io/api"
55 changes: 45 additions & 10 deletions src/smartlock.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,25 @@ smartlock_ini=${BASE_PATH}/smartlock/smartlock.ini
SECURITY=${SMARTLOCK_SECURE:-0}
CMD=start
LOCK=
CURL_CERTS_DOWNLOADER=0
while (( $# > 0 )); do
case "$1" in
--security)
SECURITY=1
shift
;;
-h|--help)
echo "Usage: smartlock.sh [--security] [--lock LOCK_ID] start | stop | restart | start-system"
echo "Usage: smartlock.sh [--security] [--lock LOCK_ID] [--curl-certs-downloader] start | stop | restart | start-system"
exit
;;
--lock)
LOCK="$2"
shift 2
;;
--curl-certs-downloader)
CURL_CERTS_DOWNLOADER=1
shift
;;
start)
CMD=start
shift
Expand Down Expand Up @@ -83,28 +88,59 @@ fi

echo "CMD: '$CMD', SECURITY: '$SECURITY', LOCK_ID: '$LOCK', SECURITY_ARGS: '$SECURITY_ARGS'"

function update_certs {
APP_PASSWORD=$(cat ${BASE_PATH}/dpm_password)
APP_NONCE=${LOCK}
API_URL=$(grep api_url ${smartlock_ini} | sed 's/api_url *= *"//; s/".*//')
USERNAME=$(grep username ${smartlock_ini} | sed 's/username *= *"//; s/".*//')
function update_certs_curl {
API_URL=$1
USERNAME=$2
PASSWORD=$3
NONCE=$4

mkdir -p ${cert_dir}/id_ca ${cert_dir}/${LOCK} ${cert_dir}/perm_ca

curl -c cookies.txt -H'Content-Type: application/json' -d"{\"username\":\"${USERNAME}\",\"password\":\"$APP_PASSWORD\"}" ${API_URL}/login
curl -c cookies.txt -H'Content-Type: application/json' -d"{\"username\":\"${USERNAME}\",\"password\":\"${PASSWORD}\"}" ${API_URL}/login

curl --silent -b cookies.txt "${API_URL}/applications/identity_ca.pem" > ${ID_CA}
curl --silent -b cookies.txt "${API_URL}/applications/permissions_ca.pem" > ${PERM_CA}
curl --silent -b cookies.txt "${API_URL}/applications/governance.xml.p7s" > ${PERM_GOV}
curl --silent -b cookies.txt "${API_URL}/applications/key_pair?nonce=${APP_NONCE}" > key-pair
curl --silent -b cookies.txt "${API_URL}/applications/permissions.xml.p7s?nonce=${APP_NONCE}" > ${PERM_PERMS}
curl --silent -b cookies.txt "${API_URL}/applications/key_pair?nonce=${NONCE}" > key-pair
curl --silent -b cookies.txt "${API_URL}/applications/permissions.xml.p7s?nonce=${NONCE}" > ${PERM_PERMS}

jq -r '.public' key-pair > ${ID_CERT}
jq -r '.private' key-pair > ${ID_PKEY}

rm -f cookies.txt key-pair
}

function update_certs_rust {
API_URL=$1
USERNAME=$2
PASSWORD=$3
NONCE=$4

if ! command -v rustc &> /dev/null
then
echo "ERROR: Rust is not installed! Install it and try again or use the --curl-certs-downloader option."
exit 1
fi

cd certs_downloader
cargo build --release
cargo run $API_URL $USERNAME $PASSWORD $NONCE $cert_dir $LOCK id_ca perm_ca
cd ..
}

function update_certs {
API_URL=$(grep api_url ${smartlock_ini} | sed 's/api_url *= *"//; s/".*//')
USERNAME=$(grep username ${smartlock_ini} | sed 's/username *= *"//; s/".*//')
PASSWORD=$(cat ${BASE_PATH}/dpm_password)
NONCE=${LOCK}

if (( $CURL_CERTS_DOWNLOADER )); then
update_certs_curl $API_URL $USERNAME $PASSWORD $NONCE
else
update_certs_rust $API_URL $USERNAME $PASSWORD $NONCE
fi
}

PID_FILE=${BASE_PATH}/smartlock.pid
start() {
if (( $SECURITY )); then
Expand Down Expand Up @@ -162,4 +198,3 @@ case "$CMD" in
exit 1
;;
esac

0 comments on commit 929074f

Please sign in to comment.