1391763d7SAndrew Rybchenko /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3391763d7SAndrew Rybchenko * 4391763d7SAndrew Rybchenko * Copyright (c) 2017-2018 Solarflare Communications Inc. 5391763d7SAndrew Rybchenko * All rights reserved. 6391763d7SAndrew Rybchenko * 7391763d7SAndrew Rybchenko * Redistribution and use in source and binary forms, with or without 8391763d7SAndrew Rybchenko * modification, are permitted provided that the following conditions are met: 9391763d7SAndrew Rybchenko * 10391763d7SAndrew Rybchenko * 1. Redistributions of source code must retain the above copyright notice, 11391763d7SAndrew Rybchenko * this list of conditions and the following disclaimer. 12391763d7SAndrew Rybchenko * 2. Redistributions in binary form must reproduce the above copyright notice, 13391763d7SAndrew Rybchenko * this list of conditions and the following disclaimer in the documentation 14391763d7SAndrew Rybchenko * and/or other materials provided with the distribution. 15391763d7SAndrew Rybchenko * 16391763d7SAndrew Rybchenko * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17391763d7SAndrew Rybchenko * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 18391763d7SAndrew Rybchenko * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19391763d7SAndrew Rybchenko * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20391763d7SAndrew Rybchenko * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21391763d7SAndrew Rybchenko * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22391763d7SAndrew Rybchenko * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23391763d7SAndrew Rybchenko * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24391763d7SAndrew Rybchenko * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25391763d7SAndrew Rybchenko * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26391763d7SAndrew Rybchenko * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27391763d7SAndrew Rybchenko * 28391763d7SAndrew Rybchenko * The views and conclusions contained in the software and documentation are 29391763d7SAndrew Rybchenko * those of the authors and should not be interpreted as representing official 30391763d7SAndrew Rybchenko * policies, either expressed or implied, of the FreeBSD Project. 31391763d7SAndrew Rybchenko */ 32391763d7SAndrew Rybchenko 33391763d7SAndrew Rybchenko #include <sys/cdefs.h> 34391763d7SAndrew Rybchenko __FBSDID("$FreeBSD$"); 35391763d7SAndrew Rybchenko 36391763d7SAndrew Rybchenko #include "efx.h" 37391763d7SAndrew Rybchenko #include "efx_impl.h" 38391763d7SAndrew Rybchenko 39391763d7SAndrew Rybchenko #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 40391763d7SAndrew Rybchenko 41391763d7SAndrew Rybchenko #if EFSYS_OPT_IMAGE_LAYOUT 42391763d7SAndrew Rybchenko 4302d7c0c3SAndrew Rybchenko /* 4402d7c0c3SAndrew Rybchenko * Utility routines to support limited parsing of ASN.1 tags. This is not a 4502d7c0c3SAndrew Rybchenko * general purpose ASN.1 parser, but is sufficient to locate the required 4602d7c0c3SAndrew Rybchenko * objects in a signed image with CMS headers. 4702d7c0c3SAndrew Rybchenko */ 4802d7c0c3SAndrew Rybchenko 4902d7c0c3SAndrew Rybchenko /* DER encodings for ASN.1 tags (see ITU-T X.690) */ 5002d7c0c3SAndrew Rybchenko #define ASN1_TAG_INTEGER (0x02) 5102d7c0c3SAndrew Rybchenko #define ASN1_TAG_OCTET_STRING (0x04) 5202d7c0c3SAndrew Rybchenko #define ASN1_TAG_OBJ_ID (0x06) 5302d7c0c3SAndrew Rybchenko #define ASN1_TAG_SEQUENCE (0x30) 5402d7c0c3SAndrew Rybchenko #define ASN1_TAG_SET (0x31) 5502d7c0c3SAndrew Rybchenko 5602d7c0c3SAndrew Rybchenko #define ASN1_TAG_IS_PRIM(tag) ((tag & 0x20) == 0) 5702d7c0c3SAndrew Rybchenko 5802d7c0c3SAndrew Rybchenko #define ASN1_TAG_PRIM_CONTEXT(n) (0x80 + (n)) 5902d7c0c3SAndrew Rybchenko #define ASN1_TAG_CONS_CONTEXT(n) (0xA0 + (n)) 6002d7c0c3SAndrew Rybchenko 6102d7c0c3SAndrew Rybchenko typedef struct efx_asn1_cursor_s { 6202d7c0c3SAndrew Rybchenko uint8_t *buffer; 6302d7c0c3SAndrew Rybchenko uint32_t length; 6402d7c0c3SAndrew Rybchenko 6502d7c0c3SAndrew Rybchenko uint8_t tag; 6602d7c0c3SAndrew Rybchenko uint32_t hdr_size; 6702d7c0c3SAndrew Rybchenko uint32_t val_size; 6802d7c0c3SAndrew Rybchenko } efx_asn1_cursor_t; 6902d7c0c3SAndrew Rybchenko 7002d7c0c3SAndrew Rybchenko /* Parse header of DER encoded ASN.1 TLV and match tag */ 7102d7c0c3SAndrew Rybchenko static __checkReturn efx_rc_t 7202d7c0c3SAndrew Rybchenko efx_asn1_parse_header_match_tag( 7302d7c0c3SAndrew Rybchenko __inout efx_asn1_cursor_t *cursor, 7402d7c0c3SAndrew Rybchenko __in uint8_t tag) 7502d7c0c3SAndrew Rybchenko { 7602d7c0c3SAndrew Rybchenko efx_rc_t rc; 7702d7c0c3SAndrew Rybchenko 7802d7c0c3SAndrew Rybchenko if (cursor == NULL || cursor->buffer == NULL || cursor->length < 2) { 7902d7c0c3SAndrew Rybchenko rc = EINVAL; 8002d7c0c3SAndrew Rybchenko goto fail1; 8102d7c0c3SAndrew Rybchenko } 8202d7c0c3SAndrew Rybchenko 8302d7c0c3SAndrew Rybchenko cursor->tag = cursor->buffer[0]; 8402d7c0c3SAndrew Rybchenko if (cursor->tag != tag) { 8502d7c0c3SAndrew Rybchenko /* Tag not matched */ 8602d7c0c3SAndrew Rybchenko rc = ENOENT; 8702d7c0c3SAndrew Rybchenko goto fail2; 8802d7c0c3SAndrew Rybchenko } 8902d7c0c3SAndrew Rybchenko 9002d7c0c3SAndrew Rybchenko if ((cursor->tag & 0x1F) == 0x1F) { 9102d7c0c3SAndrew Rybchenko /* Long tag format not used in CMS syntax */ 9202d7c0c3SAndrew Rybchenko rc = EINVAL; 9302d7c0c3SAndrew Rybchenko goto fail3; 9402d7c0c3SAndrew Rybchenko } 9502d7c0c3SAndrew Rybchenko 9602d7c0c3SAndrew Rybchenko if ((cursor->buffer[1] & 0x80) == 0) { 9702d7c0c3SAndrew Rybchenko /* Short form: length is 0..127 */ 9802d7c0c3SAndrew Rybchenko cursor->hdr_size = 2; 9902d7c0c3SAndrew Rybchenko cursor->val_size = cursor->buffer[1]; 10002d7c0c3SAndrew Rybchenko } else { 10102d7c0c3SAndrew Rybchenko /* Long form: length encoded as [0x80+nbytes][length bytes] */ 10202d7c0c3SAndrew Rybchenko uint32_t nbytes = cursor->buffer[1] & 0x7F; 10302d7c0c3SAndrew Rybchenko uint32_t offset; 10402d7c0c3SAndrew Rybchenko 10502d7c0c3SAndrew Rybchenko if (nbytes == 0) { 10602d7c0c3SAndrew Rybchenko /* Indefinite length not allowed in DER encoding */ 10702d7c0c3SAndrew Rybchenko rc = EINVAL; 10802d7c0c3SAndrew Rybchenko goto fail4; 10902d7c0c3SAndrew Rybchenko } 11002d7c0c3SAndrew Rybchenko if (2 + nbytes > cursor->length) { 11102d7c0c3SAndrew Rybchenko /* Header length overflows image buffer */ 11202d7c0c3SAndrew Rybchenko rc = EINVAL; 11302d7c0c3SAndrew Rybchenko goto fail6; 11402d7c0c3SAndrew Rybchenko } 11502d7c0c3SAndrew Rybchenko if (nbytes > sizeof (uint32_t)) { 11602d7c0c3SAndrew Rybchenko /* Length encoding too big */ 11702d7c0c3SAndrew Rybchenko rc = E2BIG; 11802d7c0c3SAndrew Rybchenko goto fail5; 11902d7c0c3SAndrew Rybchenko } 12002d7c0c3SAndrew Rybchenko cursor->hdr_size = 2 + nbytes; 12102d7c0c3SAndrew Rybchenko cursor->val_size = 0; 12202d7c0c3SAndrew Rybchenko for (offset = 2; offset < cursor->hdr_size; offset++) { 12302d7c0c3SAndrew Rybchenko cursor->val_size = 12402d7c0c3SAndrew Rybchenko (cursor->val_size << 8) | cursor->buffer[offset]; 12502d7c0c3SAndrew Rybchenko } 12602d7c0c3SAndrew Rybchenko } 12702d7c0c3SAndrew Rybchenko 12802d7c0c3SAndrew Rybchenko if ((cursor->hdr_size + cursor->val_size) > cursor->length) { 12902d7c0c3SAndrew Rybchenko /* Length overflows image buffer */ 13002d7c0c3SAndrew Rybchenko rc = E2BIG; 13102d7c0c3SAndrew Rybchenko goto fail7; 13202d7c0c3SAndrew Rybchenko } 13302d7c0c3SAndrew Rybchenko 13402d7c0c3SAndrew Rybchenko return (0); 13502d7c0c3SAndrew Rybchenko 13602d7c0c3SAndrew Rybchenko fail7: 13702d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail7); 13802d7c0c3SAndrew Rybchenko fail6: 13902d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail6); 14002d7c0c3SAndrew Rybchenko fail5: 14102d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail5); 14202d7c0c3SAndrew Rybchenko fail4: 14302d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail4); 14402d7c0c3SAndrew Rybchenko fail3: 14502d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail3); 14602d7c0c3SAndrew Rybchenko fail2: 14702d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail2); 14802d7c0c3SAndrew Rybchenko fail1: 14902d7c0c3SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 15002d7c0c3SAndrew Rybchenko 15102d7c0c3SAndrew Rybchenko return (rc); 15202d7c0c3SAndrew Rybchenko } 15302d7c0c3SAndrew Rybchenko 15402d7c0c3SAndrew Rybchenko /* Enter nested ASN.1 TLV (contained in value of current TLV) */ 15502d7c0c3SAndrew Rybchenko static __checkReturn efx_rc_t 15602d7c0c3SAndrew Rybchenko efx_asn1_enter_tag( 15702d7c0c3SAndrew Rybchenko __inout efx_asn1_cursor_t *cursor, 15802d7c0c3SAndrew Rybchenko __in uint8_t tag) 15902d7c0c3SAndrew Rybchenko { 16002d7c0c3SAndrew Rybchenko efx_rc_t rc; 16102d7c0c3SAndrew Rybchenko 16202d7c0c3SAndrew Rybchenko if (cursor == NULL) { 16302d7c0c3SAndrew Rybchenko rc = EINVAL; 16402d7c0c3SAndrew Rybchenko goto fail1; 16502d7c0c3SAndrew Rybchenko } 16602d7c0c3SAndrew Rybchenko 16702d7c0c3SAndrew Rybchenko if (ASN1_TAG_IS_PRIM(tag)) { 16802d7c0c3SAndrew Rybchenko /* Cannot enter a primitive tag */ 16902d7c0c3SAndrew Rybchenko rc = ENOTSUP; 17002d7c0c3SAndrew Rybchenko goto fail2; 17102d7c0c3SAndrew Rybchenko } 17202d7c0c3SAndrew Rybchenko rc = efx_asn1_parse_header_match_tag(cursor, tag); 17302d7c0c3SAndrew Rybchenko if (rc != 0) { 17402d7c0c3SAndrew Rybchenko /* Invalid TLV or wrong tag */ 17502d7c0c3SAndrew Rybchenko goto fail3; 17602d7c0c3SAndrew Rybchenko } 17702d7c0c3SAndrew Rybchenko 17802d7c0c3SAndrew Rybchenko /* Limit cursor range to nested TLV */ 17902d7c0c3SAndrew Rybchenko cursor->buffer += cursor->hdr_size; 18002d7c0c3SAndrew Rybchenko cursor->length = cursor->val_size; 18102d7c0c3SAndrew Rybchenko 18202d7c0c3SAndrew Rybchenko return (0); 18302d7c0c3SAndrew Rybchenko 18402d7c0c3SAndrew Rybchenko fail3: 18502d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail3); 18602d7c0c3SAndrew Rybchenko fail2: 18702d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail2); 18802d7c0c3SAndrew Rybchenko fail1: 18902d7c0c3SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 19002d7c0c3SAndrew Rybchenko 19102d7c0c3SAndrew Rybchenko return (rc); 19202d7c0c3SAndrew Rybchenko } 19302d7c0c3SAndrew Rybchenko 19402d7c0c3SAndrew Rybchenko /* 19502d7c0c3SAndrew Rybchenko * Check that the current ASN.1 TLV matches the given tag and value. 19602d7c0c3SAndrew Rybchenko * Advance cursor to next TLV on a successful match. 19702d7c0c3SAndrew Rybchenko */ 19802d7c0c3SAndrew Rybchenko static __checkReturn efx_rc_t 19902d7c0c3SAndrew Rybchenko efx_asn1_match_tag_value( 20002d7c0c3SAndrew Rybchenko __inout efx_asn1_cursor_t *cursor, 20102d7c0c3SAndrew Rybchenko __in uint8_t tag, 20202d7c0c3SAndrew Rybchenko __in const void *valp, 20302d7c0c3SAndrew Rybchenko __in uint32_t val_size) 20402d7c0c3SAndrew Rybchenko { 20502d7c0c3SAndrew Rybchenko efx_rc_t rc; 20602d7c0c3SAndrew Rybchenko 20702d7c0c3SAndrew Rybchenko if (cursor == NULL) { 20802d7c0c3SAndrew Rybchenko rc = EINVAL; 20902d7c0c3SAndrew Rybchenko goto fail1; 21002d7c0c3SAndrew Rybchenko } 21102d7c0c3SAndrew Rybchenko rc = efx_asn1_parse_header_match_tag(cursor, tag); 21202d7c0c3SAndrew Rybchenko if (rc != 0) { 21302d7c0c3SAndrew Rybchenko /* Invalid TLV or wrong tag */ 21402d7c0c3SAndrew Rybchenko goto fail2; 21502d7c0c3SAndrew Rybchenko } 21602d7c0c3SAndrew Rybchenko if (cursor->val_size != val_size) { 21702d7c0c3SAndrew Rybchenko /* Value size is different */ 21802d7c0c3SAndrew Rybchenko rc = EINVAL; 21902d7c0c3SAndrew Rybchenko goto fail3; 22002d7c0c3SAndrew Rybchenko } 22102d7c0c3SAndrew Rybchenko if (memcmp(cursor->buffer + cursor->hdr_size, valp, val_size) != 0) { 22202d7c0c3SAndrew Rybchenko /* Value content is different */ 22302d7c0c3SAndrew Rybchenko rc = EINVAL; 22402d7c0c3SAndrew Rybchenko goto fail4; 22502d7c0c3SAndrew Rybchenko } 22602d7c0c3SAndrew Rybchenko cursor->buffer += cursor->hdr_size + cursor->val_size; 22702d7c0c3SAndrew Rybchenko cursor->length -= cursor->hdr_size + cursor->val_size; 22802d7c0c3SAndrew Rybchenko 22902d7c0c3SAndrew Rybchenko return (0); 23002d7c0c3SAndrew Rybchenko 23102d7c0c3SAndrew Rybchenko fail4: 23202d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail4); 23302d7c0c3SAndrew Rybchenko fail3: 23402d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail3); 23502d7c0c3SAndrew Rybchenko fail2: 23602d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail2); 23702d7c0c3SAndrew Rybchenko fail1: 23802d7c0c3SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 23902d7c0c3SAndrew Rybchenko 24002d7c0c3SAndrew Rybchenko return (rc); 24102d7c0c3SAndrew Rybchenko } 24202d7c0c3SAndrew Rybchenko 24302d7c0c3SAndrew Rybchenko /* Advance cursor to next TLV */ 24402d7c0c3SAndrew Rybchenko static __checkReturn efx_rc_t 24502d7c0c3SAndrew Rybchenko efx_asn1_skip_tag( 24602d7c0c3SAndrew Rybchenko __inout efx_asn1_cursor_t *cursor, 24702d7c0c3SAndrew Rybchenko __in uint8_t tag) 24802d7c0c3SAndrew Rybchenko { 24902d7c0c3SAndrew Rybchenko efx_rc_t rc; 25002d7c0c3SAndrew Rybchenko 25102d7c0c3SAndrew Rybchenko if (cursor == NULL) { 25202d7c0c3SAndrew Rybchenko rc = EINVAL; 25302d7c0c3SAndrew Rybchenko goto fail1; 25402d7c0c3SAndrew Rybchenko } 25502d7c0c3SAndrew Rybchenko 25602d7c0c3SAndrew Rybchenko rc = efx_asn1_parse_header_match_tag(cursor, tag); 25702d7c0c3SAndrew Rybchenko if (rc != 0) { 25802d7c0c3SAndrew Rybchenko /* Invalid TLV or wrong tag */ 25902d7c0c3SAndrew Rybchenko goto fail2; 26002d7c0c3SAndrew Rybchenko } 26102d7c0c3SAndrew Rybchenko cursor->buffer += cursor->hdr_size + cursor->val_size; 26202d7c0c3SAndrew Rybchenko cursor->length -= cursor->hdr_size + cursor->val_size; 26302d7c0c3SAndrew Rybchenko 26402d7c0c3SAndrew Rybchenko return (0); 26502d7c0c3SAndrew Rybchenko 26602d7c0c3SAndrew Rybchenko fail2: 26702d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail2); 26802d7c0c3SAndrew Rybchenko fail1: 26902d7c0c3SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 27002d7c0c3SAndrew Rybchenko 27102d7c0c3SAndrew Rybchenko return (rc); 27202d7c0c3SAndrew Rybchenko } 27302d7c0c3SAndrew Rybchenko 27402d7c0c3SAndrew Rybchenko /* Return pointer to value octets and value size from current TLV */ 27502d7c0c3SAndrew Rybchenko static __checkReturn efx_rc_t 27602d7c0c3SAndrew Rybchenko efx_asn1_get_tag_value( 27702d7c0c3SAndrew Rybchenko __inout efx_asn1_cursor_t *cursor, 27802d7c0c3SAndrew Rybchenko __in uint8_t tag, 27902d7c0c3SAndrew Rybchenko __out uint8_t **valp, 28002d7c0c3SAndrew Rybchenko __out uint32_t *val_sizep) 28102d7c0c3SAndrew Rybchenko { 28202d7c0c3SAndrew Rybchenko efx_rc_t rc; 28302d7c0c3SAndrew Rybchenko 28402d7c0c3SAndrew Rybchenko if (cursor == NULL || valp == NULL || val_sizep == NULL) { 28502d7c0c3SAndrew Rybchenko rc = EINVAL; 28602d7c0c3SAndrew Rybchenko goto fail1; 28702d7c0c3SAndrew Rybchenko } 28802d7c0c3SAndrew Rybchenko 28902d7c0c3SAndrew Rybchenko rc = efx_asn1_parse_header_match_tag(cursor, tag); 29002d7c0c3SAndrew Rybchenko if (rc != 0) { 29102d7c0c3SAndrew Rybchenko /* Invalid TLV or wrong tag */ 29202d7c0c3SAndrew Rybchenko goto fail2; 29302d7c0c3SAndrew Rybchenko } 29402d7c0c3SAndrew Rybchenko *valp = cursor->buffer + cursor->hdr_size; 29502d7c0c3SAndrew Rybchenko *val_sizep = cursor->val_size; 29602d7c0c3SAndrew Rybchenko 29702d7c0c3SAndrew Rybchenko return (0); 29802d7c0c3SAndrew Rybchenko 29902d7c0c3SAndrew Rybchenko fail2: 30002d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail2); 30102d7c0c3SAndrew Rybchenko fail1: 30202d7c0c3SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 30302d7c0c3SAndrew Rybchenko 30402d7c0c3SAndrew Rybchenko return (rc); 30502d7c0c3SAndrew Rybchenko } 30602d7c0c3SAndrew Rybchenko 30702d7c0c3SAndrew Rybchenko /* 30802d7c0c3SAndrew Rybchenko * Utility routines for parsing CMS headers (see RFC2315, PKCS#7) 30902d7c0c3SAndrew Rybchenko */ 31002d7c0c3SAndrew Rybchenko 31102d7c0c3SAndrew Rybchenko /* OID 1.2.840.113549.1.7.2 */ 31202d7c0c3SAndrew Rybchenko static const uint8_t PKCS7_SignedData[] = 31302d7c0c3SAndrew Rybchenko { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 }; 31402d7c0c3SAndrew Rybchenko 31502d7c0c3SAndrew Rybchenko /* OID 1.2.840.113549.1.7.1 */ 31602d7c0c3SAndrew Rybchenko static const uint8_t PKCS7_Data[] = 31702d7c0c3SAndrew Rybchenko { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; 31802d7c0c3SAndrew Rybchenko 31902d7c0c3SAndrew Rybchenko /* SignedData structure version */ 32002d7c0c3SAndrew Rybchenko static const uint8_t SignedData_Version[] = 32102d7c0c3SAndrew Rybchenko { 0x03 }; 32202d7c0c3SAndrew Rybchenko 32302d7c0c3SAndrew Rybchenko /* 32402d7c0c3SAndrew Rybchenko * Check for a valid image in signed image format. This uses CMS syntax 32502d7c0c3SAndrew Rybchenko * (see RFC2315, PKCS#7) to provide signatures, and certificates required 32602d7c0c3SAndrew Rybchenko * to validate the signatures. The encapsulated content is in unsigned image 32702d7c0c3SAndrew Rybchenko * format (reflash header, image code, trailer checksum). 32802d7c0c3SAndrew Rybchenko */ 32902d7c0c3SAndrew Rybchenko static __checkReturn efx_rc_t 33002d7c0c3SAndrew Rybchenko efx_check_signed_image_header( 33102d7c0c3SAndrew Rybchenko __in void *bufferp, 33202d7c0c3SAndrew Rybchenko __in uint32_t buffer_size, 33302d7c0c3SAndrew Rybchenko __out uint32_t *content_offsetp, 33402d7c0c3SAndrew Rybchenko __out uint32_t *content_lengthp) 33502d7c0c3SAndrew Rybchenko { 33602d7c0c3SAndrew Rybchenko efx_asn1_cursor_t cursor; 33702d7c0c3SAndrew Rybchenko uint8_t *valp; 33802d7c0c3SAndrew Rybchenko uint32_t val_size; 33902d7c0c3SAndrew Rybchenko efx_rc_t rc; 34002d7c0c3SAndrew Rybchenko 34102d7c0c3SAndrew Rybchenko if (content_offsetp == NULL || content_lengthp == NULL) { 34202d7c0c3SAndrew Rybchenko rc = EINVAL; 34302d7c0c3SAndrew Rybchenko goto fail1; 34402d7c0c3SAndrew Rybchenko } 34502d7c0c3SAndrew Rybchenko cursor.buffer = (uint8_t *)bufferp; 34602d7c0c3SAndrew Rybchenko cursor.length = buffer_size; 34702d7c0c3SAndrew Rybchenko 34802d7c0c3SAndrew Rybchenko /* ContextInfo */ 34902d7c0c3SAndrew Rybchenko rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE); 35002d7c0c3SAndrew Rybchenko if (rc != 0) 35102d7c0c3SAndrew Rybchenko goto fail2; 35202d7c0c3SAndrew Rybchenko 35302d7c0c3SAndrew Rybchenko /* ContextInfo.contentType */ 35402d7c0c3SAndrew Rybchenko rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID, 35502d7c0c3SAndrew Rybchenko PKCS7_SignedData, sizeof (PKCS7_SignedData)); 35602d7c0c3SAndrew Rybchenko if (rc != 0) 35702d7c0c3SAndrew Rybchenko goto fail3; 35802d7c0c3SAndrew Rybchenko 35902d7c0c3SAndrew Rybchenko /* ContextInfo.content */ 36002d7c0c3SAndrew Rybchenko rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0)); 36102d7c0c3SAndrew Rybchenko if (rc != 0) 36202d7c0c3SAndrew Rybchenko goto fail4; 36302d7c0c3SAndrew Rybchenko 36402d7c0c3SAndrew Rybchenko /* SignedData */ 36502d7c0c3SAndrew Rybchenko rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE); 36602d7c0c3SAndrew Rybchenko if (rc != 0) 36702d7c0c3SAndrew Rybchenko goto fail5; 36802d7c0c3SAndrew Rybchenko 36902d7c0c3SAndrew Rybchenko /* SignedData.version */ 37002d7c0c3SAndrew Rybchenko rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_INTEGER, 37102d7c0c3SAndrew Rybchenko SignedData_Version, sizeof (SignedData_Version)); 37202d7c0c3SAndrew Rybchenko if (rc != 0) 37302d7c0c3SAndrew Rybchenko goto fail6; 37402d7c0c3SAndrew Rybchenko 37502d7c0c3SAndrew Rybchenko /* SignedData.digestAlgorithms */ 37602d7c0c3SAndrew Rybchenko rc = efx_asn1_skip_tag(&cursor, ASN1_TAG_SET); 37702d7c0c3SAndrew Rybchenko if (rc != 0) 37802d7c0c3SAndrew Rybchenko goto fail7; 37902d7c0c3SAndrew Rybchenko 38002d7c0c3SAndrew Rybchenko /* SignedData.encapContentInfo */ 38102d7c0c3SAndrew Rybchenko rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE); 38202d7c0c3SAndrew Rybchenko if (rc != 0) 38302d7c0c3SAndrew Rybchenko goto fail8; 38402d7c0c3SAndrew Rybchenko 38502d7c0c3SAndrew Rybchenko /* SignedData.encapContentInfo.econtentType */ 38602d7c0c3SAndrew Rybchenko rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID, 38702d7c0c3SAndrew Rybchenko PKCS7_Data, sizeof (PKCS7_Data)); 38802d7c0c3SAndrew Rybchenko if (rc != 0) 38902d7c0c3SAndrew Rybchenko goto fail9; 39002d7c0c3SAndrew Rybchenko 39102d7c0c3SAndrew Rybchenko /* SignedData.encapContentInfo.econtent */ 39202d7c0c3SAndrew Rybchenko rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0)); 39302d7c0c3SAndrew Rybchenko if (rc != 0) 39402d7c0c3SAndrew Rybchenko goto fail10; 39502d7c0c3SAndrew Rybchenko 39602d7c0c3SAndrew Rybchenko /* 39702d7c0c3SAndrew Rybchenko * The octet string contains the image header, image code bytes and 39802d7c0c3SAndrew Rybchenko * image trailer CRC (same as unsigned image layout). 39902d7c0c3SAndrew Rybchenko */ 40002d7c0c3SAndrew Rybchenko valp = NULL; 40102d7c0c3SAndrew Rybchenko val_size = 0; 40202d7c0c3SAndrew Rybchenko rc = efx_asn1_get_tag_value(&cursor, ASN1_TAG_OCTET_STRING, 40302d7c0c3SAndrew Rybchenko &valp, &val_size); 40402d7c0c3SAndrew Rybchenko if (rc != 0) 40502d7c0c3SAndrew Rybchenko goto fail11; 40602d7c0c3SAndrew Rybchenko 40702d7c0c3SAndrew Rybchenko if ((valp == NULL) || (val_size == 0)) { 40802d7c0c3SAndrew Rybchenko rc = EINVAL; 40902d7c0c3SAndrew Rybchenko goto fail12; 41002d7c0c3SAndrew Rybchenko } 41102d7c0c3SAndrew Rybchenko if (valp < (uint8_t *)bufferp) { 41202d7c0c3SAndrew Rybchenko rc = EINVAL; 41302d7c0c3SAndrew Rybchenko goto fail13; 41402d7c0c3SAndrew Rybchenko } 41502d7c0c3SAndrew Rybchenko if ((valp + val_size) > ((uint8_t *)bufferp + buffer_size)) { 41602d7c0c3SAndrew Rybchenko rc = EINVAL; 41702d7c0c3SAndrew Rybchenko goto fail14; 41802d7c0c3SAndrew Rybchenko } 41902d7c0c3SAndrew Rybchenko 42002d7c0c3SAndrew Rybchenko *content_offsetp = (uint32_t)(valp - (uint8_t *)bufferp); 42102d7c0c3SAndrew Rybchenko *content_lengthp = val_size; 42202d7c0c3SAndrew Rybchenko 42302d7c0c3SAndrew Rybchenko return (0); 42402d7c0c3SAndrew Rybchenko 42502d7c0c3SAndrew Rybchenko fail14: 42602d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail14); 42702d7c0c3SAndrew Rybchenko fail13: 42802d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail13); 42902d7c0c3SAndrew Rybchenko fail12: 43002d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail12); 43102d7c0c3SAndrew Rybchenko fail11: 43202d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail11); 43302d7c0c3SAndrew Rybchenko fail10: 43402d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail10); 43502d7c0c3SAndrew Rybchenko fail9: 43602d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail9); 43702d7c0c3SAndrew Rybchenko fail8: 43802d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail8); 43902d7c0c3SAndrew Rybchenko fail7: 44002d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail7); 44102d7c0c3SAndrew Rybchenko fail6: 44202d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail6); 44302d7c0c3SAndrew Rybchenko fail5: 44402d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail5); 44502d7c0c3SAndrew Rybchenko fail4: 44602d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail4); 44702d7c0c3SAndrew Rybchenko fail3: 44802d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail3); 44902d7c0c3SAndrew Rybchenko fail2: 45002d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail2); 45102d7c0c3SAndrew Rybchenko fail1: 45202d7c0c3SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 45302d7c0c3SAndrew Rybchenko 45402d7c0c3SAndrew Rybchenko return (rc); 45502d7c0c3SAndrew Rybchenko } 45602d7c0c3SAndrew Rybchenko 45702d7c0c3SAndrew Rybchenko static __checkReturn efx_rc_t 45802d7c0c3SAndrew Rybchenko efx_check_unsigned_image( 45902d7c0c3SAndrew Rybchenko __in void *bufferp, 46002d7c0c3SAndrew Rybchenko __in uint32_t buffer_size) 46102d7c0c3SAndrew Rybchenko { 46202d7c0c3SAndrew Rybchenko efx_image_header_t *header; 46302d7c0c3SAndrew Rybchenko efx_image_trailer_t *trailer; 46402d7c0c3SAndrew Rybchenko uint32_t crc; 46502d7c0c3SAndrew Rybchenko efx_rc_t rc; 46602d7c0c3SAndrew Rybchenko 46702d7c0c3SAndrew Rybchenko EFX_STATIC_ASSERT(sizeof (*header) == EFX_IMAGE_HEADER_SIZE); 46802d7c0c3SAndrew Rybchenko EFX_STATIC_ASSERT(sizeof (*trailer) == EFX_IMAGE_TRAILER_SIZE); 46902d7c0c3SAndrew Rybchenko 47002d7c0c3SAndrew Rybchenko /* Must have at least enough space for required image header fields */ 47102d7c0c3SAndrew Rybchenko if (buffer_size < (EFX_FIELD_OFFSET(efx_image_header_t, eih_size) + 47202d7c0c3SAndrew Rybchenko sizeof (header->eih_size))) { 47302d7c0c3SAndrew Rybchenko rc = ENOSPC; 47402d7c0c3SAndrew Rybchenko goto fail1; 47502d7c0c3SAndrew Rybchenko } 47602d7c0c3SAndrew Rybchenko header = (efx_image_header_t *)bufferp; 47702d7c0c3SAndrew Rybchenko 47802d7c0c3SAndrew Rybchenko if (header->eih_magic != EFX_IMAGE_HEADER_MAGIC) { 47902d7c0c3SAndrew Rybchenko rc = EINVAL; 48002d7c0c3SAndrew Rybchenko goto fail2; 48102d7c0c3SAndrew Rybchenko } 48202d7c0c3SAndrew Rybchenko 48302d7c0c3SAndrew Rybchenko /* 48402d7c0c3SAndrew Rybchenko * Check image header version is same or higher than lowest required 48502d7c0c3SAndrew Rybchenko * version. 48602d7c0c3SAndrew Rybchenko */ 48702d7c0c3SAndrew Rybchenko if (header->eih_version < EFX_IMAGE_HEADER_VERSION) { 48802d7c0c3SAndrew Rybchenko rc = EINVAL; 48902d7c0c3SAndrew Rybchenko goto fail3; 49002d7c0c3SAndrew Rybchenko } 49102d7c0c3SAndrew Rybchenko 49202d7c0c3SAndrew Rybchenko /* Buffer must have space for image header, code and image trailer. */ 49302d7c0c3SAndrew Rybchenko if (buffer_size < (header->eih_size + header->eih_code_size + 49402d7c0c3SAndrew Rybchenko EFX_IMAGE_TRAILER_SIZE)) { 49502d7c0c3SAndrew Rybchenko rc = ENOSPC; 49602d7c0c3SAndrew Rybchenko goto fail4; 49702d7c0c3SAndrew Rybchenko } 49802d7c0c3SAndrew Rybchenko 49902d7c0c3SAndrew Rybchenko /* Check CRC from image buffer matches computed CRC. */ 50002d7c0c3SAndrew Rybchenko trailer = (efx_image_trailer_t *)((uint8_t *)header + 50102d7c0c3SAndrew Rybchenko header->eih_size + header->eih_code_size); 50202d7c0c3SAndrew Rybchenko 50302d7c0c3SAndrew Rybchenko crc = efx_crc32_calculate(0, (uint8_t *)header, 50402d7c0c3SAndrew Rybchenko (header->eih_size + header->eih_code_size)); 50502d7c0c3SAndrew Rybchenko 50602d7c0c3SAndrew Rybchenko if (trailer->eit_crc != crc) { 50702d7c0c3SAndrew Rybchenko rc = EINVAL; 50802d7c0c3SAndrew Rybchenko goto fail5; 50902d7c0c3SAndrew Rybchenko } 51002d7c0c3SAndrew Rybchenko 51102d7c0c3SAndrew Rybchenko return (0); 51202d7c0c3SAndrew Rybchenko 51302d7c0c3SAndrew Rybchenko fail5: 51402d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail5); 51502d7c0c3SAndrew Rybchenko fail4: 51602d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail4); 51702d7c0c3SAndrew Rybchenko fail3: 51802d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail3); 51902d7c0c3SAndrew Rybchenko fail2: 52002d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail2); 52102d7c0c3SAndrew Rybchenko fail1: 52202d7c0c3SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 52302d7c0c3SAndrew Rybchenko 52402d7c0c3SAndrew Rybchenko return (rc); 52502d7c0c3SAndrew Rybchenko } 52602d7c0c3SAndrew Rybchenko 52702d7c0c3SAndrew Rybchenko __checkReturn efx_rc_t 52802d7c0c3SAndrew Rybchenko efx_check_reflash_image( 52902d7c0c3SAndrew Rybchenko __in void *bufferp, 53002d7c0c3SAndrew Rybchenko __in uint32_t buffer_size, 53102d7c0c3SAndrew Rybchenko __out efx_image_info_t *infop) 53202d7c0c3SAndrew Rybchenko { 53302d7c0c3SAndrew Rybchenko efx_image_format_t format = EFX_IMAGE_FORMAT_NO_IMAGE; 53402d7c0c3SAndrew Rybchenko uint32_t image_offset; 53502d7c0c3SAndrew Rybchenko uint32_t image_size; 53602d7c0c3SAndrew Rybchenko void *imagep; 53702d7c0c3SAndrew Rybchenko efx_rc_t rc; 53802d7c0c3SAndrew Rybchenko 53902d7c0c3SAndrew Rybchenko EFSYS_ASSERT(infop != NULL); 54002d7c0c3SAndrew Rybchenko if (infop == NULL) { 54102d7c0c3SAndrew Rybchenko rc = EINVAL; 54202d7c0c3SAndrew Rybchenko goto fail1; 54302d7c0c3SAndrew Rybchenko } 54402d7c0c3SAndrew Rybchenko memset(infop, 0, sizeof (*infop)); 54502d7c0c3SAndrew Rybchenko 54602d7c0c3SAndrew Rybchenko if (bufferp == NULL || buffer_size == 0) { 54702d7c0c3SAndrew Rybchenko rc = EINVAL; 54802d7c0c3SAndrew Rybchenko goto fail2; 54902d7c0c3SAndrew Rybchenko } 55002d7c0c3SAndrew Rybchenko 55102d7c0c3SAndrew Rybchenko /* 55202d7c0c3SAndrew Rybchenko * Check if the buffer contains an image in signed format, and if so, 55302d7c0c3SAndrew Rybchenko * locate the image header. 55402d7c0c3SAndrew Rybchenko */ 55502d7c0c3SAndrew Rybchenko rc = efx_check_signed_image_header(bufferp, buffer_size, 55602d7c0c3SAndrew Rybchenko &image_offset, &image_size); 55702d7c0c3SAndrew Rybchenko if (rc == 0) { 55802d7c0c3SAndrew Rybchenko /* 55902d7c0c3SAndrew Rybchenko * Buffer holds signed image format. Check that the encapsulated 56002d7c0c3SAndrew Rybchenko * content is in unsigned image format. 56102d7c0c3SAndrew Rybchenko */ 56202d7c0c3SAndrew Rybchenko format = EFX_IMAGE_FORMAT_SIGNED; 56302d7c0c3SAndrew Rybchenko } else { 56402d7c0c3SAndrew Rybchenko /* Check if the buffer holds image in unsigned image format */ 56502d7c0c3SAndrew Rybchenko format = EFX_IMAGE_FORMAT_UNSIGNED; 56602d7c0c3SAndrew Rybchenko image_offset = 0; 56702d7c0c3SAndrew Rybchenko image_size = buffer_size; 56802d7c0c3SAndrew Rybchenko } 56902d7c0c3SAndrew Rybchenko if (image_offset + image_size > buffer_size) { 57002d7c0c3SAndrew Rybchenko rc = E2BIG; 57102d7c0c3SAndrew Rybchenko goto fail3; 57202d7c0c3SAndrew Rybchenko } 57302d7c0c3SAndrew Rybchenko imagep = (uint8_t *)bufferp + image_offset; 57402d7c0c3SAndrew Rybchenko 57502d7c0c3SAndrew Rybchenko /* Check unsigned image layout (image header, code, image trailer) */ 57602d7c0c3SAndrew Rybchenko rc = efx_check_unsigned_image(imagep, image_size); 57702d7c0c3SAndrew Rybchenko if (rc != 0) 57802d7c0c3SAndrew Rybchenko goto fail4; 57902d7c0c3SAndrew Rybchenko 58002d7c0c3SAndrew Rybchenko /* Return image details */ 58102d7c0c3SAndrew Rybchenko infop->eii_format = format; 58202d7c0c3SAndrew Rybchenko infop->eii_imagep = bufferp; 58302d7c0c3SAndrew Rybchenko infop->eii_image_size = buffer_size; 58402d7c0c3SAndrew Rybchenko infop->eii_headerp = (efx_image_header_t *)imagep; 58502d7c0c3SAndrew Rybchenko 58602d7c0c3SAndrew Rybchenko return (0); 58702d7c0c3SAndrew Rybchenko 58802d7c0c3SAndrew Rybchenko fail4: 58902d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail4); 59002d7c0c3SAndrew Rybchenko fail3: 59102d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail3); 59202d7c0c3SAndrew Rybchenko fail2: 59302d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail2); 59402d7c0c3SAndrew Rybchenko infop->eii_format = EFX_IMAGE_FORMAT_INVALID; 59502d7c0c3SAndrew Rybchenko infop->eii_imagep = NULL; 59602d7c0c3SAndrew Rybchenko infop->eii_image_size = 0; 59702d7c0c3SAndrew Rybchenko 59802d7c0c3SAndrew Rybchenko fail1: 59902d7c0c3SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 60002d7c0c3SAndrew Rybchenko 60102d7c0c3SAndrew Rybchenko return (rc); 60202d7c0c3SAndrew Rybchenko } 60302d7c0c3SAndrew Rybchenko 60402d7c0c3SAndrew Rybchenko __checkReturn efx_rc_t 60502d7c0c3SAndrew Rybchenko efx_build_signed_image_write_buffer( 60602d7c0c3SAndrew Rybchenko __out_bcount(buffer_size) 60702d7c0c3SAndrew Rybchenko uint8_t *bufferp, 60802d7c0c3SAndrew Rybchenko __in uint32_t buffer_size, 60902d7c0c3SAndrew Rybchenko __in efx_image_info_t *infop, 61002d7c0c3SAndrew Rybchenko __out efx_image_header_t **headerpp) 61102d7c0c3SAndrew Rybchenko { 61202d7c0c3SAndrew Rybchenko signed_image_chunk_hdr_t chunk_hdr; 61302d7c0c3SAndrew Rybchenko uint32_t hdr_offset; 61402d7c0c3SAndrew Rybchenko struct { 61502d7c0c3SAndrew Rybchenko uint32_t offset; 61602d7c0c3SAndrew Rybchenko uint32_t size; 61702d7c0c3SAndrew Rybchenko } cms_header, image_header, code, image_trailer, signature; 61802d7c0c3SAndrew Rybchenko efx_rc_t rc; 61902d7c0c3SAndrew Rybchenko 62002d7c0c3SAndrew Rybchenko EFSYS_ASSERT((infop != NULL) && (headerpp != NULL)); 62102d7c0c3SAndrew Rybchenko 62202d7c0c3SAndrew Rybchenko if ((bufferp == NULL) || (buffer_size == 0) || 62302d7c0c3SAndrew Rybchenko (infop == NULL) || (headerpp == NULL)) { 62402d7c0c3SAndrew Rybchenko /* Invalid arguments */ 62502d7c0c3SAndrew Rybchenko rc = EINVAL; 62602d7c0c3SAndrew Rybchenko goto fail1; 62702d7c0c3SAndrew Rybchenko } 62802d7c0c3SAndrew Rybchenko if ((infop->eii_format != EFX_IMAGE_FORMAT_SIGNED) || 62902d7c0c3SAndrew Rybchenko (infop->eii_imagep == NULL) || 63002d7c0c3SAndrew Rybchenko (infop->eii_headerp == NULL) || 63102d7c0c3SAndrew Rybchenko ((uint8_t *)infop->eii_headerp < (uint8_t *)infop->eii_imagep) || 63202d7c0c3SAndrew Rybchenko (infop->eii_image_size < EFX_IMAGE_HEADER_SIZE) || 63302d7c0c3SAndrew Rybchenko ((size_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep) > 63402d7c0c3SAndrew Rybchenko (infop->eii_image_size - EFX_IMAGE_HEADER_SIZE))) { 63502d7c0c3SAndrew Rybchenko /* Invalid image info */ 63602d7c0c3SAndrew Rybchenko rc = EINVAL; 63702d7c0c3SAndrew Rybchenko goto fail2; 63802d7c0c3SAndrew Rybchenko } 63902d7c0c3SAndrew Rybchenko 64002d7c0c3SAndrew Rybchenko /* Locate image chunks in original signed image */ 64102d7c0c3SAndrew Rybchenko cms_header.offset = 0; 64202d7c0c3SAndrew Rybchenko cms_header.size = 64302d7c0c3SAndrew Rybchenko (uint32_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep); 64402d7c0c3SAndrew Rybchenko if ((cms_header.size > buffer_size) || 64502d7c0c3SAndrew Rybchenko (cms_header.offset > (buffer_size - cms_header.size))) { 64602d7c0c3SAndrew Rybchenko rc = EINVAL; 64702d7c0c3SAndrew Rybchenko goto fail3; 64802d7c0c3SAndrew Rybchenko } 64902d7c0c3SAndrew Rybchenko 65002d7c0c3SAndrew Rybchenko image_header.offset = cms_header.offset + cms_header.size; 65102d7c0c3SAndrew Rybchenko image_header.size = infop->eii_headerp->eih_size; 65202d7c0c3SAndrew Rybchenko if ((image_header.size > buffer_size) || 65302d7c0c3SAndrew Rybchenko (image_header.offset > (buffer_size - image_header.size))) { 65402d7c0c3SAndrew Rybchenko rc = EINVAL; 65502d7c0c3SAndrew Rybchenko goto fail4; 65602d7c0c3SAndrew Rybchenko } 65702d7c0c3SAndrew Rybchenko 65802d7c0c3SAndrew Rybchenko code.offset = image_header.offset + image_header.size; 65902d7c0c3SAndrew Rybchenko code.size = infop->eii_headerp->eih_code_size; 66002d7c0c3SAndrew Rybchenko if ((code.size > buffer_size) || 66102d7c0c3SAndrew Rybchenko (code.offset > (buffer_size - code.size))) { 66202d7c0c3SAndrew Rybchenko rc = EINVAL; 66302d7c0c3SAndrew Rybchenko goto fail5; 66402d7c0c3SAndrew Rybchenko } 66502d7c0c3SAndrew Rybchenko 66602d7c0c3SAndrew Rybchenko image_trailer.offset = code.offset + code.size; 66702d7c0c3SAndrew Rybchenko image_trailer.size = EFX_IMAGE_TRAILER_SIZE; 66802d7c0c3SAndrew Rybchenko if ((image_trailer.size > buffer_size) || 66902d7c0c3SAndrew Rybchenko (image_trailer.offset > (buffer_size - image_trailer.size))) { 67002d7c0c3SAndrew Rybchenko rc = EINVAL; 67102d7c0c3SAndrew Rybchenko goto fail6; 67202d7c0c3SAndrew Rybchenko } 67302d7c0c3SAndrew Rybchenko 67402d7c0c3SAndrew Rybchenko signature.offset = image_trailer.offset + image_trailer.size; 67502d7c0c3SAndrew Rybchenko signature.size = (uint32_t)(infop->eii_image_size - signature.offset); 67602d7c0c3SAndrew Rybchenko if ((signature.size > buffer_size) || 67702d7c0c3SAndrew Rybchenko (signature.offset > (buffer_size - signature.size))) { 67802d7c0c3SAndrew Rybchenko rc = EINVAL; 67902d7c0c3SAndrew Rybchenko goto fail7; 68002d7c0c3SAndrew Rybchenko } 68102d7c0c3SAndrew Rybchenko 68202d7c0c3SAndrew Rybchenko EFSYS_ASSERT3U(infop->eii_image_size, ==, cms_header.size + 68302d7c0c3SAndrew Rybchenko image_header.size + code.size + image_trailer.size + 68402d7c0c3SAndrew Rybchenko signature.size); 68502d7c0c3SAndrew Rybchenko 68602d7c0c3SAndrew Rybchenko /* BEGIN CSTYLED */ 68702d7c0c3SAndrew Rybchenko /* 68802d7c0c3SAndrew Rybchenko * Build signed image partition, inserting chunk headers. 68902d7c0c3SAndrew Rybchenko * 69002d7c0c3SAndrew Rybchenko * Signed Image: Image in NVRAM partition: 69102d7c0c3SAndrew Rybchenko * 69202d7c0c3SAndrew Rybchenko * +-----------------+ +-----------------+ 69302d7c0c3SAndrew Rybchenko * | CMS header | | mcfw.update |<----+ 69402d7c0c3SAndrew Rybchenko * +-----------------+ | | | 69502d7c0c3SAndrew Rybchenko * | reflash header | +-----------------+ | 69602d7c0c3SAndrew Rybchenko * +-----------------+ | chunk header: |-->--|-+ 69702d7c0c3SAndrew Rybchenko * | mcfw.update | | REFLASH_TRAILER | | | 69802d7c0c3SAndrew Rybchenko * | | +-----------------+ | | 69902d7c0c3SAndrew Rybchenko * +-----------------+ +-->| CMS header | | | 70002d7c0c3SAndrew Rybchenko * | reflash trailer | | +-----------------+ | | 70102d7c0c3SAndrew Rybchenko * +-----------------+ | | chunk header: |->-+ | | 70202d7c0c3SAndrew Rybchenko * | signature | | | REFLASH_HEADER | | | | 70302d7c0c3SAndrew Rybchenko * +-----------------+ | +-----------------+ | | | 70402d7c0c3SAndrew Rybchenko * | | reflash header |<--+ | | 70502d7c0c3SAndrew Rybchenko * | +-----------------+ | | 70602d7c0c3SAndrew Rybchenko * | | chunk header: |-->--+ | 70702d7c0c3SAndrew Rybchenko * | | IMAGE | | 70802d7c0c3SAndrew Rybchenko * | +-----------------+ | 70902d7c0c3SAndrew Rybchenko * | | reflash trailer |<------+ 71002d7c0c3SAndrew Rybchenko * | +-----------------+ 71102d7c0c3SAndrew Rybchenko * | | chunk header: | 71202d7c0c3SAndrew Rybchenko * | | SIGNATURE |->-+ 71302d7c0c3SAndrew Rybchenko * | +-----------------+ | 71402d7c0c3SAndrew Rybchenko * | | signature |<--+ 71502d7c0c3SAndrew Rybchenko * | +-----------------+ 71602d7c0c3SAndrew Rybchenko * | | ...unused... | 71702d7c0c3SAndrew Rybchenko * | +-----------------+ 71802d7c0c3SAndrew Rybchenko * +-<-| chunk header: | 71902d7c0c3SAndrew Rybchenko * >-->| CMS_HEADER | 72002d7c0c3SAndrew Rybchenko * +-----------------+ 72102d7c0c3SAndrew Rybchenko * 72202d7c0c3SAndrew Rybchenko * Each chunk header gives the partition offset and length of the image 72302d7c0c3SAndrew Rybchenko * chunk's data. The image chunk data is immediately followed by the 72402d7c0c3SAndrew Rybchenko * chunk header for the next chunk. 72502d7c0c3SAndrew Rybchenko * 72602d7c0c3SAndrew Rybchenko * The data chunk for the firmware code must be at the start of the 72702d7c0c3SAndrew Rybchenko * partition (needed for the bootloader). The first chunk header in the 72802d7c0c3SAndrew Rybchenko * chain (for the CMS header) is stored at the end of the partition. The 72902d7c0c3SAndrew Rybchenko * chain of chunk headers maintains the same logical order of image 73002d7c0c3SAndrew Rybchenko * chunks as the original signed image file. This set of constraints 73102d7c0c3SAndrew Rybchenko * results in the layout used for the data chunks and chunk headers. 73202d7c0c3SAndrew Rybchenko */ 73302d7c0c3SAndrew Rybchenko /* END CSTYLED */ 73402d7c0c3SAndrew Rybchenko memset(bufferp, 0xFF, buffer_size); 73502d7c0c3SAndrew Rybchenko 73602d7c0c3SAndrew Rybchenko EFX_STATIC_ASSERT(sizeof (chunk_hdr) == SIGNED_IMAGE_CHUNK_HDR_LEN); 73702d7c0c3SAndrew Rybchenko memset(&chunk_hdr, 0, SIGNED_IMAGE_CHUNK_HDR_LEN); 73802d7c0c3SAndrew Rybchenko 73902d7c0c3SAndrew Rybchenko /* 74002d7c0c3SAndrew Rybchenko * CMS header 74102d7c0c3SAndrew Rybchenko */ 74202d7c0c3SAndrew Rybchenko if (buffer_size < SIGNED_IMAGE_CHUNK_HDR_LEN) { 74302d7c0c3SAndrew Rybchenko rc = ENOSPC; 74402d7c0c3SAndrew Rybchenko goto fail8; 74502d7c0c3SAndrew Rybchenko } 74602d7c0c3SAndrew Rybchenko hdr_offset = buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN; 74702d7c0c3SAndrew Rybchenko 74802d7c0c3SAndrew Rybchenko chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC; 74902d7c0c3SAndrew Rybchenko chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION; 75002d7c0c3SAndrew Rybchenko chunk_hdr.id = SIGNED_IMAGE_CHUNK_CMS_HEADER; 75102d7c0c3SAndrew Rybchenko chunk_hdr.offset = code.size + SIGNED_IMAGE_CHUNK_HDR_LEN; 75202d7c0c3SAndrew Rybchenko chunk_hdr.len = cms_header.size; 75302d7c0c3SAndrew Rybchenko 75402d7c0c3SAndrew Rybchenko memcpy(bufferp + hdr_offset, &chunk_hdr, sizeof (chunk_hdr)); 75502d7c0c3SAndrew Rybchenko 75602d7c0c3SAndrew Rybchenko if ((chunk_hdr.len > buffer_size) || 75702d7c0c3SAndrew Rybchenko (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) { 75802d7c0c3SAndrew Rybchenko rc = ENOSPC; 75902d7c0c3SAndrew Rybchenko goto fail9; 76002d7c0c3SAndrew Rybchenko } 76102d7c0c3SAndrew Rybchenko memcpy(bufferp + chunk_hdr.offset, 76202d7c0c3SAndrew Rybchenko infop->eii_imagep + cms_header.offset, 76302d7c0c3SAndrew Rybchenko cms_header.size); 76402d7c0c3SAndrew Rybchenko 76502d7c0c3SAndrew Rybchenko /* 76602d7c0c3SAndrew Rybchenko * Image header 76702d7c0c3SAndrew Rybchenko */ 76802d7c0c3SAndrew Rybchenko hdr_offset = chunk_hdr.offset + chunk_hdr.len; 76902d7c0c3SAndrew Rybchenko if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) { 77002d7c0c3SAndrew Rybchenko rc = ENOSPC; 77102d7c0c3SAndrew Rybchenko goto fail10; 77202d7c0c3SAndrew Rybchenko } 77302d7c0c3SAndrew Rybchenko chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC; 77402d7c0c3SAndrew Rybchenko chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION; 77502d7c0c3SAndrew Rybchenko chunk_hdr.id = SIGNED_IMAGE_CHUNK_REFLASH_HEADER; 77602d7c0c3SAndrew Rybchenko chunk_hdr.offset = hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN; 77702d7c0c3SAndrew Rybchenko chunk_hdr.len = image_header.size; 77802d7c0c3SAndrew Rybchenko 77902d7c0c3SAndrew Rybchenko memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN); 78002d7c0c3SAndrew Rybchenko 78102d7c0c3SAndrew Rybchenko if ((chunk_hdr.len > buffer_size) || 78202d7c0c3SAndrew Rybchenko (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) { 78302d7c0c3SAndrew Rybchenko rc = ENOSPC; 78402d7c0c3SAndrew Rybchenko goto fail11; 78502d7c0c3SAndrew Rybchenko } 78602d7c0c3SAndrew Rybchenko memcpy(bufferp + chunk_hdr.offset, 78702d7c0c3SAndrew Rybchenko infop->eii_imagep + image_header.offset, 78802d7c0c3SAndrew Rybchenko image_header.size); 78902d7c0c3SAndrew Rybchenko 79002d7c0c3SAndrew Rybchenko *headerpp = (efx_image_header_t *)(bufferp + chunk_hdr.offset); 79102d7c0c3SAndrew Rybchenko 79202d7c0c3SAndrew Rybchenko /* 79302d7c0c3SAndrew Rybchenko * Firmware code 79402d7c0c3SAndrew Rybchenko */ 79502d7c0c3SAndrew Rybchenko hdr_offset = chunk_hdr.offset + chunk_hdr.len; 79602d7c0c3SAndrew Rybchenko if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) { 79702d7c0c3SAndrew Rybchenko rc = ENOSPC; 79802d7c0c3SAndrew Rybchenko goto fail12; 79902d7c0c3SAndrew Rybchenko } 80002d7c0c3SAndrew Rybchenko chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC; 80102d7c0c3SAndrew Rybchenko chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION; 80202d7c0c3SAndrew Rybchenko chunk_hdr.id = SIGNED_IMAGE_CHUNK_IMAGE; 80302d7c0c3SAndrew Rybchenko chunk_hdr.offset = 0; 80402d7c0c3SAndrew Rybchenko chunk_hdr.len = code.size; 80502d7c0c3SAndrew Rybchenko 80602d7c0c3SAndrew Rybchenko memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN); 80702d7c0c3SAndrew Rybchenko 80802d7c0c3SAndrew Rybchenko if ((chunk_hdr.len > buffer_size) || 80902d7c0c3SAndrew Rybchenko (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) { 81002d7c0c3SAndrew Rybchenko rc = ENOSPC; 81102d7c0c3SAndrew Rybchenko goto fail13; 81202d7c0c3SAndrew Rybchenko } 81302d7c0c3SAndrew Rybchenko memcpy(bufferp + chunk_hdr.offset, 81402d7c0c3SAndrew Rybchenko infop->eii_imagep + code.offset, 81502d7c0c3SAndrew Rybchenko code.size); 81602d7c0c3SAndrew Rybchenko 81702d7c0c3SAndrew Rybchenko /* 81802d7c0c3SAndrew Rybchenko * Image trailer (CRC) 81902d7c0c3SAndrew Rybchenko */ 82002d7c0c3SAndrew Rybchenko chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC; 82102d7c0c3SAndrew Rybchenko chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION; 82202d7c0c3SAndrew Rybchenko chunk_hdr.id = SIGNED_IMAGE_CHUNK_REFLASH_TRAILER; 82302d7c0c3SAndrew Rybchenko chunk_hdr.offset = hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN; 82402d7c0c3SAndrew Rybchenko chunk_hdr.len = image_trailer.size; 82502d7c0c3SAndrew Rybchenko 82602d7c0c3SAndrew Rybchenko hdr_offset = code.size; 82702d7c0c3SAndrew Rybchenko if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) { 82802d7c0c3SAndrew Rybchenko rc = ENOSPC; 82902d7c0c3SAndrew Rybchenko goto fail14; 83002d7c0c3SAndrew Rybchenko } 83102d7c0c3SAndrew Rybchenko 83202d7c0c3SAndrew Rybchenko memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN); 83302d7c0c3SAndrew Rybchenko 83402d7c0c3SAndrew Rybchenko if ((chunk_hdr.len > buffer_size) || 83502d7c0c3SAndrew Rybchenko (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) { 83602d7c0c3SAndrew Rybchenko rc = ENOSPC; 83702d7c0c3SAndrew Rybchenko goto fail15; 83802d7c0c3SAndrew Rybchenko } 83902d7c0c3SAndrew Rybchenko memcpy((uint8_t *)bufferp + chunk_hdr.offset, 84002d7c0c3SAndrew Rybchenko infop->eii_imagep + image_trailer.offset, 84102d7c0c3SAndrew Rybchenko image_trailer.size); 84202d7c0c3SAndrew Rybchenko 84302d7c0c3SAndrew Rybchenko /* 84402d7c0c3SAndrew Rybchenko * Signature 84502d7c0c3SAndrew Rybchenko */ 84602d7c0c3SAndrew Rybchenko hdr_offset = chunk_hdr.offset + chunk_hdr.len; 84702d7c0c3SAndrew Rybchenko if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) { 84802d7c0c3SAndrew Rybchenko rc = ENOSPC; 84902d7c0c3SAndrew Rybchenko goto fail16; 85002d7c0c3SAndrew Rybchenko } 85102d7c0c3SAndrew Rybchenko chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC; 85202d7c0c3SAndrew Rybchenko chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION; 85302d7c0c3SAndrew Rybchenko chunk_hdr.id = SIGNED_IMAGE_CHUNK_SIGNATURE; 85402d7c0c3SAndrew Rybchenko chunk_hdr.offset = chunk_hdr.offset + SIGNED_IMAGE_CHUNK_HDR_LEN; 85502d7c0c3SAndrew Rybchenko chunk_hdr.len = signature.size; 85602d7c0c3SAndrew Rybchenko 85702d7c0c3SAndrew Rybchenko memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN); 85802d7c0c3SAndrew Rybchenko 85902d7c0c3SAndrew Rybchenko if ((chunk_hdr.len > buffer_size) || 86002d7c0c3SAndrew Rybchenko (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) { 86102d7c0c3SAndrew Rybchenko rc = ENOSPC; 86202d7c0c3SAndrew Rybchenko goto fail17; 86302d7c0c3SAndrew Rybchenko } 86402d7c0c3SAndrew Rybchenko memcpy(bufferp + chunk_hdr.offset, 86502d7c0c3SAndrew Rybchenko infop->eii_imagep + signature.offset, 86602d7c0c3SAndrew Rybchenko signature.size); 86702d7c0c3SAndrew Rybchenko 86802d7c0c3SAndrew Rybchenko return (0); 86902d7c0c3SAndrew Rybchenko 87002d7c0c3SAndrew Rybchenko fail17: 87102d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail17); 87202d7c0c3SAndrew Rybchenko fail16: 87302d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail16); 87402d7c0c3SAndrew Rybchenko fail15: 87502d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail15); 87602d7c0c3SAndrew Rybchenko fail14: 87702d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail14); 87802d7c0c3SAndrew Rybchenko fail13: 87902d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail13); 88002d7c0c3SAndrew Rybchenko fail12: 88102d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail12); 88202d7c0c3SAndrew Rybchenko fail11: 88302d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail11); 88402d7c0c3SAndrew Rybchenko fail10: 88502d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail10); 88602d7c0c3SAndrew Rybchenko fail9: 88702d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail9); 88802d7c0c3SAndrew Rybchenko fail8: 88902d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail8); 89002d7c0c3SAndrew Rybchenko fail7: 89102d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail7); 89202d7c0c3SAndrew Rybchenko fail6: 89302d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail6); 89402d7c0c3SAndrew Rybchenko fail5: 89502d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail5); 89602d7c0c3SAndrew Rybchenko fail4: 89702d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail4); 89802d7c0c3SAndrew Rybchenko fail3: 89902d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail3); 90002d7c0c3SAndrew Rybchenko fail2: 90102d7c0c3SAndrew Rybchenko EFSYS_PROBE(fail2); 90202d7c0c3SAndrew Rybchenko fail1: 90302d7c0c3SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc); 90402d7c0c3SAndrew Rybchenko 90502d7c0c3SAndrew Rybchenko return (rc); 90602d7c0c3SAndrew Rybchenko } 907391763d7SAndrew Rybchenko 908391763d7SAndrew Rybchenko #endif /* EFSYS_OPT_IMAGE_LAYOUT */ 909391763d7SAndrew Rybchenko 910391763d7SAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */ 911