Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions base64ct/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ fixed-width line wrapping.
- URL-safe Base64: `[A-Z]`, `[a-z]`, `[0-9]`, `-`, `_`
- bcrypt Base64: `.`, `/`, `[A-Z]`, `[a-z]`, `[0-9]`
- `crypt(3)` Base64: `.`, `-`, `[0-9]`, `[A-Z]`, `[a-z]`
- PBKDF2 Base64: `[A-Z]`, `[a-z]`, `[0-9]`, `.`, `/`

## Minimum Supported Rust Version (MSRV) Policy

Expand Down
1 change: 1 addition & 0 deletions base64ct/src/alphabet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use core::{fmt::Debug, ops::RangeInclusive};

pub mod bcrypt;
pub mod crypt;
pub mod pbkdf2;
pub mod shacrypt;
pub mod standard;
pub mod url;
Expand Down
33 changes: 33 additions & 0 deletions base64ct/src/alphabet/pbkdf2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::alphabet::{Alphabet, DecodeStep, EncodeStep};

/// PBKDF2 Base64: variant of unpadded standard Base64 with `.` instead of `+`.
///
/// ```text
/// [A-Z] [a-z] [0-9] . /
/// 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2e, 0x2f
/// ```
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Base64Pbkdf2;

impl Alphabet for Base64Pbkdf2 {
const BASE: u8 = b'A';
const DECODER: &'static [DecodeStep] = DECODER;
const ENCODER: &'static [EncodeStep] = ENCODER;
const PADDED: bool = false;
type Unpadded = Self;
}

const DECODER: &[DecodeStep] = &[
DecodeStep::Range(b'A'..=b'Z', -64),
DecodeStep::Range(b'a'..=b'z', -70),
DecodeStep::Range(b'0'..=b'9', 5),
DecodeStep::Eq(b'.', 63),
DecodeStep::Eq(b'/', 64),
];

const ENCODER: &[EncodeStep] = &[
EncodeStep::Diff(25, 6),
EncodeStep::Diff(51, -75),
EncodeStep::Diff(61, -(b'.' as i16 - 0x22)),
EncodeStep::Diff(62, b'/' as i16 - b'.' as i16 - 1),
];
1 change: 1 addition & 0 deletions base64ct/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ mod test_vectors;
pub use crate::{
alphabet::{
bcrypt::Base64Bcrypt,
pbkdf2::Base64Pbkdf2,
shacrypt::Base64ShaCrypt,
standard::{Base64, Base64Unpadded},
url::{Base64Url, Base64UrlUnpadded},
Expand Down
78 changes: 78 additions & 0 deletions base64ct/tests/pbkdf2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//! PBKDF2 MCF variant of Base64

#[macro_use]
mod common;

use crate::common::*;
use base64ct::Base64Pbkdf2;

const TEST_VECTORS: &[TestVector] = &[
TestVector { raw: b"", b64: "" },
TestVector {
raw: b"\0",
b64: "AA",
},
TestVector {
raw: b"***",
b64: "Kioq",
},
TestVector {
raw: b"\x01\x02\x03\x04",
b64: "AQIDBA",
},
TestVector {
raw: b"\xAD\xAD\xAD\xAD\xAD",
b64: "ra2tra0",
},
TestVector {
raw: b"\xFF\xEF\xFE\xFF\xEF\xFE",
b64: "/./././.",
},
TestVector {
raw: b"\xFF\xFF\xFF\xFF\xFF",
b64: "//////8",
},
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: "QME/vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k",
},
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: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./AA",
},
];

impl_tests!(Base64Pbkdf2);

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

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

#[test]
fn reject_non_canonical_encoding() {
let input = "Mi";
let mut buf = [0u8; 8];
assert_eq!(
Base64Pbkdf2::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
18 changes: 17 additions & 1 deletion mcf/src/base64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
#![cfg(feature = "base64")]

use base64ct::{
Base64Bcrypt, Base64ShaCrypt, Base64Unpadded as B64, Encoding as _, Error as B64Error,
Base64Bcrypt, Base64Pbkdf2, Base64ShaCrypt, Base64Unpadded as B64, Encoding as _,
Error as B64Error,
};

#[cfg(feature = "alloc")]
Expand Down Expand Up @@ -45,6 +46,16 @@ pub enum Base64 {
/// 0x2e-0x39, 0x41-0x5a, 0x61-0x7a
/// ```
Crypt,

/// PBKDF2 Base64 encoding.
///
/// This is a variant of the unpadded standard Base64 with `.` in place of `+`:
///
/// ```text
/// [A-Z] [a-z] [0-9] . /
/// 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2e, 0x2f
/// ```
Pbkdf2,
}

impl Base64 {
Expand All @@ -54,6 +65,7 @@ impl Base64 {
Self::B64 => B64::decode(src, dst),
Self::Bcrypt => Base64Bcrypt::decode(src, dst),
Self::Crypt => Base64ShaCrypt::decode(src, dst),
Self::Pbkdf2 => Base64Pbkdf2::decode(src, dst),
}
}

Expand All @@ -64,6 +76,7 @@ impl Base64 {
Self::B64 => B64::decode_vec(input),
Self::Bcrypt => Base64Bcrypt::decode_vec(input),
Self::Crypt => Base64ShaCrypt::decode_vec(input),
Self::Pbkdf2 => Base64Pbkdf2::decode_vec(input),
}
}

Expand All @@ -76,6 +89,7 @@ impl Base64 {
Self::B64 => B64::encode(src, dst),
Self::Bcrypt => Base64Bcrypt::encode(src, dst),
Self::Crypt => Base64ShaCrypt::encode(src, dst),
Self::Pbkdf2 => Base64Pbkdf2::encode(src, dst),
}
.map_err(Into::into)
}
Expand All @@ -90,6 +104,7 @@ impl Base64 {
Self::B64 => B64::encode_string(input),
Self::Bcrypt => Base64Bcrypt::encode_string(input),
Self::Crypt => Base64ShaCrypt::encode_string(input),
Self::Pbkdf2 => Base64Pbkdf2::encode_string(input),
}
}

Expand All @@ -99,6 +114,7 @@ impl Base64 {
Self::B64 => B64::encoded_len(bytes),
Self::Bcrypt => Base64Bcrypt::encoded_len(bytes),
Self::Crypt => Base64ShaCrypt::encoded_len(bytes),
Self::Pbkdf2 => Base64Pbkdf2::encoded_len(bytes),
}
}
}