improved overall crate documentation

This commit is contained in:
Frederik Palmø 2022-09-17 15:49:17 +02:00
parent 57ab36f117
commit e8c0d38021
10 changed files with 204 additions and 36 deletions

58
Cargo.lock generated
View file

@ -5,3 +5,61 @@ version = 3
[[package]] [[package]]
name = "lore" name = "lore"
version = "2.0.1" version = "2.0.1"
dependencies = [
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"

View file

@ -11,4 +11,9 @@ keywords = ["algorithms", "compression", "cryptography", "md5", "hash"]
categories = ["algorithms", "compression", "cryptography"] categories = ["algorithms", "compression", "cryptography"]
exclude = [] exclude = []
[dev-dependencies] [dependencies]
serde = { version = ">=1.0.0", features = ["derive"], optional = true }
[features]
default = []
serde = ["dep:serde"]

View file

@ -1,19 +1,32 @@
# Lore # Lore
Hashing algorithms with a straight-forward API and no dependencies. Nightly-only hashing algorithms with a straight-forward API and no required dependencies.
Nightly toolchain required. This crate currently implements:
Currently implements:
- MD2, MD4, and MD5 - MD2, MD4, and MD5
- SHA-1
## Example Performance is not a priority of this crate, rather, the primary purpose of this crate is learning, as well as providing tests for the intermediate steps of algorithms.
This includes padding, checksums and round step functions.
The functions of this crate should probably not be used for production purposes.
Once [`slice::array_chunks`] is stabilized, this crate can be made to work on stable Rust.
The crate could be rewritten to use stable already, but this would increase the verbosity of many expressions.
[`slice::array_chunks`]: https://doc.rust-lang.org/std/primitive.slice.html#method.array_chunks
# Features
[Serde](https://crates.io/crates/serde) support is included, and is gated behind the `serde` feature.
# Examples
Basic usage:
```rust ```rust
fn main() {
let input = "lol xd"; let input = "lol xd";
let digest = lore::md5(input); let digest = lore::md5(input);
assert_eq!(digest, "982d7f24f8985a6baa5cf129acc73561"); assert_eq!(digest.to_string(), "982d7f24f8985a6baa5cf129acc73561");
}
``` ```

View file

@ -1,3 +1 @@
fn main() { fn main() {}
println!("{}", lore::md2("abc"));
}

View file

@ -1,12 +1,31 @@
use std::fmt::Display; #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{fmt::Display, hash::Hash};
pub mod md2; pub mod md2;
pub mod md4; pub mod md4;
pub mod md5; pub mod md5;
pub mod sha1;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] /// A variable-size digest, which can easily be converted into a hexadecimal string for user-facing output.
///
/// This struct is returned by all hashing functions.
///
/// # Examples
///
/// [`Digest`] implements [`Display`], which means it can automatically be converted to a human-readable format by formatting it:
///
/// ```rust
/// let digest = lore::md5("example");
/// println!("Digest: {}", digest); // -> Digest: 1a79a4d60de6718e8e5b326e338ae533
/// ```
///
/// Digest contains [`From`] implementations which convert
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Digest<const S: usize>([u8; S]); pub struct Digest<const S: usize>([u8; S]);
/// Convert the digest into a hexadecimal string representation.
impl<const S: usize> Display for Digest<S> { impl<const S: usize> Display for Digest<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str( f.write_str(
@ -14,13 +33,36 @@ impl<const S: usize> Display for Digest<S> {
.0 .0
.iter() .iter()
.map(|u| format!("{:02x}", u)) .map(|u| format!("{:02x}", u))
.collect::<Vec<_>>() .collect::<String>(),
.join(""),
) )
} }
} }
pub(crate) fn bytes_to_words_le(bytes: impl AsRef<[u8]>) -> Vec<u32> { impl<const S: usize> From<Digest<S>> for [u8; S] {
fn from(digest: Digest<S>) -> Self {
digest.0
}
}
impl<'a, const S: usize> From<&'a Digest<S>> for &'a [u8] {
fn from(digest: &'a Digest<S>) -> Self {
digest.0.as_slice()
}
}
impl<const S: usize> From<Digest<S>> for Vec<u8> {
fn from(digest: Digest<S>) -> Self {
digest.0.to_vec()
}
}
impl<const S: usize> AsRef<[u8]> for Digest<S> {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
pub fn bytes_to_words_le(bytes: impl AsRef<[u8]>) -> Vec<u32> {
bytes bytes
.as_ref() .as_ref()
.array_chunks::<4>() .array_chunks::<4>()
@ -28,7 +70,7 @@ pub(crate) fn bytes_to_words_le(bytes: impl AsRef<[u8]>) -> Vec<u32> {
.collect() .collect()
} }
pub(crate) fn words_to_bytes_le(words: impl AsRef<[u32]>) -> Vec<u8> { pub fn words_to_bytes_le(words: impl AsRef<[u32]>) -> Vec<u8> {
words words
.as_ref() .as_ref()
.iter() .iter()
@ -43,12 +85,12 @@ mod tests {
#[test] #[test]
fn bytes_to_words_le_works() { fn bytes_to_words_le_works() {
assert_eq!( assert_eq!(
vec![0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476], vec![0x6745_2301, 0xefcd_ab89, 0x98ba_dcfe, 0x1032_5476],
bytes_to_words_le([ bytes_to_words_le([
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
0x32, 0x10 0x32, 0x10
]) ])
) );
} }
#[test] #[test]
@ -58,7 +100,7 @@ mod tests {
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
0x32, 0x10 0x32, 0x10
], ],
words_to_bytes_le([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]) words_to_bytes_le([0x6745_2301, 0xefcd_ab89, 0x98ba_dcfe, 0x1032_5476])
) );
} }
} }

