diff --git a/lib/src/sv_auth.c b/lib/src/sv_auth.c index a69f7806..541a9d58 100644 --- a/lib/src/sv_auth.c +++ b/lib/src/sv_auth.c @@ -1408,7 +1408,7 @@ detect_onvif_media_signing(signed_video_t *self, const bu_info_t *bu) return SV_OK; } - const char *trusted_certificate = NULL; + const char **trusted_certificates = NULL; size_t trusted_certificate_size = 0; // Map codec to ONVIF enum. MediaSigningCodec codec = OMS_CODEC_NUM; @@ -1428,10 +1428,13 @@ detect_onvif_media_signing(signed_video_t *self, const bu_info_t *bu) self->onvif = onvif_media_signing_create(codec); SV_THROW_IF(!self->onvif, SV_EXTERNAL_ERROR); // Get the root CA certificate from Axis code. - trusted_certificate = get_axis_communications_trusted_certificate(); - trusted_certificate_size = strlen(trusted_certificate); - SV_THROW(msrc_to_svrc(onvif_media_signing_set_trusted_certificate( - self->onvif, trusted_certificate, trusted_certificate_size, false))); + trusted_certificates = get_axis_communications_trusted_certificate(); + while (*trusted_certificates) { + trusted_certificate_size = strlen(*trusted_certificates); + SV_THROW(msrc_to_svrc(onvif_media_signing_set_trusted_certificate( + self->onvif, *trusted_certificates, trusted_certificate_size))); + trusted_certificates++; + } // If the ONVIF Media Signing session has successfully been set up, register all // queued Bitstream Units to the ONVIF session. SV_THROW(reregister_bu(self)); diff --git a/lib/src/sv_axis_communications.c b/lib/src/sv_axis_communications.c index 39829181..febdf41e 100644 --- a/lib/src/sv_axis_communications.c +++ b/lib/src/sv_axis_communications.c @@ -21,6 +21,7 @@ #include "sv_axis_communications.h" #include +#include // toupper #include // BIO_* #include // EVP_* #include // PEM_* @@ -42,7 +43,7 @@ #define PUBLIC_KEY_UNCOMPRESSED_PREFIX 0x04 #define BINARY_RAW_DATA_SIZE 40 -static const char *kTrustedAxisRootCA = +static const char *kTrustedRootCAs[] = { "-----BEGIN CERTIFICATE-----\n" "MIIClDCCAfagAwIBAgIBATAKBggqhkjOPQQDBDBcMR8wHQYDVQQKExZBeGlzIENv\n" "bW11bmljYXRpb25zIEFCMRgwFgYDVQQLEw9BeGlzIEVkZ2UgVmF1bHQxHzAdBgNV\n" @@ -58,7 +59,24 @@ static const char *kTrustedAxisRootCA = "hwJBUfwiBK0TIRJebWm9/nsNAEkjbxao40oeMUg+I3mDNr7guNJUo4ugOfToGpnm\n" "3QLOhEJzyHqPBHTChxEd5bGVUW8CQgDR/ZAr405Ohk5kpM/gmzELP+fYDZfuTFut\n" "w3S8HMYSvMWbTCzN+qnq+GV1goSS6vjVr95EpDxCVIxkKOvuxhyVDg==\n" - "-----END CERTIFICATE-----\n"; + "-----END CERTIFICATE-----\n", + "-----BEGIN CERTIFICATE-----\n" + "MIIChjCCAeegAwIBAgIBATAKBggqhkjOPQQDBDBWMQswCQYDVQQGEwJTRTEfMB0G\n" + "A1UECgwWQXhpcyBDb21tdW5pY2F0aW9ucyBBQjEmMCQGA1UEAwwdQXhpcyBURUUg\n" + "U2lnbmVkIFZpZGVvIFJvb3QgQ0EwHhcNMjYwMzIzMTAyMTE0WhcNNDEwMzE5MTAy\n" + "MTE0WjBWMQswCQYDVQQGEwJTRTEfMB0GA1UECgwWQXhpcyBDb21tdW5pY2F0aW9u\n" + "cyBBQjEmMCQGA1UEAwwdQXhpcyBURUUgU2lnbmVkIFZpZGVvIFJvb3QgQ0EwgZsw\n" + "EAYHKoZIzj0CAQYFK4EEACMDgYYABAByYn89N6rzNsUTEHPRZXsdQQqorBJPX8W/\n" + "nV7j00ANnFioFDWp9WdSW/UC1ALY+SKoArUBjnzGnqTPBPtfV3UbRQBOHacPkVgL\n" + "//1OxEJfwyhhQrGTcn9KKeG8iJTKFoXArvicU+lilsjE8PV9+uUsE6zbIf4lFQLE\n" + "oU5hx+vTSxGRA6NjMGEwHwYDVR0jBBgwFoAUvtSs3PsLSpSPdTDJjXMBivgRJo4w\n" + "DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFL7UrNz7\n" + "C0qUj3UwyY1zAYr4ESaOMAoGCCqGSM49BAMEA4GMADCBiAJCAUjQy+PomgLYoKn1\n" + "4bcF1Y4Bv80jm285vdy+hQ8AdmaG6ixvHGMSaXolOhSDWKOuwDg0kIYGVF1quHCI\n" + "9vEKNZw0AkIB2b20NGna+9QE3hB0nuTmK1uYsqXfbwnQYj0dOv4YfRicHeys73u+\n" + "4+x8cqidPOf4i0Xdf+zFg9hRWxW/WGbvmA4=\n" + "-----END CERTIFICATE-----\n", + NULL}; #define ATTRIBUTES_LENGTH 37 static const uint8_t kAttributes[ATTRIBUTES_LENGTH] = {0x7b, 0x00, 0x02, 0x01, 0x29, 0x01, 0x00, @@ -106,7 +124,7 @@ typedef struct _sv_vendor_axis_communications_t { // Information needed for public key validation. EVP_MD_CTX *md_ctx; // Message digest context for verifying the public key - X509 *trusted_ca; // The trusted Axis root CA in X509 form. + X509_STORE *trusted_cas; // The trusted Axis root CAs in X509 form. uint8_t chip_id[CHIP_ID_SIZE]; struct attestation_report attestation_report; @@ -121,7 +139,8 @@ typedef struct _sv_vendor_axis_communications_t { // Declarations of static functions. static svrc_t -verify_certificate_chain(X509 *trusted_ca, STACK_OF(X509) * untrusted_certificates); +verify_certificate_chain(sv_vendor_axis_communications_t *self, + STACK_OF(X509) * untrusted_certificates); static svrc_t verify_and_parse_certificate_chain(sv_vendor_axis_communications_t *self); static svrc_t @@ -138,20 +157,16 @@ get_untrusted_certificates_size(const sv_vendor_axis_communications_t *self); * Uses the |trusted_ca| to verify the first certificate in the chain. The last certificate verified * is the |attestation_certificate| which will be used to verify the transmitted public key. */ static svrc_t -verify_certificate_chain(X509 *trusted_ca, STACK_OF(X509) * untrusted_certificates) +verify_certificate_chain(sv_vendor_axis_communications_t *self, + STACK_OF(X509) * untrusted_certificates) { - assert(trusted_ca && untrusted_certificates); + assert(self && untrusted_certificates); - X509_STORE *trust_store = NULL; + X509_STORE *trust_store = self->trusted_cas; X509_STORE_CTX *ctx = NULL; svrc_t status = SV_UNKNOWN_FAILURE; SV_TRY() - trust_store = X509_STORE_new(); - SV_THROW_IF(!trust_store, SV_EXTERNAL_ERROR); - // Load trusted CA certificate - SV_THROW_IF(X509_STORE_add_cert(trust_store, trusted_ca) != 1, SV_EXTERNAL_ERROR); - // Start a new context for certificate verification. ctx = X509_STORE_CTX_new(); SV_THROW_IF(!ctx, SV_EXTERNAL_ERROR); @@ -164,13 +179,19 @@ verify_certificate_chain(X509 *trusted_ca, STACK_OF(X509) * untrusted_certificat SV_THROW_IF( X509_STORE_CTX_init(ctx, trust_store, attestation_certificate, untrusted_certificates) != 1, SV_EXTERNAL_ERROR); - SV_THROW_IF(X509_verify_cert(ctx) != 1, SV_VENDOR_ERROR); + int verified = X509_verify_cert(ctx); + if (verified == 0) { + DEBUG_LOG("Certificate verification error: %s\n", + X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx))); + } + if (self->factory_provisioned) { + self->supplemental_authenticity.public_key_validation = verified; + } SV_CATCH() SV_DONE(status) X509_STORE_CTX_free(ctx); - X509_STORE_free(trust_store); return status; } @@ -215,17 +236,21 @@ verify_and_parse_certificate_chain(sv_vendor_axis_communications_t *self) // prevents from potential deadlock. // Get the first certificate from |stackbio|. X509 *certificate = PEM_read_bio_X509(stackbio, NULL, NULL, NULL); + SV_THROW_IF(!certificate, SV_VENDOR_ERROR); // The first certificate is the |attestation_certificate|. Keep a reference to it to extract // information from it. X509 *attestation_certificate = certificate; while (certificate && num_certificates < NUM_UNTRUSTED_CERTIFICATES + 1) { +#ifdef SIGNED_VIDEO_DEBUG + X509_print_fp(stdout, certificate); +#endif num_certificates = sk_X509_push(untrusted_certificates, certificate); // Get the next certificate. certificate = PEM_read_bio_X509(stackbio, NULL, NULL, NULL); } SV_THROW_IF(num_certificates > NUM_UNTRUSTED_CERTIFICATES, SV_VENDOR_ERROR); - SV_THROW_WITH_MSG(verify_certificate_chain(self->trusted_ca, untrusted_certificates), + SV_THROW_WITH_MSG(verify_certificate_chain(self, untrusted_certificates), "Failed verifying certificate chain"); // Extract |chip_id| from the |attestation_certificate|. @@ -235,6 +260,32 @@ verify_and_parse_certificate_chain(sv_vendor_axis_communications_t *self) // Found CN in certificate. Read that entry and convert to UTF8. entry_data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject, common_name_index)); SV_THROW_IF(ASN1_STRING_to_UTF8(&common_name_str, entry_data) <= 0, SV_EXTERNAL_ERROR); + + if (self->factory_provisioned) { + // Extract serial number from CommonName. It shall be present between the first two + // '-' characters. + char *start_pos = strstr((char *)common_name_str, "-"); + SV_THROW_IF(!start_pos, SV_VENDOR_ERROR); + start_pos++; // Skip the "-" character. + char *end_pos = strstr(start_pos, "-"); + SV_THROW_IF(!end_pos, SV_VENDOR_ERROR); + char *serial_number_str = OPENSSL_strndup(start_pos, end_pos - start_pos); + SV_THROW_IF(!serial_number_str, SV_EXTERNAL_ERROR); + start_pos = serial_number_str; + // Convert to upper case. + while (*start_pos != '\0') { + *start_pos = toupper(*start_pos); + start_pos++; + } + // Copy only if necessary. + if (strcmp(self->supplemental_authenticity.serial_number, serial_number_str)) { + memset(self->supplemental_authenticity.serial_number, 0, SV_VENDOR_AXIS_SER_NO_MAX_LENGTH); + strcpy(self->supplemental_authenticity.serial_number, serial_number_str); + } + OPENSSL_free(serial_number_str); + goto catch_error; + } + // Find the Chip ID string, which shows up right after "Axis Edge Vault Attestation ". char *chip_id_str = strstr((char *)common_name_str, AXIS_EDGE_VAULT_ATTESTATION_STR); SV_THROW_IF(!chip_id_str, SV_VENDOR_ERROR); @@ -248,7 +299,6 @@ verify_and_parse_certificate_chain(sv_vendor_axis_communications_t *self) } // Check that the chip ID has correct prefix. SV_THROW_IF(memcmp(self->chip_id, kChipIDPrefix, CHIP_ID_PREFIX_SIZE) != 0, SV_VENDOR_ERROR); - // Extract |serial_number| from the |attestation_certificate|. int ser_no_index = X509_NAME_get_index_by_NID(subject, NID_serialNumber, -1); SV_THROW_IF(ser_no_index < 0, SV_VENDOR_ERROR); @@ -377,6 +427,10 @@ verify_axis_communications_public_key(sv_vendor_axis_communications_t *self) // Initiate verification to not feasible/error. int verified_signature = -1; + if (self->factory_provisioned) { + return SV_OK; + } + svrc_t status = SV_UNKNOWN_FAILURE; SV_TRY() // If no message digest context exists, the |public_key| cannot be validated. @@ -477,26 +531,38 @@ sv_vendor_axis_communications_setup(void) if (!self) return NULL; - // Store the |kTrustedAxisRootCA| in X509 format. - BIO *ca_bio = BIO_new(BIO_s_mem()); - if (ca_bio) { - if (BIO_puts(ca_bio, kTrustedAxisRootCA) > 0) { - // Successfully written |kTrustedAxisRootCA| to |ca_bio|. Convert to X509. - self->trusted_ca = PEM_read_bio_X509(ca_bio, NULL, NULL, NULL); + self->trusted_cas = X509_STORE_new(); + if (!self->trusted_cas) { + DEBUG_LOG("Could not convert Axis root CAs to X509"); + sv_vendor_axis_communications_teardown((void *)self); + self = NULL; + return NULL; + } + + const char **trusted_certificates = kTrustedRootCAs; + while (*trusted_certificates) { + // Store each |kTrustedRootCAs| in X509_STORE. + BIO *ca_bio = BIO_new(BIO_s_mem()); + if (ca_bio) { + if (BIO_write(ca_bio, *trusted_certificates, (int)strlen(*trusted_certificates)) > 0) { + // Successfully written |*trusted_certificates| to |ca_bio|. Convert to X509. + X509 *trusted_certificate_x509 = PEM_read_bio_X509(ca_bio, NULL, NULL, NULL); + // Load trusted CA certificate + if (X509_STORE_add_cert(self->trusted_cas, trusted_certificate_x509) != 1) { + // Add better debug print + DEBUG_LOG("Failed to add trusted certificate to trust store"); + } + X509_free(trusted_certificate_x509); + } + BIO_free(ca_bio); } - BIO_free(ca_bio); + trusted_certificates++; } // Initialize |public_key_validation| to unknown/error. self->supplemental_authenticity.public_key_validation = -1; strcpy(self->supplemental_authenticity.serial_number, SERIAL_NUMBER_UNKNOWN); - if (!self->trusted_ca) { - DEBUG_LOG("Could not convert Axis root CA to X509"); - sv_vendor_axis_communications_teardown((void *)self); - self = NULL; - } - return (void *)self; } @@ -509,7 +575,7 @@ sv_vendor_axis_communications_teardown(void *handle) free(self->attestation); free(self->certificate_chain); - X509_free(self->trusted_ca); + X509_STORE_free(self->trusted_cas); free(self); } @@ -533,11 +599,6 @@ get_untrusted_certificates_size(const sv_vendor_axis_communications_t *self) const char *cert_chain_ptr = self->certificate_chain; const char *cert_ptr = self->certificate_chain; int certs_left = NUM_UNTRUSTED_CERTIFICATES + 1; - if (self->factory_provisioned) { - // Temporary solution until it is known how many (if any) intermediate certificates there are. - // Assuming no intermediate certificates. - certs_left = 2; // Leaf and root certificate. - } while (certs_left > 0 && cert_ptr) { cert_ptr = strstr(cert_chain_ptr, "-----BEGIN CERTIFICATE-----"); certs_left--; @@ -654,6 +715,7 @@ decode_axis_communications_handle(void *handle, const uint8_t *data, size_t data data_ptr += cert_size; SV_THROW_IF(data_ptr != data + data_size, SV_AUTHENTICATION_ERROR); + self->factory_provisioned = attestation_size == 0; #ifdef PRINT_DECODED_SEI char *cert_chain_str = calloc(1, cert_size + 1); SV_THROW_IF(!cert_chain_str, SV_MEMORY); @@ -900,8 +962,8 @@ sv_vendor_axis_communications_set_attestation_report(signed_video_t *sv, return SV_MEMORY; } -const char * +const char ** get_axis_communications_trusted_certificate(void) { - return kTrustedAxisRootCA; + return kTrustedRootCAs; } diff --git a/lib/src/sv_axis_communications.h b/lib/src/sv_axis_communications.h index afb5ba5f..9e2706ba 100644 --- a/lib/src/sv_axis_communications.h +++ b/lib/src/sv_axis_communications.h @@ -147,7 +147,7 @@ get_axis_communications_supplemental_authenticity(void *handle, * * @return A pointer to the trusted certificate as a null-terminated string. */ -const char * +const char ** get_axis_communications_trusted_certificate(void); /** diff --git a/lib/src/sv_onvif.c b/lib/src/sv_onvif.c index 901c5f13..b5bd5c2b 100644 --- a/lib/src/sv_onvif.c +++ b/lib/src/sv_onvif.c @@ -166,8 +166,7 @@ onvif_media_signing_authenticity_report_free( MediaSigningReturnCode onvif_media_signing_set_trusted_certificate(onvif_media_signing_t ATTR_UNUSED *self, const char ATTR_UNUSED *trusted_certificate, - size_t ATTR_UNUSED trusted_certificate_size, - bool ATTR_UNUSED user_provisioned) + size_t ATTR_UNUSED trusted_certificate_size) { return OMS_NOT_SUPPORTED; } diff --git a/lib/src/sv_onvif.h b/lib/src/sv_onvif.h index 4fbab95b..aaf6bc57 100644 --- a/lib/src/sv_onvif.h +++ b/lib/src/sv_onvif.h @@ -192,8 +192,7 @@ onvif_media_signing_authenticity_report_free( MediaSigningReturnCode onvif_media_signing_set_trusted_certificate(onvif_media_signing_t *self, const char *trusted_certificate, - size_t trusted_certificate_size, - bool user_provisioned); + size_t trusted_certificate_size); #endif // NO_ONVIF_MEDIA_SIGNING #endif // __SV_ONVIF_H__ diff --git a/lib/src/sv_tlv.c b/lib/src/sv_tlv.c index 67256a27..671e1223 100644 --- a/lib/src/sv_tlv.c +++ b/lib/src/sv_tlv.c @@ -1179,7 +1179,7 @@ sv_tlv_find_tag(const uint8_t *tlv_data, size_t tlv_data_size, sv_tlv_tag_t tag, length |= sv_read_byte(&last_two_bytes, &tlv_data_ptr, with_ep); } if (tlv_data_ptr + length > tlv_data + tlv_data_size) { - DEBUG_LOG("TLV length (%u) too large", length); + DEBUG_LOG("TLV (%d) length (%u) too large", this_tag, length); return NULL; } // Scan past the data diff --git a/media-signing-framework b/media-signing-framework index c3aaf737..7634c1b6 160000 --- a/media-signing-framework +++ b/media-signing-framework @@ -1 +1 @@ -Subproject commit c3aaf73734119ce35e4b2d968cf2e0c62b3ca398 +Subproject commit 7634c1b6303263211650b533c81226d5653d1843 diff --git a/meson.build b/meson.build index 299068d8..f2661758 100644 --- a/meson.build +++ b/meson.build @@ -33,6 +33,7 @@ endif if get_option('debugprints') add_global_arguments('-DSIGNED_VIDEO_DEBUG', language : 'c') + add_global_arguments('-DONVIF_MEDIA_SIGNING_DEBUG', language : 'c') endif if get_option('parsesei') add_global_arguments('-DPRINT_DECODED_SEI', language : 'c') diff --git a/tests/cert_chain.pem b/tests/cert_chain.pem index 3f2dd476..623a79fa 100644 --- a/tests/cert_chain.pem +++ b/tests/cert_chain.pem @@ -1,22 +1,35 @@ -----BEGIN CERTIFICATE----- -MIIBaTCCAQ8CFHZeHZ2d5CCWIkLHsQTLwEsgk1/PMAoGCCqGSM49BAMCMDcxEDAO -BgNVBAoMB1NvbWVPcmcxDTALBgNVBAsMBFRlc3QxFDASBgNVBAMMC1Rlc3QgUm9v -dENBMB4XDTI1MDEwODExMjUxOFoXDTI2MDEwODExMjUxOFowNzEQMA4GA1UECgwH -U29tZU9yZzENMAsGA1UECwwEVGVzdDEUMBIGA1UEAwwLVGVzdCBjYW1lcmEwWTAT -BgcqhkjOPQIBBggqhkjOPQMBBwNCAAQNWFD88M9YY2Ru/4TPFPSaoK/ffAnwb9GK -0N3Oh6AQu6ZjAudSvo8ppTEF4RnXIP8Pi0Tzy3SmvQLasNOKv7YxMAoGCCqGSM49 -BAMCA0gAMEUCIQCgwvrFNOSVGEn/6g7cQwJ2Lv5QhIFSioHxLKD0old4eQIgdGyq -ZIvk9mQjqyVUbMpRspztBTTh75mYRLYZ1EfYqUM= +MIIBgDCCAScCFHvP0k2/NrM7lWzf7hOnYaPbi0/PMAoGCCqGSM49BAMCMD8xEDAO +BgNVBAoMB1NvbWVPcmcxDTALBgNVBAsMBFRlc3QxHDAaBgNVBAMME1Rlc3QgSW50 +ZXJtZWRpYXRlQ0EwHhcNMjYwNjMwMTIxNDMyWhcNMjcwNjMwMTIxNDMyWjBHMRAw +DgYDVQQKDAdTb21lT3JnMQ0wCwYDVQQLDARUZXN0MSQwIgYDVQQDDBtUZXN0IGNh +bWVyYS1zZXJpYWxfbm8tMTIzNDUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQN +WFD88M9YY2Ru/4TPFPSaoK/ffAnwb9GK0N3Oh6AQu6ZjAudSvo8ppTEF4RnXIP8P +i0Tzy3SmvQLasNOKv7YxMAoGCCqGSM49BAMCA0cAMEQCIAY3Y5LX1sGWCOm2NJci +hKPZ8WfTRyXc1fMENb8vlac/AiAj8C0KS6rw8y4ToVHW4CCNHH+0WBPqXfceeNgp +QKR3rg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB2zCCAYGgAwIBAgIUOwLiJb9PxRB1NIyvfRdYZOoHOMIwCgYIKoZIzj0EAwIw +NzEQMA4GA1UECgwHU29tZU9yZzENMAsGA1UECwwEVGVzdDEUMBIGA1UEAwwLVGVz +dCBSb290Q0EwHhcNMjYwNjMwMTIxNDMyWhcNMjgwNjI5MTIxNDMyWjA/MRAwDgYD +VQQKDAdTb21lT3JnMQ0wCwYDVQQLDARUZXN0MRwwGgYDVQQDDBNUZXN0IEludGVy +bWVkaWF0ZUNBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERGNh3m2KPe9oAyoQ +RjJ1PV8AmSrtpYpbD58cY8OFFuoElPA1OyYnMjvxYp9gpG8YunauzLQ1g4NW69gv +bk0y8qNjMGEwHQYDVR0OBBYEFMuOiZO0+odgzXt19wY2R16L3L/uMB8GA1UdIwQY +MBaAFD5PGjACsS2Lgn5mgEDr3J8Bv1rmMBIGA1UdEwEB/wQIMAYBAf8CAQEwCwYD +VR0PBAQDAgEGMAoGCCqGSM49BAMCA0gAMEUCICFY0ydQFhhNx7uCf9mnNQDK4fvz +6RdaQwsukl/M/kQdAiEA1ilK/rfiaxWcmk0Eymuf6tGm8TXKxRNqwNIW/n/89a4= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIBwDCCAWagAwIBAgIBATAKBggqhkjOPQQDAjA3MRAwDgYDVQQKDAdTb21lT3Jn -MQ0wCwYDVQQLDARUZXN0MRQwEgYDVQQDDAtUZXN0IFJvb3RDQTAeFw0yNTAxMDgx -MTI1MThaFw0yNzEwMjkxMTI1MThaMDcxEDAOBgNVBAoMB1NvbWVPcmcxDTALBgNV +MQ0wCwYDVQQLDARUZXN0MRQwEgYDVQQDDAtUZXN0IFJvb3RDQTAeFw0yNjA2MzAx +MjE0MzJaFw0yOTA0MTkxMjE0MzJaMDcxEDAOBgNVBAoMB1NvbWVPcmcxDTALBgNV BAsMBFRlc3QxFDASBgNVBAMMC1Rlc3QgUm9vdENBMFkwEwYHKoZIzj0CAQYIKoZI -zj0DAQcDQgAEBXOyToMP/r5D7WauMm6t3/QE950vOermkjWWTtPrvJFuZUGli4B7 -wd3yWCertJvvAFCUs6K2BgAAKqOKSrR4S6NjMGEwHQYDVR0OBBYEFEXCHE2/WOnU -pjpM91GiBW2OXeceMB8GA1UdIwQYMBaAFEXCHE2/WOnUpjpM91GiBW2OXeceMBIG +zj0DAQcDQgAE1NMbp9bugqUvJlVSFoBw4l/b4Qqzkz8YABNrls27ArZ26JKAZbH/ +MneShe4vIEoOZoI8ONJxci+3rzXknRLkzKNjMGEwHQYDVR0OBBYEFD5PGjACsS2L +gn5mgEDr3J8Bv1rmMB8GA1UdIwQYMBaAFD5PGjACsS2Lgn5mgEDr3J8Bv1rmMBIG A1UdEwEB/wQIMAYBAf8CAQEwCwYDVR0PBAQDAgEGMAoGCCqGSM49BAMCA0gAMEUC -IBeea9/SVCdgLh54Olv4+Xw8c3EEXqWh73VjmxU+0r8wAiEAt0WmO4HqJrCCgyYD -I8LEz40c9xR+reGxEGpnx46NSK0= +IGsd1JtrQpxgYk/3pcO3/LYFWKZAAwGPf+/vZAPYAGiSAiEAgms1xr7IyktZ0g9t +2S1Hfbps4RVLQr+79h16eA7Yuw8= -----END CERTIFICATE----- diff --git a/tests/check/check_signed_video_auth.c b/tests/check/check_signed_video_auth.c index b2167bf0..f979a449 100644 --- a/tests/check/check_signed_video_auth.c +++ b/tests/check/check_signed_video_auth.c @@ -1420,7 +1420,7 @@ START_TEST(vendor_axis_communications_operation) ck_assert_int_eq(sv_rc, SV_OK); free(attestation); - sv_rc = signed_video_set_product_info(sv, HW_ID, FW_VER, NULL, "Axis Communications AB", ADDR); + sv_rc = signed_video_set_product_info(sv, HW_ID, FW_VER, SER_NO, "Axis Communications AB", ADDR); ck_assert_int_eq(sv_rc, SV_OK); // Mimic a GOP with 1 P-frame between 2 I-frames to trigger an SEI message. @@ -1536,7 +1536,7 @@ START_TEST(factory_provisioned_key) ck_assert_int_eq(sv_rc, SV_OK); free(certificate_chain); - sv_rc = signed_video_set_product_info(sv, HW_ID, FW_VER, NULL, "Axis Communications AB", ADDR); + sv_rc = signed_video_set_product_info(sv, HW_ID, FW_VER, SER_NO, "Axis Communications AB", ADDR); ck_assert_int_eq(sv_rc, SV_OK); // Mimic a GOP with 1 P-frame between 2 I-frames to trigger an SEI message. diff --git a/tests/generate-cert.sh b/tests/generate-cert.sh index 9ff0c98d..570fbd0e 100755 --- a/tests/generate-cert.sh +++ b/tests/generate-cert.sh @@ -1,46 +1,45 @@ #!/bin/bash -# Create CA private key (provide a password for the key): +# 1. Create ROOT CA +# Create private key for Root CA openssl ecparam -name prime256v1 -genkey -noout -out ca_ec.key -# Create CA certificate (provide suitable input when asked): +# Create Root CA certificate openssl req -x509 -new -nodes -key ca_ec.key -sha256 -days 1024 -set_serial 1 -out ca_ec.pem -subj "/O=SomeOrg/OU=Test/CN=Test RootCA" -config ./test_openssl.cnf -# Print the CA +# Print Root CA certificate openssl x509 -in ca_ec.pem -text -noout -# ## Intermediate EC cert ## -# # Generate test private keys -# openssl ecparam -name prime256v1 -genkey -noout -out intermediate_signing.key -# # Create CSR requirements file: -# openssl req -new -key intermediate_signing.key -out intermediate_signing.csr -subj "/O=SomeOrg/OU=Test/CN=Test camera" +# 2. Create INTERMEDIATE CA (NEW STEP) +# Create private key for Intermediate CA +openssl ecparam -name prime256v1 -genkey -noout -out intermediate_ec.key -# # Sign CSR -# openssl x509 -req -in intermediate_signing.csr -CA ca_ec.pem -CAkey ca_ec.key -CAcreateserial -out intermediate_signing.crt -days 365 -sha256 -extensions req_ext +# Create CSR (Certificate Signing Request) for Intermediate CA +openssl req -new -key intermediate_ec.key -out intermediate_ec.csr -subj "/O=SomeOrg/OU=Test/CN=Test IntermediateCA" -# # Print signed certificate -# openssl x509 -in intermediate_signing.crt -text -noout +# Sign Intermediate CSR with Root CA (Important: requires CA extensions in your cnf file) +openssl x509 -req -in intermediate_ec.csr -CA ca_ec.pem -CAkey ca_ec.key -CAcreateserial -out intermediate_ec.pem -days 730 -sha256 -extfile ./test_openssl.cnf -extensions v3_ca -# # Verify certificate -# openssl verify -verbose -CAfile ca_ec.pem intermediate_signing.crt +# Print Intermediate CA certificate +openssl x509 -in intermediate_ec.pem -text -noout -## EC ## -# Create CSR requirements file: -# openssl req -new -key ec_signing.key -out ec_signing.csr -subj "/O=SomeOrg/OU=Test/CN=Test camera" -openssl req -new -key private_ecdsa_key.pem -out ec_signing.csr -subj "/O=SomeOrg/OU=Test/CN=Test camera" +# 3. CREATE AND SIGN END-ENTITY CERTIFICATE (MODIFIED) +# Create CSR for end-entity certificate (e.g., camera) +openssl req -new -key private_ecdsa_key.pem -out ec_signing.csr -subj "/O=SomeOrg/OU=Test/CN=Test camera-serial_no-12345" -# Sign CSR -openssl x509 -req -in ec_signing.csr -CA ca_ec.pem -CAkey ca_ec.key -CAcreateserial -out ec_signing.crt -days 365 -sha256 -extensions req_ext -# openssl x509 -req -in ec_signing.csr -CA intermediate_signing.crt -CAkey intermediate_signing.key -CAcreateserial -out ec_signing.crt -days 365 -sha256 -extensions req_ext +# Sign camera's CSR with INTERMEDIATE CA (not Root CA) +openssl x509 -req -in ec_signing.csr -CA intermediate_ec.pem -CAkey intermediate_ec.key -CAcreateserial -out ec_signing.crt -days 365 -sha256 -extensions req_ext -# Print signed certificate +# Print end-entity certificate openssl x509 -in ec_signing.crt -text -noout -# Verify certificate -openssl verify -verbose -CAfile ca_ec.pem ec_signing.crt -# openssl verify -verbose -CAfile intermediate_signing.crt ec_signing.crt +# 4. VERIFY AND CREATE CHAIN (MODIFIED) +# Create a file with the entire CA chain for verification +cat intermediate_ec.pem ca_ec.pem > ca_chain.pem -# Concatenate certificates -cat ec_signing.crt ca_ec.pem > cert_chain.pem -# cat ec_signing.crt intermediate_signing.crt ca_ec.pem > cert_chain.pem +# Verify camera's certificate against the entire chain +openssl verify -verbose -CAfile ca_chain.pem ec_signing.crt + +# Create the final certificate chain (End-entity -> Intermediate -> Root) +cat ec_signing.crt intermediate_ec.pem ca_ec.pem > cert_chain.pem