Skip to content

Commit 16aad75

Browse files
author
jul-sh
authored
Implement simple remote attestation for the UEFI app (#2742)
* Extract session logic for remote attestion, GH issue ref #2741 * Move sessions logic into its own crate remove unused deps format oak-prefix crate name for consistency fix typo * Implement simple remote attestion for the UEFI app * remove unused dep * Update lockfile * Shutdown with UEFI error code if a remote attestation error occurs * Update method call to match #2751 * Remove erroneous mention of gRPC
1 parent afb0213 commit 16aad75

File tree

7 files changed

+173
-5
lines changed

7 files changed

+173
-5
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

experimental/uefi/app/Cargo.lock

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

experimental/uefi/loader/src/qemu.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ impl Qemu {
7171
// Construct the command-line arguments for `qemu`. See
7272
// https://www.qemu.org/docs/master/system/invocation.html for all available options.
7373

74+
// Needed to expose advanced CPU features to the UEFI app. Specifically
75+
// RDRAND which is required for remote attestation.
76+
cmd.arg("-enable-kvm");
77+
cmd.args(&["-cpu", "Broadwell-IBRS"]);
7478
// We're going to run qemu as a noninteractive embedded program, so disable any
7579
// graphical outputs.
7680
cmd.arg("-nographic");

experimental/uefi/runtime/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ license = "Apache-2.0"
88
[dependencies]
99
ciborium = { version = "*", default-features = false }
1010
ciborium-io = "*"
11+
oak_remote_attestation_sessions = { path = "../../../remote_attestation_sessions" }
12+
anyhow = { version = "*", default-features = false }

experimental/uefi/runtime/src/echo.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,20 @@
1616

1717
extern crate alloc;
1818

19-
use crate::Channel;
19+
use crate::{remote_attestation::AttestationHandler, Channel};
2020
use alloc::vec::Vec;
2121
use ciborium::{de, ser};
2222

2323
#[derive(Debug)]
2424
pub enum Error<T> {
2525
// An error occured while deserializing.
26-
De(ciborium::de::Error<T>),
26+
Deserialization(ciborium::de::Error<T>),
2727

2828
// An error occured while serializing.
29-
Ser(ciborium::ser::Error<T>),
29+
Serialization(ciborium::ser::Error<T>),
30+
31+
// An error occured in remote attestation.
32+
Attestation(anyhow::Error),
3033
}
3134

3235
impl<E> ciborium_io::Write for &mut dyn Channel<Error = E> {
@@ -49,10 +52,16 @@ impl<E> ciborium_io::Read for &mut dyn Channel<Error = E> {
4952
}
5053
}
5154

55+
const MOCK_SESSION_ID: [u8; 8] = [0; 8];
56+
5257
// Echoes all input on the interface back out.
5358
pub fn echo<E: core::fmt::Debug>(mut channel: &mut dyn Channel<Error = E>) -> Result<!, Error<E>> {
59+
let attestation_handler = &mut AttestationHandler::create(|v| v);
5460
loop {
55-
let msg: Vec<u8> = de::from_reader(&mut channel).map_err(Error::De)?;
56-
ser::into_writer(&msg, &mut channel).map_err(Error::Ser)?;
61+
let msg: Vec<u8> = de::from_reader(&mut channel).map_err(Error::Deserialization)?;
62+
let response = attestation_handler
63+
.message(MOCK_SESSION_ID, msg)
64+
.map_err(Error::Attestation)?;
65+
ser::into_writer(&response, &mut channel).map_err(Error::Serialization)?;
5766
}
5867
}

experimental/uefi/runtime/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#![feature(never_type)]
1919

2020
pub mod echo;
21+
mod remote_attestation;
2122

2223
/// Basic hardware abstraction layer for sending data.
2324
pub trait Channel {
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// Copyright 2022 The Project Oak Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//
16+
17+
//! Server-side implementation the remote attestation handshake protocol.
18+
//!
19+
//! A simplified version of the implementation from the `grpc_unary_attestation`
20+
//! crate. TODO(#2741): Refactor this to share more code between the two runtimes.
21+
22+
extern crate alloc;
23+
24+
use alloc::vec::Vec;
25+
use anyhow::Context;
26+
use oak_remote_attestation_sessions::{SessionId, SessionState, SessionTracker};
27+
28+
/// Number of sessions that will be kept in memory.
29+
const SESSIONS_CACHE_SIZE: usize = 10000;
30+
31+
pub struct AttestationHandler<F> {
32+
session_tracker: SessionTracker,
33+
request_handler: F,
34+
}
35+
36+
const MOCK_TEE_CERTIFICATE: [u8; 0] = [];
37+
const MOCK_ADDITIONAL_INFO: [u8; 0] = [];
38+
39+
impl<F> AttestationHandler<F>
40+
where
41+
F: Send + Sync + Clone + FnOnce(Vec<u8>) -> Vec<u8>,
42+
{
43+
pub fn create(request_handler: F) -> Self {
44+
let session_tracker = SessionTracker::create(
45+
SESSIONS_CACHE_SIZE,
46+
MOCK_TEE_CERTIFICATE.to_vec(),
47+
MOCK_ADDITIONAL_INFO.to_vec(),
48+
);
49+
50+
Self {
51+
session_tracker,
52+
request_handler,
53+
}
54+
}
55+
56+
pub fn message(&mut self, session_id: SessionId, request: Vec<u8>) -> anyhow::Result<Vec<u8>> {
57+
let mut session_state = {
58+
self.session_tracker
59+
.pop_or_create_session_state(session_id)
60+
.expect("Couldn't pop session state")
61+
};
62+
let response_body = match session_state {
63+
SessionState::HandshakeInProgress(ref mut handshaker) => {
64+
handshaker
65+
.next_step(&request)
66+
.context("Couldn't process handshake message")?
67+
// After receiving a valid `ClientIdentity` message
68+
// (the last step of the key exchange)
69+
// ServerHandshaker.next_step returns `None`. For unary
70+
// request we do want to send an explicit confirmation in
71+
// the form of a status message. Hence in case of `None`
72+
// fallback to a default (empty) response.
73+
.unwrap_or_default()
74+
}
75+
SessionState::EncryptedMessageExchange(ref mut encryptor) => {
76+
let decrypted_request = encryptor
77+
.decrypt(&request)
78+
.context("Couldn't decrypt response")?;
79+
80+
let response = (self.request_handler.clone())(decrypted_request);
81+
82+
encryptor
83+
.encrypt(&response)
84+
.context("Couldn't encrypt response")?
85+
}
86+
};
87+
88+
self.session_tracker
89+
.put_session_state(session_id, session_state);
90+
91+
Ok(response_body)
92+
}
93+
}

0 commit comments

Comments
 (0)