View file

@ -43,8 +43,14 @@ fn checksum(message: impl AsRef<[u8]>) -> Vec<u8> {
message message
} }
/// Compute the MD2 hash of the input bytes /// Computes the MD2 digest of the input bytes.
///
/// Returns a `Digest<16>` which implements `Display` in order to get at hexadecimal-string representation.
///
/// # Examples /// # Examples
///
/// Basic usage:
///
/// ``` /// ```
/// let input = "abc"; /// let input = "abc";
/// let digest = lore::md2(input); /// let digest = lore::md2(input);

View file

@ -27,7 +27,7 @@ const G: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| (x & y) | (x & z) |
const H: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| x ^ y ^ z; const H: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| x ^ y ^ z;
// pad the message to next 512-bit interval // pad the message to next 512-bit interval
pub(crate) fn pad(message: impl AsRef<[u8]>) -> Vec<u8> { pub fn pad(message: impl AsRef<[u8]>) -> Vec<u8> {
let mut message = message.as_ref().to_vec(); let mut message = message.as_ref().to_vec();
let message_length = message.len().wrapping_mul(8) as u64; let message_length = message.len().wrapping_mul(8) as u64;
@ -36,7 +36,7 @@ pub(crate) fn pad(message: impl AsRef<[u8]>) -> Vec<u8> {
// add 0 bits until length in bits is congruent to 448 mod 512 // add 0 bits until length in bits is congruent to 448 mod 512
while (message.len()) % 64 != 56 { while (message.len()) % 64 != 56 {
message.push(0u8) message.push(0u8);
} }
// append message length (64 bits) // append message length (64 bits)
@ -48,7 +48,7 @@ pub(crate) fn pad(message: impl AsRef<[u8]>) -> Vec<u8> {
// compute an invidiual step in the md4 algorithm // compute an invidiual step in the md4 algorithm
fn step([mut a, b, c, d]: [u32; 4], words: &[u32], index: usize) -> [u32; 4] { fn step([mut a, b, c, d]: [u32; 4], words: &[u32], index: usize) -> [u32; 4] {
// choose function and constant based on which round is currently active // choose function and constant based on which round is currently active
let (rc, f) = match index { let (constant, round_function) = match index {
0..=15 => (C1, F), 0..=15 => (C1, F),
16..=31 => (C2, G), 16..=31 => (C2, G),
32..=47 => (C3, H), 32..=47 => (C3, H),
@ -56,17 +56,23 @@ fn step([mut a, b, c, d]: [u32; 4], words: &[u32], index: usize) -> [u32; 4] {
}; };
// main operation // main operation
a = f(b, c, d) a = round_function(b, c, d)
.wrapping_add(a) .wrapping_add(a)
.wrapping_add(words[W[index]]) .wrapping_add(words[W[index]])
.wrapping_add(rc) .wrapping_add(constant)
.rotate_left(S[index]); .rotate_left(S[index]);
[a, b, c, d] [a, b, c, d]
} }
/// Compute the MD4 hash of the input bytes /// Computes the MD4 digest of the input bytes.
///
/// Returns a `Digest<16>` which implements `Display` in order to get at hexadecimal-string representation.
///
/// # Examples /// # Examples
///
/// Basic usage:
///
/// ``` /// ```
/// let input = "abc"; /// let input = "abc";
/// let digest = lore::md4(input); /// let digest = lore::md4(input);
@ -165,7 +171,7 @@ mod tests {
assert_eq!( assert_eq!(
hash("Rosetta Code").to_string(), hash("Rosetta Code").to_string(),
"a52bcfc6a0d0d300cdc5ddbfbefe478b" "a52bcfc6a0d0d300cdc5ddbfbefe478b"
) );
} }
#[test] #[test]

View file

@ -36,7 +36,7 @@ const H: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| x ^ y ^ z;
const I: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| y ^ (x | !z); const I: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| y ^ (x | !z);
fn step([mut a, b, c, d]: [u32; 4], words: &[u32], index: usize) -> [u32; 4] { fn step([mut a, b, c, d]: [u32; 4], words: &[u32], index: usize) -> [u32; 4] {
let f = match index { let round_function = match index {
0..=15 => F, 0..=15 => F,
16..=31 => G, 16..=31 => G,
32..=47 => H, 32..=47 => H,
@ -44,7 +44,7 @@ fn step([mut a, b, c, d]: [u32; 4], words: &[u32], index: usize) -> [u32; 4] {
_ => panic!("This function shouldn't be called using an index outside 0..48"), _ => panic!("This function shouldn't be called using an index outside 0..48"),
}; };
a = f(b, c, d) a = round_function(b, c, d)
.wrapping_add(a) .wrapping_add(a)
.wrapping_add(words[W[index]]) .wrapping_add(words[W[index]])
.wrapping_add(K[index]) .wrapping_add(K[index])
@ -54,13 +54,20 @@ fn step([mut a, b, c, d]: [u32; 4], words: &[u32], index: usize) -> [u32; 4] {
[a, b, c, d] [a, b, c, d]
} }
/// Compute the MD5 hash of the input bytes /// Computes the MD5 digest of the input bytes.
///
/// Returns a `Digest<16>` which implements `Display` in order to get at hexadecimal-string representation.
///
/// # Examples /// # Examples
///
/// Basic usage:
///
/// ``` /// ```
/// let input = "lol"; /// let input = "lol";
/// let digest = lore::md5(input); /// let digest = lore::md5(input);
/// ///
/// assert_eq!(digest.to_string(), "9cdfb439c7876e703e307864c9167a15"); /// assert_eq!(digest.to_string(), "9cdfb439c7876e703e307864c9167a15");
///
/// ``` /// ```
pub fn hash(message: impl AsRef<[u8]>) -> Digest<16> { pub fn hash(message: impl AsRef<[u8]>) -> Digest<16> {
let padded = pad(message); let padded = pad(message);

View file

@ -1,7 +1,27 @@
use crate::hash::Digest;
/// Computes the SHA1 digest of the input bytes.
///
/// Returns a `Digest<20>` which implements `Display` in order to get at hexadecimal-string representation.
///
/// # Examples
///
/// Basic usage:
///
/// ```rust
/// let input = "abc";
/// let digest = lore::sha1(input);
///
/// assert_eq!(digest.to_string(), "a9993e364706816aba3e25717850c26c9cd0d89d")
/// ```
pub fn hash(message: impl AsRef<[u8]>) -> Digest<20> {
todo!()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn sha1() {} fn sha1_hash() {}
} }

View file

@ -1,7 +1,20 @@
// rust nightly features
#![feature(array_chunks)] #![feature(array_chunks)]
// lints
#![deny(missing_docs)]
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![allow(
clippy::unreadable_literal,
clippy::missing_panics_doc,
clippy::cast_possible_truncation
)]
// docs
#![doc = include_str!("../README.md")]
mod hash; mod hash;
pub use hash::md2::hash as md2; pub use hash::md2::hash as md2;
pub use hash::md4::hash as md4; pub use hash::md4::hash as md4;
pub use hash::md5::hash as md5; pub use hash::md5::hash as md5;
pub use hash::sha1::hash as sha1;
pub use hash::Digest;