1 /* SPDX-License-Identifier: BSD-3-Clause
3 * Copyright (c) 2017-2018 Solarflare Communications Inc.
10 #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
12 #if EFSYS_OPT_IMAGE_LAYOUT
15 * Utility routines to support limited parsing of ASN.1 tags. This is not a
16 * general purpose ASN.1 parser, but is sufficient to locate the required
17 * objects in a signed image with CMS headers.
20 /* DER encodings for ASN.1 tags (see ITU-T X.690) */
21 #define ASN1_TAG_INTEGER (0x02)
22 #define ASN1_TAG_OCTET_STRING (0x04)
23 #define ASN1_TAG_OBJ_ID (0x06)
24 #define ASN1_TAG_SEQUENCE (0x30)
25 #define ASN1_TAG_SET (0x31)
27 #define ASN1_TAG_IS_PRIM(tag) ((tag & 0x20) == 0)
29 #define ASN1_TAG_PRIM_CONTEXT(n) (0x80 + (n))
30 #define ASN1_TAG_CONS_CONTEXT(n) (0xA0 + (n))
32 typedef struct efx_asn1_cursor_s {
42 /* Parse header of DER encoded ASN.1 TLV and match tag */
43 static __checkReturn efx_rc_t
44 efx_asn1_parse_header_match_tag(
45 __inout efx_asn1_cursor_t *cursor,
50 if (cursor == NULL || cursor->buffer == NULL || cursor->length < 2) {
55 cursor->tag = cursor->buffer[0];
56 if (cursor->tag != tag) {
62 if ((cursor->tag & 0x1F) == 0x1F) {
63 /* Long tag format not used in CMS syntax */
68 if ((cursor->buffer[1] & 0x80) == 0) {
69 /* Short form: length is 0..127 */
71 cursor->val_size = cursor->buffer[1];
73 /* Long form: length encoded as [0x80+nbytes][length bytes] */
74 uint32_t nbytes = cursor->buffer[1] & 0x7F;
78 /* Indefinite length not allowed in DER encoding */
82 if (2 + nbytes > cursor->length) {
83 /* Header length overflows image buffer */
87 if (nbytes > sizeof (uint32_t)) {
88 /* Length encoding too big */
92 cursor->hdr_size = 2 + nbytes;
94 for (offset = 2; offset < cursor->hdr_size; offset++) {
96 (cursor->val_size << 8) | cursor->buffer[offset];
100 if ((cursor->hdr_size + cursor->val_size) > cursor->length) {
101 /* Length overflows image buffer */
121 EFSYS_PROBE1(fail1, efx_rc_t, rc);
126 /* Enter nested ASN.1 TLV (contained in value of current TLV) */
127 static __checkReturn efx_rc_t
129 __inout efx_asn1_cursor_t *cursor,
134 if (cursor == NULL) {
139 if (ASN1_TAG_IS_PRIM(tag)) {
140 /* Cannot enter a primitive tag */
144 rc = efx_asn1_parse_header_match_tag(cursor, tag);
146 /* Invalid TLV or wrong tag */
150 /* Limit cursor range to nested TLV */
151 cursor->buffer += cursor->hdr_size;
152 cursor->length = cursor->val_size;
161 EFSYS_PROBE1(fail1, efx_rc_t, rc);
167 * Check that the current ASN.1 TLV matches the given tag and value.
168 * Advance cursor to next TLV on a successful match.
170 static __checkReturn efx_rc_t
171 efx_asn1_match_tag_value(
172 __inout efx_asn1_cursor_t *cursor,
174 __in const void *valp,
175 __in uint32_t val_size)
179 if (cursor == NULL) {
183 rc = efx_asn1_parse_header_match_tag(cursor, tag);
185 /* Invalid TLV or wrong tag */
188 if (cursor->val_size != val_size) {
189 /* Value size is different */
193 if (memcmp(cursor->buffer + cursor->hdr_size, valp, val_size) != 0) {
194 /* Value content is different */
198 cursor->buffer += cursor->hdr_size + cursor->val_size;
199 cursor->length -= cursor->hdr_size + cursor->val_size;
210 EFSYS_PROBE1(fail1, efx_rc_t, rc);
215 /* Advance cursor to next TLV */
216 static __checkReturn efx_rc_t
218 __inout efx_asn1_cursor_t *cursor,
223 if (cursor == NULL) {
228 rc = efx_asn1_parse_header_match_tag(cursor, tag);
230 /* Invalid TLV or wrong tag */
233 cursor->buffer += cursor->hdr_size + cursor->val_size;
234 cursor->length -= cursor->hdr_size + cursor->val_size;
241 EFSYS_PROBE1(fail1, efx_rc_t, rc);
246 /* Return pointer to value octets and value size from current TLV */
247 static __checkReturn efx_rc_t
248 efx_asn1_get_tag_value(
249 __inout efx_asn1_cursor_t *cursor,
251 __out uint8_t **valp,
252 __out uint32_t *val_sizep)
256 if (cursor == NULL || valp == NULL || val_sizep == NULL) {
261 rc = efx_asn1_parse_header_match_tag(cursor, tag);
263 /* Invalid TLV or wrong tag */
266 *valp = cursor->buffer + cursor->hdr_size;
267 *val_sizep = cursor->val_size;
274 EFSYS_PROBE1(fail1, efx_rc_t, rc);
281 * Utility routines for parsing CMS headers (see RFC2315, PKCS#7)
284 /* OID 1.2.840.113549.1.7.2 */
285 static const uint8_t PKCS7_SignedData[] =
286 { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
288 /* OID 1.2.840.113549.1.7.1 */
289 static const uint8_t PKCS7_Data[] =
290 { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 };
292 /* SignedData structure version */
293 static const uint8_t SignedData_Version[] =
297 * Check for a valid image in signed image format. This uses CMS syntax
298 * (see RFC2315, PKCS#7) to provide signatures, and certificates required
299 * to validate the signatures. The encapsulated content is in unsigned image
300 * format (reflash header, image code, trailer checksum).
302 static __checkReturn efx_rc_t
303 efx_check_signed_image_header(
305 __in uint32_t buffer_size,
306 __out uint32_t *content_offsetp,
307 __out uint32_t *content_lengthp)
309 efx_asn1_cursor_t cursor;
314 if (content_offsetp == NULL || content_lengthp == NULL) {
318 cursor.buffer = (uint8_t *)bufferp;
319 cursor.length = buffer_size;
322 rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
326 /* ContextInfo.contentType */
327 rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID,
328 PKCS7_SignedData, sizeof (PKCS7_SignedData));
332 /* ContextInfo.content */
333 rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0));
338 rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
342 /* SignedData.version */
343 rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_INTEGER,
344 SignedData_Version, sizeof (SignedData_Version));
348 /* SignedData.digestAlgorithms */
349 rc = efx_asn1_skip_tag(&cursor, ASN1_TAG_SET);
353 /* SignedData.encapContentInfo */
354 rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
358 /* SignedData.encapContentInfo.econtentType */
359 rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID,
360 PKCS7_Data, sizeof (PKCS7_Data));
364 /* SignedData.encapContentInfo.econtent */
365 rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0));
370 * The octet string contains the image header, image code bytes and
371 * image trailer CRC (same as unsigned image layout).
375 rc = efx_asn1_get_tag_value(&cursor, ASN1_TAG_OCTET_STRING,
380 if ((valp == NULL) || (val_size == 0)) {
384 if (valp < (uint8_t *)bufferp) {
388 if ((valp + val_size) > ((uint8_t *)bufferp + buffer_size)) {
393 *content_offsetp = (uint32_t)(valp - (uint8_t *)bufferp);
394 *content_lengthp = val_size;
425 EFSYS_PROBE1(fail1, efx_rc_t, rc);
430 static __checkReturn efx_rc_t
431 efx_check_unsigned_image(
433 __in uint32_t buffer_size)
435 efx_image_header_t *header;
436 efx_image_trailer_t *trailer;
440 EFX_STATIC_ASSERT(sizeof (*header) == EFX_IMAGE_HEADER_SIZE);
441 EFX_STATIC_ASSERT(sizeof (*trailer) == EFX_IMAGE_TRAILER_SIZE);
443 /* Must have at least enough space for required image header fields */
444 if (buffer_size < (EFX_FIELD_OFFSET(efx_image_header_t, eih_size) +
445 sizeof (header->eih_size))) {
449 header = (efx_image_header_t *)bufferp;
451 if (header->eih_magic != EFX_IMAGE_HEADER_MAGIC) {
457 * Check image header version is same or higher than lowest required
460 if (header->eih_version < EFX_IMAGE_HEADER_VERSION) {
465 /* Buffer must have space for image header, code and image trailer. */
466 if (buffer_size < (header->eih_size + header->eih_code_size +
467 EFX_IMAGE_TRAILER_SIZE)) {
472 /* Check CRC from image buffer matches computed CRC. */
473 trailer = (efx_image_trailer_t *)((uint8_t *)header +
474 header->eih_size + header->eih_code_size);
476 crc = efx_crc32_calculate(0, (uint8_t *)header,
477 (header->eih_size + header->eih_code_size));
479 if (trailer->eit_crc != crc) {
495 EFSYS_PROBE1(fail1, efx_rc_t, rc);
500 __checkReturn efx_rc_t
501 efx_check_reflash_image(
503 __in uint32_t buffer_size,
504 __out efx_image_info_t *infop)
506 efx_image_format_t format = EFX_IMAGE_FORMAT_NO_IMAGE;
507 uint32_t image_offset;
513 EFSYS_ASSERT(infop != NULL);
518 memset(infop, 0, sizeof (*infop));
520 if (bufferp == NULL || buffer_size == 0) {
526 * Check if the buffer contains an image in signed format, and if so,
527 * locate the image header.
529 rc = efx_check_signed_image_header(bufferp, buffer_size,
530 &image_offset, &image_size);
533 * Buffer holds signed image format. Check that the encapsulated
534 * content is in unsigned image format.
536 format = EFX_IMAGE_FORMAT_SIGNED;
538 /* Check if the buffer holds image in unsigned image format */
539 format = EFX_IMAGE_FORMAT_UNSIGNED;
541 image_size = buffer_size;
543 if (image_offset + image_size > buffer_size) {
547 imagep = (uint8_t *)bufferp + image_offset;
549 /* Check unsigned image layout (image header, code, image trailer) */
550 rc = efx_check_unsigned_image(imagep, image_size);
554 /* Return image details */
555 infop->eii_format = format;
556 infop->eii_imagep = bufferp;
557 infop->eii_image_size = buffer_size;
558 infop->eii_headerp = (efx_image_header_t *)imagep;
568 infop->eii_format = EFX_IMAGE_FORMAT_INVALID;
569 infop->eii_imagep = NULL;
570 infop->eii_image_size = 0;
573 EFSYS_PROBE1(fail1, efx_rc_t, rc);
578 __checkReturn efx_rc_t
579 efx_build_signed_image_write_buffer(
580 __out uint8_t *bufferp,
581 __in uint32_t buffer_size,
582 __in efx_image_info_t *infop,
583 __out efx_image_header_t **headerpp)
585 signed_image_chunk_hdr_t chunk_hdr;
590 } cms_header, image_header, code, image_trailer, signature;
593 EFSYS_ASSERT((infop != NULL) && (headerpp != NULL));
595 if ((bufferp == NULL) || (buffer_size == 0) ||
596 (infop == NULL) || (headerpp == NULL)) {
597 /* Invalid arguments */
601 if ((infop->eii_format != EFX_IMAGE_FORMAT_SIGNED) ||
602 (infop->eii_imagep == NULL) ||
603 (infop->eii_headerp == NULL) ||
604 ((uint8_t *)infop->eii_headerp < (uint8_t *)infop->eii_imagep) ||
605 (infop->eii_image_size < EFX_IMAGE_HEADER_SIZE) ||
606 ((size_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep) >
607 (infop->eii_image_size - EFX_IMAGE_HEADER_SIZE))) {
608 /* Invalid image info */
613 /* Locate image chunks in original signed image */
614 cms_header.offset = 0;
616 (uint32_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep);
617 if ((cms_header.size > buffer_size) ||
618 (cms_header.offset > (buffer_size - cms_header.size))) {
623 image_header.offset = cms_header.offset + cms_header.size;
624 image_header.size = infop->eii_headerp->eih_size;
625 if ((image_header.size > buffer_size) ||
626 (image_header.offset > (buffer_size - image_header.size))) {
631 code.offset = image_header.offset + image_header.size;
632 code.size = infop->eii_headerp->eih_code_size;
633 if ((code.size > buffer_size) ||
634 (code.offset > (buffer_size - code.size))) {
639 image_trailer.offset = code.offset + code.size;
640 image_trailer.size = EFX_IMAGE_TRAILER_SIZE;
641 if ((image_trailer.size > buffer_size) ||
642 (image_trailer.offset > (buffer_size - image_trailer.size))) {
647 signature.offset = image_trailer.offset + image_trailer.size;
648 signature.size = (uint32_t)(infop->eii_image_size - signature.offset);
649 if ((signature.size > buffer_size) ||
650 (signature.offset > (buffer_size - signature.size))) {
655 EFSYS_ASSERT3U(infop->eii_image_size, ==, cms_header.size +
656 image_header.size + code.size + image_trailer.size +
661 * Build signed image partition, inserting chunk headers.
663 * Signed Image: Image in NVRAM partition:
665 * +-----------------+ +-----------------+
666 * | CMS header | | mcfw.update |<----+
667 * +-----------------+ | | |
668 * | reflash header | +-----------------+ |
669 * +-----------------+ | chunk header: |-->--|-+
670 * | mcfw.update | | REFLASH_TRAILER | | |
671 * | | +-----------------+ | |
672 * +-----------------+ +-->| CMS header | | |
673 * | reflash trailer | | +-----------------+ | |
674 * +-----------------+ | | chunk header: |->-+ | |
675 * | signature | | | REFLASH_HEADER | | | |
676 * +-----------------+ | +-----------------+ | | |
677 * | | reflash header |<--+ | |
678 * | +-----------------+ | |
679 * | | chunk header: |-->--+ |
681 * | +-----------------+ |
682 * | | reflash trailer |<------+
683 * | +-----------------+
684 * | | chunk header: |
685 * | | SIGNATURE |->-+
686 * | +-----------------+ |
687 * | | signature |<--+
688 * | +-----------------+
690 * | +-----------------+
691 * +-<-| chunk header: |
693 * +-----------------+
695 * Each chunk header gives the partition offset and length of the image
696 * chunk's data. The image chunk data is immediately followed by the
697 * chunk header for the next chunk.
699 * The data chunk for the firmware code must be at the start of the
700 * partition (needed for the bootloader). The first chunk header in the
701 * chain (for the CMS header) is stored at the end of the partition. The
702 * chain of chunk headers maintains the same logical order of image
703 * chunks as the original signed image file. This set of constraints
704 * results in the layout used for the data chunks and chunk headers.
707 memset(bufferp, buffer_size, 0xFF);
709 EFX_STATIC_ASSERT(sizeof (chunk_hdr) == SIGNED_IMAGE_CHUNK_HDR_LEN);
710 memset(&chunk_hdr, 0, SIGNED_IMAGE_CHUNK_HDR_LEN);
715 if (buffer_size < SIGNED_IMAGE_CHUNK_HDR_LEN) {
719 hdr_offset = buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN;
721 chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
722 chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
723 chunk_hdr.id = SIGNED_IMAGE_CHUNK_CMS_HEADER;
724 chunk_hdr.offset = code.size + SIGNED_IMAGE_CHUNK_HDR_LEN;
725 chunk_hdr.len = cms_header.size;
727 memcpy(bufferp + hdr_offset, &chunk_hdr, sizeof (chunk_hdr));
729 if ((chunk_hdr.len > buffer_size) ||
730 (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
734 memcpy(bufferp + chunk_hdr.offset,
735 infop->eii_imagep + cms_header.offset,
741 hdr_offset = chunk_hdr.offset + chunk_hdr.len;
742 if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
746 chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
747 chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
748 chunk_hdr.id = SIGNED_IMAGE_CHUNK_REFLASH_HEADER;
749 chunk_hdr.offset = hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
750 chunk_hdr.len = image_header.size;
752 memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
754 if ((chunk_hdr.len > buffer_size) ||
755 (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
759 memcpy(bufferp + chunk_hdr.offset,
760 infop->eii_imagep + image_header.offset,
763 *headerpp = (efx_image_header_t *)(bufferp + chunk_hdr.offset);
768 hdr_offset = chunk_hdr.offset + chunk_hdr.len;
769 if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
773 chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
774 chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
775 chunk_hdr.id = SIGNED_IMAGE_CHUNK_IMAGE;
776 chunk_hdr.offset = 0;
777 chunk_hdr.len = code.size;
779 memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
781 if ((chunk_hdr.len > buffer_size) ||
782 (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
786 memcpy(bufferp + chunk_hdr.offset,
787 infop->eii_imagep + code.offset,
791 * Image trailer (CRC)
793 chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
794 chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
795 chunk_hdr.id = SIGNED_IMAGE_CHUNK_REFLASH_TRAILER;
796 chunk_hdr.offset = hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
797 chunk_hdr.len = image_trailer.size;
799 hdr_offset = code.size;
800 if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
805 memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
807 if ((chunk_hdr.len > buffer_size) ||
808 (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
812 memcpy((uint8_t *)bufferp + chunk_hdr.offset,
813 infop->eii_imagep + image_trailer.offset,
819 hdr_offset = chunk_hdr.offset + chunk_hdr.len;
820 if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
824 chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
825 chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
826 chunk_hdr.id = SIGNED_IMAGE_CHUNK_SIGNATURE;
827 chunk_hdr.offset = chunk_hdr.offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
828 chunk_hdr.len = signature.size;
830 memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
832 if ((chunk_hdr.len > buffer_size) ||
833 (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
837 memcpy(bufferp + chunk_hdr.offset,
838 infop->eii_imagep + signature.offset,
876 EFSYS_PROBE1(fail1, efx_rc_t, rc);
883 #endif /* EFSYS_OPT_IMAGE_LAYOUT */
885 #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */