SharkSSL C/C++ Reference
TLS, cryptography, certificates, and PSA integration
Verify firmware signatures with SharkSSL

Check that a firmware image matches a signature made with your trusted signing key before accepting an update. This small C example connects the two parts of that workflow: OpenSSL signs on the development computer; SharkSSL verifies on the device.

The demo uses ECDSA (Elliptic Curve Digital Signature Algorithm) with the P-256 curve and a SHA-256 digest. The digest is a 32-byte fingerprint of the firmware. The signature lets the device check that fingerprint using the public key paired with the signing private key.

Start with the included data to check your SharkSSL integration, then adapt the same verification call to your firmware. All data needed for the first run is inside ecdsa-demo.c.

Run the included test

You need an existing SharkSSL application with SHA-256, ECC (elliptic curve cryptography), ECDSA, the ECDSA API and P-256 enabled. The corresponding options must be 1 when compiling the library and the application:

#define SHARKSSL_USE_SHA_256 1
#define SHARKSSL_USE_ECC 1
#define SHARKSSL_ENABLE_ECDSA 1
#define SHARKSSL_ENABLE_ECDSA_API 1
#define SHARKSSL_ECC_USE_SECP256R1 1
  1. Add ecdsa-demo.c to your application using its normal SharkSSL headers and platform configuration. Keep the three test-data arrays unchanged.
  2. Declare int sharkssl_ecdsa_demo(void); and call it after your normal platform initialization. The verifier uses SharkSSL's memory allocator.
  3. Read the output through printf, or route it to your serial console/logger.

The function takes no arguments. It returns 0 only when the valid signature is accepted and the deliberately changed digest is rejected. It returns 1 if either check fails. This function is a self-test; the first verification call inside it is the operation to adapt for your update code.

Expected output:

Message: 37 bytes; DER signature: 71 bytes
SHA-256: 48fdc4caefd77f278fbdef313d7a22fe11697370975532a4ce0509c0e5e3b9c1
Valid signature: ret=0 (expected 0)
Changed digest: ret=-3294 (expected -3294)
PASS

The second result is an intentional failure. The C code compares it with SHARKSSL_ECDSA_VERIFICATION_FAIL and prints the expected numeric value. The device test needs no files, private key, PEM parser or application random number generator initialization.

Optional: run on Windows first

From a Visual Studio Developer Command Prompt, change to examples\ECDSA-Verify in your SharkSSL source SDK and run the included build script:

build-windows.cmd ..\..

If you copied the demo elsewhere, supply the SDK's full path instead:

build-windows.cmd "C:\path\to\SharkSSL"

The SDK directory must contain inc\SharkSSL.h and src. The script supports the original source tree and an amalgamated SDK with src\SharkSSL.c. It builds and runs build\ecdsa-demo.exe using settings local to this desktop build.

If you use your own desktop project, define ECDSA_DEMO_MAIN to enable the supplied main(). Leave it undefined when your application already has an entry point. A desktop pass does not replace running the test on your target.

Follow the data from signing to verification

The C file follows three steps: hash the message, verify its signature, and run the deliberate failure test. The accompanying files let you check the same inputs independently on your computer:

File What it contains
message.bin The 37 signed bytes, including the final newline
public.pem The demo public key in PEM text format
signature.der The 71-byte binary signature of message.bin

DER (Distinguished Encoding Rules) encodes the ECDSA signature as a sequence of two integers, called r and s. The verifier expects this complete structure. PEM is the text representation used to exchange the public key with OpenSSL; the C array already contains the converted SharkSSL key.

With OpenSSL installed, run these commands from this folder:

# Compare the digest, verify the signature, and inspect its encoding.
openssl dgst -sha256 message.bin
openssl dgst -sha256 -verify public.pem -signature signature.der message.bin
openssl asn1parse -inform DER -in signature.der

The digest must match the demo output, verification must report Verified OK, and the signature must contain a sequence with two integers.

Adapt the example to your firmware

Sign on the development computer

In a separate test directory containing your firmware.bin, run:

# Create a new test key pair, then sign and verify the original firmware file.
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out test-private.pem
openssl pkey -in test-private.pem -pubout -out test-public.pem
openssl dgst -sha256 -sign test-private.pem -out firmware.sig firmware.bin
openssl dgst -sha256 -verify test-public.pem -signature firmware.sig firmware.bin
# Convert the public key with the tool supplied with your SharkSSL SDK.
SharkSSLParseKey test-public.pem

These commands create a new key pair. Use its public key with its signatures; it will not match the demo's included signature. Keep the private key on the signing computer and use the generated SharkSSL public-key array on the device. For a product, provision the verification key through a trusted process.

Verify on the device

  1. Construct a SharkSslSha256Ctx once. Pass each firmware block, in order, to SharkSslSha256Ctx_append() using its actual byte count. Hash exactly the bytes signed on the computer, excluding any attached signature.
  2. Call SharkSslSha256Ctx_finish() once to obtain the raw 32-byte digest.
  3. Call sharkssl_ECDSA_verify_hash() as in step 2 of the C example. Supply your trusted public key, the binary contents of firmware.sig, its actual byte length, the digest and SHARKSSL_HASHID_SHA256.
  4. Accept the image only on SHARKSSL_ECDSA_OK. Every other result is a failure.

siglen is a U16 byte count. If your package parser stores the length in a wider type, check its bounds before converting it to U16. A P-256 signature needs at most 72 bytes in DER form, but pass the actual length, not the buffer capacity. Do not obtain the verification key from an untrusted update package.

This example covers signature verification. A complete updater also needs protected key storage, version/rollback policy and a safe installation process.

If verification fails

Check What the verifier needs
Digest 32 binary SHA-256 bytes, not a 64-character hexadecimal string
Signature Complete binary DER, not a raw 64-byte concatenation of r and s or encoded text
Signature length Exact bytes received, with no added terminator or padding
Hash argument SHARKSSL_HASHID_SHA256, not SHARKSSL_SHA256_HASH_LEN
Signed data The original firmware bytes, hashed once
Public key The SharkSSL-format key corresponding to the signing private key

openssl dgst -sha256 -sign hashes its input before signing. Give it the original firmware file. Passing a previously calculated digest file would hash that digest again. The SharkSSL verification API uses the supplied digest directly, without hashing it again.

Run the unchanged demo first. If it fails, record the printed digest, numeric return codes, SharkSSL version, target processor and library configuration. If it passes but your update fails, compare the exact key, signature bytes, signature length and digest at the call. A generic verification failure can also indicate a parsing, curve-support or allocation problem.

Related examples and reference

The SDK's RayCrypto-ECDSA.c demonstrates signing and verification within SharkSSL, including different key representations and curves. This example focuses on an OpenSSL-generated signature and a small device verification call.