|
1 | 1 | // Copyright 2021 Contributors to the Parsec project. |
2 | 2 | // SPDX-License-Identifier: Apache-2.0 |
3 | | -use crate::Context; |
| 3 | +use crate::{ |
| 4 | + handles::{ObjectHandle, TpmHandle}, |
| 5 | + interface_types::{algorithm::HashingAlgorithm, reserved_handles::Hierarchy}, |
| 6 | + structures::{Auth, Digest, HashcheckTicket, MaxBuffer}, |
| 7 | + tss2_esys::{ |
| 8 | + Esys_HMAC_Start, Esys_HashSequenceStart, Esys_SequenceComplete, Esys_SequenceUpdate, |
| 9 | + }, |
| 10 | + Context, Result, ReturnCode, |
| 11 | +}; |
| 12 | +use log::error; |
| 13 | +use std::ptr::null_mut; |
4 | 14 |
|
5 | 15 | impl Context { |
6 | | - // Missing function: HMAC_Start |
| 16 | + /// Starts HMAC sequence of large data (larger than [`MaxBuffer::MAX_SIZE`]) using the specified algorithm. |
| 17 | + /// |
| 18 | + /// # Details |
| 19 | + /// When the amount of data to be included in a digest cannot be sent to the TPM in one atomic HMAC |
| 20 | + /// command then a sequence of commands may be used to provide incremental updates to the digest. |
| 21 | + /// |
| 22 | + /// Follow the pattern: |
| 23 | + /// - Initialize sequence with [`Context::hmac_sequence_start`] |
| 24 | + /// - Send data to calculate the hash with [`Context::sequence_update`] |
| 25 | + /// - Finish hash calculation with call to [`Context::sequence_complete`] |
| 26 | + /// |
| 27 | + /// # Example |
| 28 | + /// |
| 29 | + /// ```rust |
| 30 | + /// # use tss_esapi::{Context, tcti_ldr::TctiNameConf, |
| 31 | + /// # attributes::{ObjectAttributesBuilder, SessionAttributesBuilder}, |
| 32 | + /// # structures::{ |
| 33 | + /// # Auth, MaxBuffer, Ticket, SymmetricDefinition, |
| 34 | + /// # RsaExponent, RsaScheme, KeyedHashScheme, |
| 35 | + /// # PublicBuilder, PublicKeyedHashParameters |
| 36 | + /// # }, |
| 37 | + /// # constants::{ |
| 38 | + /// # tss::{TPMA_SESSION_DECRYPT, TPMA_SESSION_ENCRYPT}, |
| 39 | + /// # SessionType, |
| 40 | + /// # }, |
| 41 | + /// # interface_types::{ |
| 42 | + /// # algorithm::{HashingAlgorithm, PublicAlgorithm, RsaSchemeAlgorithm}, |
| 43 | + /// # key_bits::RsaKeyBits, reserved_handles::Hierarchy |
| 44 | + /// # }, |
| 45 | + /// # utils::create_unrestricted_signing_rsa_public, |
| 46 | + /// # }; |
| 47 | + /// # use std::convert::TryFrom; |
| 48 | + /// # use std::str::FromStr; |
| 49 | + /// # // Create context with session |
| 50 | + /// # let mut context = |
| 51 | + /// # Context::new( |
| 52 | + /// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"), |
| 53 | + /// # ).expect("Failed to create Context"); |
| 54 | + /// # let session = context |
| 55 | + /// # .start_auth_session( |
| 56 | + /// # None, |
| 57 | + /// # None, |
| 58 | + /// # None, |
| 59 | + /// # SessionType::Hmac, |
| 60 | + /// # SymmetricDefinition::AES_256_CFB, |
| 61 | + /// # HashingAlgorithm::Sha256, |
| 62 | + /// # ) |
| 63 | + /// # .expect("Failed to create session"); |
| 64 | + /// # let (session_attributes, session_attributes_mask) = SessionAttributesBuilder::new() |
| 65 | + /// # .with_decrypt(true) |
| 66 | + /// # .with_encrypt(true) |
| 67 | + /// # .build(); |
| 68 | + /// # context.tr_sess_set_attributes(session.unwrap(), session_attributes, session_attributes_mask) |
| 69 | + /// # .expect("Failed to set attributes on session"); |
| 70 | + /// # context.set_sessions((session, None, None)); |
| 71 | + /// # |
| 72 | + /// let object_attributes = ObjectAttributesBuilder::new() |
| 73 | + /// .with_sign_encrypt(true) |
| 74 | + /// .with_sensitive_data_origin(true) |
| 75 | + /// .with_user_with_auth(true) |
| 76 | + /// .build() |
| 77 | + /// .expect("Failed to build object attributes"); |
| 78 | + /// |
| 79 | + /// let key_pub = PublicBuilder::new() |
| 80 | + /// .with_public_algorithm(PublicAlgorithm::KeyedHash) |
| 81 | + /// .with_name_hashing_algorithm(HashingAlgorithm::Sha256) |
| 82 | + /// .with_object_attributes(object_attributes) |
| 83 | + /// .with_keyed_hash_parameters(PublicKeyedHashParameters::new( |
| 84 | + /// KeyedHashScheme::HMAC_SHA_256, |
| 85 | + /// )) |
| 86 | + /// .with_keyed_hash_unique_identifier(Default::default()) |
| 87 | + /// .build() |
| 88 | + /// .expect("Failed to build public structure for key."); |
| 89 | + /// |
| 90 | + /// let key = context |
| 91 | + /// .create_primary(Hierarchy::Owner, key_pub, None, None, None, None) |
| 92 | + /// .unwrap(); |
| 93 | + /// |
| 94 | + /// let data = [0xEE; 5000]; |
| 95 | + /// |
| 96 | + /// let handle = context |
| 97 | + /// .hmac_sequence_start(key.key_handle.into(), HashingAlgorithm::Sha256, None) |
| 98 | + /// .unwrap(); |
| 99 | + /// |
| 100 | + /// let chunks = data.chunks_exact(MaxBuffer::MAX_SIZE); |
| 101 | + /// let last_chunk = chunks.remainder(); |
| 102 | + /// for chunk in chunks { |
| 103 | + /// context |
| 104 | + /// .sequence_update(handle, MaxBuffer::from_bytes(chunk).unwrap()) |
| 105 | + /// .unwrap(); |
| 106 | + /// } |
| 107 | + /// let (actual_hashed_data, ticket) = context |
| 108 | + /// .sequence_complete( |
| 109 | + /// handle, |
| 110 | + /// MaxBuffer::from_bytes(last_chunk).unwrap(), |
| 111 | + /// Hierarchy::Null, |
| 112 | + /// ) |
| 113 | + /// .unwrap(); |
| 114 | + /// ``` |
| 115 | + pub fn hmac_sequence_start( |
| 116 | + &mut self, |
| 117 | + handle: ObjectHandle, |
| 118 | + hashing_algorithm: HashingAlgorithm, |
| 119 | + auth: Option<Auth>, |
| 120 | + ) -> Result<ObjectHandle> { |
| 121 | + let mut sequence_handle = ObjectHandle::None.into(); |
| 122 | + ReturnCode::ensure_success( |
| 123 | + unsafe { |
| 124 | + Esys_HMAC_Start( |
| 125 | + self.mut_context(), |
| 126 | + handle.into(), |
| 127 | + self.optional_session_1(), |
| 128 | + self.optional_session_2(), |
| 129 | + self.optional_session_3(), |
| 130 | + &auth.unwrap_or_default().into(), |
| 131 | + hashing_algorithm.into(), |
| 132 | + &mut sequence_handle, |
| 133 | + ) |
| 134 | + }, |
| 135 | + |ret| { |
| 136 | + error!( |
| 137 | + "Error failed to perform HMAC sequence start operation: {:#010X}", |
| 138 | + ret |
| 139 | + ); |
| 140 | + }, |
| 141 | + )?; |
| 142 | + Ok(ObjectHandle::from(sequence_handle)) |
| 143 | + } |
| 144 | + |
7 | 145 | // Missing function: MAC_Start |
8 | | - // Missing function: HashSequenceStart |
9 | | - // Missing function: SequenceUpdate |
10 | | - // Missing function: SequenceComplete |
| 146 | + |
| 147 | + /// Starts hash sequence of large data (larger than [`MaxBuffer::MAX_SIZE`]) using the specified algorithm. |
| 148 | + /// |
| 149 | + /// # Details |
| 150 | + /// When the amount of data to be included in a digest cannot be sent to the TPM in one atomic hash |
| 151 | + /// command then a sequence of commands may be used to provide incremental updates to the digest. |
| 152 | + /// |
| 153 | + /// Follow the pattern: |
| 154 | + /// - Initialize sequence with [`Context::hmac_sequence_start`] |
| 155 | + /// - Send data to calculate the hash with [`Context::sequence_update`] |
| 156 | + /// - Finish hash calculation with call to [`Context::sequence_complete`] |
| 157 | + /// |
| 158 | + /// # Example |
| 159 | + /// |
| 160 | + /// ```rust |
| 161 | + /// # use tss_esapi::{Context, tcti_ldr::TctiNameConf, |
| 162 | + /// # attributes::SessionAttributesBuilder, |
| 163 | + /// # structures::{Auth, MaxBuffer, Ticket, SymmetricDefinition, RsaExponent, RsaScheme}, |
| 164 | + /// # constants::{ |
| 165 | + /// # tss::{TPMA_SESSION_DECRYPT, TPMA_SESSION_ENCRYPT}, |
| 166 | + /// # SessionType, |
| 167 | + /// # }, |
| 168 | + /// # interface_types::{ |
| 169 | + /// # algorithm::{HashingAlgorithm, RsaSchemeAlgorithm}, |
| 170 | + /// # key_bits::RsaKeyBits, reserved_handles::Hierarchy |
| 171 | + /// # }, |
| 172 | + /// # utils::create_unrestricted_signing_rsa_public, |
| 173 | + /// # }; |
| 174 | + /// # use std::convert::TryFrom; |
| 175 | + /// # use std::str::FromStr; |
| 176 | + /// # // Create context with session |
| 177 | + /// # let mut context = |
| 178 | + /// # Context::new( |
| 179 | + /// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"), |
| 180 | + /// # ).expect("Failed to create Context"); |
| 181 | + /// # let session = context |
| 182 | + /// # .start_auth_session( |
| 183 | + /// # None, |
| 184 | + /// # None, |
| 185 | + /// # None, |
| 186 | + /// # SessionType::Hmac, |
| 187 | + /// # SymmetricDefinition::AES_256_CFB, |
| 188 | + /// # HashingAlgorithm::Sha256, |
| 189 | + /// # ) |
| 190 | + /// # .expect("Failed to create session"); |
| 191 | + /// # let (session_attributes, session_attributes_mask) = SessionAttributesBuilder::new() |
| 192 | + /// # .with_decrypt(true) |
| 193 | + /// # .with_encrypt(true) |
| 194 | + /// # .build(); |
| 195 | + /// # context.tr_sess_set_attributes(session.unwrap(), session_attributes, session_attributes_mask) |
| 196 | + /// # .expect("Failed to set attributes on session"); |
| 197 | + /// # context.set_sessions((session, None, None)); |
| 198 | + /// |
| 199 | + /// let data = [0xEE; 2*1025]; |
| 200 | + /// |
| 201 | + /// let handle = context |
| 202 | + /// .hash_sequence_start(HashingAlgorithm::Sha256, None) |
| 203 | + /// .unwrap(); |
| 204 | + /// |
| 205 | + /// let chunks = data.chunks_exact(MaxBuffer::MAX_SIZE); |
| 206 | + /// let last_chunk = chunks.remainder(); |
| 207 | + /// for chunk in chunks { |
| 208 | + /// context |
| 209 | + /// .sequence_update(handle, MaxBuffer::from_bytes(chunk).unwrap()) |
| 210 | + /// .unwrap(); |
| 211 | + /// } |
| 212 | + /// let (actual_hashed_data, ticket) = context |
| 213 | + /// .sequence_complete( |
| 214 | + /// handle, |
| 215 | + /// MaxBuffer::from_bytes(last_chunk).unwrap(), |
| 216 | + /// Hierarchy::Owner, |
| 217 | + /// ) |
| 218 | + /// .unwrap(); |
| 219 | + /// ``` |
| 220 | + pub fn hash_sequence_start( |
| 221 | + &mut self, |
| 222 | + hashing_algorithm: HashingAlgorithm, |
| 223 | + auth: Option<Auth>, |
| 224 | + ) -> Result<ObjectHandle> { |
| 225 | + let mut sequence_handle = ObjectHandle::None.into(); |
| 226 | + ReturnCode::ensure_success( |
| 227 | + unsafe { |
| 228 | + Esys_HashSequenceStart( |
| 229 | + self.mut_context(), |
| 230 | + self.optional_session_1(), |
| 231 | + self.optional_session_2(), |
| 232 | + self.optional_session_3(), |
| 233 | + &auth.unwrap_or_default().into(), |
| 234 | + hashing_algorithm.into(), |
| 235 | + &mut sequence_handle, |
| 236 | + ) |
| 237 | + }, |
| 238 | + |ret| { |
| 239 | + error!( |
| 240 | + "Error failed to perform hash sequence start operation: {:#010X}", |
| 241 | + ret |
| 242 | + ); |
| 243 | + }, |
| 244 | + )?; |
| 245 | + Ok(ObjectHandle::from(sequence_handle)) |
| 246 | + } |
| 247 | + |
| 248 | + /// Continues hash or HMAC sequence. |
| 249 | + /// |
| 250 | + /// See [`Context::hash_sequence_start`], [`Context::hmac_sequence_start`]. |
| 251 | + pub fn sequence_update( |
| 252 | + &mut self, |
| 253 | + sequence_handle: ObjectHandle, |
| 254 | + data: MaxBuffer, |
| 255 | + ) -> Result<()> { |
| 256 | + ReturnCode::ensure_success( |
| 257 | + unsafe { |
| 258 | + Esys_SequenceUpdate( |
| 259 | + self.mut_context(), |
| 260 | + sequence_handle.into(), |
| 261 | + self.optional_session_1(), |
| 262 | + self.optional_session_2(), |
| 263 | + self.optional_session_3(), |
| 264 | + &data.into(), |
| 265 | + ) |
| 266 | + }, |
| 267 | + |ret| { |
| 268 | + error!( |
| 269 | + "Error failed to perform sequence update operation: {:#010X}", |
| 270 | + ret |
| 271 | + ); |
| 272 | + }, |
| 273 | + ) |
| 274 | + } |
| 275 | + |
| 276 | + /// Finishes hash or HMAC sequence. |
| 277 | + /// |
| 278 | + /// /// See [`Context::hash_sequence_start`], [`Context::hmac_sequence_start`]. |
| 279 | + pub fn sequence_complete( |
| 280 | + &mut self, |
| 281 | + sequence_handle: ObjectHandle, |
| 282 | + data: MaxBuffer, |
| 283 | + hierarchy: Hierarchy, |
| 284 | + ) -> Result<(Digest, Option<HashcheckTicket>)> { |
| 285 | + let mut out_hash_ptr = null_mut(); |
| 286 | + let mut validation_ptr = null_mut(); |
| 287 | + ReturnCode::ensure_success( |
| 288 | + unsafe { |
| 289 | + Esys_SequenceComplete( |
| 290 | + self.mut_context(), |
| 291 | + sequence_handle.into(), |
| 292 | + self.optional_session_1(), |
| 293 | + self.optional_session_2(), |
| 294 | + self.optional_session_3(), |
| 295 | + &data.into(), |
| 296 | + if cfg!(hierarchy_is_esys_tr) { |
| 297 | + ObjectHandle::from(hierarchy).into() |
| 298 | + } else { |
| 299 | + TpmHandle::from(hierarchy).into() |
| 300 | + }, |
| 301 | + &mut out_hash_ptr, |
| 302 | + &mut validation_ptr, |
| 303 | + ) |
| 304 | + }, |
| 305 | + |ret| { |
| 306 | + error!( |
| 307 | + "Error failed to perform sequence complete operation: {:#010X}", |
| 308 | + ret |
| 309 | + ); |
| 310 | + }, |
| 311 | + )?; |
| 312 | + Ok(( |
| 313 | + Digest::try_from(Context::ffi_data_to_owned(out_hash_ptr)?)?, |
| 314 | + if validation_ptr.is_null() { |
| 315 | + // For HMAC sequence validation parameter is NULL |
| 316 | + None |
| 317 | + } else { |
| 318 | + Some(HashcheckTicket::try_from(Context::ffi_data_to_owned( |
| 319 | + validation_ptr, |
| 320 | + )?)?) |
| 321 | + }, |
| 322 | + )) |
| 323 | + } |
| 324 | + |
11 | 325 | // Missing function: EventSequenceComplete |
12 | 326 | } |
0 commit comments