1 #include "hash.h"
2
3 #include <errno.h>
4 #include <openssl/evp.h>
5 #include <sys/stat.h>
6 #include <sys/types.h> /* For blksize_t */
7
8 #include "common.h"
9 #include "file.h"
10 #include "log.h"
11 #include "asn1/oid.h"
12
13 static int
get_md(char const * algorithm,EVP_MD const ** result)14 get_md(char const *algorithm, EVP_MD const **result)
15 {
16 EVP_MD const *md;
17
18 md = EVP_get_digestbyname(algorithm);
19 if (md == NULL) {
20 pr_val_err("Unknown message digest %s", algorithm);
21 return -EINVAL;
22 }
23
24 *result = md;
25 return 0;
26 }
27
28 static bool
hash_matches(unsigned char const * expected,size_t expected_len,unsigned char const * actual,unsigned int actual_len)29 hash_matches(unsigned char const *expected, size_t expected_len,
30 unsigned char const *actual, unsigned int actual_len)
31 {
32 return (expected_len == actual_len)
33 && (memcmp(expected, actual, expected_len) == 0);
34 }
35
36 static int
hash_file(char const * algorithm,struct rpki_uri * uri,unsigned char * result,unsigned int * result_len)37 hash_file(char const *algorithm, struct rpki_uri *uri, unsigned char *result,
38 unsigned int *result_len)
39 {
40 return hash_local_file(algorithm, uri_get_local(uri), result,
41 result_len);
42 }
43
44 int
hash_local_file(char const * algorithm,char const * uri,unsigned char * result,unsigned int * result_len)45 hash_local_file(char const *algorithm, char const *uri, unsigned char *result,
46 unsigned int *result_len)
47 {
48 EVP_MD const *md;
49 FILE *file;
50 struct stat stat;
51 unsigned char *buffer;
52 blksize_t buffer_len;
53 size_t consumed;
54 EVP_MD_CTX *ctx;
55 int error;
56
57 error = get_md(algorithm, &md);
58 if (error)
59 return error;
60
61 error = file_open(uri, &file, &stat);
62 if (error)
63 return error;
64
65 buffer_len = stat.st_blksize;
66 buffer = malloc(buffer_len);
67 if (buffer == NULL) {
68 error = pr_enomem();
69 goto end1;
70 }
71
72 ctx = EVP_MD_CTX_new();
73 if (ctx == NULL) {
74 error = pr_enomem();
75 goto end2;
76 }
77
78 if (!EVP_DigestInit_ex(ctx, md, NULL)) {
79 error = val_crypto_err("EVP_DigestInit_ex() failed");
80 goto end3;
81 }
82
83 do {
84 consumed = fread(buffer, 1, buffer_len, file);
85 error = ferror(file);
86 if (error) {
87 pr_val_errno(error,
88 "File reading error. Error message (apparently)");
89 goto end3;
90 }
91
92 if (!EVP_DigestUpdate(ctx, buffer, consumed)) {
93 error = val_crypto_err("EVP_DigestUpdate() failed");
94 goto end3;
95 }
96
97 } while (!feof(file));
98
99 if (!EVP_DigestFinal_ex(ctx, result, result_len))
100 error = val_crypto_err("EVP_DigestFinal_ex() failed");
101
102 end3:
103 EVP_MD_CTX_free(ctx);
104 end2:
105 free(buffer);
106 end1:
107 file_close(file);
108 return error;
109 }
110
111 /**
112 * Computes the hash of the file @uri, and compares it to @expected (The
113 * "expected" hash).
114 *
115 * Returns:
116 * 0 if no errors happened and the hashes match, or the hash doesn't match
117 * but there's an incidence to ignore such error.
118 * < 0 if there was an error that can't be ignored.
119 * > 0 if there was an error but it can be ignored (file not found and there's
120 * an incidence to ignore this).
121 */
122 int
hash_validate_mft_file(char const * algorithm,struct rpki_uri * uri,BIT_STRING_t const * expected)123 hash_validate_mft_file(char const *algorithm, struct rpki_uri *uri,
124 BIT_STRING_t const *expected)
125 {
126 unsigned char actual[EVP_MAX_MD_SIZE];
127 unsigned int actual_len;
128 int error;
129
130 if (expected->bits_unused != 0)
131 return pr_val_err("Hash string has unused bits.");
132
133 do {
134 error = hash_file(algorithm, uri, actual, &actual_len);
135 if (!error)
136 break;
137
138 if (error == EACCES || error == ENOENT) {
139 if (incidence(INID_MFT_FILE_NOT_FOUND,
140 "File '%s' listed at manifest doesn't exist.",
141 uri_val_get_printable(uri)))
142 return -EINVAL;
143
144 return error;
145 }
146 /* Any other error (crypto, enomem, file read) */
147 return ENSURE_NEGATIVE(error);
148 } while (0);
149
150 if (!hash_matches(expected->buf, expected->size, actual, actual_len)) {
151 return incidence(INID_MFT_FILE_HASH_NOT_MATCH,
152 "File '%s' does not match its manifest hash.",
153 uri_val_get_printable(uri));
154 }
155
156 return 0;
157 }
158
159 /**
160 * Computes the hash of the file @uri, and compares it to @expected HASH of
161 * @expected_len. Returns 0 if no errors happened and the hashes match.
162 */
163 int
hash_validate_file(char const * algorithm,struct rpki_uri * uri,unsigned char const * expected,size_t expected_len)164 hash_validate_file(char const *algorithm, struct rpki_uri *uri,
165 unsigned char const *expected, size_t expected_len)
166 {
167 unsigned char actual[EVP_MAX_MD_SIZE];
168 unsigned int actual_len;
169 int error;
170
171 error = hash_file(algorithm, uri, actual, &actual_len);
172 if (error)
173 return error;
174
175 if (!hash_matches(expected, expected_len, actual, actual_len)) {
176 return pr_val_err("File '%s' does not match its expected hash.",
177 uri_val_get_printable(uri));
178 }
179
180 return 0;
181 }
182
183 static int
hash_buffer(char const * algorithm,unsigned char const * content,size_t content_len,unsigned char * hash,unsigned int * hash_len)184 hash_buffer(char const *algorithm,
185 unsigned char const *content, size_t content_len,
186 unsigned char *hash, unsigned int *hash_len)
187 {
188 EVP_MD const *md;
189 EVP_MD_CTX *ctx;
190 int error = 0;
191
192 error = get_md(algorithm, &md);
193 if (error)
194 return error;
195
196 ctx = EVP_MD_CTX_new();
197 if (ctx == NULL)
198 return pr_enomem();
199
200 if (!EVP_DigestInit_ex(ctx, md, NULL)
201 || !EVP_DigestUpdate(ctx, content, content_len)
202 || !EVP_DigestFinal_ex(ctx, hash, hash_len)) {
203 error = val_crypto_err("Buffer hashing failed");
204 }
205
206 EVP_MD_CTX_free(ctx);
207 return error;
208 }
209
210 /*
211 * Returns 0 if @data's hash is @expected. Returns error code otherwise.
212 */
213 int
hash_validate(char const * algorithm,unsigned char const * expected,size_t expected_len,unsigned char const * data,size_t data_len)214 hash_validate(char const *algorithm,
215 unsigned char const *expected, size_t expected_len,
216 unsigned char const *data, size_t data_len)
217 {
218 unsigned char actual[EVP_MAX_MD_SIZE];
219 unsigned int actual_len;
220 int error;
221
222 error = hash_buffer(algorithm, data, data_len, actual, &actual_len);
223 if (error)
224 return error;
225
226 return hash_matches(expected, expected_len, actual, actual_len)
227 ? 0
228 : -EINVAL;
229 }
230
231 int
hash_validate_octet_string(char const * algorithm,OCTET_STRING_t const * expected,OCTET_STRING_t const * data)232 hash_validate_octet_string(char const *algorithm,
233 OCTET_STRING_t const *expected,
234 OCTET_STRING_t const *data)
235 {
236 return hash_validate(algorithm, expected->buf, expected->size,
237 data->buf, data->size);
238 }
239
240 /*
241 * Hash the @str using the specified @algorithm, setting the result at the
242 * @result buffer and its length at @result_len.
243 *
244 * Return 0 on success, any other value means error.
245 */
246 int
hash_str(char const * algorithm,char const * str,unsigned char * result,unsigned int * result_len)247 hash_str(char const *algorithm, char const *str, unsigned char *result,
248 unsigned int *result_len)
249 {
250 unsigned char *src;
251 int error;
252
253 src = malloc(strlen(str));
254 if (src == NULL)
255 return pr_enomem();
256
257 memcpy(src, str, strlen(str));
258
259 error = hash_buffer(algorithm, src, strlen(str), result, result_len);
260
261 free(src);
262 return error;
263 }
264