From 040188a34d5d7b8607825b25a4eb78c25c6b57cc Mon Sep 17 00:00:00 2001 From: Aviv Naaman <34813756+AvivNaaman@users.noreply.github.com> Date: Mon, 20 Jan 2025 16:46:00 +0200 Subject: [PATCH] feat: add `make_signature` and `verify_signature` to `Sspi` trait (#343) --- ffi/src/sspi/sec_handle.rs | 19 +++ src/credssp/mod.rs | 27 ++++ src/credssp/sspi_cred_ssp/mod.rs | 21 +++ src/kerberos/mod.rs | 19 +++ src/lib.rs | 221 ++++++++++++++++++++++++++++++- src/negotiate.rs | 21 +++ src/ntlm/messages/mod.rs | 3 +- src/ntlm/mod.rs | 103 ++++++++++---- src/ntlm/test.rs | 48 +++++++ src/pku2u/mod.rs | 19 +++ 10 files changed, 474 insertions(+), 27 deletions(-) diff --git a/ffi/src/sspi/sec_handle.rs b/ffi/src/sspi/sec_handle.rs index 2dc1f033..940833c3 100644 --- a/ffi/src/sspi/sec_handle.rs +++ b/ffi/src/sspi/sec_handle.rs @@ -191,6 +191,25 @@ impl Sspi for SspiHandle { let mut context = self.sspi_context.lock()?; Ok(context.change_password_sync(change_password).into()) } + + fn make_signature( + &mut self, + _flags: u32, + _message: &mut [sspi::SecurityBuffer], + _sequence_number: u32, + ) -> Result<()> { + Err(Error::new( + ErrorKind::UnsupportedFunction, + "make_signature is not supported", + )) + } + + fn verify_signature(&mut self, _message: &mut [sspi::SecurityBuffer], _sequence_number: u32) -> Result { + Err(Error::new( + ErrorKind::UnsupportedFunction, + "verify_signature is not supported", + )) + } } pub struct CredentialsHandle { diff --git a/src/credssp/mod.rs b/src/credssp/mod.rs index 1ce27cd5..7632f6c1 100644 --- a/src/credssp/mod.rs +++ b/src/credssp/mod.rs @@ -953,6 +953,33 @@ impl Sspi for SspiContext { self.change_password_impl(&mut yield_point, change_password).await })) } + + fn make_signature( + &mut self, + flags: u32, + message: &mut [SecurityBuffer], + sequence_number: u32, + ) -> crate::Result<()> { + match self { + SspiContext::Ntlm(ntlm) => ntlm.make_signature(flags, message, sequence_number), + SspiContext::Kerberos(kerberos) => kerberos.make_signature(flags, message, sequence_number), + SspiContext::Negotiate(negotiate) => negotiate.make_signature(flags, message, sequence_number), + SspiContext::Pku2u(pku2u) => pku2u.make_signature(flags, message, sequence_number), + #[cfg(feature = "tsssp")] + SspiContext::CredSsp(credssp) => credssp.make_signature(flags, message, sequence_number), + } + } + + fn verify_signature(&mut self, message: &mut [SecurityBuffer], sequence_number: u32) -> crate::Result { + match self { + SspiContext::Ntlm(ntlm) => ntlm.verify_signature(message, sequence_number), + SspiContext::Kerberos(kerberos) => kerberos.verify_signature(message, sequence_number), + SspiContext::Negotiate(negotiate) => negotiate.verify_signature(message, sequence_number), + SspiContext::Pku2u(pku2u) => pku2u.verify_signature(message, sequence_number), + #[cfg(feature = "tsssp")] + SspiContext::CredSsp(credssp) => credssp.verify_signature(message, sequence_number), + } + } } impl SspiEx for SspiContext { diff --git a/src/credssp/sspi_cred_ssp/mod.rs b/src/credssp/sspi_cred_ssp/mod.rs index ef49b3c8..7725c9de 100644 --- a/src/credssp/sspi_cred_ssp/mod.rs +++ b/src/credssp/sspi_cred_ssp/mod.rs @@ -292,6 +292,27 @@ impl Sspi for SspiCredSsp { "ChangePassword is not supported in SspiCredSsp context", )) } + + #[instrument(level = "debug", ret, fields(state = ?self.state), skip_all)] + fn make_signature( + &mut self, + _flags: u32, + _message: &mut [SecurityBuffer], + _sequence_number: u32, + ) -> crate::Result<()> { + Err(Error::new( + ErrorKind::UnsupportedFunction, + "make_signature is not supported", + )) + } + + #[instrument(level = "debug", ret, fields(state = ?self.state), skip_all)] + fn verify_signature(&mut self, _message: &mut [SecurityBuffer], _sequence_number: u32) -> crate::Result { + Err(Error::new( + ErrorKind::UnsupportedFunction, + "verify_signature is not supported", + )) + } } impl SspiImpl for SspiCredSsp { diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index 3c52284b..c4d3e013 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -441,6 +441,25 @@ impl Sspi for Kerberos { self.change_password(&mut yield_point, change_password).await })) } + + fn make_signature( + &mut self, + _flags: u32, + _message: &mut [SecurityBuffer], + _sequence_number: u32, + ) -> crate::Result<()> { + Err(Error::new( + ErrorKind::UnsupportedFunction, + "make_signature is not supported. use encrypt_message to sign messages instead", + )) + } + + fn verify_signature(&mut self, _message: &mut [SecurityBuffer], _sequence_number: u32) -> crate::Result { + Err(Error::new( + ErrorKind::UnsupportedFunction, + "verify_signature is not supported. use decrypt_message to verify signatures instead", + )) + } } impl SspiImpl for Kerberos { diff --git a/src/lib.rs b/src/lib.rs index daed46b6..a027cc29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,6 +81,8 @@ mod utils; #[cfg(all(feature = "tsssp", not(target_os = "windows")))] compile_error!("tsssp feature should be used only on Windows"); +use std::{error, fmt, io, result, str, string}; + use bitflags::bitflags; #[cfg(feature = "tsssp")] use credssp::sspi_cred_ssp; @@ -92,7 +94,6 @@ use picky_asn1_x509::Certificate; use picky_krb::gss_api::GssApiMessageError; use picky_krb::messages::KrbError; pub use security_buffer::SecurityBuffer; -use std::{error, fmt, io, result, str, string}; use utils::map_keb_error_code_to_sspi_error; pub use utils::string_to_utf16; @@ -602,6 +603,224 @@ where sequence_number: u32, ) -> Result; + /// Generates a cryptographic checksum of the message, and also includes sequencing information to prevent message loss or insertion. + /// The function allows the application to choose between several cryptographic algorithms, if supported by the chosen mechanism. + /// + /// # Parameters + /// * `flags`: package-specific flags that indicate the quality of protection. A security package can use this parameter to enable the selection of cryptographic algorithms + /// * `message`: On input, the structure references one or more `SecurityBuffer` structures of `type SecurityBufferType::Data` that contain the message to be signed, + /// and a `SecurityBuffer` of type `SecurityBufferType::Token` that receives the signature. + /// * `sequence_number`: the sequence number that the transport application assigned to the message. If the transport application does not maintain sequence numbers, this parameter must be zero + /// + /// # Returns + /// * `SspiOk` on success + /// * `Error` on error + /// + /// # Example + /// + /// ``` + /// use sspi::Sspi; + /// use sspi::Username; + /// use sspi::builders::EmptyInitializeSecurityContext; + /// use sspi::SspiImpl; + /// + /// let mut client_ntlm = sspi::Ntlm::new(); + /// let mut ntlm = sspi::Ntlm::new(); + /// + /// let mut client_output_buffer = vec![sspi::OwnedSecurityBuffer::new(Vec::new(), sspi::SecurityBufferType::Token)]; + /// let mut server_output_buffer = vec![sspi::OwnedSecurityBuffer::new(Vec::new(), sspi::SecurityBufferType::Token)]; + /// + /// let identity = sspi::AuthIdentity { + /// username: Username::parse("user").unwrap(), + /// password: "password".to_string().into(), + /// }; + /// + /// let mut client_acq_cred_result = client_ntlm + /// .acquire_credentials_handle() + /// .with_credential_use(sspi::CredentialUse::Outbound) + /// .with_auth_data(&identity) + /// .execute(&mut client_ntlm) + /// .unwrap(); + /// + /// let mut server_acq_cred_result = ntlm + /// .acquire_credentials_handle() + /// .with_credential_use(sspi::CredentialUse::Inbound) + /// .with_auth_data(&identity) + /// .execute(&mut ntlm) + /// .unwrap(); + /// + /// loop { + /// client_output_buffer[0].buffer.clear(); + /// + /// let mut builder = client_ntlm.initialize_security_context() + /// .with_credentials_handle(&mut client_acq_cred_result.credentials_handle) + /// .with_context_requirements( + /// sspi::ClientRequestFlags::CONFIDENTIALITY | sspi::ClientRequestFlags::ALLOCATE_MEMORY, + /// ) + /// .with_target_data_representation(sspi::DataRepresentation::Native) + /// .with_target_name("user") + /// .with_input(&mut server_output_buffer) + /// .with_output(&mut client_output_buffer); + /// + /// let _client_result = client_ntlm.initialize_security_context_impl(&mut builder) + /// .unwrap() + /// .resolve_to_result() + /// .unwrap(); + /// + /// let server_result = ntlm + /// .accept_security_context() + /// .with_credentials_handle(&mut server_acq_cred_result.credentials_handle) + /// .with_context_requirements(sspi::ServerRequestFlags::ALLOCATE_MEMORY) + /// .with_target_data_representation(sspi::DataRepresentation::Native) + /// .with_input(&mut client_output_buffer) + /// .with_output(&mut server_output_buffer) + /// .execute(&mut ntlm) + /// .unwrap(); + /// + /// if server_result.status == sspi::SecurityStatus::CompleteAndContinue + /// || server_result.status == sspi::SecurityStatus::CompleteNeeded + /// { + /// break; + /// } + /// } + /// + /// let _result = ntlm + /// .complete_auth_token(&mut server_output_buffer) + /// .unwrap(); + /// + /// let mut token = [0; 128]; + /// let mut data = "This is a message to be signed".as_bytes().to_vec(); + /// let mut msg_buffer = vec![ + /// sspi::SecurityBuffer::Token(token.as_mut_slice()), + /// sspi::SecurityBuffer::Data(data.as_mut_slice()), + /// ]; + /// + /// println!("Input data: {:?}", msg_buffer[1].data()); + /// + /// #[allow(unused_variables)] + /// let result = ntlm + /// .make_signature(0, &mut msg_buffer, 0).unwrap(); + /// + /// println!("Data signature: {:?}", msg_buffer[0].data()); + /// ``` + /// + /// # MSDN + /// * [MakeSignature function](https://learn.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-makesignature) + fn make_signature(&mut self, flags: u32, message: &mut [SecurityBuffer], sequence_number: u32) + -> crate::Result<()>; + + /// Verifies that a message signed by using the `make_signature` function was received in the correct sequence and has not been modified. + /// + /// # Parameters + /// * `message`: On input, the structure references one or more `SecurityBuffer` structures of `type SecurityBufferType::Data` that contain the message to be verified, + /// and a `SecurityBuffer` of type `SecurityBufferType::Token` that contains the signature. + /// * `sequence_number`: the sequence number that the transport application assigned to the message. If the transport application does not maintain sequence numbers, this parameter must be zero + /// + /// # Returns + /// * `u32` package-specific flags that indicate the quality of protection. + /// * `Error` on error + /// + /// # Example + /// + /// ``` + /// use sspi::Sspi; + /// use sspi::Username; + /// use sspi::builders::EmptyInitializeSecurityContext; + /// use sspi::SspiImpl; + /// + /// let mut ntlm = sspi::Ntlm::new(); + /// let mut server_ntlm = sspi::Ntlm::new(); + /// + /// let mut client_output_buffer = vec![sspi::OwnedSecurityBuffer::new(Vec::new(), sspi::SecurityBufferType::Token)]; + /// let mut server_output_buffer = vec![sspi::OwnedSecurityBuffer::new(Vec::new(), sspi::SecurityBufferType::Token)]; + /// + /// let identity = sspi::AuthIdentity { + /// username: Username::parse("user").unwrap(), + /// password: "password".to_string().into(), + /// }; + /// + /// let mut client_acq_cred_result = ntlm + /// .acquire_credentials_handle() + /// .with_credential_use(sspi::CredentialUse::Outbound) + /// .with_auth_data(&identity) + /// .execute(&mut ntlm) + /// .unwrap(); + /// + /// let mut server_acq_cred_result = server_ntlm + /// .acquire_credentials_handle() + /// .with_credential_use(sspi::CredentialUse::Inbound) + /// .with_auth_data(&identity) + /// .execute(&mut server_ntlm) + /// .unwrap(); + /// + /// loop { + /// client_output_buffer[0].buffer.clear(); + /// + /// let mut builder = ntlm.initialize_security_context() + /// .with_credentials_handle(&mut client_acq_cred_result.credentials_handle) + /// .with_context_requirements( + /// sspi::ClientRequestFlags::CONFIDENTIALITY | sspi::ClientRequestFlags::ALLOCATE_MEMORY, + /// ) + /// .with_target_data_representation(sspi::DataRepresentation::Native) + /// .with_target_name("user") + /// .with_input(&mut server_output_buffer) + /// .with_output(&mut client_output_buffer); + /// + /// let _client_result = ntlm.initialize_security_context_impl(&mut builder) + /// .unwrap() + /// .resolve_to_result() + /// .unwrap(); + /// + /// let server_result = server_ntlm + /// .accept_security_context() + /// .with_credentials_handle(&mut server_acq_cred_result.credentials_handle) + /// .with_context_requirements(sspi::ServerRequestFlags::ALLOCATE_MEMORY) + /// .with_target_data_representation(sspi::DataRepresentation::Native) + /// .with_input(&mut client_output_buffer) + /// .with_output(&mut server_output_buffer) + /// .execute(&mut server_ntlm) + /// .unwrap(); + /// + /// if server_result.status == sspi::SecurityStatus::CompleteAndContinue + /// || server_result.status == sspi::SecurityStatus::CompleteNeeded + /// { + /// break; + /// } + /// } + /// + /// let _result = server_ntlm + /// .complete_auth_token(&mut server_output_buffer) + /// .unwrap(); + /// + /// let mut token = [0; 128]; + /// let mut data = "This is a message".as_bytes().to_vec(); + /// let mut msg = [ + /// sspi::SecurityBuffer::Token(token.as_mut_slice()), + /// sspi::SecurityBuffer::Data(data.as_mut_slice()), + /// ]; + /// + /// let _result = server_ntlm + /// .make_signature(0, &mut msg, 0).unwrap(); + /// + /// let [mut token, mut data] = msg; + /// + /// let mut msg_buffer = vec![ + /// sspi::SecurityBuffer::Token(token.take_data()), + /// sspi::SecurityBuffer::Data(data.take_data()), + /// ]; + /// + /// #[allow(unused_variables)] + /// let signature_flags = ntlm + /// .verify_signature(&mut msg_buffer, 0) + /// .unwrap(); + /// + /// println!("Signature calculated and verified."); + /// ``` + /// + /// # MSDN + /// * [VerifySignature function](https://learn.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-verifysignature) + fn verify_signature(&mut self, message: &mut [SecurityBuffer], sequence_number: u32) -> crate::Result; + /// Decrypts a message. Some packages do not encrypt and decrypt messages but rather perform and check an integrity hash. /// /// # Parameters diff --git a/src/negotiate.rs b/src/negotiate.rs index 12fa5c3c..8025a6a6 100644 --- a/src/negotiate.rs +++ b/src/negotiate.rs @@ -374,6 +374,27 @@ impl Sspi for Negotiate { self.change_password(&mut yield_point, change_password).await })) } + + fn make_signature( + &mut self, + flags: u32, + message: &mut [SecurityBuffer], + sequence_number: u32, + ) -> crate::Result<()> { + match &mut self.protocol { + NegotiatedProtocol::Pku2u(pku2u) => pku2u.make_signature(flags, message, sequence_number), + NegotiatedProtocol::Kerberos(kerberos) => kerberos.make_signature(flags, message, sequence_number), + NegotiatedProtocol::Ntlm(ntlm) => ntlm.make_signature(flags, message, sequence_number), + } + } + + fn verify_signature(&mut self, message: &mut [SecurityBuffer], sequence_number: u32) -> crate::Result { + match &mut self.protocol { + NegotiatedProtocol::Pku2u(pku2u) => pku2u.verify_signature(message, sequence_number), + NegotiatedProtocol::Kerberos(kerberos) => kerberos.verify_signature(message, sequence_number), + NegotiatedProtocol::Ntlm(ntlm) => ntlm.verify_signature(message, sequence_number), + } + } } impl SspiImpl for Negotiate { diff --git a/src/ntlm/messages/mod.rs b/src/ntlm/messages/mod.rs index 61df5e68..24813826 100644 --- a/src/ntlm/messages/mod.rs +++ b/src/ntlm/messages/mod.rs @@ -6,9 +6,10 @@ pub mod test; mod av_pair; mod computations; -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use std::io; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; + use crate::ntlm::{NegotiateFlags, NTLM_VERSION_SIZE}; const NTLM_SIGNATURE: &[u8; NTLM_SIGNATURE_SIZE] = b"NTLMSSP\0"; diff --git a/src/ntlm/mod.rs b/src/ntlm/mod.rs index 15b27551..a4715ea3 100644 --- a/src/ntlm/mod.rs +++ b/src/ntlm/mod.rs @@ -368,6 +368,46 @@ impl Ntlm { expiry: None, }) } + + fn compute_checksum( + &mut self, + message: &mut [SecurityBuffer], + sequence_number: u32, + digest: &[u8; 16], + ) -> crate::Result<()> { + let checksum = self + .send_sealing_key + .as_mut() + .unwrap() + .process(&digest[0..SIGNATURE_CHECKSUM_SIZE]); + + let signature_buffer = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Token)?; + if signature_buffer.buf_len() < SIGNATURE_SIZE { + return Err(Error::new(ErrorKind::BufferTooSmall, "the token buffer is too small")); + } + let signature = compute_signature(&checksum, sequence_number); + signature_buffer.write_data(signature.as_slice())?; + + Ok(()) + } + + fn check_signature(&mut self, sequence_number: u32, digest: &[u8; 16], signature: &[u8]) -> crate::Result<()> { + let checksum = self + .recv_sealing_key + .as_mut() + .unwrap() + .process(&digest[0..SIGNATURE_CHECKSUM_SIZE]); + let expected_signature = compute_signature(&checksum, sequence_number); + + if signature != expected_signature.as_ref() { + return Err(Error::new( + ErrorKind::MessageAltered, + "signature verification failed, something nasty is going on", + )); + } + + Ok(()) + } } impl Sspi for Ntlm { @@ -398,18 +438,7 @@ impl Sspi for Ntlm { } data.write_data(&encrypted_data)?; - let checksum = self - .send_sealing_key - .as_mut() - .unwrap() - .process(&digest[0..SIGNATURE_CHECKSUM_SIZE]); - - let signature_buffer = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Token)?; - if signature_buffer.buf_len() < SIGNATURE_SIZE { - return Err(Error::new(ErrorKind::BufferTooSmall, "The Token buffer is too small")); - } - let signature = compute_signature(&checksum, sequence_number); - signature_buffer.write_data(signature.as_slice())?; + self.compute_checksum(message, sequence_number, &digest)?; Ok(SecurityStatus::Ok) } @@ -437,19 +466,7 @@ impl Sspi for Ntlm { save_decrypted_data(&decrypted, message)?; let digest = compute_digest(&self.recv_signing_key, sequence_number, &decrypted)?; - let checksum = self - .recv_sealing_key - .as_mut() - .unwrap() - .process(&digest[0..SIGNATURE_CHECKSUM_SIZE]); - let expected_signature = compute_signature(&checksum, sequence_number); - - if signature != expected_signature.as_ref() { - return Err(Error::new( - ErrorKind::MessageAltered, - "Signature verification failed, something nasty is going on!", - )); - } + self.check_signature(sequence_number, &digest, signature)?; Ok(DecryptionFlags::empty()) } @@ -503,6 +520,42 @@ impl Sspi for Ntlm { "NTLM does not support change pasword", )) } + + fn make_signature( + &mut self, + _flags: u32, + message: &mut [SecurityBuffer], + sequence_number: u32, + ) -> crate::Result<()> { + if self.send_sealing_key.is_none() { + self.complete_auth_token(&mut [])?; + } + + SecurityBuffer::find_buffer(message, SecurityBufferType::Token)?; // check if exists + + let data = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Data)?; + let digest = compute_digest(&self.send_signing_key, sequence_number, data.data())?; + + self.compute_checksum(message, sequence_number, &digest)?; + + Ok(()) + } + + fn verify_signature(&mut self, message: &mut [SecurityBuffer], sequence_number: u32) -> crate::Result { + if self.recv_sealing_key.is_none() { + self.complete_auth_token(&mut [])?; + } + + SecurityBuffer::find_buffer(message, SecurityBufferType::Token)?; // check if exists + + let data = SecurityBuffer::find_buffer(message, SecurityBufferType::Data)?; + let digest = compute_digest(&self.recv_signing_key, sequence_number, data.data())?; + + let signature = SecurityBuffer::find_buffer(message, SecurityBufferType::Token)?; + self.check_signature(sequence_number, &digest, signature.data())?; + + Ok(0) + } } impl SspiEx for Ntlm { diff --git a/src/ntlm/test.rs b/src/ntlm/test.rs index d1c7ba77..73677acb 100644 --- a/src/ntlm/test.rs +++ b/src/ntlm/test.rs @@ -225,6 +225,54 @@ fn decrypt_message_fails_on_incorrect_sealing_key() { assert!(context.decrypt_message(&mut buffers, TEST_SEQ_NUM).is_err()); } +#[test] +fn make_signature_verified_by_verify_signature() { + let mut sender = Ntlm::new(); + let mut reciever = Ntlm::new(); + + sender.send_signing_key = SIGNING_KEY; + sender.send_sealing_key = Some(Rc4::new(&SEALING_KEY)); + + reciever.recv_signing_key = SIGNING_KEY; + reciever.recv_sealing_key = Some(Rc4::new(&SEALING_KEY)); + + let mut plain_test_data = TEST_DATA.to_vec(); + let mut signature_test_data = [0u8; 16]; + let mut make_signature_buffers = vec![ + SecurityBuffer::Data(&mut plain_test_data), + SecurityBuffer::Token(&mut signature_test_data), + ]; + assert!(sender + .make_signature(0, &mut make_signature_buffers, TEST_SEQ_NUM) + .is_ok()); + + let mut verify_signature_buffers = vec![ + SecurityBuffer::Data(&mut plain_test_data), + SecurityBuffer::Token(&mut signature_test_data), + ]; + assert!(reciever + .verify_signature(&mut verify_signature_buffers, TEST_SEQ_NUM) + .is_ok()); +} + +#[test] +fn verify_signature_fails_on_invalid_signature() { + let mut context = Ntlm::new(); + + context.recv_signing_key = SIGNING_KEY; + context.recv_sealing_key = Some(Rc4::new(&SEALING_KEY)); + + let mut test_data = TEST_DATA.to_vec(); + let mut token = [ + 0x01, 0x00, 0x00, 0x00, 0x2e, 0xdf, 0xff, 0x61, 0x29, 0xd6, 0x4d, 0xa9, 0xd2, 0x02, 0x96, 0x49, + ]; + + let mut verify_signature_buffers = vec![SecurityBuffer::Data(&mut test_data), SecurityBuffer::Token(&mut token)]; + assert!(context + .verify_signature(&mut verify_signature_buffers, TEST_SEQ_NUM) + .is_err()); +} + #[test] fn initialize_security_context_wrong_state_negotiate() { let mut context = Ntlm::new(); diff --git a/src/pku2u/mod.rs b/src/pku2u/mod.rs index 3739c98e..69e2c197 100644 --- a/src/pku2u/mod.rs +++ b/src/pku2u/mod.rs @@ -339,6 +339,25 @@ impl Sspi for Pku2u { "Pku2u does not support change pasword", )) } + + fn make_signature( + &mut self, + _flags: u32, + _message: &mut [SecurityBuffer], + _sequence_number: u32, + ) -> crate::Result<()> { + Err(Error::new( + ErrorKind::UnsupportedFunction, + "make_signature is not supported", + )) + } + + fn verify_signature(&mut self, _message: &mut [SecurityBuffer], _sequence_number: u32) -> crate::Result { + Err(Error::new( + ErrorKind::UnsupportedFunction, + "verify_signature is not supported", + )) + } } impl SspiImpl for Pku2u {