Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
bbfaab3
Merge branch 'release/0.1.2alpha' of ssh://git.bouncycastle.org/bc-ru…
officialfrancismendoza Jul 1, 2026
19ea35f
initial trait defn for SerializableState
ounsworth Jul 1, 2026
64365de
sha2 impls SerializableState
ounsworth Jul 1, 2026
f75bc5d
rustfmt
ounsworth Jul 1, 2026
54f93f6
Incorporating Nikola and David's feedback
ounsworth Jul 6, 2026
46100d3
An in-progress SHA3 object can now be serialized
ounsworth Jul 6, 2026
6b3ecec
Impl'd SerializableState for MLDSA, and some quality-of-life fixes fo…
ounsworth Jul 6, 2026
d7192dd
rustfmt
ounsworth Jul 6, 2026
45ba742
Impl'd SerializableState for MLDSA-lowmemory.
ounsworth Jul 6, 2026
3e1ce14
Merge branch 'release/0.1.2alpha' into feature/serialize_state
ounsworth Jul 6, 2026
d9b06bd
Impl'd SerializableKeyedState for HMAC.
ounsworth Jul 7, 2026
e464f68
Fixed a bug in HMAC that could cause panics.
ounsworth Jul 7, 2026
937d859
Implemented Suspendable for HKDF and a bunch of other tidy-up.
ounsworth Jul 7, 2026
1dacaa2
rustfmt
ounsworth Jul 8, 2026
07889fc
HMAC now impl's Drop like it should.
ounsworth Jul 8, 2026
3c8d64b
Adjusted the Suspendable version checker to allow future versions fro…
ounsworth Jul 9, 2026
fd17f91
HKDF now explicitly carries a lib_ver tag
ounsworth Jul 9, 2026
d46f288
Towards removing nightly, I had to refactor how HashMLDSA gets its ha…
ounsworth Jul 9, 2026
f088ef9
rustfmt
ounsworth Jul 9, 2026
1dc415b
Added OIDs for all algorithms.
ounsworth Jul 9, 2026
b1e5483
progress at applying #to all crates
ounsworth Jul 9, 2026
ed12546
missing_docs for factory
ounsworth Jul 9, 2026
9e645c1
Merge branch 'release/0.1.2alpha' of git.bouncycastle.org:bc-rust int…
Jul 10, 2026
09bf94c
progress on missing_docs. DOES NOT COMPILE
ounsworth Jul 10, 2026
4624785
hkdf crate now has \#![forbit(missing_docs)]
ounsworth Jul 10, 2026
e265aae
hmac crate now tagged with \#![forbid(missing_docs)]
ounsworth Jul 10, 2026
26a369e
All crates now build with \#![forbid(unsafe_code)] and \#![forbid[mis…
ounsworth Jul 10, 2026
39e3cbd
New feature: Suspendable trait
ounsworth Jul 10, 2026
fabaa82
Merge branch 'release/0.1.2alpha' of git.bouncycastle.org:bc-rust int…
Jul 10, 2026
6a0aebe
Merged from release/0.1.2alpha
ounsworth Jul 10, 2026
bbf49ad
rustfmt
ounsworth Jul 10, 2026
b735296
rustfmt
ounsworth Jul 10, 2026
c18d173
bug fix
ounsworth Jul 11, 2026
fbc9401
Merge branch 'release/0.1.2alpha' of git.bouncycastle.org:bc-rust int…
Jul 11, 2026
c47afbe
Merge branch 'release/0.1.2alpha' of git.bouncycastle.org:bc-rust int…
Jul 11, 2026
f9653ee
Merge branch 'release/0.1.2alpha' of git.bouncycastle.org:bc-rust int…
Jul 12, 2026
5cfc0b3
Merge branch 'release/0.1.2alpha' of git.bouncycastle.org:bc-rust int…
Jul 14, 2026
57b7a8f
Merge branch 'release/0.1.2alpha' of git.bouncycastle.org:bc-rust int…
Jul 14, 2026
20e5adf
Rebase to upstream release/0.1.2alpha. Feature branch diversion from …
officialfrancismendoza Jul 9, 2026
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
22 changes: 14 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,22 @@ edition = "2024"
# *** Internal Dependencies ***
bouncycastle = { path = "./", version = "0.1.2" }
bouncycastle-base64 = { path = "./crypto/base64", version = "0.1.2" }
bouncycastle-core = { path = "crypto/core", version = "0.1.2" }
# `default-features = false` here (rather than on each member) is required so that individual
# crates can opt out of the `alloc` feature for `no_std` builds; members re-enable it through
# their own `alloc` feature. Building the whole workspace keeps `alloc` on via feature unification.
bouncycastle-core = { path = "crypto/core", version = "0.1.2", default-features = false }
bouncycastle-core-test-framework = { path = "./crypto/core-test-framework", version = "0.1.2" }
bouncycastle-factory = { path = "./crypto/factory", version = "0.1.2" }
bouncycastle-hex = { path = "./crypto/hex", version = "0.1.2" }
bouncycastle-hkdf = { path = "./crypto/hkdf", version = "0.1.2" }
bouncycastle-hmac = { path = "./crypto/hmac", version = "0.1.2" }
bouncycastle-hmac = { path = "./crypto/hmac", version = "0.1.2", default-features = false }
bouncycastle-mlkem = { path = "./crypto/mlkem", version = "0.1.2" }
bouncycastle-mlkem-lowmemory = { path = "./crypto/mlkem-lowmemory", version = "0.1.2" }
bouncycastle-mldsa = { path = "./crypto/mldsa", version = "0.1.2" }
bouncycastle-mldsa-lowmemory = { path = "./crypto/mldsa-lowmemory", version = "0.1.2" }
bouncycastle-rng = { path = "./crypto/rng", version = "0.1.2" }
bouncycastle-sha2 = { path = "./crypto/sha2", version = "0.1.2" }
bouncycastle-sha3 = { path = "./crypto/sha3", version = "0.1.2" }
bouncycastle-sha2 = { path = "./crypto/sha2", version = "0.1.2", default-features = false }
bouncycastle-sha3 = { path = "./crypto/sha3", version = "0.1.2", default-features = false }
bouncycastle-utils = { path = "./crypto/utils", version = "0.1.2" }


Expand All @@ -39,17 +42,20 @@ name = "bouncycastle"
version = "0.1.2"
edition.workspace = true

# The umbrella crate re-exports the whole library for downstream users who want everything in one
# dependency, so it enables `alloc` on the primitive crates whose workspace deps default it off.
# (base64/hex/hkdf/rng/factory already default `alloc` on.)
[dependencies]
bouncycastle-base64.workspace = true
bouncycastle-core.workspace = true
bouncycastle-core = { workspace = true, features = ["alloc"] }
bouncycastle-factory.workspace = true
bouncycastle-hex.workspace = true
bouncycastle-hkdf.workspace = true
bouncycastle-hmac.workspace = true
bouncycastle-hmac = { workspace = true, features = ["alloc"] }
bouncycastle-mldsa.workspace = true
bouncycastle-mldsa-lowmemory.workspace = true
bouncycastle-mlkem.workspace = true
bouncycastle-mlkem-lowmemory.workspace = true
bouncycastle-rng.workspace = true
bouncycastle-sha2.workspace = true
bouncycastle-sha3.workspace = true
bouncycastle-sha2 = { workspace = true, features = ["alloc"] }
bouncycastle-sha3 = { workspace = true, features = ["alloc"] }
6 changes: 6 additions & 0 deletions crypto/base64/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ name = "bouncycastle-base64"
version = "0.1.2"
edition.workspace = true

[features]
# `alloc` enables the (entire) `String`/`Vec`-returning encoder/decoder API. On by default; a
# `no_std` build without it exposes only the type definitions (there are no streaming `_out` variants).
default = ["alloc"]
alloc = []

[dependencies]
bouncycastle-core.workspace = true
bouncycastle-utils.workspace = true
Expand Down
18 changes: 17 additions & 1 deletion crypto/base64/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,29 @@
// /// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="
// URLSafe,

#![cfg_attr(not(test), no_std)]
#![forbid(unsafe_code)]
#![forbid(missing_docs)]

// The entire base64 encoder/decoder API returns `String`/`Vec<u8>`, so it lives behind the
// default-on `alloc` feature. base64 is inherently an allocating codec (there are currently no
// streaming `_out` variants), so a `no_std` build without `alloc` exposes only the type definitions.
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::{string::String, vec, vec::Vec};

#[cfg(feature = "alloc")]
use bouncycastle_utils::ct::Condition;

/// One-shot encode from bytes to a base64-encoded string using a constant-time implementation.
#[cfg(feature = "alloc")]
pub fn encode<T: AsRef<[u8]>>(input: T) -> String {
Base64Encoder::new().do_final(input)
}

/// One-shot decode from a base64-encoded string to bytes using a constant-time implementation.
#[cfg(feature = "alloc")]
pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, Base64Error> {
Base64Decoder::new(true).do_final(input)
}
Expand All @@ -111,11 +123,13 @@ pub enum Base64Error {
}

/// The stateful base64 encoder that supports streaming.
#[cfg(feature = "alloc")]
pub struct Base64Encoder {
buf: [u8; 3],
vals_in_buf: usize,
}

