Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions lib/src/sv_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
Expand Down
136 changes: 99 additions & 37 deletions lib/src/sv_axis_communications.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "sv_axis_communications.h"

#include <assert.h>
#include <ctype.h> // toupper
#include <openssl/bio.h> // BIO_*
#include <openssl/evp.h> // EVP_*
#include <openssl/pem.h> // PEM_*
Expand All @@ -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"
Expand All @@ -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,
Expand Down Expand Up @@ -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;

Expand All @@ -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
Expand All @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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|.
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);
}

Expand All @@ -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--;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
2 changes: 1 addition & 1 deletion lib/src/sv_axis_communications.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/**
Expand Down
3 changes: 1 addition & 2 deletions lib/src/sv_onvif.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
3 changes: 1 addition & 2 deletions lib/src/sv_onvif.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__
2 changes: 1 addition & 1 deletion lib/src/sv_tlv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Loading
Loading