tests: Add Request trait and reply types

This commit is contained in:
Robin Krahl
2024-05-22 22:55:32 +02:00
parent aad05b9573
commit efed1896bc
3 changed files with 82 additions and 33 deletions
+11 -19
View File
@@ -3,13 +3,8 @@
mod virt;
mod webauthn;
use std::collections::BTreeMap;
use ciborium::Value;
use ctap_types::ctap2::Operation;
use virt::Ctap2Error;
use webauthn::{MakeCredentialRequest, PubKeyCredParam, Rp, User};
use webauthn::{GetInfo, MakeCredential, PubKeyCredParam, Rp, User};
#[test]
fn test_ping() {
@@ -21,10 +16,9 @@ fn test_ping() {
#[test]
fn test_get_info() {
virt::run_ctap2(|device| {
let reply: BTreeMap<u8, Value> = device.call(Operation::GetInfo, &Value::Null).unwrap();
let versions: Vec<String> = reply.get(&1).unwrap().deserialized().unwrap();
assert!(versions.contains(&"FIDO_2_0".to_owned()));
assert!(versions.contains(&"FIDO_2_1".to_owned()));
let reply = device.exec(GetInfo).unwrap();
assert!(reply.versions.contains(&"FIDO_2_0".to_owned()));
assert!(reply.versions.contains(&"FIDO_2_1".to_owned()));
});
}
@@ -36,13 +30,11 @@ fn test_make_credential() {
.name("john.doe")
.display_name("John Doe");
let pub_key_cred_params = vec![PubKeyCredParam::new("public-key", -7)];
let request = MakeCredentialRequest::new(b"", rp, user, pub_key_cred_params);
let reply: BTreeMap<u8, Value> = device
.call(Operation::MakeCredential, &request.into())
.unwrap();
assert_eq!(reply.get(&1).unwrap(), &Value::from("packed"));
assert!(reply.contains_key(&2));
assert!(reply.contains_key(&3));
let request = MakeCredential::new(b"", rp, user, pub_key_cred_params);
let reply = device.exec(request).unwrap();
assert_eq!(reply.fmt, "packed");
assert!(reply.auth_data.is_bytes());
assert!(reply.att_stmt.is_map());
});
}
@@ -54,8 +46,8 @@ fn test_make_credential_invalid_params() {
.name("john.doe")
.display_name("John Doe");
let pub_key_cred_params = vec![PubKeyCredParam::new("public-key", -11)];
let request = MakeCredentialRequest::new(b"", rp, user, pub_key_cred_params);
let result = device.call::<Value>(Operation::MakeCredential, &request.into());
let request = MakeCredential::new(b"", rp, user, pub_key_cred_params);
let result = device.exec(request);
assert_eq!(result, Err(Ctap2Error(0x26)));
});
}
+8 -10
View File
@@ -13,7 +13,6 @@ use std::{
};
use ciborium::Value;
use ctap_types::ctap2::Operation;
use ctaphid::{
error::{RequestError, ResponseError},
HidDevice, HidDeviceInfo,
@@ -23,9 +22,10 @@ use ctaphid_dispatch::{
types::{Channel, Requester},
};
use fido_authenticator::{Authenticator, Config, Conforming};
use serde::de::DeserializeOwned;
use trussed_staging::virt;
use crate::webauthn::Request;
use pipe::Pipe;
static INIT_LOGGER: Once = Once::new();
@@ -92,23 +92,21 @@ where
pub struct Ctap2<'a>(ctaphid::Device<Device<'a>>);
impl Ctap2<'_> {
pub fn call<T: DeserializeOwned>(
&self,
operation: Operation,
data: &Value,
) -> Result<T, Ctap2Error> {
pub fn exec<R: Request>(&self, request: R) -> Result<R::Reply, Ctap2Error> {
let request = request.into();
let mut serialized = Vec::new();
ciborium::into_writer(data, &mut serialized).unwrap();
ciborium::into_writer(&request, &mut serialized).unwrap();
let reply = self
.0
.ctap2(operation.into(), &serialized)
.ctap2(R::COMMAND, &serialized)
.map_err(|err| match err {
ctaphid::error::Error::CommandError(ctaphid::error::CommandError::CborError(
value,
)) => Ctap2Error(value),
err => panic!("failed to execute CTAP2 command: {err:?}"),
})?;
Ok(ciborium::from_reader(reply.as_slice()).unwrap())
let value: Value = ciborium::from_reader(reply.as_slice()).unwrap();
Ok(value.into())
}
}
+63 -4
View File
@@ -1,3 +1,5 @@
use std::collections::BTreeMap;
use ciborium::Value;
#[derive(Default)]
@@ -15,6 +17,12 @@ impl From<Map> for Value {
}
}
pub trait Request: Into<Value> {
const COMMAND: u8;
type Reply: From<Value>;
}
pub struct Rp {
id: String,
name: Option<String>,
@@ -108,14 +116,14 @@ impl From<PubKeyCredParam> for Value {
}
}
pub struct MakeCredentialRequest {
pub struct MakeCredential {
client_data_hash: Vec<u8>,
rp: Rp,
user: User,
pub_key_cred_params: Vec<PubKeyCredParam>,
}
impl MakeCredentialRequest {
impl MakeCredential {
pub fn new(
client_data_hash: impl Into<Vec<u8>>,
rp: Rp,
@@ -131,8 +139,8 @@ impl MakeCredentialRequest {
}
}
impl From<MakeCredentialRequest> for Value {
fn from(request: MakeCredentialRequest) -> Value {
impl From<MakeCredential> for Value {
fn from(request: MakeCredential) -> Value {
let mut map = Map::default();
map.push(1, request.client_data_hash);
map.push(2, request.rp);
@@ -148,3 +156,54 @@ impl From<MakeCredentialRequest> for Value {
map.into()
}
}
impl Request for MakeCredential {
const COMMAND: u8 = 0x01;
type Reply = MakeCredentialReply;
}
#[derive(Debug, PartialEq)]
pub struct MakeCredentialReply {
pub fmt: String,
pub auth_data: Value,
pub att_stmt: Value,
}
impl From<Value> for MakeCredentialReply {
fn from(value: Value) -> Self {
let mut map: BTreeMap<u8, Value> = value.deserialized().unwrap();
Self {
fmt: map.remove(&1).unwrap().deserialized().unwrap(),
auth_data: map.remove(&2).unwrap(),
att_stmt: map.remove(&3).unwrap(),
}
}
}
pub struct GetInfo;
impl From<GetInfo> for Value {
fn from(_: GetInfo) -> Self {
Self::Null
}
}
impl Request for GetInfo {
const COMMAND: u8 = 0x04;
type Reply = GetInfoReply;
}
pub struct GetInfoReply {
pub versions: Vec<String>,
}
impl From<Value> for GetInfoReply {
fn from(value: Value) -> Self {
let mut map: BTreeMap<u8, Value> = value.deserialized().unwrap();
Self {
versions: map.remove(&1).unwrap().deserialized().unwrap(),
}
}
}