# AWS-LC: an OpenSSL-compatible cryptographic library with distribution packaging and symbol versioning

> AWS-LC is the general-purpose cryptographic library maintained by the AWS Cryptography team, built on code from BoringSSL and OpenSSL. It keeps most of OpenSSL's API so existing applications can move over, adds optimised assembly for x86 and Arm, and offers a distribution packaging mode with SONAME versioning, ELF symbol versioning and cohabitant headers for side-by-side installs.

**aws/aws-lc** — AWS-LC is a general-purpose cryptographic library maintained by the AWS Cryptography team for AWS and their customers. It іs based on code from the Google BoringSSL project and the OpenSSL project.

- Repository: https://github.com/aws/aws-lc
- Website: https://aws.github.io/aws-lc/headers.html
- Stars: 853 · Forks: 226
- Language: Assembly
- License: NOASSERTION
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/aws-aws-lc

## What AWS-LC is and where it comes from

AWS-LC is a general-purpose cryptographic library maintained by the AWS Cryptography team for AWS and their customers, and the repository is explicit about its lineage: it is based on code from the Google BoringSSL project and the OpenSSL project.

That lineage explains both its strengths and its constraints. BoringSSL brought the model of a small security-focused library that drops the parts of OpenSSL nobody uses, removes the compatibility promises, and accepts breaking changes in exchange for a much smaller reviewable codebase. OpenSSL brought the API surface that the entire ecosystem of software is written against. AWS-LC is the combination: BoringSSL's internals with most of OpenSSL's API preserved.

The stated goal is to maintain a secure libcrypto that is compatible with software and applications used at AWS, and there is a second purpose behind it that matters more than it first appears: AWS-LC serves as the new home for the AWS Cryptography team to publish open source contributions and enhancements that are submitted to other libcrypto projects. In other words it is a public upstream, not an internal fork.

The library is split in two, and the split is a practical detail for anyone building it. AWS-LC's libcrypto is a C library and needs a C compiler, while AWS-LC's libssl is a C++ library and needs a C++ compiler. The libssl part being C++ is unusual and is worth knowing before you plan a build.

The repository also carries the paperwork that comes with being a maintained upstream: a SECURITY.md, a CODE_OF_CONDUCT.md, a STYLE.md, an API-CONVENTIONS.md, a VERSIONING.md, an INCORPORATING.md for how to get a change in, and FUZZING.md and SANDBOXING.md as separate documents. The last two are a good signal about how the project treats the input-parsing surface that a cryptographic library inevitably has.

## Distribution packaging: SONAME, symbol versioning and cohabitant headers

The distribution packaging mode is the feature that distinguishes AWS-LC from a straight BoringSSL build, and it is worth understanding in detail because it solves a real packaging problem.

Linux and BSD systems can have several crypto libraries installed at once, each with its own shared object name, and the usual convention is to encode the ABI version in the filename so that different major versions can coexist. AWS-LC supports this distribution packaging mode for system-wide installation, and it is enabled by building with -DENABLE_DIST_PKG=1.

Three things switch on. SONAME versioning means the libraries use a standard SONAME such as libcrypto-awslc.so.1. Symbol versioning means ELF symbol versioning for ABI stability tracking, with a version node like AWS_LC_1.0. And cohabitant headers means the headers are installed to include/aws-lc/openssl/ so they can coexist with other crypto libraries rather than overwriting them.

The symbol versioning detail is the most interesting part, because it changes what a downstream distribution can promise. Symbol versioning assigns every public API symbol to a named version node. New symbols introduced in future releases get new version nodes that inherit from their predecessors, which enables fine-grained compatibility tracking. So a distribution can say which symbols a given AWS-LC release provided and which came later, rather than treating the whole library as one opaque version.

The README also notes that it defaults to on in this mode but is an independent option, -DENABLE_SYMBOL_VERSIONING, so versioned symbols can be adopted without the header relocation and binary renaming. That separation is deliberate and practical: some distributions want the symbol versioning and cannot move the headers.

The detail is documented separately in docs/SymbolVersioning.md.

## Building it, and the portability versus speed trade

The quickstart for Amazon Linux 2 gives the build sequence, and it is worth reading because it shows the shape of the toolchain the project expects.

```bash
sudo yum install cmake3 ninja-build clang perl golang
```

