Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ cargo run --release -p mem_usage_benches --bin bench_mldsa_mem_usage

The workspace has three top-level kinds of member:

1. `crypto/*` — one sub-crate per primitive (`sha2`, `sha3`, `hmac`, `hkdf`, `mlkem`, `mlkem_lowmemory`, `mldsa`, `mldsa_lowmemory`, `rng`, `hex`, `base64`, `utils`) plus the spine crates `core`, `core-test-framework`, and `factory`. Each crate is published as `bouncycastle-<name>` and depended on internally via the `workspace.dependencies` table in the root `Cargo.toml`.
2. `src/` — the umbrella `bouncycastle` crate, which is just `pub use` re-exports of every sub-crate (e.g. `bouncycastle::sha3`, `bouncycastle::mlkem`). It exists so downstream users can pull the whole library with one dependency; it has no code of its own.
1. `crypto/*` — one sub-crate per primitive (`sha2`, `sha3`, `sm3`, `hmac`, `hkdf`, `mlkem`, `mlkem_lowmemory`, `mldsa`, `mldsa_lowmemory`, `rng`, `hex`, `base64`, `utils`) plus the spine crates `core`, `core-test-framework`, and `factory`. Each crate is published as `bouncycastle-<name>` and depended on internally via the `workspace.dependencies` table in the root `Cargo.toml`.
2. `src/` — the umbrella `bouncycastle` crate, which is just `pub use` re-exports of every sub-crate (e.g. `bouncycastle::sha3`, `bouncycastle::sm3`, `bouncycastle::mlkem`). It exists so downstream users can pull the whole library with one dependency; it has no code of its own.
3. `cli/` — the `bc-rust` binary built on top of `bouncycastle`, exposing every primitive as a streaming stdin→stdout subcommand using `clap`.
4. `mem_usage_benches/` — stand-alone binary crates that measure peak stack usage of algorithms (cannot be done via criterion).

Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ bouncycastle-mldsa-lowmemory = { path = "./crypto/mldsa-lowmemory" }
bouncycastle-rng = { path = "./crypto/rng" }
bouncycastle-sha2 = { path = "./crypto/sha2" }
bouncycastle-sha3 = { path = "./crypto/sha3" }
bouncycastle-sm3 = { path = "./crypto/sm3" }
bouncycastle-utils = { path = "./crypto/utils" }


Expand Down Expand Up @@ -54,3 +55,4 @@ bouncycastle-mlkem-lowmemory.workspace = true
bouncycastle-rng.workspace = true
bouncycastle-sha2.workspace = true
bouncycastle-sha3.workspace = true
bouncycastle-sm3.workspace = true
9 changes: 9 additions & 0 deletions alpha_0.1.3_release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@

## Major features

* New algorithms added to crypto/ (PR #89):
* sm3 -- the SM3 hash (GB/T 32905-2016 / ISO/IEC 10118-3:2018), ported from bc-java. Implements `Hash`,
`Suspendable` and `AlgorithmOID`, supports bit-oriented (partial final byte) messages per GB/T 32905-2016 s. 5.2
using the same least-significant-bits convention as SHA-2/SHA-3, and is registered in `HashFactory`
(`"SM3"`) with a `bc-rust sm3` CLI subcommand.
* HMAC-SM3, in the hmac crate, registered in `MACFactory` (`"HMAC-SM3"`) with a `bc-rust hmac-sm3` CLI subcommand.
* Test vectors are the GB/T 32905-2016 Appendix A examples plus the bc-java `SM3DigestTest` / `HMac` vectors, with
additional digests cross-checked against OpenSSL and bc-java.

## Minor features / bug fixes
7 changes: 6 additions & 1 deletion cli/src/mac_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ use bouncycastle::core::key_material::{
};
use bouncycastle::core::traits::MAC;
use bouncycastle::hex;
use bouncycastle::hmac::{HMAC_SHA256, HMAC_SHA512};
use bouncycastle::hmac::{HMAC_SHA256, HMAC_SHA512, HMAC_SM3};

pub(crate) enum HMACVariant {
SHA256,
SHA512,
SM3,
}

pub(crate) fn mac_cmd(
Expand Down Expand Up @@ -48,6 +49,10 @@ pub(crate) fn mac_cmd(
let mac = HMAC_SHA512::new_allow_weak_key(&key).unwrap();
do_mac(mac, verify_val, output_hex);
}
HMACVariant::SM3 => {
let mac = HMAC_SM3::new_allow_weak_key(&key).unwrap();
do_mac(mac, verify_val, output_hex);
}
}
}

Expand Down
40 changes: 40 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod mlkem_cmd;
mod rng_cmd;
mod sha2_cmd;
mod sha3_cmd;
mod sm3_cmd;

