diff --git a/Cargo.toml b/Cargo.toml index b26431d7..1d2b5b77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,10 @@ ark-poly ={ git="https://github.com/arkworks-rs/algebra/" } ark-serialize={ git="https://github.com/arkworks-rs/algebra/" } ark-std ={ git="https://github.com/arkworks-rs/std/" } +[[bin]] +name = "hmac_sha256_bin" +path = "src/hmac/bin/hmac_sha256_bin.rs" + [[example]] name="aes_chained_cbc" diff --git a/src/hmac/README.md b/src/hmac/README.md new file mode 100644 index 00000000..d42dcb2f --- /dev/null +++ b/src/hmac/README.md @@ -0,0 +1,28 @@ +# HMAC (Hash-based Message Authentication Code) + +## Overview + +HMAC is a cryptographic primitive for providing message integrity and authenticity using a cryptographic hash function in combination with a secret key. It ensures that both the data (message) and its origin (authenticity) are verified. + +## Components + +- **Hash Function**: A one-way function that produces a fixed-size output (hash) from an input (message). Common hash functions include SHA-256, SHA-1, and MD5. +- **Secret Key**: A private key used to generate and verify the HMAC. It should be kept confidential. +- **Message**: The data or payload that is being protected by HMAC. + +## How HMAC Works + +1. **Key Padding**: If the secret key is shorter than the block size of the hash function, it is padded. If it is longer, it is hashed to reduce its length. +2. **Inner Hash Calculation**: Combine the padded key with the message and compute the hash. This step involves XORing the key with a specific value and then hashing the result concatenated with the message. +3. **Outer Hash Calculation**: Combine the padded key with a second specific value, hash the result concatenated with the inner hash, and produce the final HMAC. + +More specifically, HMAC is defined as: +$$ \text{HMAC}_K(M) = \text{H} \left( (K' \oplus \text{opad}) \Vert \text{H}((K' \oplus \text{ipad}) \Vert \text{M} \right) $$ +where: +- $H$ is a cryptographic hash function. +- $M$ is the message. +- $K$ is the secret key. +- $K'$ is a block-sized key derived from the secret key $K$. +- $\text{opad}$ is the block-sized outer padding. +- $\text{ipad}$ is the block-sized inner padding. +- $\Vert$ denotes concatenation. diff --git a/src/hmac/bin/hmac_sha256_bin.rs b/src/hmac/bin/hmac_sha256_bin.rs new file mode 100644 index 00000000..48f0ef72 --- /dev/null +++ b/src/hmac/bin/hmac_sha256_bin.rs @@ -0,0 +1,21 @@ +use std::env; + +use ronkathon::hmac::hmac_sha256::hmac_sha256; + +fn main() { + // Get command-line arguments + let args: Vec = env::args().collect(); + + // Check if an input was provided + if args.len() < 2 { + eprintln!("Please provide an input argument."); + std::process::exit(1); + } + + // Pass the first argument to the function + let key = args[1].as_bytes(); + let message = args[2].as_bytes(); + let result = hex::encode(hmac_sha256(&key, &message)); + + println!("Result: {}", result); +} diff --git a/src/hmac/hmac_sha256.rs b/src/hmac/hmac_sha256.rs new file mode 100644 index 00000000..1d50380b --- /dev/null +++ b/src/hmac/hmac_sha256.rs @@ -0,0 +1,113 @@ +//! An implementation of the HMAC using the SHA-256 hash function. + +use crate::hashes::sha256::Sha256; + +const SHA256_BLOCK_SIZE: usize = 64; +const SHA256_OUTPUT_SIZE: usize = 32; + +/// Computes a block-sized key from the given key. +/// +/// If the key is longer than the block size, it is hashed and the result is truncated. +/// Otherwise, the key is padded with zeros to fit the block size. +fn compute_block_sized_key(key: &[u8]) -> [u8; SHA256_BLOCK_SIZE] { + let mut block_sized_key = [0; SHA256_BLOCK_SIZE]; + + if key.len() > SHA256_BLOCK_SIZE { + // Hash the key and use only the first SHA256_BLOCK_SIZE bytes + let digest = Sha256::digest(key); + block_sized_key[..SHA256_OUTPUT_SIZE].copy_from_slice(&digest); + } else { + // Copy the key directly, padded with zeros if necessary + block_sized_key[..key.len()].copy_from_slice(key); + } + + block_sized_key +} + +/// Computes the HMAC of the given key and message using the SHA-256 hash function. +/// +/// # Arguments +/// +/// * `key` - The secret key used for HMAC. +/// * `message` - The message to authenticate. +/// +/// # Returns +/// +/// Returns a 32-byte HMAC-SHA256 digest. +/// +/// # Example +/// +/// ``` +/// use hex; +/// use ronkathon::hmac::hmac_sha256::hmac_sha256; +/// +/// let key = b"supersecretkey"; +/// let message = b"message"; +/// assert_eq!( +/// hex::encode(hmac_sha256(key, message)), +/// "1e46048d8b12509a93f36926f77c369a8520aa03923f854a8174bb58756cd68a" +/// ); +/// ``` +pub fn hmac_sha256(key: &[u8], message: &[u8]) -> [u8; SHA256_OUTPUT_SIZE] { + let block_sized_key = compute_block_sized_key(key); + + let mut o_key_pad = [0; SHA256_BLOCK_SIZE]; + let mut i_key_pad = [0; SHA256_BLOCK_SIZE]; + for i in 0..SHA256_BLOCK_SIZE { + o_key_pad[i] = block_sized_key[i] ^ 0x5c; + i_key_pad[i] = block_sized_key[i] ^ 0x36; + } + + // Compute the inner hash: H((K ⊕ ipad) | message) + let mut inner_hash_input = Vec::with_capacity(SHA256_BLOCK_SIZE + message.len()); + inner_hash_input.extend_from_slice(&i_key_pad); + inner_hash_input.extend_from_slice(message); + let inner_hash = Sha256::digest(&inner_hash_input); + + // Compute the outer hash: H((K ⊕ opad) | inner_hash) + let mut outer_hash_input = Vec::with_capacity(SHA256_BLOCK_SIZE + SHA256_OUTPUT_SIZE); + outer_hash_input.extend_from_slice(&o_key_pad); + outer_hash_input.extend_from_slice(&inner_hash); + Sha256::digest(&outer_hash_input) +} + +#[cfg(test)] +mod tests { + // Test gotten from the standard https://datatracker.ietf.org/doc/html/rfc4231#section-4 + + use rstest::rstest; + + use super::*; + #[rstest] + #[case( + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "4869205468657265", + "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7" + )] + #[case( + "4a656665", + "7768617420646f2079612077616e7420666f72206e6f7468696e673f", + "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843" + )] + #[case("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe")] + #[case("0102030405060708090a0b0c0d0e0f10111213141516171819", "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b")] + #[case( + "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "546573742057697468205472756e636174696f6e", + "a3b6167473100ee06e0c796c2955552bfa6f7c0a6a8aef8b93f860aab0cd20c5" + )] + #[case("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b6579202d2048617368204b6579204669727374", + "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54")] + #[case("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "5468697320697320612074657374207573696e672061206c6172676572207468616e20626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520686173686564206265666f7265206265696e6720757365642062792074686520484d414320616c676f726974686d2e", + "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2")] + fn test_hmac_sha256(#[case] key: &str, #[case] message: &str, #[case] expected: &str) { + let key = hex::decode(key).expect("Invalid hex key"); + let message = hex::decode(message).expect("Invalid hex message"); + + assert_eq!(hex::encode(hmac_sha256(&key, &message)), expected); + } +} diff --git a/src/hmac/mod.rs b/src/hmac/mod.rs new file mode 100644 index 00000000..ba36349e --- /dev/null +++ b/src/hmac/mod.rs @@ -0,0 +1,4 @@ +//! This module contains implementations of various hash-based message authentication code (HMAC) + +#![doc = include_str!("./README.md")] +pub mod hmac_sha256; diff --git a/src/lib.rs b/src/lib.rs index 40ae5dc5..efa14959 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,7 @@ pub mod curve; pub mod ecdsa; pub mod encryption; pub mod hashes; +pub mod hmac; pub mod kzg; pub mod polynomial; pub mod tree;