Skip to content

Commit

Permalink
Fix panic when corrupted hash is passed to rcrypt::verify
Browse files Browse the repository at this point in the history
Also bump version to 0.3.0 due to breaking changes in error types
  • Loading branch information
ohsayan committed Feb 23, 2022
1 parent 8f96025 commit 4d9877b
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 19 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All changes made to this project will be noted in this file.

## 0.3.0

- Fixed panic when a corrupted hash is passed to `rcrypt::verify`
- (BREAKING): `RcryptError::WrongSize` now has two `usize` tuple fields: one with expected has
size, and the other with
- (BREAKING): `RcryptError::UnsupportedHash` is now `UnsupportedHashPrefix`

## 0.2.0

- Fixed hash generation for hashes with a cost of 9
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rcrypt"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
authors = ["Sayan Nandan <nandansayan@outlook.com>"]
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ system storage, but was moved into a separate library for usage in the wider Rus
`rcrypt` is almost a drop-in replacement for the `bcrypt` crate.

The smaller hash sizes are achieved by rcrypt's
implementation of a segment compression/decompression algorithm, that compresses segments of the MCF hash
implementation of a segment compression/decompression algorithm, that compresses fields of the MCF hash
based on the [BMCF spec](https://github.com/ademarre/binary-mcf). The hashes produced are binary hashes.

## Usage
Expand Down
51 changes: 34 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
//! `rcrypt`, short for "reduced crypt" is a more compact alternative to bcrypt,
//! generating **hashes that are 33.3% smaller** (40 bytes vs 60 bytes) than bcrypt.
//!
//! To achieve this, rcrypt compresses segments of the hash, in accordance with the
//! To achieve this, rcrypt compresses fields of the hash, in accordance with the
//! [BMCF specification](https://github.com/ademarre/binary-mcf).
//! After applying the compression algorithm implemented in this crate, the
//! 60 byte bcrypt hash is compressed into a 40 byte long binary hash, that
Expand Down Expand Up @@ -86,19 +86,24 @@ pub mod bmcf {
}

mod algorithm {
const EXPECTED_PARTS: usize = 3;
use crate::{RcryptError, RcryptResult};

const EXPECTED_PARTS: usize = 3;
const RCRYPT_EXPECTED_SIZE: usize = 40;
const BCRYPT_EXPECTED_SIZE: usize = 60;
const BCRYPT_EXPECTED_SIZE_SALTDIGEST: usize = 53;
const BCRYPT_EXPECTED_SIZE_FIELDS: usize = 2;

/// Encode an MCF hash into BMCF. This is the core compression algorithm
/// used by rcrypt
pub fn encode_into_bmcf(input: &str) -> RcryptResult<Vec<u8>> {
if input.len() != 60 {
return Err(RcryptError::WrongSize(input.len()));
if input.len() != BCRYPT_EXPECTED_SIZE {
return Err(RcryptError::WrongSize(BCRYPT_EXPECTED_SIZE, input.len()));
}
if input.as_bytes()[0] != b'$' {
return Err(RcryptError::UnsupportedHash(input.as_bytes()[0]));
return Err(RcryptError::UnsupportedHashPrefix(input.as_bytes()[0]));
}
let mut buf: Vec<u8> = Vec::with_capacity(40);
let mut buf: Vec<u8> = Vec::with_capacity(RCRYPT_EXPECTED_SIZE);
let parts: Vec<&str> = input.split('$').filter(|s| !s.is_empty()).collect();
if parts.len() != EXPECTED_PARTS {
return Err(RcryptError::CorruptedHash(format!(
Expand All @@ -107,11 +112,20 @@ mod algorithm {
)));
}
match (parts[0].len(), parts[1].len(), parts[2].len()) {
(2, 2, 53) => {}
(
BCRYPT_EXPECTED_SIZE_FIELDS,
BCRYPT_EXPECTED_SIZE_FIELDS,
BCRYPT_EXPECTED_SIZE_SALTDIGEST,
) => {}
(p1l, p2l, p3l) => {
return Err(RcryptError::CorruptedHash(format!(
"Expected 3 parts with lengths 2, 2, 53. Found lengths {}, {} and {} instead",
p1l, p2l, p3l
"Expected 3 parts with lengths {}, {}, {}. Found lengths {}, {} and {} instead",
BCRYPT_EXPECTED_SIZE_FIELDS,
BCRYPT_EXPECTED_SIZE_FIELDS,
BCRYPT_EXPECTED_SIZE_SALTDIGEST,
p1l,
p2l,
p3l
)))
}
}
Expand Down Expand Up @@ -145,7 +159,10 @@ mod algorithm {
/// Decode a BMCF hash into MCF. This is the core decompression algorithm
/// used by rcrypt
pub fn decode_into_mcf(input: &[u8]) -> RcryptResult<String> {
let mut st: Vec<u8> = Vec::with_capacity(60);
if input.len() != RCRYPT_EXPECTED_SIZE {
return Err(RcryptError::WrongSize(RCRYPT_EXPECTED_SIZE, input.len()));
}
let mut st: Vec<u8> = Vec::with_capacity(BCRYPT_EXPECTED_SIZE);
st.push(b'$');
// get scheme
let header_octet = input[0];
Expand Down Expand Up @@ -196,10 +213,10 @@ mod error {
pub enum RcryptError {
/// The hash is corrupted. The description is given in the tuple field
CorruptedHash(String),
/// The hash has the wrong size
WrongSize(usize),
/// The hash is unsupported
UnsupportedHash(u8),
/// The hash has the wrong size (expected, present)
WrongSize(usize, usize),
/// The hash prefix is unsupported
UnsupportedHashPrefix(u8),
/// The cost of the hash is incorrect
BadCost(String),
/// Unknown scheme
Expand Down Expand Up @@ -230,9 +247,9 @@ mod error {
RcryptError::BcryptError(e) => write!(f, "bcrypt error: {}", e),
RcryptError::CorruptedHash(e) => write!(f, "corrupted hash: {}", e),
RcryptError::UnknownScheme(e) => write!(f, "unknown scheme: {}", e),
RcryptError::UnsupportedHash(p) => write!(f, "unsupported prefix: {}", p),
RcryptError::WrongSize(sz) => {
write!(f, "wrong hash size. expected 60 bytes, found {}", sz)
RcryptError::UnsupportedHashPrefix(p) => write!(f, "unsupported prefix: {}", p),
RcryptError::WrongSize(esz, sz) => {
write!(f, "wrong hash size. expected {} bytes, found {}", esz, sz)
}
}
}
Expand Down

0 comments on commit 4d9877b

Please sign in to comment.