소스 검색

darkirc: Perform plaintext padding on encryption

parazyd 2 년 전
부모
커밋
54fdfc0cbe
3개의 변경된 파일56개의 추가작업 그리고 20개의 파일을 삭제
  1. 3 3
      bin/darkirc/src/crypto/saltbox.rs
  2. 12 2
      bin/darkirc/src/irc/command.rs
  3. 41 15
      bin/darkirc/src/irc/server.rs

+ 3 - 3
bin/darkirc/src/crypto/saltbox.rs

@@ -45,18 +45,18 @@ pub fn encrypt(salt_box: &ChaChaBox, plaintext: &[u8]) -> String {
 }
 
 /// Attempt to decrypt given ciphertext using the given `ChaChaBox`.
-/// Returns a lossy utf-8 string on success, and `None` on failure.
+/// Returns a `Vec<u8>` on success, and `None` on failure.
 ///
 /// The encryption format we're using with `ChaChaBox` is `nonce||ciphertext`,
 /// where nonce is 24 bytes large, and the remaining data should be the ciphertext.
-pub fn try_decrypt(salt_box: &ChaChaBox, ciphertext: &[u8]) -> Option<String> {
+pub fn try_decrypt(salt_box: &ChaChaBox, ciphertext: &[u8]) -> Option<Vec<u8>> {
     // Make sure we have enough bytes to work with
     if ciphertext.len() < 25 {
         return None
     }
 
     match salt_box.decrypt((&ciphertext[0..24]).into(), &ciphertext[24..]) {
-        Ok(v) => Some(String::from_utf8_lossy(&v).into()),
+        Ok(v) => Some(v),
         Err(_) => None,
     }
 }

+ 12 - 2
bin/darkirc/src/irc/command.rs

@@ -59,6 +59,7 @@ use log::{debug, error, info};
 use super::{
     client::{Client, ReplyType},
     rpl::*,
+    server::MAX_NICK_LEN,
     IrcChannel, SERVER_NAME,
 };
 
@@ -260,7 +261,7 @@ impl Client {
         let nick = self.nickname.read().await.to_string();
         let tokens = args.split_ascii_whitespace();
         for channel in tokens {
-            if !channel.starts_with('#') {
+            if !channel.starts_with('#') || channel.as_bytes().len() > MAX_NICK_LEN {
                 self.penalty.fetch_add(1, SeqCst);
                 return Ok(vec![ReplyType::Server((
                     ERR_NEEDMOREPARAMS,
@@ -288,7 +289,7 @@ impl Client {
             channels.remove(list.as_str());
 
             for channel in list.split(',') {
-                if !channel.starts_with('#') {
+                if !channel.starts_with('#') || channel.as_bytes().len() > MAX_NICK_LEN {
                     self.penalty.fetch_add(1, SeqCst);
                     return Ok(vec![ReplyType::Server((
                         ERR_NEEDMOREPARAMS,
@@ -525,6 +526,15 @@ impl Client {
             ))])
         }
 
+        // Disallow too long nicks
+        if nickname.as_bytes().len() > MAX_NICK_LEN {
+            self.penalty.fetch_add(1, SeqCst);
+            return Ok(vec![ReplyType::Server((
+                ERR_ERRONEOUSNICKNAME,
+                format!("{} {} :Nickname too long", old_nick, nickname),
+            ))])
+        }
+
         // Set the new nickname
         *self.nickname.write().await = nickname.to_string();
 

+ 41 - 15
bin/darkirc/src/irc/server.rs

@@ -42,6 +42,9 @@ use crate::{
     DarkIrc,
 };
 
+/// Max channel/nick length
+pub const MAX_NICK_LEN: usize = 24;
+
 /// IRC server instance
 pub struct IrcServer {
     /// DarkIrc instance
@@ -251,12 +254,26 @@ impl IrcServer {
         Ok(())
     }
 
+    fn pad(string: &str) -> Vec<u8> {
+        let mut bytes = string.as_bytes().to_vec();
+        bytes.resize(MAX_NICK_LEN, 0x00);
+        bytes
+    }
+
+    fn unpad(vec: &mut Vec<u8>) {
+        if let Some(i) = vec.iter().rposition(|x| *x != 0) {
+            let new_len = i + 1;
+            vec.truncate(new_len);
+        }
+    }
+
     /// Try encrypting a given `Privmsg` if there is such a channel/contact.
     pub async fn try_encrypt(&self, privmsg: &mut Privmsg) {
         if let Some((name, channel)) = self.channels.read().await.get_key_value(&privmsg.channel) {
             if let Some(saltbox) = &channel.saltbox {
-                privmsg.channel = saltbox::encrypt(saltbox, privmsg.channel.as_bytes());
-                privmsg.nick = saltbox::encrypt(saltbox, privmsg.nick.as_bytes());
+                // We will pad the name and nick to MAX_NICK_LEN so they all look the same.
+                privmsg.channel = saltbox::encrypt(saltbox, &Self::pad(&privmsg.channel));
+                privmsg.nick = saltbox::encrypt(saltbox, &Self::pad(&privmsg.nick));
                 privmsg.msg = saltbox::encrypt(saltbox, privmsg.msg.as_bytes());
                 debug!("Successfully encrypted message for {}", name);
                 return
@@ -265,8 +282,9 @@ impl IrcServer {
 
         if let Some((name, contact)) = self.contacts.read().await.get_key_value(&privmsg.channel) {
             if let Some(saltbox) = &contact.saltbox {
-                privmsg.channel = saltbox::encrypt(saltbox, privmsg.channel.as_bytes());
-                privmsg.nick = saltbox::encrypt(saltbox, privmsg.nick.as_bytes());
+                // We will pad the nicks to MAX_NICK_LEN so they all look the same.
+                privmsg.channel = saltbox::encrypt(saltbox, &Self::pad(&privmsg.channel));
+                privmsg.nick = saltbox::encrypt(saltbox, &Self::pad(&privmsg.nick));
                 privmsg.msg = saltbox::encrypt(saltbox, privmsg.msg.as_bytes());
                 debug!("Successfully encrypted message for {}", name);
             }
@@ -296,11 +314,12 @@ impl IrcServer {
         // (i.e. decrypted) privmsg, otherwise we return the original.
         for (name, channel) in self.channels.read().await.iter() {
             if let Some(saltbox) = &channel.saltbox {
-                let Some(channel_dec) = saltbox::try_decrypt(saltbox, &channel_ciphertext) else {
+                let Some(mut channel_dec) = saltbox::try_decrypt(saltbox, &channel_ciphertext)
+                else {
                     continue
                 };
 
-                let Some(nick_dec) = saltbox::try_decrypt(saltbox, &nick_ciphertext) else {
+                let Some(mut nick_dec) = saltbox::try_decrypt(saltbox, &nick_ciphertext) else {
                     continue
                 };
 
@@ -308,21 +327,25 @@ impl IrcServer {
                     continue
                 };
 
-                privmsg.channel = channel_dec;
-                privmsg.nick = nick_dec;
-                privmsg.msg = msg_dec;
-                debug!("Successfuly decrypted message for {}", name);
+                Self::unpad(&mut channel_dec);
+                Self::unpad(&mut nick_dec);
+
+                privmsg.channel = String::from_utf8_lossy(&channel_dec).into();
+                privmsg.nick = String::from_utf8_lossy(&nick_dec).into();
+                privmsg.msg = String::from_utf8_lossy(&msg_dec).into();
+                debug!("Successfully decrypted message for {}", name);
                 return
             }
         }
 
         for (name, contact) in self.contacts.read().await.iter() {
             if let Some(saltbox) = &contact.saltbox {
-                let Some(channel_dec) = saltbox::try_decrypt(saltbox, &channel_ciphertext) else {
+                let Some(mut channel_dec) = saltbox::try_decrypt(saltbox, &channel_ciphertext)
+                else {
                     continue
                 };
 
-                let Some(nick_dec) = saltbox::try_decrypt(saltbox, &nick_ciphertext) else {
+                let Some(mut nick_dec) = saltbox::try_decrypt(saltbox, &nick_ciphertext) else {
                     continue
                 };
 
@@ -330,9 +353,12 @@ impl IrcServer {
                     continue
                 };
 
-                privmsg.channel = channel_dec;
-                privmsg.nick = nick_dec;
-                privmsg.msg = msg_dec;
+                Self::unpad(&mut channel_dec);
+                Self::unpad(&mut nick_dec);
+
+                privmsg.channel = String::from_utf8_lossy(&channel_dec).into();
+                privmsg.nick = String::from_utf8_lossy(&nick_dec).into();
+                privmsg.msg = String::from_utf8_lossy(&msg_dec).into();
                 debug!("Successfully decrypted message from {}", name);
                 return
             }