LCOV - code coverage report
Current view: top level - hal/sgx/src/trusted - endorsement.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 106 113 93.8 %
Date: 2025-07-10 13:49:13 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /**
       2             :  * The MIT License (MIT)
       3             :  *
       4             :  * Copyright (c) 2021 RSK Labs Ltd
       5             :  *
       6             :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       7             :  * of this software and associated documentation files (the "Software"), to
       8             :  * deal in the Software without restriction, including without limitation the
       9             :  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
      10             :  * sell copies of the Software, and to permit persons to whom the Software is
      11             :  * furnished to do so, subject to the following conditions:
      12             :  *
      13             :  * The above copyright notice and this permission notice shall be included in
      14             :  * all copies or substantial portions of the Software.
      15             :  *
      16             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      17             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      18             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      19             :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      20             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      21             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
      22             :  * IN THE SOFTWARE.
      23             :  */
      24             : 
      25             : #include "hal/constants.h"
      26             : #include "hal/endorsement.h"
      27             : #include "hal/exceptions.h"
      28             : #include "hal/log.h"
      29             : #include "der_utils.h"
      30             : #include "evidence.h"
      31             : 
      32             : #include <string.h>
      33             : 
      34             : #define ENDORSEMENT_FORMAT EVIDENCE_FORMAT_SGX_ECDSA
      35             : 
      36             : static struct {
      37             :     bool initialised;
      38             : 
      39             :     // Current envelope
      40             :     struct {
      41             :         uint8_t* raw;
      42             :         size_t raw_size;
      43             :         sgx_quote_t* quote;
      44             :         sgx_quote_auth_data_t* quote_auth_data;
      45             :         sgx_qe_auth_data_t qe_auth_data;
      46             :         sgx_qe_cert_data_t qe_cert_data;
      47             :     } envelope;
      48             : } G_endorsement_ctx;
      49             : 
      50             : #define ENDORSEMENT_CHECK(oe_result, error_msg)                          \
      51             :     {                                                                    \
      52             :         if (OE_OK != oe_result) {                                        \
      53             :             LOG(error_msg);                                              \
      54             :             LOG(": result=%u (%s)\n", result, oe_result_str(oe_result)); \
      55             :             return false;                                                \
      56             :         }                                                                \
      57             :     }
      58             : 
      59             : // Taken from OpenEnclave's common/sgx/quote.c
      60          20 : OE_INLINE uint16_t ReadUint16(const uint8_t* p) {
      61          20 :     return (uint16_t)(p[0] | (p[1] << 8));
      62             : }
      63             : 
      64             : // Taken from OpenEnclave's common/sgx/quote.c
      65          10 : OE_INLINE uint32_t ReadUint32(const uint8_t* p) {
      66          10 :     return (uint32_t)(p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
      67             : }
      68             : 
      69             : // Based on OpenEnclave's common/sgx/quote.c::_parse_quote()
      70             : // No validation is performed. Left to the end user.
      71             : // Maybe we could do some minimal validation in the future.
      72          11 : static bool parse_envelope(uint8_t* msg, size_t msg_size) {
      73          11 :     const uint8_t* p = G_endorsement_ctx.envelope.raw;
      74          11 :     const uint8_t* const quote_end = p + G_endorsement_ctx.envelope.raw_size;
      75          11 :     sgx_quote_t* _sgx_quote = (sgx_quote_t*)p;
      76          11 :     G_endorsement_ctx.envelope.quote = _sgx_quote;
      77             : 
      78          11 :     if (quote_end < p) {
      79             :         LOG("SGX quote parsing error. Pointer wrapper around\n");
      80           0 :         return false;
      81             :     }
      82             : 
      83          11 :     p += sizeof(sgx_quote_t);
      84             : 
      85          11 :     if (p > quote_end) {
      86             :         LOG("Parse error after parsing SGX quote, before signature\n");
      87           1 :         return false;
      88             :     }
      89          10 :     if (p + _sgx_quote->signature_len + msg_size != quote_end) {
      90             :         LOG("Parse error after parsing SGX signature\n");
      91           0 :         return false;
      92             :     }
      93             : 
      94          10 :     G_endorsement_ctx.envelope.quote_auth_data = (sgx_quote_auth_data_t*)p;
      95             : 
      96          10 :     p += sizeof(sgx_quote_auth_data_t);
      97             : 
      98          10 :     sgx_qe_auth_data_t* qe_auth_data = &G_endorsement_ctx.envelope.qe_auth_data;
      99          10 :     qe_auth_data->size = ReadUint16(p);
     100          10 :     p += 2;
     101          10 :     qe_auth_data->data = (uint8_t*)p;
     102          10 :     p += qe_auth_data->size;
     103             : 
     104          10 :     if (p > quote_end) {
     105             :         LOG("Parse error after parsing QE authorization data\n");
     106           0 :         return false;
     107             :     }
     108             : 
     109          10 :     sgx_qe_cert_data_t* qe_cert_data = &G_endorsement_ctx.envelope.qe_cert_data;
     110          10 :     qe_cert_data->type = ReadUint16(p);
     111          10 :     p += 2;
     112          10 :     qe_cert_data->size = ReadUint32(p);
     113          10 :     p += 4;
     114          10 :     qe_cert_data->data = (uint8_t*)p;
     115          10 :     p += qe_cert_data->size;
     116             : 
     117          10 :     if (memcmp(p, msg, msg_size)) {
     118             :         LOG("Parse error: got inconsistent custom message\n");
     119           0 :         return false;
     120             :     }
     121             : 
     122          10 :     p += msg_size;
     123             : 
     124          10 :     if (p != quote_end) {
     125             :         LOG("Unexpected quote length while parsing\n");
     126           1 :         return false;
     127             :     }
     128             : 
     129           9 :     return true;
     130             : }
     131             : 
     132             : // ****************************************************** //
     133             : // ********** Public interface implemenetation ********** //
     134             : // ****************************************************** //
     135             : 
     136             : #define CHECK_INITIALISED_OR_RETURN(retval)   \
     137             :     {                                         \
     138             :         if (!G_endorsement_ctx.initialised) { \
     139             :             return (retval);                  \
     140             :         }                                     \
     141             :     }
     142             : 
     143          18 : bool endorsement_init() {
     144          18 :     explicit_bzero(&G_endorsement_ctx, sizeof(G_endorsement_ctx));
     145             : 
     146             :     // Make sure the desired evidence format is supported
     147          18 :     if (!evidence_supports_format(ENDORSEMENT_FORMAT)) {
     148             :         LOG("Endorsement: evidence format not supported\n");
     149           1 :         return false;
     150             :     }
     151             : 
     152          17 :     G_endorsement_ctx.initialised = true;
     153             :     LOG("Endorsement module initialized\n");
     154          17 :     return true;
     155             : }
     156             : 
     157          20 : void endorsement_finalise() {
     158          20 :     if (G_endorsement_ctx.envelope.raw) {
     159           7 :         evidence_free(G_endorsement_ctx.envelope.raw);
     160             :     }
     161          20 :     explicit_bzero(&G_endorsement_ctx, sizeof(G_endorsement_ctx));
     162          20 : }
     163             : 
     164          14 : bool endorsement_sign(uint8_t* msg,
     165             :                       size_t msg_size,
     166             :                       uint8_t* signature_out,
     167             :                       uint8_t* signature_out_length) {
     168          14 :     CHECK_INITIALISED_OR_RETURN(false);
     169             : 
     170          13 :     if (*signature_out_length < MAX_SIGNATURE_LENGTH) {
     171             :         LOG("Output buffer for signature too small: %u bytes\n",
     172             :             *signature_out_length);
     173           1 :         goto endorsement_sign_fail;
     174             :     }
     175             : 
     176          12 :     if (G_endorsement_ctx.envelope.raw) {
     177           0 :         evidence_free(G_endorsement_ctx.envelope.raw);
     178           0 :         explicit_bzero(&G_endorsement_ctx.envelope,
     179             :                        sizeof(G_endorsement_ctx.envelope));
     180             :     }
     181             : 
     182          12 :     evidence_format_t format = {
     183             :         .id = ENDORSEMENT_FORMAT,
     184             :         .settings = NULL,
     185             :         .settings_size = 0,
     186             :     };
     187          12 :     if (!evidence_generate(&format,
     188             :                            msg,
     189             :                            msg_size,
     190             :                            &G_endorsement_ctx.envelope.raw,
     191             :                            &G_endorsement_ctx.envelope.raw_size)) {
     192             :         LOG("Error generating envelope\n");
     193           1 :         goto endorsement_sign_fail;
     194             :     }
     195             : 
     196          11 :     if (!parse_envelope(msg, msg_size)) {
     197             :         LOG("Error parsing envelope\n");
     198           2 :         goto endorsement_sign_fail;
     199             :     }
     200             : 
     201             :     // Output signature in DER format
     202           9 :     sgx_ecdsa256_signature_t* sig =
     203           9 :         &G_endorsement_ctx.envelope.quote_auth_data->signature;
     204           9 :     *signature_out_length =
     205           9 :         der_encode_signature(signature_out, *signature_out_length, sig);
     206             : 
     207           9 :     if (*signature_out_length == 0) {
     208             :         LOG("Error encoding envelope signature\n");
     209           1 :         goto endorsement_sign_fail;
     210             :     }
     211             : 
     212           8 :     return true;
     213             : 
     214           5 : endorsement_sign_fail:
     215           5 :     explicit_bzero(&G_endorsement_ctx.envelope,
     216             :                    sizeof(G_endorsement_ctx.envelope));
     217           5 :     return false;
     218             : }
     219             : 
     220           5 : uint8_t* endorsement_get_envelope() {
     221           5 :     CHECK_INITIALISED_OR_RETURN(0);
     222             : 
     223           4 :     if (G_endorsement_ctx.envelope.raw_size == 0) {
     224           1 :         return 0;
     225             :     }
     226           3 :     return G_endorsement_ctx.envelope.raw;
     227             : }
     228             : 
     229           4 : size_t endorsement_get_envelope_length() {
     230           4 :     CHECK_INITIALISED_OR_RETURN(0);
     231             : 
     232           3 :     return G_endorsement_ctx.envelope.raw_size;
     233             : }
     234             : 
     235           4 : bool endorsement_get_code_hash(uint8_t* code_hash_out,
     236             :                                uint8_t* code_hash_out_length) {
     237           4 :     CHECK_INITIALISED_OR_RETURN(false);
     238             : 
     239           4 :     if (G_endorsement_ctx.envelope.raw_size == 0) {
     240             :         LOG("No envelope available\n");
     241           1 :         return false;
     242             :     }
     243             : 
     244           3 :     if (code_hash_out == NULL) {
     245             :         LOG("Output buffer is NULL\n");
     246           1 :         return false;
     247             :     }
     248             : 
     249           2 :     if (*code_hash_out_length < HASH_LENGTH) {
     250             :         LOG("Output buffer for code hash too small: %u bytes\n",
     251             :             *code_hash_out_length);
     252           1 :         return false;
     253             :     }
     254             : 
     255           1 :     memcpy(code_hash_out,
     256           1 :            G_endorsement_ctx.envelope.quote->report_body.mrenclave,
     257             :            sizeof(G_endorsement_ctx.envelope.quote->report_body.mrenclave));
     258           1 :     *code_hash_out_length =
     259             :         sizeof(G_endorsement_ctx.envelope.quote->report_body.mrenclave);
     260             : 
     261           1 :     return true;
     262             : }
     263             : 
     264           4 : bool endorsement_get_public_key(uint8_t* public_key_out,
     265             :                                 uint8_t* public_key_out_length) {
     266           4 :     CHECK_INITIALISED_OR_RETURN(false);
     267             : 
     268           4 :     if (G_endorsement_ctx.envelope.raw_size == 0) {
     269             :         LOG("No envelope available\n");
     270           1 :         return false;
     271             :     }
     272             : 
     273           3 :     if (public_key_out == NULL) {
     274             :         LOG("Output buffer is NULL\n");
     275           1 :         return false;
     276             :     }
     277             : 
     278           2 :     if (*public_key_out_length < PUBKEY_UNCMP_LENGTH) {
     279             :         LOG("Output buffer for public key too small: %u bytes\n",
     280             :             *public_key_out_length);
     281           1 :         return false;
     282             :     }
     283             : 
     284           1 :     size_t off = 0;
     285           1 :     public_key_out[off++] = 0x04;
     286           1 :     memcpy(
     287           1 :         public_key_out + off,
     288           1 :         G_endorsement_ctx.envelope.quote_auth_data->attestation_key.x,
     289             :         sizeof(G_endorsement_ctx.envelope.quote_auth_data->attestation_key.x));
     290           1 :     off +=
     291             :         sizeof(G_endorsement_ctx.envelope.quote_auth_data->attestation_key.x);
     292           1 :     memcpy(
     293           1 :         public_key_out + off,
     294           1 :         G_endorsement_ctx.envelope.quote_auth_data->attestation_key.y,
     295             :         sizeof(G_endorsement_ctx.envelope.quote_auth_data->attestation_key.y));
     296           1 :     off +=
     297             :         sizeof(G_endorsement_ctx.envelope.quote_auth_data->attestation_key.y);
     298           1 :     *public_key_out_length = off;
     299             : 
     300             :     // Sanity check
     301           1 :     if (off != PUBKEY_UNCMP_LENGTH) {
     302             :         LOG("Unexpected attestation public key length\n");
     303           0 :         return false;
     304             :     }
     305             : 
     306           1 :     return true;
     307             : }

Generated by: LCOV version 1.16