aws_lc_rs/lib.rs
1// Copyright 2015-2016 Brian Smith.
2// SPDX-License-Identifier: ISC
3// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4// SPDX-License-Identifier: Apache-2.0 OR ISC
5#![cfg_attr(not(clippy), allow(unexpected_cfgs))]
6#![cfg_attr(not(clippy), allow(unknown_lints))]
7#![allow(clippy::doc_markdown)]
8//! A [*ring*](https://github.com/briansmith/ring)-compatible crypto library using the cryptographic
9//! operations provided by [*AWS-LC*](https://github.com/aws/aws-lc). It uses either the
10//! auto-generated [*aws-lc-sys*](https://crates.io/crates/aws-lc-sys) or
11//! [*aws-lc-fips-sys*](https://crates.io/crates/aws-lc-fips-sys)
12//! Foreign Function Interface (FFI) crates found in this repository for invoking *AWS-LC*.
13//!
14//! # Build
15//!
16//! `aws-lc-rs` is available through [crates.io](https://crates.io/crates/aws-lc-rs). It can
17//! be added to your project in the [standard way](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html)
18//! using `Cargo.toml`:
19//!
20//! ```toml
21//! [dependencies]
22//! aws-lc-rs = "1"
23//! ```
24//! Consuming projects will need a C/C++ compiler to build.
25//!
26//! **Non-FIPS builds (default):**
27//! * CMake is **never** required
28//! * Bindgen is **never** required (pre-generated bindings are provided)
29//! * Go is **never** required
30//!
31//! **FIPS builds:** Require **CMake**, **Go**, and potentially **bindgen** depending on the target platform.
32//!
33//! See our [User Guide](https://aws.github.io/aws-lc-rs/) for guidance on installing build requirements.
34//!
35//! # Feature Flags
36//!
37//! #### alloc (default)
38//!
39//! Allows implementation to allocate values of arbitrary size. (The meaning of this feature differs
40//! from the "alloc" feature of *ring*.) Currently, this is required by the `io::writer` module.
41//!
42//! #### ring-io (default)
43//!
44//! Enable feature to access the `io` module.
45//!
46//! #### ring-sig-verify (default)
47//!
48//! Enable feature to preserve compatibility with ring's `signature::VerificationAlgorithm::verify`
49//! function. This adds a requirement on `untrusted = "0.7.1"`.
50//!
51//! #### fips
52//!
53//! Enable this feature to have aws-lc-rs use the [*aws-lc-fips-sys*](https://crates.io/crates/aws-lc-fips-sys)
54//! crate for the cryptographic implementations. The aws-lc-fips-sys crate provides bindings to the
55//! latest version of the AWS-LC-FIPS module that has completed FIPS validation testing by an
56//! accredited lab and has been submitted to NIST for certification. This will continue to be the
57//! case as we periodically submit new versions of the AWS-LC-FIPS module to NIST for certification.
58//! Currently, aws-lc-fips-sys binds to
59//! [AWS-LC-FIPS 4.x](https://github.com/aws/aws-lc/tree/fips-2025-09-12-lts).
60//!
61//! Consult with your local FIPS compliance team to determine the version of AWS-LC-FIPS module that you require. Consumers
62//! needing to remain on a previous version of the AWS-LC-FIPS module should pin to specific versions of aws-lc-rs to avoid
63//! automatically being upgraded to a newer module version.
64//! (See [cargo's documentation](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html)
65//! on how to specify dependency versions.)
66//!
67//! | AWS-LC-FIPS module | aws-lc-rs |
68//! |--------------------|-----------|
69//! | 2.0.x | \<1.12.0 |
70//! | 3.0.x | \<1.18.0 |
71//! | 4.x | *latest* |
72//!
73//! Refer to the
74//! [NIST Cryptographic Module Validation Program's Modules In Progress List](https://csrc.nist.gov/Projects/cryptographic-module-validation-program/modules-in-process/Modules-In-Process-List)
75//! for the latest status of the static or dynamic AWS-LC Cryptographic Module. Please see the
76//! [FIPS.md in the aws-lc repository](https://github.com/aws/aws-lc/blob/main/crypto/fipsmodule/FIPS.md)
77//! for relevant security policies and information on supported operating environments.
78//! We will also update our release notes and documentation to reflect any changes in FIPS certification status.
79//!
80//! #### non-fips
81//!
82//! Enable this feature to guarantee that the non-FIPS [*aws-lc-sys*](https://crates.io/crates/aws-lc-sys)
83//! crate is used for cryptographic implementations. This feature is mutually exclusive with the `fips`
84//! feature - enabling both will result in a compile-time error. Use this feature when you need a
85//! compile-time guarantee that your build is using the non-FIPS cryptographic module.
86//!
87//! #### asan
88//!
89//! Performs an "address sanitizer" build. This can be used to help detect memory leaks. See the
90//! ["Address Sanitizer" section](https://doc.rust-lang.org/beta/unstable-book/compiler-flags/sanitizer.html#addresssanitizer)
91//! of the [Rust Unstable Book](https://doc.rust-lang.org/beta/unstable-book/).
92//!
93//! **Preferred alternative:** Instead of the `asan` feature flag, you can set the
94//! `AWS_LC_SYS_SANITIZER` environment variable (or `AWS_LC_FIPS_SYS_SANITIZER` for FIPS builds)
95//! to one of: `asan`, `msan`, `tsan`. This approach does not require forwarding a feature through
96//! the dependency graph and also supports MemorySanitizer and ThreadSanitizer.
97//! MSAN and TSAN require the standard library to be rebuilt with sanitizer instrumentation, so
98//! you must install the `rust-src` component and pass `-Zbuild-std` to Cargo. For example:
99//!
100//! ```bash
101//! rustup component add rust-src --toolchain nightly
102//! AWS_LC_SYS_SANITIZER=msan RUSTFLAGS="-Zsanitizer=memory -Zsanitizer-memory-track-origins" \
103//! cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
104//! ```
105//!
106//! **Note:** MSAN is not currently supported for FIPS builds due to a missing preprocessor guard
107//! in the upstream AWS-LC `bcm.c` integrity check.
108//!
109//! #### bindgen
110//!
111//! Causes `aws-lc-sys` or `aws-lc-fips-sys` to generates fresh bindings for AWS-LC instead of using
112//! the pre-generated bindings. This feature requires `libclang` to be installed. See the
113//! [requirements](https://rust-lang.github.io/rust-bindgen/requirements.html)
114//! for [rust-bindgen](https://github.com/rust-lang/rust-bindgen)
115//!
116//! #### prebuilt-nasm
117//!
118//! Enables the use of crate provided prebuilt NASM objects under certain conditions. This only affects builds for
119//! Windows x86-64 platforms. This feature is ignored if the "fips" feature is also enabled.
120//!
121//! Use of prebuilt NASM objects is prevented if either of the following conditions are true:
122//! * The NASM assembler is detected in the build environment
123//! * `AWS_LC_SYS_PREBUILT_NASM` environment variable is set with a value of `0`
124//!
125//! Be aware that [features are additive](https://doc.rust-lang.org/cargo/reference/features.html#feature-unification);
126//! by enabling this feature, it is enabled for all crates within the same build.
127//!
128//! #### dev-tests-only
129//!
130//! Enables the `rand::unsealed` module, which re-exports the normally sealed `SecureRandom` trait.
131//! This allows consumers to provide their own implementations of `SecureRandom` (e.g., a
132//! deterministic RNG) for testing purposes. When enabled, a `mut_fill` method is also available on
133//! `SecureRandom`.
134//!
135//! This feature is restricted to **dev/debug profile builds only** — attempting to use it in a
136//! release build will result in a compile-time error.
137//!
138//! It can be enabled in two ways:
139//! * **Feature flag:** `cargo test --features dev-tests-only`
140//! * **Environment variable:** `AWS_LC_RS_DEV_TESTS_ONLY=1 cargo test`
141//!
142//! **⚠️ Warning:** This feature is intended **only** for development and testing. It must not be
143//! used in production builds. The `rand::unsealed` module and `mut_fill` method are not part of the
144//! stable public API and may change without notice.
145//!
146//! #### legacy-des
147//!
148//! Enables single DES and Triple DES as opt-in symmetric ciphers under the
149//! [`cipher`] module, exposing `DES_FOR_LEGACY_USE_ONLY` (single DES),
150//! `DES_EDE_FOR_LEGACY_USE_ONLY` (2-key Triple DES) and
151//! `DES_EDE3_FOR_LEGACY_USE_ONLY` (3-key Triple DES), together with the
152//! supporting key-length and IV-length constants. Only CBC and ECB operating
153//! modes are supported.
154//!
155//! **⚠️ Warning:** Single DES and Triple DES are legacy algorithms. Single DES
156//! provides only 56 bits of effective security and has been considered insecure
157//! for decades. Triple DES has been disallowed for encryption by
158//! [NIST SP 800-131A Rev. 2](https://csrc.nist.gov/publications/detail/sp/800-131a/rev-2/final):
159//! 2-key Triple DES after 2015, and 3-key Triple DES after 2023. This feature
160//! exists solely to support interoperability. All exposed items are marked
161//! `#[deprecated]` so that any use site produces a compiler warning. **Do not
162//! use single DES or Triple DES in new designs.** If you only need
163//! confidentiality, prefer AES-GCM or another AEAD from the [`aead`] module;
164//! if you specifically need a block cipher, prefer one of the AES algorithms
165//! in [`cipher`].
166//!
167//! **Build-time impact:** The default `aws-lc-sys` bindings do not expose the
168//! low-level `DES_*` symbols this feature relies on, so enabling `legacy-des`
169//! also enables `aws-lc-sys?/all-bindings`. Concretely:
170//!
171//! * On platforms with pre-generated per-target bindings, the build switches
172//! from the universal bindings to the per-target bindings (no extra
173//! tooling required).
174//! * On platforms *without* pre-generated bindings for the selected target,
175//! `bindgen` is invoked at build time, which requires `libclang` to be
176//! installed in the build environment.
177//!
178//! Because [features are
179//! additive](https://doc.rust-lang.org/cargo/reference/features.html#feature-unification),
180//! enabling `legacy-des` anywhere in your dependency graph applies the
181//! above to the entire build.
182//!
183//! # Use of prebuilt NASM objects
184//!
185//! Prebuilt NASM objects are **only** applicable to Windows x86-64 platforms. They are **never** used on any other platform (Linux, macOS, etc.).
186//!
187//! For Windows x86 and x86-64, NASM is required for assembly code compilation. On these platforms,
188//! we recommend that you install [the NASM assembler](https://www.nasm.us/). **If NASM is
189//! detected in the build environment, it is always used** to compile the assembly files. Prebuilt NASM objects are only used as a fallback.
190//!
191//! If a NASM assembler is not available, and the "fips" feature is not enabled, then the build fails unless one of the following conditions are true:
192//!
193//! * You are building for `x86-64` and either:
194//! * The `AWS_LC_SYS_PREBUILT_NASM` environment variable is found and has a value of "1"; OR
195//! * `AWS_LC_SYS_PREBUILT_NASM` is *not found* in the environment AND the "prebuilt-nasm" feature has been enabled.
196//!
197//! If the above cases apply, then the crate provided prebuilt NASM objects will be used for the build. To prevent usage of prebuilt NASM
198//! objects, install NASM in the build environment and/or set the variable `AWS_LC_SYS_PREBUILT_NASM` to `0` in the build environment to prevent their use.
199//!
200//! ## About prebuilt NASM objects
201//!
202//! Prebuilt NASM objects are generated using automation similar to the crate provided pregenerated bindings. See the repository's
203//! [GitHub workflow configuration](https://github.com/aws/aws-lc-rs/blob/main/.github/workflows/sys-bindings-generator.yml) for more information.
204//! The prebuilt NASM objects are checked into the repository
205//! and are [available for inspection](https://github.com/aws/aws-lc-rs/tree/main/aws-lc-sys/builder/prebuilt-nasm).
206//! For each PR submitted,
207//! [CI verifies](https://github.com/aws/aws-lc-rs/blob/main/.github/workflows/tests.yml)
208//! that the NASM objects newly built from source match the NASM objects currently in the repository.
209//!
210//! # *ring*-compatibility
211//!
212//! Although this library attempts to be fully compatible with *ring* (v0.16.x), there are a few places where our
213//! behavior is observably different.
214//!
215//! * Our implementation requires the `std` library. We currently do not support a
216//! [`#![no_std]`](https://docs.rust-embedded.org/book/intro/no-std.html) build.
217//! * `aws-lc-rs` supports the platforms supported by `aws-lc-sys` and AWS-LC. See the
218//! [Platform Support](https://aws.github.io/aws-lc-rs/platform_support.html) page in our User Guide.
219//! * `Ed25519KeyPair::from_pkcs8` and `Ed25519KeyPair::from_pkcs8_maybe_unchecked` both support
220//! parsing of v1 or v2 PKCS#8 documents. If a v2 encoded key is provided to either function,
221//! public key component, if present, will be verified to match the one derived from the encoded
222//! private key.
223//!
224//! # Post-Quantum Cryptography
225//!
226//! Details on the post-quantum algorithms supported by aws-lc-rs can be found at
227//! [PQREADME](https://github.com/aws/aws-lc/tree/main/crypto/fipsmodule/PQREADME.md).
228//!
229//! # Motivation
230//!
231//! Rust developers increasingly need to deploy applications that meet US and Canadian government
232//! cryptographic requirements. We evaluated how to deliver FIPS validated cryptography in idiomatic
233//! and performant Rust, built around our AWS-LC offering. We found that the popular ring (v0.16)
234//! library fulfilled much of the cryptographic needs in the Rust community, but it did not meet the
235//! needs of developers with FIPS requirements. Our intention is to contribute a drop-in replacement
236//! for ring that provides FIPS support and is compatible with the ring API. Rust developers with
237//! prescribed cryptographic requirements can seamlessly integrate aws-lc-rs into their applications
238//! and deploy them into AWS Regions.
239
240#![warn(missing_docs)]
241#![warn(clippy::exhaustive_enums)]
242#![cfg_attr(aws_lc_rs_docsrs, feature(doc_cfg))]
243
244extern crate alloc;
245#[cfg(feature = "fips")]
246extern crate aws_lc_fips_sys as aws_lc;
247#[cfg(not(feature = "fips"))]
248extern crate aws_lc_sys as aws_lc;
249
250pub mod aead;
251pub mod agreement;
252pub mod cmac;
253pub mod constant_time;
254pub mod digest;
255pub mod error;
256pub mod hkdf;
257pub mod hmac;
258#[cfg(feature = "ring-io")]
259pub mod io;
260pub mod key_wrap;
261pub mod pbkdf2;
262pub mod pkcs8;
263pub mod rand;
264pub mod signature;
265pub mod test;
266
267mod bn;
268mod buffer;
269mod cbb;
270mod cbs;
271pub mod cipher;
272mod debug;
273mod ec;
274mod ed25519;
275pub mod encoding;
276mod endian;
277mod evp_pkey;
278mod fips;
279mod hex;
280pub mod iv;
281pub mod kdf;
282#[allow(clippy::module_name_repetitions)]
283pub mod kem;
284mod pqdsa;
285mod ptr;
286pub mod rsa;
287pub mod tls_prf;
288pub mod unstable;
289
290pub(crate) use debug::derive_debug_via_id;
291// TODO: Uncomment when MSRV >= 1.64
292// use core::ffi::CStr;
293use std::ffi::CStr;
294
295use crate::aws_lc::{
296 CRYPTO_library_init, ERR_error_string, ERR_get_error, FIPS_mode, OpenSSL_version, ERR_GET_FUNC,
297 ERR_GET_LIB, ERR_GET_REASON, OPENSSL_VERSION,
298};
299use std::sync::Once;
300
301static START: Once = Once::new();
302
303#[inline]
304/// Initialize the *AWS-LC* library. (This should generally not be needed.)
305pub fn init() {
306 START.call_once(|| unsafe {
307 CRYPTO_library_init();
308 });
309}
310
311#[cfg(feature = "fips")]
312/// Panics if the underlying implementation is not FIPS, otherwise it returns.
313///
314/// # Panics
315/// Panics if the underlying implementation is not FIPS.
316pub fn fips_mode() {
317 try_fips_mode().unwrap();
318}
319
320/// Indicates whether the underlying implementation is FIPS.
321///
322/// # Errors
323/// Return an error if the underlying implementation is not FIPS, otherwise Ok.
324pub fn try_fips_mode() -> Result<(), &'static str> {
325 init();
326 match unsafe { FIPS_mode() } {
327 1 => Ok(()),
328 _ => Err("FIPS mode not enabled!"),
329 }
330}
331
332/// The version number of the linked AWS-LC library (e.g. `"5.1.0"`).
333///
334/// This identifies the exact release you are running against. For FIPS builds it
335/// can disambiguate builds that share a FIPS module number, but it must not be
336/// used to infer FIPS certification status directly.
337///
338/// # Panics
339/// Panics if AWS-LC returns a version string that is not valid UTF-8.
340#[must_use]
341pub fn awslc_version() -> &'static str {
342 init();
343 let full = unsafe { CStr::from_ptr(OpenSSL_version(OPENSSL_VERSION)) }
344 .to_str()
345 .expect("AWS-LC version string is not valid UTF-8");
346 // "AWS-LC 5.1.0" / "AWS-LC FIPS 3.4.0" -> the trailing version token.
347 full.rsplit_once(' ').map_or(full, |(_, version)| version)
348}
349
350/// The FIPS module version this build corresponds to, or `None` if the build
351/// does not correspond to an AWS-LC FIPS release branch.
352///
353/// Unlike [`awslc_version()`], this is a build-time constant resolved from
354/// the AWS-LC headers; the library linked at runtime is not consulted. It is
355/// also not equivalent to [`try_fips_mode()`] and does not by itself imply
356/// FIPS certification status. Returns `None` when the system-library version
357/// check is skipped.
358// TODO: Resolve at runtime via the `FIPS_version()` C API once
359// aws-lc-fips-sys tracks a FIPS 5+ branch.
360#[must_use]
361pub fn fips_version() -> Option<u32> {
362 let version = aws_lc::fips_version();
363 (version != 0).then_some(version)
364}
365
366#[cfg(feature = "fips")]
367/// Panics if the underlying implementation is not using CPU jitter entropy, otherwise it returns.
368///
369/// # Panics
370/// Panics if the underlying implementation is not using CPU jitter entropy.
371pub fn fips_cpu_jitter_entropy() {
372 try_fips_cpu_jitter_entropy().unwrap();
373}
374
375/// Indicates whether the underlying implementation is FIPS.
376///
377/// # Errors
378/// Return an error if the underlying implementation is not using CPU jitter entropy, otherwise Ok.
379pub fn try_fips_cpu_jitter_entropy() -> Result<(), &'static str> {
380 init();
381 match unsafe { aws_lc::FIPS_is_entropy_cpu_jitter() } {
382 1 => Ok(()),
383 _ => Err("FIPS CPU Jitter Entropy not enabled!"),
384 }
385}
386
387#[allow(dead_code)]
388unsafe fn dump_error() {
389 let err = ERR_get_error();
390 let lib = ERR_GET_LIB(err);
391 let reason = ERR_GET_REASON(err);
392 let func = ERR_GET_FUNC(err);
393 let mut buffer = [0u8; 256];
394 ERR_error_string(err, buffer.as_mut_ptr().cast());
395 let error_msg = CStr::from_bytes_with_nul_unchecked(&buffer);
396 eprintln!("Raw Error -- {error_msg:?}\nErr: {err}, Lib: {lib}, Reason: {reason}, Func: {func}");
397}
398
399mod sealed {
400 /// Traits that are designed to only be implemented internally in *aws-lc-rs*.
401 //
402 // Usage:
403 // ```
404 // use crate::sealed;
405 //
406 // pub trait MyType: sealed::Sealed {
407 // // [...]
408 // }
409 //
410 // impl sealed::Sealed for MyType {}
411 // ```
412 pub trait Sealed {}
413}
414
415#[cfg(test)]
416mod tests {
417 use crate::{dump_error, init};
418
419 #[test]
420 fn test_init() {
421 init();
422 }
423
424 #[test]
425 fn test_dump() {
426 unsafe {
427 dump_error();
428 }
429 }
430
431 #[cfg(not(feature = "fips"))]
432 #[test]
433 fn test_fips() {
434 assert!({ crate::try_fips_mode().is_err() });
435 // Re-enable with fixed test after upstream has merged RAGDOLL
436 //assert!({ crate::try_fips_cpu_jitter_entropy().is_ok() });
437 }
438
439 #[test]
440 // FIPS mode is disabled for an ASAN build
441 #[cfg(feature = "fips")]
442 fn test_fips() {
443 #[cfg(not(feature = "asan"))]
444 crate::fips_mode();
445 if aws_lc::CFG_CPU_JITTER_ENTROPY() {
446 crate::fips_cpu_jitter_entropy();
447 }
448 }
449
450 #[test]
451 fn test_awslc_version() {
452 let version = crate::awslc_version();
453 let major = version
454 .split('.')
455 .next()
456 .and_then(|major| major.parse::<u32>().ok())
457 .expect("AWS-LC version should start with a numeric major version");
458 assert!(major > 0);
459 }
460
461 #[cfg(not(feature = "fips"))]
462 #[test]
463 fn test_fips_version() {
464 assert_eq!(crate::fips_version(), None);
465 }
466
467 #[cfg(feature = "fips")]
468 #[test]
469 fn test_fips_version() {
470 // Module versions are monotonic across FIPS branches; the pinned branch is 4.
471 assert!(crate::fips_version().unwrap() >= 4);
472 }
473}