| Crates.io | bitcoin-hmac-sha512 |
| lib.rs | bitcoin-hmac-sha512 |
| version | 0.1.1 |
| created_at | 2025-12-01 04:07:44.121117+00 |
| updated_at | 2025-12-01 04:07:44.121117+00 |
| description | Low-level, FFI-friendly HMAC-SHA-512 implementation for Bitcoin-style cryptographic workflows, using raw pointers and fixed-size buffers for high-performance message authentication. |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1959261 |
| size | 96,746 |
A minimal, FFI‑friendly HMAC‑SHA‑512 implementation tailored for Bitcoin-style cryptographic workflows.
This crate exposes a low-level HmacSha512 type that:
*const u8) and explicit lengths for data and keys.pub struct HmacSha512 {
outer: Sha512,
inner: Sha512,
}
impl HmacSha512 {
pub fn new(key: *const u8, keylen: usize) -> Self;
pub fn write(&mut self, data: *const u8, len: usize) -> &mut HmacSha512;
pub fn finalize(&mut self, hash: &mut [u8; HMAC_SHA512_OUTPUT_SIZE]);
pub fn finalize_to_array(self) -> [u8; 64];
}
HMAC_SHA512_OUTPUT_SIZE is expected to be 64 (the SHA‑512 digest size in bytes).
use bitcoin_hmac_sha512::HmacSha512;
// Example key and message
let key: [u8; 32] = [0x11; 32];
let msg: [u8; 16] = [0x22; 16];
let mut hmac = unsafe { HmacSha512::new(key.as_ptr(), key.len()) };
Semantics of new:
keylen <= 128 (one SHA‑512 block):
keylen > 128:
rkey, the inner/outer pads are created:
outer_key = rkey ^ 0x5cinner_key = rkey ^ 0x36 (via rkey ^ (0x5c ^ 0x36) in the code)Sha512 states are initialized by absorbing these pads.This is the canonical HMAC key-derivation procedure.
write)unsafe {
hmac.write(msg.as_ptr(), msg.len());
}
// Additional chunks may follow; the state is streaming.
Properties:
write feeds bytes into the inner SHA‑512 state.write(a); write(b) ≡ write(a || b).&mut HmacSha512 to allow chaining.Example:
unsafe {
HmacSha512::new(key.as_ptr(), key.len())
.write(b"hello".as_ptr(), 5)
.write(b" world".as_ptr(), 6);
}
There are two ways to obtain the final 64‑byte MAC.
finalize(&mut self, hash: &mut [u8; 64])let mut hmac = unsafe { HmacSha512::new(key.as_ptr(), key.len()) };
unsafe { hmac.write(msg.as_ptr(), msg.len()); }
let mut out = [0u8; 64];
hmac.finalize(&mut out);
// `out` now contains the HMAC-SHA-512 tag.
Mechanics:
Sha512 is finalized into a 64‑byte temporary buffer.Sha512 state.hash.This matches the formal HMAC definition:
[ \operatorname{HMAC}_k(m) = H\bigl((k'\oplus opad) ; || ; H((k'\oplus ipad) || m)\bigr) ]
where H = SHA‑512 and k' is the block‑sized key derived from k.
finalize_to_array(self) -> [u8; 64]let tag: [u8; 64] = unsafe {
HmacSha512::new(key.as_ptr(), key.len())
.write(msg.as_ptr(), msg.len())
.finalize_to_array()
};
This convenience method consumes self, internally calls finalize, and returns the 64‑byte array by value.
This crate is intentionally low‑level and uses raw pointers for maximum interop and minimal abstraction.
unsafe requirementsAll of the following must hold whenever you call functions that take pointers:
key and data pointers must be valid for reads of keylen/len bytes.u8 (trivially true for standard slices/arrays).ptr::copy_nonoverlapping.Therefore, you will usually write:
let key: Vec<u8> = obtain_key();
let msg: Vec<u8> = obtain_msg();
let mut hmac = unsafe { HmacSha512::new(key.as_ptr(), key.len()) };
unsafe { hmac.write(msg.as_ptr(), msg.len()); }
let mut out = [0u8; 64];
hmac.finalize(&mut out);
HmacSha512 instances are not intrinsically synchronized. Treat them as single‑threaded objects: one instance per logical computation, no concurrent mutation across threads without external synchronization.
The snippet does not explicitly zeroize:
rkey after initialization.Sha512 after use.If you need strict key erasure semantics (e.g., side‑channel‑aware zeroization), verify the implementation of Sha512 and consider wrapping usage with a secure‑memory abstraction.
Notably, HMAC-SHA-512 is ubiquitous in:
HMAC is defined as:
[ \operatorname{HMAC}_k(m) = H\bigl((k'\oplus opad) ; || ; H((k'\oplus ipad) || m)\bigr) ]
H is a cryptographic hash function (here: SHA‑512).k is the secret key; k' is k either truncated or hashed and then padded to the block size B=128 bytes.ipad is the byte 0x36 repeated B times.opad is the byte 0x5c repeated B times.This construction provides:
The implementation in this crate follows the standard two‑pass formulation using two Sha512 instances and XOR padding.
When verifying an HMAC tag, always compare in constant time to avoid timing side channels.
fn ct_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() { return false; }
let mut acc = 0u8;
for (&x, &y) in a.iter().zip(b.iter()) {
acc |= x ^ y;
}
acc == 0
}
fn verify_tag(key: &[u8], msg: &[u8], expected: &[u8; 64]) -> bool {
let mut out = [0u8; 64];
let mut hmac = unsafe { HmacSha512::new(key.as_ptr(), key.len()) };
unsafe { hmac.write(msg.as_ptr(), msg.len()); }
hmac.finalize(&mut out);
ct_eq(&out, expected)
}
You may wish to replace ct_eq with a formally audited constant‑time comparison from another crate when integrating into production systems.
std, but verify the manifest and any additional modules before assuming #![no_std] compatibility.0.1.1 as indicated by the user. If using another version, confirm that the API matches.