summaryrefslogtreecommitdiffstats
path: root/vendor/ct-codecs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/ct-codecs')
-rw-r--r--vendor/ct-codecs/.cargo-checksum.json1
-rw-r--r--vendor/ct-codecs/Cargo.toml32
-rw-r--r--vendor/ct-codecs/LICENSE21
-rw-r--r--vendor/ct-codecs/README.md21
-rw-r--r--vendor/ct-codecs/src/base64.rs378
-rw-r--r--vendor/ct-codecs/src/error.rs21
-rw-r--r--vendor/ct-codecs/src/hex.rs94
-rw-r--r--vendor/ct-codecs/src/lib.rs63
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)
+ }
+}