diff options
Diffstat (limited to 'vendor/ct-codecs')
-rw-r--r-- | vendor/ct-codecs/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/ct-codecs/Cargo.toml | 32 | ||||
-rw-r--r-- | vendor/ct-codecs/LICENSE | 21 | ||||
-rw-r--r-- | vendor/ct-codecs/README.md | 21 | ||||
-rw-r--r-- | vendor/ct-codecs/src/base64.rs | 378 | ||||
-rw-r--r-- | vendor/ct-codecs/src/error.rs | 21 | ||||
-rw-r--r-- | vendor/ct-codecs/src/hex.rs | 94 | ||||
-rw-r--r-- | vendor/ct-codecs/src/lib.rs | 63 |
8 files changed, 631 insertions, 0 deletions
diff --git a/vendor/ct-codecs/.cargo-checksum.json b/vendor/ct-codecs/.cargo-checksum.json new file mode 100644 index 000000000..bdc74a8f9 --- /dev/null +++ b/vendor/ct-codecs/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"57c95abc9fcbbc34a75f623667a0c40fdd8700995118549dc279ed721db7581a","LICENSE":"8d8b6ecdb215ff7c997bb24e9d8b19481b44ac9bcb479323ae6b9b3167e75f42","README.md":"7b76d3d05e9f96dec7fa776cd0fbf9e73ddeda814480c3037d3afb49f076cc36","src/base64.rs":"0142d1e8b78dfa75b028dfdb4644a4682ffab3a5569c979011f1267aa7de75b9","src/error.rs":"e2cddf2375045964303944998801538aa83c866416fd415aed45796448225762","src/hex.rs":"392afbd65106adff3f17e3d3c7541736dc0e5d1ac9bcdd0f5685f5e3c0057fb2","src/lib.rs":"265614015e8278c9522ff24d87e8fc564f13e15942e6e399c43a33d539bbeefc"},"package":"f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df"}
\ No newline at end of file diff --git a/vendor/ct-codecs/Cargo.toml b/vendor/ct-codecs/Cargo.toml new file mode 100644 index 000000000..ba29e04b4 --- /dev/null +++ b/vendor/ct-codecs/Cargo.toml @@ -0,0 +1,32 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "ct-codecs" +version = "1.1.1" +authors = ["Frank Denis <github@pureftpd.org>"] +description = "Constant-time hex and base64 codecs from libsodium reimplemented in Rust" +homepage = "https://github.com/jedisct1/rust-ct-codecs" +readme = "README.md" +keywords = ["base64", "hex", "crypto"] +categories = ["no-std", "cryptography", "encoding"] +license = "MIT" +repository = "https://github.com/jedisct1/rust-ct-codecs" +[profile.release] +codegen-units = 1 +panic = "abort" +incremental = false + +[features] +default = ["std"] +std = [] diff --git a/vendor/ct-codecs/LICENSE b/vendor/ct-codecs/LICENSE new file mode 100644 index 000000000..212477fbe --- /dev/null +++ b/vendor/ct-codecs/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2021 Frank Denis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/ct-codecs/README.md b/vendor/ct-codecs/README.md new file mode 100644 index 000000000..ccfe63588 --- /dev/null +++ b/vendor/ct-codecs/README.md @@ -0,0 +1,21 @@ +# CT-Codecs + +A reimplementation of the base64 and hexadecimal codecs from libsodium and libhydrogen in Rust. + +- Constant-time for a given length, suitable for cryptographic purposes +- Strict (base64 strings are not malleable) +- Supports padded and unpadded, original and URL-safe base64 variants +- Supports characters to be ignored by the decoder +- Zero dependencies, `no_std` friendly. + +## [API documentation](https://docs.rs/ct-codecs) + +## Example usage + +```rust +use ct_codecs::{Base64UrlSafe, Decoder, Encoder}; + +let encoded = Base64UrlSafe::encode_to_string(x)?; +let decoded = Base64UrlSafe::decode_to_vec(encoded, None)?; +``` + diff --git a/vendor/ct-codecs/src/base64.rs b/vendor/ct-codecs/src/base64.rs new file mode 100644 index 000000000..1e031de28 --- /dev/null +++ b/vendor/ct-codecs/src/base64.rs @@ -0,0 +1,378 @@ +use crate::error::*; +use crate::{Decoder, Encoder}; + +struct Base64Impl; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum Base64Variant { + Original = 1, + OriginalNoPadding = 3, + UrlSafe = 5, + UrlSafeNoPadding = 7, +} + +enum VariantMask { + NoPadding = 2, + UrlSafe = 4, +} + +impl Base64Impl { + #[inline] + fn _eq(x: u8, y: u8) -> u8 { + !(((0u16.wrapping_sub((x as u16) ^ (y as u16))) >> 8) as u8) + } + + #[inline] + fn _gt(x: u8, y: u8) -> u8 { + (((y as u16).wrapping_sub(x as u16)) >> 8) as u8 + } + + #[inline] + fn _ge(x: u8, y: u8) -> u8 { + !Self::_gt(y, x) + } + + #[inline] + fn _lt(x: u8, y: u8) -> u8 { + Self::_gt(y, x) + } + + #[inline] + fn _le(x: u8, y: u8) -> u8 { + Self::_ge(y, x) + } + + #[inline] + fn b64_byte_to_char(x: u8) -> u8 { + (Self::_lt(x, 26) & (x.wrapping_add(b'A'))) + | (Self::_ge(x, 26) & Self::_lt(x, 52) & (x.wrapping_add(b'a'.wrapping_sub(26)))) + | (Self::_ge(x, 52) & Self::_lt(x, 62) & (x.wrapping_add(b'0'.wrapping_sub(52)))) + | (Self::_eq(x, 62) & b'+') + | (Self::_eq(x, 63) & b'/') + } + + #[inline] + fn b64_char_to_byte(c: u8) -> u8 { + let x = (Self::_ge(c, b'A') & Self::_le(c, b'Z') & (c.wrapping_sub(b'A'))) + | (Self::_ge(c, b'a') & Self::_le(c, b'z') & (c.wrapping_sub(b'a'.wrapping_sub(26)))) + | (Self::_ge(c, b'0') & Self::_le(c, b'9') & (c.wrapping_sub(b'0'.wrapping_sub(52)))) + | (Self::_eq(c, b'+') & 62) + | (Self::_eq(c, b'/') & 63); + x | (Self::_eq(x, 0) & (Self::_eq(c, b'A') ^ 0xff)) + } + + #[inline] + fn b64_byte_to_urlsafe_char(x: u8) -> u8 { + (Self::_lt(x, 26) & (x.wrapping_add(b'A'))) + | (Self::_ge(x, 26) & Self::_lt(x, 52) & (x.wrapping_add(b'a'.wrapping_sub(26)))) + | (Self::_ge(x, 52) & Self::_lt(x, 62) & (x.wrapping_add(b'0'.wrapping_sub(52)))) + | (Self::_eq(x, 62) & b'-') + | (Self::_eq(x, 63) & b'_') + } + + #[inline] + fn b64_urlsafe_char_to_byte(c: u8) -> u8 { + let x = (Self::_ge(c, b'A') & Self::_le(c, b'Z') & (c.wrapping_sub(b'A'))) + | (Self::_ge(c, b'a') & Self::_le(c, b'z') & (c.wrapping_sub(b'a'.wrapping_sub(26)))) + | (Self::_ge(c, b'0') & Self::_le(c, b'9') & (c.wrapping_sub(b'0'.wrapping_sub(52)))) + | (Self::_eq(c, b'-') & 62) + | (Self::_eq(c, b'_') & 63); + x | (Self::_eq(x, 0) & (Self::_eq(c, b'A') ^ 0xff)) + } + + #[inline] + fn encoded_len(bin_len: usize, variant: Base64Variant) -> Result<usize, Error> { + let nibbles = bin_len / 3; + let rounded = nibbles * 3; + let pad = bin_len - rounded; + Ok(nibbles.checked_mul(4).ok_or(Error::Overflow)? + + ((pad | (pad >> 1)) & 1) + * (4 - (!((((variant as usize) & 2) >> 1).wrapping_sub(1)) & (3 - pad))) + + 1) + } + + pub fn encode<'t>( + b64: &'t mut [u8], + bin: &[u8], + variant: Base64Variant, + ) -> Result<&'t [u8], Error> { + let bin_len = bin.len(); + let b64_maxlen = b64.len(); + let mut acc_len = 0usize; + let mut b64_pos = 0usize; + let mut acc = 0u16; + + let nibbles = bin_len / 3; + let remainder = bin_len - 3 * nibbles; + let mut b64_len = nibbles * 4; + if remainder != 0 { + if (variant as u16 & VariantMask::NoPadding as u16) == 0 { + b64_len += 4; + } else { + b64_len += 2 + (remainder >> 1); + } + } + if b64_maxlen < b64_len { + return Err(Error::Overflow); + } + if (variant as u16 & VariantMask::UrlSafe as u16) != 0 { + for &v in bin { + acc = (acc << 8) + v as u16; + acc_len += 8; + while acc_len >= 6 { + acc_len -= 6; + b64[b64_pos] = Self::b64_byte_to_urlsafe_char(((acc >> acc_len) & 0x3f) as u8); + b64_pos += 1; + } + } + if acc_len > 0 { + b64[b64_pos] = + Self::b64_byte_to_urlsafe_char(((acc << (6 - acc_len)) & 0x3f) as u8); + b64_pos += 1; + } + } else { + for &v in bin { + acc = (acc << 8) + v as u16; + acc_len += 8; + while acc_len >= 6 { + acc_len -= 6; + b64[b64_pos] = Self::b64_byte_to_char(((acc >> acc_len) & 0x3f) as u8); + b64_pos += 1; + } + } + if acc_len > 0 { + b64[b64_pos] = Self::b64_byte_to_char(((acc << (6 - acc_len)) & 0x3f) as u8); + b64_pos += 1; + } + } + while b64_pos < b64_len { + b64[b64_pos] = b'='; + b64_pos += 1 + } + Ok(&b64[..b64_pos]) + } + + fn skip_padding<'t>( + b64: &'t [u8], + mut padding_len: usize, + ignore: Option<&[u8]>, + ) -> Result<&'t [u8], Error> { + let b64_len = b64.len(); + let mut b64_pos = 0usize; + while padding_len > 0 { + if b64_pos > b64_len { + return Err(Error::InvalidInput); + } + let c = b64[b64_pos]; + if c == b'=' { + padding_len -= 1 + } else { + match ignore { + Some(ignore) if ignore.contains(&c) => {} + _ => return Err(Error::InvalidInput), + } + } + b64_pos += 1 + } + Ok(&b64[b64_pos..]) + } + + pub fn decode<'t>( + bin: &'t mut [u8], + b64: &[u8], + ignore: Option<&[u8]>, + variant: Base64Variant, + ) -> Result<&'t [u8], Error> { + let bin_maxlen = bin.len(); + let is_urlsafe = (variant as u16 & VariantMask::UrlSafe as u16) != 0; + let mut acc = 0u16; + let mut acc_len = 0usize; + let mut bin_pos = 0usize; + let mut premature_end = None; + for (b64_pos, &c) in b64.iter().enumerate() { + let d = if is_urlsafe { + Self::b64_urlsafe_char_to_byte(c) + } else { + Self::b64_char_to_byte(c) + }; + if d == 0xff { + match ignore { + Some(ignore) if ignore.contains(&c) => continue, + _ => { + premature_end = Some(b64_pos); + break; + } + } + } + acc = (acc << 6) + d as u16; + acc_len += 6; + if acc_len >= 8 { + acc_len -= 8; + if bin_pos >= bin_maxlen { + return Err(Error::Overflow); + } + bin[bin_pos] = (acc >> acc_len) as u8; + bin_pos += 1; + } + } + if acc_len > 4 || (acc & ((1u16 << acc_len).wrapping_sub(1))) != 0 { + return Err(Error::InvalidInput); + } + let padding_len = acc_len / 2; + if let Some(premature_end) = premature_end { + let remaining = if variant as u16 & VariantMask::NoPadding as u16 == 0 { + Self::skip_padding(&b64[premature_end..], padding_len, ignore)? + } else { + &b64[premature_end..] + }; + match ignore { + None => { + if !remaining.is_empty() { + return Err(Error::InvalidInput); + } + } + Some(ignore) => { + for &c in remaining { + if !ignore.contains(&c) { + return Err(Error::InvalidInput); + } + } + } + } + } else if variant as u16 & VariantMask::NoPadding as u16 == 0 && padding_len != 0 { + return Err(Error::InvalidInput); + } + Ok(&bin[..bin_pos]) + } +} + +pub struct Base64; +pub struct Base64NoPadding; +pub struct Base64UrlSafe; +pub struct Base64UrlSafeNoPadding; + +impl Encoder for Base64 { + #[inline] + fn encoded_len(bin_len: usize) -> Result<usize, Error> { + Base64Impl::encoded_len(bin_len, Base64Variant::Original) + } + + #[inline] + fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> { + Base64Impl::encode(b64, bin.as_ref(), Base64Variant::Original) + } +} + +impl Decoder for Base64 { + #[inline] + fn decode<'t, IN: AsRef<[u8]>>( + bin: &'t mut [u8], + b64: IN, + ignore: Option<&[u8]>, + ) -> Result<&'t [u8], Error> { + Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::Original) + } +} + +impl Encoder for Base64NoPadding { + #[inline] + fn encoded_len(bin_len: usize) -> Result<usize, Error> { + Base64Impl::encoded_len(bin_len, Base64Variant::OriginalNoPadding) + } + + #[inline] + fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> { + Base64Impl::encode(b64, bin.as_ref(), Base64Variant::OriginalNoPadding) + } +} + +impl Decoder for Base64NoPadding { + #[inline] + fn decode<'t, IN: AsRef<[u8]>>( + bin: &'t mut [u8], + b64: IN, + ignore: Option<&[u8]>, + ) -> Result<&'t [u8], Error> { + Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::OriginalNoPadding) + } +} + +impl Encoder for Base64UrlSafe { + #[inline] + fn encoded_len(bin_len: usize) -> Result<usize, Error> { + Base64Impl::encoded_len(bin_len, Base64Variant::UrlSafe) + } + + #[inline] + fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> { + Base64Impl::encode(b64, bin.as_ref(), Base64Variant::UrlSafe) + } +} + +impl Decoder for Base64UrlSafe { + #[inline] + fn decode<'t, IN: AsRef<[u8]>>( + bin: &'t mut [u8], + b64: IN, + ignore: Option<&[u8]>, + ) -> Result<&'t [u8], Error> { + Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::UrlSafe) + } +} + +impl Encoder for Base64UrlSafeNoPadding { + #[inline] + fn encoded_len(bin_len: usize) -> Result<usize, Error> { + Base64Impl::encoded_len(bin_len, Base64Variant::UrlSafeNoPadding) + } + + #[inline] + fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> { + Base64Impl::encode(b64, bin.as_ref(), Base64Variant::UrlSafeNoPadding) + } +} + +impl Decoder for Base64UrlSafeNoPadding { + #[inline] + fn decode<'t, IN: AsRef<[u8]>>( + bin: &'t mut [u8], + b64: IN, + ignore: Option<&[u8]>, + ) -> Result<&'t [u8], Error> { + Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::UrlSafeNoPadding) + } +} + +#[cfg(feature = "std")] +#[test] +fn test_base64() { + let bin = [1u8, 5, 11, 15, 19, 131, 122]; + let expected = "AQULDxODeg=="; + let b64 = Base64::encode_to_string(&bin).unwrap(); + assert_eq!(b64, expected); + let bin2 = Base64::decode_to_vec(&b64, None).unwrap(); + assert_eq!(bin, &bin2[..]); +} + +#[cfg(feature = "std")] +#[test] +fn test_base64_mising_padding() { + let missing_padding = "AA"; + assert!(Base64::decode_to_vec(&missing_padding, None).is_err()); + assert!(Base64NoPadding::decode_to_vec(&missing_padding, None).is_ok()); + let missing_padding = "AAA"; + assert!(Base64::decode_to_vec(&missing_padding, None).is_err()); + assert!(Base64NoPadding::decode_to_vec(&missing_padding, None).is_ok()); +} + +#[test] +fn test_base64_no_std() { + let bin = [1u8, 5, 11, 15, 19, 131, 122]; + let expected = [65, 81, 85, 76, 68, 120, 79, 68, 101, 103, 61, 61]; + let mut b64 = [0u8; 12]; + let b64 = Base64::encode(&mut b64, &bin).unwrap(); + assert_eq!(b64, expected); + let mut bin2 = [0u8; 7]; + let bin2 = Base64::decode(&mut bin2, &b64, None).unwrap(); + assert_eq!(bin, bin2); +} diff --git a/vendor/ct-codecs/src/error.rs b/vendor/ct-codecs/src/error.rs new file mode 100644 index 000000000..471633109 --- /dev/null +++ b/vendor/ct-codecs/src/error.rs @@ -0,0 +1,21 @@ +use core::fmt::{self, Display}; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Error { + /// The provided output buffer would be too small. + Overflow, + /// The input isn't valid for the given encoding. + InvalidInput, +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Overflow => write!(f, "Overflow"), + Error::InvalidInput => write!(f, "Invalid input"), + } + } +} diff --git a/vendor/ct-codecs/src/hex.rs b/vendor/ct-codecs/src/hex.rs new file mode 100644 index 000000000..a371b82c5 --- /dev/null +++ b/vendor/ct-codecs/src/hex.rs @@ -0,0 +1,94 @@ +use crate::error::*; +use crate::{Decoder, Encoder}; + +pub struct Hex; + +impl Encoder for Hex { + #[inline] + fn encoded_len(bin_len: usize) -> Result<usize, Error> { + bin_len.checked_mul(2).ok_or(Error::Overflow) + } + + fn encode<IN: AsRef<[u8]>>(hex: &mut [u8], bin: IN) -> Result<&[u8], Error> { + let bin = bin.as_ref(); + let bin_len = bin.len(); + let hex_maxlen = hex.len(); + if hex_maxlen < bin_len.checked_shl(1).ok_or(Error::Overflow)? { + return Err(Error::Overflow); + } + for (i, v) in bin.iter().enumerate() { + let (b, c) = ((v >> 4) as u16, (v & 0xf) as u16); + let x = (((87 + c + (((c.wrapping_sub(10)) >> 8) & !38)) as u8) as u16) << 8 + | ((87 + b + (((b.wrapping_sub(10)) >> 8) & !38)) as u8) as u16; + hex[i * 2] = x as u8; + hex[i * 2 + 1] = (x >> 8) as u8; + } + Ok(&hex[..bin_len * 2]) + } +} + +impl Decoder for Hex { + fn decode<'t, IN: AsRef<[u8]>>( + bin: &'t mut [u8], + hex: IN, + ignore: Option<&[u8]>, + ) -> Result<&'t [u8], Error> { + let hex = hex.as_ref(); + let bin_maxlen = bin.len(); + let mut bin_pos = 0; + let mut state = false; + let mut c_acc = 0; + for &c in hex { + let c_num = c ^ 48; + let c_num0 = ((c_num as u16).wrapping_sub(10) >> 8) as u8; + let c_alpha = (c & !32).wrapping_sub(55); + let c_alpha0 = (((c_alpha as u16).wrapping_sub(10) + ^ ((c_alpha as u16).wrapping_sub(16))) + >> 8) as u8; + if (c_num0 | c_alpha0) == 0 { + match ignore { + Some(ignore) if ignore.contains(&c) => continue, + _ => return Err(Error::InvalidInput), + }; + } + let c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); + if bin_pos >= bin_maxlen { + return Err(Error::Overflow); + } + if !state { + c_acc = c_val << 4; + } else { + bin[bin_pos] = c_acc | c_val; + bin_pos += 1; + } + state = !state; + } + if state { + return Err(Error::InvalidInput); + } + Ok(&bin[..bin_pos]) + } +} + +#[cfg(feature = "std")] +#[test] +fn test_hex() { + let bin = [1u8, 5, 11, 15, 19, 131]; + let hex = Hex::encode_to_string(&bin).unwrap(); + let expected = "01050b0f1383"; + assert_eq!(hex, expected); + let bin2 = Hex::decode_to_vec(&hex, None).unwrap(); + assert_eq!(bin, &bin2[..]); +} + +#[test] +fn test_hex_no_std() { + let bin = [1u8, 5, 11, 15, 19, 131]; + let expected = "01050b0f1383"; + let mut hex = [0u8; 12]; + let hex = Hex::encode_to_str(&mut hex, &bin).unwrap(); + assert_eq!(&hex, &expected); + let mut bin2 = [0u8; 6]; + let bin2 = Hex::decode(&mut bin2, &hex, None).unwrap(); + assert_eq!(bin, bin2); +} diff --git a/vendor/ct-codecs/src/lib.rs b/vendor/ct-codecs/src/lib.rs new file mode 100644 index 000000000..b109c25d6 --- /dev/null +++ b/vendor/ct-codecs/src/lib.rs @@ -0,0 +1,63 @@ +//! Constant-time codecs. + +#![cfg_attr(not(feature = "std"), no_std)] +#![forbid(unsafe_code)] + +mod base64; +mod error; +mod hex; + +pub use base64::*; +pub use error::*; +pub use hex::*; + +pub trait Encoder { + /// Length of `bin_len` bytes after encoding. + fn encoded_len(bin_len: usize) -> Result<usize, Error>; + + /// Encode `bin` into `encoded`. + /// The output buffer can be larger than required; the returned slice is + /// a view of the buffer with the correct length. + fn encode<IN: AsRef<[u8]>>(encoded: &mut [u8], bin: IN) -> Result<&[u8], Error>; + + /// Encode `bin` into `encoded`, return the result as a `str`. + /// The output buffer can be larger than required; the returned slice is + /// a view of the buffer with the correct length. + fn encode_to_str<IN: AsRef<[u8]>>(encoded: &mut [u8], bin: IN) -> Result<&str, Error> { + Ok(core::str::from_utf8(Self::encode(encoded, bin)?).unwrap()) + } + + /// Encode `bin` as a `String`. + #[cfg(feature = "std")] + fn encode_to_string<IN: AsRef<[u8]>>(bin: IN) -> Result<String, Error> { + let mut encoded = vec![0u8; Self::encoded_len(bin.as_ref().len())?]; + let encoded_len = Self::encode(&mut encoded, bin)?.len(); + encoded.truncate(encoded_len); + Ok(String::from_utf8(encoded).unwrap()) + } +} + +pub trait Decoder { + /// Decode `encoded` into `bin`. + /// The output buffer can be larger than required; the returned slice is + /// a view of the buffer with the correct length. + /// `ignore` is an optional set of characters to ignore. + fn decode<'t, IN: AsRef<[u8]>>( + bin: &'t mut [u8], + encoded: IN, + ignore: Option<&[u8]>, + ) -> Result<&'t [u8], Error>; + + /// Decode `encoded` into a `Vec<u8>`. + /// `ignore` is an optional set of characters to ignore. + #[cfg(feature = "std")] + fn decode_to_vec<IN: AsRef<[u8]>>( + encoded: IN, + ignore: Option<&[u8]>, + ) -> Result<Vec<u8>, Error> { + let mut bin = vec![0u8; encoded.as_ref().len()]; + let bin_len = Self::decode(&mut bin, encoded, ignore)?.len(); + bin.truncate(bin_len); + Ok(bin) + } +} |