use crate::mac_cmd::HMACVariant;
use crate::mldsa_cmd::MLDSAAction;
Expand Down Expand Up @@ -102,6 +103,14 @@ enum Subcommands {
x: bool,
},

/// Perform SM3 of the content provided on stdin.
/// Supports streaming update for low memory footprint.
SM3 {
#[arg(short)]
/// Output the hashes in hex format.
x: bool,
},

/// Perform SHAKE128 of the content provided on stdin. Requires the output length in bytes.
/// Supports streaming update for low memory footprint.
SHAKE128 {
Expand Down Expand Up @@ -173,6 +182,31 @@ enum Subcommands {
x: bool,
},

/// Perform HMAC-SM3 of the content provided on stdin.
/// Supports streaming update for low memory footprint.
/// Note: in production uses, secrets should not be passed on the command-line because they get
/// logged in shell history. Use the file-based input instead.
HMAC_SM3 {
/// The MAC key in hex.
/// The `key_file` option is preferred to avoid leaving key material in command history.
#[arg(long)]
key: Option<String>,

/// A file containing the MAC key in binary.
/// If both key and key_file options are provided, the file will be used.
#[arg(short, long)]
key_file: Option<String>,

/// A MAC value to be verified.
/// The command will output either 0 for success or -1 for verification failure.
#[arg(short, long)]
verify: Option<String>,

#[arg(short)]
/// Output the hashes in hex format.
x: bool,
},

/// Perform HMAC-SHA256 of the content provided on stdin.
/// HKDF.extract_and_expand(salt, ikm, additional_info, L)
/// Note: in production uses, secrets should not be passed on the command-line because they get
Expand Down Expand Up @@ -525,6 +559,9 @@ fn main() {
Some(Subcommands::SHA3_512 { x }) => {
sha3_cmd::sha3_cmd(512, *x);
}
Some(Subcommands::SM3 { x }) => {
sm3_cmd::sm3_cmd(*x);
}
Some(Subcommands::SHAKE128 { length, x }) => {
sha3_cmd::shake_cmd(128, *length, *x);
}
Expand All @@ -537,6 +574,9 @@ fn main() {
Some(Subcommands::HMAC_SHA512 { key, key_file, verify, x }) => {
mac_cmd::mac_cmd(HMACVariant::SHA512, key, key_file, verify, *x)
}
Some(Subcommands::HMAC_SM3 { key, key_file, verify, x }) => {
mac_cmd::mac_cmd(HMACVariant::SM3, key, key_file, verify, *x)
}
Some(Subcommands::HKDF_SHA256 {
salt,
salt_file,
Expand Down
28 changes: 28 additions & 0 deletions cli/src/sm3_cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use bouncycastle::core::traits::Hash;
use std::io;
use std::io::{Read, Write};

use bouncycastle::sm3::SM3;

pub(crate) fn sm3_cmd(output_hex: bool) {
let mut sm3 = SM3::new();
let mut buf: [u8; 1024] = [0u8; 1024];

// read from stdin
let mut bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin");
while bytes_read != 0 {
sm3.do_update(&buf[..bytes_read]);
bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin");
}

let out = sm3.do_final();

if output_hex {
for b in out.iter() {
print!("{b:02x}");
}
} else {
io::stdout().write_all(&out).unwrap();
}
println!();
}
1 change: 1 addition & 0 deletions crypto/factory/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ bouncycastle-hkdf.workspace = true
bouncycastle-hmac.workspace = true
bouncycastle-sha2.workspace = true
bouncycastle-sha3.workspace = true
bouncycastle-sm3.workspace = true
bouncycastle-rng.workspace = true

[dev-dependencies]
Expand Down
15 changes: 15 additions & 0 deletions crypto/factory/src/hash_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ use bouncycastle_sha2 as sha2;
use bouncycastle_sha2::{SHA224_NAME, SHA256_NAME, SHA384_NAME, SHA512_NAME};
use bouncycastle_sha3 as sha3;
use bouncycastle_sha3::{SHA3_224_NAME, SHA3_256_NAME, SHA3_384_NAME, SHA3_512_NAME};
use bouncycastle_sm3 as sm3;
use bouncycastle_sm3::SM3_NAME;

/// Wrapper object for all algorithms that impl [`Hash`].
/// Note: no SHAKE because SHAKE is not NIST approved as a hash function. See FIPS 202 section A.2.
Expand All @@ -55,6 +57,8 @@ pub enum HashFactory {
SHA3_384(sha3::SHA3_384),
///
SHA3_512(sha3::SHA3_512),
///
SM3(sm3::SM3),
}

impl Default for HashFactory {
Expand Down Expand Up @@ -84,6 +88,7 @@ impl AlgorithmFactory for HashFactory {
SHA3_256_NAME => Ok(Self::SHA3_256(sha3::SHA3_256::new())),
SHA3_384_NAME => Ok(Self::SHA3_384(sha3::SHA3_384::new())),
SHA3_512_NAME => Ok(Self::SHA3_512(sha3::SHA3_512::new())),
SM3_NAME => Ok(Self::SM3(sm3::SM3::new())),
_ => Err(FactoryError::UnsupportedAlgorithm(format!(
"The algorithm: \"{}\" is not a known Hash",
alg_name
Expand Down Expand Up @@ -112,6 +117,7 @@ impl Hash for HashFactory {
Self::SHA3_256(h) => h.block_bitlen(),
Self::SHA3_384(h) => h.block_bitlen(),
Self::SHA3_512(h) => h.block_bitlen(),
Self::SM3(h) => h.block_bitlen(),
}
}

Expand All @@ -125,6 +131,7 @@ impl Hash for HashFactory {
Self::SHA3_256(h) => h.output_len(),
Self::SHA3_384(h) => h.output_len(),
Self::SHA3_512(h) => h.output_len(),
Self::SM3(h) => h.output_len(),
}
}

Expand All @@ -138,6 +145,7 @@ impl Hash for HashFactory {
Self::SHA3_256(h) => h.hash(data),
Self::SHA3_384(h) => h.hash(data),
Self::SHA3_512(h) => h.hash(data),
Self::SM3(h) => h.hash(data),
}
}

Expand All @@ -153,6 +161,7 @@ impl Hash for HashFactory {
Self::SHA3_256(h) => h.hash_out(data, output),
Self::SHA3_384(h) => h.hash_out(data, output),
Self::SHA3_512(h) => h.hash_out(data, output),
Self::SM3(h) => h.hash_out(data, output),
}
}

Expand All @@ -166,6 +175,7 @@ impl Hash for HashFactory {
Self::SHA3_256(h) => h.do_update(data),
Self::SHA3_384(h) => h.do_update(data),
Self::SHA3_512(h) => h.do_update(data),
Self::SM3(h) => h.do_update(data),
}
}

Expand All @@ -179,6 +189,7 @@ impl Hash for HashFactory {
Self::SHA3_256(h) => h.do_final(),
Self::SHA3_384(h) => h.do_final(),
Self::SHA3_512(h) => h.do_final(),
Self::SM3(h) => h.do_final(),
}
}

Expand All @@ -194,6 +205,7 @@ impl Hash for HashFactory {
Self::SHA3_256(h) => h.do_final_out(output),
Self::SHA3_384(h) => h.do_final_out(output),
Self::SHA3_512(h) => h.do_final_out(output),
Self::SM3(h) => h.do_final_out(output),
}
}

Expand All @@ -211,6 +223,7 @@ impl Hash for HashFactory {
Self::SHA3_256(h) => h.do_final_partial_bits(partial_byte, num_partial_bits),
Self::SHA3_384(h) => h.do_final_partial_bits(partial_byte, num_partial_bits),
Self::SHA3_512(h) => h.do_final_partial_bits(partial_byte, num_partial_bits),
Self::SM3(h) => h.do_final_partial_bits(partial_byte, num_partial_bits),
}
}

Expand All @@ -237,6 +250,7 @@ impl Hash for HashFactory {
Self::SHA3_512(h) => {
h.do_final_partial_bits_out(partial_byte, num_partial_bits, output)
}
Self::SM3(h) => h.do_final_partial_bits_out(partial_byte, num_partial_bits, output),
}
}

Expand All @@ -250,6 +264,7 @@ impl Hash for HashFactory {
Self::SHA3_256(h) => h.max_security_strength(),
Self::SHA3_384(h) => h.max_security_strength(),
Self::SHA3_512(h) => h.max_security_strength(),
Self::SM3(h) => h.max_security_strength(),
}
}
}
14 changes: 14 additions & 0 deletions crypto/factory/src/mac_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,14 @@ use bouncycastle_core::errors::MACError;
use bouncycastle_core::key_material::KeyMaterialTrait;
use bouncycastle_core::traits::{MAC, SecurityStrength};
use bouncycastle_hmac as hmac;
use bouncycastle_hmac::HMAC_SM3_NAME;
use bouncycastle_hmac::{
HMAC_SHA3_224_NAME, HMAC_SHA3_256_NAME, HMAC_SHA3_384_NAME, HMAC_SHA3_512_NAME,
};
use bouncycastle_hmac::{HMAC_SHA224_NAME, HMAC_SHA256_NAME, HMAC_SHA384_NAME, HMAC_SHA512_NAME};
use bouncycastle_sha2 as sha2;
use bouncycastle_sha3 as sha3;
use bouncycastle_sm3 as sm3;

