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 : }
|