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