The repository is forked, cloned and built out of tree with CMake and Ninja, in a RelWithDebInfo configuration so that the build has both optimisations and debug information:

```bash
git clone https://github.com/${YOUR_GITHUB_ACCOUNT_NAME}/aws-lc.git
mkdir aws-lc-build && cd aws-lc-build
cmake3 -GNinja \
    -DCMAKE_BUILD_TYPE=RelWithDebInfo \
    -DCMAKE_INSTALL_PREFIX=../aws-lc-install \
    ../aws-lc
ninja-build run_tests && ninja-build install
```

The full list of required dependencies and build options is in BUILDING.md, and the CONTRIBUTING.md file explains how to get a change reviewed by the maintainers. There is also a PORTING.md for people moving an application from OpenSSL, and INCORPORATING.md for how contributions are taken.

The optimisation strategy is described in the feature list. A portable C implementation of all algorithms is included, and optimised assembly implementations of select algorithms are included for some x86 and Arm CPUs.

The testing method for that assembly is the part that shows how seriously it is taken. The project uses AWS Graviton processors to test ARMv8 optimisations and Intel CPUs to test x86 and x86-64 optimisations. The Intel Software Development Emulator is used to run tests on many different x86 processors, which is how you cover a wide range of x86 parts without owning them.

If you use another CPU and want to make sure it is tested, the README asks you to open an issue to discuss adding an assembly optimised algorithm implementation to the CI. That is a reasonable bar, since hand-written assembly for a specific microarchitecture is exactly the kind of change that needs a machine to test on.

## Platform support, and what the WebAssembly caveat means

The platform matrix is split into two tiers, and the split is explicit.

Actively tested in the CI pipeline are Android on aarch64, iOS on aarch64, Linux on aarch64, ppc, ppc64, ppc64le and x86-64, macOS on aarch64 and x86-64, and Windows on x86-64. For each pull request, the proposed change is validated to confirm it successfully builds and that tests pass for these platforms. A more complete description is in the CI README under tests/ci.

The second tier is platforms of interest to the project or the community, where problems reported might not be prioritised for immediate action: Android on arm32, Emscripten on wasm32, FreeBSD on arm64 and x86-64, Linux on arm32, loongarch64, risc-v64, s390x and x86, NetBSD and OpenBSD on arm64 and x86-64, and Windows on aarch64. The README is careful about the distinction: regardless of the support level for a particular platform, contributions that improve or extend the support are welcome, and a request for a platform to be added to the CI is handled by opening an issue.

The WebAssembly support carries a list of caveats that deserves close reading, because it is the kind of thing that gets skipped. AWS-LC can be built for WebAssembly using Emscripten, but this support is experimental and has important security considerations.

There is no FIPS mode, since WASM builds cannot be FIPS-validated. Randomness relies on the runtime's getentropy() implementation. There are no side-channel protections, and the timing and cache-based mitigations that exist for native builds may not apply in the WASM environment. And there are threading limitations, because it uses Web Workers which have different security properties than native threads.

That last point is the one to dwell on. A cryptographic library without its side-channel mitigations is still useful, but it is a different security claim, and calling it a WASM build of the same library would overstate what you are getting.

FIPS compliance is documented separately in crypto/fipsmodule/FIPS.md, and the post-quantum algorithms the project supports are covered in its own section.

## Limits: compatibility is not the same as equivalence, and the maintenance burden is real

Several limits follow from the design, and they are the things to check before you commit.

The first is that API compatibility is not behavioural compatibility. The README says AWS-LC is compatible with the majority of OpenSSL's APIs, which is a careful word, and it says the team is open to discussing adding missing functionality and understanding your use case in an issue. Majority means majority. If your application calls an OpenSSL API that AWS-LC does not implement, you find out at compile time or at runtime, and the README's own instruction for a use case is to open an issue and discuss it. The PORTING.md guide exists for exactly this transition.

The second is the size of the review surface. AWS-LC inherits OpenSSL's code base through BoringSSL, so this is not a small library in absolute terms. What it is smaller than is OpenSSL's API surface and its deprecation backlog. The maintenance model is continuous upstreaming of security fixes, which is the reason to use it rather than to vendor your own copy.

The third is the platform asymmetry. Windows on aarch64 is in the second tier, not the first, and so are several Linux architectures and all the BSDs. If you deploy to one of those, you are running on a platform the project tests less aggressively, and the README is upfront that reported problems there might not be prioritised.

