1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! Utility functions and constants used by the SHA-512, HMAC, and HKDF
//! implementations.
//!
//! # Why this module exists
//!
//! This module centralizes low-level helpers that are shared across multiple
//! hash and MAC constructs:
//!
//! - Byte-order conversion between big-endian and native representation.
//! - Constant-time comparison of byte slices, mitigating timing side-channel
//! attacks during MAC verification.
//! - Common constants such as the SHA-512 block size and output size.
//!
//! By keeping these utilities in one place, the rest of the crate remains
//! focused on algorithm-specific logic without duplicating foundational code.
//!
//! # How it works
//!
//! The [`load_be`] and [`store_be`] functions convert between byte arrays and
//! 64-bit integers using big-endian order, as required by FIPS 180-4.
//! [`verify`] compares two byte slices of equal length using an XOR
//! accumulation loop and `core::hint::black_box` to prevent the compiler from
//! short-circuiting or optimizing away the comparison. This ensures that
//! verification time does not leak information about the compared values.
/// The SHA-512 block size in bytes.
///
/// Each compression round processes exactly 128 bytes (1024 bits). This
/// constant is used for padding, buffering, and HMAC key preparation.
///
/// # Examples
///
/// ```
/// use libvctrl_sha512::utils::BLOCKBYTES;
/// assert_eq!(BLOCKBYTES, 128);
/// ```
pub const BLOCKBYTES: usize = 128;
/// The SHA-512 output size in bytes.
///
/// A SHA-512 digest is always 64 bytes (512 bits). This constant is used by
/// HMAC and HKDF to size output arrays and PRKs.
///
/// # Examples
///
/// ```
/// use libvctrl_sha512::utils::BYTES;
/// assert_eq!(BYTES, 64);
/// ```
pub const BYTES: usize = 64;
/// Loads a 64-bit big-endian integer from the given byte slice at the
/// specified offset.
///
/// # How it works
///
/// The function reads eight bytes starting at `offset`, converts them to a
/// `u64` using `from_be_bytes`, and returns the result. It expects the slice
/// to contain at least `offset + 8` bytes; if not, it panics.
///
/// # Panics
///
/// Panics if `base.len() < offset + 8`.
///
/// # Examples
///
/// ```
/// use libvctrl_sha512::utils::load_be;
///
/// let bytes = [0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0];
/// assert_eq!(load_be(&bytes, 0), 0x123456789abcdef0);
/// ```
/// Stores a 64-bit integer into the given byte slice at the specified offset
/// in big-endian order.
///
/// # How it works
///
/// The function converts `x` to its big-endian byte representation and writes
/// it into `base` starting at `offset`. It assumes the slice is large enough
/// to hold eight bytes at that position.
///
/// # Panics
///
/// Panics if `base.len() < offset + 8`.
///
/// # Examples
///
/// ```
/// use libvctrl_sha512::utils::{load_be, store_be};
///
/// let mut buf = [0u8; 8];
/// store_be(&mut buf, 0, 0x0102030405060708);
/// assert_eq!(load_be(&buf, 0), 0x0102030405060708);
/// ```
/// Compares two byte slices of equal length in constant-ish time.
///
/// # Why this exists
///
/// When verifying MACs or digests, a naive `==` comparison may return early
/// on the first differing byte, leaking information about the expected value
/// through timing. This function accumulates differences across all bytes and
/// only returns a boolean at the end, making the runtime independent of the
/// number of leading matches.
///
/// # How it works
///
/// - If the lengths differ, it returns `false` immediately (length is not
/// secret).
/// - Otherwise, it XORs each corresponding byte pair and ORs the result into
/// an accumulator.
/// - On WebAssembly targets, an additional hash-based mask is applied to
/// mitigate compiler optimizations.
/// - Finally, `core::hint::black_box` is used to force the compiler to
/// materialize the accumulator before comparison, preventing it from
/// optimizing away the loop.
///
/// # Examples
///
/// ```
/// use libvctrl_sha512::utils::verify;
///
/// let a = [0u8; 64];
/// let b = [0u8; 64];
/// assert!(verify(&a, &b));
///
/// let c = [1u8; 64];
/// assert!(!verify(&a, &c));
/// ```