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 <string.h>
26 :
27 : #include "hal/endorsement.h"
28 : #include "hal/platform.h"
29 : #include "hal/exceptions.h"
30 :
31 : #include "heartbeat.h"
32 : #include "apdu.h"
33 : #include "defs.h"
34 : #include "memutil.h"
35 : #include "bc_state.h"
36 : #include "compiletime.h"
37 : #include "util.h"
38 :
39 : /*
40 : * Reset the given heartbeat context
41 : */
42 0 : static void reset_heartbeat(heartbeat_t* heartbeat_ctx) {
43 0 : explicit_bzero(heartbeat_ctx, sizeof(heartbeat_t));
44 0 : heartbeat_ctx->state = STATE_HEARTBEAT_WAIT_UD_VALUE;
45 0 : }
46 :
47 : /*
48 : * Check that the SM for the heartbeat generation
49 : * matches the expected state.
50 : *
51 : * Reset the state and throw a protocol error
52 : * otherwise.
53 : */
54 8 : static void check_state(heartbeat_t* heartbeat_ctx,
55 : state_heartbeat_t expected) {
56 8 : if (heartbeat_ctx->state != expected) {
57 0 : reset_heartbeat(heartbeat_ctx);
58 0 : THROW(ERR_HBT_PROT_INVALID);
59 : }
60 8 : }
61 :
62 : // -----------------------------------------------------------------------
63 : // Protocol implementation
64 : // -----------------------------------------------------------------------
65 :
66 : /*
67 : * Implement the heartbeat protocol.
68 : *
69 : * @arg[in] rx number of received bytes from the Host
70 : * @arg[in] heartbeat_ctx heartbeat context
71 : * @ret number of transmited bytes to the host
72 : */
73 20 : unsigned int get_heartbeat(volatile unsigned int rx,
74 : heartbeat_t* heartbeat_ctx) {
75 : // Build should fail when more bytes than available are tried to be copied
76 : // from the last signed BTC tx hash
77 : COMPILE_TIME_ASSERT(LAST_SIGNED_TX_BYTES <=
78 : sizeof(N_bc_state.last_auth_signed_btc_tx_hash));
79 :
80 20 : COMPILE_TIME_ASSERT(MAX_HEARTBEAT_MESSAGE_SIZE <= APDU_TOTAL_DATA_SIZE_OUT);
81 :
82 : uint8_t out_size;
83 :
84 20 : switch (APDU_OP()) {
85 4 : case OP_HBT_UD_VALUE:
86 : // Should receive a user-defined value
87 4 : if (APDU_DATA_SIZE(rx) != UD_VALUE_SIZE) {
88 0 : reset_heartbeat(heartbeat_ctx);
89 0 : THROW(ERR_HBT_PROT_INVALID);
90 : }
91 :
92 : // Initialize message
93 4 : explicit_bzero(heartbeat_ctx->msg, sizeof(heartbeat_ctx->msg));
94 4 : heartbeat_ctx->msg_offset = 0;
95 :
96 : // Copy the message prefix
97 8 : SAFE_MEMMOVE(heartbeat_ctx->msg,
98 : sizeof(heartbeat_ctx->msg),
99 : heartbeat_ctx->msg_offset,
100 : (void*)PIC(HEARTBEAT_MSG_PREFIX),
101 : HEARTBEAT_MSG_PREFIX_LENGTH,
102 : MEMMOVE_ZERO_OFFSET,
103 : HEARTBEAT_MSG_PREFIX_LENGTH,
104 : THROW(ERR_HBT_INTERNAL));
105 4 : heartbeat_ctx->msg_offset += HEARTBEAT_MSG_PREFIX_LENGTH;
106 :
107 : // Copy the current best block
108 8 : SAFE_MEMMOVE(heartbeat_ctx->msg,
109 : sizeof(heartbeat_ctx->msg),
110 : heartbeat_ctx->msg_offset,
111 : N_bc_state.best_block,
112 : sizeof(N_bc_state.best_block),
113 : MEMMOVE_ZERO_OFFSET,
114 : sizeof(N_bc_state.best_block),
115 : THROW(ERR_HBT_INTERNAL));
116 4 : heartbeat_ctx->msg_offset += sizeof(N_bc_state.best_block);
117 :
118 : // Copy the last LAST_SIGNED_TX_BYTES bytes of the last auth signed tx
119 8 : SAFE_MEMMOVE(heartbeat_ctx->msg,
120 : sizeof(heartbeat_ctx->msg),
121 : heartbeat_ctx->msg_offset,
122 : N_bc_state.last_auth_signed_btc_tx_hash,
123 : sizeof(N_bc_state.last_auth_signed_btc_tx_hash),
124 : MEMMOVE_ZERO_OFFSET,
125 : LAST_SIGNED_TX_BYTES,
126 : THROW(ERR_HBT_INTERNAL));
127 4 : heartbeat_ctx->msg_offset += LAST_SIGNED_TX_BYTES;
128 :
129 : // Copy the UD value from the APDU
130 8 : SAFE_MEMMOVE(heartbeat_ctx->msg,
131 : sizeof(heartbeat_ctx->msg),
132 : heartbeat_ctx->msg_offset,
133 : APDU_DATA_PTR,
134 : APDU_TOTAL_DATA_SIZE,
135 : MEMMOVE_ZERO_OFFSET,
136 : UD_VALUE_SIZE,
137 : THROW(ERR_HBT_INTERNAL));
138 4 : heartbeat_ctx->msg_offset += UD_VALUE_SIZE;
139 :
140 4 : heartbeat_ctx->state = STATE_HEARTBEAT_READY;
141 :
142 4 : return TX_FOR_DATA_SIZE(0);
143 4 : case OP_HBT_GET:
144 4 : check_state(heartbeat_ctx, STATE_HEARTBEAT_READY);
145 :
146 : // Sign message
147 4 : uint8_t endorsement_size = (uint8_t)MIN(APDU_TOTAL_DATA_SIZE_OUT, 0xFF);
148 4 : if (!endorsement_sign(heartbeat_ctx->msg,
149 4 : heartbeat_ctx->msg_offset,
150 4 : APDU_DATA_PTR,
151 : &endorsement_size)) {
152 0 : THROW(ERR_HBT_INTERNAL);
153 : }
154 :
155 4 : return TX_FOR_DATA_SIZE(endorsement_size);
156 4 : case OP_HBT_GET_MESSAGE:
157 4 : check_state(heartbeat_ctx, STATE_HEARTBEAT_READY);
158 :
159 8 : SAFE_MEMMOVE(APDU_DATA_PTR,
160 : APDU_TOTAL_DATA_SIZE,
161 : MEMMOVE_ZERO_OFFSET,
162 : heartbeat_ctx->msg,
163 : sizeof(heartbeat_ctx->msg),
164 : MEMMOVE_ZERO_OFFSET,
165 : heartbeat_ctx->msg_offset,
166 : THROW(ERR_HBT_INTERNAL));
167 :
168 4 : return TX_FOR_DATA_SIZE(heartbeat_ctx->msg_offset);
169 4 : case OP_HBT_APP_HASH:
170 4 : out_size = (uint8_t)MIN(APDU_TOTAL_DATA_SIZE_OUT, 0xFF);
171 4 : if (!endorsement_get_code_hash(APDU_DATA_PTR, &out_size)) {
172 0 : THROW(ERR_HBT_INTERNAL);
173 : }
174 4 : return TX_FOR_DATA_SIZE(out_size);
175 4 : case OP_HBT_PUBKEY:
176 4 : out_size = (uint8_t)MIN(APDU_TOTAL_DATA_SIZE_OUT, 0xFF);
177 4 : if (!endorsement_get_public_key(APDU_DATA_PTR, &out_size)) {
178 0 : THROW(ERR_HBT_INTERNAL);
179 : }
180 4 : return TX_FOR_DATA_SIZE(out_size);
181 0 : default:
182 0 : reset_heartbeat(heartbeat_ctx);
183 0 : THROW(ERR_HBT_PROT_INVALID);
184 : break;
185 : }
186 : return 0;
187 : }
|