improved overall crate documentation
This commit is contained in:
parent
57ab36f117
commit
e8c0d38021
10 changed files with 204 additions and 36 deletions
58
Cargo.lock
generated
58
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"]
|
33
README.md
33
README.md
|
@ -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.to_string(), "982d7f24f8985a6baa5cf129acc73561");
|
||||||
assert_eq!(digest, "982d7f24f8985a6baa5cf129acc73561");
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
fn main() {
|
fn main() {}
|
||||||
println!("{}", lore::md2("abc"));
|
|
||||||
}
|
|
||||||
|
|
62
src/hash.rs
62
src/hash.rs
|
@ -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])
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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() {}
|
||||||
}
|
}
|
||||||
|
|
13
src/lib.rs
13
src/lib.rs
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue