Skip to content

Commit

Permalink
Merge pull request #192 from puiterwijk/v11
Browse files Browse the repository at this point in the history
Move to FDO version 1.1
  • Loading branch information
mergify[bot] authored Jan 31, 2022
2 parents 8847146 + 865ca08 commit c99be76
Show file tree
Hide file tree
Showing 51 changed files with 2,422 additions and 546 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# fido-device-onboard-rs
An implementation of the FIDO Device Onboard Specification written in rust.

The current implementation targets specification version: [v1.0 20210323a](https://fidoalliance.org/specs/FDO/fido-device-onboard-v1.0-ps-20210323/fido-device-onboard-v1.0-ps-20210323.html).
The current implementation targets specification version: [1.1 20211214](https://fidoalliance.org/specs/FDO/FIDO-Device-Onboard-RD-v1.1-20211214/FIDO-device-onboard-spec-v1.1-rd-20211214.html).

## Components
The fido-fdo-rs implements all core components of the FIDO Device Onboard Specification including:
Expand Down
67 changes: 38 additions & 29 deletions client-linuxapp/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ use fdo_data_formats::{
constants::{DeviceSigType, HeaderKeys, RendezvousProtocolValue, TransportProtocol},
enhanced_types::{RendezvousInterpretedDirective, RendezvousInterpreterSide},
messages,
ownershipvoucher::OwnershipVoucher,
ownershipvoucher::{OwnershipVoucher, OwnershipVoucherHeader},
types::{
new_eat, COSEHeaderMap, COSESign, CipherSuite, EATokenPayload, KexSuite, KeyDeriveSide,
KeyExchange, Nonce, PayloadCreating, SigInfo, TO1DataPayload, TO2AddressEntry,
TO2ProveDevicePayload, TO2ProveOVHdrPayload, UnverifiedValue,
},
DeviceCredential, Serializable,
DeviceCredential, ProtocolVersion, Serializable,
};
use fdo_http_wrapper::client::{RequestResult, ServiceClient};

Expand Down Expand Up @@ -95,7 +95,10 @@ async fn get_client_list(rv_entry: &RendezvousInterpretedDirective) -> Result<Ve
bail!("Non-HTTP(S) protocol is not implemented");
}
for url in &urls {
service_client_list.push(fdo_http_wrapper::client::ServiceClient::new(url));
service_client_list.push(fdo_http_wrapper::client::ServiceClient::new(
ProtocolVersion::Version1_1,
url,
));
}
log::trace!("Client list: {:?}", service_client_list);
Ok(service_client_list)
Expand All @@ -114,11 +117,11 @@ async fn perform_to1(
let sig_type = DeviceSigType::StSECP384R1;

// Send: HelloRV, Receive: HelloRVAck
let hello_rv = messages::to1::HelloRV::new(
let hello_rv = messages::v11::to1::HelloRV::new(
devcred.device_guid().clone(),
SigInfo::new(sig_type, vec![]),
);
let hello_rv_ack: RequestResult<messages::to1::HelloRVAck> =
let hello_rv_ack: RequestResult<messages::v11::to1::HelloRVAck> =
client.send_request(hello_rv, None).await;
let hello_rv_ack = hello_rv_ack.context("Error sending HelloRV")?;
log::trace!("Hello RV ack: {:?}", hello_rv_ack);
Expand All @@ -145,8 +148,8 @@ async fn perform_to1(
log::trace!("Sending token: {:?}", token);

// Send: ProveToRV, Receive: RVRedirect
let prove_to_rv = messages::to1::ProveToRV::new(token);
let rv_redirect: RequestResult<messages::to1::RVRedirect> =
let prove_to_rv = messages::v11::to1::ProveToRV::new(token);
let rv_redirect: RequestResult<messages::v11::to1::RVRedirect> =
client.send_request(prove_to_rv, None).await;
let rv_redirect = rv_redirect.context("Error proving self to rendezvous server")?;

Expand Down Expand Up @@ -195,8 +198,11 @@ async fn get_ov_entries(
let mut entries = ParsedArray::new_empty();

for entry_num in 0..num_entries {
let entry_result: RequestResult<messages::to2::OVNextEntry> = client
.send_request(messages::to2::GetOVNextEntry::new(entry_num as u8), None)
let entry_result: RequestResult<messages::v11::to2::OVNextEntry> = client
.send_request(
messages::v11::to2::GetOVNextEntry::new(entry_num as u8),
None,
)
.await;
let entry_result =
entry_result.with_context(|| format!("Error getting OV entry num {}", entry_num))?;
Expand Down Expand Up @@ -231,12 +237,12 @@ async fn perform_to2(
let kexsuite = KexSuite::Ecdh384;
let ciphersuite = CipherSuite::A256Gcm;

let mut client = fdo_http_wrapper::client::ServiceClient::new(url);
let mut client = fdo_http_wrapper::client::ServiceClient::new(ProtocolVersion::Version1_1, url);

// Send: HelloDevice, Receive: ProveOVHdr
let prove_ov_hdr: RequestResult<messages::to2::ProveOVHdr> = client
let prove_ov_hdr: RequestResult<messages::v11::to2::ProveOVHdr> = client
.send_request(
messages::to2::HelloDevice::new(
messages::v11::to2::HelloDevice::new(
devcred.device_guid().clone(),
nonce5.clone(),
kexsuite,
Expand Down Expand Up @@ -277,14 +283,10 @@ async fn perform_to2(

// Verify the HMAC, we do this in an extra scope to not leak anything untrusted out
let header_hmac = {
let ov_hdr_vec = prove_ov_hdr_payload
.get_unverified_value()
.ov_header()
.serialize_data()
.context("Error serializing Ownership Voucher header")?;
let ov_hdr_vec = prove_ov_hdr_payload.get_unverified_value().ov_header();
let ov_hdr_hmac = prove_ov_hdr_payload.get_unverified_value().hmac();
devcred
.verify_hmac(&ov_hdr_vec, ov_hdr_hmac)
.verify_hmac(ov_hdr_vec, ov_hdr_hmac)
.context("Error verifying ownership voucher HMAC")?;
log::trace!("Ownership Voucher HMAC validated");

Expand All @@ -294,6 +296,7 @@ async fn perform_to2(
// Validate the PubKeyHash
{
let header = prove_ov_hdr_payload.get_unverified_value().ov_header();
let header = OwnershipVoucherHeader::deserialize_data(header)?;
let pubkey_hash = header
.manufacturer_public_key_hash(devcred.manufacturer_pubkey_hash().get_type())
.context("Error computing manufacturer public key hash")?;
Expand Down Expand Up @@ -321,11 +324,8 @@ async fn perform_to2(

// At this moment, we have validated all we can, we'll check the signature later (After we get the final bits of the OV)
let ownership_voucher = {
let header = prove_ov_hdr_payload
.get_unverified_value()
.ov_header()
.clone();
OwnershipVoucher::from_parts(header, header_hmac, ov_entries)
let header = prove_ov_hdr_payload.get_unverified_value().ov_header();
OwnershipVoucher::from_parts(ProtocolVersion::Version1_1, header, header_hmac, ov_entries)
}
.context("Error reconstructing Ownership Voucher")?;
log::trace!(
Expand Down Expand Up @@ -391,15 +391,18 @@ async fn perform_to2(
.context("Error signing ProveDevice EAT")?;

log::trace!("Prepared prove_device_token: {:?}", prove_device_token);
let prove_device_msg = messages::to2::ProveDevice::new(prove_device_token);
let setup_device: RequestResult<messages::to2::SetupDevice> =
let prove_device_msg = messages::v11::to2::ProveDevice::new(prove_device_token);
let setup_device: RequestResult<messages::v11::to2::SetupDevice> =
client.send_request(prove_device_msg, Some(new_keys)).await;
let setup_device = setup_device.context("Error proving device")?;
log::trace!("Got setup_device response: {:?}", setup_device);

// Send: DeviceServiceInfoReady, Receive: OwnerServiceInfoReady
let owner_service_info_ready: RequestResult<messages::to2::OwnerServiceInfoReady> = client
.send_request(messages::to2::DeviceServiceInfoReady::new(None, None), None)
let owner_service_info_ready: RequestResult<messages::v11::to2::OwnerServiceInfoReady> = client
.send_request(
messages::v11::to2::DeviceServiceInfoReady::new(None, None),
None,
)
.await;
let owner_service_info_ready =
owner_service_info_ready.context("Error getting OwnerServiceInfoReady")?;
Expand All @@ -421,8 +424,8 @@ async fn perform_to2(
.context("Error deactivating device credential")?;

// Send: Done, Receive: Done2
let done2: RequestResult<messages::to2::Done2> = client
.send_request(messages::to2::Done::new(nonce6), None)
let done2: RequestResult<messages::v11::to2::Done2> = client
.send_request(messages::v11::to2::Done::new(nonce6), None)
.await;
let done2 = done2.context("Error sending Done2")?;

Expand Down Expand Up @@ -498,6 +501,12 @@ async fn main() -> Result<()> {
log::info!("Device credential deactivated, skipping Device Onboarding");
return Ok(());
}
if dc.protocol_version() != ProtocolVersion::Version1_1 {
bail!(
"Device credential protocol version {} not supported",
dc.protocol_version()
);
}

// Get rv entries
let rv_info = get_rv_info(dc.as_ref())?;
Expand Down
2 changes: 1 addition & 1 deletion client-linuxapp/src/serviceinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use anyhow::{anyhow, bail, Context, Result};

use fdo_data_formats::{
constants::HashType,
messages::to2::{DeviceServiceInfo, OwnerServiceInfo},
messages::v11::to2::{DeviceServiceInfo, OwnerServiceInfo},
types::{CborSimpleTypeExt, Hash, ServiceInfo},
};
use fdo_http_wrapper::client::{RequestResult, ServiceClient};
Expand Down
62 changes: 49 additions & 13 deletions data-formats/src/cborparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ mod private {
impl Sealed for super::ParsedArraySize4 {}
impl Sealed for super::ParsedArraySize5 {}
impl Sealed for super::ParsedArraySize6 {}
impl Sealed for super::ParsedArraySize7 {}
impl Sealed for super::ParsedArraySize8 {}
}

macro_rules! parsed_array_size {
Expand Down Expand Up @@ -68,6 +70,8 @@ parsed_array_size!(3);
parsed_array_size!(4);
parsed_array_size!(5);
parsed_array_size!(6);
parsed_array_size!(7);
parsed_array_size!(8);

#[derive(Clone)]
pub struct ParsedArray<N: ParsedArraySize> {
Expand Down Expand Up @@ -264,7 +268,6 @@ impl<N: ParsedArraySize> Serializable for ParsedArray<N> {

if let Some(expected_len) = N::SIZE {
if map_len != expected_len {
log::warn!("Expected {} elements, but found {}", expected_len, map_len);
return Err(ArrayParseError::InvalidNumberOfElements(map_len, expected_len).into());
}
} else if map_len == 0 {
Expand Down Expand Up @@ -403,30 +406,58 @@ impl ParsedArray<ParsedArraySizeDynamic> {
}
}

impl<N: ParsedArraySize> ParsedArray<N>
#[derive(Clone, Default)]
pub struct ParsedArrayBuilder<N: ParsedArraySize> {
contents: Vec<Option<Vec<u8>>>,

_marker: std::marker::PhantomData<N>,
}

impl<N: ParsedArraySize> ParsedArrayBuilder<N>
where
N: ParsedArraySizeStatic,
{
/// This creates a new ParsedArray
///
/// # Safety
/// This is unsafe because the caller needs to make sure that the entries
/// are filled directly after the array.
/// This means that between the call to this new and the update entries, the
/// state of the contents of this array is undefined.
// TODO: Turn this into a builder perhaps?
pub unsafe fn new() -> Self {
let new = vec![vec![]; N::SIZE.unwrap() as usize];
pub fn new() -> Self {
let new = vec![None; N::SIZE.unwrap() as usize];
Self {
contents: new,

_marker: std::marker::PhantomData,
}
}

pub fn set<T>(&mut self, n: usize, value: &T) -> Result<(), Error>
where
T: Serializable,
{
check_bounds!(n);
self.contents[n] = Some(value.serialize_data()?);
Ok(())
}

/// This method turns the ParsedArrayBuilder into a ParsedArray.
///
/// Safety:
/// This will panic if not all the elements have been set!
pub fn build(mut self) -> ParsedArray<N> {
let contents: Option<Vec<Vec<u8>>> = self.contents.drain(..).collect();
let contents = contents.expect("Not all elements set!");

ParsedArray {
tag: None,

header: None,
contents: new,
contents,

_marker: std::marker::PhantomData,
}
}
}

impl<N: ParsedArraySize> ParsedArray<N>
where
N: ParsedArraySizeStatic,
{
pub fn set<T>(&mut self, n: usize, value: &T) -> Result<(), Error>
where
T: Serializable,
Expand Down Expand Up @@ -462,6 +493,11 @@ impl<N: ParsedArraySize> ParsedArray<N> {
T::deserialize_data(&self.contents[n])
}

pub fn get_raw(&self, n: usize) -> &[u8] {
check_bounds!(n);
&self.contents[n]
}

pub fn get_hash(&self, n: usize, hash_type: HashType) -> Result<Hash, Error> {
check_bounds!(n);
Hash::from_data(hash_type, &self.contents[n])
Expand Down
33 changes: 24 additions & 9 deletions data-formats/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ use num_traits::FromPrimitive;
use openssl::hash::MessageDigest;
use serde_repr::{Deserialize_repr, Serialize_repr};

#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq, Eq, PartialOrd)]
#[repr(u16)]
#[non_exhaustive]
pub enum ProtocolVersion {
Version1_0 = 100,
Version1_1 = 101,
}

impl std::fmt::Display for ProtocolVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", *self as u16)
}
}

#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq, Eq)]
#[repr(i8)]
#[non_exhaustive]
Expand Down Expand Up @@ -103,10 +117,11 @@ pub enum DeviceSigType {
#[repr(i16)]
#[non_exhaustive]
pub enum PublicKeyType {
Rsa2048RESTR = RS256,
Rsa = RS384,
SECP256R1 = (aws_nitro_enclaves_cose::sign::SignatureAlgorithm::ES256 as i16),
SECP384R1 = (aws_nitro_enclaves_cose::sign::SignatureAlgorithm::ES384 as i16),
Rsa2048RESTR = 1,
RsaPkcs = 5,
RsaPss = 6,
SECP256R1 = 10,
SECP384R1 = 11,
}

#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
Expand All @@ -115,7 +130,7 @@ pub enum PublicKeyType {
pub enum PublicKeyEncoding {
Crypto = 0,
X509 = 1,
COSEX509 = 2,
X5CHAIN = 2,
Cosekey = 3,
}

Expand All @@ -127,11 +142,11 @@ pub enum HeaderKeys {
EatNonce = 10,
EatUeid = 11,

CUPHNonce = -17760701, // IANA Pending
CUPHOwnerPubKey = -17760702, // IANA Pending
EUPHNonce = -17760709, // IANA Pending
CUPHNonce = 256,
CUPHOwnerPubKey = 257,
EUPHNonce = -259,

EatFDO = -17760707,
EatFDO = -257,
}

impl HeaderKeys {
Expand Down
18 changes: 9 additions & 9 deletions data-formats/src/devicecredential/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
errors::Error,
types::HMac,
types::{Guid, Hash, RendezvousInfo},
DeviceCredential,
DeviceCredential, ProtocolVersion,
};

use openssl::{pkey::PKey, sign::Signer};
Expand All @@ -11,13 +11,13 @@ use serde_tuple::Serialize_tuple;

#[derive(Debug, Serialize_tuple, Deserialize)]
pub struct FileDeviceCredential {
pub active: bool, // Active
pub protver: u16, // ProtVer
pub hmac_secret: Vec<u8>, // HmacSecret
pub device_info: String, // DeviceInfo
pub guid: Guid, // Guid
pub rvinfo: RendezvousInfo, // RVInfo
pub pubkey_hash: Hash, // PubKeyHash
pub active: bool, // Active
pub protver: ProtocolVersion, // ProtVer
pub hmac_secret: Vec<u8>, // HmacSecret
pub device_info: String, // DeviceInfo
pub guid: Guid, // Guid
pub rvinfo: RendezvousInfo, // RVInfo
pub pubkey_hash: Hash, // PubKeyHash

// Custom from here
pub private_key: Vec<u8>,
Expand All @@ -28,7 +28,7 @@ impl DeviceCredential for FileDeviceCredential {
self.active
}

fn protocol_version(&self) -> u16 {
fn protocol_version(&self) -> ProtocolVersion {
self.protver
}

Expand Down
Loading

0 comments on commit c99be76

Please sign in to comment.