Преглед на файлове

util: Add base32 encoding.

Luther Blissett преди 3 години
родител
ревизия
766c07c72c
променени са 6 файла, в които са добавени 117 реда и са изтрити 12 реда
  1. 0 7
      Cargo.lock
  2. 0 2
      Cargo.toml
  3. 2 3
      src/net/hosts.rs
  4. 110 0
      src/util/encoding/base32.rs
  5. 2 0
      src/util/encoding/mod.rs
  6. 3 0
      src/util/mod.rs

+ 0 - 7
Cargo.lock

@@ -332,12 +332,6 @@ dependencies = [
  "rustc-demangle",
 ]
 
-[[package]]
-name = "base32"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa"
-
 [[package]]
 name = "base64"
 version = "0.13.0"
@@ -1212,7 +1206,6 @@ dependencies = [
  "async-std",
  "async-trait",
  "async-tungstenite",
- "base32",
  "bincode",
  "blake2b_simd",
  "blake3",

+ 0 - 2
Cargo.toml

@@ -71,7 +71,6 @@ rustls-pemfile = {version = "1.0.1", optional = true}
 
 # Encoding
 bincode = {version = "2.0.0-rc.2", features = ["serde"], optional = true}
-base32 = {version = "0.4.0", optional = true}
 bs58 = {version = "0.4.0", optional = true}
 hex = {version = "0.4.3", optional = true}
 serde_json = {version = "1.0.85", optional = true}
@@ -210,7 +209,6 @@ dht = [
 ]
 
 net = [
-    "base32",
     "fxhash",
     "ed25519-compact",
     "fast-socks5",

+ 2 - 3
src/net/hosts.rs

@@ -8,6 +8,7 @@ use log::{debug, error, warn};
 use url::Url;
 
 use super::constants::{IP4_PRIV_RANGES, IP6_PRIV_RANGES, LOCALNET};
+use crate::util::encoding::base32;
 
 /// Pointer to hosts class.
 pub type HostsPtr = Arc<Hosts>;
@@ -275,7 +276,5 @@ fn is_valid_onion(onion: &str) -> bool {
         return false
     }
 
-    let alphabet = base32::Alphabet::RFC4648 { padding: false };
-
-    base32::decode(alphabet, onion).is_some()
+    base32::decode(&onion.to_uppercase()).is_some()
 }

+ 110 - 0
src/util/encoding/base32.rs

@@ -0,0 +1,110 @@
+//! Base32 encoding as specified by RFC4648
+//! Optional padding is the `=` character.
+// Taken from https://github.com/andreasots/base32
+use core::cmp::min;
+
+/// Standard Base32 alphabet.
+const ENCODE_STD: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+
+/// Encode a byte slice with the given base32 alphabet into a base32 string.
+pub fn encode(padding: bool, data: &[u8]) -> String {
+    let mut ret = Vec::with_capacity((data.len() + 3) / 4 * 5);
+
+    for chunk in data.chunks(5) {
+        let buf = {
+            let mut buf = [0u8; 5];
+            for (i, &b) in chunk.iter().enumerate() {
+                buf[i] = b;
+            }
+            buf
+        };
+
+        ret.push(ENCODE_STD[((buf[0] & 0xf8) >> 3) as usize]);
+        ret.push(ENCODE_STD[(((buf[0] & 0x07) << 2) | ((buf[1] & 0xc0) >> 6)) as usize]);
+        ret.push(ENCODE_STD[((buf[1] & 0x3e) >> 1) as usize]);
+        ret.push(ENCODE_STD[(((buf[1] & 0x01) << 4) | ((buf[2] & 0xf0) >> 4)) as usize]);
+        ret.push(ENCODE_STD[(((buf[2] & 0x0f) << 1) | (buf[3] >> 7)) as usize]);
+        ret.push(ENCODE_STD[((buf[3] & 0x7c) >> 2) as usize]);
+        ret.push(ENCODE_STD[(((buf[3] & 0x03) << 3) | ((buf[4] & 0xe0) >> 5)) as usize]);
+        ret.push(ENCODE_STD[(buf[4] & 0x1f) as usize]);
+    }
+
+    if data.len() % 5 != 0 {
+        let len = ret.len();
+        let num_extra = 8 - (data.len() % 5 * 8 + 4) / 5;
+        if padding {
+            for i in 1..num_extra + 1 {
+                ret[len - i] = b'=';
+            }
+        } else {
+            ret.truncate(len - num_extra);
+        }
+    }
+
+    String::from_utf8(ret).unwrap()
+}
+
+const STD_INV_ALPHABET: [i8; 43] = [
+    -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+    9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+];
+
+/// Tries to decode a base32 string into a byte vector. Returns `None` if
+/// something fails.
+pub fn decode(data: &str) -> Option<Vec<u8>> {
+    if !data.is_ascii() {
+        return None
+    }
+
+    let data = data.as_bytes();
+    let mut unpadded_data_len = data.len();
+
+    for i in 1..min(6, data.len()) + 1 {
+        if data[data.len() - i] != b'=' {
+            break
+        }
+        unpadded_data_len -= 1;
+    }
+
+    let output_length = unpadded_data_len * 5 / 8;
+    let mut ret = Vec::with_capacity((output_length + 4) / 5 * 5);
+
+    for chunk in data.chunks(8) {
+        let buf = {
+            let mut buf = [0u8; 8];
+            for (i, &c) in chunk.iter().enumerate() {
+                match STD_INV_ALPHABET.get(c.to_ascii_uppercase().wrapping_sub(b'0') as usize) {
+                    Some(&-1) | None => return None,
+                    Some(&value) => buf[i] = value as u8,
+                };
+            }
+
+            buf
+        };
+
+        ret.push((buf[0] << 3) | (buf[1] >> 2));
+        ret.push((buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4));
+        ret.push((buf[3] << 4) | (buf[4] >> 1));
+        ret.push((buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3));
+        ret.push((buf[6] << 5) | buf[7]);
+    }
+
+    ret.truncate(output_length);
+    Some(ret)
+}
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn base32_encoding_decoding() {
+        let s = b"b32Test"; // This should pad with 4 =
+        let encoded = super::encode(true, &s[..]);
+        assert_eq!(&encoded, "MIZTEVDFON2A====");
+        assert_eq!(super::decode(&encoded).unwrap(), s);
+
+        let s = b"b32Testoor"; // This shouldn't pad
+        let encoded = super::encode(true, &s[..]);
+        assert_eq!(&encoded, "MIZTEVDFON2G633S");
+        assert_eq!(super::decode(&encoded).unwrap(), s);
+    }
+}

+ 2 - 0
src/util/encoding/mod.rs

@@ -0,0 +1,2 @@
+/// Base32 encoding and decoding
+pub mod base32;

+ 3 - 0
src/util/mod.rs

@@ -9,6 +9,9 @@ pub mod cli;
 pub mod clock;
 pub use clock::{Clock, Ticks};
 
+/// Various encoding formats
+pub mod encoding;
+
 /// Filesystem utilities
 pub mod file;