Skip to content

Commit

Permalink
Adding the HMAC-SHA256 (#147)
Browse files Browse the repository at this point in the history
* Adding the HMAC-SHA256

* Adding a very simple binary

* Used the cargo formatter
  • Loading branch information
hecmas authored Aug 11, 2024
1 parent f871493 commit ed9de5d
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
28 changes: 28 additions & 0 deletions src/hmac/README.md
Original file line number Diff line number Diff line change
@@ -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.
21 changes: 21 additions & 0 deletions src/hmac/bin/hmac_sha256_bin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use std::env;

use ronkathon::hmac::hmac_sha256::hmac_sha256;

fn main() {
// Get command-line arguments
let args: Vec<String> = 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);
}
113 changes: 113 additions & 0 deletions src/hmac/hmac_sha256.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
4 changes: 4 additions & 0 deletions src/hmac/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit ed9de5d

Please sign in to comment.