#[cfg(feature = "alloc")]
impl Base64Encoder {
/// Create a new instance.
pub fn new() -> Self {
Expand Down Expand Up @@ -188,7 +202,7 @@ impl Base64Encoder {
if self.vals_in_buf == 1 {
out_buf[2] = b'=';
}
out.push_str(std::str::from_utf8(&out_buf).unwrap());
out.push_str(core::str::from_utf8(&out_buf).unwrap());
}
out
}
Expand All @@ -208,12 +222,14 @@ impl Base64Encoder {
}

/// The stateful base64 decoder that supports streaming.
#[cfg(feature = "alloc")]
pub struct Base64Decoder {
buf: [u8; 4],
vals_in_buf: usize,
skip_whitespace: bool,
}

#[cfg(feature = "alloc")]
impl Base64Decoder {
/// Create a new instance.
pub fn new(skip_whitespace: bool) -> Self {
Expand Down
3 changes: 2 additions & 1 deletion crypto/core-test-framework/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ version = "0.1.2"
edition.workspace = true

[dependencies]
bouncycastle-core.workspace = true
# The KAT test framework exercises the `Vec`/`Box`-returning trait APIs, so it always needs `alloc`.
bouncycastle-core = { workspace = true, features = ["alloc"] }

[dev-dependencies]
14 changes: 14 additions & 0 deletions crypto/core-test-framework/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ impl TestFrameworkHash {
H::default().hash_out(input, &mut output_buf);
assert_eq!(output_buf, expected_output);

/*** fn hash_array<const N: usize>(self, data: &[u8]) -> [u8; N] (no_std alternative) ***/
// Use N = 64, the maximum output length across all hashes; hash_array zero-pads the tail
// beyond output_len, so the digest lands in the first OUTPUT_LEN bytes.
let arr: [u8; 64] = H::default().hash_array(input);
assert_eq!(&arr[..H::OUTPUT_LEN], expected_output, "hash_array digest mismatch");
assert!(arr[H::OUTPUT_LEN..].iter().all(|&b| b == 0), "hash_array tail not zero-padded");

/*** fn do_final_array<const N: usize>(self) -> [u8; N] (no_std alternative) ***/
let mut message_digest = H::default();
message_digest.do_update(input);
let arr: [u8; 64] = message_digest.do_final_array();
assert_eq!(&arr[..H::OUTPUT_LEN], expected_output, "do_final_array digest mismatch");
assert!(arr[H::OUTPUT_LEN..].iter().all(|&b| b == 0), "do_final_array tail not zero-padded");

/*** fn do_update(&mut self, data: &[u8]) -> Result<(), HashError> ***/
/*** fn do_final(self) -> Result<Vec<u8>, HashError> **/

Expand Down
16 changes: 16 additions & 0 deletions crypto/core-test-framework/src/mac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ impl TestFrameworkMAC {
// Test .output_len()
assert_eq!(output_len, out.len());

// Test ::mac_array() and ::do_final_array() (no_std alternatives).
// N = 64 is >= every supported MAC output length (and >= the FIPS minimum), so the tag lands
// in the first output_len bytes with a zero-padded tail.
let arr: [u8; 64] = M::new_allow_weak_key(key).unwrap().mac_array(input).unwrap();
assert_eq!(&arr[..expected_output.len()], expected_output, "mac_array digest mismatch");
assert!(arr[expected_output.len()..].iter().all(|&b| b == 0), "mac_array tail not zero-padded");

let mut mac = M::new_allow_weak_key(key).unwrap();
mac.do_update(input);
let arr: [u8; 64] = mac.do_final_array().unwrap();
assert_eq!(&arr[..expected_output.len()], expected_output, "do_final_array digest mismatch");
assert!(
arr[expected_output.len()..].iter().all(|&b| b == 0),
"do_final_array tail not zero-padded"
);

// Test .init(), .do_update(), .do_mac_final_out()
let mut mac = M::new_allow_weak_key(key).unwrap();
mac.do_update(input);
Expand Down
10 changes: 5 additions & 5 deletions crypto/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ version = "0.1.2"
edition.workspace = true

[features]
# `std` gates the ergonomic, allocating (`Vec`-returning) one-shot APIs. It is on by
# default today; a future `--no-default-features` build is what will let `core` become
# `#![no_std]` (see the TODO at the top of `src/lib.rs`).
default = ["std"]
std = []
# `alloc` pulls in the `Vec`/`Box`-returning convenience APIs. It is on by default so that
# regular (std) builds are unaffected. `no_std` users who cannot allocate should build with
# `default-features = false` and use the `*_out(&mut [u8])` / `*_array::<N>()` APIs instead.
default = ["alloc"]
alloc = []

[dependencies]
bouncycastle-utils.workspace = true
Expand Down
10 changes: 7 additions & 3 deletions crypto/core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
//! This crate defines the core traits and types used by the rest of the bc-rust.test library.

// todo -- this is the goal, but first need to remove all the Vec in favour of compile-time array sizing.
// #![no_std]

#![cfg_attr(not(test), no_std)]
#![forbid(unsafe_code)]
#![forbid(missing_docs)]

// The `Vec`/`Box`-returning convenience APIs live behind the (default-on) `alloc` feature.
// When it is enabled we pull in the `alloc` crate; `no_std` users who disable it get the
// allocation-free `*_out(&mut [u8])` and `*_array::<N>()` APIs only.
#[cfg(feature = "alloc")]
extern crate alloc;

pub mod errors;
pub mod key_material;
pub mod suspendable_state;
Expand Down
Loading