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