/*** Defaults ***/
///
Expand Down Expand Up @@ -113,6 +115,8 @@ pub enum MACFactory {
HMAC_SHA3_384(hmac::HMAC<sha3::SHA3_384>),
///
HMAC_SHA3_512(hmac::HMAC<sha3::SHA3_512>),
///
HMAC_SM3(hmac::HMAC<sm3::SM3>),
}

impl MACFactory {
Expand Down Expand Up @@ -142,6 +146,7 @@ impl MACFactory {
HMAC_SHA3_256_NAME => Ok(Self::HMAC_SHA3_256(hmac::HMAC::<sha3::SHA3_256>::new(key)?)),
HMAC_SHA3_384_NAME => Ok(Self::HMAC_SHA3_384(hmac::HMAC::<sha3::SHA3_384>::new(key)?)),
HMAC_SHA3_512_NAME => Ok(Self::HMAC_SHA3_512(hmac::HMAC::<sha3::SHA3_512>::new(key)?)),
HMAC_SM3_NAME => Ok(Self::HMAC_SM3(hmac::HMAC::<sm3::SM3>::new(key)?)),
_ => Err(FactoryError::UnsupportedAlgorithm(format!(
"The algorithm: \"{}\" is not a known MAC",
alg_name
Expand Down Expand Up @@ -171,6 +176,7 @@ impl MAC for MACFactory {
Self::HMAC_SHA3_256(h) => h.output_len(),
Self::HMAC_SHA3_384(h) => h.output_len(),
Self::HMAC_SHA3_512(h) => h.output_len(),
Self::HMAC_SM3(h) => h.output_len(),
}
}

Expand All @@ -184,6 +190,7 @@ impl MAC for MACFactory {
Self::HMAC_SHA3_256(h) => h.mac(data),
Self::HMAC_SHA3_384(h) => h.mac(data),
Self::HMAC_SHA3_512(h) => h.mac(data),
Self::HMAC_SM3(h) => h.mac(data),
}
}

Expand All @@ -199,6 +206,7 @@ impl MAC for MACFactory {
Self::HMAC_SHA3_256(h) => h.mac_out(data, out),
Self::HMAC_SHA3_384(h) => h.mac_out(data, out),
Self::HMAC_SHA3_512(h) => h.mac_out(data, out),
Self::HMAC_SM3(h) => h.mac_out(data, out),
}
}

Expand All @@ -212,6 +220,7 @@ impl MAC for MACFactory {
Self::HMAC_SHA3_256(h) => h.verify(data, mac),
Self::HMAC_SHA3_384(h) => h.verify(data, mac),
Self::HMAC_SHA3_512(h) => h.verify(data, mac),
Self::HMAC_SM3(h) => h.verify(data, mac),
}
}

Expand All @@ -225,6 +234,7 @@ impl MAC for MACFactory {
Self::HMAC_SHA3_256(h) => h.do_update(data),
Self::HMAC_SHA3_384(h) => h.do_update(data),
Self::HMAC_SHA3_512(h) => h.do_update(data),
Self::HMAC_SM3(h) => h.do_update(data),
}
}

