Merge branch 'tor-netdoc/use-base64ct' into 'main'

Replace `base64` crate with `base64ct` crate in `tor-netdoc`

See merge request tpo/core/arti!600
This commit is contained in:
Ian Jackson 2022-06-23 18:52:14 +00:00
commit b1aab512ff
4 changed files with 83 additions and 14 deletions

10
Cargo.lock generated
View File

@ -510,9 +510,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "base64ct"
version = "1.1.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6b4d9b1225d28d360ec6a231d65af1fd99a2a095154c8040689617290569c5c"
checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
[[package]]
name = "bitflags"
@ -2289,9 +2289,9 @@ dependencies = [
[[package]]
name = "pem-rfc7468"
version = "0.2.4"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84e93a3b1cc0510b03020f33f21e62acdde3dcaef432edc95bea377fbd4c2cd4"
checksum = "8f22eb0e3c593294a99e9ff4b24cf6b752d43f193aa4415fe5077c159996d497"
dependencies = [
"base64ct",
]
@ -3822,7 +3822,7 @@ dependencies = [
name = "tor-netdoc"
version = "0.4.0"
dependencies = [
"base64",
"base64ct",
"bitflags",
"derive_more",
"digest 0.10.3",

View File

@ -45,7 +45,7 @@ experimental-api = []
dangerous-expose-struct-fields = ["visible", "visibility"]
[dependencies]
base64 = "0.13.0"
base64ct = { version = "1.5.0", features = ["alloc"] }
bitflags = "1"
derive_more = "0.99"
digest = "0.10.0"

View File

@ -8,6 +8,7 @@ use crate::parse::keyword::Keyword;
use crate::types::misc::FromBytes;
use crate::util::PauseAt;
use crate::{Error, ParseErrorKind as EK, Pos, Result};
use base64ct::{Base64, Encoding};
use std::cell::{Ref, RefCell};
use std::str::FromStr;
use tor_error::internal;
@ -290,12 +291,11 @@ impl<'a, K: Keyword> Iterator for NetDocReaderBase<'a, K> {
/// Helper: as base64::decode(), but allows newlines in the middle of the
/// encoded object.
fn base64_decode_multiline(s: &str) -> std::result::Result<Vec<u8>, base64::DecodeError> {
fn base64_decode_multiline(s: &str) -> std::result::Result<Vec<u8>, base64ct::Error> {
// base64 module hates whitespace.
let mut v = Vec::new();
let mut s = s.to_string();
s.retain(|ch| ch != '\n');
base64::decode_config_buf(s, base64::STANDARD, &mut v)?;
let v = Base64::decode_vec(&s)?;
Ok(v)
}

View File

@ -36,6 +36,7 @@ pub(crate) trait FromBytes: Sized {
/// Types for decoding base64-encoded values.
mod b64impl {
use crate::{Error, ParseErrorKind as EK, Pos, Result};
use base64ct::{Base64, Encoding};
use std::ops::RangeBounds;
/// A byte array, encoded in base64 with optional padding.
@ -44,12 +45,28 @@ mod b64impl {
impl std::str::FromStr for B64 {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let bytes = base64::decode_config(s, base64::STANDARD_NO_PAD).map_err(|_| {
// The `base64ct` crate only rejects invalid
// characters when the input is padded. Therefore,
// the input must be padded fist. For more info on
// this, take a look at this issue:
// `https://github.com/RustCrypto/utils/issues/576`
let mut string = s.to_string();
// Determine padding length
let offset = 4 - s.len() % 4;
match offset {
4 => (),
_ => {
// Add pad to input
string.push_str("=".repeat(offset).as_str());
}
}
let v = Base64::decode_vec(&string);
let v = v.map_err(|_| {
EK::BadArgument
.with_msg("Invalid base64")
.at_pos(Pos::at(s))
})?;
Ok(B64(bytes))
Ok(B64(v))
}
}
@ -455,27 +472,79 @@ mod nickname {
#[cfg(test)]
mod test {
#![allow(clippy::unwrap_used)]
use base64ct::Encoding;
use super::*;
use crate::{Pos, Result};
/// Decode s as a multi-line base64 string, ignoring ascii whitespace.
fn base64_decode_ignore_ws(s: &str) -> std::result::Result<Vec<u8>, base64::DecodeError> {
fn base64_decode_ignore_ws(s: &str) -> std::result::Result<Vec<u8>, base64ct::Error> {
let mut s = s.to_string();
s.retain(|c| !c.is_ascii_whitespace());
base64::decode(s)
base64ct::Base64::decode_vec(s.as_str())
}
#[test]
fn base64() -> Result<()> {
// Test parsing succeess:
// Unpadded:
assert_eq!("Mi43MTgyOA".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
assert!("Mi43MTgyOA".parse::<B64>()?.check_len(7..8).is_ok());
assert_eq!("Mg".parse::<B64>()?.as_bytes(), &b"2"[..]);
assert!("Mg".parse::<B64>()?.check_len(1..2).is_ok());
assert_eq!(
"8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
.parse::<B64>()?
.as_bytes(),
"🍒🍒🍒🍒🍒🍒".as_bytes()
);
assert!("8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
.parse::<B64>()?
.check_len(24..25)
.is_ok());
assert!("ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8="
.parse::<B64>()?
.check_len(32..33)
.is_ok());
// Padded:
assert_eq!("Mi43MTgyOA==".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
assert!("Mi43MTgyOA==".parse::<B64>()?.check_len(7..8).is_ok());
assert_eq!("Mg==".parse::<B64>()?.as_bytes(), &b"2"[..]);
assert!("Mg==".parse::<B64>()?.check_len(1..2).is_ok());
// Test parsing failures:
// Invalid character.
assert!("Mi43!!!!!!".parse::<B64>().is_err());
// Invalid last character.
assert!("Mi".parse::<B64>().is_err());
assert!("Mi43MTgyOA".parse::<B64>()?.check_len(7..=8).is_ok());
assert!("ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
.parse::<B64>()
.is_err());
// Invalid length.
assert!("Mi43MTgyOA".parse::<B64>()?.check_len(8..).is_err());
Ok(())
}
#[test]
fn base64_lengths() -> Result<()> {
assert_eq!("".parse::<B64>()?.as_bytes(), b"");
assert!("=".parse::<B64>().is_err());
assert!("==".parse::<B64>().is_err());
assert!("B".parse::<B64>().is_err());
assert!("B=".parse::<B64>().is_err());
assert!("B==".parse::<B64>().is_err());
assert_eq!("Bg".parse::<B64>()?.as_bytes(), b"\x06");
assert_eq!("Bg=".parse::<B64>()?.as_bytes(), b"\x06");
assert_eq!("Bg==".parse::<B64>()?.as_bytes(), b"\x06");
assert_eq!("BCg".parse::<B64>()?.as_bytes(), b"\x04\x28");
assert_eq!("BCg=".parse::<B64>()?.as_bytes(), b"\x04\x28");
assert!("BCg==".parse::<B64>().is_err());
assert_eq!("BCDE".parse::<B64>()?.as_bytes(), b"\x04\x20\xc4");
assert!("BCDE=".parse::<B64>().is_err());
assert!("BCDE==".parse::<B64>().is_err());
Ok(())
}
#[test]
fn base16() -> Result<()> {
assert_eq!("332e313432".parse::<B16>()?.as_bytes(), &b"3.142"[..]);