pw_crypto#
A set of safe (read: easy to use, hard to misuse) crypto APIs.
The following crypto services are provided by this module.
Hashing a message with SHA256.
Verifying a digital signature signed with ECDSA over the NIST P256 curve.
Many more to come …
SHA256#
Obtaining a oneshot digest.
#include "pw_crypto/sha256.h"
std::byte digest[32];
if (!pw::crypto::sha256::Hash(message, digest).ok()) {
// Handle errors.
}
// The content can also come from a pw::stream::Reader.
if (!pw::crypto::sha256::Hash(reader, digest).ok()) {
// Handle errors.
}
Hashing a long, potentially non-contiguous message.
#include "pw_crypto/sha256.h"
std::byte digest[32];
if (!pw::crypto::sha256::Sha256()
.Update(chunk1).Update(chunk2).Update(chunk...)
.Final().ok()) {
// Handle errors.
}
ECDSA#
Verifying a digital signature signed with ECDSA over the NIST P256 curve.
#include "pw_crypto/sha256.h"
std::byte digest[32];
if (!pw::crypto::sha256::Hash(message, digest).ok()) {
// handle errors.
}
if (!pw::crypto::ecdsa::VerifyP256Signature(public_key, digest,
signature).ok()) {
// handle errors.
}
Verifying a digital signature signed with ECDSA over the NIST P256 curve, with a long and/or non-contiguous message.
#include "pw_crypto/sha256.h"
std::byte digest[32];
if (!pw::crypto::sha256::Sha256()
.Update(chunk1).Update(chunk2).Update(chunkN)
.Final(digest).ok()) {
// Handle errors.
}
if (!pw::crypto::ecdsa::VerifyP256Signature(public_key, digest,
signature).ok()) {
// Handle errors.
}
AES#
Encrypting a single AES 128-bit block.
Warning
This is a low-level operation. Users should know exactly what they are doing and must ensure that this operation does not violate any safety bounds that more refined operations usually ensure.
#include "pw_crypto/aes.h"
std::byte encrypted[16];
if (!pw::crypto::unsafe::aes::EncryptBlock(key, message, encrypted).ok()) {
// Handle errors.
}
Configuration#
The crypto services offered by pw_crypto can be backed by different backend crypto libraries.
Mbed TLS#
The Mbed TLS project is a mature and full-featured crypto library that implements cryptographic primitives, X.509 certificate manipulation and the SSL/TLS and DTLS protocols.
The project also has good support for interfacing to cryptographic accelerators.
The small code footprint makes the project suitable and popular for embedded systems.
To select the Mbed TLS backend, the MbedTLS library needs to be installed and configured. If using GN, do,
# Install and configure MbedTLS
pw package install mbedtls
gn gen out --args='
dir_pw_third_party_mbedtls=getenv("PW_PACKAGE_ROOT")+"/mbedtls"
pw_crypto_SHA256_BACKEND="//pw_crypto:sha256_mbedtls_v3"
pw_crypto_ECDSA_BACKEND="//pw_crypto:ecdsa_mbedtls_v3"
pw_crypto_AES_BACKEND="//pw_crypto:aes_mbedtls_v3"
'
ninja -C out
If using Bazel, add the Mbed TLS repository to your WORKSPACE and select appropriate backends by adding them to your project’s platform:
platform(
name = "my_platform",
constraint_values = [
"@pigweed//pw_crypto:sha256_mbedtls_backend",
"@pigweed//pw_crypto:ecdsa_mbedtls_backend",
"@pigweed//pw_crypto:aes_mbedtls_backend",
# ... other constraint_values
],
)
For optimal code size and/or performance, the Mbed TLS library can be configured per product. Mbed TLS configuration is achieved by turning on and off MBEDTLS_* options in a config.h file. See //third_party/mbedtls for how this is done.
pw::crypto::sha256
does not need any special configuration as it uses the
mbedtls_sha256_* APIs directly. However you can optionally turn on
MBEDTLS_SHA256_SMALLER
to further reduce the code size to from 3KiB to
~1.8KiB at a ~30% slowdown cost (Cortex-M4).
#define MBEDTLS_SHA256_SMALLER
pw::crypto::ecdsa
requires the following minimum configurations which yields
a code size of ~12KiB.
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ECDSA_C
// The ASN1 options are needed only because mbedtls considers and verifies
// them (in check_config.h) as dependencies of MBEDTLS_ECDSA_C.
#define MBEDTLS_ASN1_WRITE_C
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_ECP_NO_INTERNAL_RNG
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
Micro ECC#
Warning
Micro ECC’s upstream hasn’t received any updates since April 2023. Please investigate to make sure that it meets your product’s security requirements before use.
To select Micro ECC, the library needs to be installed and configured.
# Install and configure Micro ECC
pw package install micro-ecc
gn gen out --args='
dir_pw_third_party_micro_ecc=getenv("PW_PACKAGE_ROOT")+"/micro-ecc"
pw_crypto_ECDSA_BACKEND="//pw_crypto:ecdsa_uecc"
'
The default micro-ecc backend uses big endian as is standard practice. It also
has a little-endian configuration which can be used to slightly reduce call
stack frame use and/or when non pw_crypto clients use the same micro-ecc
with a little-endian configuration. The little-endian version of micro-ecc
can be selected with pw_crypto_ECDSA_BACKEND="//pw_crypto:ecdsa_uecc_little_endian"
Note Micro-ECC does not implement any hashing functions, so you will need to use other backends for SHA256 functionality if needed.
Size Reports#
Below are size reports for each crypto service. These vary across configurations.
Label |
Segment |
Delta |
---|---|---|
No backend is selected. |
(ALL) |
0 |
API reference#
-
Status pw::crypto::ecdsa::VerifyP256Signature(ConstByteSpan public_key, ConstByteSpan digest, ConstByteSpan signature)#
Verifies the
signature
ofdigest
usingpublic_key
.Example:
#include "pw_crypto/sha256.h" // Verify a digital signature signed with ECDSA over the NIST P256 curve. std::byte digest[32]; if (!pw::crypto::sha256::Hash(message, digest).ok()) { // handle errors. } if (!pw::crypto::ecdsa::VerifyP256Signature(public_key, digest, signature).ok()) { // handle errors. }
- Parameters:
public_key – [in] A byte string in SEC 1 uncompressed form
(0x04||X||Y)
, which is exactly 65 bytes. Compressed forms(02/03||X)
may not be supported by some backends, e.g. Mbed TLS.digest – [in] A raw byte string, truncated to 32 bytes.
signature – [in] A raw byte string
(r||s)
of exactly 64 bytes.
- Returns:
OK
for a successful verification, or an errorStatus
otherwise.
-
inline Status pw::crypto::sha256::Hash(ConstByteSpan message, ByteSpan out_digest)#
Calculates the SHA256 digest of
message
and stores the result inout_digest
.out_digest
must be at leastkDigestSizeBytes
long.One-shot digest example:
#include "pw_crypto/sha256.h" std::byte digest[32]; if (!pw::crypto::sha256::Hash(message, digest).ok()) { // Handle errors. } // The content can also come from a pw::stream::Reader. if (!pw::crypto::sha256::Hash(reader, digest).ok()) { // Handle errors. }
Long, potentially non-contiguous message example:
#include "pw_crypto/sha256.h" std::byte digest[32]; if (!pw::crypto::sha256::Sha256() .Update(chunk1).Update(chunk2).Update(chunk...) .Final().ok()) { // Handle errors. }
-
inline Status pw::crypto::sha256::Sha256::Final(ByteSpan out_digest)#
Finishes the hashing session and outputs the final digest in the first
kDigestSizeBytes
ofout_digest
.out_digest
must be at leastkDigestSizeBytes
long.Final()
locks down theSha256
instance from any additional use.Any error, including those occurring inside the constructor or
Update()
will be reflected in the return value ofFinal()
.
-
inline Sha256 &pw::crypto::sha256::Sha256::Update(ConstByteSpan data)#
Feeds
data
to the running hasher. The feeding can involve zero or moreUpdate()
calls and the order matters.
-
enum class pw::crypto::sha256::Sha256State#
A state machine of a hashing session.
Values:
-
enumerator kReady#
Initialized and accepting input (via
Update()
).
-
enumerator kFinalized#
Finalized by
Final()
. Any additional requests toUpdate()
orFinal()
will trigger a transition tokError
.
-
enumerator kError#
In an unrecoverable error state.
-
enumerator kReady#