-
Notifications
You must be signed in to change notification settings - Fork 34
/
hss_reserve.c
198 lines (180 loc) · 7.22 KB
/
hss_reserve.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#include <string.h>
#include "common_defs.h"
#include "hss_internal.h"
#include "hss_reserve.h"
#include "endian.h"
/*
* Initialize the reservation count to the given value
*/
void hss_set_reserve_count(struct hss_working_key *w, sequence_t count) {
w->reserve_count = count;
}
/*
* Set the autoreserve count
*/
bool hss_set_autoreserve(struct hss_working_key *w,
unsigned sigs_to_autoreserve, struct hss_extra_info *info) {
if (!w) {
if (info) info->error_code = hss_error_got_null;
return false;
}
/* Note: we do not check if the working key is in a usable state */
/* There are a couple of odd-ball scenarios (e.g. when they've */
/* manually allocated the key, but haven't loaded it yet) that we */
/* don't have a good reason to disallow */
w->autoreserve = sigs_to_autoreserve;
return true;
}
/*
* This is called when we generate a signature; it checks if we need
* to write out a new private key (and advance the reservation); if it
* decides it needs to write out a new private key, it also decides how
* far it needs to advance it
*/
bool hss_advance_count(struct hss_working_key *w, sequence_t cur_count,
bool (*update_private_key)(unsigned char *private_key,
size_t len_private_key, void *context),
void *context,
struct hss_extra_info *info, bool *trash_private_key) {
if (cur_count == w->max_count) {
/* We hit the end of the root; this will be the last signature */
/* this private key can do */
w->status = hss_error_private_key_expired; /* Fail if they try to */
/* sign any more */
info->last_signature = true;
/* Make sure we zeroize the private key */
*trash_private_key = true; /* We can't trash our copy of the */
/* private key until after we've generated the signature */
/* We can trash the copy in secure storage, though */
if (update_private_key) {
unsigned char private_key[PRIVATE_KEY_LEN];
memset( private_key, PARM_SET_END, PRIVATE_KEY_LEN );
if (!update_private_key(private_key, PRIVATE_KEY_LEN, context)) {
info->error_code = hss_error_private_key_write_failed;
return false;
}
} else {
memset( context, PARM_SET_END, PRIVATE_KEY_LEN );
}
return true;
}
sequence_t new_count = cur_count + 1;
if (new_count > w->reserve_count) {
/* We need to advance the reservation */
/* Check if we have enough space to do the entire autoreservation */
if (w->max_count - new_count > w->autoreserve) {
new_count += w->autoreserve;
} else {
/* If we don't have enough space, reserve what we can */
new_count = w->max_count;
}
put_bigendian( w->private_key + PRIVATE_KEY_INDEX, new_count,
PRIVATE_KEY_INDEX_LEN );
if (update_private_key) {
if (!update_private_key(w->private_key, PRIVATE_KEY_INDEX_LEN,
context)) {
/* Oops, we couldn't write the private key; undo the */
/* reservation advance (and return an error) */
info->error_code = hss_error_private_key_write_failed;
put_bigendian( w->private_key + PRIVATE_KEY_INDEX,
w->reserve_count, PRIVATE_KEY_INDEX_LEN );
return false;
}
} else {
put_bigendian( context, new_count, PRIVATE_KEY_INDEX_LEN );
}
w->reserve_count = new_count;
}
return true;
}
/*
* This will make sure that (at least) N signatures are reserved; that is, we
* won't need to actually call the update function for the next N signatures
* generated.
* This also has the effect of minimizing the changes of a signature generation
* failure will occur (possibly because of an update failure) in the next
* N signatures.
*
* This can be useful if the update_private_key function is expensive.
*
* Note that if, N (or more) signatures are already reserved, this won't do
* anything.
*/
bool hss_reserve_signature(
struct hss_working_key *w,
bool (*update_private_key)(unsigned char *private_key,
size_t len_private_key, void *context),
void *context,
unsigned sigs_to_reserve,
struct hss_extra_info *info) {
struct hss_extra_info temp_info = { 0 };
if (!info) info = &temp_info;
if (!w) {
info->error_code = hss_error_got_null;
return false;
}
if (w->status != hss_error_none) {
info->error_code = w->status;
return false;
}
if (sigs_to_reserve > w->max_count) {
info->error_code = hss_error_not_that_many_sigs_left;
return false; /* Very funny */
}
/*
* If we're given a raw private key, make sure it's the one we're
* thinking of.
* I have no idea why someone would reserve signatures if they have
* a raw private key (which is cheap to update), however there's no
* reason we shouldn't support it
*/
if (!update_private_key) {
if (0 != memcmp( context, w->private_key, PRIVATE_KEY_LEN)) {
info->error_code = hss_error_key_mismatch;
return false; /* Private key mismatch */
}
}
/* Figure out what the current count is */
sequence_t current_count = 0;
int i;
for (i = 0; i<w->levels; i++) {
struct merkle_level *tree = w->tree[i];
/* -1 because the current_index counts the signatures to the */
/* current next level */
current_count = (current_count << tree->level) +
tree->current_index - 1;
}
current_count += 1; /* The bottom-most tree isn't advanced */
sequence_t new_reserve_count; /* This is what the new reservation */
/* setting would be (if we accept the reservation) */
if (current_count > w->max_count - sigs_to_reserve) {
/* Not that many sigantures left */
/* We can't promise to be able to generate that many signatures */
info->error_code = hss_error_not_that_many_sigs_left;
return false;
} else {
new_reserve_count = current_count + sigs_to_reserve;
}
if (new_reserve_count <= w->reserve_count) {
/* We already have (at least) that many reserved; do nothing */
return true;
}
/* Attempt to update the count in the private key */
put_bigendian( w->private_key + PRIVATE_KEY_INDEX, new_reserve_count,
PRIVATE_KEY_INDEX_LEN );
/* Update the copy in NV storage */
if (update_private_key) {
if (!update_private_key(w->private_key, PRIVATE_KEY_INDEX_LEN,
context)) {
/* Oops, couldn't update it */
put_bigendian( w->private_key + PRIVATE_KEY_INDEX,
w->reserve_count, PRIVATE_KEY_INDEX_LEN );
info->error_code = hss_error_private_key_write_failed;
return false;
}
} else {
memcpy( context, w->private_key, PRIVATE_KEY_INDEX_LEN );
}
w->reserve_count = new_reserve_count;
return true;
}