diff --git a/c/docs/client-configuration.md b/c/docs/client-configuration.md index 1833b5c6..1a3424e0 100644 --- a/c/docs/client-configuration.md +++ b/c/docs/client-configuration.md @@ -169,7 +169,7 @@ Defaults used when the matching option is left at 0: | Macro | Default | Bounds | | --- | --- | --- | -| `AZ_IOT_SU_MAX_ROOT_KEYS` | 4 | Root keys in the trust store. | +| `AZ_IOT_SU_MAX_ROOT_KEYS` | 8 | Root keys in the trust store. | | `AZ_IOT_SU_REQUEST_BUFFER_SIZE` | 16384 | Copy of the update metadata (manifest, signature and file URLs) for the current deployment; a one-file offer is about 5 KiB. A larger deployment is refused: `AZ_IOT_SU_EVENT_UPDATE_REFUSED` is raised with `AZ_IOT_ERR_NOT_ENOUGH_SPACE`. Also sizes `AZ_IOT_SU_STATE_BLOB_MAX_SIZE` and the scratch the client verifies the signature in, so the client holds about twice this value. | | `AZ_IOT_SU_VERIFY_SCRATCH_SIZE` | 8192 | Stack used by `az_iot_su_parse_update_request()` for the decoded signature (JWS header with the signing key, and the signatures). A signature that does not fit fails with `AZ_IOT_ERR_AUTH` and an `ERROR` log naming the part that is too large. | | `AZ_IOT_SU_WORKFLOW_ID_SIZE` | 64 | Workflow ID kept for reporting, duplicate detection and persistence. A deployment with a longer ID is refused: nothing is processed or reported, and `AZ_IOT_SU_EVENT_UPDATE_REFUSED` is raised with `AZ_IOT_ERR_NOT_ENOUGH_SPACE`. | diff --git a/c/docs/eng/software-updates.md b/c/docs/eng/software-updates.md index 9c0a344a..94464ae4 100644 --- a/c/docs/eng/software-updates.md +++ b/c/docs/eng/software-updates.md @@ -1241,7 +1241,7 @@ capacity is compile-time configurable: ```c #ifndef AZ_IOT_SU_MAX_ROOT_KEYS -#define AZ_IOT_SU_MAX_ROOT_KEYS 4 +#define AZ_IOT_SU_MAX_ROOT_KEYS 8 #endif ``` diff --git a/c/docs/eng/test-coverage.md b/c/docs/eng/test-coverage.md index 151a23fa..25194a7d 100644 --- a/c/docs/eng/test-coverage.md +++ b/c/docs/eng/test-coverage.md @@ -940,7 +940,7 @@ RS256 signature verification) through the crypto backends in `c/adapters/crypto_ | | Device properties too small is rejected | — | unit | Done | [device_properties_too_small_is_rejected](https://github.com/Azure/azure-iot-sdk/blob/main/c/tests/unit/su_client_test.c#L954) | | | Device properties buffer size matches the need | The reported requirement is exact, not an estimate. | unit | Done | [device_properties_buffer_size_matches_need](https://github.com/Azure/azure-iot-sdk/blob/main/c/tests/unit/su_client_test.c#L996) | | | Build report with too small a buffer is rejected | `az_iot_su_build_report()` bound. | unit | Done | [build_report_with_too_small_a_buffer_is_rejected](https://github.com/Azure/azure-iot-sdk/blob/main/c/tests/unit/su_client_test.c#L1373) | -| Manifest & crypto | Microsoft root keys are embedded | The shipped roots match the published values. | unit | Done | [microsoft_root_keys_are_embedded](https://github.com/Azure/azure-iot-sdk/blob/main/c/tests/unit/su_client_test.c#L1187) | +| Manifest & crypto | Microsoft root keys are embedded | ADU.200702.R, ADU.200703.R and ADU.241112.R, in that order; 3072-bit moduli; none disabled. | unit | Done | [microsoft_root_keys_are_embedded](https://github.com/Azure/azure-iot-sdk/blob/main/c/tests/unit/su_client_test.c#L1187) | | | Public parser accepts updateMetadata | `az_iot_su_parse_update_request()`; other shapes are NOT_FOUND. | unit | Done | [public_parser_accepts_update_metadata](https://github.com/Azure/azure-iot-sdk/blob/main/c/tests/unit/su_client_test.c) | | | Sha256 oneshot matches known answers | FIPS 180-4 vectors (empty to 1M bytes), through the SDK's one-shot SHA-256. Every crypto backend (OpenSSL; mbedTLS 3.6/4.1/4.2). | unit | Done | [sha256_oneshot_matches_known_answers](../../tests/support/crypto_contract.c) | | | Sha256 incremental matches known answers for any chunking | Chunks of 1/63/64/65/4096 bytes plus empty updates. | unit | Done | [sha256_incremental_matches_known_answers_for_any_chunking](../../tests/support/crypto_contract.c) | @@ -950,6 +950,7 @@ RS256 signature verification) through the crypto backends in `c/adapters/crypto_ | | HMAC-SHA256 matches known answers | RFC 4231 cases 1-4, 6, 7; a one-block key; empty key and data. Composed by the SDK over each backend's SHA-256. | unit | Done | [hmac_sha256_matches_known_answers](../../tests/support/crypto_contract.c) | | | HMAC-SHA256 rejects bad arguments | NULL key, data, output or backend with a non-zero length. | unit | Done | [hmac_sha256_rejects_bad_arguments](../../tests/support/crypto_contract.c) | | | Rs256 accepts known good vectors | 2048/3072/4096-bit, e=3, leading-zero modulus/exponent. | unit | Done | [rs256_accepts_known_good_vectors](../../tests/support/crypto_contract.c) | +| | Microsoft root keys verify the root key package | Each compiled-in root verifies its RS256 signature on Microsoft's published production root key package (version 2); a modulus with one byte changed is rejected. Every crypto backend. | unit | Done | [microsoft_root_keys_verify_the_root_key_package](../../tests/support/crypto_contract.c) | | | Rs256 rejects known bad vectors | Bit flips, wrong key/exponent, e=0/1/even, bad signature length or value, PSS, SHA-1/384/512, malformed PKCS#1 v1.5 encodings. | unit | Done | [rs256_rejects_known_bad_vectors](../../tests/support/crypto_contract.c) | | | Rs256 rejects missing inputs | NULL or zero-length key or signature; NULL message. | unit | Done | [rs256_rejects_missing_inputs](../../tests/support/crypto_contract.c) | | | Rs256 rejects oversized keys safely | Moduli and exponents that fill or exceed a fixed DER buffer, up to 70000 bytes; rejected with no out-of-bounds write. | unit | Done | [rs256_rejects_oversized_keys_safely](../../tests/support/crypto_contract.c) | @@ -959,6 +960,7 @@ RS256 signature verification) through the crypto backends in `c/adapters/crypto_ | | Init requires a crypto backend that verifies | None, incomplete or other-version backend is INVALID_ARG; no `verify_rs256` is NOT_SUPPORTED; the public init takes the connection's backend. | unit | Done | [init_requires_a_crypto_backend_that_verifies](https://github.com/Azure/azure-iot-sdk/blob/main/c/tests/unit/su_client_test.c) | | | Standalone entry points check the backend they need | `az_iot_su_parse_update_request()`: no backend is INVALID_ARG, no `verify_rs256` is NOT_SUPPORTED, and non-NULL outputs are zeroed on both. `az_iot_su_verify_file_hash()`: no backend is INVALID_ARG; a SHA-256-only backend verifies. | unit | Done | [standalone_entry_points_check_the_backend_they_need](https://github.com/Azure/azure-iot-sdk/blob/main/c/tests/unit/su_client_test.c) | | | Manifest signed by an unknown root key is rejected | End-to-end through `az_iot_su_parse_update_request()`. | unit | Done | [manifest_signed_by_an_unknown_root_key_is_rejected](https://github.com/Azure/azure-iot-sdk/blob/main/c/tests/unit/su_client_test.c#L1428) | +| | Manifest under root ADU.241112.R resolves to that key | With `az_iot_su_microsoft_root_keys()`, the SJWK is verified with the ADU.241112.R modulus. | unit | Done | [manifest_under_root_adu_241112_r_resolves_to_that_key](https://github.com/Azure/azure-iot-sdk/blob/main/c/tests/unit/su_client_test.c) | | | Malformed jws is rejected | Wrong segment count, bad base64url, missing header. | unit | Done | [malformed_jws_is_rejected](https://github.com/Azure/azure-iot-sdk/blob/main/c/tests/unit/su_client_test.c#L1449) | | | Near-limit nested signing key is verified | An offer near `AZ_IOT_SU_REQUEST_BUFFER_SIZE`, almost all signing JWK, verifies in the managed client. | unit | Done | [near_limit_nested_signing_key_is_verified](../../tests/unit/su_client_test.c) | | | Large signature is verified | JWS header and signing key over 2 KiB in an offer over 4 KiB; modulus in standard base64 with escaped slashes, asserted as decoded with the exponent and signature (managed and public parser). | unit | Done | [large_signature_is_verified](../../tests/unit/su_client_test.c) | diff --git a/c/inc/azure/iot/az_iot_su.h b/c/inc/azure/iot/az_iot_su.h index ebc441c7..b9aafa5f 100644 --- a/c/inc/azure/iot/az_iot_su.h +++ b/c/inc/azure/iot/az_iot_su.h @@ -77,7 +77,7 @@ extern "C" /* Maximum number of RSA root public keys the core trust store holds. */ #ifndef AZ_IOT_SU_MAX_ROOT_KEYS -#define AZ_IOT_SU_MAX_ROOT_KEYS 4 +#define AZ_IOT_SU_MAX_ROOT_KEYS 8 #endif /* Scratch buffer (in-struct) that holds a COPY of the `updateMetadata` diff --git a/c/src/features/su/su_client.c b/c/src/features/su/su_client.c index edfe9537..113971fc 100644 --- a/c/src/features/su/su_client.c +++ b/c/src/features/su/su_client.c @@ -2205,9 +2205,8 @@ void az_iot_su_client_deinit(az_iot_su_client* client) } /* az_iot_su_microsoft_root_keys() — Microsoft's compiled-in software updates production - * root public keys — is defined in su_root_keys_microsoft.c (generated from the - * official agent's hardcoded key list). Kept in a separate translation unit so - * the large key blobs live apart from the state machine. */ + * root public keys — is defined in su_root_keys_microsoft.c. Kept in a separate + * translation unit so the large key blobs live apart from the state machine. */ /* ------------------------------------------------------------------------- */ /* persistence & resume (Phase 5) */ diff --git a/c/src/features/su/su_root_keys_microsoft.c b/c/src/features/su/su_root_keys_microsoft.c index 57fb500c..c301312d 100644 --- a/c/src/features/su/su_root_keys_microsoft.c +++ b/c/src/features/su/su_root_keys_microsoft.c @@ -9,7 +9,9 @@ * big-endian public moduli of Microsoft's published software updates signing roots, * base64url-decoded from the official Device Update agent's hardcoded * root key list (Azure/iot-hub-device-update, - * src/utils/root_key_utils/src/root_key_list.c, non-test block). These are + * src/utils/root_key_utils/src/root_key_list.c, non-test block), plus + * ADU.241112.R from Microsoft's production root key package (version 2), + * whose signatures verify under both roots above. These are * PUBLIC keys and safe to embed; they are the immutable trust anchor that * verifies every software update manifest's signing chain. * @@ -76,6 +78,34 @@ static const uint8_t k_modulus_SU_200703_R[] = { 0x99, }; +static const uint8_t k_modulus_SU_241112_R[] = { + 0x00, 0x8b, 0x81, 0xa9, 0xea, 0x3d, 0x4d, 0x07, 0x69, 0x44, 0x1e, 0x1d, 0x2a, 0x5a, 0xed, 0x01, + 0xe4, 0xb0, 0x71, 0x52, 0xf1, 0xaa, 0x17, 0x8f, 0x60, 0x6e, 0xfa, 0x93, 0x8a, 0xd6, 0x99, 0xd3, + 0xa5, 0xf3, 0x52, 0xe7, 0xd9, 0x23, 0x03, 0xf4, 0x74, 0x5c, 0xd4, 0x2e, 0x5f, 0xed, 0x3d, 0x39, + 0x4a, 0x73, 0xcb, 0xde, 0x8e, 0xed, 0xc9, 0x08, 0xae, 0x84, 0xb5, 0x66, 0x82, 0xb0, 0x05, 0xd5, + 0xb4, 0x18, 0xfa, 0x0d, 0x5f, 0x24, 0x91, 0x35, 0xef, 0x87, 0x69, 0xc1, 0xdf, 0x8d, 0x14, 0xde, + 0xc7, 0x9d, 0x04, 0x6a, 0x39, 0x7d, 0x95, 0xa3, 0xfe, 0xb3, 0x47, 0xdc, 0x70, 0x7c, 0xad, 0x0e, + 0x93, 0x06, 0x24, 0xef, 0x15, 0x10, 0x27, 0x19, 0xac, 0x42, 0xa9, 0x08, 0xab, 0x61, 0xbd, 0xfc, + 0x5f, 0xb4, 0x81, 0x06, 0x9e, 0x23, 0x96, 0x39, 0xed, 0xf6, 0xec, 0xdc, 0x24, 0x00, 0x76, 0x8b, + 0xbd, 0xbb, 0xaa, 0x2e, 0x3c, 0x85, 0xab, 0xd6, 0x58, 0xb8, 0x65, 0x5b, 0x10, 0x50, 0x7d, 0x31, + 0xae, 0x53, 0x96, 0xa1, 0xef, 0xd2, 0xac, 0x9c, 0xad, 0x7a, 0x83, 0xf7, 0x86, 0x9c, 0x64, 0x61, + 0xf6, 0xd9, 0xc9, 0xf1, 0xa0, 0x80, 0x1b, 0xe7, 0x29, 0xfb, 0x71, 0x17, 0xe1, 0xff, 0x73, 0x41, + 0x77, 0xb7, 0xc8, 0x70, 0x26, 0xac, 0x66, 0x26, 0x30, 0x7d, 0xcc, 0xe8, 0x23, 0x9e, 0x31, 0x39, + 0xf6, 0x76, 0x2a, 0xa7, 0xe4, 0x26, 0x57, 0xaa, 0xc3, 0x39, 0xba, 0x0b, 0xf5, 0x90, 0x84, 0xb1, + 0x25, 0xe2, 0xfb, 0x17, 0x87, 0xed, 0x88, 0x60, 0x81, 0x7b, 0x58, 0xb7, 0x3c, 0xd6, 0x64, 0x77, + 0xaa, 0x25, 0x58, 0x52, 0xe7, 0x55, 0xf2, 0x51, 0xf6, 0x61, 0x1e, 0x4d, 0xd0, 0xbd, 0x4b, 0x1e, + 0xfc, 0x01, 0xe7, 0x59, 0xb1, 0xf0, 0x4a, 0xe3, 0x75, 0x67, 0xed, 0x1e, 0x8c, 0x95, 0x5b, 0x7b, + 0xf3, 0xf6, 0x54, 0xe6, 0x4b, 0xd5, 0x0a, 0x61, 0x49, 0xc2, 0xdb, 0xe4, 0x92, 0x62, 0x09, 0xc5, + 0x69, 0xb5, 0xdd, 0xee, 0x85, 0xe7, 0x9d, 0x78, 0x6b, 0x45, 0x10, 0x6f, 0x62, 0xb4, 0x43, 0x98, + 0xce, 0x98, 0x0b, 0xb2, 0x52, 0xf6, 0x57, 0xc2, 0x85, 0x44, 0x63, 0x72, 0x40, 0x9d, 0x25, 0x4e, + 0x3d, 0x82, 0x6d, 0xac, 0x36, 0x8e, 0x2e, 0x2f, 0x15, 0x54, 0x02, 0x48, 0x17, 0xeb, 0x45, 0x7a, + 0xac, 0x19, 0xa5, 0x61, 0x70, 0x37, 0x88, 0x4e, 0xf9, 0x48, 0xb1, 0xdd, 0xd8, 0x41, 0x73, 0xcb, + 0x95, 0xa9, 0x51, 0x41, 0x29, 0x42, 0xa8, 0x6b, 0xa8, 0x1c, 0xc1, 0xb9, 0x6b, 0xc0, 0xbc, 0x9f, + 0x75, 0xdd, 0xf5, 0x85, 0x98, 0x00, 0x61, 0xbf, 0x20, 0x07, 0xa3, 0x1b, 0x33, 0x6a, 0xd4, 0xea, + 0x6f, 0xf7, 0xed, 0x03, 0xd1, 0x82, 0x69, 0x2d, 0x2b, 0x54, 0x0a, 0xd9, 0x56, 0x42, 0x29, 0xd2, + 0x6d, +}; + static const uint8_t k_exponent_65537[] = { 0x01, 0x00, 0x01 }; static const az_iot_su_root_key k_microsoft_root_keys[] = { @@ -95,6 +125,14 @@ static const az_iot_su_root_key k_microsoft_root_keys[] = { .exponent_len = sizeof(k_exponent_65537), .disabled = false, }, + { + .kid = "ADU.241112.R", + .modulus = k_modulus_SU_241112_R, + .modulus_len = sizeof(k_modulus_SU_241112_R), + .exponent = k_exponent_65537, + .exponent_len = sizeof(k_exponent_65537), + .disabled = false, + }, }; const az_iot_su_root_key* az_iot_su_microsoft_root_keys(size_t* out_count) diff --git a/c/tests/support/crypto_contract.c b/c/tests/support/crypto_contract.c index 12147865..38d00181 100644 --- a/c/tests/support/crypto_contract.c +++ b/c/tests/support/crypto_contract.c @@ -21,6 +21,7 @@ #include "crypto_contract.h" #include "internal/crypto.h" #include "su_crypto_vectors.h" +#include "su_root_key_package_vectors.h" #define SU_ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0])) @@ -364,6 +365,50 @@ static void rs256_accepts_known_good_vectors(void** state) } } +/* Each compiled-in Microsoft root verifies its signature on the published root key + * package, so a wrong modulus byte fails here. */ +static void microsoft_root_keys_verify_the_root_key_package(void** state) +{ + (void)state; + size_t count = 0; + const az_iot_su_root_key* keys = az_iot_su_microsoft_root_keys(&count); + assert_int_equal(count, SU_ARRAY_LEN(k_su_root_key_package_signatures)); + for (size_t i = 0; i < count; ++i) + { + const su_root_key_package_signature* s = &k_su_root_key_package_signatures[i]; + assert_string_equal(keys[i].kid, s->kid); + assert_int_equal( + g_crypto->verify_rs256( + g_crypto, + keys[i].modulus, + keys[i].modulus_len, + keys[i].exponent, + keys[i].exponent_len, + k_su_root_key_package_signed, + sizeof(k_su_root_key_package_signed), + s->signature, + s->signature_len), + AZ_IOT_OK); + + uint8_t modulus[512]; + assert_true(keys[i].modulus_len <= sizeof(modulus)); + memcpy(modulus, keys[i].modulus, keys[i].modulus_len); + modulus[keys[i].modulus_len - 1] ^= 0x02; + assert_int_not_equal( + g_crypto->verify_rs256( + g_crypto, + modulus, + keys[i].modulus_len, + keys[i].exponent, + keys[i].exponent_len, + k_su_root_key_package_signed, + sizeof(k_su_root_key_package_signed), + s->signature, + s->signature_len), + AZ_IOT_OK); + } +} + static void rs256_rejects_known_bad_vectors(void** state) { (void)state; @@ -733,6 +778,7 @@ int crypto_contract_run(const char* group_name, const az_iot_crypto* crypto) cmocka_unit_test(hmac_sha256_matches_known_answers), cmocka_unit_test(hmac_sha256_rejects_bad_arguments), cmocka_unit_test(rs256_accepts_known_good_vectors), + cmocka_unit_test(microsoft_root_keys_verify_the_root_key_package), cmocka_unit_test(rs256_rejects_known_bad_vectors), cmocka_unit_test(rs256_rejects_missing_inputs), cmocka_unit_test(rs256_rejects_oversized_keys_safely), diff --git a/c/tests/support/gen_su_root_key_package_vectors.py b/c/tests/support/gen_su_root_key_package_vectors.py new file mode 100644 index 00000000..d2efa9c9 --- /dev/null +++ b/c/tests/support/gen_su_root_key_package_vectors.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# Copyright (c) Microsoft. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. + +"""Generate tests/support/su_root_key_package_vectors.h from a Microsoft root key package. + +The output is committed; tests never run this. It holds the package's signed bytes (the +`protected` object, serialized compactly as the Device Update agent does) and one RS256 +signature per root, so the compiled-in roots are checked against the published package. + + python3 c/tests/support/gen_su_root_key_package_vectors.py rootkeypackage-.json \ + > c/tests/support/su_root_key_package_vectors.h +""" + +import base64 +import json +import sys + + +def b64url(s): + return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4)) + + +def emit_bytes(name, data): + print(f"static const uint8_t {name}[] = {{") + for i in range(0, len(data), 16): + print(" " + ", ".join(f"0x{b:02x}" for b in data[i : i + 16]) + ",") + print("};\n") + + +def main(): + with open(sys.argv[1], "rb") as f: + package = json.loads(f.read()) + protected = package["protected"] + signed = json.dumps(protected, separators=(",", ":")).replace("/", "\\/").encode() + kids = list(protected["rootKeys"]) + if len(package["signatures"]) != len(kids): + sys.exit("signature count does not match root count") + + print( + """// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license +// information. + +/* SPDX-License-Identifier: MIT */ +/** + * @file su_root_key_package_vectors.h + * @brief Microsoft production root key package known answers. GENERATED by + * gen_su_root_key_package_vectors.py; do not edit. + */ +#ifndef SU_ROOT_KEY_PACKAGE_VECTORS_H +#define SU_ROOT_KEY_PACKAGE_VECTORS_H + +#include +#include + +/* clang-format off */ +""" + ) + print(f"/** @brief Root key package version. */\n#define SU_ROOT_KEY_PACKAGE_VERSION {protected['version']}\n") + print("/** @brief Signed bytes: the package's `protected` object. */") + emit_bytes("k_su_root_key_package_signed", signed) + for i, kid in enumerate(kids): + sig = package["signatures"][i] + if sig["alg"] != "RS256": + sys.exit(f"unexpected alg for {kid}") + emit_bytes(f"k_su_root_key_package_sig_{i}", b64url(sig["sig"])) + + print("/** @brief One root's RS256 signature over k_su_root_key_package_signed. */") + print("typedef struct su_root_key_package_signature\n{") + print(" const char* kid;\n const uint8_t* signature;\n size_t signature_len;") + print("} su_root_key_package_signature;\n") + print("static const su_root_key_package_signature k_su_root_key_package_signatures[] = {") + for i, kid in enumerate(kids): + print(f' {{ "{kid}", k_su_root_key_package_sig_{i}, sizeof(k_su_root_key_package_sig_{i}) }},') + print("};\n") + print("/* clang-format on */\n\n#endif /* SU_ROOT_KEY_PACKAGE_VECTORS_H */") + + +if __name__ == "__main__": + main() diff --git a/c/tests/support/su_root_key_package_vectors.h b/c/tests/support/su_root_key_package_vectors.h new file mode 100644 index 00000000..0cc67f81 --- /dev/null +++ b/c/tests/support/su_root_key_package_vectors.h @@ -0,0 +1,236 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license +// information. + +/* SPDX-License-Identifier: MIT */ +/** + * @file su_root_key_package_vectors.h + * @brief Microsoft production root key package known answers. GENERATED by + * gen_su_root_key_package_vectors.py; do not edit. + */ +#ifndef SU_ROOT_KEY_PACKAGE_VECTORS_H +#define SU_ROOT_KEY_PACKAGE_VECTORS_H + +#include +#include + +/* clang-format off */ + +/** @brief Root key package version. */ +#define SU_ROOT_KEY_PACKAGE_VERSION 2 + +/** @brief Signed bytes: the package's `protected` object. */ +static const uint8_t k_su_root_key_package_signed[] = { + 0x7b, 0x22, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x22, 0x3a, 0x31, 0x37, 0x33, 0x36, 0x31, 0x39, 0x37, + 0x38, 0x30, 0x37, 0x2c, 0x22, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x6f, 0x6f, + 0x74, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x3a, 0x5b, 0x5d, 0x2c, 0x22, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x3a, + 0x5b, 0x5d, 0x2c, 0x22, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x3a, 0x7b, 0x22, + 0x41, 0x44, 0x55, 0x2e, 0x32, 0x30, 0x30, 0x37, 0x30, 0x32, 0x2e, 0x52, 0x22, 0x3a, 0x7b, 0x22, + 0x6b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x22, 0x52, 0x53, 0x41, 0x22, 0x2c, 0x22, + 0x6e, 0x22, 0x3a, 0x22, 0x31, 0x55, 0x49, 0x75, 0x72, 0x78, 0x46, 0x55, 0x6f, 0x31, 0x42, 0x6c, + 0x68, 0x36, 0x4a, 0x4e, 0x57, 0x37, 0x6f, 0x61, 0x2d, 0x36, 0x6b, 0x79, 0x33, 0x2d, 0x6d, 0x5a, + 0x58, 0x77, 0x56, 0x46, 0x79, 0x4b, 0x2d, 0x39, 0x4e, 0x52, 0x32, 0x4a, 0x36, 0x43, 0x63, 0x6e, + 0x57, 0x4b, 0x4f, 0x6f, 0x37, 0x73, 0x58, 0x46, 0x48, 0x6b, 0x5f, 0x33, 0x6b, 0x71, 0x59, 0x53, + 0x42, 0x6e, 0x30, 0x39, 0x66, 0x62, 0x41, 0x48, 0x39, 0x69, 0x78, 0x5f, 0x33, 0x6d, 0x30, 0x71, + 0x39, 0x62, 0x78, 0x4a, 0x76, 0x42, 0x58, 0x76, 0x38, 0x49, 0x48, 0x4c, 0x50, 0x34, 0x68, 0x50, + 0x4a, 0x78, 0x32, 0x49, 0x63, 0x53, 0x68, 0x67, 0x43, 0x4c, 0x59, 0x5a, 0x30, 0x74, 0x49, 0x35, + 0x30, 0x41, 0x55, 0x66, 0x50, 0x48, 0x61, 0x47, 0x63, 0x62, 0x74, 0x5a, 0x57, 0x4c, 0x79, 0x78, + 0x69, 0x48, 0x75, 0x72, 0x56, 0x69, 0x69, 0x5f, 0x4d, 0x58, 0x4e, 0x45, 0x4d, 0x68, 0x44, 0x39, + 0x50, 0x64, 0x4f, 0x57, 0x58, 0x50, 0x39, 0x4f, 0x58, 0x4c, 0x4e, 0x72, 0x5f, 0x34, 0x75, 0x45, + 0x6d, 0x34, 0x75, 0x41, 0x75, 0x45, 0x6e, 0x51, 0x66, 0x66, 0x72, 0x57, 0x51, 0x46, 0x68, 0x32, + 0x54, 0x63, 0x42, 0x79, 0x4a, 0x33, 0x58, 0x4c, 0x6d, 0x69, 0x2d, 0x62, 0x74, 0x4a, 0x38, 0x50, + 0x4a, 0x66, 0x45, 0x63, 0x78, 0x52, 0x73, 0x4c, 0x57, 0x6a, 0x42, 0x39, 0x4c, 0x37, 0x6a, 0x76, + 0x70, 0x79, 0x5a, 0x59, 0x55, 0x36, 0x5f, 0x56, 0x48, 0x56, 0x55, 0x42, 0x55, 0x51, 0x33, 0x70, + 0x47, 0x36, 0x49, 0x50, 0x50, 0x39, 0x66, 0x70, 0x48, 0x53, 0x42, 0x42, 0x70, 0x75, 0x59, 0x55, + 0x43, 0x71, 0x37, 0x2d, 0x38, 0x68, 0x77, 0x71, 0x31, 0x75, 0x51, 0x45, 0x65, 0x5f, 0x59, 0x55, + 0x66, 0x75, 0x77, 0x50, 0x6c, 0x34, 0x50, 0x36, 0x57, 0x50, 0x71, 0x42, 0x4e, 0x69, 0x47, 0x35, + 0x6f, 0x79, 0x76, 0x36, 0x32, 0x57, 0x45, 0x4c, 0x47, 0x70, 0x54, 0x33, 0x77, 0x62, 0x35, 0x5f, + 0x51, 0x42, 0x52, 0x4b, 0x79, 0x66, 0x6f, 0x31, 0x66, 0x2d, 0x39, 0x6d, 0x63, 0x41, 0x43, 0x78, + 0x5f, 0x64, 0x76, 0x58, 0x59, 0x51, 0x30, 0x37, 0x57, 0x48, 0x52, 0x6e, 0x6c, 0x49, 0x6c, 0x31, + 0x64, 0x70, 0x5a, 0x38, 0x6b, 0x59, 0x66, 0x53, 0x6a, 0x68, 0x47, 0x58, 0x37, 0x6e, 0x75, 0x48, + 0x62, 0x4a, 0x6f, 0x76, 0x52, 0x64, 0x68, 0x6c, 0x50, 0x31, 0x4a, 0x77, 0x6d, 0x43, 0x72, 0x4c, + 0x79, 0x41, 0x52, 0x6a, 0x39, 0x63, 0x6c, 0x48, 0x7a, 0x33, 0x44, 0x30, 0x37, 0x57, 0x53, 0x6e, + 0x64, 0x4b, 0x55, 0x6a, 0x6a, 0x37, 0x62, 0x74, 0x39, 0x78, 0x7a, 0x54, 0x73, 0x42, 0x78, 0x6b, + 0x56, 0x78, 0x4a, 0x61, 0x71, 0x59, 0x47, 0x45, 0x48, 0x36, 0x44, 0x6e, 0x55, 0x42, 0x6d, 0x57, + 0x74, 0x49, 0x4b, 0x78, 0x72, 0x45, 0x6a, 0x6a, 0x34, 0x54, 0x4b, 0x43, 0x79, 0x30, 0x41, 0x66, + 0x72, 0x4d, 0x52, 0x5a, 0x76, 0x42, 0x41, 0x30, 0x55, 0x59, 0x4c, 0x35, 0x4b, 0x49, 0x32, 0x6f, + 0x48, 0x70, 0x76, 0x31, 0x65, 0x55, 0x56, 0x31, 0x73, 0x74, 0x79, 0x61, 0x45, 0x55, 0x4d, 0x49, + 0x76, 0x6d, 0x48, 0x4d, 0x6d, 0x73, 0x54, 0x4c, 0x64, 0x7a, 0x62, 0x5f, 0x67, 0x39, 0x32, 0x6f, + 0x63, 0x55, 0x39, 0x52, 0x6a, 0x67, 0x35, 0x37, 0x54, 0x66, 0x70, 0x35, 0x6d, 0x49, 0x32, 0x2d, + 0x5f, 0x49, 0x4a, 0x2d, 0x51, 0x45, 0x69, 0x70, 0x45, 0x67, 0x47, 0x6f, 0x32, 0x58, 0x37, 0x7a, + 0x70, 0x52, 0x76, 0x78, 0x2d, 0x35, 0x42, 0x33, 0x50, 0x6b, 0x43, 0x48, 0x47, 0x4d, 0x6d, 0x72, + 0x32, 0x66, 0x64, 0x35, 0x22, 0x2c, 0x22, 0x65, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, 0x37, 0x7d, + 0x2c, 0x22, 0x41, 0x44, 0x55, 0x2e, 0x32, 0x30, 0x30, 0x37, 0x30, 0x33, 0x2e, 0x52, 0x22, 0x3a, + 0x7b, 0x22, 0x6b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x22, 0x52, 0x53, 0x41, 0x22, + 0x2c, 0x22, 0x6e, 0x22, 0x3a, 0x22, 0x73, 0x71, 0x4f, 0x79, 0x64, 0x42, 0x62, 0x36, 0x75, 0x79, + 0x44, 0x35, 0x55, 0x6e, 0x62, 0x6d, 0x4a, 0x7a, 0x36, 0x41, 0x51, 0x63, 0x62, 0x2d, 0x7a, 0x7a, + 0x44, 0x35, 0x79, 0x4a, 0x62, 0x31, 0x57, 0x51, 0x71, 0x71, 0x67, 0x65, 0x64, 0x52, 0x67, 0x34, + 0x72, 0x45, 0x39, 0x52, 0x63, 0x36, 0x4c, 0x79, 0x72, 0x6d, 0x56, 0x39, 0x52, 0x78, 0x7a, 0x6f, + 0x6f, 0x39, 0x37, 0x35, 0x70, 0x56, 0x64, 0x6a, 0x36, 0x5a, 0x34, 0x73, 0x4b, 0x75, 0x54, 0x4f, + 0x34, 0x74, 0x75, 0x48, 0x6a, 0x31, 0x6f, 0x6b, 0x34, 0x6f, 0x38, 0x70, 0x78, 0x4f, 0x4f, 0x57, + 0x57, 0x38, 0x37, 0x4f, 0x51, 0x4e, 0x35, 0x65, 0x4d, 0x34, 0x71, 0x46, 0x6d, 0x72, 0x43, 0x4b, + 0x51, 0x62, 0x74, 0x50, 0x53, 0x67, 0x55, 0x71, 0x4d, 0x34, 0x73, 0x30, 0x59, 0x68, 0x45, 0x38, + 0x77, 0x38, 0x61, 0x41, 0x62, 0x65, 0x5f, 0x67, 0x43, 0x6d, 0x6b, 0x6d, 0x37, 0x65, 0x54, 0x45, + 0x63, 0x51, 0x31, 0x68, 0x79, 0x63, 0x4a, 0x50, 0x58, 0x4e, 0x63, 0x4f, 0x48, 0x31, 0x61, 0x6e, + 0x78, 0x6f, 0x45, 0x78, 0x33, 0x68, 0x78, 0x66, 0x61, 0x6f, 0x54, 0x79, 0x47, 0x66, 0x68, 0x6e, + 0x78, 0x45, 0x78, 0x59, 0x71, 0x5a, 0x48, 0x4d, 0x58, 0x54, 0x42, 0x70, 0x74, 0x61, 0x63, 0x5a, + 0x30, 0x4a, 0x48, 0x4d, 0x4e, 0x6b, 0x4d, 0x57, 0x72, 0x46, 0x46, 0x35, 0x55, 0x64, 0x58, 0x53, + 0x72, 0x78, 0x56, 0x63, 0x64, 0x6d, 0x31, 0x4f, 0x6a, 0x31, 0x32, 0x61, 0x6c, 0x62, 0x6a, 0x4b, + 0x4a, 0x73, 0x59, 0x6d, 0x41, 0x46, 0x4e, 0x39, 0x63, 0x79, 0x73, 0x48, 0x50, 0x4c, 0x39, 0x30, + 0x73, 0x32, 0x4a, 0x79, 0x51, 0x68, 0x6a, 0x44, 0x67, 0x4b, 0x75, 0x42, 0x6a, 0x2d, 0x39, 0x52, + 0x56, 0x67, 0x4e, 0x59, 0x73, 0x31, 0x37, 0x78, 0x34, 0x50, 0x69, 0x4b, 0x59, 0x54, 0x6a, 0x58, + 0x74, 0x39, 0x37, 0x37, 0x50, 0x6e, 0x73, 0x4d, 0x6d, 0x6d, 0x48, 0x48, 0x42, 0x37, 0x7a, 0x50, + 0x49, 0x70, 0x69, 0x34, 0x66, 0x33, 0x76, 0x5a, 0x32, 0x32, 0x69, 0x47, 0x2d, 0x73, 0x63, 0x5f, + 0x39, 0x79, 0x38, 0x75, 0x39, 0x49, 0x4a, 0x35, 0x5a, 0x79, 0x68, 0x67, 0x61, 0x69, 0x58, 0x4f, + 0x4e, 0x39, 0x7a, 0x72, 0x43, 0x65, 0x35, 0x63, 0x4c, 0x5a, 0x54, 0x73, 0x54, 0x7a, 0x66, 0x33, + 0x67, 0x48, 0x53, 0x32, 0x57, 0x49, 0x52, 0x51, 0x77, 0x52, 0x35, 0x5a, 0x5a, 0x57, 0x4e, 0x49, + 0x67, 0x74, 0x46, 0x67, 0x35, 0x5a, 0x51, 0x74, 0x4c, 0x33, 0x32, 0x65, 0x30, 0x64, 0x37, 0x63, + 0x6b, 0x33, 0x64, 0x30, 0x52, 0x2d, 0x34, 0x34, 0x51, 0x32, 0x6e, 0x31, 0x67, 0x54, 0x37, 0x32, + 0x5f, 0x6b, 0x77, 0x30, 0x54, 0x55, 0x64, 0x77, 0x61, 0x4b, 0x7a, 0x31, 0x76, 0x49, 0x67, 0x42, + 0x79, 0x69, 0x6d, 0x47, 0x55, 0x4c, 0x4e, 0x64, 0x78, 0x7a, 0x79, 0x47, 0x6e, 0x51, 0x58, 0x75, + 0x67, 0x6c, 0x51, 0x35, 0x37, 0x32, 0x32, 0x4b, 0x73, 0x46, 0x72, 0x31, 0x45, 0x70, 0x49, 0x31, + 0x56, 0x41, 0x57, 0x42, 0x44, 0x71, 0x75, 0x4f, 0x4c, 0x4e, 0x58, 0x58, 0x6e, 0x4d, 0x37, 0x4e, + 0x2d, 0x30, 0x57, 0x35, 0x6a, 0x48, 0x2d, 0x75, 0x50, 0x53, 0x62, 0x43, 0x62, 0x4f, 0x4c, 0x69, + 0x78, 0x57, 0x34, 0x4d, 0x2d, 0x4e, 0x37, 0x76, 0x32, 0x54, 0x45, 0x69, 0x38, 0x41, 0x53, 0x59, + 0x30, 0x63, 0x67, 0x6a, 0x68, 0x57, 0x70, 0x6c, 0x31, 0x35, 0x52, 0x45, 0x6f, 0x61, 0x38, 0x39, + 0x77, 0x57, 0x45, 0x4c, 0x50, 0x42, 0x4c, 0x53, 0x63, 0x52, 0x5f, 0x68, 0x75, 0x59, 0x42, 0x65, + 0x53, 0x6a, 0x59, 0x44, 0x47, 0x5a, 0x22, 0x2c, 0x22, 0x65, 0x22, 0x3a, 0x36, 0x35, 0x35, 0x33, + 0x37, 0x7d, 0x2c, 0x22, 0x41, 0x44, 0x55, 0x2e, 0x32, 0x34, 0x31, 0x31, 0x31, 0x32, 0x2e, 0x52, + 0x22, 0x3a, 0x7b, 0x22, 0x6b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x22, 0x52, 0x53, + 0x41, 0x22, 0x2c, 0x22, 0x6e, 0x22, 0x3a, 0x22, 0x69, 0x34, 0x47, 0x70, 0x36, 0x6a, 0x31, 0x4e, + 0x42, 0x32, 0x6c, 0x45, 0x48, 0x68, 0x30, 0x71, 0x57, 0x75, 0x30, 0x42, 0x35, 0x4c, 0x42, 0x78, + 0x55, 0x76, 0x47, 0x71, 0x46, 0x34, 0x39, 0x67, 0x62, 0x76, 0x71, 0x54, 0x69, 0x74, 0x61, 0x5a, + 0x30, 0x36, 0x58, 0x7a, 0x55, 0x75, 0x66, 0x5a, 0x49, 0x77, 0x50, 0x30, 0x64, 0x46, 0x7a, 0x55, + 0x4c, 0x6c, 0x5f, 0x74, 0x50, 0x54, 0x6c, 0x4b, 0x63, 0x38, 0x76, 0x65, 0x6a, 0x75, 0x33, 0x4a, + 0x43, 0x4b, 0x36, 0x45, 0x74, 0x57, 0x61, 0x43, 0x73, 0x41, 0x58, 0x56, 0x74, 0x42, 0x6a, 0x36, + 0x44, 0x56, 0x38, 0x6b, 0x6b, 0x54, 0x58, 0x76, 0x68, 0x32, 0x6e, 0x42, 0x33, 0x34, 0x30, 0x55, + 0x33, 0x73, 0x65, 0x64, 0x42, 0x47, 0x6f, 0x35, 0x66, 0x5a, 0x57, 0x6a, 0x5f, 0x72, 0x4e, 0x48, + 0x33, 0x48, 0x42, 0x38, 0x72, 0x51, 0x36, 0x54, 0x42, 0x69, 0x54, 0x76, 0x46, 0x52, 0x41, 0x6e, + 0x47, 0x61, 0x78, 0x43, 0x71, 0x51, 0x69, 0x72, 0x59, 0x62, 0x33, 0x38, 0x58, 0x37, 0x53, 0x42, + 0x42, 0x70, 0x34, 0x6a, 0x6c, 0x6a, 0x6e, 0x74, 0x39, 0x75, 0x7a, 0x63, 0x4a, 0x41, 0x42, 0x32, + 0x69, 0x37, 0x32, 0x37, 0x71, 0x69, 0x34, 0x38, 0x68, 0x61, 0x76, 0x57, 0x57, 0x4c, 0x68, 0x6c, + 0x57, 0x78, 0x42, 0x51, 0x66, 0x54, 0x47, 0x75, 0x55, 0x35, 0x61, 0x68, 0x37, 0x39, 0x4b, 0x73, + 0x6e, 0x4b, 0x31, 0x36, 0x67, 0x5f, 0x65, 0x47, 0x6e, 0x47, 0x52, 0x68, 0x39, 0x74, 0x6e, 0x4a, + 0x38, 0x61, 0x43, 0x41, 0x47, 0x2d, 0x63, 0x70, 0x2d, 0x33, 0x45, 0x58, 0x34, 0x66, 0x39, 0x7a, + 0x51, 0x58, 0x65, 0x33, 0x79, 0x48, 0x41, 0x6d, 0x72, 0x47, 0x59, 0x6d, 0x4d, 0x48, 0x33, 0x4d, + 0x36, 0x43, 0x4f, 0x65, 0x4d, 0x54, 0x6e, 0x32, 0x64, 0x69, 0x71, 0x6e, 0x35, 0x43, 0x5a, 0x58, + 0x71, 0x73, 0x4d, 0x35, 0x75, 0x67, 0x76, 0x31, 0x6b, 0x49, 0x53, 0x78, 0x4a, 0x65, 0x4c, 0x37, + 0x46, 0x34, 0x66, 0x74, 0x69, 0x47, 0x43, 0x42, 0x65, 0x31, 0x69, 0x33, 0x50, 0x4e, 0x5a, 0x6b, + 0x64, 0x36, 0x6f, 0x6c, 0x57, 0x46, 0x4c, 0x6e, 0x56, 0x66, 0x4a, 0x52, 0x39, 0x6d, 0x45, 0x65, + 0x54, 0x64, 0x43, 0x39, 0x53, 0x78, 0x37, 0x38, 0x41, 0x65, 0x64, 0x5a, 0x73, 0x66, 0x42, 0x4b, + 0x34, 0x33, 0x56, 0x6e, 0x37, 0x52, 0x36, 0x4d, 0x6c, 0x56, 0x74, 0x37, 0x38, 0x5f, 0x5a, 0x55, + 0x35, 0x6b, 0x76, 0x56, 0x43, 0x6d, 0x46, 0x4a, 0x77, 0x74, 0x76, 0x6b, 0x6b, 0x6d, 0x49, 0x4a, + 0x78, 0x57, 0x6d, 0x31, 0x33, 0x65, 0x36, 0x46, 0x35, 0x35, 0x31, 0x34, 0x61, 0x30, 0x55, 0x51, + 0x62, 0x32, 0x4b, 0x30, 0x51, 0x35, 0x6a, 0x4f, 0x6d, 0x41, 0x75, 0x79, 0x55, 0x76, 0x5a, 0x58, + 0x77, 0x6f, 0x56, 0x45, 0x59, 0x33, 0x4a, 0x41, 0x6e, 0x53, 0x56, 0x4f, 0x50, 0x59, 0x4a, 0x74, + 0x72, 0x44, 0x61, 0x4f, 0x4c, 0x69, 0x38, 0x56, 0x56, 0x41, 0x4a, 0x49, 0x46, 0x2d, 0x74, 0x46, + 0x65, 0x71, 0x77, 0x5a, 0x70, 0x57, 0x46, 0x77, 0x4e, 0x34, 0x68, 0x4f, 0x2d, 0x55, 0x69, 0x78, + 0x33, 0x64, 0x68, 0x42, 0x63, 0x38, 0x75, 0x56, 0x71, 0x56, 0x46, 0x42, 0x4b, 0x55, 0x4b, 0x6f, + 0x61, 0x36, 0x67, 0x63, 0x77, 0x62, 0x6c, 0x72, 0x77, 0x4c, 0x79, 0x66, 0x64, 0x64, 0x33, 0x31, + 0x68, 0x5a, 0x67, 0x41, 0x59, 0x62, 0x38, 0x67, 0x42, 0x36, 0x4d, 0x62, 0x4d, 0x32, 0x72, 0x55, + 0x36, 0x6d, 0x5f, 0x33, 0x37, 0x51, 0x50, 0x52, 0x67, 0x6d, 0x6b, 0x74, 0x4b, 0x31, 0x51, 0x4b, + 0x32, 0x56, 0x5a, 0x43, 0x4b, 0x64, 0x4a, 0x74, 0x22, 0x2c, 0x22, 0x65, 0x22, 0x3a, 0x36, 0x35, + 0x35, 0x33, 0x37, 0x7d, 0x7d, 0x2c, 0x22, 0x69, 0x73, 0x54, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x66, + 0x61, 0x6c, 0x73, 0x65, 0x7d, +}; + +static const uint8_t k_su_root_key_package_sig_0[] = { + 0x6a, 0xce, 0x16, 0x68, 0xc3, 0xb1, 0x56, 0x01, 0x76, 0xd5, 0x41, 0xee, 0x2b, 0x64, 0x56, 0x1d, + 0x7d, 0x77, 0xe7, 0x16, 0x53, 0x67, 0xb9, 0xc9, 0xc6, 0x3e, 0xa7, 0xf4, 0x7a, 0x8b, 0x04, 0xb6, + 0x29, 0xa0, 0x45, 0xf4, 0x1e, 0xaf, 0xe2, 0x53, 0xff, 0x4c, 0x73, 0x4a, 0x6a, 0x57, 0x5f, 0x08, + 0x7f, 0x0c, 0xe4, 0x7a, 0x2b, 0x58, 0x38, 0xc0, 0x92, 0x57, 0xa2, 0x1e, 0x19, 0x71, 0xf5, 0xdc, + 0xf4, 0xbc, 0xbe, 0x3b, 0x47, 0x90, 0xd9, 0x38, 0xc4, 0xff, 0xf7, 0xd8, 0x79, 0x72, 0x7e, 0x82, + 0x74, 0x8b, 0x3d, 0xb4, 0x14, 0xc6, 0x5e, 0xc6, 0xa1, 0xbf, 0xde, 0x91, 0x84, 0xa4, 0x75, 0x22, + 0x8d, 0x8b, 0x9c, 0xa9, 0xbd, 0xd4, 0xa9, 0x4f, 0x96, 0x1e, 0x48, 0xd1, 0x34, 0x1f, 0xd4, 0x52, + 0x44, 0x32, 0xf2, 0xb6, 0x22, 0x00, 0xfe, 0xa7, 0xd7, 0xf2, 0xb7, 0xb8, 0x80, 0xba, 0x3f, 0xce, + 0x30, 0x0f, 0xdf, 0x82, 0x06, 0xc2, 0x16, 0x29, 0x8b, 0xd9, 0x82, 0xa5, 0xd5, 0xc3, 0xed, 0xa4, + 0x1a, 0x4d, 0x92, 0xa2, 0x9b, 0xcb, 0xe2, 0xdd, 0x9e, 0xe6, 0x5d, 0x09, 0x96, 0x29, 0xbe, 0x3d, + 0x50, 0x50, 0x40, 0xf2, 0x68, 0x98, 0xec, 0xde, 0xdd, 0xc6, 0x30, 0x01, 0xcc, 0xbe, 0x9a, 0x48, + 0xed, 0xe9, 0xca, 0xf8, 0xc0, 0x52, 0x5e, 0xcf, 0x6c, 0x4c, 0x8a, 0xfd, 0x25, 0xc1, 0x13, 0xc1, + 0xaf, 0x51, 0x81, 0xef, 0x13, 0x41, 0x1a, 0xfb, 0x73, 0xa2, 0xb3, 0x1b, 0x90, 0xd9, 0x0e, 0xd3, + 0xc8, 0x26, 0x7c, 0xbe, 0x54, 0xf0, 0x59, 0x12, 0x04, 0x1b, 0x85, 0x59, 0x44, 0x93, 0xe5, 0x23, + 0x40, 0xee, 0x1e, 0xdf, 0xa5, 0xfb, 0xdb, 0x39, 0xba, 0x2c, 0xed, 0x0c, 0x47, 0xf3, 0x0b, 0xf7, + 0x9f, 0x00, 0x80, 0x7b, 0x1a, 0x8a, 0x3e, 0xcb, 0x43, 0xad, 0xbf, 0x08, 0x7d, 0xb0, 0x85, 0x70, + 0x6b, 0x12, 0xcb, 0x89, 0x13, 0xe4, 0xd9, 0x87, 0x0f, 0x75, 0x9b, 0x82, 0x26, 0x7c, 0x24, 0x98, + 0x49, 0xbf, 0x58, 0x74, 0xfa, 0xbf, 0xb8, 0xc6, 0xca, 0x52, 0x02, 0xda, 0xf5, 0x08, 0x57, 0x43, + 0xe4, 0xad, 0x28, 0x3c, 0x9f, 0xeb, 0x47, 0x43, 0x5c, 0x24, 0x9c, 0x39, 0x6f, 0x27, 0x95, 0x62, + 0x89, 0x1c, 0x89, 0xd3, 0xde, 0x4c, 0xe7, 0x2b, 0x75, 0x56, 0x72, 0x9c, 0x22, 0xee, 0xdf, 0x01, + 0xf8, 0x70, 0x29, 0xcb, 0xd8, 0x05, 0x69, 0xde, 0xf1, 0x23, 0x5f, 0xea, 0x69, 0x4b, 0x65, 0x02, + 0x5f, 0x38, 0x3c, 0xde, 0x65, 0xe5, 0x83, 0xde, 0x92, 0x81, 0xae, 0x54, 0xa4, 0x92, 0x09, 0xae, + 0xf3, 0x27, 0x4b, 0x26, 0x72, 0xba, 0x79, 0xe9, 0xa1, 0x6b, 0x25, 0xf0, 0x46, 0xaa, 0x81, 0x0a, + 0x00, 0x11, 0x21, 0x66, 0xca, 0x64, 0x21, 0x41, 0x4d, 0x58, 0xcd, 0x87, 0xaf, 0x48, 0x27, 0xd5, +}; + +static const uint8_t k_su_root_key_package_sig_1[] = { + 0x8d, 0x60, 0x99, 0xbd, 0xf8, 0x5a, 0xe3, 0x4e, 0x57, 0x66, 0x67, 0xd7, 0x2d, 0xb9, 0xb8, 0x4d, + 0x29, 0xdd, 0xdb, 0xee, 0xe2, 0x0f, 0xdf, 0x81, 0x2b, 0xcb, 0x39, 0x0c, 0x3e, 0x35, 0x36, 0x42, + 0x02, 0xb5, 0x6a, 0xf0, 0xbe, 0xf2, 0x00, 0x32, 0xd7, 0xf4, 0xfc, 0xff, 0x98, 0xe8, 0x9f, 0xe1, + 0xd0, 0xbb, 0x58, 0x15, 0x2b, 0x70, 0xd5, 0xda, 0xa8, 0x56, 0xc7, 0xbd, 0xcf, 0xdb, 0x05, 0xfa, + 0x46, 0x44, 0x71, 0x69, 0x17, 0xe9, 0xde, 0xe9, 0x21, 0x00, 0x3a, 0xa6, 0x0d, 0x28, 0x59, 0x81, + 0xc8, 0x56, 0x9c, 0x03, 0x2f, 0xac, 0xa9, 0x78, 0x02, 0xbe, 0x31, 0x26, 0xc3, 0xf2, 0xa2, 0x2c, + 0xdb, 0xed, 0x73, 0x6b, 0xbd, 0x6a, 0xdb, 0xb3, 0x54, 0x52, 0x84, 0x77, 0x5c, 0x30, 0x31, 0xc4, + 0x64, 0xec, 0x66, 0xe5, 0xab, 0x64, 0xb5, 0x2b, 0x13, 0xa9, 0x94, 0x08, 0xe5, 0xae, 0xa5, 0x7a, + 0xd9, 0x96, 0x9a, 0x57, 0xd7, 0x46, 0xf8, 0x0a, 0xf3, 0x08, 0xab, 0x3a, 0xd0, 0x8a, 0xe2, 0x63, + 0x31, 0xd3, 0x84, 0x09, 0xc0, 0x63, 0x62, 0xe8, 0xa8, 0x8e, 0xc9, 0xb8, 0xb4, 0x7f, 0x0e, 0xed, + 0x85, 0xb5, 0x4c, 0xca, 0x2f, 0xb8, 0x18, 0xa7, 0x2d, 0xe4, 0x36, 0x06, 0x0c, 0xc2, 0x6b, 0x89, + 0x0d, 0x9a, 0x65, 0x57, 0x59, 0x1d, 0x0d, 0xcc, 0x81, 0x99, 0x19, 0xed, 0xa3, 0xfe, 0x19, 0x36, + 0x25, 0xef, 0x11, 0xc1, 0x99, 0x2f, 0xc5, 0xa5, 0xd1, 0x7e, 0xc8, 0xd2, 0x2b, 0xb5, 0x57, 0x4f, + 0x7e, 0xce, 0xac, 0xd8, 0x51, 0x82, 0x4b, 0xca, 0xee, 0xdd, 0x03, 0x0a, 0xb9, 0xaa, 0x1c, 0x0a, + 0x64, 0x76, 0x39, 0xb3, 0x45, 0x0f, 0x9e, 0xd9, 0xf4, 0xe1, 0x3b, 0x4c, 0x23, 0xc8, 0x95, 0xb3, + 0xa3, 0x8e, 0x2e, 0xf2, 0xaa, 0x05, 0xf8, 0x99, 0x78, 0x76, 0xe7, 0x60, 0xa5, 0x0b, 0xb6, 0xe6, + 0x47, 0xdf, 0x67, 0xaf, 0x08, 0x4a, 0xcf, 0xe6, 0x00, 0xa5, 0xa4, 0x04, 0x1e, 0xef, 0xf4, 0x47, + 0xc8, 0xed, 0xb9, 0xa6, 0xee, 0x2a, 0x71, 0x9c, 0xc5, 0x65, 0xd5, 0x5a, 0x66, 0xc4, 0xc8, 0xd0, + 0x30, 0x0e, 0xb8, 0x0c, 0x7a, 0x6d, 0x4b, 0xa5, 0xa4, 0xeb, 0x24, 0x70, 0xfb, 0xc8, 0xcd, 0xae, + 0x5e, 0xef, 0x19, 0x62, 0xba, 0xe6, 0x25, 0x52, 0xc3, 0xd6, 0x7c, 0xb6, 0x37, 0x8e, 0x52, 0x18, + 0x32, 0xa9, 0xc5, 0x01, 0x25, 0x01, 0xd3, 0x2b, 0x3f, 0x1c, 0x87, 0x94, 0x05, 0xa7, 0xe2, 0xd8, + 0x37, 0xbf, 0xa8, 0x7b, 0x40, 0xda, 0xef, 0xb2, 0x1f, 0x08, 0x44, 0x61, 0xd0, 0xa8, 0xf5, 0x5a, + 0x27, 0xf6, 0xd6, 0x36, 0xc3, 0x88, 0x4b, 0x2e, 0x93, 0x57, 0xe3, 0x8b, 0xe7, 0xa1, 0x50, 0x25, + 0x97, 0xf8, 0xd4, 0x51, 0x43, 0xe5, 0x14, 0x79, 0xe4, 0xe8, 0xe3, 0xcd, 0x16, 0x76, 0xfa, 0x42, +}; + +static const uint8_t k_su_root_key_package_sig_2[] = { + 0x64, 0x02, 0xa1, 0x16, 0x60, 0x03, 0x6b, 0xad, 0xca, 0xd3, 0xf0, 0x98, 0xbe, 0xe5, 0xb9, 0xf5, + 0xc0, 0xac, 0x0c, 0xf8, 0xb1, 0x9c, 0xcd, 0x88, 0x60, 0x1d, 0x88, 0xbe, 0x3b, 0x4e, 0x1b, 0xe1, + 0xe0, 0xe7, 0x92, 0x2c, 0x6c, 0xb7, 0x55, 0x0c, 0xb4, 0x8e, 0xe0, 0x0a, 0x06, 0xe8, 0x32, 0x12, + 0xa3, 0xf7, 0xbe, 0x26, 0xe5, 0x37, 0xd5, 0xb2, 0x61, 0xa2, 0xa9, 0x3a, 0x5b, 0xb1, 0xbd, 0x39, + 0xde, 0xb7, 0xc2, 0x5f, 0x54, 0x71, 0xa5, 0xb7, 0x61, 0xa3, 0x05, 0x88, 0x24, 0x03, 0xbd, 0x70, + 0x53, 0x57, 0xad, 0x53, 0x94, 0xb5, 0x2a, 0x0c, 0x44, 0xa5, 0x12, 0x0a, 0xd9, 0x6d, 0x17, 0x00, + 0xb3, 0x7f, 0xcb, 0x83, 0x80, 0xab, 0x94, 0xe0, 0xe3, 0x63, 0xb1, 0x28, 0x69, 0xf2, 0x07, 0x83, + 0x43, 0xf2, 0x54, 0x33, 0x36, 0x4c, 0x76, 0x40, 0xbc, 0xc8, 0x2f, 0x8f, 0x39, 0xe5, 0xef, 0xc7, + 0x0b, 0x02, 0x80, 0xb4, 0x23, 0xdf, 0x84, 0x2f, 0x23, 0x0b, 0x6c, 0x44, 0x52, 0x6e, 0x69, 0xb8, + 0xf1, 0x94, 0x35, 0x08, 0x26, 0x3d, 0x6c, 0x60, 0x3c, 0x35, 0x22, 0x5a, 0x21, 0x0e, 0x38, 0x02, + 0xc1, 0x45, 0x47, 0xea, 0x82, 0x51, 0x95, 0x81, 0x8f, 0x73, 0x3c, 0x4a, 0xe2, 0x3d, 0x90, 0x2f, + 0xcc, 0x4d, 0xbf, 0xdc, 0x3a, 0x84, 0xad, 0x2e, 0x2c, 0x65, 0x31, 0x3e, 0x97, 0x93, 0x0f, 0xbb, + 0xc1, 0x79, 0x45, 0x55, 0xb5, 0xef, 0x26, 0xb9, 0x75, 0x61, 0x2b, 0xaa, 0x8b, 0xb0, 0x3a, 0x9d, + 0x71, 0xf6, 0x5d, 0x34, 0xf2, 0x05, 0x5f, 0x77, 0x48, 0x3b, 0x36, 0xc0, 0xe7, 0xd1, 0xd0, 0x53, + 0x3d, 0xd2, 0x6b, 0x72, 0x11, 0x50, 0xa1, 0x7d, 0x7a, 0x72, 0xd4, 0xde, 0xda, 0x3c, 0x42, 0xbb, + 0x9d, 0x3b, 0x16, 0x42, 0x06, 0x87, 0xc3, 0x5f, 0xbf, 0x8c, 0x10, 0xbd, 0x7a, 0x34, 0x37, 0x94, + 0xc5, 0x42, 0xfa, 0x0f, 0xb2, 0x37, 0x20, 0x92, 0xff, 0x7c, 0x54, 0xf3, 0xe6, 0x39, 0xea, 0x37, + 0x4d, 0x7a, 0xc4, 0xb9, 0x03, 0x85, 0x6f, 0x19, 0x8d, 0x78, 0x29, 0xc4, 0x4b, 0xa2, 0x04, 0x45, + 0xb8, 0xbf, 0x93, 0x21, 0xb7, 0xa9, 0xcc, 0xe8, 0x55, 0x7b, 0x99, 0x25, 0x43, 0x4c, 0x94, 0x29, + 0x61, 0x16, 0x47, 0x83, 0x5b, 0xff, 0xab, 0xc1, 0x63, 0x2e, 0xd1, 0x4b, 0x9d, 0xfc, 0x0c, 0x6d, + 0xaf, 0x3a, 0x8c, 0x50, 0x6e, 0x1b, 0x63, 0xbf, 0xc0, 0x56, 0x42, 0x71, 0x86, 0xb4, 0x29, 0x4e, + 0xf7, 0x4e, 0xb9, 0xb8, 0x51, 0x46, 0xc7, 0x7c, 0xdb, 0xb0, 0x60, 0x3a, 0xd6, 0x04, 0x8e, 0xe7, + 0x53, 0xba, 0xbf, 0xb6, 0x24, 0xee, 0x88, 0x53, 0x83, 0x99, 0x54, 0xcc, 0x32, 0x3e, 0x09, 0xe6, + 0xd3, 0x50, 0xb9, 0xe0, 0x8a, 0x3b, 0x57, 0xb5, 0xa7, 0x26, 0xe1, 0x88, 0x4e, 0xa4, 0x04, 0xd9, +}; + +/** @brief One root's RS256 signature over k_su_root_key_package_signed. */ +typedef struct su_root_key_package_signature +{ + const char* kid; + const uint8_t* signature; + size_t signature_len; +} su_root_key_package_signature; + +static const su_root_key_package_signature k_su_root_key_package_signatures[] = { + { "ADU.200702.R", k_su_root_key_package_sig_0, sizeof(k_su_root_key_package_sig_0) }, + { "ADU.200703.R", k_su_root_key_package_sig_1, sizeof(k_su_root_key_package_sig_1) }, + { "ADU.241112.R", k_su_root_key_package_sig_2, sizeof(k_su_root_key_package_sig_2) }, +}; + +/* clang-format on */ + +#endif /* SU_ROOT_KEY_PACKAGE_VECTORS_H */ diff --git a/c/tests/unit/su_client_test.c b/c/tests/unit/su_client_test.c index 221ee39e..b511d558 100644 --- a/c/tests/unit/su_client_test.c +++ b/c/tests/unit/su_client_test.c @@ -125,13 +125,19 @@ static void pad_property(char* dst, size_t cap, const char* name, size_t len) } /* Build a structurally-valid manifest JWS: header carries alg=RS256 + an SJWK - * (itself a JWS over a JWK signing key, signed by the root key `testkid`); the + * (itself a JWS over a JWK signing key, signed by the root key `root_kid`); the * payload carries SHA-256(manifest) so the binding check passes. Signature bytes * are arbitrary because the mock backend does not validate them. * `jwk_pad` and `hdr_pad` add that many bytes of an unknown property to the * signing JWK and to the manifest JWS header. `escaped_n` encodes the modulus as * standard base64 with JSON-escaped slashes, as some serializers emit it. */ -static void build_jws_ex(char* out, int32_t out_cap, size_t jwk_pad, size_t hdr_pad, bool escaped_n) +static void build_jws_ex( + char* out, + int32_t out_cap, + size_t jwk_pad, + size_t hdr_pad, + bool escaped_n, + const char* root_kid) { static char pad[16384]; @@ -150,7 +156,8 @@ static void build_jws_ex(char* out, int32_t out_cap, size_t jwk_pad, size_t hdr_ char pl_b64[256]; b64url_str(pl_json, (int32_t)strlen(pl_json), pl_b64, (int32_t)sizeof(pl_b64)); - static const char sjwk_hdr[] = "{\"alg\":\"RS256\",\"kid\":\"testkid\"}"; + char sjwk_hdr[64]; + snprintf(sjwk_hdr, sizeof(sjwk_hdr), "{\"alg\":\"RS256\",\"kid\":\"%s\"}", root_kid); char shdr_b64[128]; b64url_str(sjwk_hdr, (int32_t)strlen(sjwk_hdr), shdr_b64, (int32_t)sizeof(shdr_b64)); @@ -190,7 +197,10 @@ static void build_jws_ex(char* out, int32_t out_cap, size_t jwk_pad, size_t hdr_ assert_true(n > 0 && n < out_cap); } -static void build_jws(char* out, int32_t out_cap) { build_jws_ex(out, out_cap, 0, 0, false); } +static void build_jws(char* out, int32_t out_cap) +{ + build_jws_ex(out, out_cap, 0, 0, false, "testkid"); +} /* Build a single-step payload with a caller-chosen workflow `id` and manifest * `version`. Returns a static buffer, valid until the next call. */ @@ -272,6 +282,7 @@ typedef struct char download_urls[MAX_OPS][64]; /* Key, exponent and signature of the last verify_rs256 call. */ + const uint8_t* first_verify_modulus; /* modulus of the first RS256 verify (the root key) */ uint8_t verify_mod[8]; size_t verify_mod_len; uint8_t verify_exp[8]; @@ -403,6 +414,10 @@ static az_iot_result mock_verify_rs256( (void)signed_len; hook_log* l = mock_log(self); /* Truncated copies; the length is kept whole so a wrong decode still shows. */ + if (l->first_verify_modulus == NULL) + { + l->first_verify_modulus = mod; + } memcpy(l->verify_mod, mod, mod_len < sizeof(l->verify_mod) ? mod_len : sizeof(l->verify_mod)); l->verify_mod_len = mod_len; memcpy(l->verify_exp, exp, exp_len < sizeof(l->verify_exp) ? exp_len : sizeof(l->verify_exp)); @@ -2674,12 +2689,15 @@ static void microsoft_root_keys_are_embedded(void** state) size_t count = 0; const az_iot_su_root_key* keys = az_iot_su_microsoft_root_keys(&count); assert_non_null(keys); - assert_true(count >= 2); + static const char* const kids[] = { "ADU.200702.R", "ADU.200703.R", "ADU.241112.R" }; + assert_int_equal(count, sizeof(kids) / sizeof(kids[0])); + assert_true(count <= AZ_IOT_SU_MAX_ROOT_KEYS); for (size_t i = 0; i < count; i++) { - assert_non_null(keys[i].kid); + assert_string_equal(keys[i].kid, kids[i]); assert_non_null(keys[i].modulus); - assert_true(keys[i].modulus_len > 0); + /* 3072-bit modulus with a leading zero byte. */ + assert_int_equal(keys[i].modulus_len, 385); assert_non_null(keys[i].exponent); assert_true(keys[i].exponent_len > 0); assert_false(keys[i].disabled); @@ -4610,6 +4628,25 @@ static void manifest_signed_by_an_unknown_root_key_is_rejected(void** state) parse_with_roots(&fx->log, signed_patch(), strangers, 1, &req, &manifest), AZ_IOT_ERR_AUTH); } +static void manifest_under_root_adu_241112_r_resolves_to_that_key(void** state) +{ + fixture* fx = (fixture*)*state; + + char jws[2048]; + build_jws_ex(jws, (int32_t)sizeof(jws), 0, 0, false, "ADU.241112.R"); + static char patch[4096]; + int n = snprintf(patch, sizeof(patch), k_patch_fmt, "wf-241112", "1.1", jws); + assert_true(n > 0 && (size_t)n < sizeof(patch)); + + size_t count = 0; + const az_iot_su_root_key* keys = az_iot_su_microsoft_root_keys(&count); + az_iot_su_client_update_request req; + az_iot_su_client_update_manifest manifest; + fx->log.first_verify_modulus = NULL; + assert_int_equal(parse_with_roots(&fx->log, patch, keys, count, &req, &manifest), AZ_IOT_OK); + assert_ptr_equal(fx->log.first_verify_modulus, keys[2].modulus); +} + /* The public parser accepts the software updates updateMetadata shape too. */ static void public_parser_accepts_update_metadata(void** state) { @@ -4662,7 +4699,7 @@ static const char* large_signature_patch(void) { static char jws[AZ_IOT_SU_REQUEST_BUFFER_SIZE]; static char patch[AZ_IOT_SU_REQUEST_BUFFER_SIZE]; - build_jws_ex(jws, (int32_t)sizeof(jws), 2400, 600, true); + build_jws_ex(jws, (int32_t)sizeof(jws), 2400, 600, true, "testkid"); int n = snprintf(patch, sizeof(patch), k_patch_fmt, "large-signature", "1.1", jws); assert_true(n > 4096 && (size_t)n < sizeof(patch)); return patch; @@ -4713,7 +4750,7 @@ static void near_limit_nested_signing_key_is_verified(void** state) static char jws[AZ_IOT_SU_REQUEST_BUFFER_SIZE]; static char patch[AZ_IOT_SU_REQUEST_BUFFER_SIZE]; size_t jwk_pad = (AZ_IOT_SU_REQUEST_BUFFER_SIZE - 1500) * 9 / 16; - build_jws_ex(jws, (int32_t)sizeof(jws), jwk_pad, 0, false); + build_jws_ex(jws, (int32_t)sizeof(jws), jwk_pad, 0, false, "testkid"); int n = snprintf(patch, sizeof(patch), k_patch_fmt, "near-limit", "1.1", jws); assert_true(n > AZ_IOT_SU_REQUEST_BUFFER_SIZE * 7 / 8 && (size_t)n < sizeof(patch)); @@ -4735,7 +4772,7 @@ static void oversized_signature_is_reported_as_too_large(void** state) az_iot_su_client_update_request req; az_iot_su_client_update_manifest manifest; - build_jws_ex(jws, (int32_t)sizeof(jws), 0, AZ_IOT_SU_VERIFY_SCRATCH_SIZE, false); + build_jws_ex(jws, (int32_t)sizeof(jws), 0, AZ_IOT_SU_VERIFY_SCRATCH_SIZE, false, "testkid"); int n = snprintf(patch, sizeof(patch), k_patch_fmt, "too-large", "1.1", jws); assert_true(n > 0 && (size_t)n < sizeof(patch)); su_error_log_capture too_large = { .needle = "manifest JWS header too large", .count = 0 }; @@ -6394,6 +6431,8 @@ int main(void) cmocka_unit_test(build_report_with_too_small_a_buffer_is_rejected), cmocka_unit_test_setup_teardown( manifest_signed_by_an_unknown_root_key_is_rejected, setup, teardown), + cmocka_unit_test_setup_teardown( + manifest_under_root_adu_241112_r_resolves_to_that_key, setup, teardown), cmocka_unit_test_setup_teardown(malformed_jws_is_rejected, setup, teardown), cmocka_unit_test_setup_teardown(large_signature_is_verified, setup, teardown), cmocka_unit_test_setup_teardown(near_limit_nested_signing_key_is_verified, setup, teardown),