Replace openssl with RustCrypto

OpenSSL can be a pain to build at the best of times, so relying on a
pure Rust implementation of block ciphers makes life a little easier.
parent 5db256a7
......@@ -6,6 +6,37 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "aes"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"
dependencies = [
"aes-soft",
"aesni",
"cipher",
]
[[package]]
name = "aes-soft"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
dependencies = [
"cipher",
"opaque-debug",
]
[[package]]
name = "aesni"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
dependencies = [
"cipher",
"opaque-debug",
]
[[package]]
name = "aho-corasick"
version = "0.7.15"
......@@ -75,6 +106,22 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block-modes"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0"
dependencies = [
"block-padding",
"cipher",
]
[[package]]
name = "block-padding"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
[[package]]
name = "bumpalo"
version = "3.6.0"
......@@ -125,6 +172,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "cipher"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
dependencies = [
"generic-array",
]
[[package]]
name = "command_attr"
version = "0.3.3"
......@@ -1678,8 +1734,10 @@ checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
name = "ucc-discord-bot"
version = "0.1.0"
dependencies = [
"aes",
"async-trait",
"base64 0.13.0",
"block-modes",
"chrono",
"diesel",
"guard",
......@@ -1688,7 +1746,6 @@ dependencies = [
"lazy_static",
"ldap3",
"log",
"openssl",
"rand 0.8.3",
"rayon",
"regex",
......
......@@ -5,8 +5,10 @@ authors = ["tec <[email protected]>"]
edition = "2018"
[dependencies]
aes = "0.6"
async-trait = "0.1.42"
base64 = "0.13.0"
block-modes = "0.7"
chrono = "0.4.19"
diesel = { version = "1.4.5", features = ["sqlite"] }
guard = "0.5.0"
......@@ -15,7 +17,6 @@ indexmap = { version = "1.6.1", features = ["serde-1"] }
lazy_static = "1.4.0"
ldap3 = "0.9.1"
log = "0.4.11"
openssl = "0.10.32"
rand = "0.8.1"
rayon = "1.5.0"
regex = "1.4.3"
......
use aes::Aes128;
use base64;
use block_modes::block_padding::Pkcs7;
use block_modes::{BlockMode, Cbc};
use chrono::{prelude::Utc, DateTime};
use openssl::symm::{decrypt, encrypt, Cipher};
use rand::Rng;
use serenity::model::user::User;
use std::str;
type Aes128Cbc = Cbc<Aes128, Pkcs7>;
pub static TOKEN_LIFETIME: i64 = 300; // 5 minutes
lazy_static! {
static ref KEY: [u8; 32] = rand::thread_rng().gen::<[u8; 32]>();
static ref CIPHER: Cipher = Cipher::aes_256_cbc();
static ref IV: [u8; 16] = [0; 16];
static ref CIPHER: Aes128Cbc = Aes128Cbc::new_var(KEY.as_ref(), IV.as_ref()).unwrap();
}
fn text_encrypt(plaintext: &str) -> String {
let iv: &[u8; 16] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let encrypted_vec =
encrypt(*CIPHER, &*KEY, Some(iv), plaintext.as_bytes()).expect("encryption failed");
base64::encode(encrypted_vec.as_slice())
base64::encode(CIPHER.encrypt_vec(plaintext.as_bytes()))
}
fn text_decrypt(ciphertext: &str) -> Option<String> {
let iv: &[u8; 16] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
guard!(let Ok(cipher_vec) = base64::decode(ciphertext) else {
warn!("Unable to decode base64 text");
return None
});
guard!(let Ok(decrypted_vec) = decrypt(*CIPHER, &*KEY, Some(iv), &cipher_vec) else {
guard!(let Ok(decrypted_vec) = CIPHER.decrypt_vec(&cipher_vec) else {
warn!("Text decryption failed");
return None
});
......@@ -75,7 +76,7 @@ pub fn parse_token(discord_user: &User, encrypted_token: &str) -> Result<String,
let token_username = token_components[2];
if token_discord_user != discord_user.id.0.to_string() {
warn!("... attempt failed : DiscordID mismatch");
return Err(TokenError::DiscordIdMismatch)
return Err(TokenError::DiscordIdMismatch);
}
let time_delta_seconds = Utc::now().timestamp() - token_timestamp.timestamp();
if time_delta_seconds > TOKEN_LIFETIME {
......@@ -83,7 +84,7 @@ pub fn parse_token(discord_user: &User, encrypted_token: &str) -> Result<String,
"... attempt failed : token expired ({} seconds old)",
time_delta_seconds
);
return Err(TokenError::TokenExpired)
return Err(TokenError::TokenExpired);
}
info!(
"... verification successful (token {} seconds old)",
......
use async_trait::async_trait;
use rand::seq::SliceRandom;
use regex::Regex;
use serenity::{
model::{channel::Message, guild::Member, id::RoleId},
......@@ -89,9 +87,8 @@ impl Commands {
send_message!(
msg.channel_id,
&ctx.http,
RANDOM_SASS
.choose(&mut rand::thread_rng())
.expect("We couldn't get any sass")
RANDOM_SASS[0] //.choose(&mut rand::thread_rng())
//.expect("We couldn't get any sass")
);
return;
}
......@@ -161,7 +158,7 @@ impl Commands {
return // Err()
});
guard!(let Ok(mut discord_member) = serenity::model::id::GuildId(CONFIG.server_id)
.member(ctx.http.clone(), msg.author.id) else {
.member(ctx.http.clone(), msg.author.id).await else {
return // Err()
});
......@@ -177,7 +174,7 @@ impl Commands {
continue;
}
if discord_member.roles.contains(&RoleId::from(role))
&& discord_member.remove_role(&ctx.http, role).is_err()
&& discord_member.remove_role(&ctx.http, role).await.is_err()
{
return; // Err()
}
......@@ -186,7 +183,10 @@ impl Commands {
if !discord_member
.roles
.contains(&RoleId::from(registered_role))
&& discord_member.add_role(&ctx.http, registered_role).is_err()
&& discord_member
.add_role(&ctx.http, registered_role)
.await
.is_err()
{
return; // Err()
}
......@@ -197,41 +197,46 @@ impl Commands {
pub async fn verify(ctx: Context, msg: Message, token: &str) {
match parse_token(&msg.author, token) {
Ok(name) => {
e!(
"Unable to get member: {:?}",
serenity::model::id::GuildId(CONFIG.server_id)
.member(ctx.http.clone(), msg.author.id)
.map(|mut member| async {
let full_member = database::add_member(&msg.author.id.0, &name);
e!(
"Unable to remove role: {:?}",
member.remove_role(&ctx.http, CONFIG.unregistered_member_role)
);
guard!(let Some(member_role) = Commands::get_registered_role(name) else {
send_message!(msg.channel_id, ctx.http.clone(), "Couldn't find you in LDAP!");
return
});
e!(
"Unable to add role: {:?}",
member.add_role(&ctx.http, member_role)
);
e!(
"Unable to edit nickname: {:?}",
member.edit(&ctx.http, |m| {
m.nickname(member_nickname(&full_member));
m
})
);
let mut verification_message = MessageBuilder::new();
verification_message.push(format!("Great, {}! Verification was successful. To provide a friendly introduction to yourself, consider doing ", &full_member.username));
verification_message.push_mono(format!("{}set bio <info>", CONFIG.command_prefix));
send_message!(
msg.channel_id,
ctx.http.clone(),
verification_message.build()
);
})
);
if let Ok(mut member) = serenity::model::id::GuildId(CONFIG.server_id)
.member(ctx.http.clone(), msg.author.id)
.await
{
let full_member = database::add_member(&msg.author.id.0, &name);
e!(
"Unable to remove role: {:?}",
member
.remove_role(&ctx.http, CONFIG.unregistered_member_role)
.await
);
guard!(let Some(member_role) = Commands::get_registered_role(name) else {
send_message!(msg.channel_id, ctx.http.clone(), "Couldn't find you in LDAP!");
return
});
e!(
"Unable to add role: {:?}",
member.add_role(&ctx.http, member_role).await
);
e!(
"Unable to edit nickname: {:?}",
member
.edit(&ctx.http, |m| {
m.nickname(member_nickname(&full_member));
m
})
.await
);
let mut verification_message = MessageBuilder::new();
verification_message.push(format!("Great, {}! Verification was successful. To provide a friendly introduction to yourself, consider doing ", &full_member.username));
verification_message
.push_mono(format!("{}set bio <info>", CONFIG.command_prefix));
send_message!(
msg.channel_id,
ctx.http.clone(),
verification_message.build()
);
} else {
error!("Unable to get member: {:?}", name)
}
}
Err(reason) => send_message!(
msg.channel_id,
......@@ -282,15 +287,17 @@ impl Commands {
}
let member = possible_member.unwrap();
info!("Found matching profile, UCC username: {}", &member.username);
let user = match ctx.http.get_user(member.discord_id as _).await {
Ok(u) => u,
Err(e) => {
error!("Couldn't find matching Discord ID for username!");
return;
}
};
let result = msg.channel_id.send_message(&ctx.http, |m| {
m.embed(|embed| {
embed.colour(serenity::utils::Colour::LIGHTER_GREY);
embed.footer(|f| async {
let user = &ctx
.http
.get_user(member.discord_id.clone() as u64)
.await
.expect("We expected this user to exist... they didn't ;(");
embed.footer(|f| {
f.text(&user.name);
f.icon_url(
user.static_avatar_url()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment