2 * Copyright (C) 2011 Sansar Choinyambuu
3 * HSR Hochschule fuer Technik Rapperswil
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>.
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
16 #include "imv_attestation_state.h"
18 #include <imv/imv_agent.h>
19 #include <pa_tnc/pa_tnc_msg.h>
20 #include <ietf/ietf_attr.h>
21 #include <ietf/ietf_attr_pa_tnc_error.h>
23 #include <tcg/pts/pts_database.h>
25 #include <tcg/tcg_attr.h>
26 #include <tcg/tcg_pts_attr_proto_caps.h>
27 #include <tcg/tcg_pts_attr_meas_algo.h>
28 #include <tcg/tcg_pts_attr_get_tpm_version_info.h>
29 #include <tcg/tcg_pts_attr_tpm_version_info.h>
30 #include <tcg/tcg_pts_attr_get_aik.h>
31 #include <tcg/tcg_pts_attr_aik.h>
32 #include <tcg/tcg_pts_attr_req_funct_comp_evid.h>
33 #include <tcg/tcg_pts_attr_gen_attest_evid.h>
34 #include <tcg/tcg_pts_attr_simple_comp_evid.h>
35 #include <tcg/tcg_pts_attr_simple_evid_final.h>
36 #include <tcg/tcg_pts_attr_req_file_meas.h>
37 #include <tcg/tcg_pts_attr_file_meas.h>
39 #include <tncif_pa_subtypes.h>
43 #include <utils/linked_list.h>
47 static const char imv_name
[] = "Attestation";
49 #define IMV_VENDOR_ID PEN_TCG
50 #define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS
53 * UTF-8 encoding of the character used to delimiter the filename
55 #define SOLIDUS_UTF 0x002F
56 #define REVERSE_SOLIDUS_UTF 0x005C
58 static imv_agent_t
*imv_attestation
;
61 * Supported PTS measurement algorithms
63 static pts_meas_algorithms_t supported_algorithms
= 0;
66 * PTS file measurement database
68 static pts_database_t
*pts_db
;
71 * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2
73 TNC_Result
TNC_IMV_Initialize(TNC_IMVID imv_id
,
74 TNC_Version min_version
,
75 TNC_Version max_version
,
76 TNC_Version
*actual_version
)
82 DBG1(DBG_IMV
, "IMV \"%s\" has already been initialized", imv_name
);
83 return TNC_RESULT_ALREADY_INITIALIZED
;
85 imv_attestation
= imv_agent_create(imv_name
, IMV_VENDOR_ID
, IMV_SUBTYPE
,
86 imv_id
, actual_version
);
87 if (!imv_attestation
|| !pts_meas_probe_algorithms(&supported_algorithms
))
89 return TNC_RESULT_FATAL
;
91 if (min_version
> TNC_IFIMV_VERSION_1
|| max_version
< TNC_IFIMV_VERSION_1
)
93 DBG1(DBG_IMV
, "no common IF-IMV version");
94 return TNC_RESULT_NO_COMMON_VERSION
;
98 * Specify supported PTS measurement algorithms
100 * sha1 : PTS_MEAS_ALGO_SHA1
101 * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256
102 * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384
104 * we expect the PTS-IMC to select the strongest supported algorithm
106 hash_alg
= lib
->settings
->get_str(lib
->settings
,
107 "libimcv.plugins.imv-attestation.hash_algorithm", "sha256");
108 if (!strcaseeq(hash_alg
, "sha384") && !strcaseeq(hash_alg
, "sha2_384"))
110 /* remove SHA384 algorithm */
111 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA384
;
113 if (strcaseeq(hash_alg
, "sha1"))
115 /* remove SHA256 algorithm */
116 supported_algorithms
&= ~PTS_MEAS_ALGO_SHA256
;
119 /* attach file measurement database */
120 uri
= lib
->settings
->get_str(lib
->settings
,
121 "libimcv.plugins.imv-attestation.database", NULL
);
122 pts_db
= pts_database_create(uri
);
124 return TNC_RESULT_SUCCESS
;
128 * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2
130 TNC_Result
TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id
,
131 TNC_ConnectionID connection_id
,
132 TNC_ConnectionState new_state
)
135 imv_attestation_state_t
*attestation_state
;
138 if (!imv_attestation
)
140 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
141 return TNC_RESULT_NOT_INITIALIZED
;
145 case TNC_CONNECTION_STATE_CREATE
:
146 state
= imv_attestation_state_create(connection_id
);
147 return imv_attestation
->create_state(imv_attestation
, state
);
148 case TNC_CONNECTION_STATE_DELETE
:
149 return imv_attestation
->delete_state(imv_attestation
, connection_id
);
150 case TNC_CONNECTION_STATE_HANDSHAKE
:
151 result
= imv_attestation
->change_state(imv_attestation
, connection_id
,
153 if (result
!= TNC_RESULT_SUCCESS
)
157 attestation_state
= (imv_attestation_state_t
*)state
;
159 /* TODO: Get some configurations */
161 return TNC_RESULT_SUCCESS
;
163 return imv_attestation
->change_state(imv_attestation
, connection_id
,
168 static TNC_Result
send_message(TNC_ConnectionID connection_id
)
174 imv_attestation_state_t
*attestation_state
;
175 imv_attestation_handshake_state_t handshake_state
;
177 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
179 return TNC_RESULT_FATAL
;
181 attestation_state
= (imv_attestation_state_t
*)state
;
182 handshake_state
= attestation_state
->get_handshake_state(attestation_state
);
183 pts
= attestation_state
->get_pts(attestation_state
);
185 msg
= pa_tnc_msg_create();
188 /* Switch on the attribute type IMV has received */
189 switch (handshake_state
)
191 case IMV_ATTESTATION_STATE_INIT
:
193 pa_tnc_attr_t
*attr_req_proto_cap
, *attr_meas_algo
;
194 pts_proto_caps_flag_t flags
;
196 /* Send Request Protocol Capabilities attribute */
197 flags
= pts
->get_proto_caps(pts
);
198 attr_req_proto_cap
= tcg_pts_attr_proto_caps_create(flags
, TRUE
);
199 attr_req_proto_cap
->set_noskip_flag(attr_req_proto_cap
, TRUE
);
200 msg
->add_attribute(msg
, attr_req_proto_cap
);
202 /* Send Measurement Algorithms attribute */
203 attr_meas_algo
= tcg_pts_attr_meas_algo_create(supported_algorithms
, FALSE
);
204 attr_meas_algo
->set_noskip_flag(attr_meas_algo
, TRUE
);
205 msg
->add_attribute(msg
, attr_meas_algo
);
209 case IMV_ATTESTATION_STATE_MEAS
:
211 pa_tnc_attr_t
*attr_req_file_meas
;
212 enumerator_t
*enumerator
;
213 pts_meas_algorithms_t communicated_caps
;
214 u_int32_t delimiter
= SOLIDUS_UTF
;
216 char *product
, *path
;
218 /* Send Get TPM Version Information attribute */
219 communicated_caps
= pts
->get_proto_caps(pts
);
220 if (communicated_caps
& PTS_PROTO_CAPS_T
)
222 pa_tnc_attr_t
*attr_get_tpm_version
, *attr_get_aik
;
224 attr_get_tpm_version
= tcg_pts_attr_get_tpm_version_info_create();
225 attr_get_tpm_version
->set_noskip_flag(attr_get_tpm_version
, TRUE
);
226 msg
->add_attribute(msg
, attr_get_tpm_version
);
228 /* Send Get AIK attribute */
229 /* TODO: Uncomment when the retrieving of AIK on IMC side is implemented */
230 //attr_get_aik = tcg_pts_attr_get_aik_create();
231 //attr_get_aik->set_noskip_flag(attr_get_aik, TRUE);
232 //msg->add_attribute(msg, attr_get_aik);
235 /* Send Request File Measurement attribute */
237 * Add files to measure to PTS Request File Measurement attribute
239 product
= "Ubuntu 10.10 x86_64";
245 enumerator
= pts_db
->create_file_enumerator(pts_db
, product
);
250 while (enumerator
->enumerate(enumerator
, &id
, &type
, &path
))
255 DBG2(DBG_IMV
, "id = %d, type = %d, path = '%s'", id
, type
, path
);
257 is_directory
= (type
!= 0) ?
true : false;
258 path_chunk
= chunk_create(path
, strlen(path
));
259 path_chunk
= chunk_clone(path_chunk
);
261 attr_req_file_meas
= tcg_pts_attr_req_file_meas_create(is_directory
,
262 (u_int16_t
)id
, delimiter
, path_chunk
);
263 attr_req_file_meas
->set_noskip_flag(attr_req_file_meas
, TRUE
);
264 msg
->add_attribute(msg
, attr_req_file_meas
);
266 enumerator
->destroy(enumerator
);
270 case IMV_ATTESTATION_STATE_COMP_EVID
:
271 case IMV_ATTESTATION_STATE_IML
:
272 DBG1(DBG_IMV
, "Attestation IMV has nothing to send: \"%s\"", handshake_state
);
273 return TNC_RESULT_FATAL
;
275 DBG1(DBG_IMV
, "Attestation IMV is in unknown state: \"%s\"", handshake_state
);
276 return TNC_RESULT_FATAL
;
280 result
= imv_attestation
->send_message(imv_attestation
, connection_id
,
281 msg
->get_encoding(msg
));
288 * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2
290 TNC_Result
TNC_IMV_ReceiveMessage(TNC_IMVID imv_id
,
291 TNC_ConnectionID connection_id
,
292 TNC_BufferReference msg
,
294 TNC_MessageType msg_type
)
296 pa_tnc_msg_t
*pa_tnc_msg
;
299 imv_attestation_state_t
*attestation_state
;
301 enumerator_t
*enumerator
;
303 bool fatal_error
= FALSE
;
305 if (!imv_attestation
)
307 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
308 return TNC_RESULT_NOT_INITIALIZED
;
311 /* get current IMV state */
312 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
314 return TNC_RESULT_FATAL
;
316 attestation_state
= (imv_attestation_state_t
*)state
;
317 pts
= attestation_state
->get_pts(attestation_state
);
319 /* parse received PA-TNC message and automatically handle any errors */
320 result
= imv_attestation
->receive_message(imv_attestation
, connection_id
,
321 chunk_create(msg
, msg_len
), msg_type
,
324 /* no parsed PA-TNC attributes available if an error occurred */
330 /* analyze PA-TNC attributes */
331 enumerator
= pa_tnc_msg
->create_attribute_enumerator(pa_tnc_msg
);
332 while (enumerator
->enumerate(enumerator
, &attr
))
334 if (attr
->get_vendor_id(attr
) == PEN_IETF
&&
335 attr
->get_type(attr
) == IETF_ATTR_PA_TNC_ERROR
)
337 ietf_attr_pa_tnc_error_t
*error_attr
;
338 pa_tnc_error_code_t error_code
;
339 chunk_t msg_info
, attr_info
;
342 error_attr
= (ietf_attr_pa_tnc_error_t
*)attr
;
343 error_code
= error_attr
->get_error_code(error_attr
);
344 msg_info
= error_attr
->get_msg_info(error_attr
);
346 DBG1(DBG_IMV
, "received PA-TNC error '%N' concerning message %#B",
347 pa_tnc_error_code_names
, error_code
, &msg_info
);
350 case PA_ERROR_INVALID_PARAMETER
:
351 offset
= error_attr
->get_offset(error_attr
);
352 DBG1(DBG_IMV
, " occurred at offset of %u bytes", offset
);
354 case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED
:
355 attr_info
= error_attr
->get_attr_info(error_attr
);
356 DBG1(DBG_IMV
, " unsupported attribute %#B", &attr_info
);
363 else if (attr
->get_vendor_id(attr
) == PEN_TCG
)
365 switch(attr
->get_type(attr
))
367 case TCG_PTS_PROTO_CAPS
:
369 tcg_pts_attr_proto_caps_t
*attr_cast
;
370 pts_proto_caps_flag_t flags
;
372 attr_cast
= (tcg_pts_attr_proto_caps_t
*)attr
;
373 flags
= attr_cast
->get_flags(attr_cast
);
374 pts
->set_proto_caps(pts
, flags
);
376 attestation_state
->set_handshake_state(attestation_state
,
377 IMV_ATTESTATION_STATE_MEAS
);
380 case TCG_PTS_MEAS_ALGO_SELECTION
:
382 tcg_pts_attr_meas_algo_t
*attr_cast
;
383 pts_meas_algorithms_t selected_algorithm
;
385 attr_cast
= (tcg_pts_attr_meas_algo_t
*)attr
;
386 selected_algorithm
= attr_cast
->get_algorithms(attr_cast
);
387 pts
->set_meas_algorithm(pts
, selected_algorithm
);
389 attestation_state
->set_handshake_state(attestation_state
,
390 IMV_ATTESTATION_STATE_MEAS
);
393 case TCG_PTS_TPM_VERSION_INFO
:
395 tcg_pts_attr_tpm_version_info_t
*attr_cast
;
396 chunk_t tpm_version_info
;
398 attr_cast
= (tcg_pts_attr_tpm_version_info_t
*)attr
;
399 tpm_version_info
= attr_cast
->get_tpm_version_info(attr_cast
);
400 pts
->set_tpm_version_info(pts
, tpm_version_info
);
402 attestation_state
->set_handshake_state(attestation_state
,
403 IMV_ATTESTATION_STATE_END
);
408 /* TODO: Save the AIK key and certificate */
409 attestation_state
->set_handshake_state(attestation_state
,
410 IMV_ATTESTATION_STATE_END
);
414 /* PTS-based Attestation Evidence */
415 case TCG_PTS_SIMPLE_COMP_EVID
:
417 case TCG_PTS_SIMPLE_EVID_FINAL
:
419 case TCG_PTS_FILE_MEAS
:
421 tcg_pts_attr_file_meas_t
*attr_cast
;
422 u_int64_t num_of_files
;
423 u_int16_t request_id
;
426 attr_cast
= (tcg_pts_attr_file_meas_t
*)attr
;
427 num_of_files
= attr_cast
->get_number_of_files(attr_cast
);
428 request_id
= attr_cast
->get_request_id(attr_cast
);
429 meas_len
= attr_cast
->get_meas_len(attr_cast
);
431 /* TODO: Start working here */
433 attestation_state
->set_handshake_state(attestation_state
,
434 IMV_ATTESTATION_STATE_END
);
438 /* TODO: Not implemented yet */
439 case TCG_PTS_DH_NONCE_PARAMS_RESP
:
440 case TCG_PTS_UNIX_FILE_META
:
441 case TCG_PTS_INTEG_MEAS_LOG
:
442 /* Attributes using XML */
443 case TCG_PTS_TEMPL_REF_MANI_SET_META
:
444 case TCG_PTS_VERIFICATION_RESULT
:
445 case TCG_PTS_INTEG_REPORT
:
447 case TCG_PTS_WIN_FILE_META
:
448 case TCG_PTS_REGISTRY_VALUE
:
449 /* Received on IMC side only*/
450 case TCG_PTS_REQ_PROTO_CAPS
:
451 case TCG_PTS_DH_NONCE_PARAMS_REQ
:
452 case TCG_PTS_DH_NONCE_FINISH
:
453 case TCG_PTS_MEAS_ALGO
:
454 case TCG_PTS_GET_TPM_VERSION_INFO
:
455 case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META
:
456 case TCG_PTS_UPDATE_TEMPL_REF_MANI
:
457 case TCG_PTS_GET_AIK
:
458 case TCG_PTS_REQ_FUNCT_COMP_EVID
:
459 case TCG_PTS_GEN_ATTEST_EVID
:
460 case TCG_PTS_REQ_FILE_META
:
461 case TCG_PTS_REQ_FILE_MEAS
:
462 case TCG_PTS_REQ_INTEG_MEAS_LOG
:
464 DBG1(DBG_IMV
, "received unsupported attribute '%N'",
465 tcg_attr_names
, attr
->get_type(attr
));
470 enumerator
->destroy(enumerator
);
471 pa_tnc_msg
->destroy(pa_tnc_msg
);
475 state
->set_recommendation(state
,
476 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
477 TNC_IMV_EVALUATION_RESULT_ERROR
);
478 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
480 return send_message(connection_id
);
484 * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2
486 TNC_Result
TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id
,
487 TNC_ConnectionID connection_id
)
489 if (!imv_attestation
)
491 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
492 return TNC_RESULT_NOT_INITIALIZED
;
494 return imv_attestation
->provide_recommendation(imv_attestation
, connection_id
);
498 * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2
500 TNC_Result
TNC_IMV_BatchEnding(TNC_IMVID imv_id
,
501 TNC_ConnectionID connection_id
)
504 imv_attestation_state_t
*attestation_state
;
506 if (!imv_attestation
)
508 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
509 return TNC_RESULT_NOT_INITIALIZED
;
511 /* get current IMV state */
512 if (!imv_attestation
->get_state(imv_attestation
, connection_id
, &state
))
514 return TNC_RESULT_FATAL
;
516 attestation_state
= (imv_attestation_state_t
*)state
;
518 /* Check if IMV has to initiate the PA-TNC exchange */
519 if (attestation_state
->get_handshake_state(attestation_state
) ==
520 IMV_ATTESTATION_STATE_INIT
)
522 return send_message(connection_id
);
524 return TNC_RESULT_SUCCESS
;
528 * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2
530 TNC_Result
TNC_IMV_Terminate(TNC_IMVID imv_id
)
532 if (!imv_attestation
)
534 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
535 return TNC_RESULT_NOT_INITIALIZED
;
538 imv_attestation
->destroy(imv_attestation
);
539 imv_attestation
= NULL
;
541 return TNC_RESULT_SUCCESS
;
545 * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2
547 TNC_Result
TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id
,
548 TNC_TNCS_BindFunctionPointer bind_function
)
550 if (!imv_attestation
)
552 DBG1(DBG_IMV
, "IMV \"%s\" has not been initialized", imv_name
);
553 return TNC_RESULT_NOT_INITIALIZED
;
555 return imv_attestation
->bind_functions(imv_attestation
, bind_function
);