Skip to content

Commit c912e80

Browse files
committed
Extend nss-rs with SHA3 support
1 parent 78715d7 commit c912e80

4 files changed

Lines changed: 233 additions & 8 deletions

File tree

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ serde_derive = { version = "1.0", default-features = false }
3030
toml = { version = "0.5", default-features = false }
3131

3232
[dev-dependencies]
33+
hex = "0.4"
3334
test-fixture = {path = "test-fixture"}
3435

3536
[package.metadata.cargo-machete]

src/hash.rs

Lines changed: 222 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
// option. This file may not be copied, modified, or distributed
55
// except according to those terms.
66

7+
//! Cryptographic hash functions.
8+
//!
9+
//! This module provides access to NSS's hash function implementations,
10+
//! supporting both SHA-2 and SHA-3 families.
11+
712
use std::convert::TryFrom as _;
813

914
use crate::{
@@ -13,37 +18,65 @@ use crate::{
1318
Error,
1419
};
1520

16-
//
17-
// Constants
18-
//
19-
21+
/// Supported hash algorithms.
22+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2023
pub enum HashAlgorithm {
24+
/// SHA-2 256-bit (32 bytes output)
2125
SHA2_256,
26+
/// SHA-2 384-bit (48 bytes output)
2227
SHA2_384,
28+
/// SHA-2 512-bit (64 bytes output)
2329
SHA2_512,
30+
/// SHA-3 224-bit (28 bytes output)
31+
SHA3_224,
32+
/// SHA-3 256-bit (32 bytes output)
33+
SHA3_256,
34+
/// SHA-3 384-bit (48 bytes output)
35+
SHA3_384,
36+
/// SHA-3 512-bit (64 bytes output)
37+
SHA3_512,
2438
}
2539

2640
const fn hash_alg_to_oid(alg: &HashAlgorithm) -> SECOidTag::Type {
2741
match alg {
2842
HashAlgorithm::SHA2_256 => SECOidTag::SEC_OID_SHA256,
2943
HashAlgorithm::SHA2_384 => SECOidTag::SEC_OID_SHA384,
3044
HashAlgorithm::SHA2_512 => SECOidTag::SEC_OID_SHA512,
45+
HashAlgorithm::SHA3_224 => SECOidTag::SEC_OID_SHA3_224,
46+
HashAlgorithm::SHA3_256 => SECOidTag::SEC_OID_SHA3_256,
47+
HashAlgorithm::SHA3_384 => SECOidTag::SEC_OID_SHA3_384,
48+
HashAlgorithm::SHA3_512 => SECOidTag::SEC_OID_SHA3_512,
3149
}
3250
}
3351

52+
/// Returns the output length in bytes for the given hash algorithm.
3453
#[must_use]
3554
pub const fn hash_alg_to_hash_len(alg: &HashAlgorithm) -> usize {
3655
match alg {
3756
HashAlgorithm::SHA2_256 => p11::SHA256_LENGTH as usize,
3857
HashAlgorithm::SHA2_384 => p11::SHA384_LENGTH as usize,
3958
HashAlgorithm::SHA2_512 => p11::SHA512_LENGTH as usize,
59+
HashAlgorithm::SHA3_224 => 28,
60+
HashAlgorithm::SHA3_256 => p11::SHA3_256_LENGTH as usize,
61+
HashAlgorithm::SHA3_384 => p11::SHA3_384_LENGTH as usize,
62+
HashAlgorithm::SHA3_512 => p11::SHA3_512_LENGTH as usize,
4063
}
4164
}
4265

43-
//
44-
// Hash function
45-
//
46-
66+
/// Compute a cryptographic hash of the input data.
67+
///
68+
/// # Arguments
69+
///
70+
/// * `alg` - The hash algorithm to use.
71+
/// * `data` - The data to hash.
72+
///
73+
/// # Returns
74+
///
75+
/// The hash digest as a byte vector.
76+
///
77+
/// # Errors
78+
///
79+
/// Returns an error if NSS initialization fails or the hash operation fails.
4780
pub fn hash(alg: &HashAlgorithm, data: &[u8]) -> Result<Vec<u8>, Error> {
4881
init()?;
4982

@@ -64,3 +97,184 @@ pub fn hash(alg: &HashAlgorithm, data: &[u8]) -> Result<Vec<u8>, Error> {
6497
};
6598
Ok(digest)
6699
}
100+
101+
/// Convenience function for SHA-256 hash.
102+
///
103+
/// # Errors
104+
///
105+
/// Returns an error if NSS initialization fails or the hash operation fails.
106+
pub fn sha256(data: &[u8]) -> Result<[u8; 32], Error> {
107+
let digest = hash(&HashAlgorithm::SHA2_256, data)?;
108+
let mut result = [0u8; 32];
109+
result.copy_from_slice(&digest);
110+
Ok(result)
111+
}
112+
113+
/// Convenience function for SHA-384 hash.
114+
///
115+
/// # Errors
116+
///
117+
/// Returns an error if NSS initialization fails or the hash operation fails.
118+
pub fn sha384(data: &[u8]) -> Result<[u8; 48], Error> {
119+
let digest = hash(&HashAlgorithm::SHA2_384, data)?;
120+
let mut result = [0u8; 48];
121+
result.copy_from_slice(&digest);
122+
Ok(result)
123+
}
124+
125+
/// Convenience function for SHA-512 hash.
126+
///
127+
/// # Errors
128+
///
129+
/// Returns an error if NSS initialization fails or the hash operation fails.
130+
pub fn sha512(data: &[u8]) -> Result<[u8; 64], Error> {
131+
let digest = hash(&HashAlgorithm::SHA2_512, data)?;
132+
let mut result = [0u8; 64];
133+
result.copy_from_slice(&digest);
134+
Ok(result)
135+
}
136+
137+
/// Convenience function for SHA3-224 hash.
138+
///
139+
/// # Errors
140+
///
141+
/// Returns an error if NSS initialization fails or the hash operation fails.
142+
pub fn sha3_224(data: &[u8]) -> Result<[u8; 28], Error> {
143+
let digest = hash(&HashAlgorithm::SHA3_224, data)?;
144+
let mut result = [0u8; 28];
145+
result.copy_from_slice(&digest);
146+
Ok(result)
147+
}
148+
149+
/// Convenience function for SHA3-256 hash.
150+
///
151+
/// # Errors
152+
///
153+
/// Returns an error if NSS initialization fails or the hash operation fails.
154+
pub fn sha3_256(data: &[u8]) -> Result<[u8; 32], Error> {
155+
let digest = hash(&HashAlgorithm::SHA3_256, data)?;
156+
let mut result = [0u8; 32];
157+
result.copy_from_slice(&digest);
158+
Ok(result)
159+
}
160+
161+
/// Convenience function for SHA3-384 hash.
162+
///
163+
/// # Errors
164+
///
165+
/// Returns an error if NSS initialization fails or the hash operation fails.
166+
pub fn sha3_384(data: &[u8]) -> Result<[u8; 48], Error> {
167+
let digest = hash(&HashAlgorithm::SHA3_384, data)?;
168+
let mut result = [0u8; 48];
169+
result.copy_from_slice(&digest);
170+
Ok(result)
171+
}
172+
173+
/// Convenience function for SHA3-512 hash.
174+
///
175+
/// # Errors
176+
///
177+
/// Returns an error if NSS initialization fails or the hash operation fails.
178+
pub fn sha3_512(data: &[u8]) -> Result<[u8; 64], Error> {
179+
let digest = hash(&HashAlgorithm::SHA3_512, data)?;
180+
let mut result = [0u8; 64];
181+
result.copy_from_slice(&digest);
182+
Ok(result)
183+
}
184+
185+
#[cfg(test)]
186+
#[cfg_attr(coverage_nightly, coverage(off))]
187+
mod tests {
188+
use test_fixture::fixture_init;
189+
190+
use super::*;
191+
192+
#[test]
193+
fn test_sha256() {
194+
fixture_init();
195+
let data = b"hello world";
196+
let digest = sha256(data).unwrap();
197+
assert_eq!(digest.len(), 32);
198+
// Known SHA-256 hash of "hello world"
199+
assert_eq!(
200+
hex::encode(digest),
201+
"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
202+
);
203+
}
204+
205+
#[test]
206+
fn test_sha384() {
207+
fixture_init();
208+
let data = b"hello world";
209+
let digest = sha384(data).unwrap();
210+
assert_eq!(digest.len(), 48);
211+
}
212+
213+
#[test]
214+
fn test_sha512() {
215+
fixture_init();
216+
let data = b"hello world";
217+
let digest = sha512(data).unwrap();
218+
assert_eq!(digest.len(), 64);
219+
}
220+
221+
#[test]
222+
fn test_sha3_224() {
223+
fixture_init();
224+
let data = b"hello world";
225+
let digest = sha3_224(data).unwrap();
226+
assert_eq!(digest.len(), 28);
227+
}
228+
229+
#[test]
230+
fn test_sha3_256() {
231+
fixture_init();
232+
let data = b"hello world";
233+
let digest = sha3_256(data).unwrap();
234+
assert_eq!(digest.len(), 32);
235+
// Known SHA3-256 hash of "hello world"
236+
assert_eq!(
237+
hex::encode(digest),
238+
"644bcc7e564373040999aac89e7622f3ca71fba1d972fd94a31c3bfbf24e3938"
239+
);
240+
}
241+
242+
#[test]
243+
fn test_sha3_384() {
244+
fixture_init();
245+
let data = b"hello world";
246+
let digest = sha3_384(data).unwrap();
247+
assert_eq!(digest.len(), 48);
248+
}
249+
250+
#[test]
251+
fn test_sha3_512() {
252+
fixture_init();
253+
let data = b"hello world";
254+
let digest = sha3_512(data).unwrap();
255+
assert_eq!(digest.len(), 64);
256+
}
257+
258+
#[test]
259+
fn test_hash_empty() {
260+
fixture_init();
261+
let data = b"";
262+
let digest = sha256(data).unwrap();
263+
// Known SHA-256 hash of empty string
264+
assert_eq!(
265+
hex::encode(digest),
266+
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
267+
);
268+
}
269+
270+
#[test]
271+
fn test_hash_algorithm_lengths() {
272+
assert_eq!(hash_alg_to_hash_len(&HashAlgorithm::SHA2_256), 32);
273+
assert_eq!(hash_alg_to_hash_len(&HashAlgorithm::SHA2_384), 48);
274+
assert_eq!(hash_alg_to_hash_len(&HashAlgorithm::SHA2_512), 64);
275+
assert_eq!(hash_alg_to_hash_len(&HashAlgorithm::SHA3_224), 28);
276+
assert_eq!(hash_alg_to_hash_len(&HashAlgorithm::SHA3_256), 32);
277+
assert_eq!(hash_alg_to_hash_len(&HashAlgorithm::SHA3_384), 48);
278+
assert_eq!(hash_alg_to_hash_len(&HashAlgorithm::SHA3_512), 64);
279+
}
280+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ pub use self::{
7979
KemId, SymmetricSuite,
8080
},
8181
err::{secstatus_to_res, Error, IntoResult, PRErrorCode, Res},
82+
hash::{
83+
hash, sha256, sha384, sha3_224, sha3_256, sha3_384, sha3_512, sha512, HashAlgorithm,
84+
},
8285
ext::{ExtensionHandler, ExtensionHandlerResult, ExtensionWriterResult},
8386
p11::{random, randomize, PrivateKey, PublicKey, SymKey},
8487
replay::AntiReplay,

0 commit comments

Comments
 (0)