/* Copyright (c) 2026 Real Time Logic LLC. * Verify an OpenSSL-generated firmware signature with SharkSSL. * * The signing computer signs the message with its private key. This device * example hashes the same bytes with SHA-256 and verifies the signature with * the matching public key. ECDSA is the signature algorithm; P-256 is its curve. * * Add this file to your SharkSSL application and call sharkssl_ecdsa_demo(). * Define ECDSA_DEMO_MAIN only when you want the desktop main() below. * See README.md for setup, expected output and signing your own firmware. */ #include "SharkSslCrypto.h" #include #if !SHARKSSL_USE_SHA_256 || !SHARKSSL_USE_ECC || \ !SHARKSSL_ENABLE_ECDSA || !SHARKSSL_ENABLE_ECDSA_API || \ !SHARKSSL_ECC_USE_SECP256R1 #error Enable SHA-256, ECC, ECDSA, the ECDSA API and SECP256R1 in SharkSSL. #endif /* Test data: keep these three arrays together for the first run. * message.bin contains these bytes, including the final newline (0x0A). * sizeof(message) - 1 excludes the C string's extra terminating zero. */ static const U8 message[] = "SharkSSL firmware verification demo.\n"; /* Public key converted from public.pem into SharkSSL's binary key format. * For a real update, use your own trusted public key stored on the device. */ static const U8 public_key[] = { 0x30, 0x82, 0x00, 0x00, 0x0A, 0x00, 0x17, 0x20, 0x1E, 0xCA, 0x16, 0xAA, 0x68, 0x1F, 0x87, 0x83, 0xCE, 0x91, 0x21, 0x65, 0x53, 0x73, 0x5D, 0xF4, 0x83, 0xEF, 0xAC, 0x52, 0x64, 0xB4, 0x1F, 0x6F, 0x8D, 0x55, 0x48, 0x11, 0x76, 0xC7, 0x2F, 0xA2, 0x94, 0x06, 0x99, 0xBD, 0xC9, 0x5A, 0xA1, 0xEE, 0xB5, 0x35, 0xDE, 0x31, 0xA0, 0x9C, 0x53, 0x04, 0xE0, 0x5F, 0xFA, 0x5C, 0x27, 0xD4, 0x68, 0x60, 0x17, 0xC9, 0x00, 0x37, 0x3F, 0x52, 0x19, 0xFA, }; /* Exact bytes of signature.der, produced by OpenSSL on the signing computer. * DER is the binary encoding of the ECDSA signature's two integers, r and s. * This signature is 71 bytes. Other signatures can have different lengths. * Preserve all bytes and their actual count; do not use strlen or add padding. */ static const U8 signature[] = { 0x30, 0x45, 0x02, 0x20, 0x3D, 0xA0, 0xE5, 0x0A, 0x22, 0x45, 0x5F, 0xB9, 0x61, 0xC3, 0x17, 0x21, 0x83, 0x88, 0x74, 0x38, 0x9F, 0x42, 0x5E, 0x4D, 0xC0, 0xFA, 0xA0, 0xF6, 0x40, 0x9B, 0xBD, 0xDD, 0xE5, 0xFB, 0x23, 0x3F, 0x02, 0x21, 0x00, 0xAA, 0xB0, 0x4F, 0x12, 0xB8, 0x11, 0x24, 0xBB, 0xAC, 0x32, 0x64, 0xF7, 0x66, 0x7A, 0x27, 0x9C, 0xB1, 0x12, 0xD2, 0x8E, 0x08, 0x75, 0xE4, 0xA0, 0x2B, 0x88, 0x95, 0xB5, 0xDF, 0x2F, 0xD1, 0x6D, }; /* Run the included known-good test, then a deliberate failure test. * No arguments. Returns 0 only if BOTH tests behave as expected, else 1. * printf provides diagnostics; redirect it to your serial console if needed. * The test uses the arrays above and SharkSSL's configured memory allocator. */ int sharkssl_ecdsa_demo(void) { SharkSslSha256Ctx ctx; U8 digest[SHARKSSL_SHA256_HASH_LEN]; sharkssl_ECDSA_RetVal ret; unsigned int i; /* 1. Hash the original message bytes. * For firmware, construct once, append each firmware block in order, and * finish once after the last block. Exclude the appended signature itself. */ SharkSslSha256Ctx_constructor(&ctx); SharkSslSha256Ctx_append(&ctx, message, (U32)(sizeof(message) - 1)); SharkSslSha256Ctx_finish(&ctx, digest); printf("Message: %u bytes; DER signature: %u bytes\n", (unsigned int)(sizeof(message) - 1), (unsigned int)sizeof(signature)); printf("SHA-256: "); for (i = 0; i < sizeof(digest); ++i) printf("%02x", (unsigned int)digest[i]); printf("\n"); /* 2. Verify using the trusted public key and the actual signature length. * digest contains 32 binary bytes; the printed hexadecimal text is only * for diagnostics. The last argument identifies SHA-256, not its length. * The casts match the API's key and signature pointer types. */ ret = sharkssl_ECDSA_verify_hash((SharkSslECCKey)public_key, (U8*)signature, (U16)sizeof(signature), digest, SHARKSSL_HASHID_SHA256); printf("Valid signature: ret=%d (expected %d)\n", (int)ret, (int)SHARKSSL_ECDSA_OK); if (ret != SHARKSSL_ECDSA_OK) return 1; /* 3. Test only: change one digest byte and require verification to fail. * This confirms that an unsuccessful verification is handled correctly. * In your firmware update path, use step 2 with the unmodified digest and * accept the image only when that call returns SHARKSSL_ECDSA_OK. */ digest[0] ^= 1; ret = sharkssl_ECDSA_verify_hash((SharkSslECCKey)public_key, (U8*)signature, (U16)sizeof(signature), digest, SHARKSSL_HASHID_SHA256); printf("Changed digest: ret=%d (expected %d)\n", (int)ret, (int)SHARKSSL_ECDSA_VERIFICATION_FAIL); if (ret != SHARKSSL_ECDSA_VERIFICATION_FAIL) return 1; printf("PASS\n"); return 0; } #ifdef ECDSA_DEMO_MAIN int main(void) { return sharkssl_ecdsa_demo(); } #endif