Skip to content

Commit

Permalink
base64ct: crypt(3) encoding (#239)
Browse files Browse the repository at this point in the history
Used by SHA-crypt
  • Loading branch information
tarcieri committed Feb 1, 2021
1 parent 094bfc6 commit ca602ad
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 5 deletions.
1 change: 1 addition & 0 deletions base64ct/src/encoding.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Base64 encodings

pub mod bcrypt;
pub mod crypt;
pub(crate) mod standard;
pub mod url;
2 changes: 1 addition & 1 deletion base64ct/src/encoding/bcrypt.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! bcrypt Base64 encoding
//! bcrypt Base64 encoding.
//!
//! ```text
//! ./ [A-Z] [a-z] [0-9]
Expand Down
75 changes: 75 additions & 0 deletions base64ct/src/encoding/crypt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//! `crypt(3)` Base64 encoding.
//!
//! ```text
//! [.-9] [A-Z] [a-z]
//! 0x2e-0x39, 0x41-0x5a, 0x61-0x7a
//! ```

use crate::{
decoder::{self, match_range_ct},
encoder::{self, match_gt_ct},
Error, InvalidEncodingError, InvalidLengthError,
};

#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};

/// Decode a `crypt(3)` Base64 string into the provided
/// destination buffer.
pub fn decode(src: impl AsRef<[u8]>, dst: &mut [u8]) -> Result<&[u8], Error> {
decoder::decode(src, dst, false, decode_6bits)
}

/// Decode a `crypt(3)` Base64 string in-place.
pub fn decode_in_place(buf: &mut [u8]) -> Result<&[u8], InvalidEncodingError> {
decoder::decode_in_place(buf, false, decode_6bits)
}

/// Decode a `crypt(3)` Base64 string into a byte vector.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn decode_vec(input: &str) -> Result<Vec<u8>, Error> {
decoder::decode_vec(input, false, decode_6bits)
}

/// Encode the input byte slice as `crypt(3)` Base64 with padding.
///
/// Writes the result into the provided destination slice, returning an
/// ASCII-encoded Base64 string value.
pub fn encode<'a>(src: &[u8], dst: &'a mut [u8]) -> Result<&'a str, InvalidLengthError> {
encoder::encode(src, dst, false, encode_6bits)
}

/// Encode input byte slice into a [`String`] containing `crypt(3)` Base64
/// without padding.
///
/// # Panics
/// If `input` length is greater than `usize::MAX/4`.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn encode_string(input: &[u8]) -> String {
encoder::encode_string(input, false, encode_6bits)
}

/// Get the length of Base64 produced by encoding the given bytes.
///
/// WARNING: this function will return `0` for lengths greater than `usize::MAX/4`!
pub fn encoded_len(bytes: &[u8]) -> usize {
encoder::encoded_len(bytes, false)
}

#[inline(always)]
fn decode_6bits(src: u8) -> i16 {
let mut res: i16 = -1;
res += match_range_ct(src, b'.'..b'9', src as i16 - 45);
res += match_range_ct(src, b'A'..b'Z', src as i16 - 52);
res + match_range_ct(src, b'a'..b'z', src as i16 - 58)
}

#[inline(always)]
fn encode_6bits(mut src: i16) -> u8 {
src += 0x2e;
src += match_gt_ct(src, 0x39, 7);
src += match_gt_ct(src, 0x5a, 6);
src as u8
}
2 changes: 1 addition & 1 deletion base64ct/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ mod encoding;
mod errors;

pub use encoding::{
bcrypt,
bcrypt, crypt,
standard::{padded, unpadded},
url,
};
Expand Down
9 changes: 8 additions & 1 deletion base64ct/tests/bcrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,16 @@ const TEST_VECTORS: &[TestVector] = &[

impl_tests!();

#[test]
fn reject_trailing_whitespace() {
let input = "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i\n";
let mut buf = [0u8; 1024];
assert_eq!(decode(input, &mut buf), Err(Error::InvalidEncoding));
}

#[test]
fn unpadded_reject_trailing_equals() {
let input = "QME_vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k=";
let input = "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i=";
let mut buf = [0u8; 1024];
assert_eq!(decode(input, &mut buf), Err(Error::InvalidEncoding));
}
62 changes: 62 additions & 0 deletions base64ct/tests/crypt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//! `crypt(3)` Base64 tests

#[macro_use]
mod common;

use crate::common::*;
use base64ct::crypt::*;

const TEST_VECTORS: &[TestVector] = &[
TestVector { raw: b"", b64: "" },
TestVector {
raw: b"\0",
b64: "..",
},
TestVector {
raw: b"***",
b64: "8Wce",
},
TestVector {
raw: b"\x01\x02\x03\x04",
b64: ".E61/.",
},
TestVector {
raw: b"\xAD\xAD\xAD\xAD\xAD",
b64: "fOqhfOo",
},
TestVector {
raw: b"\xFF\xEF\xFE\xFF\xEF\xFE",
b64: "zyzyzyzy",
},
TestVector {
raw: b"\xFF\xFF\xFF\xFF\xFF",
b64: "zzzzzzw",
},
TestVector {
raw: b"\x40\xC1\x3F\xBD\x05\x4C\x72\x2A\xA3\xC2\xF2\x11\x73\xC0\x69\xEA\
\x49\x7D\x35\x29\x6B\xCC\x24\x65\xF6\xF9\xD0\x41\x08\x7B\xD7\xA9",
b64: "EA2zjEJAQWeXkj6FQw/duYZxBGZfn0FZxjbEEEVvpuY",
},
TestVector {
raw: b"\x00\x10\x83\x10Q\x87 \x92\x8B0\xD3\x8FA\x14\x93QU\x97a\x96\x9Bq\
\xD7\x9F\x82\x18\xA3\x92Y\xA7\xA2\x9A\xAB\xB2\xDB\xAF\xC3\x1C\xB3\
\xFB\xF0\x00",
b64: "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnyz..",
},
];

impl_tests!();

#[test]
fn reject_trailing_whitespace() {
let input = "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i\n";
let mut buf = [0u8; 1024];
assert_eq!(decode(input, &mut buf), Err(Error::InvalidEncoding));
}

#[test]
fn unpadded_reject_trailing_equals() {
let input = "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i=";
let mut buf = [0u8; 1024];
assert_eq!(decode(input, &mut buf), Err(Error::InvalidEncoding));
}
4 changes: 2 additions & 2 deletions base64ct/tests/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,14 @@ mod unpadded {

#[test]
fn reject_trailing_whitespace() {
let input = "QME/vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k\n";
let input = "EA2zjEJAQWeXkj6FQw/duYZxBGZfn0FZxjbEEEVvpuY\n";
let mut buf = [0u8; 1024];
assert_eq!(decode(input, &mut buf), Err(Error::InvalidEncoding));
}

#[test]
fn unpadded_reject_trailing_equals() {
let input = "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i=";
let input = "EA2zjEJAQWeXkj6FQw/duYZxBGZfn0FZxjbEEEVvpuY=";
let mut buf = [0u8; 1024];
assert_eq!(decode(input, &mut buf), Err(Error::InvalidEncoding));
}
Expand Down

0 comments on commit ca602ad

Please sign in to comment.