Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propose side-channel resistant feature #153

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -25,6 +25,7 @@ structopt = "0.3"
default = ["std"]
alloc = []
std = []
slow_but_safe = []

[profile.bench]
# Useful for better disassembly when using `perf record` and `perf annotate`
Expand Down
36 changes: 36 additions & 0 deletions src/decode.rs
Expand Up @@ -444,6 +444,18 @@ fn write_u64(output: &mut [u8], value: u64) {
output[..8].copy_from_slice(&value.to_be_bytes());
}

#[cfg(feature = "slow_but_safe")]
fn decode_aligned(b64ch: u8, decode_table: &[u8; 256]) -> u8 {
let mut result: u8 = 0x00;
let mut mask: u8;
let idx: [u8;2] = [ b64ch % 64, b64ch % 64 + 64];
for i in 0..2 {
mask = 0xFF ^ (((idx[i] == b64ch) as i8 - 1) as u8);
result = result | (decode_table[idx[i] as usize] & mask);
}
result
}

/// Decode 8 bytes of input into 6 bytes of output. 8 bytes of output will be written, but only the
/// first 6 of those contain meaningful data.
///
Expand All @@ -463,13 +475,19 @@ fn decode_chunk(
) -> Result<(), DecodeError> {
let mut accum: u64;

#[cfg(not(feature = "slow_but_safe"))]
let morsel = decode_table[input[0] as usize];
#[cfg(feature = "slow_but_safe")]
let morsel = decode_aligned(input[0], decode_table);
if morsel == tables::INVALID_VALUE {
return Err(DecodeError::InvalidByte(index_at_start_of_input, input[0]));
}
accum = (morsel as u64) << 58;

#[cfg(not(feature = "slow_but_safe"))]
let morsel = decode_table[input[1] as usize];
#[cfg(feature = "slow_but_safe")]
let morsel = decode_aligned(input[1], decode_table);
if morsel == tables::INVALID_VALUE {
return Err(DecodeError::InvalidByte(
index_at_start_of_input + 1,
Expand All @@ -478,7 +496,10 @@ fn decode_chunk(
}
accum |= (morsel as u64) << 52;

#[cfg(not(feature = "slow_but_safe"))]
let morsel = decode_table[input[2] as usize];
#[cfg(feature = "slow_but_safe")]
let morsel = decode_aligned(input[2], decode_table);
if morsel == tables::INVALID_VALUE {
return Err(DecodeError::InvalidByte(
index_at_start_of_input + 2,
Expand All @@ -487,7 +508,10 @@ fn decode_chunk(
}
accum |= (morsel as u64) << 46;

#[cfg(not(feature = "slow_but_safe"))]
let morsel = decode_table[input[3] as usize];
#[cfg(feature = "slow_but_safe")]
let morsel = decode_aligned(input[3], decode_table);
if morsel == tables::INVALID_VALUE {
return Err(DecodeError::InvalidByte(
index_at_start_of_input + 3,
Expand All @@ -496,7 +520,10 @@ fn decode_chunk(
}
accum |= (morsel as u64) << 40;

#[cfg(not(feature = "slow_but_safe"))]
let morsel = decode_table[input[4] as usize];
#[cfg(feature = "slow_but_safe")]
let morsel = decode_aligned(input[4], decode_table);
if morsel == tables::INVALID_VALUE {
return Err(DecodeError::InvalidByte(
index_at_start_of_input + 4,
Expand All @@ -505,7 +532,10 @@ fn decode_chunk(
}
accum |= (morsel as u64) << 34;

#[cfg(not(feature = "slow_but_safe"))]
let morsel = decode_table[input[5] as usize];
#[cfg(feature = "slow_but_safe")]
let morsel = decode_aligned(input[5], decode_table);
if morsel == tables::INVALID_VALUE {
return Err(DecodeError::InvalidByte(
index_at_start_of_input + 5,
Expand All @@ -514,7 +544,10 @@ fn decode_chunk(
}
accum |= (morsel as u64) << 28;

#[cfg(not(feature = "slow_but_safe"))]
let morsel = decode_table[input[6] as usize];
#[cfg(feature = "slow_but_safe")]
let morsel = decode_aligned(input[6], decode_table);
if morsel == tables::INVALID_VALUE {
return Err(DecodeError::InvalidByte(
index_at_start_of_input + 6,
Expand All @@ -523,7 +556,10 @@ fn decode_chunk(
}
accum |= (morsel as u64) << 22;

#[cfg(not(feature = "slow_but_safe"))]
let morsel = decode_table[input[7] as usize];
#[cfg(feature = "slow_but_safe")]
let morsel = decode_aligned(input[7], decode_table);
if morsel == tables::INVALID_VALUE {
return Err(DecodeError::InvalidByte(
index_at_start_of_input + 7,
Expand Down
12 changes: 6 additions & 6 deletions src/lib.rs
Expand Up @@ -138,12 +138,12 @@ impl CharacterSet {

fn decode_table(self) -> &'static [u8; 256] {
match self {
CharacterSet::Standard => tables::STANDARD_DECODE,
CharacterSet::UrlSafe => tables::URL_SAFE_DECODE,
CharacterSet::Crypt => tables::CRYPT_DECODE,
CharacterSet::Bcrypt => tables::BCRYPT_DECODE,
CharacterSet::ImapMutf7 => tables::IMAP_MUTF7_DECODE,
CharacterSet::BinHex => tables::BINHEX_DECODE,
CharacterSet::Standard => &tables::STANDARD_DECODE_HOLDER.data,
CharacterSet::UrlSafe => &tables::URL_SAFE_DECODE_HOLDER.data,
CharacterSet::Crypt => &tables::CRYPT_DECODE_HOLDER.data,
CharacterSet::Bcrypt => &tables::BCRYPT_DECODE_HOLDER.data,
CharacterSet::ImapMutf7 => &tables::IMAP_MUTF7_DECODE_HOLDER.data,
CharacterSet::BinHex => &tables::BINHEX_DECODE_HOLDER.data,
}
}
}
Expand Down
40 changes: 40 additions & 0 deletions src/tables.rs
@@ -1,3 +1,27 @@
#[repr(align(64))]
pub struct DecodeTableHolder {
pub data: [u8; 256],
}

pub const STANDARD_DECODE_HOLDER: DecodeTableHolder = DecodeTableHolder {
data: *STANDARD_DECODE,
};
pub const URL_SAFE_DECODE_HOLDER: DecodeTableHolder = DecodeTableHolder {
data: *URL_SAFE_DECODE,
};
pub const CRYPT_DECODE_HOLDER: DecodeTableHolder = DecodeTableHolder {
data: *CRYPT_DECODE,
};
pub const BCRYPT_DECODE_HOLDER: DecodeTableHolder = DecodeTableHolder {
data: *BCRYPT_DECODE,
};
pub const IMAP_MUTF7_DECODE_HOLDER: DecodeTableHolder = DecodeTableHolder {
data: *IMAP_MUTF7_DECODE,
};
pub const BINHEX_DECODE_HOLDER: DecodeTableHolder = DecodeTableHolder {
data: *BINHEX_DECODE,
};

pub const INVALID_VALUE: u8 = 255;
#[rustfmt::skip]
pub const STANDARD_ENCODE: &[u8; 64] = &[
Expand Down Expand Up @@ -1955,3 +1979,19 @@ pub const BINHEX_DECODE: &[u8; 256] = &[
INVALID_VALUE, // input 254 (0xFE)
INVALID_VALUE, // input 255 (0xFF)
];

#[test]
fn alignment_check() {
let p: *const u8 = STANDARD_DECODE_HOLDER.data.as_ptr();
assert_eq!((p as u64) % 64, 0);
let p: *const u8 = URL_SAFE_DECODE_HOLDER.data.as_ptr();
assert_eq!((p as u64) % 64, 0);
let p: *const u8 = CRYPT_DECODE_HOLDER.data.as_ptr();
assert_eq!((p as u64) % 64, 0);
let p: *const u8 = BCRYPT_DECODE_HOLDER.data.as_ptr();
assert_eq!((p as u64) % 64, 0);
let p: *const u8 = IMAP_MUTF7_DECODE_HOLDER.data.as_ptr();
assert_eq!((p as u64) % 64, 0);
let p: *const u8 = BINHEX_DECODE_HOLDER.data.as_ptr();
assert_eq!((p as u64) % 64, 0);
}