LCOV - code coverage report
Current view: top level - hal/sgx/src/trusted - seed.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 87 89 97.8 %
Date: 2025-07-10 13:49:13 Functions: 9 9 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 <stdio.h>
      26             : #include <string.h>
      27             : #include <stdint.h>
      28             : #include <secp256k1.h>
      29             : #include <openenclave/enclave.h>
      30             : 
      31             : #include "hal/hash.h"
      32             : #include "hal/seed.h"
      33             : #include "hal/constants.h"
      34             : #include "hal/log.h"
      35             : 
      36             : #include "secret_store.h"
      37             : #include "bip32.h"
      38             : #include "random.h"
      39             : 
      40             : #define SEST_SEED_KEY "seed"
      41             : 
      42             : // Globals
      43             : static bool G_seed_available;
      44             : static uint8_t G_seed[SEED_LENGTH];
      45             : 
      46             : static secp256k1_context* sp_ctx = NULL;
      47             : 
      48          26 : bool seed_init() {
      49             :     // Init the secp256k1 context
      50          26 :     if (!sp_ctx)
      51           1 :         sp_ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
      52             : 
      53          26 :     memset(G_seed, 0, sizeof(G_seed));
      54          26 :     G_seed_available = false;
      55             : 
      56          26 :     if (!sest_exists(SEST_SEED_KEY)) {
      57             :         // Module is in a wiped state
      58          11 :         return true;
      59             :     }
      60             : 
      61             :     // Read seed
      62          15 :     uint8_t seed_length = 0;
      63          15 :     if (!(seed_length = sest_read(SEST_SEED_KEY, G_seed, sizeof(G_seed)))) {
      64             :         LOG("Could not load the current seed\n");
      65           1 :         return false;
      66             :     }
      67             : 
      68             :     // Make sure seed is sound
      69          14 :     if (seed_length != sizeof(G_seed)) {
      70             :         LOG("Detected invalid seed\n");
      71           1 :         return false;
      72             :     }
      73             : 
      74          13 :     G_seed_available = true;
      75             :     LOG("Seed loaded\n");
      76             : 
      77          13 :     return true;
      78             : }
      79             : 
      80           2 : bool seed_wipe() {
      81           2 :     bool success = sest_remove(SEST_SEED_KEY);
      82           2 :     memset(G_seed, 0, sizeof(G_seed));
      83           2 :     G_seed_available = false;
      84             : 
      85           2 :     return success;
      86             : }
      87             : 
      88           5 : bool seed_generate(uint8_t* client_seed, uint8_t client_seed_size) {
      89           5 :     if (G_seed_available) {
      90             :         LOG("Seed already exists\n");
      91           1 :         return false;
      92             :     }
      93             : 
      94           4 :     if (client_seed_size != SEED_LENGTH) {
      95             :         LOG("Invalid client seed size\n");
      96           1 :         return false;
      97             :     }
      98             : 
      99             :     uint8_t random_bytes[SEED_LENGTH];
     100           3 :     if (!random_getrandom(random_bytes, sizeof(random_bytes))) {
     101             :         LOG("Error generating random seed\n");
     102           1 :         return false;
     103             :     }
     104             : 
     105          66 :     for (size_t i = 0; i < SEED_LENGTH; i++) {
     106          64 :         G_seed[i] = random_bytes[i] ^ client_seed[i];
     107             :     }
     108             : 
     109           2 :     if (!sest_write(SEST_SEED_KEY, G_seed, SEED_LENGTH)) {
     110             :         LOG("Error persisting generated seed\n");
     111           1 :         memset(G_seed, 0, sizeof(G_seed));
     112           1 :         return false;
     113             :     }
     114             : 
     115           1 :     G_seed_available = true;
     116           1 :     printf("Seed generated\n");
     117           1 :     return true;
     118             : }
     119             : 
     120           6 : static bool derive_privkey(uint32_t* path,
     121             :                            uint8_t path_length,
     122             :                            uint8_t* privkey_out,
     123             :                            size_t privkey_out_size) {
     124           6 :     if (!bip32_derive_private(privkey_out,
     125             :                               privkey_out_size,
     126             :                               G_seed,
     127             :                               sizeof(G_seed),
     128             :                               path,
     129             :                               path_length)) {
     130           2 :         return false;
     131             :     }
     132             : 
     133           4 :     return true;
     134             : }
     135             : 
     136          38 : bool seed_available() {
     137          38 :     return G_seed_available;
     138             : }
     139             : 
     140           4 : bool seed_derive_pubkey(uint32_t* path,
     141             :                         uint8_t path_length,
     142             :                         uint8_t* pubkey_out,
     143             :                         uint8_t* pubkey_out_length) {
     144             :     secp256k1_pubkey sp_pubkey;
     145             :     uint8_t derived_privkey[PRIVATE_KEY_LENGTH];
     146             : 
     147             :     LOG("Deriving public key for path...\n");
     148             : 
     149             :     // Derive the private key
     150           4 :     if (!derive_privkey(
     151             :             path, path_length, derived_privkey, sizeof(derived_privkey))) {
     152             :         LOG("Error deriving private key for public key gathering\n");
     153           1 :         return false;
     154             :     }
     155             : 
     156             :     // Derive the public key and serialize it uncompressed
     157           3 :     if (!secp256k1_ec_pubkey_create(sp_ctx, &sp_pubkey, derived_privkey)) {
     158             :         LOG("Error deriving public key\n");
     159           1 :         return false;
     160             :     }
     161             : 
     162           2 :     if (*pubkey_out_length < PUBKEY_UNCMP_LENGTH) {
     163             :         LOG("Overflow while deriving public key\n");
     164           1 :         return false;
     165             :     }
     166           1 :     size_t temp_length = *pubkey_out_length;
     167           1 :     secp256k1_ec_pubkey_serialize(sp_ctx,
     168             :                                   pubkey_out,
     169             :                                   &temp_length,
     170             :                                   &sp_pubkey,
     171             :                                   SECP256K1_EC_UNCOMPRESSED);
     172             : 
     173           1 :     if (temp_length != PUBKEY_UNCMP_LENGTH) {
     174             :         LOG("Unexpected error while deriving public key\n");
     175           0 :         return false;
     176             :     }
     177           1 :     *pubkey_out_length = (uint8_t)temp_length;
     178             : 
     179             :     LOG_HEX("Pubkey for path is:", pubkey_out, *pubkey_out_length);
     180             : 
     181           1 :     return true;
     182             : }
     183             : 
     184           3 : bool seed_sign(uint32_t* path,
     185             :                uint8_t path_length,
     186             :                uint8_t* hash32,
     187             :                uint8_t* sig_out,
     188             :                uint8_t* sig_out_length) {
     189             :     secp256k1_ecdsa_signature sp_sig;
     190             :     uint8_t derived_privkey[PRIVATE_KEY_LENGTH];
     191             : 
     192           3 :     if (*sig_out_length < MAX_SIGNATURE_LENGTH) {
     193             :         LOG("Overflow while signing\n");
     194           1 :         return false;
     195             :     }
     196             : 
     197             :     LOG_HEX("Signing hash:", hash32, HASH_LENGTH);
     198             : 
     199             :     // Derive the private key
     200           2 :     if (!derive_privkey(
     201             :             path, path_length, derived_privkey, sizeof(derived_privkey))) {
     202             :         LOG("Error deriving private key for signing\n");
     203           1 :         return false;
     204             :     }
     205             : 
     206             :     // Sign and serialize as DER
     207           1 :     secp256k1_ecdsa_sign(sp_ctx, &sp_sig, hash32, derived_privkey, NULL, NULL);
     208           1 :     size_t temp_length = *sig_out_length;
     209           1 :     secp256k1_ecdsa_signature_serialize_der(
     210             :         sp_ctx, sig_out, &temp_length, &sp_sig);
     211           1 :     if (temp_length > MAX_SIGNATURE_LENGTH) {
     212             :         LOG("Overflow while signing\n");
     213           0 :         return false;
     214             :     }
     215           1 :     *sig_out_length = (uint8_t)temp_length;
     216             : 
     217             :     LOG_HEX("Signature is: ", sig_out, *sig_out_length);
     218             : 
     219           1 :     return true;
     220             : }
     221             : 
     222           4 : bool seed_output_USE_FROM_EXPORT_ONLY(uint8_t* out, size_t* out_size) {
     223             :     // We need a seed
     224           4 :     if (!G_seed_available) {
     225             :         LOG("Seed: no seed available to output\n");
     226           1 :         return false;
     227             :     }
     228             : 
     229             :     // Output buffer validations
     230           3 :     if (*out_size < sizeof(G_seed)) {
     231             :         LOG("Seed: output buffer to small to write seed\n");
     232           1 :         return false;
     233             :     }
     234           2 :     if (!oe_is_within_enclave(out, *out_size)) {
     235             :         LOG("Seed: output buffer not strictly within the enclave\n");
     236           1 :         return false;
     237             :     }
     238             : 
     239             :     // Write seed
     240           1 :     memcpy(out, G_seed, sizeof(G_seed));
     241           1 :     *out_size = sizeof(G_seed);
     242           1 :     return true;
     243             : }
     244             : 
     245           5 : bool seed_set_USE_FROM_EXPORT_ONLY(uint8_t* in, size_t in_size) {
     246             :     // We need no seed
     247           5 :     if (G_seed_available) {
     248             :         LOG("Seed: already set\n");
     249           1 :         return false;
     250             :     }
     251             : 
     252             :     // Input buffer validations
     253           4 :     if (in_size < sizeof(G_seed)) {
     254             :         LOG("Seed: input buffer too small to set seed\n");
     255           1 :         return false;
     256             :     }
     257           3 :     if (!oe_is_within_enclave(in, in_size)) {
     258             :         LOG("Seed: input buffer not strictly within the enclave\n");
     259           1 :         return false;
     260             :     }
     261             : 
     262             :     // Set seed
     263           2 :     G_seed_available = false;
     264           2 :     memcpy(G_seed, in, sizeof(G_seed));
     265           2 :     if (!sest_write(SEST_SEED_KEY, G_seed, SEED_LENGTH)) {
     266             :         LOG("Seed: error persisting given seed\n");
     267           1 :         memset(G_seed, 0, sizeof(G_seed));
     268           1 :         return false;
     269             :     }
     270             : 
     271           1 :     G_seed_available = true;
     272             :     LOG("Seed set\n");
     273           1 :     return true;
     274             : }

Generated by: LCOV version 1.16