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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
//! # Internal Utilities – Endianness & Constant‑Time Comparison
//!
//! This module provides the low‑level helper functions used throughout the
//! `libvctrl_sha512` crate. None of these items are part of the public API,
//! but they are documented here for maintainers and security reviewers.
//!
//! ## Contents
//!
//! - **Constants** – the SHA‑512 block size and output size, available as
//! [`BLOCKBYTES`] and [`BYTES`].
//! - **Big‑endian conversion** – [`load_be`] and [`store_be`] convert between
//! byte slices and `u64` values in a portable, efficient way.
//! - **Constant‑time comparison** – [`verify`] compares two byte slices in a
//! way that does not depend on the data, preventing timing side‑channel
//! leakage.
//!
//! ## Security Considerations
//!
//! ### `verify`
//!
//! The `verify` function is used for every MAC and hash comparison in this
//! crate. It has the following properties:
//!
//! - **Length check first** – if the slices have different lengths, the
//! function returns `false` immediately. This is safe because the length
//! is always publicly known (e.g., a 64‑byte HMAC).
//! - **Bitwise accumulation** – for each byte pair `(a, b)`, the XOR
//! difference `a ^ b` is OR‑ed into an accumulator `v`. The loop does not
//! branch on the value of the bytes, so its runtime is independent of the
//! number of matching bytes.
//! - **Volatile read barrier** – before the final comparison, `v` is read
//! through `core::ptr::read_volatile`. This prevents the compiler from
//! optimizing away the accumulation loop or short‑circuiting the final
//! check, both of which could leak timing information.
//! - **WebAssembly hardening** – on `wasm32` and `wasm64` targets, an
//! additional mixing step is performed before the byte‑wise XOR. This
//! compensates for the lack of a constant‑time instruction set in some
//! WASM runtimes.
//!
//! ### Endian‑handling
//!
//! `load_be` and `store_be` use `u64::from_be_bytes` and `u64::to_be_bytes`,
//! which are guaranteed to compile to efficient single‑instruction loads and
//! stores on both big‑ and little‑endian architectures. This is both more
//! readable and more optimizer‑friendly than manual byte shuffling.
//!
//! ## Examples
//!
//! The utilities are `pub` within the crate. Typical usage looks like this:
//!
//! ```rust
//! use libvctrl_sha512::utils::verify;
//!
//! let a = [0xAB; 64];
//! let b = [0xAB; 64];
//! assert!(verify(&a, &b));
//!
//! let c = [0xCD; 64];
//! assert!(!verify(&a, &c));
//! ```
/// SHA‑512 block size in bytes.
///
/// Every complete message block processed by the compression function is
/// exactly 128 bytes (1024 bits). This constant is used internally for buffer
/// management and padding.
pub const BLOCKBYTES: usize = 128;
/// SHA‑512 output size in bytes.
///
/// The final digest is always 64 bytes (512 bits). This constant is re‑exported
/// at the crate root for convenience.
pub const BYTES: usize = 64;
/// Load a big‑endian `u64` from `base` starting at `offset`.
///
/// This is equivalent to reading 8 bytes from `base[offset..offset+8]` and
/// interpreting them as a big‑endian unsigned 64‑bit integer. It is used to
/// load the message schedule words and initial vector values.
///
/// # Panics
///
/// Panics if `offset + 8` exceeds the length of `base`. In practice, all
/// callers ensure sufficient buffer space, so this panic indicates a bug.
///
/// # Performance
///
/// Uses [`u64::from_be_bytes`], which maps to a single `bswap` instruction
/// on little‑endian targets and a simple load on big‑endian targets.
///
/// # Example
///
/// ```rust
/// use libvctrl_sha512::utils::load_be;
///
/// let bytes = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
/// let value = load_be(&bytes, 0);
/// assert_eq!(value, 0x0102030405060708);
/// ```
/// Store a `u64` as big‑endian bytes into `base` starting at `offset`.
///
/// Writes the 8‑byte big‑endian representation of `x` into
/// `base[offset..offset+8]`. This is used to write the final hash digest and
/// the message length in the padding block.
///
/// # Panics
///
/// Panics if `offset + 8` exceeds the length of `base`.
///
/// # Performance
///
/// Uses [`u64::to_be_bytes`], which generates optimal code on all
/// architectures.
///
/// # Example
///
/// ```rust
/// use libvctrl_sha512::utils::store_be;
///
/// let mut buffer = [0u8; 8];
/// store_be(&mut buffer, 0, 0x0102030405060708);
/// assert_eq!(buffer, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
/// ```
/// Constant‑time comparison of two byte slices.
///
/// This function is used to compare digests, HMAC tags, and other
/// cryptographic material without leaking information about the byte values
/// through timing.
///
/// # Algorithm
///
/// 1. If `x.len() != y.len()`, return `false` immediately (lengths are
/// public knowledge in our protocols, so this does not leak secrets).
/// 2. On WebAssembly targets (`wasm32`/`wasm64`), an additional mixing
/// phase is applied to each byte before XORing, because some WASM
/// runtimes do not guarantee constant‑time behaviour for all operations.
/// 3. The XOR of each corresponding byte pair is accumulated into a `u32`
/// accumulator `v`. The loop processes all bytes unconditionally.
/// 4. A volatile read of `v` prevents compiler optimizations from
/// short‑circuiting the comparison.
/// 5. The result is `v == 0`, i.e., `true` only if all bytes matched.
///
/// # Security
///
/// This implementation follows the recommendations from several widely
/// deployed cryptographic libraries. The volatile barrier is a common
/// idiom to stop the optimizer from collapsing the comparison into a
/// data‑dependent branch. The additional WASM mixing compensates for
/// the fact that some WASM engines may not implement bitwise operations
/// in constant time.
///
/// # Why not use `subtle`?
///
/// This crate intentionally avoids external dependencies to keep the
/// trusted code base minimal. The `verify` function is small and
/// well‑audited, making it suitable for the security needs of SHA‑512,
/// HMAC, and HKDF.
///
/// # Examples
///
/// ```rust
/// use libvctrl_sha512::utils::verify;
///
/// let a = [0xAB; 64];
/// let b = [0xAB; 64];
/// assert!(verify(&a, &b));
/// assert!(!verify(&a, &[0; 64]));
/// ```