9898//! | Object | Size (bytes) |
9999//! |----------------------------------------------------------|--------------|
100100//! | `SHA224`, `SHA256` | 112 |
101- //! | `SHA384`, `SHA512`, `SHA512_224`, `SHA512_256` | 208 |
101+ //! | `SHA384`, `SHA512`, `SHA512t<T>` (incl. ` SHA512_224`, `SHA512_256`) | 208 |
102102//! | Suspended `SHA224`/`SHA256` state | 108 |
103- //! | Suspended `SHA384`/`SHA512`/`SHA512_224`/`SHA512_256` state | 204 |
103+ //! | Suspended `SHA384`/`SHA512`/`SHA512t<T>` state | 204 |
104+ //!
105+ //! `T` does not affect either size: the truncation happens on the way out of `do_final`, so every
106+ //! member of the SHA-512 family carries the same 512-bit chaining value and 1024-bit buffer.
104107//!
105108//! # Security Considerations
106109//!
107110//! * SHA-224/256/384/512 offer 112/128/192/256 bits of collision resistance respectively;
108- //! SHA-512/224 and SHA-512/256 offer 112 and 128 bits (SP 800-107r1, Table 1 (§4.2)).
111+ //! SHA-512/224 and SHA-512/256 offer 112 and 128 bits (SP 800-107r1, Table 1 (§4.2)). More
112+ //! generally SHA-512/t offers t/2 bits, which is what [`SHA512t`]'s `MAX_SECURITY_STRENGTH`
113+ //! reports, rounded down to a modelled level.
114+ //! * **Only two SHA-512/t truncations are approved.** [`SHA512t`] is generic over `T`, but FIPS
115+ //! 180-4 s. 5.3.6 approves only t = 224 and t = 256. Any other `T` is a well-defined hash that
116+ //! is nonetheless unapproved, and has to be constructed through
117+ //! [`SHA512Internal::new_allow_unapproved_t`](sha512::SHA512Internal::new_allow_unapproved_t)
118+ //! rather than `new()`; see [`SHA512t`] for the reasoning and the compile-time gate. Small `t`
119+ //! is also simply weak -- SHA-512/8 has a one-byte digest -- and carries a
120+ //! `SecurityStrength::None`.
109121//! * SHA-2 is a Merkle–Damgård construction and is therefore subject to length-extension:
110122//! `H(k || m)` is not a secure MAC. Use HMAC (`bouncycastle-hmac`) for keyed hashing.
111123//! * SHA-224, SHA-384, SHA-512/224 and SHA-512/256 are truncations of SHA-256 or SHA-512 with
@@ -162,9 +174,68 @@ pub type SHA256 = SHA256Internal<SHA256Params>;
162174pub type SHA384 = SHA512Internal < SHA384Params > ;
163175/// Public type for SHA512.
164176pub type SHA512 = SHA512Internal < SHA512Params > ;
165- /// Public type for the SHA-512/t truncating family (FIPS 180-4 s. 5.3.6): SHA-512 with a t-specific initial
166- /// hash value, truncated to `T` bits. Only the NIST-approved truncations `T = 224` and `T = 256`
167- /// can be instantiated, enforced by the sealing trait `SHA512InitValue`; see [`SHA512_224`] and [`SHA512_256`].
177+ /// Public type for the SHA-512/t truncating family (FIPS 180-4 s. 5.3.6): SHA-512 with a
178+ /// t-specific initial hash value, truncated to `T` bits.
179+ ///
180+ /// `T` may be any truncation the standard defines a hash for -- "any positive integer without a
181+ /// leading zero such that t < 512, and t is not 384" -- narrowed here to multiples of 8, since the
182+ /// digest has to be a whole number of bytes. Anything else is a compile error naming the rule it
183+ /// broke. The initial hash value is produced at compile time by the s. 5.3.6 IV Generation
184+ /// Function, so a new `T` costs nothing at runtime and needs no table.
185+ ///
186+ /// ```
187+ /// use bouncycastle_core::traits::Hash;
188+ /// use bouncycastle_sha2::{SHA512_256, SHA512t};
189+ ///
190+ /// // An approved truncation: the ordinary constructor.
191+ /// let digest = SHA512_256::new().hash(b"abc");
192+ /// assert_eq!(digest.len(), 32);
193+ ///
194+ /// // SHA512t<256> *is* SHA512_256.
195+ /// assert_eq!(SHA512t::<256>::new().hash(b"abc"), digest);
196+ /// ```
197+ ///
198+ /// # Only `T = 224` and `T = 256` are approved
199+ ///
200+ /// FIPS 180-4 s. 5.3.6 approves exactly two truncations, SHA-512/224 and SHA-512/256 ("Other
201+ /// SHA-512/t hash algorithms with different t values may be specified in [SP 800-107] in the
202+ /// future as the need arises"). Every other `T` is a well-defined SHA-512/t but not an approved
203+ /// hash algorithm, so it must not be used where an approved one is required.
204+ ///
205+ /// That distinction is enforced rather than merely documented, in the same shape as
206+ /// `ElectronicCodeBook::ENCRYPTION_APPROVED` in the cipher traits: the unapproved truncations carry
207+ /// [`SHA512tParams::FIPS_APPROVED`]` == false`, and
208+ /// [`SHA512Internal::new`](sha512::SHA512Internal::new) checks it in an inline `const`. Building
209+ /// one the ordinary way -- including through `Default`, and so through any generic code that
210+ /// requires it -- is therefore a compile error at the call site, and
211+ /// [`SHA512Internal::new_allow_unapproved_t`](sha512::SHA512Internal::new_allow_unapproved_t) is
212+ /// the way to say you meant it:
213+ ///
214+ /// ```
215+ /// use bouncycastle_core::traits::{Algorithm, Hash};
216+ /// use bouncycastle_sha2::{SHA512t, SHA512tParams};
217+ ///
218+ /// assert!(!SHA512tParams::<96>::FIPS_APPROVED);
219+ /// let digest = SHA512t::<96>::new_allow_unapproved_t().hash(b"");
220+ /// assert_eq!(digest.len(), 12);
221+ /// assert_eq!(<SHA512t<96> as Algorithm>::ALG_NAME, "SHA512/96");
222+ /// ```
223+ ///
224+ /// ```compile_fail
225+ /// use bouncycastle_sha2::SHA512t;
226+ /// // SHA-512/96 is not an approved hash algorithm, so `new()` does not build.
227+ /// let _ = SHA512t::<96>::new();
228+ /// ```
229+ ///
230+ /// ```compile_fail
231+ /// use bouncycastle_sha2::SHA512t;
232+ /// // FIPS 180-4 s. 5.3.6: "t is not 384" -- SHA384 is its own algorithm with its own IV.
233+ /// let _ = SHA512t::<384>::new_allow_unapproved_t();
234+ /// ```
235+ ///
236+ /// See [`SHA512_224`] and [`SHA512_256`] for the approved pair, which are aliases of this type and
237+ /// are additionally the only truncations with an assigned [`AlgorithmOID`] and a `HashFactory`
238+ /// entry.
168239pub type SHA512t < const T : usize > = SHA512Internal < SHA512tParams < T > > ;
169240/// Public type for SHA512/224 (FIPS 180-4 s. 6.6).
170241pub type SHA512_224 = SHA512t < 224 > ;
@@ -189,6 +260,16 @@ trait SHA256InitValue: HashAlgParams {
189260trait SHA512InitValue : HashAlgParams {
190261 /// The initial hash value H(0), FIPS 180-4 s. 5.3.4 / 5.3.5 / 5.3.6.
191262 const H0 : [ u64 ; 8 ] ;
263+
264+ /// Whether this parameter set is an approved hash algorithm.
265+ ///
266+ /// `true` for SHA-384, SHA-512 and the two approved truncations SHA-512/224 and SHA-512/256;
267+ /// `false` for every other SHA-512/t, which FIPS 180-4 s. 5.3.6 defines but does not approve.
268+ /// [`SHA512Internal::new`] checks this in an inline `const`, so constructing an unapproved
269+ /// truncation the ordinary way is a compile error at the call site and
270+ /// [`SHA512Internal::new_allow_unapproved_t`] is the deliberate way in -- the same shape as
271+ /// `ElectronicCodeBook::ENCRYPTION_APPROVED` in the cipher traits.
272+ const FIPS_APPROVED : bool = true ;
192273}
193274
194275/// The public hash types expose the same parameters as their `*Params` marker, so the constants
@@ -297,53 +378,104 @@ impl SHA512InitValue for SHA512Params {
297378/*** SHA-512/t ***/
298379/// The parameters for SHA-512/t (FIPS 180-4 s. 5.3.6), for a truncation of `T` bits.
299380///
300- /// The parameter traits are implemented only for the NIST-approved truncations `T = 224` and
301- /// `T = 256` ("Other SHA-512/t hash algorithms with different t values may be specified in
302- /// [SP 800-107] in the future as the need arises"), so any other `T` is a compile-time error.
381+ /// Implemented for every `T` the section defines a hash for, with two restrictions checked when
382+ /// the parameter set is instantiated, so a bad `T` is a compile error rather than a runtime one:
383+ ///
384+ /// * FIPS 180-4 s. 5.3.6's own rule, "t is any positive integer without a leading zero such that
385+ /// t < 512, and t is not 384";
386+ /// * this crate's additional requirement that `T` be a multiple of 8, since the digest has to be a
387+ /// whole number of bytes. See [`sha512::sha512t_h0`] for why.
388+ ///
389+ /// Only `T = 224` and `T = 256` are *approved* ("Other SHA-512/t hash algorithms with different t
390+ /// values may be specified in [SP 800-107] in the future as the need arises"); the rest are
391+ /// defined but unapproved, and are gated behind
392+ /// [`SHA512Internal::new_allow_unapproved_t`](sha512::SHA512Internal::new_allow_unapproved_t).
303393#[ derive( Clone ) ]
304394pub struct SHA512tParams < const T : usize > ;
305395
306- /*** SHA512/224 ***/
307- impl Algorithm for SHA512tParams < 224 > {
308- const ALG_NAME : & ' static str = SHA512_224_NAME ;
309- const MAX_SECURITY_STRENGTH : SecurityStrength = SecurityStrength :: _112bit;
396+ impl < const T : usize > SHA512tParams < T > {
397+ /// Whether SHA-512/`T` is an approved hash algorithm: FIPS 180-4 s. 5.3.6 approves only
398+ /// t = 224 and t = 256.
399+ ///
400+ /// This is what [`SHA512Internal::new`](sha512::SHA512Internal::new) gates on, so it is also
401+ /// the answer to "does this truncation need
402+ /// [`new_allow_unapproved_t`](sha512::SHA512Internal::new_allow_unapproved_t)?". Public so a
403+ /// caller can make the same check -- `const { assert!(SHA512tParams::<T>::FIPS_APPROVED) }` in
404+ /// generic code -- without reaching into the sealed parameter trait.
405+ pub const FIPS_APPROVED : bool = sha512:: t_is_fips_approved ( T ) ;
406+
407+ /// `"SHA512/t"` with `T` in decimal, NUL-padded; see [`Self::ALG_NAME_STR`].
408+ const ALG_NAME_BYTES : [ u8 ; sha512:: ALG_NAME_BUF_LEN ] = sha512:: alg_name_bytes ( T ) ;
409+
410+ /// The algorithm name, e.g. `"SHA512/224"`. Built at compile time from `T` because a const
411+ /// generic cannot be formatted into a `&'static str` directly.
412+ const ALG_NAME_STR : & ' static str = {
413+ let bytes: & ' static [ u8 ; sha512:: ALG_NAME_BUF_LEN ] = & Self :: ALG_NAME_BYTES ;
414+ let ( name, _padding) = bytes. split_at ( sha512:: alg_name_len ( T ) ) ;
415+ match core:: str:: from_utf8 ( name) {
416+ Ok ( name) => name,
417+ // unreachable: alg_name_bytes writes only ASCII.
418+ Err ( _) => panic ! ( "SHA-512/t algorithm name is not UTF-8" ) ,
419+ }
420+ } ;
421+ }
422+
423+ impl < const T : usize > Algorithm for SHA512tParams < T > {
424+ const ALG_NAME : & ' static str = Self :: ALG_NAME_STR ;
425+ /// SP 800-107 Rev 1 Table 1: a t-bit digest offers t/2 bits of collision resistance, rounded
426+ /// down to a modelled level. This reproduces the values the two approved truncations carry:
427+ /// 112-bit for SHA-512/224 and 128-bit for SHA-512/256.
428+ const MAX_SECURITY_STRENGTH : SecurityStrength = SecurityStrength :: from_bits ( T / 2 ) ;
310429}
311- impl HashAlgParams for SHA512tParams < 224 > {
312- const OUTPUT_LEN : usize = 28 ; // FIPS 180-4 s. 6.6 exception 2: truncated to the left-most 224 bits
430+ impl < const T : usize > HashAlgParams for SHA512tParams < T > {
431+ /// FIPS 180-4 s. 6.6 / s. 6.7 exception 2: truncated to the left-most `T` bits. `T` is a
432+ /// multiple of 8 (checked by [`sha512::check_t`]), so this is exact.
433+ const OUTPUT_LEN : usize = T / 8 ;
313434 const BLOCK_LEN : usize = 128 ; // FIPS 180-4 Figure 1: block size 1024 bits
314435}
436+ impl < const T : usize > SHA512InitValue for SHA512tParams < T > {
437+ /// FIPS 180-4 s. 5.3.6: H(0) from the IV Generation Function. For t = 224 and t = 256 this is
438+ /// the value listed in s. 5.3.6.1 / s. 5.3.6.2, pinned against those words by
439+ /// `tests/sha512t_h0_tests.rs`.
440+ const H0 : [ u64 ; 8 ] = sha512t_h0 ( T ) ;
441+ // Not recursive: inherent associated consts win name resolution, so this is the public
442+ // `SHA512tParams::<T>::FIPS_APPROVED` above, forwarded so the two cannot disagree.
443+ const FIPS_APPROVED : bool = Self :: FIPS_APPROVED ;
444+ }
445+
446+ // The two approved truncations get everything else from the generic impls above; only their
447+ // object identifiers, which exist for no other t, are specific to them.
448+
449+ /*** SHA512/224 ***/
315450/// Assigned by NIST in the Computer Security Objects Register: id-sha512-224 { hashAlgs 5 }
316451impl AlgorithmOID for SHA512_224 {
317452 const OID : & ' static [ u32 ] = & [ 2 , 16 , 840 , 1 , 101 , 3 , 4 , 2 , 5 ] ;
318453 const OID_DER : & ' static [ u8 ] =
319454 & [ 0x06 , 0x09 , 0x60 , 0x86 , 0x48 , 0x01 , 0x65 , 0x03 , 0x04 , 0x02 , 0x05 ] ;
320455}
321- impl SHA512InitValue for SHA512tParams < 224 > {
322- // FIPS 180-4 s. 6.6 exception 1: H(0) as specified in s. 5.3.6.1 (pinned against the words
323- // listed there by tests/sha512t_h0_tests.rs).
324- const H0 : [ u64 ; 8 ] = sha512t_h0 ( 224 ) ;
325- }
326456
327457/*** SHA512/256 ***/
328- impl Algorithm for SHA512tParams < 256 > {
329- const ALG_NAME : & ' static str = SHA512_256_NAME ;
330- const MAX_SECURITY_STRENGTH : SecurityStrength = SecurityStrength :: _128bit;
331- }
332- impl HashAlgParams for SHA512tParams < 256 > {
333- const OUTPUT_LEN : usize = 32 ; // FIPS 180-4 s. 6.7 exception 2: truncated to the left-most 256 bits
334- const BLOCK_LEN : usize = 128 ; // FIPS 180-4 Figure 1: block size 1024 bits
335- }
336458/// Assigned by NIST in the Computer Security Objects Register: id-sha512-256 { hashAlgs 6 }
337459impl AlgorithmOID for SHA512_256 {
338460 const OID : & ' static [ u32 ] = & [ 2 , 16 , 840 , 1 , 101 , 3 , 4 , 2 , 6 ] ;
339461 const OID_DER : & ' static [ u8 ] =
340462 & [ 0x06 , 0x09 , 0x60 , 0x86 , 0x48 , 0x01 , 0x65 , 0x03 , 0x04 , 0x02 , 0x06 ] ;
341463}
342- impl SHA512InitValue for SHA512tParams < 256 > {
343- // FIPS 180-4 s. 6.7 exception 1: H(0) as specified in s. 5.3.6.2 (pinned against the words
344- // listed there by tests/sha512t_h0_tests.rs).
345- const H0 : [ u64 ; 8 ] = sha512t_h0 ( 256 ) ;
346- }
464+
465+ // The generic name and output length must keep reproducing exactly what the two approved
466+ // truncations had when they were spelled out by hand, and the two approved truncations must stay
467+ // the only approved ones. `cargo mutants` cannot see a const assertion fail, so these are paired
468+ // with the runtime coverage in tests/sha512t_tests.rs rather than replacing it.
469+ const _: ( ) = assert ! ( matches!( SHA512tParams :: <224 >:: ALG_NAME_STR . as_bytes( ) , b"SHA512/224" ) ) ;
470+ const _: ( ) = assert ! ( matches!( SHA512tParams :: <256 >:: ALG_NAME_STR . as_bytes( ) , b"SHA512/256" ) ) ;
471+ const _: ( ) = assert ! ( matches!( SHA512_224_NAME . as_bytes( ) , b"SHA512/224" ) ) ;
472+ const _: ( ) = assert ! ( matches!( SHA512_256_NAME . as_bytes( ) , b"SHA512/256" ) ) ;
473+ const _: ( ) = assert ! ( SHA512tParams :: <224 >:: OUTPUT_LEN == 28 ) ;
474+ const _: ( ) = assert ! ( SHA512tParams :: <256 >:: OUTPUT_LEN == 32 ) ;
475+ const _: ( ) = assert ! ( SHA512tParams :: <224 >:: FIPS_APPROVED ) ;
476+ const _: ( ) = assert ! ( SHA512tParams :: <256 >:: FIPS_APPROVED ) ;
477+ const _: ( ) = assert ! ( !SHA512tParams :: <8 >:: FIPS_APPROVED ) ;
478+ const _: ( ) = assert ! ( !SHA512tParams :: <504 >:: FIPS_APPROVED ) ;
347479
348480pub use sha256:: SUSPENDED_SHA256_STATE_LEN ;
349481pub use sha512:: SUSPENDED_SHA512_STATE_LEN ;
0 commit comments