Moved hashing functionalities to pts object
[strongswan.git] / src / libimcv / tcg / pts / pts.c
1 /*
2 * Copyright (C) 2011 Sansar Choinyambuu
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "pts.h"
17
18 #include <debug.h>
19 #include <crypto/hashers/hasher.h>
20
21 #include <trousers/tss.h>
22 #include <trousers/trousers.h>
23
24 #include <dirent.h>
25 #include <errno.h>
26
27 #define PTS_BUF_SIZE 32768
28
29 typedef struct private_pts_t private_pts_t;
30
31 /**
32 * Private data of a pts_t object.
33 *
34 */
35 struct private_pts_t {
36
37 /**
38 * Public pts_t interface.
39 */
40 pts_t public;
41
42 /**
43 * PTS Protocol Capabilities
44 */
45 pts_proto_caps_flag_t proto_caps;
46
47 /**
48 * PTS Measurement Algorithm
49 */
50 pts_meas_algorithms_t algorithm;
51
52 /**
53 * Do we have an activated TPM
54 */
55 bool has_tpm;
56
57 /**
58 * Contains a TPM_CAP_VERSION_INFO struct
59 */
60 chunk_t tpm_version_info;
61
62 };
63
64 METHOD(pts_t, get_proto_caps, pts_proto_caps_flag_t,
65 private_pts_t *this)
66 {
67 return this->proto_caps;
68 }
69
70 METHOD(pts_t, set_proto_caps, void,
71 private_pts_t *this, pts_proto_caps_flag_t flags)
72 {
73 this->proto_caps = flags;
74 DBG2(DBG_TNC, "supported PTS protocol capabilities: %s%s%s%s%s",
75 flags & PTS_PROTO_CAPS_C ? "C" : ".",
76 flags & PTS_PROTO_CAPS_V ? "V" : ".",
77 flags & PTS_PROTO_CAPS_D ? "D" : ".",
78 flags & PTS_PROTO_CAPS_T ? "T" : ".",
79 flags & PTS_PROTO_CAPS_X ? "X" : ".");
80 }
81
82 METHOD(pts_t, get_meas_algorithm, pts_meas_algorithms_t,
83 private_pts_t *this)
84 {
85 return this->algorithm;
86 }
87
88 METHOD(pts_t, set_meas_algorithm, void,
89 private_pts_t *this, pts_meas_algorithms_t algorithm)
90 {
91 hash_algorithm_t hash_alg;
92
93 hash_alg = pts_meas_to_hash_algorithm(algorithm);
94 DBG2(DBG_TNC, "selected PTS measurement algorithm is %N",
95 hash_algorithm_names, hash_alg);
96 if (hash_alg != HASH_UNKNOWN)
97 {
98 this->algorithm = algorithm;
99 }
100 }
101
102 /**
103 * Print TPM 1.2 Version Info
104 */
105 static void print_tpm_version_info(private_pts_t *this)
106 {
107 TPM_CAP_VERSION_INFO versionInfo;
108 UINT64 offset = 0;
109 TSS_RESULT result;
110
111 result = Trspi_UnloadBlob_CAP_VERSION_INFO(&offset,
112 this->tpm_version_info.ptr, &versionInfo);
113 if (result != TSS_SUCCESS)
114 {
115 DBG1(DBG_TNC, "could not parse tpm version info: tss error 0x%x",
116 result);
117 }
118 else
119 {
120 DBG2(DBG_TNC, "TPM 1.2 Version Info: Chip Version: %hhu.%hhu.%hhu.%hhu,"
121 " Spec Level: %hu, Errata Rev: %hhu, Vendor ID: %.4s",
122 versionInfo.version.major, versionInfo.version.minor,
123 versionInfo.version.revMajor, versionInfo.version.revMinor,
124 versionInfo.specLevel, versionInfo.errataRev,
125 versionInfo.tpmVendorID);
126 }
127 }
128
129 METHOD(pts_t, get_tpm_version_info, bool,
130 private_pts_t *this, chunk_t *info)
131 {
132 if (!this->has_tpm)
133 {
134 return FALSE;
135 }
136 *info = this->tpm_version_info;
137 print_tpm_version_info(this);
138 return TRUE;
139 }
140
141 METHOD(pts_t, set_tpm_version_info, void,
142 private_pts_t *this, chunk_t info)
143 {
144 this->tpm_version_info = chunk_clone(info);
145 print_tpm_version_info(this);
146 }
147
148
149 /**
150 * Get Hash Measurement of a file
151 */
152
153 METHOD(pts_t, hash_file, bool,
154 private_pts_t *this, char *path, char *out)
155 {
156 char buffer[PTS_BUF_SIZE];
157 FILE *file;
158 int bytes_read;
159 hasher_t *hasher;
160 hash_algorithm_t hash_alg;
161
162 /* Create a hasher */
163 hash_alg = pts_meas_to_hash_algorithm(this->algorithm);
164 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
165 if (!hasher)
166 {
167 DBG1(DBG_IMC, "hasher %N not available", hash_algorithm_names, hash_alg);
168 return false;
169 }
170
171 file = fopen(path, "rb");
172 if (!file)
173 {
174 DBG1(DBG_IMC,"file '%s' can not be opened", path);
175 hasher->destroy(hasher);
176 return false;
177 }
178 while (TRUE)
179 {
180 bytes_read = fread(buffer, 1, sizeof(buffer), file);
181 if (bytes_read > 0)
182 {
183 hasher->get_hash(hasher, chunk_create(buffer, bytes_read), NULL);
184 }
185 else
186 {
187 hasher->get_hash(hasher, chunk_empty, out);
188 break;
189 }
190 }
191 fclose(file);
192 hasher->destroy(hasher);
193
194 return true;
195 }
196
197 /**
198 * Get hash of all the files in a directory
199 */
200
201 METHOD(pts_t, hash_directory, bool,
202 private_pts_t *this, char *path, linked_list_t *file_measurements)
203 {
204 DIR *dir;
205 struct dirent *ent;
206 file_meas_entry_t *entry;
207
208 file_measurements = linked_list_create();
209 entry = malloc_thing(file_meas_entry_t);
210
211 dir = opendir(path);
212 if (dir == NULL)
213 {
214 DBG1(DBG_IMC, "opening directory '%s' failed: %s", path, strerror(errno));
215 return false;
216 }
217 while ((ent = readdir(dir)))
218 {
219 char *file_hash;
220
221 if(this->public.hash_file(&this->public,ent->d_name,file_hash) != true)
222 {
223 DBG1(DBG_IMC, "Hashing the given file has failed");
224 return false;
225 }
226
227 entry->measurement = chunk_create(file_hash,strlen(file_hash));
228 entry->file_name_len = strlen(ent->d_name);
229 entry->file_name = chunk_create(ent->d_name,strlen(ent->d_name));
230
231 file_measurements->insert_last(file_measurements,entry);
232 }
233 closedir(dir);
234
235 return true;
236 }
237
238 METHOD(pts_t, destroy, void,
239 private_pts_t *this)
240 {
241 free(this->tpm_version_info.ptr);
242 free(this);
243 }
244
245 /**
246 * Check for a TPM by querying for TPM Version Info
247 */
248 static bool has_tpm(private_pts_t *this)
249 {
250 TSS_HCONTEXT hContext;
251 TSS_HTPM hTPM;
252 TSS_RESULT result;
253
254 result = Tspi_Context_Create(&hContext);
255 if (result != TSS_SUCCESS)
256 {
257 goto err;
258 }
259 result = Tspi_Context_Connect(hContext, NULL);
260 if (result != TSS_SUCCESS)
261 {
262 goto err;
263 }
264 result = Tspi_Context_GetTpmObject (hContext, &hTPM);
265 if (result != TSS_SUCCESS)
266 {
267 goto err;
268 }
269 result = Tspi_TPM_GetCapability(hTPM, TSS_TPMCAP_VERSION_VAL, 0, NULL,
270 &this->tpm_version_info.len,
271 &this->tpm_version_info.ptr);
272 if (result != TSS_SUCCESS)
273 {
274 goto err;
275 }
276 this->tpm_version_info = chunk_clone(this->tpm_version_info);
277 return TRUE;
278
279 err:
280 DBG1(DBG_TNC, "TPM not available: tss error 0x%x", result);
281 return FALSE;
282 }
283
284 /**
285 * See header
286 */
287 pts_t *pts_create(bool is_imc)
288 {
289 private_pts_t *this;
290
291 INIT(this,
292 .public = {
293 .get_proto_caps = _get_proto_caps,
294 .set_proto_caps = _set_proto_caps,
295 .get_meas_algorithm = _get_meas_algorithm,
296 .set_meas_algorithm = _set_meas_algorithm,
297 .get_tpm_version_info = _get_tpm_version_info,
298 .set_tpm_version_info = _set_tpm_version_info,
299 .hash_file = _hash_file,
300 .hash_directory = _hash_directory,
301 .destroy = _destroy,
302 },
303 .proto_caps = PTS_PROTO_CAPS_V,
304 .algorithm = PTS_MEAS_ALGO_SHA256,
305 );
306
307 if (is_imc)
308 {
309 if (has_tpm(this))
310 {
311 this->has_tpm = TRUE;
312 this->proto_caps |= PTS_PROTO_CAPS_T;
313 }
314 }
315 else
316 {
317 this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_C;
318 }
319
320 return &this->public;
321 }
322