Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,4 @@ once_cell = { default-features = false, version = "1.17.0" }
webrtc-util = { version = "0.8.0" }
embed-doc-image = { version = "0.1.4" }
hyper = { version = "0.14.25" }
ctap-hid-fido2 = { version = "3.5.0" }
1 change: 1 addition & 0 deletions citadel_proto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ itertools = { workspace = true }
tracing = { workspace = true, optional = true }
bytemuck = { workspace = true, features = ["derive"] }
chrono = { workspace = true }
ctap-hid-fido2 = { workspace = true }

[dev-dependencies]
citadel_logging = { workspace = true }
Expand Down
9 changes: 9 additions & 0 deletions citadel_proto/src/proto/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub(crate) mod packet_flags {
pub(crate) const FILE: u8 = 9;
pub(crate) const UDP: u8 = 10;
pub(crate) const HOLE_PUNCH: u8 = 11;
pub(crate) const FIDO2: u8 = 12;
}

pub(crate) mod aux {
Expand Down Expand Up @@ -118,6 +119,14 @@ pub(crate) mod packet_flags {
pub(crate) const KEEP_ALIVE: u8 = 1;
pub(crate) const HOLE_PUNCH: u8 = 2;
}

pub(crate) mod fido2 {
pub(crate) const REGISTER: u8 = 0;
pub(crate) const CHALLENGE: u8 = 1;
pub(crate) const ATTESTATION: u8 = 2;
pub(crate) const SUCCESS: u8 = 3;
pub(crate) const FAILURE: u8 = 4;
}
}
}

Expand Down
282 changes: 282 additions & 0 deletions citadel_proto/src/proto/packet_crafter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,288 @@ pub mod do_disconnect {
}
}

/// For creating FIDO2 packets
pub mod fido2 {
use bytes::{BufMut, BytesMut};
use ctap_hid_fido2::fidokey::get_assertion::get_assertion_params::Assertion;
use ctap_hid_fido2::fidokey::make_credential::Attestation;
use zerocopy::{I64, U128, U32, U64};

use crate::constants::HDP_HEADER_BYTE_LEN;
use crate::prelude::{Ticket, VirtualTargetType};
use crate::proto::packet::{packet_flags, HdpHeader};
use citadel_crypt::stacked_ratchet::constructor::{AliceToBobTransfer, BobToAliceTransfer};
use citadel_crypt::stacked_ratchet::StackedRatchet;
use citadel_types::crypto::SecurityLevel;
use citadel_user::auth::proposed_credentials::ProposedCredentials;
use citadel_user::serialization::SyncIO;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub(crate) struct Fido2RegisterPacket {
pub(crate) rpid: String,
pub(crate) require_password: bool,
}

/// Crafts a packet used to initiate the FIDO2 registration process
pub(crate) fn craft_register_packet(
hyper_ratchet: &StackedRatchet,
ticket: Ticket,
security_level: SecurityLevel,
virtual_target: VirtualTargetType,
timestamp: i64,
rpid: String,
require_password: bool,
) -> BytesMut {
let header = HdpHeader {
protocol_version: (*crate::constants::PROTOCOL_VERSION).into(),
cmd_primary: packet_flags::cmd::primary::FIDO2,
cmd_aux: packet_flags::cmd::aux::fido2::REGISTER,
algorithm: 0,
security_level: security_level.value(),
context_info: U128::new(ticket.0),
group: U64::new(0),
wave_id: U32::new(0),
session_cid: U64::new(hyper_ratchet.get_cid()),
drill_version: U32::new(hyper_ratchet.version()),
timestamp: I64::new(timestamp),
target_cid: U64::new(virtual_target.get_target_cid()),
};

let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN);
header.inscribe_into(&mut packet);
let payload = Fido2RegisterPacket {
rpid,
require_password,
};

payload.serialize_into_buf(&mut packet).unwrap();

hyper_ratchet
.protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet)
.unwrap();

packet
}

#[derive(Serialize, Deserialize)]
pub(crate) struct Fido2ChallengePacket {
pub(crate) challenge: [u8; 32],
pub(crate) rpid: String,
pub(crate) require_password: bool,
}

/// Creates a packet with FIDO2 challenge to be used in authentication
pub(crate) fn craft_challenge_packet(
hyper_ratchet: &StackedRatchet,
ticket: Ticket,
security_level: SecurityLevel,
virtual_target: VirtualTargetType,
timestamp: i64,
challenge: [u8; 32],
rpid: String,
require_password: bool,
) -> BytesMut {
let header = HdpHeader {
protocol_version: (*crate::constants::PROTOCOL_VERSION).into(),
cmd_primary: packet_flags::cmd::primary::FIDO2,
cmd_aux: packet_flags::cmd::aux::fido2::CHALLENGE,
algorithm: 0,
security_level: security_level.value(),
context_info: U128::new(ticket.0),
group: U64::new(0),
wave_id: U32::new(0),
session_cid: U64::new(hyper_ratchet.get_cid()),
drill_version: U32::new(hyper_ratchet.version()),
timestamp: I64::new(timestamp),
target_cid: U64::new(virtual_target.get_target_cid()),
};

let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN);
header.inscribe_into(&mut packet);
let payload = Fido2ChallengePacket {
challenge,
rpid,
require_password,
};

payload.serialize_into_buf(&mut packet).unwrap();

hyper_ratchet
.protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet)
.unwrap();

packet
}

#[derive(Serialize, Deserialize)]
pub(crate) struct Fido2AttestationPacket {
pub(crate) attestation: Attestation,
pub(crate) proposed_credentials: Option<ProposedCredentials>,
}