The fourth is the assembly component. Hand-optimised assembly for selected algorithms on selected CPUs means the library's output can differ between architectures in timing and, in principle, in the last bits of some computations. That is a legitimate reason to prefer the portable path for some deployments, and it means the library has two code paths per algorithm to reason about.

The fifth is the licensing picture. The repository records a custom licence that GitHub cannot classify, with the text in the LICENSE file, and the tree carries a NOTICE file and a third_party/ directory. A library derived from OpenSSL carries OpenSSL's Apache-2.0 terms, and BoringSSL's own licence comes with it, so a project with its own licence obligations needs to read all three before redistributing.

## AWS-LC against OpenSSL and against BoringSSL directly

Two comparisons make the choice clearer.

The first is against staying on OpenSSL. The difference in approach is the maintenance model rather than the code. OpenSSL has a large API surface and a long history of deprecations that must keep working for compatibility, and its release cadence is slower because every change has to be assessed against that surface. AWS-LC carries BoringSSL's model of a smaller surface with faster iteration, while keeping enough of OpenSSL's API that most applications move without source changes. For an organisation that wants OpenSSL's API without OpenSSL's backlog, that is the trade.

The cost is that compatibility is partial, so a port is a project rather than a package swap, and the porting guide is the first thing to read.

The second comparison is against using BoringSSL directly. AWS-LC and BoringSSL share ancestry and differ in emphasis: BoringSSL is what Chrome and other Google products use, and it makes no compatibility promise with OpenSSL at all. AWS-LC is the OpenSSL-compatible branch of that family, maintained by a team whose stated purpose is compatibility with software used at AWS.

For a product built on AWS infrastructure, that is a reasonable default, because the vendor is funding the work and the ecosystem expectations are met. For an application that does not care about OpenSSL compatibility and wants the smallest possible surface, BoringSSL is the more honest choice.

The distribution packaging mode is the piece that tips the decision for distributions and for projects that must coexist with another libcrypto on the same host. A package manager or an appliance that already ships OpenSSL needs the shared object names, the headers in a separate directory and the ability to track which symbols came from which release. That is exactly what ENABLE_DIST_PKG provides, and it is not something you get by building BoringSSL yourself.

## Conclusion

Adopt AWS-LC when you want OpenSSL-compatible cryptography with a maintained upstream behind it, you need the distribution packaging mode so your library coexists with another libcrypto on the same system, or you run on Arm and want tested assembly for it. Do not adopt it expecting a smaller codebase than OpenSSL, since it carries the same lineage. Verify first that your platform and compiler are in the actively tested matrix, that any OpenSSL API you use is present rather than assumed compatible, and that you read the porting guide and the sandboxing and fuzzing documents before you depend on it.

## FAQ

### What is AWS-LC and what is it based on?

AWS-LC is a general-purpose cryptographic library maintained by the AWS Cryptography team for AWS and their customers. It is based on code from the Google BoringSSL project and the OpenSSL project, contains portable C implementations of the algorithms needed for TLS, and includes optimised assembly versions for some x86 and Arm CPUs. Its goal is to maintain a secure libcrypto compatible with the software and applications used at AWS.

### What does the AWS-LC distribution packaging mode give you?

Building with -DENABLE_DIST_PKG=1 enables SONAME versioning so libraries use a standard name such as libcrypto-awslc.so.1, ELF symbol versioning for ABI stability tracking with version nodes like AWS_LC_1.0, and cohabitant headers installed to include/aws-lc/openssl/ so they coexist with other crypto libraries. Symbol versioning assigns every public API symbol to a version node, with new symbols in later releases getting new nodes that inherit from their predecessors.

### What are the limitations of building AWS-LC for WebAssembly?

The README states the support is experimental with important security considerations: WASM builds cannot be FIPS-validated and have no FIPS mode, randomness relies on the runtime's getentropy() implementation, the timing and cache-based side-channel mitigations of native builds may not apply, and threading uses Web Workers, which have different security properties from native threads.

## Sources

- [aws/aws-lc on GitHub](https://github.com/aws/aws-lc)
- [Issues](https://github.com/aws/aws-lc/issues)
- [Project website](https://aws.github.io/aws-lc/headers.html)
- [README](https://github.com/aws/aws-lc/blob/main/README.md)
- [Releases](https://github.com/aws/aws-lc/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/aws-aws-lc
