forked from cisco/hash-sigs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hss_param.c
153 lines (137 loc) · 5.44 KB
/
hss_param.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
#include <string.h>
#include "hss.h"
#include "hss_internal.h"
#include "endian.h"
#include "hss_zeroize.h"
/*
* Convert a parameter set into the compressed version we use within a private
* key. This is the private key that'll end up being updated constantly, and
* so we try to make it as small as possible
*/
bool hss_compress_param_set( unsigned char *compressed,
int levels,
const param_set_t *lm_type,
const param_set_t *lm_ots_type,
size_t len_compressed ) {
int i;
for (i=0; i<levels; i++) {
if (len_compressed == 0) return false;
param_set_t a = *lm_type++;
param_set_t b = *lm_ots_type++;
/* All the parameter sets we support are small */
/* Review this format if we need to support larger ones */
if (a > 0x0e || b > 0x0e) return false;
/* Make sure the parm sets are supported */
switch (a) {
case LMS_SHA256_N32_H5: case LMS_SHA256_N32_H10:
case LMS_SHA256_N32_H15: case LMS_SHA256_N32_H20:
case LMS_SHA256_N32_H25:
break;
default:
return false;
}
switch (b) {
case LMOTS_SHA256_N32_W1: case LMOTS_SHA256_N32_W2:
case LMOTS_SHA256_N32_W4: case LMOTS_SHA256_N32_W8:
break;
default:
return false;
}
*compressed++ = (a<<4) + b;
len_compressed--;
}
while (len_compressed) {
*compressed++ = PARM_SET_END;
len_compressed--;
}
return true;
}
/*
* This returns the parameter set for a given private key.
* This is here to solve a chicken-and-egg problem: the hss_working_key
* must be initialized to the same parameter set as the private key,
* but (other than this function, or somehow remembering it) there's
* no way to retreive the parameter set.
*
* read_private_key/context will read the private key (if read_private_key is
* NULL, context is assumed to point to the private key)
*
* On success, *levels will be set to the number of levels, and lm_type[]
* and lm_ots_type[] will be set to the lm/ots parameter sets
*
* On success, this returns true; on failure (can't read the private key, or
* the private key is invalid), returns false
*/
bool hss_get_parameter_set( unsigned *levels,
param_set_t lm_type[ MAX_HSS_LEVELS ],
param_set_t lm_ots_type[ MAX_HSS_LEVELS ],
bool (*read_private_key)(unsigned char *private_key,
size_t len_private_key, void *context),
void *context) {
unsigned char private_key[ PRIVATE_KEY_LEN ];
bool success = false;
if (read_private_key) {
if (!read_private_key( private_key, PRIVATE_KEY_SEED, context )) {
goto failed;
}
} else {
if (!context) return false;
memcpy( private_key, context, PRIVATE_KEY_SEED );
}
/* Scan through the private key to recover the parameter sets */
unsigned total_height = 0;
unsigned level;
for (level=0; level < MAX_HSS_LEVELS; level++) {
unsigned char c = private_key[PRIVATE_KEY_PARAM_SET + level];
if (c == PARM_SET_END) break;
/* Decode this level's parameter set */
param_set_t lm = (c >> 4);
param_set_t ots = (c & 0x0f);
/* Make sure both are supported */
/* While we're here, add up the total Merkle height */
switch (lm) {
case LMS_SHA256_N32_H5: total_height += 5; break;
case LMS_SHA256_N32_H10: total_height += 10; break;
case LMS_SHA256_N32_H15: total_height += 15; break;
case LMS_SHA256_N32_H20: total_height += 20; break;
case LMS_SHA256_N32_H25: total_height += 25; break;
default: goto failed;
}
switch (ots) {
case LMOTS_SHA256_N32_W1:
case LMOTS_SHA256_N32_W2:
case LMOTS_SHA256_N32_W4:
case LMOTS_SHA256_N32_W8:
break;
default: goto failed;
}
lm_type[level] = lm;
lm_ots_type[level] = ots;
}
if (level < MIN_HSS_LEVELS || level > MAX_HSS_LEVELS) goto failed;
*levels = level;
/* Make sure that the rest of the private key has PARM_SET_END */
unsigned i;
for (i = level+1; i<MAX_HSS_LEVELS; i++) {
unsigned char c = private_key[PRIVATE_KEY_PARAM_SET + i];
if (c != PARM_SET_END) goto failed;
}
/* One final check; make sure that the sequence number listed in the */
/* private key is in range */
if (total_height > 64) total_height = 64; /* (bounded by 2**64) */
sequence_t max_count = ((sequence_t)2 << (total_height-1)) - 1;
/* height-1 so we don't try to shift by 64, and hit U.B. */
/* We use the count 0xffff..ffff to signify 'we've used up all our */
/* signatures'. Make sure that is above max_count, even for */
/* parameter sets that can literally generate 2**64 signatures (by */
/* letting them generate only 2**64-1) */
if (total_height == 64) max_count--;
sequence_t current_count = get_bigendian(
private_key + PRIVATE_KEY_INDEX, PRIVATE_KEY_INDEX_LEN );
if (current_count > max_count) goto failed; /* Private key expired */
success = true; /* It worked! */
failed:
/* There might be private keying material here */
hss_zeroize( private_key, sizeof private_key );
return success;
}