/// Crafts a packet with an attestation in response to a FIDO2 registration challenge
#[allow(unused_results)]
pub(crate) fn craft_attestation_packet(
hyper_ratchet: &StackedRatchet,
ticket: Ticket,
timestamp: i64,
credentials: Option<ProposedCredentials>,
attestation: Attestation,
security_level: SecurityLevel,
virtual_target: VirtualTargetType,
) -> BytesMut {
let header = HdpHeader {
protocol_version: (*crate::constants::PROTOCOL_VERSION).into(),
cmd_primary: packet_flags::cmd::primary::FIDO2,
cmd_aux: packet_flags::cmd::aux::fido2::ATTESTATION,
algorithm: 0,
security_level: security_level.value(),
context_info: U128::new(ticket.0),
group: U64::new(0),
wave_id: U32::new(0),
session_cid: U64::new(hyper_ratchet.get_cid()),
drill_version: U32::new(hyper_ratchet.version()),
timestamp: I64::new(timestamp),
target_cid: U64::new(virtual_target.get_target_cid()),
};

let total_len = HDP_HEADER_BYTE_LEN;
let mut packet = BytesMut::with_capacity(total_len);
let payload = Fido2AttestationPacket {
attestation,
proposed_credentials: credentials,
};
header.inscribe_into(&mut packet);
payload.serialize_into_buf(&mut packet).unwrap();

hyper_ratchet
.protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet)
.unwrap();

packet
}

#[derive(Serialize, Deserialize)]
pub(crate) struct Fido2AssertionPacket {
pub(crate) assertion: Assertion,
pub(crate) proposed_credentials: Option<ProposedCredentials>,
}

/// Crafts a packet with an assertion in response to a FIDO2 authentication challenge
#[allow(unused_results)]
pub(crate) fn craft_assertion_packet(
hyper_ratchet: &StackedRatchet,
ticket: Ticket,
timestamp: i64,
credentials: Option<ProposedCredentials>,
assertion: Assertion,
security_level: SecurityLevel,
virtual_target: VirtualTargetType,
) -> BytesMut {
let header = HdpHeader {
protocol_version: (*crate::constants::PROTOCOL_VERSION).into(),
cmd_primary: packet_flags::cmd::primary::FIDO2,
cmd_aux: packet_flags::cmd::aux::fido2::ATTESTATION,
algorithm: 0,
security_level: security_level.value(),
context_info: U128::new(ticket.0),
group: U64::new(0),
wave_id: U32::new(0),
session_cid: U64::new(hyper_ratchet.get_cid()),
drill_version: U32::new(hyper_ratchet.version()),
timestamp: I64::new(timestamp),
target_cid: U64::new(virtual_target.get_target_cid()),
};

let total_len = HDP_HEADER_BYTE_LEN;
let mut packet = BytesMut::with_capacity(total_len);
let payload = Fido2AssertionPacket {
assertion,
proposed_credentials: credentials,
};
header.inscribe_into(&mut packet);
payload.serialize_into_buf(&mut packet).unwrap();

hyper_ratchet
.protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet)
.unwrap();

packet
}

pub(crate) fn craft_success_packet<T: AsRef<[u8]>>(
hyper_ratchet: &StackedRatchet,
ticket: Ticket,
virtual_target: VirtualTargetType,
timestamp: i64,
success_message: T,
security_level: SecurityLevel,
) -> BytesMut {
let success_message = success_message.as_ref();
let success_message_len = success_message.len();
let header = HdpHeader {
protocol_version: (*crate::constants::PROTOCOL_VERSION).into(),
cmd_primary: packet_flags::cmd::primary::FIDO2,
cmd_aux: packet_flags::cmd::aux::fido2::SUCCESS,
algorithm: 0,
security_level: security_level.value(),
context_info: U128::new(ticket.0),
group: U64::new(0),
wave_id: U32::new(0),
session_cid: U64::new(hyper_ratchet.get_cid()),
drill_version: U32::new(hyper_ratchet.version()),
timestamp: I64::new(timestamp),
target_cid: U64::new(virtual_target.get_target_cid()),
};

let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN + success_message_len);
header.inscribe_into(&mut packet);
packet.put(success_message);

hyper_ratchet
.protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet)
.unwrap();

packet
}

pub(crate) fn craft_failure_packet<T: AsRef<[u8]>>(
hyper_ratchet: &StackedRatchet,
ticket: Ticket,
virtual_target: VirtualTargetType,
timestamp: i64,
error_message: T,
security_level: SecurityLevel,
) -> BytesMut {
let error_message = error_message.as_ref();
let header = HdpHeader {
protocol_version: (*crate::constants::PROTOCOL_VERSION).into(),
cmd_primary: packet_flags::cmd::primary::FIDO2,
cmd_aux: packet_flags::cmd::aux::fido2::FAILURE,
algorithm: 0,
security_level: security_level.value(),
context_info: U128::new(ticket.0),
group: U64::new(0),
wave_id: U32::new(0),
session_cid: U64::new(hyper_ratchet.get_cid()),
drill_version: U32::new(hyper_ratchet.version()),
timestamp: I64::new(timestamp),
target_cid: U64::new(virtual_target.get_target_cid()),
};

let mut packet = BytesMut::with_capacity(HDP_HEADER_BYTE_LEN + error_message.len());
header.inscribe_into(&mut packet);
packet.put(error_message);

hyper_ratchet
.protect_message_packet(Some(security_level), HDP_HEADER_BYTE_LEN, &mut packet)
.unwrap();

packet
}
}

pub(crate) mod do_drill_update {
use bytes::BytesMut;
use zerocopy::{I64, U128, U32, U64};
Expand Down