Expand All @@ -238,6 +248,7 @@ impl MAC for MACFactory {
Self::HMAC_SHA3_256(h) => h.do_final(),
Self::HMAC_SHA3_384(h) => h.do_final(),
Self::HMAC_SHA3_512(h) => h.do_final(),
Self::HMAC_SM3(h) => h.do_final(),
}
}

Expand All @@ -253,6 +264,7 @@ impl MAC for MACFactory {
Self::HMAC_SHA3_256(h) => h.do_final_out(&mut out),
Self::HMAC_SHA3_384(h) => h.do_final_out(&mut out),
Self::HMAC_SHA3_512(h) => h.do_final_out(&mut out),
Self::HMAC_SM3(h) => h.do_final_out(&mut out),
}
}

Expand All @@ -266,6 +278,7 @@ impl MAC for MACFactory {
Self::HMAC_SHA3_256(h) => h.do_verify_final(mac),
Self::HMAC_SHA3_384(h) => h.do_verify_final(mac),
Self::HMAC_SHA3_512(h) => h.do_verify_final(mac),
Self::HMAC_SM3(h) => h.do_verify_final(mac),
}
}

Expand All @@ -279,6 +292,7 @@ impl MAC for MACFactory {
Self::HMAC_SHA3_256(h) => h.max_security_strength(),
Self::HMAC_SHA3_384(h) => h.max_security_strength(),
Self::HMAC_SHA3_512(h) => h.max_security_strength(),
Self::HMAC_SM3(h) => h.max_security_strength(),
}
}
}
Loading
Loading