blob: 682f5d3b76151a74d9cdd604a85633243be7b560 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use spdm_types::deps::SpdmDeps;
use zerocopy::AsBytes;
use super::internal::SpdmState;
use crate::session::SpdmSessionManager;
use crate::types::code::{RequestCode, ResponseCode};
use crate::types::error::{ErrorCode, SpdmResult};
use crate::types::message::FinishRequest;
use crate::Spdm;
/// WaitForFinishDispatcher handles the state where the responder is waiting
/// for the requester to send the Finish request. After processing Finish
/// request successfully, the session is successfully established.
///
/// This trait is only used for separating impl blocks of `Spdm` struct by functionality.
pub trait WaitForFinishDispatcher {
fn dispatch_request_wait_for_finish(
&mut self,
buf: &mut [u8],
req_size: usize,
req_code: RequestCode,
) -> SpdmResult<usize>;
}
/// FinishDispatcher handles the Finish request.
///
/// This trait is only used for separating impl blocks of `Spdm` struct by functionality.
pub trait FinishDispatcher {
/// Handles the Finish request and if successful, writes the Finish response to `buf`.
/// Finish request format: see `FinishRequest` struct.
/// Finish response format:
/// | Byte offset | Size (bytes) | Field | Description |
/// | :---------- | :----------- | :------------------ | :------------------ |
/// | 0 | 1 | SPDMVersion | SPDM_THIS_VERSION |
/// | 1 | 1 | RequestResponseCode | RequestCode::Finish |
/// | 2 | 1 | Param1 | Reserved |
/// | 3 | 1 | Param2 | Reserved |
fn dispatch_finish_request(&mut self, buf: &mut [u8], req_size: usize) -> SpdmResult<usize>;
}
impl<D: SpdmDeps> FinishDispatcher for Spdm<D> {
fn dispatch_finish_request(&mut self, buf: &mut [u8], req_size: usize) -> SpdmResult<usize> {
if req_size != FinishRequest::SIZE {
return Err(ErrorCode::InvalidRequest);
}
let request: &FinishRequest = (&buf[..req_size])
.try_into()
.map_err(|_| ErrorCode::Unspecified)?;
self.extend_transcript_hash_with_finish_request(
&request.as_bytes()[..FinishRequest::PARTIAL_SIZE],
)?;
self.verify_finish_message(&request.signature)?;
self.validate_finish_message_hmac(&request.requester_verify_data)?;
buf[1] = ResponseCode::Finish.0;
buf[2] = 0;
buf[3] = 0;
// Advance state.
self.state = SpdmState::SessionEstablished;
Ok(4)
}
}
impl<D: SpdmDeps> WaitForFinishDispatcher for Spdm<D> {
fn dispatch_request_wait_for_finish(
&mut self,
buf: &mut [u8],
req_size: usize,
req_code: RequestCode,
) -> SpdmResult<usize> {
match req_code {
RequestCode::Finish => self.dispatch_finish_request(buf, req_size),
_ => Err(ErrorCode::UnexpectedRequest),
}
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "mock")]
mod mock {
use mocktopus::mocking::{MockResult, Mockable};
use super::super::*;
use crate::deps::{SpdmTestDeps, TestIdentity, TestVendor};
#[test]
fn finish_request() {
let mut buf = [0; FinishRequest::SIZE];
Spdm::<SpdmTestDeps>::extend_transcript_hash_with_finish_request
.mock_safe(|_, _| MockResult::Return(Ok(())));
Spdm::<SpdmTestDeps>::verify_finish_message
.mock_safe(|_, _| MockResult::Return(Ok(())));
Spdm::<SpdmTestDeps>::validate_finish_message_hmac
.mock_safe(|_, _| MockResult::Return(Ok(())));
let mut spdm = Spdm::<SpdmTestDeps>::new(TestIdentity, TestVendor);
let result = spdm.dispatch_finish_request(&mut buf, FinishRequest::SIZE);
assert_eq!(result, Ok(4));
}
}
}