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

Generated by: LCOV version 1.16